diff --git a/.gitignore b/.gitignore index 6586246..3f9bc5e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ +.phpcs-cache composer.phar composer.lock phpcs.xml +phpstan.neon vendor/ diff --git a/.travis.yml b/.travis.yml index 6f0bf6d..95f327c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,26 +2,19 @@ dist: trusty language: php php: - - 7.1 - - 7.2 - - nightly - -matrix: - allow_failures: - - php: nightly + - 7.1 + - 7.2 + - nightly cache: - directories: - - $HOME/.composer/cache + directories: + - $HOME/.composer/cache before_install: - - mv ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini{,.disabled} || echo "xdebug not available" - - travis_retry composer self-update + - mv ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini{,.disabled} || echo "xdebug not available" + - travis_retry composer self-update -install: - - composer require react/promise:2.* - - composer require psr/http-message:1.* - - travis_retry composer update --prefer-dist +install: travis_retry composer update --prefer-dist script: ./vendor/bin/phpunit --group default,ReactPromise @@ -45,21 +38,16 @@ jobs: after_script: - wget https://scrutinizer-ci.com/ocular.phar - php ocular.phar code-coverage:upload --format=php-clover clover.xml - - stage: Pull request coding standard - if: type = pull_request + + - stage: Code Quality + php: 7.1 + env: CODING_STANDARD install: travis_retry composer install --prefer-dist script: - - | - if [ $TRAVIS_BRANCH != "master" ]; then - git remote set-branches --add origin $TRAVIS_BRANCH; - git fetch origin $TRAVIS_BRANCH; - fi - - git merge-base origin/$TRAVIS_BRANCH $TRAVIS_PULL_REQUEST_SHA || git fetch origin +refs/pull/$TRAVIS_PULL_REQUEST/merge --unshallow - - wget https://github.com/diff-sniffer/git/releases/download/0.1.0/git-phpcs.phar - - php git-phpcs.phar origin/$TRAVIS_BRANCH...$TRAVIS_PULL_REQUEST_SHA -# - stage: Coding standard -# if: NOT type = pull_request -# php: 7.1 -# install: travis_retry composer install --prefer-dist -# script: -# - ./vendor/bin/phpcs + - ./vendor/bin/phpcs + + - stage: Code Quality + php: 7.1 + env: STATIC_ANALYSIS + install: travis_retry composer install --prefer-dist + script: composer static-analysis diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e8ec9e6..95804ce 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,6 +25,8 @@ composer install ./vendor/bin/phpunit ``` +Some tests have annotation `@see it('')`. It is used for reference to same tests in [graphql-js implementation](https://github.com/graphql/graphql-js) with the same description. + ## Coding Standard Coding standard of this project is based on [Doctrine CS](https://github.com/doctrine/coding-standard). To run the inspection: diff --git a/composer.json b/composer.json index d0a9d34..152bcdf 100644 --- a/composer.json +++ b/composer.json @@ -15,8 +15,11 @@ }, "require-dev": { "doctrine/coding-standard": "^4.0", + "phpstan/phpstan": "^0.10.3", + "phpstan/phpstan-phpunit": "^0.10.0", "phpunit/phpunit": "^7.2", - "psr/http-message": "^1.0" + "psr/http-message": "^1.0", + "react/promise": "2.*" }, "config": { "preferred-install": "dist", @@ -39,6 +42,7 @@ "psr/http-message": "To use standard GraphQL server" }, "scripts": { - "lint" : "phpcs" + "lint" : "phpcs", + "static-analysis": "phpstan analyse --ansi -l 1 -c phpstan.neon.dist src tests" } } diff --git a/docs/getting-started.md b/docs/getting-started.md index 6b1a0cf..4477cda 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -4,7 +4,7 @@ first learn about GraphQL on [the official website](http://graphql.org/learn/). # Installation -Using [composer](https://getcomposer.org/doc/00-intro.md), simply run: +Using [composer](https://getcomposer.org/doc/00-intro.md), run: ```sh composer require webonyx/graphql-php diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..b2b86dc --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,3 @@ +includes: + - vendor/phpstan/phpstan-phpunit/extension.neon + - vendor/phpstan/phpstan-phpunit/rules.neon diff --git a/src/Deferred.php b/src/Deferred.php index 5182fb7..6e962de 100644 --- a/src/Deferred.php +++ b/src/Deferred.php @@ -1,23 +1,20 @@ isEmpty()) { + while ($q && ! $q->isEmpty()) { /** @var self $dfd */ $dfd = $q->dequeue(); $dfd->run(); @@ -38,7 +35,7 @@ class Deferred public function __construct(callable $callback) { $this->callback = $callback; - $this->promise = new SyncPromise(); + $this->promise = new SyncPromise(); self::getQueue()->enqueue($this); } @@ -47,7 +44,7 @@ class Deferred return $this->promise->then($onFulfilled, $onRejected); } - private function run() + public function run() : void { try { $cb = $this->callback; diff --git a/src/Executor/Executor.php b/src/Executor/Executor.php index 2a94e38..511939e 100644 --- a/src/Executor/Executor.php +++ b/src/Executor/Executor.php @@ -35,6 +35,7 @@ use GraphQL\Utils\TypeInfo; use GraphQL\Utils\Utils; use function array_keys; use function array_merge; +use function array_reduce; use function array_values; use function get_class; use function is_array; @@ -68,7 +69,7 @@ class Executor self::$UNDEFINED = Utils::undefined(); } - $this->exeContext = $context; + $this->exeContext = $context; $this->subFieldCache = new \SplObjectStorage(); } @@ -285,7 +286,7 @@ class Executor // field and its descendants will be omitted, and sibling fields will still // be executed. An execution which encounters errors will still result in a // resolved Promise. - $data = $this->executeOperation($this->exeContext->operation, $this->exeContext->rootValue); + $data = $this->executeOperation($this->exeContext->operation, $this->exeContext->rootValue); $result = $this->buildResponse($data); // Note: we deviate here from the reference implementation a bit by always returning promise @@ -995,8 +996,7 @@ class Executor * If the callback does not return a Promise, then this function will also not * return a Promise. * - * @param mixed[] $values - * @param \Closure $callback + * @param mixed[] $values * @param Promise|mixed|null $initialValue * @return mixed[] */ @@ -1291,23 +1291,18 @@ class Executor return $this->executeFields($returnType, $result, $path, $subFieldNodes); } - /** - * @param ObjectType $returnType - * @param $fieldNodes - * @return ArrayObject - */ - private function collectSubFields(ObjectType $returnType, $fieldNodes): ArrayObject + private function collectSubFields(ObjectType $returnType, $fieldNodes) : ArrayObject { - if (!isset($this->subFieldCache[$returnType])) { + if (! isset($this->subFieldCache[$returnType])) { $this->subFieldCache[$returnType] = new \SplObjectStorage(); } - if (!isset($this->subFieldCache[$returnType][$fieldNodes])) { + if (! isset($this->subFieldCache[$returnType][$fieldNodes])) { // Collect sub-fields to execute to complete this value. - $subFieldNodes = new \ArrayObject(); + $subFieldNodes = new \ArrayObject(); $visitedFragmentNames = new \ArrayObject(); foreach ($fieldNodes as $fieldNode) { - if (!isset($fieldNode->selectionSet)) { + if (! isset($fieldNode->selectionSet)) { continue; } diff --git a/src/Executor/Promise/Promise.php b/src/Executor/Promise/Promise.php index 8079663..2f060e1 100644 --- a/src/Executor/Promise/Promise.php +++ b/src/Executor/Promise/Promise.php @@ -6,6 +6,7 @@ namespace GraphQL\Executor\Promise; use GraphQL\Executor\Promise\Adapter\SyncPromise; use GraphQL\Utils\Utils; +use React\Promise\Promise as ReactPromise; /** * Convenience wrapper for promises represented by Promise Adapter diff --git a/src/GraphQL.php b/src/GraphQL.php index d5c6237..f7915bb 100644 --- a/src/GraphQL.php +++ b/src/GraphQL.php @@ -1,4 +1,7 @@ wait($promise); } @@ -93,30 +103,23 @@ class GraphQL * Useful for Async PHP platforms. * * @api - * @param PromiseAdapter $promiseAdapter - * @param \GraphQL\Type\Schema $schema - * @param string|DocumentNode $source - * @param mixed $rootValue - * @param mixed $context - * @param array|null $variableValues - * @param string|null $operationName - * @param callable $fieldResolver - * @param array $validationRules - * - * @return Promise + * @param string|DocumentNode $source + * @param mixed $rootValue + * @param mixed $context + * @param mixed[]|null $variableValues + * @param ValidationRule[]|null $validationRules */ public static function promiseToExecute( PromiseAdapter $promiseAdapter, - \GraphQL\Type\Schema $schema, + SchemaType $schema, $source, $rootValue = null, $context = null, $variableValues = null, - $operationName = null, - callable $fieldResolver = null, - array $validationRules = null - ) - { + ?string $operationName = null, + ?callable $fieldResolver = null, + ?array $validationRules = null + ) : Promise { try { if ($source instanceof DocumentNode) { $documentNode = $source; @@ -125,36 +128,38 @@ class GraphQL } // FIXME - if (!empty($validationRules)) { - foreach ($validationRules as $rule) { - if ($rule instanceof QueryComplexity) { - $rule->setRawVariableValues($variableValues); - } - } - } else { + if (empty($validationRules)) { /** @var QueryComplexity $queryComplexity */ $queryComplexity = DocumentValidator::getRule(QueryComplexity::class); $queryComplexity->setRawVariableValues($variableValues); + } else { + foreach ($validationRules as $rule) { + if (! ($rule instanceof QueryComplexity)) { + continue; + } + + $rule->setRawVariableValues($variableValues); + } } $validationErrors = DocumentValidator::validate($schema, $documentNode, $validationRules); - if (!empty($validationErrors)) { + if (! empty($validationErrors)) { return $promiseAdapter->createFulfilled( new ExecutionResult(null, $validationErrors) ); - } else { - return Executor::promiseToExecute( - $promiseAdapter, - $schema, - $documentNode, - $rootValue, - $context, - $variableValues, - $operationName, - $fieldResolver - ); } + + return Executor::promiseToExecute( + $promiseAdapter, + $schema, + $documentNode, + $rootValue, + $context, + $variableValues, + $operationName, + $fieldResolver + ); } catch (Error $e) { return $promiseAdapter->createFulfilled( new ExecutionResult(null, [$e]) @@ -165,29 +170,28 @@ class GraphQL /** * @deprecated Use executeQuery()->toArray() instead * - * @param \GraphQL\Type\Schema $schema * @param string|DocumentNode $source - * @param mixed $rootValue - * @param mixed $contextValue - * @param array|null $variableValues - * @param string|null $operationName - * @return Promise|array + * @param mixed $rootValue + * @param mixed $contextValue + * @param mixed[]|null $variableValues + * @return Promise|mixed[] */ public static function execute( - \GraphQL\Type\Schema $schema, + SchemaType $schema, $source, $rootValue = null, $contextValue = null, $variableValues = null, - $operationName = null - ) - { + ?string $operationName = null + ) { trigger_error( __METHOD__ . ' is deprecated, use GraphQL::executeQuery()->toArray() as a quick replacement', E_USER_DEPRECATED ); - $result = self::promiseToExecute( - $promiseAdapter = Executor::getPromiseAdapter(), + + $promiseAdapter = Executor::getPromiseAdapter(); + $result = self::promiseToExecute( + $promiseAdapter, $schema, $source, $rootValue, @@ -199,40 +203,40 @@ class GraphQL if ($promiseAdapter instanceof SyncPromiseAdapter) { $result = $promiseAdapter->wait($result)->toArray(); } else { - $result = $result->then(function(ExecutionResult $r) { + $result = $result->then(function (ExecutionResult $r) { return $r->toArray(); }); } + return $result; } /** * @deprecated renamed to executeQuery() * - * @param \GraphQL\Type\Schema $schema * @param string|DocumentNode $source - * @param mixed $rootValue - * @param mixed $contextValue - * @param array|null $variableValues - * @param string|null $operationName + * @param mixed $rootValue + * @param mixed $contextValue + * @param mixed[]|null $variableValues * * @return ExecutionResult|Promise */ public static function executeAndReturnResult( - \GraphQL\Type\Schema $schema, + SchemaType $schema, $source, $rootValue = null, $contextValue = null, $variableValues = null, - $operationName = null - ) - { + ?string $operationName = null + ) { trigger_error( __METHOD__ . ' is deprecated, use GraphQL::executeQuery() as a quick replacement', E_USER_DEPRECATED ); - $result = self::promiseToExecute( - $promiseAdapter = Executor::getPromiseAdapter(), + + $promiseAdapter = Executor::getPromiseAdapter(); + $result = self::promiseToExecute( + $promiseAdapter, $schema, $source, $rootValue, @@ -240,9 +244,11 @@ class GraphQL $variableValues, $operationName ); + if ($promiseAdapter instanceof SyncPromiseAdapter) { $result = $promiseAdapter->wait($result); } + return $result; } @@ -252,7 +258,7 @@ class GraphQL * @api * @return Directive[] */ - public static function getStandardDirectives() + public static function getStandardDirectives() : array { return array_values(Directive::getInternalDirectives()); } @@ -263,7 +269,7 @@ class GraphQL * @api * @return Type[] */ - public static function getStandardTypes() + public static function getStandardTypes() : array { return array_values(Type::getInternalTypes()); } @@ -274,23 +280,17 @@ class GraphQL * @api * @return ValidationRule[] */ - public static function getStandardValidationRules() + public static function getStandardValidationRules() : array { return array_values(DocumentValidator::defaultRules()); } - /** - * @param callable $fn - */ - public static function setDefaultFieldResolver(callable $fn) + public static function setDefaultFieldResolver(callable $fn) : void { Executor::setDefaultFieldResolver($fn); } - /** - * @param PromiseAdapter|null $promiseAdapter - */ - public static function setPromiseAdapter(PromiseAdapter $promiseAdapter = null) + public static function setPromiseAdapter(?PromiseAdapter $promiseAdapter = null) : void { Executor::setPromiseAdapter($promiseAdapter); } @@ -301,7 +301,7 @@ class GraphQL * @deprecated Renamed to getStandardDirectives * @return Directive[] */ - public static function getInternalDirectives() + public static function getInternalDirectives() : array { return self::getStandardDirectives(); } diff --git a/src/Language/Visitor.php b/src/Language/Visitor.php index e266f0f..94c83e7 100644 --- a/src/Language/Visitor.php +++ b/src/Language/Visitor.php @@ -263,7 +263,8 @@ class Visitor $visitFn = self::getVisitFn($visitor, $node->kind, $isLeaving); if ($visitFn) { - $result = call_user_func($visitFn, $node, $key, $parent, $path, $ancestors); + $result = call_user_func($visitFn, $node, $key, $parent, $path, $ancestors); + $editValue = null; if ($result !== null) { if ($result instanceof VisitorOperation) { diff --git a/src/Schema.php b/src/Schema.php index 39d500b..351047a 100644 --- a/src/Schema.php +++ b/src/Schema.php @@ -1,6 +1,12 @@ name) + sprintf('%s fields must be an array or a callable which returns such an array.', $this->name) ); } diff --git a/src/Type/Definition/IntType.php b/src/Type/Definition/IntType.php index 6c0a8f3..5b87643 100644 --- a/src/Type/Definition/IntType.php +++ b/src/Type/Definition/IntType.php @@ -65,7 +65,7 @@ values. Int can represent values between -(2^31) and 2^31 - 1. '; } $int = intval($num); // int cast with == used for performance reasons - // @codingStandardsIgnoreLine + // phpcs:ignore if ($int != $num) { throw new Error( 'Int cannot represent non-integer value: ' . diff --git a/src/Utils/AST.php b/src/Utils/AST.php index a1eaac2..ca61a94 100644 --- a/src/Utils/AST.php +++ b/src/Utils/AST.php @@ -255,7 +255,7 @@ class AST } if (is_float($serialized)) { // int cast with == used for performance reasons - // @codingStandardsIgnoreLine + // phpcs:ignore if ((int) $serialized == $serialized) { return new IntValueNode(['value' => $serialized]); } diff --git a/src/Validator/Rules/KnownDirectives.php b/src/Validator/Rules/KnownDirectives.php index ecccafd..e9a0b8c 100644 --- a/src/Validator/Rules/KnownDirectives.php +++ b/src/Validator/Rules/KnownDirectives.php @@ -7,7 +7,9 @@ namespace GraphQL\Validator\Rules; use GraphQL\Error\Error; use GraphQL\Language\AST\DirectiveNode; use GraphQL\Language\AST\InputObjectTypeDefinitionNode; +use GraphQL\Language\AST\Node; use GraphQL\Language\AST\NodeKind; +use GraphQL\Language\AST\NodeList; use GraphQL\Language\DirectiveLocation; use GraphQL\Validator\ValidationContext; use function count; @@ -59,7 +61,9 @@ class KnownDirectives extends ValidationRule } /** - * @param (Node|NodeList)[] $ancestors + * @param Node[]|NodeList[] $ancestors The type is actually (Node|NodeList)[] but this PSR-5 syntax is so far not supported by most of the tools + * + * @return string */ private function getDirectiveLocationForASTPath(array $ancestors) { diff --git a/src/Validator/Rules/VariablesDefaultValueAllowed.php b/src/Validator/Rules/VariablesDefaultValueAllowed.php index 64808fc..4f2b5cf 100644 --- a/src/Validator/Rules/VariablesDefaultValueAllowed.php +++ b/src/Validator/Rules/VariablesDefaultValueAllowed.php @@ -44,10 +44,10 @@ class VariablesDefaultValueAllowed extends ValidationRule return Visitor::skipNode(); }, - NodeKind::SELECTION_SET => function (SelectionSetNode $node) use ($context) { + NodeKind::SELECTION_SET => function (SelectionSetNode $node) { return Visitor::skipNode(); }, - NodeKind::FRAGMENT_DEFINITION => function (FragmentDefinitionNode $node) use ($context) { + NodeKind::FRAGMENT_DEFINITION => function (FragmentDefinitionNode $node) { return Visitor::skipNode(); }, ]; diff --git a/tests/Executor/AbstractPromiseTest.php b/tests/Executor/AbstractPromiseTest.php index 8a6f1d1..70e2edd 100644 --- a/tests/Executor/AbstractPromiseTest.php +++ b/tests/Executor/AbstractPromiseTest.php @@ -1,79 +1,83 @@ 'Pet', + 'name' => 'Pet', 'fields' => [ - 'name' => [ 'type' => Type::string() ] - ] + 'name' => ['type' => Type::string()], + ], ]); $DogType = new ObjectType([ - 'name' => 'Dog', - 'interfaces' => [ $PetType ], - 'isTypeOf' => function($obj) { - return new Deferred(function() use ($obj) { + 'name' => 'Dog', + 'interfaces' => [$PetType], + 'isTypeOf' => function ($obj) { + return new Deferred(function () use ($obj) { return $obj instanceof Dog; }); }, - 'fields' => [ - 'name' => [ 'type' => Type::string() ], - 'woofs' => [ 'type' => Type::boolean() ], - ] + 'fields' => [ + 'name' => ['type' => Type::string()], + 'woofs' => ['type' => Type::boolean()], + ], ]); $CatType = new ObjectType([ - 'name' => 'Cat', - 'interfaces' => [ $PetType ], - 'isTypeOf' => function($obj) { - return new Deferred(function() use ($obj) { + 'name' => 'Cat', + 'interfaces' => [$PetType], + 'isTypeOf' => function ($obj) { + return new Deferred(function () use ($obj) { return $obj instanceof Cat; }); }, - 'fields' => [ - 'name' => [ 'type' => Type::string() ], - 'meows' => [ 'type' => Type::boolean() ], - ] + 'fields' => [ + 'name' => ['type' => Type::string()], + 'meows' => ['type' => Type::boolean()], + ], ]); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ 'pets' => [ - 'type' => Type::listOf($PetType), - 'resolve' => function() { + 'type' => Type::listOf($PetType), + 'resolve' => function () { return [ new Dog('Odie', true), - new Cat('Garfield', false) + new Cat('Garfield', false), ]; - } - ] - ] + }, + ], + ], ]), - 'types' => [ $CatType, $DogType ] + 'types' => [$CatType, $DogType], ]); $query = '{ @@ -93,10 +97,10 @@ class AbstractPromiseTest extends TestCase $expected = [ 'data' => [ 'pets' => [ - [ 'name' => 'Odie', 'woofs' => true ], - [ 'name' => 'Garfield', 'meows' => false ] - ] - ] + ['name' => 'Odie', 'woofs' => true], + ['name' => 'Garfield', 'meows' => false], + ], + ], ]; $this->assertEquals($expected, $result); @@ -107,58 +111,57 @@ class AbstractPromiseTest extends TestCase */ public function testIsTypeOfCanBeRejected() : void { - $PetType = new InterfaceType([ - 'name' => 'Pet', + 'name' => 'Pet', 'fields' => [ - 'name' => ['type' => Type::string()] - ] + 'name' => ['type' => Type::string()], + ], ]); $DogType = new ObjectType([ - 'name' => 'Dog', + 'name' => 'Dog', 'interfaces' => [$PetType], - 'isTypeOf' => function () { + 'isTypeOf' => function () { return new Deferred(function () { throw new UserError('We are testing this error'); }); }, - 'fields' => [ - 'name' => ['type' => Type::string()], + 'fields' => [ + 'name' => ['type' => Type::string()], 'woofs' => ['type' => Type::boolean()], - ] + ], ]); $CatType = new ObjectType([ - 'name' => 'Cat', + 'name' => 'Cat', 'interfaces' => [$PetType], - 'isTypeOf' => function ($obj) { + 'isTypeOf' => function ($obj) { return new Deferred(function () use ($obj) { return $obj instanceof Cat; }); }, - 'fields' => [ - 'name' => ['type' => Type::string()], + 'fields' => [ + 'name' => ['type' => Type::string()], 'meows' => ['type' => Type::boolean()], - ] + ], ]); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ 'pets' => [ - 'type' => Type::listOf($PetType), + 'type' => Type::listOf($PetType), 'resolve' => function () { return [ new Dog('Odie', true), - new Cat('Garfield', false) + new Cat('Garfield', false), ]; - } - ] - ] + }, + ], + ], ]), - 'types' => [$CatType, $DogType] + 'types' => [$CatType, $DogType], ]); $query = '{ @@ -176,21 +179,21 @@ class AbstractPromiseTest extends TestCase $result = GraphQL::executeQuery($schema, $query)->toArray(); $expected = [ - 'data' => [ - 'pets' => [null, null] + 'data' => [ + 'pets' => [null, null], ], 'errors' => [ [ - 'message' => 'We are testing this error', + 'message' => 'We are testing this error', 'locations' => [['line' => 2, 'column' => 7]], - 'path' => ['pets', 0], + 'path' => ['pets', 0], ], [ - 'message' => 'We are testing this error', + 'message' => 'We are testing this error', 'locations' => [['line' => 2, 'column' => 7]], - 'path' => ['pets', 1] - ] - ] + 'path' => ['pets', 1], + ], + ], ]; $this->assertArraySubset($expected, $result); @@ -201,50 +204,49 @@ class AbstractPromiseTest extends TestCase */ public function testIsTypeOfUsedToResolveRuntimeTypeForUnion() : void { - $DogType = new ObjectType([ - 'name' => 'Dog', + 'name' => 'Dog', 'isTypeOf' => function ($obj) { return new Deferred(function () use ($obj) { return $obj instanceof Dog; }); }, - 'fields' => [ - 'name' => ['type' => Type::string()], + 'fields' => [ + 'name' => ['type' => Type::string()], 'woofs' => ['type' => Type::boolean()], - ] + ], ]); $CatType = new ObjectType([ - 'name' => 'Cat', + 'name' => 'Cat', 'isTypeOf' => function ($obj) { return new Deferred(function () use ($obj) { return $obj instanceof Cat; }); }, - 'fields' => [ - 'name' => ['type' => Type::string()], + 'fields' => [ + 'name' => ['type' => Type::string()], 'meows' => ['type' => Type::boolean()], - ] + ], ]); $PetType = new UnionType([ - 'name' => 'Pet', - 'types' => [$DogType, $CatType] + 'name' => 'Pet', + 'types' => [$DogType, $CatType], ]); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ 'pets' => [ - 'type' => Type::listOf($PetType), + 'type' => Type::listOf($PetType), 'resolve' => function () { return [new Dog('Odie', true), new Cat('Garfield', false)]; - } - ] - ] - ]) + }, + ], + ], + ]), ]); $query = '{ @@ -266,9 +268,9 @@ class AbstractPromiseTest extends TestCase 'data' => [ 'pets' => [ ['name' => 'Odie', 'woofs' => true], - ['name' => 'Garfield', 'meows' => false] - ] - ] + ['name' => 'Garfield', 'meows' => false], + ], + ], ]; $this->assertEquals($expected, $result); @@ -280,7 +282,7 @@ class AbstractPromiseTest extends TestCase public function testResolveTypeOnInterfaceYieldsUsefulError() : void { $PetType = new InterfaceType([ - 'name' => 'Pet', + 'name' => 'Pet', 'resolveType' => function ($obj) use (&$DogType, &$CatType, &$HumanType) { return new Deferred(function () use ($obj, $DogType, $CatType, $HumanType) { if ($obj instanceof Dog) { @@ -292,58 +294,59 @@ class AbstractPromiseTest extends TestCase if ($obj instanceof Human) { return $HumanType; } + return null; }); }, - 'fields' => [ - 'name' => ['type' => Type::string()] - ] + 'fields' => [ + 'name' => ['type' => Type::string()], + ], ]); $HumanType = new ObjectType([ - 'name' => 'Human', + 'name' => 'Human', 'fields' => [ 'name' => ['type' => Type::string()], - ] + ], ]); $DogType = new ObjectType([ - 'name' => 'Dog', + 'name' => 'Dog', 'interfaces' => [$PetType], - 'fields' => [ - 'name' => ['type' => Type::string()], + 'fields' => [ + 'name' => ['type' => Type::string()], 'woofs' => ['type' => Type::boolean()], - ] + ], ]); $CatType = new ObjectType([ - 'name' => 'Cat', + 'name' => 'Cat', 'interfaces' => [$PetType], - 'fields' => [ - 'name' => ['type' => Type::string()], + 'fields' => [ + 'name' => ['type' => Type::string()], 'meows' => ['type' => Type::boolean()], - ] + ], ]); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ 'pets' => [ - 'type' => Type::listOf($PetType), + 'type' => Type::listOf($PetType), 'resolve' => function () { return new Deferred(function () { return [ new Dog('Odie', true), new Cat('Garfield', false), - new Human('Jon') + new Human('Jon'), ]; }); - } - ] - ] + }, + ], + ], ]), - 'types' => [$CatType, $DogType] + 'types' => [$CatType, $DogType], ]); $query = '{ @@ -361,20 +364,20 @@ class AbstractPromiseTest extends TestCase $result = GraphQL::executeQuery($schema, $query)->toArray(true); $expected = [ - 'data' => [ + 'data' => [ 'pets' => [ ['name' => 'Odie', 'woofs' => true], ['name' => 'Garfield', 'meows' => false], - null - ] + null, + ], ], 'errors' => [ [ 'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".', - 'locations' => [['line' => 2, 'column' => 7]], - 'path' => ['pets', 2] + 'locations' => [['line' => 2, 'column' => 7]], + 'path' => ['pets', 2], ], - ] + ], ]; $this->assertArraySubset($expected, $result); @@ -385,32 +388,31 @@ class AbstractPromiseTest extends TestCase */ public function testResolveTypeOnUnionYieldsUsefulError() : void { - $HumanType = new ObjectType([ - 'name' => 'Human', + 'name' => 'Human', 'fields' => [ 'name' => ['type' => Type::string()], - ] + ], ]); $DogType = new ObjectType([ - 'name' => 'Dog', + 'name' => 'Dog', 'fields' => [ - 'name' => ['type' => Type::string()], + 'name' => ['type' => Type::string()], 'woofs' => ['type' => Type::boolean()], - ] + ], ]); $CatType = new ObjectType([ - 'name' => 'Cat', + 'name' => 'Cat', 'fields' => [ - 'name' => ['type' => Type::string()], + 'name' => ['type' => Type::string()], 'meows' => ['type' => Type::boolean()], - ] + ], ]); $PetType = new UnionType([ - 'name' => 'Pet', + 'name' => 'Pet', 'resolveType' => function ($obj) use ($DogType, $CatType, $HumanType) { return new Deferred(function () use ($obj, $DogType, $CatType, $HumanType) { if ($obj instanceof Dog) { @@ -422,28 +424,29 @@ class AbstractPromiseTest extends TestCase if ($obj instanceof Human) { return $HumanType; } + return null; }); }, - 'types' => [$DogType, $CatType] + 'types' => [$DogType, $CatType], ]); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ 'pets' => [ - 'type' => Type::listOf($PetType), + 'type' => Type::listOf($PetType), 'resolve' => function () { return [ new Dog('Odie', true), new Cat('Garfield', false), - new Human('Jon') + new Human('Jon'), ]; - } - ] - ] - ]) + }, + ], + ], + ]), ]); $query = '{ @@ -462,20 +465,20 @@ class AbstractPromiseTest extends TestCase $result = GraphQL::executeQuery($schema, $query)->toArray(true); $expected = [ - 'data' => [ + 'data' => [ 'pets' => [ ['name' => 'Odie', 'woofs' => true], ['name' => 'Garfield', 'meows' => false], - null - ] + null, + ], ], 'errors' => [ [ 'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".', - 'locations' => [['line' => 2, 'column' => 7]], - 'path' => ['pets', 2] - ] - ] + 'locations' => [['line' => 2, 'column' => 7]], + 'path' => ['pets', 2], + ], + ], ]; $this->assertArraySubset($expected, $result); @@ -487,7 +490,7 @@ class AbstractPromiseTest extends TestCase public function testResolveTypeAllowsResolvingWithTypeName() : void { $PetType = new InterfaceType([ - 'name' => 'Pet', + 'name' => 'Pet', 'resolveType' => function ($obj) { return new Deferred(function () use ($obj) { if ($obj instanceof Dog) { @@ -496,49 +499,49 @@ class AbstractPromiseTest extends TestCase if ($obj instanceof Cat) { return 'Cat'; } + return null; }); }, - 'fields' => [ - 'name' => ['type' => Type::string()] - ] + 'fields' => [ + 'name' => ['type' => Type::string()], + ], ]); - $DogType = new ObjectType([ - 'name' => 'Dog', + 'name' => 'Dog', 'interfaces' => [$PetType], - 'fields' => [ - 'name' => ['type' => Type::string()], + 'fields' => [ + 'name' => ['type' => Type::string()], 'woofs' => ['type' => Type::boolean()], - ] + ], ]); $CatType = new ObjectType([ - 'name' => 'Cat', + 'name' => 'Cat', 'interfaces' => [$PetType], - 'fields' => [ - 'name' => ['type' => Type::string()], + 'fields' => [ + 'name' => ['type' => Type::string()], 'meows' => ['type' => Type::boolean()], - ] + ], ]); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ 'pets' => [ - 'type' => Type::listOf($PetType), + 'type' => Type::listOf($PetType), 'resolve' => function () { return [ new Dog('Odie', true), - new Cat('Garfield', false) + new Cat('Garfield', false), ]; - } - ] - ] + }, + ], + ], ]), - 'types' => [$CatType, $DogType] + 'types' => [$CatType, $DogType], ]); $query = '{ @@ -560,8 +563,8 @@ class AbstractPromiseTest extends TestCase 'pets' => [ ['name' => 'Odie', 'woofs' => true], ['name' => 'Garfield', 'meows' => false], - ] - ] + ], + ], ]; $this->assertEquals($expected, $result); } @@ -571,53 +574,52 @@ class AbstractPromiseTest extends TestCase */ public function testResolveTypeCanBeCaught() : void { - $PetType = new InterfaceType([ - 'name' => 'Pet', + 'name' => 'Pet', 'resolveType' => function () { return new Deferred(function () { throw new UserError('We are testing this error'); }); }, - 'fields' => [ - 'name' => ['type' => Type::string()] - ] + 'fields' => [ + 'name' => ['type' => Type::string()], + ], ]); $DogType = new ObjectType([ - 'name' => 'Dog', + 'name' => 'Dog', 'interfaces' => [$PetType], - 'fields' => [ - 'name' => ['type' => Type::string()], + 'fields' => [ + 'name' => ['type' => Type::string()], 'woofs' => ['type' => Type::boolean()], - ] + ], ]); $CatType = new ObjectType([ - 'name' => 'Cat', + 'name' => 'Cat', 'interfaces' => [$PetType], - 'fields' => [ - 'name' => ['type' => Type::string()], + 'fields' => [ + 'name' => ['type' => Type::string()], 'meows' => ['type' => Type::boolean()], - ] + ], ]); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ 'pets' => [ - 'type' => Type::listOf($PetType), + 'type' => Type::listOf($PetType), 'resolve' => function () { return [ new Dog('Odie', true), - new Cat('Garfield', false) + new Cat('Garfield', false), ]; - } - ] - ] + }, + ], + ], ]), - 'types' => [$CatType, $DogType] + 'types' => [$CatType, $DogType], ]); $query = '{ @@ -635,21 +637,21 @@ class AbstractPromiseTest extends TestCase $result = GraphQL::executeQuery($schema, $query)->toArray(); $expected = [ - 'data' => [ - 'pets' => [null, null] + 'data' => [ + 'pets' => [null, null], ], 'errors' => [ [ - 'message' => 'We are testing this error', + 'message' => 'We are testing this error', 'locations' => [['line' => 2, 'column' => 7]], - 'path' => ['pets', 0] + 'path' => ['pets', 0], ], [ - 'message' => 'We are testing this error', + 'message' => 'We are testing this error', 'locations' => [['line' => 2, 'column' => 7]], - 'path' => ['pets', 1] - ] - ] + 'path' => ['pets', 1], + ], + ], ]; $this->assertArraySubset($expected, $result); diff --git a/tests/Executor/AbstractTest.php b/tests/Executor/AbstractTest.php index 38ebd1f..97197cb 100644 --- a/tests/Executor/AbstractTest.php +++ b/tests/Executor/AbstractTest.php @@ -1,23 +1,28 @@ 'Pet', + 'name' => 'Pet', 'fields' => [ - 'name' => ['type' => Type::string()] - ] + 'name' => ['type' => Type::string()], + ], ]); // Added to interface type when defined $dogType = new ObjectType([ - 'name' => 'Dog', + 'name' => 'Dog', 'interfaces' => [$petType], - 'isTypeOf' => function($obj) { return $obj instanceof Dog; }, - 'fields' => [ - 'name' => ['type' => Type::string()], - 'woofs' => ['type' => Type::boolean()] - ] + 'isTypeOf' => function ($obj) { + return $obj instanceof Dog; + }, + 'fields' => [ + 'name' => ['type' => Type::string()], + 'woofs' => ['type' => Type::boolean()], + ], ]); $catType = new ObjectType([ - 'name' => 'Cat', + 'name' => 'Cat', 'interfaces' => [$petType], - 'isTypeOf' => function ($obj) { + 'isTypeOf' => function ($obj) { return $obj instanceof Cat; }, - 'fields' => [ - 'name' => ['type' => Type::string()], + 'fields' => [ + 'name' => ['type' => Type::string()], 'meows' => ['type' => Type::boolean()], - ] + ], ]); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ 'pets' => [ - 'type' => Type::listOf($petType), + 'type' => Type::listOf($petType), 'resolve' => function () { return [new Dog('Odie', true), new Cat('Garfield', false)]; - } - ] - ] + }, + ], + ], ]), - 'types' => [$catType, $dogType] + 'types' => [$catType, $dogType], ]); $query = '{ @@ -84,8 +91,8 @@ class AbstractTest extends TestCase $expected = new ExecutionResult([ 'pets' => [ ['name' => 'Odie', 'woofs' => true], - ['name' => 'Garfield', 'meows' => false] - ] + ['name' => 'Garfield', 'meows' => false], + ], ]); $result = Executor::execute($schema, Parser::parse($query)); @@ -98,42 +105,44 @@ class AbstractTest extends TestCase public function testIsTypeOfUsedToResolveRuntimeTypeForUnion() : void { $dogType = new ObjectType([ - 'name' => 'Dog', - 'isTypeOf' => function($obj) { return $obj instanceof Dog; }, - 'fields' => [ - 'name' => ['type' => Type::string()], - 'woofs' => ['type' => Type::boolean()] - ] + 'name' => 'Dog', + 'isTypeOf' => function ($obj) { + return $obj instanceof Dog; + }, + 'fields' => [ + 'name' => ['type' => Type::string()], + 'woofs' => ['type' => Type::boolean()], + ], ]); $catType = new ObjectType([ - 'name' => 'Cat', + 'name' => 'Cat', 'isTypeOf' => function ($obj) { return $obj instanceof Cat; }, - 'fields' => [ - 'name' => ['type' => Type::string()], + 'fields' => [ + 'name' => ['type' => Type::string()], 'meows' => ['type' => Type::boolean()], - ] + ], ]); $petType = new UnionType([ - 'name' => 'Pet', - 'types' => [$dogType, $catType] + 'name' => 'Pet', + 'types' => [$dogType, $catType], ]); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ 'pets' => [ - 'type' => Type::listOf($petType), - 'resolve' => function() { - return [ new Dog('Odie', true), new Cat('Garfield', false) ]; - } - ] - ] - ]) + 'type' => Type::listOf($petType), + 'resolve' => function () { + return [new Dog('Odie', true), new Cat('Garfield', false)]; + }, + ], + ], + ]), ]); $query = '{ @@ -151,8 +160,8 @@ class AbstractTest extends TestCase $expected = new ExecutionResult([ 'pets' => [ ['name' => 'Odie', 'woofs' => true], - ['name' => 'Garfield', 'meows' => false] - ] + ['name' => 'Garfield', 'meows' => false], + ], ]); $this->assertEquals($expected, Executor::execute($schema, Parser::parse($query))); @@ -161,14 +170,14 @@ class AbstractTest extends TestCase /** * @see it('resolveType on Interface yields useful error') */ - function testResolveTypeOnInterfaceYieldsUsefulError() + public function testResolveTypeOnInterfaceYieldsUsefulError() : void { - $DogType = null; - $CatType = null; + $DogType = null; + $CatType = null; $HumanType = null; $PetType = new InterfaceType([ - 'name' => 'Pet', + 'name' => 'Pet', 'resolveType' => function ($obj) use (&$DogType, &$CatType, &$HumanType) { if ($obj instanceof Dog) { return $DogType; @@ -179,58 +188,58 @@ class AbstractTest extends TestCase if ($obj instanceof Human) { return $HumanType; } + return null; }, - 'fields' => [ - 'name' => ['type' => Type::string()] - ] + 'fields' => [ + 'name' => ['type' => Type::string()], + ], ]); $HumanType = new ObjectType([ - 'name' => 'Human', + 'name' => 'Human', 'fields' => [ 'name' => ['type' => Type::string()], - ] + ], ]); $DogType = new ObjectType([ - 'name' => 'Dog', + 'name' => 'Dog', 'interfaces' => [$PetType], - 'fields' => [ - 'name' => ['type' => Type::string()], + 'fields' => [ + 'name' => ['type' => Type::string()], 'woofs' => ['type' => Type::boolean()], - ] + ], ]); $CatType = new ObjectType([ - 'name' => 'Cat', + 'name' => 'Cat', 'interfaces' => [$PetType], - 'fields' => [ - 'name' => ['type' => Type::string()], + 'fields' => [ + 'name' => ['type' => Type::string()], 'meows' => ['type' => Type::boolean()], - ] + ], ]); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ 'pets' => [ - 'type' => Type::listOf($PetType), + 'type' => Type::listOf($PetType), 'resolve' => function () { return [ new Dog('Odie', true), new Cat('Garfield', false), - new Human('Jon') + new Human('Jon'), ]; - } - ] + }, + ], ], ]), - 'types' => [$DogType, $CatType] + 'types' => [$DogType, $CatType], ]); - $query = '{ pets { name @@ -244,20 +253,21 @@ class AbstractTest extends TestCase }'; $expected = [ - 'data' => [ + 'data' => [ 'pets' => [ ['name' => 'Odie', 'woofs' => true], ['name' => 'Garfield', 'meows' => false], - null - ] + null, + ], ], 'errors' => [[ 'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".', - 'locations' => [['line' => 2, 'column' => 11]], - 'path' => ['pets', 2] - ]] + 'locations' => [['line' => 2, 'column' => 11]], + 'path' => ['pets', 2], + ], + ], ]; - $actual = GraphQL::executeQuery($schema, $query)->toArray(true); + $actual = GraphQL::executeQuery($schema, $query)->toArray(true); $this->assertArraySubset($expected, $actual); } @@ -268,30 +278,30 @@ class AbstractTest extends TestCase public function testResolveTypeOnUnionYieldsUsefulError() : void { $HumanType = new ObjectType([ - 'name' => 'Human', + 'name' => 'Human', 'fields' => [ 'name' => ['type' => Type::string()], - ] + ], ]); $DogType = new ObjectType([ - 'name' => 'Dog', + 'name' => 'Dog', 'fields' => [ - 'name' => ['type' => Type::string()], + 'name' => ['type' => Type::string()], 'woofs' => ['type' => Type::boolean()], - ] + ], ]); $CatType = new ObjectType([ - 'name' => 'Cat', + 'name' => 'Cat', 'fields' => [ - 'name' => ['type' => Type::string()], + 'name' => ['type' => Type::string()], 'meows' => ['type' => Type::boolean()], - ] + ], ]); $PetType = new UnionType([ - 'name' => 'Pet', + 'name' => 'Pet', 'resolveType' => function ($obj) use ($DogType, $CatType, $HumanType) { if ($obj instanceof Dog) { return $DogType; @@ -303,25 +313,25 @@ class AbstractTest extends TestCase return $HumanType; } }, - 'types' => [$DogType, $CatType] + 'types' => [$DogType, $CatType], ]); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ 'pets' => [ - 'type' => Type::listOf($PetType), + 'type' => Type::listOf($PetType), 'resolve' => function () { return [ new Dog('Odie', true), new Cat('Garfield', false), - new Human('Jon') + new Human('Jon'), ]; - } - ] - ] - ]) + }, + ], + ], + ]), ]); $query = '{ @@ -337,22 +347,27 @@ class AbstractTest extends TestCase } }'; - $result = GraphQL::executeQuery($schema, $query)->toArray(true); + $result = GraphQL::executeQuery($schema, $query)->toArray(true); $expected = [ - 'data' => [ + 'data' => [ 'pets' => [ - ['name' => 'Odie', - 'woofs' => true], - ['name' => 'Garfield', - 'meows' => false], - null - ] + [ + 'name' => 'Odie', + 'woofs' => true, + ], + [ + 'name' => 'Garfield', + 'meows' => false, + ], + null, + ], ], 'errors' => [[ 'debugMessage' => 'Runtime Object type "Human" is not a possible type for "Pet".', - 'locations' => [['line' => 2, 'column' => 11]], - 'path' => ['pets', 2] - ]] + 'locations' => [['line' => 2, 'column' => 11]], + 'path' => ['pets', 2], + ], + ], ]; $this->assertArraySubset($expected, $result); } @@ -363,25 +378,25 @@ class AbstractTest extends TestCase public function testReturningInvalidValueFromResolveTypeYieldsUsefulError() : void { $fooInterface = new InterfaceType([ - 'name' => 'FooInterface', - 'fields' => ['bar' => ['type' => Type::string()]], + 'name' => 'FooInterface', + 'fields' => ['bar' => ['type' => Type::string()]], 'resolveType' => function () { return []; }, ]); $fooObject = new ObjectType([ - 'name' => 'FooObject', - 'fields' => ['bar' => ['type' => Type::string()]], + 'name' => 'FooObject', + 'fields' => ['bar' => ['type' => Type::string()]], 'interfaces' => [$fooInterface], ]); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ 'foo' => [ - 'type' => $fooInterface, + 'type' => $fooInterface, 'resolve' => function () { return 'dummy'; }, @@ -394,18 +409,18 @@ class AbstractTest extends TestCase $result = GraphQL::executeQuery($schema, '{ foo { bar } }'); $expected = [ - 'data' => ['foo' => null], + 'data' => ['foo' => null], 'errors' => [ [ - 'message' => 'Internal server error', + 'message' => 'Internal server error', 'debugMessage' => 'Abstract type FooInterface must resolve to an Object type at ' . 'runtime for field Query.foo with value "dummy", received "[]". ' . 'Either the FooInterface type should provide a "resolveType" ' . 'function or each possible type should provide an "isTypeOf" function.', - 'locations' => [['line' => 1, 'column' => 3]], - 'path' => ['foo'], - 'category' => 'internal', + 'locations' => [['line' => 1, 'column' => 3]], + 'path' => ['foo'], + 'category' => 'internal', ], ], ]; @@ -418,51 +433,56 @@ class AbstractTest extends TestCase public function testResolveTypeAllowsResolvingWithTypeName() : void { $PetType = new InterfaceType([ - 'name' => 'Pet', - 'resolveType' => function($obj) { - if ($obj instanceof Dog) return 'Dog'; - if ($obj instanceof Cat) return 'Cat'; + 'name' => 'Pet', + 'resolveType' => function ($obj) { + if ($obj instanceof Dog) { + return 'Dog'; + } + if ($obj instanceof Cat) { + return 'Cat'; + } + return null; }, - 'fields' => [ - 'name' => [ 'type' => Type::string() ] - ] + 'fields' => [ + 'name' => ['type' => Type::string()], + ], ]); $DogType = new ObjectType([ - 'name' => 'Dog', - 'interfaces' => [ $PetType ], - 'fields' => [ - 'name' => [ 'type' => Type::string() ], - 'woofs' => [ 'type' => Type::boolean() ], - ] + 'name' => 'Dog', + 'interfaces' => [$PetType], + 'fields' => [ + 'name' => ['type' => Type::string()], + 'woofs' => ['type' => Type::boolean()], + ], ]); $CatType = new ObjectType([ - 'name' => 'Cat', - 'interfaces' => [ $PetType ], - 'fields' => [ - 'name' => [ 'type' => Type::string() ], - 'meows' => [ 'type' => Type::boolean() ], - ] + 'name' => 'Cat', + 'interfaces' => [$PetType], + 'fields' => [ + 'name' => ['type' => Type::string()], + 'meows' => ['type' => Type::boolean()], + ], ]); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ 'pets' => [ - 'type' => Type::listOf($PetType), - 'resolve' => function() { + 'type' => Type::listOf($PetType), + 'resolve' => function () { return [ new Dog('Odie', true), - new Cat('Garfield', false) + new Cat('Garfield', false), ]; - } - ] - ] + }, + ], + ], ]), - 'types' => [ $CatType, $DogType ] + 'types' => [$CatType, $DogType], ]); $query = '{ @@ -479,51 +499,52 @@ class AbstractTest extends TestCase $result = GraphQL::executeQuery($schema, $query)->toArray(); - $this->assertEquals([ - 'data' => [ - 'pets' => [ - ['name' => 'Odie', 'woofs' => true], - ['name' => 'Garfield', 'meows' => false] - ] - ] - ], $result); + $this->assertEquals( + [ + 'data' => [ + 'pets' => [ + ['name' => 'Odie', 'woofs' => true], + ['name' => 'Garfield', 'meows' => false], + ], + ], + ], + $result + ); } public function testHintsOnConflictingTypeInstancesInResolveType() : void { - $createTest = function() use (&$iface) { + $createTest = function () use (&$iface) { return new ObjectType([ - 'name' => 'Test', - 'fields' => [ - 'a' => Type::string() + 'name' => 'Test', + 'fields' => [ + 'a' => Type::string(), ], - 'interfaces' => function() use ($iface) { + 'interfaces' => function () use ($iface) { return [$iface]; - } + }, ]); }; $iface = new InterfaceType([ - 'name' => 'Node', - 'fields' => [ - 'a' => Type::string() + 'name' => 'Node', + 'fields' => [ + 'a' => Type::string(), ], - 'resolveType' => function() use (&$createTest) { + 'resolveType' => function () use (&$createTest) { return $createTest(); - } + }, ]); $query = new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ 'node' => $iface, - 'test' => $createTest() - ] + 'test' => $createTest(), + ], ]); - $schema = new Schema([ - 'query' => $query, - ]); + $schema = new Schema(['query' => $query]); $schema->assertValid(); $query = ' @@ -537,9 +558,9 @@ class AbstractTest extends TestCase $result = Executor::execute($schema, Parser::parse($query), ['node' => ['a' => 'value']]); $this->assertEquals( - 'Schema must contain unique named types but contains multiple types named "Test". '. - 'Make sure that `resolveType` function of abstract type "Node" returns the same type instance '. - 'as referenced anywhere else within the schema '. + 'Schema must contain unique named types but contains multiple types named "Test". ' . + 'Make sure that `resolveType` function of abstract type "Node" returns the same type instance ' . + 'as referenced anywhere else within the schema ' . '(see http://webonyx.github.io/graphql-php/type-system/#type-registry).', $result->errors[0]->getMessage() ); diff --git a/tests/Executor/DeferredFieldsTest.php b/tests/Executor/DeferredFieldsTest.php index 775b5ab..511be53 100644 --- a/tests/Executor/DeferredFieldsTest.php +++ b/tests/Executor/DeferredFieldsTest.php @@ -1,33 +1,44 @@ categoryDataSource = [ ['id' => 1, 'name' => 'Category #1', 'topStoryId' => 8], ['id' => 2, 'name' => 'Category #2', 'topStoryId' => 3], - ['id' => 3, 'name' => 'Category #3', 'topStoryId' => 9] + ['id' => 3, 'name' => 'Category #3', 'topStoryId' => 9], ]; - $this->path = []; + $this->path = []; $this->userType = new ObjectType([ - 'name' => 'User', - 'fields' => function() { + 'name' => 'User', + 'fields' => function () { return [ - 'name' => [ - 'type' => Type::string(), + 'name' => [ + 'type' => Type::string(), 'resolve' => function ($user, $args, $context, ResolveInfo $info) { $this->path[] = $info->path; + return $user['name']; - } + }, ], 'bestFriend' => [ - 'type' => $this->userType, - 'resolve' => function($user, $args, $context, ResolveInfo $info) { + 'type' => $this->userType, + 'resolve' => function ($user, $args, $context, ResolveInfo $info) { $this->path[] = $info->path; - return new Deferred(function() use ($user) { + return new Deferred(function () use ($user) { $this->path[] = 'deferred-for-best-friend-of-' . $user['id']; - return Utils::find($this->userDataSource, function($entry) use ($user) { - return $entry['id'] === $user['bestFriendId']; - }); + + return Utils::find( + $this->userDataSource, + function ($entry) use ($user) { + return $entry['id'] === $user['bestFriendId']; + } + ); }); - } - ] + }, + ], ]; - } + }, ]); $this->storyType = new ObjectType([ - 'name' => 'Story', + 'name' => 'Story', 'fields' => [ - 'title' => [ - 'type' => Type::string(), - 'resolve' => function($entry, $args, $context, ResolveInfo $info) { - $this->path[] = $info->path; - return $entry['title']; - } - ], - 'author' => [ - 'type' => $this->userType, - 'resolve' => function($story, $args, $context, ResolveInfo $info) { + 'title' => [ + 'type' => Type::string(), + 'resolve' => function ($entry, $args, $context, ResolveInfo $info) { $this->path[] = $info->path; - return new Deferred(function() use ($story) { + return $entry['title']; + }, + ], + 'author' => [ + 'type' => $this->userType, + 'resolve' => function ($story, $args, $context, ResolveInfo $info) { + $this->path[] = $info->path; + + return new Deferred(function () use ($story) { $this->path[] = 'deferred-for-story-' . $story['id'] . '-author'; - return Utils::find($this->userDataSource, function($entry) use ($story) { - return $entry['id'] === $story['authorId']; - }); + + return Utils::find( + $this->userDataSource, + function ($entry) use ($story) { + return $entry['id'] === $story['authorId']; + } + ); }); - } - ] - ] + }, + ], + ], ]); $this->categoryType = new ObjectType([ - 'name' => 'Category', + 'name' => 'Category', 'fields' => [ 'name' => [ - 'type' => Type::string(), - 'resolve' => function($category, $args, $context, ResolveInfo $info) { + 'type' => Type::string(), + 'resolve' => function ($category, $args, $context, ResolveInfo $info) { $this->path[] = $info->path; + return $category['name']; - } + }, ], - 'stories' => [ - 'type' => Type::listOf($this->storyType), - 'resolve' => function($category, $args, $context, ResolveInfo $info) { + 'stories' => [ + 'type' => Type::listOf($this->storyType), + 'resolve' => function ($category, $args, $context, ResolveInfo $info) { $this->path[] = $info->path; - return Utils::filter($this->storyDataSource, function($story) use ($category) { - return in_array($category['id'], $story['categoryIds']); - }); - } + + return Utils::filter( + $this->storyDataSource, + function ($story) use ($category) { + return in_array($category['id'], $story['categoryIds']); + } + ); + }, ], 'topStory' => [ - 'type' => $this->storyType, - 'resolve' => function($category, $args, $context, ResolveInfo $info) { + 'type' => $this->storyType, + 'resolve' => function ($category, $args, $context, ResolveInfo $info) { $this->path[] = $info->path; return new Deferred(function () use ($category) { $this->path[] = 'deferred-for-category-' . $category['id'] . '-topStory'; - return Utils::find($this->storyDataSource, function($story) use ($category) { - return $story['id'] === $category['topStoryId']; - }); + + return Utils::find( + $this->storyDataSource, + function ($story) use ($category) { + return $story['id'] === $category['topStoryId']; + } + ); }); - } - ] - ] + }, + ], + ], ]); $this->queryType = new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ - 'topStories' => [ - 'type' => Type::listOf($this->storyType), - 'resolve' => function($val, $args, $context, ResolveInfo $info) { + 'topStories' => [ + 'type' => Type::listOf($this->storyType), + 'resolve' => function ($val, $args, $context, ResolveInfo $info) { $this->path[] = $info->path; - return Utils::filter($this->storyDataSource, function($story) { - return $story['id'] % 2 === 1; - }); - } + + return Utils::filter( + $this->storyDataSource, + function ($story) { + return $story['id'] % 2 === 1; + } + ); + }, ], 'featuredCategory' => [ - 'type' => $this->categoryType, - 'resolve' => function($val, $args, $context, ResolveInfo $info) { + 'type' => $this->categoryType, + 'resolve' => function ($val, $args, $context, ResolveInfo $info) { $this->path[] = $info->path; + return $this->categoryDataSource[0]; - } + }, ], - 'categories' => [ - 'type' => Type::listOf($this->categoryType), - 'resolve' => function($val, $args, $context, ResolveInfo $info) { + 'categories' => [ + 'type' => Type::listOf($this->categoryType), + 'resolve' => function ($val, $args, $context, ResolveInfo $info) { $this->path[] = $info->path; + return $this->categoryDataSource; - } - ] - ] + }, + ], + ], ]); parent::setUp(); @@ -202,12 +238,12 @@ class DeferredFieldsTest extends TestCase '); $schema = new Schema([ - 'query' => $this->queryType + 'query' => $this->queryType, ]); $expected = [ 'data' => [ - 'topStories' => [ + 'topStories' => [ ['title' => 'Story #1', 'author' => ['name' => 'John']], ['title' => 'Story #3', 'author' => ['name' => 'Joe']], ['title' => 'Story #5', 'author' => ['name' => 'John']], @@ -220,9 +256,9 @@ class DeferredFieldsTest extends TestCase ['title' => 'Story #4', 'author' => ['name' => 'Joe']], ['title' => 'Story #6', 'author' => ['name' => 'Jane']], ['title' => 'Story #8', 'author' => ['name' => 'John']], - ] - ] - ] + ], + ], + ], ]; $result = Executor::execute($schema, $query); @@ -292,12 +328,12 @@ class DeferredFieldsTest extends TestCase '); $schema = new Schema([ - 'query' => $this->queryType + 'query' => $this->queryType, ]); $author1 = ['name' => 'John', 'bestFriend' => ['name' => 'Dirk']]; $author2 = ['name' => 'Jane', 'bestFriend' => ['name' => 'Joe']]; - $author3 = ['name' => 'Joe', 'bestFriend' => ['name' => 'Jane']]; + $author3 = ['name' => 'Joe', 'bestFriend' => ['name' => 'Jane']]; $author4 = ['name' => 'Dirk', 'bestFriend' => ['name' => 'John']]; $expected = [ @@ -306,8 +342,8 @@ class DeferredFieldsTest extends TestCase ['name' => 'Category #1', 'topStory' => ['title' => 'Story #8', 'author' => $author1]], ['name' => 'Category #2', 'topStory' => ['title' => 'Story #3', 'author' => $author3]], ['name' => 'Category #3', 'topStory' => ['title' => 'Story #9', 'author' => $author2]], - ] - ] + ], + ], ]; $result = Executor::execute($schema, $query); @@ -352,54 +388,56 @@ class DeferredFieldsTest extends TestCase public function testComplexRecursiveDeferredFields() : void { $complexType = new ObjectType([ - 'name' => 'ComplexType', - 'fields' => function() use (&$complexType) { + 'name' => 'ComplexType', + 'fields' => function () use (&$complexType) { return [ - 'sync' => [ - 'type' => Type::string(), - 'resolve' => function($v, $a, $c, ResolveInfo $info) { - $this->path[] = $info->path; - return 'sync'; - } - ], - 'deferred' => [ - 'type' => Type::string(), - 'resolve' => function($v, $a, $c, ResolveInfo $info) { + 'sync' => [ + 'type' => Type::string(), + 'resolve' => function ($v, $a, $c, ResolveInfo $info) { $this->path[] = $info->path; - return new Deferred(function() use ($info) { + return 'sync'; + }, + ], + 'deferred' => [ + 'type' => Type::string(), + 'resolve' => function ($v, $a, $c, ResolveInfo $info) { + $this->path[] = $info->path; + + return new Deferred(function () use ($info) { $this->path[] = ['!dfd for: ', $info->path]; + return 'deferred'; }); - } + }, ], - 'nest' => [ - 'type' => $complexType, - 'resolve' => function($v, $a, $c, ResolveInfo $info) { + 'nest' => [ + 'type' => $complexType, + 'resolve' => function ($v, $a, $c, ResolveInfo $info) { $this->path[] = $info->path; + return []; - } + }, ], 'deferredNest' => [ - 'type' => $complexType, - 'resolve' => function($v, $a, $c, ResolveInfo $info) { + 'type' => $complexType, + 'resolve' => function ($v, $a, $c, ResolveInfo $info) { $this->path[] = $info->path; - return new Deferred(function() use ($info) { + return new Deferred(function () use ($info) { $this->path[] = ['!dfd nest for: ', $info->path]; + return []; }); - } - ] + }, + ], ]; - } + }, ]); - $schema = new Schema([ - 'query' => $complexType - ]); + $schema = new Schema(['query' => $complexType]); - $query = Parser::parse(' + $query = Parser::parse(' { nest { sync @@ -427,34 +465,34 @@ class DeferredFieldsTest extends TestCase } } '); - $result = Executor::execute($schema, $query); + $result = Executor::execute($schema, $query); $expected = [ 'data' => [ - 'nest' => [ - 'sync' => 'sync', - 'deferred' => 'deferred', - 'nest' => [ - 'sync' => 'sync', - 'deferred' => 'deferred' + 'nest' => [ + 'sync' => 'sync', + 'deferred' => 'deferred', + 'nest' => [ + 'sync' => 'sync', + 'deferred' => 'deferred', ], 'deferredNest' => [ - 'sync' => 'sync', - 'deferred' => 'deferred' - ] + 'sync' => 'sync', + 'deferred' => 'deferred', + ], ], 'deferredNest' => [ - 'sync' => 'sync', - 'deferred' => 'deferred', - 'nest' => [ - 'sync' => 'sync', - 'deferred' => 'deferred' + 'sync' => 'sync', + 'deferred' => 'deferred', + 'nest' => [ + 'sync' => 'sync', + 'deferred' => 'deferred', ], 'deferredNest' => [ - 'sync' => 'sync', - 'deferred' => 'deferred' - ] - ] - ] + 'sync' => 'sync', + 'deferred' => 'deferred', + ], + ], + ], ]; $this->assertEquals($expected, $result->toArray()); diff --git a/tests/Executor/DirectivesTest.php b/tests/Executor/DirectivesTest.php index 295e2f1..7150c2e 100644 --- a/tests/Executor/DirectivesTest.php +++ b/tests/Executor/DirectivesTest.php @@ -1,16 +1,27 @@ assertEquals(['data' => ['a' => 'a', 'b' => 'b']], $this->executeTestQuery('{ a, b }')); } + /** + * @param Source|string $doc + * @return mixed[] + */ + private function executeTestQuery($doc) : array + { + return Executor::execute(self::getSchema(), Parser::parse($doc), self::getData())->toArray(); + } + + private static function getSchema() : Schema + { + if (! self::$schema) { + self::$schema = new Schema([ + 'query' => new ObjectType([ + 'name' => 'TestType', + 'fields' => [ + 'a' => ['type' => Type::string()], + 'b' => ['type' => Type::string()], + ], + ]), + ]); + } + + return self::$schema; + } + + /** + * @return string[] + */ + private static function getData() : array + { + return self::$data ?: (self::$data = [ + 'a' => 'a', + 'b' => 'b', + ]); + } + public function testWorksOnScalars() : void { // if true includes scalar - $this->assertEquals(['data' => ['a' => 'a', 'b' => 'b']], $this->executeTestQuery('{ a, b @include(if: true) }')); + $this->assertEquals( + ['data' => ['a' => 'a', 'b' => 'b']], + $this->executeTestQuery('{ a, b @include(if: true) }') + ); // if false omits on scalar $this->assertEquals(['data' => ['a' => 'a']], $this->executeTestQuery('{ a, b @include(if: false) }')); @@ -200,37 +251,4 @@ class DirectivesTest extends TestCase $this->executeTestQuery('{ a, b @include(if: false) @skip(if: false) }') ); } - - private static $schema; - - private static $data; - - private static function getSchema() - { - if (!self::$schema) { - self::$schema = new Schema([ - 'query' => new ObjectType([ - 'name' => 'TestType', - 'fields' => [ - 'a' => ['type' => Type::string()], - 'b' => ['type' => Type::string()] - ] - ]) - ]); - } - return self::$schema; - } - - private static function getData() - { - return self::$data ?: (self::$data = [ - 'a' => 'a', - 'b' => 'b' - ]); - } - - private function executeTestQuery($doc) - { - return Executor::execute(self::getSchema(), Parser::parse($doc), self::getData())->toArray(); - } } diff --git a/tests/Executor/ExecutionResultTest.php b/tests/Executor/ExecutionResultTest.php index 69fbd78..ce20221 100644 --- a/tests/Executor/ExecutionResultTest.php +++ b/tests/Executor/ExecutionResultTest.php @@ -1,4 +1,7 @@ 'Pet', - 'fields' => function() { + 'name' => 'Pet', + 'fields' => function () { return [ - 'name' => ['type' => Type::string()] + 'name' => ['type' => Type::string()], ]; - } + }, ]); // Added to interface type when defined $dogType = new ObjectType([ - 'name' => 'Dog', + 'name' => 'Dog', 'interfaces' => [$petType], - 'isTypeOf' => function($obj) { return $obj instanceof Dog; }, - 'fields' => function() { + 'isTypeOf' => function ($obj) { + return $obj instanceof Dog; + }, + 'fields' => function () { return [ - 'name' => ['type' => Type::string()], - 'woofs' => ['type' => Type::boolean()] + 'name' => ['type' => Type::string()], + 'woofs' => ['type' => Type::boolean()], ]; - } + }, ]); $catType = new ObjectType([ - 'name' => 'Cat', + 'name' => 'Cat', 'interfaces' => [$petType], - 'isTypeOf' => function ($obj) { + 'isTypeOf' => function ($obj) { return $obj instanceof Cat; }, - 'fields' => function() { + 'fields' => function () { return [ - 'name' => ['type' => Type::string()], + 'name' => ['type' => Type::string()], 'meows' => ['type' => Type::boolean()], ]; - } + }, ]); $schema = new Schema([ - 'query' => new ObjectType([ - 'name' => 'Query', + 'query' => new ObjectType([ + 'name' => 'Query', 'fields' => [ 'pets' => [ - 'type' => Type::listOf($petType), + 'type' => Type::listOf($petType), 'resolve' => function () { return [new Dog('Odie', true), new Cat('Garfield', false)]; - } - ] - ] + }, + ], + ], ]), - 'types' => [$catType, $dogType], - 'typeLoader' => function($name) use ($dogType, $petType, $catType) { + 'types' => [$catType, $dogType], + 'typeLoader' => function ($name) use ($dogType, $petType, $catType) { switch ($name) { case 'Dog': return $dogType; @@ -102,7 +122,7 @@ class ExecutorLazySchemaTest extends TestCase case 'Cat': return $catType; } - } + }, ]); $query = '{ @@ -120,7 +140,7 @@ class ExecutorLazySchemaTest extends TestCase $expected = new ExecutionResult([ 'pets' => [ ['name' => 'Odie', 'woofs' => true], - ['name' => 'Garfield', 'meows' => false] + ['name' => 'Garfield', 'meows' => false], ], ]); @@ -134,43 +154,46 @@ class ExecutorLazySchemaTest extends TestCase $this->assertInstanceOf(Error::class, $result->errors[0]->getPrevious()); $this->assertEquals( - 'GraphQL Interface Type `Pet` returned `null` from it`s `resolveType` function for value: instance of '. - 'GraphQL\Tests\Executor\Dog. Switching to slow resolution method using `isTypeOf` of all possible '. - 'implementations. It requires full schema scan and degrades query performance significantly. '. + 'GraphQL Interface Type `Pet` returned `null` from it`s `resolveType` function for value: instance of ' . + 'GraphQL\Tests\Executor\TestClasses\Dog. Switching to slow resolution method using `isTypeOf` of all possible ' . + 'implementations. It requires full schema scan and degrades query performance significantly. ' . 'Make sure your `resolveType` always returns valid implementation or throws.', - $result->errors[0]->getMessage()); + $result->errors[0]->getMessage() + ); } public function testHintsOnConflictingTypeInstancesInDefinitions() : void { - $calls = []; - $typeLoader = function($name) use (&$calls) { + $calls = []; + $typeLoader = function ($name) use (&$calls) { $calls[] = $name; switch ($name) { case 'Test': return new ObjectType([ - 'name' => 'Test', - 'fields' => function() { + 'name' => 'Test', + 'fields' => function () { return [ 'test' => Type::string(), ]; - } + }, ]); default: return null; } }; + $query = new ObjectType([ - 'name' => 'Query', - 'fields' => function() use ($typeLoader) { + 'name' => 'Query', + 'fields' => function () use ($typeLoader) { return [ - 'test' => $typeLoader('Test') + 'test' => $typeLoader('Test'), ]; - } + }, ]); + $schema = new Schema([ - 'query' => $query, - 'typeLoader' => $typeLoader + 'query' => $query, + 'typeLoader' => $typeLoader, ]); $query = ' @@ -186,8 +209,8 @@ class ExecutorLazySchemaTest extends TestCase $this->assertEquals(['Test', 'Test'], $calls); $this->assertEquals( - 'Schema must contain unique named types but contains multiple types named "Test". '. - 'Make sure that type loader returns the same instance as defined in Query.test '. + 'Schema must contain unique named types but contains multiple types named "Test". ' . + 'Make sure that type loader returns the same instance as defined in Query.test ' . '(see http://webonyx.github.io/graphql-php/type-system/#type-registry).', $result->errors[0]->getMessage() ); @@ -200,54 +223,161 @@ class ExecutorLazySchemaTest extends TestCase public function testSimpleQuery() : void { $schema = new Schema([ - 'query' => $this->loadType('Query'), - 'typeLoader' => function($name) { + 'query' => $this->loadType('Query'), + 'typeLoader' => function ($name) { return $this->loadType($name, true); - } + }, ]); - $query = '{ object { string } }'; + $query = '{ object { string } }'; $result = Executor::execute( $schema, Parser::parse($query), ['object' => ['string' => 'test']] ); - $expected = [ + $expected = [ 'data' => ['object' => ['string' => 'test']], ]; $expectedExecutorCalls = [ 'Query.fields', 'SomeObject', - 'SomeObject.fields' + 'SomeObject.fields', ]; $this->assertEquals($expected, $result->toArray(true)); $this->assertEquals($expectedExecutorCalls, $this->calls); } + public function loadType($name, $isExecutorCall = false) + { + if ($isExecutorCall) { + $this->calls[] = $name; + } + $this->loadedTypes[$name] = true; + + switch ($name) { + case 'Query': + return $this->queryType ?: $this->queryType = new ObjectType([ + 'name' => 'Query', + 'fields' => function () { + $this->calls[] = 'Query.fields'; + + return [ + 'object' => ['type' => $this->loadType('SomeObject')], + 'other' => ['type' => $this->loadType('OtherObject')], + ]; + }, + ]); + case 'SomeObject': + return $this->someObjectType ?: $this->someObjectType = new ObjectType([ + 'name' => 'SomeObject', + 'fields' => function () { + $this->calls[] = 'SomeObject.fields'; + + return [ + 'string' => ['type' => Type::string()], + 'object' => ['type' => $this->someObjectType], + ]; + }, + 'interfaces' => function () { + $this->calls[] = 'SomeObject.interfaces'; + + return [ + $this->loadType('SomeInterface'), + ]; + }, + ]); + case 'OtherObject': + return $this->otherObjectType ?: $this->otherObjectType = new ObjectType([ + 'name' => 'OtherObject', + 'fields' => function () { + $this->calls[] = 'OtherObject.fields'; + + return [ + 'union' => ['type' => $this->loadType('SomeUnion')], + 'iface' => ['type' => Type::nonNull($this->loadType('SomeInterface'))], + ]; + }, + ]); + case 'DeeperObject': + return $this->deeperObjectType ?: $this->deeperObjectType = new ObjectType([ + 'name' => 'DeeperObject', + 'fields' => function () { + return [ + 'scalar' => ['type' => $this->loadType('SomeScalar')], + ]; + }, + ]); + case 'SomeScalar': + return $this->someScalarType ?: $this->someScalarType = new CustomScalarType([ + 'name' => 'SomeScalar', + 'serialize' => function ($value) { + return $value; + }, + 'parseValue' => function ($value) { + return $value; + }, + 'parseLiteral' => function () { + }, + ]); + case 'SomeUnion': + return $this->someUnionType ?: $this->someUnionType = new UnionType([ + 'name' => 'SomeUnion', + 'resolveType' => function () { + $this->calls[] = 'SomeUnion.resolveType'; + + return $this->loadType('DeeperObject'); + }, + 'types' => function () { + $this->calls[] = 'SomeUnion.types'; + + return [$this->loadType('DeeperObject')]; + }, + ]); + case 'SomeInterface': + return $this->someInterfaceType ?: $this->someInterfaceType = new InterfaceType([ + 'name' => 'SomeInterface', + 'resolveType' => function () { + $this->calls[] = 'SomeInterface.resolveType'; + + return $this->loadType('SomeObject'); + }, + 'fields' => function () { + $this->calls[] = 'SomeInterface.fields'; + + return [ + 'string' => ['type' => Type::string()], + ]; + }, + ]); + default: + return null; + } + } + public function testDeepQuery() : void { $schema = new Schema([ - 'query' => $this->loadType('Query'), - 'typeLoader' => function($name) { + 'query' => $this->loadType('Query'), + 'typeLoader' => function ($name) { return $this->loadType($name, true); - } + }, ]); - $query = '{ object { object { object { string } } } }'; + $query = '{ object { object { object { string } } } }'; $result = Executor::execute( $schema, Parser::parse($query), ['object' => ['object' => ['object' => ['string' => 'test']]]] ); - $expected = [ - 'data' => ['object' => ['object' => ['object' => ['string' => 'test']]]] + $expected = [ + 'data' => ['object' => ['object' => ['object' => ['string' => 'test']]]], ]; $expectedLoadedTypes = [ - 'Query' => true, - 'SomeObject' => true, - 'OtherObject' => true + 'Query' => true, + 'SomeObject' => true, + 'OtherObject' => true, ]; $this->assertEquals($expected, $result->toArray(true)); @@ -256,7 +386,7 @@ class ExecutorLazySchemaTest extends TestCase $expectedExecutorCalls = [ 'Query.fields', 'SomeObject', - 'SomeObject.fields' + 'SomeObject.fields', ]; $this->assertEquals($expectedExecutorCalls, $this->calls); } @@ -264,13 +394,13 @@ class ExecutorLazySchemaTest extends TestCase public function testResolveUnion() : void { $schema = new Schema([ - 'query' => $this->loadType('Query'), - 'typeLoader' => function($name) { + 'query' => $this->loadType('Query'), + 'typeLoader' => function ($name) { return $this->loadType($name, true); - } + }, ]); - $query = ' + $query = ' { other { union { @@ -285,17 +415,17 @@ class ExecutorLazySchemaTest extends TestCase ['other' => ['union' => ['scalar' => 'test']]] ); - $expected = [ + $expected = [ 'data' => ['other' => ['union' => ['scalar' => 'test']]], ]; $expectedLoadedTypes = [ - 'Query' => true, - 'SomeObject' => true, - 'OtherObject' => true, - 'SomeUnion' => true, + 'Query' => true, + 'SomeObject' => true, + 'OtherObject' => true, + 'SomeUnion' => true, 'SomeInterface' => true, - 'DeeperObject' => true, - 'SomeScalar' => true, + 'DeeperObject' => true, + 'SomeScalar' => true, ]; $this->assertEquals($expected, $result->toArray(true)); @@ -313,98 +443,4 @@ class ExecutorLazySchemaTest extends TestCase ]; $this->assertEquals($expectedCalls, $this->calls); } - - public function loadType($name, $isExecutorCall = false) - { - if ($isExecutorCall) { - $this->calls[] = $name; - } - $this->loadedTypes[$name] = true; - - switch ($name) { - case 'Query': - return $this->QueryType ?: $this->QueryType = new ObjectType([ - 'name' => 'Query', - 'fields' => function() { - $this->calls[] = 'Query.fields'; - return [ - 'object' => ['type' => $this->loadType('SomeObject')], - 'other' => ['type' => $this->loadType('OtherObject')], - ]; - } - ]); - case 'SomeObject': - return $this->SomeObjectType ?: $this->SomeObjectType = new ObjectType([ - 'name' => 'SomeObject', - 'fields' => function() { - $this->calls[] = 'SomeObject.fields'; - return [ - 'string' => ['type' => Type::string()], - 'object' => ['type' => $this->SomeObjectType] - ]; - }, - 'interfaces' => function() { - $this->calls[] = 'SomeObject.interfaces'; - return [ - $this->loadType('SomeInterface') - ]; - } - ]); - case 'OtherObject': - return $this->OtherObjectType ?: $this->OtherObjectType = new ObjectType([ - 'name' => 'OtherObject', - 'fields' => function() { - $this->calls[] = 'OtherObject.fields'; - return [ - 'union' => ['type' => $this->loadType('SomeUnion')], - 'iface' => ['type' => Type::nonNull($this->loadType('SomeInterface'))], - ]; - } - ]); - case 'DeeperObject': - return $this->DeeperObjectType ?: $this->DeeperObjectType = new ObjectType([ - 'name' => 'DeeperObject', - 'fields' => function() { - return [ - 'scalar' => ['type' => $this->loadType('SomeScalar')], - ]; - } - ]); - case 'SomeScalar'; - return $this->SomeScalarType ?: $this->SomeScalarType = new CustomScalarType([ - 'name' => 'SomeScalar', - 'serialize' => function($value) {return $value;}, - 'parseValue' => function($value) {return $value;}, - 'parseLiteral' => function() {} - ]); - case 'SomeUnion': - return $this->SomeUnionType ?: $this->SomeUnionType = new UnionType([ - 'name' => 'SomeUnion', - 'resolveType' => function() { - $this->calls[] = 'SomeUnion.resolveType'; - return $this->loadType('DeeperObject'); - }, - 'types' => function() { - $this->calls[] = 'SomeUnion.types'; - return [ $this->loadType('DeeperObject') ]; - } - ]); - case 'SomeInterface': - return $this->SomeInterfaceType ?: $this->SomeInterfaceType = new InterfaceType([ - 'name' => 'SomeInterface', - 'resolveType' => function() { - $this->calls[] = 'SomeInterface.resolveType'; - return $this->loadType('SomeObject'); - }, - 'fields' => function() { - $this->calls[] = 'SomeInterface.fields'; - return [ - 'string' => ['type' => Type::string() ] - ]; - } - ]); - default: - return null; - } - } } diff --git a/tests/Executor/ExecutorSchemaTest.php b/tests/Executor/ExecutorSchemaTest.php index 5c62b09..9ea79fb 100644 --- a/tests/Executor/ExecutorSchemaTest.php +++ b/tests/Executor/ExecutorSchemaTest.php @@ -1,74 +1,77 @@ 'Image', + $BlogImage = new ObjectType([ + 'name' => 'Image', 'fields' => [ - 'url' => ['type' => Type::string()], - 'width' => ['type' => Type::int()], + 'url' => ['type' => Type::string()], + 'width' => ['type' => Type::int()], 'height' => ['type' => Type::int()], - ] + ], ]); $BlogAuthor = new ObjectType([ - 'name' => 'Author', - 'fields' => function() use (&$BlogArticle, &$BlogImage) { + 'name' => 'Author', + 'fields' => function () use (&$BlogArticle, &$BlogImage) { return [ - 'id' => ['type' => Type::string()], - 'name' => ['type' => Type::string()], - 'pic' => [ - 'args' => ['width' => ['type' => Type::int()], 'height' => ['type' => Type::int()]], - 'type' => $BlogImage, + 'id' => ['type' => Type::string()], + 'name' => ['type' => Type::string()], + 'pic' => [ + 'args' => ['width' => ['type' => Type::int()], 'height' => ['type' => Type::int()]], + 'type' => $BlogImage, 'resolve' => function ($obj, $args) { return $obj['pic']($args['width'], $args['height']); - } + }, ], - 'recentArticle' => $BlogArticle + 'recentArticle' => $BlogArticle, ]; - } + }, ]); $BlogArticle = new ObjectType([ - 'name' => 'Article', + 'name' => 'Article', 'fields' => [ - 'id' => ['type' => Type::nonNull(Type::string())], + 'id' => ['type' => Type::nonNull(Type::string())], 'isPublished' => ['type' => Type::boolean()], - 'author' => ['type' => $BlogAuthor], - 'title' => ['type' => Type::string()], - 'body' => ['type' => Type::string()], - 'keywords' => ['type' => Type::listOf(Type::string())] - ] + 'author' => ['type' => $BlogAuthor], + 'title' => ['type' => Type::string()], + 'body' => ['type' => Type::string()], + 'keywords' => ['type' => Type::listOf(Type::string())], + ], ]); $BlogQuery = new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ 'article' => [ - 'type' => $BlogArticle, - 'args' => ['id' => ['type' => Type::id()]], + 'type' => $BlogArticle, + 'args' => ['id' => ['type' => Type::id()]], 'resolve' => function ($_, $args) { return $this->article($args['id']); - } + }, ], - 'feed' => [ - 'type' => Type::listOf($BlogArticle), + 'feed' => [ + 'type' => Type::listOf($BlogArticle), 'resolve' => function () { return [ $this->article(1), @@ -80,16 +83,15 @@ class ExecutorSchemaTest extends TestCase $this->article(7), $this->article(8), $this->article(9), - $this->article(10) + $this->article(10), ]; - } - ] - ] + }, + ], + ], ]); $BlogSchema = new Schema(['query' => $BlogQuery]); - $request = ' { feed { @@ -126,51 +128,71 @@ class ExecutorSchemaTest extends TestCase $expected = [ 'data' => [ - 'feed' => [ - ['id' => '1', - 'title' => 'My Article 1'], - ['id' => '2', - 'title' => 'My Article 2'], - ['id' => '3', - 'title' => 'My Article 3'], - ['id' => '4', - 'title' => 'My Article 4'], - ['id' => '5', - 'title' => 'My Article 5'], - ['id' => '6', - 'title' => 'My Article 6'], - ['id' => '7', - 'title' => 'My Article 7'], - ['id' => '8', - 'title' => 'My Article 8'], - ['id' => '9', - 'title' => 'My Article 9'], - ['id' => '10', - 'title' => 'My Article 10'] + 'feed' => [ + [ + 'id' => '1', + 'title' => 'My Article 1', + ], + [ + 'id' => '2', + 'title' => 'My Article 2', + ], + [ + 'id' => '3', + 'title' => 'My Article 3', + ], + [ + 'id' => '4', + 'title' => 'My Article 4', + ], + [ + 'id' => '5', + 'title' => 'My Article 5', + ], + [ + 'id' => '6', + 'title' => 'My Article 6', + ], + [ + 'id' => '7', + 'title' => 'My Article 7', + ], + [ + 'id' => '8', + 'title' => 'My Article 8', + ], + [ + 'id' => '9', + 'title' => 'My Article 9', + ], + [ + 'id' => '10', + 'title' => 'My Article 10', + ], ], 'article' => [ - 'id' => '1', + 'id' => '1', 'isPublished' => true, - 'title' => 'My Article 1', - 'body' => 'This is a post', - 'author' => [ - 'id' => '123', - 'name' => 'John Smith', - 'pic' => [ - 'url' => 'cdn://123', - 'width' => 640, - 'height' => 480 + 'title' => 'My Article 1', + 'body' => 'This is a post', + 'author' => [ + 'id' => '123', + 'name' => 'John Smith', + 'pic' => [ + 'url' => 'cdn://123', + 'width' => 640, + 'height' => 480, ], 'recentArticle' => [ - 'id' => '1', + 'id' => '1', 'isPublished' => true, - 'title' => 'My Article 1', - 'body' => 'This is a post', - 'keywords' => ['foo', 'bar', '1', 'true', null] - ] - ] - ] - ] + 'title' => 'My Article 1', + 'body' => 'This is a post', + 'keywords' => ['foo', 'bar', '1', 'true', null], + ], + ], + ], + ], ]; $this->assertEquals($expected, Executor::execute($BlogSchema, Parser::parse($request))->toArray()); @@ -179,30 +201,32 @@ class ExecutorSchemaTest extends TestCase private function article($id) { $johnSmith = null; - $article = function($id) use (&$johnSmith) { + $article = function ($id) use (&$johnSmith) { return [ - 'id' => $id, + 'id' => $id, 'isPublished' => 'true', - 'author' => $johnSmith, - 'title' => 'My Article ' . $id, - 'body' => 'This is a post', - 'hidden' => 'This data is not exposed in the schema', - 'keywords' => ['foo', 'bar', 1, true, null] + 'author' => $johnSmith, + 'title' => 'My Article ' . $id, + 'body' => 'This is a post', + 'hidden' => 'This data is not exposed in the schema', + 'keywords' => ['foo', 'bar', 1, true, null], ]; }; - $getPic = function($uid, $width, $height) { + $getPic = function ($uid, $width, $height) { return [ - 'url' => "cdn://$uid", - 'width' => $width, - 'height' => $height + 'url' => sprintf('cdn://%s', $uid), + 'width' => $width, + 'height' => $height, ]; }; $johnSmith = [ - 'id' => 123, - 'name' => 'John Smith', - 'pic' => function($width, $height) use ($getPic) {return $getPic(123, $width, $height);}, + 'id' => 123, + 'name' => 'John Smith', + 'pic' => function ($width, $height) use ($getPic) { + return $getPic(123, $width, $height); + }, 'recentArticle' => $article(1), ]; diff --git a/tests/Executor/ExecutorTest.php b/tests/Executor/ExecutorTest.php index de63db2..64b6eb7 100644 --- a/tests/Executor/ExecutorTest.php +++ b/tests/Executor/ExecutorTest.php @@ -1,20 +1,25 @@ function () { return 'Apple';}, - 'b' => function () {return 'Banana';}, - 'c' => function () {return 'Cookie';}, - 'd' => function () {return 'Donut';}, - 'e' => function () {return 'Egg';}, - 'f' => 'Fish', - 'pic' => function ($size = 50) { + 'a' => function () { + return 'Apple'; + }, + 'b' => function () { + return 'Banana'; + }, + 'c' => function () { + return 'Cookie'; + }, + 'd' => function () { + return 'Donut'; + }, + 'e' => function () { + return 'Egg'; + }, + 'f' => 'Fish', + 'pic' => function ($size = 50) { return 'Pic of size: ' . $size; }, - 'promise' => function() use ($promiseData) { + 'promise' => function () use ($promiseData) { return $promiseData(); }, - 'deep' => function () use (&$deepData) { + 'deep' => function () use (&$deepData) { return $deepData; - } + }, ]; + // Required for that & reference above $deepData = [ - 'a' => function () { return 'Already Been Done'; }, - 'b' => function () { return 'Boring'; }, - 'c' => function () { + 'a' => function () { + return 'Already Been Done'; + }, + 'b' => function () { + return 'Boring'; + }, + 'c' => function () { return ['Contrived', null, 'Confusing']; }, 'deeper' => function () use (&$data) { return [$data, null, $data]; - } + }, ]; - $doc = ' query Example($size: Int) { a, @@ -99,68 +118,70 @@ class ExecutorTest extends TestCase } '; - $ast = Parser::parse($doc); + $ast = Parser::parse($doc); $expected = [ 'data' => [ - 'a' => 'Apple', - 'b' => 'Banana', - 'x' => 'Cookie', - 'd' => 'Donut', - 'e' => 'Egg', - 'f' => 'Fish', - 'pic' => 'Pic of size: 100', - 'promise' => [ - 'a' => 'Apple' - ], - 'deep' => [ - 'a' => 'Already Been Done', - 'b' => 'Boring', - 'c' => [ 'Contrived', null, 'Confusing' ], + 'a' => 'Apple', + 'b' => 'Banana', + 'x' => 'Cookie', + 'd' => 'Donut', + 'e' => 'Egg', + 'f' => 'Fish', + 'pic' => 'Pic of size: 100', + 'promise' => ['a' => 'Apple'], + 'deep' => [ + 'a' => 'Already Been Done', + 'b' => 'Boring', + 'c' => ['Contrived', null, 'Confusing'], 'deeper' => [ - [ 'a' => 'Apple', 'b' => 'Banana' ], + ['a' => 'Apple', 'b' => 'Banana'], null, - [ 'a' => 'Apple', 'b' => 'Banana' ] - ] - ] - ] + ['a' => 'Apple', 'b' => 'Banana'], + ], + ], + ], ]; $deepDataType = null; - $dataType = new ObjectType([ - 'name' => 'DataType', - 'fields' => function() use (&$dataType, &$deepDataType) { + $dataType = new ObjectType([ + 'name' => 'DataType', + 'fields' => function () use (&$dataType, &$deepDataType) { return [ - 'a' => [ 'type' => Type::string() ], - 'b' => [ 'type' => Type::string() ], - 'c' => [ 'type' => Type::string() ], - 'd' => [ 'type' => Type::string() ], - 'e' => [ 'type' => Type::string() ], - 'f' => [ 'type' => Type::string() ], - 'pic' => [ - 'args' => [ 'size' => ['type' => Type::int() ] ], - 'type' => Type::string(), - 'resolve' => function($obj, $args) { + 'a' => ['type' => Type::string()], + 'b' => ['type' => Type::string()], + 'c' => ['type' => Type::string()], + 'd' => ['type' => Type::string()], + 'e' => ['type' => Type::string()], + 'f' => ['type' => Type::string()], + 'pic' => [ + 'args' => ['size' => ['type' => Type::int()]], + 'type' => Type::string(), + 'resolve' => function ($obj, $args) { return $obj['pic']($args['size']); - } + }, ], 'promise' => ['type' => $dataType], - 'deep' => ['type' => $deepDataType], + 'deep' => ['type' => $deepDataType], ]; - } + }, ]); + // Required for that & reference above $deepDataType = new ObjectType([ - 'name' => 'DeepDataType', + 'name' => 'DeepDataType', 'fields' => [ - 'a' => [ 'type' => Type::string() ], - 'b' => [ 'type' => Type::string() ], - 'c' => [ 'type' => Type::listOf(Type::string()) ], - 'deeper' => [ 'type' => Type::listOf($dataType) ] - ] + 'a' => ['type' => Type::string()], + 'b' => ['type' => Type::string()], + 'c' => ['type' => Type::listOf(Type::string())], + 'deeper' => ['type' => Type::listOf($dataType)], + ], ]); - $schema = new Schema(['query' => $dataType]); + $schema = new Schema(['query' => $dataType]); - $this->assertEquals($expected, Executor::execute($schema, $ast, $data, null, ['size' => 100], 'Example')->toArray()); + $this->assertEquals( + $expected, + Executor::execute($schema, $ast, $data, null, ['size' => 100], 'Example')->toArray() + ); } /** @@ -183,42 +204,52 @@ class ExecutorTest extends TestCase '); $Type = new ObjectType([ - 'name' => 'Type', - 'fields' => function() use (&$Type) { + 'name' => 'Type', + 'fields' => function () use (&$Type) { return [ - 'a' => ['type' => Type::string(), 'resolve' => function () { - return 'Apple'; - }], - 'b' => ['type' => Type::string(), 'resolve' => function () { - return 'Banana'; - }], - 'c' => ['type' => Type::string(), 'resolve' => function () { - return 'Cherry'; - }], + 'a' => [ + 'type' => Type::string(), + 'resolve' => function () { + return 'Apple'; + }, + ], + 'b' => [ + 'type' => Type::string(), + 'resolve' => function () { + return 'Banana'; + }, + ], + 'c' => [ + 'type' => Type::string(), + 'resolve' => function () { + return 'Cherry'; + }, + ], 'deep' => [ - 'type' => $Type, + 'type' => $Type, 'resolve' => function () { return []; - } - ] + }, + ], ]; - } + }, ]); - $schema = new Schema(['query' => $Type]); + + $schema = new Schema(['query' => $Type]); $expected = [ 'data' => [ - 'a' => 'Apple', - 'b' => 'Banana', - 'c' => 'Cherry', + 'a' => 'Apple', + 'b' => 'Banana', + 'c' => 'Cherry', 'deep' => [ - 'b' => 'Banana', - 'c' => 'Cherry', + 'b' => 'Banana', + 'c' => 'Cherry', 'deeper' => [ 'b' => 'Banana', - 'c' => 'Cherry' - ] - ] - ] + 'c' => 'Cherry', + ], + ], + ], ]; $this->assertEquals($expected, Executor::execute($schema, $ast)->toArray()); @@ -232,37 +263,40 @@ class ExecutorTest extends TestCase $ast = Parser::parse('query ($var: String) { result: test }'); /** @var ResolveInfo $info */ - $info = null; + $info = null; $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Test', + 'name' => 'Test', 'fields' => [ 'test' => [ - 'type' => Type::string(), - 'resolve' => function($val, $args, $ctx, $_info) use (&$info) { + 'type' => Type::string(), + 'resolve' => function ($val, $args, $ctx, $_info) use (&$info) { $info = $_info; - } - ] - ] - ]) + }, + ], + ], + ]), ]); - $rootValue = [ 'root' => 'val' ]; + $rootValue = ['root' => 'val']; - Executor::execute($schema, $ast, $rootValue, null, [ 'var' => '123' ]); + Executor::execute($schema, $ast, $rootValue, null, ['var' => '123']); - $this->assertEquals([ - 'fieldName', - 'fieldNodes', - 'returnType', - 'parentType', - 'path', - 'schema', - 'fragments', - 'rootValue', - 'operation', - 'variableValues', - ], array_keys((array) $info)); + $this->assertEquals( + [ + 'fieldName', + 'fieldNodes', + 'returnType', + 'parentType', + 'path', + 'schema', + 'fragments', + 'rootValue', + 'operation', + 'variableValues', + ], + array_keys((array) $info) + ); $this->assertEquals('test', $info->fieldName); $this->assertEquals(1, count($info->fieldNodes)); @@ -286,24 +320,22 @@ class ExecutorTest extends TestCase $gotHere = false; - $data = [ - 'contextThing' => 'thing', - ]; + $data = ['contextThing' => 'thing']; - $ast = Parser::parse($doc); + $ast = Parser::parse($doc); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Type', + 'name' => 'Type', 'fields' => [ 'a' => [ - 'type' => Type::string(), - 'resolve' => function ($context) use ($doc, &$gotHere) { + 'type' => Type::string(), + 'resolve' => function ($context) use (&$gotHere) { $this->assertEquals('thing', $context['contextThing']); $gotHere = true; - } - ] - ] - ]) + }, + ], + ], + ]), ]); Executor::execute($schema, $ast, $data, null, [], 'Example'); @@ -326,22 +358,22 @@ class ExecutorTest extends TestCase $docAst = Parser::parse($doc); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Type', + 'name' => 'Type', 'fields' => [ 'b' => [ - 'args' => [ - 'numArg' => ['type' => Type::int()], - 'stringArg' => ['type' => Type::string()] + 'args' => [ + 'numArg' => ['type' => Type::int()], + 'stringArg' => ['type' => Type::string()], ], - 'type' => Type::string(), + 'type' => Type::string(), 'resolve' => function ($_, $args) use (&$gotHere) { $this->assertEquals(123, $args['numArg']); $this->assertEquals('foo', $args['stringArg']); $gotHere = true; - } - ] - ] - ]) + }, + ], + ], + ]), ]); Executor::execute($schema, $docAst, null, null, [], 'Example'); $this->assertSame($gotHere, true); @@ -368,18 +400,18 @@ class ExecutorTest extends TestCase }'; $data = [ - 'sync' => function () { + 'sync' => function () { return 'sync'; }, - 'syncError' => function () { + 'syncError' => function () { throw new UserError('Error getting syncError'); }, - 'syncRawError' => function() { + 'syncRawError' => function () { throw new UserError('Error getting syncRawError'); }, // inherited from JS reference implementation, but make no sense in this PHP impl // leaving it just to simplify migrations from newer js versions - 'syncReturnError' => function() { + 'syncReturnError' => function () { return new UserError('Error getting syncReturnError'); }, 'syncReturnErrorList' => function () { @@ -387,39 +419,43 @@ class ExecutorTest extends TestCase 'sync0', new UserError('Error getting syncReturnErrorList1'), 'sync2', - new UserError('Error getting syncReturnErrorList3') + new UserError('Error getting syncReturnErrorList3'), ]; }, - 'async' => function() { - return new Deferred(function() { return 'async'; }); + 'async' => function () { + return new Deferred(function () { + return 'async'; + }); }, - 'asyncReject' => function() { - return new Deferred(function() { throw new UserError('Error getting asyncReject'); }); + 'asyncReject' => function () { + return new Deferred(function () { + throw new UserError('Error getting asyncReject'); + }); }, - 'asyncRawReject' => function () { - return new Deferred(function() { + 'asyncRawReject' => function () { + return new Deferred(function () { throw new UserError('Error getting asyncRawReject'); }); }, - 'asyncEmptyReject' => function () { - return new Deferred(function() { + 'asyncEmptyReject' => function () { + return new Deferred(function () { throw new UserError(); }); }, - 'asyncError' => function() { - return new Deferred(function() { + 'asyncError' => function () { + return new Deferred(function () { throw new UserError('Error getting asyncError'); }); }, // inherited from JS reference implementation, but make no sense in this PHP impl // leaving it just to simplify migrations from newer js versions - 'asyncRawError' => function() { - return new Deferred(function() { + 'asyncRawError' => function () { + return new Deferred(function () { throw new UserError('Error getting asyncRawError'); }); }, - 'asyncReturnError' => function() { - return new Deferred(function() { + 'asyncReturnError' => function () { + return new Deferred(function () { throw new UserError('Error getting asyncReturnError'); }); }, @@ -428,96 +464,96 @@ class ExecutorTest extends TestCase $docAst = Parser::parse($doc); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Type', + 'name' => 'Type', 'fields' => [ - 'sync' => ['type' => Type::string()], - 'syncError' => ['type' => Type::string()], - 'syncRawError' => ['type' => Type::string()], - 'syncReturnError' => ['type' => Type::string()], + 'sync' => ['type' => Type::string()], + 'syncError' => ['type' => Type::string()], + 'syncRawError' => ['type' => Type::string()], + 'syncReturnError' => ['type' => Type::string()], 'syncReturnErrorList' => ['type' => Type::listOf(Type::string())], - 'async' => ['type' => Type::string()], - 'asyncReject' => ['type' => Type::string() ], - 'asyncRawReject' => ['type' => Type::string() ], - 'asyncEmptyReject' => ['type' => Type::string() ], - 'asyncError' => ['type' => Type::string()], - 'asyncRawError' => ['type' => Type::string()], - 'asyncReturnError' => ['type' => Type::string()], - ] - ]) + 'async' => ['type' => Type::string()], + 'asyncReject' => ['type' => Type::string()], + 'asyncRawReject' => ['type' => Type::string()], + 'asyncEmptyReject' => ['type' => Type::string()], + 'asyncError' => ['type' => Type::string()], + 'asyncRawError' => ['type' => Type::string()], + 'asyncReturnError' => ['type' => Type::string()], + ], + ]), ]); $expected = [ - 'data' => [ - 'sync' => 'sync', - 'syncError' => null, - 'syncRawError' => null, - 'syncReturnError' => null, + 'data' => [ + 'sync' => 'sync', + 'syncError' => null, + 'syncRawError' => null, + 'syncReturnError' => null, 'syncReturnErrorList' => ['sync0', null, 'sync2', null], - 'async' => 'async', - 'asyncReject' => null, - 'asyncRawReject' => null, - 'asyncEmptyReject' => null, - 'asyncError' => null, - 'asyncRawError' => null, - 'asyncReturnError' => null, + 'async' => 'async', + 'asyncReject' => null, + 'asyncRawReject' => null, + 'asyncEmptyReject' => null, + 'asyncError' => null, + 'asyncRawError' => null, + 'asyncReturnError' => null, ], 'errors' => [ [ - 'message' => 'Error getting syncError', + 'message' => 'Error getting syncError', 'locations' => [['line' => 3, 'column' => 7]], - 'path' => ['syncError'] + 'path' => ['syncError'], ], [ - 'message' => 'Error getting syncRawError', - 'locations' => [ [ 'line' => 4, 'column' => 7 ] ], - 'path'=> [ 'syncRawError' ] + 'message' => 'Error getting syncRawError', + 'locations' => [['line' => 4, 'column' => 7]], + 'path' => ['syncRawError'], ], [ - 'message' => 'Error getting syncReturnError', + 'message' => 'Error getting syncReturnError', 'locations' => [['line' => 5, 'column' => 7]], - 'path' => ['syncReturnError'] + 'path' => ['syncReturnError'], ], [ - 'message' => 'Error getting syncReturnErrorList1', + 'message' => 'Error getting syncReturnErrorList1', 'locations' => [['line' => 6, 'column' => 7]], - 'path' => ['syncReturnErrorList', 1] + 'path' => ['syncReturnErrorList', 1], ], [ - 'message' => 'Error getting syncReturnErrorList3', + 'message' => 'Error getting syncReturnErrorList3', 'locations' => [['line' => 6, 'column' => 7]], - 'path' => ['syncReturnErrorList', 3] + 'path' => ['syncReturnErrorList', 3], ], [ - 'message' => 'Error getting asyncReject', + 'message' => 'Error getting asyncReject', 'locations' => [['line' => 8, 'column' => 7]], - 'path' => ['asyncReject'] + 'path' => ['asyncReject'], ], [ - 'message' => 'Error getting asyncRawReject', + 'message' => 'Error getting asyncRawReject', 'locations' => [['line' => 9, 'column' => 7]], - 'path' => ['asyncRawReject'] + 'path' => ['asyncRawReject'], ], [ - 'message' => 'An unknown error occurred.', + 'message' => 'An unknown error occurred.', 'locations' => [['line' => 10, 'column' => 7]], - 'path' => ['asyncEmptyReject'] + 'path' => ['asyncEmptyReject'], ], [ - 'message' => 'Error getting asyncError', + 'message' => 'Error getting asyncError', 'locations' => [['line' => 11, 'column' => 7]], - 'path' => ['asyncError'] + 'path' => ['asyncError'], ], [ - 'message' => 'Error getting asyncRawError', - 'locations' => [ [ 'line' => 12, 'column' => 7 ] ], - 'path' => [ 'asyncRawError' ] + 'message' => 'Error getting asyncRawError', + 'locations' => [['line' => 12, 'column' => 7]], + 'path' => ['asyncRawError'], ], [ - 'message' => 'Error getting asyncReturnError', + 'message' => 'Error getting asyncReturnError', 'locations' => [['line' => 13, 'column' => 7]], - 'path' => ['asyncReturnError'] + 'path' => ['asyncReturnError'], ], - ] + ], ]; $result = Executor::execute($schema, $docAst, $data)->toArray(); @@ -530,16 +566,16 @@ class ExecutorTest extends TestCase */ public function testUsesTheInlineOperationIfNoOperationIsProvided() : void { - $doc = '{ a }'; - $data = ['a' => 'b']; - $ast = Parser::parse($doc); + $doc = '{ a }'; + $data = ['a' => 'b']; + $ast = Parser::parse($doc); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Type', + 'name' => 'Type', 'fields' => [ 'a' => ['type' => Type::string()], - ] - ]) + ], + ]), ]); $ex = Executor::execute($schema, $ast, $data); @@ -552,16 +588,16 @@ class ExecutorTest extends TestCase */ public function testUsesTheOnlyOperationIfNoOperationIsProvided() : void { - $doc = 'query Example { a }'; - $data = [ 'a' => 'b' ]; - $ast = Parser::parse($doc); + $doc = 'query Example { a }'; + $data = ['a' => 'b']; + $ast = Parser::parse($doc); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Type', + 'name' => 'Type', 'fields' => [ - 'a' => [ 'type' => Type::string() ], - ] - ]) + 'a' => ['type' => Type::string()], + ], + ]), ]); $ex = Executor::execute($schema, $ast, $data); @@ -573,16 +609,16 @@ class ExecutorTest extends TestCase */ public function testUsesTheNamedOperationIfOperationNameIsProvided() : void { - $doc = 'query Example { first: a } query OtherExample { second: a }'; - $data = [ 'a' => 'b' ]; - $ast = Parser::parse($doc); + $doc = 'query Example { first: a } query OtherExample { second: a }'; + $data = ['a' => 'b']; + $ast = Parser::parse($doc); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Type', + 'name' => 'Type', 'fields' => [ - 'a' => [ 'type' => Type::string() ], - ] - ]) + 'a' => ['type' => Type::string()], + ], + ]), ]); $result = Executor::execute($schema, $ast, $data, null, null, 'OtherExample'); @@ -594,25 +630,23 @@ class ExecutorTest extends TestCase */ public function testProvidesErrorIfNoOperationIsProvided() : void { - $doc = 'fragment Example on Type { a }'; - $data = [ 'a' => 'b' ]; - $ast = Parser::parse($doc); + $doc = 'fragment Example on Type { a }'; + $data = ['a' => 'b']; + $ast = Parser::parse($doc); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Type', + 'name' => 'Type', 'fields' => [ - 'a' => [ 'type' => Type::string() ], - ] - ]) + 'a' => ['type' => Type::string()], + ], + ]), ]); - $result = Executor::execute($schema, $ast, $data); + $result = Executor::execute($schema, $ast, $data); $expected = [ 'errors' => [ - [ - 'message' => 'Must provide an operation.', - ] - ] + ['message' => 'Must provide an operation.'], + ], ]; $this->assertArraySubset($expected, $result->toArray()); @@ -623,26 +657,24 @@ class ExecutorTest extends TestCase */ public function testErrorsIfNoOperationIsProvidedWithMultipleOperations() : void { - $doc = 'query Example { a } query OtherExample { a }'; - $data = ['a' => 'b']; - $ast = Parser::parse($doc); + $doc = 'query Example { a } query OtherExample { a }'; + $data = ['a' => 'b']; + $ast = Parser::parse($doc); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Type', + 'name' => 'Type', 'fields' => [ 'a' => ['type' => Type::string()], - ] - ]) + ], + ]), ]); $result = Executor::execute($schema, $ast, $data); $expected = [ 'errors' => [ - [ - 'message' => 'Must provide operation name if query contains multiple operations.', - ] - ] + ['message' => 'Must provide operation name if query contains multiple operations.'], + ], ]; $this->assertArraySubset($expected, $result->toArray()); @@ -653,18 +685,17 @@ class ExecutorTest extends TestCase */ public function testErrorsIfUnknownOperationNameIsProvided() : void { - $doc = 'query Example { a } query OtherExample { a }'; - $ast = Parser::parse($doc); + $doc = 'query Example { a } query OtherExample { a }'; + $ast = Parser::parse($doc); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Type', + 'name' => 'Type', 'fields' => [ 'a' => ['type' => Type::string()], - ] - ]) + ], + ]), ]); - $result = Executor::execute( $schema, $ast, @@ -676,10 +707,8 @@ class ExecutorTest extends TestCase $expected = [ 'errors' => [ - [ - 'message' => 'Unknown operation named "UnknownExample".', - ] - ] + ['message' => 'Unknown operation named "UnknownExample".'], + ], ]; @@ -691,22 +720,22 @@ class ExecutorTest extends TestCase */ public function testUsesTheQuerySchemaForQueries() : void { - $doc = 'query Q { a } mutation M { c }'; - $data = ['a' => 'b', 'c' => 'd']; - $ast = Parser::parse($doc); + $doc = 'query Q { a } mutation M { c }'; + $data = ['a' => 'b', 'c' => 'd']; + $ast = Parser::parse($doc); $schema = new Schema([ - 'query' => new ObjectType([ - 'name' => 'Q', + 'query' => new ObjectType([ + 'name' => 'Q', 'fields' => [ 'a' => ['type' => Type::string()], - ] + ], ]), 'mutation' => new ObjectType([ - 'name' => 'M', + 'name' => 'M', 'fields' => [ 'c' => ['type' => Type::string()], - ] - ]) + ], + ]), ]); $queryResult = Executor::execute($schema, $ast, $data, null, [], 'Q'); @@ -718,22 +747,22 @@ class ExecutorTest extends TestCase */ public function testUsesTheMutationSchemaForMutations() : void { - $doc = 'query Q { a } mutation M { c }'; - $data = [ 'a' => 'b', 'c' => 'd' ]; - $ast = Parser::parse($doc); - $schema = new Schema([ - 'query' => new ObjectType([ - 'name' => 'Q', + $doc = 'query Q { a } mutation M { c }'; + $data = ['a' => 'b', 'c' => 'd']; + $ast = Parser::parse($doc); + $schema = new Schema([ + 'query' => new ObjectType([ + 'name' => 'Q', 'fields' => [ 'a' => ['type' => Type::string()], - ] + ], ]), 'mutation' => new ObjectType([ - 'name' => 'M', + 'name' => 'M', 'fields' => [ - 'c' => [ 'type' => Type::string() ], - ] - ]) + 'c' => ['type' => Type::string()], + ], + ]), ]); $mutationResult = Executor::execute($schema, $ast, $data, null, [], 'M'); $this->assertEquals(['data' => ['c' => 'd']], $mutationResult->toArray()); @@ -744,22 +773,22 @@ class ExecutorTest extends TestCase */ public function testUsesTheSubscriptionSchemaForSubscriptions() : void { - $doc = 'query Q { a } subscription S { a }'; - $data = [ 'a' => 'b', 'c' => 'd' ]; - $ast = Parser::parse($doc); + $doc = 'query Q { a } subscription S { a }'; + $data = ['a' => 'b', 'c' => 'd']; + $ast = Parser::parse($doc); $schema = new Schema([ - 'query' => new ObjectType([ - 'name' => 'Q', + 'query' => new ObjectType([ + 'name' => 'Q', 'fields' => [ - 'a' => [ 'type' => Type::string() ], - ] + 'a' => ['type' => Type::string()], + ], ]), 'subscription' => new ObjectType([ - 'name' => 'S', + 'name' => 'S', 'fields' => [ - 'a' => [ 'type' => Type::string() ], - ] - ]) + 'a' => ['type' => Type::string()], + ], + ]), ]); $subscriptionResult = Executor::execute($schema, $ast, $data, null, [], 'S'); @@ -768,7 +797,7 @@ class ExecutorTest extends TestCase public function testCorrectFieldOrderingDespiteExecutionOrder() : void { - $doc = '{ + $doc = '{ a, b, c, @@ -780,13 +809,17 @@ class ExecutorTest extends TestCase return 'a'; }, 'b' => function () { - return new Deferred(function () { return 'b'; }); + return new Deferred(function () { + return 'b'; + }); }, 'c' => function () { return 'c'; }, 'd' => function () { - return new Deferred(function () { return 'd'; }); + return new Deferred(function () { + return 'd'; + }); }, 'e' => function () { return 'e'; @@ -796,16 +829,16 @@ class ExecutorTest extends TestCase $ast = Parser::parse($doc); $queryType = new ObjectType([ - 'name' => 'DeepDataType', + 'name' => 'DeepDataType', 'fields' => [ - 'a' => [ 'type' => Type::string() ], - 'b' => [ 'type' => Type::string() ], - 'c' => [ 'type' => Type::string() ], - 'd' => [ 'type' => Type::string() ], - 'e' => [ 'type' => Type::string() ], - ] + 'a' => ['type' => Type::string()], + 'b' => ['type' => Type::string()], + 'c' => ['type' => Type::string()], + 'd' => ['type' => Type::string()], + 'e' => ['type' => Type::string()], + ], ]); - $schema = new Schema(['query' => $queryType]); + $schema = new Schema(['query' => $queryType]); $expected = [ 'data' => [ @@ -814,7 +847,7 @@ class ExecutorTest extends TestCase 'c' => 'c', 'd' => 'd', 'e' => 'e', - ] + ], ]; $this->assertEquals($expected, Executor::execute($schema, $ast, $data)->toArray()); @@ -825,7 +858,7 @@ class ExecutorTest extends TestCase */ public function testAvoidsRecursion() : void { - $doc = ' + $doc = ' query Q { a ...Frag @@ -837,15 +870,15 @@ class ExecutorTest extends TestCase ...Frag } '; - $data = ['a' => 'b']; - $ast = Parser::parse($doc); + $data = ['a' => 'b']; + $ast = Parser::parse($doc); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Type', + 'name' => 'Type', 'fields' => [ 'a' => ['type' => Type::string()], - ] - ]) + ], + ]), ]); $queryResult = Executor::execute($schema, $ast, $data, null, [], 'Q'); @@ -857,23 +890,23 @@ class ExecutorTest extends TestCase */ public function testDoesNotIncludeIllegalFieldsInOutput() : void { - $doc = 'mutation M { + $doc = 'mutation M { thisIsIllegalDontIncludeMe }'; - $ast = Parser::parse($doc); - $schema = new Schema([ - 'query' => new ObjectType([ - 'name' => 'Q', + $ast = Parser::parse($doc); + $schema = new Schema([ + 'query' => new ObjectType([ + 'name' => 'Q', 'fields' => [ 'a' => ['type' => Type::string()], - ] + ], ]), 'mutation' => new ObjectType([ - 'name' => 'M', + 'name' => 'M', 'fields' => [ 'c' => ['type' => Type::string()], - ] - ]) + ], + ]), ]); $mutationResult = Executor::execute($schema, $ast); $this->assertEquals(['data' => []], $mutationResult->toArray()); @@ -886,29 +919,29 @@ class ExecutorTest extends TestCase { $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Type', + 'name' => 'Type', 'fields' => [ 'field' => [ - 'type' => Type::string(), - 'resolve' => function($data, $args) {return $args ? json_encode($args) : '';}, - 'args' => [ + 'type' => Type::string(), + 'resolve' => function ($data, $args) { + return $args ? json_encode($args) : ''; + }, + 'args' => [ 'a' => ['type' => Type::boolean()], 'b' => ['type' => Type::boolean()], 'c' => ['type' => Type::boolean()], 'd' => ['type' => Type::int()], - 'e' => ['type' => Type::int()] - ] - ] - ] - ]) + 'e' => ['type' => Type::int()], + ], + ], + ], + ]), ]); - $query = Parser::parse('{ field(a: true, c: false, e: 0) }'); - $result = Executor::execute($schema, $query); + $query = Parser::parse('{ field(a: true, c: false, e: 0) }'); + $result = Executor::execute($schema, $query); $expected = [ - 'data' => [ - 'field' => '{"a":true,"c":false,"e":0}' - ] + 'data' => ['field' => '{"a":true,"c":false,"e":0}'], ]; $this->assertEquals($expected, $result->toArray()); @@ -920,48 +953,54 @@ class ExecutorTest extends TestCase public function testFailsWhenAnIsTypeOfCheckIsNotMet() : void { $SpecialType = new ObjectType([ - 'name' => 'SpecialType', - 'isTypeOf' => function($obj) { + 'name' => 'SpecialType', + 'isTypeOf' => function ($obj) { return $obj instanceof Special; }, - 'fields' => [ - 'value' => ['type' => Type::string()] - ] + 'fields' => [ + 'value' => ['type' => Type::string()], + ], ]); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ 'specials' => [ - 'type' => Type::listOf($SpecialType), - 'resolve' => function($rootValue) { + 'type' => Type::listOf($SpecialType), + 'resolve' => function ($rootValue) { return $rootValue['specials']; - } - ] - ] - ]) + }, + ], + ], + ]), ]); - $query = Parser::parse('{ specials { value } }'); - $value = [ - 'specials' => [ new Special('foo'), new NotSpecial('bar') ] + $query = Parser::parse('{ specials { value } }'); + $value = [ + 'specials' => [new Special('foo'), new NotSpecial('bar')], ]; $result = Executor::execute($schema, $query, $value); - $this->assertEquals([ - 'specials' => [ - ['value' => 'foo'], - null - ] - ], $result->data); + $this->assertEquals( + [ + 'specials' => [ + ['value' => 'foo'], + null, + ], + ], + $result->data + ); $this->assertEquals(1, count($result->errors)); - $this->assertEquals([ - 'message' => 'Expected value of type "SpecialType" but got: instance of GraphQL\Tests\Executor\NotSpecial.', - 'locations' => [['line' => 1, 'column' => 3]], - 'path' => ['specials', 1] - ], $result->errors[0]->toSerializableArray()); + $this->assertEquals( + [ + 'message' => 'Expected value of type "SpecialType" but got: instance of GraphQL\Tests\Executor\TestClasses\NotSpecial.', + 'locations' => [['line' => 1, 'column' => 3]], + 'path' => ['specials', 1], + ], + $result->errors[0]->toSerializableArray() + ); } /** @@ -977,20 +1016,17 @@ class ExecutorTest extends TestCase $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ - 'foo' => ['type' => Type::string()] - ] - ]) + 'foo' => ['type' => Type::string()], + ], + ]), ]); - $result = Executor::execute($schema, $query); $expected = [ - 'data' => [ - 'foo' => null, - ], + 'data' => ['foo' => null], ]; $this->assertArraySubset($expected, $result->toArray()); @@ -1005,11 +1041,11 @@ class ExecutorTest extends TestCase $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ - 'foo' => ['type' => Type::string()] - ] - ]) + 'foo' => ['type' => Type::string()], + ], + ]), ]); // For the purposes of test, just return the name of the field! @@ -1028,7 +1064,7 @@ class ExecutorTest extends TestCase ); $expected = [ - 'data' => ['foo' => 'foo'] + 'data' => ['foo' => 'foo'], ]; $this->assertEquals($expected, $result->toArray()); @@ -1038,12 +1074,14 @@ class ExecutorTest extends TestCase { $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Type', + 'name' => 'Type', 'fields' => [ 'field' => [ - 'type' => Type::string(), - 'resolve' => function($data, $args) {return $args ? json_encode($args) : '';}, - 'args' => [ + 'type' => Type::string(), + 'resolve' => function ($data, $args) { + return $args ? json_encode($args) : ''; + }, + 'args' => [ 'a' => ['type' => Type::boolean(), 'defaultValue' => 1], 'b' => ['type' => Type::boolean(), 'defaultValue' => null], 'c' => ['type' => Type::boolean(), 'defaultValue' => 0], @@ -1051,25 +1089,25 @@ class ExecutorTest extends TestCase 'e' => ['type' => Type::int(), 'defaultValue' => '0'], 'f' => ['type' => Type::int(), 'defaultValue' => 'some-string'], 'g' => ['type' => Type::boolean()], - 'h' => ['type' => new InputObjectType([ - 'name' => 'ComplexType', - 'fields' => [ - 'a' => ['type' => Type::int()], - 'b' => ['type' => Type::string()] - ] - ]), 'defaultValue' => ['a' => 1, 'b' => 'test']] - ] - ] - ] - ]) + 'h' => [ + 'type' => new InputObjectType([ + 'name' => 'ComplexType', + 'fields' => [ + 'a' => ['type' => Type::int()], + 'b' => ['type' => Type::string()], + ], + ]), 'defaultValue' => ['a' => 1, 'b' => 'test'], + ], + ], + ], + ], + ]), ]); - $query = Parser::parse('{ field }'); - $result = Executor::execute($schema, $query); + $query = Parser::parse('{ field }'); + $result = Executor::execute($schema, $query); $expected = [ - 'data' => [ - 'field' => '{"a":1,"b":null,"c":0,"d":false,"e":"0","f":"some-string","h":{"a":1,"b":"test"}}' - ] + 'data' => ['field' => '{"a":1,"b":null,"c":0,"d":false,"e":"0","f":"some-string","h":{"a":1,"b":"test"}}'], ]; $this->assertEquals($expected, $result->toArray()); @@ -1083,43 +1121,43 @@ class ExecutorTest extends TestCase $iface = null; $a = new ObjectType([ - 'name' => 'A', - 'fields' => [ - 'id' => Type::id() + 'name' => 'A', + 'fields' => [ + 'id' => Type::id(), ], - 'interfaces' => function() use (&$iface) { + 'interfaces' => function () use (&$iface) { return [$iface]; - } + }, ]); $b = new ObjectType([ - 'name' => 'B', - 'fields' => [ - 'id' => Type::id() + 'name' => 'B', + 'fields' => [ + 'id' => Type::id(), ], - 'interfaces' => function() use (&$iface) { + 'interfaces' => function () use (&$iface) { return [$iface]; - } + }, ]); $iface = new InterfaceType([ - 'name' => 'Iface', - 'fields' => [ - 'id' => Type::id() + 'name' => 'Iface', + 'fields' => [ + 'id' => Type::id(), ], - 'resolveType' => function($v) use ($a, $b) { + 'resolveType' => function ($v) use ($a, $b) { return $v['type'] === 'A' ? $a : $b; - } + }, ]); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ - 'ab' => Type::listOf($iface) - ] + 'ab' => Type::listOf($iface), + ], ]), - 'types' => [$a, $b] + 'types' => [$a, $b], ]); $data = [ @@ -1127,8 +1165,8 @@ class ExecutorTest extends TestCase ['id' => 1, 'type' => 'A'], ['id' => 2, 'type' => 'A'], ['id' => 3, 'type' => 'B'], - ['id' => 4, 'type' => 'B'] - ] + ['id' => 4, 'type' => 'B'], + ], ]; $query = Parser::parse(' @@ -1143,15 +1181,18 @@ class ExecutorTest extends TestCase $result = Executor::execute($schema, $query, $data, null); - $this->assertEquals([ - 'data' => [ - 'ab' => [ - ['id' => '1'], - ['id' => '2'], - new \stdClass(), - new \stdClass() - ] - ] - ], $result->toArray()); + $this->assertEquals( + [ + 'data' => [ + 'ab' => [ + ['id' => '1'], + ['id' => '2'], + new \stdClass(), + new \stdClass(), + ], + ], + ], + $result->toArray() + ); } } diff --git a/tests/Executor/LazyInterfaceTest.php b/tests/Executor/LazyInterfaceTest.php index a099187..f9fcc31 100644 --- a/tests/Executor/LazyInterfaceTest.php +++ b/tests/Executor/LazyInterfaceTest.php @@ -1,104 +1,34 @@ 'query', - 'fields' => function () { - return [ - 'lazyInterface' => [ - 'type' => $this->getLazyInterfaceType(), - 'resolve' => function() { - return []; - } - ] - ]; - } - ]); - - $this->schema = new Schema(['query' => $query, 'types' => [$this->getTestObjectType()]]); - } - - /** - * Returns the LazyInterface - * - * @return InterfaceType - */ - protected function getLazyInterfaceType() - { - if (!$this->lazyInterface) { - $this->lazyInterface = new InterfaceType([ - 'name' => 'LazyInterface', - 'fields' => [ - 'a' => Type::string() - ], - 'resolveType' => function() { - return $this->getTestObjectType(); - }, - ]); - } - - return $this->lazyInterface; - } - - /** - * Returns the test ObjectType - * @return ObjectType - */ - protected function getTestObjectType() - { - if (!$this->testObject) { - $this->testObject = new ObjectType([ - 'name' => 'TestObject', - 'fields' => [ - 'name' => [ - 'type' => Type::string(), - 'resolve' => function() { - return 'testname'; - } - ] - ], - 'interfaces' => [$this->getLazyInterfaceType()] - ]); - } - - return $this->testObject; - } - /** * Handles execution of a lazily created interface */ @@ -116,12 +46,78 @@ class LazyInterfaceTest extends TestCase $expected = [ 'data' => [ - 'lazyInterface' => [ - 'name' => 'testname' - ] - ] + 'lazyInterface' => ['name' => 'testname'], + ], ]; $this->assertEquals($expected, Executor::execute($this->schema, Parser::parse($request))->toArray()); } + + /** + * Setup schema + */ + protected function setUp() + { + $query = new ObjectType([ + 'name' => 'query', + 'fields' => function () { + return [ + 'lazyInterface' => [ + 'type' => $this->getLazyInterfaceType(), + 'resolve' => function () { + return []; + }, + ], + ]; + }, + ]); + + $this->schema = new Schema(['query' => $query, 'types' => [$this->getTestObjectType()]]); + } + + /** + * Returns the LazyInterface + * + * @return InterfaceType + */ + protected function getLazyInterfaceType() + { + if (! $this->lazyInterface) { + $this->lazyInterface = new InterfaceType([ + 'name' => 'LazyInterface', + 'fields' => [ + 'a' => Type::string(), + ], + 'resolveType' => function () { + return $this->getTestObjectType(); + }, + ]); + } + + return $this->lazyInterface; + } + + /** + * Returns the test ObjectType + * @return ObjectType + */ + protected function getTestObjectType() + { + if (! $this->testObject) { + $this->testObject = new ObjectType([ + 'name' => 'TestObject', + 'fields' => [ + 'name' => [ + 'type' => Type::string(), + 'resolve' => function () { + return 'testname'; + }, + ], + ], + 'interfaces' => [$this->getLazyInterfaceType()], + ]); + } + + return $this->testObject; + } } diff --git a/tests/Executor/ListsTest.php b/tests/Executor/ListsTest.php index 3ab1406..f9906f9 100644 --- a/tests/Executor/ListsTest.php +++ b/tests/Executor/ListsTest.php @@ -1,22 +1,21 @@ checkHandlesNullableLists( - [ 1, 2 ], - [ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ] + [1, 2], + ['data' => ['nest' => ['test' => [1, 2]]]] ); // Contains null $this->checkHandlesNullableLists( - [ 1, null, 2 ], - [ 'data' => [ 'nest' => [ 'test' => [ 1, null, 2 ] ] ] ] + [1, null, 2], + ['data' => ['nest' => ['test' => [1, null, 2]]]] ); // Returns null $this->checkHandlesNullableLists( null, - [ 'data' => [ 'nest' => [ 'test' => null ] ] ] + ['data' => ['nest' => ['test' => null]]] ); } + private function checkHandlesNullableLists($testData, $expected) + { + $testType = Type::listOf(Type::int()); + $this->check($testType, $testData, $expected); + } + + private function check($testType, $testData, $expected, $debug = false) + { + $data = ['test' => $testData]; + $dataType = null; + + $dataType = new ObjectType([ + 'name' => 'DataType', + 'fields' => function () use (&$testType, &$dataType, $data) { + return [ + 'test' => ['type' => $testType], + 'nest' => [ + 'type' => $dataType, + 'resolve' => function () use ($data) { + return $data; + }, + ], + ]; + }, + ]); + + $schema = new Schema(['query' => $dataType]); + + $ast = Parser::parse('{ nest { test } }'); + + $result = Executor::execute($schema, $ast, $data); + $this->assertArraySubset($expected, $result->toArray($debug)); + } + /** * [T] */ @@ -48,26 +81,26 @@ class ListsTest extends TestCase { // Contains values $this->checkHandlesNullableLists( - new Deferred(function() { - return [1,2]; + new Deferred(function () { + return [1, 2]; }), - [ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ] + ['data' => ['nest' => ['test' => [1, 2]]]] ); // Contains null $this->checkHandlesNullableLists( - new Deferred(function() { + new Deferred(function () { return [1, null, 2]; }), - [ 'data' => [ 'nest' => [ 'test' => [ 1, null, 2 ] ] ] ] + ['data' => ['nest' => ['test' => [1, null, 2]]]] ); // Returns null $this->checkHandlesNullableLists( - new Deferred(function() { + new Deferred(function () { return null; }), - [ 'data' => [ 'nest' => [ 'test' => null ] ] ] + ['data' => ['nest' => ['test' => null]]] ); // Rejected @@ -78,14 +111,14 @@ class ListsTest extends TestCase }); }, [ - 'data' => ['nest' => ['test' => null]], + 'data' => ['nest' => ['test' => null]], 'errors' => [ [ - 'message' => 'bad', + 'message' => 'bad', 'locations' => [['line' => 1, 'column' => 10]], - 'path' => ['nest', 'test'] - ] - ] + 'path' => ['nest', 'test'], + ], + ], ] ); } @@ -98,57 +131,64 @@ class ListsTest extends TestCase // Contains values $this->checkHandlesNullableLists( [ - new Deferred(function() { + new Deferred(function () { return 1; }), - new Deferred(function() { + new Deferred(function () { return 2; - })], - [ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ] + }), + ], + ['data' => ['nest' => ['test' => [1, 2]]]] ); // Contains null $this->checkHandlesNullableLists( [ - new Deferred(function() {return 1;}), - new Deferred(function() {return null;}), - new Deferred(function() {return 2;}) + new Deferred(function () { + return 1; + }), + new Deferred(function () { + return null; + }), + new Deferred(function () { + return 2; + }), ], - [ 'data' => [ 'nest' => [ 'test' => [ 1, null, 2 ] ] ] ] + ['data' => ['nest' => ['test' => [1, null, 2]]]] ); // Returns null $this->checkHandlesNullableLists( - new Deferred(function() { + new Deferred(function () { return null; }), - [ 'data' => [ 'nest' => [ 'test' => null ] ] ] + ['data' => ['nest' => ['test' => null]]] ); // Contains reject $this->checkHandlesNullableLists( function () { return [ - new Deferred(function() { + new Deferred(function () { return 1; }), - new Deferred(function() { + new Deferred(function () { throw new UserError('bad'); }), - new Deferred(function() { + new Deferred(function () { return 2; - }) + }), ]; }, [ - 'data' => ['nest' => ['test' => [1, null, 2]]], + 'data' => ['nest' => ['test' => [1, null, 2]]], 'errors' => [ [ - 'message' => 'bad', + 'message' => 'bad', 'locations' => [['line' => 1, 'column' => 10]], - 'path' => ['nest', 'test', 1] - ] - ] + 'path' => ['nest', 'test', 1], + ], + ], ] ); } @@ -160,32 +200,38 @@ class ListsTest extends TestCase { // Contains values $this->checkHandlesNonNullableLists( - [ 1, 2 ], - [ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ] + [1, 2], + ['data' => ['nest' => ['test' => [1, 2]]]] ); // Contains null $this->checkHandlesNonNullableLists( - [ 1, null, 2 ], - [ 'data' => [ 'nest' => [ 'test' => [ 1, null, 2 ] ] ] ] + [1, null, 2], + ['data' => ['nest' => ['test' => [1, null, 2]]]] ); // Returns null $this->checkHandlesNonNullableLists( null, [ - 'data' => [ 'nest' => null ], + 'data' => ['nest' => null], 'errors' => [ [ 'debugMessage' => 'Cannot return null for non-nullable field DataType.test.', - 'locations' => [['line' => 1, 'column' => 10]] - ] - ] + 'locations' => [['line' => 1, 'column' => 10]], + ], + ], ], true ); } + private function checkHandlesNonNullableLists($testData, $expected, $debug = false) + { + $testType = Type::nonNull(Type::listOf(Type::int())); + $this->check($testType, $testData, $expected, $debug); + } + /** * [T]! */ @@ -193,31 +239,31 @@ class ListsTest extends TestCase { // Contains values $this->checkHandlesNonNullableLists( - new Deferred(function() { - return [1,2]; + new Deferred(function () { + return [1, 2]; }), - [ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ] + ['data' => ['nest' => ['test' => [1, 2]]]] ); // Contains null $this->checkHandlesNonNullableLists( - new Deferred(function() { + new Deferred(function () { return [1, null, 2]; }), - [ 'data' => [ 'nest' => [ 'test' => [ 1, null, 2 ] ] ] ] + ['data' => ['nest' => ['test' => [1, null, 2]]]] ); // Returns null $this->checkHandlesNonNullableLists( null, [ - 'data' => [ 'nest' => null ], + 'data' => ['nest' => null], 'errors' => [ [ 'debugMessage' => 'Cannot return null for non-nullable field DataType.test.', - 'locations' => [['line' => 1, 'column' => 10]] - ] - ] + 'locations' => [['line' => 1, 'column' => 10]], + ], + ], ], true ); @@ -225,19 +271,19 @@ class ListsTest extends TestCase // Rejected $this->checkHandlesNonNullableLists( function () { - return new Deferred(function() { + return new Deferred(function () { throw new UserError('bad'); }); }, [ - 'data' => ['nest' => null], + 'data' => ['nest' => null], 'errors' => [ [ - 'message' => 'bad', + 'message' => 'bad', 'locations' => [['line' => 1, 'column' => 10]], - 'path' => ['nest', 'test'] - ] - ] + 'path' => ['nest', 'test'], + ], + ], ] ); } @@ -250,56 +296,56 @@ class ListsTest extends TestCase // Contains values $this->checkHandlesNonNullableLists( [ - new Deferred(function() { + new Deferred(function () { return 1; }), - new Deferred(function() { + new Deferred(function () { return 2; - }) + }), ], - [ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ] + ['data' => ['nest' => ['test' => [1, 2]]]] ); // Contains null $this->checkHandlesNonNullableLists( [ - new Deferred(function() { + new Deferred(function () { return 1; }), - new Deferred(function() { + new Deferred(function () { return null; }), - new Deferred(function() { + new Deferred(function () { return 2; - }) + }), ], - [ 'data' => [ 'nest' => [ 'test' => [ 1, null, 2 ] ] ] ] + ['data' => ['nest' => ['test' => [1, null, 2]]]] ); // Contains reject $this->checkHandlesNonNullableLists( function () { return [ - new Deferred(function() { + new Deferred(function () { return 1; }), - new Deferred(function() { + new Deferred(function () { throw new UserError('bad'); }), - new Deferred(function() { + new Deferred(function () { return 2; - }) + }), ]; }, [ - 'data' => ['nest' => ['test' => [1, null, 2]]], + 'data' => ['nest' => ['test' => [1, null, 2]]], 'errors' => [ [ - 'message' => 'bad', + 'message' => 'bad', 'locations' => [['line' => 1, 'column' => 10]], - 'path' => ['nest', 'test', 1] - ] - ] + 'path' => ['nest', 'test', 1], + ], + ], ] ); } @@ -311,21 +357,21 @@ class ListsTest extends TestCase { // Contains values $this->checkHandlesListOfNonNulls( - [ 1, 2 ], - [ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ] + [1, 2], + ['data' => ['nest' => ['test' => [1, 2]]]] ); // Contains null $this->checkHandlesListOfNonNulls( - [ 1, null, 2 ], + [1, null, 2], [ - 'data' => [ 'nest' => [ 'test' => null ] ], + 'data' => ['nest' => ['test' => null]], 'errors' => [ [ 'debugMessage' => 'Cannot return null for non-nullable field DataType.test.', - 'locations' => [ ['line' => 1, 'column' => 10] ] - ] - ] + 'locations' => [['line' => 1, 'column' => 10]], + ], + ], ], true ); @@ -333,10 +379,16 @@ class ListsTest extends TestCase // Returns null $this->checkHandlesListOfNonNulls( null, - [ 'data' => [ 'nest' => [ 'test' => null ] ] ] + ['data' => ['nest' => ['test' => null]]] ); } + private function checkHandlesListOfNonNulls($testData, $expected, $debug = false) + { + $testType = Type::listOf(Type::nonNull(Type::int())); + $this->check($testType, $testData, $expected, $debug); + } + /** * [T!] */ @@ -344,53 +396,53 @@ class ListsTest extends TestCase { // Contains values $this->checkHandlesListOfNonNulls( - new Deferred(function() { + new Deferred(function () { return [1, 2]; }), - [ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ] + ['data' => ['nest' => ['test' => [1, 2]]]] ); // Contains null $this->checkHandlesListOfNonNulls( - new Deferred(function() { + new Deferred(function () { return [1, null, 2]; }), [ - 'data' => [ 'nest' => [ 'test' => null ] ], + 'data' => ['nest' => ['test' => null]], 'errors' => [ [ 'debugMessage' => 'Cannot return null for non-nullable field DataType.test.', - 'locations' => [['line' => 1, 'column' => 10]] - ] - ] + 'locations' => [['line' => 1, 'column' => 10]], + ], + ], ], true ); // Returns null $this->checkHandlesListOfNonNulls( - new Deferred(function() { + new Deferred(function () { return null; }), - [ 'data' => [ 'nest' => [ 'test' => null ] ] ] + ['data' => ['nest' => ['test' => null]]] ); // Rejected $this->checkHandlesListOfNonNulls( function () { - return new Deferred(function() { + return new Deferred(function () { throw new UserError('bad'); }); }, [ - 'data' => ['nest' => ['test' => null]], + 'data' => ['nest' => ['test' => null]], 'errors' => [ [ - 'message' => 'bad', + 'message' => 'bad', 'locations' => [['line' => 1, 'column' => 10]], - 'path' => ['nest', 'test'] - ] - ] + 'path' => ['nest', 'test'], + ], + ], ] ); } @@ -403,50 +455,56 @@ class ListsTest extends TestCase // Contains values $this->checkHandlesListOfNonNulls( [ - new Deferred(function() { + new Deferred(function () { return 1; }), - new Deferred(function() { + new Deferred(function () { return 2; - }) + }), ], - [ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ] + ['data' => ['nest' => ['test' => [1, 2]]]] ); // Contains null $this->checkHandlesListOfNonNulls( [ - new Deferred(function() {return 1;}), - new Deferred(function() {return null;}), - new Deferred(function() {return 2;}) + new Deferred(function () { + return 1; + }), + new Deferred(function () { + return null; + }), + new Deferred(function () { + return 2; + }), ], - [ 'data' => [ 'nest' => [ 'test' => null ] ] ] + ['data' => ['nest' => ['test' => null]]] ); // Contains reject $this->checkHandlesListOfNonNulls( function () { return [ - new Deferred(function() { + new Deferred(function () { return 1; }), - new Deferred(function() { + new Deferred(function () { throw new UserError('bad'); }), - new Deferred(function() { + new Deferred(function () { return 2; - }) + }), ]; }, [ - 'data' => ['nest' => ['test' => null]], + 'data' => ['nest' => ['test' => null]], 'errors' => [ [ - 'message' => 'bad', + 'message' => 'bad', 'locations' => [['line' => 1, 'column' => 10]], - 'path' => ['nest', 'test', 1] - ] - ] + 'path' => ['nest', 'test', 1], + ], + ], ] ); } @@ -458,22 +516,21 @@ class ListsTest extends TestCase { // Contains values $this->checkHandlesNonNullListOfNonNulls( - [ 1, 2 ], - [ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ] + [1, 2], + ['data' => ['nest' => ['test' => [1, 2]]]] ); - // Contains null $this->checkHandlesNonNullListOfNonNulls( - [ 1, null, 2 ], + [1, null, 2], [ - 'data' => [ 'nest' => null ], + 'data' => ['nest' => null], 'errors' => [ [ 'debugMessage' => 'Cannot return null for non-nullable field DataType.test.', - 'locations' => [['line' => 1, 'column' => 10 ]] - ] - ] + 'locations' => [['line' => 1, 'column' => 10]], + ], + ], ], true ); @@ -482,18 +539,24 @@ class ListsTest extends TestCase $this->checkHandlesNonNullListOfNonNulls( null, [ - 'data' => [ 'nest' => null ], + 'data' => ['nest' => null], 'errors' => [ [ 'debugMessage' => 'Cannot return null for non-nullable field DataType.test.', - 'locations' => [ ['line' => 1, 'column' => 10] ] - ] - ] + 'locations' => [['line' => 1, 'column' => 10]], + ], + ], ], true ); } + public function checkHandlesNonNullListOfNonNulls($testData, $expected, $debug = false) + { + $testType = Type::nonNull(Type::listOf(Type::nonNull(Type::int()))); + $this->check($testType, $testData, $expected, $debug); + } + /** * [T!]! */ @@ -501,42 +564,42 @@ class ListsTest extends TestCase { // Contains values $this->checkHandlesNonNullListOfNonNulls( - new Deferred(function() { + new Deferred(function () { return [1, 2]; }), - [ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ] + ['data' => ['nest' => ['test' => [1, 2]]]] ); // Contains null $this->checkHandlesNonNullListOfNonNulls( - new Deferred(function() { + new Deferred(function () { return [1, null, 2]; }), [ - 'data' => [ 'nest' => null ], + 'data' => ['nest' => null], 'errors' => [ [ 'debugMessage' => 'Cannot return null for non-nullable field DataType.test.', - 'locations' => [ ['line' => 1, 'column' => 10] ] - ] - ] + 'locations' => [['line' => 1, 'column' => 10]], + ], + ], ], true ); // Returns null $this->checkHandlesNonNullListOfNonNulls( - new Deferred(function() { + new Deferred(function () { return null; }), [ - 'data' => [ 'nest' => null ], + 'data' => ['nest' => null], 'errors' => [ [ 'debugMessage' => 'Cannot return null for non-nullable field DataType.test.', - 'locations' => [ ['line' => 1, 'column' => 10] ] - ] - ] + 'locations' => [['line' => 1, 'column' => 10]], + ], + ], ], true ); @@ -544,19 +607,19 @@ class ListsTest extends TestCase // Rejected $this->checkHandlesNonNullListOfNonNulls( function () { - return new Deferred(function() { + return new Deferred(function () { throw new UserError('bad'); }); }, [ - 'data' => ['nest' => null ], + 'data' => ['nest' => null], 'errors' => [ [ - 'message' => 'bad', + 'message' => 'bad', 'locations' => [['line' => 1, 'column' => 10]], - 'path' => ['nest', 'test'] - ] - ] + 'path' => ['nest', 'test'], + ], + ], ] ); } @@ -569,38 +632,38 @@ class ListsTest extends TestCase // Contains values $this->checkHandlesNonNullListOfNonNulls( [ - new Deferred(function() { + new Deferred(function () { return 1; }), - new Deferred(function() { + new Deferred(function () { return 2; - }) + }), ], - [ 'data' => [ 'nest' => [ 'test' => [ 1, 2 ] ] ] ] + ['data' => ['nest' => ['test' => [1, 2]]]] ); // Contains null $this->checkHandlesNonNullListOfNonNulls( [ - new Deferred(function() { + new Deferred(function () { return 1; }), - new Deferred(function() { + new Deferred(function () { return null; }), - new Deferred(function() { + new Deferred(function () { return 2; - }) + }), ], [ - 'data' => [ 'nest' => null ], + 'data' => ['nest' => null], 'errors' => [ [ 'debugMessage' => 'Cannot return null for non-nullable field DataType.test.', - 'locations' => [['line' => 1, 'column' => 10]] - ] - ] + 'locations' => [['line' => 1, 'column' => 10]], + ], + ], ], true ); @@ -609,83 +672,27 @@ class ListsTest extends TestCase $this->checkHandlesNonNullListOfNonNulls( function () { return [ - new Deferred(function() { + new Deferred(function () { return 1; }), - new Deferred(function() { + new Deferred(function () { throw new UserError('bad'); }), - new Deferred(function() { + new Deferred(function () { return 2; - }) + }), ]; }, [ - 'data' => ['nest' => null ], + 'data' => ['nest' => null], 'errors' => [ [ - 'message' => 'bad', + 'message' => 'bad', 'locations' => [['line' => 1, 'column' => 10]], - 'path' => ['nest', 'test'] - ] - ] + 'path' => ['nest', 'test'], + ], + ], ] ); } - - private function checkHandlesNullableLists($testData, $expected) - { - $testType = Type::listOf(Type::int()); - $this->check($testType, $testData, $expected); - } - - private function checkHandlesNonNullableLists($testData, $expected, $debug = false) - { - $testType = Type::nonNull(Type::listOf(Type::int())); - $this->check($testType, $testData, $expected, $debug); - } - - private function checkHandlesListOfNonNulls($testData, $expected, $debug = false) - { - $testType = Type::listOf(Type::nonNull(Type::int())); - $this->check($testType, $testData, $expected, $debug); - } - - public function checkHandlesNonNullListOfNonNulls($testData, $expected, $debug = false) - { - $testType = Type::nonNull(Type::listOf(Type::nonNull(Type::int()))); - $this->check($testType, $testData, $expected, $debug); - } - - private function check($testType, $testData, $expected, $debug = false) - { - $data = ['test' => $testData]; - $dataType = null; - - $dataType = new ObjectType([ - 'name' => 'DataType', - 'fields' => function () use (&$testType, &$dataType, $data) { - return [ - 'test' => [ - 'type' => $testType - ], - 'nest' => [ - 'type' => $dataType, - 'resolve' => function () use ($data) { - return $data; - } - ] - ]; - } - ]); - - $schema = new Schema([ - 'query' => $dataType - ]); - - $ast = Parser::parse('{ nest { test } }'); - - $result = Executor::execute($schema, $ast, $data); - $this->assertArraySubset($expected, $result->toArray($debug)); - } } diff --git a/tests/Executor/MutationsTest.php b/tests/Executor/MutationsTest.php index bc83b5d..157de57 100644 --- a/tests/Executor/MutationsTest.php +++ b/tests/Executor/MutationsTest.php @@ -1,27 +1,26 @@ schema(), $ast, new Root(6)); - $expected = [ + $expected = [ 'data' => [ - 'first' => [ - 'theNumber' => 1 - ], - 'second' => [ - 'theNumber' => 2 - ], - 'third' => [ - 'theNumber' => 3 - ], - 'fourth' => [ - 'theNumber' => 4 - ], - 'fifth' => [ - 'theNumber' => 5 - ] - ] + 'first' => ['theNumber' => 1], + 'second' => ['theNumber' => 2], + 'third' => ['theNumber' => 3], + 'fourth' => ['theNumber' => 4], + 'fifth' => ['theNumber' => 5], + ], ]; $this->assertEquals($expected, $mutationResult->toArray()); } + private function schema() : Schema + { + $numberHolderType = new ObjectType([ + 'fields' => [ + 'theNumber' => ['type' => Type::int()], + ], + 'name' => 'NumberHolder', + ]); + $schema = new Schema([ + 'query' => new ObjectType([ + 'fields' => [ + 'numberHolder' => ['type' => $numberHolderType], + ], + 'name' => 'Query', + ]), + 'mutation' => new ObjectType([ + 'fields' => [ + 'immediatelyChangeTheNumber' => [ + 'type' => $numberHolderType, + 'args' => ['newNumber' => ['type' => Type::int()]], + 'resolve' => function (Root $obj, $args) { + return $obj->immediatelyChangeTheNumber($args['newNumber']); + }, + ], + 'promiseToChangeTheNumber' => [ + 'type' => $numberHolderType, + 'args' => ['newNumber' => ['type' => Type::int()]], + 'resolve' => function (Root $obj, $args) { + return $obj->promiseToChangeTheNumber($args['newNumber']); + }, + ], + 'failToChangeTheNumber' => [ + 'type' => $numberHolderType, + 'args' => ['newNumber' => ['type' => Type::int()]], + 'resolve' => function (Root $obj, $args) { + $obj->failToChangeTheNumber(); + }, + ], + 'promiseAndFailToChangeTheNumber' => [ + 'type' => $numberHolderType, + 'args' => ['newNumber' => ['type' => Type::int()]], + 'resolve' => function (Root $obj, $args) { + return $obj->promiseAndFailToChangeTheNumber(); + }, + ], + ], + 'name' => 'Mutation', + ]), + ]); + + return $schema; + } + /** * @see it('evaluates mutations correctly in the presense of a failed mutation') */ public function testEvaluatesMutationsCorrectlyInThePresenseOfAFailedMutation() : void { - $doc = 'mutation M { + $doc = 'mutation M { first: immediatelyChangeTheNumber(newNumber: 1) { theNumber }, @@ -87,147 +129,28 @@ class MutationsTest extends TestCase theNumber } }'; - $ast = Parser::parse($doc); + $ast = Parser::parse($doc); $mutationResult = Executor::execute($this->schema(), $ast, new Root(6)); - $expected = [ - 'data' => [ - 'first' => [ - 'theNumber' => 1 - ], - 'second' => [ - 'theNumber' => 2 - ], - 'third' => null, - 'fourth' => [ - 'theNumber' => 4 - ], - 'fifth' => [ - 'theNumber' => 5 - ], - 'sixth' => null, + $expected = [ + 'data' => [ + 'first' => ['theNumber' => 1], + 'second' => ['theNumber' => 2], + 'third' => null, + 'fourth' => ['theNumber' => 4], + 'fifth' => ['theNumber' => 5], + 'sixth' => null, ], 'errors' => [ [ 'debugMessage' => 'Cannot change the number', - 'locations' => [['line' => 8, 'column' => 7]] + 'locations' => [['line' => 8, 'column' => 7]], ], [ 'debugMessage' => 'Cannot change the number', - 'locations' => [['line' => 17, 'column' => 7]] - ] - ] + 'locations' => [['line' => 17, 'column' => 7]], + ], + ], ]; $this->assertArraySubset($expected, $mutationResult->toArray(true)); } - - private function schema() - { - $numberHolderType = new ObjectType([ - 'fields' => [ - 'theNumber' => ['type' => Type::int()], - ], - 'name' => 'NumberHolder', - ]); - $schema = new Schema([ - 'query' => new ObjectType([ - 'fields' => [ - 'numberHolder' => ['type' => $numberHolderType], - ], - 'name' => 'Query', - ]), - 'mutation' => new ObjectType([ - 'fields' => [ - 'immediatelyChangeTheNumber' => [ - 'type' => $numberHolderType, - 'args' => ['newNumber' => ['type' => Type::int()]], - 'resolve' => (function (Root $obj, $args) { - return $obj->immediatelyChangeTheNumber($args['newNumber']); - }) - ], - 'promiseToChangeTheNumber' => [ - 'type' => $numberHolderType, - 'args' => ['newNumber' => ['type' => Type::int()]], - 'resolve' => (function (Root $obj, $args) { - return $obj->promiseToChangeTheNumber($args['newNumber']); - }) - ], - 'failToChangeTheNumber' => [ - 'type' => $numberHolderType, - 'args' => ['newNumber' => ['type' => Type::int()]], - 'resolve' => (function (Root $obj, $args) { - return $obj->failToChangeTheNumber($args['newNumber']); - }) - ], - 'promiseAndFailToChangeTheNumber' => [ - 'type' => $numberHolderType, - 'args' => ['newNumber' => ['type' => Type::int()]], - 'resolve' => (function (Root $obj, $args) { - return $obj->promiseAndFailToChangeTheNumber($args['newNumber']); - }) - ] - ], - 'name' => 'Mutation', - ]) - ]); - return $schema; - } -} - -class NumberHolder -{ - public $theNumber; - - public function __construct($originalNumber) - { - $this->theNumber = $originalNumber; - } -} - -class Root { - public $numberHolder; - - public function __construct($originalNumber) - { - $this->numberHolder = new NumberHolder($originalNumber); - } - - /** - * @param $newNumber - * @return NumberHolder - */ - public function immediatelyChangeTheNumber($newNumber) - { - $this->numberHolder->theNumber = $newNumber; - return $this->numberHolder; - } - - /** - * @param $newNumber - * - * @return Deferred - */ - public function promiseToChangeTheNumber($newNumber) - { - return new Deferred(function () use ($newNumber) { - return $this->immediatelyChangeTheNumber($newNumber); - }); - } - - /** - * @throws \Exception - */ - public function failToChangeTheNumber() - { - throw new \Exception('Cannot change the number'); - } - - /** - * @return Deferred - */ - public function promiseAndFailToChangeTheNumber() - { - return new Deferred(function () { - throw new \Exception("Cannot change the number"); - }); - } } diff --git a/tests/Executor/NonNullTest.php b/tests/Executor/NonNullTest.php index 2d2a528..5727d5c 100644 --- a/tests/Executor/NonNullTest.php +++ b/tests/Executor/NonNullTest.php @@ -1,16 +1,18 @@ syncError = new UserError('sync'); - $this->syncNonNullError = new UserError('syncNonNull'); - $this->promiseError = new UserError('promise'); + $this->syncError = new UserError('sync'); + $this->syncNonNullError = new UserError('syncNonNull'); + $this->promiseError = new UserError('promise'); $this->promiseNonNullError = new UserError('promiseNonNull'); $this->throwingData = [ - 'sync' => function () { + 'sync' => function () { throw $this->syncError; }, - 'syncNonNull' => function () { + 'syncNonNull' => function () { throw $this->syncNonNullError; }, - 'promise' => function () { + 'promise' => function () { return new Deferred(function () { throw $this->promiseError; }); }, - 'promiseNonNull' => function () { + 'promiseNonNull' => function () { return new Deferred(function () { throw $this->promiseNonNullError; }); }, - 'syncNest' => function () { + 'syncNest' => function () { return $this->throwingData; }, - 'syncNonNullNest' => function () { + 'syncNonNullNest' => function () { return $this->throwingData; }, - 'promiseNest' => function () { + 'promiseNest' => function () { return new Deferred(function () { return $this->throwingData; }); @@ -74,29 +81,29 @@ class NonNullTest extends TestCase ]; $this->nullingData = [ - 'sync' => function () { + 'sync' => function () { return null; }, - 'syncNonNull' => function () { + 'syncNonNull' => function () { return null; }, - 'promise' => function () { + 'promise' => function () { return new Deferred(function () { return null; }); }, - 'promiseNonNull' => function () { + 'promiseNonNull' => function () { return new Deferred(function () { return null; }); }, - 'syncNest' => function () { + 'syncNest' => function () { return $this->nullingData; }, - 'syncNonNullNest' => function () { + 'syncNonNullNest' => function () { return $this->nullingData; }, - 'promiseNest' => function () { + 'promiseNest' => function () { return new Deferred(function () { return $this->nullingData; }); @@ -109,19 +116,19 @@ class NonNullTest extends TestCase ]; $dataType = new ObjectType([ - 'name' => 'DataType', - 'fields' => function() use (&$dataType) { + 'name' => 'DataType', + 'fields' => function () use (&$dataType) { return [ - 'sync' => ['type' => Type::string()], - 'syncNonNull' => ['type' => Type::nonNull(Type::string())], - 'promise' => Type::string(), - 'promiseNonNull' => Type::nonNull(Type::string()), - 'syncNest' => $dataType, - 'syncNonNullNest' => Type::nonNull($dataType), - 'promiseNest' => $dataType, + 'sync' => ['type' => Type::string()], + 'syncNonNull' => ['type' => Type::nonNull(Type::string())], + 'promise' => Type::string(), + 'promiseNonNull' => Type::nonNull(Type::string()), + 'syncNest' => $dataType, + 'syncNonNullNest' => Type::nonNull($dataType), + 'promiseNest' => $dataType, 'promiseNonNullNest' => Type::nonNull($dataType), ]; - } + }, ]); $this->schema = new Schema(['query' => $dataType]); @@ -143,17 +150,18 @@ class NonNullTest extends TestCase $ast = Parser::parse($doc); $expected = [ - 'data' => [ - 'sync' => null, - ], + 'data' => ['sync' => null], 'errors' => [ FormattedError::create( $this->syncError->getMessage(), [new SourceLocation(3, 9)] - ) - ] + ), + ], ]; - $this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()); + $this->assertArraySubset( + $expected, + Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray() + ); } public function testNullsANullableFieldThatThrowsInAPromise() : void @@ -167,18 +175,19 @@ class NonNullTest extends TestCase $ast = Parser::parse($doc); $expected = [ - 'data' => [ - 'promise' => null, - ], + 'data' => ['promise' => null], 'errors' => [ FormattedError::create( $this->promiseError->getMessage(), [new SourceLocation(3, 9)] - ) - ] + ), + ], ]; - $this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()); + $this->assertArraySubset( + $expected, + Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray() + ); } public function testNullsASynchronouslyReturnedObjectThatContainsANonNullableFieldThatThrowsSynchronously() : void @@ -195,14 +204,15 @@ class NonNullTest extends TestCase $ast = Parser::parse($doc); $expected = [ - 'data' => [ - 'syncNest' => null - ], + 'data' => ['syncNest' => null], 'errors' => [ - FormattedError::create($this->syncNonNullError->getMessage(), [new SourceLocation(4, 11)]) - ] + FormattedError::create($this->syncNonNullError->getMessage(), [new SourceLocation(4, 11)]), + ], ]; - $this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()); + $this->assertArraySubset( + $expected, + Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray() + ); } public function testNullsAsynchronouslyReturnedObjectThatContainsANonNullableFieldThatThrowsInAPromise() : void @@ -218,15 +228,16 @@ class NonNullTest extends TestCase $ast = Parser::parse($doc); $expected = [ - 'data' => [ - 'syncNest' => null - ], + 'data' => ['syncNest' => null], 'errors' => [ - FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(4, 11)]) - ] + FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(4, 11)]), + ], ]; - $this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()); + $this->assertArraySubset( + $expected, + Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray() + ); } public function testNullsAnObjectReturnedInAPromiseThatContainsANonNullableFieldThatThrowsSynchronously() : void @@ -242,15 +253,16 @@ class NonNullTest extends TestCase $ast = Parser::parse($doc); $expected = [ - 'data' => [ - 'promiseNest' => null - ], + 'data' => ['promiseNest' => null], 'errors' => [ - FormattedError::create($this->syncNonNullError->getMessage(), [new SourceLocation(4, 11)]) - ] + FormattedError::create($this->syncNonNullError->getMessage(), [new SourceLocation(4, 11)]), + ], ]; - $this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()); + $this->assertArraySubset( + $expected, + Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray() + ); } public function testNullsAnObjectReturnedInAPromiseThatContainsANonNullableFieldThatThrowsInAPromise() : void @@ -266,15 +278,16 @@ class NonNullTest extends TestCase $ast = Parser::parse($doc); $expected = [ - 'data' => [ - 'promiseNest' => null - ], + 'data' => ['promiseNest' => null], 'errors' => [ - FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(4, 11)]) - ] + FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(4, 11)]), + ], ]; - $this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()); + $this->assertArraySubset( + $expected, + Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray() + ); } /** @@ -314,28 +327,28 @@ class NonNullTest extends TestCase $ast = Parser::parse($doc); $expected = [ - 'data' => [ - 'syncNest' => [ - 'sync' => null, - 'promise' => null, - 'syncNest' => [ - 'sync' => null, + 'data' => [ + 'syncNest' => [ + 'sync' => null, + 'promise' => null, + 'syncNest' => [ + 'sync' => null, 'promise' => null, ], 'promiseNest' => [ - 'sync' => null, + 'sync' => null, 'promise' => null, ], ], 'promiseNest' => [ - 'sync' => null, - 'promise' => null, - 'syncNest' => [ - 'sync' => null, + 'sync' => null, + 'promise' => null, + 'syncNest' => [ + 'sync' => null, 'promise' => null, ], 'promiseNest' => [ - 'sync' => null, + 'sync' => null, 'promise' => null, ], ], @@ -353,10 +366,13 @@ class NonNullTest extends TestCase FormattedError::create($this->promiseError->getMessage(), [new SourceLocation(17, 11)]), FormattedError::create($this->promiseError->getMessage(), [new SourceLocation(20, 13)]), FormattedError::create($this->promiseError->getMessage(), [new SourceLocation(24, 13)]), - ] + ], ]; - $this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()); + $this->assertArraySubset( + $expected, + Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray() + ); } public function testNullsTheFirstNullableObjectAfterAFieldThrowsInALongChainOfFieldsThatAreNonNull() : void @@ -413,10 +429,10 @@ class NonNullTest extends TestCase $ast = Parser::parse($doc); $expected = [ - 'data' => [ - 'syncNest' => null, - 'promiseNest' => null, - 'anotherNest' => null, + 'data' => [ + 'syncNest' => null, + 'promiseNest' => null, + 'anotherNest' => null, 'anotherPromiseNest' => null, ], 'errors' => [ @@ -424,10 +440,13 @@ class NonNullTest extends TestCase FormattedError::create($this->syncNonNullError->getMessage(), [new SourceLocation(19, 19)]), FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(30, 19)]), FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(41, 19)]), - ] + ], ]; - $this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()); + $this->assertArraySubset( + $expected, + Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray() + ); } public function testNullsANullableFieldThatSynchronouslyReturnsNull() : void @@ -441,11 +460,12 @@ class NonNullTest extends TestCase $ast = Parser::parse($doc); $expected = [ - 'data' => [ - 'sync' => null, - ] + 'data' => ['sync' => null], ]; - $this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray()); + $this->assertEquals( + $expected, + Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray() + ); } public function testNullsANullableFieldThatReturnsNullInAPromise() : void @@ -459,12 +479,13 @@ class NonNullTest extends TestCase $ast = Parser::parse($doc); $expected = [ - 'data' => [ - 'promise' => null, - ] + 'data' => ['promise' => null], ]; - $this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray()); + $this->assertArraySubset( + $expected, + Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray() + ); } public function testNullsASynchronouslyReturnedObjectThatContainsANonNullableFieldThatReturnsNullSynchronously() : void @@ -480,17 +501,18 @@ class NonNullTest extends TestCase $ast = Parser::parse($doc); $expected = [ - 'data' => [ - 'syncNest' => null - ], + 'data' => ['syncNest' => null], 'errors' => [ [ 'debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.', - 'locations' => [['line' => 4, 'column' => 11]] - ] - ] + 'locations' => [['line' => 4, 'column' => 11]], + ], + ], ]; - $this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray(true)); + $this->assertArraySubset( + $expected, + Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray(true) + ); } public function testNullsASynchronouslyReturnedObjectThatContainsANonNullableFieldThatReturnsNullInAPromise() : void @@ -506,15 +528,13 @@ class NonNullTest extends TestCase $ast = Parser::parse($doc); $expected = [ - 'data' => [ - 'syncNest' => null, - ], + 'data' => ['syncNest' => null], 'errors' => [ [ 'debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.', - 'locations' => [['line' => 4, 'column' => 11]] + 'locations' => [['line' => 4, 'column' => 11]], ], - ] + ], ]; $this->assertArraySubset( @@ -536,15 +556,13 @@ class NonNullTest extends TestCase $ast = Parser::parse($doc); $expected = [ - 'data' => [ - 'promiseNest' => null, - ], + 'data' => ['promiseNest' => null], 'errors' => [ [ 'debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.', - 'locations' => [['line' => 4, 'column' => 11]] + 'locations' => [['line' => 4, 'column' => 11]], ], - ] + ], ]; $this->assertArraySubset( @@ -566,15 +584,13 @@ class NonNullTest extends TestCase $ast = Parser::parse($doc); $expected = [ - 'data' => [ - 'promiseNest' => null, - ], + 'data' => ['promiseNest' => null], 'errors' => [ [ 'debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.', - 'locations' => [['line' => 4, 'column' => 11]] + 'locations' => [['line' => 4, 'column' => 11]], ], - ] + ], ]; $this->assertArraySubset( @@ -618,31 +634,31 @@ class NonNullTest extends TestCase $expected = [ 'data' => [ - 'syncNest' => [ - 'sync' => null, - 'promise' => null, - 'syncNest' => [ - 'sync' => null, + 'syncNest' => [ + 'sync' => null, + 'promise' => null, + 'syncNest' => [ + 'sync' => null, 'promise' => null, ], 'promiseNest' => [ - 'sync' => null, + 'sync' => null, 'promise' => null, - ] + ], ], 'promiseNest' => [ - 'sync' => null, - 'promise' => null, - 'syncNest' => [ - 'sync' => null, + 'sync' => null, + 'promise' => null, + 'syncNest' => [ + 'sync' => null, 'promise' => null, ], 'promiseNest' => [ - 'sync' => null, + 'sync' => null, 'promise' => null, - ] - ] - ] + ], + ], + ], ]; $actual = Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray(); @@ -703,18 +719,18 @@ class NonNullTest extends TestCase $ast = Parser::parse($doc); $expected = [ - 'data' => [ - 'syncNest' => null, - 'promiseNest' => null, - 'anotherNest' => null, + 'data' => [ + 'syncNest' => null, + 'promiseNest' => null, + 'anotherNest' => null, 'anotherPromiseNest' => null, ], 'errors' => [ - ['debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.', 'locations' => [ ['line' => 8, 'column' => 19]]], - ['debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.', 'locations' => [ ['line' => 19, 'column' => 19]]], - ['debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.', 'locations' => [ ['line' => 30, 'column' => 19]]], - ['debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.', 'locations' => [ ['line' => 41, 'column' => 19]]], - ] + ['debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.', 'locations' => [['line' => 8, 'column' => 19]]], + ['debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.', 'locations' => [['line' => 19, 'column' => 19]]], + ['debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.', 'locations' => [['line' => 30, 'column' => 19]]], + ['debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.', 'locations' => [['line' => 41, 'column' => 19]]], + ], ]; $this->assertArraySubset( @@ -734,10 +750,10 @@ class NonNullTest extends TestCase $expected = [ 'errors' => [ - FormattedError::create($this->syncNonNullError->getMessage(), [new SourceLocation(2, 17)]) - ] + FormattedError::create($this->syncNonNullError->getMessage(), [new SourceLocation(2, 17)]), + ], ]; - $actual = Executor::execute($this->schema, Parser::parse($doc), $this->throwingData)->toArray(); + $actual = Executor::execute($this->schema, Parser::parse($doc), $this->throwingData)->toArray(); $this->assertArraySubset($expected, $actual); } @@ -752,10 +768,13 @@ class NonNullTest extends TestCase $expected = [ 'errors' => [ FormattedError::create($this->promiseNonNullError->getMessage(), [new SourceLocation(2, 17)]), - ] + ], ]; - $this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray()); + $this->assertArraySubset( + $expected, + Executor::execute($this->schema, $ast, $this->throwingData, null, [], 'Q')->toArray() + ); } public function testNullsTheTopLevelIfSyncNonNullableFieldReturnsNull() : void @@ -769,9 +788,9 @@ class NonNullTest extends TestCase 'errors' => [ [ 'debugMessage' => 'Cannot return null for non-nullable field DataType.syncNonNull.', - 'locations' => [['line' => 2, 'column' => 17]] + 'locations' => [['line' => 2, 'column' => 17]], ], - ] + ], ]; $this->assertArraySubset( $expected, @@ -791,9 +810,9 @@ class NonNullTest extends TestCase 'errors' => [ [ 'debugMessage' => 'Cannot return null for non-nullable field DataType.promiseNonNull.', - 'locations' => [['line' => 2, 'column' => 17]] + 'locations' => [['line' => 2, 'column' => 17]], ], - ] + ], ]; $this->assertArraySubset( diff --git a/tests/Executor/Promise/ReactPromiseAdapterTest.php b/tests/Executor/Promise/ReactPromiseAdapterTest.php index 6859aeb..e007ae3 100644 --- a/tests/Executor/Promise/ReactPromiseAdapterTest.php +++ b/tests/Executor/Promise/ReactPromiseAdapterTest.php @@ -1,17 +1,17 @@ markTestSkipped('react/promise package must be installed to run GraphQL\Tests\Executor\Promise\ReactPromiseAdapterTest'); + if (class_exists('React\Promise\Promise')) { + return; } + + $this->markTestSkipped('react/promise package must be installed to run GraphQL\Tests\Executor\Promise\ReactPromiseAdapterTest'); } public function testIsThenableReturnsTrueWhenAReactPromiseIsGiven() : void { $reactAdapter = new ReactPromiseAdapter(); - $this->assertSame(true, $reactAdapter->isThenable(new ReactPromise(function() {}))); - $this->assertSame(true, $reactAdapter->isThenable(new FulfilledPromise())); - $this->assertSame(true, $reactAdapter->isThenable(new RejectedPromise())); - $this->assertSame(true, $reactAdapter->isThenable(new LazyPromise(function() {}))); - $this->assertSame(false, $reactAdapter->isThenable(false)); - $this->assertSame(false, $reactAdapter->isThenable(true)); - $this->assertSame(false, $reactAdapter->isThenable(1)); - $this->assertSame(false, $reactAdapter->isThenable(0)); - $this->assertSame(false, $reactAdapter->isThenable('test')); - $this->assertSame(false, $reactAdapter->isThenable('')); - $this->assertSame(false, $reactAdapter->isThenable([])); - $this->assertSame(false, $reactAdapter->isThenable(new \stdClass())); + $this->assertTrue( + $reactAdapter->isThenable(new ReactPromise(function () { + })) + ); + $this->assertTrue($reactAdapter->isThenable(new FulfilledPromise())); + $this->assertTrue($reactAdapter->isThenable(new RejectedPromise())); + $this->assertTrue( + $reactAdapter->isThenable(new LazyPromise(function () { + })) + ); + $this->assertFalse($reactAdapter->isThenable(false)); + $this->assertFalse($reactAdapter->isThenable(true)); + $this->assertFalse($reactAdapter->isThenable(1)); + $this->assertFalse($reactAdapter->isThenable(0)); + $this->assertFalse($reactAdapter->isThenable('test')); + $this->assertFalse($reactAdapter->isThenable('')); + $this->assertFalse($reactAdapter->isThenable([])); + $this->assertFalse($reactAdapter->isThenable(new \stdClass())); } public function testConvertsReactPromisesToGraphQlOnes() : void @@ -58,13 +66,16 @@ class ReactPromiseAdapterTest extends TestCase { $reactAdapter = new ReactPromiseAdapter(); $reactPromise = new FulfilledPromise(1); - $promise = $reactAdapter->convertThenable($reactPromise); + $promise = $reactAdapter->convertThenable($reactPromise); $result = null; - $resultPromise = $reactAdapter->then($promise, function ($value) use (&$result) { - $result = $value; - }); + $resultPromise = $reactAdapter->then( + $promise, + function ($value) use (&$result) { + $result = $value; + } + ); $this->assertSame(1, $result); $this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $resultPromise); @@ -73,9 +84,9 @@ class ReactPromiseAdapterTest extends TestCase public function testCreate() : void { - $reactAdapter = new ReactPromiseAdapter(); + $reactAdapter = new ReactPromiseAdapter(); $resolvedPromise = $reactAdapter->create(function ($resolve) { - $resolve(1); + $resolve(1); }); $this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $resolvedPromise); @@ -84,7 +95,7 @@ class ReactPromiseAdapterTest extends TestCase $result = null; $resolvedPromise->then(function ($value) use (&$result) { - $result = $value; + $result = $value; }); $this->assertSame(1, $result); @@ -92,7 +103,7 @@ class ReactPromiseAdapterTest extends TestCase public function testCreateFulfilled() : void { - $reactAdapter = new ReactPromiseAdapter(); + $reactAdapter = new ReactPromiseAdapter(); $fulfilledPromise = $reactAdapter->createFulfilled(1); $this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $fulfilledPromise); @@ -109,7 +120,7 @@ class ReactPromiseAdapterTest extends TestCase public function testCreateRejected() : void { - $reactAdapter = new ReactPromiseAdapter(); + $reactAdapter = new ReactPromiseAdapter(); $rejectedPromise = $reactAdapter->createRejected(new \Exception('I am a bad promise')); $this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $rejectedPromise); @@ -117,9 +128,12 @@ class ReactPromiseAdapterTest extends TestCase $exception = null; - $rejectedPromise->then(null, function ($error) use (&$exception) { - $exception = $error; - }); + $rejectedPromise->then( + null, + function ($error) use (&$exception) { + $exception = $error; + } + ); $this->assertInstanceOf('\Exception', $exception); $this->assertEquals('I am a bad promise', $exception->getMessage()); @@ -128,7 +142,7 @@ class ReactPromiseAdapterTest extends TestCase public function testAll() : void { $reactAdapter = new ReactPromiseAdapter(); - $promises = [new FulfilledPromise(1), new FulfilledPromise(2), new FulfilledPromise(3)]; + $promises = [new FulfilledPromise(1), new FulfilledPromise(2), new FulfilledPromise(3)]; $allPromise = $reactAdapter->all($promises); @@ -138,7 +152,7 @@ class ReactPromiseAdapterTest extends TestCase $result = null; $allPromise->then(function ($values) use (&$result) { - $result = $values; + $result = $values; }); $this->assertSame([1, 2, 3], $result); @@ -147,9 +161,9 @@ class ReactPromiseAdapterTest extends TestCase public function testAllShouldPreserveTheOrderOfTheArrayWhenResolvingAsyncPromises() : void { $reactAdapter = new ReactPromiseAdapter(); - $deferred = new Deferred(); - $promises = [new FulfilledPromise(1), $deferred->promise(), new FulfilledPromise(3)]; - $result = null; + $deferred = new Deferred(); + $promises = [new FulfilledPromise(1), $deferred->promise(), new FulfilledPromise(3)]; + $result = null; $reactAdapter->all($promises)->then(function ($values) use (&$result) { $result = $values; diff --git a/tests/Executor/Promise/SyncPromiseAdapterTest.php b/tests/Executor/Promise/SyncPromiseAdapterTest.php index bbe838b..edb0682 100644 --- a/tests/Executor/Promise/SyncPromiseAdapterTest.php +++ b/tests/Executor/Promise/SyncPromiseAdapterTest.php @@ -1,4 +1,7 @@ assertEquals(true, $this->promises->isThenable(new Deferred(function() {}))); + $this->assertEquals( + true, + $this->promises->isThenable(new Deferred(function () { + })) + ); $this->assertEquals(false, $this->promises->isThenable(false)); $this->assertEquals(false, $this->promises->isThenable(true)); $this->assertEquals(false, $this->promises->isThenable(1)); @@ -35,7 +40,8 @@ class SyncPromiseAdapterTest extends TestCase public function testConvert() : void { - $dfd = new Deferred(function() {}); + $dfd = new Deferred(function () { + }); $result = $this->promises->convertThenable($dfd); $this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $result); @@ -48,7 +54,8 @@ class SyncPromiseAdapterTest extends TestCase public function testThen() : void { - $dfd = new Deferred(function() {}); + $dfd = new Deferred(function () { + }); $promise = $this->promises->convertThenable($dfd); $result = $this->promises->then($promise); @@ -59,18 +66,55 @@ class SyncPromiseAdapterTest extends TestCase public function testCreatePromise() : void { - $promise = $this->promises->create(function($resolve, $reject) {}); + $promise = $this->promises->create(function ($resolve, $reject) { + }); $this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $promise); $this->assertInstanceOf('GraphQL\Executor\Promise\Adapter\SyncPromise', $promise->adoptedPromise); - $promise = $this->promises->create(function($resolve, $reject) { + $promise = $this->promises->create(function ($resolve, $reject) { $resolve('A'); }); $this->assertValidPromise($promise, null, 'A', SyncPromise::FULFILLED); } + private function assertValidPromise($promise, $expectedNextReason, $expectedNextValue, $expectedNextState) + { + $this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $promise); + $this->assertInstanceOf('GraphQL\Executor\Promise\Adapter\SyncPromise', $promise->adoptedPromise); + + $actualNextValue = null; + $actualNextReason = null; + $onFulfilledCalled = false; + $onRejectedCalled = false; + + $promise->then( + function ($nextValue) use (&$actualNextValue, &$onFulfilledCalled) { + $onFulfilledCalled = true; + $actualNextValue = $nextValue; + }, + function (\Throwable $reason) use (&$actualNextReason, &$onRejectedCalled) { + $onRejectedCalled = true; + $actualNextReason = $reason->getMessage(); + } + ); + + $this->assertSame($onFulfilledCalled, false); + $this->assertSame($onRejectedCalled, false); + + SyncPromise::runQueue(); + + if ($expectedNextState !== SyncPromise::PENDING) { + $this->assertSame(! $expectedNextReason, $onFulfilledCalled); + $this->assertSame(! ! $expectedNextReason, $onRejectedCalled); + } + + $this->assertSame($expectedNextValue, $actualNextValue); + $this->assertSame($expectedNextReason, $actualNextReason); + $this->assertSame($expectedNextState, $promise->adoptedPromise->state); + } + public function testCreateFulfilledPromise() : void { $promise = $this->promises->createFulfilled('test'); @@ -94,8 +138,8 @@ class SyncPromiseAdapterTest extends TestCase $promise1 = new SyncPromise(); $promise2 = new SyncPromise(); $promise3 = $promise2->then( - function($value) { - return $value .'-value3'; + function ($value) { + return $value . '-value3'; } ); @@ -105,7 +149,7 @@ class SyncPromiseAdapterTest extends TestCase new Promise($promise2, $this->promises), 3, new Promise($promise3, $this->promises), - [] + [], ]; $promise = $this->promises->all($data); @@ -114,36 +158,46 @@ class SyncPromiseAdapterTest extends TestCase $promise1->resolve('value1'); $this->assertValidPromise($promise, null, null, SyncPromise::PENDING); $promise2->resolve('value2'); - $this->assertValidPromise($promise, null, ['1', 'value1', 'value2', 3, 'value2-value3', []], SyncPromise::FULFILLED); + $this->assertValidPromise( + $promise, + null, + ['1', 'value1', 'value2', 3, 'value2-value3', []], + SyncPromise::FULFILLED + ); } public function testWait() : void { $called = []; - $deferred1 = new Deferred(function() use (&$called) { + $deferred1 = new Deferred(function () use (&$called) { $called[] = 1; + return 1; }); - $deferred2 = new Deferred(function() use (&$called) { + $deferred2 = new Deferred(function () use (&$called) { $called[] = 2; + return 2; }); $p1 = $this->promises->convertThenable($deferred1); $p2 = $this->promises->convertThenable($deferred2); - $p3 = $p2->then(function() use (&$called) { - $dfd = new Deferred(function() use (&$called) { + $p3 = $p2->then(function () use (&$called) { + $dfd = new Deferred(function () use (&$called) { $called[] = 3; + return 3; }); + return $this->promises->convertThenable($dfd); }); - $p4 = $p3->then(function() use (&$called) { - return new Deferred(function() use (&$called) { + $p4 = $p3->then(function () use (&$called) { + return new Deferred(function () use (&$called) { $called[] = 4; + return 4; }); }); @@ -156,46 +210,10 @@ class SyncPromiseAdapterTest extends TestCase $this->assertEquals(SyncPromise::PENDING, $all->adoptedPromise->state); $this->assertEquals([1, 2], $called); - $expectedResult = [0,1,2,3,4]; - $result = $this->promises->wait($all); + $expectedResult = [0, 1, 2, 3, 4]; + $result = $this->promises->wait($all); $this->assertEquals($expectedResult, $result); $this->assertEquals([1, 2, 3, 4], $called); - $this->assertValidPromise($all, null, [0,1,2,3,4], SyncPromise::FULFILLED); - } - - private function assertValidPromise($promise, $expectedNextReason, $expectedNextValue, $expectedNextState) - { - $this->assertInstanceOf('GraphQL\Executor\Promise\Promise', $promise); - $this->assertInstanceOf('GraphQL\Executor\Promise\Adapter\SyncPromise', $promise->adoptedPromise); - - $actualNextValue = null; - $actualNextReason = null; - $onFulfilledCalled = false; - $onRejectedCalled = false; - - $promise->then( - function($nextValue) use (&$actualNextValue, &$onFulfilledCalled) { - $onFulfilledCalled = true; - $actualNextValue = $nextValue; - }, - function(\Exception $reason) use (&$actualNextReason, &$onRejectedCalled) { - $onRejectedCalled = true; - $actualNextReason = $reason->getMessage(); - } - ); - - $this->assertSame($onFulfilledCalled, false); - $this->assertSame($onRejectedCalled, false); - - SyncPromise::runQueue(); - - if ($expectedNextState !== SyncPromise::PENDING) { - $this->assertSame(!$expectedNextReason, $onFulfilledCalled); - $this->assertSame(!!$expectedNextReason, $onRejectedCalled); - } - - $this->assertSame($expectedNextValue, $actualNextValue); - $this->assertSame($expectedNextReason, $actualNextReason); - $this->assertSame($expectedNextState, $promise->adoptedPromise->state); + $this->assertValidPromise($all, null, [0, 1, 2, 3, 4], SyncPromise::FULFILLED); } } diff --git a/tests/Executor/Promise/SyncPromiseTest.php b/tests/Executor/Promise/SyncPromiseTest.php index 6c878aa..fefba45 100644 --- a/tests/Executor/Promise/SyncPromiseTest.php +++ b/tests/Executor/Promise/SyncPromiseTest.php @@ -1,25 +1,32 @@ assertEquals(SyncPromise::PENDING, $promise->state); @@ -63,8 +69,7 @@ class SyncPromiseTest extends TestCase $expectedNextValue, $expectedNextReason, $expectedNextState - ) - { + ) { $promise = new SyncPromise(); $this->assertEquals(SyncPromise::PENDING, $promise->state); @@ -85,21 +90,27 @@ class SyncPromiseTest extends TestCase $expectedNextValue, $expectedNextReason, $expectedNextState - ) - { + ) { $promise = new SyncPromise(); $this->assertEquals(SyncPromise::PENDING, $promise->state); $promise->resolve($resolvedValue); $this->assertEquals(SyncPromise::FULFILLED, $promise->state); - $nextPromise = $promise->then(null, function() {}); + $nextPromise = $promise->then( + null, + function () { + } + ); $this->assertSame($promise, $nextPromise); $onRejectedCalled = false; - $nextPromise = $promise->then($onFulfilled, function () use (&$onRejectedCalled) { - $onRejectedCalled = true; - }); + $nextPromise = $promise->then( + $onFulfilled, + function () use (&$onRejectedCalled) { + $onRejectedCalled = true; + } + ); if ($onFulfilled) { $this->assertNotSame($promise, $nextPromise); @@ -124,19 +135,57 @@ class SyncPromiseTest extends TestCase $this->assertValidPromise($nextPromise3, $expectedNextReason, $expectedNextValue, $expectedNextState); } + private function assertValidPromise( + SyncPromise $promise, + $expectedNextReason, + $expectedNextValue, + $expectedNextState + ) { + $actualNextValue = null; + $actualNextReason = null; + $onFulfilledCalled = false; + $onRejectedCalled = false; + + $promise->then( + function ($nextValue) use (&$actualNextValue, &$onFulfilledCalled) { + $onFulfilledCalled = true; + $actualNextValue = $nextValue; + }, + function (\Throwable $reason) use (&$actualNextReason, &$onRejectedCalled) { + $onRejectedCalled = true; + $actualNextReason = $reason->getMessage(); + } + ); + + $this->assertEquals($onFulfilledCalled, false); + $this->assertEquals($onRejectedCalled, false); + + SyncPromise::runQueue(); + + $this->assertEquals(! $expectedNextReason, $onFulfilledCalled); + $this->assertEquals(! ! $expectedNextReason, $onRejectedCalled); + + $this->assertEquals($expectedNextValue, $actualNextValue); + $this->assertEquals($expectedNextReason, $actualNextReason); + $this->assertEquals($expectedNextState, $promise->state); + } + public function getRejectedPromiseData() { - $onRejectedReturnsNull = function() { + $onRejectedReturnsNull = function () { return null; }; - $onRejectedReturnsSomeValue = function($reason) { + + $onRejectedReturnsSomeValue = function ($reason) { return 'some-value'; }; - $onRejectedThrowsSameReason = function($reason) { + + $onRejectedThrowsSameReason = function ($reason) { throw $reason; }; - $onRejectedThrowsOtherReason = function($value) { - throw new \Exception("onRejected throws other!"); + + $onRejectedThrowsOtherReason = function ($value) { + throw new \Exception('onRejected throws other!'); }; return [ @@ -158,8 +207,7 @@ class SyncPromiseTest extends TestCase $expectedNextValue, $expectedNextReason, $expectedNextState - ) - { + ) { $promise = new SyncPromise(); $this->assertEquals(SyncPromise::PENDING, $promise->state); @@ -169,7 +217,6 @@ class SyncPromiseTest extends TestCase $this->expectException(\Throwable::class); $this->expectExceptionMessage('Cannot change rejection reason'); $promise->reject(new \Exception('other-reason')); - } /** @@ -181,8 +228,7 @@ class SyncPromiseTest extends TestCase $expectedNextValue, $expectedNextReason, $expectedNextState - ) - { + ) { $promise = new SyncPromise(); $this->assertEquals(SyncPromise::PENDING, $promise->state); @@ -203,8 +249,7 @@ class SyncPromiseTest extends TestCase $expectedNextValue, $expectedNextReason, $expectedNextState - ) - { + ) { $promise = new SyncPromise(); $this->assertEquals(SyncPromise::PENDING, $promise->state); @@ -214,22 +259,26 @@ class SyncPromiseTest extends TestCase try { $promise->reject(new \Exception('other-reason')); $this->fail('Expected exception not thrown'); - } catch (\Exception $e) { + } catch (\Throwable $e) { $this->assertEquals('Cannot change rejection reason', $e->getMessage()); } try { $promise->resolve('anything'); $this->fail('Expected exception not thrown'); - } catch (\Exception $e) { + } catch (\Throwable $e) { $this->assertEquals('Cannot resolve rejected promise', $e->getMessage()); } - $nextPromise = $promise->then(function() {}, null); + $nextPromise = $promise->then( + function () { + }, + null + ); $this->assertSame($promise, $nextPromise); $onFulfilledCalled = false; - $nextPromise = $promise->then( + $nextPromise = $promise->then( function () use (&$onFulfilledCalled) { $onFulfilledCalled = true; }, @@ -266,7 +315,7 @@ class SyncPromiseTest extends TestCase try { $promise->resolve($promise); $this->fail('Expected exception not thrown'); - } catch (\Exception $e) { + } catch (\Throwable $e) { $this->assertEquals('Cannot resolve promise with self', $e->getMessage()); $this->assertEquals(SyncPromise::PENDING, $promise->state); } @@ -299,24 +348,25 @@ class SyncPromiseTest extends TestCase throw $e; } catch (\Throwable $e) { $this->assertEquals(SyncPromise::PENDING, $promise->state); - } catch (\Exception $e) { - $this->assertEquals(SyncPromise::PENDING, $promise->state); } - $promise->reject(new \Exception("Rejected Reason")); - $this->assertValidPromise($promise, "Rejected Reason", null, SyncPromise::REJECTED); + $promise->reject(new \Exception('Rejected Reason')); + $this->assertValidPromise($promise, 'Rejected Reason', null, SyncPromise::REJECTED); - $promise = new SyncPromise(); - $promise2 = $promise->then(null, function() { - return 'value'; - }); - $promise->reject(new \Exception("Rejected Again")); + $promise = new SyncPromise(); + $promise2 = $promise->then( + null, + function () { + return 'value'; + } + ); + $promise->reject(new \Exception('Rejected Again')); $this->assertValidPromise($promise2, null, 'value', SyncPromise::FULFILLED); - $promise = new SyncPromise(); + $promise = new SyncPromise(); $promise2 = $promise->then(); - $promise->reject(new \Exception("Rejected Once Again")); - $this->assertValidPromise($promise2, "Rejected Once Again", null, SyncPromise::REJECTED); + $promise->reject(new \Exception('Rejected Once Again')); + $this->assertValidPromise($promise2, 'Rejected Once Again', null, SyncPromise::REJECTED); } public function testPendingPromiseThen() : void @@ -331,12 +381,14 @@ class SyncPromiseTest extends TestCase // Make sure that it queues derivative promises until resolution: $onFulfilledCount = 0; - $onRejectedCount = 0; - $onFulfilled = function($value) use (&$onFulfilledCount) { + $onRejectedCount = 0; + $onFulfilled = function ($value) use (&$onFulfilledCount) { $onFulfilledCount++; + return $onFulfilledCount; }; - $onRejected = function($reason) use (&$onRejectedCount) { + + $onRejected = function ($reason) use (&$onRejectedCount) { $onRejectedCount++; throw $reason; }; @@ -367,35 +419,4 @@ class SyncPromiseTest extends TestCase $this->assertValidPromise($nextPromise3, null, 2, SyncPromise::FULFILLED); $this->assertValidPromise($nextPromise4, null, 3, SyncPromise::FULFILLED); } - - private function assertValidPromise(SyncPromise $promise, $expectedNextReason, $expectedNextValue, $expectedNextState) - { - $actualNextValue = null; - $actualNextReason = null; - $onFulfilledCalled = false; - $onRejectedCalled = false; - - $promise->then( - function($nextValue) use (&$actualNextValue, &$onFulfilledCalled) { - $onFulfilledCalled = true; - $actualNextValue = $nextValue; - }, - function(\Exception $reason) use (&$actualNextReason, &$onRejectedCalled) { - $onRejectedCalled = true; - $actualNextReason = $reason->getMessage(); - } - ); - - $this->assertEquals($onFulfilledCalled, false); - $this->assertEquals($onRejectedCalled, false); - - SyncPromise::runQueue(); - - $this->assertEquals(!$expectedNextReason, $onFulfilledCalled); - $this->assertEquals(!!$expectedNextReason, $onRejectedCalled); - - $this->assertEquals($expectedNextValue, $actualNextValue); - $this->assertEquals($expectedNextReason, $actualNextReason); - $this->assertEquals($expectedNextState, $promise->state); - } } diff --git a/tests/Executor/ResolveTest.php b/tests/Executor/ResolveTest.php index 0d05e53..f838695 100644 --- a/tests/Executor/ResolveTest.php +++ b/tests/Executor/ResolveTest.php @@ -1,30 +1,22 @@ new ObjectType([ - 'name' => 'Query', - 'fields' => [ - 'test' => $testField - ] - ]) - ]); - } - /** * @see it('default function accesses properties') */ @@ -32,9 +24,7 @@ class ResolveTest extends TestCase { $schema = $this->buildSchema(['type' => Type::string()]); - $source = [ - 'test' => 'testValue' - ]; + $source = ['test' => 'testValue']; $this->assertEquals( ['data' => ['test' => 'testValue']], @@ -42,18 +32,28 @@ class ResolveTest extends TestCase ); } + private function buildSchema($testField) + { + return new Schema([ + 'query' => new ObjectType([ + 'name' => 'Query', + 'fields' => ['test' => $testField], + ]), + ]); + } + /** * @see it('default function calls methods') */ public function testDefaultFunctionCallsClosures() : void { - $schema = $this->buildSchema(['type' => Type::string()]); + $schema = $this->buildSchema(['type' => Type::string()]); $_secret = 'secretValue' . uniqid(); $source = [ - 'test' => function() use ($_secret) { + 'test' => function () use ($_secret) { return $_secret; - } + }, ]; $this->assertEquals( ['data' => ['test' => $_secret]], @@ -69,7 +69,7 @@ class ResolveTest extends TestCase $schema = $this->buildSchema([ 'type' => Type::int(), 'args' => [ - 'addend1' => [ 'type' => Type::int() ], + 'addend1' => ['type' => Type::int()], ], ]); @@ -85,14 +85,14 @@ class ResolveTest extends TestCase public function testUsesProvidedResolveFunction() : void { $schema = $this->buildSchema([ - 'type' => Type::string(), - 'args' => [ + 'type' => Type::string(), + 'args' => [ 'aStr' => ['type' => Type::string()], 'aInt' => ['type' => Type::int()], ], 'resolve' => function ($source, $args) { return json_encode([$source, $args]); - } + }, ]); $this->assertEquals( diff --git a/tests/Executor/SyncTest.php b/tests/Executor/SyncTest.php index 3c1c989..a0ccbaa 100644 --- a/tests/Executor/SyncTest.php +++ b/tests/Executor/SyncTest.php @@ -1,8 +1,10 @@ schema = new Schema([ - 'query' => new ObjectType([ - 'name' => 'Query', + 'query' => new ObjectType([ + 'name' => 'Query', 'fields' => [ - 'syncField' => [ - 'type' => Type::string(), + 'syncField' => [ + 'type' => Type::string(), 'resolve' => function ($rootValue) { return $rootValue; - } + }, ], 'asyncField' => [ - 'type' => Type::string(), + 'type' => Type::string(), 'resolve' => function ($rootValue) { return new Deferred(function () use ($rootValue) { return $rootValue; }); - } - ] - ] + }, + ], + ], ]), 'mutation' => new ObjectType([ - 'name' => 'Mutation', + 'name' => 'Mutation', 'fields' => [ 'syncMutationField' => [ - 'type' => Type::string(), + 'type' => Type::string(), 'resolve' => function ($rootValue) { return $rootValue; - } - ] - ] - ]) + }, + ], + ], + ]), ]); + $this->promiseAdapter = new SyncPromiseAdapter(); } @@ -70,7 +73,7 @@ class SyncTest extends TestCase */ public function testDoesNotReturnAPromiseForInitialErrors() : void { - $doc = 'fragment Example on Query { syncField }'; + $doc = 'fragment Example on Query { syncField }'; $result = $this->execute( $this->schema, Parser::parse($doc), @@ -79,106 +82,6 @@ class SyncTest extends TestCase $this->assertSync(['errors' => [['message' => 'Must provide an operation.']]], $result); } - /** - * @see it('does not return a Promise if fields are all synchronous') - */ - public function testDoesNotReturnAPromiseIfFieldsAreAllSynchronous() : void - { - $doc = 'query Example { syncField }'; - $result = $this->execute( - $this->schema, - Parser::parse($doc), - 'rootValue' - ); - $this->assertSync(['data' => ['syncField' => 'rootValue']], $result); - } - - /** - * @see it('does not return a Promise if mutation fields are all synchronous') - */ - public function testDoesNotReturnAPromiseIfMutationFieldsAreAllSynchronous() : void - { - $doc = 'mutation Example { syncMutationField }'; - $result = $this->execute( - $this->schema, - Parser::parse($doc), - 'rootValue' - ); - $this->assertSync(['data' => ['syncMutationField' => 'rootValue']], $result); - } - - /** - * @see it('returns a Promise if any field is asynchronous') - */ - public function testReturnsAPromiseIfAnyFieldIsAsynchronous() : void - { - $doc = 'query Example { syncField, asyncField }'; - $result = $this->execute( - $this->schema, - Parser::parse($doc), - 'rootValue' - ); - $this->assertAsync(['data' => ['syncField' => 'rootValue', 'asyncField' => 'rootValue']], $result); - } - - // Describe: graphqlSync - - /** - * @see it('does not return a Promise for syntax errors') - */ - public function testDoesNotReturnAPromiseForSyntaxErrors() : void - { - $doc = 'fragment Example on Query { { { syncField }'; - $result = $this->graphqlSync( - $this->schema, - $doc - ); - $this->assertSync([ - 'errors' => [ - ['message' => 'Syntax Error: Expected Name, found {', - 'locations' => [['line' => 1, 'column' => 29]]] - ] - ], $result); - } - - /** - * @see it('does not return a Promise for validation errors') - */ - public function testDoesNotReturnAPromiseForValidationErrors() : void - { - $doc = 'fragment Example on Query { unknownField }'; - $validationErrors = DocumentValidator::validate($this->schema, Parser::parse($doc)); - $result = $this->graphqlSync( - $this->schema, - $doc - ); - $expected = [ - 'errors' => Utils::map($validationErrors, function ($e) { - return FormattedError::createFromException($e); - }) - ]; - $this->assertSync($expected, $result); - } - - /** - * @see it('does not return a Promise for sync execution') - */ - public function testDoesNotReturnAPromiseForSyncExecution() : void - { - $doc = 'query Example { syncField }'; - $result = $this->graphqlSync( - $this->schema, - $doc, - 'rootValue' - ); - $this->assertSync(['data' => ['syncField' => 'rootValue']], $result); - } - - private function graphqlSync($schema, $doc, $rootValue = null) - { - return GraphQL::promiseToExecute($this->promiseAdapter, $schema, $doc, $rootValue); - } - private function execute($schema, $doc, $rootValue = null) { return Executor::promiseToExecute($this->promiseAdapter, $schema, $doc, $rootValue); @@ -191,7 +94,56 @@ class SyncTest extends TestCase $this->assertInstanceOf(SyncPromise::class, $actualResult->adoptedPromise, $message); $this->assertEquals(SyncPromise::FULFILLED, $actualResult->adoptedPromise->state, $message); $this->assertInstanceOf(ExecutionResult::class, $actualResult->adoptedPromise->result, $message); - $this->assertArraySubset($expectedFinalArray, $actualResult->adoptedPromise->result->toArray(), $message); + $this->assertArraySubset( + $expectedFinalArray, + $actualResult->adoptedPromise->result->toArray(), + false, + $message + ); + } + + /** + * @see it('does not return a Promise if fields are all synchronous') + */ + public function testDoesNotReturnAPromiseIfFieldsAreAllSynchronous() : void + { + $doc = 'query Example { syncField }'; + $result = $this->execute( + $this->schema, + Parser::parse($doc), + 'rootValue' + ); + $this->assertSync(['data' => ['syncField' => 'rootValue']], $result); + } + + // Describe: graphqlSync + + /** + * @see it('does not return a Promise if mutation fields are all synchronous') + */ + public function testDoesNotReturnAPromiseIfMutationFieldsAreAllSynchronous() : void + { + $doc = 'mutation Example { syncMutationField }'; + $result = $this->execute( + $this->schema, + Parser::parse($doc), + 'rootValue' + ); + $this->assertSync(['data' => ['syncMutationField' => 'rootValue']], $result); + } + + /** + * @see it('returns a Promise if any field is asynchronous') + */ + public function testReturnsAPromiseIfAnyFieldIsAsynchronous() : void + { + $doc = 'query Example { syncField, asyncField }'; + $result = $this->execute( + $this->schema, + Parser::parse($doc), + 'rootValue' + ); + $this->assertAsync(['data' => ['syncField' => 'rootValue', 'asyncField' => 'rootValue']], $result); } private function assertAsync($expectedFinalArray, $actualResult) @@ -202,6 +154,70 @@ class SyncTest extends TestCase $this->assertEquals(SyncPromise::PENDING, $actualResult->adoptedPromise->state, $message); $resolvedResult = $this->promiseAdapter->wait($actualResult); $this->assertInstanceOf(ExecutionResult::class, $resolvedResult, $message); - $this->assertArraySubset($expectedFinalArray, $resolvedResult->toArray(), $message); + $this->assertArraySubset($expectedFinalArray, $resolvedResult->toArray(), false, $message); + } + + /** + * @see it('does not return a Promise for syntax errors') + */ + public function testDoesNotReturnAPromiseForSyntaxErrors() : void + { + $doc = 'fragment Example on Query { { { syncField }'; + $result = $this->graphqlSync( + $this->schema, + $doc + ); + $this->assertSync( + [ + 'errors' => [ + [ + 'message' => 'Syntax Error: Expected Name, found {', + 'locations' => [['line' => 1, 'column' => 29]], + ], + ], + ], + $result + ); + } + + private function graphqlSync($schema, $doc, $rootValue = null) + { + return GraphQL::promiseToExecute($this->promiseAdapter, $schema, $doc, $rootValue); + } + + /** + * @see it('does not return a Promise for validation errors') + */ + public function testDoesNotReturnAPromiseForValidationErrors() : void + { + $doc = 'fragment Example on Query { unknownField }'; + $validationErrors = DocumentValidator::validate($this->schema, Parser::parse($doc)); + $result = $this->graphqlSync( + $this->schema, + $doc + ); + $expected = [ + 'errors' => Utils::map( + $validationErrors, + function ($e) { + return FormattedError::createFromException($e); + } + ), + ]; + $this->assertSync($expected, $result); + } + + /** + * @see it('does not return a Promise for sync execution') + */ + public function testDoesNotReturnAPromiseForSyncExecution() : void + { + $doc = 'query Example { syncField }'; + $result = $this->graphqlSync( + $this->schema, + $doc, + 'rootValue' + ); + $this->assertSync(['data' => ['syncField' => 'rootValue']], $result); } } diff --git a/tests/Executor/TestClasses.php b/tests/Executor/TestClasses.php deleted file mode 100644 index 0ae6ca6..0000000 --- a/tests/Executor/TestClasses.php +++ /dev/null @@ -1,128 +0,0 @@ -name = $name; - $this->woofs = $woofs; - } -} - -class Cat -{ - function __construct($name, $meows) - { - $this->name = $name; - $this->meows = $meows; - } -} - -class Human -{ - function __construct($name) - { - $this->name = $name; - } -} - -class Person -{ - public $name; - public $pets; - public $friends; - - function __construct($name, $pets = null, $friends = null) - { - $this->name = $name; - $this->pets = $pets; - $this->friends = $friends; - } -} - -class ComplexScalar extends ScalarType -{ - public static function create() - { - return new self(); - } - - public $name = 'ComplexScalar'; - - /** - * {@inheritdoc} - */ - public function serialize($value) - { - if ($value === 'DeserializedValue') { - return 'SerializedValue'; - } - - throw new Error("Cannot serialize value as ComplexScalar: " . Utils::printSafe($value)); - } - - /** - * {@inheritdoc} - */ - public function parseValue($value) - { - if ($value === 'SerializedValue') { - return 'DeserializedValue'; - } - - throw new Error("Cannot represent value as ComplexScalar: " . Utils::printSafe($value)); - } - - /** - * {@inheritdoc} - */ - public function parseLiteral($valueNode, array $variables = null) - { - if ($valueNode->value === 'SerializedValue') { - return 'DeserializedValue'; - } - - throw new Error("Cannot represent literal as ComplexScalar: " . Utils::printSafe($valueNode->value)); - } -} - -class Special -{ - public $value; - - public function __construct($value) - { - $this->value = $value; - } -} - -class NotSpecial -{ - public $value; - - public function __construct($value) - { - $this->value = $value; - } -} - -class Adder -{ - public $num; - - public $test; - - public function __construct($num) - { - $this->num = $num; - - $this->test = function($source, $args, $context) { - return $this->num + $args['addend1'] + $context['addend2']; - }; - } -} diff --git a/tests/Executor/TestClasses/Adder.php b/tests/Executor/TestClasses/Adder.php new file mode 100644 index 0000000..b086b51 --- /dev/null +++ b/tests/Executor/TestClasses/Adder.php @@ -0,0 +1,23 @@ +num = $num; + + $this->test = function ($source, $args, $context) { + return $this->num + $args['addend1'] + $context['addend2']; + }; + } +} diff --git a/tests/Executor/TestClasses/Cat.php b/tests/Executor/TestClasses/Cat.php new file mode 100644 index 0000000..500e6a9 --- /dev/null +++ b/tests/Executor/TestClasses/Cat.php @@ -0,0 +1,20 @@ +name = $name; + $this->meows = $meows; + } +} diff --git a/tests/Executor/TestClasses/ComplexScalar.php b/tests/Executor/TestClasses/ComplexScalar.php new file mode 100644 index 0000000..91197a3 --- /dev/null +++ b/tests/Executor/TestClasses/ComplexScalar.php @@ -0,0 +1,56 @@ +value === 'SerializedValue') { + return 'DeserializedValue'; + } + + throw new Error('Cannot represent literal as ComplexScalar: ' . Utils::printSafe($valueNode->value)); + } +} diff --git a/tests/Executor/TestClasses/Dog.php b/tests/Executor/TestClasses/Dog.php new file mode 100644 index 0000000..46861c3 --- /dev/null +++ b/tests/Executor/TestClasses/Dog.php @@ -0,0 +1,20 @@ +name = $name; + $this->woofs = $woofs; + } +} diff --git a/tests/Executor/TestClasses/Human.php b/tests/Executor/TestClasses/Human.php new file mode 100644 index 0000000..b4ae7d8 --- /dev/null +++ b/tests/Executor/TestClasses/Human.php @@ -0,0 +1,16 @@ +name = $name; + } +} diff --git a/tests/Executor/TestClasses/NotSpecial.php b/tests/Executor/TestClasses/NotSpecial.php new file mode 100644 index 0000000..b54f991 --- /dev/null +++ b/tests/Executor/TestClasses/NotSpecial.php @@ -0,0 +1,19 @@ +value = $value; + } +} diff --git a/tests/Executor/TestClasses/NumberHolder.php b/tests/Executor/TestClasses/NumberHolder.php new file mode 100644 index 0000000..72d0072 --- /dev/null +++ b/tests/Executor/TestClasses/NumberHolder.php @@ -0,0 +1,16 @@ +theNumber = $originalNumber; + } +} diff --git a/tests/Executor/TestClasses/Person.php b/tests/Executor/TestClasses/Person.php new file mode 100644 index 0000000..210c625 --- /dev/null +++ b/tests/Executor/TestClasses/Person.php @@ -0,0 +1,28 @@ +name = $name; + $this->pets = $pets; + $this->friends = $friends; + } +} diff --git a/tests/Executor/TestClasses/Root.php b/tests/Executor/TestClasses/Root.php new file mode 100644 index 0000000..812db16 --- /dev/null +++ b/tests/Executor/TestClasses/Root.php @@ -0,0 +1,44 @@ +numberHolder = new NumberHolder($originalNumber); + } + + public function promiseToChangeTheNumber($newNumber) : Deferred + { + return new Deferred(function () use ($newNumber) { + return $this->immediatelyChangeTheNumber($newNumber); + }); + } + + public function immediatelyChangeTheNumber($newNumber) : NumberHolder + { + $this->numberHolder->theNumber = $newNumber; + + return $this->numberHolder; + } + + public function failToChangeTheNumber() : void + { + throw new \Exception('Cannot change the number'); + } + + public function promiseAndFailToChangeTheNumber() : Deferred + { + return new Deferred(function () { + $this->failToChangeTheNumber(); + }); + } +} diff --git a/tests/Executor/TestClasses/Special.php b/tests/Executor/TestClasses/Special.php new file mode 100644 index 0000000..e8293d0 --- /dev/null +++ b/tests/Executor/TestClasses/Special.php @@ -0,0 +1,19 @@ +value = $value; + } +} diff --git a/tests/Executor/UnionInterfaceTest.php b/tests/Executor/UnionInterfaceTest.php index 4d1d934..3e21692 100644 --- a/tests/Executor/UnionInterfaceTest.php +++ b/tests/Executor/UnionInterfaceTest.php @@ -1,64 +1,76 @@ 'Named', + 'name' => 'Named', 'fields' => [ - 'name' => ['type' => Type::string()] - ] + 'name' => ['type' => Type::string()], + ], ]); $DogType = new ObjectType([ - 'name' => 'Dog', + 'name' => 'Dog', 'interfaces' => [$NamedType], - 'fields' => [ - 'name' => ['type' => Type::string()], - 'woofs' => ['type' => Type::boolean()] + 'fields' => [ + 'name' => ['type' => Type::string()], + 'woofs' => ['type' => Type::boolean()], ], - 'isTypeOf' => function ($value) { + 'isTypeOf' => function ($value) { return $value instanceof Dog; - } + }, ]); $CatType = new ObjectType([ - 'name' => 'Cat', + 'name' => 'Cat', 'interfaces' => [$NamedType], - 'fields' => [ - 'name' => ['type' => Type::string()], - 'meows' => ['type' => Type::boolean()] + 'fields' => [ + 'name' => ['type' => Type::string()], + 'meows' => ['type' => Type::boolean()], ], - 'isTypeOf' => function ($value) { + 'isTypeOf' => function ($value) { return $value instanceof Cat; - } + }, ]); $PetType = new UnionType([ - 'name' => 'Pet', - 'types' => [$DogType, $CatType], + 'name' => 'Pet', + 'types' => [$DogType, $CatType], 'resolveType' => function ($value) use ($DogType, $CatType) { if ($value instanceof Dog) { return $DogType; @@ -66,32 +78,31 @@ class UnionInterfaceTest extends TestCase if ($value instanceof Cat) { return $CatType; } - } + }, ]); $PersonType = new ObjectType([ - 'name' => 'Person', + 'name' => 'Person', 'interfaces' => [$NamedType], - 'fields' => [ - 'name' => ['type' => Type::string()], - 'pets' => ['type' => Type::listOf($PetType)], - 'friends' => ['type' => Type::listOf($NamedType)] + 'fields' => [ + 'name' => ['type' => Type::string()], + 'pets' => ['type' => Type::listOf($PetType)], + 'friends' => ['type' => Type::listOf($NamedType)], ], - 'isTypeOf' => function ($value) { + 'isTypeOf' => function ($value) { return $value instanceof Person; - } + }, ]); $this->schema = new Schema([ 'query' => $PersonType, - 'types' => [ $PetType ] + 'types' => [$PetType], ]); $this->garfield = new Cat('Garfield', false); - $this->odie = new Dog('Odie', true); - $this->liz = new Person('Liz'); - $this->john = new Person('John', [$this->garfield, $this->odie], [$this->liz, $this->odie]); - + $this->odie = new Dog('Odie', true); + $this->liz = new Person('Liz'); + $this->john = new Person('John', [$this->garfield, $this->odie], [$this->liz, $this->odie]); } // Execute: Union and intersection types @@ -101,7 +112,6 @@ class UnionInterfaceTest extends TestCase */ public function testCanIntrospectOnUnionAndIntersectionTypes() : void { - $ast = Parser::parse(' { Named: __type(name: "Named") { @@ -128,33 +138,33 @@ class UnionInterfaceTest extends TestCase $expected = [ 'data' => [ 'Named' => [ - 'kind' => 'INTERFACE', - 'name' => 'Named', - 'fields' => [ - ['name' => 'name'] + 'kind' => 'INTERFACE', + 'name' => 'Named', + 'fields' => [ + ['name' => 'name'], ], - 'interfaces' => null, + 'interfaces' => null, 'possibleTypes' => [ ['name' => 'Person'], ['name' => 'Dog'], - ['name' => 'Cat'] + ['name' => 'Cat'], ], - 'enumValues' => null, - 'inputFields' => null + 'enumValues' => null, + 'inputFields' => null, ], - 'Pet' => [ - 'kind' => 'UNION', - 'name' => 'Pet', - 'fields' => null, - 'interfaces' => null, + 'Pet' => [ + 'kind' => 'UNION', + 'name' => 'Pet', + 'fields' => null, + 'interfaces' => null, 'possibleTypes' => [ ['name' => 'Dog'], - ['name' => 'Cat'] + ['name' => 'Cat'], ], - 'enumValues' => null, - 'inputFields' => null - ] - ] + 'enumValues' => null, + 'inputFields' => null, + ], + ], ]; $this->assertEquals($expected, Executor::execute($this->schema, $ast)->toArray()); } @@ -165,7 +175,7 @@ class UnionInterfaceTest extends TestCase public function testExecutesUsingUnionTypes() : void { // NOTE: This is an *invalid* query, but it should be an *executable* query. - $ast = Parser::parse(' + $ast = Parser::parse(' { __typename name @@ -180,12 +190,12 @@ class UnionInterfaceTest extends TestCase $expected = [ 'data' => [ '__typename' => 'Person', - 'name' => 'John', - 'pets' => [ + 'name' => 'John', + 'pets' => [ ['__typename' => 'Cat', 'name' => 'Garfield', 'meows' => false], - ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true] - ] - ] + ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true], + ], + ], ]; $this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray()); @@ -197,7 +207,7 @@ class UnionInterfaceTest extends TestCase public function testExecutesUnionTypesWithInlineFragments() : void { // This is the valid version of the query in the above test. - $ast = Parser::parse(' + $ast = Parser::parse(' { __typename name @@ -217,13 +227,13 @@ class UnionInterfaceTest extends TestCase $expected = [ 'data' => [ '__typename' => 'Person', - 'name' => 'John', - 'pets' => [ + 'name' => 'John', + 'pets' => [ ['__typename' => 'Cat', 'name' => 'Garfield', 'meows' => false], - ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true] - ] + ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true], + ], - ] + ], ]; $this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray()); } @@ -234,7 +244,7 @@ class UnionInterfaceTest extends TestCase public function testExecutesUsingInterfaceTypes() : void { // NOTE: This is an *invalid* query, but it should be an *executable* query. - $ast = Parser::parse(' + $ast = Parser::parse(' { __typename name @@ -249,12 +259,12 @@ class UnionInterfaceTest extends TestCase $expected = [ 'data' => [ '__typename' => 'Person', - 'name' => 'John', - 'friends' => [ + 'name' => 'John', + 'friends' => [ ['__typename' => 'Person', 'name' => 'Liz'], - ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true] - ] - ] + ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true], + ], + ], ]; $this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray()); @@ -266,7 +276,7 @@ class UnionInterfaceTest extends TestCase public function testExecutesInterfaceTypesWithInlineFragments() : void { // This is the valid version of the query in the above test. - $ast = Parser::parse(' + $ast = Parser::parse(' { __typename name @@ -285,12 +295,12 @@ class UnionInterfaceTest extends TestCase $expected = [ 'data' => [ '__typename' => 'Person', - 'name' => 'John', - 'friends' => [ + 'name' => 'John', + 'friends' => [ ['__typename' => 'Person', 'name' => 'Liz'], - ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true] - ] - ] + ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true], + ], + ], ]; $this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray(true)); @@ -336,16 +346,16 @@ class UnionInterfaceTest extends TestCase $expected = [ 'data' => [ '__typename' => 'Person', - 'name' => 'John', - 'pets' => [ + 'name' => 'John', + 'pets' => [ ['__typename' => 'Cat', 'name' => 'Garfield', 'meows' => false], - ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true] + ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true], ], - 'friends' => [ + 'friends' => [ ['__typename' => 'Person', 'name' => 'Liz'], - ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true] - ] - ] + ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true], + ], + ], ]; $this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray()); @@ -356,36 +366,45 @@ class UnionInterfaceTest extends TestCase */ public function testGetsExecutionInfoInResolver() : void { - $encounteredContext = null; - $encounteredSchema = null; + $encounteredContext = null; + $encounteredSchema = null; $encounteredRootValue = null; - $PersonType2 = null; + $PersonType2 = null; $NamedType2 = new InterfaceType([ - 'name' => 'Named', - 'fields' => [ - 'name' => ['type' => Type::string()] + 'name' => 'Named', + 'fields' => [ + 'name' => ['type' => Type::string()], ], - 'resolveType' => function ($obj, $context, ResolveInfo $info) use (&$encounteredContext, &$encounteredSchema, &$encounteredRootValue, &$PersonType2) { - $encounteredContext = $context; - $encounteredSchema = $info->schema; + 'resolveType' => function ( + $obj, + $context, + ResolveInfo $info + ) use ( + &$encounteredContext, + & + $encounteredSchema, + &$encounteredRootValue, + &$PersonType2 + ) { + $encounteredContext = $context; + $encounteredSchema = $info->schema; $encounteredRootValue = $info->rootValue; + return $PersonType2; - } + }, ]); $PersonType2 = new ObjectType([ - 'name' => 'Person', + 'name' => 'Person', 'interfaces' => [$NamedType2], - 'fields' => [ - 'name' => ['type' => Type::string()], + 'fields' => [ + 'name' => ['type' => Type::string()], 'friends' => ['type' => Type::listOf($NamedType2)], ], ]); - $schema2 = new Schema([ - 'query' => $PersonType2 - ]); + $schema2 = new Schema(['query' => $PersonType2]); $john2 = new Person('John', [], [$this->liz]); diff --git a/tests/Executor/ValuesTest.php b/tests/Executor/ValuesTest.php index 4d7bf2d..f2713e7 100644 --- a/tests/Executor/ValuesTest.php +++ b/tests/Executor/ValuesTest.php @@ -1,4 +1,7 @@ expectInputVariablesMatchOutputVariables(['idInput' => '123456789']); $this->assertEquals( - ['errors'=> [], 'coerced' => ['idInput' => '123456789']], - self::runTestCase(['idInput' => 123456789]), + ['errors' => [], 'coerced' => ['idInput' => '123456789']], + $this->runTestCase(['idInput' => 123456789]), 'Integer ID was not converted to string' ); } + private function expectInputVariablesMatchOutputVariables($variables) : void + { + $this->assertEquals( + $variables, + $this->runTestCase($variables)['coerced'], + 'Output variables did not match input variables' . PHP_EOL . var_export($variables, true) . PHP_EOL + ); + } + + /** + * @param mixed[] $variables + * @return mixed[] + */ + private function runTestCase($variables) : array + { + return Values::getVariableValues(self::getSchema(), self::getVariableDefinitionNodes(), $variables); + } + + private static function getSchema() : Schema + { + if (! self::$schema) { + self::$schema = new Schema([ + 'query' => new ObjectType([ + 'name' => 'Query', + 'fields' => [ + 'test' => [ + 'type' => Type::boolean(), + 'args' => [ + 'idInput' => Type::id(), + 'boolInput' => Type::boolean(), + 'intInput' => Type::int(), + 'stringInput' => Type::string(), + 'floatInput' => Type::float(), + ], + ], + ], + ]), + ]); + } + + return self::$schema; + } + + /** + * @return VariableDefinitionNode[] + */ + private static function getVariableDefinitionNodes() : array + { + $idInputDefinition = new VariableDefinitionNode([ + 'variable' => new VariableNode(['name' => new NameNode(['value' => 'idInput'])]), + 'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'ID'])]), + ]); + $boolInputDefinition = new VariableDefinitionNode([ + 'variable' => new VariableNode(['name' => new NameNode(['value' => 'boolInput'])]), + 'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'Boolean'])]), + ]); + $intInputDefinition = new VariableDefinitionNode([ + 'variable' => new VariableNode(['name' => new NameNode(['value' => 'intInput'])]), + 'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'Int'])]), + ]); + $stringInputDefintion = new VariableDefinitionNode([ + 'variable' => new VariableNode(['name' => new NameNode(['value' => 'stringInput'])]), + 'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'String'])]), + ]); + $floatInputDefinition = new VariableDefinitionNode([ + 'variable' => new VariableNode(['name' => new NameNode(['value' => 'floatInput'])]), + 'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'Float'])]), + ]); + + return [$idInputDefinition, $boolInputDefinition, $intInputDefinition, $stringInputDefintion, $floatInputDefinition]; + } + public function testGetBooleanVariableValues() : void { $this->expectInputVariablesMatchOutputVariables(['boolInput' => true]); @@ -64,11 +145,20 @@ class ValuesTest extends TestCase $this->expectGraphQLError(['idInput' => true]); } + private function expectGraphQLError($variables) : void + { + $result = $this->runTestCase($variables); + $this->assertGreaterThan(0, count($result['errors'])); + } + public function testFloatForIDVariableThrowsError() : void { $this->expectGraphQLError(['idInput' => 1.0]); } + /** + * Helpers for running test cases and making assertions + */ public function testStringForBooleanVariableThrowsError() : void { $this->expectGraphQLError(['boolInput' => 'true']); @@ -98,77 +188,4 @@ class ValuesTest extends TestCase { $this->expectGraphQLError(['intInput' => -2147483649]); } - - // Helpers for running test cases and making assertions - - private function expectInputVariablesMatchOutputVariables($variables) - { - $this->assertEquals( - $variables, - self::runTestCase($variables)['coerced'], - 'Output variables did not match input variables' . PHP_EOL . var_export($variables, true) . PHP_EOL - ); - } - - private function expectGraphQLError($variables) - { - $result = self::runTestCase($variables); - $this->assertGreaterThan(0, count($result['errors'])); - } - - private static $schema; - - private static function getSchema() - { - if (!self::$schema) { - self::$schema = new Schema([ - 'query' => new ObjectType([ - 'name' => 'Query', - 'fields' => [ - 'test' => [ - 'type' => Type::boolean(), - 'args' => [ - 'idInput' => Type::id(), - 'boolInput' => Type::boolean(), - 'intInput' => Type::int(), - 'stringInput' => Type::string(), - 'floatInput' => Type::float() - ] - ], - ] - ]) - ]); - } - return self::$schema; - } - - private static function getVariableDefinitionNodes() - { - $idInputDefinition = new VariableDefinitionNode([ - 'variable' => new VariableNode(['name' => new NameNode(['value' => 'idInput'])]), - 'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'ID'])]) - ]); - $boolInputDefinition = new VariableDefinitionNode([ - 'variable' => new VariableNode(['name' => new NameNode(['value' => 'boolInput'])]), - 'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'Boolean'])]) - ]); - $intInputDefinition = new VariableDefinitionNode([ - 'variable' => new VariableNode(['name' => new NameNode(['value' => 'intInput'])]), - 'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'Int'])]) - ]); - $stringInputDefintion = new VariableDefinitionNode([ - 'variable' => new VariableNode(['name' => new NameNode(['value' => 'stringInput'])]), - 'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'String'])]) - ]); - $floatInputDefinition = new VariableDefinitionNode([ - 'variable' => new VariableNode(['name' => new NameNode(['value' => 'floatInput'])]), - 'type' => new NamedTypeNode(['name' => new NameNode(['value' => 'Float'])]) - ]); - return [$idInputDefinition, $boolInputDefinition, $intInputDefinition, $stringInputDefintion, $floatInputDefinition]; - } - - private function runTestCase($variables) - { - return Values::getVariableValues(self::getSchema(), self::getVariableDefinitionNodes(), $variables); - } } diff --git a/tests/Executor/VariablesTest.php b/tests/Executor/VariablesTest.php index 89392d2..5f374ad 100644 --- a/tests/Executor/VariablesTest.php +++ b/tests/Executor/VariablesTest.php @@ -1,16 +1,19 @@ [ - 'fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}' - ] + 'data' => ['fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}'], ]; $this->assertEquals($expected, $result->toArray()); // properly parses single value to list: - $result = $this->executeQuery(' + $result = $this->executeQuery(' { fieldWithObjectInput(input: {a: "foo", b: "bar", c: "baz"}) } @@ -46,7 +46,7 @@ class VariablesTest extends TestCase $this->assertEquals($expected, $result->toArray()); // properly parses null value to null - $result = $this->executeQuery(' + $result = $this->executeQuery(' { fieldWithObjectInput(input: {a: null, b: null, c: "C", d: null}) } @@ -56,7 +56,7 @@ class VariablesTest extends TestCase $this->assertEquals($expected, $result->toArray()); // properly parses null value in list - $result = $this->executeQuery(' + $result = $this->executeQuery(' { fieldWithObjectInput(input: {b: ["A",null,"C"], c: "C"}) } @@ -73,12 +73,13 @@ class VariablesTest extends TestCase '); $expected = [ - 'data' => ['fieldWithObjectInput' => null], + 'data' => ['fieldWithObjectInput' => null], 'errors' => [[ - 'message' => 'Argument "input" has invalid value ["foo", "bar", "baz"].', - 'path' => ['fieldWithObjectInput'], - 'locations' => [['line' => 3, 'column' => 39]] - ]] + 'message' => 'Argument "input" has invalid value ["foo", "bar", "baz"].', + 'path' => ['fieldWithObjectInput'], + 'locations' => [['line' => 3, 'column' => 39]], + ], + ], ]; $this->assertArraySubset($expected, $result->toArray()); @@ -94,6 +95,77 @@ class VariablesTest extends TestCase ); } + private function executeQuery($query, $variableValues = null) + { + $document = Parser::parse($query); + + return Executor::execute($this->schema(), $document, null, null, $variableValues); + } + + /** + * Describe: Handles nullable scalars + */ + public function schema() : Schema + { + $ComplexScalarType = ComplexScalar::create(); + + $TestInputObject = new InputObjectType([ + 'name' => 'TestInputObject', + 'fields' => [ + 'a' => ['type' => Type::string()], + 'b' => ['type' => Type::listOf(Type::string())], + 'c' => ['type' => Type::nonNull(Type::string())], + 'd' => ['type' => $ComplexScalarType], + ], + ]); + + $TestNestedInputObject = new InputObjectType([ + 'name' => 'TestNestedInputObject', + 'fields' => [ + 'na' => ['type' => Type::nonNull($TestInputObject)], + 'nb' => ['type' => Type::nonNull(Type::string())], + ], + ]); + + $TestType = new ObjectType([ + 'name' => 'TestType', + 'fields' => [ + 'fieldWithObjectInput' => $this->fieldWithInputArg(['type' => $TestInputObject]), + 'fieldWithNullableStringInput' => $this->fieldWithInputArg(['type' => Type::string()]), + 'fieldWithNonNullableStringInput' => $this->fieldWithInputArg(['type' => Type::nonNull(Type::string())]), + 'fieldWithDefaultArgumentValue' => $this->fieldWithInputArg([ + 'type' => Type::string(), + 'defaultValue' => 'Hello World', + ]), + 'fieldWithNestedInputObject' => $this->fieldWithInputArg([ + 'type' => $TestNestedInputObject, + 'defaultValue' => 'Hello World', + ]), + 'list' => $this->fieldWithInputArg(['type' => Type::listOf(Type::string())]), + 'nnList' => $this->fieldWithInputArg(['type' => Type::nonNull(Type::listOf(Type::string()))]), + 'listNN' => $this->fieldWithInputArg(['type' => Type::listOf(Type::nonNull(Type::string()))]), + 'nnListNN' => $this->fieldWithInputArg(['type' => Type::nonNull(Type::listOf(Type::nonNull(Type::string())))]), + ], + ]); + + return new Schema(['query' => $TestType]); + } + + private function fieldWithInputArg($inputArg) + { + return [ + 'type' => Type::string(), + 'args' => ['input' => $inputArg], + 'resolve' => function ($_, $args) { + if (isset($args['input'])) { + return json_encode($args['input']); + } + + return null; + }, + ]; + } + public function testUsingVariables() : void { $doc = ' @@ -119,7 +191,7 @@ class VariablesTest extends TestCase '); $expected = [ - 'data' => ['fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}'] + 'data' => ['fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}'], ]; $this->assertEquals($expected, $result->toArray()); @@ -132,63 +204,61 @@ class VariablesTest extends TestCase ); // executes with complex scalar input: - $params = [ 'input' => [ 'c' => 'foo', 'd' => 'SerializedValue' ] ]; - $result = $this->executeQuery($doc, $params); + $params = ['input' => ['c' => 'foo', 'd' => 'SerializedValue']]; + $result = $this->executeQuery($doc, $params); $expected = [ - 'data' => [ - 'fieldWithObjectInput' => '{"c":"foo","d":"DeserializedValue"}' - ] + 'data' => ['fieldWithObjectInput' => '{"c":"foo","d":"DeserializedValue"}'], ]; $this->assertEquals($expected, $result->toArray()); // errors on null for nested non-null: - $params = ['input' => ['a' => 'foo', 'b' => 'bar', 'c' => null]]; - $result = $this->executeQuery($doc, $params); + $params = ['input' => ['a' => 'foo', 'b' => 'bar', 'c' => null]]; + $result = $this->executeQuery($doc, $params); $expected = [ 'errors' => [ [ - 'message' => + 'message' => 'Variable "$input" got invalid value ' . '{"a":"foo","b":"bar","c":null}; ' . 'Expected non-nullable type String! not to be null at value.c.', 'locations' => [['line' => 2, 'column' => 21]], - 'category' => 'graphql' - ] - ] + 'category' => 'graphql', + ], + ], ]; $this->assertEquals($expected, $result->toArray()); // errors on incorrect type: - $params = [ 'input' => 'foo bar' ]; - $result = $this->executeQuery($doc, $params); + $params = ['input' => 'foo bar']; + $result = $this->executeQuery($doc, $params); $expected = [ 'errors' => [ [ - 'message' => + 'message' => 'Variable "$input" got invalid value "foo bar"; ' . 'Expected type TestInputObject to be an object.', - 'locations' => [ [ 'line' => 2, 'column' => 21 ] ], - 'category' => 'graphql', - ] - ] + 'locations' => [['line' => 2, 'column' => 21]], + 'category' => 'graphql', + ], + ], ]; $this->assertEquals($expected, $result->toArray()); // errors on omission of nested non-null: $params = ['input' => ['a' => 'foo', 'b' => 'bar']]; - $result = $this->executeQuery($doc, $params); + $result = $this->executeQuery($doc, $params); $expected = [ 'errors' => [ [ - 'message' => - 'Variable "$input" got invalid value {"a":"foo","b":"bar"}; '. + 'message' => + 'Variable "$input" got invalid value {"a":"foo","b":"bar"}; ' . 'Field value.c of required type String! was not provided.', 'locations' => [['line' => 2, 'column' => 21]], - 'category' => 'graphql', - ] - ] + 'category' => 'graphql', + ], + ], ]; $this->assertEquals($expected, $result->toArray()); @@ -198,62 +268,59 @@ class VariablesTest extends TestCase fieldWithNestedObjectInput(input: $input) } '; - $params = [ 'input' => [ 'na' => [ 'a' => 'foo' ] ] ]; + $params = ['input' => ['na' => ['a' => 'foo']]]; - $result = $this->executeQuery($nestedDoc, $params); + $result = $this->executeQuery($nestedDoc, $params); $expected = [ 'errors' => [ [ - 'message' => + 'message' => 'Variable "$input" got invalid value {"na":{"a":"foo"}}; ' . 'Field value.na.c of required type String! was not provided.', 'locations' => [['line' => 2, 'column' => 19]], - 'category' => 'graphql', + 'category' => 'graphql', ], [ - 'message' => + 'message' => 'Variable "$input" got invalid value {"na":{"a":"foo"}}; ' . 'Field value.nb of required type String! was not provided.', 'locations' => [['line' => 2, 'column' => 19]], - 'category' => 'graphql', - ] - ] + 'category' => 'graphql', + ], + ], ]; $this->assertEquals($expected, $result->toArray()); - // errors on addition of unknown input field - $params = ['input' => [ 'a' => 'foo', 'b' => 'bar', 'c' => 'baz', 'extra' => 'dog' ]]; - $result = $this->executeQuery($doc, $params); + $params = ['input' => ['a' => 'foo', 'b' => 'bar', 'c' => 'baz', 'extra' => 'dog']]; + $result = $this->executeQuery($doc, $params); $expected = [ 'errors' => [ [ - 'message' => + 'message' => 'Variable "$input" got invalid value ' . '{"a":"foo","b":"bar","c":"baz","extra":"dog"}; ' . 'Field "extra" is not defined by type TestInputObject.', 'locations' => [['line' => 2, 'column' => 21]], - 'category' => 'graphql', - ] - ] + 'category' => 'graphql', + ], + ], ]; $this->assertEquals($expected, $result->toArray()); } - // Describe: Handles nullable scalars - /** * @see it('allows nullable inputs to be omitted') */ public function testAllowsNullableInputsToBeOmitted() : void { - $result = $this->executeQuery(' + $result = $this->executeQuery(' { fieldWithNullableStringInput } '); $expected = [ - 'data' => ['fieldWithNullableStringInput' => null] + 'data' => ['fieldWithNullableStringInput' => null], ]; $this->assertEquals($expected, $result->toArray()); @@ -264,7 +331,7 @@ class VariablesTest extends TestCase */ public function testAllowsNullableInputsToBeOmittedInAVariable() : void { - $result = $this->executeQuery(' + $result = $this->executeQuery(' query SetsNullable($value: String) { fieldWithNullableStringInput(input: $value) } @@ -279,7 +346,7 @@ class VariablesTest extends TestCase */ public function testAllowsNullableInputsToBeOmittedInAnUnlistedVariable() : void { - $result = $this->executeQuery(' + $result = $this->executeQuery(' query SetsNullable { fieldWithNullableStringInput(input: $value) } @@ -288,12 +355,15 @@ class VariablesTest extends TestCase $this->assertEquals($expected, $result->toArray()); } + + // Describe: Handles non-nullable scalars + /** * @see it('allows nullable inputs to be set to null in a variable') */ public function testAllowsNullableInputsToBeSetToNullInAVariable() : void { - $result = $this->executeQuery(' + $result = $this->executeQuery(' query SetsNullable($value: String) { fieldWithNullableStringInput(input: $value) } @@ -308,12 +378,12 @@ class VariablesTest extends TestCase */ public function testAllowsNullableInputsToBeSetToAValueInAVariable() : void { - $doc = ' + $doc = ' query SetsNullable($value: String) { fieldWithNullableStringInput(input: $value) } '; - $result = $this->executeQuery($doc, ['value' => 'a']); + $result = $this->executeQuery($doc, ['value' => 'a']); $expected = ['data' => ['fieldWithNullableStringInput' => '"a"']]; $this->assertEquals($expected, $result->toArray()); } @@ -323,7 +393,7 @@ class VariablesTest extends TestCase */ public function testAllowsNullableInputsToBeSetToAValueDirectly() : void { - $result = $this->executeQuery(' + $result = $this->executeQuery(' { fieldWithNullableStringInput(input: "a") } @@ -332,21 +402,18 @@ class VariablesTest extends TestCase $this->assertEquals($expected, $result->toArray()); } - - // Describe: Handles non-nullable scalars - /** * @see it('allows non-nullable inputs to be omitted given a default') */ public function testAllowsNonNullableInputsToBeOmittedGivenADefault() : void { - $result = $this->executeQuery(' + $result = $this->executeQuery(' query SetsNonNullable($value: String = "default") { fieldWithNonNullableStringInput(input: $value) } '); $expected = [ - 'data' => ['fieldWithNonNullableStringInput' => '"default"'] + 'data' => ['fieldWithNonNullableStringInput' => '"default"'], ]; $this->assertEquals($expected, $result->toArray()); } @@ -365,11 +432,11 @@ class VariablesTest extends TestCase $expected = [ 'errors' => [ [ - 'message' => 'Variable "$value" of required type "String!" was not provided.', + 'message' => 'Variable "$value" of required type "String!" was not provided.', 'locations' => [['line' => 2, 'column' => 31]], - 'category' => 'graphql' - ] - ] + 'category' => 'graphql', + ], + ], ]; $this->assertEquals($expected, $result->toArray()); } @@ -379,22 +446,22 @@ class VariablesTest extends TestCase */ public function testDoesNotAllowNonNullableInputsToBeSetToNullInAVariable() : void { - $doc = ' + $doc = ' query SetsNonNullable($value: String!) { fieldWithNonNullableStringInput(input: $value) } '; - $result = $this->executeQuery($doc, ['value' => null]); + $result = $this->executeQuery($doc, ['value' => null]); $expected = [ 'errors' => [ [ - 'message' => + 'message' => 'Variable "$value" got invalid value null; ' . 'Expected non-nullable type String! not to be null.', 'locations' => [['line' => 2, 'column' => 31]], - 'category' => 'graphql', - ] - ] + 'category' => 'graphql', + ], + ], ]; $this->assertEquals($expected, $result->toArray()); } @@ -404,12 +471,12 @@ class VariablesTest extends TestCase */ public function testAllowsNonNullableInputsToBeSetToAValueInAVariable() : void { - $doc = ' + $doc = ' query SetsNonNullable($value: String!) { fieldWithNonNullableStringInput(input: $value) } '; - $result = $this->executeQuery($doc, ['value' => 'a']); + $result = $this->executeQuery($doc, ['value' => 'a']); $expected = ['data' => ['fieldWithNonNullableStringInput' => '"a"']]; $this->assertEquals($expected, $result->toArray()); } @@ -419,7 +486,7 @@ class VariablesTest extends TestCase */ public function testAllowsNonNullableInputsToBeSetToAValueDirectly() : void { - $result = $this->executeQuery(' + $result = $this->executeQuery(' { fieldWithNonNullableStringInput(input: "a") } @@ -433,47 +500,50 @@ class VariablesTest extends TestCase */ public function testReportsErrorForMissingNonNullableInputs() : void { - $result = $this->executeQuery(' + $result = $this->executeQuery(' { fieldWithNonNullableStringInput } '); $expected = [ - 'data' => ['fieldWithNonNullableStringInput' => null], + 'data' => ['fieldWithNonNullableStringInput' => null], 'errors' => [[ - 'message' => 'Argument "input" of required type "String!" was not provided.', - 'locations' => [ [ 'line' => 3, 'column' => 9 ] ], - 'path' => [ 'fieldWithNonNullableStringInput' ], - 'category' => 'graphql', - ]] + 'message' => 'Argument "input" of required type "String!" was not provided.', + 'locations' => [['line' => 3, 'column' => 9]], + 'path' => ['fieldWithNonNullableStringInput'], + 'category' => 'graphql', + ], + ], ]; $this->assertEquals($expected, $result->toArray()); } + // Describe: Handles lists and nullability + /** * @see it('reports error for array passed into string input') */ public function testReportsErrorForArrayPassedIntoStringInput() : void { - - $doc = ' + $doc = ' query SetsNonNullable($value: String!) { fieldWithNonNullableStringInput(input: $value) } '; $variables = ['value' => [1, 2, 3]]; - $result = $this->executeQuery($doc, $variables); + $result = $this->executeQuery($doc, $variables); $expected = [ 'errors' => [[ - 'message' => + 'message' => 'Variable "$value" got invalid value [1,2,3]; Expected type ' . 'String; String cannot represent an array value: [1,2,3]', - 'category' => 'graphql', + 'category' => 'graphql', 'locations' => [ - ['line' => 2, 'column' => 31] - ] - ]] + ['line' => 2, 'column' => 31], + ], + ], + ], ]; $this->assertEquals($expected, $result->toArray()); } @@ -498,38 +568,37 @@ class VariablesTest extends TestCase // and are being run against a new schema which have introduced a breaking // change to make a formerly non-required argument required, this asserts // failure before allowing the underlying code to receive a non-null value. - $result = $this->executeQuery(' + $result = $this->executeQuery(' { fieldWithNonNullableStringInput(input: $foo) } '); $expected = [ - 'data' => ['fieldWithNonNullableStringInput' => null], + 'data' => ['fieldWithNonNullableStringInput' => null], 'errors' => [[ - 'message' => + 'message' => 'Argument "input" of required type "String!" was provided the ' . 'variable "$foo" which was not provided a runtime value.', 'locations' => [['line' => 3, 'column' => 48]], - 'path' => ['fieldWithNonNullableStringInput'], - 'category' => 'graphql', - ]] + 'path' => ['fieldWithNonNullableStringInput'], + 'category' => 'graphql', + ], + ], ]; $this->assertEquals($expected, $result->toArray()); } - // Describe: Handles lists and nullability - /** * @see it('allows lists to be null') */ public function testAllowsListsToBeNull() : void { - $doc = ' + $doc = ' query q($input:[String]) { list(input: $input) } '; - $result = $this->executeQuery($doc, ['input' => null]); + $result = $this->executeQuery($doc, ['input' => null]); $expected = ['data' => ['list' => null]]; $this->assertEquals($expected, $result->toArray()); @@ -540,12 +609,12 @@ class VariablesTest extends TestCase */ public function testAllowsListsToContainValues() : void { - $doc = ' + $doc = ' query q($input:[String]) { list(input: $input) } '; - $result = $this->executeQuery($doc, ['input' => ['A']]); + $result = $this->executeQuery($doc, ['input' => ['A']]); $expected = ['data' => ['list' => '["A"]']]; $this->assertEquals($expected, $result->toArray()); } @@ -555,12 +624,12 @@ class VariablesTest extends TestCase */ public function testAllowsListsToContainNull() : void { - $doc = ' + $doc = ' query q($input:[String]) { list(input: $input) } '; - $result = $this->executeQuery($doc, ['input' => ['A',null,'B']]); + $result = $this->executeQuery($doc, ['input' => ['A', null, 'B']]); $expected = ['data' => ['list' => '["A",null,"B"]']]; $this->assertEquals($expected, $result->toArray()); } @@ -570,22 +639,22 @@ class VariablesTest extends TestCase */ public function testDoesNotAllowNonNullListsToBeNull() : void { - $doc = ' + $doc = ' query q($input:[String]!) { nnList(input: $input) } '; - $result = $this->executeQuery($doc, ['input' => null]); + $result = $this->executeQuery($doc, ['input' => null]); $expected = [ 'errors' => [ [ - 'message' => + 'message' => 'Variable "$input" got invalid value null; ' . 'Expected non-nullable type [String]! not to be null.', 'locations' => [['line' => 2, 'column' => 17]], - 'category' => 'graphql', - ] - ] + 'category' => 'graphql', + ], + ], ]; $this->assertEquals($expected, $result->toArray()); } @@ -595,12 +664,12 @@ class VariablesTest extends TestCase */ public function testAllowsNonNullListsToContainValues() : void { - $doc = ' + $doc = ' query q($input:[String]!) { nnList(input: $input) } '; - $result = $this->executeQuery($doc, ['input' => ['A']]); + $result = $this->executeQuery($doc, ['input' => ['A']]); $expected = ['data' => ['nnList' => '["A"]']]; $this->assertEquals($expected, $result->toArray()); } @@ -610,12 +679,12 @@ class VariablesTest extends TestCase */ public function testAllowsNonNullListsToContainNull() : void { - $doc = ' + $doc = ' query q($input:[String]!) { nnList(input: $input) } '; - $result = $this->executeQuery($doc, ['input' => ['A',null,'B']]); + $result = $this->executeQuery($doc, ['input' => ['A', null, 'B']]); $expected = ['data' => ['nnList' => '["A",null,"B"]']]; $this->assertEquals($expected, $result->toArray()); } @@ -625,12 +694,12 @@ class VariablesTest extends TestCase */ public function testAllowsListsOfNonNullsToBeNull() : void { - $doc = ' + $doc = ' query q($input:[String!]) { listNN(input: $input) } '; - $result = $this->executeQuery($doc, ['input' => null]); + $result = $this->executeQuery($doc, ['input' => null]); $expected = ['data' => ['listNN' => null]]; $this->assertEquals($expected, $result->toArray()); } @@ -640,12 +709,12 @@ class VariablesTest extends TestCase */ public function testAllowsListsOfNonNullsToContainValues() : void { - $doc = ' + $doc = ' query q($input:[String!]) { listNN(input: $input) } '; - $result = $this->executeQuery($doc, ['input' => ['A']]); + $result = $this->executeQuery($doc, ['input' => ['A']]); $expected = ['data' => ['listNN' => '["A"]']]; $this->assertEquals($expected, $result->toArray()); } @@ -655,22 +724,22 @@ class VariablesTest extends TestCase */ public function testDoesNotAllowListsOfNonNullsToContainNull() : void { - $doc = ' + $doc = ' query q($input:[String!]) { listNN(input: $input) } '; - $result = $this->executeQuery($doc, ['input' => ['A', null, 'B']]); + $result = $this->executeQuery($doc, ['input' => ['A', null, 'B']]); $expected = [ 'errors' => [ [ - 'message' => + 'message' => 'Variable "$input" got invalid value ["A",null,"B"]; ' . 'Expected non-nullable type String! not to be null at value[1].', - 'locations' => [ ['line' => 2, 'column' => 17] ], - 'category' => 'graphql', - ] - ] + 'locations' => [['line' => 2, 'column' => 17]], + 'category' => 'graphql', + ], + ], ]; $this->assertEquals($expected, $result->toArray()); } @@ -680,22 +749,22 @@ class VariablesTest extends TestCase */ public function testDoesNotAllowNonNullListsOfNonNullsToBeNull() : void { - $doc = ' + $doc = ' query q($input:[String!]!) { nnListNN(input: $input) } '; - $result = $this->executeQuery($doc, ['input' => null]); + $result = $this->executeQuery($doc, ['input' => null]); $expected = [ 'errors' => [ [ - 'message' => + 'message' => 'Variable "$input" got invalid value null; ' . 'Expected non-nullable type [String!]! not to be null.', - 'locations' => [ ['line' => 2, 'column' => 17] ], - 'category' => 'graphql', - ] - ] + 'locations' => [['line' => 2, 'column' => 17]], + 'category' => 'graphql', + ], + ], ]; $this->assertEquals($expected, $result->toArray()); } @@ -705,37 +774,39 @@ class VariablesTest extends TestCase */ public function testAllowsNonNullListsOfNonNullsToContainValues() : void { - $doc = ' + $doc = ' query q($input:[String!]!) { nnListNN(input: $input) } '; - $result = $this->executeQuery($doc, ['input' => ['A']]); + $result = $this->executeQuery($doc, ['input' => ['A']]); $expected = ['data' => ['nnListNN' => '["A"]']]; $this->assertEquals($expected, $result->toArray()); } + // Describe: Execute: Uses argument default values + /** * @see it('does not allow non-null lists of non-nulls to contain null') */ public function testDoesNotAllowNonNullListsOfNonNullsToContainNull() : void { - $doc = ' + $doc = ' query q($input:[String!]!) { nnListNN(input: $input) } '; - $result = $this->executeQuery($doc, ['input' => ['A', null, 'B']]); + $result = $this->executeQuery($doc, ['input' => ['A', null, 'B']]); $expected = [ 'errors' => [ [ - 'message' => + 'message' => 'Variable "$input" got invalid value ["A",null,"B"]; ' . 'Expected non-nullable type String! not to be null at value[1].', - 'locations' => [ ['line' => 2, 'column' => 17] ], - 'category' => 'graphql', - ] - ] + 'locations' => [['line' => 2, 'column' => 17]], + 'category' => 'graphql', + ], + ], ]; $this->assertEquals($expected, $result->toArray()); } @@ -745,23 +816,23 @@ class VariablesTest extends TestCase */ public function testDoesNotAllowInvalidTypesToBeUsedAsValues() : void { - $doc = ' + $doc = ' query q($input: TestType!) { fieldWithObjectInput(input: $input) } '; - $vars = [ 'input' => [ 'list' => [ 'A', 'B' ] ] ]; - $result = $this->executeQuery($doc, $vars); + $vars = ['input' => ['list' => ['A', 'B']]]; + $result = $this->executeQuery($doc, $vars); $expected = [ 'errors' => [ [ - 'message' => + 'message' => 'Variable "$input" expected value of type "TestType!" which cannot ' . 'be used as an input type.', 'locations' => [['line' => 2, 'column' => 25]], - 'category' => 'graphql', - ] - ] + 'category' => 'graphql', + ], + ], ]; $this->assertEquals($expected, $result->toArray()); } @@ -771,29 +842,28 @@ class VariablesTest extends TestCase */ public function testDoesNotAllowUnknownTypesToBeUsedAsValues() : void { - $doc = ' + $doc = ' query q($input: UnknownType!) { fieldWithObjectInput(input: $input) } '; $vars = ['input' => 'whoknows']; - $result = $this->executeQuery($doc, $vars); + $result = $this->executeQuery($doc, $vars); $expected = [ 'errors' => [ [ - 'message' => + 'message' => 'Variable "$input" expected value of type "UnknownType!" which ' . 'cannot be used as an input type.', 'locations' => [['line' => 2, 'column' => 25]], - 'category' => 'graphql', - ] - ] + 'category' => 'graphql', + ], + ], ]; $this->assertEquals($expected, $result->toArray()); } - // Describe: Execute: Uses argument default values /** * @see it('when no argument provided') */ @@ -834,84 +904,17 @@ class VariablesTest extends TestCase }'); $expected = [ - 'data' => ['fieldWithDefaultArgumentValue' => null], + 'data' => ['fieldWithDefaultArgumentValue' => null], 'errors' => [[ - 'message' => + 'message' => 'Argument "input" has invalid value WRONG_TYPE.', - 'locations' => [ [ 'line' => 2, 'column' => 50 ] ], - 'path' => [ 'fieldWithDefaultArgumentValue' ], - 'category' => 'graphql', - ]] + 'locations' => [['line' => 2, 'column' => 50]], + 'path' => ['fieldWithDefaultArgumentValue'], + 'category' => 'graphql', + ], + ], ]; $this->assertEquals($expected, $result->toArray()); } - - - public function schema() - { - $ComplexScalarType = ComplexScalar::create(); - - $TestInputObject = new InputObjectType([ - 'name' => 'TestInputObject', - 'fields' => [ - 'a' => ['type' => Type::string()], - 'b' => ['type' => Type::listOf(Type::string())], - 'c' => ['type' => Type::nonNull(Type::string())], - 'd' => ['type' => $ComplexScalarType], - ] - ]); - - $TestNestedInputObject = new InputObjectType([ - 'name' => 'TestNestedInputObject', - 'fields' => [ - 'na' => [ 'type' => Type::nonNull($TestInputObject) ], - 'nb' => [ 'type' => Type::nonNull(Type::string()) ], - ], - ]); - - $TestType = new ObjectType([ - 'name' => 'TestType', - 'fields' => [ - 'fieldWithObjectInput' => $this->fieldWithInputArg(['type' => $TestInputObject]), - 'fieldWithNullableStringInput' => $this->fieldWithInputArg(['type' => Type::string()]), - 'fieldWithNonNullableStringInput' => $this->fieldWithInputArg(['type' => Type::nonNull(Type::string())]), - 'fieldWithDefaultArgumentValue' => $this->fieldWithInputArg([ - 'type' => Type::string(), - 'defaultValue' => 'Hello World', - ]), - 'fieldWithNestedInputObject' => $this->fieldWithInputArg([ - 'type' => $TestNestedInputObject, - 'defaultValue' => 'Hello World' - ]), - 'list' => $this->fieldWithInputArg(['type' => Type::listOf(Type::string())]), - 'nnList' => $this->fieldWithInputArg(['type' => Type::nonNull(Type::listOf(Type::string()))]), - 'listNN' => $this->fieldWithInputArg(['type' => Type::listOf(Type::nonNull(Type::string()))]), - 'nnListNN' => $this->fieldWithInputArg(['type' => Type::nonNull(Type::listOf(Type::nonNull(Type::string())))]), - ] - ]); - - $schema = new Schema(['query' => $TestType]); - return $schema; - } - - private function fieldWithInputArg($inputArg) - { - return [ - 'type' => Type::string(), - 'args' => ['input' => $inputArg], - 'resolve' => function ($_, $args) { - if (isset($args['input'])) { - return json_encode($args['input']); - } - return null; - }, - ]; - } - - private function executeQuery($query, $variableValues = null) - { - $document = Parser::parse($query); - return Executor::execute($this->schema(), $document, null, null, $variableValues); - } } diff --git a/tests/Language/LexerTest.php b/tests/Language/LexerTest.php index a109232..3cdd6ef 100644 --- a/tests/Language/LexerTest.php +++ b/tests/Language/LexerTest.php @@ -1,13 +1,18 @@ 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') */ public function testAcceptsBomHeader() : void { - $bom = Utils::chr(0xFEFF); + $bom = Utils::chr(0xFEFF); $expected = [ - 'kind' => Token::NAME, + 'kind' => Token::NAME, 'start' => 2, - 'end' => 5, - 'value' => 'foo' + 'end' => 5, + 'value' => 'foo', ]; $this->assertArraySubset($expected, (array) $this->lexOne($bom . ' foo')); @@ -45,12 +78,12 @@ class LexerTest extends TestCase public function testRecordsLineAndColumn() : void { $expected = [ - 'kind' => Token::NAME, - 'start' => 8, - 'end' => 11, - 'line' => 4, + 'kind' => Token::NAME, + 'start' => 8, + 'end' => 11, + 'line' => 4, 'column' => 3, - 'value' => 'foo' + 'value' => 'foo', ]; $this->assertArraySubset($expected, (array) $this->lexOne("\n \r\n \r foo\n")); } @@ -67,10 +100,10 @@ class LexerTest extends TestCase '; $expected = [ - 'kind' => Token::NAME, + 'kind' => Token::NAME, 'start' => 6, - 'end' => 9, - 'value' => 'foo' + 'end' => 9, + 'value' => 'foo', ]; $this->assertArraySubset($expected, (array) $this->lexOne($example1)); @@ -80,18 +113,18 @@ class LexerTest extends TestCase '; $expected = [ - 'kind' => Token::NAME, + 'kind' => Token::NAME, 'start' => 18, - 'end' => 21, - 'value' => 'foo' + 'end' => 21, + 'value' => 'foo', ]; $this->assertArraySubset($expected, (array) $this->lexOne($example2)); $expected = [ - 'kind' => Token::NAME, + 'kind' => Token::NAME, 'start' => 3, - 'end' => 6, - 'value' => 'foo' + 'end' => 6, + 'value' => 'foo', ]; $example3 = ',,,foo,,,'; @@ -131,7 +164,7 @@ class LexerTest extends TestCase */ public function testUpdatesLineNumbersInErrorForFileContext() : void { - $str = '' . + $str = '' . "\n" . "\n" . " ?\n" . @@ -181,63 +214,86 @@ class LexerTest extends TestCase */ public function testLexesStrings() : void { - $this->assertArraySubset([ - 'kind' => Token::STRING, - 'start' => 0, - 'end' => 8, - 'value' => 'simple' - ], (array) $this->lexOne('"simple"')); + $this->assertArraySubset( + [ + 'kind' => Token::STRING, + 'start' => 0, + 'end' => 8, + '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([ - 'kind' => Token::STRING, - 'start' => 0, - 'end' => 15, - 'value' => ' white space ' - ], (array) $this->lexOne('" white space "')); + $this->assertArraySubset( + [ + 'kind' => Token::STRING, + 'start' => 0, + 'end' => 10, + 'value' => 'quote "', + ], + (array) $this->lexOne('"quote \\""') + ); - $this->assertArraySubset([ - 'kind' => Token::STRING, - 'start' => 0, - 'end' => 10, - 'value' => 'quote "' - ], (array) $this->lexOne('"quote \\""')); + $this->assertArraySubset( + [ + 'kind' => Token::STRING, + 'start' => 0, + 'end' => 25, + 'value' => 'escaped \n\r\b\t\f', + ], + (array) $this->lexOne('"escaped \\\\n\\\\r\\\\b\\\\t\\\\f"') + ); - $this->assertArraySubset([ - 'kind' => Token::STRING, - 'start' => 0, - 'end' => 25, - 'value' => 'escaped \n\r\b\t\f' - ], (array) $this->lexOne('"escaped \\\\n\\\\r\\\\b\\\\t\\\\f"')); + $this->assertArraySubset( + [ + 'kind' => Token::STRING, + 'start' => 0, + 'end' => 16, + 'value' => 'slashes \\ \/', + ], + (array) $this->lexOne('"slashes \\\\ \\\\/"') + ); - $this->assertArraySubset([ - 'kind' => Token::STRING, - 'start' => 0, - 'end' => 16, - 'value' => 'slashes \\ \/' - ], (array) $this->lexOne('"slashes \\\\ \\\\/"')); - - $this->assertArraySubset([ - 'kind' => Token::STRING, - 'start' => 0, - 'end' => 13, - 'value' => 'unicode яуц' - ], (array) $this->lexOne('"unicode яуц"')); + $this->assertArraySubset( + [ + 'kind' => Token::STRING, + 'start' => 0, + 'end' => 13, + 'value' => 'unicode яуц', + ], + (array) $this->lexOne('"unicode яуц"') + ); $unicode = json_decode('"\u1234\u5678\u90AB\uCDEF"'); - $this->assertArraySubset([ - 'kind' => Token::STRING, - 'start' => 0, - 'end' => 34, - 'value' => 'unicode ' . $unicode - ], (array) $this->lexOne('"unicode \u1234\u5678\u90AB\uCDEF"')); + $this->assertArraySubset( + [ + 'kind' => Token::STRING, + 'start' => 0, + 'end' => 34, + 'value' => 'unicode ' . $unicode, + ], + (array) $this->lexOne('"unicode \u1234\u5678\u90AB\uCDEF"') + ); - $this->assertArraySubset([ - 'kind' => Token::STRING, - 'start' => 0, - 'end' => 26, - 'value' => $unicode - ], (array) $this->lexOne('"\u1234\u5678\u90AB\uCDEF"')); + $this->assertArraySubset( + [ + 'kind' => Token::STRING, + 'start' => 0, + 'end' => 26, + 'value' => $unicode, + ], + (array) $this->lexOne('"\u1234\u5678\u90AB\uCDEF"') + ); } /** @@ -245,86 +301,128 @@ class LexerTest extends TestCase */ public function testLexesBlockString() : void { - $this->assertArraySubset([ - 'kind' => Token::BLOCK_STRING, - 'start' => 0, - 'end' => 12, - 'value' => 'simple' - ], (array) $this->lexOne('"""simple"""')); + $this->assertArraySubset( + [ + 'kind' => Token::BLOCK_STRING, + 'start' => 0, + 'end' => 12, + 'value' => 'simple', + ], + (array) $this->lexOne('"""simple"""') + ); - $this->assertArraySubset([ - 'kind' => Token::BLOCK_STRING, - 'start' => 0, - 'end' => 19, - 'value' => ' white space ' - ], (array) $this->lexOne('""" white space """')); + $this->assertArraySubset( + [ + 'kind' => Token::BLOCK_STRING, + 'start' => 0, + 'end' => 19, + 'value' => ' white space ', + ], + (array) $this->lexOne('""" white space """') + ); - $this->assertArraySubset([ - 'kind' => Token::BLOCK_STRING, - 'start' => 0, - 'end' => 22, - 'value' => 'contains " quote' - ], (array) $this->lexOne('"""contains " quote"""')); + $this->assertArraySubset( + [ + 'kind' => Token::BLOCK_STRING, + 'start' => 0, + 'end' => 22, + 'value' => 'contains " quote', + ], + (array) $this->lexOne('"""contains " quote"""') + ); - $this->assertArraySubset([ - 'kind' => Token::BLOCK_STRING, - 'start' => 0, - 'end' => 31, - 'value' => 'contains """ triplequote' - ], (array) $this->lexOne('"""contains \\""" triplequote"""')); + $this->assertArraySubset( + [ + 'kind' => Token::BLOCK_STRING, + 'start' => 0, + 'end' => 31, + 'value' => 'contains """ triplequote', + ], + (array) $this->lexOne('"""contains \\""" triplequote"""') + ); - $this->assertArraySubset([ - 'kind' => Token::BLOCK_STRING, - 'start' => 0, - 'end' => 16, - 'value' => "multi\nline" - ], (array) $this->lexOne("\"\"\"multi\nline\"\"\"")); + $this->assertArraySubset( + [ + 'kind' => Token::BLOCK_STRING, + 'start' => 0, + 'end' => 16, + 'value' => "multi\nline", + ], + (array) $this->lexOne("\"\"\"multi\nline\"\"\"") + ); - $this->assertArraySubset([ - 'kind' => Token::BLOCK_STRING, - 'start' => 0, - 'end' => 28, - 'value' => "multi\nline\nnormalized" - ], (array) $this->lexOne("\"\"\"multi\rline\r\nnormalized\"\"\"")); + $this->assertArraySubset( + [ + 'kind' => Token::BLOCK_STRING, + 'start' => 0, + 'end' => 28, + 'value' => "multi\nline\nnormalized", + ], + (array) $this->lexOne("\"\"\"multi\rline\r\nnormalized\"\"\"") + ); - $this->assertArraySubset([ - 'kind' => Token::BLOCK_STRING, - 'start' => 0, - 'end' => 32, - 'value' => 'unescaped \\n\\r\\b\\t\\f\\u1234' - ], (array) $this->lexOne('"""unescaped \\n\\r\\b\\t\\f\\u1234"""')); + $this->assertArraySubset( + [ + 'kind' => Token::BLOCK_STRING, + 'start' => 0, + 'end' => 32, + 'value' => 'unescaped \\n\\r\\b\\t\\f\\u1234', + ], + (array) $this->lexOne('"""unescaped \\n\\r\\b\\t\\f\\u1234"""') + ); - $this->assertArraySubset([ - 'kind' => Token::BLOCK_STRING, - 'start' => 0, - 'end' => 19, - 'value' => 'slashes \\\\ \\/' - ], (array) $this->lexOne('"""slashes \\\\ \\/"""')); + $this->assertArraySubset( + [ + 'kind' => Token::BLOCK_STRING, + 'start' => 0, + 'end' => 19, + 'value' => 'slashes \\\\ \\/', + ], + (array) $this->lexOne('"""slashes \\\\ \\/"""') + ); - $this->assertArraySubset([ - 'kind' => Token::BLOCK_STRING, - 'start' => 0, - 'end' => 68, - 'value' => "spans\n multiple\n lines" - ], (array) $this->lexOne("\"\"\" + $this->assertArraySubset( + [ + 'kind' => Token::BLOCK_STRING, + 'start' => 0, + 'end' => 68, + 'value' => "spans\n multiple\n lines", + ], + (array) $this->lexOne('""" spans multiple lines - \"\"\"")); + """') + ); } - public function reportsUsefulStringErrors() { + public function reportsUsefulStringErrors() + { return [ - ['"', "Unterminated string.", $this->loc(1, 2)], - ['"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)], + ['"', 'Unterminated string.', $this->loc(1, 2)], + ['"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 + ), + ], ['"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' . "\r" . 'line"', "Unterminated string.", $this->loc(1, 7)], - ['"bad \\z esc"', "Invalid character escape sequence: \\z", $this->loc(1, 7)], + ['"multi' . "\n" . '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 \\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 \\u0XX1 esc"', "Invalid character escape sequence: \\u0XX1", $this->loc(1, 7)], @@ -336,25 +434,40 @@ class LexerTest extends TestCase /** * @dataProvider reportsUsefulStringErrors - * @see it('lex reports useful string errors') + * @see it('lex reports useful string errors') */ public function testLexReportsUsefulStringErrors($str, $expectedMessage, $location) : void { $this->expectSyntaxError($str, $expectedMessage, $location); } - public function reportsUsefulBlockStringErrors() { + public function reportsUsefulBlockStringErrors() + { return [ - ['"""', "Unterminated string.", $this->loc(1, 4)], - ['"""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)], + ['"""', 'Unterminated string.', $this->loc(1, 4)], + ['"""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 + ), + ], ]; } /** * @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 { @@ -435,21 +548,21 @@ class LexerTest extends TestCase public function reportsUsefulNumberErrors() { return [ - [ '00', "Invalid number, unexpected digit after 0: \"0\"", $this->loc(1, 2)], - [ '+1', "Cannot parse the unexpected character \"+\".", $this->loc(1, 1)], - [ '1.', "Invalid number, expected digit but got: ", $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)], - [ '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)], - [ '1.0e', "Invalid number, expected digit but got: ", $this->loc(1, 5)], - [ '1.0eA', "Invalid number, expected digit but got: \"A\"", $this->loc(1, 5)], + ['00', 'Invalid number, unexpected digit after 0: "0"', $this->loc(1, 2)], + ['+1', 'Cannot parse the unexpected character "+".', $this->loc(1, 1)], + ['1.', 'Invalid number, expected digit but got: ', $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)], + ['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)], + ['1.0e', 'Invalid number, expected digit but got: ', $this->loc(1, 5)], + ['1.0eA', 'Invalid number, expected digit but got: "A"', $this->loc(1, 5)], ]; } /** * @dataProvider reportsUsefulNumberErrors - * @see it('lex reports useful number errors') + * @see it('lex reports useful number errors') */ public function testReportsUsefulNumberErrors($str, $expectedMessage, $location) : void { @@ -521,8 +634,8 @@ class LexerTest extends TestCase $unicode2 = json_decode('"\u200b"'); 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)], [$unicode2, "Cannot parse the unexpected character \"\\u200b\".", $this->loc(1, 1)], ]; @@ -530,7 +643,7 @@ class LexerTest extends TestCase /** * @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 { @@ -542,17 +655,20 @@ class LexerTest extends TestCase */ public function testReportsUsefulDashesInfo() : void { - $q = 'a-b'; + $q = 'a-b'; $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->expectExceptionMessage('Syntax Error: Invalid number, expected digit but got: "b"'); try { $lexer->advance(); $this->fail('Expected exception not thrown'); - } catch(SyntaxError $error) { - $this->assertEquals([$this->loc(1,3)], $error->getLocations()); + } catch (SyntaxError $error) { + $this->assertEquals([$this->loc(1, 3)], $error->getLocations()); throw $error; } } @@ -580,49 +696,28 @@ class LexerTest extends TestCase $tokens = []; for ($tok = $startToken; $tok; $tok = $tok->next) { - if (!empty($tokens)) { + if (! empty($tokens)) { // Tokens are double-linked, prev should point to last seen token. $this->assertSame($tokens[count($tokens) - 1], $tok->prev); } $tokens[] = $tok; } - $this->assertEquals([ - '', - '{', - 'Comment', - 'Name', - '}', - '' - ], 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; - } + $this->assertEquals( + [ + '', + '{', + 'Comment', + 'Name', + '}', + '', + ], + Utils::map( + $tokens, + function ($tok) { + return $tok->kind; + } + ) + ); } } diff --git a/tests/Language/ParserTest.php b/tests/Language/ParserTest.php index c50ecfb..49f461a 100644 --- a/tests/Language/ParserTest.php +++ b/tests/Language/ParserTest.php @@ -1,7 +1,11 @@ ", "Syntax Error: Expected Name, found \n\nGraphQL request (1:2)\n1: {\n ^\n", [1], [new SourceLocation(1, 2)]], - ['{ ...MissingOn } + [ + '{', + 'Syntax Error: Expected Name, found ', + "Syntax Error: Expected Name, found \n\nGraphQL request (1:2)\n1: {\n ^\n", + [1], + [new SourceLocation( + 1, + 2 + ), + ], + ], + [ + '{ ...MissingOn } 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",], - ['{ 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"], +', '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", + ], + ['{ 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 - * @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 { Parser::parse($str); $this->fail('Expected exception not thrown'); @@ -110,10 +133,27 @@ fragment MissingOn Type $this->expectSyntaxError( 'query Foo($x: Complex = { a: { b: [ $var ] } }) { field }', '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"') */ @@ -122,7 +162,7 @@ fragment MissingOn Type $this->expectSyntaxError( 'fragment on on on { on }', 'Unexpected Name "on"', - $this->loc(1,10) + $this->loc(1, 10) ); } @@ -134,7 +174,7 @@ fragment MissingOn Type $this->expectSyntaxError( '{ ...on }', '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. - $char = Utils::chr(0x0A0A); + $char = Utils::chr(0x0A0A); $query = << new NodeList([ new FieldNode([ - 'name' => new NameNode(['value' => 'field']), - 'arguments' => new NodeList([ + 'name' => new NameNode(['value' => 'field']), + 'arguments' => new NodeList([ new ArgumentNode([ - 'name' => new NameNode(['value' => 'arg']), - 'value' => new StringValueNode([ - 'value' => "Has a $char multi-byte character." - ]) - ]) + 'name' => new NameNode(['value' => 'arg']), + 'value' => new StringValueNode( + ['value' => sprintf('Has a %s multi-byte character.', $char)] + ), + ]), ]), - 'directives' => new NodeList([]) - ]) - ]) + 'directives' => new NodeList([]), + ]), + ]), ]); $this->assertEquals($expected, $result->definitions[0]->selectionSet); @@ -180,7 +220,7 @@ HEREDOC; { // Following should not throw: $kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql'); - $result = Parser::parse($kitchenSink); + $result = Parser::parse($kitchenSink); $this->assertNotEmpty($result); } @@ -196,7 +236,7 @@ HEREDOC; 'mutation', 'subscription', 'true', - 'false' + 'false', ]; foreach ($nonKeywords as $keyword) { $fragmentName = $keyword; @@ -205,14 +245,19 @@ HEREDOC; } // Expected not to throw: - $result = Parser::parse("query $keyword { - ... $fragmentName - ... on $keyword { field } + $result = Parser::parse(<<assertNotEmpty($result); } } @@ -286,94 +331,102 @@ fragment $fragmentName on Type { '); $result = Parser::parse($source); - $loc = function($start, $end) use ($source) { + $loc = function ($start, $end) { return [ 'start' => $start, - 'end' => $end + 'end' => $end, ]; }; $expected = [ - 'kind' => NodeKind::DOCUMENT, - 'loc' => $loc(0, 41), + 'kind' => NodeKind::DOCUMENT, + 'loc' => $loc(0, 41), 'definitions' => [ [ - 'kind' => NodeKind::OPERATION_DEFINITION, - 'loc' => $loc(0, 40), - 'operation' => 'query', - 'name' => null, + 'kind' => NodeKind::OPERATION_DEFINITION, + 'loc' => $loc(0, 40), + 'operation' => 'query', + 'name' => null, 'variableDefinitions' => [], - 'directives' => [], - 'selectionSet' => [ - 'kind' => NodeKind::SELECTION_SET, - 'loc' => $loc(0, 40), + 'directives' => [], + 'selectionSet' => [ + 'kind' => NodeKind::SELECTION_SET, + 'loc' => $loc(0, 40), 'selections' => [ [ - 'kind' => NodeKind::FIELD, - 'loc' => $loc(4, 38), - 'alias' => null, - 'name' => [ - 'kind' => NodeKind::NAME, - 'loc' => $loc(4, 8), - 'value' => 'node' + 'kind' => NodeKind::FIELD, + 'loc' => $loc(4, 38), + 'alias' => null, + 'name' => [ + 'kind' => NodeKind::NAME, + 'loc' => $loc(4, 8), + 'value' => 'node', ], - 'arguments' => [ + 'arguments' => [ [ - 'kind' => NodeKind::ARGUMENT, - 'name' => [ - 'kind' => NodeKind::NAME, - 'loc' => $loc(9, 11), - 'value' => 'id' + 'kind' => NodeKind::ARGUMENT, + 'name' => [ + 'kind' => NodeKind::NAME, + 'loc' => $loc(9, 11), + 'value' => 'id', ], 'value' => [ - 'kind' => NodeKind::INT, - 'loc' => $loc(13, 14), - 'value' => '4' + 'kind' => NodeKind::INT, + 'loc' => $loc(13, 14), + 'value' => '4', ], - 'loc' => $loc(9, 14, $source) - ] + 'loc' => $loc(9, 14, $source), + ], ], - 'directives' => [], + 'directives' => [], 'selectionSet' => [ - 'kind' => NodeKind::SELECTION_SET, - 'loc' => $loc(16, 38), + 'kind' => NodeKind::SELECTION_SET, + 'loc' => $loc(16, 38), 'selections' => [ [ - 'kind' => NodeKind::FIELD, - 'loc' => $loc(22, 24), - 'alias' => null, - 'name' => [ - 'kind' => NodeKind::NAME, - 'loc' => $loc(22, 24), - 'value' => 'id' + 'kind' => NodeKind::FIELD, + 'loc' => $loc(22, 24), + 'alias' => null, + 'name' => [ + 'kind' => NodeKind::NAME, + 'loc' => $loc(22, 24), + 'value' => 'id', ], - 'arguments' => [], - 'directives' => [], - 'selectionSet' => null + 'arguments' => [], + 'directives' => [], + 'selectionSet' => null, ], [ - 'kind' => NodeKind::FIELD, - 'loc' => $loc(30, 34), - 'alias' => null, - 'name' => [ - 'kind' => NodeKind::NAME, - 'loc' => $loc(30, 34), - 'value' => 'name' + 'kind' => NodeKind::FIELD, + 'loc' => $loc(30, 34), + 'alias' => null, + 'name' => [ + 'kind' => NodeKind::NAME, + 'loc' => $loc(30, 34), + 'value' => 'name', ], - 'arguments' => [], - 'directives' => [], - 'selectionSet' => null - ] - ] - ] - ] - ] - ] - ] - ] + 'arguments' => [], + 'directives' => [], + '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); - $loc = function($start, $end) use ($source) { + $loc = function ($start, $end) { return [ 'start' => $start, - 'end' => $end + 'end' => $end, ]; }; $expected = [ - 'kind' => NodeKind::DOCUMENT, - 'loc' => $loc(0, 30), + 'kind' => NodeKind::DOCUMENT, + 'loc' => $loc(0, 30), 'definitions' => [ [ - 'kind' => NodeKind::OPERATION_DEFINITION, - 'loc' => $loc(0, 29), - 'operation' => 'query', - 'name' => null, + 'kind' => NodeKind::OPERATION_DEFINITION, + 'loc' => $loc(0, 29), + 'operation' => 'query', + 'name' => null, 'variableDefinitions' => [], - 'directives' => [], - 'selectionSet' => [ - 'kind' => NodeKind::SELECTION_SET, - 'loc' => $loc(6, 29), + 'directives' => [], + 'selectionSet' => [ + 'kind' => NodeKind::SELECTION_SET, + 'loc' => $loc(6, 29), 'selections' => [ [ - 'kind' => NodeKind::FIELD, - 'loc' => $loc(10, 27), - 'alias' => null, - 'name' => [ - 'kind' => NodeKind::NAME, - 'loc' => $loc(10, 14), - 'value' => 'node' + 'kind' => NodeKind::FIELD, + 'loc' => $loc(10, 27), + 'alias' => null, + 'name' => [ + 'kind' => NodeKind::NAME, + 'loc' => $loc(10, 14), + 'value' => 'node', ], - 'arguments' => [], - 'directives' => [], + 'arguments' => [], + 'directives' => [], 'selectionSet' => [ - 'kind' => NodeKind::SELECTION_SET, - 'loc' => $loc(15, 27), + 'kind' => NodeKind::SELECTION_SET, + 'loc' => $loc(15, 27), 'selections' => [ [ - 'kind' => NodeKind::FIELD, - 'loc' => $loc(21, 23), - 'alias' => null, - 'name' => [ - 'kind' => NodeKind::NAME, - 'loc' => $loc(21, 23), - 'value' => 'id' + 'kind' => NodeKind::FIELD, + 'loc' => $loc(21, 23), + 'alias' => null, + 'name' => [ + 'kind' => NodeKind::NAME, + 'loc' => $loc(21, 23), + 'value' => 'id', ], - 'arguments' => [], - 'directives' => [], - 'selectionSet' => null - ] - ] - ] - ] - ] - ] - ] - ] + 'arguments' => [], + 'directives' => [], + 'selectionSet' => null, + ], + ], + ], + ], + ], + ], + ], + ], ]; $this->assertEquals($expected, $this->nodeToArray($result)); @@ -475,6 +528,8 @@ fragment $fragmentName on Type { Parser::parse($source); } + // Describe: parseValue + /** * @see it('contains location information that only stringifys start/end') */ @@ -495,6 +550,8 @@ fragment $fragmentName on Type { $this->assertEquals($source, $result->loc->source); } + // Describe: parseType + /** * @see it('contains references to start and end tokens') */ @@ -506,17 +563,18 @@ fragment $fragmentName on Type { $this->assertEquals('', $result->loc->endToken->kind); } - // Describe: parseValue - /** * @see it('parses null value') */ public function testParsesNullValues() : void { - $this->assertEquals([ - 'kind' => NodeKind::NULL, - 'loc' => ['start' => 0, 'end' => 4] - ], $this->nodeToArray(Parser::parseValue('null'))); + $this->assertEquals( + [ + 'kind' => NodeKind::NULL, + 'loc' => ['start' => 0, 'end' => 4], + ], + $this->nodeToArray(Parser::parseValue('null')) + ); } /** @@ -524,41 +582,45 @@ fragment $fragmentName on Type { */ public function testParsesListValues() : void { - $this->assertEquals([ - 'kind' => NodeKind::LST, - 'loc' => ['start' => 0, 'end' => 11], - 'values' => [ - [ - 'kind' => NodeKind::INT, - 'loc' => ['start' => 1, 'end' => 4], - 'value' => '123' + $this->assertEquals( + [ + 'kind' => NodeKind::LST, + 'loc' => ['start' => 0, 'end' => 11], + 'values' => [ + [ + 'kind' => NodeKind::INT, + 'loc' => ['start' => 1, 'end' => 4], + 'value' => '123', + ], + [ + 'kind' => NodeKind::STRING, + 'loc' => ['start' => 5, 'end' => 10], + 'value' => 'abc', + 'block' => false, + ], ], - [ - 'kind' => NodeKind::STRING, - 'loc' => ['start' => 5, 'end' => 10], - 'value' => 'abc', - 'block' => false - ] - ] - ], $this->nodeToArray(Parser::parseValue('[123 "abc"]'))); + ], + $this->nodeToArray(Parser::parseValue('[123 "abc"]')) + ); } - // Describe: parseType - /** * @see it('parses well known types') */ public function testParsesWellKnownTypes() : void { - $this->assertEquals([ - 'kind' => NodeKind::NAMED_TYPE, - 'loc' => ['start' => 0, 'end' => 6], - 'name' => [ - 'kind' => NodeKind::NAME, - 'loc' => ['start' => 0, 'end' => 6], - 'value' => 'String' - ] - ], $this->nodeToArray(Parser::parseType('String'))); + $this->assertEquals( + [ + 'kind' => NodeKind::NAMED_TYPE, + 'loc' => ['start' => 0, 'end' => 6], + 'name' => [ + 'kind' => NodeKind::NAME, + 'loc' => ['start' => 0, 'end' => 6], + 'value' => 'String', + ], + ], + $this->nodeToArray(Parser::parseType('String')) + ); } /** @@ -566,15 +628,18 @@ fragment $fragmentName on Type { */ public function testParsesCustomTypes() : void { - $this->assertEquals([ - 'kind' => NodeKind::NAMED_TYPE, - 'loc' => ['start' => 0, 'end' => 6], - 'name' => [ - 'kind' => NodeKind::NAME, - 'loc' => ['start' => 0, 'end' => 6], - 'value' => 'MyType' - ] - ], $this->nodeToArray(Parser::parseType('MyType'))); + $this->assertEquals( + [ + 'kind' => NodeKind::NAMED_TYPE, + 'loc' => ['start' => 0, 'end' => 6], + 'name' => [ + 'kind' => NodeKind::NAME, + 'loc' => ['start' => 0, 'end' => 6], + 'value' => 'MyType', + ], + ], + $this->nodeToArray(Parser::parseType('MyType')) + ); } /** @@ -582,19 +647,22 @@ fragment $fragmentName on Type { */ public function testParsesListTypes() : void { - $this->assertEquals([ - 'kind' => NodeKind::LIST_TYPE, - 'loc' => ['start' => 0, 'end' => 8], - 'type' => [ - 'kind' => NodeKind::NAMED_TYPE, - 'loc' => ['start' => 1, 'end' => 7], - 'name' => [ - 'kind' => NodeKind::NAME, - 'loc' => ['start' => 1, 'end' => 7], - 'value' => 'MyType' - ] - ] - ], $this->nodeToArray(Parser::parseType('[MyType]'))); + $this->assertEquals( + [ + 'kind' => NodeKind::LIST_TYPE, + 'loc' => ['start' => 0, 'end' => 8], + 'type' => [ + 'kind' => NodeKind::NAMED_TYPE, + 'loc' => ['start' => 1, 'end' => 7], + 'name' => [ + 'kind' => NodeKind::NAME, + 'loc' => ['start' => 1, 'end' => 7], + 'value' => 'MyType', + ], + ], + ], + $this->nodeToArray(Parser::parseType('[MyType]')) + ); } /** @@ -602,19 +670,22 @@ fragment $fragmentName on Type { */ public function testParsesNonNullTypes() : void { - $this->assertEquals([ - 'kind' => NodeKind::NON_NULL_TYPE, - 'loc' => ['start' => 0, 'end' => 7], - 'type' => [ - 'kind' => NodeKind::NAMED_TYPE, - 'loc' => ['start' => 0, 'end' => 6], - 'name' => [ - 'kind' => NodeKind::NAME, - 'loc' => ['start' => 0, 'end' => 6], - 'value' => 'MyType' - ] - ] - ], $this->nodeToArray(Parser::parseType('MyType!'))); + $this->assertEquals( + [ + 'kind' => NodeKind::NON_NULL_TYPE, + 'loc' => ['start' => 0, 'end' => 7], + 'type' => [ + 'kind' => NodeKind::NAMED_TYPE, + 'loc' => ['start' => 0, 'end' => 6], + 'name' => [ + 'kind' => NodeKind::NAME, + 'loc' => ['start' => 0, 'end' => 6], + 'value' => 'MyType', + ], + ], + ], + $this->nodeToArray(Parser::parseType('MyType!')) + ); } /** @@ -622,48 +693,25 @@ fragment $fragmentName on Type { */ public function testParsesNestedTypes() : void { - $this->assertEquals([ - 'kind' => NodeKind::LIST_TYPE, - 'loc' => ['start' => 0, 'end' => 9], - 'type' => [ - 'kind' => NodeKind::NON_NULL_TYPE, - 'loc' => ['start' => 1, 'end' => 8], + $this->assertEquals( + [ + 'kind' => NodeKind::LIST_TYPE, + 'loc' => ['start' => 0, 'end' => 9], 'type' => [ - 'kind' => NodeKind::NAMED_TYPE, - 'loc' => ['start' => 1, 'end' => 7], - 'name' => [ - 'kind' => NodeKind::NAME, - 'loc' => ['start' => 1, 'end' => 7], - 'value' => 'MyType' - ] - ] - ] - ], $this->nodeToArray(Parser::parseType('[MyType!]'))); - } - - /** - * @param Node $node - * @return array - */ - public static function nodeToArray(Node $node) - { - return TestUtils::nodeToArray($node); - } - - private function loc($line, $column) - { - return new SourceLocation($line, $column); - } - - private function expectSyntaxError($text, $message, $location) - { - $this->expectException(SyntaxError::class); - $this->expectExceptionMessage($message); - try { - Parser::parse($text); - } catch (SyntaxError $error) { - $this->assertEquals([$location], $error->getLocations()); - throw $error; - } + 'kind' => NodeKind::NON_NULL_TYPE, + 'loc' => ['start' => 1, 'end' => 8], + 'type' => [ + 'kind' => NodeKind::NAMED_TYPE, + 'loc' => ['start' => 1, 'end' => 7], + 'name' => [ + 'kind' => NodeKind::NAME, + 'loc' => ['start' => 1, 'end' => 7], + 'value' => 'MyType', + ], + ], + ], + ], + $this->nodeToArray(Parser::parseType('[MyType!]')) + ); } } diff --git a/tests/Language/PrinterTest.php b/tests/Language/PrinterTest.php index cba6b11..53312fb 100644 --- a/tests/Language/PrinterTest.php +++ b/tests/Language/PrinterTest.php @@ -1,18 +1,15 @@ cloneDeep(); $this->assertEquals($astCopy, $ast); @@ -66,7 +63,7 @@ class PrinterTest extends TestCase $this->assertEquals($expected, Printer::doPrint($queryAstShorthanded)); $mutationAst = Parser::parse('mutation { id, name }'); - $expected = 'mutation { + $expected = 'mutation { id name } @@ -76,7 +73,7 @@ class PrinterTest extends TestCase $queryAstWithArtifacts = Parser::parse( 'query ($foo: TestType) @testDirective { id, name }' ); - $expected = 'query ($foo: TestType) @testDirective { + $expected = 'query ($foo: TestType) @testDirective { id name } @@ -86,7 +83,7 @@ class PrinterTest extends TestCase $mutationAstWithArtifacts = Parser::parse( 'mutation ($foo: TestType) @testDirective { id, name }' ); - $expected = 'mutation ($foo: TestType) @testDirective { + $expected = 'mutation ($foo: TestType) @testDirective { id name } @@ -100,13 +97,13 @@ class PrinterTest extends TestCase public function testCorrectlyPrintsSingleLineBlockStringsWithLeadingSpace() : void { $mutationAstWithArtifacts = Parser::parse( - '{ field(arg: """ space-led value""") }' + '{ field(arg: """ space-led value""") }' ); - $expected = '{ + $expected = '{ 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 """) }' - ); - $expected = '{ + ); + $expected = '{ field(arg: """ first 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 = <<assertEquals($expected, Printer::doPrint($mutationAstWithArtifacts)); + $this->assertEquals($expected, Printer::doPrint($mutationAstWithArtifacts)); } /** @@ -202,7 +200,7 @@ END; public function testPrintsKitchenSink() : void { $kitchenSink = file_get_contents(__DIR__ . '/kitchen-sink.graphql'); - $ast = Parser::parse($kitchenSink); + $ast = Parser::parse($kitchenSink); $printed = Printer::doPrint($ast); diff --git a/tests/Language/SchemaParserTest.php b/tests/Language/SchemaParserTest.php index 465cf27..36571b7 100644 --- a/tests/Language/SchemaParserTest.php +++ b/tests/Language/SchemaParserTest.php @@ -1,4 +1,7 @@ NodeKind::DOCUMENT, + 'kind' => NodeKind::DOCUMENT, 'definitions' => [ [ - 'kind' => NodeKind::OBJECT_TYPE_DEFINITION, - 'name' => $this->nameNode('Hello', $loc(6, 11)), - 'interfaces' => [], - 'directives' => [], - 'fields' => [ + 'kind' => NodeKind::OBJECT_TYPE_DEFINITION, + 'name' => $this->nameNode('Hello', $loc(6, 11)), + 'interfaces' => [], + 'directives' => [], + 'fields' => [ $this->fieldNode( $this->nameNode('world', $loc(16, 21)), $this->typeNode('String', $loc(23, 29)), $loc(16, 29) - ) + ), ], - 'loc' => $loc(1, 31), - 'description' => null - ] + 'loc' => $loc(1, 31), + 'description' => null, + ], ], - 'loc' => $loc(0, 31) + 'loc' => $loc(0, 31), ]; $this->assertEquals($expected, TestUtils::nodeToArray($doc)); } + private function nameNode($name, $loc) + { + return [ + 'kind' => NodeKind::NAME, + 'value' => $name, + 'loc' => $loc, + ]; + } + + private function fieldNode($name, $type, $loc) + { + return $this->fieldNodeWithArgs($name, $type, [], $loc); + } + + private function fieldNodeWithArgs($name, $type, $args, $loc) + { + return [ + 'kind' => NodeKind::FIELD_DEFINITION, + 'name' => $name, + 'arguments' => $args, + 'type' => $type, + 'directives' => [], + 'loc' => $loc, + 'description' => null, + ]; + } + + private function typeNode($name, $loc) + { + return [ + 'kind' => NodeKind::NAMED_TYPE, + 'name' => ['kind' => NodeKind::NAME, 'value' => $name, 'loc' => $loc], + 'loc' => $loc, + ]; + } + /** * @see it('parses type with description string') */ @@ -57,34 +97,36 @@ type Hello { type Hello { world: String }'; - $doc = Parser::parse($body); - $loc = function($start, $end) {return TestUtils::locArray($start, $end);}; + $doc = Parser::parse($body); + $loc = function ($start, $end) { + return TestUtils::locArray($start, $end); + }; $expected = [ - 'kind' => NodeKind::DOCUMENT, + 'kind' => NodeKind::DOCUMENT, 'definitions' => [ [ - 'kind' => NodeKind::OBJECT_TYPE_DEFINITION, - 'name' => $this->nameNode('Hello', $loc(20, 25)), - 'interfaces' => [], - 'directives' => [], - 'fields' => [ + 'kind' => NodeKind::OBJECT_TYPE_DEFINITION, + 'name' => $this->nameNode('Hello', $loc(20, 25)), + 'interfaces' => [], + 'directives' => [], + 'fields' => [ $this->fieldNode( $this->nameNode('world', $loc(30, 35)), $this->typeNode('String', $loc(37, 43)), $loc(30, 43) - ) + ), ], - 'loc' => $loc(1, 45), + 'loc' => $loc(1, 45), 'description' => [ - 'kind' => NodeKind::STRING, + 'kind' => NodeKind::STRING, 'value' => 'Description', - 'loc' => $loc(1, 14), - 'block' => false - ] - ] + 'loc' => $loc(1, 14), + 'block' => false, + ], + ], ], - 'loc' => $loc(0, 45) + 'loc' => $loc(0, 45), ]; $this->assertEquals($expected, TestUtils::nodeToArray($doc)); } @@ -102,34 +144,36 @@ Description type Hello { world: String }'; - $doc = Parser::parse($body); - $loc = function($start, $end) {return TestUtils::locArray($start, $end);}; + $doc = Parser::parse($body); + $loc = function ($start, $end) { + return TestUtils::locArray($start, $end); + }; $expected = [ - 'kind' => NodeKind::DOCUMENT, + 'kind' => NodeKind::DOCUMENT, 'definitions' => [ [ - 'kind' => NodeKind::OBJECT_TYPE_DEFINITION, - 'name' => $this->nameNode('Hello', $loc(60, 65)), - 'interfaces' => [], - 'directives' => [], - 'fields' => [ + 'kind' => NodeKind::OBJECT_TYPE_DEFINITION, + 'name' => $this->nameNode('Hello', $loc(60, 65)), + 'interfaces' => [], + 'directives' => [], + 'fields' => [ $this->fieldNode( $this->nameNode('world', $loc(70, 75)), $this->typeNode('String', $loc(77, 83)), $loc(70, 83) - ) + ), ], - 'loc' => $loc(1, 85), + 'loc' => $loc(1, 85), 'description' => [ - 'kind' => NodeKind::STRING, + 'kind' => NodeKind::STRING, 'value' => 'Description', - 'loc' => $loc(1, 20), - 'block' => true - ] - ] + 'loc' => $loc(1, 20), + 'block' => true, + ], + ], ], - 'loc' => $loc(0, 85) + 'loc' => $loc(0, 85), ]; $this->assertEquals($expected, TestUtils::nodeToArray($doc)); } @@ -144,29 +188,30 @@ extend type Hello { world: String } '; - $doc = Parser::parse($body); - $loc = function($start, $end) { + $doc = Parser::parse($body); + $loc = function ($start, $end) { return TestUtils::locArray($start, $end); }; + $expected = [ - 'kind' => NodeKind::DOCUMENT, + 'kind' => NodeKind::DOCUMENT, 'definitions' => [ [ - 'kind' => NodeKind::OBJECT_TYPE_EXTENSION, - 'name' => $this->nameNode('Hello', $loc(13, 18)), + 'kind' => NodeKind::OBJECT_TYPE_EXTENSION, + 'name' => $this->nameNode('Hello', $loc(13, 18)), 'interfaces' => [], 'directives' => [], - 'fields' => [ + 'fields' => [ $this->fieldNode( $this->nameNode('world', $loc(23, 28)), $this->typeNode('String', $loc(30, 36)), $loc(23, 36) - ) + ), ], - 'loc' => $loc(1, 38) - ] + 'loc' => $loc(1, 38), + ], ], - 'loc' => $loc(0, 39) + 'loc' => $loc(0, 39), ]; $this->assertEquals($expected, TestUtils::nodeToArray($doc)); } @@ -177,25 +222,26 @@ extend type Hello { public function testExtensionWithoutFields() : void { $body = 'extend type Hello implements Greeting'; - $doc = Parser::parse($body); - $loc = function($start, $end) { + $doc = Parser::parse($body); + $loc = function ($start, $end) { return TestUtils::locArray($start, $end); }; + $expected = [ - 'kind' => NodeKind::DOCUMENT, + 'kind' => NodeKind::DOCUMENT, 'definitions' => [ [ - 'kind' => NodeKind::OBJECT_TYPE_EXTENSION, - 'name' => $this->nameNode('Hello', $loc(12, 17)), + 'kind' => NodeKind::OBJECT_TYPE_EXTENSION, + 'name' => $this->nameNode('Hello', $loc(12, 17)), 'interfaces' => [ $this->typeNode('Greeting', $loc(29, 37)), ], 'directives' => [], - 'fields' => [], - 'loc' => $loc(0, 37) - ] + 'fields' => [], + 'loc' => $loc(0, 37), + ], ], - 'loc' => $loc(0, 37) + 'loc' => $loc(0, 37), ]; $this->assertEquals($expected, TestUtils::nodeToArray($doc)); } @@ -205,33 +251,33 @@ extend type Hello { */ public function testExtensionWithoutFieldsFollowedByExtension() : void { - $body = ' + $body = ' extend type Hello implements Greeting extend type Hello implements SecondGreeting '; - $doc = Parser::parse($body); + $doc = Parser::parse($body); $expected = [ - 'kind' => 'Document', + 'kind' => 'Document', 'definitions' => [ [ - 'kind' => 'ObjectTypeExtension', - 'name' => $this->nameNode('Hello', ['start' => 23, 'end' => 28]), + 'kind' => 'ObjectTypeExtension', + 'name' => $this->nameNode('Hello', ['start' => 23, 'end' => 28]), 'interfaces' => [$this->typeNode('Greeting', ['start' => 40, 'end' => 48])], 'directives' => [], - 'fields' => [], - 'loc' => ['start' => 11, 'end' => 48], + 'fields' => [], + 'loc' => ['start' => 11, 'end' => 48], ], [ - 'kind' => 'ObjectTypeExtension', - 'name' => $this->nameNode('Hello', ['start' => 76, 'end' => 81]), + 'kind' => 'ObjectTypeExtension', + 'name' => $this->nameNode('Hello', ['start' => 76, 'end' => 81]), 'interfaces' => [$this->typeNode('SecondGreeting', ['start' => 93, 'end' => 107])], 'directives' => [], - 'fields' => [], - 'loc' => ['start' => 64, 'end' => 107], + 'fields' => [], + 'loc' => ['start' => 64, 'end' => 107], ], ], - 'loc' => ['start' => 0, 'end' => 116], + 'loc' => ['start' => 0, 'end' => 116], ]; $this->assertEquals($expected, $doc->toArray(true)); } @@ -248,6 +294,23 @@ extend type Hello { ); } + private function expectSyntaxError($text, $message, $location) + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage($message); + try { + Parser::parse($text); + } catch (SyntaxError $error) { + $this->assertEquals([$location], $error->getLocations()); + throw $error; + } + } + + private function loc($line, $column) + { + return new SourceLocation($line, $column); + } + /** * @see it('Extension do not include descriptions') */ @@ -291,35 +354,36 @@ extend type Hello { type Hello { world: String! }'; - $loc = function($start, $end) { + $doc = Parser::parse($body); + + $loc = function ($start, $end) { return TestUtils::locArray($start, $end); }; - $doc = Parser::parse($body); $expected = [ - 'kind' => NodeKind::DOCUMENT, + 'kind' => NodeKind::DOCUMENT, 'definitions' => [ [ - 'kind' => NodeKind::OBJECT_TYPE_DEFINITION, - 'name' => $this->nameNode('Hello', $loc(6,11)), - 'interfaces' => [], - 'directives' => [], - 'fields' => [ + 'kind' => NodeKind::OBJECT_TYPE_DEFINITION, + 'name' => $this->nameNode('Hello', $loc(6, 11)), + 'interfaces' => [], + 'directives' => [], + 'fields' => [ $this->fieldNode( $this->nameNode('world', $loc(16, 21)), [ 'kind' => NodeKind::NON_NULL_TYPE, 'type' => $this->typeNode('String', $loc(23, 29)), - 'loc' => $loc(23, 30) + 'loc' => $loc(23, 30), ], - $loc(16,30) - ) + $loc(16, 30) + ), ], - 'loc' => $loc(1,32), - 'description' => null - ] + 'loc' => $loc(1, 32), + 'description' => null, + ], ], - 'loc' => $loc(0,32) + 'loc' => $loc(0, 32), ]; $this->assertEquals($expected, TestUtils::nodeToArray($doc)); @@ -331,31 +395,33 @@ type Hello { public function testSimpleTypeInheritingInterface() : void { $body = 'type Hello implements World { field: String }'; - $loc = function($start, $end) { return TestUtils::locArray($start, $end); }; - $doc = Parser::parse($body); + $doc = Parser::parse($body); + $loc = function ($start, $end) { + return TestUtils::locArray($start, $end); + }; $expected = [ - 'kind' => NodeKind::DOCUMENT, + 'kind' => NodeKind::DOCUMENT, 'definitions' => [ [ - 'kind' => NodeKind::OBJECT_TYPE_DEFINITION, - 'name' => $this->nameNode('Hello', $loc(5, 10)), - 'interfaces' => [ - $this->typeNode('World', $loc(22, 27)) + 'kind' => NodeKind::OBJECT_TYPE_DEFINITION, + 'name' => $this->nameNode('Hello', $loc(5, 10)), + 'interfaces' => [ + $this->typeNode('World', $loc(22, 27)), ], - 'directives' => [], - 'fields' => [ + 'directives' => [], + 'fields' => [ $this->fieldNode( $this->nameNode('field', $loc(30, 35)), $this->typeNode('String', $loc(37, 43)), $loc(30, 43) - ) + ), ], - 'loc' => $loc(0, 45), - 'description' => null - ] + 'loc' => $loc(0, 45), + 'description' => null, + ], ], - 'loc' => $loc(0, 45) + 'loc' => $loc(0, 45), ]; $this->assertEquals($expected, TestUtils::nodeToArray($doc)); @@ -367,32 +433,34 @@ type Hello { public function testSimpleTypeInheritingMultipleInterfaces() : void { $body = 'type Hello implements Wo & rld { field: String }'; - $loc = function($start, $end) {return TestUtils::locArray($start, $end);}; - $doc = Parser::parse($body); + $doc = Parser::parse($body); + $loc = function ($start, $end) { + return TestUtils::locArray($start, $end); + }; $expected = [ - 'kind' => NodeKind::DOCUMENT, + 'kind' => NodeKind::DOCUMENT, 'definitions' => [ [ - 'kind' => NodeKind::OBJECT_TYPE_DEFINITION, - 'name' => $this->nameNode('Hello', $loc(5, 10)), - 'interfaces' => [ + 'kind' => NodeKind::OBJECT_TYPE_DEFINITION, + 'name' => $this->nameNode('Hello', $loc(5, 10)), + 'interfaces' => [ $this->typeNode('Wo', $loc(22, 24)), - $this->typeNode('rld', $loc(27, 30)) + $this->typeNode('rld', $loc(27, 30)), ], - 'directives' => [], - 'fields' => [ + 'directives' => [], + 'fields' => [ $this->fieldNode( $this->nameNode('field', $loc(33, 38)), $this->typeNode('String', $loc(40, 46)), $loc(33, 46) - ) + ), ], - 'loc' => $loc(0, 48), - 'description' => null - ] + 'loc' => $loc(0, 48), + 'description' => null, + ], ], - 'loc' => $loc(0, 48) + 'loc' => $loc(0, 48), ]; $this->assertEquals($expected, TestUtils::nodeToArray($doc)); @@ -404,31 +472,34 @@ type Hello { public function testSimpleTypeInheritingMultipleInterfacesWithLeadingAmpersand() : void { $body = 'type Hello implements & Wo & rld { field: String }'; - $loc = function($start, $end) {return TestUtils::locArray($start, $end);}; - $doc = Parser::parse($body); + $doc = Parser::parse($body); + $loc = function ($start, $end) { + return TestUtils::locArray($start, $end); + }; + $expected = [ - 'kind' => 'Document', + 'kind' => 'Document', 'definitions' => [ [ - 'kind' => 'ObjectTypeDefinition', - 'name' => $this->nameNode('Hello', $loc(5, 10)), - 'interfaces' => [ + 'kind' => 'ObjectTypeDefinition', + 'name' => $this->nameNode('Hello', $loc(5, 10)), + 'interfaces' => [ $this->typeNode('Wo', $loc(24, 26)), $this->typeNode('rld', $loc(29, 32)), ], - 'directives' => [], - 'fields' => [ + 'directives' => [], + 'fields' => [ $this->fieldNode( $this->nameNode('field', $loc(35, 40)), $this->typeNode('String', $loc(42, 48)), $loc(35, 48) ), ], - 'loc' => $loc(0, 50), + 'loc' => $loc(0, 50), 'description' => null, ], ], - 'loc' => $loc(0, 50), + 'loc' => $loc(0, 50), ]; $this->assertEquals($expected, TestUtils::nodeToArray($doc)); } @@ -439,52 +510,67 @@ type Hello { public function testSingleValueEnum() : void { $body = 'enum Hello { WORLD }'; - $loc = function($start, $end) {return TestUtils::locArray($start, $end);}; - $doc = Parser::parse($body); + $doc = Parser::parse($body); + $loc = function ($start, $end) { + return TestUtils::locArray($start, $end); + }; $expected = [ - 'kind' => NodeKind::DOCUMENT, + 'kind' => NodeKind::DOCUMENT, 'definitions' => [ [ - 'kind' => NodeKind::ENUM_TYPE_DEFINITION, - 'name' => $this->nameNode('Hello', $loc(5, 10)), - 'directives' => [], - 'values' => [$this->enumValueNode('WORLD', $loc(13, 18))], - 'loc' => $loc(0, 20), - 'description' => null - ] + 'kind' => NodeKind::ENUM_TYPE_DEFINITION, + 'name' => $this->nameNode('Hello', $loc(5, 10)), + 'directives' => [], + 'values' => [$this->enumValueNode('WORLD', $loc(13, 18))], + 'loc' => $loc(0, 20), + 'description' => null, + ], ], - 'loc' => $loc(0, 20) + 'loc' => $loc(0, 20), ]; $this->assertEquals($expected, TestUtils::nodeToArray($doc)); } + private function enumValueNode($name, $loc) + { + return [ + 'kind' => NodeKind::ENUM_VALUE_DEFINITION, + 'name' => $this->nameNode($name, $loc), + 'directives' => [], + 'loc' => $loc, + 'description' => null, + ]; + } + /** * @see it('Double value enum') */ public function testDoubleValueEnum() : void { $body = 'enum Hello { WO, RLD }'; - $loc = function($start, $end) {return TestUtils::locArray($start, $end);}; - $doc = Parser::parse($body); + $doc = Parser::parse($body); + $loc = function ($start, $end) { + return TestUtils::locArray($start, $end); + }; $expected = [ - 'kind' => NodeKind::DOCUMENT, + 'kind' => NodeKind::DOCUMENT, 'definitions' => [ [ - 'kind' => NodeKind::ENUM_TYPE_DEFINITION, - 'name' => $this->nameNode('Hello', $loc(5, 10)), - 'directives' => [], - 'values' => [ + 'kind' => NodeKind::ENUM_TYPE_DEFINITION, + 'name' => $this->nameNode('Hello', $loc(5, 10)), + 'directives' => [], + 'values' => [ $this->enumValueNode('WO', $loc(13, 15)), - $this->enumValueNode('RLD', $loc(17, 20)) + $this->enumValueNode('RLD', $loc(17, 20)), ], - 'loc' => $loc(0, 22), - 'description' => null - ] + 'loc' => $loc(0, 22), + 'description' => null, + ], ], - 'loc' => $loc(0, 22) + 'loc' => $loc(0, 22), ]; $this->assertEquals($expected, TestUtils::nodeToArray($doc)); @@ -499,28 +585,30 @@ type Hello { interface Hello { world: String }'; - $doc = Parser::parse($body); - $loc = function($start, $end) {return TestUtils::locArray($start, $end);}; + $doc = Parser::parse($body); + $loc = function ($start, $end) { + return TestUtils::locArray($start, $end); + }; $expected = [ - 'kind' => NodeKind::DOCUMENT, + 'kind' => NodeKind::DOCUMENT, 'definitions' => [ [ - 'kind' => NodeKind::INTERFACE_TYPE_DEFINITION, - 'name' => $this->nameNode('Hello', $loc(11, 16)), - 'directives' => [], - 'fields' => [ + 'kind' => NodeKind::INTERFACE_TYPE_DEFINITION, + 'name' => $this->nameNode('Hello', $loc(11, 16)), + 'directives' => [], + 'fields' => [ $this->fieldNode( $this->nameNode('world', $loc(21, 26)), $this->typeNode('String', $loc(28, 34)), $loc(21, 34) - ) + ), ], - 'loc' => $loc(1, 36), - 'description' => null - ] + 'loc' => $loc(1, 36), + 'description' => null, + ], ], - 'loc' => $loc(0,36) + 'loc' => $loc(0, 36), ]; $this->assertEquals($expected, TestUtils::nodeToArray($doc)); } @@ -534,18 +622,20 @@ interface Hello { type Hello { world(flag: Boolean): String }'; - $doc = Parser::parse($body); - $loc = function($start, $end) {return TestUtils::locArray($start, $end);}; + $doc = Parser::parse($body); + $loc = function ($start, $end) { + return TestUtils::locArray($start, $end); + }; $expected = [ - 'kind' => NodeKind::DOCUMENT, + 'kind' => NodeKind::DOCUMENT, 'definitions' => [ [ - 'kind' => NodeKind::OBJECT_TYPE_DEFINITION, - 'name' => $this->nameNode('Hello', $loc(6, 11)), - 'interfaces' => [], - 'directives' => [], - 'fields' => [ + 'kind' => NodeKind::OBJECT_TYPE_DEFINITION, + 'name' => $this->nameNode('Hello', $loc(6, 11)), + 'interfaces' => [], + 'directives' => [], + 'fields' => [ $this->fieldNodeWithArgs( $this->nameNode('world', $loc(16, 21)), $this->typeNode('String', $loc(38, 44)), @@ -555,21 +645,34 @@ type Hello { $this->typeNode('Boolean', $loc(28, 35)), null, $loc(22, 35) - ) + ), ], $loc(16, 44) - ) + ), ], - 'loc' => $loc(1, 46), - 'description' => null - ] + 'loc' => $loc(1, 46), + 'description' => null, + ], ], - 'loc' => $loc(0, 46) + 'loc' => $loc(0, 46), ]; $this->assertEquals($expected, TestUtils::nodeToArray($doc)); } + private function inputValueNode($name, $type, $defaultValue, $loc) + { + return [ + 'kind' => NodeKind::INPUT_VALUE_DEFINITION, + 'name' => $name, + 'type' => $type, + 'defaultValue' => $defaultValue, + 'directives' => [], + 'loc' => $loc, + 'description' => null, + ]; + } + /** * @see it('Simple field with arg with default value') */ @@ -579,18 +682,20 @@ type Hello { type Hello { world(flag: Boolean = true): String }'; - $doc = Parser::parse($body); - $loc = function($start, $end) {return TestUtils::locArray($start, $end);}; + $doc = Parser::parse($body); + $loc = function ($start, $end) { + return TestUtils::locArray($start, $end); + }; $expected = [ - 'kind' => NodeKind::DOCUMENT, + 'kind' => NodeKind::DOCUMENT, 'definitions' => [ [ - 'kind' => NodeKind::OBJECT_TYPE_DEFINITION, - 'name' => $this->nameNode('Hello', $loc(6, 11)), - 'interfaces' => [], - 'directives' => [], - 'fields' => [ + 'kind' => NodeKind::OBJECT_TYPE_DEFINITION, + 'name' => $this->nameNode('Hello', $loc(6, 11)), + 'interfaces' => [], + 'directives' => [], + 'fields' => [ $this->fieldNodeWithArgs( $this->nameNode('world', $loc(16, 21)), $this->typeNode('String', $loc(45, 51)), @@ -600,16 +705,16 @@ type Hello { $this->typeNode('Boolean', $loc(28, 35)), ['kind' => NodeKind::BOOLEAN, 'value' => true, 'loc' => $loc(38, 42)], $loc(22, 42) - ) + ), ], $loc(16, 51) - ) + ), ], - 'loc' => $loc(1, 53), - 'description' => null - ] + 'loc' => $loc(1, 53), + 'description' => null, + ], ], - 'loc' => $loc(0, 53) + 'loc' => $loc(0, 53), ]; $this->assertEquals($expected, TestUtils::nodeToArray($doc)); } @@ -623,37 +728,45 @@ type Hello { type Hello { world(things: [String]): String }'; - $doc = Parser::parse($body); - $loc = function($start, $end) {return TestUtils::locArray($start, $end);}; + $doc = Parser::parse($body); + $loc = function ($start, $end) { + return TestUtils::locArray($start, $end); + }; $expected = [ - 'kind' => NodeKind::DOCUMENT, + 'kind' => NodeKind::DOCUMENT, 'definitions' => [ [ - 'kind' => NodeKind::OBJECT_TYPE_DEFINITION, - 'name' => $this->nameNode('Hello', $loc(6, 11)), - 'interfaces' => [], - 'directives' => [], - 'fields' => [ + 'kind' => NodeKind::OBJECT_TYPE_DEFINITION, + 'name' => $this->nameNode('Hello', $loc(6, 11)), + 'interfaces' => [], + 'directives' => [], + 'fields' => [ $this->fieldNodeWithArgs( $this->nameNode('world', $loc(16, 21)), $this->typeNode('String', $loc(41, 47)), [ $this->inputValueNode( - $this->nameNode('things', $loc(22,28)), - ['kind' => NodeKind::LIST_TYPE, 'type' => $this->typeNode('String', $loc(31, 37)), 'loc' => $loc(30, 38)], + $this->nameNode('things', $loc(22, 28)), + [ + 'kind' => NodeKind::LIST_TYPE, + 'type' => $this->typeNode( + 'String', + $loc(31, 37) + ), 'loc' => $loc(30, 38), + ], null, $loc(22, 38) - ) + ), ], $loc(16, 47) - ) + ), ], - 'loc' => $loc(1, 49), - 'description' => null - ] + 'loc' => $loc(1, 49), + 'description' => null, + ], ], - 'loc' => $loc(0, 49) + 'loc' => $loc(0, 49), ]; $this->assertEquals($expected, TestUtils::nodeToArray($doc)); @@ -668,18 +781,20 @@ type Hello { type Hello { world(argOne: Boolean, argTwo: Int): String }'; - $doc = Parser::parse($body); - $loc = function($start, $end) {return TestUtils::locArray($start, $end);}; + $doc = Parser::parse($body); + $loc = function ($start, $end) { + return TestUtils::locArray($start, $end); + }; $expected = [ - 'kind' => NodeKind::DOCUMENT, + 'kind' => NodeKind::DOCUMENT, 'definitions' => [ [ - 'kind' => NodeKind::OBJECT_TYPE_DEFINITION, - 'name' => $this->nameNode('Hello', $loc(6, 11)), - 'interfaces' => [], - 'directives' => [], - 'fields' => [ + 'kind' => NodeKind::OBJECT_TYPE_DEFINITION, + 'name' => $this->nameNode('Hello', $loc(6, 11)), + 'interfaces' => [], + 'directives' => [], + 'fields' => [ $this->fieldNodeWithArgs( $this->nameNode('world', $loc(16, 21)), $this->typeNode('String', $loc(53, 59)), @@ -695,16 +810,16 @@ type Hello { $this->typeNode('Int', $loc(47, 50)), null, $loc(39, 50) - ) + ), ], $loc(16, 59) - ) + ), ], - 'loc' => $loc(1, 61), - 'description' => null - ] + 'loc' => $loc(1, 61), + 'description' => null, + ], ], - 'loc' => $loc(0, 61) + 'loc' => $loc(0, 61), ]; $this->assertEquals($expected, TestUtils::nodeToArray($doc)); @@ -716,21 +831,24 @@ type Hello { public function testSimpleUnion() : void { $body = 'union Hello = World'; - $doc = Parser::parse($body); - $loc = function($start, $end) {return TestUtils::locArray($start, $end);}; + $doc = Parser::parse($body); + $loc = function ($start, $end) { + return TestUtils::locArray($start, $end); + }; + $expected = [ - 'kind' => NodeKind::DOCUMENT, + 'kind' => NodeKind::DOCUMENT, 'definitions' => [ [ - 'kind' => NodeKind::UNION_TYPE_DEFINITION, - 'name' => $this->nameNode('Hello', $loc(6, 11)), - 'directives' => [], - 'types' => [$this->typeNode('World', $loc(14, 19))], - 'loc' => $loc(0, 19), - 'description' => null - ] + 'kind' => NodeKind::UNION_TYPE_DEFINITION, + 'name' => $this->nameNode('Hello', $loc(6, 11)), + 'directives' => [], + 'types' => [$this->typeNode('World', $loc(14, 19))], + 'loc' => $loc(0, 19), + 'description' => null, + ], ], - 'loc' => $loc(0, 19) + 'loc' => $loc(0, 19), ]; $this->assertEquals($expected, TestUtils::nodeToArray($doc)); @@ -742,53 +860,54 @@ type Hello { public function testUnionWithTwoTypes() : void { $body = 'union Hello = Wo | Rld'; - $doc = Parser::parse($body); - $loc = function($start, $end) {return TestUtils::locArray($start, $end);}; + $doc = Parser::parse($body); + $loc = function ($start, $end) { + return TestUtils::locArray($start, $end); + }; $expected = [ - 'kind' => NodeKind::DOCUMENT, + 'kind' => NodeKind::DOCUMENT, 'definitions' => [ [ - 'kind' => NodeKind::UNION_TYPE_DEFINITION, - 'name' => $this->nameNode('Hello', $loc(6, 11)), - 'directives' => [], - 'types' => [ + 'kind' => NodeKind::UNION_TYPE_DEFINITION, + 'name' => $this->nameNode('Hello', $loc(6, 11)), + 'directives' => [], + 'types' => [ $this->typeNode('Wo', $loc(14, 16)), - $this->typeNode('Rld', $loc(19, 22)) + $this->typeNode('Rld', $loc(19, 22)), ], - 'loc' => $loc(0, 22), - 'description' => null - ] + 'loc' => $loc(0, 22), + 'description' => null, + ], ], - 'loc' => $loc(0, 22) + 'loc' => $loc(0, 22), ]; $this->assertEquals($expected, TestUtils::nodeToArray($doc)); } - /** * @see it('Union with two types and leading pipe') */ public function testUnionWithTwoTypesAndLeadingPipe() : void { - $body = 'union Hello = | Wo | Rld'; - $doc = Parser::parse($body); + $body = 'union Hello = | Wo | Rld'; + $doc = Parser::parse($body); $expected = [ - 'kind' => 'Document', + 'kind' => 'Document', 'definitions' => [ [ - 'kind' => 'UnionTypeDefinition', - 'name' => $this->nameNode('Hello', ['start' => 6, 'end' => 11]), - 'directives' => [], - 'types' => [ + 'kind' => 'UnionTypeDefinition', + 'name' => $this->nameNode('Hello', ['start' => 6, 'end' => 11]), + 'directives' => [], + 'types' => [ $this->typeNode('Wo', ['start' => 16, 'end' => 18]), $this->typeNode('Rld', ['start' => 21, 'end' => 24]), ], - 'loc' => ['start' => 0, 'end' => 24], - 'description' => null - ] + 'loc' => ['start' => 0, 'end' => 24], + 'description' => null, + ], ], - 'loc' => ['start' => 0, 'end' => 24], + 'loc' => ['start' => 0, 'end' => 24], ]; $this->assertEquals($expected, TestUtils::nodeToArray($doc)); } @@ -847,20 +966,23 @@ type Hello { public function testScalar() : void { $body = 'scalar Hello'; - $doc = Parser::parse($body); - $loc = function($start, $end) {return TestUtils::locArray($start, $end);}; + $doc = Parser::parse($body); + $loc = function ($start, $end) { + return TestUtils::locArray($start, $end); + }; + $expected = [ - 'kind' => NodeKind::DOCUMENT, + 'kind' => NodeKind::DOCUMENT, 'definitions' => [ [ - 'kind' => NodeKind::SCALAR_TYPE_DEFINITION, - 'name' => $this->nameNode('Hello', $loc(7, 12)), - 'directives' => [], - 'loc' => $loc(0, 12), - 'description' => null - ] + 'kind' => NodeKind::SCALAR_TYPE_DEFINITION, + 'name' => $this->nameNode('Hello', $loc(7, 12)), + 'directives' => [], + 'loc' => $loc(0, 12), + 'description' => null, + ], ], - 'loc' => $loc(0, 12) + 'loc' => $loc(0, 12), ]; $this->assertEquals($expected, TestUtils::nodeToArray($doc)); } @@ -874,29 +996,31 @@ type Hello { input Hello { world: String }'; - $doc = Parser::parse($body); - $loc = function($start, $end) {return TestUtils::locArray($start, $end);}; + $doc = Parser::parse($body); + $loc = function ($start, $end) { + return TestUtils::locArray($start, $end); + }; $expected = [ - 'kind' => NodeKind::DOCUMENT, + 'kind' => NodeKind::DOCUMENT, 'definitions' => [ [ - 'kind' => NodeKind::INPUT_OBJECT_TYPE_DEFINITION, - 'name' => $this->nameNode('Hello', $loc(7, 12)), - 'directives' => [], - 'fields' => [ + 'kind' => NodeKind::INPUT_OBJECT_TYPE_DEFINITION, + 'name' => $this->nameNode('Hello', $loc(7, 12)), + 'directives' => [], + 'fields' => [ $this->inputValueNode( $this->nameNode('world', $loc(17, 22)), $this->typeNode('String', $loc(24, 30)), null, $loc(17, 30) - ) + ), ], - 'loc' => $loc(1, 32), - 'description' => null - ] + 'loc' => $loc(1, 32), + 'description' => null, + ], ], - 'loc' => $loc(0, 32) + 'loc' => $loc(0, 32), ]; $this->assertEquals($expected, TestUtils::nodeToArray($doc)); } @@ -943,8 +1067,8 @@ input Hello { */ public function testAllowLegacySDLEmptyFieldsOption() : void { - $body = 'type Hello { }'; - $doc = Parser::parse($body, ['allowLegacySDLEmptyFields' => true]); + $body = 'type Hello { }'; + $doc = Parser::parse($body, ['allowLegacySDLEmptyFields' => true]); $expected = [ 'definitions' => [ [ @@ -966,8 +1090,8 @@ input Hello { */ public function testDefaultSDLImplementsInterfaces() : void { - $body = 'type Hello implements Wo rld { field: String }'; - $doc = Parser::parse($body, ['allowLegacySDLImplementsInterfaces' => true]); + $body = 'type Hello implements Wo rld { field: String }'; + $doc = Parser::parse($body, ['allowLegacySDLImplementsInterfaces' => true]); $expected = [ 'definitions' => [ [ @@ -980,81 +1104,4 @@ input Hello { ]; $this->assertArraySubset($expected, $doc->toArray(true)); } - - private function typeNode($name, $loc) - { - return [ - 'kind' => NodeKind::NAMED_TYPE, - 'name' => ['kind' => NodeKind::NAME, 'value' => $name, 'loc' => $loc], - 'loc' => $loc - ]; - } - - private function nameNode($name, $loc) - { - return [ - 'kind' => NodeKind::NAME, - 'value' => $name, - 'loc' => $loc - ]; - } - - private function fieldNode($name, $type, $loc) - { - return $this->fieldNodeWithArgs($name, $type, [], $loc); - } - - private function fieldNodeWithArgs($name, $type, $args, $loc) - { - return [ - 'kind' => NodeKind::FIELD_DEFINITION, - 'name' => $name, - 'arguments' => $args, - 'type' => $type, - 'directives' => [], - 'loc' => $loc, - 'description' => null - ]; - } - - private function enumValueNode($name, $loc) - { - return [ - 'kind' => NodeKind::ENUM_VALUE_DEFINITION, - 'name' => $this->nameNode($name, $loc), - 'directives' => [], - 'loc' => $loc, - 'description' => null - ]; - } - - private function inputValueNode($name, $type, $defaultValue, $loc) - { - return [ - 'kind' => NodeKind::INPUT_VALUE_DEFINITION, - 'name' => $name, - 'type' => $type, - 'defaultValue' => $defaultValue, - 'directives' => [], - 'loc' => $loc, - 'description' => null - ]; - } - - private function loc($line, $column) - { - return new SourceLocation($line, $column); - } - - private function expectSyntaxError($text, $message, $location) - { - $this->expectException(SyntaxError::class); - $this->expectExceptionMessage($message); - try { - Parser::parse($text); - } catch (SyntaxError $error) { - $this->assertEquals([$location], $error->getLocations()); - throw $error; - } - } } diff --git a/tests/Language/SchemaPrinterTest.php b/tests/Language/SchemaPrinterTest.php index b0cc05f..8c90401 100644 --- a/tests/Language/SchemaPrinterTest.php +++ b/tests/Language/SchemaPrinterTest.php @@ -1,5 +1,8 @@ new NameNode(['value' => 'foo']) + 'name' => new NameNode(['value' => 'foo']), ]); $this->assertEquals('scalar foo', Printer::doPrint($ast)); } @@ -41,7 +45,7 @@ class SchemaPrinterTest extends TestCase { $kitchenSink = file_get_contents(__DIR__ . '/schema-kitchen-sink.graphql'); - $ast = Parser::parse($kitchenSink); + $ast = Parser::parse($kitchenSink); $astCopy = $ast->cloneDeep(); Printer::doPrint($ast); @@ -52,7 +56,7 @@ class SchemaPrinterTest extends TestCase { $kitchenSink = file_get_contents(__DIR__ . '/schema-kitchen-sink.graphql'); - $ast = Parser::parse($kitchenSink); + $ast = Parser::parse($kitchenSink); $printed = Printer::doPrint($ast); $expected = 'schema { diff --git a/tests/Language/SerializationTest.php b/tests/Language/SerializationTest.php index 7624fdc..403dbfb 100644 --- a/tests/Language/SerializationTest.php +++ b/tests/Language/SerializationTest.php @@ -1,5 +1,8 @@ assertEquals($expectedAst, $ast->toArray(true)); } 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); - $actualAst = AST::fromArray($serializedAst); - $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]); + $actualAst = AST::fromArray($serializedAst); + $parsedAst = Parser::parse($kitchenSink); $this->assertNodesAreEqual($parsedAst, $actualAst); } /** * Compares two nodes by actually iterating over all NodeLists, properly comparing locations (ignoring tokens), etc * - * @param $expected - * @param $actual - * @param array $path + * @param string[] $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->assertEquals(get_class($expected), get_class($actual), $err); $expectedVars = get_object_vars($expected); - $actualVars = get_object_vars($actual); - $this->assertSame(count($expectedVars), count($actualVars), $err); + $actualVars = get_object_vars($actual); + $this->assertCount(count($expectedVars), $actualVars, $err); $this->assertEquals(array_keys($expectedVars), array_keys($actualVars), $err); foreach ($expectedVars as $name => $expectedValue) { $actualValue = $actualVars[$name]; - $tmpPath = $path; - $tmpPath[] = $name; - $err = "Mismatch at AST path: " . implode(', ', $tmpPath); + $tmpPath = $path; + $tmpPath[] = $name; + $err = 'Mismatch at AST path: ' . implode(', ', $tmpPath); if ($expectedValue instanceof Node) { $this->assertNodesAreEqual($expectedValue, $actualValue, $tmpPath); - } else if ($expectedValue instanceof NodeList) { + } elseif ($expectedValue instanceof NodeList) { $this->assertEquals(count($expectedValue), count($actualValue), $err); $this->assertInstanceOf(NodeList::class, $actualValue, $err); foreach ($expectedValue as $index => $listNode) { - $tmpPath2 = $tmpPath; - $tmpPath2 [] = $index; + $tmpPath2 = $tmpPath; + $tmpPath2[] = $index; $this->assertNodesAreEqual($listNode, $actualValue[$index], $tmpPath2); } - } else if ($expectedValue instanceof Location) { + } elseif ($expectedValue instanceof Location) { $this->assertInstanceOf(Location::class, $actualValue, $err); $this->assertSame($expectedValue->start, $actualValue->start, $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); + } } diff --git a/tests/Language/TestUtils.php b/tests/Language/TestUtils.php index b36377c..c6e3cbe 100644 --- a/tests/Language/TestUtils.php +++ b/tests/Language/TestUtils.php @@ -1,36 +1,41 @@ $node->kind, - 'loc' => self::locationToArray($node->loc) + 'loc' => self::locationToArray($node->loc), ]; foreach (get_object_vars($node) as $prop => $propValue) { - if (isset($result[$prop])) + if (isset($result[$prop])) { continue; + } if (is_array($propValue) || $propValue instanceof NodeList) { $tmp = []; foreach ($propValue as $tmp1) { $tmp[] = $tmp1 instanceof Node ? self::nodeToArray($tmp1) : (array) $tmp1; } - } else if ($propValue instanceof Node) { + } elseif ($propValue instanceof Node) { $tmp = self::nodeToArray($propValue); - } else if (is_scalar($propValue) || null === $propValue) { + } elseif (is_scalar($propValue) || $propValue === null) { $tmp = $propValue; } else { $tmp = null; @@ -38,27 +43,25 @@ class TestUtils $result[$prop] = $tmp; } + return $result; } /** - * @param Location $loc - * @return array + * @return int[] */ - public static function locationToArray(Location $loc) + public static function locationToArray(Location $loc) : array { return [ 'start' => $loc->start, - 'end' => $loc->end + 'end' => $loc->end, ]; } /** - * @param $start - * @param $end - * @return array + * @return int[] */ - public static function locArray($start, $end) + public static function locArray(int $start, int $end) : array { return ['start' => $start, 'end' => $end]; } diff --git a/tests/Language/TokenTest.php b/tests/Language/TokenTest.php index ea21d20..54fc245 100644 --- a/tests/Language/TokenTest.php +++ b/tests/Language/TokenTest.php @@ -1,5 +1,8 @@ 'Kind', - 'value' => null, - 'line' => 3, - 'column' => 5 + 'kind' => 'Kind', + 'value' => null, + 'line' => 3, + 'column' => 5, ]; $this->assertEquals($expected, $token->toArray()); diff --git a/tests/Language/VisitorTest.php b/tests/Language/VisitorTest.php index 300dea8..5c62397 100644 --- a/tests/Language/VisitorTest.php +++ b/tests/Language/VisitorTest.php @@ -29,15 +29,40 @@ use function iterator_to_array; class VisitorTest extends ValidatorTestCase { - private function getNodeByPath(DocumentNode $ast, $path) + public function testValidatesPathArgument() : void { - $result = $ast; - foreach ($path as $key) { - $resultArray = $result instanceof NodeList ? iterator_to_array($result) : $result->toArray(); - $this->assertArrayHasKey($key, $resultArray); - $result = $resultArray[$key]; - } - return $result; + $visited = []; + + $ast = Parser::parse('{ a }', ['noLocation' => true]); + + Visitor::visit( + $ast, + [ + 'enter' => function ($node, $key, $parent, $path) use ($ast, &$visited) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['enter', $path]; + }, + 'leave' => function ($node, $key, $parent, $path) use ($ast, &$visited) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['leave', $path]; + }, + ] + ); + + $expected = [ + ['enter', []], + ['enter', ['definitions', 0]], + ['enter', ['definitions', 0, 'selectionSet']], + ['enter', ['definitions', 0, 'selectionSet', 'selections', 0]], + ['enter', ['definitions', 0, 'selectionSet', 'selections', 0, 'name']], + ['leave', ['definitions', 0, 'selectionSet', 'selections', 0, 'name']], + ['leave', ['definitions', 0, 'selectionSet', 'selections', 0]], + ['leave', ['definitions', 0, 'selectionSet']], + ['leave', ['definitions', 0]], + ['leave', []], + ]; + + $this->assertEquals($expected, $visited); } private function checkVisitorFnArgs($ast, $args, $isEdited = false) @@ -58,6 +83,7 @@ class VisitorTest extends ValidatorTestCase $this->assertEquals(null, $parent); $this->assertEquals([], $path); $this->assertEquals([], $ancestors); + return; } @@ -84,66 +110,50 @@ class VisitorTest extends ValidatorTestCase } } - public function testValidatesPathArgument() : void + private function getNodeByPath(DocumentNode $ast, $path) { - $visited = []; + $result = $ast; + foreach ($path as $key) { + $resultArray = $result instanceof NodeList ? iterator_to_array($result) : $result->toArray(); + $this->assertArrayHasKey($key, $resultArray); + $result = $resultArray[$key]; + } - $ast = Parser::parse('{ a }', ['noLocation' => true]); - - Visitor::visit($ast, [ - 'enter' => function ($node, $key, $parent, $path) use ($ast, &$visited) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['enter', $path]; - }, - 'leave' => function ($node, $key, $parent, $path) use ($ast, &$visited) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['leave', $path]; - }, - ]); - - $expected = [ - ['enter', []], - ['enter', ['definitions', 0]], - ['enter', ['definitions', 0, 'selectionSet']], - ['enter', ['definitions', 0, 'selectionSet', 'selections', 0]], - ['enter', ['definitions', 0, 'selectionSet', 'selections', 0, 'name']], - ['leave', ['definitions', 0, 'selectionSet', 'selections', 0, 'name']], - ['leave', ['definitions', 0, 'selectionSet', 'selections', 0]], - ['leave', ['definitions', 0, 'selectionSet']], - ['leave', ['definitions', 0]], - ['leave', []], - ]; - - $this->assertEquals($expected, $visited); + return $result; } public function testAllowsEditingNodeOnEnterAndOnLeave() : void { - $ast = Parser::parse('{ a, b, c { a, b, c } }', [ 'noLocation' => true ]); + $ast = Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]); $selectionSet = null; - $editedAst = Visitor::visit($ast, [ - NodeKind::OPERATION_DEFINITION => [ - 'enter' => function (OperationDefinitionNode $node) use (&$selectionSet, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $selectionSet = $node->selectionSet; + $editedAst = Visitor::visit( + $ast, + [ + NodeKind::OPERATION_DEFINITION => [ + 'enter' => function (OperationDefinitionNode $node) use (&$selectionSet, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $selectionSet = $node->selectionSet; - $newNode = clone $node; - $newNode->selectionSet = new SelectionSetNode([ - 'selections' => [], - ]); - $newNode->didEnter = true; - return $newNode; - }, - 'leave' => function (OperationDefinitionNode $node) use (&$selectionSet, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args(), true); - $newNode = clone $node; - $newNode->selectionSet = $selectionSet; - $newNode->didLeave = true; - return $newNode; - }, - ], - ]); + $newNode = clone $node; + $newNode->selectionSet = new SelectionSetNode([ + 'selections' => [], + ]); + $newNode->didEnter = true; + + return $newNode; + }, + 'leave' => function (OperationDefinitionNode $node) use (&$selectionSet, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args(), true); + $newNode = clone $node; + $newNode->selectionSet = $selectionSet; + $newNode->didLeave = true; + + return $newNode; + }, + ], + ] + ); $this->assertNotEquals($ast, $editedAst); @@ -156,25 +166,29 @@ class VisitorTest extends ValidatorTestCase public function testAllowsEditingRootNodeOnEnterAndLeave() : void { - $ast = Parser::parse('{ a, b, c { a, b, c } }', [ 'noLocation' => true ]); + $ast = Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]); $definitions = $ast->definitions; - $editedAst = Visitor::visit($ast, [ - NodeKind::DOCUMENT => [ - 'enter' => function (DocumentNode $node) use ($ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $tmp = clone $node; - $tmp->definitions = []; - $tmp->didEnter = true; - return $tmp; - }, - 'leave' => function (DocumentNode $node) use ($definitions, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args(), true); - $node->definitions = $definitions; - $node->didLeave = true; - }, - ], - ]); + $editedAst = Visitor::visit( + $ast, + [ + NodeKind::DOCUMENT => [ + 'enter' => function (DocumentNode $node) use ($ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $tmp = clone $node; + $tmp->definitions = []; + $tmp->didEnter = true; + + return $tmp; + }, + 'leave' => function (DocumentNode $node) use ($definitions, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args(), true); + $node->definitions = $definitions; + $node->didLeave = true; + }, + ], + ] + ); $this->assertNotEquals($ast, $editedAst); @@ -188,14 +202,17 @@ class VisitorTest extends ValidatorTestCase public function testAllowsForEditingOnEnter() : void { $ast = Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]); - $editedAst = Visitor::visit($ast, [ - 'enter' => function ($node) use ($ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - if ($node instanceof FieldNode && $node->name->value === 'b') { - return Visitor::removeNode(); - } - }, - ]); + $editedAst = Visitor::visit( + $ast, + [ + 'enter' => function ($node) use ($ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + if ($node instanceof FieldNode && $node->name->value === 'b') { + return Visitor::removeNode(); + } + }, + ] + ); $this->assertEquals( Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]), @@ -210,14 +227,17 @@ class VisitorTest extends ValidatorTestCase public function testAllowsForEditingOnLeave() : void { $ast = Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]); - $editedAst = Visitor::visit($ast, [ - 'leave' => function ($node) use ($ast) { - $this->checkVisitorFnArgs($ast, func_get_args(), true); - if ($node instanceof FieldNode && $node->name->value === 'b') { - return Visitor::removeNode(); - } - }, - ]); + $editedAst = Visitor::visit( + $ast, + [ + 'leave' => function ($node) use ($ast) { + $this->checkVisitorFnArgs($ast, func_get_args(), true); + if ($node instanceof FieldNode && $node->name->value === 'b') { + return Visitor::removeNode(); + } + }, + ] + ); $this->assertEquals( Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]), @@ -240,23 +260,26 @@ class VisitorTest extends ValidatorTestCase $ast = Parser::parse('{ a { x } }', ['noLocation' => true]); - Visitor::visit($ast, [ - 'enter' => function ($node) use ($addedField, &$didVisitAddedField, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args(), true); - if ($node instanceof FieldNode && $node->name->value === 'a') { - return new FieldNode([ - 'selectionSet' => new SelectionSetNode([ - 'selections' => NodeList::create([$addedField])->merge($node->selectionSet->selections), - ]), - ]); - } - if ($node !== $addedField) { - return; - } + Visitor::visit( + $ast, + [ + 'enter' => function ($node) use ($addedField, &$didVisitAddedField, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args(), true); + if ($node instanceof FieldNode && $node->name->value === 'a') { + return new FieldNode([ + 'selectionSet' => new SelectionSetNode([ + 'selections' => NodeList::create([$addedField])->merge($node->selectionSet->selections), + ]), + ]); + } + if ($node !== $addedField) { + return; + } - $didVisitAddedField = true; - }, - ]); + $didVisitAddedField = true; + }, + ] + ); $this->assertTrue($didVisitAddedField); } @@ -266,36 +289,39 @@ class VisitorTest extends ValidatorTestCase $visited = []; $ast = Parser::parse('{ a, b { x }, c }', ['noLocation' => true]); - Visitor::visit($ast, [ - 'enter' => function (Node $node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['enter', $node->kind, $node->value ?? null]; - if ($node instanceof FieldNode && $node->name->value === 'b') { - return Visitor::skipNode(); - } - }, - 'leave' => function (Node $node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['leave', $node->kind, $node->value ?? null]; - }, - ]); + Visitor::visit( + $ast, + [ + 'enter' => function (Node $node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['enter', $node->kind, $node->value ?? null]; + if ($node instanceof FieldNode && $node->name->value === 'b') { + return Visitor::skipNode(); + } + }, + 'leave' => function (Node $node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['leave', $node->kind, $node->value ?? null]; + }, + ] + ); $expected = [ - [ '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 ], + ['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], ]; $this->assertEquals($expected, $visited); @@ -306,34 +332,37 @@ class VisitorTest extends ValidatorTestCase $visited = []; $ast = Parser::parse('{ a, b { x }, c }', ['noLocation' => true]); - Visitor::visit($ast, [ - 'enter' => function (Node $node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['enter', $node->kind, $node->value ?? null]; - if ($node instanceof NameNode && $node->value === 'x') { - return Visitor::stop(); - } - }, - 'leave' => function (Node $node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['leave', $node->kind, $node->value ?? null]; - }, - ]); + Visitor::visit( + $ast, + [ + 'enter' => function (Node $node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['enter', $node->kind, $node->value ?? null]; + if ($node instanceof NameNode && $node->value === 'x') { + return Visitor::stop(); + } + }, + 'leave' => function (Node $node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['leave', $node->kind, $node->value ?? null]; + }, + ] + ); $expected = [ - [ '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' ], + ['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'], ]; $this->assertEquals($expected, $visited); @@ -344,37 +373,43 @@ class VisitorTest extends ValidatorTestCase $visited = []; $ast = Parser::parse('{ a, b { x }, c }', ['noLocation' => true]); - Visitor::visit($ast, [ - 'enter' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['enter', $node->kind, $node->value ?? null]; - }, - 'leave' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['leave', $node->kind, $node->value ?? null]; + Visitor::visit( + $ast, + [ + 'enter' => function ($node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['enter', $node->kind, $node->value ?? null]; + }, + 'leave' => function ($node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['leave', $node->kind, $node->value ?? null]; - if ($node->kind === NodeKind::NAME && $node->value === 'x') { - return Visitor::stop(); - } - }, - ]); + if ($node->kind === NodeKind::NAME && $node->value === 'x') { + return Visitor::stop(); + } + }, + ] + ); - $this->assertEquals($visited, [ - [ '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' ], - ]); + $this->assertEquals( + $visited, + [ + ['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'], + ] + ); } public function testAllowsANamedFunctionsVisitorAPI() : void @@ -382,32 +417,35 @@ class VisitorTest extends ValidatorTestCase $visited = []; $ast = Parser::parse('{ a, b { x }, c }', ['noLocation' => true]); - Visitor::visit($ast, [ - NodeKind::NAME => function (NameNode $node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['enter', $node->kind, $node->value]; - }, - NodeKind::SELECTION_SET => [ - 'enter' => function (SelectionSetNode $node) use (&$visited, $ast) { + Visitor::visit( + $ast, + [ + NodeKind::NAME => function (NameNode $node) use (&$visited, $ast) { $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['enter', $node->kind, null]; + $visited[] = ['enter', $node->kind, $node->value]; }, - 'leave' => function (SelectionSetNode $node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['leave', $node->kind, null]; - }, - ], - ]); + NodeKind::SELECTION_SET => [ + 'enter' => function (SelectionSetNode $node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['enter', $node->kind, null]; + }, + 'leave' => function (SelectionSetNode $node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['leave', $node->kind, null]; + }, + ], + ] + ); $expected = [ - [ 'enter', 'SelectionSet', null ], - [ 'enter', 'Name', 'a' ], - [ 'enter', 'Name', 'b' ], - [ 'enter', 'SelectionSet', null ], - [ 'enter', 'Name', 'x' ], - [ 'leave', 'SelectionSet', null ], - [ 'enter', 'Name', 'c' ], - [ 'leave', 'SelectionSet', null ], + ['enter', 'SelectionSet', null], + ['enter', 'Name', 'a'], + ['enter', 'Name', 'b'], + ['enter', 'SelectionSet', null], + ['enter', 'Name', 'x'], + ['leave', 'SelectionSet', null], + ['enter', 'Name', 'c'], + ['leave', 'SelectionSet', null], ]; $this->assertEquals($expected, $visited); @@ -418,22 +456,25 @@ class VisitorTest extends ValidatorTestCase $ast = Parser::parse( 'fragment a($v: Boolean = false) on t { f }', [ - 'noLocation' => true, + 'noLocation' => true, 'experimentalFragmentVariables' => true, ] ); $visited = []; - Visitor::visit($ast, [ - 'enter' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['enter', $node->kind, $node->value ?? null]; - }, - 'leave' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['leave', $node->kind, $node->value ?? null]; - }, - ]); + Visitor::visit( + $ast, + [ + 'enter' => function ($node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['enter', $node->kind, $node->value ?? null]; + }, + 'leave' => function ($node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['leave', $node->kind, $node->value ?? null]; + }, + ] + ); $expected = [ ['enter', 'Document', null], @@ -475,330 +516,333 @@ class VisitorTest extends ValidatorTestCase $ast = Parser::parse($kitchenSink); $visited = []; - Visitor::visit($ast, [ - 'enter' => function (Node $node, $key, $parent) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $r = ['enter', $node->kind, $key, $parent instanceof Node ? $parent->kind : null]; - $visited[] = $r; - }, - 'leave' => function (Node $node, $key, $parent) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $r = ['leave', $node->kind, $key, $parent instanceof Node ? $parent->kind : null]; - $visited[] = $r; - }, - ]); + Visitor::visit( + $ast, + [ + 'enter' => function (Node $node, $key, $parent) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $r = ['enter', $node->kind, $key, $parent instanceof Node ? $parent->kind : null]; + $visited[] = $r; + }, + 'leave' => function (Node $node, $key, $parent) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $r = ['leave', $node->kind, $key, $parent instanceof Node ? $parent->kind : null]; + $visited[] = $r; + }, + ] + ); $expected = [ - [ 'enter', 'Document', null, null ], - [ 'enter', 'OperationDefinition', 0, null ], - [ 'enter', 'Name', 'name', 'OperationDefinition' ], - [ 'leave', 'Name', 'name', 'OperationDefinition' ], - [ 'enter', 'VariableDefinition', 0, null ], - [ 'enter', 'Variable', 'variable', 'VariableDefinition' ], - [ 'enter', 'Name', 'name', 'Variable' ], - [ 'leave', 'Name', 'name', 'Variable' ], - [ 'leave', 'Variable', 'variable', 'VariableDefinition' ], - [ 'enter', 'NamedType', 'type', 'VariableDefinition' ], - [ 'enter', 'Name', 'name', 'NamedType' ], - [ 'leave', 'Name', 'name', 'NamedType' ], - [ 'leave', 'NamedType', 'type', 'VariableDefinition' ], - [ 'leave', 'VariableDefinition', 0, null ], - [ 'enter', 'VariableDefinition', 1, null ], - [ 'enter', 'Variable', 'variable', 'VariableDefinition' ], - [ 'enter', 'Name', 'name', 'Variable' ], - [ 'leave', 'Name', 'name', 'Variable' ], - [ 'leave', 'Variable', 'variable', 'VariableDefinition' ], - [ 'enter', 'NamedType', 'type', 'VariableDefinition' ], - [ 'enter', 'Name', 'name', 'NamedType' ], - [ 'leave', 'Name', 'name', 'NamedType' ], - [ 'leave', 'NamedType', 'type', 'VariableDefinition' ], - [ 'enter', 'EnumValue', 'defaultValue', 'VariableDefinition' ], - [ 'leave', 'EnumValue', 'defaultValue', 'VariableDefinition' ], - [ 'leave', 'VariableDefinition', 1, null ], - [ 'enter', 'SelectionSet', 'selectionSet', 'OperationDefinition' ], - [ 'enter', 'Field', 0, null ], - [ 'enter', 'Name', 'alias', 'Field' ], - [ 'leave', 'Name', 'alias', 'Field' ], - [ 'enter', 'Name', 'name', 'Field' ], - [ 'leave', 'Name', 'name', 'Field' ], - [ 'enter', 'Argument', 0, null ], - [ 'enter', 'Name', 'name', 'Argument' ], - [ 'leave', 'Name', 'name', 'Argument' ], - [ 'enter', 'ListValue', 'value', 'Argument' ], - [ 'enter', 'IntValue', 0, null ], - [ 'leave', 'IntValue', 0, null ], - [ 'enter', 'IntValue', 1, null ], - [ 'leave', 'IntValue', 1, null ], - [ 'leave', 'ListValue', 'value', 'Argument' ], - [ 'leave', 'Argument', 0, null ], - [ 'enter', 'SelectionSet', 'selectionSet', 'Field' ], - [ 'enter', 'Field', 0, null ], - [ 'enter', 'Name', 'name', 'Field' ], - [ 'leave', 'Name', 'name', 'Field' ], - [ 'leave', 'Field', 0, null ], - [ 'enter', 'InlineFragment', 1, null ], - [ 'enter', 'NamedType', 'typeCondition', 'InlineFragment' ], - [ 'enter', 'Name', 'name', 'NamedType' ], - [ 'leave', 'Name', 'name', 'NamedType' ], - [ 'leave', 'NamedType', 'typeCondition', 'InlineFragment' ], - [ 'enter', 'Directive', 0, null ], - [ 'enter', 'Name', 'name', 'Directive' ], - [ 'leave', 'Name', 'name', 'Directive' ], - [ 'leave', 'Directive', 0, null ], - [ 'enter', 'SelectionSet', 'selectionSet', 'InlineFragment' ], - [ 'enter', 'Field', 0, null ], - [ 'enter', 'Name', 'name', 'Field' ], - [ 'leave', 'Name', 'name', 'Field' ], - [ 'enter', 'SelectionSet', 'selectionSet', 'Field' ], - [ 'enter', 'Field', 0, null ], - [ 'enter', 'Name', 'name', 'Field' ], - [ 'leave', 'Name', 'name', 'Field' ], - [ 'leave', 'Field', 0, null ], - [ 'enter', 'Field', 1, null ], - [ 'enter', 'Name', 'alias', 'Field' ], - [ 'leave', 'Name', 'alias', 'Field' ], - [ 'enter', 'Name', 'name', 'Field' ], - [ 'leave', 'Name', 'name', 'Field' ], - [ 'enter', 'Argument', 0, null ], - [ 'enter', 'Name', 'name', 'Argument' ], - [ 'leave', 'Name', 'name', 'Argument' ], - [ 'enter', 'IntValue', 'value', 'Argument' ], - [ 'leave', 'IntValue', 'value', 'Argument' ], - [ 'leave', 'Argument', 0, null ], - [ 'enter', 'Argument', 1, null ], - [ 'enter', 'Name', 'name', 'Argument' ], - [ 'leave', 'Name', 'name', 'Argument' ], - [ 'enter', 'Variable', 'value', 'Argument' ], - [ 'enter', 'Name', 'name', 'Variable' ], - [ 'leave', 'Name', 'name', 'Variable' ], - [ 'leave', 'Variable', 'value', 'Argument' ], - [ 'leave', 'Argument', 1, null ], - [ 'enter', 'Directive', 0, null ], - [ 'enter', 'Name', 'name', 'Directive' ], - [ 'leave', 'Name', 'name', 'Directive' ], - [ 'enter', 'Argument', 0, null ], - [ 'enter', 'Name', 'name', 'Argument' ], - [ 'leave', 'Name', 'name', 'Argument' ], - [ 'enter', 'Variable', 'value', 'Argument' ], - [ 'enter', 'Name', 'name', 'Variable' ], - [ 'leave', 'Name', 'name', 'Variable' ], - [ 'leave', 'Variable', 'value', 'Argument' ], - [ 'leave', 'Argument', 0, null ], - [ 'leave', 'Directive', 0, null ], - [ 'enter', 'SelectionSet', 'selectionSet', 'Field' ], - [ 'enter', 'Field', 0, null ], - [ 'enter', 'Name', 'name', 'Field' ], - [ 'leave', 'Name', 'name', 'Field' ], - [ 'leave', 'Field', 0, null ], - [ 'enter', 'FragmentSpread', 1, null ], - [ 'enter', 'Name', 'name', 'FragmentSpread' ], - [ 'leave', 'Name', 'name', 'FragmentSpread' ], - [ 'leave', 'FragmentSpread', 1, null ], - [ 'leave', 'SelectionSet', 'selectionSet', 'Field' ], - [ 'leave', 'Field', 1, null ], - [ 'leave', 'SelectionSet', 'selectionSet', 'Field' ], - [ 'leave', 'Field', 0, null ], - [ 'leave', 'SelectionSet', 'selectionSet', 'InlineFragment' ], - [ 'leave', 'InlineFragment', 1, null ], - [ 'enter', 'InlineFragment', 2, null ], - [ 'enter', 'Directive', 0, null ], - [ 'enter', 'Name', 'name', 'Directive' ], - [ 'leave', 'Name', 'name', 'Directive' ], - [ 'enter', 'Argument', 0, null ], - [ 'enter', 'Name', 'name', 'Argument' ], - [ 'leave', 'Name', 'name', 'Argument' ], - [ 'enter', 'Variable', 'value', 'Argument' ], - [ 'enter', 'Name', 'name', 'Variable' ], - [ 'leave', 'Name', 'name', 'Variable' ], - [ 'leave', 'Variable', 'value', 'Argument' ], - [ 'leave', 'Argument', 0, null ], - [ 'leave', 'Directive', 0, null ], - [ 'enter', 'SelectionSet', 'selectionSet', 'InlineFragment' ], - [ 'enter', 'Field', 0, null ], - [ 'enter', 'Name', 'name', 'Field' ], - [ 'leave', 'Name', 'name', 'Field' ], - [ 'leave', 'Field', 0, null ], - [ 'leave', 'SelectionSet', 'selectionSet', 'InlineFragment' ], - [ 'leave', 'InlineFragment', 2, null ], - [ 'enter', 'InlineFragment', 3, null ], - [ 'enter', 'SelectionSet', 'selectionSet', 'InlineFragment' ], - [ 'enter', 'Field', 0, null ], - [ 'enter', 'Name', 'name', 'Field' ], - [ 'leave', 'Name', 'name', 'Field' ], - [ 'leave', 'Field', 0, null ], - [ 'leave', 'SelectionSet', 'selectionSet', 'InlineFragment' ], - [ 'leave', 'InlineFragment', 3, null ], - [ 'leave', 'SelectionSet', 'selectionSet', 'Field' ], - [ 'leave', 'Field', 0, null ], - [ 'leave', 'SelectionSet', 'selectionSet', 'OperationDefinition' ], - [ 'leave', 'OperationDefinition', 0, null ], - [ 'enter', 'OperationDefinition', 1, null ], - [ 'enter', 'Name', 'name', 'OperationDefinition' ], - [ 'leave', 'Name', 'name', 'OperationDefinition' ], - [ 'enter', 'SelectionSet', 'selectionSet', 'OperationDefinition' ], - [ 'enter', 'Field', 0, null ], - [ 'enter', 'Name', 'name', 'Field' ], - [ 'leave', 'Name', 'name', 'Field' ], - [ 'enter', 'Argument', 0, null ], - [ 'enter', 'Name', 'name', 'Argument' ], - [ 'leave', 'Name', 'name', 'Argument' ], - [ 'enter', 'IntValue', 'value', 'Argument' ], - [ 'leave', 'IntValue', 'value', 'Argument' ], - [ 'leave', 'Argument', 0, null ], - [ 'enter', 'Directive', 0, null ], - [ 'enter', 'Name', 'name', 'Directive' ], - [ 'leave', 'Name', 'name', 'Directive' ], - [ 'leave', 'Directive', 0, null ], - [ 'enter', 'SelectionSet', 'selectionSet', 'Field' ], - [ 'enter', 'Field', 0, null ], - [ 'enter', 'Name', 'name', 'Field' ], - [ 'leave', 'Name', 'name', 'Field' ], - [ 'enter', 'SelectionSet', 'selectionSet', 'Field' ], - [ 'enter', 'Field', 0, null ], - [ 'enter', 'Name', 'name', 'Field' ], - [ 'leave', 'Name', 'name', 'Field' ], - [ 'leave', 'Field', 0, null ], - [ 'leave', 'SelectionSet', 'selectionSet', 'Field' ], - [ 'leave', 'Field', 0, null ], - [ 'leave', 'SelectionSet', 'selectionSet', 'Field' ], - [ 'leave', 'Field', 0, null ], - [ 'leave', 'SelectionSet', 'selectionSet', 'OperationDefinition' ], - [ 'leave', 'OperationDefinition', 1, null ], - [ 'enter', 'OperationDefinition', 2, null ], - [ 'enter', 'Name', 'name', 'OperationDefinition' ], - [ 'leave', 'Name', 'name', 'OperationDefinition' ], - [ 'enter', 'VariableDefinition', 0, null ], - [ 'enter', 'Variable', 'variable', 'VariableDefinition' ], - [ 'enter', 'Name', 'name', 'Variable' ], - [ 'leave', 'Name', 'name', 'Variable' ], - [ 'leave', 'Variable', 'variable', 'VariableDefinition' ], - [ 'enter', 'NamedType', 'type', 'VariableDefinition' ], - [ 'enter', 'Name', 'name', 'NamedType' ], - [ 'leave', 'Name', 'name', 'NamedType' ], - [ 'leave', 'NamedType', 'type', 'VariableDefinition' ], - [ 'leave', 'VariableDefinition', 0, null ], - [ 'enter', 'SelectionSet', 'selectionSet', 'OperationDefinition' ], - [ 'enter', 'Field', 0, null ], - [ 'enter', 'Name', 'name', 'Field' ], - [ 'leave', 'Name', 'name', 'Field' ], - [ 'enter', 'Argument', 0, null ], - [ 'enter', 'Name', 'name', 'Argument' ], - [ 'leave', 'Name', 'name', 'Argument' ], - [ 'enter', 'Variable', 'value', 'Argument' ], - [ 'enter', 'Name', 'name', 'Variable' ], - [ 'leave', 'Name', 'name', 'Variable' ], - [ 'leave', 'Variable', 'value', 'Argument' ], - [ 'leave', 'Argument', 0, null ], - [ 'enter', 'SelectionSet', 'selectionSet', 'Field' ], - [ 'enter', 'Field', 0, null ], - [ 'enter', 'Name', 'name', 'Field' ], - [ 'leave', 'Name', 'name', 'Field' ], - [ 'enter', 'SelectionSet', 'selectionSet', 'Field' ], - [ 'enter', 'Field', 0, null ], - [ 'enter', 'Name', 'name', 'Field' ], - [ 'leave', 'Name', 'name', 'Field' ], - [ 'enter', 'SelectionSet', 'selectionSet', 'Field' ], - [ 'enter', 'Field', 0, null ], - [ 'enter', 'Name', 'name', 'Field' ], - [ 'leave', 'Name', 'name', 'Field' ], - [ 'leave', 'Field', 0, null ], - [ 'leave', 'SelectionSet', 'selectionSet', 'Field' ], - [ 'leave', 'Field', 0, null ], - [ 'enter', 'Field', 1, null ], - [ 'enter', 'Name', 'name', 'Field' ], - [ 'leave', 'Name', 'name', 'Field' ], - [ 'enter', 'SelectionSet', 'selectionSet', 'Field' ], - [ 'enter', 'Field', 0, null ], - [ 'enter', 'Name', 'name', 'Field' ], - [ 'leave', 'Name', 'name', 'Field' ], - [ 'leave', 'Field', 0, null ], - [ 'leave', 'SelectionSet', 'selectionSet', 'Field' ], - [ 'leave', 'Field', 1, null ], - [ 'leave', 'SelectionSet', 'selectionSet', 'Field' ], - [ 'leave', 'Field', 0, null ], - [ 'leave', 'SelectionSet', 'selectionSet', 'Field' ], - [ 'leave', 'Field', 0, null ], - [ 'leave', 'SelectionSet', 'selectionSet', 'OperationDefinition' ], - [ 'leave', 'OperationDefinition', 2, null ], - [ 'enter', 'FragmentDefinition', 3, null ], - [ 'enter', 'Name', 'name', 'FragmentDefinition' ], - [ 'leave', 'Name', 'name', 'FragmentDefinition' ], - [ 'enter', 'NamedType', 'typeCondition', 'FragmentDefinition' ], - [ 'enter', 'Name', 'name', 'NamedType' ], - [ 'leave', 'Name', 'name', 'NamedType' ], - [ 'leave', 'NamedType', 'typeCondition', 'FragmentDefinition' ], - [ 'enter', 'SelectionSet', 'selectionSet', 'FragmentDefinition' ], - [ 'enter', 'Field', 0, null ], - [ 'enter', 'Name', 'name', 'Field' ], - [ 'leave', 'Name', 'name', 'Field' ], - [ 'enter', 'Argument', 0, null ], - [ 'enter', 'Name', 'name', 'Argument' ], - [ 'leave', 'Name', 'name', 'Argument' ], - [ 'enter', 'Variable', 'value', 'Argument' ], - [ 'enter', 'Name', 'name', 'Variable' ], - [ 'leave', 'Name', 'name', 'Variable' ], - [ 'leave', 'Variable', 'value', 'Argument' ], - [ 'leave', 'Argument', 0, null ], - [ 'enter', 'Argument', 1, null ], - [ 'enter', 'Name', 'name', 'Argument' ], - [ 'leave', 'Name', 'name', 'Argument' ], - [ 'enter', 'Variable', 'value', 'Argument' ], - [ 'enter', 'Name', 'name', 'Variable' ], - [ 'leave', 'Name', 'name', 'Variable' ], - [ 'leave', 'Variable', 'value', 'Argument' ], - [ 'leave', 'Argument', 1, null ], - [ 'enter', 'Argument', 2, null ], - [ 'enter', 'Name', 'name', 'Argument' ], - [ 'leave', 'Name', 'name', 'Argument' ], - [ 'enter', 'ObjectValue', 'value', 'Argument' ], - [ 'enter', 'ObjectField', 0, null ], - [ 'enter', 'Name', 'name', 'ObjectField' ], - [ 'leave', 'Name', 'name', 'ObjectField' ], - [ 'enter', 'StringValue', 'value', 'ObjectField' ], - [ 'leave', 'StringValue', 'value', 'ObjectField' ], - [ 'leave', 'ObjectField', 0, null ], - [ 'enter', 'ObjectField', 1, null ], - [ 'enter', 'Name', 'name', 'ObjectField' ], - [ 'leave', 'Name', 'name', 'ObjectField' ], - [ 'enter', 'StringValue', 'value', 'ObjectField' ], - [ 'leave', 'StringValue', 'value', 'ObjectField' ], - [ 'leave', 'ObjectField', 1, null ], - [ 'leave', 'ObjectValue', 'value', 'Argument' ], - [ 'leave', 'Argument', 2, null ], - [ 'leave', 'Field', 0, null ], - [ 'leave', 'SelectionSet', 'selectionSet', 'FragmentDefinition' ], - [ 'leave', 'FragmentDefinition', 3, null ], - [ 'enter', 'OperationDefinition', 4, null ], - [ 'enter', 'SelectionSet', 'selectionSet', 'OperationDefinition' ], - [ 'enter', 'Field', 0, null ], - [ 'enter', 'Name', 'name', 'Field' ], - [ 'leave', 'Name', 'name', 'Field' ], - [ 'enter', 'Argument', 0, null ], - [ 'enter', 'Name', 'name', 'Argument' ], - [ 'leave', 'Name', 'name', 'Argument' ], - [ 'enter', 'BooleanValue', 'value', 'Argument' ], - [ 'leave', 'BooleanValue', 'value', 'Argument' ], - [ 'leave', 'Argument', 0, null ], - [ 'enter', 'Argument', 1, null ], - [ 'enter', 'Name', 'name', 'Argument' ], - [ 'leave', 'Name', 'name', 'Argument' ], - [ 'enter', 'BooleanValue', 'value', 'Argument' ], - [ 'leave', 'BooleanValue', 'value', 'Argument' ], - [ 'leave', 'Argument', 1, null ], - [ 'enter', 'Argument', 2, null ], - [ 'enter', 'Name', 'name', 'Argument' ], - [ 'leave', 'Name', 'name', 'Argument' ], - [ 'enter', 'NullValue', 'value', 'Argument' ], - [ 'leave', 'NullValue', 'value', 'Argument' ], - [ 'leave', 'Argument', 2, null ], - [ 'leave', 'Field', 0, null ], - [ 'enter', 'Field', 1, null ], - [ 'enter', 'Name', 'name', 'Field' ], - [ 'leave', 'Name', 'name', 'Field' ], - [ 'leave', 'Field', 1, null ], - [ 'leave', 'SelectionSet', 'selectionSet', 'OperationDefinition' ], - [ 'leave', 'OperationDefinition', 4, null ], - [ 'leave', 'Document', null, null ], + ['enter', 'Document', null, null], + ['enter', 'OperationDefinition', 0, null], + ['enter', 'Name', 'name', 'OperationDefinition'], + ['leave', 'Name', 'name', 'OperationDefinition'], + ['enter', 'VariableDefinition', 0, null], + ['enter', 'Variable', 'variable', 'VariableDefinition'], + ['enter', 'Name', 'name', 'Variable'], + ['leave', 'Name', 'name', 'Variable'], + ['leave', 'Variable', 'variable', 'VariableDefinition'], + ['enter', 'NamedType', 'type', 'VariableDefinition'], + ['enter', 'Name', 'name', 'NamedType'], + ['leave', 'Name', 'name', 'NamedType'], + ['leave', 'NamedType', 'type', 'VariableDefinition'], + ['leave', 'VariableDefinition', 0, null], + ['enter', 'VariableDefinition', 1, null], + ['enter', 'Variable', 'variable', 'VariableDefinition'], + ['enter', 'Name', 'name', 'Variable'], + ['leave', 'Name', 'name', 'Variable'], + ['leave', 'Variable', 'variable', 'VariableDefinition'], + ['enter', 'NamedType', 'type', 'VariableDefinition'], + ['enter', 'Name', 'name', 'NamedType'], + ['leave', 'Name', 'name', 'NamedType'], + ['leave', 'NamedType', 'type', 'VariableDefinition'], + ['enter', 'EnumValue', 'defaultValue', 'VariableDefinition'], + ['leave', 'EnumValue', 'defaultValue', 'VariableDefinition'], + ['leave', 'VariableDefinition', 1, null], + ['enter', 'SelectionSet', 'selectionSet', 'OperationDefinition'], + ['enter', 'Field', 0, null], + ['enter', 'Name', 'alias', 'Field'], + ['leave', 'Name', 'alias', 'Field'], + ['enter', 'Name', 'name', 'Field'], + ['leave', 'Name', 'name', 'Field'], + ['enter', 'Argument', 0, null], + ['enter', 'Name', 'name', 'Argument'], + ['leave', 'Name', 'name', 'Argument'], + ['enter', 'ListValue', 'value', 'Argument'], + ['enter', 'IntValue', 0, null], + ['leave', 'IntValue', 0, null], + ['enter', 'IntValue', 1, null], + ['leave', 'IntValue', 1, null], + ['leave', 'ListValue', 'value', 'Argument'], + ['leave', 'Argument', 0, null], + ['enter', 'SelectionSet', 'selectionSet', 'Field'], + ['enter', 'Field', 0, null], + ['enter', 'Name', 'name', 'Field'], + ['leave', 'Name', 'name', 'Field'], + ['leave', 'Field', 0, null], + ['enter', 'InlineFragment', 1, null], + ['enter', 'NamedType', 'typeCondition', 'InlineFragment'], + ['enter', 'Name', 'name', 'NamedType'], + ['leave', 'Name', 'name', 'NamedType'], + ['leave', 'NamedType', 'typeCondition', 'InlineFragment'], + ['enter', 'Directive', 0, null], + ['enter', 'Name', 'name', 'Directive'], + ['leave', 'Name', 'name', 'Directive'], + ['leave', 'Directive', 0, null], + ['enter', 'SelectionSet', 'selectionSet', 'InlineFragment'], + ['enter', 'Field', 0, null], + ['enter', 'Name', 'name', 'Field'], + ['leave', 'Name', 'name', 'Field'], + ['enter', 'SelectionSet', 'selectionSet', 'Field'], + ['enter', 'Field', 0, null], + ['enter', 'Name', 'name', 'Field'], + ['leave', 'Name', 'name', 'Field'], + ['leave', 'Field', 0, null], + ['enter', 'Field', 1, null], + ['enter', 'Name', 'alias', 'Field'], + ['leave', 'Name', 'alias', 'Field'], + ['enter', 'Name', 'name', 'Field'], + ['leave', 'Name', 'name', 'Field'], + ['enter', 'Argument', 0, null], + ['enter', 'Name', 'name', 'Argument'], + ['leave', 'Name', 'name', 'Argument'], + ['enter', 'IntValue', 'value', 'Argument'], + ['leave', 'IntValue', 'value', 'Argument'], + ['leave', 'Argument', 0, null], + ['enter', 'Argument', 1, null], + ['enter', 'Name', 'name', 'Argument'], + ['leave', 'Name', 'name', 'Argument'], + ['enter', 'Variable', 'value', 'Argument'], + ['enter', 'Name', 'name', 'Variable'], + ['leave', 'Name', 'name', 'Variable'], + ['leave', 'Variable', 'value', 'Argument'], + ['leave', 'Argument', 1, null], + ['enter', 'Directive', 0, null], + ['enter', 'Name', 'name', 'Directive'], + ['leave', 'Name', 'name', 'Directive'], + ['enter', 'Argument', 0, null], + ['enter', 'Name', 'name', 'Argument'], + ['leave', 'Name', 'name', 'Argument'], + ['enter', 'Variable', 'value', 'Argument'], + ['enter', 'Name', 'name', 'Variable'], + ['leave', 'Name', 'name', 'Variable'], + ['leave', 'Variable', 'value', 'Argument'], + ['leave', 'Argument', 0, null], + ['leave', 'Directive', 0, null], + ['enter', 'SelectionSet', 'selectionSet', 'Field'], + ['enter', 'Field', 0, null], + ['enter', 'Name', 'name', 'Field'], + ['leave', 'Name', 'name', 'Field'], + ['leave', 'Field', 0, null], + ['enter', 'FragmentSpread', 1, null], + ['enter', 'Name', 'name', 'FragmentSpread'], + ['leave', 'Name', 'name', 'FragmentSpread'], + ['leave', 'FragmentSpread', 1, null], + ['leave', 'SelectionSet', 'selectionSet', 'Field'], + ['leave', 'Field', 1, null], + ['leave', 'SelectionSet', 'selectionSet', 'Field'], + ['leave', 'Field', 0, null], + ['leave', 'SelectionSet', 'selectionSet', 'InlineFragment'], + ['leave', 'InlineFragment', 1, null], + ['enter', 'InlineFragment', 2, null], + ['enter', 'Directive', 0, null], + ['enter', 'Name', 'name', 'Directive'], + ['leave', 'Name', 'name', 'Directive'], + ['enter', 'Argument', 0, null], + ['enter', 'Name', 'name', 'Argument'], + ['leave', 'Name', 'name', 'Argument'], + ['enter', 'Variable', 'value', 'Argument'], + ['enter', 'Name', 'name', 'Variable'], + ['leave', 'Name', 'name', 'Variable'], + ['leave', 'Variable', 'value', 'Argument'], + ['leave', 'Argument', 0, null], + ['leave', 'Directive', 0, null], + ['enter', 'SelectionSet', 'selectionSet', 'InlineFragment'], + ['enter', 'Field', 0, null], + ['enter', 'Name', 'name', 'Field'], + ['leave', 'Name', 'name', 'Field'], + ['leave', 'Field', 0, null], + ['leave', 'SelectionSet', 'selectionSet', 'InlineFragment'], + ['leave', 'InlineFragment', 2, null], + ['enter', 'InlineFragment', 3, null], + ['enter', 'SelectionSet', 'selectionSet', 'InlineFragment'], + ['enter', 'Field', 0, null], + ['enter', 'Name', 'name', 'Field'], + ['leave', 'Name', 'name', 'Field'], + ['leave', 'Field', 0, null], + ['leave', 'SelectionSet', 'selectionSet', 'InlineFragment'], + ['leave', 'InlineFragment', 3, null], + ['leave', 'SelectionSet', 'selectionSet', 'Field'], + ['leave', 'Field', 0, null], + ['leave', 'SelectionSet', 'selectionSet', 'OperationDefinition'], + ['leave', 'OperationDefinition', 0, null], + ['enter', 'OperationDefinition', 1, null], + ['enter', 'Name', 'name', 'OperationDefinition'], + ['leave', 'Name', 'name', 'OperationDefinition'], + ['enter', 'SelectionSet', 'selectionSet', 'OperationDefinition'], + ['enter', 'Field', 0, null], + ['enter', 'Name', 'name', 'Field'], + ['leave', 'Name', 'name', 'Field'], + ['enter', 'Argument', 0, null], + ['enter', 'Name', 'name', 'Argument'], + ['leave', 'Name', 'name', 'Argument'], + ['enter', 'IntValue', 'value', 'Argument'], + ['leave', 'IntValue', 'value', 'Argument'], + ['leave', 'Argument', 0, null], + ['enter', 'Directive', 0, null], + ['enter', 'Name', 'name', 'Directive'], + ['leave', 'Name', 'name', 'Directive'], + ['leave', 'Directive', 0, null], + ['enter', 'SelectionSet', 'selectionSet', 'Field'], + ['enter', 'Field', 0, null], + ['enter', 'Name', 'name', 'Field'], + ['leave', 'Name', 'name', 'Field'], + ['enter', 'SelectionSet', 'selectionSet', 'Field'], + ['enter', 'Field', 0, null], + ['enter', 'Name', 'name', 'Field'], + ['leave', 'Name', 'name', 'Field'], + ['leave', 'Field', 0, null], + ['leave', 'SelectionSet', 'selectionSet', 'Field'], + ['leave', 'Field', 0, null], + ['leave', 'SelectionSet', 'selectionSet', 'Field'], + ['leave', 'Field', 0, null], + ['leave', 'SelectionSet', 'selectionSet', 'OperationDefinition'], + ['leave', 'OperationDefinition', 1, null], + ['enter', 'OperationDefinition', 2, null], + ['enter', 'Name', 'name', 'OperationDefinition'], + ['leave', 'Name', 'name', 'OperationDefinition'], + ['enter', 'VariableDefinition', 0, null], + ['enter', 'Variable', 'variable', 'VariableDefinition'], + ['enter', 'Name', 'name', 'Variable'], + ['leave', 'Name', 'name', 'Variable'], + ['leave', 'Variable', 'variable', 'VariableDefinition'], + ['enter', 'NamedType', 'type', 'VariableDefinition'], + ['enter', 'Name', 'name', 'NamedType'], + ['leave', 'Name', 'name', 'NamedType'], + ['leave', 'NamedType', 'type', 'VariableDefinition'], + ['leave', 'VariableDefinition', 0, null], + ['enter', 'SelectionSet', 'selectionSet', 'OperationDefinition'], + ['enter', 'Field', 0, null], + ['enter', 'Name', 'name', 'Field'], + ['leave', 'Name', 'name', 'Field'], + ['enter', 'Argument', 0, null], + ['enter', 'Name', 'name', 'Argument'], + ['leave', 'Name', 'name', 'Argument'], + ['enter', 'Variable', 'value', 'Argument'], + ['enter', 'Name', 'name', 'Variable'], + ['leave', 'Name', 'name', 'Variable'], + ['leave', 'Variable', 'value', 'Argument'], + ['leave', 'Argument', 0, null], + ['enter', 'SelectionSet', 'selectionSet', 'Field'], + ['enter', 'Field', 0, null], + ['enter', 'Name', 'name', 'Field'], + ['leave', 'Name', 'name', 'Field'], + ['enter', 'SelectionSet', 'selectionSet', 'Field'], + ['enter', 'Field', 0, null], + ['enter', 'Name', 'name', 'Field'], + ['leave', 'Name', 'name', 'Field'], + ['enter', 'SelectionSet', 'selectionSet', 'Field'], + ['enter', 'Field', 0, null], + ['enter', 'Name', 'name', 'Field'], + ['leave', 'Name', 'name', 'Field'], + ['leave', 'Field', 0, null], + ['leave', 'SelectionSet', 'selectionSet', 'Field'], + ['leave', 'Field', 0, null], + ['enter', 'Field', 1, null], + ['enter', 'Name', 'name', 'Field'], + ['leave', 'Name', 'name', 'Field'], + ['enter', 'SelectionSet', 'selectionSet', 'Field'], + ['enter', 'Field', 0, null], + ['enter', 'Name', 'name', 'Field'], + ['leave', 'Name', 'name', 'Field'], + ['leave', 'Field', 0, null], + ['leave', 'SelectionSet', 'selectionSet', 'Field'], + ['leave', 'Field', 1, null], + ['leave', 'SelectionSet', 'selectionSet', 'Field'], + ['leave', 'Field', 0, null], + ['leave', 'SelectionSet', 'selectionSet', 'Field'], + ['leave', 'Field', 0, null], + ['leave', 'SelectionSet', 'selectionSet', 'OperationDefinition'], + ['leave', 'OperationDefinition', 2, null], + ['enter', 'FragmentDefinition', 3, null], + ['enter', 'Name', 'name', 'FragmentDefinition'], + ['leave', 'Name', 'name', 'FragmentDefinition'], + ['enter', 'NamedType', 'typeCondition', 'FragmentDefinition'], + ['enter', 'Name', 'name', 'NamedType'], + ['leave', 'Name', 'name', 'NamedType'], + ['leave', 'NamedType', 'typeCondition', 'FragmentDefinition'], + ['enter', 'SelectionSet', 'selectionSet', 'FragmentDefinition'], + ['enter', 'Field', 0, null], + ['enter', 'Name', 'name', 'Field'], + ['leave', 'Name', 'name', 'Field'], + ['enter', 'Argument', 0, null], + ['enter', 'Name', 'name', 'Argument'], + ['leave', 'Name', 'name', 'Argument'], + ['enter', 'Variable', 'value', 'Argument'], + ['enter', 'Name', 'name', 'Variable'], + ['leave', 'Name', 'name', 'Variable'], + ['leave', 'Variable', 'value', 'Argument'], + ['leave', 'Argument', 0, null], + ['enter', 'Argument', 1, null], + ['enter', 'Name', 'name', 'Argument'], + ['leave', 'Name', 'name', 'Argument'], + ['enter', 'Variable', 'value', 'Argument'], + ['enter', 'Name', 'name', 'Variable'], + ['leave', 'Name', 'name', 'Variable'], + ['leave', 'Variable', 'value', 'Argument'], + ['leave', 'Argument', 1, null], + ['enter', 'Argument', 2, null], + ['enter', 'Name', 'name', 'Argument'], + ['leave', 'Name', 'name', 'Argument'], + ['enter', 'ObjectValue', 'value', 'Argument'], + ['enter', 'ObjectField', 0, null], + ['enter', 'Name', 'name', 'ObjectField'], + ['leave', 'Name', 'name', 'ObjectField'], + ['enter', 'StringValue', 'value', 'ObjectField'], + ['leave', 'StringValue', 'value', 'ObjectField'], + ['leave', 'ObjectField', 0, null], + ['enter', 'ObjectField', 1, null], + ['enter', 'Name', 'name', 'ObjectField'], + ['leave', 'Name', 'name', 'ObjectField'], + ['enter', 'StringValue', 'value', 'ObjectField'], + ['leave', 'StringValue', 'value', 'ObjectField'], + ['leave', 'ObjectField', 1, null], + ['leave', 'ObjectValue', 'value', 'Argument'], + ['leave', 'Argument', 2, null], + ['leave', 'Field', 0, null], + ['leave', 'SelectionSet', 'selectionSet', 'FragmentDefinition'], + ['leave', 'FragmentDefinition', 3, null], + ['enter', 'OperationDefinition', 4, null], + ['enter', 'SelectionSet', 'selectionSet', 'OperationDefinition'], + ['enter', 'Field', 0, null], + ['enter', 'Name', 'name', 'Field'], + ['leave', 'Name', 'name', 'Field'], + ['enter', 'Argument', 0, null], + ['enter', 'Name', 'name', 'Argument'], + ['leave', 'Name', 'name', 'Argument'], + ['enter', 'BooleanValue', 'value', 'Argument'], + ['leave', 'BooleanValue', 'value', 'Argument'], + ['leave', 'Argument', 0, null], + ['enter', 'Argument', 1, null], + ['enter', 'Name', 'name', 'Argument'], + ['leave', 'Name', 'name', 'Argument'], + ['enter', 'BooleanValue', 'value', 'Argument'], + ['leave', 'BooleanValue', 'value', 'Argument'], + ['leave', 'Argument', 1, null], + ['enter', 'Argument', 2, null], + ['enter', 'Name', 'name', 'Argument'], + ['leave', 'Name', 'name', 'Argument'], + ['enter', 'NullValue', 'value', 'Argument'], + ['leave', 'NullValue', 'value', 'Argument'], + ['leave', 'Argument', 2, null], + ['leave', 'Field', 0, null], + ['enter', 'Field', 1, null], + ['enter', 'Name', 'name', 'Field'], + ['leave', 'Name', 'name', 'Field'], + ['leave', 'Field', 1, null], + ['leave', 'SelectionSet', 'selectionSet', 'OperationDefinition'], + ['leave', 'OperationDefinition', 4, null], + ['leave', 'Document', null, null], ]; $this->assertEquals($expected, $visited); @@ -813,41 +857,47 @@ class VisitorTest extends ValidatorTestCase $visited = []; $ast = Parser::parse('{ a, b { x }, c }'); - Visitor::visit($ast, Visitor::visitInParallel([ + Visitor::visit( + $ast, + Visitor::visitInParallel([ + [ + 'enter' => function ($node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['enter', $node->kind, $node->value ?? null]; + + if ($node->kind === 'Field' && isset($node->name->value) && $node->name->value === 'b') { + return Visitor::skipNode(); + } + }, + + 'leave' => function ($node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['leave', $node->kind, $node->value ?? null]; + }, + ], + ]) + ); + + $this->assertEquals( [ - 'enter' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = [ 'enter', $node->kind, $node->value ?? null]; - - if ($node->kind === 'Field' && isset($node->name->value) && $node->name->value === 'b') { - return Visitor::skipNode(); - } - }, - - 'leave' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['leave', $node->kind, $node->value ?? null]; - }, + ['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], ], - ])); - - $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); + $visited + ); } public function testAllowsSkippingDifferentSubTrees() : void @@ -855,71 +905,77 @@ class VisitorTest extends ValidatorTestCase $visited = []; $ast = Parser::parse('{ a { x }, b { y} }'); - Visitor::visit($ast, Visitor::visitInParallel([ - [ - 'enter' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['no-a', 'enter', $node->kind, $node->value ?? null]; - if ($node->kind === 'Field' && isset($node->name->value) && $node->name->value === 'a') { - return Visitor::skipNode(); - } - }, - 'leave' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = [ 'no-a', 'leave', $node->kind, $node->value ?? null ]; - }, - ], - [ - 'enter' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['no-b', 'enter', $node->kind, $node->value ?? null]; - if ($node->kind === 'Field' && isset($node->name->value) && $node->name->value === 'b') { - return Visitor::skipNode(); - } - }, - 'leave' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['no-b', 'leave', $node->kind, $node->value ?? null]; - }, - ], - ])); + Visitor::visit( + $ast, + Visitor::visitInParallel([ + [ + 'enter' => function ($node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['no-a', 'enter', $node->kind, $node->value ?? null]; + if ($node->kind === 'Field' && isset($node->name->value) && $node->name->value === 'a') { + return Visitor::skipNode(); + } + }, + 'leave' => function ($node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['no-a', 'leave', $node->kind, $node->value ?? null]; + }, + ], + [ + 'enter' => function ($node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['no-b', 'enter', $node->kind, $node->value ?? null]; + if ($node->kind === 'Field' && isset($node->name->value) && $node->name->value === 'b') { + return Visitor::skipNode(); + } + }, + 'leave' => function ($node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['no-b', 'leave', $node->kind, $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); + $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 + ); } public function testAllowsEarlyExitWhileVisiting2() : void @@ -927,37 +983,43 @@ class VisitorTest extends ValidatorTestCase $visited = []; $ast = Parser::parse('{ a, b { x }, c }'); - Visitor::visit($ast, Visitor::visitInParallel([ [ - 'enter' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $value = $node->value ?? null; - $visited[] = ['enter', $node->kind, $value]; - if ($node->kind === 'Name' && $value === 'x') { - return Visitor::stop(); - } - }, - 'leave' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['leave', $node->kind, $node->value ?? null]; - }, - ], - ])); + Visitor::visit( + $ast, + Visitor::visitInParallel([[ + 'enter' => function ($node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $value = $node->value ?? null; + $visited[] = ['enter', $node->kind, $value]; + if ($node->kind === 'Name' && $value === 'x') { + return Visitor::stop(); + } + }, + 'leave' => function ($node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['leave', $node->kind, $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); + $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 + ); } public function testAllowsEarlyExitFromDifferentPoints() : void @@ -965,59 +1027,65 @@ class VisitorTest extends ValidatorTestCase $visited = []; $ast = Parser::parse('{ a { y }, b { x } }'); - Visitor::visit($ast, Visitor::visitInParallel([ - [ - 'enter' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $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, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = [ 'break-a', 'leave', $node->kind, $node->value ?? null ]; - }, - ], - [ - 'enter' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $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, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['break-b', 'leave', $node->kind, $node->value ?? null]; - }, - ], - ])); + Visitor::visit( + $ast, + Visitor::visitInParallel([ + [ + 'enter' => function ($node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $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, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['break-a', 'leave', $node->kind, $node->value ?? null]; + }, + ], + [ + 'enter' => function ($node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $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, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['break-b', 'leave', $node->kind, $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); + $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 + ); } public function testAllowsEarlyExitWhileLeaving2() : void @@ -1025,38 +1093,44 @@ class VisitorTest extends ValidatorTestCase $visited = []; $ast = Parser::parse('{ a, b { x }, c }'); - Visitor::visit($ast, Visitor::visitInParallel([ [ - 'enter' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['enter', $node->kind, $node->value ?? null]; - }, - 'leave' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $value = $node->value ?? null; - $visited[] = ['leave', $node->kind, $value]; - if ($node->kind === 'Name' && $value === 'x') { - return Visitor::stop(); - } - }, - ], - ])); + Visitor::visit( + $ast, + Visitor::visitInParallel([[ + 'enter' => function ($node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['enter', $node->kind, $node->value ?? null]; + }, + 'leave' => function ($node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $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); + $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 + ); } public function testAllowsEarlyExitFromLeavingDifferentPoints() : void @@ -1064,73 +1138,79 @@ class VisitorTest extends ValidatorTestCase $visited = []; $ast = Parser::parse('{ a { y }, b { x } }'); - Visitor::visit($ast, Visitor::visitInParallel([ - [ - 'enter' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['break-a', 'enter', $node->kind, $node->value ?? null]; - }, - 'leave' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['break-a', 'leave', $node->kind, $node->value ?? null]; - if ($node->kind === 'Field' && isset($node->name->value) && $node->name->value === 'a') { - return Visitor::stop(); - } - }, - ], - [ - 'enter' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['break-b', 'enter', $node->kind, $node->value ?? null]; - }, - 'leave' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['break-b', 'leave', $node->kind, $node->value ?? null]; - if ($node->kind === 'Field' && isset($node->name->value) && $node->name->value === 'b') { - return Visitor::stop(); - } - }, - ], - ])); + Visitor::visit( + $ast, + Visitor::visitInParallel([ + [ + 'enter' => function ($node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['break-a', 'enter', $node->kind, $node->value ?? null]; + }, + 'leave' => function ($node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['break-a', 'leave', $node->kind, $node->value ?? null]; + if ($node->kind === 'Field' && isset($node->name->value) && $node->name->value === 'a') { + return Visitor::stop(); + } + }, + ], + [ + 'enter' => function ($node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['break-b', 'enter', $node->kind, $node->value ?? null]; + }, + 'leave' => function ($node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['break-b', 'leave', $node->kind, $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); + $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 + ); } public function testAllowsForEditingOnEnter2() : void @@ -1138,26 +1218,29 @@ class VisitorTest extends ValidatorTestCase $visited = []; $ast = Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]); - $editedAst = Visitor::visit($ast, Visitor::visitInParallel([ - [ - 'enter' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - if ($node->kind === 'Field' && isset($node->name->value) && $node->name->value === 'b') { - return Visitor::removeNode(); - } - }, - ], - [ - 'enter' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['enter', $node->kind, $node->value ?? null]; - }, - 'leave' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args(), true); - $visited[] = ['leave', $node->kind, $node->value ?? null]; - }, - ], - ])); + $editedAst = Visitor::visit( + $ast, + Visitor::visitInParallel([ + [ + 'enter' => function ($node) use ($ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + if ($node->kind === 'Field' && isset($node->name->value) && $node->name->value === 'b') { + return Visitor::removeNode(); + } + }, + ], + [ + 'enter' => function ($node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['enter', $node->kind, $node->value ?? null]; + }, + 'leave' => function ($node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args(), true); + $visited[] = ['leave', $node->kind, $node->value ?? null]; + }, + ], + ]) + ); $this->assertEquals( Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]), @@ -1169,7 +1252,8 @@ class VisitorTest extends ValidatorTestCase $editedAst ); - $this->assertEquals([ + $this->assertEquals( + [ ['enter', 'Document', null], ['enter', 'OperationDefinition', null], ['enter', 'SelectionSet', null], @@ -1194,7 +1278,9 @@ class VisitorTest extends ValidatorTestCase ['leave', 'SelectionSet', null], ['leave', 'OperationDefinition', null], ['leave', 'Document', null], - ], $visited); + ], + $visited + ); } public function testAllowsForEditingOnLeave2() : void @@ -1202,26 +1288,29 @@ class VisitorTest extends ValidatorTestCase $visited = []; $ast = Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]); - $editedAst = Visitor::visit($ast, Visitor::visitInParallel([ - [ - 'leave' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args(), true); - if ($node->kind === 'Field' && isset($node->name->value) && $node->name->value === 'b') { - return Visitor::removeNode(); - } - }, - ], - [ - 'enter' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $visited[] = ['enter', $node->kind, $node->value ?? null]; - }, - 'leave' => function ($node) use (&$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args(), true); - $visited[] = ['leave', $node->kind, $node->value ?? null]; - }, - ], - ])); + $editedAst = Visitor::visit( + $ast, + Visitor::visitInParallel([ + [ + 'leave' => function ($node) use ($ast) { + $this->checkVisitorFnArgs($ast, func_get_args(), true); + if ($node->kind === 'Field' && isset($node->name->value) && $node->name->value === 'b') { + return Visitor::removeNode(); + } + }, + ], + [ + 'enter' => function ($node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $visited[] = ['enter', $node->kind, $node->value ?? null]; + }, + 'leave' => function ($node) use (&$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args(), true); + $visited[] = ['leave', $node->kind, $node->value ?? null]; + }, + ], + ]) + ); $this->assertEquals( Parser::parse('{ a, b, c { a, b, c } }', ['noLocation' => true]), @@ -1233,7 +1322,8 @@ class VisitorTest extends ValidatorTestCase $editedAst ); - $this->assertEquals([ + $this->assertEquals( + [ ['enter', 'Document', null], ['enter', 'OperationDefinition', null], ['enter', 'SelectionSet', null], @@ -1264,10 +1354,11 @@ class VisitorTest extends ValidatorTestCase ['leave', 'SelectionSet', null], ['leave', 'OperationDefinition', null], ['leave', 'Document', null], - ], $visited); + ], + $visited + ); } - /** * Describe: visitWithTypeInfo */ @@ -1278,38 +1369,45 @@ class VisitorTest extends ValidatorTestCase $typeInfo = new TypeInfo(ValidatorTestCase::getTestSchema()); $ast = Parser::parse('{ human(id: 4) { name, pets { ... { name } }, unknown } }'); - Visitor::visit($ast, Visitor::visitWithTypeInfo($typeInfo, [ - 'enter' => function ($node) use ($typeInfo, &$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $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, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args()); - $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, - ]; - }, - ])); + Visitor::visit( + $ast, + Visitor::visitWithTypeInfo( + $typeInfo, + [ + 'enter' => function ($node) use ($typeInfo, &$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $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, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args()); + $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([ + $this->assertEquals( + [ ['enter', 'Document', null, null, null, null], ['enter', 'OperationDefinition', null, null, 'QueryRoot', null], ['enter', 'SelectionSet', null, 'QueryRoot', 'QueryRoot', null], @@ -1350,7 +1448,9 @@ class VisitorTest extends ValidatorTestCase ['leave', 'SelectionSet', null, 'QueryRoot', 'QueryRoot', null], ['leave', 'OperationDefinition', null, null, 'QueryRoot', null], ['leave', 'Document', null, null, null, null], - ], $visited); + ], + $visited + ); } public function testMaintainsTypeInfoDuringEdit() : void @@ -1361,66 +1461,79 @@ class VisitorTest extends ValidatorTestCase $ast = Parser::parse( '{ human(id: 4) { name, pets }, alien }' ); - $editedAst = Visitor::visit($ast, Visitor::visitWithTypeInfo($typeInfo, [ - 'enter' => function ($node) use ($typeInfo, &$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args(), true); - $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, - ]; + $editedAst = Visitor::visit( + $ast, + Visitor::visitWithTypeInfo( + $typeInfo, + [ + 'enter' => function ($node) use ($typeInfo, &$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args(), true); + $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 FieldNode([ - 'alias' => $node->alias, - 'name' => $node->name, - 'arguments' => $node->arguments, - 'directives' => $node->directives, - 'selectionSet' => new SelectionSetNode([ - 'kind' => 'SelectionSet', - 'selections' => [new FieldNode([ - 'name' => new NameNode(['value' => '__typename']), + // Make a query valid by adding missing selection sets. + if ($node->kind === 'Field' && + ! $node->selectionSet && + Type::isCompositeType(Type::getNamedType($type)) + ) { + return new FieldNode([ + 'alias' => $node->alias, + 'name' => $node->name, + 'arguments' => $node->arguments, + 'directives' => $node->directives, + 'selectionSet' => new SelectionSetNode([ + 'kind' => 'SelectionSet', + 'selections' => [new FieldNode([ + 'name' => new NameNode(['value' => '__typename']), + ]), + ], ]), - ], - ]), - ]); - } - }, - 'leave' => function ($node) use ($typeInfo, &$visited, $ast) { - $this->checkVisitorFnArgs($ast, func_get_args(), true); - $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, - ]; - }, - ])); + ]); + } + }, + 'leave' => function ($node) use ($typeInfo, &$visited, $ast) { + $this->checkVisitorFnArgs($ast, func_get_args(), true); + $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 }, alien }' + )), + Printer::doPrint($ast) + ); - $this->assertEquals(Printer::doPrint(Parser::parse( - '{ human(id: 4) { name, pets { __typename } }, alien { __typename } }' - )), Printer::doPrint($editedAst)); + $this->assertEquals( + Printer::doPrint(Parser::parse( + '{ human(id: 4) { name, pets { __typename } }, alien { __typename } }' + )), + Printer::doPrint($editedAst) + ); - $this->assertEquals([ + $this->assertEquals( + [ ['enter', 'Document', null, null, null, null], ['enter', 'OperationDefinition', null, null, 'QueryRoot', null], ['enter', 'SelectionSet', null, 'QueryRoot', 'QueryRoot', null], @@ -1463,6 +1576,8 @@ class VisitorTest extends ValidatorTestCase ['leave', 'SelectionSet', null, 'QueryRoot', 'QueryRoot', null], ['leave', 'OperationDefinition', null, null, 'QueryRoot', null], ['leave', 'Document', null, null, null, null], - ], $visited); + ], + $visited + ); } } diff --git a/tests/Server/Psr7/PsrRequestStub.php b/tests/Server/Psr7/PsrRequestStub.php index 6a912af..c60dcac 100644 --- a/tests/Server/Psr7/PsrRequestStub.php +++ b/tests/Server/Psr7/PsrRequestStub.php @@ -1,10 +1,17 @@ headers[$name]) ? $this->headers[$name] : []; + + return $this->headers[$name] ?? []; } /** @@ -137,7 +141,7 @@ class PsrRequestStub implements ServerRequestInterface */ public function getHeaderLine($name) { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -150,14 +154,14 @@ class PsrRequestStub implements ServerRequestInterface * immutability of the message, and MUST return an instance that has the * new and/or updated header and value. * - * @param string $name Case-insensitive header field name. + * @param string $name Case-insensitive header field name. * @param string|string[] $value Header value(s). * @return static * @throws \InvalidArgumentException for invalid header names or values. */ public function withHeader($name, $value) { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -171,14 +175,14 @@ class PsrRequestStub implements ServerRequestInterface * immutability of the message, and MUST return an instance that has the * new header and/or value. * - * @param string $name Case-insensitive header field name to add. + * @param string $name Case-insensitive header field name to add. * @param string|string[] $value Header value(s). * @return static * @throws \InvalidArgumentException for invalid header names or values. */ public function withAddedHeader($name, $value) { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -195,7 +199,7 @@ class PsrRequestStub implements ServerRequestInterface */ public function withoutHeader($name) { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -223,7 +227,7 @@ class PsrRequestStub implements ServerRequestInterface */ public function withBody(StreamInterface $body) { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -244,7 +248,7 @@ class PsrRequestStub implements ServerRequestInterface */ public function getRequestTarget() { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -266,7 +270,7 @@ class PsrRequestStub implements ServerRequestInterface */ public function withRequestTarget($requestTarget) { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -296,7 +300,7 @@ class PsrRequestStub implements ServerRequestInterface */ public function withMethod($method) { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -339,13 +343,13 @@ class PsrRequestStub implements ServerRequestInterface * new UriInterface instance. * * @link http://tools.ietf.org/html/rfc3986#section-4.3 - * @param UriInterface $uri New request URI to use. - * @param bool $preserveHost Preserve the original state of the Host header. + * @param UriInterface $uri New request URI to use. + * @param bool $preserveHost Preserve the original state of the Host header. * @return static */ public function withUri(UriInterface $uri, $preserveHost = false) { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -359,7 +363,7 @@ class PsrRequestStub implements ServerRequestInterface */ public function getServerParams() { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -374,7 +378,7 @@ class PsrRequestStub implements ServerRequestInterface */ public function getCookieParams() { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -396,7 +400,7 @@ class PsrRequestStub implements ServerRequestInterface */ public function withCookieParams(array $cookies) { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -440,7 +444,7 @@ class PsrRequestStub implements ServerRequestInterface */ public function withQueryParams(array $query) { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -457,7 +461,7 @@ class PsrRequestStub implements ServerRequestInterface */ public function getUploadedFiles() { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -473,7 +477,7 @@ class PsrRequestStub implements ServerRequestInterface */ public function withUploadedFiles(array $uploadedFiles) { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -526,7 +530,7 @@ class PsrRequestStub implements ServerRequestInterface */ public function withParsedBody($data) { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -542,7 +546,7 @@ class PsrRequestStub implements ServerRequestInterface */ public function getAttributes() { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -556,13 +560,13 @@ class PsrRequestStub implements ServerRequestInterface * specifying a default value to return if the attribute is not found. * * @see getAttributes() - * @param string $name The attribute name. - * @param mixed $default Default value to return if the attribute does not exist. + * @param string $name The attribute name. + * @param mixed $default Default value to return if the attribute does not exist. * @return mixed */ public function getAttribute($name, $default = null) { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -576,13 +580,13 @@ class PsrRequestStub implements ServerRequestInterface * updated attribute. * * @see getAttributes() - * @param string $name The attribute name. - * @param mixed $value The value of the attribute. + * @param string $name The attribute name. + * @param mixed $value The value of the attribute. * @return static */ public function withAttribute($name, $value) { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -601,6 +605,6 @@ class PsrRequestStub implements ServerRequestInterface */ public function withoutAttribute($name) { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } } diff --git a/tests/Server/Psr7/PsrResponseStub.php b/tests/Server/Psr7/PsrResponseStub.php index 01201f2..bac2ffb 100644 --- a/tests/Server/Psr7/PsrResponseStub.php +++ b/tests/Server/Psr7/PsrResponseStub.php @@ -1,10 +1,15 @@ headers[$name][] = $value; + return $tmp; } @@ -162,14 +168,14 @@ class PsrResponseStub implements ResponseInterface * immutability of the message, and MUST return an instance that has the * new header and/or value. * - * @param string $name Case-insensitive header field name to add. + * @param string $name Case-insensitive header field name to add. * @param string|string[] $value Header value(s). * @return static * @throws \InvalidArgumentException for invalid header names or values. */ public function withAddedHeader($name, $value) { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -186,7 +192,7 @@ class PsrResponseStub implements ResponseInterface */ public function withoutHeader($name) { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -196,7 +202,7 @@ class PsrResponseStub implements ResponseInterface */ public function getBody() { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -214,8 +220,9 @@ class PsrResponseStub implements ResponseInterface */ public function withBody(StreamInterface $body) { - $tmp = clone $this; + $tmp = clone $this; $tmp->body = $body; + return $tmp; } @@ -229,7 +236,7 @@ class PsrResponseStub implements ResponseInterface */ public function getStatusCode() { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -245,7 +252,7 @@ class PsrResponseStub implements ResponseInterface * * @link http://tools.ietf.org/html/rfc7231#section-6 * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml - * @param int $code The 3-digit integer result code to set. + * @param int $code The 3-digit integer result code to set. * @param string $reasonPhrase The reason phrase to use with the * provided status code; if none is provided, implementations MAY * use the defaults as suggested in the HTTP specification. @@ -254,8 +261,9 @@ class PsrResponseStub implements ResponseInterface */ public function withStatus($code, $reasonPhrase = '') { - $tmp = clone $this; + $tmp = clone $this; $tmp->statusCode = $code; + return $tmp; } @@ -274,6 +282,6 @@ class PsrResponseStub implements ResponseInterface */ public function getReasonPhrase() { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } -} \ No newline at end of file +} diff --git a/tests/Server/Psr7/PsrStreamStub.php b/tests/Server/Psr7/PsrStreamStub.php index 50adecf..736ada9 100644 --- a/tests/Server/Psr7/PsrStreamStub.php +++ b/tests/Server/Psr7/PsrStreamStub.php @@ -1,8 +1,16 @@ content?:''); + return strlen($this->content ?: ''); } /** @@ -66,7 +74,7 @@ class PsrStreamStub implements StreamInterface */ public function tell() { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -76,7 +84,7 @@ class PsrStreamStub implements StreamInterface */ public function eof() { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -86,7 +94,7 @@ class PsrStreamStub implements StreamInterface */ public function isSeekable() { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -103,7 +111,7 @@ class PsrStreamStub implements StreamInterface */ public function seek($offset, $whence = SEEK_SET) { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -118,7 +126,7 @@ class PsrStreamStub implements StreamInterface */ public function rewind() { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -141,6 +149,7 @@ class PsrStreamStub implements StreamInterface public function write($string) { $this->content = $string; + return strlen($string); } @@ -151,7 +160,7 @@ class PsrStreamStub implements StreamInterface */ public function isReadable() { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -166,7 +175,7 @@ class PsrStreamStub implements StreamInterface */ public function read($length) { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } /** @@ -195,6 +204,6 @@ class PsrStreamStub implements StreamInterface */ public function getMetadata($key = null) { - throw new \Exception("Not implemented"); + throw new \Exception('Not implemented'); } } diff --git a/tests/Server/PsrResponseTest.php b/tests/Server/PsrResponseTest.php index b3ff6ec..3f01538 100644 --- a/tests/Server/PsrResponseTest.php +++ b/tests/Server/PsrResponseTest.php @@ -1,18 +1,22 @@ 'value']); - $stream = new PsrStreamStub(); + $result = new ExecutionResult(['key' => 'value']); + $stream = new PsrStreamStub(); $psrResponse = new PsrResponseStub(); $helper = new Helper(); diff --git a/tests/Server/QueryExecutionTest.php b/tests/Server/QueryExecutionTest.php index f443d9e..334d5b9 100644 --- a/tests/Server/QueryExecutionTest.php +++ b/tests/Server/QueryExecutionTest.php @@ -1,4 +1,7 @@ buildSchema(); + $schema = $this->buildSchema(); $this->config = ServerConfig::create() ->setSchema($schema); } @@ -34,20 +37,36 @@ class QueryExecutionTest extends ServerTestCase $query = '{f1}'; $expected = [ - 'data' => [ - 'f1' => 'f1' - ] + 'data' => ['f1' => 'f1'], ]; $this->assertQueryResultEquals($expected, $query); } + private function assertQueryResultEquals($expected, $query, $variables = null) + { + $result = $this->executeQuery($query, $variables); + $this->assertArraySubset($expected, $result->toArray(true)); + + return $result; + } + + private function executeQuery($query, $variables = null, $readonly = false) + { + $op = OperationParams::create(['query' => $query, 'variables' => $variables], $readonly); + $helper = new Helper(); + $result = $helper->executeOperation($this->config, $op); + $this->assertInstanceOf(ExecutionResult::class, $result); + + return $result; + } + public function testReturnsSyntaxErrors() : void { $query = '{f1'; $result = $this->executeQuery($query); - $this->assertSame(null, $result->data); + $this->assertNull($result->data); $this->assertCount(1, $result->errors); $this->assertContains( 'Syntax Error: Expected Name, found ', @@ -70,11 +89,12 @@ class QueryExecutionTest extends ServerTestCase $expected = [ 'data' => [ 'fieldWithSafeException' => null, - 'f1' => 'f1' + 'f1' => 'f1', ], 'errors' => [ [ 'message' => 'This is the exception we want', + 'path' => ['fieldWithSafeException'], 'trace' => [] ] @@ -100,7 +120,7 @@ class QueryExecutionTest extends ServerTestCase public function testPassesRootValueAndContext() : void { $rootValue = 'myRootValue'; - $context = new \stdClass(); + $context = new \stdClass(); $this->config ->setContext($context) @@ -112,7 +132,7 @@ class QueryExecutionTest extends ServerTestCase } '; - $this->assertTrue(!isset($context->testedRootValue)); + $this->assertTrue(! isset($context->testedRootValue)); $this->executeQuery($query); $this->assertSame($rootValue, $context->testedRootValue); } @@ -120,30 +140,30 @@ class QueryExecutionTest extends ServerTestCase public function testPassesVariables() : void { $variables = ['a' => 'a', 'b' => 'b']; - $query = ' + $query = ' query ($a: String!, $b: String!) { a: fieldWithArg(arg: $a) b: fieldWithArg(arg: $b) } '; - $expected = [ + $expected = [ 'data' => [ 'a' => 'a', - 'b' => 'b' - ] + 'b' => 'b', + ], ]; $this->assertQueryResultEquals($expected, $query, $variables); } public function testPassesCustomValidationRules() : void { - $query = ' + $query = ' {nonExistentField} '; $expected = [ 'errors' => [ - ['message' => 'Cannot query field "nonExistentField" on type "Query".'] - ] + ['message' => 'Cannot query field "nonExistentField" on type "Query".'], + ], ]; $this->assertQueryResultEquals($expected, $query); @@ -151,15 +171,16 @@ class QueryExecutionTest extends ServerTestCase $called = false; $rules = [ - new CustomValidationRule('SomeRule', function() use (&$called) { + new CustomValidationRule('SomeRule', function () use (&$called) { $called = true; + return []; - }) + }), ]; $this->config->setValidationRules($rules); $expected = [ - 'data' => [] + 'data' => [], ]; $this->assertQueryResultEquals($expected, $query); $this->assertTrue($called); @@ -170,11 +191,12 @@ class QueryExecutionTest extends ServerTestCase $called = false; $params = $doc = $operationType = null; - $this->config->setValidationRules(function($p, $d, $o) use (&$called, &$params, &$doc, &$operationType) { - $called = true; - $params = $p; - $doc = $d; + $this->config->setValidationRules(function ($p, $d, $o) use (&$called, &$params, &$doc, &$operationType) { + $called = true; + $params = $p; + $doc = $d; $operationType = $o; + return []; }); @@ -188,23 +210,25 @@ class QueryExecutionTest extends ServerTestCase public function testAllowsDifferentValidationRulesDependingOnOperation() : void { - $q1 = '{f1}'; - $q2 = '{invalid}'; + $q1 = '{f1}'; + $q2 = '{invalid}'; $called1 = false; $called2 = false; - $this->config->setValidationRules(function(OperationParams $params) use ($q1, $q2, &$called1, &$called2) { + $this->config->setValidationRules(function (OperationParams $params) use ($q1, &$called1, &$called2) { if ($params->query === $q1) { $called1 = true; + return DocumentValidator::allRules(); - } else { - $called2 = true; - return [ - new CustomValidationRule('MyRule', function(ValidationContext $context) { - $context->reportError(new Error("This is the error we are looking for!")); - }) - ]; } + + $called2 = true; + + return [ + new CustomValidationRule('MyRule', function (ValidationContext $context) { + $context->reportError(new Error('This is the error we are looking for!')); + }), + ]; }); $expected = ['data' => ['f1' => 'f1']]; @@ -212,8 +236,8 @@ class QueryExecutionTest extends ServerTestCase $this->assertTrue($called1); $this->assertFalse($called2); - $called1 = false; - $called2 = false; + $called1 = false; + $called2 = false; $expected = ['errors' => [['message' => 'This is the error we are looking for!']]]; $this->assertQueryResultEquals($expected, $q2); $this->assertFalse($called1); @@ -223,7 +247,7 @@ class QueryExecutionTest extends ServerTestCase public function testAllowsSkippingValidation() : void { $this->config->setValidationRules([]); - $query = '{nonExistentField}'; + $query = '{nonExistentField}'; $expected = ['data' => []]; $this->assertQueryResultEquals($expected, $query); } @@ -235,23 +259,29 @@ class QueryExecutionTest extends ServerTestCase $expected = [ 'errors' => [ [ - 'message' => 'Persisted queries are not supported by this server', - 'category' => 'request' - ] - ] + 'message' => 'Persisted queries are not supported by this server', + 'category' => 'request', + ], + ], ]; $this->assertEquals($expected, $result->toArray()); } + private function executePersistedQuery($queryId, $variables = null) + { + $op = OperationParams::create(['queryId' => $queryId, 'variables' => $variables]); + $helper = new Helper(); + $result = $helper->executeOperation($this->config, $op); + $this->assertInstanceOf(ExecutionResult::class, $result); + + return $result; + } + public function testBatchedQueriesAreDisabledByDefault() : void { $batch = [ - [ - 'query' => '{invalid}' - ], - [ - 'query' => '{f1,fieldWithSafeException}' - ] + ['query' => '{invalid}'], + ['query' => '{f1,fieldWithSafeException}'], ]; $result = $this->executeBatchedQuery($batch); @@ -260,18 +290,18 @@ class QueryExecutionTest extends ServerTestCase [ 'errors' => [ [ - 'message' => 'Batched queries are not supported by this server', - 'category' => 'request' - ] - ] + 'message' => 'Batched queries are not supported by this server', + 'category' => 'request', + ], + ], ], [ 'errors' => [ [ - 'message' => 'Batched queries are not supported by this server', - 'category' => 'request' - ] - ] + 'message' => 'Batched queries are not supported by this server', + 'category' => 'request', + ], + ], ], ]; @@ -279,6 +309,31 @@ class QueryExecutionTest extends ServerTestCase $this->assertEquals($expected[1], $result[1]->toArray()); } + /** + * @param mixed[][] $qs + */ + private function executeBatchedQuery(array $qs) + { + $batch = []; + foreach ($qs as $params) { + $batch[] = OperationParams::create($params); + } + $helper = new Helper(); + $result = $helper->executeBatch($this->config, $batch); + $this->assertInternalType('array', $result); + $this->assertCount(count($qs), $result); + + foreach ($result as $index => $entry) { + $this->assertInstanceOf( + ExecutionResult::class, + $entry, + sprintf('Result at %s is not an instance of %s', $index, ExecutionResult::class) + ); + } + + return $result; + } + public function testMutationsAreNotAllowedInReadonlyMode() : void { $mutation = 'mutation { a }'; @@ -286,10 +341,10 @@ class QueryExecutionTest extends ServerTestCase $expected = [ 'errors' => [ [ - 'message' => 'GET supports only query operation', - 'category' => 'request' - ] - ] + 'message' => 'GET supports only query operation', + 'category' => 'request', + ], + ], ]; $result = $this->executeQuery($mutation, null, true); @@ -299,9 +354,10 @@ class QueryExecutionTest extends ServerTestCase public function testAllowsPersistentQueries() : void { $called = false; - $this->config->setPersistentQueryLoader(function($queryId, OperationParams $params) use (&$called) { + $this->config->setPersistentQueryLoader(function ($queryId, OperationParams $params) use (&$called) { $called = true; $this->assertEquals('some-id', $queryId); + return '{f1}'; }); @@ -309,17 +365,16 @@ class QueryExecutionTest extends ServerTestCase $this->assertTrue($called); $expected = [ - 'data' => [ - 'f1' => 'f1' - ] + 'data' => ['f1' => 'f1'], ]; $this->assertEquals($expected, $result->toArray()); // Make sure it allows returning document node: $called = false; - $this->config->setPersistentQueryLoader(function($queryId, OperationParams $params) use (&$called) { + $this->config->setPersistentQueryLoader(function ($queryId, OperationParams $params) use (&$called) { $called = true; $this->assertEquals('some-id', $queryId); + return Parser::parse('{f1}'); }); $result = $this->executePersistedQuery('some-id'); @@ -334,7 +389,7 @@ class QueryExecutionTest extends ServerTestCase 'Persistent query loader must return query string or instance of GraphQL\Language\AST\DocumentNode ' . 'but got: {"err":"err"}' ); - $this->config->setPersistentQueryLoader(function($queryId, OperationParams $params) use (&$called) { + $this->config->setPersistentQueryLoader(function () { return ['err' => 'err']; }); $this->executePersistedQuery('some-id'); @@ -342,56 +397,55 @@ class QueryExecutionTest extends ServerTestCase public function testPersistedQueriesAreStillValidatedByDefault() : void { - $this->config->setPersistentQueryLoader(function() { + $this->config->setPersistentQueryLoader(function () { return '{invalid}'; }); - $result = $this->executePersistedQuery('some-id'); + $result = $this->executePersistedQuery('some-id'); $expected = [ 'errors' => [ [ - 'message' => 'Cannot query field "invalid" on type "Query".', - 'locations' => [ ['line' => 1, 'column' => 2] ], - 'category' => 'graphql' - ] - ] + 'message' => 'Cannot query field "invalid" on type "Query".', + 'locations' => [['line' => 1, 'column' => 2]], + 'category' => 'graphql', + ], + ], ]; $this->assertEquals($expected, $result->toArray()); - } public function testAllowSkippingValidationForPersistedQueries() : void { $this->config - ->setPersistentQueryLoader(function($queryId) { + ->setPersistentQueryLoader(function ($queryId) { if ($queryId === 'some-id') { return '{invalid}'; - } else { - return '{invalid2}'; } + + return '{invalid2}'; }) - ->setValidationRules(function(OperationParams $params) { + ->setValidationRules(function (OperationParams $params) { if ($params->queryId === 'some-id') { return []; - } else { - return DocumentValidator::allRules(); } + + return DocumentValidator::allRules(); }); - $result = $this->executePersistedQuery('some-id'); + $result = $this->executePersistedQuery('some-id'); $expected = [ - 'data' => [] + 'data' => [], ]; $this->assertEquals($expected, $result->toArray()); - $result = $this->executePersistedQuery('some-other-id'); + $result = $this->executePersistedQuery('some-other-id'); $expected = [ 'errors' => [ [ - 'message' => 'Cannot query field "invalid2" on type "Query".', - 'locations' => [ ['line' => 1, 'column' => 2] ], - 'category' => 'graphql' - ] - ] + 'message' => 'Cannot query field "invalid2" on type "Query".', + 'locations' => [['line' => 1, 'column' => 2]], + 'category' => 'graphql', + ], + ], ]; $this->assertEquals($expected, $result->toArray()); } @@ -400,7 +454,7 @@ class QueryExecutionTest extends ServerTestCase { $this->expectException(InvariantViolation::class); $this->expectExceptionMessage('Expecting validation rules to be array or callable returning array, but got: instance of stdClass'); - $this->config->setValidationRules(function(OperationParams $params) { + $this->config->setValidationRules(function (OperationParams $params) { return new \stdClass(); }); $this->executeQuery('{f1}'); @@ -411,28 +465,24 @@ class QueryExecutionTest extends ServerTestCase $this->config->setQueryBatching(true); $batch = [ + ['query' => '{invalid}'], + ['query' => '{f1,fieldWithSafeException}'], [ - 'query' => '{invalid}' - ], - [ - 'query' => '{f1,fieldWithSafeException}' - ], - [ - 'query' => ' + 'query' => ' query ($a: String!, $b: String!) { a: fieldWithArg(arg: $a) b: fieldWithArg(arg: $b) } ', 'variables' => ['a' => 'a', 'b' => 'b'], - ] + ], ]; $result = $this->executeBatchedQuery($batch); $expected = [ [ - 'errors' => [['message' => 'Cannot query field "invalid" on type "Query".']] + 'errors' => [['message' => 'Cannot query field "invalid" on type "Query".']], ], [ 'data' => [ @@ -440,15 +490,15 @@ class QueryExecutionTest extends ServerTestCase 'fieldWithSafeException' => null ], 'errors' => [ - ['message' => 'This is the exception we want'] - ] + ['message' => 'This is the exception we want'], + ], ], [ 'data' => [ 'a' => 'a', - 'b' => 'b' - ] - ] + 'b' => 'b', + ], + ], ]; $this->assertArraySubset($expected[0], $result[0]->toArray()); @@ -459,15 +509,9 @@ class QueryExecutionTest extends ServerTestCase public function testDeferredsAreSharedAmongAllBatchedQueries() : void { $batch = [ - [ - 'query' => '{dfd(num: 1)}' - ], - [ - 'query' => '{dfd(num: 2)}' - ], - [ - 'query' => '{dfd(num: 3)}', - ] + ['query' => '{dfd(num: 1)}'], + ['query' => '{dfd(num: 2)}'], + ['query' => '{dfd(num: 3)}'], ]; $calls = []; @@ -476,13 +520,14 @@ class QueryExecutionTest extends ServerTestCase ->setQueryBatching(true) ->setRootValue('1') ->setContext([ - 'buffer' => function($num) use (&$calls) { - $calls[] = "buffer: $num"; + 'buffer' => function ($num) use (&$calls) { + $calls[] = sprintf('buffer: %d', $num); + }, + 'load' => function ($num) use (&$calls) { + $calls[] = sprintf('load: %d', $num); + + return sprintf('loaded: %d', $num); }, - 'load' => function($num) use (&$calls) { - $calls[] = "load: $num"; - return "loaded: $num"; - } ]); $result = $this->executeBatchedQuery($batch); @@ -499,19 +544,13 @@ class QueryExecutionTest extends ServerTestCase $expected = [ [ - 'data' => [ - 'dfd' => 'loaded: 1' - ] + 'data' => ['dfd' => 'loaded: 1'], ], [ - 'data' => [ - 'dfd' => 'loaded: 2' - ] + 'data' => ['dfd' => 'loaded: 2'], ], [ - 'data' => [ - 'dfd' => 'loaded: 3' - ] + 'data' => ['dfd' => 'loaded: 3'], ], ]; @@ -522,7 +561,7 @@ class QueryExecutionTest extends ServerTestCase public function testValidatesParamsBeforeExecution() : void { - $op = OperationParams::create(['queryBad' => '{f1}']); + $op = OperationParams::create(['queryBad' => '{f1}']); $helper = new Helper(); $result = $helper->executeOperation($this->config, $op); $this->assertInstanceOf(ExecutionResult::class, $result); @@ -546,10 +585,10 @@ class QueryExecutionTest extends ServerTestCase $called = false; $params = $doc = $operationType = null; - $this->config->setContext(function($p, $d, $o) use (&$called, &$params, &$doc, &$operationType) { - $called = true; - $params = $p; - $doc = $d; + $this->config->setContext(function ($p, $d, $o) use (&$called, &$params, &$doc, &$operationType) { + $called = true; + $params = $p; + $doc = $d; $operationType = $o; }); @@ -566,10 +605,10 @@ class QueryExecutionTest extends ServerTestCase $called = false; $params = $doc = $operationType = null; - $this->config->setRootValue(function($p, $d, $o) use (&$called, &$params, &$doc, &$operationType) { - $called = true; - $params = $p; - $doc = $d; + $this->config->setRootValue(function ($p, $d, $o) use (&$called, &$params, &$doc, &$operationType) { + $called = true; + $params = $p; + $doc = $d; $operationType = $o; }); @@ -584,20 +623,21 @@ class QueryExecutionTest extends ServerTestCase public function testAppliesErrorFormatter() : void { $called = false; - $error = null; - $this->config->setErrorFormatter(function($e) use (&$called, &$error) { + $error = null; + $this->config->setErrorFormatter(function ($e) use (&$called, &$error) { $called = true; - $error = $e; + $error = $e; + return ['test' => 'formatted']; }); $result = $this->executeQuery('{fieldWithSafeException}'); $this->assertFalse($called); $formatted = $result->toArray(); - $expected = [ + $expected = [ 'errors' => [ - ['test' => 'formatted'] - ] + ['test' => 'formatted'], + ], ]; $this->assertTrue($called); $this->assertArraySubset($expected, $formatted); @@ -605,28 +645,29 @@ class QueryExecutionTest extends ServerTestCase // Assert debugging still works even with custom formatter $formatted = $result->toArray(Debug::INCLUDE_TRACE); - $expected = [ + $expected = [ 'errors' => [ [ - 'test' => 'formatted', - 'trace' => [] - ] - ] + 'test' => 'formatted', + 'trace' => [], + ], + ], ]; $this->assertArraySubset($expected, $formatted); } public function testAppliesErrorsHandler() : void { - $called = false; - $errors = null; + $called = false; + $errors = null; $formatter = null; - $this->config->setErrorsHandler(function($e, $f) use (&$called, &$errors, &$formatter) { - $called = true; - $errors = $e; + $this->config->setErrorsHandler(function ($e, $f) use (&$called, &$errors, &$formatter) { + $called = true; + $errors = $e; $formatter = $f; + return [ - ['test' => 'handled'] + ['test' => 'handled'], ]; }); @@ -634,10 +675,10 @@ class QueryExecutionTest extends ServerTestCase $this->assertFalse($called); $formatted = $result->toArray(); - $expected = [ + $expected = [ 'errors' => [ - ['test' => 'handled'] - ] + ['test' => 'handled'], + ], ]; $this->assertTrue($called); $this->assertArraySubset($expected, $formatted); @@ -646,46 +687,4 @@ class QueryExecutionTest extends ServerTestCase $this->assertInternalType('callable', $formatter); $this->assertArraySubset($expected, $formatted); } - - private function executePersistedQuery($queryId, $variables = null) - { - $op = OperationParams::create(['queryId' => $queryId, 'variables' => $variables]); - $helper = new Helper(); - $result = $helper->executeOperation($this->config, $op); - $this->assertInstanceOf(ExecutionResult::class, $result); - return $result; - } - - private function executeQuery($query, $variables = null, $readonly = false) - { - $op = OperationParams::create(['query' => $query, 'variables' => $variables], $readonly); - $helper = new Helper(); - $result = $helper->executeOperation($this->config, $op); - $this->assertInstanceOf(ExecutionResult::class, $result); - return $result; - } - - private function executeBatchedQuery(array $qs) - { - $batch = []; - foreach ($qs as $params) { - $batch[] = OperationParams::create($params); - } - $helper = new Helper(); - $result = $helper->executeBatch($this->config, $batch); - $this->assertInternalType('array', $result); - $this->assertCount(count($qs), $result); - - foreach ($result as $index => $entry) { - $this->assertInstanceOf(ExecutionResult::class, $entry, "Result at $index is not an instance of " . ExecutionResult::class); - } - return $result; - } - - private function assertQueryResultEquals($expected, $query, $variables = null) - { - $result = $this->executeQuery($query, $variables); - $this->assertArraySubset($expected, $result->toArray(true)); - return $result; - } } diff --git a/tests/Server/RequestParsingTest.php b/tests/Server/RequestParsingTest.php index 47d715c..ba3a215 100644 --- a/tests/Server/RequestParsingTest.php +++ b/tests/Server/RequestParsingTest.php @@ -1,7 +1,9 @@ $this->parseRawRequest('application/graphql', $query), - 'psr' => $this->parsePsrRequest('application/graphql', $query) + 'psr' => $this->parsePsrRequest('application/graphql', $query), ]; foreach ($parsed as $source => $parsedBody) { @@ -26,20 +30,91 @@ class RequestParsingTest extends TestCase } } + /** + * @param string $contentType + * @param string $content + * + * @return OperationParams|OperationParams[] + */ + private function parseRawRequest($contentType, $content, string $method = 'POST') + { + $_SERVER['CONTENT_TYPE'] = $contentType; + $_SERVER['REQUEST_METHOD'] = $method; + + $helper = new Helper(); + + return $helper->parseHttpRequest(function () use ($content) { + return $content; + }); + } + + /** + * @param string $contentType + * @param string $content + * + * @return OperationParams|OperationParams[] + */ + private function parsePsrRequest($contentType, $content, string $method = 'POST') + { + $psrRequestBody = new PsrStreamStub(); + $psrRequestBody->content = $content; + + $psrRequest = new PsrRequestStub(); + $psrRequest->headers['content-type'] = [$contentType]; + $psrRequest->method = $method; + $psrRequest->body = $psrRequestBody; + + if ($contentType === 'application/json') { + $parsedBody = json_decode($content, true); + $parsedBody = $parsedBody === false ? null : $parsedBody; + } else { + $parsedBody = null; + } + + $psrRequest->parsedBody = $parsedBody; + + $helper = new Helper(); + + return $helper->parsePsrRequest($psrRequest); + } + + /** + * @param OperationParams $params + * @param string $query + * @param string $queryId + * @param mixed|null $variables + * @param string $operation + */ + private function assertValidOperationParams( + $params, + $query, + $queryId = null, + $variables = null, + $operation = null, + $message = '' + ) { + $this->assertInstanceOf(OperationParams::class, $params, $message); + + $this->assertSame($query, $params->query, $message); + $this->assertSame($queryId, $params->queryId, $message); + $this->assertSame($variables, $params->variables, $message); + $this->assertSame($operation, $params->operation, $message); + } + public function testParsesUrlencodedRequest() : void { - $query = '{my query}'; + $query = '{my query}'; $variables = ['test' => 1, 'test2' => 2]; $operation = 'op'; - $post = [ - 'query' => $query, - 'variables' => $variables, - 'operationName' => $operation + $post = [ + 'query' => $query, + 'variables' => $variables, + 'operationName' => $operation, ]; $parsed = [ 'raw' => $this->parseRawFormUrlencodedRequest($post), - 'psr' => $this->parsePsrFormUrlEncodedRequest($post) + 'psr' => $this->parsePsrFormUrlEncodedRequest($post), ]; foreach ($parsed as $method => $parsedBody) { @@ -48,20 +123,53 @@ class RequestParsingTest extends TestCase } } + /** + * @param mixed[] $postValue + * @return OperationParams|OperationParams[] + */ + private function parseRawFormUrlencodedRequest($postValue) + { + $_SERVER['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; + $_SERVER['REQUEST_METHOD'] = 'POST'; + $_POST = $postValue; + + $helper = new Helper(); + + return $helper->parseHttpRequest(function () { + throw new InvariantViolation("Shouldn't read from php://input for urlencoded request"); + }); + } + + /** + * @param mixed[] $postValue + * @return OperationParams[]|OperationParams + */ + private function parsePsrFormUrlEncodedRequest($postValue) + { + $psrRequest = new PsrRequestStub(); + $psrRequest->headers['content-type'] = ['application/x-www-form-urlencoded']; + $psrRequest->method = 'POST'; + $psrRequest->parsedBody = $postValue; + + $helper = new Helper(); + + return $helper->parsePsrRequest($psrRequest); + } + public function testParsesGetRequest() : void { - $query = '{my query}'; + $query = '{my query}'; $variables = ['test' => 1, 'test2' => 2]; $operation = 'op'; - $get = [ - 'query' => $query, - 'variables' => $variables, - 'operationName' => $operation + $get = [ + 'query' => $query, + 'variables' => $variables, + 'operationName' => $operation, ]; $parsed = [ 'raw' => $this->parseRawGetRequest($get), - 'psr' => $this->parsePsrGetRequest($get) + 'psr' => $this->parsePsrGetRequest($get), ]; foreach ($parsed as $method => $parsedBody) { @@ -70,20 +178,51 @@ class RequestParsingTest extends TestCase } } + /** + * @param mixed[] $getValue + * @return OperationParams + */ + private function parseRawGetRequest($getValue) + { + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_GET = $getValue; + + $helper = new Helper(); + + return $helper->parseHttpRequest(function () { + throw new InvariantViolation("Shouldn't read from php://input for urlencoded request"); + }); + } + + /** + * @param mixed[] $getValue + * @return OperationParams[]|OperationParams + */ + private function parsePsrGetRequest($getValue) + { + $psrRequest = new PsrRequestStub(); + $psrRequest->method = 'GET'; + $psrRequest->queryParams = $getValue; + + $helper = new Helper(); + + return $helper->parsePsrRequest($psrRequest); + } + public function testParsesMultipartFormdataRequest() : void { - $query = '{my query}'; + $query = '{my query}'; $variables = ['test' => 1, 'test2' => 2]; $operation = 'op'; - $post = [ - 'query' => $query, - 'variables' => $variables, - 'operationName' => $operation + $post = [ + 'query' => $query, + 'variables' => $variables, + 'operationName' => $operation, ]; $parsed = [ 'raw' => $this->parseRawMultipartFormdataRequest($post), - 'psr' => $this->parsePsrMultipartFormdataRequest($post) + 'psr' => $this->parsePsrMultipartFormdataRequest($post), ]; foreach ($parsed as $method => $parsedBody) { @@ -92,20 +231,53 @@ class RequestParsingTest extends TestCase } } + /** + * @param mixed[] $postValue + * @return OperationParams|OperationParams[] + */ + private function parseRawMultipartFormDataRequest($postValue) + { + $_SERVER['CONTENT_TYPE'] = 'multipart/form-data; boundary=----FormBoundary'; + $_SERVER['REQUEST_METHOD'] = 'POST'; + $_POST = $postValue; + + $helper = new Helper(); + + return $helper->parseHttpRequest(function () { + throw new InvariantViolation("Shouldn't read from php://input for multipart/form-data request"); + }); + } + + /** + * @param mixed[] $postValue + * @return OperationParams|OperationParams[] + */ + private function parsePsrMultipartFormDataRequest($postValue) + { + $psrRequest = new PsrRequestStub(); + $psrRequest->headers['content-type'] = ['multipart/form-data; boundary=----FormBoundary']; + $psrRequest->method = 'POST'; + $psrRequest->parsedBody = $postValue; + + $helper = new Helper(); + + return $helper->parsePsrRequest($psrRequest); + } + public function testParsesJSONRequest() : void { - $query = '{my query}'; + $query = '{my query}'; $variables = ['test' => 1, 'test2' => 2]; $operation = 'op'; - $body = [ - 'query' => $query, - 'variables' => $variables, - 'operationName' => $operation + $body = [ + 'query' => $query, + 'variables' => $variables, + 'operationName' => $operation, ]; $parsed = [ 'raw' => $this->parseRawRequest('application/json', json_encode($body)), - 'psr' => $this->parsePsrRequest('application/json', json_encode($body)) + 'psr' => $this->parsePsrRequest('application/json', json_encode($body)), ]; foreach ($parsed as $method => $parsedBody) { $this->assertValidOperationParams($parsedBody, $query, null, $variables, $operation, $method); @@ -115,18 +287,18 @@ class RequestParsingTest extends TestCase public function testParsesVariablesAsJSON() : void { - $query = '{my query}'; + $query = '{my query}'; $variables = ['test' => 1, 'test2' => 2]; $operation = 'op'; - $body = [ - 'query' => $query, - 'variables' => json_encode($variables), - 'operationName' => $operation + $body = [ + 'query' => $query, + 'variables' => json_encode($variables), + 'operationName' => $operation, ]; $parsed = [ 'raw' => $this->parseRawRequest('application/json', json_encode($body)), - 'psr' => $this->parsePsrRequest('application/json', json_encode($body)) + 'psr' => $this->parsePsrRequest('application/json', json_encode($body)), ]; foreach ($parsed as $method => $parsedBody) { $this->assertValidOperationParams($parsedBody, $query, null, $variables, $operation, $method); @@ -136,14 +308,14 @@ class RequestParsingTest extends TestCase public function testIgnoresInvalidVariablesJson() : void { - $query = '{my query}'; + $query = '{my query}'; $variables = '"some invalid json'; $operation = 'op'; - $body = [ - 'query' => $query, - 'variables' => $variables, - 'operationName' => $operation + $body = [ + 'query' => $query, + 'variables' => $variables, + 'operationName' => $operation, ]; $parsed = [ 'raw' => $this->parseRawRequest('application/json', json_encode($body)), @@ -157,27 +329,41 @@ class RequestParsingTest extends TestCase public function testParsesBatchJSONRequest() : void { - $body = [ + $body = [ [ - 'query' => '{my query}', - 'variables' => ['test' => 1, 'test2' => 2], - 'operationName' => 'op' + 'query' => '{my query}', + 'variables' => ['test' => 1, 'test2' => 2], + 'operationName' => 'op', ], [ - 'queryId' => 'my-query-id', - 'variables' => ['test' => 1, 'test2' => 2], - 'operationName' => 'op2' + 'queryId' => 'my-query-id', + 'variables' => ['test' => 1, 'test2' => 2], + 'operationName' => 'op2', ], ]; $parsed = [ 'raw' => $this->parseRawRequest('application/json', json_encode($body)), - 'psr' => $this->parsePsrRequest('application/json', json_encode($body)) + 'psr' => $this->parsePsrRequest('application/json', json_encode($body)), ]; foreach ($parsed as $method => $parsedBody) { $this->assertInternalType('array', $parsedBody, $method); $this->assertCount(2, $parsedBody, $method); - $this->assertValidOperationParams($parsedBody[0], $body[0]['query'], null, $body[0]['variables'], $body[0]['operationName'], $method); - $this->assertValidOperationParams($parsedBody[1], null, $body[1]['queryId'], $body[1]['variables'], $body[1]['operationName'], $method); + $this->assertValidOperationParams( + $parsedBody[0], + $body[0]['query'], + null, + $body[0]['variables'], + $body[0]['operationName'], + $method + ); + $this->assertValidOperationParams( + $parsedBody[1], + null, + $body[1]['queryId'], + $body[1]['variables'], + $body[1]['operationName'], + $method + ); } } @@ -187,8 +373,8 @@ class RequestParsingTest extends TestCase $this->expectException(RequestError::class); $this->expectExceptionMessage('Could not parse JSON: Syntax error'); - $this->parseRawRequest('application/json', $body); - } + $this->parseRawRequest('application/json', $body); + } public function testFailsParsingInvalidRawJsonRequestPsr() : void { @@ -196,7 +382,7 @@ class RequestParsingTest extends TestCase $this->expectException(InvariantViolation::class); $this->expectExceptionMessage('PSR-7 request is expected to provide parsed body for "application/json" requests but got null'); - $this->parsePsrRequest('application/json', $body); + $this->parsePsrRequest('application/json', $body); } public function testFailsParsingNonPreParsedPsrRequest() : void @@ -222,8 +408,8 @@ class RequestParsingTest extends TestCase $this->expectException(RequestError::class); $this->expectExceptionMessage('GraphQL Server expects JSON object or array, but got "str"'); - $this->parseRawRequest('application/json', $body); - } + $this->parseRawRequest('application/json', $body); + } public function testFailsParsingNonArrayOrObjectJsonRequestPsr() : void { @@ -231,13 +417,13 @@ class RequestParsingTest extends TestCase $this->expectException(RequestError::class); $this->expectExceptionMessage('GraphQL Server expects JSON object or array, but got "str"'); - $this->parsePsrRequest('application/json', $body); - } + $this->parsePsrRequest('application/json', $body); + } public function testFailsParsingInvalidContentTypeRaw() : void { $contentType = 'not-supported-content-type'; - $body = 'test'; + $body = 'test'; $this->expectException(RequestError::class); $this->expectExceptionMessage('Unexpected content type: "not-supported-content-type"'); @@ -247,194 +433,38 @@ class RequestParsingTest extends TestCase public function testFailsParsingInvalidContentTypePsr() : void { $contentType = 'not-supported-content-type'; - $body = 'test'; + $body = 'test'; $this->expectException(RequestError::class); $this->expectExceptionMessage('Unexpected content type: "not-supported-content-type"'); - $this->parseRawRequest($contentType, $body); - } + $this->parseRawRequest($contentType, $body); + } public function testFailsWithMissingContentTypeRaw() : void { $this->expectException(RequestError::class); $this->expectExceptionMessage('Missing "Content-Type" header'); - $this->parseRawRequest(null, 'test'); - } + $this->parseRawRequest(null, 'test'); + } public function testFailsWithMissingContentTypePsr() : void { $this->expectException(RequestError::class); $this->expectExceptionMessage('Missing "Content-Type" header'); - $this->parsePsrRequest(null, 'test'); + $this->parsePsrRequest(null, 'test'); } public function testFailsOnMethodsOtherThanPostOrGetRaw() : void { $this->expectException(RequestError::class); $this->expectExceptionMessage('HTTP Method "PUT" is not supported'); - $this->parseRawRequest('application/json', json_encode([]), "PUT"); + $this->parseRawRequest('application/json', json_encode([]), 'PUT'); } public function testFailsOnMethodsOtherThanPostOrGetPsr() : void { $this->expectException(RequestError::class); $this->expectExceptionMessage('HTTP Method "PUT" is not supported'); - $this->parsePsrRequest('application/json', json_encode([]), "PUT"); - } - - /** - * @param string $contentType - * @param string $content - * @param $method - * - * @return OperationParams|OperationParams[] - */ - private function parseRawRequest($contentType, $content, $method = 'POST') - { - $_SERVER['CONTENT_TYPE'] = $contentType; - $_SERVER['REQUEST_METHOD'] = $method; - - $helper = new Helper(); - return $helper->parseHttpRequest(function() use ($content) { - return $content; - }); - } - - /** - * @param string $contentType - * @param string $content - * @param $method - * - * @return OperationParams|OperationParams[] - */ - private function parsePsrRequest($contentType, $content, $method = 'POST') - { - $psrRequestBody = new PsrStreamStub(); - $psrRequestBody->content = $content; - - $psrRequest = new PsrRequestStub(); - $psrRequest->headers['content-type'] = [$contentType]; - $psrRequest->method = $method; - $psrRequest->body = $psrRequestBody; - - if ($contentType === 'application/json') { - $parsedBody = json_decode($content, true); - $parsedBody = $parsedBody === false ? null : $parsedBody; - } else { - $parsedBody = null; - } - - $psrRequest->parsedBody = $parsedBody; - - $helper = new Helper(); - return $helper->parsePsrRequest($psrRequest); - } - - /** - * @param array $postValue - * @return OperationParams|OperationParams[] - */ - private function parseRawFormUrlencodedRequest($postValue) - { - $_SERVER['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; - $_SERVER['REQUEST_METHOD'] = 'POST'; - $_POST = $postValue; - - $helper = new Helper(); - return $helper->parseHttpRequest(function() { - throw new InvariantViolation("Shouldn't read from php://input for urlencoded request"); - }); - } - - /** - * @param $postValue - * @return array|Helper - */ - private function parsePsrFormUrlEncodedRequest($postValue) - { - $psrRequest = new PsrRequestStub(); - $psrRequest->headers['content-type'] = ['application/x-www-form-urlencoded']; - $psrRequest->method = 'POST'; - $psrRequest->parsedBody = $postValue; - - $helper = new Helper(); - return $helper->parsePsrRequest($psrRequest); - } - - /** - * @param array $postValue - * @return OperationParams|OperationParams[] - */ - private function parseRawMultipartFormDataRequest($postValue) - { - $_SERVER['CONTENT_TYPE'] = 'multipart/form-data; boundary=----FormBoundary'; - $_SERVER['REQUEST_METHOD'] = 'POST'; - $_POST = $postValue; - - $helper = new Helper(); - return $helper->parseHttpRequest(function() { - throw new InvariantViolation("Shouldn't read from php://input for multipart/form-data request"); - }); - } - - /** - * @param $postValue - * @return array|Helper - */ - private function parsePsrMultipartFormDataRequest($postValue) - { - $psrRequest = new PsrRequestStub(); - $psrRequest->headers['content-type'] = ['multipart/form-data; boundary=----FormBoundary']; - $psrRequest->method = 'POST'; - $psrRequest->parsedBody = $postValue; - - $helper = new Helper(); - return $helper->parsePsrRequest($psrRequest); - } - - /** - * @param $getValue - * @return OperationParams - */ - private function parseRawGetRequest($getValue) - { - $_SERVER['REQUEST_METHOD'] = 'GET'; - $_GET = $getValue; - - $helper = new Helper(); - return $helper->parseHttpRequest(function() { - throw new InvariantViolation("Shouldn't read from php://input for urlencoded request"); - }); - } - - /** - * @param $getValue - * @return array|Helper - */ - private function parsePsrGetRequest($getValue) - { - $psrRequest = new PsrRequestStub(); - $psrRequest->method = 'GET'; - $psrRequest->queryParams = $getValue; - - $helper = new Helper(); - return $helper->parsePsrRequest($psrRequest); - } - - /** - * @param OperationParams $params - * @param string $query - * @param string $queryId - * @param array $variables - * @param string $operation - */ - private function assertValidOperationParams($params, $query, $queryId = null, $variables = null, $operation = null, $message = '') - { - $this->assertInstanceOf(OperationParams::class, $params, $message); - - $this->assertSame($query, $params->query, $message); - $this->assertSame($queryId, $params->queryId, $message); - $this->assertSame($variables, $params->variables, $message); - $this->assertSame($operation, $params->operation, $message); + $this->parsePsrRequest('application/json', json_encode([]), 'PUT'); } } diff --git a/tests/Server/RequestValidationTest.php b/tests/Server/RequestValidationTest.php index e205bfd..07b99fe 100644 --- a/tests/Server/RequestValidationTest.php +++ b/tests/Server/RequestValidationTest.php @@ -1,4 +1,7 @@ 'b', 'c' => 'd']; $operation = 'op'; $parsedBody = OperationParams::create([ - 'query' => $query, - 'variables' => $variables, + 'query' => $query, + 'variables' => $variables, 'operationName' => $operation, ]); $this->assertValid($parsedBody); } + private function assertValid($parsedRequest) + { + $helper = new Helper(); + $errors = $helper->validateOperationParams($parsedRequest); + $this->assertEmpty($errors, isset($errors[0]) ? $errors[0]->getMessage() : ''); + } + public function testRequestWithQueryIdShouldValidate() : void { - $queryId = 'some-query-id'; + $queryId = 'some-query-id'; $variables = ['a' => 'b', 'c' => 'd']; $operation = 'op'; $parsedBody = OperationParams::create([ - 'queryId' => $queryId, - 'variables' => $variables, + 'queryId' => $queryId, + 'variables' => $variables, 'operationName' => $operation, ]); @@ -40,7 +50,7 @@ class RequestValidationTest extends TestCase public function testRequiresQueryOrQueryId() : void { $parsedBody = OperationParams::create([ - 'variables' => ['foo' => 'bar'], + 'variables' => ['foo' => 'bar'], 'operationName' => 'op', ]); @@ -50,10 +60,21 @@ class RequestValidationTest extends TestCase ); } + private function assertInputError($parsedRequest, $expectedMessage) + { + $helper = new Helper(); + $errors = $helper->validateOperationParams($parsedRequest); + if (! empty($errors[0])) { + $this->assertEquals($expectedMessage, $errors[0]->getMessage()); + } else { + $this->fail('Expected error not returned'); + } + } + public function testFailsWhenBothQueryAndQueryIdArePresent() : void { $parsedBody = OperationParams::create([ - 'query' => '{my query}', + 'query' => '{my query}', 'queryId' => 'my-query-id', ]); @@ -66,7 +87,7 @@ class RequestValidationTest extends TestCase public function testFailsWhenQueryParameterIsNotString() : void { $parsedBody = OperationParams::create([ - 'query' => ['t' => '{my query}'] + 'query' => ['t' => '{my query}'], ]); $this->assertInputError( @@ -78,7 +99,7 @@ class RequestValidationTest extends TestCase public function testFailsWhenQueryIdParameterIsNotString() : void { $parsedBody = OperationParams::create([ - 'queryId' => ['t' => '{my query}'] + 'queryId' => ['t' => '{my query}'], ]); $this->assertInputError( @@ -90,8 +111,8 @@ class RequestValidationTest extends TestCase public function testFailsWhenOperationParameterIsNotString() : void { $parsedBody = OperationParams::create([ - 'query' => '{my query}', - 'operationName' => [] + 'query' => '{my query}', + 'operationName' => [], ]); $this->assertInputError( @@ -105,17 +126,17 @@ class RequestValidationTest extends TestCase */ public function testIgnoresNullAndEmptyStringVariables() : void { - $query = '{my q}'; + $query = '{my q}'; $parsedBody = OperationParams::create([ - 'query' => $query, - 'variables' => null + 'query' => $query, + 'variables' => null, ]); $this->assertValid($parsedBody); - $variables = ""; + $variables = ''; $parsedBody = OperationParams::create([ - 'query' => $query, - 'variables' => $variables + 'query' => $query, + 'variables' => $variables, ]); $this->assertValid($parsedBody); } @@ -123,8 +144,8 @@ class RequestValidationTest extends TestCase public function testFailsWhenVariablesParameterIsNotObject() : void { $parsedBody = OperationParams::create([ - 'query' => '{my query}', - 'variables' => 0 + 'query' => '{my query}', + 'variables' => 0, ]); $this->assertInputError( @@ -132,22 +153,4 @@ class RequestValidationTest extends TestCase 'GraphQL Request parameter "variables" must be object or JSON string parsed to object, but got 0' ); } - - private function assertValid($parsedRequest) - { - $helper = new Helper(); - $errors = $helper->validateOperationParams($parsedRequest); - $this->assertEmpty($errors, isset($errors[0]) ? $errors[0]->getMessage() : ''); - } - - private function assertInputError($parsedRequest, $expectedMessage) - { - $helper = new Helper(); - $errors = $helper->validateOperationParams($parsedRequest); - if (!empty($errors[0])) { - $this->assertEquals($expectedMessage, $errors[0]->getMessage()); - } else { - $this->fail('Expected error not returned'); - } - } } diff --git a/tests/Server/ServerConfigTest.php b/tests/Server/ServerConfigTest.php index d8af920..b02eb38 100644 --- a/tests/Server/ServerConfigTest.php +++ b/tests/Server/ServerConfigTest.php @@ -1,12 +1,15 @@ assertEquals(null, $config->getSchema()); - $this->assertEquals(null, $config->getContext()); - $this->assertEquals(null, $config->getRootValue()); - $this->assertEquals(null, $config->getErrorFormatter()); - $this->assertEquals(null, $config->getErrorsHandler()); - $this->assertEquals(null, $config->getPromiseAdapter()); - $this->assertEquals(null, $config->getValidationRules()); - $this->assertEquals(null, $config->getFieldResolver()); - $this->assertEquals(null, $config->getPersistentQueryLoader()); - $this->assertEquals(false, $config->getDebug()); - $this->assertEquals(false, $config->getQueryBatching()); + $this->assertNull($config->getSchema()); + $this->assertNull($config->getContext()); + $this->assertNull($config->getRootValue()); + $this->assertNull($config->getErrorFormatter()); + $this->assertNull($config->getErrorsHandler()); + $this->assertNull($config->getPromiseAdapter()); + $this->assertNull($config->getValidationRules()); + $this->assertNull($config->getFieldResolver()); + $this->assertNull($config->getPersistentQueryLoader()); + $this->assertFalse($config->getDebug()); + $this->assertFalse($config->getQueryBatching()); } public function testAllowsSettingSchema() : void @@ -70,7 +73,8 @@ class ServerConfigTest extends TestCase { $config = ServerConfig::create(); - $formatter = function() {}; + $formatter = function () { + }; $config->setErrorFormatter($formatter); $this->assertSame($formatter, $config->getErrorFormatter()); @@ -83,7 +87,8 @@ class ServerConfigTest extends TestCase { $config = ServerConfig::create(); - $handler = function() {}; + $handler = function () { + }; $config->setErrorsHandler($handler); $this->assertSame($handler, $config->getErrorsHandler()); @@ -113,11 +118,17 @@ class ServerConfigTest extends TestCase $config->setValidationRules($rules); $this->assertSame($rules, $config->getValidationRules()); - $rules = [function() {}]; + $rules = [function () { + }, + ]; $config->setValidationRules($rules); $this->assertSame($rules, $config->getValidationRules()); - $rules = function() {return [function() {}];}; + $rules = function () { + return [function () { + }, + ]; + }; $config->setValidationRules($rules); $this->assertSame($rules, $config->getValidationRules()); } @@ -126,7 +137,8 @@ class ServerConfigTest extends TestCase { $config = ServerConfig::create(); - $resolver = function() {}; + $resolver = function () { + }; $config->setFieldResolver($resolver); $this->assertSame($resolver, $config->getFieldResolver()); @@ -139,7 +151,8 @@ class ServerConfigTest extends TestCase { $config = ServerConfig::create(); - $loader = function() {}; + $loader = function () { + }; $config->setPersistentQueryLoader($loader); $this->assertSame($loader, $config->getPersistentQueryLoader()); @@ -153,27 +166,32 @@ class ServerConfigTest extends TestCase $config = ServerConfig::create(); $config->setDebug(true); - $this->assertSame(true, $config->getDebug()); + $this->assertTrue($config->getDebug()); $config->setDebug(false); - $this->assertSame(false, $config->getDebug()); + $this->assertFalse($config->getDebug()); } public function testAcceptsArray() : void { $arr = [ - 'schema' => new \GraphQL\Type\Schema([ - 'query' => new ObjectType(['name' => 't', 'fields' => ['a' => Type::string()]]) + 'schema' => new Schema([ + 'query' => new ObjectType(['name' => 't', 'fields' => ['a' => Type::string()]]), ]), - 'context' => new \stdClass(), - 'rootValue' => new \stdClass(), - 'errorFormatter' => function() {}, - 'promiseAdapter' => new SyncPromiseAdapter(), - 'validationRules' => [function() {}], - 'fieldResolver' => function() {}, - 'persistentQueryLoader' => function() {}, - 'debug' => true, - 'queryBatching' => true, + 'context' => new \stdClass(), + 'rootValue' => new \stdClass(), + 'errorFormatter' => function () { + }, + 'promiseAdapter' => new SyncPromiseAdapter(), + 'validationRules' => [function () { + }, + ], + 'fieldResolver' => function () { + }, + 'persistentQueryLoader' => function () { + }, + 'debug' => true, + 'queryBatching' => true, ]; $config = ServerConfig::create($arr); @@ -186,15 +204,13 @@ class ServerConfigTest extends TestCase $this->assertSame($arr['validationRules'], $config->getValidationRules()); $this->assertSame($arr['fieldResolver'], $config->getFieldResolver()); $this->assertSame($arr['persistentQueryLoader'], $config->getPersistentQueryLoader()); - $this->assertSame(true, $config->getDebug()); - $this->assertSame(true, $config->getQueryBatching()); + $this->assertTrue($config->getDebug()); + $this->assertTrue($config->getQueryBatching()); } public function testThrowsOnInvalidArrayKey() : void { - $arr = [ - 'missingKey' => 'value' - ]; + $arr = ['missingKey' => 'value']; $this->expectException(InvariantViolation::class); $this->expectExceptionMessage('Unknown server config option "missingKey"'); @@ -204,7 +220,7 @@ class ServerConfigTest extends TestCase public function testInvalidValidationRules() : void { - $rules = new \stdClass(); + $rules = new \stdClass(); $config = ServerConfig::create(); $this->expectException(InvariantViolation::class); diff --git a/tests/Server/ServerTestCase.php b/tests/Server/ServerTestCase.php index 5620337..7c87258 100644 --- a/tests/Server/ServerTestCase.php +++ b/tests/Server/ServerTestCase.php @@ -1,6 +1,8 @@ new ObjectType([ - 'name' => 'Query', + 'query' => new ObjectType([ + 'name' => 'Query', 'fields' => [ - 'f1' => [ - 'type' => Type::string(), - 'resolve' => function($root, $args, $context, $info) { + 'f1' => [ + 'type' => Type::string(), + 'resolve' => function ($root, $args, $context, $info) { return $info->fieldName; - } + }, ], - 'fieldWithPhpError' => [ - 'type' => Type::string(), - 'resolve' => function($root, $args, $context, $info) { + 'fieldWithPhpError' => [ + 'type' => Type::string(), + 'resolve' => function ($root, $args, $context, $info) { trigger_error('deprecated', E_USER_DEPRECATED); trigger_error('notice', E_USER_NOTICE); trigger_error('warning', E_USER_WARNING); $a = []; $a['test']; // should produce PHP notice + return $info->fieldName; - } + }, ], 'fieldWithSafeException' => [ 'type' => Type::string(), @@ -48,54 +55,56 @@ abstract class ServerTestCase extends TestCase } ], 'testContextAndRootValue' => [ - 'type' => Type::string(), - 'resolve' => function($root, $args, $context, $info) { + 'type' => Type::string(), + 'resolve' => function ($root, $args, $context, $info) { $context->testedRootValue = $root; + return $info->fieldName; - } + }, ], - 'fieldWithArg' => [ - 'type' => Type::string(), - 'args' => [ + 'fieldWithArg' => [ + 'type' => Type::string(), + 'args' => [ 'arg' => [ - 'type' => Type::nonNull(Type::string()) + 'type' => Type::nonNull(Type::string()), ], ], - 'resolve' => function($root, $args) { + 'resolve' => function ($root, $args) { return $args['arg']; - } + }, ], - 'dfd' => [ - 'type' => Type::string(), - 'args' => [ + 'dfd' => [ + 'type' => Type::string(), + 'args' => [ 'num' => [ - 'type' => Type::nonNull(Type::int()) + 'type' => Type::nonNull(Type::int()), ], ], - 'resolve' => function($root, $args, $context) { + 'resolve' => function ($root, $args, $context) { $context['buffer']($args['num']); - return new Deferred(function() use ($args, $context) { + return new Deferred(function () use ($args, $context) { return $context['load']($args['num']); }); - } - ] - ] + }, + ], + ], ]), 'mutation' => new ObjectType([ - 'name' => 'Mutation', + 'name' => 'Mutation', 'fields' => [ 'm1' => [ 'type' => new ObjectType([ - 'name' => 'TestMutation', + 'name' => 'TestMutation', 'fields' => [ - 'result' => Type::string() - ] - ]) - ] - ] - ]) + 'result' => Type::string(), + ], + ]), + ], + ], + ]), ]); + return $schema; } } diff --git a/tests/Server/StandardServerTest.php b/tests/Server/StandardServerTest.php index 5c22788..3f5bfe4 100644 --- a/tests/Server/StandardServerTest.php +++ b/tests/Server/StandardServerTest.php @@ -1,4 +1,7 @@ buildSchema(); + $schema = $this->buildSchema(); $this->config = ServerConfig::create() ->setSchema($schema); } public function testSimpleRequestExecutionWithOutsideParsing() : void { - $body = json_encode([ - 'query' => '{f1}' - ]); + $body = json_encode(['query' => '{f1}']); $parsedBody = $this->parseRawRequest('application/json', $body); - $server = new StandardServer($this->config); + $server = new StandardServer($this->config); - $result = $server->executeRequest($parsedBody); + $result = $server->executeRequest($parsedBody); $expected = [ - 'data' => [ - 'f1' => 'f1', - ] + 'data' => ['f1' => 'f1'], ]; $this->assertEquals($expected, $result->toArray(true)); } + private function parseRawRequest($contentType, $content, $method = 'POST') + { + $_SERVER['CONTENT_TYPE'] = $contentType; + $_SERVER['REQUEST_METHOD'] = $method; + + $helper = new Helper(); + + return $helper->parseHttpRequest(function () use ($content) { + return $content; + }); + } + public function testSimplePsrRequestExecution() : void { - $body = [ - 'query' => '{f1}' - ]; + $body = ['query' => '{f1}']; $expected = [ - 'data' => [ - 'f1' => 'f1' - ] + 'data' => ['f1' => 'f1'], ]; $request = $this->preparePsrRequest('application/json', $body); $this->assertPsrRequestEquals($expected, $request); } - public function testMultipleOperationPsrRequestExecution() : void + private function preparePsrRequest($contentType, $parsedBody) { - $body = [ - 'query' => 'query firstOp {fieldWithPhpError} query secondOp {f1}', - 'operationName' => 'secondOp' - ]; + $psrRequest = new PsrRequestStub(); + $psrRequest->headers['content-type'] = [$contentType]; + $psrRequest->method = 'POST'; + $psrRequest->parsedBody = $parsedBody; - $expected = [ - 'data' => [ - 'f1' => 'f1' - ] - ]; + return $psrRequest; + } - $request = $this->preparePsrRequest('application/json', $body); - $this->assertPsrRequestEquals($expected, $request); + private function assertPsrRequestEquals($expected, $request) + { + $result = $this->executePsrRequest($request); + $this->assertArraySubset($expected, $result->toArray(true)); + + return $result; } private function executePsrRequest($psrRequest) @@ -79,33 +85,22 @@ class StandardServerTest extends ServerTestCase $server = new StandardServer($this->config); $result = $server->executePsrRequest($psrRequest); $this->assertInstanceOf(ExecutionResult::class, $result); + return $result; } - private function assertPsrRequestEquals($expected, $request) + public function testMultipleOperationPsrRequestExecution() : void { - $result = $this->executePsrRequest($request); - $this->assertArraySubset($expected, $result->toArray(true)); - return $result; - } + $body = [ + 'query' => 'query firstOp {fieldWithPhpError} query secondOp {f1}', + 'operationName' => 'secondOp', + ]; - private function preparePsrRequest($contentType, $parsedBody) - { - $psrRequest = new PsrRequestStub(); - $psrRequest->headers['content-type'] = [$contentType]; - $psrRequest->method = 'POST'; - $psrRequest->parsedBody = $parsedBody; - return $psrRequest; - } + $expected = [ + 'data' => ['f1' => 'f1'], + ]; - private function parseRawRequest($contentType, $content, $method = 'POST') - { - $_SERVER['CONTENT_TYPE'] = $contentType; - $_SERVER['REQUEST_METHOD'] = $method; - - $helper = new Helper(); - return $helper->parseHttpRequest(function() use ($content) { - return $content; - }); + $request = $this->preparePsrRequest('application/json', $body); + $this->assertPsrRequestEquals($expected, $request); } } diff --git a/tests/StarWarsData.php b/tests/StarWarsData.php index beb61fc..9034d99 100644 --- a/tests/StarWarsData.php +++ b/tests/StarWarsData.php @@ -1,62 +1,31 @@ '1000', - 'name' => 'Luke Skywalker', - 'friends' => ['1002', '1003', '2000', '2001'], - 'appearsIn' => [4, 5, 6], - 'homePlanet' => 'Tatooine', - ]; + $humans = self::humans(); + $droids = self::droids(); + if (isset($humans[$id])) { + return $humans[$id]; + } + if (isset($droids[$id])) { + return $droids[$id]; + } + + return null; } - private static function vader() - { - return [ - 'id' => '1001', - 'name' => 'Darth Vader', - 'friends' => ['1004'], - 'appearsIn' => [4, 5, 6], - 'homePlanet' => 'Tatooine', - ]; - } - - private static function han() - { - return [ - 'id' => '1002', - 'name' => 'Han Solo', - 'friends' => ['1000', '1003', '2001'], - 'appearsIn' => [4, 5, 6], - ]; - } - - private static function leia() - { - return [ - 'id' => '1003', - 'name' => 'Leia Organa', - 'friends' => ['1000', '1002', '2000', '2001'], - 'appearsIn' => [4, 5, 6], - 'homePlanet' => 'Alderaan', - ]; - } - - private static function tarkin() - { - return [ - 'id' => '1004', - 'name' => 'Wilhuff Tarkin', - 'friends' => ['1001'], - 'appearsIn' => [4], - ]; - } - - static function humans() + public static function humans() { return [ '1000' => self::luke(), @@ -67,13 +36,74 @@ class StarWarsData ]; } + private static function luke() + { + return [ + 'id' => '1000', + 'name' => 'Luke Skywalker', + 'friends' => ['1002', '1003', '2000', '2001'], + 'appearsIn' => [4, 5, 6], + 'homePlanet' => 'Tatooine', + ]; + } + + private static function vader() + { + return [ + 'id' => '1001', + 'name' => 'Darth Vader', + 'friends' => ['1004'], + 'appearsIn' => [4, 5, 6], + 'homePlanet' => 'Tatooine', + ]; + } + + private static function han() + { + return [ + 'id' => '1002', + 'name' => 'Han Solo', + 'friends' => ['1000', '1003', '2001'], + 'appearsIn' => [4, 5, 6], + ]; + } + + private static function leia() + { + return [ + 'id' => '1003', + 'name' => 'Leia Organa', + 'friends' => ['1000', '1002', '2000', '2001'], + 'appearsIn' => [4, 5, 6], + 'homePlanet' => 'Alderaan', + ]; + } + + private static function tarkin() + { + return [ + 'id' => '1004', + 'name' => 'Wilhuff Tarkin', + 'friends' => ['1001'], + 'appearsIn' => [4], + ]; + } + + public static function droids() + { + return [ + '2000' => self::threepio(), + '2001' => self::artoo(), + ]; + } + private static function threepio() { return [ - 'id' => '2000', - 'name' => 'C-3PO', - 'friends' => ['1000', '1002', '1003', '2001'], - 'appearsIn' => [4, 5, 6], + 'id' => '2000', + 'name' => 'C-3PO', + 'friends' => ['1000', '1002', '1003', '2001'], + 'appearsIn' => [4, 5, 6], 'primaryFunction' => 'Protocol', ]; } @@ -82,81 +112,60 @@ class StarWarsData * We export artoo directly because the schema returns him * from a root field, and hence needs to reference him. */ - static function artoo() + private static function artoo() { return [ - 'id' => '2001', - 'name' => 'R2-D2', - 'friends' => ['1000', '1002', '1003'], - 'appearsIn' => [4, 5, 6], + 'id' => '2001', + 'name' => 'R2-D2', + 'friends' => ['1000', '1002', '1003'], + 'appearsIn' => [4, 5, 6], 'primaryFunction' => 'Astromech', ]; } - static function droids() - { - return [ - '2000' => self::threepio(), - '2001' => self::artoo(), - ]; - } - - /** - * Helper function to get a character by ID. - */ - static function getCharacter($id) - { - $humans = self::humans(); - $droids = self::droids(); - if (isset($humans[$id])) { - return $humans[$id]; - } - if (isset($droids[$id])) { - return $droids[$id]; - } - return null; - } - /** * Allows us to query for a character's friends. */ - static function getFriends($character) + public static function getFriends($character) { return array_map([__CLASS__, 'getCharacter'], $character['friends']); } /** - * @param $episode - * @return array + * @param int $episode + * @return mixed[] */ - static function getHero($episode) + public static function getHero($episode) { if ($episode === 5) { // Luke is the hero of Episode V. return self::luke(); } + // Artoo is the hero otherwise. return self::artoo(); } /** - * @param $id + * @param string $id * @return mixed|null */ - static function getHuman($id) + public static function getHuman($id) { $humans = self::humans(); - return isset($humans[$id]) ? $humans[$id] : null; + + return $humans[$id] ?? null; } /** - * @param $id + * @param string $id * @return mixed|null */ - static function getDroid($id) + public static function getDroid($id) { $droids = self::droids(); - return isset($droids[$id]) ? $droids[$id] : null; + + return $droids[$id] ?? null; } } diff --git a/tests/StarWarsIntrospectionTest.php b/tests/StarWarsIntrospectionTest.php index 07c605e..9410ac5 100644 --- a/tests/StarWarsIntrospectionTest.php +++ b/tests/StarWarsIntrospectionTest.php @@ -1,4 +1,7 @@ '__EnumValue'], ['name' => '__Directive'], ['name' => '__DirectiveLocation'], - ] - ] + ], + ], ]; $this->assertValidQuery($query, $expected); } + /** + * Helper function to test a query and the expected response. + */ + private function assertValidQuery($query, $expected) : void + { + $this->assertEquals(['data' => $expected], GraphQL::executeQuery(StarWarsSchema::build(), $query)->toArray()); + } + /** * @see it('Allows querying the schema for query type') */ public function testAllowsQueryingTheSchemaForQueryType() : void { - $query = ' + $query = ' query IntrospectionQueryTypeQuery { __schema { queryType { @@ -66,10 +77,8 @@ class StarWarsIntrospectionTest extends TestCase '; $expected = [ '__schema' => [ - 'queryType' => [ - 'name' => 'Query' - ], - ] + 'queryType' => ['name' => 'Query'], + ], ]; $this->assertValidQuery($query, $expected); } @@ -79,7 +88,7 @@ class StarWarsIntrospectionTest extends TestCase */ public function testAllowsQueryingTheSchemaForASpecificType() : void { - $query = ' + $query = ' query IntrospectionDroidTypeQuery { __type(name: "Droid") { name @@ -87,9 +96,7 @@ class StarWarsIntrospectionTest extends TestCase } '; $expected = [ - '__type' => [ - 'name' => 'Droid' - ] + '__type' => ['name' => 'Droid'], ]; $this->assertValidQuery($query, $expected); } @@ -99,7 +106,7 @@ class StarWarsIntrospectionTest extends TestCase */ public function testAllowsQueryingForAnObjectKind() : void { - $query = ' + $query = ' query IntrospectionDroidKindQuery { __type(name: "Droid") { name @@ -110,8 +117,8 @@ class StarWarsIntrospectionTest extends TestCase $expected = [ '__type' => [ 'name' => 'Droid', - 'kind' => 'OBJECT' - ] + 'kind' => 'OBJECT', + ], ]; $this->assertValidQuery($query, $expected); } @@ -121,7 +128,7 @@ class StarWarsIntrospectionTest extends TestCase */ public function testAllowsQueryingForInterfaceKind() : void { - $query = ' + $query = ' query IntrospectionCharacterKindQuery { __type(name: "Character") { name @@ -132,8 +139,8 @@ class StarWarsIntrospectionTest extends TestCase $expected = [ '__type' => [ 'name' => 'Character', - 'kind' => 'INTERFACE' - ] + 'kind' => 'INTERFACE', + ], ]; $this->assertValidQuery($query, $expected); } @@ -143,7 +150,7 @@ class StarWarsIntrospectionTest extends TestCase */ public function testAllowsQueryingForObjectFields() : void { - $query = ' + $query = ' query IntrospectionDroidFieldsQuery { __type(name: "Droid") { name @@ -159,52 +166,52 @@ class StarWarsIntrospectionTest extends TestCase '; $expected = [ '__type' => [ - 'name' => 'Droid', + 'name' => 'Droid', 'fields' => [ [ 'name' => 'id', 'type' => [ 'name' => null, - 'kind' => 'NON_NULL' - ] + 'kind' => 'NON_NULL', + ], ], [ 'name' => 'name', 'type' => [ 'name' => 'String', - 'kind' => 'SCALAR' - ] + 'kind' => 'SCALAR', + ], ], [ 'name' => 'friends', 'type' => [ 'name' => null, - 'kind' => 'LIST' - ] + 'kind' => 'LIST', + ], ], [ 'name' => 'appearsIn', 'type' => [ 'name' => null, - 'kind' => 'LIST' - ] + 'kind' => 'LIST', + ], ], [ 'name' => 'secretBackstory', 'type' => [ 'name' => 'String', - 'kind' => 'SCALAR' - ] + 'kind' => 'SCALAR', + ], ], [ 'name' => 'primaryFunction', 'type' => [ 'name' => 'String', - 'kind' => 'SCALAR' - ] - ] - ] - ] + 'kind' => 'SCALAR', + ], + ], + ], + ], ]; $this->assertValidQuery($query, $expected); } @@ -214,7 +221,7 @@ class StarWarsIntrospectionTest extends TestCase */ public function testAllowsQueryingTheSchemaForNestedObjectFields() : void { - $query = ' + $query = ' query IntrospectionDroidNestedFieldsQuery { __type(name: "Droid") { name @@ -234,67 +241,67 @@ class StarWarsIntrospectionTest extends TestCase '; $expected = [ '__type' => [ - 'name' => 'Droid', + 'name' => 'Droid', 'fields' => [ [ 'name' => 'id', 'type' => [ - 'name' => null, - 'kind' => 'NON_NULL', + 'name' => null, + 'kind' => 'NON_NULL', 'ofType' => [ 'name' => 'String', - 'kind' => 'SCALAR' - ] - ] + 'kind' => 'SCALAR', + ], + ], ], [ 'name' => 'name', 'type' => [ - 'name' => 'String', - 'kind' => 'SCALAR', - 'ofType' => null - ] + 'name' => 'String', + 'kind' => 'SCALAR', + 'ofType' => null, + ], ], [ 'name' => 'friends', 'type' => [ - 'name' => null, - 'kind' => 'LIST', + 'name' => null, + 'kind' => 'LIST', 'ofType' => [ 'name' => 'Character', - 'kind' => 'INTERFACE' - ] - ] + 'kind' => 'INTERFACE', + ], + ], ], [ 'name' => 'appearsIn', 'type' => [ - 'name' => null, - 'kind' => 'LIST', + 'name' => null, + 'kind' => 'LIST', 'ofType' => [ 'name' => 'Episode', - 'kind' => 'ENUM' - ] - ] + 'kind' => 'ENUM', + ], + ], ], [ 'name' => 'secretBackstory', 'type' => [ - 'name' => 'String', - 'kind' => 'SCALAR', - 'ofType' => null - ] + 'name' => 'String', + 'kind' => 'SCALAR', + 'ofType' => null, + ], ], [ 'name' => 'primaryFunction', 'type' => [ - 'name' => 'String', - 'kind' => 'SCALAR', - 'ofType' => null - ] - ] - ] - ] + 'name' => 'String', + 'kind' => 'SCALAR', + 'ofType' => null, + ], + ], + ], + ], ]; $this->assertValidQuery($query, $expected); } @@ -329,7 +336,7 @@ class StarWarsIntrospectionTest extends TestCase } '; - $expected = array( + $expected = [ '__schema' => [ 'queryType' => [ 'fields' => [ @@ -337,13 +344,13 @@ class StarWarsIntrospectionTest extends TestCase 'name' => 'hero', 'args' => [ [ - 'defaultValue' => NULL, - 'description' => 'If omitted, returns the hero of the whole saga. If provided, returns the hero of that particular episode.', - 'name' => 'episode', - 'type' => [ - 'kind' => 'ENUM', - 'name' => 'Episode', - 'ofType' => NULL, + 'defaultValue' => null, + 'description' => 'If omitted, returns the hero of the whole saga. If provided, returns the hero of that particular episode.', + 'name' => 'episode', + 'type' => [ + 'kind' => 'ENUM', + 'name' => 'Episode', + 'ofType' => null, ], ], ], @@ -352,17 +359,17 @@ class StarWarsIntrospectionTest extends TestCase 'name' => 'human', 'args' => [ [ - 'name' => 'id', - 'description' => 'id of the human', - 'type' => [ - 'kind' => 'NON_NULL', - 'name' => NULL, + 'name' => 'id', + 'description' => 'id of the human', + 'type' => [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => [ 'kind' => 'SCALAR', 'name' => 'String', ], ], - 'defaultValue' => NULL, + 'defaultValue' => null, ], ], ], @@ -370,25 +377,25 @@ class StarWarsIntrospectionTest extends TestCase 'name' => 'droid', 'args' => [ [ - 'name' => 'id', - 'description' => 'id of the droid', - 'type' => [ - 'kind' => 'NON_NULL', - 'name' => NULL, + 'name' => 'id', + 'description' => 'id of the droid', + 'type' => [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => [ 'kind' => 'SCALAR', 'name' => 'String', ], ], - 'defaultValue' => NULL, + 'defaultValue' => null, ], ], ], ], ], ], - ); + ]; $this->assertValidQuery($query, $expected); } @@ -398,7 +405,7 @@ class StarWarsIntrospectionTest extends TestCase */ public function testAllowsQueryingTheSchemaForDocumentation() : void { - $query = ' + $query = ' query IntrospectionDroidDescriptionQuery { __type(name: "Droid") { name @@ -408,18 +415,10 @@ class StarWarsIntrospectionTest extends TestCase '; $expected = [ '__type' => [ - 'name' => 'Droid', - 'description' => 'A mechanical creature in the Star Wars universe.' - ] + 'name' => 'Droid', + 'description' => 'A mechanical creature in the Star Wars universe.', + ], ]; $this->assertValidQuery($query, $expected); } - - /** - * Helper function to test a query and the expected response. - */ - private function assertValidQuery($query, $expected) - { - $this->assertEquals(['data' => $expected], GraphQL::executeQuery(StarWarsSchema::build(), $query)->toArray()); - } } diff --git a/tests/StarWarsQueryTest.php b/tests/StarWarsQueryTest.php index aa478dd..c7b5a73 100644 --- a/tests/StarWarsQueryTest.php +++ b/tests/StarWarsQueryTest.php @@ -1,21 +1,20 @@ [ - 'name' => 'R2-D2' - ] + 'hero' => ['name' => 'R2-D2'], ]; $this->assertValidQuery($query, $expected); } + /** + * Helper function to test a query and the expected response. + */ + private function assertValidQuery($query, $expected) : void + { + $this->assertEquals( + ['data' => $expected], + GraphQL::executeQuery(StarWarsSchema::build(), $query)->toArray() + ); + } + + // Describe: Nested Queries + /** * @see it('Allows us to query for the ID and friends of R2-D2') */ public function testAllowsUsToQueryForTheIDAndFriendsOfR2D2() : void { - $query = ' + $query = ' query HeroNameAndFriendsQuery { hero { id @@ -48,32 +58,26 @@ class StarWarsQueryTest extends TestCase '; $expected = [ 'hero' => [ - 'id' => '2001', - 'name' => 'R2-D2', + 'id' => '2001', + 'name' => 'R2-D2', 'friends' => [ - [ - 'name' => 'Luke Skywalker', - ], - [ - 'name' => 'Han Solo', - ], - [ - 'name' => 'Leia Organa', - ], - ] - ] + ['name' => 'Luke Skywalker'], + ['name' => 'Han Solo'], + ['name' => 'Leia Organa'], + ], + ], ]; $this->assertValidQuery($query, $expected); } - // Describe: Nested Queries + // Describe: Using IDs and query parameters to refetch objects /** * @see it('Allows us to query for the friends of friends of R2-D2') */ public function testAllowsUsToQueryForTheFriendsOfFriendsOfR2D2() : void { - $query = ' + $query = ' query NestedQuery { hero { name @@ -89,52 +93,50 @@ class StarWarsQueryTest extends TestCase '; $expected = [ 'hero' => [ - 'name' => 'R2-D2', + 'name' => 'R2-D2', 'friends' => [ [ - 'name' => 'Luke Skywalker', - 'appearsIn' => ['NEWHOPE', 'EMPIRE', 'JEDI',], - 'friends' => [ - ['name' => 'Han Solo',], - ['name' => 'Leia Organa',], - ['name' => 'C-3PO',], - ['name' => 'R2-D2',], + 'name' => 'Luke Skywalker', + 'appearsIn' => ['NEWHOPE', 'EMPIRE', 'JEDI'], + 'friends' => [ + ['name' => 'Han Solo'], + ['name' => 'Leia Organa'], + ['name' => 'C-3PO'], + ['name' => 'R2-D2'], ], ], [ - 'name' => 'Han Solo', + 'name' => 'Han Solo', 'appearsIn' => ['NEWHOPE', 'EMPIRE', 'JEDI'], - 'friends' => [ - ['name' => 'Luke Skywalker',], + 'friends' => [ + ['name' => 'Luke Skywalker'], ['name' => 'Leia Organa'], - ['name' => 'R2-D2',], - ] + ['name' => 'R2-D2'], + ], ], [ - 'name' => 'Leia Organa', + 'name' => 'Leia Organa', 'appearsIn' => ['NEWHOPE', 'EMPIRE', 'JEDI'], - 'friends' => + 'friends' => [ - ['name' => 'Luke Skywalker',], - ['name' => 'Han Solo',], - ['name' => 'C-3PO',], - ['name' => 'R2-D2',], + ['name' => 'Luke Skywalker'], + ['name' => 'Han Solo'], + ['name' => 'C-3PO'], + ['name' => 'R2-D2'], ], ], ], - ] + ], ]; $this->assertValidQuery($query, $expected); } - // Describe: Using IDs and query parameters to refetch objects - /** * @see it('Using IDs and query parameters to refetch objects') */ public function testAllowsUsToQueryForLukeSkywalkerDirectlyUsingHisID() : void { - $query = ' + $query = ' query FetchLukeQuery { human(id: "1000") { name @@ -142,9 +144,7 @@ class StarWarsQueryTest extends TestCase } '; $expected = [ - 'human' => [ - 'name' => 'Luke Skywalker' - ] + 'human' => ['name' => 'Luke Skywalker'], ]; $this->assertValidQuery($query, $expected); @@ -155,44 +155,49 @@ class StarWarsQueryTest extends TestCase */ public function testGenericQueryToGetLukeSkywalkerById() : void { - $query = ' + $query = ' query FetchSomeIDQuery($someId: String!) { human(id: $someId) { name } } '; - $params = [ - 'someId' => '1000' - ]; + $params = ['someId' => '1000']; $expected = [ - 'human' => [ - 'name' => 'Luke Skywalker' - ] + 'human' => ['name' => 'Luke Skywalker'], ]; $this->assertValidQueryWithParams($query, $params, $expected); } + /** + * Helper function to test a query with params and the expected response. + */ + private function assertValidQueryWithParams($query, $params, $expected) + { + $this->assertEquals( + ['data' => $expected], + GraphQL::executeQuery(StarWarsSchema::build(), $query, null, null, $params)->toArray() + ); + } + + // Using aliases to change the key in the response + /** * @see it('Allows us to create a generic query, then use it to fetch Han Solo using his ID') */ public function testGenericQueryToGetHanSoloById() : void { - $query = ' + $query = ' query FetchSomeIDQuery($someId: String!) { human(id: $someId) { name } } '; - $params = [ - 'someId' => '1002' - ]; + $params = ['someId' => '1002']; $expected = [ - 'human' => [ - 'name' => 'Han Solo' - ] + 'human' => ['name' => 'Han Solo'], ]; $this->assertValidQueryWithParams($query, $params, $expected); } @@ -202,30 +207,26 @@ class StarWarsQueryTest extends TestCase */ public function testGenericQueryWithInvalidId() : void { - $query = ' + $query = ' query humanQuery($id: String!) { human(id: $id) { name } } '; - $params = [ - 'id' => 'not a valid id' - ]; - $expected = [ - 'human' => null - ]; + $params = ['id' => 'not a valid id']; + $expected = ['human' => null]; $this->assertValidQueryWithParams($query, $params, $expected); } - // Using aliases to change the key in the response + // Uses fragments to express more complex queries /** * @see it('Allows us to query for Luke, changing his key with an alias') */ - function testLukeKeyAlias() + public function testLukeKeyAlias() : void { - $query = ' + $query = ' query FetchLukeAliased { luke: human(id: "1000") { name @@ -233,9 +234,7 @@ class StarWarsQueryTest extends TestCase } '; $expected = [ - 'luke' => [ - 'name' => 'Luke Skywalker' - ], + 'luke' => ['name' => 'Luke Skywalker'], ]; $this->assertValidQuery($query, $expected); } @@ -243,9 +242,9 @@ class StarWarsQueryTest extends TestCase /** * @see it('Allows us to query for both Luke and Leia, using two root fields and an alias') */ - function testTwoRootKeysAsAnAlias() + public function testTwoRootKeysAsAnAlias() : void { - $query = ' + $query = ' query FetchLukeAndLeiaAliased { luke: human(id: "1000") { name @@ -256,24 +255,18 @@ class StarWarsQueryTest extends TestCase } '; $expected = [ - 'luke' => [ - 'name' => 'Luke Skywalker' - ], - 'leia' => [ - 'name' => 'Leia Organa' - ] + 'luke' => ['name' => 'Luke Skywalker'], + 'leia' => ['name' => 'Leia Organa'], ]; $this->assertValidQuery($query, $expected); } - // Uses fragments to express more complex queries - /** * @see it('Allows us to query using duplicated content') */ - function testQueryUsingDuplicatedContent() + public function testQueryUsingDuplicatedContent() : void { - $query = ' + $query = ' query DuplicateFields { luke: human(id: "1000") { name @@ -287,13 +280,13 @@ class StarWarsQueryTest extends TestCase '; $expected = [ 'luke' => [ - 'name' => 'Luke Skywalker', - 'homePlanet' => 'Tatooine' + 'name' => 'Luke Skywalker', + 'homePlanet' => 'Tatooine', ], 'leia' => [ - 'name' => 'Leia Organa', - 'homePlanet' => 'Alderaan' - ] + 'name' => 'Leia Organa', + 'homePlanet' => 'Alderaan', + ], ]; $this->assertValidQuery($query, $expected); } @@ -301,7 +294,7 @@ class StarWarsQueryTest extends TestCase /** * @see it('Allows us to use a fragment to avoid duplicating content') */ - function testUsingFragment() + public function testUsingFragment() : void { $query = ' query UseFragment { @@ -321,13 +314,13 @@ class StarWarsQueryTest extends TestCase $expected = [ 'luke' => [ - 'name' => 'Luke Skywalker', - 'homePlanet' => 'Tatooine' + 'name' => 'Luke Skywalker', + 'homePlanet' => 'Tatooine', ], 'leia' => [ - 'name' => 'Leia Organa', - 'homePlanet' => 'Alderaan' - ] + 'name' => 'Leia Organa', + 'homePlanet' => 'Alderaan', + ], ]; $this->assertValidQuery($query, $expected); } @@ -337,7 +330,7 @@ class StarWarsQueryTest extends TestCase */ public function testVerifyThatR2D2IsADroid() : void { - $query = ' + $query = ' query CheckTypeOfR2 { hero { __typename @@ -348,7 +341,7 @@ class StarWarsQueryTest extends TestCase $expected = [ 'hero' => [ '__typename' => 'Droid', - 'name' => 'R2-D2' + 'name' => 'R2-D2', ], ]; $this->assertValidQuery($query, $expected); @@ -371,32 +364,10 @@ class StarWarsQueryTest extends TestCase $expected = [ 'hero' => [ '__typename' => 'Human', - 'name' => 'Luke Skywalker' + 'name' => 'Luke Skywalker', ], ]; $this->assertValidQuery($query, $expected); } - - /** - * Helper function to test a query and the expected response. - */ - private function assertValidQuery($query, $expected) - { - $this->assertEquals( - ['data' => $expected], - GraphQL::executeQuery(StarWarsSchema::build(), $query)->toArray() - ); - } - - /** - * Helper function to test a query with params and the expected response. - */ - private function assertValidQueryWithParams($query, $params, $expected) - { - $this->assertEquals( - ['data' => $expected], - GraphQL::executeQuery(StarWarsSchema::build(), $query, null, null, $params)->toArray() - ); - } } diff --git a/tests/StarWarsSchema.php b/tests/StarWarsSchema.php index 91c3378..f5dbfe1 100644 --- a/tests/StarWarsSchema.php +++ b/tests/StarWarsSchema.php @@ -1,4 +1,7 @@ 'Episode', + 'name' => 'Episode', 'description' => 'One of the films in the Star Wars Trilogy', - 'values' => [ + 'values' => [ 'NEWHOPE' => [ - 'value' => 4, - 'description' => 'Released in 1977.' + 'value' => 4, + 'description' => 'Released in 1977.', ], - 'EMPIRE' => [ - 'value' => 5, - 'description' => 'Released in 1980.' + 'EMPIRE' => [ + 'value' => 5, + 'description' => 'Released in 1980.', ], - 'JEDI' => [ - 'value' => 6, - 'description' => 'Released in 1983.' + 'JEDI' => [ + 'value' => 6, + 'description' => 'Released in 1983.', ], - ] + ], ]); $humanType = null; @@ -101,28 +106,28 @@ class StarWarsSchema * } */ $characterInterface = new InterfaceType([ - 'name' => 'Character', + 'name' => 'Character', 'description' => 'A character in the Star Wars Trilogy', - 'fields' => function() use (&$characterInterface, $episodeEnum) { + 'fields' => function () use (&$characterInterface, $episodeEnum) { return [ - 'id' => [ - 'type' => Type::nonNull(Type::string()), + 'id' => [ + 'type' => Type::nonNull(Type::string()), 'description' => 'The id of the character.', ], - 'name' => [ - 'type' => Type::string(), - 'description' => 'The name of the character.' + 'name' => [ + 'type' => Type::string(), + 'description' => 'The name of the character.', ], - 'friends' => [ - 'type' => Type::listOf($characterInterface), + 'friends' => [ + 'type' => Type::listOf($characterInterface), 'description' => 'The friends of the character, or an empty list if they have none.', ], - 'appearsIn' => [ - 'type' => Type::listOf($episodeEnum), - 'description' => 'Which movies they appear in.' + 'appearsIn' => [ + 'type' => Type::listOf($episodeEnum), + 'description' => 'Which movies they appear in.', ], 'secretBackstory' => [ - 'type' => Type::string(), + 'type' => Type::string(), 'description' => 'All secrets about their past.', ], ]; @@ -145,26 +150,26 @@ class StarWarsSchema * } */ $humanType = new ObjectType([ - 'name' => 'Human', + 'name' => 'Human', 'description' => 'A humanoid creature in the Star Wars universe.', - 'fields' => [ - 'id' => [ - 'type' => new NonNull(Type::string()), + 'fields' => [ + 'id' => [ + 'type' => new NonNull(Type::string()), 'description' => 'The id of the human.', ], - 'name' => [ - 'type' => Type::string(), + 'name' => [ + 'type' => Type::string(), 'description' => 'The name of the human.', ], - 'friends' => [ - 'type' => Type::listOf($characterInterface), + 'friends' => [ + 'type' => Type::listOf($characterInterface), 'description' => 'The friends of the human, or an empty list if they have none.', - 'resolve' => function ($human, $args, $context, ResolveInfo $info) { - $fieldSelection = $info->getFieldSelection(); + 'resolve' => function ($human, $args, $context, ResolveInfo $info) { + $fieldSelection = $info->getFieldSelection(); $fieldSelection['id'] = true; $friends = array_map( - function($friend) use ($fieldSelection) { + function ($friend) use ($fieldSelection) { return array_intersect_key($friend, $fieldSelection); }, StarWarsData::getFriends($human) @@ -173,24 +178,24 @@ class StarWarsSchema return $friends; }, ], - 'appearsIn' => [ - 'type' => Type::listOf($episodeEnum), - 'description' => 'Which movies they appear in.' + 'appearsIn' => [ + 'type' => Type::listOf($episodeEnum), + 'description' => 'Which movies they appear in.', ], - 'homePlanet' => [ - 'type' => Type::string(), - 'description' => 'The home planet of the human, or null if unknown.' + 'homePlanet' => [ + 'type' => Type::string(), + 'description' => 'The home planet of the human, or null if unknown.', ], 'secretBackstory' => [ - 'type' => Type::string(), + 'type' => Type::string(), 'description' => 'Where are they from and how they came to be who they are.', - 'resolve' => function() { + 'resolve' => function () { // This is to demonstrate error reporting throw new \Exception('secretBackstory is secret.'); }, ], ], - 'interfaces' => [$characterInterface] + 'interfaces' => [$characterInterface], ]); /** @@ -207,42 +212,42 @@ class StarWarsSchema * } */ $droidType = new ObjectType([ - 'name' => 'Droid', + 'name' => 'Droid', 'description' => 'A mechanical creature in the Star Wars universe.', - 'fields' => [ - 'id' => [ - 'type' => Type::nonNull(Type::string()), + 'fields' => [ + 'id' => [ + 'type' => Type::nonNull(Type::string()), 'description' => 'The id of the droid.', ], - 'name' => [ - 'type' => Type::string(), - 'description' => 'The name of the droid.' + 'name' => [ + 'type' => Type::string(), + 'description' => 'The name of the droid.', ], - 'friends' => [ - 'type' => Type::listOf($characterInterface), + 'friends' => [ + 'type' => Type::listOf($characterInterface), 'description' => 'The friends of the droid, or an empty list if they have none.', - 'resolve' => function ($droid) { + 'resolve' => function ($droid) { return StarWarsData::getFriends($droid); }, ], - 'appearsIn' => [ - 'type' => Type::listOf($episodeEnum), - 'description' => 'Which movies they appear in.' + 'appearsIn' => [ + 'type' => Type::listOf($episodeEnum), + 'description' => 'Which movies they appear in.', ], 'secretBackstory' => [ - 'type' => Type::string(), + 'type' => Type::string(), 'description' => 'Construction date and the name of the designer.', - 'resolve' => function() { + 'resolve' => function () { // This is to demonstrate error reporting throw new \Exception('secretBackstory is secret.'); }, ], 'primaryFunction' => [ - 'type' => Type::string(), - 'description' => 'The primary function of the droid.' - ] + 'type' => Type::string(), + 'description' => 'The primary function of the droid.', + ], ], - 'interfaces' => [$characterInterface] + 'interfaces' => [$characterInterface], ]); /** @@ -260,49 +265,51 @@ class StarWarsSchema * */ $queryType = new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ - 'hero' => [ - 'type' => $characterInterface, - 'args' => [ + 'hero' => [ + 'type' => $characterInterface, + 'args' => [ 'episode' => [ 'description' => 'If omitted, returns the hero of the whole saga. If provided, returns the hero of that particular episode.', - 'type' => $episodeEnum - ] + 'type' => $episodeEnum, + ], ], 'resolve' => function ($root, $args) { - return StarWarsData::getHero(isset($args['episode']) ? $args['episode'] : null); + return StarWarsData::getHero($args['episode'] ?? null); }, ], 'human' => [ - 'type' => $humanType, - 'args' => [ + 'type' => $humanType, + 'args' => [ 'id' => [ - 'name' => 'id', + 'name' => 'id', 'description' => 'id of the human', - 'type' => Type::nonNull(Type::string()) - ] + 'type' => Type::nonNull(Type::string()), + ], ], 'resolve' => function ($root, $args) { $humans = StarWarsData::humans(); - return isset($humans[$args['id']]) ? $humans[$args['id']] : null; - } + + return $humans[$args['id']] ?? null; + }, ], 'droid' => [ - 'type' => $droidType, - 'args' => [ + 'type' => $droidType, + 'args' => [ 'id' => [ - 'name' => 'id', + 'name' => 'id', 'description' => 'id of the droid', - 'type' => Type::nonNull(Type::string()) - ] + 'type' => Type::nonNull(Type::string()), + ], ], 'resolve' => function ($root, $args) { $droids = StarWarsData::droids(); - return isset($droids[$args['id']]) ? $droids[$args['id']] : null; - } - ] - ] + + return $droids[$args['id']] ?? null; + }, + ], + ], ]); return new Schema(['query' => $queryType]); diff --git a/tests/StarWarsValidationTest.php b/tests/StarWarsValidationTest.php index a4b0018..1bc565d 100644 --- a/tests/StarWarsValidationTest.php +++ b/tests/StarWarsValidationTest.php @@ -1,4 +1,7 @@ assertEquals(true, empty($errors)); } + /** + * Helper function to test a query and the expected response. + */ + private function validationErrors($query) + { + $ast = Parser::parse($query); + + return DocumentValidator::validate(StarWarsSchema::build(), $ast); + } + /** * @see it('Notes that non-existent fields are invalid') */ public function testThatNonExistentFieldsAreInvalid() : void { - $query = ' + $query = ' query HeroSpaceshipQuery { hero { favoriteSpaceship @@ -73,7 +86,7 @@ class StarWarsValidationTest extends TestCase */ public function testDisallowsFieldsOnScalars() : void { - $query = ' + $query = ' query HeroFieldsOnScalarQuery { hero { name { @@ -91,7 +104,7 @@ class StarWarsValidationTest extends TestCase */ public function testDisallowsObjectFieldsOnInterfaces() : void { - $query = ' + $query = ' query DroidFieldOnCharacter { hero { name @@ -108,7 +121,7 @@ class StarWarsValidationTest extends TestCase */ public function testAllowsObjectFieldsInFragments() : void { - $query = ' + $query = ' query DroidFieldInFragment { hero { name @@ -129,7 +142,7 @@ class StarWarsValidationTest extends TestCase */ public function testAllowsObjectFieldsInInlineFragments() : void { - $query = ' + $query = ' query DroidFieldInFragment { hero { name @@ -142,13 +155,4 @@ class StarWarsValidationTest extends TestCase $errors = $this->validationErrors($query); $this->assertEquals(true, empty($errors)); } - - /** - * Helper function to test a query and the expected response. - */ - private function validationErrors($query) - { - $ast = Parser::parse($query); - return DocumentValidator::validate(StarWarsSchema::build(), $ast); - } } diff --git a/tests/Type/DefinitionTest.php b/tests/Type/DefinitionTest.php index d5b6b50..ec58783 100644 --- a/tests/Type/DefinitionTest.php +++ b/tests/Type/DefinitionTest.php @@ -1,11 +1,13 @@ objectType = new ObjectType(['name' => 'Object', 'fields' => ['tmp' => Type::string()]]); - $this->interfaceType = new InterfaceType(['name' => 'Interface']); - $this->unionType = new UnionType(['name' => 'Union', 'types' => [$this->objectType]]); - $this->enumType = new EnumType(['name' => 'Enum']); + $this->objectType = new ObjectType(['name' => 'Object', 'fields' => ['tmp' => Type::string()]]); + $this->interfaceType = new InterfaceType(['name' => 'Interface']); + $this->unionType = new UnionType(['name' => 'Union', 'types' => [$this->objectType]]); + $this->enumType = new EnumType(['name' => 'Enum']); $this->inputObjectType = new InputObjectType(['name' => 'InputObject']); $this->objectWithIsTypeOf = new ObjectType([ - 'name' => 'ObjectWithIsTypeOf', + 'name' => 'ObjectWithIsTypeOf', 'fields' => ['f' => ['type' => Type::string()]], ]); $this->scalarType = new CustomScalarType([ - 'name' => 'Scalar', - 'serialize' => function () {}, - 'parseValue' => function () {}, - 'parseLiteral' => function () {}, + 'name' => 'Scalar', + 'serialize' => function () { + }, + 'parseValue' => function () { + }, + 'parseLiteral' => function () { + }, ]); $this->blogImage = new ObjectType([ - 'name' => 'Image', + 'name' => 'Image', 'fields' => [ - 'url' => ['type' => Type::string()], - 'width' => ['type' => Type::int()], - 'height' => ['type' => Type::int()] - ] + 'url' => ['type' => Type::string()], + 'width' => ['type' => Type::int()], + 'height' => ['type' => Type::int()], + ], ]); $this->blogAuthor = new ObjectType([ - 'name' => 'Author', - 'fields' => function() { + 'name' => 'Author', + 'fields' => function () { return [ - 'id' => ['type' => Type::string()], - 'name' => ['type' => Type::string()], - 'pic' => [ 'type' => $this->blogImage, 'args' => [ - 'width' => ['type' => Type::int()], - 'height' => ['type' => Type::int()] - ]], + 'id' => ['type' => Type::string()], + 'name' => ['type' => Type::string()], + 'pic' => [ + 'type' => $this->blogImage, + 'args' => [ + 'width' => ['type' => Type::int()], + 'height' => ['type' => Type::int()], + ], + ], 'recentArticle' => $this->blogArticle, ]; }, ]); $this->blogArticle = new ObjectType([ - 'name' => 'Article', + 'name' => 'Article', 'fields' => [ - 'id' => ['type' => Type::string()], + 'id' => ['type' => Type::string()], 'isPublished' => ['type' => Type::boolean()], - 'author' => ['type' => $this->blogAuthor], - 'title' => ['type' => Type::string()], - 'body' => ['type' => Type::string()] - ] + 'author' => ['type' => $this->blogAuthor], + 'title' => ['type' => Type::string()], + 'body' => ['type' => Type::string()], + ], ]); $this->blogQuery = new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ - 'article' => ['type' => $this->blogArticle, 'args' => [ - 'id' => ['type' => Type::string()] - ]], - 'feed' => ['type' => new ListOfType($this->blogArticle)] - ] + 'article' => [ + 'type' => $this->blogArticle, + 'args' => [ + 'id' => ['type' => Type::string()], + ], + ], + 'feed' => ['type' => new ListOfType($this->blogArticle)], + ], ]); $this->blogMutation = new ObjectType([ - 'name' => 'Mutation', + 'name' => 'Mutation', 'fields' => [ - 'writeArticle' => ['type' => $this->blogArticle] - ] + 'writeArticle' => ['type' => $this->blogArticle], + ], ]); $this->blogSubscription = new ObjectType([ - 'name' => 'Subscription', + 'name' => 'Subscription', 'fields' => [ 'articleSubscribe' => [ - 'args' => [ 'id' => [ 'type' => Type::string() ]], - 'type' => $this->blogArticle - ] - ] + 'args' => ['id' => ['type' => Type::string()]], + 'type' => $this->blogArticle, + ], + ], ]); } @@ -175,7 +165,7 @@ class DefinitionTest extends TestCase public function testDefinesAQueryOnlySchema() : void { $blogSchema = new Schema([ - 'query' => $this->blogQuery + 'query' => $this->blogQuery, ]); $this->assertSame($blogSchema->getQueryType(), $this->blogQuery); @@ -187,7 +177,7 @@ class DefinitionTest extends TestCase /** @var ObjectType $articleFieldType */ $articleFieldType = $articleField->getType(); - $titleField = $articleFieldType->getField('title'); + $titleField = $articleFieldType->getField('title'); $this->assertInstanceOf('GraphQL\Type\Definition\FieldDefinition', $titleField); $this->assertSame('title', $titleField->name); @@ -219,8 +209,8 @@ class DefinitionTest extends TestCase public function testDefinesAMutationSchema() : void { $schema = new Schema([ - 'query' => $this->blogQuery, - 'mutation' => $this->blogMutation + 'query' => $this->blogQuery, + 'mutation' => $this->blogMutation, ]); $this->assertSame($this->blogMutation, $schema->getMutationType()); @@ -238,8 +228,8 @@ class DefinitionTest extends TestCase public function testDefinesSubscriptionSchema() : void { $schema = new Schema([ - 'query' => $this->blogQuery, - 'subscription' => $this->blogSubscription + 'query' => $this->blogQuery, + 'subscription' => $this->blogSubscription, ]); $this->assertEquals($this->blogSubscription, $schema->getSubscriptionType()); @@ -256,21 +246,24 @@ class DefinitionTest extends TestCase public function testDefinesEnumTypeWithDeprecatedValue() : void { $enumTypeWithDeprecatedValue = new EnumType([ - 'name' => 'EnumWithDeprecatedValue', + 'name' => 'EnumWithDeprecatedValue', 'values' => [ - 'foo' => ['deprecationReason' => 'Just because'] - ] + 'foo' => ['deprecationReason' => 'Just because'], + ], ]); $value = $enumTypeWithDeprecatedValue->getValues()[0]; - $this->assertArraySubset([ - 'name' => 'foo', - 'description' => null, - 'deprecationReason' => 'Just because', - 'value' => 'foo', - 'astNode' => null - ], (array) $value); + $this->assertArraySubset( + [ + 'name' => 'foo', + 'description' => null, + 'deprecationReason' => 'Just because', + 'value' => 'foo', + 'astNode' => null, + ], + (array) $value + ); $this->assertEquals(true, $value->isDeprecated()); } @@ -281,35 +274,35 @@ class DefinitionTest extends TestCase public function testDefinesAnEnumTypeWithAValueOfNullAndUndefined() : void { $EnumTypeWithNullishValue = new EnumType([ - 'name' => 'EnumWithNullishValue', + 'name' => 'EnumWithNullishValue', 'values' => [ - 'NULL' => ['value' => null], + 'NULL' => ['value' => null], 'UNDEFINED' => ['value' => null], - ] + ], ]); $expected = [ [ - 'name' => 'NULL', - 'description' => null, + 'name' => 'NULL', + 'description' => null, 'deprecationReason' => null, - 'value' => null, - 'astNode' => null, + 'value' => null, + 'astNode' => null, ], [ - 'name' => 'UNDEFINED', - 'description' => null, + 'name' => 'UNDEFINED', + 'description' => null, 'deprecationReason' => null, - 'value' => null, - 'astNode' => null, + 'value' => null, + 'astNode' => null, ], ]; $actual = $EnumTypeWithNullishValue->getValues(); $this->assertEquals(count($expected), count($actual)); - $this->assertArraySubset($expected[0], (array)$actual[0]); - $this->assertArraySubset($expected[1], (array)$actual[1]); + $this->assertArraySubset($expected[0], (array) $actual[0]); + $this->assertArraySubset($expected[1], (array) $actual[1]); } /** @@ -318,13 +311,13 @@ class DefinitionTest extends TestCase public function testDefinesAnObjectTypeWithDeprecatedField() : void { $TypeWithDeprecatedField = new ObjectType([ - 'name' => 'foo', - 'fields' => [ - 'bar' => [ - 'type' => Type::string(), - 'deprecationReason' => 'A terrible reason' - ] - ] + 'name' => 'foo', + 'fields' => [ + 'bar' => [ + 'type' => Type::string(), + 'deprecationReason' => 'A terrible reason', + ], + ], ]); $field = $TypeWithDeprecatedField->getField('bar'); @@ -342,26 +335,26 @@ class DefinitionTest extends TestCase public function testIncludesNestedInputObjectInTheMap() : void { $nestedInputObject = new InputObjectType([ - 'name' => 'NestedInputObject', - 'fields' => ['value' => ['type' => Type::string()]] + 'name' => 'NestedInputObject', + 'fields' => ['value' => ['type' => Type::string()]], ]); - $someInputObject = new InputObjectType([ - 'name' => 'SomeInputObject', - 'fields' => ['nested' => ['type' => $nestedInputObject]] + $someInputObject = new InputObjectType([ + 'name' => 'SomeInputObject', + 'fields' => ['nested' => ['type' => $nestedInputObject]], ]); - $someMutation = new ObjectType([ - 'name' => 'SomeMutation', + $someMutation = new ObjectType([ + 'name' => 'SomeMutation', 'fields' => [ 'mutateSomething' => [ 'type' => $this->blogArticle, - 'args' => ['input' => ['type' => $someInputObject]] - ] - ] + 'args' => ['input' => ['type' => $someInputObject]], + ], + ], ]); $schema = new Schema([ - 'query' => $this->blogQuery, - 'mutation' => $someMutation + 'query' => $this->blogQuery, + 'mutation' => $someMutation, ]); $this->assertSame($nestedInputObject, $schema->getType('NestedInputObject')); } @@ -372,28 +365,28 @@ class DefinitionTest extends TestCase public function testIncludesInterfaceSubtypesInTheTypeMap() : void { $someInterface = new InterfaceType([ - 'name' => 'SomeInterface', + 'name' => 'SomeInterface', 'fields' => [ - 'f' => ['type' => Type::int()] - ] + 'f' => ['type' => Type::int()], + ], ]); $someSubtype = new ObjectType([ - 'name' => 'SomeSubtype', - 'fields' => [ - 'f' => ['type' => Type::int()] + 'name' => 'SomeSubtype', + 'fields' => [ + 'f' => ['type' => Type::int()], ], 'interfaces' => [$someInterface], ]); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ - 'iface' => ['type' => $someInterface] - ] + 'iface' => ['type' => $someInterface], + ], ]), - 'types' => [$someSubtype] + 'types' => [$someSubtype], ]); $this->assertSame($someSubtype, $schema->getType('SomeSubtype')); } @@ -406,28 +399,30 @@ class DefinitionTest extends TestCase $someInterface = null; $someSubtype = new ObjectType([ - 'name' => 'SomeSubtype', - 'fields' => [ - 'f' => ['type' => Type::int()] + 'name' => 'SomeSubtype', + 'fields' => [ + 'f' => ['type' => Type::int()], ], - 'interfaces' => function() use (&$someInterface) { return [$someInterface]; }, + 'interfaces' => function () use (&$someInterface) { + return [$someInterface]; + }, ]); $someInterface = new InterfaceType([ - 'name' => 'SomeInterface', + 'name' => 'SomeInterface', 'fields' => [ - 'f' => ['type' => Type::int()] - ] + 'f' => ['type' => Type::int()], + ], ]); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ - 'iface' => ['type' => $someInterface] - ] + 'iface' => ['type' => $someInterface], + ], ]), - 'types' => [$someSubtype] + 'types' => [$someSubtype], ]); $this->assertSame($someSubtype, $schema->getType('SomeSubtype')); @@ -483,11 +478,15 @@ class DefinitionTest extends TestCase [$this->interfaceType, false], [$this->unionType, false], [$this->enumType, true], - [$this->inputObjectType, true] + [$this->inputObjectType, true], ]; foreach ($expected as $index => $entry) { - $this->assertSame($entry[1], Type::isInputType($entry[0]), "Type {$entry[0]} was detected incorrectly"); + $this->assertSame( + $entry[1], + Type::isInputType($entry[0]), + sprintf('Type %s was detected incorrectly', $entry[0]) + ); } } @@ -502,11 +501,15 @@ class DefinitionTest extends TestCase [$this->interfaceType, true], [$this->unionType, true], [$this->enumType, true], - [$this->inputObjectType, false] + [$this->inputObjectType, false], ]; foreach ($expected as $index => $entry) { - $this->assertSame($entry[1], Type::isOutputType($entry[0]), "Type {$entry[0]} was detected incorrectly"); + $this->assertSame( + $entry[1], + Type::isOutputType($entry[0]), + sprintf('Type %s was detected incorrectly', $entry[0]) + ); } } @@ -528,8 +531,10 @@ class DefinitionTest extends TestCase public function testAllowsThunkForUnionTypes() : void { $union = new UnionType([ - 'name' => 'ThunkUnion', - 'types' => function() {return [$this->objectType]; } + 'name' => 'ThunkUnion', + 'types' => function () { + return [$this->objectType]; + }, ]); $types = $union->getTypes(); @@ -541,52 +546,52 @@ class DefinitionTest extends TestCase { // See https://github.com/webonyx/graphql-php/issues/16 $node = new InterfaceType([ - 'name' => 'Node', + 'name' => 'Node', 'fields' => [ - 'id' => ['type' => Type::nonNull(Type::id())] - ] + 'id' => ['type' => Type::nonNull(Type::id())], + ], ]); - $blog = null; + $blog = null; $called = false; $user = new ObjectType([ - 'name' => 'User', - 'fields' => function() use (&$blog, &$called) { + 'name' => 'User', + 'fields' => function () use (&$blog, &$called) { $this->assertNotNull($blog, 'Blog type is expected to be defined at this point, but it is null'); $called = true; return [ - 'id' => ['type' => Type::nonNull(Type::id())], - 'blogs' => ['type' => Type::nonNull(Type::listOf(Type::nonNull($blog)))] + 'id' => ['type' => Type::nonNull(Type::id())], + 'blogs' => ['type' => Type::nonNull(Type::listOf(Type::nonNull($blog)))], ]; }, - 'interfaces' => function() use ($node) { + 'interfaces' => function () use ($node) { return [$node]; - } + }, ]); $blog = new ObjectType([ - 'name' => 'Blog', - 'fields' => function() use ($user) { + 'name' => 'Blog', + 'fields' => function () use ($user) { return [ - 'id' => ['type' => Type::nonNull(Type::id())], - 'owner' => ['type' => Type::nonNull($user)] + 'id' => ['type' => Type::nonNull(Type::id())], + 'owner' => ['type' => Type::nonNull($user)], ]; }, - 'interfaces' => function() use ($node) { + 'interfaces' => function () use ($node) { return [$node]; - } + }, ]); $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ - 'node' => ['type' => $node] - ] + 'node' => ['type' => $node], + ], ]), - 'types' => [$user, $blog] + 'types' => [$user, $blog], ]); $this->assertTrue($called); @@ -604,30 +609,32 @@ class DefinitionTest extends TestCase public function testInputObjectTypeAllowsRecursiveDefinitions() : void { - $called = false; + $called = false; $inputObject = new InputObjectType([ - 'name' => 'InputObject', - 'fields' => function() use (&$inputObject, &$called) { + 'name' => 'InputObject', + 'fields' => function () use (&$inputObject, &$called) { $called = true; + return [ - 'value' => ['type' => Type::string()], - 'nested' => ['type' => $inputObject ] + 'value' => ['type' => Type::string()], + 'nested' => ['type' => $inputObject], ]; - } + }, ]); + $someMutation = new ObjectType([ - 'name' => 'SomeMutation', + 'name' => 'SomeMutation', 'fields' => [ 'mutateSomething' => [ 'type' => $this->blogArticle, - 'args' => ['input' => ['type' => $inputObject]] - ] - ] + 'args' => ['input' => ['type' => $inputObject]], + ], + ], ]); $schema = new Schema([ - 'query' => $this->blogQuery, - 'mutation' => $someMutation + 'query' => $this->blogQuery, + 'mutation' => $someMutation, ]); $this->assertSame($inputObject, $schema->getType('InputObject')); @@ -639,28 +646,27 @@ class DefinitionTest extends TestCase public function testInterfaceTypeAllowsRecursiveDefinitions() : void { - $called = false; + $called = false; $interface = new InterfaceType([ - 'name' => 'SomeInterface', - 'fields' => function() use (&$interface, &$called) { + 'name' => 'SomeInterface', + 'fields' => function () use (&$interface, &$called) { $called = true; + return [ - 'value' => ['type' => Type::string()], - 'nested' => ['type' => $interface ] + 'value' => ['type' => Type::string()], + 'nested' => ['type' => $interface], ]; - } + }, ]); $query = new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ - 'test' => ['type' => $interface] - ] + 'test' => ['type' => $interface], + ], ]); - $schema = new Schema([ - 'query' => $query - ]); + $schema = new Schema(['query' => $query]); $this->assertSame($interface, $schema->getType('SomeInterface')); $this->assertTrue($called); @@ -672,33 +678,29 @@ class DefinitionTest extends TestCase public function testAllowsShorthandFieldDefinition() : void { $interface = new InterfaceType([ - 'name' => 'SomeInterface', - 'fields' => function() use (&$interface) { + 'name' => 'SomeInterface', + 'fields' => function () use (&$interface) { return [ - 'value' => Type::string(), - 'nested' => $interface, + 'value' => Type::string(), + 'nested' => $interface, 'withArg' => [ 'type' => Type::string(), 'args' => [ - 'arg1' => Type::int() - ] - ] + 'arg1' => Type::int(), + ], + ], ]; - } + }, ]); $query = new ObjectType([ - 'name' => 'Query', - 'fields' => [ - 'test' => $interface - ] + 'name' => 'Query', + 'fields' => ['test' => $interface], ]); - $schema = new Schema([ - 'query' => $query - ]); + $schema = new Schema(['query' => $query]); - $valueField = $schema->getType('SomeInterface')->getField('value'); + $valueField = $schema->getType('SomeInterface')->getField('value'); $nestedField = $schema->getType('SomeInterface')->getField('nested'); $this->assertEquals(Type::string(), $valueField->getType()); @@ -727,15 +729,18 @@ class DefinitionTest extends TestCase public function testAllowsOverridingInternalTypes() : void { $idType = new CustomScalarType([ - 'name' => 'ID', - 'serialize' => function() {}, - 'parseValue' => function() {}, - 'parseLiteral' => function() {} + 'name' => 'ID', + 'serialize' => function () { + }, + 'parseValue' => function () { + }, + 'parseLiteral' => function () { + }, ]); $schema = new Schema([ 'query' => new ObjectType(['name' => 'Query', 'fields' => []]), - 'types' => [$idType] + 'types' => [$idType], ]); $this->assertSame($idType, $schema->getType('ID')); @@ -749,7 +754,7 @@ class DefinitionTest extends TestCase public function testAcceptsAnObjectTypeWithAFieldFunction() : void { $objType = new ObjectType([ - 'name' => 'SomeObject', + 'name' => 'SomeObject', 'fields' => function () { return [ 'f' => ['type' => Type::string()], @@ -766,10 +771,8 @@ class DefinitionTest extends TestCase public function testRejectsAnObjectTypeFieldWithUndefinedConfig() : void { $objType = new ObjectType([ - 'name' => 'SomeObject', - 'fields' => [ - 'f' => null, - ], + 'name' => 'SomeObject', + 'fields' => ['f' => null], ]); $this->expectException(InvariantViolation::class); $this->expectExceptionMessage( @@ -784,7 +787,7 @@ class DefinitionTest extends TestCase public function testRejectsAnObjectTypeWithIncorrectlyTypedFields() : void { $objType = new ObjectType([ - 'name' => 'SomeObject', + 'name' => 'SomeObject', 'fields' => [['field' => Type::string()]], ]); $this->expectException(InvariantViolation::class); @@ -801,7 +804,7 @@ class DefinitionTest extends TestCase public function testRejectsAnObjectTypeWithAFieldFunctionThatReturnsIncorrectType() : void { $objType = new ObjectType([ - 'name' => 'SomeObject', + 'name' => 'SomeObject', 'fields' => function () { return [['field' => Type::string()]]; }, @@ -823,7 +826,7 @@ class DefinitionTest extends TestCase { $this->expectNotToPerformAssertions(); $objType = new ObjectType([ - 'name' => 'SomeObject', + 'name' => 'SomeObject', 'fields' => [ 'goodField' => [ 'type' => Type::string(), @@ -845,10 +848,10 @@ class DefinitionTest extends TestCase public function testDoesNotAllowIsDeprecatedWithoutDeprecationReasonOnField() : void { $OldObject = new ObjectType([ - 'name' => 'OldObject', + 'name' => 'OldObject', 'fields' => [ 'field' => [ - 'type' => Type::string(), + 'type' => Type::string(), 'isDeprecated' => true, ], ], @@ -869,9 +872,9 @@ class DefinitionTest extends TestCase public function testAcceptsAnObjectTypeWithArrayInterfaces() : void { $objType = new ObjectType([ - 'name' => 'SomeObject', + 'name' => 'SomeObject', 'interfaces' => [$this->interfaceType], - 'fields' => ['f' => ['type' => Type::string()]], + 'fields' => ['f' => ['type' => Type::string()]], ]); $this->assertSame($this->interfaceType, $objType->getInterfaces()[0]); } @@ -882,11 +885,11 @@ class DefinitionTest extends TestCase public function testAcceptsAnObjectTypeWithInterfacesAsAFunctionReturningAnArray() : void { $objType = new ObjectType([ - 'name' => 'SomeObject', + 'name' => 'SomeObject', 'interfaces' => function () { return [$this->interfaceType]; }, - 'fields' => ['f' => ['type' => Type::string()]], + 'fields' => ['f' => ['type' => Type::string()]], ]); $this->assertSame($this->interfaceType, $objType->getInterfaces()[0]); } @@ -897,9 +900,9 @@ class DefinitionTest extends TestCase public function testRejectsAnObjectTypeWithIncorrectlyTypedInterfaces() : void { $objType = new ObjectType([ - 'name' => 'SomeObject', + 'name' => 'SomeObject', 'interfaces' => new \stdClass(), - 'fields' => ['f' => ['type' => Type::string()]], + 'fields' => ['f' => ['type' => Type::string()]], ]); $this->expectException(InvariantViolation::class); $this->expectExceptionMessage( @@ -914,11 +917,11 @@ class DefinitionTest extends TestCase public function testRejectsAnObjectTypeWithInterfacesAsAFunctionReturningAnIncorrectType() : void { $objType = new ObjectType([ - 'name' => 'SomeObject', + 'name' => 'SomeObject', 'interfaces' => function () { return new \stdClass(); }, - 'fields' => ['f' => ['type' => Type::string()]], + 'fields' => ['f' => ['type' => Type::string()]], ]); $this->expectException(InvariantViolation::class); $this->expectExceptionMessage( @@ -929,30 +932,6 @@ class DefinitionTest extends TestCase // Type System: Object fields must have valid resolve values - private function schemaWithObjectWithFieldResolver($resolveValue) - { - $BadResolverType = new ObjectType([ - 'name' => 'BadResolver', - 'fields' => [ - 'badField' => [ - 'type' => Type::string(), - 'resolve' => $resolveValue, - ], - ], - ]); - - $schema = new Schema([ - 'query' => new ObjectType([ - 'name' => 'Query', - 'fields' => [ - 'f' => ['type' => $BadResolverType], - ], - ]), - ]); - $schema->assertValid(); - return $schema; - } - /** * @see it('accepts a lambda as an Object field resolver') */ @@ -960,7 +939,33 @@ class DefinitionTest extends TestCase { $this->expectNotToPerformAssertions(); // should not throw: - $this->schemaWithObjectWithFieldResolver(function () {}); + $this->schemaWithObjectWithFieldResolver(function () { + }); + } + + private function schemaWithObjectWithFieldResolver($resolveValue) + { + $BadResolverType = new ObjectType([ + 'name' => 'BadResolver', + 'fields' => [ + 'badField' => [ + 'type' => Type::string(), + 'resolve' => $resolveValue, + ], + ], + ]); + + $schema = new Schema([ + 'query' => new ObjectType([ + 'name' => 'Query', + 'fields' => [ + 'f' => ['type' => $BadResolverType], + ], + ]), + ]); + $schema->assertValid(); + + return $schema; } /** @@ -987,21 +992,8 @@ class DefinitionTest extends TestCase $this->schemaWithObjectWithFieldResolver(0); } - // Type System: Interface types must be resolvable - private function schemaWithFieldType($type) - { - $schema = new Schema([ - 'query' => new ObjectType([ - 'name' => 'Query', - 'fields' => ['field' => ['type' => $type]], - ]), - 'types' => [$type], - ]); - $schema->assertValid(); - return $schema; - } /** * @see it('accepts an Interface type defining resolveType') */ @@ -1009,20 +1001,34 @@ class DefinitionTest extends TestCase { $this->expectNotToPerformAssertions(); $AnotherInterfaceType = new InterfaceType([ - 'name' => 'AnotherInterface', + 'name' => 'AnotherInterface', 'fields' => ['f' => ['type' => Type::string()]], ]); // Should not throw: $this->schemaWithFieldType( new ObjectType([ - 'name' => 'SomeObject', + 'name' => 'SomeObject', 'interfaces' => [$AnotherInterfaceType], - 'fields' => ['f' => ['type' => Type::string()]], + 'fields' => ['f' => ['type' => Type::string()]], ]) ); } + private function schemaWithFieldType($type) + { + $schema = new Schema([ + 'query' => new ObjectType([ + 'name' => 'Query', + 'fields' => ['field' => ['type' => $type]], + ]), + 'types' => [$type], + ]); + $schema->assertValid(); + + return $schema; + } + /** * @see it('accepts an Interface with implementing type defining isTypeOf') */ @@ -1030,16 +1036,16 @@ class DefinitionTest extends TestCase { $this->expectNotToPerformAssertions(); $InterfaceTypeWithoutResolveType = new InterfaceType([ - 'name' => 'InterfaceTypeWithoutResolveType', + 'name' => 'InterfaceTypeWithoutResolveType', 'fields' => ['f' => ['type' => Type::string()]], ]); // Should not throw: $this->schemaWithFieldType( new ObjectType([ - 'name' => 'SomeObject', + 'name' => 'SomeObject', 'interfaces' => [$InterfaceTypeWithoutResolveType], - 'fields' => ['f' => ['type' => Type::string()]], + 'fields' => ['f' => ['type' => Type::string()]], ]) ); } @@ -1051,16 +1057,16 @@ class DefinitionTest extends TestCase { $this->expectNotToPerformAssertions(); $AnotherInterfaceType = new InterfaceType([ - 'name' => 'AnotherInterface', + 'name' => 'AnotherInterface', 'fields' => ['f' => ['type' => Type::string()]], ]); // Should not throw: $this->schemaWithFieldType( new ObjectType([ - 'name' => 'SomeObject', + 'name' => 'SomeObject', 'interfaces' => [$AnotherInterfaceType], - 'fields' => ['f' => ['type' => Type::string()]], + 'fields' => ['f' => ['type' => Type::string()]], ]) ); } @@ -1076,23 +1082,15 @@ class DefinitionTest extends TestCase ); $type = new InterfaceType([ - 'name' => 'AnotherInterface', + 'name' => 'AnotherInterface', 'resolveType' => new \stdClass(), - 'fields' => ['f' => ['type' => Type::string()]], + 'fields' => ['f' => ['type' => Type::string()]], ]); $type->assertValid(); } // Type System: Union types must be resolvable - private function ObjectWithIsTypeOf() - { - return new ObjectType([ - 'name' => 'ObjectWithIsTypeOf', - 'fields' => ['f' => ['type' => Type::string()]], - ]); - } - /** * @see it('accepts a Union type defining resolveType') */ @@ -1102,7 +1100,7 @@ class DefinitionTest extends TestCase // Should not throw: $this->schemaWithFieldType( new UnionType([ - 'name' => 'SomeUnion', + 'name' => 'SomeUnion', 'types' => [$this->objectType], ]) ); @@ -1117,7 +1115,7 @@ class DefinitionTest extends TestCase // Should not throw: $this->schemaWithFieldType( new UnionType([ - 'name' => 'SomeUnion', + 'name' => 'SomeUnion', 'types' => [$this->objectWithIsTypeOf], ]) ); @@ -1132,7 +1130,7 @@ class DefinitionTest extends TestCase // Should not throw: $this->schemaWithFieldType( new UnionType([ - 'name' => 'SomeUnion', + 'name' => 'SomeUnion', 'types' => [$this->objectWithIsTypeOf], ]) ); @@ -1149,15 +1147,13 @@ class DefinitionTest extends TestCase ); $this->schemaWithFieldType( new UnionType([ - 'name' => 'SomeUnion', + 'name' => 'SomeUnion', 'resolveType' => new \stdClass(), - 'types' => [$this->objectWithIsTypeOf], + 'types' => [$this->objectWithIsTypeOf], ]) ); } - // Type System: Scalar types must be serializable - /** * @see it('accepts a Scalar type defining serialize') */ @@ -1167,7 +1163,7 @@ class DefinitionTest extends TestCase // Should not throw $this->schemaWithFieldType( new CustomScalarType([ - 'name' => 'SomeScalar', + 'name' => 'SomeScalar', 'serialize' => function () { return null; }, @@ -1175,6 +1171,8 @@ class DefinitionTest extends TestCase ); } + // Type System: Scalar types must be serializable + /** * @see it('rejects a Scalar type not defining serialize') */ @@ -1187,9 +1185,7 @@ class DefinitionTest extends TestCase 'functions are also provided.' ); $this->schemaWithFieldType( - new CustomScalarType([ - 'name' => 'SomeScalar', - ]) + new CustomScalarType(['name' => 'SomeScalar']) ); } @@ -1206,7 +1202,7 @@ class DefinitionTest extends TestCase ); $this->schemaWithFieldType( new CustomScalarType([ - 'name' => 'SomeScalar', + 'name' => 'SomeScalar', 'serialize' => new \stdClass(), ]) ); @@ -1221,10 +1217,10 @@ class DefinitionTest extends TestCase // Should not throw: $this->schemaWithFieldType( new CustomScalarType([ - 'name' => 'SomeScalar', - 'serialize' => function () { + 'name' => 'SomeScalar', + 'serialize' => function () { }, - 'parseValue' => function () { + 'parseValue' => function () { }, 'parseLiteral' => function () { }, @@ -1243,8 +1239,8 @@ class DefinitionTest extends TestCase ); $this->schemaWithFieldType( new CustomScalarType([ - 'name' => 'SomeScalar', - 'serialize' => function () { + 'name' => 'SomeScalar', + 'serialize' => function () { }, 'parseValue' => function () { }, @@ -1263,8 +1259,8 @@ class DefinitionTest extends TestCase ); $this->schemaWithFieldType( new CustomScalarType([ - 'name' => 'SomeScalar', - 'serialize' => function () { + 'name' => 'SomeScalar', + 'serialize' => function () { }, 'parseLiteral' => function () { }, @@ -1283,17 +1279,15 @@ class DefinitionTest extends TestCase ); $this->schemaWithFieldType( new CustomScalarType([ - 'name' => 'SomeScalar', - 'serialize' => function () { + 'name' => 'SomeScalar', + 'serialize' => function () { }, - 'parseValue' => new \stdClass(), + 'parseValue' => new \stdClass(), 'parseLiteral' => new \stdClass(), ]) ); } - // Type System: Object types must be assertable - /** * @see it('accepts an Object type with an isTypeOf function') */ @@ -1303,12 +1297,14 @@ class DefinitionTest extends TestCase // Should not throw $this->schemaWithFieldType( new ObjectType([ - 'name' => 'AnotherObject', + 'name' => 'AnotherObject', 'fields' => ['f' => ['type' => Type::string()]], ]) ); } + // Type System: Object types must be assertable + /** * @see it('rejects an Object type with an incorrect type for isTypeOf') */ @@ -1320,15 +1316,13 @@ class DefinitionTest extends TestCase ); $this->schemaWithFieldType( new ObjectType([ - 'name' => 'AnotherObject', + 'name' => 'AnotherObject', 'isTypeOf' => new \stdClass(), - 'fields' => ['f' => ['type' => Type::string()]], + 'fields' => ['f' => ['type' => Type::string()]], ]) ); } - // Type System: Union types must be array - /** * @see it('accepts a Union type with array types') */ @@ -1338,12 +1332,14 @@ class DefinitionTest extends TestCase // Should not throw: $this->schemaWithFieldType( new UnionType([ - 'name' => 'SomeUnion', + 'name' => 'SomeUnion', 'types' => [$this->objectType], ]) ); } + // Type System: Union types must be array + /** * @see it('accepts a Union type with function returning an array of types') */ @@ -1352,7 +1348,7 @@ class DefinitionTest extends TestCase $this->expectNotToPerformAssertions(); $this->schemaWithFieldType( new UnionType([ - 'name' => 'SomeUnion', + 'name' => 'SomeUnion', 'types' => function () { return [$this->objectType]; }, @@ -1370,9 +1366,7 @@ class DefinitionTest extends TestCase 'Must provide Array of types or a callable which returns such an array for Union SomeUnion' ); $this->schemaWithFieldType( - new UnionType([ - 'name' => 'SomeUnion', - ]) + new UnionType(['name' => 'SomeUnion']) ); } @@ -1387,21 +1381,19 @@ class DefinitionTest extends TestCase ); $this->schemaWithFieldType( new UnionType([ - 'name' => 'SomeUnion', - 'types' => (object)[ 'test' => $this->objectType, ], + 'name' => 'SomeUnion', + 'types' => (object) ['test' => $this->objectType], ]) ); } - // Type System: Input Objects must have fields - /** * @see it('accepts an Input Object type with fields') */ public function testAcceptsAnInputObjectTypeWithFields() : void { $inputObjType = new InputObjectType([ - 'name' => 'SomeInputObject', + 'name' => 'SomeInputObject', 'fields' => [ 'f' => ['type' => Type::string()], ], @@ -1410,13 +1402,15 @@ class DefinitionTest extends TestCase $this->assertSame(Type::string(), $inputObjType->getField('f')->getType()); } + // Type System: Input Objects must have fields + /** * @see it('accepts an Input Object type with a field function') */ public function testAcceptsAnInputObjectTypeWithAFieldFunction() : void { $inputObjType = new InputObjectType([ - 'name' => 'SomeInputObject', + 'name' => 'SomeInputObject', 'fields' => function () { return [ 'f' => ['type' => Type::string()], @@ -1433,12 +1427,12 @@ class DefinitionTest extends TestCase public function testRejectsAnInputObjectTypeWithIncorrectFields() : void { $inputObjType = new InputObjectType([ - 'name' => 'SomeInputObject', + 'name' => 'SomeInputObject', 'fields' => [], ]); $this->expectException(InvariantViolation::class); $this->expectExceptionMessage( - 'SomeInputObject fields must be an associative array with field names as keys or a callable '. + 'SomeInputObject fields must be an associative array with field names as keys or a callable ' . 'which returns such an array.' ); $inputObjType->assertValid(); @@ -1450,7 +1444,7 @@ class DefinitionTest extends TestCase public function testRejectsAnInputObjectTypeWithFieldsFunctionThatReturnsIncorrectType() : void { $inputObjType = new InputObjectType([ - 'name' => 'SomeInputObject', + 'name' => 'SomeInputObject', 'fields' => function () { return []; }, @@ -1463,18 +1457,16 @@ class DefinitionTest extends TestCase $inputObjType->assertValid(); } - // Type System: Input Object fields must not have resolvers - /** * @see it('rejects an Input Object type with resolvers') */ public function testRejectsAnInputObjectTypeWithResolvers() : void { $inputObjType = new InputObjectType([ - 'name' => 'SomeInputObject', + 'name' => 'SomeInputObject', 'fields' => [ 'f' => [ - 'type' => Type::string(), + 'type' => Type::string(), 'resolve' => function () { return 0; }, @@ -1489,16 +1481,18 @@ class DefinitionTest extends TestCase $inputObjType->assertValid(); } + // Type System: Input Object fields must not have resolvers + /** * @see it('rejects an Input Object type with resolver constant') */ public function testRejectsAnInputObjectTypeWithResolverConstant() : void { $inputObjType = new InputObjectType([ - 'name' => 'SomeInputObject', + 'name' => 'SomeInputObject', 'fields' => [ 'f' => [ - 'type' => Type::string(), + 'type' => Type::string(), 'resolve' => new \stdClass(), ], ], @@ -1511,15 +1505,13 @@ class DefinitionTest extends TestCase $inputObjType->assertValid(); } - // Type System: Enum types must be well defined - /** * @see it('accepts a well defined Enum type with empty value definition') */ public function testAcceptsAWellDefinedEnumTypeWithEmptyValueDefinition() : void { $enumType = new EnumType([ - 'name' => 'SomeEnum', + 'name' => 'SomeEnum', 'values' => [ 'FOO' => [], 'BAR' => [], @@ -1529,13 +1521,15 @@ class DefinitionTest extends TestCase $this->assertEquals('BAR', $enumType->getValue('BAR')->value); } + // Type System: Enum types must be well defined + /** * @see it('accepts a well defined Enum type with internal value definition') */ public function testAcceptsAWellDefinedEnumTypeWithInternalValueDefinition() : void { $enumType = new EnumType([ - 'name' => 'SomeEnum', + 'name' => 'SomeEnum', 'values' => [ 'FOO' => ['value' => 10], 'BAR' => ['value' => 20], @@ -1551,7 +1545,7 @@ class DefinitionTest extends TestCase public function testRejectsAnEnumTypeWithIncorrectlyTypedValues() : void { $enumType = new EnumType([ - 'name' => 'SomeEnum', + 'name' => 'SomeEnum', 'values' => [['FOO' => 10]], ]); $this->expectException(InvariantViolation::class); @@ -1567,11 +1561,9 @@ class DefinitionTest extends TestCase public function testDoesNotAllowIsDeprecatedWithoutDeprecationReasonOnEnum() : void { $enumType = new EnumType([ - 'name' => 'SomeEnum', + 'name' => 'SomeEnum', 'values' => [ - 'FOO' => [ - 'isDeprecated' => true, - ], + 'FOO' => ['isDeprecated' => true], ], ]); $this->expectException(InvariantViolation::class); @@ -1582,7 +1574,6 @@ class DefinitionTest extends TestCase $enumType->assertValid(); } - /** * Type System: List must accept only types */ @@ -1606,27 +1597,26 @@ class DefinitionTest extends TestCase try { Type::listOf($type); } catch (\Throwable $e) { - $this->fail("List is expected to accept type: " . get_class($type) . ", but got error: ". $e->getMessage()); + $this->fail('List is expected to accept type: ' . get_class($type) . ', but got error: ' . $e->getMessage()); } } foreach ($badTypes as $badType) { $typeStr = Utils::printSafe($badType); try { Type::listOf($badType); - $this->fail("List should not accept $typeStr"); + $this->fail(sprintf('List should not accept %s', $typeStr)); } catch (InvariantViolation $e) { - $this->assertEquals("Expected $typeStr to be a GraphQL type.", $e->getMessage()); + $this->assertEquals(sprintf('Expected %s to be a GraphQL type.', $typeStr), $e->getMessage()); } } } - /** * Type System: NonNull must only accept non-nullable types */ public function testNonNullMustOnlyAcceptNonNullableTypes() : void { - $nullableTypes = [ + $nullableTypes = [ Type::string(), $this->scalarType, $this->objectType, @@ -1648,67 +1638,67 @@ class DefinitionTest extends TestCase try { Type::nonNull($type); } catch (\Throwable $e) { - $this->fail("NonNull is expected to accept type: " . get_class($type) . ", but got error: ". $e->getMessage()); + $this->fail('NonNull is expected to accept type: ' . get_class($type) . ', but got error: ' . $e->getMessage()); } } foreach ($notNullableTypes as $badType) { $typeStr = Utils::printSafe($badType); try { Type::nonNull($badType); - $this->fail("Nulls should not accept $typeStr"); + $this->fail(sprintf('Nulls should not accept %s', $typeStr)); } catch (InvariantViolation $e) { - $this->assertEquals("Expected $typeStr to be a GraphQL nullable type.", $e->getMessage()); + $this->assertEquals(sprintf('Expected %s to be a GraphQL nullable type.', $typeStr), $e->getMessage()); } } } - // Type System: A Schema must contain uniquely named types - /** * @see it('rejects a Schema which redefines a built-in type') */ public function testRejectsASchemaWhichRedefinesABuiltInType() : void { $FakeString = new CustomScalarType([ - 'name' => 'String', + 'name' => 'String', 'serialize' => function () { }, ]); $QueryType = new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ 'normal' => ['type' => Type::string()], - 'fake' => ['type' => $FakeString], + 'fake' => ['type' => $FakeString], ], ]); $this->expectException(InvariantViolation::class); $this->expectExceptionMessage( - 'Schema must contain unique named types but contains multiple types named "String" '. + 'Schema must contain unique named types but contains multiple types named "String" ' . '(see http://webonyx.github.io/graphql-php/type-system/#type-registry).' ); $schema = new Schema(['query' => $QueryType]); $schema->assertValid(); } + // Type System: A Schema must contain uniquely named types + /** * @see it('rejects a Schema which defines an object type twice') */ public function testRejectsASchemaWhichDefinesAnObjectTypeTwice() : void { $A = new ObjectType([ - 'name' => 'SameName', + 'name' => 'SameName', 'fields' => ['f' => ['type' => Type::string()]], ]); $B = new ObjectType([ - 'name' => 'SameName', + 'name' => 'SameName', 'fields' => ['f' => ['type' => Type::string()]], ]); $QueryType = new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ 'a' => ['type' => $A], 'b' => ['type' => $B], @@ -1719,7 +1709,7 @@ class DefinitionTest extends TestCase 'Schema must contain unique named types but contains multiple types named "SameName" ' . '(see http://webonyx.github.io/graphql-php/type-system/#type-registry).' ); - $schema = new Schema([ 'query' => $QueryType ]); + $schema = new Schema(['query' => $QueryType]); $schema->assertValid(); } @@ -1729,24 +1719,24 @@ class DefinitionTest extends TestCase public function testRejectsASchemaWhichHaveSameNamedObjectsImplementingAnInterface() : void { $AnotherInterface = new InterfaceType([ - 'name' => 'AnotherInterface', + 'name' => 'AnotherInterface', 'fields' => ['f' => ['type' => Type::string()]], ]); $FirstBadObject = new ObjectType([ - 'name' => 'BadObject', + 'name' => 'BadObject', 'interfaces' => [$AnotherInterface], - 'fields' => ['f' => ['type' => Type::string()]], + 'fields' => ['f' => ['type' => Type::string()]], ]); $SecondBadObject = new ObjectType([ - 'name' => 'BadObject', + 'name' => 'BadObject', 'interfaces' => [$AnotherInterface], - 'fields' => ['f' => ['type' => Type::string()]], + 'fields' => ['f' => ['type' => Type::string()]], ]); $QueryType = new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ 'iface' => ['type' => $AnotherInterface], ], @@ -1763,4 +1753,12 @@ class DefinitionTest extends TestCase ]); $schema->assertValid(); } + + public function objectWithIsTypeOf() : ObjectType + { + return new ObjectType([ + 'name' => 'ObjectWithIsTypeOf', + 'fields' => ['f' => ['type' => Type::string()]], + ]); + } } diff --git a/tests/Type/EnumTypeTest.php b/tests/Type/EnumTypeTest.php index f8ab00a..8445d60 100644 --- a/tests/Type/EnumTypeTest.php +++ b/tests/Type/EnumTypeTest.php @@ -1,68 +1,77 @@ 'Color', + 'name' => 'Color', 'values' => [ - 'RED' => ['value' => 0], + 'RED' => ['value' => 0], 'GREEN' => ['value' => 1], - 'BLUE' => ['value' => 2], - ] + 'BLUE' => ['value' => 2], + ], ]); $simpleEnum = new EnumType([ - 'name' => 'SimpleEnum', + 'name' => 'SimpleEnum', 'values' => [ - 'ONE', 'TWO', 'THREE' - ] + 'ONE', + 'TWO', + 'THREE', + ], ]); - $Complex1 = ['someRandomFunction' => function() {}]; + $Complex1 = [ + 'someRandomFunction' => function () { + }, + ]; $Complex2 = new \ArrayObject(['someRandomValue' => 123]); $ComplexEnum = new EnumType([ - 'name' => 'Complex', + 'name' => 'Complex', 'values' => [ 'ONE' => ['value' => $Complex1], - 'TWO' => ['value' => $Complex2] - ] + 'TWO' => ['value' => $Complex2], + ], ]); $QueryType = new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ - 'colorEnum' => [ - 'type' => $ColorType, - 'args' => [ - 'fromEnum' => ['type' => $ColorType], - 'fromInt' => ['type' => Type::int()], + 'colorEnum' => [ + 'type' => $ColorType, + 'args' => [ + 'fromEnum' => ['type' => $ColorType], + 'fromInt' => ['type' => Type::int()], 'fromString' => ['type' => Type::string()], ], 'resolve' => function ($value, $args) { @@ -75,28 +84,28 @@ class EnumTypeTest extends TestCase if (isset($args['fromEnum'])) { return $args['fromEnum']; } - } + }, ], - 'simpleEnum' => [ - 'type' => $simpleEnum, - 'args' => [ - 'fromName' => ['type' => Type::string()], - 'fromValue' => ['type' => Type::string()] + 'simpleEnum' => [ + 'type' => $simpleEnum, + 'args' => [ + 'fromName' => ['type' => Type::string()], + 'fromValue' => ['type' => Type::string()], ], - 'resolve' => function($value, $args) { + 'resolve' => function ($value, $args) { if (isset($args['fromName'])) { return $args['fromName']; } if (isset($args['fromValue'])) { return $args['fromValue']; } - } + }, ], - 'colorInt' => [ - 'type' => Type::int(), - 'args' => [ + 'colorInt' => [ + 'type' => Type::int(), + 'args' => [ 'fromEnum' => ['type' => $ColorType], - 'fromInt' => ['type' => Type::int()], + 'fromInt' => ['type' => Type::int()], ], 'resolve' => function ($value, $args) { if (isset($args['fromInt'])) { @@ -105,75 +114,76 @@ class EnumTypeTest extends TestCase if (isset($args['fromEnum'])) { return $args['fromEnum']; } - } + }, ], 'complexEnum' => [ - 'type' => $ComplexEnum, - 'args' => [ - 'fromEnum' => [ - 'type' => $ComplexEnum, + 'type' => $ComplexEnum, + 'args' => [ + 'fromEnum' => [ + 'type' => $ComplexEnum, // Note: defaultValue is provided an *internal* representation for // Enums, rather than the string name. - 'defaultValue' => $Complex1 + 'defaultValue' => $Complex1, ], 'provideGoodValue' => [ 'type' => Type::boolean(), ], - 'provideBadValue' => [ - 'type' => Type::boolean() - ] + 'provideBadValue' => [ + 'type' => Type::boolean(), + ], ], - 'resolve' => function($value, $args) use ($Complex1, $Complex2) { - if (!empty($args['provideGoodValue'])) { + 'resolve' => function ($value, $args) use ($Complex2) { + if (! empty($args['provideGoodValue'])) { // Note: this is one of the references of the internal values which // ComplexEnum allows. return $Complex2; } - if (!empty($args['provideBadValue'])) { + if (! empty($args['provideBadValue'])) { // Note: similar shape, but not the same *reference* // as Complex2 above. Enum internal values require === equality. return new \ArrayObject(['someRandomValue' => 123]); } + return $args['fromEnum']; - } - ] - ] + }, + ], + ], ]); $MutationType = new ObjectType([ - 'name' => 'Mutation', + 'name' => 'Mutation', 'fields' => [ 'favoriteEnum' => [ - 'type' => $ColorType, - 'args' => ['color' => ['type' => $ColorType]], + 'type' => $ColorType, + 'args' => ['color' => ['type' => $ColorType]], 'resolve' => function ($value, $args) { - return isset($args['color']) ? $args['color'] : null; - } - ] - ] + return $args['color'] ?? null; + }, + ], + ], ]); $SubscriptionType = new ObjectType([ - 'name' => 'Subscription', + 'name' => 'Subscription', 'fields' => [ 'subscribeToEnum' => [ - 'type' => $ColorType, - 'args' => ['color' => ['type' => $ColorType]], + 'type' => $ColorType, + 'args' => ['color' => ['type' => $ColorType]], 'resolve' => function ($value, $args) { - return isset($args['color']) ? $args['color'] : null; - } - ] - ] + return $args['color'] ?? null; + }, + ], + ], ]); - $this->Complex1 = $Complex1; - $this->Complex2 = $Complex2; + $this->Complex1 = $Complex1; + $this->Complex2 = $Complex2; $this->ComplexEnum = $ComplexEnum; $this->schema = new Schema([ - 'query' => $QueryType, - 'mutation' => $MutationType, - 'subscription' => $SubscriptionType + 'query' => $QueryType, + 'mutation' => $MutationType, + 'subscription' => $SubscriptionType, ]); } @@ -221,12 +231,34 @@ class EnumTypeTest extends TestCase '{ colorEnum(fromEnum: "GREEN") }', null, [ - 'message' => "Expected type Color, found \"GREEN\"; Did you mean the enum value GREEN?", - 'locations' => [new SourceLocation(1, 23)] + 'message' => 'Expected type Color, found "GREEN"; Did you mean the enum value GREEN?', + 'locations' => [new SourceLocation(1, 23)], ] ); } + private function expectFailure($query, $vars, $err) + { + $result = GraphQL::executeQuery($this->schema, $query, null, null, $vars); + $this->assertEquals(1, count($result->errors)); + + if (is_array($err)) { + $this->assertEquals( + $err['message'], + $result->errors[0]->getMessage() + ); + $this->assertEquals( + $err['locations'], + $result->errors[0]->getLocations() + ); + } else { + $this->assertEquals( + $err, + $result->errors[0]->getMessage() + ); + } + } + /** * @see it('does not accept valuesNotInTheEnum') */ @@ -236,8 +268,8 @@ class EnumTypeTest extends TestCase '{ colorEnum(fromEnum: GREENISH) }', null, [ - 'message' => "Expected type Color, found GREENISH; Did you mean the enum value GREEN?", - 'locations' => [new SourceLocation(1, 23)] + 'message' => 'Expected type Color, found GREENISH; Did you mean the enum value GREEN?', + 'locations' => [new SourceLocation(1, 23)], ] ); } @@ -251,8 +283,8 @@ class EnumTypeTest extends TestCase '{ colorEnum(fromEnum: green) }', null, [ - 'message' => "Expected type Color, found green; Did you mean the enum value GREEN?", - 'locations' => [new SourceLocation(1, 23)] + 'message' => 'Expected type Color, found green; Did you mean the enum value GREEN?', + 'locations' => [new SourceLocation(1, 23)], ] ); } @@ -266,9 +298,9 @@ class EnumTypeTest extends TestCase '{ colorEnum(fromString: "GREEN") }', null, [ - 'message' => 'Expected a value of type "Color" but received: GREEN', + 'message' => 'Expected a value of type "Color" but received: GREEN', 'locations' => [new SourceLocation(1, 3)], - 'path' => ['colorEnum'], + 'path' => ['colorEnum'], ] ); } @@ -281,7 +313,7 @@ class EnumTypeTest extends TestCase $this->expectFailure( '{ colorEnum(fromEnum: 1) }', null, - "Expected type Color, found 1." + 'Expected type Color, found 1.' ); } @@ -293,7 +325,7 @@ class EnumTypeTest extends TestCase $this->expectFailure( '{ colorEnum(fromInt: GREEN) }', null, - "Expected type Int, found GREEN." + 'Expected type Int, found GREEN.' ); } @@ -381,7 +413,7 @@ class EnumTypeTest extends TestCase $this->expectFailure( 'query test($color: Int!) { colorEnum(fromEnum: $color) }', ['color' => 2], - 'Variable "$color" of type "Int!" used in position ' . 'expecting type "Color".' + 'Variable "$color" of type "Int!" used in position expecting type "Color".' ); } @@ -392,10 +424,13 @@ class EnumTypeTest extends TestCase { $this->assertEquals( ['data' => ['colorEnum' => 'RED', 'colorInt' => 0]], - GraphQL::executeQuery($this->schema, "{ + GraphQL::executeQuery( + $this->schema, + '{ colorEnum(fromEnum: RED) colorInt(fromEnum: RED) - }")->toArray() + }' + )->toArray() ); } @@ -406,10 +441,13 @@ class EnumTypeTest extends TestCase { $this->assertEquals( ['data' => ['colorEnum' => null, 'colorInt' => null]], - GraphQL::executeQuery($this->schema, "{ + GraphQL::executeQuery( + $this->schema, + '{ colorEnum colorInt - }")->toArray() + }' + )->toArray() ); } @@ -419,7 +457,7 @@ class EnumTypeTest extends TestCase public function testPresentsGetValuesAPIForComplexEnums() : void { $ComplexEnum = $this->ComplexEnum; - $values = $ComplexEnum->getValues(); + $values = $ComplexEnum->getValues(); $this->assertEquals(2, count($values)); $this->assertEquals('ONE', $values[0]->name); @@ -446,25 +484,29 @@ class EnumTypeTest extends TestCase */ public function testMayBeInternallyRepresentedWithComplexValues() : void { - $result = GraphQL::executeQuery($this->schema, '{ + $result = GraphQL::executeQuery( + $this->schema, + '{ first: complexEnum second: complexEnum(fromEnum: TWO) good: complexEnum(provideGoodValue: true) bad: complexEnum(provideBadValue: true) - }')->toArray(true); + }' + )->toArray(true); $expected = [ - 'data' => [ - 'first' => 'ONE', + 'data' => [ + 'first' => 'ONE', 'second' => 'TWO', - 'good' => 'TWO', - 'bad' => null + 'good' => 'TWO', + 'bad' => null, ], 'errors' => [[ 'debugMessage' => 'Expected a value of type "Complex" but received: instance of ArrayObject', - 'locations' => [['line' => 5, 'column' => 9]] - ]] + 'locations' => [['line' => 5, 'column' => 9]], + ], + ], ]; $this->assertArraySubset($expected, $result); @@ -489,35 +531,14 @@ class EnumTypeTest extends TestCase $this->assertArraySubset( [ - 'data' => ['first' => 'ONE', 'second' => 'TWO', 'third' => null], + 'data' => ['first' => 'ONE', 'second' => 'TWO', 'third' => null], 'errors' => [[ 'debugMessage' => 'Expected a value of type "SimpleEnum" but received: WRONG', - 'locations' => [['line' => 4, 'column' => 13]] - ]] + 'locations' => [['line' => 4, 'column' => 13]], + ], + ], ], GraphQL::executeQuery($this->schema, $q)->toArray(true) ); } - - private function expectFailure($query, $vars, $err) - { - $result = GraphQL::executeQuery($this->schema, $query, null, null, $vars); - $this->assertEquals(1, count($result->errors)); - - if (is_array($err)) { - $this->assertEquals( - $err['message'], - $result->errors[0]->getMessage() - ); - $this->assertEquals( - $err['locations'], - $result->errors[0]->getLocations() - ); - } else { - $this->assertEquals( - $err, - $result->errors[0]->getMessage() - ); - } - } } diff --git a/tests/Type/IntrospectionTest.php b/tests/Type/IntrospectionTest.php index b44a168..cd710cc 100644 --- a/tests/Type/IntrospectionTest.php +++ b/tests/Type/IntrospectionTest.php @@ -1,1067 +1,1027 @@ new ObjectType([ - 'name' => 'QueryRoot', - 'fields' => ['a' => Type::string()] - ]) + 'name' => 'QueryRoot', + 'fields' => ['a' => Type::string()], + ]), ]); - $request = Introspection::getIntrospectionQuery(['descriptions' => false]); - $expected = array ( + $request = Introspection::getIntrospectionQuery(['descriptions' => false]); + $expected = [ 'data' => - array ( + [ '__schema' => - array ( - 'mutationType' => NULL, - 'subscriptionType' => NULL, - 'queryType' => - array ( - 'name' => 'QueryRoot', - ), - 'types' => - array ( - array ( - 'kind' => 'OBJECT', - 'name' => 'QueryRoot', - 'inputFields' => NULL, - 'interfaces' => - array ( - ), - 'enumValues' => NULL, - 'possibleTypes' => NULL, - 'fields' => array ( - array ( - 'name' => 'a', - 'args' => array(), - 'type' => array( - 'kind' => 'SCALAR', - 'name' => 'String', - 'ofType' => null - ), - 'isDeprecated' => false, + [ + 'mutationType' => null, + 'subscriptionType' => null, + 'queryType' => + ['name' => 'QueryRoot'], + 'types' => + [ + [ + 'kind' => 'OBJECT', + 'name' => 'QueryRoot', + 'inputFields' => null, + 'interfaces' => + [], + 'enumValues' => null, + 'possibleTypes' => null, + 'fields' => [ + [ + 'name' => 'a', + 'args' => [], + 'type' => [ + 'kind' => 'SCALAR', + 'name' => 'String', + 'ofType' => null, + ], + 'isDeprecated' => false, 'deprecationReason' => null, - ) - ) - ), - array ( - 'kind' => 'SCALAR', - 'name' => 'String', - 'fields' => NULL, - 'inputFields' => NULL, - 'interfaces' => NULL, - 'enumValues' => NULL, - 'possibleTypes' => NULL, - ), - array ( - 'kind' => 'SCALAR', - 'name' => 'ID', - 'fields' => NULL, - 'inputFields' => NULL, - 'interfaces' => NULL, - 'enumValues' => NULL, - 'possibleTypes' => NULL, - ), - array ( - 'kind' => 'SCALAR', - 'name' => 'Float', - 'fields' => NULL, - 'inputFields' => NULL, - 'interfaces' => NULL, - 'enumValues' => NULL, - 'possibleTypes' => NULL, - ), - array ( - 'kind' => 'SCALAR', - 'name' => 'Int', - 'fields' => NULL, - 'inputFields' => NULL, - 'interfaces' => NULL, - 'enumValues' => NULL, - 'possibleTypes' => NULL, - ), - array ( - 'kind' => 'SCALAR', - 'name' => 'Boolean', - 'fields' => NULL, - 'inputFields' => NULL, - 'interfaces' => NULL, - 'enumValues' => NULL, - 'possibleTypes' => NULL, - ), - array ( - 'kind' => 'OBJECT', - 'name' => '__Schema', - 'fields' => - array ( + ], + ], + ], + [ + 'kind' => 'SCALAR', + 'name' => 'String', + 'fields' => null, + 'inputFields' => null, + 'interfaces' => null, + 'enumValues' => null, + 'possibleTypes' => null, + ], + [ + 'kind' => 'SCALAR', + 'name' => 'ID', + 'fields' => null, + 'inputFields' => null, + 'interfaces' => null, + 'enumValues' => null, + 'possibleTypes' => null, + ], + [ + 'kind' => 'SCALAR', + 'name' => 'Float', + 'fields' => null, + 'inputFields' => null, + 'interfaces' => null, + 'enumValues' => null, + 'possibleTypes' => null, + ], + [ + 'kind' => 'SCALAR', + 'name' => 'Int', + 'fields' => null, + 'inputFields' => null, + 'interfaces' => null, + 'enumValues' => null, + 'possibleTypes' => null, + ], + [ + 'kind' => 'SCALAR', + 'name' => 'Boolean', + 'fields' => null, + 'inputFields' => null, + 'interfaces' => null, + 'enumValues' => null, + 'possibleTypes' => null, + ], + [ + 'kind' => 'OBJECT', + 'name' => '__Schema', + 'fields' => + [ 0 => - array ( - 'name' => 'types', - 'args' => - array ( - ), - 'type' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'name' => 'types', + 'args' => + [], + 'type' => + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( - 'kind' => 'LIST', - 'name' => NULL, + [ + 'kind' => 'LIST', + 'name' => null, 'ofType' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( + [ 'kind' => 'OBJECT', - 'name' => '__Type' - ), - ), - ), - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + 'name' => '__Type', + ], + ], + ], + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 1 => - array ( - 'name' => 'queryType', - 'args' => - array ( - ), - 'type' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'name' => 'queryType', + 'args' => + [], + 'type' => + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( + [ 'kind' => 'OBJECT', 'name' => '__Type', - ), - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), - array ( - 'name' => 'mutationType', - 'args' => - array ( - ), - 'type' => - array ( + ], + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], + [ + 'name' => 'mutationType', + 'args' => + [], + 'type' => + [ 'kind' => 'OBJECT', 'name' => '__Type', - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), - array ( - 'name' => 'subscriptionType', - 'args' => - array ( - ), - 'type' => - array ( + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], + [ + 'name' => 'subscriptionType', + 'args' => + [], + 'type' => + [ 'kind' => 'OBJECT', 'name' => '__Type', - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), - array ( - 'name' => 'directives', - 'args' => - array ( - ), - 'type' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], + [ + 'name' => 'directives', + 'args' => + [], + 'type' => + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( - 'kind' => 'LIST', - 'name' => NULL, + [ + 'kind' => 'LIST', + 'name' => null, 'ofType' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( + [ 'kind' => 'OBJECT', 'name' => '__Directive', - ), - ), - ), - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), - ), - 'inputFields' => NULL, - 'interfaces' => - array ( - ), - 'enumValues' => NULL, - 'possibleTypes' => NULL, - ), - array ( - 'kind' => 'OBJECT', - 'name' => '__Type', - 'fields' => - array ( + ], + ], + ], + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], + ], + 'inputFields' => null, + 'interfaces' => + [], + 'enumValues' => null, + 'possibleTypes' => null, + ], + [ + 'kind' => 'OBJECT', + 'name' => '__Type', + 'fields' => + [ 0 => - array ( - 'name' => 'kind', - 'args' => - array ( - ), - 'type' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'name' => 'kind', + 'args' => + [], + 'type' => + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( + [ 'kind' => 'ENUM', 'name' => '__TypeKind', - ), - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + ], + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 1 => - array ( - 'name' => 'name', - 'args' => - array ( - ), - 'type' => - array ( + [ + 'name' => 'name', + 'args' => + [], + 'type' => + [ 'kind' => 'SCALAR', 'name' => 'String', - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 2 => - array ( - 'name' => 'description', - 'args' => - array ( - ), - 'type' => - array ( + [ + 'name' => 'description', + 'args' => + [], + 'type' => + [ 'kind' => 'SCALAR', 'name' => 'String', - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 3 => - array ( - 'name' => 'fields', - 'args' => - array ( + [ + 'name' => 'fields', + 'args' => + [ 0 => - array ( - 'name' => 'includeDeprecated', - 'type' => - array ( + [ + 'name' => 'includeDeprecated', + 'type' => + [ 'kind' => 'SCALAR', 'name' => 'Boolean', - ), + ], 'defaultValue' => 'false', - ), - ), - 'type' => - array ( - 'kind' => 'LIST', - 'name' => NULL, + ], + ], + 'type' => + [ + 'kind' => 'LIST', + 'name' => null, 'ofType' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( + [ 'kind' => 'OBJECT', 'name' => '__Field', - ), - ), - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + ], + ], + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 4 => - array ( - 'name' => 'interfaces', - 'args' => - array ( - ), - 'type' => - array ( - 'kind' => 'LIST', - 'name' => NULL, + [ + 'name' => 'interfaces', + 'args' => + [], + 'type' => + [ + 'kind' => 'LIST', + 'name' => null, 'ofType' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( + [ 'kind' => 'OBJECT', 'name' => '__Type', - ), - ), - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + ], + ], + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 5 => - array ( - 'name' => 'possibleTypes', - 'args' => - array ( - ), - 'type' => - array ( - 'kind' => 'LIST', - 'name' => NULL, + [ + 'name' => 'possibleTypes', + 'args' => + [], + 'type' => + [ + 'kind' => 'LIST', + 'name' => null, 'ofType' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( + [ 'kind' => 'OBJECT', 'name' => '__Type', - ), - ), - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + ], + ], + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 6 => - array ( - 'name' => 'enumValues', - 'args' => - array ( + [ + 'name' => 'enumValues', + 'args' => + [ 0 => - array ( - 'name' => 'includeDeprecated', - 'type' => - array ( + [ + 'name' => 'includeDeprecated', + 'type' => + [ 'kind' => 'SCALAR', 'name' => 'Boolean', - ), + ], 'defaultValue' => 'false', - ), - ), - 'type' => - array ( - 'kind' => 'LIST', - 'name' => NULL, + ], + ], + 'type' => + [ + 'kind' => 'LIST', + 'name' => null, 'ofType' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( + [ 'kind' => 'OBJECT', 'name' => '__EnumValue', - ), - ), - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + ], + ], + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 7 => - array ( - 'name' => 'inputFields', - 'args' => - array ( - ), - 'type' => - array ( - 'kind' => 'LIST', - 'name' => NULL, + [ + 'name' => 'inputFields', + 'args' => + [], + 'type' => + [ + 'kind' => 'LIST', + 'name' => null, 'ofType' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( + [ 'kind' => 'OBJECT', 'name' => '__InputValue', - ), - ), - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + ], + ], + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 8 => - array ( - 'name' => 'ofType', - 'args' => - array ( - ), - 'type' => - array ( + [ + 'name' => 'ofType', + 'args' => + [], + 'type' => + [ 'kind' => 'OBJECT', 'name' => '__Type', - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), - ), - 'inputFields' => NULL, - 'interfaces' => - array ( - ), - 'enumValues' => NULL, - 'possibleTypes' => NULL, - ), - array ( - 'kind' => 'ENUM', - 'name' => '__TypeKind', - 'fields' => NULL, - 'inputFields' => NULL, - 'interfaces' => NULL, - 'enumValues' => - array ( + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], + ], + 'inputFields' => null, + 'interfaces' => + [], + 'enumValues' => null, + 'possibleTypes' => null, + ], + [ + 'kind' => 'ENUM', + 'name' => '__TypeKind', + 'fields' => null, + 'inputFields' => null, + 'interfaces' => null, + 'enumValues' => + [ 0 => - array ( - 'name' => 'SCALAR', - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + [ + 'name' => 'SCALAR', + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 1 => - array ( - 'name' => 'OBJECT', - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + [ + 'name' => 'OBJECT', + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 2 => - array ( - 'name' => 'INTERFACE', - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + [ + 'name' => 'INTERFACE', + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 3 => - array ( - 'name' => 'UNION', - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + [ + 'name' => 'UNION', + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 4 => - array ( - 'name' => 'ENUM', - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + [ + 'name' => 'ENUM', + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 5 => - array ( - 'name' => 'INPUT_OBJECT', - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + [ + 'name' => 'INPUT_OBJECT', + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 6 => - array ( - 'name' => 'LIST', - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + [ + 'name' => 'LIST', + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 7 => - array ( - 'name' => 'NON_NULL', - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), - ), - 'possibleTypes' => NULL, - ), - array ( - 'kind' => 'OBJECT', - 'name' => '__Field', - 'fields' => - array ( + [ + 'name' => 'NON_NULL', + 'isDeprecated' => false, + 'deprecationReason' => null, + ], + ], + 'possibleTypes' => null, + ], + [ + 'kind' => 'OBJECT', + 'name' => '__Field', + 'fields' => + [ 0 => - array ( - 'name' => 'name', - 'args' => - array ( - ), - 'type' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'name' => 'name', + 'args' => + [], + 'type' => + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( + [ 'kind' => 'SCALAR', 'name' => 'String', - ), - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + ], + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 1 => - array ( - 'name' => 'description', - 'args' => - array ( - ), - 'type' => - array ( + [ + 'name' => 'description', + 'args' => + [], + 'type' => + [ 'kind' => 'SCALAR', 'name' => 'String', - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 2 => - array ( - 'name' => 'args', - 'args' => - array ( - ), - 'type' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'name' => 'args', + 'args' => + [], + 'type' => + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( - 'kind' => 'LIST', - 'name' => NULL, + [ + 'kind' => 'LIST', + 'name' => null, 'ofType' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( + [ 'kind' => 'OBJECT', 'name' => '__InputValue', - ), - ), - ), - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + ], + ], + ], + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 3 => - array ( - 'name' => 'type', - 'args' => - array ( - ), - 'type' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'name' => 'type', + 'args' => + [], + 'type' => + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( + [ 'kind' => 'OBJECT', 'name' => '__Type', - ), - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + ], + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 4 => - array ( - 'name' => 'isDeprecated', - 'args' => - array ( - ), - 'type' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'name' => 'isDeprecated', + 'args' => + [], + 'type' => + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( + [ 'kind' => 'SCALAR', 'name' => 'Boolean', - ), - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + ], + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 5 => - array ( - 'name' => 'deprecationReason', - 'args' => - array ( - ), - 'type' => - array ( + [ + 'name' => 'deprecationReason', + 'args' => + [], + 'type' => + [ 'kind' => 'SCALAR', 'name' => 'String', - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), - ), - 'inputFields' => NULL, - 'interfaces' => - array ( - ), - 'enumValues' => NULL, - 'possibleTypes' => NULL, - ), - array ( - 'kind' => 'OBJECT', - 'name' => '__InputValue', - 'fields' => - array ( + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], + ], + 'inputFields' => null, + 'interfaces' => + [], + 'enumValues' => null, + 'possibleTypes' => null, + ], + [ + 'kind' => 'OBJECT', + 'name' => '__InputValue', + 'fields' => + [ 0 => - array ( - 'name' => 'name', - 'args' => - array ( - ), - 'type' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'name' => 'name', + 'args' => + [], + 'type' => + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( + [ 'kind' => 'SCALAR', 'name' => 'String', - ), - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + ], + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 1 => - array ( - 'name' => 'description', - 'args' => - array ( - ), - 'type' => - array ( + [ + 'name' => 'description', + 'args' => + [], + 'type' => + [ 'kind' => 'SCALAR', 'name' => 'String', - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 2 => - array ( - 'name' => 'type', - 'args' => - array ( - ), - 'type' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'name' => 'type', + 'args' => + [], + 'type' => + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( + [ 'kind' => 'OBJECT', 'name' => '__Type', - ), - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + ], + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 3 => - array ( - 'name' => 'defaultValue', - 'args' => - array ( - ), - 'type' => - array ( + [ + 'name' => 'defaultValue', + 'args' => + [], + 'type' => + [ 'kind' => 'SCALAR', 'name' => 'String', - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), - ), - 'inputFields' => NULL, - 'interfaces' => - array ( - ), - 'enumValues' => NULL, - 'possibleTypes' => NULL, - ), - array ( - 'kind' => 'OBJECT', - 'name' => '__EnumValue', - 'fields' => - array ( + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], + ], + 'inputFields' => null, + 'interfaces' => + [], + 'enumValues' => null, + 'possibleTypes' => null, + ], + [ + 'kind' => 'OBJECT', + 'name' => '__EnumValue', + 'fields' => + [ 0 => - array ( - 'name' => 'name', - 'args' => - array ( - ), - 'type' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'name' => 'name', + 'args' => + [], + 'type' => + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( + [ 'kind' => 'SCALAR', 'name' => 'String', - ), - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + ], + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 1 => - array ( - 'name' => 'description', - 'args' => - array ( - ), - 'type' => - array ( + [ + 'name' => 'description', + 'args' => + [], + 'type' => + [ 'kind' => 'SCALAR', 'name' => 'String', - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 2 => - array ( - 'name' => 'isDeprecated', - 'args' => - array ( - ), - 'type' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'name' => 'isDeprecated', + 'args' => + [], + 'type' => + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( + [ 'kind' => 'SCALAR', 'name' => 'Boolean', - ), - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + ], + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 3 => - array ( - 'name' => 'deprecationReason', - 'args' => - array ( - ), - 'type' => - array ( + [ + 'name' => 'deprecationReason', + 'args' => + [], + 'type' => + [ 'kind' => 'SCALAR', 'name' => 'String', - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), - ), - 'inputFields' => NULL, - 'interfaces' => - array ( - ), - 'enumValues' => NULL, - 'possibleTypes' => NULL, - ), - array ( - 'kind' => 'OBJECT', - 'name' => '__Directive', - 'fields' => - array ( + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], + ], + 'inputFields' => null, + 'interfaces' => + [], + 'enumValues' => null, + 'possibleTypes' => null, + ], + [ + 'kind' => 'OBJECT', + 'name' => '__Directive', + 'fields' => + [ 0 => - array ( - 'name' => 'name', - 'args' => - array ( - ), - 'type' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'name' => 'name', + 'args' => + [], + 'type' => + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( + [ 'kind' => 'SCALAR', 'name' => 'String', - ), - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + ], + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 1 => - array ( - 'name' => 'description', - 'args' => - array ( - ), - 'type' => - array ( + [ + 'name' => 'description', + 'args' => + [], + 'type' => + [ 'kind' => 'SCALAR', 'name' => 'String', - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 2 => - array ( - 'name' => 'locations', - 'args' => - array ( - ), - 'type' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'name' => 'locations', + 'args' => + [], + 'type' => + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( - 'kind' => 'LIST', - 'name' => NULL, + [ + 'kind' => 'LIST', + 'name' => null, 'ofType' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( + [ 'kind' => 'ENUM', 'name' => '__DirectiveLocation', - ), - ), - ), - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + ], + ], + ], + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 3 => - array ( - 'name' => 'args', - 'args' => - array ( - ), - 'type' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'name' => 'args', + 'args' => + [], + 'type' => + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( - 'kind' => 'LIST', - 'name' => NULL, + [ + 'kind' => 'LIST', + 'name' => null, 'ofType' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( + [ 'kind' => 'OBJECT', 'name' => '__InputValue', - ), - ), - ), - ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, - ), + ], + ], + ], + ], + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 4 => - array ( - 'name' => 'onOperation', - 'args' => - array ( - ), - 'type' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'name' => 'onOperation', + 'args' => + [], + 'type' => + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( + [ 'kind' => 'SCALAR', 'name' => 'Boolean', - ), - ), - 'isDeprecated' => true, + ], + ], + 'isDeprecated' => true, 'deprecationReason' => 'Use `locations`.', - ), + ], 5 => - array ( - 'name' => 'onFragment', - 'args' => - array ( - ), - 'type' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'name' => 'onFragment', + 'args' => + [], + 'type' => + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( + [ 'kind' => 'SCALAR', 'name' => 'Boolean', - ), - ), - 'isDeprecated' => true, + ], + ], + 'isDeprecated' => true, 'deprecationReason' => 'Use `locations`.', - ), + ], 6 => - array ( - 'name' => 'onField', - 'args' => - array ( - ), - 'type' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'name' => 'onField', + 'args' => + [], + 'type' => + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( + [ 'kind' => 'SCALAR', 'name' => 'Boolean', - ), - ), - 'isDeprecated' => true, + ], + ], + 'isDeprecated' => true, 'deprecationReason' => 'Use `locations`.', - ), - ), - 'inputFields' => NULL, - 'interfaces' => - array ( - ), - 'enumValues' => NULL, - 'possibleTypes' => NULL, - ), - array ( - 'kind' => 'ENUM', - 'name' => '__DirectiveLocation', - 'fields' => NULL, - 'inputFields' => NULL, - 'interfaces' => NULL, - 'enumValues' => - array ( + ], + ], + 'inputFields' => null, + 'interfaces' => + [], + 'enumValues' => null, + 'possibleTypes' => null, + ], + [ + 'kind' => 'ENUM', + 'name' => '__DirectiveLocation', + 'fields' => null, + 'inputFields' => null, + 'interfaces' => null, + 'enumValues' => + [ 0 => - array ( - 'name' => 'QUERY', - 'isDeprecated' => false, - 'deprecationReason' => null - ), + [ + 'name' => 'QUERY', + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 1 => - array ( - 'name' => 'MUTATION', - 'isDeprecated' => false, - 'deprecationReason' => null - ), + [ + 'name' => 'MUTATION', + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 2 => - array ( - 'name' => 'SUBSCRIPTION', - 'isDeprecated' => false, - 'deprecationReason' => null - ), + [ + 'name' => 'SUBSCRIPTION', + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 3 => - array ( - 'name' => 'FIELD', - 'isDeprecated' => false, - 'deprecationReason' => null - ), + [ + 'name' => 'FIELD', + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 4 => - array ( - 'name' => 'FRAGMENT_DEFINITION', - 'isDeprecated' => false, - 'deprecationReason' => null - ), + [ + 'name' => 'FRAGMENT_DEFINITION', + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 5 => - array ( - 'name' => 'FRAGMENT_SPREAD', - 'isDeprecated' => false, - 'deprecationReason' => null - ), + [ + 'name' => 'FRAGMENT_SPREAD', + 'isDeprecated' => false, + 'deprecationReason' => null, + ], 6 => - array ( - 'name' => 'INLINE_FRAGMENT', - 'isDeprecated' => false, - 'deprecationReason' => null - ), - ), - 'possibleTypes' => NULL, - ), - ), - 'directives' => - array ( + [ + 'name' => 'INLINE_FRAGMENT', + 'isDeprecated' => false, + 'deprecationReason' => null, + ], + ], + 'possibleTypes' => null, + ], + ], + 'directives' => + [ 0 => - array ( - 'name' => 'include', + [ + 'name' => 'include', 'locations' => - array ( + [ 0 => 'FIELD', 1 => 'FRAGMENT_SPREAD', 2 => 'INLINE_FRAGMENT', - ), - 'args' => - array ( + ], + 'args' => + [ 0 => - array ( - 'defaultValue' => NULL, - 'name' => 'if', - 'type' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'defaultValue' => null, + 'name' => 'if', + 'type' => + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( + [ 'kind' => 'SCALAR', 'name' => 'Boolean', - ), - ), - ), - ), - ), + ], + ], + ], + ], + ], 1 => - array ( - 'name' => 'skip', + [ + 'name' => 'skip', 'locations' => - array ( + [ 0 => 'FIELD', 1 => 'FRAGMENT_SPREAD', 2 => 'INLINE_FRAGMENT', - ), - 'args' => - array ( + ], + 'args' => + [ 0 => - array ( - 'defaultValue' => NULL, - 'name' => 'if', - 'type' => - array ( - 'kind' => 'NON_NULL', - 'name' => NULL, + [ + 'defaultValue' => null, + 'name' => 'if', + 'type' => + [ + 'kind' => 'NON_NULL', + 'name' => null, 'ofType' => - array ( + [ 'kind' => 'SCALAR', 'name' => 'Boolean', - ), - ), - ), - ), - ), - ), - ), - ) - ); + ], + ], + ], + ], + ], + ], + ], + ], + ]; $actual = GraphQL::executeQuery($emptySchema, $request)->toArray(); @@ -1072,31 +1032,31 @@ class IntrospectionTest extends TestCase /** * @see it('introspects on input object') */ - function testIntrospectsOnInputObject() + public function testIntrospectsOnInputObject() : void { $TestInputObject = new InputObjectType([ - 'name' => 'TestInputObject', + 'name' => 'TestInputObject', 'fields' => [ 'a' => ['type' => Type::string(), 'defaultValue' => "tes\t de\fault"], 'b' => ['type' => Type::listOf(Type::string())], - 'c' => ['type' => Type::string(), 'defaultValue' => null ] - ] + 'c' => ['type' => Type::string(), 'defaultValue' => null], + ], ]); $TestType = new ObjectType([ - 'name' => 'TestType', + 'name' => 'TestType', 'fields' => [ 'field' => [ - 'type' => Type::string(), - 'args' => ['complex' => ['type' => $TestInputObject]], + 'type' => Type::string(), + 'args' => ['complex' => ['type' => $TestInputObject]], 'resolve' => function ($_, $args) { return json_encode($args['complex']); - } - ] - ] + }, + ], + ], ]); - $schema = new Schema(['query' => $TestType]); + $schema = new Schema(['query' => $TestType]); $request = ' { __type(name: "TestInputObject") { @@ -1128,39 +1088,38 @@ class IntrospectionTest extends TestCase } '; - $expectedFragment = [ - 'kind' => 'INPUT_OBJECT', - 'name' => 'TestInputObject', + 'kind' => 'INPUT_OBJECT', + 'name' => 'TestInputObject', 'inputFields' => [ [ - 'name' => 'a', - 'type' => [ - 'kind' => 'SCALAR', - 'name' => 'String', - 'ofType' => null + 'name' => 'a', + 'type' => [ + 'kind' => 'SCALAR', + 'name' => 'String', + 'ofType' => null, ], - 'defaultValue' => '"tes\t de\fault"' + 'defaultValue' => '"tes\t de\fault"', ], [ - 'name' => 'b', - 'type' => [ - 'kind' => 'LIST', - 'name' => null, - 'ofType' => ['kind' => 'SCALAR', 'name' => 'String', 'ofType' => null] + 'name' => 'b', + 'type' => [ + 'kind' => 'LIST', + 'name' => null, + 'ofType' => ['kind' => 'SCALAR', 'name' => 'String', 'ofType' => null], ], - 'defaultValue' => null + 'defaultValue' => null, ], [ - 'name' => 'c', - 'type' => [ - 'kind' => 'SCALAR', - 'name' => 'String', - 'ofType' => null + 'name' => 'c', + 'type' => [ + 'kind' => 'SCALAR', + 'name' => 'String', + 'ofType' => null, ], - 'defaultValue' => 'null' // defaultValue was set (even if it was set to null) - ] - ] + 'defaultValue' => 'null',// defaultValue was set (even if it was set to null) + ], + ], ]; $result = GraphQL::executeQuery($schema, $request)->toArray(); @@ -1174,15 +1133,15 @@ class IntrospectionTest extends TestCase public function testSupportsTheTypeRootField() : void { $TestType = new ObjectType([ - 'name' => 'TestType', + 'name' => 'TestType', 'fields' => [ 'testField' => [ 'type' => Type::string(), - ] - ] + ], + ], ]); - $schema = new Schema(['query' => $TestType]); + $schema = new Schema(['query' => $TestType]); $request = ' { __type(name: "TestType") { @@ -1191,11 +1150,11 @@ class IntrospectionTest extends TestCase } '; - $expected = ['data' => [ - '__type' => [ - 'name' => 'TestType' - ] - ]]; + $expected = [ + 'data' => [ + '__type' => ['name' => 'TestType'], + ], + ]; $this->assertEquals($expected, GraphQL::executeQuery($schema, $request)->toArray()); } @@ -1205,21 +1164,20 @@ class IntrospectionTest extends TestCase */ public function testIdentifiesDeprecatedFields() : void { - $TestType = new ObjectType([ - 'name' => 'TestType', + 'name' => 'TestType', 'fields' => [ 'nonDeprecated' => [ 'type' => Type::string(), ], - 'deprecated' => [ - 'type' => Type::string(), - 'deprecationReason' => 'Removed in 1.0' - ] - ] + 'deprecated' => [ + 'type' => Type::string(), + 'deprecationReason' => 'Removed in 1.0', + ], + ], ]); - $schema = new Schema(['query' => $TestType]); + $schema = new Schema(['query' => $TestType]); $request = ' { __type(name: "TestType") { @@ -1236,21 +1194,21 @@ class IntrospectionTest extends TestCase $expected = [ 'data' => [ '__type' => [ - 'name' => 'TestType', + 'name' => 'TestType', 'fields' => [ [ - 'name' => 'nonDeprecated', - 'isDeprecated' => false, - 'deprecationReason' => null + 'name' => 'nonDeprecated', + 'isDeprecated' => false, + 'deprecationReason' => null, ], [ - 'name' => 'deprecated', - 'isDeprecated' => true, - 'deprecationReason' => 'Removed in 1.0' - ] - ] - ] - ] + 'name' => 'deprecated', + 'isDeprecated' => true, + 'deprecationReason' => 'Removed in 1.0', + ], + ], + ], + ], ]; $this->assertEquals($expected, GraphQL::executeQuery($schema, $request)->toArray()); } @@ -1261,19 +1219,19 @@ class IntrospectionTest extends TestCase public function testRespectsTheIncludeDeprecatedParameterForFields() : void { $TestType = new ObjectType([ - 'name' => 'TestType', + 'name' => 'TestType', 'fields' => [ 'nonDeprecated' => [ 'type' => Type::string(), ], - 'deprecated' => [ - 'type' => Type::string(), - 'deprecationReason' => 'Removed in 1.0' - ] - ] + 'deprecated' => [ + 'type' => Type::string(), + 'deprecationReason' => 'Removed in 1.0', + ], + ], ]); - $schema = new Schema(['query' => $TestType]); + $schema = new Schema(['query' => $TestType]); $request = ' { __type(name: "TestType") { @@ -1294,27 +1252,19 @@ class IntrospectionTest extends TestCase $expected = [ 'data' => [ '__type' => [ - 'name' => 'TestType', - 'trueFields' => [ - [ - 'name' => 'nonDeprecated', - ], - [ - 'name' => 'deprecated', - ] + 'name' => 'TestType', + 'trueFields' => [ + ['name' => 'nonDeprecated'], + ['name' => 'deprecated'], ], - 'falseFields' => [ - [ - 'name' => 'nonDeprecated', - ] + 'falseFields' => [ + ['name' => 'nonDeprecated'], ], 'omittedFields' => [ - [ - 'name' => 'nonDeprecated', - ] + ['name' => 'nonDeprecated'], ], - ] - ] + ], + ], ]; $this->assertEquals($expected, GraphQL::executeQuery($schema, $request)->toArray()); @@ -1326,24 +1276,22 @@ class IntrospectionTest extends TestCase public function testIdentifiesDeprecatedEnumValues() : void { $TestEnum = new EnumType([ - 'name' => 'TestEnum', + 'name' => 'TestEnum', 'values' => [ - 'NONDEPRECATED' => ['value' => 0], - 'DEPRECATED' => ['value' => 1, 'deprecationReason' => 'Removed in 1.0'], - 'ALSONONDEPRECATED' => ['value' => 2] - ] + 'NONDEPRECATED' => ['value' => 0], + 'DEPRECATED' => ['value' => 1, 'deprecationReason' => 'Removed in 1.0'], + 'ALSONONDEPRECATED' => ['value' => 2], + ], ]); $TestType = new ObjectType([ - 'name' => 'TestType', + 'name' => 'TestType', 'fields' => [ - 'testEnum' => [ - 'type' => $TestEnum, - ], - ] + 'testEnum' => ['type' => $TestEnum], + ], ]); - $schema = new Schema(['query' => $TestType]); + $schema = new Schema(['query' => $TestType]); $request = ' { __type(name: "TestEnum") { @@ -1360,26 +1308,26 @@ class IntrospectionTest extends TestCase $expected = [ 'data' => [ '__type' => [ - 'name' => 'TestEnum', + 'name' => 'TestEnum', 'enumValues' => [ [ - 'name' => 'NONDEPRECATED', - 'isDeprecated' => false, - 'deprecationReason' => null + 'name' => 'NONDEPRECATED', + 'isDeprecated' => false, + 'deprecationReason' => null, ], [ - 'name' => 'DEPRECATED', - 'isDeprecated' => true, - 'deprecationReason' => 'Removed in 1.0' + 'name' => 'DEPRECATED', + 'isDeprecated' => true, + 'deprecationReason' => 'Removed in 1.0', ], [ - 'name' => 'ALSONONDEPRECATED', - 'isDeprecated' => false, - 'deprecationReason' => null - ] - ] - ] - ] + 'name' => 'ALSONONDEPRECATED', + 'isDeprecated' => false, + 'deprecationReason' => null, + ], + ], + ], + ], ]; $this->assertEquals($expected, GraphQL::executeQuery($schema, $request)->toArray()); } @@ -1390,25 +1338,23 @@ class IntrospectionTest extends TestCase public function testRespectsTheIncludeDeprecatedParameterForEnumValues() : void { $TestEnum = new EnumType([ - 'name' => 'TestEnum', + 'name' => 'TestEnum', 'values' => [ - 'NONDEPRECATED' => ['value' => 0], - 'DEPRECATED' => ['value' => 1, 'deprecationReason' => 'Removed in 1.0'], - 'ALSONONDEPRECATED' => ['value' => 2] - ] + 'NONDEPRECATED' => ['value' => 0], + 'DEPRECATED' => ['value' => 1, 'deprecationReason' => 'Removed in 1.0'], + 'ALSONONDEPRECATED' => ['value' => 2], + ], ]); $TestType = new ObjectType([ - 'name' => 'TestType', + 'name' => 'TestType', 'fields' => [ - 'testEnum' => [ - 'type' => $TestEnum, - ], - ] + 'testEnum' => ['type' => $TestEnum], + ], ]); - $schema = new Schema(['query' => $TestType]); - $request = ' + $schema = new Schema(['query' => $TestType]); + $request = ' { __type(name: "TestEnum") { name @@ -1427,22 +1373,22 @@ class IntrospectionTest extends TestCase $expected = [ 'data' => [ '__type' => [ - 'name' => 'TestEnum', - 'trueValues' => [ + 'name' => 'TestEnum', + 'trueValues' => [ ['name' => 'NONDEPRECATED'], ['name' => 'DEPRECATED'], - ['name' => 'ALSONONDEPRECATED'] + ['name' => 'ALSONONDEPRECATED'], ], - 'falseValues' => [ + 'falseValues' => [ ['name' => 'NONDEPRECATED'], - ['name' => 'ALSONONDEPRECATED'] + ['name' => 'ALSONONDEPRECATED'], ], 'omittedValues' => [ ['name' => 'NONDEPRECATED'], - ['name' => 'ALSONONDEPRECATED'] + ['name' => 'ALSONONDEPRECATED'], ], - ] - ] + ], + ], ]; $this->assertEquals($expected, GraphQL::executeQuery($schema, $request)->toArray()); } @@ -1453,16 +1399,16 @@ class IntrospectionTest extends TestCase public function testFailsAsExpectedOnTheTypeRootFieldWithoutAnArg() : void { $TestType = new ObjectType([ - 'name' => 'TestType', + 'name' => 'TestType', 'fields' => [ 'testField' => [ 'type' => Type::string(), - ] - ] + ], + ], ]); - $schema = new Schema(['query' => $TestType]); - $request = ' + $schema = new Schema(['query' => $TestType]); + $request = ' { __type { name @@ -1472,9 +1418,10 @@ class IntrospectionTest extends TestCase $expected = [ 'errors' => [ FormattedError::create( - ProvidedNonNullArguments::missingFieldArgMessage('__type', 'name', 'String!'), [new SourceLocation(3, 9)] - ) - ] + ProvidedNonNullArguments::missingFieldArgMessage('__type', 'name', 'String!'), + [new SourceLocation(3, 9)] + ), + ], ]; $this->assertArraySubset($expected, GraphQL::executeQuery($schema, $request)->toArray()); } @@ -1485,12 +1432,12 @@ class IntrospectionTest extends TestCase public function testExposesDescriptionsOnTypesAndFields() : void { $QueryRoot = new ObjectType([ - 'name' => 'QueryRoot', - 'fields' => ['a' => Type::string()] + 'name' => 'QueryRoot', + 'fields' => ['a' => Type::string()], ]); - $schema = new Schema(['query' => $QueryRoot]); - $request = ' + $schema = new Schema(['query' => $QueryRoot]); + $request = ' { schemaType: __type(name: "__Schema") { name, @@ -1505,36 +1452,36 @@ class IntrospectionTest extends TestCase $expected = [ 'data' => [ 'schemaType' => [ - 'name' => '__Schema', + 'name' => '__Schema', 'description' => 'A GraphQL Schema defines the capabilities of a ' . 'GraphQL server. It exposes all available types and ' . 'directives on the server, as well as the entry ' . 'points for query, mutation, and subscription operations.', - 'fields' => [ + 'fields' => [ [ - 'name' => 'types', - 'description' => 'A list of all types supported by this server.' + 'name' => 'types', + 'description' => 'A list of all types supported by this server.', ], [ - 'name' => 'queryType', - 'description' => 'The type that query operations will be rooted at.' + 'name' => 'queryType', + 'description' => 'The type that query operations will be rooted at.', ], [ - 'name' => 'mutationType', + 'name' => 'mutationType', 'description' => 'If this server supports mutation, the type that ' . - 'mutation operations will be rooted at.' + 'mutation operations will be rooted at.', ], [ - 'name' => 'subscriptionType', - 'description' => 'If this server support subscription, the type that subscription operations will be rooted at.' + 'name' => 'subscriptionType', + 'description' => 'If this server support subscription, the type that subscription operations will be rooted at.', ], [ - 'name' => 'directives', - 'description' => 'A list of all directives supported by this server.' - ] - ] - ] - ] + 'name' => 'directives', + 'description' => 'A list of all directives supported by this server.', + ], + ], + ], + ], ]; $this->assertEquals($expected, GraphQL::executeQuery($schema, $request)->toArray()); } @@ -1545,12 +1492,12 @@ class IntrospectionTest extends TestCase public function testExposesDescriptionsOnEnums() : void { $QueryRoot = new ObjectType([ - 'name' => 'QueryRoot', - 'fields' => ['a' => Type::string()] + 'name' => 'QueryRoot', + 'fields' => ['a' => Type::string()], ]); - $schema = new Schema(['query' => $QueryRoot]); - $request = ' + $schema = new Schema(['query' => $QueryRoot]); + $request = ' { typeKindType: __type(name: "__TypeKind") { name, @@ -1565,51 +1512,51 @@ class IntrospectionTest extends TestCase $expected = [ 'data' => [ 'typeKindType' => [ - 'name' => '__TypeKind', + 'name' => '__TypeKind', 'description' => 'An enum describing what kind of type a given `__Type` is.', - 'enumValues' => [ + 'enumValues' => [ [ 'description' => 'Indicates this type is a scalar.', - 'name' => 'SCALAR' + 'name' => 'SCALAR', ], [ 'description' => 'Indicates this type is an object. ' . '`fields` and `interfaces` are valid fields.', - 'name' => 'OBJECT' + 'name' => 'OBJECT', ], [ 'description' => 'Indicates this type is an interface. ' . '`fields` and `possibleTypes` are valid fields.', - 'name' => 'INTERFACE' + 'name' => 'INTERFACE', ], [ 'description' => 'Indicates this type is a union. ' . '`possibleTypes` is a valid field.', - 'name' => 'UNION' + 'name' => 'UNION', ], [ 'description' => 'Indicates this type is an enum. ' . '`enumValues` is a valid field.', - 'name' => 'ENUM' + 'name' => 'ENUM', ], [ 'description' => 'Indicates this type is an input object. ' . '`inputFields` is a valid field.', - 'name' => 'INPUT_OBJECT' + 'name' => 'INPUT_OBJECT', ], [ 'description' => 'Indicates this type is a list. ' . '`ofType` is a valid field.', - 'name' => 'LIST' + 'name' => 'LIST', ], [ 'description' => 'Indicates this type is a non-null. ' . '`ofType` is a valid field.', - 'name' => 'NON_NULL' - ] - ] - ] - ] + 'name' => 'NON_NULL', + ], + ], + ], + ], ]; $this->assertEquals($expected, GraphQL::executeQuery($schema, $request)->toArray()); diff --git a/tests/Type/ObjectIdStub.php b/tests/Type/ObjectIdStub.php index b0453ab..3049569 100644 --- a/tests/Type/ObjectIdStub.php +++ b/tests/Type/ObjectIdStub.php @@ -1,11 +1,12 @@ node = new InterfaceType([ - 'name' => 'Node', + 'name' => 'Node', 'fields' => [ - 'id' => Type::string() - ] + 'id' => Type::string(), + ], ]); $this->content = new InterfaceType([ - 'name' => 'Content', - 'fields' => function() { + 'name' => 'Content', + 'fields' => function () { return [ - 'title' => Type::string(), - 'body' => Type::string(), - 'author' => $this->user, - 'comments' => Type::listOf($this->comment), - 'categories' => Type::listOf($this->category) + 'title' => Type::string(), + 'body' => Type::string(), + 'author' => $this->user, + 'comments' => Type::listOf($this->comment), + 'categories' => Type::listOf($this->category), ]; - } + }, ]); $this->blogStory = new ObjectType([ - 'name' => 'BlogStory', + 'name' => 'BlogStory', 'interfaces' => [ $this->node, - $this->content + $this->content, ], - 'fields' => function() { - return [ - $this->node->getField('id'), - $this->content->getField('title'), - $this->content->getField('body'), - $this->content->getField('author'), - $this->content->getField('comments'), - $this->content->getField('categories') - ]; - }, - ]); - - $this->link = new ObjectType([ - 'name' => 'Link', - 'interfaces' => [ - $this->node, - $this->content - ], - 'fields' => function() { + 'fields' => function () { return [ $this->node->getField('id'), $this->content->getField('title'), @@ -135,143 +98,166 @@ class ResolutionTest extends TestCase $this->content->getField('author'), $this->content->getField('comments'), $this->content->getField('categories'), - 'url' => Type::string() ]; }, ]); + new ObjectType([ + 'name' => 'Link', + 'interfaces' => [ + $this->node, + $this->content, + ], + 'fields' => function () { + return [ + 'id' => $this->node->getField('id'), + 'title' => $this->content->getField('title'), + 'body' => $this->content->getField('body'), + 'author' => $this->content->getField('author'), + 'comments' => $this->content->getField('comments'), + 'categories' => $this->content->getField('categories'), + 'url' => Type::string(), + ]; + }, + + ]); + + $this->videoMetadata = new ObjectType([ + 'name' => 'VideoMetadata', + 'fields' => [ + 'lat' => Type::float(), + 'lng' => Type::float(), + ], + ]); + $this->video = new ObjectType([ - 'name' => 'Video', + 'name' => 'Video', 'interfaces' => [ $this->node, - $this->content + $this->content, ], - 'fields' => function() { + 'fields' => function () { return [ - $this->node->getField('id'), - $this->content->getField('title'), - $this->content->getField('body'), - $this->content->getField('author'), - $this->content->getField('comments'), - $this->content->getField('categories'), - 'streamUrl' => Type::string(), + 'id' => $this->node->getField('id'), + 'title' => $this->content->getField('title'), + 'body' => $this->content->getField('body'), + 'author' => $this->content->getField('author'), + 'comments' => $this->content->getField('comments'), + 'categories' => $this->content->getField('categories'), + 'streamUrl' => Type::string(), 'downloadUrl' => Type::string(), - 'metadata' => $this->videoMetadata = new ObjectType([ - 'name' => 'VideoMetadata', - 'fields' => [ - 'lat' => Type::float(), - 'lng' => Type::float() - ] - ]) + 'metadata' => $this->videoMetadata, ]; - } + }, ]); $this->comment = new ObjectType([ - 'name' => 'Comment', + 'name' => 'Comment', 'interfaces' => [ - $this->node + $this->node, ], - 'fields' => function() { + 'fields' => function () { return [ - $this->node->getField('id'), - 'author' => $this->user, - 'text' => Type::string(), + 'id' => $this->node->getField('id'), + 'author' => $this->user, + 'text' => Type::string(), 'replies' => Type::listOf($this->comment), - 'parent' => $this->comment, - 'content' => $this->content + 'parent' => $this->comment, + 'content' => $this->content, ]; - } + }, ]); $this->user = new ObjectType([ - 'name' => 'User', + 'name' => 'User', 'interfaces' => [ - $this->node + $this->node, ], - 'fields' => function() { + 'fields' => function () { return [ - $this->node->getField('id'), + 'id' => $this->node->getField('id'), 'name' => Type::string(), ]; - } + }, ]); $this->category = new ObjectType([ - 'name' => 'Category', + 'name' => 'Category', 'interfaces' => [ - $this->node + $this->node, ], - 'fields' => function() { + 'fields' => function () { return [ - $this->node->getField('id'), - 'name' => Type::string() + 'id' => $this->node->getField('id'), + 'name' => Type::string(), ]; - } + }, ]); $this->mention = new UnionType([ - 'name' => 'Mention', + 'name' => 'Mention', 'types' => [ $this->user, - $this->category - ] + $this->category, + ], ]); $this->query = new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ - 'viewer' => $this->user, + 'viewer' => $this->user, 'latestContent' => $this->content, - 'node' => $this->node, - 'mentions' => Type::listOf($this->mention) - ] + 'node' => $this->node, + 'mentions' => Type::listOf($this->mention), + ], + ]); + + $this->postStoryMutationInput = new InputObjectType([ + 'name' => 'PostStoryMutationInput', + 'fields' => [ + 'title' => Type::string(), + 'body' => Type::string(), + 'author' => Type::id(), + 'category' => Type::id(), + ], ]); $this->mutation = new ObjectType([ - 'name' => 'Mutation', + 'name' => 'Mutation', 'fields' => [ - 'postStory' => [ + 'postStory' => [ 'type' => $this->postStoryMutation = new ObjectType([ - 'name' => 'PostStoryMutation', + 'name' => 'PostStoryMutation', 'fields' => [ - 'story' => $this->blogStory - ] + 'story' => $this->blogStory, + ], ]), 'args' => [ - 'input' => Type::nonNull($this->postStoryMutationInput = new InputObjectType([ - 'name' => 'PostStoryMutationInput', - 'fields' => [ - 'title' => Type::string(), - 'body' => Type::string(), - 'author' => Type::id(), - 'category' => Type::id() - ] - ])), - 'clientRequestId' => Type::string() - ] + 'input' => Type::nonNull($this->postStoryMutationInput), + 'clientRequestId' => Type::string(), + ], ], 'postComment' => [ 'type' => $this->postCommentMutation = new ObjectType([ - 'name' => 'PostCommentMutation', + 'name' => 'PostCommentMutation', 'fields' => [ - 'comment' => $this->comment - ] + 'comment' => $this->comment, + ], ]), 'args' => [ - 'input' => Type::nonNull($this->postCommentMutationInput = new InputObjectType([ - 'name' => 'PostCommentMutationInput', + 'input' => Type::nonNull($this->postCommentMutationInput = new InputObjectType([ + 'name' => 'PostCommentMutationInput', 'fields' => [ - 'text' => Type::nonNull(Type::string()), - 'author' => Type::nonNull(Type::id()), + 'text' => Type::nonNull(Type::string()), + 'author' => Type::nonNull(Type::id()), 'content' => Type::id(), - 'parent' => Type::id() - ] + 'parent' => Type::id(), + ], ])), - 'clientRequestId' => Type::string() - ] - ] - ] + 'clientRequestId' => Type::string(), + ], + ], + ], ]); } @@ -279,29 +265,29 @@ class ResolutionTest extends TestCase { // Has internal types by default: $eagerTypeResolution = new EagerResolution([]); - $expectedTypeMap = [ - 'ID' => Type::id(), - 'String' => Type::string(), - 'Float' => Type::float(), - 'Int' => Type::int(), - 'Boolean' => Type::boolean() + $expectedTypeMap = [ + 'ID' => Type::id(), + 'String' => Type::string(), + 'Float' => Type::float(), + 'Int' => Type::int(), + 'Boolean' => Type::boolean(), ]; $this->assertEquals($expectedTypeMap, $eagerTypeResolution->getTypeMap()); $expectedDescriptor = [ - 'version' => '1.0', - 'typeMap' => [ - 'ID' => 1, - 'String' => 1, - 'Float' => 1, - 'Int' => 1, + 'version' => '1.0', + 'typeMap' => [ + 'ID' => 1, + 'String' => 1, + 'Float' => 1, + 'Int' => 1, 'Boolean' => 1, ], - 'possibleTypeMap' => [] + 'possibleTypeMap' => [], ]; $this->assertEquals($expectedDescriptor, $eagerTypeResolution->getDescriptor()); - $this->assertSame(null, $eagerTypeResolution->resolveType('User')); + $this->assertNull($eagerTypeResolution->resolveType('User')); $this->assertSame([], $eagerTypeResolution->resolvePossibleTypes($this->node)); $this->assertSame([], $eagerTypeResolution->resolvePossibleTypes($this->content)); $this->assertSame([], $eagerTypeResolution->resolvePossibleTypes($this->mention)); @@ -321,72 +307,76 @@ class ResolutionTest extends TestCase $this->assertSame($this->postStoryMutation, $eagerTypeResolution->resolveType('PostStoryMutation')); $this->assertSame($this->postStoryMutationInput, $eagerTypeResolution->resolveType('PostStoryMutationInput')); $this->assertSame($this->postCommentMutation, $eagerTypeResolution->resolveType('PostCommentMutation')); - $this->assertSame($this->postCommentMutationInput, $eagerTypeResolution->resolveType('PostCommentMutationInput')); + $this->assertSame( + $this->postCommentMutationInput, + $eagerTypeResolution->resolveType('PostCommentMutationInput') + ); $this->assertEquals([$this->blogStory], $eagerTypeResolution->resolvePossibleTypes($this->content)); - $this->assertEquals([$this->user, $this->comment, $this->category, $this->blogStory], $eagerTypeResolution->resolvePossibleTypes($this->node)); + $this->assertEquals( + [$this->user, $this->comment, $this->category, $this->blogStory], + $eagerTypeResolution->resolvePossibleTypes($this->node) + ); $this->assertEquals([$this->user, $this->category], $eagerTypeResolution->resolvePossibleTypes($this->mention)); $expectedTypeMap = [ - 'Query' => $this->query, - 'Mutation' => $this->mutation, - 'User' => $this->user, - 'Node' => $this->node, - 'String' => Type::string(), - 'Content' => $this->content, - 'Comment' => $this->comment, - 'Mention' => $this->mention, - 'BlogStory' => $this->blogStory, - 'Category' => $this->category, - 'PostStoryMutationInput' => $this->postStoryMutationInput, - 'ID' => Type::id(), - 'PostStoryMutation' => $this->postStoryMutation, + 'Query' => $this->query, + 'Mutation' => $this->mutation, + 'User' => $this->user, + 'Node' => $this->node, + 'String' => Type::string(), + 'Content' => $this->content, + 'Comment' => $this->comment, + 'Mention' => $this->mention, + 'BlogStory' => $this->blogStory, + 'Category' => $this->category, + 'PostStoryMutationInput' => $this->postStoryMutationInput, + 'ID' => Type::id(), + 'PostStoryMutation' => $this->postStoryMutation, 'PostCommentMutationInput' => $this->postCommentMutationInput, - 'PostCommentMutation' => $this->postCommentMutation, - 'Float' => Type::float(), - 'Int' => Type::int(), - 'Boolean' => Type::boolean() + 'PostCommentMutation' => $this->postCommentMutation, + 'Float' => Type::float(), + 'Int' => Type::int(), + 'Boolean' => Type::boolean(), ]; $this->assertEquals($expectedTypeMap, $eagerTypeResolution->getTypeMap()); $expectedDescriptor = [ - 'version' => '1.0', - 'typeMap' => [ - 'Query' => 1, - 'Mutation' => 1, - 'User' => 1, - 'Node' => 1, - 'String' => 1, - 'Content' => 1, - 'Comment' => 1, - 'Mention' => 1, - 'BlogStory' => 1, - 'Category' => 1, - 'PostStoryMutationInput' => 1, - 'ID' => 1, - 'PostStoryMutation' => 1, + 'version' => '1.0', + 'typeMap' => [ + 'Query' => 1, + 'Mutation' => 1, + 'User' => 1, + 'Node' => 1, + 'String' => 1, + 'Content' => 1, + 'Comment' => 1, + 'Mention' => 1, + 'BlogStory' => 1, + 'Category' => 1, + 'PostStoryMutationInput' => 1, + 'ID' => 1, + 'PostStoryMutation' => 1, 'PostCommentMutationInput' => 1, - 'PostCommentMutation' => 1, - 'Float' => 1, - 'Int' => 1, - 'Boolean' => 1 + 'PostCommentMutation' => 1, + 'Float' => 1, + 'Int' => 1, + 'Boolean' => 1, ], 'possibleTypeMap' => [ - 'Node' => [ - 'User' => 1, - 'Comment' => 1, - 'Category' => 1, - 'BlogStory' => 1 - ], - 'Content' => [ - 'BlogStory' => 1 + 'Node' => [ + 'User' => 1, + 'Comment' => 1, + 'Category' => 1, + 'BlogStory' => 1, ], + 'Content' => ['BlogStory' => 1], 'Mention' => [ - 'User' => 1, - 'Category' => 1 - ] - ] + 'User' => 1, + 'Category' => 1, + ], + ], ]; $this->assertEquals($expectedDescriptor, $eagerTypeResolution->getDescriptor()); @@ -402,7 +392,10 @@ class ResolutionTest extends TestCase $this->assertEquals(null, $eagerTypeResolution->resolveType('VideoMetadata')); $this->assertEquals([$this->blogStory], $eagerTypeResolution->resolvePossibleTypes($this->content)); - $this->assertEquals([$this->user, $this->comment, $this->category, $this->blogStory], $eagerTypeResolution->resolvePossibleTypes($this->node)); + $this->assertEquals( + [$this->user, $this->comment, $this->category, $this->blogStory], + $eagerTypeResolution->resolvePossibleTypes($this->node) + ); $this->assertEquals([$this->user, $this->category], $eagerTypeResolution->resolvePossibleTypes($this->mention)); $eagerTypeResolution = new EagerResolution([null, $this->video, null]); @@ -410,52 +403,53 @@ class ResolutionTest extends TestCase $this->assertEquals($this->video, $eagerTypeResolution->resolveType('Video')); $this->assertEquals([$this->video], $eagerTypeResolution->resolvePossibleTypes($this->content)); - $this->assertEquals([$this->video, $this->user, $this->comment, $this->category], $eagerTypeResolution->resolvePossibleTypes($this->node)); + $this->assertEquals( + [$this->video, $this->user, $this->comment, $this->category], + $eagerTypeResolution->resolvePossibleTypes($this->node) + ); $this->assertEquals([], $eagerTypeResolution->resolvePossibleTypes($this->mention)); $expectedTypeMap = [ - 'Video' => $this->video, - 'Node' => $this->node, - 'String' => Type::string(), - 'Content' => $this->content, - 'User' => $this->user, - 'Comment' => $this->comment, - 'Category' => $this->category, + 'Video' => $this->video, + 'Node' => $this->node, + 'String' => Type::string(), + 'Content' => $this->content, + 'User' => $this->user, + 'Comment' => $this->comment, + 'Category' => $this->category, 'VideoMetadata' => $this->videoMetadata, - 'Float' => Type::float(), - 'ID' => Type::id(), - 'Int' => Type::int(), - 'Boolean' => Type::boolean() + 'Float' => Type::float(), + 'ID' => Type::id(), + 'Int' => Type::int(), + 'Boolean' => Type::boolean(), ]; $this->assertEquals($expectedTypeMap, $eagerTypeResolution->getTypeMap()); $expectedDescriptor = [ - 'version' => '1.0', - 'typeMap' => [ - 'Video' => 1, - 'Node' => 1, - 'String' => 1, - 'Content' => 1, - 'User' => 1, - 'Comment' => 1, - 'Category' => 1, + 'version' => '1.0', + 'typeMap' => [ + 'Video' => 1, + 'Node' => 1, + 'String' => 1, + 'Content' => 1, + 'User' => 1, + 'Comment' => 1, + 'Category' => 1, 'VideoMetadata' => 1, - 'Float' => 1, - 'ID' => 1, - 'Int' => 1, - 'Boolean' => 1 + 'Float' => 1, + 'ID' => 1, + 'Int' => 1, + 'Boolean' => 1, ], 'possibleTypeMap' => [ - 'Node' => [ - 'Video' => 1, - 'User' => 1, - 'Comment' => 1, - 'Category' => 1 + 'Node' => [ + 'Video' => 1, + 'User' => 1, + 'Comment' => 1, + 'Category' => 1, ], - 'Content' => [ - 'Video' => 1 - ] - ] + 'Content' => ['Video' => 1], + ], ]; $this->assertEquals($expectedDescriptor, $eagerTypeResolution->getDescriptor()); } @@ -463,11 +457,11 @@ class ResolutionTest extends TestCase public function testLazyResolutionFollowsEagerResolution() : void { // Lazy resolution should work the same way as eager resolution works, except that it should load types on demand - $eager = new EagerResolution([]); + $eager = new EagerResolution([]); $emptyDescriptor = $eager->getDescriptor(); - $typeLoader = function($name) { - throw new \Exception("This should be never called for empty descriptor"); + $typeLoader = function ($name) { + throw new \Exception('This should be never called for empty descriptor'); }; $lazy = new LazyResolution($emptyDescriptor, $typeLoader); @@ -478,11 +472,12 @@ class ResolutionTest extends TestCase $eager = new EagerResolution([$this->query, $this->mutation]); - $called = 0; + $called = 0; $descriptor = $eager->getDescriptor(); - $typeLoader = function($name) use (&$called) { + $typeLoader = function ($name) use (&$called) { $called++; $prop = lcfirst($name); + return $this->{$prop}; }; @@ -507,7 +502,10 @@ class ResolutionTest extends TestCase $this->assertSame($eager->resolveType('PostStoryMutation'), $lazy->resolveType('PostStoryMutation')); $this->assertSame($eager->resolveType('PostStoryMutationInput'), $lazy->resolveType('PostStoryMutationInput')); $this->assertSame($eager->resolveType('PostCommentMutation'), $lazy->resolveType('PostCommentMutation')); - $this->assertSame($eager->resolveType('PostCommentMutationInput'), $lazy->resolveType('PostCommentMutationInput')); + $this->assertSame( + $eager->resolveType('PostCommentMutationInput'), + $lazy->resolveType('PostCommentMutationInput') + ); $this->assertSame(13, $called); $this->assertEquals($eager->resolvePossibleTypes($this->content), $lazy->resolvePossibleTypes($this->content)); @@ -515,8 +513,8 @@ class ResolutionTest extends TestCase $this->assertEquals($eager->resolvePossibleTypes($this->mention), $lazy->resolvePossibleTypes($this->mention)); $called = 0; - $eager = new EagerResolution([$this->video]); - $lazy = new LazyResolution($eager->getDescriptor(), $typeLoader); + $eager = new EagerResolution([$this->video]); + $lazy = new LazyResolution($eager->getDescriptor(), $typeLoader); $this->assertEquals($eager->resolveType('VideoMetadata'), $lazy->resolveType('VideoMetadata')); $this->assertEquals($eager->resolveType('Video'), $lazy->resolveType('Video')); @@ -527,40 +525,6 @@ class ResolutionTest extends TestCase $this->assertEquals($eager->resolvePossibleTypes($this->mention), $lazy->resolvePossibleTypes($this->mention)); } - private function createLazy(){ - - $descriptor = [ - 'version' => '1.0', - 'typeMap' => [ - 'null' => 1, - 'int' => 1 - ], - 'possibleTypeMap' => [ - 'a' => [ - 'null' => 1, - ], - 'b' => [ - 'int' => 1 - ] - ] - ]; - - $invalidTypeLoader = function($name) { - switch ($name) { - case 'null': - return null; - case 'int': - return 7; - } - }; - - $lazy = new LazyResolution($descriptor, $invalidTypeLoader); - $value = $lazy->resolveType('null'); - $this->assertEquals(null, $value); - - return $lazy; - } - public function testLazyThrowsOnInvalidLoadedType() : void { $lazy = $this->createLazy(); @@ -569,9 +533,39 @@ class ResolutionTest extends TestCase $lazy->resolveType('int'); } + private function createLazy() + { + $descriptor = [ + 'version' => '1.0', + 'typeMap' => [ + 'null' => 1, + 'int' => 1, + ], + 'possibleTypeMap' => [ + 'a' => ['null' => 1], + 'b' => ['int' => 1], + ], + ]; + + $invalidTypeLoader = function ($name) { + switch ($name) { + case 'null': + return null; + case 'int': + return 7; + } + }; + + $lazy = new LazyResolution($descriptor, $invalidTypeLoader); + $value = $lazy->resolveType('null'); + $this->assertEquals(null, $value); + + return $lazy; + } + public function testLazyThrowsOnInvalidLoadedPossibleType() : void { - $tmp = new InterfaceType(['name' => 'a', 'fields' => []]); + $tmp = new InterfaceType(['name' => 'a', 'fields' => []]); $lazy = $this->createLazy(); $this->expectException(InvariantViolation::class); $this->expectExceptionMessage('Lazy Type Resolution Error: Implementation null of interface a is expected to be instance of ObjectType, but got NULL'); @@ -580,7 +574,7 @@ class ResolutionTest extends TestCase public function testLazyThrowsOnInvalidLoadedPossibleTypeWithInteger() : void { - $tmp = new InterfaceType(['name' => 'b', 'fields' => []]); + $tmp = new InterfaceType(['name' => 'b', 'fields' => []]); $lazy = $this->createLazy(); $this->expectException(InvariantViolation::class); $this->expectExceptionMessage('Lazy Type Resolution Error: Expecting GraphQL Type instance, but got integer'); diff --git a/tests/Type/ResolveInfoTest.php b/tests/Type/ResolveInfoTest.php index 768f555..5c5b5f1 100644 --- a/tests/Type/ResolveInfoTest.php +++ b/tests/Type/ResolveInfoTest.php @@ -1,11 +1,14 @@ 'Image', + 'name' => 'Image', 'fields' => [ - 'url' => ['type' => Type::string()], - 'width' => ['type' => Type::int()], - 'height' => ['type' => Type::int()] - ] + 'url' => ['type' => Type::string()], + 'width' => ['type' => Type::int()], + 'height' => ['type' => Type::int()], + ], ]); $article = null; $author = new ObjectType([ - 'name' => 'Author', - 'fields' => function() use ($image, &$article) { + 'name' => 'Author', + 'fields' => function () use ($image, &$article) { return [ - 'id' => ['type' => Type::string()], - 'name' => ['type' => Type::string()], - 'pic' => [ 'type' => $image, 'args' => [ - 'width' => ['type' => Type::int()], - 'height' => ['type' => Type::int()] - ]], + 'id' => ['type' => Type::string()], + 'name' => ['type' => Type::string()], + 'pic' => [ + 'type' => $image, + 'args' => [ + 'width' => ['type' => Type::int()], + 'height' => ['type' => Type::int()], + ], + ], 'recentArticle' => ['type' => $article], ]; }, ]); $reply = new ObjectType([ - 'name' => 'Reply', + 'name' => 'Reply', 'fields' => [ 'author' => ['type' => $author], - 'body' => ['type' => Type::string()] - ] + 'body' => ['type' => Type::string()], + ], ]); $article = new ObjectType([ - 'name' => 'Article', + 'name' => 'Article', 'fields' => [ - 'id' => ['type' => Type::string()], + 'id' => ['type' => Type::string()], 'isPublished' => ['type' => Type::boolean()], - 'author' => ['type' => $author], - 'title' => ['type' => Type::string()], - 'body' => ['type' => Type::string()], - 'image' => ['type' => $image], - 'replies' => ['type' => Type::listOf($reply)] - ] + 'author' => ['type' => $author], + 'title' => ['type' => Type::string()], + 'body' => ['type' => Type::string()], + 'image' => ['type' => $image], + 'replies' => ['type' => Type::listOf($reply)], + ], ]); - $doc = ' + $doc = ' query Test { article { author { @@ -100,59 +106,70 @@ class ResolveInfoTest extends TestCase } '; $expectedDefaultSelection = [ - 'author' => true, - 'image' => true, - 'replies' => true + 'author' => true, + 'image' => true, + 'replies' => true, ]; - $expectedDeepSelection = [ - 'author' => [ + $expectedDeepSelection = [ + 'author' => [ 'name' => true, - 'pic' => [ - 'url' => true, - 'width' => true - ] + 'pic' => [ + 'url' => true, + 'width' => true, + ], ], - 'image' => [ - 'width' => true, + 'image' => [ + 'width' => true, 'height' => true, - 'url' => true + 'url' => true, ], 'replies' => [ - 'body' => true, + 'body' => true, 'author' => [ - 'id' => true, - 'name' => true, - 'pic' => [ - 'url' => true, - 'width' => true, - 'height' => true + 'id' => true, + 'name' => true, + 'pic' => [ + 'url' => true, + 'width' => true, + 'height' => true, ], 'recentArticle' => [ - 'id' => true, + 'id' => true, 'title' => true, - 'body' => true - ] - ] - ] + 'body' => true, + ], + ], + ], ]; - $hasCalled = false; + $hasCalled = false; $actualDefaultSelection = null; - $actualDeepSelection = null; + $actualDeepSelection = null; $blogQuery = new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ 'article' => [ - 'type' => $article, - 'resolve' => function($value, $args, $context, ResolveInfo $info) use (&$hasCalled, &$actualDefaultSelection, &$actualDeepSelection) { - $hasCalled = true; + 'type' => $article, + 'resolve' => function ( + $value, + $args, + $context, + ResolveInfo $info + ) use ( + &$hasCalled, + & + $actualDefaultSelection, + &$actualDeepSelection + ) { + $hasCalled = true; $actualDefaultSelection = $info->getFieldSelection(); - $actualDeepSelection = $info->getFieldSelection(5); + $actualDeepSelection = $info->getFieldSelection(5); + return null; - } - ] - ] + }, + ], + ], ]); $schema = new Schema(['query' => $blogQuery]); @@ -167,50 +184,53 @@ class ResolveInfoTest extends TestCase public function testMergedFragmentsFieldSelection() : void { $image = new ObjectType([ - 'name' => 'Image', + 'name' => 'Image', 'fields' => [ - 'url' => ['type' => Type::string()], - 'width' => ['type' => Type::int()], - 'height' => ['type' => Type::int()] - ] + 'url' => ['type' => Type::string()], + 'width' => ['type' => Type::int()], + 'height' => ['type' => Type::int()], + ], ]); $article = null; $author = new ObjectType([ - 'name' => 'Author', - 'fields' => function() use ($image, &$article) { + 'name' => 'Author', + 'fields' => function () use ($image, &$article) { return [ - 'id' => ['type' => Type::string()], - 'name' => ['type' => Type::string()], - 'pic' => [ 'type' => $image, 'args' => [ - 'width' => ['type' => Type::int()], - 'height' => ['type' => Type::int()] - ]], + 'id' => ['type' => Type::string()], + 'name' => ['type' => Type::string()], + 'pic' => [ + 'type' => $image, + 'args' => [ + 'width' => ['type' => Type::int()], + 'height' => ['type' => Type::int()], + ], + ], 'recentArticle' => ['type' => $article], ]; }, ]); $reply = new ObjectType([ - 'name' => 'Reply', + 'name' => 'Reply', 'fields' => [ 'author' => ['type' => $author], - 'body' => ['type' => Type::string()] - ] + 'body' => ['type' => Type::string()], + ], ]); $article = new ObjectType([ - 'name' => 'Article', + 'name' => 'Article', 'fields' => [ - 'id' => ['type' => Type::string()], + 'id' => ['type' => Type::string()], 'isPublished' => ['type' => Type::boolean()], - 'author' => ['type' => $author], - 'title' => ['type' => Type::string()], - 'body' => ['type' => Type::string()], - 'image' => ['type' => $image], - 'replies' => ['type' => Type::listOf($reply)] - ] + 'author' => ['type' => $author], + 'title' => ['type' => Type::string()], + 'body' => ['type' => Type::string()], + 'image' => ['type' => $image], + 'replies' => ['type' => Type::listOf($reply)], + ], ]); $doc = ' @@ -264,53 +284,63 @@ class ResolveInfoTest extends TestCase '; $expectedDeepSelection = [ - 'author' => [ + 'author' => [ 'name' => true, - 'pic' => [ - 'url' => true, - 'width' => true - ] + 'pic' => [ + 'url' => true, + 'width' => true, + ], ], - 'image' => [ - 'width' => true, + 'image' => [ + 'width' => true, 'height' => true, - 'url' => true + 'url' => true, ], 'replies' => [ - 'body' => true, //this would be missing if not for the fix https://github.com/webonyx/graphql-php/pull/98 + 'body' => true, //this would be missing if not for the fix https://github.com/webonyx/graphql-php/pull/98 'author' => [ - 'id' => true, - 'name' => true, - 'pic' => [ - 'url' => true, - 'width' => true, - 'height' => true + 'id' => true, + 'name' => true, + 'pic' => [ + 'url' => true, + 'width' => true, + 'height' => true, ], 'recentArticle' => [ - 'id' => true, + 'id' => true, 'title' => true, - 'body' => true - ] - ] - ] + 'body' => true, + ], + ], + ], ]; - $hasCalled = false; + $hasCalled = false; $actualDefaultSelection = null; - $actualDeepSelection = null; + $actualDeepSelection = null; $blogQuery = new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ 'article' => [ - 'type' => $article, - 'resolve' => function($value, $args, $context, ResolveInfo $info) use (&$hasCalled, &$actualDeepSelection) { - $hasCalled = true; + 'type' => $article, + 'resolve' => function ( + $value, + $args, + $context, + ResolveInfo $info + ) use ( + &$hasCalled, + & + $actualDeepSelection + ) { + $hasCalled = true; $actualDeepSelection = $info->getFieldSelection(5); + return null; - } - ] - ] + }, + ], + ], ]); $schema = new Schema(['query' => $blogQuery]); @@ -320,6 +350,4 @@ class ResolveInfoTest extends TestCase $this->assertEquals(['data' => ['article' => null]], $result); $this->assertEquals($expectedDeepSelection, $actualDeepSelection); } - - } diff --git a/tests/Type/ScalarSerializationTest.php b/tests/Type/ScalarSerializationTest.php index e26f0a1..fce19bc 100644 --- a/tests/Type/ScalarSerializationTest.php +++ b/tests/Type/ScalarSerializationTest.php @@ -1,4 +1,7 @@ expectException(Error::class); $this->expectExceptionMessage('Int cannot represent non-integer value: 1.1'); $intType->serialize(1.1); - } public function testSerializesOutputIntCannotRepresentNegativeFloat() : void @@ -51,7 +52,6 @@ class ScalarSerializationTest extends TestCase $this->expectException(Error::class); $this->expectExceptionMessage('Int cannot represent non-integer value: -1.1'); $intType->serialize(-1.1); - } public function testSerializesOutputIntCannotRepresentNumericString() : void @@ -60,7 +60,6 @@ class ScalarSerializationTest extends TestCase $this->expectException(Error::class); $this->expectExceptionMessage('Int cannot represent non 32-bit signed integer value: Int cannot represent non-integer value: "-1.1"'); $intType->serialize('Int cannot represent non-integer value: "-1.1"'); - } public function testSerializesOutputIntCannotRepresentBiggerThan32Bits() : void @@ -71,7 +70,6 @@ class ScalarSerializationTest extends TestCase $this->expectException(Error::class); $this->expectExceptionMessage('Int cannot represent non 32-bit signed integer value: 9876504321'); $intType->serialize(9876504321); - } public function testSerializesOutputIntCannotRepresentLowerThan32Bits() : void @@ -104,7 +102,6 @@ class ScalarSerializationTest extends TestCase $this->expectException(Error::class); $this->expectExceptionMessage('Int cannot represent non 32-bit signed integer value: one'); $intType->serialize('one'); - } public function testSerializesOutputIntCannotRepresentEmptyString() : void @@ -189,14 +186,13 @@ class ScalarSerializationTest extends TestCase { $boolType = Type::boolean(); - $this->assertSame(true, $boolType->serialize('string')); - $this->assertSame(false, $boolType->serialize('')); - $this->assertSame(true, $boolType->serialize('1')); - $this->assertSame(true, $boolType->serialize(1)); - $this->assertSame(false, $boolType->serialize(0)); - $this->assertSame(true, $boolType->serialize(true)); - $this->assertSame(false, $boolType->serialize(false)); - + $this->assertTrue($boolType->serialize('string')); + $this->assertFalse($boolType->serialize('')); + $this->assertTrue($boolType->serialize('1')); + $this->assertTrue($boolType->serialize(1)); + $this->assertFalse($boolType->serialize(0)); + $this->assertTrue($boolType->serialize(true)); + $this->assertFalse($boolType->serialize(false)); // TODO: how should it behave on '0'? } diff --git a/tests/Type/SchemaTest.php b/tests/Type/SchemaTest.php index 7224622..c9db24f 100644 --- a/tests/Type/SchemaTest.php +++ b/tests/Type/SchemaTest.php @@ -1,4 +1,7 @@ interfaceType = new InterfaceType([ - 'name' => 'Interface', + 'name' => 'Interface', 'fields' => ['fieldName' => ['type' => Type::string()]], ]); $this->implementingType = new ObjectType([ - 'name' => 'Object', + 'name' => 'Object', 'interfaces' => [$this->interfaceType], - 'fields' => ['fieldName' => ['type' => Type::string(), 'resolve' => function () { - return ''; - }]], + 'fields' => [ + 'fieldName' => [ + 'type' => Type::string(), + 'resolve' => function () { + return ''; + }, + ], + ], ]); $this->directiveInputType = new InputObjectType([ - 'name' => 'DirInput', + 'name' => 'DirInput', 'fields' => [ 'field' => [ 'type' => Type::string(), @@ -50,7 +63,7 @@ class SchemaTest extends TestCase ]); $this->wrappedDirectiveInputType = new InputObjectType([ - 'name' => 'WrappedDirInput', + 'name' => 'WrappedDirInput', 'fields' => [ 'field' => [ 'type' => Type::string(), @@ -59,10 +72,10 @@ class SchemaTest extends TestCase ]); $this->directive = new Directive([ - 'name' => 'dir', + 'name' => 'dir', 'locations' => ['OBJECT'], - 'args' => [ - 'arg' => [ + 'args' => [ + 'arg' => [ 'type' => $this->directiveInputType, ], 'argList' => [ @@ -72,11 +85,11 @@ class SchemaTest extends TestCase ]); $this->schema = new Schema([ - 'query' => new ObjectType([ - 'name' => 'Query', + 'query' => new ObjectType([ + 'name' => 'Query', 'fields' => [ 'getObject' => [ - 'type' => $this->interfaceType, + 'type' => $this->interfaceType, 'resolve' => function () { return []; }, diff --git a/tests/Type/TestClasses.php b/tests/Type/TestClasses.php deleted file mode 100644 index b9c277c..0000000 --- a/tests/Type/TestClasses.php +++ /dev/null @@ -1,32 +0,0 @@ - [ - 'a' => Type::string() - ] - ]; - parent::__construct($config); - } -} - -// Note: named OtherCustom vs OtherCustomType intentionally -class OtherCustom extends ObjectType -{ - public function __construct() - { - $config = [ - 'fields' => [ - 'b' => Type::string() - ] - ]; - parent::__construct($config); - } -} diff --git a/tests/Type/TestClasses/MyCustomType.php b/tests/Type/TestClasses/MyCustomType.php new file mode 100644 index 0000000..7ad0f5e --- /dev/null +++ b/tests/Type/TestClasses/MyCustomType.php @@ -0,0 +1,21 @@ + [ + 'a' => Type::string(), + ], + ]; + parent::__construct($config); + } +} diff --git a/tests/Type/TestClasses/OtherCustom.php b/tests/Type/TestClasses/OtherCustom.php new file mode 100644 index 0000000..50f4daf --- /dev/null +++ b/tests/Type/TestClasses/OtherCustom.php @@ -0,0 +1,24 @@ + [ + 'b' => Type::string(), + ], + ]; + parent::__construct($config); + } +} diff --git a/tests/Type/TypeLoaderTest.php b/tests/Type/TypeLoaderTest.php index 1893ca7..3024568 100644 --- a/tests/Type/TypeLoaderTest.php +++ b/tests/Type/TypeLoaderTest.php @@ -1,6 +1,8 @@ calls = []; $this->node = new InterfaceType([ - 'name' => 'Node', - 'fields' => function() { + 'name' => 'Node', + 'fields' => function () { $this->calls[] = 'Node.fields'; + return [ - 'id' => Type::string() + 'id' => Type::string(), ]; }, - 'resolveType' => function() {} + 'resolveType' => function () { + }, ]); $this->content = new InterfaceType([ - 'name' => 'Content', - 'fields' => function() { + 'name' => 'Content', + 'fields' => function () { $this->calls[] = 'Content.fields'; + return [ 'title' => Type::string(), - 'body' => Type::string(), + 'body' => Type::string(), ]; }, - 'resolveType' => function() {} + 'resolveType' => function () { + }, ]); $this->blogStory = new ObjectType([ - 'name' => 'BlogStory', + 'name' => 'BlogStory', 'interfaces' => [ $this->node, - $this->content + $this->content, ], - 'fields' => function() { + 'fields' => function () { $this->calls[] = 'BlogStory.fields'; + return [ $this->node->getField('id'), $this->content->getField('title'), @@ -101,53 +91,56 @@ class TypeLoaderTest extends TestCase ]); $this->query = new ObjectType([ - 'name' => 'Query', - 'fields' => function() { + 'name' => 'Query', + 'fields' => function () { $this->calls[] = 'Query.fields'; + return [ 'latestContent' => $this->content, - 'node' => $this->node, + 'node' => $this->node, ]; - } + }, ]); $this->mutation = new ObjectType([ - 'name' => 'Mutation', - 'fields' => function() { + 'name' => 'Mutation', + 'fields' => function () { $this->calls[] = 'Mutation.fields'; + return [ 'postStory' => [ 'type' => $this->postStoryMutation, 'args' => [ - 'input' => Type::nonNull($this->postStoryMutationInput), - 'clientRequestId' => Type::string() - ] - ] + 'input' => Type::nonNull($this->postStoryMutationInput), + 'clientRequestId' => Type::string(), + ], + ], ]; - } + }, ]); $this->postStoryMutation = new ObjectType([ - 'name' => 'PostStoryMutation', + 'name' => 'PostStoryMutation', 'fields' => [ - 'story' => $this->blogStory - ] + 'story' => $this->blogStory, + ], ]); $this->postStoryMutationInput = new InputObjectType([ - 'name' => 'PostStoryMutationInput', + 'name' => 'PostStoryMutationInput', 'fields' => [ - 'title' => Type::string(), - 'body' => Type::string(), - 'author' => Type::id(), - 'category' => Type::id() - ] + 'title' => Type::string(), + 'body' => Type::string(), + 'author' => Type::id(), + 'category' => Type::id(), + ], ]); - $this->typeLoader = function($name) { + $this->typeLoader = function ($name) { $this->calls[] = $name; - $prop = lcfirst($name); - return isset($this->{$prop}) ? $this->{$prop} : null; + $prop = lcfirst($name); + + return $this->{$prop} ?? null; }; } @@ -155,11 +148,12 @@ class TypeLoaderTest extends TestCase { $this->expectNotToPerformAssertions(); new Schema([ - 'query' => new ObjectType([ - 'name' => 'Query', - 'fields' => ['a' => Type::string()] + 'query' => new ObjectType([ + 'name' => 'Query', + 'fields' => ['a' => Type::string()], ]), - 'typeLoader' => function() {} + 'typeLoader' => function () { + }, ]); } @@ -169,20 +163,20 @@ class TypeLoaderTest extends TestCase $this->expectExceptionMessage('Schema type loader must be callable if provided but got: []'); new Schema([ - 'query' => new ObjectType([ - 'name' => 'Query', - 'fields' => ['a' => Type::string()] + 'query' => new ObjectType([ + 'name' => 'Query', + 'fields' => ['a' => Type::string()], ]), - 'typeLoader' => [] + 'typeLoader' => [], ]); } public function testWorksWithoutTypeLoader() : void { $schema = new Schema([ - 'query' => $this->query, + 'query' => $this->query, 'mutation' => $this->mutation, - 'types' => [$this->blogStory] + 'types' => [$this->blogStory], ]); $expected = [ @@ -203,12 +197,12 @@ class TypeLoaderTest extends TestCase $this->assertSame($this->postStoryMutationInput, $schema->getType('PostStoryMutationInput')); $expectedTypeMap = [ - 'Query' => $this->query, - 'Mutation' => $this->mutation, - 'Node' => $this->node, - 'String' => Type::string(), - 'Content' => $this->content, - 'BlogStory' => $this->blogStory, + 'Query' => $this->query, + 'Mutation' => $this->mutation, + 'Node' => $this->node, + 'String' => Type::string(), + 'Content' => $this->content, + 'BlogStory' => $this->blogStory, 'PostStoryMutationInput' => $this->postStoryMutationInput, ]; @@ -218,9 +212,9 @@ class TypeLoaderTest extends TestCase public function testWorksWithTypeLoader() : void { $schema = new Schema([ - 'query' => $this->query, - 'mutation' => $this->mutation, - 'typeLoader' => $this->typeLoader + 'query' => $this->query, + 'mutation' => $this->mutation, + 'typeLoader' => $this->typeLoader, ]); $this->assertEquals([], $this->calls); @@ -244,8 +238,8 @@ class TypeLoaderTest extends TestCase public function testOnlyCallsLoaderOnce() : void { $schema = new Schema([ - 'query' => $this->query, - 'typeLoader' => $this->typeLoader + 'query' => $this->query, + 'typeLoader' => $this->typeLoader, ]); $schema->getType('Node'); @@ -258,8 +252,9 @@ class TypeLoaderTest extends TestCase public function testFailsOnNonExistentType() : void { $schema = new Schema([ - 'query' => $this->query, - 'typeLoader' => function() {} + 'query' => $this->query, + 'typeLoader' => function () { + }, ]); $this->expectException(InvariantViolation::class); @@ -271,10 +266,10 @@ class TypeLoaderTest extends TestCase public function testFailsOnNonType() : void { $schema = new Schema([ - 'query' => $this->query, - 'typeLoader' => function() { + 'query' => $this->query, + 'typeLoader' => function () { return new \stdClass(); - } + }, ]); $this->expectException(InvariantViolation::class); @@ -286,10 +281,10 @@ class TypeLoaderTest extends TestCase public function testFailsOnInvalidLoad() : void { $schema = new Schema([ - 'query' => $this->query, - 'typeLoader' => function() { + 'query' => $this->query, + 'typeLoader' => function () { return $this->content; - } + }, ]); $this->expectException(InvariantViolation::class); @@ -301,10 +296,10 @@ class TypeLoaderTest extends TestCase public function testPassesThroughAnExceptionInLoader() : void { $schema = new Schema([ - 'query' => $this->query, - 'typeLoader' => function() { - throw new \Exception("This is the exception we are looking for"); - } + 'query' => $this->query, + 'typeLoader' => function () { + throw new \Exception('This is the exception we are looking for'); + }, ]); $this->expectException(\Throwable::class); @@ -316,14 +311,14 @@ class TypeLoaderTest extends TestCase public function testReturnsIdenticalResults() : void { $withoutLoader = new Schema([ - 'query' => $this->query, - 'mutation' => $this->mutation + 'query' => $this->query, + 'mutation' => $this->mutation, ]); $withLoader = new Schema([ - 'query' => $this->query, - 'mutation' => $this->mutation, - 'typeLoader' => $this->typeLoader + 'query' => $this->query, + 'mutation' => $this->mutation, + 'typeLoader' => $this->typeLoader, ]); $this->assertSame($withoutLoader->getQueryType(), $withLoader->getQueryType()); @@ -335,9 +330,9 @@ class TypeLoaderTest extends TestCase public function testSkipsLoaderForInternalTypes() : void { $schema = new Schema([ - 'query' => $this->query, - 'mutation' => $this->mutation, - 'typeLoader' => $this->typeLoader + 'query' => $this->query, + 'mutation' => $this->mutation, + 'typeLoader' => $this->typeLoader, ]); $type = $schema->getType('ID'); diff --git a/tests/Type/ValidationTest.php b/tests/Type/ValidationTest.php index d388bb5..362574b 100644 --- a/tests/Type/ValidationTest.php +++ b/tests/Type/ValidationTest.php @@ -1,43 +1,63 @@ Number = 1; $this->SomeScalarType = new CustomScalarType([ - 'name' => 'SomeScalar', - 'serialize' => function() {}, - 'parseValue' => function() {}, - 'parseLiteral' => function() {} + 'name' => 'SomeScalar', + 'serialize' => function () { + }, + 'parseValue' => function () { + }, + 'parseLiteral' => function () { + }, ]); $this->SomeObjectType = new ObjectType([ - 'name' => 'SomeObject', - 'fields' => [ 'f' => [ 'type' => Type::string() ] ], - 'interfaces' => function() {return [$this->SomeInterfaceType];} + 'name' => 'SomeObject', + 'fields' => ['f' => ['type' => Type::string()]], + 'interfaces' => function () { + return [$this->SomeInterfaceType]; + }, ]); + $this->SomeUnionType = new UnionType([ - 'name' => 'SomeUnion', - 'types' => [ $this->SomeObjectType ] + 'name' => 'SomeUnion', + 'types' => [$this->SomeObjectType], ]); $this->SomeInterfaceType = new InterfaceType([ - 'name' => 'SomeInterface', - 'fields' => [ 'f' => ['type' => Type::string() ]] + 'name' => 'SomeInterface', + 'fields' => ['f' => ['type' => Type::string()]], ]); $this->SomeEnumType = new EnumType([ - 'name' => 'SomeEnum', + 'name' => 'SomeEnum', 'values' => [ - 'ONLY' => [] - ] + 'ONLY' => [], + ], ]); $this->SomeInputObjectType = new InputObjectType([ - 'name' => 'SomeInputObject', + 'name' => 'SomeInputObject', 'fields' => [ - 'val' => [ 'type' => Type::string(), 'defaultValue' => 'hello' ] - ] + 'val' => ['type' => Type::string(), 'defaultValue' => 'hello'], + ], ]); $this->outputTypes = $this->withModifiers([ @@ -86,11 +112,11 @@ class ValidationTest extends TestCase $this->SomeEnumType, $this->SomeObjectType, $this->SomeUnionType, - $this->SomeInterfaceType + $this->SomeInterfaceType, ]); - $this->notOutputTypes = $this->withModifiers([ - $this->SomeInputObjectType, + $this->notOutputTypes = $this->withModifiers([ + $this->SomeInputObjectType, ]); $this->notOutputTypes[] = $this->Number; @@ -112,61 +138,75 @@ class ValidationTest extends TestCase Warning::suppress(Warning::WARNING_NOT_A_TYPE); } + private function withModifiers($types) + { + return array_merge( + $types, + Utils::map( + $types, + function ($type) { + return Type::listOf($type); + } + ), + Utils::map( + $types, + function ($type) { + return Type::nonNull($type); + } + ), + Utils::map( + $types, + function ($type) { + return Type::nonNull(Type::listOf($type)); + } + ) + ); + } + public function tearDown() { parent::tearDown(); Warning::enable(Warning::WARNING_NOT_A_TYPE); } - /** - * @param InvariantViolation[]|Error[] $array - * @param array $messages - */ - private function assertContainsValidationMessage($array, $messages) { - $this->assertCount( - count($messages), - $array, - 'For messages: ' . $messages[0]['message'] . "\n" . - "Received: \n" . join("\n", array_map(function($error) { return $error->getMessage(); }, $array)) - ); - foreach ($array as $index => $error) { - if(!isset($messages[$index]) || !$error instanceof Error) { - $this->fail('Received unexpected error: ' . $error->getMessage()); - } - $this->assertEquals($messages[$index]['message'], $error->getMessage()); - $errorLocations = []; - foreach ($error->getLocations() as $location) { - $errorLocations[] = $location->toArray(); - } - $this->assertEquals( - isset($messages[$index]['locations']) ? $messages[$index]['locations'] : [], - $errorLocations - ); - } - } - public function testRejectsTypesWithoutNames() : void { - $this->assertEachCallableThrows([ - function() { - return new ObjectType([]); - }, - function() { - return new EnumType([]); - }, - function() { - return new InputObjectType([]); - }, - function() { - return new UnionType([]); - }, - function() { - return new InterfaceType([]); - } - ], 'Must provide name.'); + $this->assertEachCallableThrows( + [ + function () { + return new ObjectType([]); + }, + function () { + return new EnumType([]); + }, + function () { + return new InputObjectType([]); + }, + function () { + return new UnionType([]); + }, + function () { + return new InterfaceType([]); + }, + ], + 'Must provide name.' + ); } - // DESCRIBE: Type System: A Schema must have Object root types + /** + * DESCRIBE: Type System: A Schema must have Object root types + */ + private function assertEachCallableThrows($closures, $expectedError) + { + foreach ($closures as $index => $factory) { + try { + $factory(); + $this->fail('Expected exception not thrown for entry ' . $index); + } catch (InvariantViolation $e) { + $this->assertEquals($expectedError, $e->getMessage(), 'Error in callable #' . $index); + } + } + } /** * @see it('accepts a Schema whose query type is an object type') @@ -273,7 +313,6 @@ class ValidationTest extends TestCase [['message' => 'Query root type must be provided.']] ); - $schemaWithDef = BuildSchema::build(' schema { mutation: MutationRoot @@ -287,12 +326,50 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schemaWithDef->validate(), [[ - 'message' => 'Query root type must be provided.', + 'message' => 'Query root type must be provided.', 'locations' => [['line' => 2, 'column' => 7]], - ]] + ], + ] ); } + /** + * @param InvariantViolation[]|Error[] $array + * @param string[][] $messages + */ + private function assertContainsValidationMessage($array, $messages) + { + $this->assertCount( + count($messages), + $array, + sprintf('For messages: %s', $messages[0]['message']) . "\n" . + "Received: \n" . + implode( + "\n", + array_map( + function ($error) { + return $error->getMessage(); + }, + $array + ) + ) + ); + foreach ($array as $index => $error) { + if (! isset($messages[$index]) || ! $error instanceof Error) { + $this->fail('Received unexpected error: ' . $error->getMessage()); + } + $this->assertEquals($messages[$index]['message'], $error->getMessage()); + $errorLocations = []; + foreach ($error->getLocations() as $location) { + $errorLocations[] = $location->toArray(); + } + $this->assertEquals( + $messages[$index]['locations'] ?? [], + $errorLocations + ); + } + } + /** * @see it('rejects a Schema whose query root type is not an Object type') */ @@ -307,12 +384,12 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'Query root type must be Object type, it cannot be Query.', + 'message' => 'Query root type must be Object type, it cannot be Query.', 'locations' => [['line' => 2, 'column' => 7]], - ]] + ], + ] ); - $schemaWithDef = BuildSchema::build(' schema { query: SomeInputObject @@ -326,9 +403,10 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schemaWithDef->validate(), [[ - 'message' => 'Query root type must be Object type, it cannot be SomeInputObject.', + 'message' => 'Query root type must be Object type, it cannot be SomeInputObject.', 'locations' => [['line' => 3, 'column' => 16]], - ]] + ], + ] ); } @@ -350,9 +428,10 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'Mutation root type must be Object type if provided, it cannot be Mutation.', + 'message' => 'Mutation root type must be Object type if provided, it cannot be Mutation.', 'locations' => [['line' => 6, 'column' => 7]], - ]] + ], + ] ); $schemaWithDef = BuildSchema::build(' @@ -373,12 +452,15 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schemaWithDef->validate(), [[ - 'message' => 'Mutation root type must be Object type if provided, it cannot be SomeInputObject.', + 'message' => 'Mutation root type must be Object type if provided, it cannot be SomeInputObject.', 'locations' => [['line' => 4, 'column' => 19]], - ]] + ], + ] ); } + // DESCRIBE: Type System: Objects must have fields + /** * @see it('rejects a Schema whose subscription type is an input type') */ @@ -397,9 +479,10 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'Subscription root type must be Object type if provided, it cannot be Subscription.', + 'message' => 'Subscription root type must be Object type if provided, it cannot be Subscription.', 'locations' => [['line' => 6, 'column' => 7]], - ]] + ], + ] ); $schemaWithDef = BuildSchema::build(' @@ -420,12 +503,11 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schemaWithDef->validate(), [[ - 'message' => 'Subscription root type must be Object type if provided, it cannot be SomeInputObject.', + 'message' => 'Subscription root type must be Object type if provided, it cannot be SomeInputObject.', 'locations' => [['line' => 4, 'column' => 23]], - ]] + ], + ] ); - - } /** @@ -434,8 +516,8 @@ class ValidationTest extends TestCase public function testRejectsASchemaWhoseDirectivesAreIncorrectlyTyped() : void { $schema = new Schema([ - 'query' => $this->SomeObjectType, - 'directives' => ['somedirective'] + 'query' => $this->SomeObjectType, + 'directives' => ['somedirective'], ]); $this->assertContainsValidationMessage( @@ -444,8 +526,6 @@ class ValidationTest extends TestCase ); } - // DESCRIBE: Type System: Objects must have fields - /** * @see it('accepts an Object type with fields object') */ @@ -480,14 +560,15 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'Type IncompleteObject must define one or more fields.', + 'message' => 'Type IncompleteObject must define one or more fields.', 'locations' => [['line' => 6, 'column' => 7]], - ]] + ], + ] ); $manualSchema = $this->schemaWithFieldType( new ObjectType([ - 'name' => 'IncompleteObject', + 'name' => 'IncompleteObject', 'fields' => [], ]) ); @@ -499,8 +580,10 @@ class ValidationTest extends TestCase $manualSchema2 = $this->schemaWithFieldType( new ObjectType([ - 'name' => 'IncompleteObject', - 'fields' => function () { return []; }, + 'name' => 'IncompleteObject', + 'fields' => function () { + return []; + }, ]) ); @@ -510,6 +593,20 @@ class ValidationTest extends TestCase ); } + /** + * DESCRIBE: Type System: Fields args must be properly named + */ + private function schemaWithFieldType($type) : Schema + { + return new Schema([ + 'query' => new ObjectType([ + 'name' => 'Query', + 'fields' => ['f' => ['type' => $type]], + ]), + 'types' => [$type], + ]); + } + /** * @see it('rejects an Object type with incorrectly named fields') */ @@ -517,9 +614,9 @@ class ValidationTest extends TestCase { $schema = $this->schemaWithFieldType( new ObjectType([ - 'name' => 'SomeObject', + 'name' => 'SomeObject', 'fields' => [ - 'bad-name-with-dashes' => ['type' => Type::string()] + 'bad-name-with-dashes' => ['type' => Type::string()], ], ]) ); @@ -528,42 +625,44 @@ class ValidationTest extends TestCase $schema->validate(), [[ 'message' => 'Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but ' . - '"bad-name-with-dashes" does not.', - ]] + '"bad-name-with-dashes" does not.', + ], + ] ); } + /** + * DESCRIBE: Type System: Union types must be valid + */ public function testAcceptsShorthandNotationForFields() : void { $this->expectNotToPerformAssertions(); $schema = $this->schemaWithFieldType( new ObjectType([ - 'name' => 'SomeObject', + 'name' => 'SomeObject', 'fields' => [ - 'field' => Type::string() - ] + 'field' => Type::string(), + ], ]) ); $schema->assertValid(); } - // DESCRIBE: Type System: Fields args must be properly named - /** * @see it('accepts field args with valid names') */ public function testAcceptsFieldArgsWithValidNames() : void { $schema = $this->schemaWithFieldType(new ObjectType([ - 'name' => 'SomeObject', + 'name' => 'SomeObject', 'fields' => [ 'goodField' => [ 'type' => Type::string(), 'args' => [ - 'goodArg' => ['type' => Type::string()] - ] - ] - ] + 'goodArg' => ['type' => Type::string()], + ], + ], + ], ])); $this->assertEquals([], $schema->validate()); } @@ -574,17 +673,17 @@ class ValidationTest extends TestCase public function testRejectsFieldArgWithInvalidNames() : void { $QueryType = new ObjectType([ - 'name' => 'SomeObject', + 'name' => 'SomeObject', 'fields' => [ 'badField' => [ 'type' => Type::string(), 'args' => [ - 'bad-name-with-dashes' => ['type' => Type::string()] - ] - ] - ] + 'bad-name-with-dashes' => ['type' => Type::string()], + ], + ], + ], ]); - $schema = new Schema(['query' => $QueryType]); + $schema = new Schema(['query' => $QueryType]); $this->assertContainsValidationMessage( $schema->validate(), @@ -592,8 +691,6 @@ class ValidationTest extends TestCase ); } - // DESCRIBE: Type System: Union types must be valid - /** * @see it('accepts a Union type with member types') */ @@ -620,6 +717,8 @@ class ValidationTest extends TestCase $this->assertEquals([], $schema->validate()); } + // DESCRIBE: Type System: Input Objects must have fields + /** * @see it('rejects a Union type with empty types') */ @@ -635,9 +734,10 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'Union type BadUnion must define one or more member types.', + 'message' => 'Union type BadUnion must define one or more member types.', 'locations' => [['line' => 6, 'column' => 7]], - ]] + ], + ] ); } @@ -667,9 +767,10 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'Union type BadUnion can only include type TypeA once.', + 'message' => 'Union type BadUnion can only include type TypeA once.', 'locations' => [['line' => 15, 'column' => 11], ['line' => 17, 'column' => 11]], - ]] + ], + ] ); } @@ -699,11 +800,11 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'Union type BadUnion can only include Object types, ' . - 'it cannot include String.', + 'message' => 'Union type BadUnion can only include Object types, ' . + 'it cannot include String.', 'locations' => [['line' => 16, 'column' => 11]], - ]] - + ], + ] ); $badUnionMemberTypes = [ @@ -716,7 +817,7 @@ class ValidationTest extends TestCase $this->SomeInputObjectType, ]; - foreach($badUnionMemberTypes as $memberType) { + foreach ($badUnionMemberTypes as $memberType) { $badSchema = $this->schemaWithFieldType( new UnionType(['name' => 'BadUnion', 'types' => [$memberType]]) ); @@ -724,13 +825,14 @@ class ValidationTest extends TestCase $badSchema->validate(), [[ 'message' => 'Union type BadUnion can only include Object types, ' . - "it cannot include ". Utils::printSafe($memberType) . ".", - ]] + 'it cannot include ' . Utils::printSafe($memberType) . '.', + ], + ] ); } } - // DESCRIBE: Type System: Input Objects must have fields + // DESCRIBE: Type System: Enum types must be well defined /** * @see it('accepts an Input Object type with fields') @@ -764,9 +866,10 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'Input Object type SomeInputObject must define one or more fields.', + 'message' => 'Input Object type SomeInputObject must define one or more fields.', 'locations' => [['line' => 6, 'column' => 7]], - ]] + ], + ] ); } @@ -794,18 +897,19 @@ class ValidationTest extends TestCase '); $this->assertContainsValidationMessage( $schema->validate(), - [[ - 'message' => 'The type of SomeInputObject.badObject must be Input Type but got: SomeObject.', - 'locations' => [['line' => 13, 'column' => 20]], - ],[ - 'message' => 'The type of SomeInputObject.badUnion must be Input Type but got: SomeUnion.', - 'locations' => [['line' => 14, 'column' => 19]], - ]] + [ + [ + 'message' => 'The type of SomeInputObject.badObject must be Input Type but got: SomeObject.', + 'locations' => [['line' => 13, 'column' => 20]], + ], + [ + 'message' => 'The type of SomeInputObject.badUnion must be Input Type but got: SomeUnion.', + 'locations' => [['line' => 14, 'column' => 19]], + ], + ] ); } - // DESCRIBE: Type System: Enum types must be well defined - /** * @see it('rejects an Enum type without values') */ @@ -821,9 +925,10 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'Enum type SomeEnum must define one or more values.', + 'message' => 'Enum type SomeEnum must define one or more values.', 'locations' => [['line' => 6, 'column' => 7]], - ]] + ], + ] ); } @@ -845,38 +950,32 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'Enum type SomeEnum can include value SOME_VALUE only once.', + 'message' => 'Enum type SomeEnum can include value SOME_VALUE only once.', 'locations' => [['line' => 7, 'column' => 9], ['line' => 8, 'column' => 9]], - ]] + ], + ] ); } public function testDoesNotAllowIsDeprecatedWithoutDeprecationReasonOnEnum() : void { $enum = new EnumType([ - 'name' => 'SomeEnum', + 'name' => 'SomeEnum', 'values' => [ - 'value' => ['isDeprecated' => true] - ] + 'value' => ['isDeprecated' => true], + ], ]); $this->expectException(InvariantViolation::class); $this->expectExceptionMessage('SomeEnum.value should provide "deprecationReason" instead of "isDeprecated".'); $enum->assertValid(); } - private function schemaWithEnum($name) - { - return $this->schemaWithFieldType( - new EnumType([ - 'name' => 'SomeEnum', - 'values' => [ - $name => [] - ] - ]) - ); - } - - public function invalidEnumValueName() + /** + * DESCRIBE: Type System: Object fields must have output types + * + * @return string[][] + */ + public function invalidEnumValueName() : array { return [ ['#value', 'Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "#value" does not.'], @@ -889,7 +988,7 @@ class ValidationTest extends TestCase } /** - * @see it('rejects an Enum type with incorrectly named values') + * @see it('rejects an Enum type with incorrectly named values') * @dataProvider invalidEnumValueName */ public function testRejectsAnEnumTypeWithIncorrectlyNamedValues($name, $expectedMessage) : void @@ -898,13 +997,22 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), - [[ - 'message' => $expectedMessage, - ]] + [['message' => $expectedMessage], + ] ); } - // DESCRIBE: Type System: Object fields must have output types + private function schemaWithEnum($name) + { + return $this->schemaWithFieldType( + new EnumType([ + 'name' => 'SomeEnum', + 'values' => [ + $name => [], + ], + ]) + ); + } /** * @see it('accepts an output type as an Object field type') @@ -917,6 +1025,29 @@ class ValidationTest extends TestCase } } + /** + * DESCRIBE: Type System: Objects can only implement unique interfaces + */ + private function schemaWithObjectFieldOfType($fieldType) : Schema + { + $BadObjectType = new ObjectType([ + 'name' => 'BadObject', + 'fields' => [ + 'badField' => ['type' => $fieldType], + ], + ]); + + return new Schema([ + 'query' => new ObjectType([ + 'name' => 'Query', + 'fields' => [ + 'f' => ['type' => $BadObjectType], + ], + ]), + 'types' => [$this->SomeObjectType], + ]); + } + /** * @see it('rejects an empty Object field type') */ @@ -926,9 +1057,8 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), - [[ - 'message' => 'The type of BadObject.badField must be Output Type but got: null.', - ]] + [['message' => 'The type of BadObject.badField must be Output Type but got: null.'], + ] ); } @@ -944,7 +1074,8 @@ class ValidationTest extends TestCase $schema->validate(), [[ 'message' => 'The type of BadObject.badField must be Output Type but got: ' . Utils::printSafe($type) . '.', - ]] + ], + ] ); } } @@ -966,29 +1097,28 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'The type of Query.field must be Output Type but got: [SomeInputObject].', + 'message' => 'The type of Query.field must be Output Type but got: [SomeInputObject].', 'locations' => [['line' => 3, 'column' => 16]], - ]] + ], + ] ); } - // DESCRIBE: Type System: Objects can only implement unique interfaces + // DESCRIBE: Type System: Interface fields must have output types /** * @see it('rejects an Object implementing a non-type values') */ public function testRejectsAnObjectImplementingANonTypeValues() : void { - $schema = new Schema([ + $schema = new Schema([ 'query' => new ObjectType([ - 'name' => 'BadObject', + 'name' => 'BadObject', 'interfaces' => [null], - 'fields' => ['a' => Type::string()] + 'fields' => ['a' => Type::string()], ]), ]); - $expected = [ - 'message' => 'Type BadObject must only implement Interface types, it cannot implement null.' - ]; + $expected = ['message' => 'Type BadObject must only implement Interface types, it cannot implement null.']; $this->assertContainsValidationMessage( $schema->validate(), @@ -1017,9 +1147,10 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'Type BadObject must only implement Interface types, it cannot implement SomeInputObject.', + 'message' => 'Type BadObject must only implement Interface types, it cannot implement SomeInputObject.', 'locations' => [['line' => 10, 'column' => 33]], - ]] + ], + ] ); } @@ -1044,9 +1175,10 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'Type AnotherObject can only implement AnotherInterface once.', + 'message' => 'Type AnotherObject can only implement AnotherInterface once.', 'locations' => [['line' => 10, 'column' => 37], ['line' => 10, 'column' => 56]], - ]] + ], + ] ); } @@ -1075,13 +1207,14 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'Type AnotherObject can only implement AnotherInterface once.', + 'message' => 'Type AnotherObject can only implement AnotherInterface once.', 'locations' => [['line' => 10, 'column' => 37], ['line' => 14, 'column' => 38]], - ]] + ], + ] ); } - // DESCRIBE: Type System: Interface fields must have output types + // DESCRIBE: Type System: Field arguments must have input types /** * @see it('accepts an output type as an Interface field type') @@ -1094,6 +1227,25 @@ class ValidationTest extends TestCase } } + private function schemaWithInterfaceFieldOfType($fieldType) + { + $BadInterfaceType = new InterfaceType([ + 'name' => 'BadInterface', + 'fields' => [ + 'badField' => ['type' => $fieldType], + ], + ]); + + return new Schema([ + 'query' => new ObjectType([ + 'name' => 'Query', + 'fields' => [ + 'f' => ['type' => $BadInterfaceType], + ], + ]), + ]); + } + /** * @see it('rejects an empty Interface field type') */ @@ -1102,9 +1254,8 @@ class ValidationTest extends TestCase $schema = $this->schemaWithInterfaceFieldOfType(null); $this->assertContainsValidationMessage( $schema->validate(), - [[ - 'message' => 'The type of BadInterface.badField must be Output Type but got: null.', - ]] + [['message' => 'The type of BadInterface.badField must be Output Type but got: null.'], + ] ); } @@ -1120,11 +1271,14 @@ class ValidationTest extends TestCase $schema->validate(), [[ 'message' => 'The type of BadInterface.badField must be Output Type but got: ' . Utils::printSafe($type) . '.', - ]] + ], + ] ); } } + // DESCRIBE: Type System: Input Object fields must have input types + /** * @see it('rejects a non-output type as an Interface field type with locations') */ @@ -1146,14 +1300,13 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'The type of SomeInterface.field must be Output Type but got: SomeInputObject.', + 'message' => 'The type of SomeInterface.field must be Output Type but got: SomeInputObject.', 'locations' => [['line' => 7, 'column' => 16]], - ]] + ], + ] ); } - // DESCRIBE: Type System: Field arguments must have input types - /** * @see it('accepts an input type as a field arg type') */ @@ -1165,6 +1318,30 @@ class ValidationTest extends TestCase } } + private function schemaWithArgOfType($argType) + { + $BadObjectType = new ObjectType([ + 'name' => 'BadObject', + 'fields' => [ + 'badField' => [ + 'type' => Type::string(), + 'args' => [ + 'badArg' => ['type' => $argType], + ], + ], + ], + ]); + + return new Schema([ + 'query' => new ObjectType([ + 'name' => 'Query', + 'fields' => [ + 'f' => ['type' => $BadObjectType], + ], + ]), + ]); + } + /** * @see it('rejects an empty field arg type') */ @@ -1173,12 +1350,13 @@ class ValidationTest extends TestCase $schema = $this->schemaWithArgOfType(null); $this->assertContainsValidationMessage( $schema->validate(), - [[ - 'message' => 'The type of BadObject.badField(badArg:) must be Input Type but got: null.', - ]] + [['message' => 'The type of BadObject.badField(badArg:) must be Input Type but got: null.'], + ] ); } + // DESCRIBE: Objects must adhere to Interface they implement + /** * @see it('rejects a non-input type as a field arg type') */ @@ -1190,7 +1368,8 @@ class ValidationTest extends TestCase $schema->validate(), [[ 'message' => 'The type of BadObject.badField(badArg:) must be Input Type but got: ' . Utils::printSafe($type) . '.', - ]] + ], + ] ); } } @@ -1212,14 +1391,13 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'The type of Query.test(arg:) must be Input Type but got: SomeObject.', + 'message' => 'The type of Query.test(arg:) must be Input Type but got: SomeObject.', 'locations' => [['line' => 3, 'column' => 19]], - ]] + ], + ] ); } - // DESCRIBE: Type System: Input Object fields must have input types - /** * @see it('accepts an input type as an input field type') */ @@ -1231,6 +1409,30 @@ class ValidationTest extends TestCase } } + private function schemaWithInputFieldOfType($inputFieldType) + { + $BadInputObjectType = new InputObjectType([ + 'name' => 'BadInputObject', + 'fields' => [ + 'badField' => ['type' => $inputFieldType], + ], + ]); + + return new Schema([ + 'query' => new ObjectType([ + 'name' => 'Query', + 'fields' => [ + 'f' => [ + 'type' => Type::string(), + 'args' => [ + 'badArg' => ['type' => $BadInputObjectType], + ], + ], + ], + ]), + ]); + } + /** * @see it('rejects an empty input field type') */ @@ -1239,9 +1441,8 @@ class ValidationTest extends TestCase $schema = $this->schemaWithInputFieldOfType(null); $this->assertContainsValidationMessage( $schema->validate(), - [[ - 'message' => 'The type of BadInputObject.badField must be Input Type but got: null.', - ]] + [['message' => 'The type of BadInputObject.badField must be Input Type but got: null.'], + ] ); } @@ -1256,7 +1457,8 @@ class ValidationTest extends TestCase $schema->validate(), [[ 'message' => 'The type of BadInputObject.badField must be Input Type but got: ' . Utils::printSafe($type) . '.', - ]] + ], + ] ); } } @@ -1282,14 +1484,13 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'The type of SomeInputObject.foo must be Input Type but got: SomeObject.', + 'message' => 'The type of SomeInputObject.foo must be Input Type but got: SomeObject.', 'locations' => [['line' => 7, 'column' => 14]], - ]] + ], + ] ); } - // DESCRIBE: Objects must adhere to Interface they implement - /** * @see it('accepts an Object which implements an Interface') */ @@ -1335,10 +1536,10 @@ class ValidationTest extends TestCase } '); - $this->assertEquals( - [], - $schema->validate() - ); + $this->assertEquals( + [], + $schema->validate() + ); } /** @@ -1388,10 +1589,11 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'Interface field AnotherInterface.field expected but ' . - 'AnotherObject does not provide it.', + 'message' => 'Interface field AnotherInterface.field expected but ' . + 'AnotherObject does not provide it.', 'locations' => [['line' => 7, 'column' => 9], ['line' => 10, 'column' => 7]], - ]] + ], + ] ); } @@ -1417,10 +1619,11 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'Interface field AnotherInterface.field expects type String but ' . + 'message' => 'Interface field AnotherInterface.field expects type String but ' . 'AnotherObject.field is type Int.', 'locations' => [['line' => 7, 'column' => 31], ['line' => 11, 'column' => 31]], - ]] + ], + ] ); } @@ -1449,10 +1652,11 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'Interface field AnotherInterface.field expects type A but ' . + 'message' => 'Interface field AnotherInterface.field expects type A but ' . 'AnotherObject.field is type B.', 'locations' => [['line' => 10, 'column' => 16], ['line' => 14, 'column' => 16]], - ]] + ], + ] ); } @@ -1503,7 +1707,7 @@ class ValidationTest extends TestCase } '); - $this->assertEquals([],$schema->validate()); + $this->assertEquals([], $schema->validate()); } /** @@ -1528,10 +1732,11 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'Interface field argument AnotherInterface.field(input:) expected ' . - 'but AnotherObject.field does not provide it.', + 'message' => 'Interface field argument AnotherInterface.field(input:) expected ' . + 'but AnotherObject.field does not provide it.', 'locations' => [['line' => 7, 'column' => 15], ['line' => 11, 'column' => 9]], - ]] + ], + ] ); } @@ -1557,10 +1762,11 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'Interface field argument AnotherInterface.field(input:) expects ' . - 'type String but AnotherObject.field(input:) is type Int.', + 'message' => 'Interface field argument AnotherInterface.field(input:) expects ' . + 'type String but AnotherObject.field(input:) is type Int.', 'locations' => [['line' => 7, 'column' => 22], ['line' => 11, 'column' => 22]], - ]] + ], + ] ); } @@ -1585,15 +1791,18 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), - [[ - 'message' => 'Interface field AnotherInterface.field expects type String but ' . - 'AnotherObject.field is type Int.', - 'locations' => [['line' => 7, 'column' => 31], ['line' => 11, 'column' => 28]], - ], [ - 'message' => 'Interface field argument AnotherInterface.field(input:) expects ' . - 'type String but AnotherObject.field(input:) is type Int.', - 'locations' => [['line' => 7, 'column' => 22], ['line' => 11, 'column' => 22]], - ]] + [ + [ + 'message' => 'Interface field AnotherInterface.field expects type String but ' . + 'AnotherObject.field is type Int.', + 'locations' => [['line' => 7, 'column' => 31], ['line' => 11, 'column' => 28]], + ], + [ + 'message' => 'Interface field argument AnotherInterface.field(input:) expects ' . + 'type String but AnotherObject.field(input:) is type Int.', + 'locations' => [['line' => 7, 'column' => 22], ['line' => 11, 'column' => 22]], + ], + ] ); } @@ -1619,11 +1828,12 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'Object field argument AnotherObject.field(anotherInput:) is of ' . - 'required type String! but is not also provided by the Interface ' . - 'field AnotherInterface.field.', + 'message' => 'Object field argument AnotherObject.field(anotherInput:) is of ' . + 'required type String! but is not also provided by the Interface ' . + 'field AnotherInterface.field.', 'locations' => [['line' => 11, 'column' => 44], ['line' => 7, 'column' => 9]], - ]] + ], + ] ); } @@ -1671,10 +1881,11 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'Interface field AnotherInterface.field expects type [String] ' . - 'but AnotherObject.field is type String.', + 'message' => 'Interface field AnotherInterface.field expects type [String] ' . + 'but AnotherObject.field is type String.', 'locations' => [['line' => 7, 'column' => 16], ['line' => 11, 'column' => 16]], - ]] + ], + ] ); } @@ -1700,10 +1911,11 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'Interface field AnotherInterface.field expects type String but ' . - 'AnotherObject.field is type [String].', + 'message' => 'Interface field AnotherInterface.field expects type String but ' . + 'AnotherObject.field is type [String].', 'locations' => [['line' => 7, 'column' => 16], ['line' => 11, 'column' => 16]], - ]] + ], + ] ); } @@ -1751,24 +1963,25 @@ class ValidationTest extends TestCase $this->assertContainsValidationMessage( $schema->validate(), [[ - 'message' => 'Interface field AnotherInterface.field expects type String! ' . - 'but AnotherObject.field is type String.', + 'message' => 'Interface field AnotherInterface.field expects type String! ' . + 'but AnotherObject.field is type String.', 'locations' => [['line' => 7, 'column' => 16], ['line' => 11, 'column' => 16]], - ]] + ], + ] ); } public function testRejectsDifferentInstancesOfTheSameType() : void { // Invalid: always creates new instance vs returning one from registry - $typeLoader = function($name) use (&$typeLoader) { + $typeLoader = function ($name) { switch ($name) { case 'Query': return new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ - 'test' => Type::string() - ] + 'test' => Type::string(), + ], ]); default: return null; @@ -1776,141 +1989,14 @@ class ValidationTest extends TestCase }; $schema = new Schema([ - 'query' => $typeLoader('Query'), - 'typeLoader' => $typeLoader + 'query' => $typeLoader('Query'), + 'typeLoader' => $typeLoader, ]); $this->expectException(InvariantViolation::class); $this->expectExceptionMessage( - 'Type loader returns different instance for Query than field/argument definitions. '. + 'Type loader returns different instance for Query than field/argument definitions. ' . 'Make sure you always return the same instance for the same type name.' ); $schema->assertValid(); } - - - private function assertEachCallableThrows($closures, $expectedError) - { - foreach ($closures as $index => $factory) { - try { - $factory(); - $this->fail('Expected exception not thrown for entry ' . $index); - } catch (InvariantViolation $e) { - $this->assertEquals($expectedError, $e->getMessage(), 'Error in callable #' . $index); - } - } - } - - private function schemaWithFieldType($type) - { - return new Schema([ - 'query' => new ObjectType([ - 'name' => 'Query', - 'fields' => ['f' => ['type' => $type]] - ]), - 'types' => [$type], - ]); - } - - private function schemaWithObjectFieldOfType($fieldType) - { - $BadObjectType = new ObjectType([ - 'name' => 'BadObject', - 'fields' => [ - 'badField' => ['type' => $fieldType] - ] - ]); - - return new Schema([ - 'query' => new ObjectType([ - 'name' => 'Query', - 'fields' => [ - 'f' => ['type' => $BadObjectType] - ] - ]), - 'types' => [$this->SomeObjectType] - ]); - } - - private function withModifiers($types) - { - return array_merge( - $types, - Utils::map($types, function ($type) { - return Type::listOf($type); - }), - Utils::map($types, function ($type) { - return Type::nonNull($type); - }), - Utils::map($types, function ($type) { - return Type::nonNull(Type::listOf($type)); - }) - ); - } - - private function schemaWithInterfaceFieldOfType($fieldType) - { - $BadInterfaceType = new InterfaceType([ - 'name' => 'BadInterface', - 'fields' => [ - 'badField' => ['type' => $fieldType] - ] - ]); - - return new Schema([ - 'query' => new ObjectType([ - 'name' => 'Query', - 'fields' => [ - 'f' => ['type' => $BadInterfaceType] - ] - ]), - ]); - } - - private function schemaWithArgOfType($argType) - { - $BadObjectType = new ObjectType([ - 'name' => 'BadObject', - 'fields' => [ - 'badField' => [ - 'type' => Type::string(), - 'args' => [ - 'badArg' => ['type' => $argType] - ] - ] - ] - ]); - - return new Schema([ - 'query' => new ObjectType([ - 'name' => 'Query', - 'fields' => [ - 'f' => ['type' => $BadObjectType] - ] - ]) - ]); - } - - private function schemaWithInputFieldOfType($inputFieldType) - { - $BadInputObjectType = new InputObjectType([ - 'name' => 'BadInputObject', - 'fields' => [ - 'badField' => ['type' => $inputFieldType] - ] - ]); - - return new Schema([ - 'query' => new ObjectType([ - 'name' => 'Query', - 'fields' => [ - 'f' => [ - 'type' => Type::string(), - 'args' => [ - 'badArg' => ['type' => $BadInputObjectType] - ] - ] - ] - ]) - ]); - } } diff --git a/tests/Utils/AssertValidNameTest.php b/tests/Utils/AssertValidNameTest.php index 87fbfea..0876acd 100644 --- a/tests/Utils/AssertValidNameTest.php +++ b/tests/Utils/AssertValidNameTest.php @@ -1,4 +1,7 @@ assertEquals(new NullValueNode([]), AST::astFromValue(null, Type::boolean())); $this->assertEquals(new BooleanValueNode(['value' => false]), AST::astFromValue(0, Type::boolean())); $this->assertEquals(new BooleanValueNode(['value' => true]), AST::astFromValue(1, Type::boolean())); - $this->assertEquals(new BooleanValueNode(['value' => false]), AST::astFromValue(0, Type::nonNull(Type::boolean()))); - $this->assertEquals(null, AST::astFromValue(null, Type::nonNull(Type::boolean()))); // Note: null means that AST cannot + $this->assertEquals( + new BooleanValueNode(['value' => false]), + AST::astFromValue(0, Type::nonNull(Type::boolean())) + ); + $this->assertEquals( + null, + AST::astFromValue(null, Type::nonNull(Type::boolean())) + ); // Note: null means that AST cannot } /** @@ -59,7 +70,10 @@ class AstFromValueTest extends TestCase { $this->expectException(\Throwable::class); $this->expectExceptionMessage('Int cannot represent non 32-bit signed integer value: 1.0E+40'); - AST::astFromValue(1e40, Type::int()); // Note: js version will produce 1e+40, both values are valid GraphQL floats + AST::astFromValue( + 1e40, + Type::int() + ); // Note: js version will produce 1e+40, both values are valid GraphQL floats } /** @@ -112,7 +126,7 @@ class AstFromValueTest extends TestCase */ public function testDoesNotConvertsNonNullValuestoNullValue() : void { - $this->assertSame(null, AST::astFromValue(null, Type::nonNull(Type::boolean()))); + $this->assertNull(AST::astFromValue(null, Type::nonNull(Type::boolean()))); } /** @@ -121,13 +135,41 @@ class AstFromValueTest extends TestCase public function testConvertsStringValuesToEnumASTsIfPossible() : void { $this->assertEquals(new EnumValueNode(['value' => 'HELLO']), AST::astFromValue('HELLO', $this->myEnum())); - $this->assertEquals(new EnumValueNode(['value' => 'COMPLEX']), AST::astFromValue($this->complexValue(), $this->myEnum())); + $this->assertEquals( + new EnumValueNode(['value' => 'COMPLEX']), + AST::astFromValue($this->complexValue(), $this->myEnum()) + ); // Note: case sensitive - $this->assertEquals(null, AST::astFromValue('hello', $this->myEnum())); + $this->assertNull(AST::astFromValue('hello', $this->myEnum())); // Note: Not a valid enum value - $this->assertEquals(null, AST::astFromValue('VALUE', $this->myEnum())); + $this->assertNull(AST::astFromValue('VALUE', $this->myEnum())); + } + + /** + * @return EnumType + */ + private function myEnum() + { + return new EnumType([ + 'name' => 'MyEnum', + 'values' => [ + 'HELLO' => [], + 'GOODBYE' => [], + 'COMPLEX' => ['value' => $this->complexValue()], + ], + ]); + } + + private function complexValue() + { + if (! $this->complexValue) { + $this->complexValue = new \stdClass(); + $this->complexValue->someArbitrary = 'complexValue'; + } + + return $this->complexValue; } /** @@ -138,8 +180,8 @@ class AstFromValueTest extends TestCase $value1 = new ListValueNode([ 'values' => [ new StringValueNode(['value' => 'FOO']), - new StringValueNode(['value' => 'BAR']) - ] + new StringValueNode(['value' => 'BAR']), + ], ]); $this->assertEquals($value1, AST::astFromValue(['FOO', 'BAR'], Type::listOf(Type::string()))); @@ -147,7 +189,7 @@ class AstFromValueTest extends TestCase 'values' => [ new EnumValueNode(['value' => 'HELLO']), new EnumValueNode(['value' => 'GOODBYE']), - ] + ], ]); $this->assertEquals($value2, AST::astFromValue(['HELLO', 'GOODBYE'], Type::listOf($this->myEnum()))); } @@ -157,7 +199,10 @@ class AstFromValueTest extends TestCase */ public function testConvertsListSingletons() : void { - $this->assertEquals(new StringValueNode(['value' => 'FOO']), AST::astFromValue('FOO', Type::listOf(Type::string()))); + $this->assertEquals( + new StringValueNode(['value' => 'FOO']), + AST::astFromValue('FOO', Type::listOf(Type::string())) + ); } /** @@ -166,18 +211,18 @@ class AstFromValueTest extends TestCase public function testConvertsInputObjects() : void { $inputObj = new InputObjectType([ - 'name' => 'MyInputObj', + 'name' => 'MyInputObj', 'fields' => [ 'foo' => Type::float(), - 'bar' => $this->myEnum() - ] + 'bar' => $this->myEnum(), + ], ]); $expected = new ObjectValueNode([ 'fields' => [ $this->objectField('foo', new IntValueNode(['value' => '3'])), - $this->objectField('bar', new EnumValueNode(['value' => 'HELLO'])) - ] + $this->objectField('bar', new EnumValueNode(['value' => 'HELLO'])), + ], ]); $data = ['foo' => 3, 'bar' => 'HELLO']; @@ -185,62 +230,38 @@ class AstFromValueTest extends TestCase $this->assertEquals($expected, AST::astFromValue((object) $data, $inputObj)); } + /** + * @param mixed $value + * @return ObjectFieldNode + */ + private function objectField(string $name, $value) + { + return new ObjectFieldNode([ + 'name' => new NameNode(['value' => $name]), + 'value' => $value, + ]); + } + /** * @see it('converts input objects with explicit nulls') */ public function testConvertsInputObjectsWithExplicitNulls() : void { $inputObj = new InputObjectType([ - 'name' => 'MyInputObj', + 'name' => 'MyInputObj', 'fields' => [ 'foo' => Type::float(), - 'bar' => $this->myEnum() - ] + 'bar' => $this->myEnum(), + ], ]); - $this->assertEquals(new ObjectValueNode([ + $this->assertEquals( + new ObjectValueNode([ 'fields' => [ - $this->objectField('foo', new NullValueNode([])) - ] - ]), AST::astFromValue(['foo' => null], $inputObj)); - } - - private $complexValue; - - private function complexValue() - { - if (!$this->complexValue) { - $this->complexValue = new \stdClass(); - $this->complexValue->someArbitrary = 'complexValue'; - } - return $this->complexValue; - } - - /** - * @return EnumType - */ - private function myEnum() - { - return new EnumType([ - 'name' => 'MyEnum', - 'values' => [ - 'HELLO' => [], - 'GOODBYE' => [], - 'COMPLEX' => ['value' => $this->complexValue()] - ] - ]); - } - - /** - * @param $name - * @param $value - * @return ObjectFieldNode - */ - private function objectField($name, $value) - { - return new ObjectFieldNode([ - 'name' => new NameNode(['value' => $name]), - 'value' => $value - ]); + $this->objectField('foo', new NullValueNode([])), + ], + ]), + AST::astFromValue(['foo' => null], $inputObj) + ); } } diff --git a/tests/Utils/AstFromValueUntypedTest.php b/tests/Utils/AstFromValueUntypedTest.php index eb168cf..6c78fed 100644 --- a/tests/Utils/AstFromValueUntypedTest.php +++ b/tests/Utils/AstFromValueUntypedTest.php @@ -1,4 +1,7 @@ assertEquals( - $expected, - AST::valueFromASTUntyped(Parser::parseValue($valueText), $variables) - ); - } - /** * @see it('parses simple values') */ @@ -29,6 +24,17 @@ class AstFromValueUntypedTest extends TestCase $this->assertTestCase('abc123', 'abc123'); } + /** + * @param mixed[]|null $variables + */ + private function assertTestCase($valueText, $expected, ?array $variables = null) : void + { + $this->assertEquals( + $expected, + AST::valueFromASTUntyped(Parser::parseValue($valueText), $variables) + ); + } + /** * @see it('parses lists of values') */ diff --git a/tests/Utils/BreakingChangesFinderTest.php b/tests/Utils/BreakingChangesFinderTest.php index 37f575d..6af2723 100644 --- a/tests/Utils/BreakingChangesFinderTest.php +++ b/tests/Utils/BreakingChangesFinderTest.php @@ -1,5 +1,7 @@ queryType = new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ 'field1' => [ - 'type' => Type::string() - ] - ] + 'type' => Type::string(), + ], + ], ]); } @@ -38,32 +42,32 @@ class BreakingChangesFinderTest extends TestCase */ public function testShouldDetectIfTypeWasRemovedOrNot() : void { - $type1 = new ObjectType([ - 'name' => 'Type1', + $type1 = new ObjectType([ + 'name' => 'Type1', 'fields' => [ 'field1' => ['type' => Type::string()], - ] + ], ]); - $type2 = new ObjectType([ - 'name' => 'Type2', + $type2 = new ObjectType([ + 'name' => 'Type2', 'fields' => [ 'field1' => ['type' => Type::string()], - ] + ], ]); $oldSchema = new Schema([ 'query' => $this->queryType, - 'types' => [$type1, $type2] + 'types' => [$type1, $type2], ]); $newSchema = new Schema([ 'query' => $this->queryType, - 'types' => [$type2] + 'types' => [$type2], ]); $expected = [ [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_TYPE_REMOVED, - 'description' => 'Type1 was removed.' - ] + 'type' => BreakingChangesFinder::BREAKING_CHANGE_TYPE_REMOVED, + 'description' => 'Type1 was removed.', + ], ]; $this->assertEquals( @@ -80,39 +84,39 @@ class BreakingChangesFinderTest extends TestCase public function testShouldDetectIfATypeChangedItsType() : void { $objectType = new ObjectType([ - 'name' => 'ObjectType', + 'name' => 'ObjectType', 'fields' => [ 'field1' => ['type' => Type::string()], - ] + ], ]); $interfaceType = new InterfaceType([ - 'name' => 'Type1', + 'name' => 'Type1', 'fields' => [ - 'field1' => ['type' => Type::string()] - ] + 'field1' => ['type' => Type::string()], + ], ]); $unionType = new UnionType([ - 'name' => 'Type1', + 'name' => 'Type1', 'types' => [$objectType], ]); $oldSchema = new Schema([ 'query' => $this->queryType, - 'types' => [$interfaceType] + 'types' => [$interfaceType], ]); $newSchema = new Schema([ 'query' => $this->queryType, - 'types' => [$unionType] + 'types' => [$unionType], ]); $expected = [ [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_TYPE_CHANGED_KIND, - 'description' => 'Type1 changed from an Interface type to a Union type.' - ] + 'type' => BreakingChangesFinder::BREAKING_CHANGE_TYPE_CHANGED_KIND, + 'description' => 'Type1 changed from an Interface type to a Union type.', + ], ]; $this->assertEquals( @@ -127,36 +131,36 @@ class BreakingChangesFinderTest extends TestCase public function testShouldDetectIfAFieldOnATypeWasDeletedOrChangedType() : void { $typeA = new ObjectType([ - 'name' => 'TypeA', + 'name' => 'TypeA', 'fields' => [ 'field1' => ['type' => Type::string()], - ] + ], ]); // logically equivalent to TypeA; findBreakingFieldChanges shouldn't // treat this as different than TypeA - $typeA2 = new ObjectType([ - 'name' => 'TypeA', + $typeA2 = new ObjectType([ + 'name' => 'TypeA', 'fields' => [ 'field1' => ['type' => Type::string()], - ] + ], ]); - $typeB = new ObjectType([ - 'name' => 'TypeB', + $typeB = new ObjectType([ + 'name' => 'TypeB', 'fields' => [ 'field1' => ['type' => Type::string()], - ] + ], ]); $oldType1 = new InterfaceType([ - 'name' => 'Type1', + 'name' => 'Type1', 'fields' => [ - 'field1' => ['type' => $typeA], - 'field2' => ['type' => Type::string()], - 'field3' => ['type' => Type::string()], - 'field4' => ['type' => $typeA], - 'field6' => ['type' => Type::string()], - 'field7' => ['type' => Type::listOf(Type::string())], - 'field8' => ['type' => Type::int()], - 'field9' => ['type' => Type::nonNull(Type::int())], + 'field1' => ['type' => $typeA], + 'field2' => ['type' => Type::string()], + 'field3' => ['type' => Type::string()], + 'field4' => ['type' => $typeA], + 'field6' => ['type' => Type::string()], + 'field7' => ['type' => Type::listOf(Type::string())], + 'field8' => ['type' => Type::int()], + 'field9' => ['type' => Type::nonNull(Type::int())], 'field10' => ['type' => Type::nonNull(Type::listOf(Type::int()))], 'field11' => ['type' => Type::int()], 'field12' => ['type' => Type::listOf(Type::int())], @@ -167,21 +171,22 @@ class BreakingChangesFinderTest extends TestCase 'field17' => ['type' => Type::listOf(Type::int())], 'field18' => [ 'type' => Type::listOf(Type::nonNull( - Type::listOf(Type::nonNull(Type::int())))), + Type::listOf(Type::nonNull(Type::int())) + )), ], - ] + ], ]); $newType1 = new InterfaceType([ - 'name' => 'Type1', + 'name' => 'Type1', 'fields' => [ - 'field1' => ['type' => $typeA2], - 'field3' => ['type' => Type::boolean()], - 'field4' => ['type' => $typeB], - 'field5' => ['type' => Type::string()], - 'field6' => ['type' => Type::listOf(Type::string())], - 'field7' => ['type' => Type::string()], - 'field8' => ['type' => Type::nonNull(Type::int())], - 'field9' => ['type' => Type::int()], + 'field1' => ['type' => $typeA2], + 'field3' => ['type' => Type::boolean()], + 'field4' => ['type' => $typeB], + 'field5' => ['type' => Type::string()], + 'field6' => ['type' => Type::listOf(Type::string())], + 'field7' => ['type' => Type::string()], + 'field8' => ['type' => Type::nonNull(Type::int())], + 'field9' => ['type' => Type::int()], 'field10' => ['type' => Type::listOf(Type::int())], 'field11' => ['type' => Type::nonNull(Type::listOf(Type::int()))], 'field12' => ['type' => Type::listOf(Type::nonNull(Type::int()))], @@ -192,9 +197,10 @@ class BreakingChangesFinderTest extends TestCase 'field17' => ['type' => Type::nonNull(Type::listOf(Type::int()))], 'field18' => [ 'type' => Type::listOf( - Type::listOf(Type::nonNull(Type::int()))), + Type::listOf(Type::nonNull(Type::int())) + ), ], - ] + ], ]); $oldSchema = new Schema([ @@ -209,60 +215,63 @@ class BreakingChangesFinderTest extends TestCase $expectedFieldChanges = [ [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_REMOVED, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_REMOVED, 'description' => 'Type1.field2 was removed.', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, 'description' => 'Type1.field3 changed type from String to Boolean.', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, 'description' => 'Type1.field4 changed type from TypeA to TypeB.', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, 'description' => 'Type1.field6 changed type from String to [String].', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, 'description' => 'Type1.field7 changed type from [String] to String.', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, 'description' => 'Type1.field9 changed type from Int! to Int.', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, 'description' => 'Type1.field10 changed type from [Int]! to [Int].', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, 'description' => 'Type1.field11 changed type from Int to [Int]!.', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, 'description' => 'Type1.field13 changed type from [Int!] to [Int].', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, 'description' => 'Type1.field14 changed type from [Int] to [[Int]].', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, 'description' => 'Type1.field15 changed type from [[Int]] to [Int].', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, 'description' => 'Type1.field16 changed type from Int! to [Int]!.', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, 'description' => 'Type1.field18 changed type from [[Int!]!] to [[Int!]].', ], ]; - $this->assertEquals($expectedFieldChanges, BreakingChangesFinder::findFieldsThatChangedTypeOnObjectOrInterfaceTypes($oldSchema, $newSchema)); + $this->assertEquals( + $expectedFieldChanges, + BreakingChangesFinder::findFieldsThatChangedTypeOnObjectOrInterfaceTypes($oldSchema, $newSchema) + ); } /** @@ -271,162 +280,168 @@ class BreakingChangesFinderTest extends TestCase public function testShouldDetectIfFieldsOnInputTypesChangedKindOrWereRemoved() : void { $oldInputType = new InputObjectType([ - 'name' => 'InputType1', + 'name' => 'InputType1', 'fields' => [ - 'field1' => [ + 'field1' => [ 'type' => Type::string(), ], - 'field2' => [ + 'field2' => [ 'type' => Type::boolean(), ], - 'field3' => [ - 'type' => Type::listOf(Type::string()) + 'field3' => [ + 'type' => Type::listOf(Type::string()), ], - 'field4' => [ + 'field4' => [ 'type' => Type::nonNull(Type::string()), ], - 'field5' => [ + 'field5' => [ 'type' => Type::string(), ], - 'field6' => [ - 'type' => Type::listOf(Type::int()) + 'field6' => [ + 'type' => Type::listOf(Type::int()), ], - 'field7' => [ - 'type' => Type::nonNull(Type::listOf(Type::int())) + 'field7' => [ + 'type' => Type::nonNull(Type::listOf(Type::int())), ], - 'field8' => [ + 'field8' => [ 'type' => Type::int(), ], - 'field9' => [ - 'type' => Type::listOf(Type::int()) + 'field9' => [ + 'type' => Type::listOf(Type::int()), ], 'field10' => [ - 'type' => Type::listOf(Type::nonNull(Type::int())) + 'type' => Type::listOf(Type::nonNull(Type::int())), ], 'field11' => [ - 'type' => Type::listOf(Type::int()) + 'type' => Type::listOf(Type::int()), ], 'field12' => [ - 'type' => Type::listOf(Type::listOf(Type::int())) + 'type' => Type::listOf(Type::listOf(Type::int())), ], 'field13' => [ - 'type' => Type::nonNull(Type::int()) + 'type' => Type::nonNull(Type::int()), ], 'field14' => [ - 'type' => Type::listOf(Type::nonNull(Type::listOf(Type::int()))) + 'type' => Type::listOf(Type::nonNull(Type::listOf(Type::int()))), ], 'field15' => [ - 'type' => Type::listOf(Type::nonNull(Type::listOf(Type::int()))) - ] - ] + 'type' => Type::listOf(Type::nonNull(Type::listOf(Type::int()))), + ], + ], ]); $newInputType = new InputObjectType([ - 'name' => 'InputType1', + 'name' => 'InputType1', 'fields' => [ - 'field1' => [ + 'field1' => [ 'type' => Type::int(), ], - 'field3' => [ - 'type' => Type::string() + 'field3' => [ + 'type' => Type::string(), ], - 'field4' => [ - 'type' => Type::string() + 'field4' => [ + 'type' => Type::string(), ], - 'field5' => [ - 'type' => Type::nonNull(Type::string()) + 'field5' => [ + 'type' => Type::nonNull(Type::string()), ], - 'field6' => [ - 'type' => Type::nonNull(Type::listOf(Type::int())) + 'field6' => [ + 'type' => Type::nonNull(Type::listOf(Type::int())), ], - 'field7' => [ - 'type' => Type::listOf(Type::int()) + 'field7' => [ + 'type' => Type::listOf(Type::int()), ], - 'field8' => [ - 'type' => Type::nonNull(Type::listOf(Type::int())) + 'field8' => [ + 'type' => Type::nonNull(Type::listOf(Type::int())), ], - 'field9' => [ - 'type' => Type::listOf(Type::nonNull(Type::int())) + 'field9' => [ + 'type' => Type::listOf(Type::nonNull(Type::int())), ], 'field10' => [ - 'type' => Type::listOf(Type::int()) + 'type' => Type::listOf(Type::int()), ], 'field11' => [ - 'type' => Type::listOf(Type::listOf(Type::int())) + 'type' => Type::listOf(Type::listOf(Type::int())), ], 'field12' => [ - 'type' => Type::listOf(Type::int()) + 'type' => Type::listOf(Type::int()), ], 'field13' => [ - 'type' => Type::nonNull(Type::listOf(Type::int())) + 'type' => Type::nonNull(Type::listOf(Type::int())), ], 'field14' => [ - 'type' => Type::listOf(Type::listOf(Type::int())) + 'type' => Type::listOf(Type::listOf(Type::int())), ], 'field15' => [ - 'type' => Type::listOf(Type::nonNull(Type::listOf(Type::nonNull(Type::int())))) - ] - ] + 'type' => Type::listOf(Type::nonNull(Type::listOf(Type::nonNull(Type::int())))), + ], + ], ]); $oldSchema = new Schema([ 'query' => $this->queryType, - 'types' => [$oldInputType] + 'types' => [$oldInputType], ]); $newSchema = new Schema([ 'query' => $this->queryType, - 'types' => [$newInputType] + 'types' => [$newInputType], ]); $expectedFieldChanges = [ [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, 'description' => 'InputType1.field1 changed type from String to Int.', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_REMOVED, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_REMOVED, 'description' => 'InputType1.field2 was removed.', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, 'description' => 'InputType1.field3 changed type from [String] to String.', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, 'description' => 'InputType1.field5 changed type from String to String!.', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, 'description' => 'InputType1.field6 changed type from [Int] to [Int]!.', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, 'description' => 'InputType1.field8 changed type from Int to [Int]!.', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, 'description' => 'InputType1.field9 changed type from [Int] to [Int!].', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, 'description' => 'InputType1.field11 changed type from [Int] to [[Int]].', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, 'description' => 'InputType1.field12 changed type from [[Int]] to [Int].', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, 'description' => 'InputType1.field13 changed type from Int! to [Int]!.', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, 'description' => 'InputType1.field15 changed type from [[Int]!] to [[Int!]!].', ], ]; - $this->assertEquals($expectedFieldChanges, BreakingChangesFinder::findFieldsThatChangedTypeOnInputObjectTypes($oldSchema, $newSchema)['breakingChanges']); + $this->assertEquals( + $expectedFieldChanges, + BreakingChangesFinder::findFieldsThatChangedTypeOnInputObjectTypes( + $oldSchema, + $newSchema + )['breakingChanges'] + ); } /** @@ -435,19 +450,19 @@ class BreakingChangesFinderTest extends TestCase public function testShouldDetectIfANonNullFieldIsAddedToAnInputType() : void { $oldInputType = new InputObjectType([ - 'name' => 'InputType1', + 'name' => 'InputType1', 'fields' => [ - 'field1' => Type::string() - ] + 'field1' => Type::string(), + ], ]); $newInputType = new InputObjectType([ - 'name' => 'InputType1', + 'name' => 'InputType1', 'fields' => [ - 'field1' => Type::string(), + 'field1' => Type::string(), 'requiredField' => Type::nonNull(Type::int()), - 'optionalField' => Type::boolean() - ] + 'optionalField' => Type::boolean(), + ], ]); $oldSchema = new Schema([ @@ -462,14 +477,17 @@ class BreakingChangesFinderTest extends TestCase $expected = [ [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_NON_NULL_INPUT_FIELD_ADDED, - 'description' => 'A non-null field requiredField on input type InputType1 was added.' + 'type' => BreakingChangesFinder::BREAKING_CHANGE_NON_NULL_INPUT_FIELD_ADDED, + 'description' => 'A non-null field requiredField on input type InputType1 was added.', ], ]; $this->assertEquals( $expected, - BreakingChangesFinder::findFieldsThatChangedTypeOnInputObjectTypes($oldSchema, $newSchema)['breakingChanges'] + BreakingChangesFinder::findFieldsThatChangedTypeOnInputObjectTypes( + $oldSchema, + $newSchema + )['breakingChanges'] ); } @@ -479,38 +497,38 @@ class BreakingChangesFinderTest extends TestCase public function testShouldRetectIfATypeWasRemovedFromAUnionType() : void { $type1 = new ObjectType([ - 'name' => 'Type1', + 'name' => 'Type1', 'fields' => [ - 'field1' => Type::string() - ] + 'field1' => Type::string(), + ], ]); // logially equivalent to type1; findTypesRemovedFromUnions should not // treat this as different than type1 $type1a = new ObjectType([ - 'name' => 'Type1', + 'name' => 'Type1', 'fields' => [ - 'field1' => Type::string() - ] + 'field1' => Type::string(), + ], ]); - $type2 = new ObjectType([ - 'name' => 'Type2', + $type2 = new ObjectType([ + 'name' => 'Type2', 'fields' => [ - 'field1' => Type::string() - ] + 'field1' => Type::string(), + ], ]); - $type3 = new ObjectType([ - 'name' => 'Type3', + $type3 = new ObjectType([ + 'name' => 'Type3', 'fields' => [ - 'field1' => Type::string() - ] + 'field1' => Type::string(), + ], ]); $oldUnionType = new UnionType([ - 'name' => 'UnionType1', + 'name' => 'UnionType1', 'types' => [$type1, $type2], ]); $newUnionType = new UnionType([ - 'name' => 'UnionType1', + 'name' => 'UnionType1', 'types' => [$type1a, $type3], ]); @@ -525,9 +543,9 @@ class BreakingChangesFinderTest extends TestCase $expected = [ [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_TYPE_REMOVED_FROM_UNION, - 'description' => 'Type2 was removed from union type UnionType1.' - ] + 'type' => BreakingChangesFinder::BREAKING_CHANGE_TYPE_REMOVED_FROM_UNION, + 'description' => 'Type2 was removed from union type UnionType1.', + ], ]; $this->assertEquals( @@ -542,37 +560,37 @@ class BreakingChangesFinderTest extends TestCase public function testShouldDetectIfAValueWasRemovedFromAnEnumType() : void { $oldEnumType = new EnumType([ - 'name' => 'EnumType1', + 'name' => 'EnumType1', 'values' => [ 'VALUE0' => 0, 'VALUE1' => 1, - 'VALUE2' => 2 - ] + 'VALUE2' => 2, + ], ]); $newEnumType = new EnumType([ - 'name' => 'EnumType1', + 'name' => 'EnumType1', 'values' => [ 'VALUE0' => 0, 'VALUE2' => 1, - 'VALUE3' => 2 - ] + 'VALUE3' => 2, + ], ]); $oldSchema = new Schema([ 'query' => $this->queryType, - 'types' => [$oldEnumType] + 'types' => [$oldEnumType], ]); $newSchema = new Schema([ 'query' => $this->queryType, - 'types' => [$newEnumType] + 'types' => [$newEnumType], ]); $expected = [ [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_VALUE_REMOVED_FROM_ENUM, - 'description' => 'VALUE1 was removed from enum type EnumType1.' - ] + 'type' => BreakingChangesFinder::BREAKING_CHANGE_VALUE_REMOVED_FROM_ENUM, + 'description' => 'VALUE1 was removed from enum type EnumType1.', + ], ]; $this->assertEquals( @@ -587,52 +605,52 @@ class BreakingChangesFinderTest extends TestCase public function testShouldDetectIfAFieldArgumentWasRemoved() : void { $oldType = new ObjectType([ - 'name' => 'Type1', + 'name' => 'Type1', 'fields' => [ 'field1' => [ 'type' => Type::string(), 'args' => [ - 'name' => Type::string() - ] - ] - ] + 'name' => Type::string(), + ], + ], + ], ]); $inputType = new InputObjectType([ - 'name' => 'InputType1', + 'name' => 'InputType1', 'fields' => [ - 'field1' => Type::string() - ] + 'field1' => Type::string(), + ], ]); $oldInterfaceType = new InterfaceType([ - 'name' => 'Interface1', + 'name' => 'Interface1', 'fields' => [ 'field1' => [ 'type' => Type::string(), 'args' => [ - 'arg1' => Type::boolean(), - 'objectArg' => $inputType - ] - ] - ] + 'arg1' => Type::boolean(), + 'objectArg' => $inputType, + ], + ], + ], ]); $newType = new ObjectType([ - 'name' => 'Type1', + 'name' => 'Type1', 'fields' => [ 'field1' => [ 'type' => Type::string(), - 'args' => [] - ] - ] + 'args' => [], + ], + ], ]); $newInterfaceType = new InterfaceType([ - 'name' => 'Interface1', + 'name' => 'Interface1', 'fields' => [ - 'field1' => Type::string() - ] + 'field1' => Type::string(), + ], ]); $oldSchema = new Schema([ @@ -647,20 +665,23 @@ class BreakingChangesFinderTest extends TestCase $expectedChanges = [ [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_REMOVED, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_REMOVED, 'description' => 'Type1.field1 arg name was removed', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_REMOVED, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_REMOVED, 'description' => 'Interface1.field1 arg arg1 was removed', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_REMOVED, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_REMOVED, 'description' => 'Interface1.field1 arg objectArg was removed', - ] + ], ]; - $this->assertEquals($expectedChanges, BreakingChangesFinder::findArgChanges($oldSchema, $newSchema)['breakingChanges']); + $this->assertEquals( + $expectedChanges, + BreakingChangesFinder::findArgChanges($oldSchema, $newSchema)['breakingChanges'] + ); } /** @@ -669,119 +690,122 @@ class BreakingChangesFinderTest extends TestCase public function testShouldDetectIfAFieldArgumentHasChangedType() : void { $oldType = new ObjectType([ - 'name' => 'Type1', + 'name' => 'Type1', 'fields' => [ 'field1' => [ 'type' => Type::string(), 'args' => [ - 'arg1' => Type::string(), - 'arg2' => Type::string(), - 'arg3' => Type::listOf(Type::string()), - 'arg4' => Type::string(), - 'arg5' => Type::nonNull(Type::string()), - 'arg6' => Type::nonNull(Type::string()), - 'arg7' => Type::nonNull(Type::listOf(Type::int())), - 'arg8' => Type::int(), - 'arg9' => Type::listOf(Type::int()), + 'arg1' => Type::string(), + 'arg2' => Type::string(), + 'arg3' => Type::listOf(Type::string()), + 'arg4' => Type::string(), + 'arg5' => Type::nonNull(Type::string()), + 'arg6' => Type::nonNull(Type::string()), + 'arg7' => Type::nonNull(Type::listOf(Type::int())), + 'arg8' => Type::int(), + 'arg9' => Type::listOf(Type::int()), 'arg10' => Type::listOf(Type::nonNull(Type::int())), 'arg11' => Type::listOf(Type::int()), 'arg12' => Type::listOf(Type::listOf(Type::int())), 'arg13' => Type::nonNull(Type::int()), 'arg14' => Type::listOf(Type::nonNull(Type::listOf(Type::int()))), - 'arg15' => Type::listOf(Type::nonNull(Type::listOf(Type::int()))) - ] - ] - ] + 'arg15' => Type::listOf(Type::nonNull(Type::listOf(Type::int()))), + ], + ], + ], ]); $newType = new ObjectType([ - 'name' => 'Type1', + 'name' => 'Type1', 'fields' => [ 'field1' => [ 'type' => Type::string(), 'args' => [ - 'arg1' => Type::int(), - 'arg2' => Type::listOf(Type::string()), - 'arg3' => Type::string(), - 'arg4' => Type::nonNull(Type::string()), - 'arg5' => Type::int(), - 'arg6' => Type::nonNull(Type::int()), - 'arg7' => Type::listOf(Type::int()), - 'arg8' => Type::nonNull(Type::listOf(Type::int())), - 'arg9' => Type::listOf(Type::nonNull(Type::int())), + 'arg1' => Type::int(), + 'arg2' => Type::listOf(Type::string()), + 'arg3' => Type::string(), + 'arg4' => Type::nonNull(Type::string()), + 'arg5' => Type::int(), + 'arg6' => Type::nonNull(Type::int()), + 'arg7' => Type::listOf(Type::int()), + 'arg8' => Type::nonNull(Type::listOf(Type::int())), + 'arg9' => Type::listOf(Type::nonNull(Type::int())), 'arg10' => Type::listOf(Type::int()), 'arg11' => Type::listOf(Type::listOf(Type::int())), 'arg12' => Type::listOf(Type::int()), 'arg13' => Type::nonNull(Type::listOf(Type::int())), 'arg14' => Type::listOf(Type::listOf(Type::int())), - 'arg15' => Type::listOf(Type::nonNull(Type::listOf(Type::nonNull(Type::int())))) - ] - ] - ] + 'arg15' => Type::listOf(Type::nonNull(Type::listOf(Type::nonNull(Type::int())))), + ], + ], + ], ]); $oldSchema = new Schema([ 'query' => $this->queryType, - 'types' => [$oldType] + 'types' => [$oldType], ]); $newSchema = new Schema([ 'query' => $this->queryType, - 'types' => [$newType] + 'types' => [$newType], ]); $expectedChanges = [ [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, 'description' => 'Type1.field1 arg arg1 has changed type from String to Int', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, - 'description' => 'Type1.field1 arg arg2 has changed type from String to [String]' + 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, + 'description' => 'Type1.field1 arg arg2 has changed type from String to [String]', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, 'description' => 'Type1.field1 arg arg3 has changed type from [String] to String', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, 'description' => 'Type1.field1 arg arg4 has changed type from String to String!', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, 'description' => 'Type1.field1 arg arg5 has changed type from String! to Int', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, 'description' => 'Type1.field1 arg arg6 has changed type from String! to Int!', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, 'description' => 'Type1.field1 arg arg8 has changed type from Int to [Int]!', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, 'description' => 'Type1.field1 arg arg9 has changed type from [Int] to [Int!]', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, 'description' => 'Type1.field1 arg arg11 has changed type from [Int] to [[Int]]', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, 'description' => 'Type1.field1 arg arg12 has changed type from [[Int]] to [Int]', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, 'description' => 'Type1.field1 arg arg13 has changed type from Int! to [Int]!', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, 'description' => 'Type1.field1 arg arg15 has changed type from [[Int]!] to [[Int!]!]', ], ]; - $this->assertEquals($expectedChanges, BreakingChangesFinder::findArgChanges($oldSchema, $newSchema)['breakingChanges']); + $this->assertEquals( + $expectedChanges, + BreakingChangesFinder::findArgChanges($oldSchema, $newSchema)['breakingChanges'] + ); } /** @@ -789,42 +813,44 @@ class BreakingChangesFinderTest extends TestCase */ public function testShouldDetectIfANonNullFieldArgumentWasAdded() : void { - $oldType = new ObjectType([ - 'name' => 'Type1', - 'fields' => [ - 'field1' => [ - 'type' => Type::string(), - 'args' => [ - 'arg1' => Type::string() - ]] - ] - ]); - $newType = new ObjectType([ - 'name' => 'Type1', + $oldType = new ObjectType([ + 'name' => 'Type1', 'fields' => [ 'field1' => [ 'type' => Type::string(), 'args' => [ 'arg1' => Type::string(), + ], + ], + ], + ]); + $newType = new ObjectType([ + 'name' => 'Type1', + 'fields' => [ + 'field1' => [ + 'type' => Type::string(), + 'args' => [ + 'arg1' => Type::string(), 'newRequiredArg' => Type::nonNull(Type::string()), - 'newOptionalArg' => Type::int() - ]] - ] + 'newOptionalArg' => Type::int(), + ], + ], + ], ]); $oldSchema = new Schema([ 'query' => $this->queryType, - 'types' => [$oldType] + 'types' => [$oldType], ]); $newSchema = new Schema([ 'query' => $this->queryType, - 'types' => [$newType] + 'types' => [$newType], ]); $expected = [ [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_NON_NULL_ARG_ADDED, - 'description' => 'A non-null arg newRequiredArg on Type1.field1 was added' - ] + 'type' => BreakingChangesFinder::BREAKING_CHANGE_NON_NULL_ARG_ADDED, + 'description' => 'A non-null arg newRequiredArg on Type1.field1 was added', + ], ]; $this->assertEquals( @@ -839,43 +865,43 @@ class BreakingChangesFinderTest extends TestCase public function testShouldNotFlagArgsWithTheSameTypeSignatureAsBreaking() : void { $inputType1a = new InputObjectType([ - 'name' => 'InputType1', + 'name' => 'InputType1', 'fields' => [ - 'field1' => Type::string() - ] + 'field1' => Type::string(), + ], ]); $inputType1b = new InputObjectType([ - 'name' => 'InputType1', + 'name' => 'InputType1', 'fields' => [ - 'field1' => Type::string() - ] + 'field1' => Type::string(), + ], ]); $oldType = new ObjectType([ - 'name' => 'Type1', + 'name' => 'Type1', 'fields' => [ 'field1' => [ 'type' => Type::int(), 'args' => [ 'arg1' => Type::nonNull(Type::int()), - 'arg2' => $inputType1a - ] - ] - ] + 'arg2' => $inputType1a, + ], + ], + ], ]); $newType = new ObjectType([ - 'name' => 'Type1', + 'name' => 'Type1', 'fields' => [ 'field1' => [ 'type' => Type::int(), 'args' => [ 'arg1' => Type::nonNull(Type::int()), - 'arg2' => $inputType1b - ] - ] - ] + 'arg2' => $inputType1b, + ], + ], + ], ]); $oldSchema = new Schema([ @@ -896,26 +922,26 @@ class BreakingChangesFinderTest extends TestCase public function testShouldConsiderArgsThatMoveAwayFromNonNullAsNonBreaking() : void { $oldType = new ObjectType([ - 'name' => 'Type1', + 'name' => 'Type1', 'fields' => [ 'field1' => [ 'type' => Type::string(), 'args' => [ 'arg1' => Type::nonNull(Type::string()), - ] - ] - ] + ], + ], + ], ]); $newType = new ObjectType([ - 'name' => 'Type1', + 'name' => 'Type1', 'fields' => [ 'field1' => [ 'type' => Type::string(), 'args' => [ - 'arg1' => Type::string() - ] - ] - ] + 'arg1' => Type::string(), + ], + ], + ], ]); $oldSchema = new Schema([ @@ -936,23 +962,23 @@ class BreakingChangesFinderTest extends TestCase public function testShouldDetectInterfacesRemovedFromTypes() : void { $interface1 = new InterfaceType([ - 'name' => 'Interface1', + 'name' => 'Interface1', 'fields' => [ - 'field1' => Type::string() + 'field1' => Type::string(), ], ]); - $oldType = new ObjectType([ - 'name' => 'Type1', + $oldType = new ObjectType([ + 'name' => 'Type1', 'interfaces' => [$interface1], - 'fields' => [ - 'field1' => Type::string() - ] + 'fields' => [ + 'field1' => Type::string(), + ], ]); - $newType = new ObjectType([ - 'name' => 'Type1', + $newType = new ObjectType([ + 'name' => 'Type1', 'fields' => [ - 'field1' => Type::string() - ] + 'field1' => Type::string(), + ], ]); $oldSchema = new Schema([ @@ -966,8 +992,8 @@ class BreakingChangesFinderTest extends TestCase $expected = [ [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_INTERFACE_REMOVED_FROM_OBJECT, - 'description' => 'Type1 no longer implements interface Interface1.' + 'type' => BreakingChangesFinder::BREAKING_CHANGE_INTERFACE_REMOVED_FROM_OBJECT, + 'description' => 'Type1 no longer implements interface Interface1.', ], ]; @@ -983,150 +1009,148 @@ class BreakingChangesFinderTest extends TestCase public function testShouldDetectAllBreakingChanges() : void { $typeThatGetsRemoved = new ObjectType([ - 'name' => 'TypeThatGetsRemoved', + 'name' => 'TypeThatGetsRemoved', 'fields' => [ - 'field1' => Type::string() - ] + 'field1' => Type::string(), + ], ]); $argThatChanges = new ObjectType([ - 'name' => 'ArgThatChanges', + 'name' => 'ArgThatChanges', 'fields' => [ 'field1' => [ 'type' => Type::string(), 'args' => [ - 'id' => Type::int() - ] - ] - ] + 'id' => Type::int(), + ], + ], + ], ]); $argChanged = new ObjectType([ - 'name' => 'ArgThatChanges', + 'name' => 'ArgThatChanges', 'fields' => [ 'field1' => [ 'type' => Type::string(), 'args' => [ - 'id' => Type::string() - ] - ] - ] + 'id' => Type::string(), + ], + ], + ], ]); $typeThatChangesTypeOld = new ObjectType([ - 'name' => 'TypeThatChangesType', + 'name' => 'TypeThatChangesType', 'fields' => [ - 'field1' => Type::string() - ] + 'field1' => Type::string(), + ], ]); $typeThatChangesTypeNew = new InterfaceType([ - 'name' => 'TypeThatChangesType', + 'name' => 'TypeThatChangesType', 'fields' => [ - 'field1' => Type::string() - ] + 'field1' => Type::string(), + ], ]); $typeThatHasBreakingFieldChangesOld = new InterfaceType([ - 'name' => 'TypeThatHasBreakingFieldChanges', + 'name' => 'TypeThatHasBreakingFieldChanges', 'fields' => [ 'field1' => Type::string(), - 'field2' => Type::string() - ] + 'field2' => Type::string(), + ], ]); $typeThatHasBreakingFieldChangesNew = new InterfaceType([ - 'name' => 'TypeThatHasBreakingFieldChanges', + 'name' => 'TypeThatHasBreakingFieldChanges', 'fields' => [ - 'field2' => Type::boolean() - ] + 'field2' => Type::boolean(), + ], ]); $typeInUnion1 = new ObjectType([ - 'name' => 'TypeInUnion1', + 'name' => 'TypeInUnion1', 'fields' => [ - 'field1' => Type::string() - ] + 'field1' => Type::string(), + ], ]); $typeInUnion2 = new ObjectType([ - 'name' => 'TypeInUnion2', + 'name' => 'TypeInUnion2', 'fields' => [ - 'field1' => Type::string() - ] + 'field1' => Type::string(), + ], ]); $unionTypeThatLosesATypeOld = new UnionType([ - 'name' => 'UnionTypeThatLosesAType', + 'name' => 'UnionTypeThatLosesAType', 'types' => [$typeInUnion1, $typeInUnion2], ]); $unionTypeThatLosesATypeNew = new UnionType([ - 'name' => 'UnionTypeThatLosesAType', + 'name' => 'UnionTypeThatLosesAType', 'types' => [$typeInUnion1], ]); $enumTypeThatLosesAValueOld = new EnumType([ - 'name' => 'EnumTypeThatLosesAValue', + 'name' => 'EnumTypeThatLosesAValue', 'values' => [ 'VALUE0' => 0, 'VALUE1' => 1, - 'VALUE2' => 2 - ] + 'VALUE2' => 2, + ], ]); $enumTypeThatLosesAValueNew = new EnumType([ - 'name' => 'EnumTypeThatLosesAValue', + 'name' => 'EnumTypeThatLosesAValue', 'values' => [ 'VALUE1' => 1, - 'VALUE2' => 2 - ] + 'VALUE2' => 2, + ], ]); $interface1 = new InterfaceType([ - 'name' => 'Interface1', + 'name' => 'Interface1', 'fields' => [ - 'field1' => Type::string() + 'field1' => Type::string(), ], ]); $typeThatLosesInterfaceOld = new ObjectType([ - 'name' => 'TypeThatLosesInterface1', + 'name' => 'TypeThatLosesInterface1', 'interfaces' => [$interface1], - 'fields' => [ - 'field1' => Type::string() - ] + 'fields' => [ + 'field1' => Type::string(), + ], ]); $typeThatLosesInterfaceNew = new ObjectType([ - 'name' => 'TypeThatLosesInterface1', + 'name' => 'TypeThatLosesInterface1', 'fields' => [ - 'field1' => Type::string() - ] + 'field1' => Type::string(), + ], ]); - $directiveThatIsRemoved = Directive::skipDirective(); - $directiveThatRemovesArgOld = new Directive([ - 'name' => 'DirectiveThatRemovesArg', + $directiveThatIsRemoved = Directive::skipDirective(); + $directiveThatRemovesArgOld = new Directive([ + 'name' => 'DirectiveThatRemovesArg', 'locations' => [DirectiveLocation::FIELD_DEFINITION], - 'args' => FieldArgument::createMap([ - 'arg1' => [ - 'name' => 'arg1', - ], + 'args' => FieldArgument::createMap([ + 'arg1' => ['name' => 'arg1'], ]), ]); - $directiveThatRemovesArgNew = new Directive([ - 'name' => 'DirectiveThatRemovesArg', + $directiveThatRemovesArgNew = new Directive([ + 'name' => 'DirectiveThatRemovesArg', 'locations' => [DirectiveLocation::FIELD_DEFINITION], ]); - $nonNullDirectiveAddedOld = new Directive([ - 'name' => 'NonNullDirectiveAdded', + $nonNullDirectiveAddedOld = new Directive([ + 'name' => 'NonNullDirectiveAdded', 'locations' => [DirectiveLocation::FIELD_DEFINITION], ]); - $nonNullDirectiveAddedNew = new Directive([ - 'name' => 'NonNullDirectiveAdded', + $nonNullDirectiveAddedNew = new Directive([ + 'name' => 'NonNullDirectiveAdded', 'locations' => [DirectiveLocation::FIELD_DEFINITION], - 'args' => FieldArgument::createMap([ + 'args' => FieldArgument::createMap([ 'arg1' => [ 'name' => 'arg1', 'type' => Type::nonNull(Type::boolean()), @@ -1134,58 +1158,58 @@ class BreakingChangesFinderTest extends TestCase ]), ]); $directiveRemovedLocationOld = new Directive([ - 'name' => 'Directive Name', + 'name' => 'Directive Name', 'locations' => [DirectiveLocation::FIELD_DEFINITION, DirectiveLocation::QUERY], ]); $directiveRemovedLocationNew = new Directive([ - 'name' => 'Directive Name', + 'name' => 'Directive Name', 'locations' => [DirectiveLocation::FIELD_DEFINITION], ]); $oldSchema = new Schema([ - 'query' => $this->queryType, - 'types' => [ + 'query' => $this->queryType, + 'types' => [ $typeThatGetsRemoved, $typeThatChangesTypeOld, $typeThatHasBreakingFieldChangesOld, $unionTypeThatLosesATypeOld, $enumTypeThatLosesAValueOld, $argThatChanges, - $typeThatLosesInterfaceOld + $typeThatLosesInterfaceOld, ], 'directives' => [ $directiveThatIsRemoved, $directiveThatRemovesArgOld, $nonNullDirectiveAddedOld, $directiveRemovedLocationOld, - ] + ], ]); $newSchema = new Schema([ - 'query' => $this->queryType, - 'types' => [ + 'query' => $this->queryType, + 'types' => [ $typeThatChangesTypeNew, $typeThatHasBreakingFieldChangesNew, $unionTypeThatLosesATypeNew, $enumTypeThatLosesAValueNew, $argChanged, $typeThatLosesInterfaceNew, - $interface1 + $interface1, ], 'directives' => [ $directiveThatRemovesArgNew, $nonNullDirectiveAddedNew, $directiveRemovedLocationNew, - ] + ], ]); $expectedBreakingChanges = [ [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_TYPE_REMOVED, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_TYPE_REMOVED, 'description' => 'TypeThatGetsRemoved was removed.', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_TYPE_REMOVED, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_TYPE_REMOVED, 'description' => 'TypeInUnion2 was removed.', ], /* This is reported in the js version because builtin sclar types are added on demand @@ -1195,52 +1219,55 @@ class BreakingChangesFinderTest extends TestCase 'description' => 'Int was removed.' ],*/ [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_TYPE_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_TYPE_CHANGED_KIND, 'description' => 'TypeThatChangesType changed from an Object type to an Interface type.', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_REMOVED, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_REMOVED, 'description' => 'TypeThatHasBreakingFieldChanges.field1 was removed.', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_FIELD_CHANGED_KIND, 'description' => 'TypeThatHasBreakingFieldChanges.field2 changed type from String to Boolean.', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_TYPE_REMOVED_FROM_UNION, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_TYPE_REMOVED_FROM_UNION, 'description' => 'TypeInUnion2 was removed from union type UnionTypeThatLosesAType.', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_VALUE_REMOVED_FROM_ENUM, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_VALUE_REMOVED_FROM_ENUM, 'description' => 'VALUE0 was removed from enum type EnumTypeThatLosesAValue.', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_ARG_CHANGED_KIND, 'description' => 'ArgThatChanges.field1 arg id has changed type from Int to String', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_INTERFACE_REMOVED_FROM_OBJECT, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_INTERFACE_REMOVED_FROM_OBJECT, 'description' => 'TypeThatLosesInterface1 no longer implements interface Interface1.', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_DIRECTIVE_REMOVED, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_DIRECTIVE_REMOVED, 'description' => 'skip was removed', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_DIRECTIVE_ARG_REMOVED, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_DIRECTIVE_ARG_REMOVED, 'description' => 'arg1 was removed from DirectiveThatRemovesArg', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_NON_NULL_DIRECTIVE_ARG_ADDED, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_NON_NULL_DIRECTIVE_ARG_ADDED, 'description' => 'A non-null arg arg1 on directive NonNullDirectiveAdded was added', ], [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_DIRECTIVE_LOCATION_REMOVED, + 'type' => BreakingChangesFinder::BREAKING_CHANGE_DIRECTIVE_LOCATION_REMOVED, 'description' => 'QUERY was removed from Directive Name', - ] + ], ]; - $this->assertEquals($expectedBreakingChanges, BreakingChangesFinder::findBreakingChanges($oldSchema, $newSchema)); + $this->assertEquals( + $expectedBreakingChanges, + BreakingChangesFinder::findBreakingChanges($oldSchema, $newSchema) + ); } /** @@ -1260,12 +1287,15 @@ class BreakingChangesFinderTest extends TestCase $expectedBreakingChanges = [ [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_DIRECTIVE_REMOVED, - 'description' => "{$includeDirective->name} was removed", - ] + 'type' => BreakingChangesFinder::BREAKING_CHANGE_DIRECTIVE_REMOVED, + 'description' => sprintf('%s was removed', $includeDirective->name), + ], ]; - $this->assertEquals($expectedBreakingChanges, BreakingChangesFinder::findRemovedDirectives($oldSchema, $newSchema)); + $this->assertEquals( + $expectedBreakingChanges, + BreakingChangesFinder::findRemovedDirectives($oldSchema, $newSchema) + ); } /** @@ -1283,12 +1313,15 @@ class BreakingChangesFinderTest extends TestCase $expectedBreakingChanges = [ [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_DIRECTIVE_REMOVED, - 'description' => "{$deprecatedDirective->name} was removed", - ] + 'type' => BreakingChangesFinder::BREAKING_CHANGE_DIRECTIVE_REMOVED, + 'description' => sprintf('%s was removed', $deprecatedDirective->name), + ], ]; - $this->assertEquals($expectedBreakingChanges, BreakingChangesFinder::findRemovedDirectives($oldSchema, $newSchema)); + $this->assertEquals( + $expectedBreakingChanges, + BreakingChangesFinder::findRemovedDirectives($oldSchema, $newSchema) + ); } /** @@ -1299,34 +1332,35 @@ class BreakingChangesFinderTest extends TestCase $oldSchema = new Schema([ 'directives' => [ new Directive([ - 'name' => 'DirectiveWithArg', + 'name' => 'DirectiveWithArg', 'locations' => [DirectiveLocation::FIELD_DEFINITION], - 'args' => FieldArgument::createMap([ - 'arg1' => [ - 'name' => 'arg1', - ], + 'args' => FieldArgument::createMap([ + 'arg1' => ['name' => 'arg1'], ]), - ]) + ]), ], ]); $newSchema = new Schema([ 'directives' => [ new Directive([ - 'name' => 'DirectiveWithArg', + 'name' => 'DirectiveWithArg', 'locations' => [DirectiveLocation::FIELD_DEFINITION], - ]) + ]), ], ]); $expectedBreakingChanges = [ [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_DIRECTIVE_ARG_REMOVED, - 'description' => "arg1 was removed from DirectiveWithArg", - ] + 'type' => BreakingChangesFinder::BREAKING_CHANGE_DIRECTIVE_ARG_REMOVED, + 'description' => 'arg1 was removed from DirectiveWithArg', + ], ]; - $this->assertEquals($expectedBreakingChanges, BreakingChangesFinder::findRemovedDirectiveArgs($oldSchema, $newSchema)); + $this->assertEquals( + $expectedBreakingChanges, + BreakingChangesFinder::findRemovedDirectiveArgs($oldSchema, $newSchema) + ); } /** @@ -1337,35 +1371,38 @@ class BreakingChangesFinderTest extends TestCase $oldSchema = new Schema([ 'directives' => [ new Directive([ - 'name' => 'DirectiveName', + 'name' => 'DirectiveName', 'locations' => [DirectiveLocation::FIELD_DEFINITION], - ]) + ]), ], ]); $newSchema = new Schema([ 'directives' => [ new Directive([ - 'name' => 'DirectiveName', + 'name' => 'DirectiveName', 'locations' => [DirectiveLocation::FIELD_DEFINITION], - 'args' => FieldArgument::createMap([ + 'args' => FieldArgument::createMap([ 'arg1' => [ 'name' => 'arg1', 'type' => Type::nonNull(Type::boolean()), ], ]), - ]) + ]), ], ]); $expectedBreakingChanges = [ [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_NON_NULL_DIRECTIVE_ARG_ADDED, - 'description' => "A non-null arg arg1 on directive DirectiveName was added", - ] + 'type' => BreakingChangesFinder::BREAKING_CHANGE_NON_NULL_DIRECTIVE_ARG_ADDED, + 'description' => 'A non-null arg arg1 on directive DirectiveName was added', + ], ]; - $this->assertEquals($expectedBreakingChanges, BreakingChangesFinder::findAddedNonNullDirectiveArgs($oldSchema, $newSchema)); + $this->assertEquals( + $expectedBreakingChanges, + BreakingChangesFinder::findAddedNonNullDirectiveArgs($oldSchema, $newSchema) + ); } /** @@ -1374,16 +1411,19 @@ class BreakingChangesFinderTest extends TestCase public function testShouldDetectLocationsRemovedFromADirective() : void { $d1 = new Directive([ - 'name' => 'Directive Name', + 'name' => 'Directive Name', 'locations' => [DirectiveLocation::FIELD_DEFINITION, DirectiveLocation::QUERY], ]); $d2 = new Directive([ - 'name' => 'Directive Name', + 'name' => 'Directive Name', 'locations' => [DirectiveLocation::FIELD_DEFINITION], ]); - $this->assertEquals([DirectiveLocation::QUERY], BreakingChangesFinder::findRemovedLocationsForDirective($d1, $d2)); + $this->assertEquals( + [DirectiveLocation::QUERY], + BreakingChangesFinder::findRemovedLocationsForDirective($d1, $d2) + ); } /** @@ -1394,32 +1434,35 @@ class BreakingChangesFinderTest extends TestCase $oldSchema = new Schema([ 'directives' => [ new Directive([ - 'name' => 'Directive Name', + 'name' => 'Directive Name', 'locations' => [ DirectiveLocation::FIELD_DEFINITION, - DirectiveLocation::QUERY + DirectiveLocation::QUERY, ], - ]) + ]), ], ]); $newSchema = new Schema([ 'directives' => [ new Directive([ - 'name' => 'Directive Name', + 'name' => 'Directive Name', 'locations' => [DirectiveLocation::FIELD_DEFINITION], - ]) + ]), ], ]); $expectedBreakingChanges = [ [ - 'type' => BreakingChangesFinder::BREAKING_CHANGE_DIRECTIVE_LOCATION_REMOVED, - 'description' => "QUERY was removed from Directive Name", - ] + 'type' => BreakingChangesFinder::BREAKING_CHANGE_DIRECTIVE_LOCATION_REMOVED, + 'description' => 'QUERY was removed from Directive Name', + ], ]; - $this->assertEquals($expectedBreakingChanges, BreakingChangesFinder::findRemovedDirectiveLocations($oldSchema, $newSchema)); + $this->assertEquals( + $expectedBreakingChanges, + BreakingChangesFinder::findRemovedDirectiveLocations($oldSchema, $newSchema) + ); } // DESCRIBE: findDangerousChanges @@ -1431,33 +1474,33 @@ class BreakingChangesFinderTest extends TestCase public function testShouldDetectIfAnArgumentsDefaultValueHasChanged() : void { $oldType = new ObjectType([ - 'name' => 'Type1', + 'name' => 'Type1', 'fields' => [ 'field1' => [ 'type' => Type::string(), 'args' => [ 'name' => [ - 'type' => Type::string(), - 'defaultValue' => 'test' - ] - ] - ] - ] + 'type' => Type::string(), + 'defaultValue' => 'test', + ], + ], + ], + ], ]); $newType = new ObjectType([ - 'name' => 'Type1', + 'name' => 'Type1', 'fields' => [ 'field1' => [ 'type' => Type::string(), 'args' => [ 'name' => [ - 'type' => Type::string(), - 'defaultValue' => 'Test' - ] - ] - ] - ] + 'type' => Type::string(), + 'defaultValue' => 'Test', + ], + ], + ], + ], ]); $oldSchema = new Schema([ @@ -1472,9 +1515,9 @@ class BreakingChangesFinderTest extends TestCase $expected = [ [ - 'type' => BreakingChangesFinder::DANGEROUS_CHANGE_ARG_DEFAULT_VALUE_CHANGED, - 'description' => 'Type1.field1 arg name has changed defaultValue' - ] + 'type' => BreakingChangesFinder::DANGEROUS_CHANGE_ARG_DEFAULT_VALUE_CHANGED, + 'description' => 'Type1.field1 arg name has changed defaultValue', + ], ]; $this->assertEquals( @@ -1489,19 +1532,19 @@ class BreakingChangesFinderTest extends TestCase public function testShouldDetectIfAValueWasAddedToAnEnumType() : void { $oldEnumType = new EnumType([ - 'name' => 'EnumType1', + 'name' => 'EnumType1', 'values' => [ 'VALUE0' => 0, 'VALUE1' => 1, - ] + ], ]); $newEnumType = new EnumType([ - 'name' => 'EnumType1', + 'name' => 'EnumType1', 'values' => [ 'VALUE0' => 0, 'VALUE1' => 1, - 'VALUE2' => 2 - ] + 'VALUE2' => 2, + ], ]); $oldSchema = new Schema([ @@ -1516,9 +1559,9 @@ class BreakingChangesFinderTest extends TestCase $expected = [ [ - 'type' => BreakingChangesFinder::DANGEROUS_CHANGE_VALUE_ADDED_TO_ENUM, - 'description' => 'VALUE2 was added to enum type EnumType1.' - ] + 'type' => BreakingChangesFinder::DANGEROUS_CHANGE_VALUE_ADDED_TO_ENUM, + 'description' => 'VALUE2 was added to enum type EnumType1.', + ], ]; $this->assertEquals( @@ -1533,22 +1576,22 @@ class BreakingChangesFinderTest extends TestCase public function testShouldDetectInterfacesAddedToTypes() : void { $interface1 = new InterfaceType([ - 'name' => 'Interface1', + 'name' => 'Interface1', 'fields' => [ 'field1' => Type::string(), ], ]); - $oldType = new ObjectType([ - 'name' => 'Type1', + $oldType = new ObjectType([ + 'name' => 'Type1', 'fields' => [ 'field1' => Type::string(), ], ]); $newType = new ObjectType([ - 'name' => 'Type1', + 'name' => 'Type1', 'interfaces' => [$interface1], - 'fields' => [ + 'fields' => [ 'field1' => Type::string(), ], ]); @@ -1565,9 +1608,9 @@ class BreakingChangesFinderTest extends TestCase $expected = [ [ - 'type' => BreakingChangesFinder::DANGEROUS_CHANGE_INTERFACE_ADDED_TO_OBJECT, - 'description' => 'Interface1 added to interfaces implemented by Type1.' - ] + 'type' => BreakingChangesFinder::DANGEROUS_CHANGE_INTERFACE_ADDED_TO_OBJECT, + 'description' => 'Interface1 added to interfaces implemented by Type1.', + ], ]; $this->assertEquals( @@ -1582,32 +1625,32 @@ class BreakingChangesFinderTest extends TestCase public function testShouldDetectIfATypeWasAddedToAUnionType() : void { $type1 = new ObjectType([ - 'name' => 'Type1', + 'name' => 'Type1', 'fields' => [ - 'field1' => Type::string() - ] + 'field1' => Type::string(), + ], ]); // logially equivalent to type1; findTypesRemovedFromUnions should not //treat this as different than type1 $type1a = new ObjectType([ - 'name' => 'Type1', + 'name' => 'Type1', 'fields' => [ - 'field1' => Type::string() - ] + 'field1' => Type::string(), + ], ]); - $type2 = new ObjectType([ - 'name' => 'Type2', + $type2 = new ObjectType([ + 'name' => 'Type2', 'fields' => [ - 'field1' => Type::string() - ] + 'field1' => Type::string(), + ], ]); $oldUnionType = new UnionType([ - 'name' => 'UnionType1', + 'name' => 'UnionType1', 'types' => [$type1], ]); $newUnionType = new UnionType([ - 'name' => 'UnionType1', + 'name' => 'UnionType1', 'types' => [$type1a, $type2], ]); @@ -1623,9 +1666,9 @@ class BreakingChangesFinderTest extends TestCase $expected = [ [ - 'type' => BreakingChangesFinder::DANGEROUS_CHANGE_TYPE_ADDED_TO_UNION, - 'description' => 'Type2 was added to union type UnionType1.' - ] + 'type' => BreakingChangesFinder::DANGEROUS_CHANGE_TYPE_ADDED_TO_UNION, + 'description' => 'Type2 was added to union type UnionType1.', + ], ]; $this->assertEquals( @@ -1640,7 +1683,7 @@ class BreakingChangesFinderTest extends TestCase public function testShouldDetectIfANullableFieldWasAddedToAnInput() : void { $oldInputType = new InputObjectType([ - 'name' => 'InputType1', + 'name' => 'InputType1', 'fields' => [ 'field1' => [ 'type' => Type::string(), @@ -1648,7 +1691,7 @@ class BreakingChangesFinderTest extends TestCase ], ]); $newInputType = new InputObjectType([ - 'name' => 'InputType1', + 'name' => 'InputType1', 'fields' => [ 'field1' => [ 'type' => Type::string(), @@ -1661,26 +1704,28 @@ class BreakingChangesFinderTest extends TestCase $oldSchema = new Schema([ 'query' => $this->queryType, - 'types' => [ - $oldInputType, - ] + 'types' => [$oldInputType], ]); $newSchema = new Schema([ 'query' => $this->queryType, - 'types' => [ - $newInputType, - ] + 'types' => [$newInputType], ]); $expectedFieldChanges = [ [ 'description' => 'A nullable field field2 on input type InputType1 was added.', - 'type' => BreakingChangesFinder::DANGEROUS_CHANGE_NULLABLE_INPUT_FIELD_ADDED, + 'type' => BreakingChangesFinder::DANGEROUS_CHANGE_NULLABLE_INPUT_FIELD_ADDED, ], ]; - $this->assertEquals($expectedFieldChanges, BreakingChangesFinder::findFieldsThatChangedTypeOnInputObjectTypes($oldSchema, $newSchema)['dangerousChanges']); + $this->assertEquals( + $expectedFieldChanges, + BreakingChangesFinder::findFieldsThatChangedTypeOnInputObjectTypes( + $oldSchema, + $newSchema + )['dangerousChanges'] + ); } /** @@ -1689,90 +1734,90 @@ class BreakingChangesFinderTest extends TestCase public function testShouldFindAllDangerousChanges() : void { $enumThatGainsAValueOld = new EnumType([ - 'name' => 'EnumType1', + 'name' => 'EnumType1', 'values' => [ 'VALUE0' => 0, 'VALUE1' => 1, - ] + ], ]); $enumThatGainsAValueNew = new EnumType([ - 'name' => 'EnumType1', + 'name' => 'EnumType1', 'values' => [ 'VALUE0' => 0, 'VALUE1' => 1, - 'VALUE2' => 2 - ] + 'VALUE2' => 2, + ], ]); $oldType = new ObjectType([ - 'name' => 'Type1', + 'name' => 'Type1', 'fields' => [ 'field1' => [ 'type' => Type::string(), 'args' => [ 'name' => [ - 'type' => Type::string(), - 'defaultValue' => 'test' - ] - ] - ] - ] + 'type' => Type::string(), + 'defaultValue' => 'test', + ], + ], + ], + ], ]); - $typeInUnion1 = new ObjectType([ - 'name' => 'TypeInUnion1', + $typeInUnion1 = new ObjectType([ + 'name' => 'TypeInUnion1', 'fields' => [ - 'field1' => Type::string() - ] + 'field1' => Type::string(), + ], ]); - $typeInUnion2 = new ObjectType([ - 'name' => 'TypeInUnion2', + $typeInUnion2 = new ObjectType([ + 'name' => 'TypeInUnion2', 'fields' => [ - 'field1' => Type::string() - ] + 'field1' => Type::string(), + ], ]); $unionTypeThatGainsATypeOld = new UnionType([ - 'name' => 'UnionTypeThatGainsAType', + 'name' => 'UnionTypeThatGainsAType', 'types' => [$typeInUnion1], ]); $unionTypeThatGainsATypeNew = new UnionType([ - 'name' => 'UnionTypeThatGainsAType', + 'name' => 'UnionTypeThatGainsAType', 'types' => [$typeInUnion1, $typeInUnion2], ]); $newType = new ObjectType([ - 'name' => 'Type1', + 'name' => 'Type1', 'fields' => [ 'field1' => [ 'type' => Type::string(), 'args' => [ 'name' => [ - 'type' => Type::string(), - 'defaultValue' => 'Test' - ] - ] - ] - ] + 'type' => Type::string(), + 'defaultValue' => 'Test', + ], + ], + ], + ], ]); $interface1 = new InterfaceType([ - 'name' => 'Interface1', + 'name' => 'Interface1', 'fields' => [ 'field1' => Type::string(), ], ]); $typeThatGainsInterfaceOld = new ObjectType([ - 'name' => 'TypeThatGainsInterface1', + 'name' => 'TypeThatGainsInterface1', 'fields' => [ 'field1' => Type::string(), ], ]); $typeThatGainsInterfaceNew = new ObjectType([ - 'name' => 'TypeThatGainsInterface1', + 'name' => 'TypeThatGainsInterface1', 'interfaces' => [$interface1], - 'fields' => [ + 'fields' => [ 'field1' => Type::string(), ], ]); @@ -1783,8 +1828,8 @@ class BreakingChangesFinderTest extends TestCase $oldType, $enumThatGainsAValueOld, $typeThatGainsInterfaceOld, - $unionTypeThatGainsATypeOld - ] + $unionTypeThatGainsATypeOld, + ], ]); $newSchema = new Schema([ @@ -1793,30 +1838,33 @@ class BreakingChangesFinderTest extends TestCase $newType, $enumThatGainsAValueNew, $typeThatGainsInterfaceNew, - $unionTypeThatGainsATypeNew - ] + $unionTypeThatGainsATypeNew, + ], ]); $expectedDangerousChanges = [ [ 'description' => 'Type1.field1 arg name has changed defaultValue', - 'type' => BreakingChangesFinder::DANGEROUS_CHANGE_ARG_DEFAULT_VALUE_CHANGED, + 'type' => BreakingChangesFinder::DANGEROUS_CHANGE_ARG_DEFAULT_VALUE_CHANGED, ], [ 'description' => 'VALUE2 was added to enum type EnumType1.', - 'type' => BreakingChangesFinder::DANGEROUS_CHANGE_VALUE_ADDED_TO_ENUM, + 'type' => BreakingChangesFinder::DANGEROUS_CHANGE_VALUE_ADDED_TO_ENUM, ], [ - 'type' => BreakingChangesFinder::DANGEROUS_CHANGE_INTERFACE_ADDED_TO_OBJECT, + 'type' => BreakingChangesFinder::DANGEROUS_CHANGE_INTERFACE_ADDED_TO_OBJECT, 'description' => 'Interface1 added to interfaces implemented by TypeThatGainsInterface1.', ], [ - 'type' => BreakingChangesFinder::DANGEROUS_CHANGE_TYPE_ADDED_TO_UNION, + 'type' => BreakingChangesFinder::DANGEROUS_CHANGE_TYPE_ADDED_TO_UNION, 'description' => 'TypeInUnion2 was added to union type UnionTypeThatGainsAType.', - ] + ], ]; - $this->assertEquals($expectedDangerousChanges, BreakingChangesFinder::findDangerousChanges($oldSchema, $newSchema)); + $this->assertEquals( + $expectedDangerousChanges, + BreakingChangesFinder::findDangerousChanges($oldSchema, $newSchema) + ); } /** @@ -1825,7 +1873,7 @@ class BreakingChangesFinderTest extends TestCase public function testShouldDetectIfANullableFieldArgumentWasAdded() : void { $oldType = new ObjectType([ - 'name' => 'Type1', + 'name' => 'Type1', 'fields' => [ 'field1' => [ 'type' => Type::string(), @@ -1839,7 +1887,7 @@ class BreakingChangesFinderTest extends TestCase ]); $newType = new ObjectType([ - 'name' => 'Type1', + 'name' => 'Type1', 'fields' => [ 'field1' => [ 'type' => Type::string(), @@ -1868,10 +1916,13 @@ class BreakingChangesFinderTest extends TestCase $expectedFieldChanges = [ [ 'description' => 'A nullable arg arg2 on Type1.field1 was added', - 'type' => BreakingChangesFinder::DANGEROUS_CHANGE_NULLABLE_ARG_ADDED, + 'type' => BreakingChangesFinder::DANGEROUS_CHANGE_NULLABLE_ARG_ADDED, ], ]; - $this->assertEquals($expectedFieldChanges, BreakingChangesFinder::findArgChanges($oldSchema, $newSchema)['dangerousChanges']); + $this->assertEquals( + $expectedFieldChanges, + BreakingChangesFinder::findArgChanges($oldSchema, $newSchema)['dangerousChanges'] + ); } } diff --git a/tests/Utils/BuildSchemaTest.php b/tests/Utils/BuildSchemaTest.php index eb0e619..45dc5dd 100644 --- a/tests/Utils/BuildSchemaTest.php +++ b/tests/Utils/BuildSchemaTest.php @@ -1,4 +1,7 @@ function ($root, $args) { return $args['x'] + $args['y']; - } + }, ]; $result = GraphQL::executeQuery( @@ -71,7 +69,7 @@ class BuildSchemaTest extends TestCase */ public function testSimpleType() : void { - $body = ' + $body = ' type HelloScalars { str: String int: Int @@ -84,12 +82,20 @@ type HelloScalars { $this->assertEquals($output, $body); } + private function cycleOutput($body, $options = []) + { + $ast = Parser::parse($body); + $schema = BuildSchema::buildAST($ast, null, $options); + + return "\n" . SchemaPrinter::doPrint($schema, $options); + } + /** * @see it('With directives') */ public function testWithDirectives() : void { - $body = ' + $body = ' directive @foo(arg: Int) on FIELD type Query { @@ -105,7 +111,7 @@ type Query { */ public function testSupportsDescriptions() : void { - $body = ' + $body = ' """This is a directive""" directive @foo( """It has an argument""" @@ -137,7 +143,7 @@ type Query { */ public function testSupportsOptionForCommentDescriptions() : void { - $body = ' + $body = ' # This is a directive directive @foo( # It has an argument @@ -159,7 +165,7 @@ type Query { str: String } '; - $output = $this->cycleOutput($body, [ 'commentDescriptions' => true ]); + $output = $this->cycleOutput($body, ['commentDescriptions' => true]); $this->assertEquals($body, $output); } @@ -168,7 +174,7 @@ type Query { */ public function testMaintainsSkipAndInclude() : void { - $body = ' + $body = ' type Query { str: String } @@ -185,7 +191,7 @@ type Query { */ public function testOverridingDirectivesExcludesSpecified() : void { - $body = ' + $body = ' directive @skip on FIELD directive @include on FIELD directive @deprecated on FIELD_DEFINITION @@ -206,7 +212,7 @@ type Query { */ public function testAddingDirectivesMaintainsSkipAndInclude() : void { - $body = ' + $body = ' directive @foo(arg: Int) on FIELD type Query { @@ -225,7 +231,7 @@ type Query { */ public function testTypeModifiers() : void { - $body = ' + $body = ' type HelloScalars { nonNullStr: String! listOfStrs: [String] @@ -243,7 +249,7 @@ type HelloScalars { */ public function testRecursiveType() : void { - $body = ' + $body = ' type Query { str: String recurse: Query @@ -258,7 +264,7 @@ type Query { */ public function testTwoTypesCircular() : void { - $body = ' + $body = ' schema { query: TypeOne } @@ -282,7 +288,7 @@ type TypeTwo { */ public function testSingleArgumentField() : void { - $body = ' + $body = ' type Query { str(int: Int): String floatToStr(float: Float): String @@ -300,7 +306,7 @@ type Query { */ public function testSimpleTypeWithMultipleArguments() : void { - $body = ' + $body = ' type Query { str(int: Int, bool: Boolean): String } @@ -314,7 +320,7 @@ type Query { */ public function testSimpleTypeWithInterface() : void { - $body = ' + $body = ' type Query implements WorldInterface { str: String } @@ -332,7 +338,7 @@ interface WorldInterface { */ public function testSimpleOutputEnum() : void { - $body = ' + $body = ' enum Hello { WORLD } @@ -350,7 +356,7 @@ type Query { */ public function testSimpleInputEnum() : void { - $body = ' + $body = ' enum Hello { WORLD } @@ -368,7 +374,7 @@ type Query { */ public function testMultipleValueEnum() : void { - $body = ' + $body = ' enum Hello { WO RLD @@ -387,7 +393,7 @@ type Query { */ public function testSimpleUnion() : void { - $body = ' + $body = ' union Hello = World type Query { @@ -407,7 +413,7 @@ type World { */ public function testMultipleUnion() : void { - $body = ' + $body = ' union Hello = WorldOne | WorldTwo type Query { @@ -431,7 +437,7 @@ type WorldTwo { */ public function testSpecifyingUnionTypeUsingTypename() : void { - $schema = BuildSchema::buildAST(Parser::parse(' + $schema = BuildSchema::buildAST(Parser::parse(' type Query { fruits: [Fruit] } @@ -446,7 +452,7 @@ type WorldTwo { length: Int } ')); - $query = ' + $query = ' { fruits { ... on Apple { @@ -458,25 +464,25 @@ type WorldTwo { } } '; - $root = [ + $root = [ 'fruits' => [ [ - 'color' => 'green', + 'color' => 'green', '__typename' => 'Apple', ], [ - 'length' => 5, + 'length' => 5, '__typename' => 'Banana', - ] - ] + ], + ], ]; $expected = [ 'data' => [ 'fruits' => [ ['color' => 'green'], ['length' => 5], - ] - ] + ], + ], ]; $result = GraphQL::executeQuery($schema, $query, $root); @@ -488,7 +494,7 @@ type WorldTwo { */ public function testSpecifyingInterfaceUsingTypename() : void { - $schema = BuildSchema::buildAST(Parser::parse(' + $schema = BuildSchema::buildAST(Parser::parse(' type Query { characters: [Character] } @@ -507,7 +513,7 @@ type WorldTwo { primaryFunction: String } ')); - $query = ' + $query = ' { characters { name @@ -520,27 +526,27 @@ type WorldTwo { } } '; - $root = [ + $root = [ 'characters' => [ [ - 'name' => 'Han Solo', + 'name' => 'Han Solo', 'totalCredits' => 10, - '__typename' => 'Human', + '__typename' => 'Human', ], [ - 'name' => 'R2-D2', + 'name' => 'R2-D2', 'primaryFunction' => 'Astromech', - '__typename' => 'Droid', - ] - ] + '__typename' => 'Droid', + ], + ], ]; $expected = [ 'data' => [ 'characters' => [ ['name' => 'Han Solo', 'totalCredits' => 10], ['name' => 'R2-D2', 'primaryFunction' => 'Astromech'], - ] - ] + ], + ], ]; $result = GraphQL::executeQuery($schema, $query, $root); @@ -552,7 +558,7 @@ type WorldTwo { */ public function testCustomScalar() : void { - $body = ' + $body = ' scalar CustomScalar type Query { @@ -568,7 +574,7 @@ type Query { */ public function testInputObject() : void { - $body = ' + $body = ' input Input { int: Int } @@ -586,7 +592,7 @@ type Query { */ public function testSimpleArgumentFieldWithDefault() : void { - $body = ' + $body = ' type Query { str(int: Int = 2): String } @@ -600,7 +606,7 @@ type Query { */ public function testCustomScalarArgumentFieldWithDefault() : void { - $body = ' + $body = ' scalar CustomScalar type Query { @@ -616,7 +622,7 @@ type Query { */ public function testSimpleTypeWithMutation() : void { - $body = ' + $body = ' schema { query: HelloScalars mutation: Mutation @@ -641,7 +647,7 @@ type Mutation { */ public function testSimpleTypeWithSubscription() : void { - $body = ' + $body = ' schema { query: HelloScalars subscription: Subscription @@ -666,7 +672,7 @@ type Subscription { */ public function testUnreferencedTypeImplementingReferencedInterface() : void { - $body = ' + $body = ' type Concrete implements Iface { key: String } @@ -688,7 +694,7 @@ type Query { */ public function testUnreferencedTypeImplementingReferencedUnion() : void { - $body = ' + $body = ' type Concrete { key: String } @@ -708,7 +714,7 @@ union Union = Concrete */ public function testSupportsDeprecated() : void { - $body = ' + $body = ' enum MyEnum { VALUE OLD_VALUE @deprecated @@ -724,7 +730,7 @@ type Query { $output = $this->cycleOutput($body); $this->assertEquals($output, $body); - $ast = Parser::parse($body); + $ast = Parser::parse($body); $schema = BuildSchema::buildAST($ast); /** @var EnumType $myEnum */ @@ -785,15 +791,15 @@ type Query { directive @test(arg: TestScalar) on FIELD '); - $schema = BuildSchema::buildAST($schemaAST); + $schema = BuildSchema::buildAST($schemaAST); /** @var ObjectType $query */ - $query = $schema->getType('Query'); - $testInput = $schema->getType('TestInput'); - $testEnum = $schema->getType('TestEnum'); - $testUnion = $schema->getType('TestUnion'); + $query = $schema->getType('Query'); + $testInput = $schema->getType('TestInput'); + $testEnum = $schema->getType('TestEnum'); + $testUnion = $schema->getType('TestUnion'); $testInterface = $schema->getType('TestInterface'); - $testType = $schema->getType('TestType'); - $testScalar = $schema->getType('TestScalar'); + $testType = $schema->getType('TestType'); + $testScalar = $schema->getType('TestScalar'); $testDirective = $schema->getDirective('test'); $restoredIDL = SchemaPrinter::doPrint(BuildSchema::build( @@ -813,9 +819,15 @@ type Query { $testField = $query->getField('testField'); $this->assertEquals('testField(testArg: TestInput): TestUnion', Printer::doPrint($testField->astNode)); $this->assertEquals('testArg: TestInput', Printer::doPrint($testField->args[0]->astNode)); - $this->assertEquals('testInputField: TestEnum', Printer::doPrint($testInput->getField('testInputField')->astNode)); + $this->assertEquals( + 'testInputField: TestEnum', + Printer::doPrint($testInput->getField('testInputField')->astNode) + ); $this->assertEquals('TEST_VALUE', Printer::doPrint($testEnum->getValue('TEST_VALUE')->astNode)); - $this->assertEquals('interfaceField: String', Printer::doPrint($testInterface->getField('interfaceField')->astNode)); + $this->assertEquals( + 'interfaceField: String', + Printer::doPrint($testInterface->getField('interfaceField')->astNode) + ); $this->assertEquals('interfaceField: String', Printer::doPrint($testType->getField('interfaceField')->astNode)); $this->assertEquals('arg: TestScalar', Printer::doPrint($testDirective->args[0]->astNode)); } @@ -893,7 +905,7 @@ type Hello { bar: Bar } '; - $doc = Parser::parse($body); + $doc = Parser::parse($body); BuildSchema::buildAST($doc); } @@ -918,7 +930,7 @@ type Yellow { isColor: Boolean } '; - $doc = Parser::parse($body); + $doc = Parser::parse($body); BuildSchema::buildAST($doc); } @@ -944,7 +956,7 @@ type Yellow { isColor: Boolean } '; - $doc = Parser::parse($body); + $doc = Parser::parse($body); BuildSchema::buildAST($doc); } @@ -970,7 +982,7 @@ type Yellow { isColor: Boolean } '; - $doc = Parser::parse($body); + $doc = Parser::parse($body); BuildSchema::buildAST($doc); } @@ -981,7 +993,7 @@ type Yellow { { $this->expectException(Error::class); $this->expectExceptionMessage('Type "Bar" not found in document.'); - $body = ' + $body = ' schema { query: Hello } @@ -990,7 +1002,7 @@ type Hello { bar: Bar } '; - $doc = Parser::parse($body); + $doc = Parser::parse($body); $schema = BuildSchema::buildAST($doc); $schema->getTypeMap(); } @@ -1002,12 +1014,12 @@ type Hello { { $this->expectException(Error::class); $this->expectExceptionMessage('Type "Bar" not found in document.'); - $body = ' + $body = ' type Query implements Bar { field: String } '; - $doc = Parser::parse($body); + $doc = Parser::parse($body); $schema = BuildSchema::buildAST($doc); $schema->getTypeMap(); } @@ -1019,11 +1031,11 @@ type Query implements Bar { { $this->expectException(Error::class); $this->expectExceptionMessage('Type "Bar" not found in document.'); - $body = ' + $body = ' union TestUnion = Bar type Query { testUnion: TestUnion } '; - $doc = Parser::parse($body); + $doc = Parser::parse($body); $schema = BuildSchema::buildAST($doc); $schema->getTypeMap(); } @@ -1044,7 +1056,7 @@ type Hello { str: String } '; - $doc = Parser::parse($body); + $doc = Parser::parse($body); BuildSchema::buildAST($doc); } @@ -1065,7 +1077,7 @@ type Hello { str: String } '; - $doc = Parser::parse($body); + $doc = Parser::parse($body); BuildSchema::buildAST($doc); } @@ -1091,7 +1103,7 @@ type Wat { str: String } '; - $doc = Parser::parse($body); + $doc = Parser::parse($body); BuildSchema::buildAST($doc); } @@ -1109,7 +1121,7 @@ schema { query Foo { field } '; - $doc = Parser::parse($body); + $doc = Parser::parse($body); BuildSchema::buildAST($doc); } @@ -1127,7 +1139,7 @@ schema { fragment Foo on Type { field } '; - $doc = Parser::parse($body); + $doc = Parser::parse($body); BuildSchema::buildAST($doc); } @@ -1149,7 +1161,7 @@ type Repeated { id: String } '; - $doc = Parser::parse($body); + $doc = Parser::parse($body); $this->expectException(Error::class); $this->expectExceptionMessage('Type "Repeated" was defined more than once.'); @@ -1179,14 +1191,15 @@ interface Hello { world: String } '; - $doc = Parser::parse($body); + $doc = Parser::parse($body); $decorated = []; - $calls = []; + $calls = []; - $typeConfigDecorator = function($defaultConfig, $node, $allNodesMap) use (&$decorated, &$calls) { + $typeConfigDecorator = function ($defaultConfig, $node, $allNodesMap) use (&$decorated, &$calls) { $decorated[] = $defaultConfig['name']; - $calls[] = [$defaultConfig, $node, $allNodesMap]; + $calls[] = [$defaultConfig, $node, $allNodesMap]; + return ['description' => 'My description of ' . $node->name->value] + $defaultConfig; }; @@ -1204,19 +1217,21 @@ interface Hello { $this->assertEquals(array_keys($allNodesMap), ['Query', 'Color', 'Hello']); $this->assertEquals('My description of Query', $schema->getType('Query')->description); - list($defaultConfig, $node, $allNodesMap) = $calls[1]; $this->assertInstanceOf(EnumTypeDefinitionNode::class, $node); $this->assertEquals('Color', $defaultConfig['name']); $enumValue = [ - 'description' => '', - 'deprecationReason' => '' + 'description' => '', + 'deprecationReason' => '', ]; - $this->assertArraySubset([ - 'RED' => $enumValue, - 'GREEN' => $enumValue, - 'BLUE' => $enumValue, - ], $defaultConfig['values']); + $this->assertArraySubset( + [ + 'RED' => $enumValue, + 'GREEN' => $enumValue, + 'BLUE' => $enumValue, + ], + $defaultConfig['values'] + ); $this->assertCount(4, $defaultConfig); // 3 + astNode $this->assertEquals(array_keys($allNodesMap), ['Query', 'Color', 'Hello']); $this->assertEquals('My description of Color', $schema->getType('Color')->description); @@ -1233,7 +1248,7 @@ interface Hello { public function testCreatesTypesLazily() : void { - $body = ' + $body = ' schema { query: Query } @@ -1258,11 +1273,12 @@ type World implements Hello { world: String } '; - $doc = Parser::parse($body); + $doc = Parser::parse($body); $created = []; - $typeConfigDecorator = function($config, $node) use (&$created) { + $typeConfigDecorator = function ($config, $node) use (&$created) { $created[] = $node->name->value; + return $config; }; @@ -1282,5 +1298,4 @@ type World implements Hello { $this->assertArrayHasKey('Hello', $types); $this->assertArrayHasKey('World', $types); } - } diff --git a/tests/Utils/CoerceValueTest.php b/tests/Utils/CoerceValueTest.php index 0585214..803e272 100644 --- a/tests/Utils/CoerceValueTest.php +++ b/tests/Utils/CoerceValueTest.php @@ -1,8 +1,9 @@ testEnum = new EnumType([ - 'name' => 'TestEnum', + 'name' => 'TestEnum', 'values' => [ 'FOO' => 'InternalFoo', 'BAR' => 123456789, @@ -26,7 +30,7 @@ class CoerceValueTest extends TestCase ]); $this->testInputObject = new InputObjectType([ - 'name' => 'TestInputObject', + 'name' => 'TestInputObject', 'fields' => [ 'foo' => Type::nonNull(Type::int()), 'bar' => Type::int(), @@ -34,9 +38,9 @@ class CoerceValueTest extends TestCase ]); } - // Describe: coerceValue - /** + * Describe: coerceValue + * * @see it('coercing an array to GraphQLString produces an error') */ public function testCoercingAnArrayToGraphQLStringProducesAnError() : void @@ -53,7 +57,17 @@ class CoerceValueTest extends TestCase ); } - // Describe: for GraphQLInt + /** + * Describe: for GraphQLInt + */ + private function expectError($result, $expected) + { + $this->assertInternalType('array', $result); + $this->assertInternalType('array', $result['errors']); + $this->assertCount(1, $result['errors']); + $this->assertEquals($expected, $result['errors'][0]->getMessage()); + $this->assertEquals(Utils::undefined(), $result['value']); + } /** * @see it('returns no error for int input') @@ -64,6 +78,13 @@ class CoerceValueTest extends TestCase $this->expectNoErrors($result); } + private function expectNoErrors($result) + { + $this->assertInternalType('array', $result); + $this->assertNull($result['errors']); + $this->assertNotEquals(Utils::undefined(), $result['value']); + } + /** * @see it('returns no error for negative int input') */ @@ -115,6 +136,8 @@ class CoerceValueTest extends TestCase ); } + // Describe: for GraphQLFloat + /** * @see it('returns a single error for char input') */ @@ -139,8 +162,6 @@ class CoerceValueTest extends TestCase ); } - // Describe: for GraphQLFloat - /** * @see it('returns no error for int input') */ @@ -189,6 +210,8 @@ class CoerceValueTest extends TestCase ); } + // DESCRIBE: for GraphQLEnum + /** * @see it('returns a single error for char input') */ @@ -213,8 +236,6 @@ class CoerceValueTest extends TestCase ); } - // DESCRIBE: for GraphQLEnum - /** * @see it('returns no error for a known enum name') */ @@ -229,6 +250,8 @@ class CoerceValueTest extends TestCase $this->assertEquals(123456789, $barResult['value']); } + // DESCRIBE: for GraphQLInputObject + /** * @see it('results error for misspelled enum value') */ @@ -250,8 +273,6 @@ class CoerceValueTest extends TestCase $this->expectError($result2, 'Expected type TestEnum.'); } - // DESCRIBE: for GraphQLInputObject - /** * @see it('returns no error for a valid input') */ @@ -277,7 +298,10 @@ class CoerceValueTest extends TestCase public function testReturnErrorForAnInvalidField() : void { $result = Value::coerceValue(['foo' => 'abc'], $this->testInputObject); - $this->expectError($result, 'Expected type Int at value.foo; Int cannot represent non 32-bit signed integer value: abc'); + $this->expectError( + $result, + 'Expected type Int at value.foo; Int cannot represent non 32-bit signed integer value: abc' + ); } /** @@ -286,10 +310,13 @@ class CoerceValueTest extends TestCase public function testReturnsMultipleErrorsForMultipleInvalidFields() : void { $result = Value::coerceValue(['foo' => 'abc', 'bar' => 'def'], $this->testInputObject); - $this->assertEquals([ - 'Expected type Int at value.foo; Int cannot represent non 32-bit signed integer value: abc', - 'Expected type Int at value.bar; Int cannot represent non 32-bit signed integer value: def', - ], $result['errors']); + $this->assertEquals( + [ + 'Expected type Int at value.foo; Int cannot represent non 32-bit signed integer value: abc', + 'Expected type Int at value.bar; Int cannot represent non 32-bit signed integer value: def', + ], + $result['errors'] + ); } /** @@ -318,20 +345,4 @@ class CoerceValueTest extends TestCase $result = Value::coerceValue(['foo' => 123, 'bart' => 123], $this->testInputObject); $this->expectError($result, 'Field "bart" is not defined by type TestInputObject; did you mean bar?'); } - - private function expectNoErrors($result) - { - $this->assertInternalType('array', $result); - $this->assertNull($result['errors']); - $this->assertNotEquals(Utils::undefined(), $result['value']); - } - - - private function expectError($result, $expected) { - $this->assertInternalType('array', $result); - $this->assertInternalType('array', $result['errors']); - $this->assertCount(1, $result['errors']); - $this->assertEquals($expected, $result['errors'][0]->getMessage()); - $this->assertEquals(Utils::undefined(), $result['value']); - } } diff --git a/tests/Utils/ExtractTypesTest.php b/tests/Utils/ExtractTypesTest.php index 8818899..33f60cf 100644 --- a/tests/Utils/ExtractTypesTest.php +++ b/tests/Utils/ExtractTypesTest.php @@ -1,5 +1,8 @@ node = new InterfaceType([ - 'name' => 'Node', + 'name' => 'Node', 'fields' => [ - 'id' => Type::string() - ] + 'id' => Type::string(), + ], ]); $this->content = new InterfaceType([ - 'name' => 'Content', - 'fields' => function() { + 'name' => 'Content', + 'fields' => function () { return [ - 'title' => Type::string(), - 'body' => Type::string(), - 'author' => $this->user, - 'comments' => Type::listOf($this->comment), - 'categories' => Type::listOf($this->category) + 'title' => Type::string(), + 'body' => Type::string(), + 'author' => $this->user, + 'comments' => Type::listOf($this->comment), + 'categories' => Type::listOf($this->category), ]; - } + }, ]); $this->blogStory = new ObjectType([ - 'name' => 'BlogStory', + 'name' => 'BlogStory', 'interfaces' => [ $this->node, - $this->content + $this->content, ], - 'fields' => function() { - return [ - $this->node->getField('id'), - $this->content->getField('title'), - $this->content->getField('body'), - $this->content->getField('author'), - $this->content->getField('comments'), - $this->content->getField('categories') - ]; - }, - ]); - - $this->link = new ObjectType([ - 'name' => 'Link', - 'interfaces' => [ - $this->node, - $this->content - ], - 'fields' => function() { + 'fields' => function () { return [ $this->node->getField('id'), $this->content->getField('title'), @@ -134,156 +90,176 @@ class ExtractTypesTest extends TestCase $this->content->getField('author'), $this->content->getField('comments'), $this->content->getField('categories'), - 'url' => Type::string() ]; }, ]); - $this->video = new ObjectType([ - 'name' => 'Video', + new ObjectType([ + 'name' => 'Link', 'interfaces' => [ $this->node, - $this->content + $this->content, ], - 'fields' => function() { + 'fields' => function () { return [ - $this->node->getField('id'), - $this->content->getField('title'), - $this->content->getField('body'), - $this->content->getField('author'), - $this->content->getField('comments'), - $this->content->getField('categories'), - 'streamUrl' => Type::string(), + 'id' => $this->node->getField('id'), + 'title' => $this->content->getField('title'), + 'body' => $this->content->getField('body'), + 'author' => $this->content->getField('author'), + 'comments' => $this->content->getField('comments'), + 'categories' => $this->content->getField('categories'), + 'url' => Type::string(), + ]; + }, + ]); + + new ObjectType([ + 'name' => 'Video', + 'interfaces' => [ + $this->node, + $this->content, + ], + 'fields' => function () { + return [ + 'id' => $this->node->getField('id'), + 'title' => $this->content->getField('title'), + 'body' => $this->content->getField('body'), + 'author' => $this->content->getField('author'), + 'comments' => $this->content->getField('comments'), + 'categories' => $this->content->getField('categories'), + 'streamUrl' => Type::string(), 'downloadUrl' => Type::string(), - 'metadata' => $this->videoMetadata = new ObjectType([ - 'name' => 'VideoMetadata', + 'metadata' => new ObjectType([ + 'name' => 'VideoMetadata', 'fields' => [ 'lat' => Type::float(), - 'lng' => Type::float() - ] - ]) + 'lng' => Type::float(), + ], + ]), ]; - } + }, ]); $this->comment = new ObjectType([ - 'name' => 'Comment', + 'name' => 'Comment', 'interfaces' => [ - $this->node + $this->node, ], - 'fields' => function() { + 'fields' => function () { return [ - $this->node->getField('id'), - 'author' => $this->user, - 'text' => Type::string(), + 'id' => $this->node->getField('id'), + 'author' => $this->user, + 'text' => Type::string(), 'replies' => Type::listOf($this->comment), - 'parent' => $this->comment, - 'content' => $this->content + 'parent' => $this->comment, + 'content' => $this->content, ]; - } + }, ]); $this->user = new ObjectType([ - 'name' => 'User', + 'name' => 'User', 'interfaces' => [ - $this->node + $this->node, ], - 'fields' => function() { + 'fields' => function () { return [ - $this->node->getField('id'), + 'id' => $this->node->getField('id'), 'name' => Type::string(), ]; - } + }, ]); $this->category = new ObjectType([ - 'name' => 'Category', + 'name' => 'Category', 'interfaces' => [ - $this->node + $this->node, ], - 'fields' => function() { + 'fields' => function () { return [ - $this->node->getField('id'), - 'name' => Type::string() + 'id' => $this->node->getField('id'), + 'name' => Type::string(), ]; - } + }, ]); $this->mention = new UnionType([ - 'name' => 'Mention', + 'name' => 'Mention', 'types' => [ $this->user, - $this->category - ] + $this->category, + ], ]); $this->query = new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ - 'viewer' => $this->user, + 'viewer' => $this->user, 'latestContent' => $this->content, - 'node' => $this->node, - 'mentions' => Type::listOf($this->mention) - ] + 'node' => $this->node, + 'mentions' => Type::listOf($this->mention), + ], + ]); + + $this->postStoryMutationInput = new InputObjectType([ + 'name' => 'PostStoryMutationInput', + 'fields' => [ + 'title' => Type::string(), + 'body' => Type::string(), + 'author' => Type::id(), + 'category' => Type::id(), + ], ]); $this->mutation = new ObjectType([ - 'name' => 'Mutation', + 'name' => 'Mutation', 'fields' => [ - 'postStory' => [ + 'postStory' => [ 'type' => $this->postStoryMutation = new ObjectType([ - 'name' => 'PostStoryMutation', + 'name' => 'PostStoryMutation', 'fields' => [ - 'story' => $this->blogStory - ] + 'story' => $this->blogStory, + ], ]), 'args' => [ - 'input' => Type::nonNull($this->postStoryMutationInput = new InputObjectType([ - 'name' => 'PostStoryMutationInput', - 'fields' => [ - 'title' => Type::string(), - 'body' => Type::string(), - 'author' => Type::id(), - 'category' => Type::id() - ] - ])), - 'clientRequestId' => Type::string() - ] + 'input' => Type::nonNull($this->postStoryMutationInput), + 'clientRequestId' => Type::string(), + ], ], 'postComment' => [ 'type' => $this->postCommentMutation = new ObjectType([ - 'name' => 'PostCommentMutation', + 'name' => 'PostCommentMutation', 'fields' => [ - 'comment' => $this->comment - ] + 'comment' => $this->comment, + ], ]), 'args' => [ - 'input' => Type::nonNull($this->postCommentMutationInput = new InputObjectType([ - 'name' => 'PostCommentMutationInput', + 'input' => Type::nonNull($this->postCommentMutationInput = new InputObjectType([ + 'name' => 'PostCommentMutationInput', 'fields' => [ - 'text' => Type::nonNull(Type::string()), - 'author' => Type::nonNull(Type::id()), + 'text' => Type::nonNull(Type::string()), + 'author' => Type::nonNull(Type::id()), 'content' => Type::id(), - 'parent' => Type::id() - ] + 'parent' => Type::id(), + ], ])), - 'clientRequestId' => Type::string() - ] - ] - ] + 'clientRequestId' => Type::string(), + ], + ], + ], ]); } public function testExtractTypesFromQuery() : void { $expectedTypeMap = [ - 'Query' => $this->query, - 'User' => $this->user, - 'Node' => $this->node, - 'String' => Type::string(), - 'Content' => $this->content, - 'Comment' => $this->comment, - 'Mention' => $this->mention, + 'Query' => $this->query, + 'User' => $this->user, + 'Node' => $this->node, + 'String' => Type::string(), + 'Content' => $this->content, + 'Comment' => $this->comment, + 'Mention' => $this->mention, 'Category' => $this->category, ]; @@ -294,19 +270,19 @@ class ExtractTypesTest extends TestCase public function testExtractTypesFromMutation() : void { $expectedTypeMap = [ - 'Mutation' => $this->mutation, - 'User' => $this->user, - 'Node' => $this->node, - 'String' => Type::string(), - 'Content' => $this->content, - 'Comment' => $this->comment, - 'BlogStory' => $this->blogStory, - 'Category' => $this->category, - 'PostStoryMutationInput' => $this->postStoryMutationInput, - 'ID' => Type::id(), - 'PostStoryMutation' => $this->postStoryMutation, + 'Mutation' => $this->mutation, + 'User' => $this->user, + 'Node' => $this->node, + 'String' => Type::string(), + 'Content' => $this->content, + 'Comment' => $this->comment, + 'BlogStory' => $this->blogStory, + 'Category' => $this->category, + 'PostStoryMutationInput' => $this->postStoryMutationInput, + 'ID' => Type::id(), + 'PostStoryMutation' => $this->postStoryMutation, 'PostCommentMutationInput' => $this->postCommentMutationInput, - 'PostCommentMutation' => $this->postCommentMutation, + 'PostCommentMutation' => $this->postCommentMutation, ]; $actualTypeMap = TypeInfo::extractTypes($this->mutation); @@ -316,16 +292,16 @@ class ExtractTypesTest extends TestCase public function testThrowsOnMultipleTypesWithSameName() : void { $otherUserType = new ObjectType([ - 'name' => 'User', - 'fields' => ['a' => Type::string()] + 'name' => 'User', + 'fields' => ['a' => Type::string()], ]); $queryType = new ObjectType([ - 'name' => 'Test', + 'name' => 'Test', 'fields' => [ 'otherUser' => $otherUserType, - 'user' => $this->user - ] + 'user' => $this->user, + ], ]); $this->expectException(InvariantViolation::class); diff --git a/tests/Utils/IsValidLiteralValueTest.php b/tests/Utils/IsValidLiteralValueTest.php index 9f322e4..0e4bc04 100644 --- a/tests/Utils/IsValidLiteralValueTest.php +++ b/tests/Utils/IsValidLiteralValueTest.php @@ -1,17 +1,18 @@ mixedStore = new MixedStore(); } + public function testAcceptsNullKeys() : void + { + foreach ($this->getPossibleValues() as $value) { + $this->assertAcceptsKeyValue(null, $value); + } + } + public function getPossibleValues() { return [ @@ -30,16 +37,38 @@ class MixedStoreTest extends TestCase 'a', [], new \stdClass(), - function() {}, - new MixedStore() + function () { + }, + new MixedStore(), ]; } - public function testAcceptsNullKeys() : void + private function assertAcceptsKeyValue($key, $value) { - foreach ($this->getPossibleValues() as $value) { - $this->assertAcceptsKeyValue(null, $value); - } + $err = 'Failed assertion that MixedStore accepts key ' . + Utils::printSafe($key) . ' with value ' . Utils::printSafe($value); + + $this->assertFalse($this->mixedStore->offsetExists($key), $err); + $this->mixedStore->offsetSet($key, $value); + $this->assertTrue($this->mixedStore->offsetExists($key), $err); + $this->assertSame($value, $this->mixedStore->offsetGet($key), $err); + $this->mixedStore->offsetUnset($key); + $this->assertFalse($this->mixedStore->offsetExists($key), $err); + $this->assertProvidesArrayAccess($key, $value); + } + + private function assertProvidesArrayAccess($key, $value) + { + $err = 'Failed assertion that MixedStore provides array access for key ' . + Utils::printSafe($key) . ' with value ' . Utils::printSafe($value); + + $this->assertFalse(isset($this->mixedStore[$key]), $err); + $this->mixedStore[$key] = $value; + $this->assertTrue(isset($this->mixedStore[$key]), $err); + $this->assertEquals(! empty($value), ! empty($this->mixedStore[$key]), $err); + $this->assertSame($value, $this->mixedStore[$key], $err); + unset($this->mixedStore[$key]); + $this->assertFalse(isset($this->mixedStore[$key]), $err); } public function testAcceptsBoolKeys() : void @@ -93,35 +122,11 @@ class MixedStoreTest extends TestCase foreach ($this->getPossibleValues() as $value) { $this->assertAcceptsKeyValue(new \stdClass(), $value); $this->assertAcceptsKeyValue(new MixedStore(), $value); - $this->assertAcceptsKeyValue(function() {}, $value); + $this->assertAcceptsKeyValue( + function () { + }, + $value + ); } } - - private function assertAcceptsKeyValue($key, $value) - { - $err = 'Failed assertion that MixedStore accepts key ' . - Utils::printSafe($key) . ' with value ' . Utils::printSafe($value); - - $this->assertFalse($this->mixedStore->offsetExists($key), $err); - $this->mixedStore->offsetSet($key, $value); - $this->assertTrue($this->mixedStore->offsetExists($key), $err); - $this->assertSame($value, $this->mixedStore->offsetGet($key), $err); - $this->mixedStore->offsetUnset($key); - $this->assertFalse($this->mixedStore->offsetExists($key), $err); - $this->assertProvidesArrayAccess($key, $value); - } - - private function assertProvidesArrayAccess($key, $value) - { - $err = 'Failed assertion that MixedStore provides array access for key ' . - Utils::printSafe($key) . ' with value ' . Utils::printSafe($value); - - $this->assertFalse(isset($this->mixedStore[$key]), $err); - $this->mixedStore[$key] = $value; - $this->assertTrue(isset($this->mixedStore[$key]), $err); - $this->assertEquals(!empty($value), !empty($this->mixedStore[$key]), $err); - $this->assertSame($value, $this->mixedStore[$key], $err); - unset($this->mixedStore[$key]); - $this->assertFalse(isset($this->mixedStore[$key]), $err); - } } diff --git a/tests/Utils/QuotedOrListTest.php b/tests/Utils/QuotedOrListTest.php index 4b0c7b1..65d393b 100644 --- a/tests/Utils/QuotedOrListTest.php +++ b/tests/Utils/QuotedOrListTest.php @@ -1,16 +1,15 @@ assertEquals($schemaText, SchemaPrinter::doPrint(BuildSchema::build($schemaText))); - return "\n" . $schemaText; - } - - private function printSingleFieldSchema($fieldConfig) - { - $query = new ObjectType([ - 'name' => 'Query', - 'fields' => [ - 'singleField' => $fieldConfig - ] - ]); - return $this->printForTest(new Schema(['query' => $query])); - } - /** * @see it('Prints String Field') */ public function testPrintsStringField() : void { $output = $this->printSingleFieldSchema([ - 'type' => Type::string() + 'type' => Type::string(), ]); - $this->assertEquals(' + $this->assertEquals( + ' type Query { singleField: String } -', $output); +', + $output + ); + } + + private function printSingleFieldSchema($fieldConfig) + { + $query = new ObjectType([ + 'name' => 'Query', + 'fields' => ['singleField' => $fieldConfig], + ]); + + return $this->printForTest(new Schema(['query' => $query])); + } + + private function printForTest($schema) + { + $schemaText = SchemaPrinter::doPrint($schema); + $this->assertEquals($schemaText, SchemaPrinter::doPrint(BuildSchema::build($schemaText))); + + return "\n" . $schemaText; } /** @@ -58,13 +64,16 @@ type Query { public function testPrintArrayStringField() : void { $output = $this->printSingleFieldSchema([ - 'type' => Type::listOf(Type::string()) + 'type' => Type::listOf(Type::string()), ]); - $this->assertEquals(' + $this->assertEquals( + ' type Query { singleField: [String] } -', $output); +', + $output + ); } /** @@ -73,13 +82,16 @@ type Query { public function testPrintNonNullStringField() : void { $output = $this->printSingleFieldSchema([ - 'type' => Type::nonNull(Type::string()) + 'type' => Type::nonNull(Type::string()), ]); - $this->assertEquals(' + $this->assertEquals( + ' type Query { singleField: String! } -', $output); +', + $output + ); } /** @@ -88,13 +100,16 @@ type Query { public function testPrintNonNullArrayStringField() : void { $output = $this->printSingleFieldSchema([ - 'type' => Type::nonNull(Type::listOf(Type::string())) + 'type' => Type::nonNull(Type::listOf(Type::string())), ]); - $this->assertEquals(' + $this->assertEquals( + ' type Query { singleField: [String]! } -', $output); +', + $output + ); } /** @@ -103,13 +118,16 @@ type Query { public function testPrintArrayNonNullStringField() : void { $output = $this->printSingleFieldSchema([ - 'type' => Type::listOf(Type::nonNull(Type::string())) + 'type' => Type::listOf(Type::nonNull(Type::string())), ]); - $this->assertEquals(' + $this->assertEquals( + ' type Query { singleField: [String!] } -', $output); +', + $output + ); } /** @@ -118,13 +136,16 @@ type Query { public function testPrintNonNullArrayNonNullStringField() : void { $output = $this->printSingleFieldSchema([ - 'type' => Type::nonNull(Type::listOf(Type::nonNull(Type::string()))) + 'type' => Type::nonNull(Type::listOf(Type::nonNull(Type::string()))), ]); - $this->assertEquals(' + $this->assertEquals( + ' type Query { singleField: [String!]! } -', $output); +', + $output + ); } /** @@ -133,18 +154,19 @@ type Query { public function testPrintObjectField() : void { $fooType = new ObjectType([ - 'name' => 'Foo', - 'fields' => ['str' => ['type' => Type::string()]] + 'name' => 'Foo', + 'fields' => ['str' => ['type' => Type::string()]], ]); $root = new ObjectType([ - 'name' => 'Query', - 'fields' => ['foo' => ['type' => $fooType]] + 'name' => 'Query', + 'fields' => ['foo' => ['type' => $fooType]], ]); $schema = new Schema(['query' => $root]); $output = $this->printForTest($schema); - $this->assertEquals(' + $this->assertEquals( + ' type Foo { str: String } @@ -152,7 +174,9 @@ type Foo { type Query { foo: Foo } -', $output); +', + $output + ); } /** @@ -162,13 +186,16 @@ type Query { { $output = $this->printSingleFieldSchema([ 'type' => Type::string(), - 'args' => ['argOne' => ['type' => Type::int()]] + 'args' => ['argOne' => ['type' => Type::int()]], ]); - $this->assertEquals(' + $this->assertEquals( + ' type Query { singleField(argOne: Int): String } -', $output); +', + $output + ); } /** @@ -178,13 +205,16 @@ type Query { { $output = $this->printSingleFieldSchema([ 'type' => Type::string(), - 'args' => ['argOne' => ['type' => Type::int(), 'defaultValue' => 2]] + 'args' => ['argOne' => ['type' => Type::int(), 'defaultValue' => 2]], ]); - $this->assertEquals(' + $this->assertEquals( + ' type Query { singleField(argOne: Int = 2): String } -', $output); +', + $output + ); } /** @@ -196,11 +226,14 @@ type Query { 'type' => Type::string(), 'args' => ['argOne' => ['type' => Type::string(), 'defaultValue' => "tes\t de\fault"]], ]); - $this->assertEquals(' + $this->assertEquals( + ' type Query { singleField(argOne: String = "tes\t de\fault"): String } -', $output); +', + $output + ); } /** @@ -210,13 +243,16 @@ type Query { { $output = $this->printSingleFieldSchema([ 'type' => Type::string(), - 'args' => ['argOne' => ['type' => Type::int(), 'defaultValue' => null]] + 'args' => ['argOne' => ['type' => Type::int(), 'defaultValue' => null]], ]); - $this->assertEquals(' + $this->assertEquals( + ' type Query { singleField(argOne: Int = null): String } -', $output); +', + $output + ); } /** @@ -226,13 +262,16 @@ type Query { { $output = $this->printSingleFieldSchema([ 'type' => Type::string(), - 'args' => ['argOne' => ['type' => Type::nonNull(Type::int())]] + 'args' => ['argOne' => ['type' => Type::nonNull(Type::int())]], ]); - $this->assertEquals(' + $this->assertEquals( + ' type Query { singleField(argOne: Int!): String } -', $output); +', + $output + ); } /** @@ -244,14 +283,17 @@ type Query { 'type' => Type::string(), 'args' => [ 'argOne' => ['type' => Type::int()], - 'argTwo' => ['type' => Type::string()] - ] + 'argTwo' => ['type' => Type::string()], + ], ]); - $this->assertEquals(' + $this->assertEquals( + ' type Query { singleField(argOne: Int, argTwo: String): String } -', $output); +', + $output + ); } /** @@ -262,16 +304,19 @@ type Query { $output = $this->printSingleFieldSchema([ 'type' => Type::string(), 'args' => [ - 'argOne' => ['type' => Type::int(), 'defaultValue' => 1], - 'argTwo' => ['type' => Type::string()], - 'argThree' => ['type' => Type::boolean()] - ] + 'argOne' => ['type' => Type::int(), 'defaultValue' => 1], + 'argTwo' => ['type' => Type::string()], + 'argThree' => ['type' => Type::boolean()], + ], ]); - $this->assertEquals(' + $this->assertEquals( + ' type Query { singleField(argOne: Int = 1, argTwo: String, argThree: Boolean): String } -', $output); +', + $output + ); } /** @@ -282,16 +327,19 @@ type Query { $output = $this->printSingleFieldSchema([ 'type' => Type::string(), 'args' => [ - 'argOne' => ['type' => Type::int()], - 'argTwo' => ['type' => Type::string(), 'defaultValue' => 'foo'], - 'argThree' => ['type' => Type::boolean()] - ] + 'argOne' => ['type' => Type::int()], + 'argTwo' => ['type' => Type::string(), 'defaultValue' => 'foo'], + 'argThree' => ['type' => Type::boolean()], + ], ]); - $this->assertEquals(' + $this->assertEquals( + ' type Query { singleField(argOne: Int, argTwo: String = "foo", argThree: Boolean): String } -', $output); +', + $output + ); } /** @@ -302,16 +350,19 @@ type Query { $output = $this->printSingleFieldSchema([ 'type' => Type::string(), 'args' => [ - 'argOne' => ['type' => Type::int()], - 'argTwo' => ['type' => Type::string()], - 'argThree' => ['type' => Type::boolean(), 'defaultValue' => false] - ] + 'argOne' => ['type' => Type::int()], + 'argTwo' => ['type' => Type::string()], + 'argThree' => ['type' => Type::boolean(), 'defaultValue' => false], + ], ]); - $this->assertEquals(' + $this->assertEquals( + ' type Query { singleField(argOne: Int, argTwo: String, argThree: Boolean = false): String } -', $output); +', + $output + ); } /** @@ -320,14 +371,12 @@ type Query { public function testPrintsCustomQueryRootType() : void { $customQueryType = new ObjectType([ - 'name' => 'CustomQueryType', + 'name' => 'CustomQueryType', 'fields' => ['bar' => ['type' => Type::string()]], ]); - $schema = new Schema([ - 'query' => $customQueryType, - ]); - $output = $this->printForTest($schema); + $schema = new Schema(['query' => $customQueryType]); + $output = $this->printForTest($schema); $expected = ' schema { query: CustomQueryType @@ -346,27 +395,28 @@ type CustomQueryType { public function testPrintInterface() : void { $fooType = new InterfaceType([ - 'name' => 'Foo', - 'fields' => ['str' => ['type' => Type::string()]] + 'name' => 'Foo', + 'fields' => ['str' => ['type' => Type::string()]], ]); $barType = new ObjectType([ - 'name' => 'Bar', - 'fields' => ['str' => ['type' => Type::string()]], - 'interfaces' => [$fooType] + 'name' => 'Bar', + 'fields' => ['str' => ['type' => Type::string()]], + 'interfaces' => [$fooType], ]); $query = new ObjectType([ - 'name' => 'Query', - 'fields' => ['bar' => ['type' => $barType]] + 'name' => 'Query', + 'fields' => ['bar' => ['type' => $barType]], ]); $schema = new Schema([ 'query' => $query, - 'types' => [$barType] + 'types' => [$barType], ]); $output = $this->printForTest($schema); - $this->assertEquals(' + $this->assertEquals( + ' type Bar implements Foo { str: String } @@ -378,7 +428,9 @@ interface Foo { type Query { bar: Bar } -', $output); +', + $output + ); } /** @@ -387,35 +439,36 @@ type Query { public function testPrintMultipleInterface() : void { $fooType = new InterfaceType([ - 'name' => 'Foo', - 'fields' => ['str' => ['type' => Type::string()]] + 'name' => 'Foo', + 'fields' => ['str' => ['type' => Type::string()]], ]); $baazType = new InterfaceType([ - 'name' => 'Baaz', - 'fields' => ['int' => ['type' => Type::int()]] + 'name' => 'Baaz', + 'fields' => ['int' => ['type' => Type::int()]], ]); $barType = new ObjectType([ - 'name' => 'Bar', - 'fields' => [ + 'name' => 'Bar', + 'fields' => [ 'str' => ['type' => Type::string()], - 'int' => ['type' => Type::int()] + 'int' => ['type' => Type::int()], ], - 'interfaces' => [$fooType, $baazType] + 'interfaces' => [$fooType, $baazType], ]); $query = new ObjectType([ - 'name' => 'Query', - 'fields' => ['bar' => ['type' => $barType]] + 'name' => 'Query', + 'fields' => ['bar' => ['type' => $barType]], ]); $schema = new Schema([ 'query' => $query, - 'types' => [$barType] + 'types' => [$barType], ]); $output = $this->printForTest($schema); - $this->assertEquals(' + $this->assertEquals( + ' interface Baaz { int: Int } @@ -432,7 +485,9 @@ interface Foo { type Query { bar: Bar } -', $output); +', + $output + ); } /** @@ -441,36 +496,37 @@ type Query { public function testPrintUnions() : void { $fooType = new ObjectType([ - 'name' => 'Foo', - 'fields' => ['bool' => ['type' => Type::boolean()]] + 'name' => 'Foo', + 'fields' => ['bool' => ['type' => Type::boolean()]], ]); $barType = new ObjectType([ - 'name' => 'Bar', - 'fields' => ['str' => ['type' => Type::string()]] + 'name' => 'Bar', + 'fields' => ['str' => ['type' => Type::string()]], ]); $singleUnion = new UnionType([ - 'name' => 'SingleUnion', - 'types' => [$fooType] + 'name' => 'SingleUnion', + 'types' => [$fooType], ]); $multipleUnion = new UnionType([ - 'name' => 'MultipleUnion', - 'types' => [$fooType, $barType] + 'name' => 'MultipleUnion', + 'types' => [$fooType, $barType], ]); $query = new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ - 'single' => ['type' => $singleUnion], - 'multiple' => ['type' => $multipleUnion] - ] + 'single' => ['type' => $singleUnion], + 'multiple' => ['type' => $multipleUnion], + ], ]); $schema = new Schema(['query' => $query]); $output = $this->printForTest($schema); - $this->assertEquals(' + $this->assertEquals( + ' type Bar { str: String } @@ -487,7 +543,9 @@ type Query { } union SingleUnion = Foo -', $output); +', + $output + ); } /** @@ -496,23 +554,24 @@ union SingleUnion = Foo public function testInputType() : void { $inputType = new InputObjectType([ - 'name' => 'InputType', - 'fields' => ['int' => ['type' => Type::int()]] + 'name' => 'InputType', + 'fields' => ['int' => ['type' => Type::int()]], ]); $query = new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ 'str' => [ 'type' => Type::string(), - 'args' => ['argOne' => ['type' => $inputType]] - ] - ] + 'args' => ['argOne' => ['type' => $inputType]], + ], + ], ]); $schema = new Schema(['query' => $query]); $output = $this->printForTest($schema); - $this->assertEquals(' + $this->assertEquals( + ' input InputType { int: Int } @@ -520,7 +579,9 @@ input InputType { type Query { str(argOne: InputType): String } -', $output); +', + $output + ); } /** @@ -529,28 +590,31 @@ type Query { public function testCustomScalar() : void { $oddType = new CustomScalarType([ - 'name' => 'Odd', - 'serialize' => function($value) { + 'name' => 'Odd', + 'serialize' => function ($value) { return $value % 2 === 1 ? $value : null; - } + }, ]); $query = new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ - 'odd' => ['type' => $oddType] - ] + 'odd' => ['type' => $oddType], + ], ]); $schema = new Schema(['query' => $query]); $output = $this->printForTest($schema); - $this->assertEquals(' + $this->assertEquals( + ' scalar Odd type Query { odd: Odd } -', $output); +', + $output + ); } /** @@ -559,24 +623,25 @@ type Query { public function testEnum() : void { $RGBType = new EnumType([ - 'name' => 'RGB', + 'name' => 'RGB', 'values' => [ - 'RED' => ['value' => 0], + 'RED' => ['value' => 0], 'GREEN' => ['value' => 1], - 'BLUE' => ['value' => 2] - ] + 'BLUE' => ['value' => 2], + ], ]); $query = new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ - 'rgb' => ['type' => $RGBType] - ] + 'rgb' => ['type' => $RGBType], + ], ]); $schema = new Schema(['query' => $query]); $output = $this->printForTest($schema); - $this->assertEquals(' + $this->assertEquals( + ' type Query { rgb: RGB } @@ -586,7 +651,9 @@ enum RGB { GREEN BLUE } -', $output); +', + $output + ); } /** @@ -595,32 +662,35 @@ enum RGB { public function testPrintsCustomDirectives() : void { $query = new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ 'field' => ['type' => Type::string()], - ] + ], ]); $customDirectives = new Directive([ - 'name' => 'customDirective', + 'name' => 'customDirective', 'locations' => [ - DirectiveLocation::FIELD - ] + DirectiveLocation::FIELD, + ], ]); $schema = new Schema([ - 'query' => $query, + 'query' => $query, 'directives' => [$customDirectives], ]); $output = $this->printForTest($schema); - $this->assertEquals(' + $this->assertEquals( + ' directive @customDirective on FIELD type Query { field: String } -', $output); +', + $output + ); } /** @@ -629,19 +699,22 @@ type Query { public function testOneLinePrintsAShortDescription() : void { $description = 'This field is awesome'; - $output = $this->printSingleFieldSchema([ - "type" => Type::string(), - "description" => $description + $output = $this->printSingleFieldSchema([ + 'type' => Type::string(), + 'description' => $description, ]); - $this->assertEquals(' + $this->assertEquals( + ' type Query { """This field is awesome""" singleField: String } -', $output); +', + $output + ); - $recreatedRoot = BuildSchema::build($output)->getTypeMap()['Query']; + $recreatedRoot = BuildSchema::build($output)->getTypeMap()['Query']; $recreatedField = $recreatedRoot->getFields()['singleField']; $this->assertEquals($description, $recreatedField->description); } @@ -652,21 +725,24 @@ type Query { public function testDoesNotOneLinePrintADescriptionThatEndsWithAQuote() : void { $description = 'This field is "awesome"'; - $output = $this->printSingleFieldSchema([ - "type" => Type::string(), - "description" => $description + $output = $this->printSingleFieldSchema([ + 'type' => Type::string(), + 'description' => $description, ]); - $this->assertEquals(' + $this->assertEquals( + ' type Query { """ This field is "awesome" """ singleField: String } -', $output); +', + $output + ); - $recreatedRoot = BuildSchema::build($output)->getTypeMap()['Query']; + $recreatedRoot = BuildSchema::build($output)->getTypeMap()['Query']; $recreatedField = $recreatedRoot->getFields()['singleField']; $this->assertEquals($description, $recreatedField->description); } @@ -677,20 +753,23 @@ type Query { public function testPReservesLeadingSpacesWhenPrintingADescription() : void { $description = ' This field is "awesome"'; - $output = $this->printSingleFieldSchema([ - "type" => Type::string(), - "description" => $description + $output = $this->printSingleFieldSchema([ + 'type' => Type::string(), + 'description' => $description, ]); - $this->assertEquals(' + $this->assertEquals( + ' type Query { """ This field is "awesome" """ singleField: String } -', $output); +', + $output + ); - $recreatedRoot = BuildSchema::build($output)->getTypeMap()['Query']; + $recreatedRoot = BuildSchema::build($output)->getTypeMap()['Query']; $recreatedField = $recreatedRoot->getFields()['singleField']; $this->assertEquals($description, $recreatedField->description); } @@ -701,14 +780,14 @@ type Query { public function testPrintIntrospectionSchema() : void { $query = new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ - 'onlyField' => ['type' => Type::string()] - ] + 'onlyField' => ['type' => Type::string()], + ], ]); - $schema = new Schema(['query' => $query]); - $output = SchemaPrinter::printIntrosepctionSchema($schema); + $schema = new Schema(['query' => $query]); + $output = SchemaPrinter::printIntrosepctionSchema($schema); $introspectionSchema = <<<'EOT' """ Directs the executor to include this field or fragment only when the `if` argument is true. @@ -946,16 +1025,17 @@ EOT; public function testPrintIntrospectionSchemaWithCommentDescriptions() : void { $query = new ObjectType([ - 'name' => 'Query', + 'name' => 'Query', 'fields' => [ - 'onlyField' => ['type' => Type::string()] - ] + 'onlyField' => ['type' => Type::string()], + ], ]); - $schema = new Schema(['query' => $query]); - $output = SchemaPrinter::printIntrosepctionSchema($schema, [ - 'commentDescriptions' => true - ]); + $schema = new Schema(['query' => $query]); + $output = SchemaPrinter::printIntrosepctionSchema( + $schema, + ['commentDescriptions' => true] + ); $introspectionSchema = <<<'EOT' # Directs the executor to include this field or fragment only when the `if` argument is true. directive @include( diff --git a/tests/Utils/SuggestionListTest.php b/tests/Utils/SuggestionListTest.php index dcb152f..465f3a2 100644 --- a/tests/Utils/SuggestionListTest.php +++ b/tests/Utils/SuggestionListTest.php @@ -1,16 +1,15 @@ assertEquals($expected, AST::valueFromAST(Parser::parseValue($valueText), $type)); - } - - private function runTestCaseWithVars($variables, $type, $valueText, $expected) - { - $this->assertEquals($expected, AST::valueFromAST(Parser::parseValue($valueText), $type, $variables)); - } + /** @var InputObjectType */ + private $inputObj; /** * @see it('rejects empty input') @@ -45,6 +40,11 @@ class ValueFromAstTest extends TestCase $this->runTestCase(Type::id(), '"123456"', '123456'); } + private function runTestCase($type, $valueText, $expected) + { + $this->assertEquals($expected, AST::valueFromAST(Parser::parseValue($valueText), $type)); + } + /** * @see it('does not convert when input coercion rules reject a value') */ @@ -68,13 +68,13 @@ class ValueFromAstTest extends TestCase public function testConvertsEnumValuesAccordingToInputCoercionRules() : void { $testEnum = new EnumType([ - 'name' => 'TestColor', + 'name' => 'TestColor', 'values' => [ - 'RED' => ['value' => 1], + 'RED' => ['value' => 1], 'GREEN' => ['value' => 2], - 'BLUE' => ['value' => 3], - 'NULL' => ['value' => null], - ] + 'BLUE' => ['value' => 3], + 'NULL' => ['value' => null], + ], ]); $this->runTestCase($testEnum, 'RED', 1); @@ -100,14 +100,14 @@ class ValueFromAstTest extends TestCase public function testCoercesListsOfValues() : void { $listOfBool = Type::listOf(Type::boolean()); - $undefined = Utils::undefined(); + $undefined = Utils::undefined(); - $this->runTestCase($listOfBool, 'true', [ true ]); + $this->runTestCase($listOfBool, 'true', [true]); $this->runTestCase($listOfBool, '123', $undefined); $this->runTestCase($listOfBool, 'null', null); - $this->runTestCase($listOfBool, '[true, false]', [ true, false ]); + $this->runTestCase($listOfBool, '[true, false]', [true, false]); $this->runTestCase($listOfBool, '[true, 123]', $undefined); - $this->runTestCase($listOfBool, '[true, null]', [ true, null ]); + $this->runTestCase($listOfBool, '[true, null]', [true, null]); $this->runTestCase($listOfBool, '{ true: true }', $undefined); } @@ -117,14 +117,14 @@ class ValueFromAstTest extends TestCase public function testCoercesNonNullListsOfValues() : void { $nonNullListOfBool = Type::nonNull(Type::listOf(Type::boolean())); - $undefined = Utils::undefined(); + $undefined = Utils::undefined(); - $this->runTestCase($nonNullListOfBool, 'true', [ true ]); + $this->runTestCase($nonNullListOfBool, 'true', [true]); $this->runTestCase($nonNullListOfBool, '123', $undefined); $this->runTestCase($nonNullListOfBool, 'null', $undefined); - $this->runTestCase($nonNullListOfBool, '[true, false]', [ true, false ]); + $this->runTestCase($nonNullListOfBool, '[true, false]', [true, false]); $this->runTestCase($nonNullListOfBool, '[true, 123]', $undefined); - $this->runTestCase($nonNullListOfBool, '[true, null]', [ true, null ]); + $this->runTestCase($nonNullListOfBool, '[true, null]', [true, null]); } /** @@ -133,12 +133,12 @@ class ValueFromAstTest extends TestCase public function testCoercesListsOfNonNullValues() : void { $listOfNonNullBool = Type::listOf(Type::nonNull(Type::boolean())); - $undefined = Utils::undefined(); + $undefined = Utils::undefined(); - $this->runTestCase($listOfNonNullBool, 'true', [ true ]); + $this->runTestCase($listOfNonNullBool, 'true', [true]); $this->runTestCase($listOfNonNullBool, '123', $undefined); $this->runTestCase($listOfNonNullBool, 'null', null); - $this->runTestCase($listOfNonNullBool, '[true, false]', [ true, false ]); + $this->runTestCase($listOfNonNullBool, '[true, false]', [true, false]); $this->runTestCase($listOfNonNullBool, '[true, 123]', $undefined); $this->runTestCase($listOfNonNullBool, '[true, null]', $undefined); } @@ -149,56 +149,63 @@ class ValueFromAstTest extends TestCase public function testCoercesNonNullListsOfNonNullValues() : void { $nonNullListOfNonNullBool = Type::nonNull(Type::listOf(Type::nonNull(Type::boolean()))); - $undefined = Utils::undefined(); + $undefined = Utils::undefined(); - $this->runTestCase($nonNullListOfNonNullBool, 'true', [ true ]); + $this->runTestCase($nonNullListOfNonNullBool, 'true', [true]); $this->runTestCase($nonNullListOfNonNullBool, '123', $undefined); $this->runTestCase($nonNullListOfNonNullBool, 'null', $undefined); - $this->runTestCase($nonNullListOfNonNullBool, '[true, false]', [ true, false ]); + $this->runTestCase($nonNullListOfNonNullBool, '[true, false]', [true, false]); $this->runTestCase($nonNullListOfNonNullBool, '[true, 123]', $undefined); $this->runTestCase($nonNullListOfNonNullBool, '[true, null]', $undefined); } - private $inputObj; - - private function inputObj() - { - return $this->inputObj ?: $this->inputObj = new InputObjectType([ - 'name' => 'TestInput', - 'fields' => [ - 'int' => [ 'type' => Type::int(), 'defaultValue' => 42 ], - 'bool' => [ 'type' => Type::boolean() ], - 'requiredBool' => [ 'type' => Type::nonNull(Type::boolean()) ], - ] - ]); - } - /** * @see it('coerces input objects according to input coercion rules') */ public function testCoercesInputObjectsAccordingToInputCoercionRules() : void { $testInputObj = $this->inputObj(); - $undefined = Utils::undefined(); + $undefined = Utils::undefined(); $this->runTestCase($testInputObj, 'null', null); $this->runTestCase($testInputObj, '123', $undefined); $this->runTestCase($testInputObj, '[]', $undefined); $this->runTestCase($testInputObj, '{ int: 123, requiredBool: false }', ['int' => 123, 'requiredBool' => false]); - $this->runTestCase($testInputObj, '{ bool: true, requiredBool: false }', [ 'int' => 42, 'bool' => true, 'requiredBool' => false ]); + $this->runTestCase( + $testInputObj, + '{ bool: true, requiredBool: false }', + ['int' => 42, 'bool' => true, 'requiredBool' => false] + ); $this->runTestCase($testInputObj, '{ int: true, requiredBool: true }', $undefined); $this->runTestCase($testInputObj, '{ requiredBool: null }', $undefined); $this->runTestCase($testInputObj, '{ bool: true }', $undefined); } + private function inputObj() + { + return $this->inputObj ?: $this->inputObj = new InputObjectType([ + 'name' => 'TestInput', + 'fields' => [ + 'int' => ['type' => Type::int(), 'defaultValue' => 42], + 'bool' => ['type' => Type::boolean()], + 'requiredBool' => ['type' => Type::nonNull(Type::boolean())], + ], + ]); + } + /** * @see it('accepts variable values assuming already coerced') */ public function testAcceptsVariableValuesAssumingAlreadyCoerced() : void { $this->runTestCaseWithVars([], Type::boolean(), '$var', Utils::undefined()); - $this->runTestCaseWithVars([ 'var' => true ], Type::boolean(), '$var', true); - $this->runTestCaseWithVars([ 'var' => null ], Type::boolean(), '$var', null); + $this->runTestCaseWithVars(['var' => true], Type::boolean(), '$var', true); + $this->runTestCaseWithVars(['var' => null], Type::boolean(), '$var', null); + } + + private function runTestCaseWithVars($variables, $type, $valueText, $expected) + { + $this->assertEquals($expected, AST::valueFromAST(Parser::parseValue($valueText), $type, $variables)); } /** @@ -206,16 +213,16 @@ class ValueFromAstTest extends TestCase */ public function testAssertsVariablesAreProvidedAsItemsInLists() : void { - $listOfBool = Type::listOf(Type::boolean()); + $listOfBool = Type::listOf(Type::boolean()); $listOfNonNullBool = Type::listOf(Type::nonNull(Type::boolean())); - $this->runTestCaseWithVars([], $listOfBool, '[ $foo ]', [ null ]); + $this->runTestCaseWithVars([], $listOfBool, '[ $foo ]', [null]); $this->runTestCaseWithVars([], $listOfNonNullBool, '[ $foo ]', Utils::undefined()); - $this->runTestCaseWithVars([ 'foo' => true ], $listOfNonNullBool, '[ $foo ]', [ true ]); + $this->runTestCaseWithVars(['foo' => true], $listOfNonNullBool, '[ $foo ]', [true]); // Note: variables are expected to have already been coerced, so we // do not expect the singleton wrapping behavior for variables. - $this->runTestCaseWithVars([ 'foo' => true ], $listOfNonNullBool, '$foo', true); - $this->runTestCaseWithVars([ 'foo' => [ true ] ], $listOfNonNullBool, '$foo', [ true ]); + $this->runTestCaseWithVars(['foo' => true], $listOfNonNullBool, '$foo', true); + $this->runTestCaseWithVars(['foo' => [true]], $listOfNonNullBool, '$foo', [true]); } /** @@ -229,7 +236,7 @@ class ValueFromAstTest extends TestCase [], $testInputObj, '{ int: $foo, bool: $foo, requiredBool: true }', - [ 'int' => 42, 'requiredBool' => true ] + ['int' => 42, 'requiredBool' => true] ); $this->runTestCaseWithVars( [], @@ -238,10 +245,10 @@ class ValueFromAstTest extends TestCase Utils::undefined() ); $this->runTestCaseWithVars( - [ 'foo' => true ], + ['foo' => true], $testInputObj, '{ requiredBool: $foo }', - [ 'int' => 42, 'requiredBool' => true ] + ['int' => 42, 'requiredBool' => true] ); } } diff --git a/tests/UtilsTest.php b/tests/UtilsTest.php index facc85b..0984714 100644 --- a/tests/UtilsTest.php +++ b/tests/UtilsTest.php @@ -1,4 +1,7 @@ requiredKey = 'value'; $this->expectException(\InvalidArgumentException::class); diff --git a/tests/Validator/DisableIntrospectionTest.php b/tests/Validator/DisableIntrospectionTest.php index 461daae..f5d61b9 100644 --- a/tests/Validator/DisableIntrospectionTest.php +++ b/tests/Validator/DisableIntrospectionTest.php @@ -1,4 +1,7 @@ expectFailsRule(new DisableIntrospection(DisableIntrospection::ENABLED), ' + $this->expectFailsRule( + new DisableIntrospection(DisableIntrospection::ENABLED), + ' query { __schema { queryType { @@ -23,16 +27,26 @@ class DisableIntrospectionTest extends ValidatorTestCase } } ', - [$this->error(3, 9)] + [$this->error(3, 9)] ); } - + + private function error($line, $column) + { + return FormattedError::create( + DisableIntrospection::introspectionDisabledMessage(), + [new SourceLocation($line, $column)] + ); + } + /** * @see it('fails if the query contains __type') */ public function testQueryContainsType() : void { - $this->expectFailsRule(new DisableIntrospection(DisableIntrospection::ENABLED), ' + $this->expectFailsRule( + new DisableIntrospection(DisableIntrospection::ENABLED), + ' query { __type( name: "Query" @@ -50,7 +64,9 @@ class DisableIntrospectionTest extends ValidatorTestCase */ public function testValidQuery() : void { - $this->expectPassesRule(new DisableIntrospection(DisableIntrospection::ENABLED), ' + $this->expectPassesRule( + new DisableIntrospection(DisableIntrospection::ENABLED), + ' query { user { name @@ -60,7 +76,8 @@ class DisableIntrospectionTest extends ValidatorTestCase } } } - '); + ' + ); } /** @@ -68,7 +85,9 @@ class DisableIntrospectionTest extends ValidatorTestCase */ public function testQueryWhenDisabled() : void { - $this->expectPassesRule(new DisableIntrospection(DisableIntrospection::DISABLED), ' + $this->expectPassesRule( + new DisableIntrospection(DisableIntrospection::DISABLED), + ' query { __type( name: "Query" @@ -76,7 +95,8 @@ class DisableIntrospectionTest extends ValidatorTestCase name } } - '); + ' + ); } /** @@ -86,7 +106,9 @@ class DisableIntrospectionTest extends ValidatorTestCase { $disableIntrospection = new DisableIntrospection(DisableIntrospection::DISABLED); $disableIntrospection->setEnabled(DisableIntrospection::ENABLED); - $this->expectFailsRule($disableIntrospection, ' + $this->expectFailsRule( + $disableIntrospection, + ' query { __type( name: "Query" @@ -106,7 +128,9 @@ class DisableIntrospectionTest extends ValidatorTestCase { $disableIntrospection = new DisableIntrospection(DisableIntrospection::ENABLED); $disableIntrospection->setEnabled(DisableIntrospection::DISABLED); - $this->expectPassesRule($disableIntrospection, ' + $this->expectPassesRule( + $disableIntrospection, + ' query { __type( name: "Query" @@ -114,15 +138,7 @@ class DisableIntrospectionTest extends ValidatorTestCase name } } - '); - } - - - private function error($line, $column) - { - return FormattedError::create( - DisableIntrospection::introspectionDisabledMessage(), - [ new SourceLocation($line, $column) ] + ' ); } } diff --git a/tests/Validator/ExecutableDefinitionsTest.php b/tests/Validator/ExecutableDefinitionsTest.php index 28ab13a..0751755 100644 --- a/tests/Validator/ExecutableDefinitionsTest.php +++ b/tests/Validator/ExecutableDefinitionsTest.php @@ -1,27 +1,31 @@ expectPassesRule(new ExecutableDefinitions, ' + $this->expectPassesRule( + new ExecutableDefinitions(), + ' query Foo { dog { name } } - '); + ' + ); } /** @@ -29,7 +33,9 @@ class ExecutableDefinitionsTest extends ValidatorTestCase */ public function testWithOperationAndFragment() : void { - $this->expectPassesRule(new ExecutableDefinitions, ' + $this->expectPassesRule( + new ExecutableDefinitions(), + ' query Foo { dog { name @@ -40,7 +46,8 @@ class ExecutableDefinitionsTest extends ValidatorTestCase fragment Frag on Dog { name } - '); + ' + ); } /** @@ -48,7 +55,9 @@ class ExecutableDefinitionsTest extends ValidatorTestCase */ public function testWithTypeDefinition() : void { - $this->expectFailsRule(new ExecutableDefinitions, ' + $this->expectFailsRule( + new ExecutableDefinitions(), + ' query Foo { dog { name @@ -66,14 +75,15 @@ class ExecutableDefinitionsTest extends ValidatorTestCase [ $this->nonExecutableDefinition('Cow', 8, 12), $this->nonExecutableDefinition('Dog', 12, 19), - ]); + ] + ); } private function nonExecutableDefinition($defName, $line, $column) { return FormattedError::create( ExecutableDefinitions::nonExecutableDefinitionMessage($defName), - [ new SourceLocation($line, $column) ] + [new SourceLocation($line, $column)] ); } } diff --git a/tests/Validator/FieldsOnCorrectTypeTest.php b/tests/Validator/FieldsOnCorrectTypeTest.php index a838f3c..5897b5a 100644 --- a/tests/Validator/FieldsOnCorrectTypeTest.php +++ b/tests/Validator/FieldsOnCorrectTypeTest.php @@ -1,4 +1,7 @@ expectPassesRule(new FieldsOnCorrectType(), ' + $this->expectPassesRule( + new FieldsOnCorrectType(), + ' fragment objectFieldSelection on Dog { __typename name } - '); + ' + ); } /** @@ -27,12 +32,15 @@ class FieldsOnCorrectTypeTest extends ValidatorTestCase */ public function testAliasedObjectFieldSelection() : void { - $this->expectPassesRule(new FieldsOnCorrectType, ' + $this->expectPassesRule( + new FieldsOnCorrectType(), + ' fragment aliasedObjectFieldSelection on Dog { tn : __typename otherName : name } - '); + ' + ); } /** @@ -40,12 +48,15 @@ class FieldsOnCorrectTypeTest extends ValidatorTestCase */ public function testInterfaceFieldSelection() : void { - $this->expectPassesRule(new FieldsOnCorrectType, ' + $this->expectPassesRule( + new FieldsOnCorrectType(), + ' fragment interfaceFieldSelection on Pet { __typename name } - '); + ' + ); } /** @@ -53,11 +64,14 @@ class FieldsOnCorrectTypeTest extends ValidatorTestCase */ public function testAliasedInterfaceFieldSelection() : void { - $this->expectPassesRule(new FieldsOnCorrectType, ' + $this->expectPassesRule( + new FieldsOnCorrectType(), + ' fragment interfaceFieldSelection on Pet { otherName : name } - '); + ' + ); } /** @@ -65,11 +79,14 @@ class FieldsOnCorrectTypeTest extends ValidatorTestCase */ public function testLyingAliasSelection() : void { - $this->expectPassesRule(new FieldsOnCorrectType, ' + $this->expectPassesRule( + new FieldsOnCorrectType(), + ' fragment lyingAliasSelection on Dog { name : nickname } - '); + ' + ); } /** @@ -77,11 +94,14 @@ class FieldsOnCorrectTypeTest extends ValidatorTestCase */ public function testIgnoresFieldsOnUnknownType() : void { - $this->expectPassesRule(new FieldsOnCorrectType, ' + $this->expectPassesRule( + new FieldsOnCorrectType(), + ' fragment unknownSelection on UnknownType { unknownField } - '); + ' + ); } /** @@ -89,7 +109,9 @@ class FieldsOnCorrectTypeTest extends ValidatorTestCase */ public function testReportsErrorsWhenTypeIsKnownAgain() : void { - $this->expectFailsRule(new FieldsOnCorrectType, ' + $this->expectFailsRule( + new FieldsOnCorrectType(), + ' fragment typeKnownAgain on Pet { unknown_pet_field { ... on Cat { @@ -99,17 +121,27 @@ class FieldsOnCorrectTypeTest extends ValidatorTestCase }', [ $this->undefinedField('unknown_pet_field', 'Pet', [], [], 3, 9), - $this->undefinedField('unknown_cat_field', 'Cat', [], [], 5, 13) + $this->undefinedField('unknown_cat_field', 'Cat', [], [], 5, 13), ] ); } + private function undefinedField($field, $type, $suggestedTypes, $suggestedFields, $line, $column) + { + return FormattedError::create( + FieldsOnCorrectType::undefinedFieldMessage($field, $type, $suggestedTypes, $suggestedFields), + [new SourceLocation($line, $column)] + ); + } + /** * @see it('Field not defined on fragment') */ public function testFieldNotDefinedOnFragment() : void { - $this->expectFailsRule(new FieldsOnCorrectType, ' + $this->expectFailsRule( + new FieldsOnCorrectType(), + ' fragment fieldNotDefined on Dog { meowVolume }', @@ -122,7 +154,9 @@ class FieldsOnCorrectTypeTest extends ValidatorTestCase */ public function testIgnoresDeeplyUnknownField() : void { - $this->expectFailsRule(new FieldsOnCorrectType, ' + $this->expectFailsRule( + new FieldsOnCorrectType(), + ' fragment deepFieldNotDefined on Dog { unknown_field { deeper_unknown_field @@ -137,7 +171,9 @@ class FieldsOnCorrectTypeTest extends ValidatorTestCase */ public function testSubFieldNotDefined() : void { - $this->expectFailsRule(new FieldsOnCorrectType, ' + $this->expectFailsRule( + new FieldsOnCorrectType(), + ' fragment subFieldNotDefined on Human { pets { unknown_field @@ -152,7 +188,9 @@ class FieldsOnCorrectTypeTest extends ValidatorTestCase */ public function testFieldNotDefinedOnInlineFragment() : void { - $this->expectFailsRule(new FieldsOnCorrectType, ' + $this->expectFailsRule( + new FieldsOnCorrectType(), + ' fragment fieldNotDefined on Pet { ... on Dog { meowVolume @@ -167,7 +205,9 @@ class FieldsOnCorrectTypeTest extends ValidatorTestCase */ public function testAliasedFieldTargetNotDefined() : void { - $this->expectFailsRule(new FieldsOnCorrectType, ' + $this->expectFailsRule( + new FieldsOnCorrectType(), + ' fragment aliasedFieldTargetNotDefined on Dog { volume : mooVolume }', @@ -180,7 +220,9 @@ class FieldsOnCorrectTypeTest extends ValidatorTestCase */ public function testAliasedLyingFieldTargetNotDefined() : void { - $this->expectFailsRule(new FieldsOnCorrectType, ' + $this->expectFailsRule( + new FieldsOnCorrectType(), + ' fragment aliasedLyingFieldTargetNotDefined on Dog { barkVolume : kawVolume }', @@ -193,7 +235,9 @@ class FieldsOnCorrectTypeTest extends ValidatorTestCase */ public function testNotDefinedOnInterface() : void { - $this->expectFailsRule(new FieldsOnCorrectType, ' + $this->expectFailsRule( + new FieldsOnCorrectType(), + ' fragment notDefinedOnInterface on Pet { tailLength }', @@ -206,7 +250,9 @@ class FieldsOnCorrectTypeTest extends ValidatorTestCase */ public function testDefinedOnImplmentorsButNotOnInterface() : void { - $this->expectFailsRule(new FieldsOnCorrectType, ' + $this->expectFailsRule( + new FieldsOnCorrectType(), + ' fragment definedOnImplementorsButNotInterface on Pet { nickname }', @@ -219,7 +265,9 @@ class FieldsOnCorrectTypeTest extends ValidatorTestCase */ public function testMetaFieldSelectionOnUnion() : void { - $this->expectPassesRule(new FieldsOnCorrectType, ' + $this->expectPassesRule( + new FieldsOnCorrectType(), + ' fragment directFieldSelectionOnUnion on CatOrDog { __typename }' @@ -231,7 +279,9 @@ class FieldsOnCorrectTypeTest extends ValidatorTestCase */ public function testDirectFieldSelectionOnUnion() : void { - $this->expectFailsRule(new FieldsOnCorrectType, ' + $this->expectFailsRule( + new FieldsOnCorrectType(), + ' fragment directFieldSelectionOnUnion on CatOrDog { directField }', @@ -244,7 +294,9 @@ class FieldsOnCorrectTypeTest extends ValidatorTestCase */ public function testDefinedOnImplementorsQueriedOnUnion() : void { - $this->expectFailsRule(new FieldsOnCorrectType, ' + $this->expectFailsRule( + new FieldsOnCorrectType(), + ' fragment definedOnImplementorsQueriedOnUnion on CatOrDog { name }', @@ -255,32 +307,39 @@ class FieldsOnCorrectTypeTest extends ValidatorTestCase [], 3, 9 - )] + ), + ] ); } + // Describe: Fields on correct type error message + /** * @see it('valid field in inline fragment') */ public function testValidFieldInInlineFragment() : void { - $this->expectPassesRule(new FieldsOnCorrectType, ' + $this->expectPassesRule( + new FieldsOnCorrectType(), + ' fragment objectFieldSelection on Pet { ... on Dog { name } } - '); + ' + ); } - // Describe: Fields on correct type error message - /** * @see it('Works with no suggestions') */ public function testWorksWithNoSuggestions() : void { - $this->assertEquals('Cannot query field "f" on type "T".', FieldsOnCorrectType::undefinedFieldMessage('f', 'T', [], [])); + $this->assertEquals( + 'Cannot query field "f" on type "T".', + FieldsOnCorrectType::undefinedFieldMessage('f', 'T', [], []) + ); } /** @@ -324,12 +383,15 @@ class FieldsOnCorrectTypeTest extends ValidatorTestCase $expected = 'Cannot query field "f" on type "T". ' . 'Did you mean to use an inline fragment on "A", "B", "C", "D", or "E"?'; - $this->assertEquals($expected, FieldsOnCorrectType::undefinedFieldMessage( - 'f', - 'T', - ['A', 'B', 'C', 'D', 'E', 'F'], - [] - )); + $this->assertEquals( + $expected, + FieldsOnCorrectType::undefinedFieldMessage( + 'f', + 'T', + ['A', 'B', 'C', 'D', 'E', 'F'], + [] + ) + ); } /** @@ -340,19 +402,14 @@ class FieldsOnCorrectTypeTest extends ValidatorTestCase $expected = 'Cannot query field "f" on type "T". ' . 'Did you mean "z", "y", "x", "w", or "v"?'; - $this->assertEquals($expected, FieldsOnCorrectType::undefinedFieldMessage( - 'f', - 'T', - [], - ['z', 'y', 'x', 'w', 'v', 'u'] - )); - } - - private function undefinedField($field, $type, $suggestedTypes, $suggestedFields, $line, $column) - { - return FormattedError::create( - FieldsOnCorrectType::undefinedFieldMessage($field, $type, $suggestedTypes, $suggestedFields), - [new SourceLocation($line, $column)] + $this->assertEquals( + $expected, + FieldsOnCorrectType::undefinedFieldMessage( + 'f', + 'T', + [], + ['z', 'y', 'x', 'w', 'v', 'u'] + ) ); } } diff --git a/tests/Validator/FragmentsOnCompositeTypesTest.php b/tests/Validator/FragmentsOnCompositeTypesTest.php index b1a116a..deec5c4 100644 --- a/tests/Validator/FragmentsOnCompositeTypesTest.php +++ b/tests/Validator/FragmentsOnCompositeTypesTest.php @@ -1,4 +1,7 @@ expectPassesRule(new FragmentsOnCompositeTypes, ' + $this->expectPassesRule( + new FragmentsOnCompositeTypes(), + ' fragment validFragment on Dog { barks } - '); + ' + ); } /** @@ -26,11 +31,14 @@ class FragmentsOnCompositeTypesTest extends ValidatorTestCase */ public function testInterfaceIsValidFragmentType() : void { - $this->expectPassesRule(new FragmentsOnCompositeTypes, ' + $this->expectPassesRule( + new FragmentsOnCompositeTypes(), + ' fragment validFragment on Pet { name } - '); + ' + ); } /** @@ -38,13 +46,16 @@ class FragmentsOnCompositeTypesTest extends ValidatorTestCase */ public function testObjectIsValidInlineFragmentType() : void { - $this->expectPassesRule(new FragmentsOnCompositeTypes, ' + $this->expectPassesRule( + new FragmentsOnCompositeTypes(), + ' fragment validFragment on Pet { ... on Dog { barks } } - '); + ' + ); } /** @@ -52,13 +63,16 @@ class FragmentsOnCompositeTypesTest extends ValidatorTestCase */ public function testInlineFragmentWithoutTypeIsValid() : void { - $this->expectPassesRule(new FragmentsOnCompositeTypes, ' + $this->expectPassesRule( + new FragmentsOnCompositeTypes(), + ' fragment validFragment on Pet { ... { name } } - '); + ' + ); } /** @@ -66,11 +80,14 @@ class FragmentsOnCompositeTypesTest extends ValidatorTestCase */ public function testUnionIsValidFragmentType() : void { - $this->expectPassesRule(new FragmentsOnCompositeTypes, ' + $this->expectPassesRule( + new FragmentsOnCompositeTypes(), + ' fragment validFragment on CatOrDog { __typename } - '); + ' + ); } /** @@ -78,56 +95,14 @@ class FragmentsOnCompositeTypesTest extends ValidatorTestCase */ public function testScalarIsInvalidFragmentType() : void { - $this->expectFailsRule(new FragmentsOnCompositeTypes, ' + $this->expectFailsRule( + new FragmentsOnCompositeTypes(), + ' fragment scalarFragment on Boolean { bad } ', - [$this->error('scalarFragment', 'Boolean', 2, 34)]); - } - - /** - * @see it('enum is invalid fragment type') - */ - public function testEnumIsInvalidFragmentType() : void - { - $this->expectFailsRule(new FragmentsOnCompositeTypes, ' - fragment scalarFragment on FurColor { - bad - } - ', - [$this->error('scalarFragment', 'FurColor', 2, 34)]); - } - - /** - * @see it('input object is invalid fragment type') - */ - public function testInputObjectIsInvalidFragmentType() : void - { - $this->expectFailsRule(new FragmentsOnCompositeTypes, ' - fragment inputFragment on ComplexInput { - stringField - } - ', - [$this->error('inputFragment', 'ComplexInput', 2, 33)]); - } - - /** - * @see it('scalar is invalid inline fragment type') - */ - public function testScalarIsInvalidInlineFragmentType() : void - { - $this->expectFailsRule(new FragmentsOnCompositeTypes, ' - fragment invalidFragment on Pet { - ... on String { - barks - } - } - ', - [FormattedError::create( - FragmentsOnCompositeTypes::inlineFragmentOnNonCompositeErrorMessage('String'), - [new SourceLocation(3, 16)] - )] + [$this->error('scalarFragment', 'Boolean', 2, 34)] ); } @@ -135,7 +110,61 @@ class FragmentsOnCompositeTypesTest extends ValidatorTestCase { return FormattedError::create( FragmentsOnCompositeTypes::fragmentOnNonCompositeErrorMessage($fragName, $typeName), - [ new SourceLocation($line, $column) ] + [new SourceLocation($line, $column)] + ); + } + + /** + * @see it('enum is invalid fragment type') + */ + public function testEnumIsInvalidFragmentType() : void + { + $this->expectFailsRule( + new FragmentsOnCompositeTypes(), + ' + fragment scalarFragment on FurColor { + bad + } + ', + [$this->error('scalarFragment', 'FurColor', 2, 34)] + ); + } + + /** + * @see it('input object is invalid fragment type') + */ + public function testInputObjectIsInvalidFragmentType() : void + { + $this->expectFailsRule( + new FragmentsOnCompositeTypes(), + ' + fragment inputFragment on ComplexInput { + stringField + } + ', + [$this->error('inputFragment', 'ComplexInput', 2, 33)] + ); + } + + /** + * @see it('scalar is invalid inline fragment type') + */ + public function testScalarIsInvalidInlineFragmentType() : void + { + $this->expectFailsRule( + new FragmentsOnCompositeTypes(), + ' + fragment invalidFragment on Pet { + ... on String { + barks + } + } + ', + [FormattedError::create( + FragmentsOnCompositeTypes::inlineFragmentOnNonCompositeErrorMessage('String'), + [new SourceLocation(3, 16)] + ), + ] ); } } diff --git a/tests/Validator/KnownArgumentNamesTest.php b/tests/Validator/KnownArgumentNamesTest.php index 8c3fc24..4c3c163 100644 --- a/tests/Validator/KnownArgumentNamesTest.php +++ b/tests/Validator/KnownArgumentNamesTest.php @@ -1,4 +1,7 @@ expectPassesRule(new KnownArgumentNames, ' + $this->expectPassesRule( + new KnownArgumentNames(), + ' fragment argOnRequiredArg on Dog { doesKnowCommand(dogCommand: SIT) } - '); + ' + ); } /** @@ -26,11 +31,14 @@ class KnownArgumentNamesTest extends ValidatorTestCase */ public function testMultipleArgsAreKnown() : void { - $this->expectPassesRule(new KnownArgumentNames, ' + $this->expectPassesRule( + new KnownArgumentNames(), + ' fragment multipleArgs on ComplicatedArgs { multipleReqs(req1: 1, req2: 2) } - '); + ' + ); } /** @@ -38,11 +46,14 @@ class KnownArgumentNamesTest extends ValidatorTestCase */ public function testIgnoresArgsOfUnknownFields() : void { - $this->expectPassesRule(new KnownArgumentNames, ' + $this->expectPassesRule( + new KnownArgumentNames(), + ' fragment argOnUnknownField on Dog { unknownField(unknownArg: SIT) } - '); + ' + ); } /** @@ -50,11 +61,14 @@ class KnownArgumentNamesTest extends ValidatorTestCase */ public function testMultipleArgsInReverseOrderAreKnown() : void { - $this->expectPassesRule(new KnownArgumentNames, ' + $this->expectPassesRule( + new KnownArgumentNames(), + ' fragment multipleArgsReverseOrder on ComplicatedArgs { multipleReqs(req2: 2, req1: 1) } - '); + ' + ); } /** @@ -62,11 +76,14 @@ class KnownArgumentNamesTest extends ValidatorTestCase */ public function testNoArgsOnOptionalArg() : void { - $this->expectPassesRule(new KnownArgumentNames, ' + $this->expectPassesRule( + new KnownArgumentNames(), + ' fragment noArgOnOptionalArg on Dog { isHousetrained } - '); + ' + ); } /** @@ -74,7 +91,9 @@ class KnownArgumentNamesTest extends ValidatorTestCase */ public function testArgsAreKnownDeeply() : void { - $this->expectPassesRule(new KnownArgumentNames, ' + $this->expectPassesRule( + new KnownArgumentNames(), + ' { dog { doesKnowCommand(dogCommand: SIT) @@ -87,7 +106,8 @@ class KnownArgumentNamesTest extends ValidatorTestCase } } } - '); + ' + ); } /** @@ -95,11 +115,14 @@ class KnownArgumentNamesTest extends ValidatorTestCase */ public function testDirectiveArgsAreKnown() : void { - $this->expectPassesRule(new KnownArgumentNames, ' + $this->expectPassesRule( + new KnownArgumentNames(), + ' { dog @skip(if: true) } - '); + ' + ); } /** @@ -107,13 +130,25 @@ class KnownArgumentNamesTest extends ValidatorTestCase */ public function testUndirectiveArgsAreInvalid() : void { - $this->expectFailsRule(new KnownArgumentNames, ' + $this->expectFailsRule( + new KnownArgumentNames(), + ' { dog @skip(unless: true) } - ', [ - $this->unknownDirectiveArg('unless', 'skip', [], 3, 19), - ]); + ', + [ + $this->unknownDirectiveArg('unless', 'skip', [], 3, 19), + ] + ); + } + + private function unknownDirectiveArg($argName, $directiveName, $suggestedArgs, $line, $column) + { + return FormattedError::create( + KnownArgumentNames::unknownDirectiveArgMessage($argName, $directiveName, $suggestedArgs), + [new SourceLocation($line, $column)] + ); } /** @@ -121,13 +156,17 @@ class KnownArgumentNamesTest extends ValidatorTestCase */ public function testMisspelledDirectiveArgsAreReported() : void { - $this->expectFailsRule(new KnownArgumentNames, ' + $this->expectFailsRule( + new KnownArgumentNames(), + ' { dog @skip(iff: true) } - ', [ - $this->unknownDirectiveArg('iff', 'skip', ['if'], 3, 19), - ]); + ', + [ + $this->unknownDirectiveArg('iff', 'skip', ['if'], 3, 19), + ] + ); } /** @@ -135,13 +174,25 @@ class KnownArgumentNamesTest extends ValidatorTestCase */ public function testInvalidArgName() : void { - $this->expectFailsRule(new KnownArgumentNames, ' + $this->expectFailsRule( + new KnownArgumentNames(), + ' fragment invalidArgName on Dog { doesKnowCommand(unknown: true) } - ', [ - $this->unknownArg('unknown', 'doesKnowCommand', 'Dog', [],3, 25), - ]); + ', + [ + $this->unknownArg('unknown', 'doesKnowCommand', 'Dog', [], 3, 25), + ] + ); + } + + private function unknownArg($argName, $fieldName, $typeName, $suggestedArgs, $line, $column) + { + return FormattedError::create( + KnownArgumentNames::unknownArgMessage($argName, $fieldName, $typeName, $suggestedArgs), + [new SourceLocation($line, $column)] + ); } /** @@ -149,13 +200,17 @@ class KnownArgumentNamesTest extends ValidatorTestCase */ public function testMisspelledArgNameIsReported() : void { - $this->expectFailsRule(new KnownArgumentNames, ' + $this->expectFailsRule( + new KnownArgumentNames(), + ' fragment invalidArgName on Dog { doesKnowCommand(dogcommand: true) } - ', [ - $this->unknownArg('dogcommand', 'doesKnowCommand', 'Dog', ['dogCommand'],3, 25), - ]); + ', + [ + $this->unknownArg('dogcommand', 'doesKnowCommand', 'Dog', ['dogCommand'], 3, 25), + ] + ); } /** @@ -163,14 +218,18 @@ class KnownArgumentNamesTest extends ValidatorTestCase */ public function testUnknownArgsAmongstKnownArgs() : void { - $this->expectFailsRule(new KnownArgumentNames, ' + $this->expectFailsRule( + new KnownArgumentNames(), + ' fragment oneGoodArgOneInvalidArg on Dog { doesKnowCommand(whoknows: 1, dogCommand: SIT, unknown: true) } - ', [ - $this->unknownArg('whoknows', 'doesKnowCommand', 'Dog', [], 3, 25), - $this->unknownArg('unknown', 'doesKnowCommand', 'Dog', [], 3, 55), - ]); + ', + [ + $this->unknownArg('whoknows', 'doesKnowCommand', 'Dog', [], 3, 25), + $this->unknownArg('unknown', 'doesKnowCommand', 'Dog', [], 3, 55), + ] + ); } /** @@ -178,7 +237,9 @@ class KnownArgumentNamesTest extends ValidatorTestCase */ public function testUnknownArgsDeeply() : void { - $this->expectFailsRule(new KnownArgumentNames, ' + $this->expectFailsRule( + new KnownArgumentNames(), + ' { dog { doesKnowCommand(unknown: true) @@ -191,25 +252,11 @@ class KnownArgumentNamesTest extends ValidatorTestCase } } } - ', [ - $this->unknownArg('unknown', 'doesKnowCommand', 'Dog', [], 4, 27), - $this->unknownArg('unknown', 'doesKnowCommand', 'Dog', [], 9, 31), - ]); - } - - private function unknownArg($argName, $fieldName, $typeName, $suggestedArgs, $line, $column) - { - return FormattedError::create( - KnownArgumentNames::unknownArgMessage($argName, $fieldName, $typeName, $suggestedArgs), - [new SourceLocation($line, $column)] - ); - } - - private function unknownDirectiveArg($argName, $directiveName, $suggestedArgs, $line, $column) - { - return FormattedError::create( - KnownArgumentNames::unknownDirectiveArgMessage($argName, $directiveName, $suggestedArgs), - [new SourceLocation($line, $column)] + ', + [ + $this->unknownArg('unknown', 'doesKnowCommand', 'Dog', [], 4, 27), + $this->unknownArg('unknown', 'doesKnowCommand', 'Dog', [], 9, 31), + ] ); } } diff --git a/tests/Validator/KnownDirectivesTest.php b/tests/Validator/KnownDirectivesTest.php index 7f9fd39..014e892 100644 --- a/tests/Validator/KnownDirectivesTest.php +++ b/tests/Validator/KnownDirectivesTest.php @@ -1,4 +1,7 @@ expectPassesRule(new KnownDirectives, ' + $this->expectPassesRule( + new KnownDirectives(), + ' query Foo { name ...Frag @@ -23,7 +27,8 @@ class KnownDirectivesTest extends ValidatorTestCase fragment Frag on Dog { name } - '); + ' + ); } /** @@ -31,7 +36,9 @@ class KnownDirectivesTest extends ValidatorTestCase */ public function testWithKnownDirectives() : void { - $this->expectPassesRule(new KnownDirectives, ' + $this->expectPassesRule( + new KnownDirectives(), + ' { dog @include(if: true) { name @@ -40,7 +47,8 @@ class KnownDirectivesTest extends ValidatorTestCase name } } - '); + ' + ); } /** @@ -48,15 +56,25 @@ class KnownDirectivesTest extends ValidatorTestCase */ public function testWithUnknownDirective() : void { - $this->expectFailsRule(new KnownDirectives, ' + $this->expectFailsRule( + new KnownDirectives(), + ' { dog @unknown(directive: "value") { name } } - ', [ - $this->unknownDirective('unknown', 3, 13) - ]); + ', + [$this->unknownDirective('unknown', 3, 13)] + ); + } + + private function unknownDirective($directiveName, $line, $column) + { + return FormattedError::create( + KnownDirectives::unknownDirectiveMessage($directiveName), + [new SourceLocation($line, $column)] + ); } /** @@ -64,7 +82,9 @@ class KnownDirectivesTest extends ValidatorTestCase */ public function testWithManyUnknownDirectives() : void { - $this->expectFailsRule(new KnownDirectives, ' + $this->expectFailsRule( + new KnownDirectives(), + ' { dog @unknown(directive: "value") { name @@ -76,11 +96,13 @@ class KnownDirectivesTest extends ValidatorTestCase } } } - ', [ - $this->unknownDirective('unknown', 3, 13), - $this->unknownDirective('unknown', 6, 15), - $this->unknownDirective('unknown', 8, 16) - ]); + ', + [ + $this->unknownDirective('unknown', 3, 13), + $this->unknownDirective('unknown', 6, 15), + $this->unknownDirective('unknown', 8, 16), + ] + ); } /** @@ -88,7 +110,9 @@ class KnownDirectivesTest extends ValidatorTestCase */ public function testWithWellPlacedDirectives() : void { - $this->expectPassesRule(new KnownDirectives, ' + $this->expectPassesRule( + new KnownDirectives(), + ' query Foo @onQuery { name @include(if: true) ...Frag @include(if: true) @@ -99,15 +123,20 @@ class KnownDirectivesTest extends ValidatorTestCase mutation Bar @onMutation { someField } - '); + ' + ); } + // within schema language + /** * @see it('with misplaced directives') */ public function testWithMisplacedDirectives() : void { - $this->expectFailsRule(new KnownDirectives, ' + $this->expectFailsRule( + new KnownDirectives(), + ' query Foo @include(if: true) { name @onQuery ...Frag @onQuery @@ -116,22 +145,32 @@ class KnownDirectivesTest extends ValidatorTestCase mutation Bar @onQuery { someField } - ', [ - $this->misplacedDirective('include', 'QUERY', 2, 17), - $this->misplacedDirective('onQuery', 'FIELD', 3, 14), - $this->misplacedDirective('onQuery', 'FRAGMENT_SPREAD', 4, 17), - $this->misplacedDirective('onQuery', 'MUTATION', 7, 20), - ]); + ', + [ + $this->misplacedDirective('include', 'QUERY', 2, 17), + $this->misplacedDirective('onQuery', 'FIELD', 3, 14), + $this->misplacedDirective('onQuery', 'FRAGMENT_SPREAD', 4, 17), + $this->misplacedDirective('onQuery', 'MUTATION', 7, 20), + ] + ); } - // within schema language + private function misplacedDirective($directiveName, $placement, $line, $column) + { + return FormattedError::create( + KnownDirectives::misplacedDirectiveMessage($directiveName, $placement), + [new SourceLocation($line, $column)] + ); + } /** * @see it('with well placed directives') */ public function testWSLWithWellPlacedDirectives() : void { - $this->expectPassesRule(new KnownDirectives, ' + $this->expectPassesRule( + new KnownDirectives(), + ' type MyObj implements MyInterface @onObject { myField(myArg: Int @onArgumentDefinition): String @onFieldDefinition } @@ -167,7 +206,8 @@ class KnownDirectivesTest extends ValidatorTestCase schema @onSchema { query: MyQuery } - '); + ' + ); } /** @@ -175,7 +215,9 @@ class KnownDirectivesTest extends ValidatorTestCase */ public function testWSLWithMisplacedDirectives() : void { - $this->expectFailsRule(new KnownDirectives, ' + $this->expectFailsRule( + new KnownDirectives(), + ' type MyObj implements MyInterface @onInterface { myField(myArg: Int @onInputFieldDefinition): String @onInputFieldDefinition } @@ -217,20 +259,4 @@ class KnownDirectivesTest extends ValidatorTestCase ] ); } - - private function unknownDirective($directiveName, $line, $column) - { - return FormattedError::create( - KnownDirectives::unknownDirectiveMessage($directiveName), - [ new SourceLocation($line, $column) ] - ); - } - - function misplacedDirective($directiveName, $placement, $line, $column) - { - return FormattedError::create( - KnownDirectives::misplacedDirectiveMessage($directiveName, $placement), - [new SourceLocation($line, $column)] - ); - } } diff --git a/tests/Validator/KnownFragmentNamesTest.php b/tests/Validator/KnownFragmentNamesTest.php index 5764150..cc9ce86 100644 --- a/tests/Validator/KnownFragmentNamesTest.php +++ b/tests/Validator/KnownFragmentNamesTest.php @@ -1,20 +1,24 @@ expectPassesRule(new KnownFragmentNames, ' + $this->expectPassesRule( + new KnownFragmentNames(), + ' { human(id: 4) { ...HumanFields1 @@ -33,7 +37,8 @@ class KnownFragmentNamesTest extends ValidatorTestCase fragment HumanFields3 on Human { name } - '); + ' + ); } /** @@ -41,7 +46,9 @@ class KnownFragmentNamesTest extends ValidatorTestCase */ public function testUnknownFragmentNamesAreInvalid() : void { - $this->expectFailsRule(new KnownFragmentNames, ' + $this->expectFailsRule( + new KnownFragmentNames(), + ' { human(id: 4) { ...UnknownFragment1 @@ -54,11 +61,13 @@ class KnownFragmentNamesTest extends ValidatorTestCase name ...UnknownFragment3 } - ', [ - $this->undefFrag('UnknownFragment1', 4, 14), - $this->undefFrag('UnknownFragment2', 6, 16), - $this->undefFrag('UnknownFragment3', 12, 12) - ]); + ', + [ + $this->undefFrag('UnknownFragment1', 4, 14), + $this->undefFrag('UnknownFragment2', 6, 16), + $this->undefFrag('UnknownFragment3', 12, 12), + ] + ); } private function undefFrag($fragName, $line, $column) diff --git a/tests/Validator/KnownTypeNamesTest.php b/tests/Validator/KnownTypeNamesTest.php index 9604cb8..12834aa 100644 --- a/tests/Validator/KnownTypeNamesTest.php +++ b/tests/Validator/KnownTypeNamesTest.php @@ -1,4 +1,7 @@ expectPassesRule(new KnownTypeNames, ' + $this->expectPassesRule( + new KnownTypeNames(), + ' query Foo($var: String, $required: [String!]!) { user(id: 4) { pets { ... on Pet { name }, ...PetFields } @@ -23,7 +27,8 @@ class KnownTypeNamesTest extends ValidatorTestCase fragment PetFields on Pet { name } - '); + ' + ); } /** @@ -31,7 +36,9 @@ class KnownTypeNamesTest extends ValidatorTestCase */ public function testUnknownTypeNamesAreInvalid() : void { - $this->expectFailsRule(new KnownTypeNames, ' + $this->expectFailsRule( + new KnownTypeNames(), + ' query Foo($var: JumbledUpLetters) { user(id: 4) { name @@ -41,11 +48,21 @@ class KnownTypeNamesTest extends ValidatorTestCase fragment PetFields on Peettt { name } - ', [ - $this->unknownType('JumbledUpLetters', [], 2, 23), - $this->unknownType('Badger', [], 5, 25), - $this->unknownType('Peettt', ['Pet'], 8, 29) - ]); + ', + [ + $this->unknownType('JumbledUpLetters', [], 2, 23), + $this->unknownType('Badger', [], 5, 25), + $this->unknownType('Peettt', ['Pet'], 8, 29), + ] + ); + } + + private function unknownType($typeName, $suggestedTypes, $line, $column) + { + return FormattedError::create( + KnownTypeNames::unknownTypeMessage($typeName, $suggestedTypes), + [new SourceLocation($line, $column)] + ); } /** @@ -53,7 +70,9 @@ class KnownTypeNamesTest extends ValidatorTestCase */ public function testIgnoresTypeDefinitions() : void { - $this->expectFailsRule(new KnownTypeNames, ' + $this->expectFailsRule( + new KnownTypeNames(), + ' type NotInTheSchema { field: FooBar } @@ -69,16 +88,10 @@ class KnownTypeNamesTest extends ValidatorTestCase id } } - ', [ - $this->unknownType('NotInTheSchema', [], 12, 23), - ]); - } - - private function unknownType($typeName, $suggestedTypes, $line, $column) - { - return FormattedError::create( - KnownTypeNames::unknownTypeMessage($typeName, $suggestedTypes), - [new SourceLocation($line, $column)] + ', + [ + $this->unknownType('NotInTheSchema', [], 12, 23), + ] ); } } diff --git a/tests/Validator/LoneAnonymousOperationTest.php b/tests/Validator/LoneAnonymousOperationTest.php index e8540c8..cdd7983 100644 --- a/tests/Validator/LoneAnonymousOperationTest.php +++ b/tests/Validator/LoneAnonymousOperationTest.php @@ -1,4 +1,7 @@ expectPassesRule(new LoneAnonymousOperation, ' + $this->expectPassesRule( + new LoneAnonymousOperation(), + ' fragment fragA on Type { field } - '); + ' + ); } /** @@ -26,11 +31,14 @@ class LoneAnonymousOperationTest extends ValidatorTestCase */ public function testOneAnonOperation() : void { - $this->expectPassesRule(new LoneAnonymousOperation, ' + $this->expectPassesRule( + new LoneAnonymousOperation(), + ' { field } - '); + ' + ); } /** @@ -38,7 +46,9 @@ class LoneAnonymousOperationTest extends ValidatorTestCase */ public function testMultipleNamedOperations() : void { - $this->expectPassesRule(new LoneAnonymousOperation, ' + $this->expectPassesRule( + new LoneAnonymousOperation(), + ' query Foo { field } @@ -46,7 +56,8 @@ class LoneAnonymousOperationTest extends ValidatorTestCase query Bar { field } - '); + ' + ); } /** @@ -54,14 +65,17 @@ class LoneAnonymousOperationTest extends ValidatorTestCase */ public function testAnonOperationWithFragment() : void { - $this->expectPassesRule(new LoneAnonymousOperation, ' + $this->expectPassesRule( + new LoneAnonymousOperation(), + ' { ...Foo } fragment Foo on Type { field } - '); + ' + ); } /** @@ -69,51 +83,21 @@ class LoneAnonymousOperationTest extends ValidatorTestCase */ public function testMultipleAnonOperations() : void { - $this->expectFailsRule(new LoneAnonymousOperation, ' + $this->expectFailsRule( + new LoneAnonymousOperation(), + ' { fieldA } { fieldB } - ', [ - $this->anonNotAlone(2, 7), - $this->anonNotAlone(5, 7) - ]); - } - - /** - * @see it('anon operation with a mutation') - */ - public function testAnonOperationWithMutation() : void - { - $this->expectFailsRule(new LoneAnonymousOperation, ' - { - fieldA - } - mutation Foo { - fieldB - } - ', [ - $this->anonNotAlone(2, 7) - ]); - } - - /** - * @see it('anon operation with a subscription') - */ - public function testAnonOperationWithSubscription() : void - { - $this->expectFailsRule(new LoneAnonymousOperation, ' - { - fieldA - } - subscription Foo { - fieldB - } - ', [ - $this->anonNotAlone(2, 7) - ]); + ', + [ + $this->anonNotAlone(2, 7), + $this->anonNotAlone(5, 7), + ] + ); } private function anonNotAlone($line, $column) @@ -122,6 +106,47 @@ class LoneAnonymousOperationTest extends ValidatorTestCase LoneAnonymousOperation::anonOperationNotAloneMessage(), [new SourceLocation($line, $column)] ); + } + /** + * @see it('anon operation with a mutation') + */ + public function testAnonOperationWithMutation() : void + { + $this->expectFailsRule( + new LoneAnonymousOperation(), + ' + { + fieldA + } + mutation Foo { + fieldB + } + ', + [ + $this->anonNotAlone(2, 7), + ] + ); + } + + /** + * @see it('anon operation with a subscription') + */ + public function testAnonOperationWithSubscription() : void + { + $this->expectFailsRule( + new LoneAnonymousOperation(), + ' + { + fieldA + } + subscription Foo { + fieldB + } + ', + [ + $this->anonNotAlone(2, 7), + ] + ); } } diff --git a/tests/Validator/NoFragmentCyclesTest.php b/tests/Validator/NoFragmentCyclesTest.php index a962dc3..f0a2a63 100644 --- a/tests/Validator/NoFragmentCyclesTest.php +++ b/tests/Validator/NoFragmentCyclesTest.php @@ -1,4 +1,7 @@ expectPassesRule(new NoFragmentCycles(), ' + $this->expectPassesRule( + new NoFragmentCycles(), + ' fragment fragA on Dog { ...fragB } fragment fragB on Dog { name } - '); + ' + ); } /** @@ -25,10 +30,13 @@ class NoFragmentCyclesTest extends ValidatorTestCase */ public function testSpreadingTwiceIsNotCircular() : void { - $this->expectPassesRule(new NoFragmentCycles, ' + $this->expectPassesRule( + new NoFragmentCycles(), + ' fragment fragA on Dog { ...fragB, ...fragB } fragment fragB on Dog { name } - '); + ' + ); } /** @@ -36,11 +44,14 @@ class NoFragmentCyclesTest extends ValidatorTestCase */ public function testSpreadingTwiceIndirectlyIsNotCircular() : void { - $this->expectPassesRule(new NoFragmentCycles, ' + $this->expectPassesRule( + new NoFragmentCycles(), + ' fragment fragA on Dog { ...fragB, ...fragC } fragment fragB on Dog { ...fragC } fragment fragC on Dog { name } - '); + ' + ); } /** @@ -48,7 +59,9 @@ class NoFragmentCyclesTest extends ValidatorTestCase */ public function testDoubleSpreadWithinAbstractTypes() : void { - $this->expectPassesRule(new NoFragmentCycles, ' + $this->expectPassesRule( + new NoFragmentCycles(), + ' fragment nameFragment on Pet { ... on Dog { name } ... on Cat { name } @@ -58,7 +71,8 @@ class NoFragmentCyclesTest extends ValidatorTestCase ... on Dog { ...nameFragment } ... on Cat { ...nameFragment } } - '); + ' + ); } /** @@ -66,11 +80,14 @@ class NoFragmentCyclesTest extends ValidatorTestCase */ public function testDoesNotFalsePositiveOnUnknownFragment() : void { - $this->expectPassesRule(new NoFragmentCycles, ' + $this->expectPassesRule( + new NoFragmentCycles(), + ' fragment nameFragment on Pet { ...UnknownFragment } - '); + ' + ); } /** @@ -78,11 +95,23 @@ class NoFragmentCyclesTest extends ValidatorTestCase */ public function testSpreadingRecursivelyWithinFieldFails() : void { - $this->expectFailsRule(new NoFragmentCycles, ' + $this->expectFailsRule( + new NoFragmentCycles(), + ' fragment fragA on Human { relatives { ...fragA } }, - ', [ - $this->cycleError('fragA', [], 2, 45) - ]); + ', + [ + $this->cycleError('fragA', [], 2, 45), + ] + ); + } + + private function cycleError($fargment, $spreadNames, $line, $column) + { + return FormattedError::create( + NoFragmentCycles::cycleErrorMessage($fargment, $spreadNames), + [new SourceLocation($line, $column)] + ); } /** @@ -90,11 +119,15 @@ class NoFragmentCyclesTest extends ValidatorTestCase */ public function testNoSpreadingItselfDirectly() : void { - $this->expectFailsRule(new NoFragmentCycles, ' + $this->expectFailsRule( + new NoFragmentCycles(), + ' fragment fragA on Dog { ...fragA } - ', [ - $this->cycleError('fragA', [], 2, 31) - ]); + ', + [ + $this->cycleError('fragA', [], 2, 31), + ] + ); } /** @@ -102,15 +135,19 @@ class NoFragmentCyclesTest extends ValidatorTestCase */ public function testNoSpreadingItselfDirectlyWithinInlineFragment() : void { - $this->expectFailsRule(new NoFragmentCycles, ' + $this->expectFailsRule( + new NoFragmentCycles(), + ' fragment fragA on Pet { ... on Dog { ...fragA } } - ', [ - $this->cycleError('fragA', [], 4, 11) - ]); + ', + [ + $this->cycleError('fragA', [], 4, 11), + ] + ); } /** @@ -118,15 +155,19 @@ class NoFragmentCyclesTest extends ValidatorTestCase */ public function testNoSpreadingItselfIndirectly() : void { - $this->expectFailsRule(new NoFragmentCycles, ' + $this->expectFailsRule( + new NoFragmentCycles(), + ' fragment fragA on Dog { ...fragB } fragment fragB on Dog { ...fragA } - ', [ - FormattedError::create( - NoFragmentCycles::cycleErrorMessage('fragA', ['fragB']), - [ new SourceLocation(2, 31), new SourceLocation(3, 31) ] - ) - ]); + ', + [ + FormattedError::create( + NoFragmentCycles::cycleErrorMessage('fragA', ['fragB']), + [new SourceLocation(2, 31), new SourceLocation(3, 31)] + ), + ] + ); } /** @@ -134,15 +175,19 @@ class NoFragmentCyclesTest extends ValidatorTestCase */ public function testNoSpreadingItselfIndirectlyReportsOppositeOrder() : void { - $this->expectFailsRule(new NoFragmentCycles, ' + $this->expectFailsRule( + new NoFragmentCycles(), + ' fragment fragB on Dog { ...fragA } fragment fragA on Dog { ...fragB } - ', [ - FormattedError::create( - NoFragmentCycles::cycleErrorMessage('fragB', ['fragA']), - [new SourceLocation(2, 31), new SourceLocation(3, 31)] - ) - ]); + ', + [ + FormattedError::create( + NoFragmentCycles::cycleErrorMessage('fragB', ['fragA']), + [new SourceLocation(2, 31), new SourceLocation(3, 31)] + ), + ] + ); } /** @@ -150,7 +195,9 @@ class NoFragmentCyclesTest extends ValidatorTestCase */ public function testNoSpreadingItselfIndirectlyWithinInlineFragment() : void { - $this->expectFailsRule(new NoFragmentCycles, ' + $this->expectFailsRule( + new NoFragmentCycles(), + ' fragment fragA on Pet { ... on Dog { ...fragB @@ -161,12 +208,14 @@ class NoFragmentCyclesTest extends ValidatorTestCase ...fragA } } - ', [ - FormattedError::create( - NoFragmentCycles::cycleErrorMessage('fragA', ['fragB']), - [new SourceLocation(4, 11), new SourceLocation(9, 11)] - ) - ]); + ', + [ + FormattedError::create( + NoFragmentCycles::cycleErrorMessage('fragA', ['fragB']), + [new SourceLocation(4, 11), new SourceLocation(9, 11)] + ), + ] + ); } /** @@ -174,7 +223,9 @@ class NoFragmentCyclesTest extends ValidatorTestCase */ public function testNoSpreadingItselfDeeply() : void { - $this->expectFailsRule(new NoFragmentCycles, ' + $this->expectFailsRule( + new NoFragmentCycles(), + ' fragment fragA on Dog { ...fragB } fragment fragB on Dog { ...fragC } fragment fragC on Dog { ...fragO } @@ -183,28 +234,30 @@ class NoFragmentCyclesTest extends ValidatorTestCase fragment fragZ on Dog { ...fragO } fragment fragO on Dog { ...fragP } fragment fragP on Dog { ...fragA, ...fragX } - ', [ - FormattedError::create( - NoFragmentCycles::cycleErrorMessage('fragA', [ 'fragB', 'fragC', 'fragO', 'fragP' ]), - [ - new SourceLocation(2, 31), - new SourceLocation(3, 31), - new SourceLocation(4, 31), - new SourceLocation(8, 31), - new SourceLocation(9, 31), - ] - ), - FormattedError::create( - NoFragmentCycles::cycleErrorMessage('fragO', [ 'fragP', 'fragX', 'fragY', 'fragZ' ]), - [ - new SourceLocation(8, 31), - new SourceLocation(9, 41), - new SourceLocation(5, 31), - new SourceLocation(6, 31), - new SourceLocation(7, 31), - ] - ) - ]); + ', + [ + FormattedError::create( + NoFragmentCycles::cycleErrorMessage('fragA', ['fragB', 'fragC', 'fragO', 'fragP']), + [ + new SourceLocation(2, 31), + new SourceLocation(3, 31), + new SourceLocation(4, 31), + new SourceLocation(8, 31), + new SourceLocation(9, 31), + ] + ), + FormattedError::create( + NoFragmentCycles::cycleErrorMessage('fragO', ['fragP', 'fragX', 'fragY', 'fragZ']), + [ + new SourceLocation(8, 31), + new SourceLocation(9, 41), + new SourceLocation(5, 31), + new SourceLocation(6, 31), + new SourceLocation(7, 31), + ] + ), + ] + ); } /** @@ -212,20 +265,24 @@ class NoFragmentCyclesTest extends ValidatorTestCase */ public function testNoSpreadingItselfDeeplyTwoPaths() : void { - $this->expectFailsRule(new NoFragmentCycles, ' + $this->expectFailsRule( + new NoFragmentCycles(), + ' fragment fragA on Dog { ...fragB, ...fragC } fragment fragB on Dog { ...fragA } fragment fragC on Dog { ...fragA } - ', [ - FormattedError::create( - NoFragmentCycles::cycleErrorMessage('fragA', ['fragB']), - [new SourceLocation(2, 31), new SourceLocation(3, 31)] - ), - FormattedError::create( - NoFragmentCycles::cycleErrorMessage('fragA', ['fragC']), - [new SourceLocation(2, 41), new SourceLocation(4, 31)] - ) - ]); + ', + [ + FormattedError::create( + NoFragmentCycles::cycleErrorMessage('fragA', ['fragB']), + [new SourceLocation(2, 31), new SourceLocation(3, 31)] + ), + FormattedError::create( + NoFragmentCycles::cycleErrorMessage('fragA', ['fragC']), + [new SourceLocation(2, 41), new SourceLocation(4, 31)] + ), + ] + ); } /** @@ -233,20 +290,24 @@ class NoFragmentCyclesTest extends ValidatorTestCase */ public function testNoSpreadingItselfDeeplyTwoPathsTraverseOrder() : void { - $this->expectFailsRule(new NoFragmentCycles, ' + $this->expectFailsRule( + new NoFragmentCycles(), + ' fragment fragA on Dog { ...fragC } fragment fragB on Dog { ...fragC } fragment fragC on Dog { ...fragA, ...fragB } - ', [ - FormattedError::create( - NoFragmentCycles::cycleErrorMessage('fragA', [ 'fragC' ]), - [new SourceLocation(2,31), new SourceLocation(4,31)] - ), - FormattedError::create( - NoFragmentCycles::cycleErrorMessage('fragC', [ 'fragB' ]), - [new SourceLocation(4, 41), new SourceLocation(3, 31)] - ) - ]); + ', + [ + FormattedError::create( + NoFragmentCycles::cycleErrorMessage('fragA', ['fragC']), + [new SourceLocation(2, 31), new SourceLocation(4, 31)] + ), + FormattedError::create( + NoFragmentCycles::cycleErrorMessage('fragC', ['fragB']), + [new SourceLocation(4, 41), new SourceLocation(3, 31)] + ), + ] + ); } /** @@ -254,35 +315,31 @@ class NoFragmentCyclesTest extends ValidatorTestCase */ public function testNoSpreadingItselfDeeplyAndImmediately() : void { - $this->expectFailsRule(new NoFragmentCycles, ' + $this->expectFailsRule( + new NoFragmentCycles(), + ' fragment fragA on Dog { ...fragB } fragment fragB on Dog { ...fragB, ...fragC } fragment fragC on Dog { ...fragA, ...fragB } - ', [ - FormattedError::create( - NoFragmentCycles::cycleErrorMessage('fragB', []), - [new SourceLocation(3, 31)] - ), - FormattedError::create( - NoFragmentCycles::cycleErrorMessage('fragA', [ 'fragB', 'fragC' ]), - [ - new SourceLocation(2, 31), - new SourceLocation(3, 41), - new SourceLocation(4, 31) - ] - ), - FormattedError::create( - NoFragmentCycles::cycleErrorMessage('fragB', [ 'fragC' ]), - [new SourceLocation(3, 41), new SourceLocation(4, 41)] - ) - ]); - } - - private function cycleError($fargment, $spreadNames, $line, $column) - { - return FormattedError::create( - NoFragmentCycles::cycleErrorMessage($fargment, $spreadNames), - [new SourceLocation($line, $column)] + ', + [ + FormattedError::create( + NoFragmentCycles::cycleErrorMessage('fragB', []), + [new SourceLocation(3, 31)] + ), + FormattedError::create( + NoFragmentCycles::cycleErrorMessage('fragA', ['fragB', 'fragC']), + [ + new SourceLocation(2, 31), + new SourceLocation(3, 41), + new SourceLocation(4, 31), + ] + ), + FormattedError::create( + NoFragmentCycles::cycleErrorMessage('fragB', ['fragC']), + [new SourceLocation(3, 41), new SourceLocation(4, 41)] + ), + ] ); } } diff --git a/tests/Validator/NoUndefinedVariablesTest.php b/tests/Validator/NoUndefinedVariablesTest.php index b4b56b3..ea2e25e 100644 --- a/tests/Validator/NoUndefinedVariablesTest.php +++ b/tests/Validator/NoUndefinedVariablesTest.php @@ -1,4 +1,7 @@ expectPassesRule(new NoUndefinedVariables(), ' + $this->expectPassesRule( + new NoUndefinedVariables(), + ' query Foo($a: String, $b: String, $c: String) { field(a: $a, b: $b, c: $c) } - '); + ' + ); } /** @@ -26,7 +31,9 @@ class NoUndefinedVariablesTest extends ValidatorTestCase */ public function testAllVariablesDeeplyDefined() : void { - $this->expectPassesRule(new NoUndefinedVariables, ' + $this->expectPassesRule( + new NoUndefinedVariables(), + ' query Foo($a: String, $b: String, $c: String) { field(a: $a) { field(b: $b) { @@ -34,7 +41,8 @@ class NoUndefinedVariablesTest extends ValidatorTestCase } } } - '); + ' + ); } /** @@ -42,7 +50,9 @@ class NoUndefinedVariablesTest extends ValidatorTestCase */ public function testAllVariablesDeeplyInInlineFragmentsDefined() : void { - $this->expectPassesRule(new NoUndefinedVariables, ' + $this->expectPassesRule( + new NoUndefinedVariables(), + ' query Foo($a: String, $b: String, $c: String) { ... on Type { field(a: $a) { @@ -54,7 +64,8 @@ class NoUndefinedVariablesTest extends ValidatorTestCase } } } - '); + ' + ); } /** @@ -62,7 +73,9 @@ class NoUndefinedVariablesTest extends ValidatorTestCase */ public function testAllVariablesInFragmentsDeeplyDefined() : void { - $this->expectPassesRule(new NoUndefinedVariables, ' + $this->expectPassesRule( + new NoUndefinedVariables(), + ' query Foo($a: String, $b: String, $c: String) { ...FragA } @@ -79,7 +92,8 @@ class NoUndefinedVariablesTest extends ValidatorTestCase fragment FragC on Type { field(c: $c) } - '); + ' + ); } /** @@ -88,7 +102,9 @@ class NoUndefinedVariablesTest extends ValidatorTestCase public function testVariableWithinSingleFragmentDefinedInMultipleOperations() : void { // variable within single fragment defined in multiple operations - $this->expectPassesRule(new NoUndefinedVariables, ' + $this->expectPassesRule( + new NoUndefinedVariables(), + ' query Foo($a: String) { ...FragA } @@ -98,7 +114,8 @@ class NoUndefinedVariablesTest extends ValidatorTestCase fragment FragA on Type { field(a: $a) } - '); + ' + ); } /** @@ -106,7 +123,9 @@ class NoUndefinedVariablesTest extends ValidatorTestCase */ public function testVariableWithinFragmentsDefinedInOperations() : void { - $this->expectPassesRule(new NoUndefinedVariables, ' + $this->expectPassesRule( + new NoUndefinedVariables(), + ' query Foo($a: String) { ...FragA } @@ -119,7 +138,8 @@ class NoUndefinedVariablesTest extends ValidatorTestCase fragment FragB on Type { field(b: $b) } - '); + ' + ); } /** @@ -127,7 +147,9 @@ class NoUndefinedVariablesTest extends ValidatorTestCase */ public function testVariableWithinRecursiveFragmentDefined() : void { - $this->expectPassesRule(new NoUndefinedVariables, ' + $this->expectPassesRule( + new NoUndefinedVariables(), + ' query Foo($a: String) { ...FragA } @@ -136,7 +158,8 @@ class NoUndefinedVariablesTest extends ValidatorTestCase ...FragA } } - '); + ' + ); } /** @@ -144,13 +167,31 @@ class NoUndefinedVariablesTest extends ValidatorTestCase */ public function testVariableNotDefined() : void { - $this->expectFailsRule(new NoUndefinedVariables, ' + $this->expectFailsRule( + new NoUndefinedVariables(), + ' query Foo($a: String, $b: String, $c: String) { field(a: $a, b: $b, c: $c, d: $d) } - ', [ - $this->undefVar('d', 3, 39, 'Foo', 2, 7) - ]); + ', + [ + $this->undefVar('d', 3, 39, 'Foo', 2, 7), + ] + ); + } + + private function undefVar($varName, $line, $column, $opName = null, $l2 = null, $c2 = null) + { + $locs = [new SourceLocation($line, $column)]; + + if ($l2 && $c2) { + $locs[] = new SourceLocation($l2, $c2); + } + + return FormattedError::create( + NoUndefinedVariables::undefinedVarMessage($varName, $opName), + $locs + ); } /** @@ -158,13 +199,17 @@ class NoUndefinedVariablesTest extends ValidatorTestCase */ public function testVariableNotDefinedByUnNamedQuery() : void { - $this->expectFailsRule(new NoUndefinedVariables, ' + $this->expectFailsRule( + new NoUndefinedVariables(), + ' { field(a: $a) } - ', [ - $this->undefVar('a', 3, 18, '', 2, 7) - ]); + ', + [ + $this->undefVar('a', 3, 18, '', 2, 7), + ] + ); } /** @@ -172,14 +217,18 @@ class NoUndefinedVariablesTest extends ValidatorTestCase */ public function testMultipleVariablesNotDefined() : void { - $this->expectFailsRule(new NoUndefinedVariables, ' + $this->expectFailsRule( + new NoUndefinedVariables(), + ' query Foo($b: String) { field(a: $a, b: $b, c: $c) } - ', [ - $this->undefVar('a', 3, 18, 'Foo', 2, 7), - $this->undefVar('c', 3, 32, 'Foo', 2, 7) - ]); + ', + [ + $this->undefVar('a', 3, 18, 'Foo', 2, 7), + $this->undefVar('c', 3, 32, 'Foo', 2, 7), + ] + ); } /** @@ -187,16 +236,20 @@ class NoUndefinedVariablesTest extends ValidatorTestCase */ public function testVariableInFragmentNotDefinedByUnNamedQuery() : void { - $this->expectFailsRule(new NoUndefinedVariables, ' + $this->expectFailsRule( + new NoUndefinedVariables(), + ' { ...FragA } fragment FragA on Type { field(a: $a) } - ', [ - $this->undefVar('a', 6, 18, '', 2, 7) - ]); + ', + [ + $this->undefVar('a', 6, 18, '', 2, 7), + ] + ); } /** @@ -204,7 +257,9 @@ class NoUndefinedVariablesTest extends ValidatorTestCase */ public function testVariableInFragmentNotDefinedByOperation() : void { - $this->expectFailsRule(new NoUndefinedVariables, ' + $this->expectFailsRule( + new NoUndefinedVariables(), + ' query Foo($a: String, $b: String) { ...FragA } @@ -221,9 +276,11 @@ class NoUndefinedVariablesTest extends ValidatorTestCase fragment FragC on Type { field(c: $c) } - ', [ - $this->undefVar('c', 16, 18, 'Foo', 2, 7) - ]); + ', + [ + $this->undefVar('c', 16, 18, 'Foo', 2, 7), + ] + ); } /** @@ -231,7 +288,9 @@ class NoUndefinedVariablesTest extends ValidatorTestCase */ public function testMultipleVariablesInFragmentsNotDefined() : void { - $this->expectFailsRule(new NoUndefinedVariables, ' + $this->expectFailsRule( + new NoUndefinedVariables(), + ' query Foo($b: String) { ...FragA } @@ -248,10 +307,12 @@ class NoUndefinedVariablesTest extends ValidatorTestCase fragment FragC on Type { field(c: $c) } - ', [ - $this->undefVar('a', 6, 18, 'Foo', 2, 7), - $this->undefVar('c', 16, 18, 'Foo', 2, 7) - ]); + ', + [ + $this->undefVar('a', 6, 18, 'Foo', 2, 7), + $this->undefVar('c', 16, 18, 'Foo', 2, 7), + ] + ); } /** @@ -259,7 +320,9 @@ class NoUndefinedVariablesTest extends ValidatorTestCase */ public function testSingleVariableInFragmentNotDefinedByMultipleOperations() : void { - $this->expectFailsRule(new NoUndefinedVariables, ' + $this->expectFailsRule( + new NoUndefinedVariables(), + ' query Foo($a: String) { ...FragAB } @@ -269,10 +332,12 @@ class NoUndefinedVariablesTest extends ValidatorTestCase fragment FragAB on Type { field(a: $a, b: $b) } - ', [ - $this->undefVar('b', 9, 25, 'Foo', 2, 7), - $this->undefVar('b', 9, 25, 'Bar', 5, 7) - ]); + ', + [ + $this->undefVar('b', 9, 25, 'Foo', 2, 7), + $this->undefVar('b', 9, 25, 'Bar', 5, 7), + ] + ); } /** @@ -280,7 +345,9 @@ class NoUndefinedVariablesTest extends ValidatorTestCase */ public function testVariablesInFragmentNotDefinedByMultipleOperations() : void { - $this->expectFailsRule(new NoUndefinedVariables, ' + $this->expectFailsRule( + new NoUndefinedVariables(), + ' query Foo($b: String) { ...FragAB } @@ -290,10 +357,12 @@ class NoUndefinedVariablesTest extends ValidatorTestCase fragment FragAB on Type { field(a: $a, b: $b) } - ', [ - $this->undefVar('a', 9, 18, 'Foo', 2, 7), - $this->undefVar('b', 9, 25, 'Bar', 5, 7) - ]); + ', + [ + $this->undefVar('a', 9, 18, 'Foo', 2, 7), + $this->undefVar('b', 9, 25, 'Bar', 5, 7), + ] + ); } /** @@ -301,7 +370,9 @@ class NoUndefinedVariablesTest extends ValidatorTestCase */ public function testVariableInFragmentUsedByOtherOperation() : void { - $this->expectFailsRule(new NoUndefinedVariables, ' + $this->expectFailsRule( + new NoUndefinedVariables(), + ' query Foo($b: String) { ...FragA } @@ -314,10 +385,12 @@ class NoUndefinedVariablesTest extends ValidatorTestCase fragment FragB on Type { field(b: $b) } - ', [ - $this->undefVar('a', 9, 18, 'Foo', 2, 7), - $this->undefVar('b', 12, 18, 'Bar', 5, 7) - ]); + ', + [ + $this->undefVar('a', 9, 18, 'Foo', 2, 7), + $this->undefVar('b', 12, 18, 'Bar', 5, 7), + ] + ); } /** @@ -325,7 +398,9 @@ class NoUndefinedVariablesTest extends ValidatorTestCase */ public function testMultipleUndefinedVariablesProduceMultipleErrors() : void { - $this->expectFailsRule(new NoUndefinedVariables, ' + $this->expectFailsRule( + new NoUndefinedVariables(), + ' query Foo($b: String) { ...FragAB } @@ -340,28 +415,15 @@ class NoUndefinedVariablesTest extends ValidatorTestCase fragment FragC on Type { field2(c: $c) } - ', [ - $this->undefVar('a', 9, 19, 'Foo', 2, 7), - $this->undefVar('a', 11, 19, 'Foo', 2, 7), - $this->undefVar('c', 14, 19, 'Foo', 2, 7), - $this->undefVar('b', 9, 26, 'Bar', 5, 7), - $this->undefVar('b', 11, 26, 'Bar', 5, 7), - $this->undefVar('c', 14, 19, 'Bar', 5, 7), - ]); - } - - - private function undefVar($varName, $line, $column, $opName = null, $l2 = null, $c2 = null) - { - $locs = [new SourceLocation($line, $column)]; - - if ($l2 && $c2) { - $locs[] = new SourceLocation($l2, $c2); - } - - return FormattedError::create( - NoUndefinedVariables::undefinedVarMessage($varName, $opName), - $locs + ', + [ + $this->undefVar('a', 9, 19, 'Foo', 2, 7), + $this->undefVar('a', 11, 19, 'Foo', 2, 7), + $this->undefVar('c', 14, 19, 'Foo', 2, 7), + $this->undefVar('b', 9, 26, 'Bar', 5, 7), + $this->undefVar('b', 11, 26, 'Bar', 5, 7), + $this->undefVar('c', 14, 19, 'Bar', 5, 7), + ] ); } } diff --git a/tests/Validator/NoUnusedFragmentsTest.php b/tests/Validator/NoUnusedFragmentsTest.php index 323f915..9d794a8 100644 --- a/tests/Validator/NoUnusedFragmentsTest.php +++ b/tests/Validator/NoUnusedFragmentsTest.php @@ -1,4 +1,7 @@ expectPassesRule(new NoUnusedFragments(), ' + $this->expectPassesRule( + new NoUnusedFragments(), + ' { human(id: 4) { ...HumanFields1 @@ -33,7 +37,8 @@ class NoUnusedFragmentsTest extends ValidatorTestCase fragment HumanFields3 on Human { name } - '); + ' + ); } /** @@ -41,7 +46,9 @@ class NoUnusedFragmentsTest extends ValidatorTestCase */ public function testAllFragmentNamesAreUsedByMultipleOperations() : void { - $this->expectPassesRule(new NoUnusedFragments, ' + $this->expectPassesRule( + new NoUnusedFragments(), + ' query Foo { human(id: 4) { ...HumanFields1 @@ -62,7 +69,8 @@ class NoUnusedFragmentsTest extends ValidatorTestCase fragment HumanFields3 on Human { name } - '); + ' + ); } /** @@ -70,7 +78,9 @@ class NoUnusedFragmentsTest extends ValidatorTestCase */ public function testContainsUnknownFragments() : void { - $this->expectFailsRule(new NoUnusedFragments, ' + $this->expectFailsRule( + new NoUnusedFragments(), + ' query Foo { human(id: 4) { ...HumanFields1 @@ -97,10 +107,20 @@ class NoUnusedFragmentsTest extends ValidatorTestCase fragment Unused2 on Human { name } - ', [ - $this->unusedFrag('Unused1', 22, 7), - $this->unusedFrag('Unused2', 25, 7), - ]); + ', + [ + $this->unusedFrag('Unused1', 22, 7), + $this->unusedFrag('Unused2', 25, 7), + ] + ); + } + + private function unusedFrag($fragName, $line, $column) + { + return FormattedError::create( + NoUnusedFragments::unusedFragMessage($fragName), + [new SourceLocation($line, $column)] + ); } /** @@ -108,7 +128,9 @@ class NoUnusedFragmentsTest extends ValidatorTestCase */ public function testContainsUnknownFragmentsWithRefCycle() : void { - $this->expectFailsRule(new NoUnusedFragments, ' + $this->expectFailsRule( + new NoUnusedFragments(), + ' query Foo { human(id: 4) { ...HumanFields1 @@ -137,10 +159,12 @@ class NoUnusedFragmentsTest extends ValidatorTestCase name ...Unused1 } - ', [ - $this->unusedFrag('Unused1', 22, 7), - $this->unusedFrag('Unused2', 26, 7), - ]); + ', + [ + $this->unusedFrag('Unused1', 22, 7), + $this->unusedFrag('Unused2', 26, 7), + ] + ); } /** @@ -148,8 +172,9 @@ class NoUnusedFragmentsTest extends ValidatorTestCase */ public function testContainsUnknownAndUndefFragments() : void { - - $this->expectFailsRule(new NoUnusedFragments, ' + $this->expectFailsRule( + new NoUnusedFragments(), + ' query Foo { human(id: 4) { ...bar @@ -158,16 +183,10 @@ class NoUnusedFragmentsTest extends ValidatorTestCase fragment foo on Human { name } - ', [ - $this->unusedFrag('foo', 7, 7), - ]); - } - - private function unusedFrag($fragName, $line, $column) - { - return FormattedError::create( - NoUnusedFragments::unusedFragMessage($fragName), - [new SourceLocation($line, $column)] + ', + [ + $this->unusedFrag('foo', 7, 7), + ] ); } } diff --git a/tests/Validator/NoUnusedVariablesTest.php b/tests/Validator/NoUnusedVariablesTest.php index c261c15..7dc7d40 100644 --- a/tests/Validator/NoUnusedVariablesTest.php +++ b/tests/Validator/NoUnusedVariablesTest.php @@ -1,4 +1,7 @@ expectPassesRule(new NoUnusedVariables(), ' + $this->expectPassesRule( + new NoUnusedVariables(), + ' query Foo($a: String, $b: String, $c: String) { field(a: $a, b: $b, c: $c) } - '); + ' + ); } /** @@ -26,7 +31,9 @@ class NoUnusedVariablesTest extends ValidatorTestCase */ public function testUsesAllVariablesDeeply() : void { - $this->expectPassesRule(new NoUnusedVariables, ' + $this->expectPassesRule( + new NoUnusedVariables(), + ' query Foo($a: String, $b: String, $c: String) { field(a: $a) { field(b: $b) { @@ -34,7 +41,8 @@ class NoUnusedVariablesTest extends ValidatorTestCase } } } - '); + ' + ); } /** @@ -42,7 +50,9 @@ class NoUnusedVariablesTest extends ValidatorTestCase */ public function testUsesAllVariablesDeeplyInInlineFragments() : void { - $this->expectPassesRule(new NoUnusedVariables, ' + $this->expectPassesRule( + new NoUnusedVariables(), + ' query Foo($a: String, $b: String, $c: String) { ... on Type { field(a: $a) { @@ -54,7 +64,8 @@ class NoUnusedVariablesTest extends ValidatorTestCase } } } - '); + ' + ); } /** @@ -62,7 +73,9 @@ class NoUnusedVariablesTest extends ValidatorTestCase */ public function testUsesAllVariablesInFragments() : void { - $this->expectPassesRule(new NoUnusedVariables, ' + $this->expectPassesRule( + new NoUnusedVariables(), + ' query Foo($a: String, $b: String, $c: String) { ...FragA } @@ -79,7 +92,8 @@ class NoUnusedVariablesTest extends ValidatorTestCase fragment FragC on Type { field(c: $c) } - '); + ' + ); } /** @@ -87,7 +101,9 @@ class NoUnusedVariablesTest extends ValidatorTestCase */ public function testVariableUsedByFragmentInMultipleOperations() : void { - $this->expectPassesRule(new NoUnusedVariables, ' + $this->expectPassesRule( + new NoUnusedVariables(), + ' query Foo($a: String) { ...FragA } @@ -100,7 +116,8 @@ class NoUnusedVariablesTest extends ValidatorTestCase fragment FragB on Type { field(b: $b) } - '); + ' + ); } /** @@ -108,7 +125,9 @@ class NoUnusedVariablesTest extends ValidatorTestCase */ public function testVariableUsedByRecursiveFragment() : void { - $this->expectPassesRule(new NoUnusedVariables, ' + $this->expectPassesRule( + new NoUnusedVariables(), + ' query Foo($a: String) { ...FragA } @@ -117,7 +136,8 @@ class NoUnusedVariablesTest extends ValidatorTestCase ...FragA } } - '); + ' + ); } /** @@ -125,13 +145,25 @@ class NoUnusedVariablesTest extends ValidatorTestCase */ public function testVariableNotUsed() : void { - $this->expectFailsRule(new NoUnusedVariables, ' + $this->expectFailsRule( + new NoUnusedVariables(), + ' query ($a: String, $b: String, $c: String) { field(a: $a, b: $b) } - ', [ - $this->unusedVar('c', null, 2, 38) - ]); + ', + [ + $this->unusedVar('c', null, 2, 38), + ] + ); + } + + private function unusedVar($varName, $opName, $line, $column) + { + return FormattedError::create( + NoUnusedVariables::unusedVariableMessage($varName, $opName), + [new SourceLocation($line, $column)] + ); } /** @@ -139,14 +171,18 @@ class NoUnusedVariablesTest extends ValidatorTestCase */ public function testMultipleVariablesNotUsed() : void { - $this->expectFailsRule(new NoUnusedVariables, ' + $this->expectFailsRule( + new NoUnusedVariables(), + ' query Foo($a: String, $b: String, $c: String) { field(b: $b) } - ', [ - $this->unusedVar('a', 'Foo', 2, 17), - $this->unusedVar('c', 'Foo', 2, 41) - ]); + ', + [ + $this->unusedVar('a', 'Foo', 2, 17), + $this->unusedVar('c', 'Foo', 2, 41), + ] + ); } /** @@ -154,7 +190,9 @@ class NoUnusedVariablesTest extends ValidatorTestCase */ public function testVariableNotUsedInFragments() : void { - $this->expectFailsRule(new NoUnusedVariables, ' + $this->expectFailsRule( + new NoUnusedVariables(), + ' query Foo($a: String, $b: String, $c: String) { ...FragA } @@ -171,9 +209,11 @@ class NoUnusedVariablesTest extends ValidatorTestCase fragment FragC on Type { field } - ', [ - $this->unusedVar('c', 'Foo', 2, 41) - ]); + ', + [ + $this->unusedVar('c', 'Foo', 2, 41), + ] + ); } /** @@ -181,7 +221,9 @@ class NoUnusedVariablesTest extends ValidatorTestCase */ public function testMultipleVariablesNotUsed2() : void { - $this->expectFailsRule(new NoUnusedVariables, ' + $this->expectFailsRule( + new NoUnusedVariables(), + ' query Foo($a: String, $b: String, $c: String) { ...FragA } @@ -198,10 +240,12 @@ class NoUnusedVariablesTest extends ValidatorTestCase fragment FragC on Type { field } - ', [ - $this->unusedVar('a', 'Foo', 2, 17), - $this->unusedVar('c', 'Foo', 2, 41) - ]); + ', + [ + $this->unusedVar('a', 'Foo', 2, 17), + $this->unusedVar('c', 'Foo', 2, 41), + ] + ); } /** @@ -209,7 +253,9 @@ class NoUnusedVariablesTest extends ValidatorTestCase */ public function testVariableNotUsedByUnreferencedFragment() : void { - $this->expectFailsRule(new NoUnusedVariables, ' + $this->expectFailsRule( + new NoUnusedVariables(), + ' query Foo($b: String) { ...FragA } @@ -219,9 +265,11 @@ class NoUnusedVariablesTest extends ValidatorTestCase fragment FragB on Type { field(b: $b) } - ', [ - $this->unusedVar('b', 'Foo', 2, 17) - ]); + ', + [ + $this->unusedVar('b', 'Foo', 2, 17), + ] + ); } /** @@ -229,7 +277,9 @@ class NoUnusedVariablesTest extends ValidatorTestCase */ public function testVariableNotUsedByFragmentUsedByOtherOperation() : void { - $this->expectFailsRule(new NoUnusedVariables, ' + $this->expectFailsRule( + new NoUnusedVariables(), + ' query Foo($b: String) { ...FragA } @@ -242,17 +292,11 @@ class NoUnusedVariablesTest extends ValidatorTestCase fragment FragB on Type { field(b: $b) } - ', [ - $this->unusedVar('b', 'Foo', 2, 17), - $this->unusedVar('a', 'Bar', 5, 17) - ]); - } - - private function unusedVar($varName, $opName, $line, $column) - { - return FormattedError::create( - NoUnusedVariables::unusedVariableMessage($varName, $opName), - [new SourceLocation($line, $column)] + ', + [ + $this->unusedVar('b', 'Foo', 2, 17), + $this->unusedVar('a', 'Bar', 5, 17), + ] ); } } diff --git a/tests/Validator/OverlappingFieldsCanBeMergedTest.php b/tests/Validator/OverlappingFieldsCanBeMergedTest.php index 2aaed7e..e10e020 100644 --- a/tests/Validator/OverlappingFieldsCanBeMergedTest.php +++ b/tests/Validator/OverlappingFieldsCanBeMergedTest.php @@ -1,29 +1,34 @@ expectPassesRule(new OverlappingFieldsCanBeMerged(), ' + $this->expectPassesRule( + new OverlappingFieldsCanBeMerged(), + ' fragment uniqueFields on Dog { name nickname } - '); + ' + ); } /** @@ -31,12 +36,15 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testIdenticalFields() : void { - $this->expectPassesRule(new OverlappingFieldsCanBeMerged, ' + $this->expectPassesRule( + new OverlappingFieldsCanBeMerged(), + ' fragment mergeIdenticalFields on Dog { name name } - '); + ' + ); } /** @@ -44,12 +52,15 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testIdenticalFieldsWithIdenticalArgs() : void { - $this->expectPassesRule(new OverlappingFieldsCanBeMerged, ' + $this->expectPassesRule( + new OverlappingFieldsCanBeMerged(), + ' fragment mergeIdenticalFieldsWithIdenticalArgs on Dog { doesKnowCommand(dogCommand: SIT) doesKnowCommand(dogCommand: SIT) } - '); + ' + ); } /** @@ -57,12 +68,15 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testIdenticalFieldsWithIdenticalDirectives() : void { - $this->expectPassesRule(new OverlappingFieldsCanBeMerged, ' + $this->expectPassesRule( + new OverlappingFieldsCanBeMerged(), + ' fragment mergeSameFieldsWithSameDirectives on Dog { name @include(if: true) name @include(if: true) } - '); + ' + ); } /** @@ -70,12 +84,15 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testDifferentArgsWithDifferentAliases() : void { - $this->expectPassesRule(new OverlappingFieldsCanBeMerged, ' + $this->expectPassesRule( + new OverlappingFieldsCanBeMerged(), + ' fragment differentArgsWithDifferentAliases on Dog { knowsSit : doesKnowCommand(dogCommand: SIT) knowsDown : doesKnowCommand(dogCommand: DOWN) } - '); + ' + ); } /** @@ -83,12 +100,15 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testDifferentDirectivesWithDifferentAliases() : void { - $this->expectPassesRule(new OverlappingFieldsCanBeMerged, ' + $this->expectPassesRule( + new OverlappingFieldsCanBeMerged(), + ' fragment differentDirectivesWithDifferentAliases on Dog { nameIfTrue : name @include(if: true) nameIfFalse : name @include(if: false) } - '); + ' + ); } /** @@ -99,12 +119,15 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase // Note: Differing skip/include directives don't create an ambiguous return // value and are acceptable in conditions where differing runtime values // may have the same desired effect of including or skipping a field. - $this->expectPassesRule(new OverlappingFieldsCanBeMerged, ' + $this->expectPassesRule( + new OverlappingFieldsCanBeMerged(), + ' fragment differentDirectivesWithDifferentAliases on Dog { name @include(if: true) name @include(if: false) } - '); + ' + ); } /** @@ -112,17 +135,24 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testSameAliasesWithDifferentFieldTargets() : void { - $this->expectFailsRule(new OverlappingFieldsCanBeMerged, ' + $this->expectFailsRule( + new OverlappingFieldsCanBeMerged(), + ' fragment sameAliasesWithDifferentFieldTargets on Dog { fido : name fido : nickname } - ', [ - FormattedError::create( - OverlappingFieldsCanBeMerged::fieldsConflictMessage('fido', 'name and nickname are different fields'), - [new SourceLocation(3, 9), new SourceLocation(4, 9)] - ) - ]); + ', + [ + FormattedError::create( + OverlappingFieldsCanBeMerged::fieldsConflictMessage( + 'fido', + 'name and nickname are different fields' + ), + [new SourceLocation(3, 9), new SourceLocation(4, 9)] + ), + ] + ); } /** @@ -132,7 +162,9 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase { // This is valid since no object can be both a "Dog" and a "Cat", thus // these fields can never overlap. - $this->expectPassesRule(new OverlappingFieldsCanBeMerged, ' + $this->expectPassesRule( + new OverlappingFieldsCanBeMerged(), + ' fragment sameAliasesWithDifferentFieldTargets on Pet { ... on Dog { name @@ -141,7 +173,8 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase name: nickname } } - '); + ' + ); } /** @@ -149,17 +182,24 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testAliasMaskingDirectFieldAccess() : void { - $this->expectFailsRule(new OverlappingFieldsCanBeMerged, ' + $this->expectFailsRule( + new OverlappingFieldsCanBeMerged(), + ' fragment aliasMaskingDirectFieldAccess on Dog { name : nickname name } - ', [ - FormattedError::create( - OverlappingFieldsCanBeMerged::fieldsConflictMessage('name', 'nickname and name are different fields'), - [new SourceLocation(3, 9), new SourceLocation(4, 9)] - ) - ]); + ', + [ + FormattedError::create( + OverlappingFieldsCanBeMerged::fieldsConflictMessage( + 'name', + 'nickname and name are different fields' + ), + [new SourceLocation(3, 9), new SourceLocation(4, 9)] + ), + ] + ); } /** @@ -167,17 +207,24 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testDifferentArgsSecondAddsAnArgument() : void { - $this->expectFailsRule(new OverlappingFieldsCanBeMerged, ' + $this->expectFailsRule( + new OverlappingFieldsCanBeMerged(), + ' fragment conflictingArgs on Dog { doesKnowCommand doesKnowCommand(dogCommand: HEEL) } - ', [ - FormattedError::create( - OverlappingFieldsCanBeMerged::fieldsConflictMessage('doesKnowCommand', 'they have differing arguments'), - [new SourceLocation(3, 9), new SourceLocation(4, 9)] - ) - ]); + ', + [ + FormattedError::create( + OverlappingFieldsCanBeMerged::fieldsConflictMessage( + 'doesKnowCommand', + 'they have differing arguments' + ), + [new SourceLocation(3, 9), new SourceLocation(4, 9)] + ), + ] + ); } /** @@ -185,7 +232,9 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testDifferentArgsSecondMissingAnArgument() : void { - $this->expectFailsRule(new OverlappingFieldsCanBeMerged, ' + $this->expectFailsRule( + new OverlappingFieldsCanBeMerged(), + ' fragment conflictingArgs on Dog { doesKnowCommand(dogCommand: SIT) doesKnowCommand @@ -193,9 +242,12 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase ', [ FormattedError::create( - OverlappingFieldsCanBeMerged::fieldsConflictMessage('doesKnowCommand', 'they have differing arguments'), + OverlappingFieldsCanBeMerged::fieldsConflictMessage( + 'doesKnowCommand', + 'they have differing arguments' + ), [new SourceLocation(3, 9), new SourceLocation(4, 9)] - ) + ), ] ); } @@ -205,17 +257,24 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testConflictingArgs() : void { - $this->expectFailsRule(new OverlappingFieldsCanBeMerged, ' + $this->expectFailsRule( + new OverlappingFieldsCanBeMerged(), + ' fragment conflictingArgs on Dog { doesKnowCommand(dogCommand: SIT) doesKnowCommand(dogCommand: HEEL) } - ', [ - FormattedError::create( - OverlappingFieldsCanBeMerged::fieldsConflictMessage('doesKnowCommand', 'they have differing arguments'), - [new SourceLocation(3,9), new SourceLocation(4,9)] - ) - ]); + ', + [ + FormattedError::create( + OverlappingFieldsCanBeMerged::fieldsConflictMessage( + 'doesKnowCommand', + 'they have differing arguments' + ), + [new SourceLocation(3, 9), new SourceLocation(4, 9)] + ), + ] + ); } /** @@ -225,7 +284,9 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase { // This is valid since no object can be both a "Dog" and a "Cat", thus // these fields can never overlap. - $this->expectPassesRule(new OverlappingFieldsCanBeMerged, ' + $this->expectPassesRule( + new OverlappingFieldsCanBeMerged(), + ' fragment conflictingArgs on Pet { ... on Dog { name(surname: true) @@ -234,7 +295,8 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase name } } - '); + ' + ); } /** @@ -242,7 +304,9 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testEncountersConflictInFragments() : void { - $this->expectFailsRule(new OverlappingFieldsCanBeMerged, ' + $this->expectFailsRule( + new OverlappingFieldsCanBeMerged(), + ' { ...A ...B @@ -253,12 +317,14 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase fragment B on Type { x: b } - ', [ - FormattedError::create( - OverlappingFieldsCanBeMerged::fieldsConflictMessage('x', 'a and b are different fields'), - [new SourceLocation(7, 9), new SourceLocation(10, 9)] - ) - ]); + ', + [ + FormattedError::create( + OverlappingFieldsCanBeMerged::fieldsConflictMessage('x', 'a and b are different fields'), + [new SourceLocation(7, 9), new SourceLocation(10, 9)] + ), + ] + ); } /** @@ -266,7 +332,9 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testReportsEachConflictOnce() : void { - $this->expectFailsRule(new OverlappingFieldsCanBeMerged, ' + $this->expectFailsRule( + new OverlappingFieldsCanBeMerged(), + ' { f1 { ...A @@ -288,20 +356,22 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase fragment B on Type { x: b } - ', [ - FormattedError::create( - OverlappingFieldsCanBeMerged::fieldsConflictMessage('x', 'a and b are different fields'), - [new SourceLocation(18, 9), new SourceLocation(21, 9)] - ), - FormattedError::create( - OverlappingFieldsCanBeMerged::fieldsConflictMessage('x', 'c and a are different fields'), - [new SourceLocation(14, 11), new SourceLocation(18, 9)] - ), - FormattedError::create( - OverlappingFieldsCanBeMerged::fieldsConflictMessage('x', 'c and b are different fields'), - [new SourceLocation(14, 11), new SourceLocation(21, 9)] - ) - ]); + ', + [ + FormattedError::create( + OverlappingFieldsCanBeMerged::fieldsConflictMessage('x', 'a and b are different fields'), + [new SourceLocation(18, 9), new SourceLocation(21, 9)] + ), + FormattedError::create( + OverlappingFieldsCanBeMerged::fieldsConflictMessage('x', 'c and a are different fields'), + [new SourceLocation(14, 11), new SourceLocation(18, 9)] + ), + FormattedError::create( + OverlappingFieldsCanBeMerged::fieldsConflictMessage('x', 'c and b are different fields'), + [new SourceLocation(14, 11), new SourceLocation(21, 9)] + ), + ] + ); } /** @@ -309,7 +379,9 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testDeepConflict() : void { - $this->expectFailsRule(new OverlappingFieldsCanBeMerged, ' + $this->expectFailsRule( + new OverlappingFieldsCanBeMerged(), + ' { field { x: a @@ -318,17 +390,22 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase x: b } } - ', [ - FormattedError::create( - OverlappingFieldsCanBeMerged::fieldsConflictMessage('field', [['x', 'a and b are different fields']]), - [ - new SourceLocation(3, 9), - new SourceLocation(4, 11), - new SourceLocation(6,9), - new SourceLocation(7, 11) - ] - ) - ]); + ', + [ + FormattedError::create( + OverlappingFieldsCanBeMerged::fieldsConflictMessage( + 'field', + [['x', 'a and b are different fields']] + ), + [ + new SourceLocation(3, 9), + new SourceLocation(4, 11), + new SourceLocation(6, 9), + new SourceLocation(7, 11), + ] + ), + ] + ); } /** @@ -336,7 +413,9 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testDeepConflictWithMultipleIssues() : void { - $this->expectFailsRule(new OverlappingFieldsCanBeMerged, ' + $this->expectFailsRule( + new OverlappingFieldsCanBeMerged(), + ' { field { x: a @@ -347,22 +426,27 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase y: d } } - ', [ - FormattedError::create( - OverlappingFieldsCanBeMerged::fieldsConflictMessage('field', [ - ['x', 'a and b are different fields'], - ['y', 'c and d are different fields'] - ]), - [ - new SourceLocation(3,9), - new SourceLocation(4,11), - new SourceLocation(5,11), - new SourceLocation(7,9), - new SourceLocation(8,11), - new SourceLocation(9,11) - ] - ) - ]); + ', + [ + FormattedError::create( + OverlappingFieldsCanBeMerged::fieldsConflictMessage( + 'field', + [ + ['x', 'a and b are different fields'], + ['y', 'c and d are different fields'], + ] + ), + [ + new SourceLocation(3, 9), + new SourceLocation(4, 11), + new SourceLocation(5, 11), + new SourceLocation(7, 9), + new SourceLocation(8, 11), + new SourceLocation(9, 11), + ] + ), + ] + ); } /** @@ -370,7 +454,9 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testVeryDeepConflict() : void { - $this->expectFailsRule(new OverlappingFieldsCanBeMerged, ' + $this->expectFailsRule( + new OverlappingFieldsCanBeMerged(), + ' { field { deepField { @@ -383,19 +469,24 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase } } } - ', [ - FormattedError::create( - OverlappingFieldsCanBeMerged::fieldsConflictMessage('field', [['deepField', [['x', 'a and b are different fields']]]]), - [ - new SourceLocation(3,9), - new SourceLocation(4,11), - new SourceLocation(5,13), - new SourceLocation(8,9), - new SourceLocation(9,11), - new SourceLocation(10,13) - ] - ) - ]); + ', + [ + FormattedError::create( + OverlappingFieldsCanBeMerged::fieldsConflictMessage( + 'field', + [['deepField', [['x', 'a and b are different fields']]]] + ), + [ + new SourceLocation(3, 9), + new SourceLocation(4, 11), + new SourceLocation(5, 13), + new SourceLocation(8, 9), + new SourceLocation(9, 11), + new SourceLocation(10, 13), + ] + ), + ] + ); } /** @@ -403,7 +494,9 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testReportsDeepConflictToNearestCommonAncestor() : void { - $this->expectFailsRule(new OverlappingFieldsCanBeMerged, ' + $this->expectFailsRule( + new OverlappingFieldsCanBeMerged(), + ' { field { deepField { @@ -419,17 +512,22 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase } } } - ', [ - FormattedError::create( - OverlappingFieldsCanBeMerged::fieldsConflictMessage('deepField', [['x', 'a and b are different fields']]), - [ - new SourceLocation(4,11), - new SourceLocation(5,13), - new SourceLocation(7,11), - new SourceLocation(8,13) - ] - ) - ]); + ', + [ + FormattedError::create( + OverlappingFieldsCanBeMerged::fieldsConflictMessage( + 'deepField', + [['x', 'a and b are different fields']] + ), + [ + new SourceLocation(4, 11), + new SourceLocation(5, 13), + new SourceLocation(7, 11), + new SourceLocation(8, 13), + ] + ), + ] + ); } /** @@ -437,7 +535,9 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testReportsDeepConflictToNearestCommonAncestorInFragments() : void { - $this->expectFailsRule(new OverlappingFieldsCanBeMerged, ' + $this->expectFailsRule( + new OverlappingFieldsCanBeMerged(), + ' { field { ...F @@ -461,17 +561,22 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase } } } - ', [ - FormattedError::create( - OverlappingFieldsCanBeMerged::fieldsConflictMessage('deeperField', [['x', 'a and b are different fields']]), - [ - new SourceLocation(12,11), - new SourceLocation(13,13), - new SourceLocation(15,11), - new SourceLocation(16,13), - ] - ) - ]); + ', + [ + FormattedError::create( + OverlappingFieldsCanBeMerged::fieldsConflictMessage( + 'deeperField', + [['x', 'a and b are different fields']] + ), + [ + new SourceLocation(12, 11), + new SourceLocation(13, 13), + new SourceLocation(15, 11), + new SourceLocation(16, 13), + ] + ), + ] + ); } /** @@ -479,7 +584,9 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testReportsDeepConflictInNestedFragments() : void { - $this->expectFailsRule(new OverlappingFieldsCanBeMerged, ' + $this->expectFailsRule( + new OverlappingFieldsCanBeMerged(), + ' { field { ...F @@ -502,22 +609,27 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase fragment J on T { x: b } - ', [ - FormattedError::create( - OverlappingFieldsCanBeMerged::fieldsConflictMessage('field', [ - ['x', 'a and b are different fields'], - ['y', 'c and d are different fields'], - ]), - [ - new SourceLocation(3,9), - new SourceLocation(11,9), - new SourceLocation(15,9), - new SourceLocation(6,9), - new SourceLocation(22,9), - new SourceLocation(18,9), - ] - ) - ]); + ', + [ + FormattedError::create( + OverlappingFieldsCanBeMerged::fieldsConflictMessage( + 'field', + [ + ['x', 'a and b are different fields'], + ['y', 'c and d are different fields'], + ] + ), + [ + new SourceLocation(3, 9), + new SourceLocation(11, 9), + new SourceLocation(15, 9), + new SourceLocation(6, 9), + new SourceLocation(22, 9), + new SourceLocation(18, 9), + ] + ), + ] + ); } /** @@ -525,7 +637,9 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testIgnoresUnknownFragments() : void { - $this->expectPassesRule(new OverlappingFieldsCanBeMerged, ' + $this->expectPassesRule( + new OverlappingFieldsCanBeMerged(), + ' { field { ...Unknown @@ -536,7 +650,8 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase field ...OtherUnknown } - '); + ' + ); } // Describe: return types must be unambiguous @@ -550,7 +665,10 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase // type IntBox and the interface type NonNullStringBox1. While that // condition does not exist in the current schema, the schema could // expand in the future to allow this. Thus it is invalid. - $this->expectFailsRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, ' + $this->expectFailsRuleWithSchema( + $this->getSchema(), + new OverlappingFieldsCanBeMerged(), + ' { someBox { ...on IntBox { @@ -561,16 +679,136 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase } } } - ', [ - FormattedError::create( - OverlappingFieldsCanBeMerged::fieldsConflictMessage( - 'scalar', - 'they return conflicting types Int and String!' + ', + [ + FormattedError::create( + OverlappingFieldsCanBeMerged::fieldsConflictMessage( + 'scalar', + 'they return conflicting types Int and String!' + ), + [ + new SourceLocation(5, 15), + new SourceLocation(8, 15), + ] ), - [new SourceLocation(5, 15), - new SourceLocation(8, 15)] - ) + ] + ); + } + + private function getSchema() + { + $StringBox = null; + $IntBox = null; + $SomeBox = null; + + $SomeBox = new InterfaceType([ + 'name' => 'SomeBox', + 'fields' => function () use (&$SomeBox) { + return [ + 'deepBox' => ['type' => $SomeBox], + 'unrelatedField' => ['type' => Type::string()], + ]; + }, ]); + + $StringBox = new ObjectType([ + 'name' => 'StringBox', + 'interfaces' => [$SomeBox], + 'fields' => function () use (&$StringBox, &$IntBox) { + return [ + 'scalar' => ['type' => Type::string()], + 'deepBox' => ['type' => $StringBox], + 'unrelatedField' => ['type' => Type::string()], + 'listStringBox' => ['type' => Type::listOf($StringBox)], + 'stringBox' => ['type' => $StringBox], + 'intBox' => ['type' => $IntBox], + ]; + }, + ]); + + $IntBox = new ObjectType([ + 'name' => 'IntBox', + 'interfaces' => [$SomeBox], + 'fields' => function () use (&$StringBox, &$IntBox) { + return [ + 'scalar' => ['type' => Type::int()], + 'deepBox' => ['type' => $IntBox], + 'unrelatedField' => ['type' => Type::string()], + 'listStringBox' => ['type' => Type::listOf($StringBox)], + 'stringBox' => ['type' => $StringBox], + 'intBox' => ['type' => $IntBox], + ]; + }, + ]); + + $NonNullStringBox1 = new InterfaceType([ + 'name' => 'NonNullStringBox1', + 'fields' => [ + 'scalar' => ['type' => Type::nonNull(Type::string())], + ], + ]); + + $NonNullStringBox1Impl = new ObjectType([ + 'name' => 'NonNullStringBox1Impl', + 'interfaces' => [$SomeBox, $NonNullStringBox1], + 'fields' => [ + 'scalar' => ['type' => Type::nonNull(Type::string())], + 'unrelatedField' => ['type' => Type::string()], + 'deepBox' => ['type' => $SomeBox], + ], + ]); + + $NonNullStringBox2 = new InterfaceType([ + 'name' => 'NonNullStringBox2', + 'fields' => [ + 'scalar' => ['type' => Type::nonNull(Type::string())], + ], + ]); + + $NonNullStringBox2Impl = new ObjectType([ + 'name' => 'NonNullStringBox2Impl', + 'interfaces' => [$SomeBox, $NonNullStringBox2], + 'fields' => [ + 'scalar' => ['type' => Type::nonNull(Type::string())], + 'unrelatedField' => ['type' => Type::string()], + 'deepBox' => ['type' => $SomeBox], + ], + ]); + + $Connection = new ObjectType([ + 'name' => 'Connection', + 'fields' => [ + 'edges' => [ + 'type' => Type::listOf(new ObjectType([ + 'name' => 'Edge', + 'fields' => [ + 'node' => [ + 'type' => new ObjectType([ + 'name' => 'Node', + 'fields' => [ + 'id' => ['type' => Type::id()], + 'name' => ['type' => Type::string()], + ], + ]), + ], + ], + ])), + ], + ], + ]); + + $schema = new Schema([ + 'query' => new ObjectType([ + 'name' => 'QueryRoot', + 'fields' => [ + 'someBox' => ['type' => $SomeBox], + 'connection' => ['type' => $Connection], + ], + ]), + 'types' => [$IntBox, $StringBox, $NonNullStringBox1Impl, $NonNullStringBox2Impl], + ]); + + return $schema; } /** @@ -581,7 +819,10 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase // In this case `deepBox` returns `SomeBox` in the first usage, and // `StringBox` in the second usage. These return types are not the same! // however this is valid because the return *shapes* are compatible. - $this->expectPassesRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, ' + $this->expectPassesRuleWithSchema( + $this->getSchema(), + new OverlappingFieldsCanBeMerged(), + ' { someBox { ... on SomeBox { @@ -596,7 +837,8 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase } } } - '); + ' + ); } /** @@ -604,7 +846,10 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testDisallowsDifferingReturnTypesDespiteNoOverlap() : void { - $this->expectFailsRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, ' + $this->expectFailsRuleWithSchema( + $this->getSchema(), + new OverlappingFieldsCanBeMerged(), + ' { someBox { ... on IntBox { @@ -615,16 +860,20 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase } } } - ', [ - FormattedError::create( - OverlappingFieldsCanBeMerged::fieldsConflictMessage( - 'scalar', - 'they return conflicting types Int and String' + ', + [ + FormattedError::create( + OverlappingFieldsCanBeMerged::fieldsConflictMessage( + 'scalar', + 'they return conflicting types Int and String' + ), + [ + new SourceLocation(5, 15), + new SourceLocation(8, 15), + ] ), - [ new SourceLocation(5, 15), - new SourceLocation(8, 15)] - ) - ]); + ] + ); } /** @@ -632,7 +881,10 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testReportsCorrectlyWhenANonExclusiveFollowsAnExclusive() : void { - $this->expectFailsRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, ' + $this->expectFailsRuleWithSchema( + $this->getSchema(), + new OverlappingFieldsCanBeMerged(), + ' { someBox { ... on IntBox { @@ -675,20 +927,22 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase fragment Y on SomeBox { scalar: unrelatedField } - ', [ - FormattedError::create( - OverlappingFieldsCanBeMerged::fieldsConflictMessage( - 'other', - [['scalar', 'scalar and unrelatedField are different fields']] + ', + [ + FormattedError::create( + OverlappingFieldsCanBeMerged::fieldsConflictMessage( + 'other', + [['scalar', 'scalar and unrelatedField are different fields']] + ), + [ + new SourceLocation(31, 11), + new SourceLocation(39, 11), + new SourceLocation(34, 11), + new SourceLocation(42, 11), + ] ), - [ - new SourceLocation(31, 11), - new SourceLocation(39, 11), - new SourceLocation(34, 11), - new SourceLocation(42, 11), - ] - ) - ]); + ] + ); } /** @@ -696,7 +950,10 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testDisallowsDifferingReturnTypeNullabilityDespiteNoOverlap() : void { - $this->expectFailsRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, ' + $this->expectFailsRuleWithSchema( + $this->getSchema(), + new OverlappingFieldsCanBeMerged(), + ' { someBox { ... on NonNullStringBox1 { @@ -707,16 +964,20 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase } } } - ', [ - FormattedError::create( - OverlappingFieldsCanBeMerged::fieldsConflictMessage( - 'scalar', - 'they return conflicting types String! and String' + ', + [ + FormattedError::create( + OverlappingFieldsCanBeMerged::fieldsConflictMessage( + 'scalar', + 'they return conflicting types String! and String' + ), + [ + new SourceLocation(5, 15), + new SourceLocation(8, 15), + ] ), - [new SourceLocation(5, 15), - new SourceLocation(8, 15)] - ) - ]); + ] + ); } /** @@ -724,7 +985,10 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testDisallowsDifferingReturnTypeListDespiteNoOverlap() : void { - $this->expectFailsRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, ' + $this->expectFailsRuleWithSchema( + $this->getSchema(), + new OverlappingFieldsCanBeMerged(), + ' { someBox { ... on IntBox { @@ -739,19 +1003,25 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase } } } - ', [ - FormattedError::create( - OverlappingFieldsCanBeMerged::fieldsConflictMessage( - 'box', - 'they return conflicting types [StringBox] and StringBox' + ', + [ + FormattedError::create( + OverlappingFieldsCanBeMerged::fieldsConflictMessage( + 'box', + 'they return conflicting types [StringBox] and StringBox' + ), + [ + new SourceLocation(5, 15), + new SourceLocation(10, 15), + ] ), - [new SourceLocation(5, 15), - new SourceLocation(10, 15)] - ) - ]); + ] + ); - - $this->expectFailsRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, ' + $this->expectFailsRuleWithSchema( + $this->getSchema(), + new OverlappingFieldsCanBeMerged(), + ' { someBox { ... on IntBox { @@ -766,21 +1036,28 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase } } } - ', [ - FormattedError::create( - OverlappingFieldsCanBeMerged::fieldsConflictMessage( - 'box', - 'they return conflicting types StringBox and [StringBox]' + ', + [ + FormattedError::create( + OverlappingFieldsCanBeMerged::fieldsConflictMessage( + 'box', + 'they return conflicting types StringBox and [StringBox]' + ), + [ + new SourceLocation(5, 15), + new SourceLocation(10, 15), + ] ), - [new SourceLocation(5, 15), - new SourceLocation(10, 15)] - ) - ]); + ] + ); } public function testDisallowsDifferingSubfields() : void { - $this->expectFailsRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, ' + $this->expectFailsRuleWithSchema( + $this->getSchema(), + new OverlappingFieldsCanBeMerged(), + ' { someBox { ... on IntBox { @@ -796,17 +1073,21 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase } } } - ', [ + ', + [ - FormattedError::create( - OverlappingFieldsCanBeMerged::fieldsConflictMessage( - 'val', - 'scalar and unrelatedField are different fields' + FormattedError::create( + OverlappingFieldsCanBeMerged::fieldsConflictMessage( + 'val', + 'scalar and unrelatedField are different fields' + ), + [ + new SourceLocation(6, 17), + new SourceLocation(7, 17), + ] ), - [new SourceLocation(6, 17), - new SourceLocation(7, 17)] - ) - ]); + ] + ); } /** @@ -814,7 +1095,10 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testDisallowsDifferingDeepReturnTypesDespiteNoOverlap() : void { - $this->expectFailsRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, ' + $this->expectFailsRuleWithSchema( + $this->getSchema(), + new OverlappingFieldsCanBeMerged(), + ' { someBox { ... on IntBox { @@ -829,20 +1113,22 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase } } } - ', [ - FormattedError::create( - OverlappingFieldsCanBeMerged::fieldsConflictMessage( - 'box', - [ [ 'scalar', 'they return conflicting types String and Int' ] ] + ', + [ + FormattedError::create( + OverlappingFieldsCanBeMerged::fieldsConflictMessage( + 'box', + [['scalar', 'they return conflicting types String and Int']] + ), + [ + new SourceLocation(5, 15), + new SourceLocation(6, 17), + new SourceLocation(10, 15), + new SourceLocation(11, 17), + ] ), - [ - new SourceLocation(5, 15), - new SourceLocation(6, 17), - new SourceLocation(10, 15), - new SourceLocation(11, 17) - ] - ) - ]); + ] + ); } /** @@ -850,7 +1136,10 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testAllowsNonConflictingOverlapingTypes() : void { - $this->expectPassesRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, ' + $this->expectPassesRuleWithSchema( + $this->getSchema(), + new OverlappingFieldsCanBeMerged(), + ' { someBox { ... on IntBox { @@ -861,7 +1150,8 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase } } } - '); + ' + ); } /** @@ -869,7 +1159,10 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testSameWrappedScalarReturnTypes() : void { - $this->expectPassesRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, ' + $this->expectPassesRuleWithSchema( + $this->getSchema(), + new OverlappingFieldsCanBeMerged(), + ' { someBox { ...on NonNullStringBox1 { @@ -880,7 +1173,8 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase } } } - '); + ' + ); } /** @@ -888,14 +1182,18 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testAllowsInlineTypelessFragments() : void { - $this->expectPassesRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, ' + $this->expectPassesRuleWithSchema( + $this->getSchema(), + new OverlappingFieldsCanBeMerged(), + ' { a ... { a } } - '); + ' + ); } /** @@ -903,7 +1201,10 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testComparesDeepTypesIncludingList() : void { - $this->expectFailsRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, ' + $this->expectFailsRuleWithSchema( + $this->getSchema(), + new OverlappingFieldsCanBeMerged(), + ' { connection { ...edgeID @@ -922,27 +1223,35 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase } } } - ', [ - FormattedError::create( - OverlappingFieldsCanBeMerged::fieldsConflictMessage('edges', [['node', [['id', 'name and id are different fields']]]]), - [ - new SourceLocation(5, 13), - new SourceLocation(6, 15), - new SourceLocation(7, 17), - new SourceLocation(14, 11), - new SourceLocation(15, 13), - new SourceLocation(16, 15), - ] - ) - ]); - } + ', + [ + FormattedError::create( + OverlappingFieldsCanBeMerged::fieldsConflictMessage( + 'edges', + [['node', [['id', 'name and id are different fields']]]] + ), + [ + new SourceLocation(5, 13), + new SourceLocation(6, 15), + new SourceLocation(7, 17), + new SourceLocation(14, 11), + new SourceLocation(15, 13), + new SourceLocation(16, 15), + ] + ), + ] + ); + } /** * @see it('ignores unknown types') */ public function testIgnoresUnknownTypes() : void { - $this->expectPassesRuleWithSchema($this->getSchema(), new OverlappingFieldsCanBeMerged, ' + $this->expectPassesRuleWithSchema( + $this->getSchema(), + new OverlappingFieldsCanBeMerged(), + ' { someBox { ...on UnknownType { @@ -953,7 +1262,8 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase } } } - '); + ' + ); } /** @@ -964,7 +1274,7 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase // The error template should end with a hint for the user to try using // different aliases. $error = OverlappingFieldsCanBeMerged::fieldsConflictMessage('x', 'a and b are different fields'); - $hint = 'Use different aliases on the fields to fetch both if this was intentional.'; + $hint = 'Use different aliases on the fields to fetch both if this was intentional.'; $this->assertStringEndsWith($hint, $error); } @@ -974,9 +1284,12 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testDoesNotInfiniteLoopOnRecursiveFragment() : void { - $this->expectPassesRule(new OverlappingFieldsCanBeMerged, ' + $this->expectPassesRule( + new OverlappingFieldsCanBeMerged(), + ' fragment fragA on Human { name, relatives { name, ...fragA } } - '); + ' + ); } /** @@ -984,9 +1297,12 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testDoesNotInfiniteLoopOnImmeditelyRecursiveFragment() : void { - $this->expectPassesRule(new OverlappingFieldsCanBeMerged, ' + $this->expectPassesRule( + new OverlappingFieldsCanBeMerged(), + ' fragment fragA on Human { name, ...fragA } - '); + ' + ); } /** @@ -994,11 +1310,14 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testDoesNotInfiniteLoopOnTransitivelyRecursiveFragment() : void { - $this->expectPassesRule(new OverlappingFieldsCanBeMerged, ' + $this->expectPassesRule( + new OverlappingFieldsCanBeMerged(), + ' fragment fragA on Human { name, ...fragB } fragment fragB on Human { name, ...fragC } fragment fragC on Human { name, ...fragA } - '); + ' + ); } /** @@ -1006,7 +1325,9 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase */ public function testFindInvalidCaseEvenWithImmediatelyRecursiveFragment() : void { - $this->expectFailsRule(new OverlappingFieldsCanBeMerged, ' + $this->expectFailsRule( + new OverlappingFieldsCanBeMerged(), + ' fragment sameAliasesWithDifferentFieldTargets on Dob { ...sameAliasesWithDifferentFieldTargets fido: name @@ -1023,124 +1344,8 @@ class OverlappingFieldsCanBeMergedTest extends ValidatorTestCase new SourceLocation(4, 9), new SourceLocation(5, 9), ] - ) - ]); - } - - private function getSchema() - { - $StringBox = null; - $IntBox = null; - $SomeBox = null; - - $SomeBox = new InterfaceType([ - 'name' => 'SomeBox', - 'fields' => function() use (&$SomeBox) { - return [ - 'deepBox' => ['type' => $SomeBox], - 'unrelatedField' => ['type' => Type::string()] - ]; - - } - ]); - - $StringBox = new ObjectType([ - 'name' => 'StringBox', - 'interfaces' => [$SomeBox], - 'fields' => function() use (&$StringBox, &$IntBox) { - return [ - 'scalar' => ['type' => Type::string()], - 'deepBox' => ['type' => $StringBox], - 'unrelatedField' => ['type' => Type::string()], - 'listStringBox' => ['type' => Type::listOf($StringBox)], - 'stringBox' => ['type' => $StringBox], - 'intBox' => ['type' => $IntBox], - ]; - } - ]); - - $IntBox = new ObjectType([ - 'name' => 'IntBox', - 'interfaces' => [$SomeBox], - 'fields' => function() use (&$StringBox, &$IntBox) { - return [ - 'scalar' => ['type' => Type::int()], - 'deepBox' => ['type' => $IntBox], - 'unrelatedField' => ['type' => Type::string()], - 'listStringBox' => ['type' => Type::listOf($StringBox)], - 'stringBox' => ['type' => $StringBox], - 'intBox' => ['type' => $IntBox], - ]; - } - ]); - - $NonNullStringBox1 = new InterfaceType([ - 'name' => 'NonNullStringBox1', - 'fields' => [ - 'scalar' => [ 'type' => Type::nonNull(Type::string()) ] + ), ] - ]); - - $NonNullStringBox1Impl = new ObjectType([ - 'name' => 'NonNullStringBox1Impl', - 'interfaces' => [ $SomeBox, $NonNullStringBox1 ], - 'fields' => [ - 'scalar' => [ 'type' => Type::nonNull(Type::string()) ], - 'unrelatedField' => ['type' => Type::string() ], - 'deepBox' => [ 'type' => $SomeBox ], - ] - ]); - - $NonNullStringBox2 = new InterfaceType([ - 'name' => 'NonNullStringBox2', - 'fields' => [ - 'scalar' => ['type' => Type::nonNull(Type::string())] - ] - ]); - - $NonNullStringBox2Impl = new ObjectType([ - 'name' => 'NonNullStringBox2Impl', - 'interfaces' => [ $SomeBox, $NonNullStringBox2 ], - 'fields' => [ - 'scalar' => [ 'type' => Type::nonNull(Type::string()) ], - 'unrelatedField' => [ 'type' => Type::string() ], - 'deepBox' => [ 'type' => $SomeBox ], - ] - ]); - - $Connection = new ObjectType([ - 'name' => 'Connection', - 'fields' => [ - 'edges' => [ - 'type' => Type::listOf(new ObjectType([ - 'name' => 'Edge', - 'fields' => [ - 'node' => [ - 'type' => new ObjectType([ - 'name' => 'Node', - 'fields' => [ - 'id' => ['type' => Type::id()], - 'name' => ['type' => Type::string()] - ] - ]) - ] - ] - ])) - ] - ] - ]); - - $schema = new Schema([ - 'query' => new ObjectType([ - 'name' => 'QueryRoot', - 'fields' => [ - 'someBox' => ['type' => $SomeBox], - 'connection' => ['type' => $Connection] - ] - ]), - 'types' => [$IntBox, $StringBox, $NonNullStringBox1Impl, $NonNullStringBox2Impl] - ]); - - return $schema; + ); } } diff --git a/tests/Validator/PossibleFragmentSpreadsTest.php b/tests/Validator/PossibleFragmentSpreadsTest.php index f5f074b..c4a8b29 100644 --- a/tests/Validator/PossibleFragmentSpreadsTest.php +++ b/tests/Validator/PossibleFragmentSpreadsTest.php @@ -1,4 +1,7 @@ expectPassesRule(new PossibleFragmentSpreads(), ' + $this->expectPassesRule( + new PossibleFragmentSpreads(), + ' fragment objectWithinObject on Dog { ...dogFragment } fragment dogFragment on Dog { barkVolume } - '); + ' + ); } /** @@ -25,9 +30,12 @@ class PossibleFragmentSpreadsTest extends ValidatorTestCase */ public function testOfTheSameObjectWithInlineFragment() : void { - $this->expectPassesRule(new PossibleFragmentSpreads, ' + $this->expectPassesRule( + new PossibleFragmentSpreads(), + ' fragment objectWithinObjectAnon on Dog { ... on Dog { barkVolume } } - '); + ' + ); } /** @@ -35,10 +43,13 @@ class PossibleFragmentSpreadsTest extends ValidatorTestCase */ public function testObjectIntoAnImplementedInterface() : void { - $this->expectPassesRule(new PossibleFragmentSpreads, ' + $this->expectPassesRule( + new PossibleFragmentSpreads(), + ' fragment objectWithinInterface on Pet { ...dogFragment } fragment dogFragment on Dog { barkVolume } - '); + ' + ); } /** @@ -46,10 +57,13 @@ class PossibleFragmentSpreadsTest extends ValidatorTestCase */ public function testObjectIntoContainingUnion() : void { - $this->expectPassesRule(new PossibleFragmentSpreads, ' + $this->expectPassesRule( + new PossibleFragmentSpreads(), + ' fragment objectWithinUnion on CatOrDog { ...dogFragment } fragment dogFragment on Dog { barkVolume } - '); + ' + ); } /** @@ -57,10 +71,13 @@ class PossibleFragmentSpreadsTest extends ValidatorTestCase */ public function testUnionIntoContainedObject() : void { - $this->expectPassesRule(new PossibleFragmentSpreads, ' + $this->expectPassesRule( + new PossibleFragmentSpreads(), + ' fragment unionWithinObject on Dog { ...catOrDogFragment } fragment catOrDogFragment on CatOrDog { __typename } - '); + ' + ); } /** @@ -68,10 +85,13 @@ class PossibleFragmentSpreadsTest extends ValidatorTestCase */ public function testUnionIntoOverlappingInterface() : void { - $this->expectPassesRule(new PossibleFragmentSpreads, ' + $this->expectPassesRule( + new PossibleFragmentSpreads(), + ' fragment unionWithinInterface on Pet { ...catOrDogFragment } fragment catOrDogFragment on CatOrDog { __typename } - '); + ' + ); } /** @@ -79,10 +99,13 @@ class PossibleFragmentSpreadsTest extends ValidatorTestCase */ public function testUnionIntoOverlappingUnion() : void { - $this->expectPassesRule(new PossibleFragmentSpreads, ' + $this->expectPassesRule( + new PossibleFragmentSpreads(), + ' fragment unionWithinUnion on DogOrHuman { ...catOrDogFragment } fragment catOrDogFragment on CatOrDog { __typename } - '); + ' + ); } /** @@ -90,10 +113,13 @@ class PossibleFragmentSpreadsTest extends ValidatorTestCase */ public function testInterfaceIntoImplementedObject() : void { - $this->expectPassesRule(new PossibleFragmentSpreads, ' + $this->expectPassesRule( + new PossibleFragmentSpreads(), + ' fragment interfaceWithinObject on Dog { ...petFragment } fragment petFragment on Pet { name } - '); + ' + ); } /** @@ -101,10 +127,13 @@ class PossibleFragmentSpreadsTest extends ValidatorTestCase */ public function testInterfaceIntoOverlappingInterface() : void { - $this->expectPassesRule(new PossibleFragmentSpreads, ' + $this->expectPassesRule( + new PossibleFragmentSpreads(), + ' fragment interfaceWithinInterface on Pet { ...beingFragment } fragment beingFragment on Being { name } - '); + ' + ); } /** @@ -112,9 +141,12 @@ class PossibleFragmentSpreadsTest extends ValidatorTestCase */ public function testInterfaceIntoOverlappingInterfaceInInlineFragment() : void { - $this->expectPassesRule(new PossibleFragmentSpreads, ' + $this->expectPassesRule( + new PossibleFragmentSpreads(), + ' fragment interfaceWithinInterface on Pet { ... on Being { name } } - '); + ' + ); } /** @@ -122,10 +154,13 @@ class PossibleFragmentSpreadsTest extends ValidatorTestCase */ public function testInterfaceIntoOverlappingUnion() : void { - $this->expectPassesRule(new PossibleFragmentSpreads, ' + $this->expectPassesRule( + new PossibleFragmentSpreads(), + ' fragment interfaceWithinUnion on CatOrDog { ...petFragment } fragment petFragment on Pet { name } - '); + ' + ); } /** @@ -133,10 +168,13 @@ class PossibleFragmentSpreadsTest extends ValidatorTestCase */ public function testIgnoresIncorrectTypeCaughtByFragmentsOnCompositeTypes() : void { - $this->expectPassesRule(new PossibleFragmentSpreads, ' + $this->expectPassesRule( + new PossibleFragmentSpreads(), + ' fragment petFragment on Pet { ...badInADifferentWay } fragment badInADifferentWay on String { name } - '); + ' + ); } /** @@ -144,7 +182,9 @@ class PossibleFragmentSpreadsTest extends ValidatorTestCase */ public function testDifferentObjectIntoObject() : void { - $this->expectFailsRule(new PossibleFragmentSpreads, ' + $this->expectFailsRule( + new PossibleFragmentSpreads(), + ' fragment invalidObjectWithinObject on Cat { ...dogFragment } fragment dogFragment on Dog { barkVolume } ', @@ -152,12 +192,22 @@ class PossibleFragmentSpreadsTest extends ValidatorTestCase ); } + private function error($fragName, $parentType, $fragType, $line, $column) + { + return FormattedError::create( + PossibleFragmentSpreads::typeIncompatibleSpreadMessage($fragName, $parentType, $fragType), + [new SourceLocation($line, $column)] + ); + } + /** * @see it('different object into object in inline fragment') */ public function testDifferentObjectIntoObjectInInlineFragment() : void { - $this->expectFailsRule(new PossibleFragmentSpreads, ' + $this->expectFailsRule( + new PossibleFragmentSpreads(), + ' fragment invalidObjectWithinObjectAnon on Cat { ... on Dog { barkVolume } } @@ -166,12 +216,22 @@ class PossibleFragmentSpreadsTest extends ValidatorTestCase ); } + private function errorAnon($parentType, $fragType, $line, $column) + { + return FormattedError::create( + PossibleFragmentSpreads::typeIncompatibleAnonSpreadMessage($parentType, $fragType), + [new SourceLocation($line, $column)] + ); + } + /** * @see it('object into not implementing interface') */ public function testObjectIntoNotImplementingInterface() : void { - $this->expectFailsRule(new PossibleFragmentSpreads, ' + $this->expectFailsRule( + new PossibleFragmentSpreads(), + ' fragment invalidObjectWithinInterface on Pet { ...humanFragment } fragment humanFragment on Human { pets { name } } ', @@ -184,7 +244,9 @@ class PossibleFragmentSpreadsTest extends ValidatorTestCase */ public function testObjectIntoNotContainingUnion() : void { - $this->expectFailsRule(new PossibleFragmentSpreads, ' + $this->expectFailsRule( + new PossibleFragmentSpreads(), + ' fragment invalidObjectWithinUnion on CatOrDog { ...humanFragment } fragment humanFragment on Human { pets { name } } ', @@ -197,7 +259,9 @@ class PossibleFragmentSpreadsTest extends ValidatorTestCase */ public function testUnionIntoNotContainedObject() : void { - $this->expectFailsRule(new PossibleFragmentSpreads, ' + $this->expectFailsRule( + new PossibleFragmentSpreads(), + ' fragment invalidUnionWithinObject on Human { ...catOrDogFragment } fragment catOrDogFragment on CatOrDog { __typename } ', @@ -210,7 +274,9 @@ class PossibleFragmentSpreadsTest extends ValidatorTestCase */ public function testUnionIntoNonOverlappingInterface() : void { - $this->expectFailsRule(new PossibleFragmentSpreads, ' + $this->expectFailsRule( + new PossibleFragmentSpreads(), + ' fragment invalidUnionWithinInterface on Pet { ...humanOrAlienFragment } fragment humanOrAlienFragment on HumanOrAlien { __typename } ', @@ -223,7 +289,9 @@ class PossibleFragmentSpreadsTest extends ValidatorTestCase */ public function testUnionIntoNonOverlappingUnion() : void { - $this->expectFailsRule(new PossibleFragmentSpreads, ' + $this->expectFailsRule( + new PossibleFragmentSpreads(), + ' fragment invalidUnionWithinUnion on CatOrDog { ...humanOrAlienFragment } fragment humanOrAlienFragment on HumanOrAlien { __typename } ', @@ -236,7 +304,9 @@ class PossibleFragmentSpreadsTest extends ValidatorTestCase */ public function testInterfaceIntoNonImplementingObject() : void { - $this->expectFailsRule(new PossibleFragmentSpreads, ' + $this->expectFailsRule( + new PossibleFragmentSpreads(), + ' fragment invalidInterfaceWithinObject on Cat { ...intelligentFragment } fragment intelligentFragment on Intelligent { iq } ', @@ -252,12 +322,15 @@ class PossibleFragmentSpreadsTest extends ValidatorTestCase // Ideally this should fail, but our new lazy schema doesn't scan through all types and fields // So we don't have enough knowledge to check interface intersection and always allow this to pass: - $this->expectPassesRule(new PossibleFragmentSpreads, ' + $this->expectPassesRule( + new PossibleFragmentSpreads(), + ' fragment invalidInterfaceWithinInterface on Pet { ...intelligentFragment } fragment intelligentFragment on Intelligent { iq } - '); + ' + ); } /** @@ -268,11 +341,14 @@ class PossibleFragmentSpreadsTest extends ValidatorTestCase // Ideally this should fail, but our new lazy schema doesn't scan through all types and fields // So we don't have enough knowledge to check interface intersection and always allow this to pass: - $this->expectPassesRule(new PossibleFragmentSpreads, ' + $this->expectPassesRule( + new PossibleFragmentSpreads(), + ' fragment invalidInterfaceWithinInterfaceAnon on Pet { ...on Intelligent { iq } } - '); + ' + ); } /** @@ -280,27 +356,13 @@ class PossibleFragmentSpreadsTest extends ValidatorTestCase */ public function testInterfaceIntoNonOverlappingUnion() : void { - $this->expectFailsRule(new PossibleFragmentSpreads, ' + $this->expectFailsRule( + new PossibleFragmentSpreads(), + ' fragment invalidInterfaceWithinUnion on HumanOrAlien { ...petFragment } fragment petFragment on Pet { name } ', [$this->error('petFragment', 'HumanOrAlien', 'Pet', 2, 62)] ); } - - private function error($fragName, $parentType, $fragType, $line, $column) - { - return FormattedError::create( - PossibleFragmentSpreads::typeIncompatibleSpreadMessage($fragName, $parentType, $fragType), - [new SourceLocation($line, $column)] - ); - } - - private function errorAnon($parentType, $fragType, $line, $column) - { - return FormattedError::create( - PossibleFragmentSpreads::typeIncompatibleAnonSpreadMessage($parentType, $fragType), - [new SourceLocation($line, $column)] - ); - } } diff --git a/tests/Validator/ProvidedNonNullArgumentsTest.php b/tests/Validator/ProvidedNonNullArgumentsTest.php index 25291e7..803fcda 100644 --- a/tests/Validator/ProvidedNonNullArgumentsTest.php +++ b/tests/Validator/ProvidedNonNullArgumentsTest.php @@ -1,4 +1,7 @@ expectPassesRule(new ProvidedNonNullArguments, ' + $this->expectPassesRule( + new ProvidedNonNullArguments(), + ' { dog { isHousetrained(unknownArgument: true) } } - '); + ' + ); } // Valid non-nullable value: @@ -31,13 +36,16 @@ class ProvidedNonNullArgumentsTest extends ValidatorTestCase */ public function testArgOnOptionalArg() : void { - $this->expectPassesRule(new ProvidedNonNullArguments, ' + $this->expectPassesRule( + new ProvidedNonNullArguments(), + ' { dog { isHousetrained(atOtherHomes: true) } } - '); + ' + ); } /** @@ -45,13 +53,16 @@ class ProvidedNonNullArgumentsTest extends ValidatorTestCase */ public function testNoArgOnOptionalArg() : void { - $this->expectPassesRule(new ProvidedNonNullArguments, ' + $this->expectPassesRule( + new ProvidedNonNullArguments(), + ' { dog { isHousetrained } } - '); + ' + ); } /** @@ -59,13 +70,16 @@ class ProvidedNonNullArgumentsTest extends ValidatorTestCase */ public function testMultipleArgs() : void { - $this->expectPassesRule(new ProvidedNonNullArguments, ' + $this->expectPassesRule( + new ProvidedNonNullArguments(), + ' { complicatedArgs { multipleReqs(req1: 1, req2: 2) } } - '); + ' + ); } /** @@ -73,13 +87,16 @@ class ProvidedNonNullArgumentsTest extends ValidatorTestCase */ public function testMultipleArgsReverseOrder() : void { - $this->expectPassesRule(new ProvidedNonNullArguments, ' + $this->expectPassesRule( + new ProvidedNonNullArguments(), + ' { complicatedArgs { multipleReqs(req2: 2, req1: 1) } } - '); + ' + ); } /** @@ -87,13 +104,16 @@ class ProvidedNonNullArgumentsTest extends ValidatorTestCase */ public function testNoArgsOnMultipleOptional() : void { - $this->expectPassesRule(new ProvidedNonNullArguments, ' + $this->expectPassesRule( + new ProvidedNonNullArguments(), + ' { complicatedArgs { multipleOpts } } - '); + ' + ); } /** @@ -101,13 +121,16 @@ class ProvidedNonNullArgumentsTest extends ValidatorTestCase */ public function testOneArgOnMultipleOptional() : void { - $this->expectPassesRule(new ProvidedNonNullArguments, ' + $this->expectPassesRule( + new ProvidedNonNullArguments(), + ' { complicatedArgs { multipleOpts(opt1: 1) } } - '); + ' + ); } /** @@ -115,13 +138,16 @@ class ProvidedNonNullArgumentsTest extends ValidatorTestCase */ public function testSecondArgOnMultipleOptional() : void { - $this->expectPassesRule(new ProvidedNonNullArguments, ' + $this->expectPassesRule( + new ProvidedNonNullArguments(), + ' { complicatedArgs { multipleOpts(opt2: 1) } } - '); + ' + ); } /** @@ -129,13 +155,16 @@ class ProvidedNonNullArgumentsTest extends ValidatorTestCase */ public function testMultipleReqsOnMixedList() : void { - $this->expectPassesRule(new ProvidedNonNullArguments, ' + $this->expectPassesRule( + new ProvidedNonNullArguments(), + ' { complicatedArgs { multipleOptAndReq(req1: 3, req2: 4) } } - '); + ' + ); } /** @@ -143,13 +172,16 @@ class ProvidedNonNullArgumentsTest extends ValidatorTestCase */ public function testMultipleReqsAndOneOptOnMixedList() : void { - $this->expectPassesRule(new ProvidedNonNullArguments, ' + $this->expectPassesRule( + new ProvidedNonNullArguments(), + ' { complicatedArgs { multipleOptAndReq(req1: 3, req2: 4, opt1: 5) } } - '); + ' + ); } /** @@ -157,13 +189,16 @@ class ProvidedNonNullArgumentsTest extends ValidatorTestCase */ public function testAllReqsAndOptsOnMixedList() : void { - $this->expectPassesRule(new ProvidedNonNullArguments, ' + $this->expectPassesRule( + new ProvidedNonNullArguments(), + ' { complicatedArgs { multipleOptAndReq(req1: 3, req2: 4, opt1: 5, opt2: 6) } } - '); + ' + ); } // Invalid non-nullable value @@ -173,15 +208,25 @@ class ProvidedNonNullArgumentsTest extends ValidatorTestCase */ public function testMissingOneNonNullableArgument() : void { - $this->expectFailsRule(new ProvidedNonNullArguments, ' + $this->expectFailsRule( + new ProvidedNonNullArguments(), + ' { complicatedArgs { multipleReqs(req2: 2) } } - ', [ - $this->missingFieldArg('multipleReqs', 'req1', 'Int!', 4, 13) - ]); + ', + [$this->missingFieldArg('multipleReqs', 'req1', 'Int!', 4, 13)] + ); + } + + private function missingFieldArg($fieldName, $argName, $typeName, $line, $column) + { + return FormattedError::create( + ProvidedNonNullArguments::missingFieldArgMessage($fieldName, $argName, $typeName), + [new SourceLocation($line, $column)] + ); } /** @@ -189,46 +234,57 @@ class ProvidedNonNullArgumentsTest extends ValidatorTestCase */ public function testMissingMultipleNonNullableArguments() : void { - $this->expectFailsRule(new ProvidedNonNullArguments, ' + $this->expectFailsRule( + new ProvidedNonNullArguments(), + ' { complicatedArgs { multipleReqs } } - ', [ - $this->missingFieldArg('multipleReqs', 'req1', 'Int!', 4, 13), - $this->missingFieldArg('multipleReqs', 'req2', 'Int!', 4, 13), - ]); + ', + [ + $this->missingFieldArg('multipleReqs', 'req1', 'Int!', 4, 13), + $this->missingFieldArg('multipleReqs', 'req2', 'Int!', 4, 13), + ] + ); } + // Describe: Directive arguments + /** * @see it('Incorrect value and missing argument') */ public function testIncorrectValueAndMissingArgument() : void { - $this->expectFailsRule(new ProvidedNonNullArguments, ' + $this->expectFailsRule( + new ProvidedNonNullArguments(), + ' { complicatedArgs { multipleReqs(req1: "one") } } - ', [ - $this->missingFieldArg('multipleReqs', 'req2', 'Int!', 4, 13), - ]); + ', + [ + $this->missingFieldArg('multipleReqs', 'req2', 'Int!', 4, 13), + ] + ); } - // Describe: Directive arguments - /** * @see it('ignores unknown directives') */ public function testIgnoresUnknownDirectives() : void { - $this->expectPassesRule(new ProvidedNonNullArguments, ' + $this->expectPassesRule( + new ProvidedNonNullArguments(), + ' { dog @unknown } - '); + ' + ); } /** @@ -236,7 +292,9 @@ class ProvidedNonNullArgumentsTest extends ValidatorTestCase */ public function testWithDirectivesOfValidTypes() : void { - $this->expectPassesRule(new ProvidedNonNullArguments, ' + $this->expectPassesRule( + new ProvidedNonNullArguments(), + ' { dog @include(if: true) { name @@ -245,7 +303,8 @@ class ProvidedNonNullArgumentsTest extends ValidatorTestCase name } } - '); + ' + ); } /** @@ -253,23 +312,19 @@ class ProvidedNonNullArgumentsTest extends ValidatorTestCase */ public function testWithDirectiveWithMissingTypes() : void { - $this->expectFailsRule(new ProvidedNonNullArguments, ' + $this->expectFailsRule( + new ProvidedNonNullArguments(), + ' { dog @include { name @skip } } - ', [ - $this->missingDirectiveArg('include', 'if', 'Boolean!', 3, 15), - $this->missingDirectiveArg('skip', 'if', 'Boolean!', 4, 18) - ]); - } - - private function missingFieldArg($fieldName, $argName, $typeName, $line, $column) - { - return FormattedError::create( - ProvidedNonNullArguments::missingFieldArgMessage($fieldName, $argName, $typeName), - [new SourceLocation($line, $column)] + ', + [ + $this->missingDirectiveArg('include', 'if', 'Boolean!', 3, 15), + $this->missingDirectiveArg('skip', 'if', 'Boolean!', 4, 18), + ] ); } diff --git a/tests/Validator/QueryComplexityTest.php b/tests/Validator/QueryComplexityTest.php index dcc1852..8847f76 100644 --- a/tests/Validator/QueryComplexityTest.php +++ b/tests/Validator/QueryComplexityTest.php @@ -1,4 +1,7 @@ setMaxQueryComplexity($maxDepth); - } - - return self::$rule; - } - public function testSimpleQueries() : void { $query = 'query MyQuery { human { firstName } }'; @@ -48,6 +25,19 @@ class QueryComplexityTest extends QuerySecurityTestCase $this->assertDocumentValidators($query, 2, 3); } + private function assertDocumentValidators($query, $queryComplexity, $startComplexity) + { + for ($maxComplexity = $startComplexity; $maxComplexity >= 0; --$maxComplexity) { + $positions = []; + + if ($maxComplexity < $queryComplexity && $maxComplexity !== QueryComplexity::DISABLED) { + $positions = [$this->createFormattedError($maxComplexity, $queryComplexity)]; + } + + $this->assertDocumentValidator($query, $maxComplexity, $positions); + } + } + public function testInlineFragmentQueries() : void { $query = 'query MyQuery { human { ... on Human { firstName } } }'; @@ -92,6 +82,22 @@ class QueryComplexityTest extends QuerySecurityTestCase $this->assertDocumentValidators($query, 3, 4); } + /** + * @param int $maxDepth + * + * @return QueryComplexity + */ + protected function getRule($maxDepth = null) + { + if (self::$rule === null) { + self::$rule = new QueryComplexity($maxDepth); + } elseif ($maxDepth !== null) { + self::$rule->setMaxQueryComplexity($maxDepth); + } + + return self::$rule; + } + public function testQueryWithEnabledIncludeDirectives() : void { $query = 'query MyQuery($withDogs: Boolean!) { human { dogs(name: "Root") @include(if:$withDogs) { name } } }'; @@ -133,8 +139,8 @@ class QueryComplexityTest extends QuerySecurityTestCase $query = 'query MyQuery($withDogs: Boolean!, $withoutDogName: Boolean!) { human { dogs(name: "Root") @include(if:$withDogs) { name @skip(if:$withoutDogName) } } }'; $this->getRule()->setRawVariableValues([ - 'withDogs' => true, - 'withoutDogName' => true + 'withDogs' => true, + 'withoutDogName' => true, ]); $this->assertDocumentValidators($query, 2, 3); @@ -159,16 +165,19 @@ class QueryComplexityTest extends QuerySecurityTestCase { $query = 'query MyQuery { human(name: INVALID_VALUE) { dogs {name} } }'; - $reportedError = new Error("OtherValidatorError"); - $otherRule = new CustomValidationRule('otherRule', function(ValidationContext $context) use ($reportedError) { - return [ - NodeKind::OPERATION_DEFINITION => [ - 'leave' => function() use ($context, $reportedError) { - $context->reportError($reportedError); - } - ] - ]; - }); + $reportedError = new Error('OtherValidatorError'); + $otherRule = new CustomValidationRule( + 'otherRule', + function (ValidationContext $context) use ($reportedError) { + return [ + NodeKind::OPERATION_DEFINITION => [ + 'leave' => function () use ($context, $reportedError) { + $context->reportError($reportedError); + }, + ], + ]; + } + ); $errors = DocumentValidator::validate( QuerySecuritySchema::buildSchema(), @@ -187,16 +196,14 @@ class QueryComplexityTest extends QuerySecurityTestCase ); } - private function assertDocumentValidators($query, $queryComplexity, $startComplexity) + /** + * @param int $max + * @param int $count + * + * @return string + */ + protected function getErrorMessage($max, $count) { - for ($maxComplexity = $startComplexity; $maxComplexity >= 0; --$maxComplexity) { - $positions = []; - - if ($maxComplexity < $queryComplexity && $maxComplexity !== QueryComplexity::DISABLED) { - $positions = [$this->createFormattedError($maxComplexity, $queryComplexity)]; - } - - $this->assertDocumentValidator($query, $maxComplexity, $positions); - } + return QueryComplexity::maxQueryComplexityErrorMessage($max, $count); } } diff --git a/tests/Validator/QueryDepthTest.php b/tests/Validator/QueryDepthTest.php index 1063156..bccb26c 100644 --- a/tests/Validator/QueryDepthTest.php +++ b/tests/Validator/QueryDepthTest.php @@ -1,35 +1,19 @@ assertDocumentValidator($this->buildRecursiveQuery($queryDepth), $maxQueryDepth, $expectedErrors); } + private function buildRecursiveQuery($depth) + { + $query = sprintf('query MyQuery { human%s }', $this->buildRecursiveQueryPart($depth)); + + return $query; + } + + private function buildRecursiveQueryPart($depth) + { + $templates = [ + 'human' => ' { firstName%s } ', + 'dog' => ' dogs { name%s } ', + ]; + + $part = $templates['human']; + + for ($i = 1; $i <= $depth; ++$i) { + $key = ($i % 2 === 1) ? 'human' : 'dog'; + $template = $templates[$key]; + + $part = sprintf($part, ($key === 'human' ? ' owner ' : '') . $template); + } + $part = str_replace('%s', '', $part); + + return $part; + } + /** - * @param $queryDepth - * @param int $maxQueryDepth - * @param array $expectedErrors + * @param int $queryDepth + * @param int $maxQueryDepth + * @param string[][] $expectedErrors * @dataProvider queryDataProvider */ public function testFragmentQueries($queryDepth, $maxQueryDepth = 7, $expectedErrors = []) : void { - $this->assertDocumentValidator($this->buildRecursiveUsingFragmentQuery($queryDepth), $maxQueryDepth, $expectedErrors); + $this->assertDocumentValidator( + $this->buildRecursiveUsingFragmentQuery($queryDepth), + $maxQueryDepth, + $expectedErrors + ); + } + + private function buildRecursiveUsingFragmentQuery($depth) + { + $query = sprintf( + 'query MyQuery { human { ...F1 } } fragment F1 on Human %s', + $this->buildRecursiveQueryPart($depth) + ); + + return $query; } /** - * @param $queryDepth - * @param int $maxQueryDepth - * @param array $expectedErrors + * @param int $queryDepth + * @param int $maxQueryDepth + * @param string[][] $expectedErrors * @dataProvider queryDataProvider */ public function testInlineFragmentQueries($queryDepth, $maxQueryDepth = 7, $expectedErrors = []) : void { - $this->assertDocumentValidator($this->buildRecursiveUsingInlineFragmentQuery($queryDepth), $maxQueryDepth, $expectedErrors); + $this->assertDocumentValidator( + $this->buildRecursiveUsingInlineFragmentQuery($queryDepth), + $maxQueryDepth, + $expectedErrors + ); + } + + private function buildRecursiveUsingInlineFragmentQuery($depth) + { + $query = sprintf( + 'query MyQuery { human { ...on Human %s } }', + $this->buildRecursiveQueryPart($depth) + ); + + return $query; } public function testComplexityIntrospectionQuery() : void @@ -99,50 +138,24 @@ class QueryDepthTest extends QuerySecurityTestCase ]; } - private function buildRecursiveQuery($depth) + /** + * @param int $max + * @param int $count + * + * @return string + */ + protected function getErrorMessage($max, $count) { - $query = sprintf('query MyQuery { human%s }', $this->buildRecursiveQueryPart($depth)); - - return $query; + return QueryDepth::maxQueryDepthErrorMessage($max, $count); } - private function buildRecursiveUsingFragmentQuery($depth) + /** + * @param int $maxDepth + * + * @return QueryDepth + */ + protected function getRule($maxDepth) { - $query = sprintf( - 'query MyQuery { human { ...F1 } } fragment F1 on Human %s', - $this->buildRecursiveQueryPart($depth) - ); - - return $query; - } - - private function buildRecursiveUsingInlineFragmentQuery($depth) - { - $query = sprintf( - 'query MyQuery { human { ...on Human %s } }', - $this->buildRecursiveQueryPart($depth) - ); - - return $query; - } - - private function buildRecursiveQueryPart($depth) - { - $templates = [ - 'human' => ' { firstName%s } ', - 'dog' => ' dogs { name%s } ', - ]; - - $part = $templates['human']; - - for ($i = 1; $i <= $depth; ++$i) { - $key = ($i % 2 == 1) ? 'human' : 'dog'; - $template = $templates[$key]; - - $part = sprintf($part, ('human' == $key ? ' owner ' : '').$template); - } - $part = str_replace('%s', '', $part); - - return $part; + return new QueryDepth($maxDepth); } } diff --git a/tests/Validator/QuerySecuritySchema.php b/tests/Validator/QuerySecuritySchema.php index 024990a..534a340 100644 --- a/tests/Validator/QuerySecuritySchema.php +++ b/tests/Validator/QuerySecuritySchema.php @@ -1,18 +1,25 @@ static::buildQueryRootType() + 'query' => static::buildQueryRootType(), ]); return self::$schema; @@ -33,12 +40,12 @@ class QuerySecuritySchema public static function buildQueryRootType() { - if (null !== self::$queryRootType) { + if (self::$queryRootType !== null) { return self::$queryRootType; } self::$queryRootType = new ObjectType([ - 'name' => 'QueryRoot', + 'name' => 'QueryRoot', 'fields' => [ 'human' => [ 'type' => self::buildHumanType(), @@ -52,18 +59,18 @@ class QuerySecuritySchema public static function buildHumanType() { - if (null !== self::$humanType) { + if (self::$humanType !== null) { return self::$humanType; } self::$humanType = new ObjectType( [ - 'name' => 'Human', - 'fields' => function() { + 'name' => 'Human', + 'fields' => function () { return [ 'firstName' => ['type' => Type::nonNull(Type::string())], - 'dogs' => [ - 'type' => Type::nonNull( + 'dogs' => [ + 'type' => Type::nonNull( Type::listOf( Type::nonNull(self::buildDogType()) ) @@ -73,7 +80,7 @@ class QuerySecuritySchema return $childrenComplexity + $complexity; }, - 'args' => ['name' => ['type' => Type::string()]], + 'args' => ['name' => ['type' => Type::string()]], ], ]; }, @@ -85,15 +92,15 @@ class QuerySecuritySchema public static function buildDogType() { - if (null !== self::$dogType) { + if (self::$dogType !== null) { return self::$dogType; } self::$dogType = new ObjectType( [ - 'name' => 'Dog', + 'name' => 'Dog', 'fields' => [ - 'name' => ['type' => Type::nonNull(Type::string())], + 'name' => ['type' => Type::nonNull(Type::string())], 'master' => [ 'type' => self::buildHumanType(), ], diff --git a/tests/Validator/QuerySecurityTestCase.php b/tests/Validator/QuerySecurityTestCase.php index 5188e3e..a4e5ba5 100644 --- a/tests/Validator/QuerySecurityTestCase.php +++ b/tests/Validator/QuerySecurityTestCase.php @@ -1,30 +1,20 @@ getRule(-1); } - protected function createFormattedError($max, $count, $locations = []) - { - return FormattedError::create($this->getErrorMessage($max, $count), $locations); - } - - protected function assertDocumentValidator($queryString, $max, array $expectedErrors = []) - { - $errors = DocumentValidator::validate( - QuerySecuritySchema::buildSchema(), - Parser::parse($queryString), - [$this->getRule($max)] - ); - - $this->assertEquals($expectedErrors, array_map(['GraphQL\Error\Error', 'formatError'], $errors), $queryString); - - return $errors; - } + /** + * @param int $max + * + * @return QuerySecurityRule + */ + abstract protected function getRule($max); protected function assertIntrospectionQuery($maxExpected) { @@ -59,6 +38,48 @@ abstract class QuerySecurityTestCase extends TestCase $this->assertMaxValue($query, $maxExpected); } + protected function assertMaxValue($query, $maxExpected) + { + $this->assertDocumentValidator($query, $maxExpected); + $newMax = $maxExpected - 1; + if ($newMax === QuerySecurityRule::DISABLED) { + return; + } + $this->assertDocumentValidator($query, $newMax, [$this->createFormattedError($newMax, $maxExpected)]); + } + + /** + * @param string $queryString + * @param int $max + * @param string[][] $expectedErrors + * @return Error[] + */ + protected function assertDocumentValidator($queryString, $max, array $expectedErrors = []) : array + { + $errors = DocumentValidator::validate( + QuerySecuritySchema::buildSchema(), + Parser::parse($queryString), + [$this->getRule($max)] + ); + + $this->assertEquals($expectedErrors, array_map([Error::class, 'formatError'], $errors), $queryString); + + return $errors; + } + + protected function createFormattedError($max, $count, $locations = []) + { + return FormattedError::create($this->getErrorMessage($max, $count), $locations); + } + + /** + * @param int $max + * @param int $count + * + * @return string + */ + abstract protected function getErrorMessage($max, $count); + protected function assertIntrospectionTypeMetaFieldQuery($maxExpected) { $query = ' @@ -84,14 +105,4 @@ abstract class QuerySecurityTestCase extends TestCase '; $this->assertMaxValue($query, $maxExpected); } - - protected function assertMaxValue($query, $maxExpected) - { - $this->assertDocumentValidator($query, $maxExpected); - $newMax = $maxExpected - 1; - if ($newMax === QuerySecurityRule::DISABLED) { - return; - } - $this->assertDocumentValidator($query, $newMax, [$this->createFormattedError($newMax, $maxExpected)]); - } } diff --git a/tests/Validator/ScalarLeafsTest.php b/tests/Validator/ScalarLeafsTest.php index 1d9db96..c787be3 100644 --- a/tests/Validator/ScalarLeafsTest.php +++ b/tests/Validator/ScalarLeafsTest.php @@ -1,4 +1,7 @@ expectPassesRule(new ScalarLeafs, ' + $this->expectPassesRule( + new ScalarLeafs(), + ' fragment scalarSelection on Dog { barks } - '); + ' + ); } /** @@ -26,11 +31,23 @@ class ScalarLeafsTest extends ValidatorTestCase */ public function testObjectTypeMissingSelection() : void { - $this->expectFailsRule(new ScalarLeafs, ' + $this->expectFailsRule( + new ScalarLeafs(), + ' query directQueryOnObjectWithoutSubFields { human } - ', [$this->missingObjSubselection('human', 'Human', 3, 9)]); + ', + [$this->missingObjSubselection('human', 'Human', 3, 9)] + ); + } + + private function missingObjSubselection($field, $type, $line, $column) + { + return FormattedError::create( + ScalarLeafs::requiredSubselectionMessage($field, $type), + [new SourceLocation($line, $column)] + ); } /** @@ -38,11 +55,15 @@ class ScalarLeafsTest extends ValidatorTestCase */ public function testInterfaceTypeMissingSelection() : void { - $this->expectFailsRule(new ScalarLeafs, ' + $this->expectFailsRule( + new ScalarLeafs(), + ' { human { pets } } - ', [$this->missingObjSubselection('pets', '[Pet]', 3, 17)]); + ', + [$this->missingObjSubselection('pets', '[Pet]', 3, 17)] + ); } /** @@ -50,11 +71,14 @@ class ScalarLeafsTest extends ValidatorTestCase */ public function testValidScalarSelectionWithArgs() : void { - $this->expectPassesRule(new ScalarLeafs, ' + $this->expectPassesRule( + new ScalarLeafs(), + ' fragment scalarSelectionWithArgs on Dog { doesKnowCommand(dogCommand: SIT) } - '); + ' + ); } /** @@ -62,67 +86,14 @@ class ScalarLeafsTest extends ValidatorTestCase */ public function testScalarSelectionNotAllowedOnBoolean() : void { - $this->expectFailsRule(new ScalarLeafs, ' + $this->expectFailsRule( + new ScalarLeafs(), + ' fragment scalarSelectionsNotAllowedOnBoolean on Dog { barks { sinceWhen } } ', - [$this->noScalarSubselection('barks', 'Boolean', 3, 15)]); - } - - /** - * @see it('scalar selection not allowed on Enum') - */ - public function testScalarSelectionNotAllowedOnEnum() : void - { - $this->expectFailsRule(new ScalarLeafs, ' - fragment scalarSelectionsNotAllowedOnEnum on Cat { - furColor { inHexdec } - } - ', - [$this->noScalarSubselection('furColor', 'FurColor', 3, 18)] - ); - } - - /** - * @see it('scalar selection not allowed with args') - */ - public function testScalarSelectionNotAllowedWithArgs() : void - { - $this->expectFailsRule(new ScalarLeafs, ' - fragment scalarSelectionsNotAllowedWithArgs on Dog { - doesKnowCommand(dogCommand: SIT) { sinceWhen } - } - ', - [$this->noScalarSubselection('doesKnowCommand', 'Boolean', 3, 42)] - ); - } - - /** - * @see it('Scalar selection not allowed with directives') - */ - public function testScalarSelectionNotAllowedWithDirectives() : void - { - $this->expectFailsRule(new ScalarLeafs, ' - fragment scalarSelectionsNotAllowedWithDirectives on Dog { - name @include(if: true) { isAlsoHumanName } - } - ', - [$this->noScalarSubselection('name', 'String', 3, 33)] - ); - } - - /** - * @see it('Scalar selection not allowed with directives and args') - */ - public function testScalarSelectionNotAllowedWithDirectivesAndArgs() : void - { - $this->expectFailsRule(new ScalarLeafs, ' - fragment scalarSelectionsNotAllowedWithDirectivesAndArgs on Dog { - doesKnowCommand(dogCommand: SIT) @include(if: true) { sinceWhen } - } - ', - [$this->noScalarSubselection('doesKnowCommand', 'Boolean', 3, 61)] + [$this->noScalarSubselection('barks', 'Boolean', 3, 15)] ); } @@ -134,11 +105,67 @@ class ScalarLeafsTest extends ValidatorTestCase ); } - private function missingObjSubselection($field, $type, $line, $column) + /** + * @see it('scalar selection not allowed on Enum') + */ + public function testScalarSelectionNotAllowedOnEnum() : void { - return FormattedError::create( - ScalarLeafs::requiredSubselectionMessage($field, $type), - [new SourceLocation($line, $column)] + $this->expectFailsRule( + new ScalarLeafs(), + ' + fragment scalarSelectionsNotAllowedOnEnum on Cat { + furColor { inHexdec } + } + ', + [$this->noScalarSubselection('furColor', 'FurColor', 3, 18)] + ); + } + + /** + * @see it('scalar selection not allowed with args') + */ + public function testScalarSelectionNotAllowedWithArgs() : void + { + $this->expectFailsRule( + new ScalarLeafs(), + ' + fragment scalarSelectionsNotAllowedWithArgs on Dog { + doesKnowCommand(dogCommand: SIT) { sinceWhen } + } + ', + [$this->noScalarSubselection('doesKnowCommand', 'Boolean', 3, 42)] + ); + } + + /** + * @see it('Scalar selection not allowed with directives') + */ + public function testScalarSelectionNotAllowedWithDirectives() : void + { + $this->expectFailsRule( + new ScalarLeafs(), + ' + fragment scalarSelectionsNotAllowedWithDirectives on Dog { + name @include(if: true) { isAlsoHumanName } + } + ', + [$this->noScalarSubselection('name', 'String', 3, 33)] + ); + } + + /** + * @see it('Scalar selection not allowed with directives and args') + */ + public function testScalarSelectionNotAllowedWithDirectivesAndArgs() : void + { + $this->expectFailsRule( + new ScalarLeafs(), + ' + fragment scalarSelectionsNotAllowedWithDirectivesAndArgs on Dog { + doesKnowCommand(dogCommand: SIT) @include(if: true) { sinceWhen } + } + ', + [$this->noScalarSubselection('doesKnowCommand', 'Boolean', 3, 61)] ); } } diff --git a/tests/Validator/UniqueArgumentNamesTest.php b/tests/Validator/UniqueArgumentNamesTest.php index 4652aa1..44b0ac9 100644 --- a/tests/Validator/UniqueArgumentNamesTest.php +++ b/tests/Validator/UniqueArgumentNamesTest.php @@ -1,4 +1,7 @@ expectPassesRule(new UniqueArgumentNames(), ' + $this->expectPassesRule( + new UniqueArgumentNames(), + ' { field } - '); + ' + ); } /** @@ -26,11 +31,14 @@ class UniqueArgumentNamesTest extends ValidatorTestCase */ public function testNoArgumentsOnDirective() : void { - $this->expectPassesRule(new UniqueArgumentNames, ' + $this->expectPassesRule( + new UniqueArgumentNames(), + ' { field @directive } - '); + ' + ); } /** @@ -38,11 +46,14 @@ class UniqueArgumentNamesTest extends ValidatorTestCase */ public function testArgumentOnField() : void { - $this->expectPassesRule(new UniqueArgumentNames, ' + $this->expectPassesRule( + new UniqueArgumentNames(), + ' { field(arg: "value") } - '); + ' + ); } /** @@ -50,11 +61,14 @@ class UniqueArgumentNamesTest extends ValidatorTestCase */ public function testArgumentOnDirective() : void { - $this->expectPassesRule(new UniqueArgumentNames, ' + $this->expectPassesRule( + new UniqueArgumentNames(), + ' { field @directive(arg: "value") } - '); + ' + ); } /** @@ -62,12 +76,15 @@ class UniqueArgumentNamesTest extends ValidatorTestCase */ public function testSameArgumentOnTwoFields() : void { - $this->expectPassesRule(new UniqueArgumentNames, ' + $this->expectPassesRule( + new UniqueArgumentNames(), + ' { one: field(arg: "value") two: field(arg: "value") } - '); + ' + ); } /** @@ -75,11 +92,14 @@ class UniqueArgumentNamesTest extends ValidatorTestCase */ public function testSameArgumentOnFieldAndDirective() : void { - $this->expectPassesRule(new UniqueArgumentNames, ' + $this->expectPassesRule( + new UniqueArgumentNames(), + ' { field(arg: "value") @directive(arg: "value") } - '); + ' + ); } /** @@ -87,11 +107,14 @@ class UniqueArgumentNamesTest extends ValidatorTestCase */ public function testSameArgumentOnTwoDirectives() : void { - $this->expectPassesRule(new UniqueArgumentNames, ' + $this->expectPassesRule( + new UniqueArgumentNames(), + ' { field @directive1(arg: "value") @directive2(arg: "value") } - '); + ' + ); } /** @@ -99,11 +122,14 @@ class UniqueArgumentNamesTest extends ValidatorTestCase */ public function testMultipleFieldArguments() : void { - $this->expectPassesRule(new UniqueArgumentNames, ' + $this->expectPassesRule( + new UniqueArgumentNames(), + ' { field(arg1: "value", arg2: "value", arg3: "value") } - '); + ' + ); } /** @@ -111,11 +137,14 @@ class UniqueArgumentNamesTest extends ValidatorTestCase */ public function testMultipleDirectiveArguments() : void { - $this->expectPassesRule(new UniqueArgumentNames, ' + $this->expectPassesRule( + new UniqueArgumentNames(), + ' { field @directive(arg1: "value", arg2: "value", arg3: "value") } - '); + ' + ); } /** @@ -123,57 +152,15 @@ class UniqueArgumentNamesTest extends ValidatorTestCase */ public function testDuplicateFieldArguments() : void { - $this->expectFailsRule(new UniqueArgumentNames, ' + $this->expectFailsRule( + new UniqueArgumentNames(), + ' { field(arg1: "value", arg1: "value") } - ', [ - $this->duplicateArg('arg1', 3, 15, 3, 30) - ]); - } - - /** - * @see it('many duplicate field arguments') - */ - public function testManyDuplicateFieldArguments() : void - { - $this->expectFailsRule(new UniqueArgumentNames, ' - { - field(arg1: "value", arg1: "value", arg1: "value") - } - ', [ - $this->duplicateArg('arg1', 3, 15, 3, 30), - $this->duplicateArg('arg1', 3, 15, 3, 45) - ]); - } - - /** - * @see it('duplicate directive arguments') - */ - public function testDuplicateDirectiveArguments() : void - { - $this->expectFailsRule(new UniqueArgumentNames, ' - { - field @directive(arg1: "value", arg1: "value") - } - ', [ - $this->duplicateArg('arg1', 3, 26, 3, 41) - ]); - } - - /** - * @see it('many duplicate directive arguments') - */ - public function testManyDuplicateDirectiveArguments() : void - { - $this->expectFailsRule(new UniqueArgumentNames, ' - { - field @directive(arg1: "value", arg1: "value", arg1: "value") - } - ', [ - $this->duplicateArg('arg1', 3, 26, 3, 41), - $this->duplicateArg('arg1', 3, 26, 3, 56) - ]); + ', + [$this->duplicateArg('arg1', 3, 15, 3, 30)] + ); } private function duplicateArg($argName, $l1, $c1, $l2, $c2) @@ -183,4 +170,58 @@ class UniqueArgumentNamesTest extends ValidatorTestCase [new SourceLocation($l1, $c1), new SourceLocation($l2, $c2)] ); } + + /** + * @see it('many duplicate field arguments') + */ + public function testManyDuplicateFieldArguments() : void + { + $this->expectFailsRule( + new UniqueArgumentNames(), + ' + { + field(arg1: "value", arg1: "value", arg1: "value") + } + ', + [ + $this->duplicateArg('arg1', 3, 15, 3, 30), + $this->duplicateArg('arg1', 3, 15, 3, 45), + ] + ); + } + + /** + * @see it('duplicate directive arguments') + */ + public function testDuplicateDirectiveArguments() : void + { + $this->expectFailsRule( + new UniqueArgumentNames(), + ' + { + field @directive(arg1: "value", arg1: "value") + } + ', + [$this->duplicateArg('arg1', 3, 26, 3, 41)] + ); + } + + /** + * @see it('many duplicate directive arguments') + */ + public function testManyDuplicateDirectiveArguments() : void + { + $this->expectFailsRule( + new UniqueArgumentNames(), + ' + { + field @directive(arg1: "value", arg1: "value", arg1: "value") + } + ', + [ + $this->duplicateArg('arg1', 3, 26, 3, 41), + $this->duplicateArg('arg1', 3, 26, 3, 56), + ] + ); + } } diff --git a/tests/Validator/UniqueDirectivesPerLocationTest.php b/tests/Validator/UniqueDirectivesPerLocationTest.php index 199f106..e185220 100644 --- a/tests/Validator/UniqueDirectivesPerLocationTest.php +++ b/tests/Validator/UniqueDirectivesPerLocationTest.php @@ -1,4 +1,7 @@ expectPassesRule(new UniqueDirectivesPerLocation(), ' + $this->expectPassesRule( + new UniqueDirectivesPerLocation(), + ' fragment Test on Type { field } - '); + ' + ); } /** @@ -22,11 +28,14 @@ class UniqueDirectivesPerLocationTest extends ValidatorTestCase */ public function testUniqueDirectivesInDifferentLocations() : void { - $this->expectPassesRule(new UniqueDirectivesPerLocation(), ' + $this->expectPassesRule( + new UniqueDirectivesPerLocation(), + ' fragment Test on Type @directiveA { field @directiveB } - '); + ' + ); } /** @@ -34,11 +43,14 @@ class UniqueDirectivesPerLocationTest extends ValidatorTestCase */ public function testUniqueDirectivesInSameLocations() : void { - $this->expectPassesRule(new UniqueDirectivesPerLocation(), ' + $this->expectPassesRule( + new UniqueDirectivesPerLocation(), + ' fragment Test on Type @directiveA @directiveB { field @directiveA @directiveB } - '); + ' + ); } /** @@ -46,11 +58,14 @@ class UniqueDirectivesPerLocationTest extends ValidatorTestCase */ public function testSameDirectivesInDifferentLocations() : void { - $this->expectPassesRule(new UniqueDirectivesPerLocation(), ' + $this->expectPassesRule( + new UniqueDirectivesPerLocation(), + ' fragment Test on Type @directiveA { field @directiveA } - '); + ' + ); } /** @@ -58,12 +73,15 @@ class UniqueDirectivesPerLocationTest extends ValidatorTestCase */ public function testSameDirectivesInSimilarLocations() : void { - $this->expectPassesRule(new UniqueDirectivesPerLocation(), ' + $this->expectPassesRule( + new UniqueDirectivesPerLocation(), + ' fragment Test on Type { field @directive field @directive } - '); + ' + ); } /** @@ -71,13 +89,26 @@ class UniqueDirectivesPerLocationTest extends ValidatorTestCase */ public function testDuplicateDirectivesInOneLocation() : void { - $this->expectFailsRule(new UniqueDirectivesPerLocation(), ' + $this->expectFailsRule( + new UniqueDirectivesPerLocation(), + ' fragment Test on Type { field @directive @directive } - ', [ - $this->duplicateDirective('directive', 3, 15, 3, 26) - ]); + ', + [$this->duplicateDirective('directive', 3, 15, 3, 26)] + ); + } + + private function duplicateDirective($directiveName, $l1, $c1, $l2, $c2) + { + return [ + 'message' => UniqueDirectivesPerLocation::duplicateDirectiveMessage($directiveName), + 'locations' => [ + ['line' => $l1, 'column' => $c1], + ['line' => $l2, 'column' => $c2], + ], + ]; } /** @@ -85,14 +116,18 @@ class UniqueDirectivesPerLocationTest extends ValidatorTestCase */ public function testManyDuplicateDirectivesInOneLocation() : void { - $this->expectFailsRule(new UniqueDirectivesPerLocation(), ' + $this->expectFailsRule( + new UniqueDirectivesPerLocation(), + ' fragment Test on Type { field @directive @directive @directive } - ', [ - $this->duplicateDirective('directive', 3, 15, 3, 26), - $this->duplicateDirective('directive', 3, 15, 3, 37) - ]); + ', + [ + $this->duplicateDirective('directive', 3, 15, 3, 26), + $this->duplicateDirective('directive', 3, 15, 3, 37), + ] + ); } /** @@ -100,14 +135,18 @@ class UniqueDirectivesPerLocationTest extends ValidatorTestCase */ public function testDifferentDuplicateDirectivesInOneLocation() : void { - $this->expectFailsRule(new UniqueDirectivesPerLocation, ' + $this->expectFailsRule( + new UniqueDirectivesPerLocation(), + ' fragment Test on Type { field @directiveA @directiveB @directiveA @directiveB } - ', [ - $this->duplicateDirective('directiveA', 3, 15, 3, 39), - $this->duplicateDirective('directiveB', 3, 27, 3, 51) - ]); + ', + [ + $this->duplicateDirective('directiveA', 3, 15, 3, 39), + $this->duplicateDirective('directiveB', 3, 27, 3, 51), + ] + ); } /** @@ -115,24 +154,17 @@ class UniqueDirectivesPerLocationTest extends ValidatorTestCase */ public function testDuplicateDirectivesInManyLocations() : void { - $this->expectFailsRule(new UniqueDirectivesPerLocation(), ' + $this->expectFailsRule( + new UniqueDirectivesPerLocation(), + ' fragment Test on Type @directive @directive { field @directive @directive } - ', [ - $this->duplicateDirective('directive', 2, 29, 2, 40), - $this->duplicateDirective('directive', 3, 15, 3, 26) - ]); - } - - private function duplicateDirective($directiveName, $l1, $c1, $l2, $c2) - { - return [ - 'message' =>UniqueDirectivesPerLocation::duplicateDirectiveMessage($directiveName), - 'locations' => [ - ['line' => $l1, 'column' => $c1], - ['line' => $l2, 'column' => $c2] + ', + [ + $this->duplicateDirective('directive', 2, 29, 2, 40), + $this->duplicateDirective('directive', 3, 15, 3, 26), ] - ]; + ); } } diff --git a/tests/Validator/UniqueFragmentNamesTest.php b/tests/Validator/UniqueFragmentNamesTest.php index 426e803..b34b76f 100644 --- a/tests/Validator/UniqueFragmentNamesTest.php +++ b/tests/Validator/UniqueFragmentNamesTest.php @@ -1,4 +1,7 @@ expectPassesRule(new UniqueFragmentNames(), ' + $this->expectPassesRule( + new UniqueFragmentNames(), + ' { field } - '); + ' + ); } /** @@ -26,7 +31,9 @@ class UniqueFragmentNamesTest extends ValidatorTestCase */ public function testOneFragment() : void { - $this->expectPassesRule(new UniqueFragmentNames, ' + $this->expectPassesRule( + new UniqueFragmentNames(), + ' { ...fragA } @@ -34,7 +41,8 @@ class UniqueFragmentNamesTest extends ValidatorTestCase fragment fragA on Type { field } - '); + ' + ); } /** @@ -42,7 +50,9 @@ class UniqueFragmentNamesTest extends ValidatorTestCase */ public function testManyFragments() : void { - $this->expectPassesRule(new UniqueFragmentNames, ' + $this->expectPassesRule( + new UniqueFragmentNames(), + ' { ...fragA ...fragB @@ -57,7 +67,8 @@ class UniqueFragmentNamesTest extends ValidatorTestCase fragment fragC on Type { fieldC } - '); + ' + ); } /** @@ -65,7 +76,9 @@ class UniqueFragmentNamesTest extends ValidatorTestCase */ public function testInlineFragmentsAreAlwaysUnique() : void { - $this->expectPassesRule(new UniqueFragmentNames, ' + $this->expectPassesRule( + new UniqueFragmentNames(), + ' { ...on Type { fieldA @@ -74,7 +87,8 @@ class UniqueFragmentNamesTest extends ValidatorTestCase fieldB } } - '); + ' + ); } /** @@ -82,14 +96,17 @@ class UniqueFragmentNamesTest extends ValidatorTestCase */ public function testFragmentAndOperationNamedTheSame() : void { - $this->expectPassesRule(new UniqueFragmentNames, ' + $this->expectPassesRule( + new UniqueFragmentNames(), + ' query Foo { ...Foo } fragment Foo on Type { field } - '); + ' + ); } /** @@ -97,7 +114,9 @@ class UniqueFragmentNamesTest extends ValidatorTestCase */ public function testFragmentsNamedTheSame() : void { - $this->expectFailsRule(new UniqueFragmentNames, ' + $this->expectFailsRule( + new UniqueFragmentNames(), + ' { ...fragA } @@ -107,26 +126,9 @@ class UniqueFragmentNamesTest extends ValidatorTestCase fragment fragA on Type { fieldB } - ', [ - $this->duplicateFrag('fragA', 5, 16, 8, 16) - ]); - } - - /** - * @see it('fragments named the same without being referenced') - */ - public function testFragmentsNamedTheSameWithoutBeingReferenced() : void - { - $this->expectFailsRule(new UniqueFragmentNames, ' - fragment fragA on Type { - fieldA - } - fragment fragA on Type { - fieldB - } - ', [ - $this->duplicateFrag('fragA', 2, 16, 5, 16) - ]); + ', + [$this->duplicateFrag('fragA', 5, 16, 8, 16)] + ); } private function duplicateFrag($fragName, $l1, $c1, $l2, $c2) @@ -136,4 +138,23 @@ class UniqueFragmentNamesTest extends ValidatorTestCase [new SourceLocation($l1, $c1), new SourceLocation($l2, $c2)] ); } + + /** + * @see it('fragments named the same without being referenced') + */ + public function testFragmentsNamedTheSameWithoutBeingReferenced() : void + { + $this->expectFailsRule( + new UniqueFragmentNames(), + ' + fragment fragA on Type { + fieldA + } + fragment fragA on Type { + fieldB + } + ', + [$this->duplicateFrag('fragA', 2, 16, 5, 16)] + ); + } } diff --git a/tests/Validator/UniqueInputFieldNamesTest.php b/tests/Validator/UniqueInputFieldNamesTest.php index 676dea3..81c35a0 100644 --- a/tests/Validator/UniqueInputFieldNamesTest.php +++ b/tests/Validator/UniqueInputFieldNamesTest.php @@ -1,4 +1,7 @@ expectPassesRule(new UniqueInputFieldNames(), ' + $this->expectPassesRule( + new UniqueInputFieldNames(), + ' { field(arg: { f: true }) } - '); + ' + ); } /** @@ -26,11 +31,14 @@ class UniqueInputFieldNamesTest extends ValidatorTestCase */ public function testSameInputObjectWithinTwoArgs() : void { - $this->expectPassesRule(new UniqueInputFieldNames, ' + $this->expectPassesRule( + new UniqueInputFieldNames(), + ' { field(arg1: { f: true }, arg2: { f: true }) } - '); + ' + ); } /** @@ -38,11 +46,14 @@ class UniqueInputFieldNamesTest extends ValidatorTestCase */ public function testMultipleInputObjectFields() : void { - $this->expectPassesRule(new UniqueInputFieldNames, ' + $this->expectPassesRule( + new UniqueInputFieldNames(), + ' { field(arg: { f1: "value", f2: "value", f3: "value" }) } - '); + ' + ); } /** @@ -50,7 +61,9 @@ class UniqueInputFieldNamesTest extends ValidatorTestCase */ public function testAllowsForNestedInputObjectsWithSimilarFields() : void { - $this->expectPassesRule(new UniqueInputFieldNames, ' + $this->expectPassesRule( + new UniqueInputFieldNames(), + ' { field(arg: { deep: { @@ -62,7 +75,8 @@ class UniqueInputFieldNamesTest extends ValidatorTestCase id: 1 }) } - '); + ' + ); } /** @@ -70,28 +84,15 @@ class UniqueInputFieldNamesTest extends ValidatorTestCase */ public function testDuplicateInputObjectFields() : void { - $this->expectFailsRule(new UniqueInputFieldNames, ' + $this->expectFailsRule( + new UniqueInputFieldNames(), + ' { field(arg: { f1: "value", f1: "value" }) } - ', [ - $this->duplicateField('f1', 3, 22, 3, 35) - ]); - } - - /** - * @see it('many duplicate input object fields') - */ - public function testManyDuplicateInputObjectFields() : void - { - $this->expectFailsRule(new UniqueInputFieldNames, ' - { - field(arg: { f1: "value", f1: "value", f1: "value" }) - } - ', [ - $this->duplicateField('f1', 3, 22, 3, 35), - $this->duplicateField('f1', 3, 22, 3, 48) - ]); + ', + [$this->duplicateField('f1', 3, 22, 3, 35)] + ); } private function duplicateField($name, $l1, $c1, $l2, $c2) @@ -101,4 +102,23 @@ class UniqueInputFieldNamesTest extends ValidatorTestCase [new SourceLocation($l1, $c1), new SourceLocation($l2, $c2)] ); } + + /** + * @see it('many duplicate input object fields') + */ + public function testManyDuplicateInputObjectFields() : void + { + $this->expectFailsRule( + new UniqueInputFieldNames(), + ' + { + field(arg: { f1: "value", f1: "value", f1: "value" }) + } + ', + [ + $this->duplicateField('f1', 3, 22, 3, 35), + $this->duplicateField('f1', 3, 22, 3, 48), + ] + ); + } } diff --git a/tests/Validator/UniqueOperationNamesTest.php b/tests/Validator/UniqueOperationNamesTest.php index 11cecdc..3bc90b5 100644 --- a/tests/Validator/UniqueOperationNamesTest.php +++ b/tests/Validator/UniqueOperationNamesTest.php @@ -1,4 +1,7 @@ expectPassesRule(new UniqueOperationNames(), ' + $this->expectPassesRule( + new UniqueOperationNames(), + ' fragment fragA on Type { field } - '); + ' + ); } /** @@ -26,11 +31,14 @@ class UniqueOperationNamesTest extends ValidatorTestCase */ public function testOneAnonOperation() : void { - $this->expectPassesRule(new UniqueOperationNames, ' + $this->expectPassesRule( + new UniqueOperationNames(), + ' { field } - '); + ' + ); } /** @@ -38,11 +46,14 @@ class UniqueOperationNamesTest extends ValidatorTestCase */ public function testOneNamedOperation() : void { - $this->expectPassesRule(new UniqueOperationNames, ' + $this->expectPassesRule( + new UniqueOperationNames(), + ' query Foo { field } - '); + ' + ); } /** @@ -50,7 +61,9 @@ class UniqueOperationNamesTest extends ValidatorTestCase */ public function testMultipleOperations() : void { - $this->expectPassesRule(new UniqueOperationNames, ' + $this->expectPassesRule( + new UniqueOperationNames(), + ' query Foo { field } @@ -58,7 +71,8 @@ class UniqueOperationNamesTest extends ValidatorTestCase query Bar { field } - '); + ' + ); } /** @@ -66,7 +80,9 @@ class UniqueOperationNamesTest extends ValidatorTestCase */ public function testMultipleOperationsOfDifferentTypes() : void { - $this->expectPassesRule(new UniqueOperationNames, ' + $this->expectPassesRule( + new UniqueOperationNames(), + ' query Foo { field } @@ -78,7 +94,8 @@ class UniqueOperationNamesTest extends ValidatorTestCase subscription Baz { field } - '); + ' + ); } /** @@ -86,14 +103,17 @@ class UniqueOperationNamesTest extends ValidatorTestCase */ public function testFragmentAndOperationNamedTheSame() : void { - $this->expectPassesRule(new UniqueOperationNames, ' + $this->expectPassesRule( + new UniqueOperationNames(), + ' query Foo { ...Foo } fragment Foo on Type { field } - '); + ' + ); } /** @@ -101,50 +121,18 @@ class UniqueOperationNamesTest extends ValidatorTestCase */ public function testMultipleOperationsOfSameName() : void { - $this->expectFailsRule(new UniqueOperationNames, ' + $this->expectFailsRule( + new UniqueOperationNames(), + ' query Foo { fieldA } query Foo { fieldB } - ', [ - $this->duplicateOp('Foo', 2, 13, 5, 13) - ]); - } - - /** - * @see it('multiple ops of same name of different types (mutation)') - */ - public function testMultipleOpsOfSameNameOfDifferentTypesMutation() : void - { - $this->expectFailsRule(new UniqueOperationNames, ' - query Foo { - fieldA - } - mutation Foo { - fieldB - } - ', [ - $this->duplicateOp('Foo', 2, 13, 5, 16) - ]); - } - - /** - * @see it('multiple ops of same name of different types (subscription)') - */ - public function testMultipleOpsOfSameNameOfDifferentTypesSubscription() : void - { - $this->expectFailsRule(new UniqueOperationNames, ' - query Foo { - fieldA - } - subscription Foo { - fieldB - } - ', [ - $this->duplicateOp('Foo', 2, 13, 5, 20) - ]); + ', + [$this->duplicateOp('Foo', 2, 13, 5, 13)] + ); } private function duplicateOp($opName, $l1, $c1, $l2, $c2) @@ -154,4 +142,42 @@ class UniqueOperationNamesTest extends ValidatorTestCase [new SourceLocation($l1, $c1), new SourceLocation($l2, $c2)] ); } + + /** + * @see it('multiple ops of same name of different types (mutation)') + */ + public function testMultipleOpsOfSameNameOfDifferentTypesMutation() : void + { + $this->expectFailsRule( + new UniqueOperationNames(), + ' + query Foo { + fieldA + } + mutation Foo { + fieldB + } + ', + [$this->duplicateOp('Foo', 2, 13, 5, 16)] + ); + } + + /** + * @see it('multiple ops of same name of different types (subscription)') + */ + public function testMultipleOpsOfSameNameOfDifferentTypesSubscription() : void + { + $this->expectFailsRule( + new UniqueOperationNames(), + ' + query Foo { + fieldA + } + subscription Foo { + fieldB + } + ', + [$this->duplicateOp('Foo', 2, 13, 5, 20)] + ); + } } diff --git a/tests/Validator/UniqueVariableNamesTest.php b/tests/Validator/UniqueVariableNamesTest.php index a5776a4..f587efb 100644 --- a/tests/Validator/UniqueVariableNamesTest.php +++ b/tests/Validator/UniqueVariableNamesTest.php @@ -1,4 +1,7 @@ expectPassesRule(new UniqueVariableNames(), ' + $this->expectPassesRule( + new UniqueVariableNames(), + ' query A($x: Int, $y: String) { __typename } query B($x: String, $y: Int) { __typename } - '); + ' + ); } /** @@ -25,16 +30,20 @@ class UniqueVariableNamesTest extends ValidatorTestCase */ public function testDuplicateVariableNames() : void { - $this->expectFailsRule(new UniqueVariableNames, ' + $this->expectFailsRule( + new UniqueVariableNames(), + ' query A($x: Int, $x: Int, $x: String) { __typename } query B($x: String, $x: Int) { __typename } query C($x: Int, $x: Int) { __typename } - ', [ - $this->duplicateVariable('x', 2, 16, 2, 25), - $this->duplicateVariable('x', 2, 16, 2, 34), - $this->duplicateVariable('x', 3, 16, 3, 28), - $this->duplicateVariable('x', 4, 16, 4, 25) - ]); + ', + [ + $this->duplicateVariable('x', 2, 16, 2, 25), + $this->duplicateVariable('x', 2, 16, 2, 34), + $this->duplicateVariable('x', 3, 16, 3, 28), + $this->duplicateVariable('x', 4, 16, 4, 25), + ] + ); } private function duplicateVariable($name, $l1, $c1, $l2, $c2) diff --git a/tests/Validator/ValidationTest.php b/tests/Validator/ValidationTest.php index 7bfb4b2..4b5534f 100644 --- a/tests/Validator/ValidationTest.php +++ b/tests/Validator/ValidationTest.php @@ -1,10 +1,12 @@ "Expected type Invalid, found \"bad value\"; Invalid scalar is always invalid: bad value", - 'locations' => [ ['line' => 3, 'column' => 25] ] + 'message' => 'Expected type Invalid, found "bad value"; Invalid scalar is always invalid: bad value', + 'locations' => [['line' => 3, 'column' => 25]], ]; $this->expectInvalid( @@ -53,8 +55,8 @@ class ValidationTest extends ValidatorTestCase $query = '{invalid}'; $expectedError = [ - 'message' => 'Cannot query field "invalid" on type "QueryRoot". Did you mean "invalidArg"?', - 'locations' => [ ['line' => 1, 'column' => 2] ] + 'message' => 'Cannot query field "invalid" on type "QueryRoot". Did you mean "invalidArg"?', + 'locations' => [['line' => 1, 'column' => 2]], ]; $this->expectFailsCompleteValidation($query, [$expectedError]); $this->expectValid($this->getTestSchema(), [], $query); diff --git a/tests/Validator/ValidatorTestCase.php b/tests/Validator/ValidatorTestCase.php index e0b9d20..22aa1e1 100644 --- a/tests/Validator/ValidatorTestCase.php +++ b/tests/Validator/ValidatorTestCase.php @@ -1,8 +1,10 @@ expectValid(self::getTestSchema(), [$rule], $queryString); + } + + protected function expectValid($schema, $rules, $queryString) : void + { + $this->assertEquals( + [], + DocumentValidator::validate($schema, Parser::parse($queryString), $rules), + 'Should validate' + ); + } + /** * @return Schema */ @@ -24,198 +42,198 @@ abstract class ValidatorTestCase extends TestCase $FurColor = null; $Being = new InterfaceType([ - 'name' => 'Being', + 'name' => 'Being', 'fields' => [ 'name' => [ 'type' => Type::string(), - 'args' => ['surname' => ['type' => Type::boolean()]] - ] + 'args' => ['surname' => ['type' => Type::boolean()]], + ], ], ]); $Pet = new InterfaceType([ - 'name' => 'Pet', + 'name' => 'Pet', 'fields' => [ 'name' => [ 'type' => Type::string(), - 'args' => ['surname' => ['type' => Type::boolean()]] - ] + 'args' => ['surname' => ['type' => Type::boolean()]], + ], ], ]); $Canine = new InterfaceType([ - 'name' => 'Canine', + 'name' => 'Canine', 'fields' => function () { return [ 'name' => [ 'type' => Type::string(), - 'args' => ['surname' => ['type' => Type::boolean()]] - ] + 'args' => ['surname' => ['type' => Type::boolean()]], + ], ]; - } + }, ]); $DogCommand = new EnumType([ - 'name' => 'DogCommand', + 'name' => 'DogCommand', 'values' => [ - 'SIT' => ['value' => 0], + 'SIT' => ['value' => 0], 'HEEL' => ['value' => 1], - 'DOWN' => ['value' => 2] - ] + 'DOWN' => ['value' => 2], + ], ]); $Dog = new ObjectType([ - 'name' => 'Dog', - 'fields' => [ - 'name' => [ + 'name' => 'Dog', + 'fields' => [ + 'name' => [ 'type' => Type::string(), - 'args' => ['surname' => ['type' => Type::boolean()]] + 'args' => ['surname' => ['type' => Type::boolean()]], ], - 'nickname' => ['type' => Type::string()], - 'barkVolume' => ['type' => Type::int()], - 'barks' => ['type' => Type::boolean()], + 'nickname' => ['type' => Type::string()], + 'barkVolume' => ['type' => Type::int()], + 'barks' => ['type' => Type::boolean()], 'doesKnowCommand' => [ 'type' => Type::boolean(), - 'args' => ['dogCommand' => ['type' => $DogCommand]] + 'args' => ['dogCommand' => ['type' => $DogCommand]], ], - 'isHousetrained' => [ + 'isHousetrained' => [ 'type' => Type::boolean(), - 'args' => ['atOtherHomes' => ['type' => Type::boolean(), 'defaultValue' => true]] + 'args' => ['atOtherHomes' => ['type' => Type::boolean(), 'defaultValue' => true]], ], - 'isAtLocation' => [ + 'isAtLocation' => [ 'type' => Type::boolean(), - 'args' => ['x' => ['type' => Type::int()], 'y' => ['type' => Type::int()]] - ] + 'args' => ['x' => ['type' => Type::int()], 'y' => ['type' => Type::int()]], + ], ], - 'interfaces' => [$Being, $Pet, $Canine] + 'interfaces' => [$Being, $Pet, $Canine], ]); $Cat = new ObjectType([ - 'name' => 'Cat', - 'fields' => function () use (&$FurColor) { + 'name' => 'Cat', + 'fields' => function () use (&$FurColor) { return [ - 'name' => [ + 'name' => [ 'type' => Type::string(), - 'args' => ['surname' => ['type' => Type::boolean()]] + 'args' => ['surname' => ['type' => Type::boolean()]], ], - 'nickname' => ['type' => Type::string()], - 'meows' => ['type' => Type::boolean()], + 'nickname' => ['type' => Type::string()], + 'meows' => ['type' => Type::boolean()], 'meowVolume' => ['type' => Type::int()], - 'furColor' => $FurColor + 'furColor' => $FurColor, ]; }, - 'interfaces' => [$Being, $Pet] + 'interfaces' => [$Being, $Pet], ]); $CatOrDog = new UnionType([ - 'name' => 'CatOrDog', + 'name' => 'CatOrDog', 'types' => [$Dog, $Cat], ]); $Intelligent = new InterfaceType([ - 'name' => 'Intelligent', + 'name' => 'Intelligent', 'fields' => [ - 'iq' => ['type' => Type::int()] - ] + 'iq' => ['type' => Type::int()], + ], ]); $Human = null; $Human = new ObjectType([ - 'name' => 'Human', + 'name' => 'Human', 'interfaces' => [$Being, $Intelligent], - 'fields' => function () use (&$Human, $Pet) { + 'fields' => function () use (&$Human, $Pet) { return [ - 'name' => [ + 'name' => [ 'type' => Type::string(), - 'args' => ['surname' => ['type' => Type::boolean()]] + 'args' => ['surname' => ['type' => Type::boolean()]], ], - 'pets' => ['type' => Type::listOf($Pet)], + 'pets' => ['type' => Type::listOf($Pet)], 'relatives' => ['type' => Type::listOf($Human)], - 'iq' => ['type' => Type::int()] + 'iq' => ['type' => Type::int()], ]; - } + }, ]); $Alien = new ObjectType([ - 'name' => 'Alien', + 'name' => 'Alien', 'interfaces' => [$Being, $Intelligent], - 'fields' => [ - 'iq' => ['type' => Type::int()], - 'name' => [ + 'fields' => [ + 'iq' => ['type' => Type::int()], + 'name' => [ 'type' => Type::string(), - 'args' => ['surname' => ['type' => Type::boolean()]] + 'args' => ['surname' => ['type' => Type::boolean()]], ], - 'numEyes' => ['type' => Type::int()] - ] + 'numEyes' => ['type' => Type::int()], + ], ]); $DogOrHuman = new UnionType([ - 'name' => 'DogOrHuman', + 'name' => 'DogOrHuman', 'types' => [$Dog, $Human], ]); $HumanOrAlien = new UnionType([ - 'name' => 'HumanOrAlien', + 'name' => 'HumanOrAlien', 'types' => [$Human, $Alien], ]); $FurColor = new EnumType([ - 'name' => 'FurColor', + 'name' => 'FurColor', 'values' => [ - 'BROWN' => ['value' => 0], - 'BLACK' => ['value' => 1], - 'TAN' => ['value' => 2], + 'BROWN' => ['value' => 0], + 'BLACK' => ['value' => 1], + 'TAN' => ['value' => 2], 'SPOTTED' => ['value' => 3], - 'NO_FUR' => ['value' => null], + 'NO_FUR' => ['value' => null], ], ]); $ComplexInput = new InputObjectType([ - 'name' => 'ComplexInput', + 'name' => 'ComplexInput', 'fields' => [ - 'requiredField' => ['type' => Type::nonNull(Type::boolean())], - 'intField' => ['type' => Type::int()], - 'stringField' => ['type' => Type::string()], - 'booleanField' => ['type' => Type::boolean()], - 'stringListField' => ['type' => Type::listOf(Type::string())] - ] + 'requiredField' => ['type' => Type::nonNull(Type::boolean())], + 'intField' => ['type' => Type::int()], + 'stringField' => ['type' => Type::string()], + 'booleanField' => ['type' => Type::boolean()], + 'stringListField' => ['type' => Type::listOf(Type::string())], + ], ]); $ComplicatedArgs = new ObjectType([ - 'name' => 'ComplicatedArgs', + 'name' => 'ComplicatedArgs', // TODO List // TODO Coercion // TODO NotNulls 'fields' => [ - 'intArgField' => [ + 'intArgField' => [ 'type' => Type::string(), 'args' => ['intArg' => ['type' => Type::int()]], ], - 'nonNullIntArgField' => [ + 'nonNullIntArgField' => [ 'type' => Type::string(), 'args' => ['nonNullIntArg' => ['type' => Type::nonNull(Type::int())]], ], - 'stringArgField' => [ + 'stringArgField' => [ 'type' => Type::string(), 'args' => ['stringArg' => ['type' => Type::string()]], ], - 'booleanArgField' => [ + 'booleanArgField' => [ 'type' => Type::string(), 'args' => ['booleanArg' => ['type' => Type::boolean()]], ], - 'enumArgField' => [ + 'enumArgField' => [ 'type' => Type::string(), 'args' => ['enumArg' => ['type' => $FurColor]], ], - 'floatArgField' => [ + 'floatArgField' => [ 'type' => Type::string(), 'args' => ['floatArg' => ['type' => Type::float()]], ], - 'idArgField' => [ + 'idArgField' => [ 'type' => Type::string(), 'args' => ['idArg' => ['type' => Type::id()]], ], - 'stringListArgField' => [ + 'stringListArgField' => [ 'type' => Type::string(), 'args' => ['stringListArg' => ['type' => Type::listOf(Type::string())]], ], @@ -227,194 +245,191 @@ abstract class ValidatorTestCase extends TestCase ], ], ], - 'complexArgField' => [ + 'complexArgField' => [ 'type' => Type::string(), 'args' => ['complexArg' => ['type' => $ComplexInput]], ], - 'multipleReqs' => [ + 'multipleReqs' => [ 'type' => Type::string(), 'args' => [ 'req1' => ['type' => Type::nonNull(Type::int())], 'req2' => ['type' => Type::nonNull(Type::int())], ], ], - 'multipleOpts' => [ + 'multipleOpts' => [ 'type' => Type::string(), 'args' => [ 'opt1' => [ - 'type' => Type::int(), + 'type' => Type::int(), 'defaultValue' => 0, ], 'opt2' => [ - 'type' => Type::int(), + 'type' => Type::int(), 'defaultValue' => 0, ], ], ], - 'multipleOptAndReq' => [ + 'multipleOptAndReq' => [ 'type' => Type::string(), 'args' => [ 'req1' => ['type' => Type::nonNull(Type::int())], 'req2' => ['type' => Type::nonNull(Type::int())], 'opt1' => [ - 'type' => Type::int(), + 'type' => Type::int(), 'defaultValue' => 0, ], 'opt2' => [ - 'type' => Type::int(), + 'type' => Type::int(), 'defaultValue' => 0, ], ], ], - ] + ], ]); $invalidScalar = new CustomScalarType([ - 'name' => 'Invalid', - 'serialize' => function ($value) { + 'name' => 'Invalid', + 'serialize' => function ($value) { return $value; }, 'parseLiteral' => function ($node) { throw new \Exception('Invalid scalar is always invalid: ' . $node->value); }, - 'parseValue' => function ($node) { + 'parseValue' => function ($node) { throw new \Exception('Invalid scalar is always invalid: ' . $node); }, ]); $anyScalar = new CustomScalarType([ - 'name' => 'Any', - 'serialize' => function ($value) { + 'name' => 'Any', + 'serialize' => function ($value) { return $value; }, 'parseLiteral' => function ($node) { return $node; }, // Allows any value - 'parseValue' => function ($value) { + 'parseValue' => function ($value) { return $value; }, // Allows any value ]); $queryRoot = new ObjectType([ - 'name' => 'QueryRoot', + 'name' => 'QueryRoot', 'fields' => [ - 'human' => [ + 'human' => [ 'args' => ['id' => ['type' => Type::id()]], - 'type' => $Human + 'type' => $Human, ], - 'alien' => ['type' => $Alien], - 'dog' => ['type' => $Dog], - 'cat' => ['type' => $Cat], - 'pet' => ['type' => $Pet], - 'catOrDog' => ['type' => $CatOrDog], - 'dogOrHuman' => ['type' => $DogOrHuman], - 'humanOrAlien' => ['type' => $HumanOrAlien], + 'alien' => ['type' => $Alien], + 'dog' => ['type' => $Dog], + 'cat' => ['type' => $Cat], + 'pet' => ['type' => $Pet], + 'catOrDog' => ['type' => $CatOrDog], + 'dogOrHuman' => ['type' => $DogOrHuman], + 'humanOrAlien' => ['type' => $HumanOrAlien], 'complicatedArgs' => ['type' => $ComplicatedArgs], - 'invalidArg' => [ + 'invalidArg' => [ 'args' => [ - 'arg' => ['type' => $invalidScalar] + 'arg' => ['type' => $invalidScalar], ], 'type' => Type::string(), ], - 'anyArg' => [ + 'anyArg' => [ 'args' => ['arg' => ['type' => $anyScalar]], 'type' => Type::string(), ], - ] + ], ]); $testSchema = new Schema([ - 'query' => $queryRoot, + 'query' => $queryRoot, 'directives' => [ Directive::includeDirective(), Directive::skipDirective(), new Directive([ - 'name' => 'onQuery', + 'name' => 'onQuery', 'locations' => ['QUERY'], ]), new Directive([ - 'name' => 'onMutation', + 'name' => 'onMutation', 'locations' => ['MUTATION'], ]), new Directive([ - 'name' => 'onSubscription', + 'name' => 'onSubscription', 'locations' => ['SUBSCRIPTION'], ]), new Directive([ - 'name' => 'onField', + 'name' => 'onField', 'locations' => ['FIELD'], ]), new Directive([ - 'name' => 'onFragmentDefinition', + 'name' => 'onFragmentDefinition', 'locations' => ['FRAGMENT_DEFINITION'], ]), new Directive([ - 'name' => 'onFragmentSpread', + 'name' => 'onFragmentSpread', 'locations' => ['FRAGMENT_SPREAD'], ]), new Directive([ - 'name' => 'onInlineFragment', + 'name' => 'onInlineFragment', 'locations' => ['INLINE_FRAGMENT'], ]), new Directive([ - 'name' => 'onSchema', + 'name' => 'onSchema', 'locations' => ['SCHEMA'], ]), new Directive([ - 'name' => 'onScalar', + 'name' => 'onScalar', 'locations' => ['SCALAR'], ]), new Directive([ - 'name' => 'onObject', + 'name' => 'onObject', 'locations' => ['OBJECT'], ]), new Directive([ - 'name' => 'onFieldDefinition', + 'name' => 'onFieldDefinition', 'locations' => ['FIELD_DEFINITION'], ]), new Directive([ - 'name' => 'onArgumentDefinition', + 'name' => 'onArgumentDefinition', 'locations' => ['ARGUMENT_DEFINITION'], ]), new Directive([ - 'name' => 'onInterface', + 'name' => 'onInterface', 'locations' => ['INTERFACE'], ]), new Directive([ - 'name' => 'onUnion', + 'name' => 'onUnion', 'locations' => ['UNION'], ]), new Directive([ - 'name' => 'onEnum', + 'name' => 'onEnum', 'locations' => ['ENUM'], ]), new Directive([ - 'name' => 'onEnumValue', + 'name' => 'onEnumValue', 'locations' => ['ENUM_VALUE'], ]), new Directive([ - 'name' => 'onInputObject', + 'name' => 'onInputObject', 'locations' => ['INPUT_OBJECT'], ]), new Directive([ - 'name' => 'onInputFieldDefinition', + 'name' => 'onInputFieldDefinition', 'locations' => ['INPUT_FIELD_DEFINITION'], ]), ], ]); + return $testSchema; } - function expectValid($schema, $rules, $queryString) + protected function expectFailsRule($rule, $queryString, $errors) { - $this->assertEquals( - [], - DocumentValidator::validate($schema, Parser::parse($queryString), $rules), - 'Should validate' - ); + return $this->expectInvalid(self::getTestSchema(), [$rule], $queryString, $errors); } - function expectInvalid($schema, $rules, $queryString, $expectedErrors) + protected function expectInvalid($schema, $rules, $queryString, $expectedErrors) { $errors = DocumentValidator::validate($schema, Parser::parse($queryString), $rules); @@ -424,33 +439,23 @@ abstract class ValidatorTestCase extends TestCase return $errors; } - function expectPassesRule($rule, $queryString) - { - $this->expectValid($this->getTestSchema(), [$rule], $queryString); - } - - function expectFailsRule($rule, $queryString, $errors) - { - return $this->expectInvalid($this->getTestSchema(), [$rule], $queryString, $errors); - } - - function expectPassesRuleWithSchema($schema, $rule, $queryString) + protected function expectPassesRuleWithSchema($schema, $rule, $queryString) : void { $this->expectValid($schema, [$rule], $queryString); } - function expectFailsRuleWithSchema($schema, $rule, $queryString, $errors) + protected function expectFailsRuleWithSchema($schema, $rule, $queryString, $errors) : void { $this->expectInvalid($schema, [$rule], $queryString, $errors); } - function expectPassesCompleteValidation($queryString) + protected function expectPassesCompleteValidation($queryString) : void { - $this->expectValid($this->getTestSchema(), DocumentValidator::allRules(), $queryString); + $this->expectValid(self::getTestSchema(), DocumentValidator::allRules(), $queryString); } - function expectFailsCompleteValidation($queryString, $errors) + protected function expectFailsCompleteValidation($queryString, $errors) : void { - $this->expectInvalid($this->getTestSchema(), DocumentValidator::allRules(), $queryString, $errors); + $this->expectInvalid(self::getTestSchema(), DocumentValidator::allRules(), $queryString, $errors); } } diff --git a/tests/Validator/ValuesOfCorrectTypeTest.php b/tests/Validator/ValuesOfCorrectTypeTest.php index 2d9f79b..f0e035e 100644 --- a/tests/Validator/ValuesOfCorrectTypeTest.php +++ b/tests/Validator/ValuesOfCorrectTypeTest.php @@ -1,4 +1,7 @@ expectPassesRule( + new ValuesOfCorrectType(), + ' + { + complicatedArgs { + intArgField(intArg: 2) + } + } + ' + ); + } + + /** + * @see it('Good negative int value') + */ + public function testGoodNegativeIntValue() : void + { + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' + { + complicatedArgs { + intArgField(intArg: -2) + } + } + ' + ); + } + + /** + * @see it('Good boolean value') + */ + public function testGoodBooleanValue() : void + { + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' + { + complicatedArgs { + booleanArgField(booleanArg: true) + } + } + ' + ); + } + + // Validate: Values of correct type + // Valid values + + /** + * @see it('Good string value') + */ + public function testGoodStringValue() : void + { + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' + { + complicatedArgs { + stringArgField(stringArg: "foo") + } + } + ' + ); + } + + /** + * @see it('Good float value') + */ + public function testGoodFloatValue() : void + { + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' + { + complicatedArgs { + floatArgField(floatArg: 1.1) + } + } + ' + ); + } + + public function testGoodNegativeFloatValue() : void + { + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' + { + complicatedArgs { + floatArgField(floatArg: -1.1) + } + } + ' + ); + } + + /** + * @see it('Int into Float') + */ + public function testIntIntoFloat() : void + { + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' + { + complicatedArgs { + floatArgField(floatArg: 1) + } + } + ' + ); + } + + /** + * @see it('Int into ID') + */ + public function testIntIntoID() : void + { + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' + { + complicatedArgs { + idArgField(idArg: 1) + } + } + ' + ); + } + + /** + * @see it('String into ID') + */ + public function testStringIntoID() : void + { + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' + { + complicatedArgs { + idArgField(idArg: "someIdString") + } + } + ' + ); + } + + /** + * @see it('Good enum value') + */ + public function testGoodEnumValue() : void + { + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' + { + dog { + doesKnowCommand(dogCommand: SIT) + } + } + ' + ); + } + + /** + * @see it('Enum with null value') + */ + public function testEnumWithNullValue() : void + { + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' + { + complicatedArgs { + enumArgField(enumArg: NO_FUR) + } + } + ' + ); + } + + /** + * @see it('null into nullable type') + */ + public function testNullIntoNullableType() : void + { + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' + { + complicatedArgs { + intArgField(intArg: null) + } + } + ' + ); + + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' + { + dog(a: null, b: null, c:{ requiredField: true, intField: null }) { + name + } + } + ' + ); + } + + /** + * @see it('Int into String') + */ + public function testIntIntoString() : void + { + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' + { + complicatedArgs { + stringArgField(stringArg: 1) + } + } + ', + [ + $this->badValue('String', '1', 4, 39), + ] + ); + } + private function badValue($typeName, $value, $line, $column, $message = null) { return FormattedError::create( @@ -19,252 +257,46 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase ); } - private function requiredField($typeName, $fieldName, $fieldTypeName, $line, $column) { - return FormattedError::create( - ValuesOfCorrectType::requiredFieldMessage( - $typeName, - $fieldName, - $fieldTypeName - ), - [new SourceLocation($line, $column)] - ); - } - - private function unknownField($typeName, $fieldName, $line, $column, $message = null) { - return FormattedError::create( - ValuesOfCorrectType::unknownFieldMessage( - $typeName, - $fieldName, - $message - ), - [new SourceLocation($line, $column)] - ); - } - - // Validate: Values of correct type - // Valid values - - /** - * @see it('Good int value') - */ - public function testGoodIntValue() : void - { - $this->expectPassesRule(new ValuesOfCorrectType, ' - { - complicatedArgs { - intArgField(intArg: 2) - } - } - '); - } - - /** - * @see it('Good negative int value') - */ - public function testGoodNegativeIntValue() : void - { - $this->expectPassesRule(new ValuesOfCorrectType, ' - { - complicatedArgs { - intArgField(intArg: -2) - } - } - '); - } - - /** - * @see it('Good boolean value') - */ - public function testGoodBooleanValue() : void - { - $this->expectPassesRule(new ValuesOfCorrectType, ' - { - complicatedArgs { - booleanArgField(booleanArg: true) - } - } - '); - } - - /** - * @see it('Good string value') - */ - public function testGoodStringValue() : void - { - $this->expectPassesRule(new ValuesOfCorrectType, ' - { - complicatedArgs { - stringArgField(stringArg: "foo") - } - } - '); - } - - /** - * @see it('Good float value') - */ - public function testGoodFloatValue() : void - { - $this->expectPassesRule(new ValuesOfCorrectType, ' - { - complicatedArgs { - floatArgField(floatArg: 1.1) - } - } - '); - } - - public function testGoodNegativeFloatValue() : void - { - $this->expectPassesRule(new ValuesOfCorrectType, ' - { - complicatedArgs { - floatArgField(floatArg: -1.1) - } - } - '); - } - - /** - * @see it('Int into Float') - */ - public function testIntIntoFloat() : void - { - $this->expectPassesRule(new ValuesOfCorrectType, ' - { - complicatedArgs { - floatArgField(floatArg: 1) - } - } - '); - } - - /** - * @see it('Int into ID') - */ - public function testIntIntoID() : void - { - $this->expectPassesRule(new ValuesOfCorrectType, ' - { - complicatedArgs { - idArgField(idArg: 1) - } - } - '); - } - - /** - * @see it('String into ID') - */ - public function testStringIntoID() : void - { - $this->expectPassesRule(new ValuesOfCorrectType, ' - { - complicatedArgs { - idArgField(idArg: "someIdString") - } - } - '); - } - - /** - * @see it('Good enum value') - */ - public function testGoodEnumValue() : void - { - $this->expectPassesRule(new ValuesOfCorrectType, ' - { - dog { - doesKnowCommand(dogCommand: SIT) - } - } - '); - } - - /** - * @see it('Enum with null value') - */ - public function testEnumWithNullValue() : void - { - $this->expectPassesRule(new ValuesOfCorrectType, ' - { - complicatedArgs { - enumArgField(enumArg: NO_FUR) - } - } - '); - } - - /** - * @see it('null into nullable type') - */ - public function testNullIntoNullableType() : void - { - $this->expectPassesRule(new ValuesOfCorrectType, ' - { - complicatedArgs { - intArgField(intArg: null) - } - } - '); - - $this->expectPassesRule(new ValuesOfCorrectType, ' - { - dog(a: null, b: null, c:{ requiredField: true, intField: null }) { - name - } - } - '); - } - - // Invalid String values - - /** - * @see it('Int into String') - */ - public function testIntIntoString() : void - { - $this->expectFailsRule(new ValuesOfCorrectType, ' - { - complicatedArgs { - stringArgField(stringArg: 1) - } - } - ', [ - $this->badValue('String', '1', 4, 39) - ]); - } - /** * @see it('Float into String') */ public function testFloatIntoString() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { stringArgField(stringArg: 1.0) } } - ', [ - $this->badValue('String', '1.0', 4, 39) - ]); + ', + [ + $this->badValue('String', '1.0', 4, 39), + ] + ); } + // Invalid String values + /** * @see it('Boolean into String') */ public function testBooleanIntoString() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { stringArgField(stringArg: true) } } - ', [ - $this->badValue('String', 'true', 4, 39) - ]); + ', + [ + $this->badValue('String', 'true', 4, 39), + ] + ); } /** @@ -272,33 +304,39 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testUnquotedStringIntoString() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { stringArgField(stringArg: BAR) } } - ', [ - $this->badValue('String', 'BAR', 4, 39) - ]); + ', + [ + $this->badValue('String', 'BAR', 4, 39), + ] + ); } - // Invalid Int values - /** * @see it('String into Int') */ public function testStringIntoInt() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { intArgField(intArg: "3") } } - ', [ - $this->badValue('Int', '"3"', 4, 33) - ]); + ', + [ + $this->badValue('Int', '"3"', 4, 33), + ] + ); } /** @@ -306,31 +344,41 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testBigIntIntoInt() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { intArgField(intArg: 829384293849283498239482938) } } - ', [ - $this->badValue('Int', '829384293849283498239482938', 4, 33) - ]); + ', + [ + $this->badValue('Int', '829384293849283498239482938', 4, 33), + ] + ); } + // Invalid Int values + /** * @see it('Unquoted String into Int') */ public function testUnquotedStringIntoInt() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { intArgField(intArg: FOO) } } - ', [ - $this->badValue('Int', 'FOO', 4, 33) - ]); + ', + [ + $this->badValue('Int', 'FOO', 4, 33), + ] + ); } /** @@ -338,15 +386,19 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testSimpleFloatIntoInt() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { intArgField(intArg: 3.0) } } - ', [ - $this->badValue('Int', '3.0', 4, 33) - ]); + ', + [ + $this->badValue('Int', '3.0', 4, 33), + ] + ); } /** @@ -354,33 +406,39 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testFloatIntoInt() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { intArgField(intArg: 3.333) } } - ', [ - $this->badValue('Int', '3.333', 4, 33) - ]); + ', + [ + $this->badValue('Int', '3.333', 4, 33), + ] + ); } - // Invalid Float values - /** * @see it('String into Float') */ public function testStringIntoFloat() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { floatArgField(floatArg: "3.333") } } - ', [ - $this->badValue('Float', '"3.333"', 4, 37) - ]); + ', + [ + $this->badValue('Float', '"3.333"', 4, 37), + ] + ); } /** @@ -388,49 +446,61 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testBooleanIntoFloat() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { floatArgField(floatArg: true) } } - ', [ - $this->badValue('Float', 'true', 4, 37) - ]); + ', + [ + $this->badValue('Float', 'true', 4, 37), + ] + ); } + // Invalid Float values + /** * @see it('Unquoted into Float') */ public function testUnquotedIntoFloat() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { floatArgField(floatArg: FOO) } } - ', [ - $this->badValue('Float', 'FOO', 4, 37) - ]); + ', + [ + $this->badValue('Float', 'FOO', 4, 37), + ] + ); } - // Invalid Boolean value - /** * @see it('Int into Boolean') */ public function testIntIntoBoolean() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { booleanArgField(booleanArg: 2) } } - ', [ - $this->badValue('Boolean', '2', 4, 41) - ]); + ', + [ + $this->badValue('Boolean', '2', 4, 41), + ] + ); } /** @@ -438,31 +508,41 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testFloatIntoBoolean() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { booleanArgField(booleanArg: 1.0) } } - ', [ - $this->badValue('Boolean', '1.0', 4, 41) - ]); + ', + [ + $this->badValue('Boolean', '1.0', 4, 41), + ] + ); } + // Invalid Boolean value + /** * @see it('String into Boolean') */ public function testStringIntoBoolean() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { booleanArgField(booleanArg: "true") } } - ', [ - $this->badValue('Boolean', '"true"', 4, 41) - ]); + ', + [ + $this->badValue('Boolean', '"true"', 4, 41), + ] + ); } /** @@ -470,33 +550,39 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testUnquotedIntoBoolean() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { booleanArgField(booleanArg: TRUE) } } - ', [ - $this->badValue('Boolean', 'TRUE', 4, 41) - ]); + ', + [ + $this->badValue('Boolean', 'TRUE', 4, 41), + ] + ); } - // Invalid ID value - /** * @see it('Float into ID') */ public function testFloatIntoID() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { idArgField(idArg: 1.0) } } - ', [ - $this->badValue('ID', '1.0', 4, 31) - ]); + ', + [ + $this->badValue('ID', '1.0', 4, 31), + ] + ); } /** @@ -504,49 +590,61 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testBooleanIntoID() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { idArgField(idArg: true) } } - ', [ - $this->badValue('ID', 'true', 4, 31) - ]); + ', + [ + $this->badValue('ID', 'true', 4, 31), + ] + ); } + // Invalid ID value + /** * @see it('Unquoted into ID') */ public function testUnquotedIntoID() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { idArgField(idArg: SOMETHING) } } - ', [ - $this->badValue('ID', 'SOMETHING', 4, 31) - ]); + ', + [ + $this->badValue('ID', 'SOMETHING', 4, 31), + ] + ); } - // Invalid Enum value - /** * @see it('Int into Enum') */ public function testIntIntoEnum() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { dog { doesKnowCommand(dogCommand: 2) } } - ', [ - $this->badValue('DogCommand', '2', 4, 41) - ]); + ', + [ + $this->badValue('DogCommand', '2', 4, 41), + ] + ); } /** @@ -554,37 +652,47 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testFloatIntoEnum() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { dog { doesKnowCommand(dogCommand: 1.0) } } - ', [ - $this->badValue('DogCommand', '1.0', 4, 41) - ]); + ', + [ + $this->badValue('DogCommand', '1.0', 4, 41), + ] + ); } + // Invalid Enum value + /** * @see it('String into Enum') */ public function testStringIntoEnum() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { dog { doesKnowCommand(dogCommand: "SIT") } } - ', [ - $this->badValue( - 'DogCommand', - '"SIT"', - 4, - 41, - 'Did you mean the enum value SIT?' - ) - ]); + ', + [ + $this->badValue( + 'DogCommand', + '"SIT"', + 4, + 41, + 'Did you mean the enum value SIT?' + ), + ] + ); } /** @@ -592,15 +700,19 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testBooleanIntoEnum() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { dog { doesKnowCommand(dogCommand: true) } } - ', [ - $this->badValue('DogCommand', 'true', 4, 41) - ]); + ', + [ + $this->badValue('DogCommand', 'true', 4, 41), + ] + ); } /** @@ -608,15 +720,19 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testUnknownEnumValueIntoEnum() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { dog { doesKnowCommand(dogCommand: JUGGLE) } } - ', [ - $this->badValue('DogCommand', 'JUGGLE', 4, 41) - ]); + ', + [ + $this->badValue('DogCommand', 'JUGGLE', 4, 41), + ] + ); } /** @@ -624,37 +740,42 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testDifferentCaseEnumValueIntoEnum() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { dog { doesKnowCommand(dogCommand: sit) } } - ', [ - $this->badValue( - 'DogCommand', - 'sit', - 4, - 41, - 'Did you mean the enum value SIT?' - ) - ]); + ', + [ + $this->badValue( + 'DogCommand', + 'sit', + 4, + 41, + 'Did you mean the enum value SIT?' + ), + ] + ); } - // Valid List value - /** * @see it('Good list value') */ public function testGoodListValue() : void { - $this->expectPassesRule(new ValuesOfCorrectType, ' + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { stringListArgField(stringListArg: ["one", null, "two"]) } } - '); + ' + ); } /** @@ -662,27 +783,35 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testEmptyListValue() : void { - $this->expectPassesRule(new ValuesOfCorrectType, ' + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { stringListArgField(stringListArg: []) } } - '); + ' + ); } + // Valid List value + /** * @see it('Null value') */ public function testNullValue() : void { - $this->expectPassesRule(new ValuesOfCorrectType, ' + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { stringListArgField(stringListArg: null) } } - '); + ' + ); } /** @@ -690,31 +819,36 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testSingleValueIntoList() : void { - $this->expectPassesRule(new ValuesOfCorrectType, ' + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { stringListArgField(stringListArg: "one") } } - '); + ' + ); } - // Invalid List value - /** * @see it('Incorrect item type') */ public function testIncorrectItemtype() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { stringListArgField(stringListArg: ["one", 2]) } } - ', [ - $this->badValue('String', '2', 4, 55), - ]); + ', + [ + $this->badValue('String', '2', 4, 55), + ] + ); } /** @@ -722,31 +856,38 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testSingleValueOfIncorrectType() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { stringListArgField(stringListArg: 1) } } - ', [ - $this->badValue('[String]', '1', 4, 47), - ]); + ', + [ + $this->badValue('[String]', '1', 4, 47), + ] + ); } - // Valid non-nullable value + // Invalid List value /** * @see it('Arg on optional arg') */ public function testArgOnOptionalArg() : void { - $this->expectPassesRule(new ValuesOfCorrectType, ' + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' { dog { isHousetrained(atOtherHomes: true) } } - '); + ' + ); } /** @@ -754,27 +895,35 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testNoArgOnOptionalArg() : void { - $this->expectPassesRule(new ValuesOfCorrectType, ' + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' { dog { isHousetrained } } - '); + ' + ); } + // Valid non-nullable value + /** * @see it('Multiple args') */ public function testMultipleArgs() : void { - $this->expectPassesRule(new ValuesOfCorrectType, ' + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { multipleReqs(req1: 1, req2: 2) } } - '); + ' + ); } /** @@ -782,13 +931,16 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testMultipleArgsReverseOrder() : void { - $this->expectPassesRule(new ValuesOfCorrectType, ' + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { multipleReqs(req2: 2, req1: 1) } } - '); + ' + ); } /** @@ -796,13 +948,16 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testNoArgsOnMultipleOptional() : void { - $this->expectPassesRule(new ValuesOfCorrectType, ' + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { multipleOpts } } - '); + ' + ); } /** @@ -810,13 +965,16 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testOneArgOnMultipleOptional() : void { - $this->expectPassesRule(new ValuesOfCorrectType, ' + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { multipleOpts(opt1: 1) } } - '); + ' + ); } /** @@ -824,13 +982,16 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testSecondArgOnMultipleOptional() : void { - $this->expectPassesRule(new ValuesOfCorrectType, ' + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { multipleOpts(opt2: 1) } } - '); + ' + ); } /** @@ -838,13 +999,16 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testMultipleReqsOnMixedList() : void { - $this->expectPassesRule(new ValuesOfCorrectType, ' + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { multipleOptAndReq(req1: 3, req2: 4) } } - '); + ' + ); } /** @@ -852,13 +1016,16 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testMultipleReqsAndOneOptOnMixedList() : void { - $this->expectPassesRule(new ValuesOfCorrectType, ' + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { multipleOptAndReq(req1: 3, req2: 4, opt1: 5) } } - '); + ' + ); } /** @@ -866,32 +1033,37 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testAllReqsAndOptsOnMixedList() : void { - $this->expectPassesRule(new ValuesOfCorrectType, ' + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { multipleOptAndReq(req1: 3, req2: 4, opt1: 5, opt2: 6) } } - '); + ' + ); } - // Invalid non-nullable value - /** * @see it('Incorrect value type') */ public function testIncorrectValueType() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { multipleReqs(req2: "two", req1: "one") } } - ', [ - $this->badValue('Int!', '"two"', 4, 32), - $this->badValue('Int!', '"one"', 4, 45), - ]); + ', + [ + $this->badValue('Int!', '"two"', 4, 32), + $this->badValue('Int!', '"one"', 4, 45), + ] + ); } /** @@ -899,48 +1071,58 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testIncorrectValueAndMissingArgumentProvidedNonNullArguments() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { multipleReqs(req1: "one") } } - ', [ - $this->badValue('Int!', '"one"', 4, 32), - ]); + ', + [ + $this->badValue('Int!', '"one"', 4, 32), + ] + ); } + // Invalid non-nullable value + /** * @see it('Null value') */ public function testNullValue2() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { multipleReqs(req1: null) } } - ', [ - $this->badValue('Int!', 'null', 4, 32), - ]); + ', + [ + $this->badValue('Int!', 'null', 4, 32), + ] + ); } - - // DESCRIBE: Valid input object value - /** * @see it('Optional arg, despite required field in type') */ public function testOptionalArgDespiteRequiredFieldInType() : void { - $this->expectPassesRule(new ValuesOfCorrectType, ' + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { complexArgField } } - '); + ' + ); } /** @@ -948,27 +1130,36 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testPartialObjectOnlyRequired() : void { - $this->expectPassesRule(new ValuesOfCorrectType, ' + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { complexArgField(complexArg: { requiredField: true }) } } - '); + ' + ); } + + // DESCRIBE: Valid input object value + /** * @see it('Partial object, required field can be falsey') */ public function testPartialObjectRequiredFieldCanBeFalsey() : void { - $this->expectPassesRule(new ValuesOfCorrectType, ' + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { complexArgField(complexArg: { requiredField: false }) } } - '); + ' + ); } /** @@ -976,13 +1167,16 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testPartialObjectIncludingRequired() : void { - $this->expectPassesRule(new ValuesOfCorrectType, ' + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { complexArgField(complexArg: { requiredField: true, intField: 4 }) } } - '); + ' + ); } /** @@ -990,7 +1184,9 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testFullObject() : void { - $this->expectPassesRule(new ValuesOfCorrectType, ' + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { complexArgField(complexArg: { @@ -1002,7 +1198,8 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase }) } } - '); + ' + ); } /** @@ -1010,7 +1207,9 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testFullObjectWithFieldsInDifferentOrder() : void { - $this->expectPassesRule(new ValuesOfCorrectType, ' + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { complexArgField(complexArg: { @@ -1022,33 +1221,52 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase }) } } - '); + ' + ); } - // DESCRIBE: Invalid input object value - /** * @see it('Partial object, missing required') */ public function testPartialObjectMissingRequired() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { complexArgField(complexArg: { intField: 4 }) } } - ', [ - $this->requiredField('ComplexInput', 'requiredField', 'Boolean!', 4, 41), - ]); + ', + [ + $this->requiredField('ComplexInput', 'requiredField', 'Boolean!', 4, 41), + ] + ); } + private function requiredField($typeName, $fieldName, $fieldTypeName, $line, $column) + { + return FormattedError::create( + ValuesOfCorrectType::requiredFieldMessage( + $typeName, + $fieldName, + $fieldTypeName + ), + [new SourceLocation($line, $column)] + ); + } + + // DESCRIBE: Invalid input object value + /** * @see it('Partial object, invalid field type') */ public function testPartialObjectInvalidFieldType() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { complexArgField(complexArg: { @@ -1057,9 +1275,11 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase }) } } - ', [ - $this->badValue('String', '2', 5, 40), - ]); + ', + [ + $this->badValue('String', '2', 5, 40), + ] + ); } /** @@ -1070,7 +1290,9 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testPartialObjectUnknownFieldArg() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { complicatedArgs { complexArgField(complexArg: { @@ -1079,37 +1301,53 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase }) } } - ', [ - $this->unknownField( - 'ComplexInput', - 'unknownField', - 6, - 15, - 'Did you mean intField or booleanField?' - ), - ]); + ', + [ + $this->unknownField( + 'ComplexInput', + 'unknownField', + 6, + 15, + 'Did you mean intField or booleanField?' + ), + ] + ); } - + private function unknownField($typeName, $fieldName, $line, $column, $message = null) + { + return FormattedError::create( + ValuesOfCorrectType::unknownFieldMessage( + $typeName, + $fieldName, + $message + ), + [new SourceLocation($line, $column)] + ); + } /** * @see it('reports original error for custom scalar which throws') */ public function testReportsOriginalErrorForCustomScalarWhichThrows() : void { - $errors = $this->expectFailsRule(new ValuesOfCorrectType, ' + $errors = $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { invalidArg(arg: 123) } - ', [ - $this->badValue( - 'Invalid', - '123', - 3, - 27, - 'Invalid scalar is always invalid: 123' - ), - ]); + ', + [ + $this->badValue( + 'Invalid', + '123', + 3, + 27, + 'Invalid scalar is always invalid: 123' + ), + ] + ); $this->assertEquals( 'Invalid scalar is always invalid: 123', @@ -1122,14 +1360,17 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testAllowsCustomScalarToAcceptComplexLiterals() : void { - $this->expectPassesRule(new ValuesOfCorrectType, ' + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' { test1: anyArg(arg: 123) test2: anyArg(arg: "abc") test3: anyArg(arg: [123, "abc"]) test4: anyArg(arg: {deep: [123, "abc"]}) } - '); + ' + ); } // DESCRIBE: Directive arguments @@ -1139,7 +1380,9 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testWithDirectivesOfValidTypes() : void { - $this->expectPassesRule(new ValuesOfCorrectType, ' + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' { dog @include(if: true) { name @@ -1148,7 +1391,8 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase name } } - '); + ' + ); } /** @@ -1156,16 +1400,20 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testWithDirectiveWithIncorrectTypes() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' { dog @include(if: "yes") { name @skip(if: ENUM) } } - ', [ - $this->badValue('Boolean!', '"yes"', 3, 28), - $this->badValue('Boolean!', 'ENUM', 4, 28), - ]); + ', + [ + $this->badValue('Boolean!', '"yes"', 3, 28), + $this->badValue('Boolean!', 'ENUM', 4, 28), + ] + ); } // DESCRIBE: Variable default values @@ -1175,7 +1423,9 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testVariablesWithValidDefaultValues() : void { - $this->expectPassesRule(new ValuesOfCorrectType, ' + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' query WithDefaultValues( $a: Int = 1, $b: String = "ok", @@ -1183,7 +1433,8 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase ) { dog { name } } - '); + ' + ); } /** @@ -1191,7 +1442,9 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testVariablesWithValidDefaultNullValues() : void { - $this->expectPassesRule(new ValuesOfCorrectType, ' + $this->expectPassesRule( + new ValuesOfCorrectType(), + ' query WithDefaultValues( $a: Int = null, $b: String = null, @@ -1199,7 +1452,8 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase ) { dog { name } } - '); + ' + ); } /** @@ -1207,7 +1461,9 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testVariablesWithInvalidDefaultNullValues() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' query WithDefaultValues( $a: Int! = null, $b: String! = null, @@ -1215,11 +1471,13 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase ) { dog { name } } - ', [ - $this->badValue('Int!', 'null', 3, 22), - $this->badValue('String!', 'null', 4, 25), - $this->badValue('Boolean!', 'null', 5, 47), - ]); + ', + [ + $this->badValue('Int!', 'null', 3, 22), + $this->badValue('String!', 'null', 4, 25), + $this->badValue('Boolean!', 'null', 5, 47), + ] + ); } /** @@ -1227,7 +1485,9 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testVariablesWithInvalidDefaultValues() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' query InvalidDefaultValues( $a: Int = "one", $b: String = 4, @@ -1235,11 +1495,13 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase ) { dog { name } } - ', [ - $this->badValue('Int', '"one"', 3, 21), - $this->badValue('String', '4', 4, 24), - $this->badValue('ComplexInput', '"notverycomplex"', 5, 30), - ]); + ', + [ + $this->badValue('Int', '"one"', 3, 21), + $this->badValue('String', '4', 4, 24), + $this->badValue('ComplexInput', '"notverycomplex"', 5, 30), + ] + ); } /** @@ -1247,16 +1509,20 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testVariablesWithComplexInvalidDefaultValues() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' query WithDefaultValues( $a: ComplexInput = { requiredField: 123, intField: "abc" } ) { dog { name } } - ', [ - $this->badValue('Boolean!', '123', 3, 47), - $this->badValue('Int', '"abc"', 3, 62), - ]); + ', + [ + $this->badValue('Boolean!', '123', 3, 47), + $this->badValue('Int', '"abc"', 3, 62), + ] + ); } /** @@ -1264,13 +1530,17 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testComplexVariablesMissingRequiredField() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' query MissingRequiredField($a: ComplexInput = {intField: 3}) { dog { name } } - ', [ - $this->requiredField('ComplexInput', 'requiredField', 'Boolean!', 2, 55), - ]); + ', + [ + $this->requiredField('ComplexInput', 'requiredField', 'Boolean!', 2, 55), + ] + ); } /** @@ -1278,12 +1548,16 @@ class ValuesOfCorrectTypeTest extends ValidatorTestCase */ public function testListVariablesWithInvalidItem() : void { - $this->expectFailsRule(new ValuesOfCorrectType, ' + $this->expectFailsRule( + new ValuesOfCorrectType(), + ' query InvalidItem($a: [String] = ["one", 2]) { dog { name } } - ', [ - $this->badValue('String', '2', 2, 50), - ]); + ', + [ + $this->badValue('String', '2', 2, 50), + ] + ); } } diff --git a/tests/Validator/VariablesAreInputTypesTest.php b/tests/Validator/VariablesAreInputTypesTest.php index 5031434..5264314 100644 --- a/tests/Validator/VariablesAreInputTypesTest.php +++ b/tests/Validator/VariablesAreInputTypesTest.php @@ -1,4 +1,7 @@ expectPassesRule(new VariablesAreInputTypes(), ' + $this->expectPassesRule( + new VariablesAreInputTypes(), + ' query Foo($a: String, $b: [Boolean!]!, $c: ComplexInput) { field(a: $a, b: $b, c: $c) } - '); + ' + ); } /** @@ -26,11 +31,14 @@ class VariablesAreInputTypesTest extends ValidatorTestCase */ public function testOutputTypesAreInvalid() : void { - $this->expectFailsRule(new VariablesAreInputTypes, ' + $this->expectFailsRule( + new VariablesAreInputTypes(), + ' query Foo($a: Dog, $b: [[CatOrDog!]]!, $c: Pet) { field(a: $a, b: $b, c: $c) } - ', [ + ', + [ FormattedError::create( VariablesAreInputTypes::nonInputTypeOnVarMessage('a', 'Dog'), [new SourceLocation(2, 21)] @@ -42,7 +50,7 @@ class VariablesAreInputTypesTest extends ValidatorTestCase FormattedError::create( VariablesAreInputTypes::nonInputTypeOnVarMessage('c', 'Pet'), [new SourceLocation(2, 50)] - ) + ), ] ); } diff --git a/tests/Validator/VariablesDefaultValueAllowedTest.php b/tests/Validator/VariablesDefaultValueAllowedTest.php index 468b6a4..ddebae2 100644 --- a/tests/Validator/VariablesDefaultValueAllowedTest.php +++ b/tests/Validator/VariablesDefaultValueAllowedTest.php @@ -1,4 +1,7 @@ expectPassesRule( + new VariablesDefaultValueAllowed(), + ' + query NullableValues($a: Int, $b: String, $c: ComplexInput) { + dog { name } + } + ' + ); + } + + // DESCRIBE: Validate: Variable default value is allowed + + /** + * @see it('required variables without default values') + */ + public function testRequiredVariablesWithoutDefaultValues() : void + { + $this->expectPassesRule( + new VariablesDefaultValueAllowed(), + ' + query RequiredValues($a: Int!, $b: String!) { + dog { name } + } + ' + ); + } + + /** + * @see it('variables with valid default values') + */ + public function testVariablesWithValidDefaultValues() : void + { + $this->expectPassesRule( + new VariablesDefaultValueAllowed(), + ' + query WithDefaultValues( + $a: Int = 1, + $b: String = "ok", + $c: ComplexInput = { requiredField: true, intField: 3 } + ) { + dog { name } + } + ' + ); + } + + /** + * @see it('variables with valid default null values') + */ + public function testVariablesWithValidDefaultNullValues() : void + { + $this->expectPassesRule( + new VariablesDefaultValueAllowed(), + ' + query WithDefaultValues( + $a: Int = null, + $b: String = null, + $c: ComplexInput = { requiredField: true, intField: null } + ) { + dog { name } + } + ' + ); + } + + /** + * @see it('no required variables with default values') + */ + public function testNoRequiredVariablesWithDefaultValues() : void + { + $this->expectFailsRule( + new VariablesDefaultValueAllowed(), + ' + query UnreachableDefaultValues($a: Int! = 3, $b: String! = "default") { + dog { name } + } + ', + [ + $this->defaultForRequiredVar('a', 'Int!', 'Int', 2, 49), + $this->defaultForRequiredVar('b', 'String!', 'String', 2, 66), + ] + ); + } + private function defaultForRequiredVar($varName, $typeName, $guessTypeName, $line, $column) { return FormattedError::create( @@ -19,91 +111,22 @@ class VariablesDefaultValueAllowedTest extends ValidatorTestCase ); } - // DESCRIBE: Validate: Variable default value is allowed - - /** - * @see it('variables with no default values') - */ - public function testVariablesWithNoDefaultValues() : void - { - $this->expectPassesRule(new VariablesDefaultValueAllowed(), ' - query NullableValues($a: Int, $b: String, $c: ComplexInput) { - dog { name } - } - '); - } - - /** - * @see it('required variables without default values') - */ - public function testRequiredVariablesWithoutDefaultValues() : void - { - $this->expectPassesRule(new VariablesDefaultValueAllowed(), ' - query RequiredValues($a: Int!, $b: String!) { - dog { name } - } - '); - } - - /** - * @see it('variables with valid default values') - */ - public function testVariablesWithValidDefaultValues() : void - { - $this->expectPassesRule(new VariablesDefaultValueAllowed(), ' - query WithDefaultValues( - $a: Int = 1, - $b: String = "ok", - $c: ComplexInput = { requiredField: true, intField: 3 } - ) { - dog { name } - } - '); - } - - /** - * @see it('variables with valid default null values') - */ - public function testVariablesWithValidDefaultNullValues() : void - { - $this->expectPassesRule(new VariablesDefaultValueAllowed(), ' - query WithDefaultValues( - $a: Int = null, - $b: String = null, - $c: ComplexInput = { requiredField: true, intField: null } - ) { - dog { name } - } - '); - } - - /** - * @see it('no required variables with default values') - */ - public function testNoRequiredVariablesWithDefaultValues() : void - { - $this->expectFailsRule(new VariablesDefaultValueAllowed(), ' - query UnreachableDefaultValues($a: Int! = 3, $b: String! = "default") { - dog { name } - } - ', [ - $this->defaultForRequiredVar('a', 'Int!', 'Int', 2, 49), - $this->defaultForRequiredVar('b', 'String!', 'String', 2, 66), - ]); - } - /** * @see it('variables with invalid default null values') */ public function testNullIntoNullableType() : void { - $this->expectFailsRule(new VariablesDefaultValueAllowed(), ' + $this->expectFailsRule( + new VariablesDefaultValueAllowed(), + ' query WithDefaultValues($a: Int! = null, $b: String! = null) { dog { name } } - ', [ - $this->defaultForRequiredVar('a', 'Int!', 'Int', 2, 42), - $this->defaultForRequiredVar('b', 'String!', 'String', 2, 62), - ]); + ', + [ + $this->defaultForRequiredVar('a', 'Int!', 'Int', 2, 42), + $this->defaultForRequiredVar('b', 'String!', 'String', 2, 62), + ] + ); } } diff --git a/tests/Validator/VariablesInAllowedPositionTest.php b/tests/Validator/VariablesInAllowedPositionTest.php index fa8a69a..b1a1edd 100644 --- a/tests/Validator/VariablesInAllowedPositionTest.php +++ b/tests/Validator/VariablesInAllowedPositionTest.php @@ -1,4 +1,7 @@ Boolean') */ public function testBooleanXBoolean() : void { // Boolean => Boolean - $this->expectPassesRule(new VariablesInAllowedPosition(), ' + $this->expectPassesRule( + new VariablesInAllowedPosition(), + ' query Query($booleanArg: Boolean) { complicatedArgs { booleanArgField(booleanArg: $booleanArg) } } - '); + ' + ); } /** @@ -31,7 +36,9 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase public function testBooleanXBooleanWithinFragment() : void { // Boolean => Boolean within fragment - $this->expectPassesRule(new VariablesInAllowedPosition, ' + $this->expectPassesRule( + new VariablesInAllowedPosition(), + ' fragment booleanArgFrag on ComplicatedArgs { booleanArgField(booleanArg: $booleanArg) } @@ -41,9 +48,12 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase ...booleanArgFrag } } - '); + ' + ); - $this->expectPassesRule(new VariablesInAllowedPosition, ' + $this->expectPassesRule( + new VariablesInAllowedPosition(), + ' query Query($booleanArg: Boolean) { complicatedArgs { @@ -53,7 +63,8 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase fragment booleanArgFrag on ComplicatedArgs { booleanArgField(booleanArg: $booleanArg) } - '); + ' + ); } /** @@ -62,14 +73,17 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase public function testBooleanNonNullXBoolean() : void { // Boolean! => Boolean - $this->expectPassesRule(new VariablesInAllowedPosition, ' + $this->expectPassesRule( + new VariablesInAllowedPosition(), + ' query Query($nonNullBooleanArg: Boolean!) { complicatedArgs { booleanArgField(booleanArg: $nonNullBooleanArg) } } - '); + ' + ); } /** @@ -78,7 +92,9 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase public function testBooleanNonNullXBooleanWithinFragment() : void { // Boolean! => Boolean within fragment - $this->expectPassesRule(new VariablesInAllowedPosition, ' + $this->expectPassesRule( + new VariablesInAllowedPosition(), + ' fragment booleanArgFrag on ComplicatedArgs { booleanArgField(booleanArg: $nonNullBooleanArg) } @@ -89,7 +105,8 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase ...booleanArgFrag } } - '); + ' + ); } /** @@ -98,14 +115,17 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase public function testIntXIntNonNullWithDefault() : void { // Int => Int! with default - $this->expectPassesRule(new VariablesInAllowedPosition, ' + $this->expectPassesRule( + new VariablesInAllowedPosition(), + ' query Query($intArg: Int = 1) { complicatedArgs { nonNullIntArgField(nonNullIntArg: $intArg) } } - '); + ' + ); } /** @@ -113,14 +133,17 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase */ public function testListOfStringXListOfString() : void { - $this->expectPassesRule(new VariablesInAllowedPosition, ' + $this->expectPassesRule( + new VariablesInAllowedPosition(), + ' query Query($stringListVar: [String]) { complicatedArgs { stringListArgField(stringListArg: $stringListVar) } } - '); + ' + ); } /** @@ -128,14 +151,17 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase */ public function testListOfStringNonNullXListOfString() : void { - $this->expectPassesRule(new VariablesInAllowedPosition, ' + $this->expectPassesRule( + new VariablesInAllowedPosition(), + ' query Query($stringListVar: [String!]) { complicatedArgs { stringListArgField(stringListArg: $stringListVar) } } - '); + ' + ); } /** @@ -143,14 +169,17 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase */ public function testStringXListOfStringInItemPosition() : void { - $this->expectPassesRule(new VariablesInAllowedPosition, ' + $this->expectPassesRule( + new VariablesInAllowedPosition(), + ' query Query($stringVar: String) { complicatedArgs { stringListArgField(stringListArg: [$stringVar]) } } - '); + ' + ); } /** @@ -158,14 +187,17 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase */ public function testStringNonNullXListOfStringInItemPosition() : void { - $this->expectPassesRule(new VariablesInAllowedPosition, ' + $this->expectPassesRule( + new VariablesInAllowedPosition(), + ' query Query($stringVar: String!) { complicatedArgs { stringListArgField(stringListArg: [$stringVar]) } } - '); + ' + ); } /** @@ -173,14 +205,17 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase */ public function testComplexInputXComplexInput() : void { - $this->expectPassesRule(new VariablesInAllowedPosition, ' + $this->expectPassesRule( + new VariablesInAllowedPosition(), + ' query Query($complexVar: ComplexInput) { complicatedArgs { complexArgField(complexArg: $ComplexInput) } } - '); + ' + ); } /** @@ -188,14 +223,17 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase */ public function testComplexInputXComplexInputInFieldPosition() : void { - $this->expectPassesRule(new VariablesInAllowedPosition, ' + $this->expectPassesRule( + new VariablesInAllowedPosition(), + ' query Query($boolVar: Boolean = false) { complicatedArgs { complexArgField(complexArg: {requiredArg: $boolVar}) } } - '); + ' + ); } /** @@ -203,12 +241,15 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase */ public function testBooleanNonNullXBooleanNonNullInDirective() : void { - $this->expectPassesRule(new VariablesInAllowedPosition, ' + $this->expectPassesRule( + new VariablesInAllowedPosition(), + ' query Query($boolVar: Boolean!) { dog @include(if: $boolVar) } - '); + ' + ); } /** @@ -216,12 +257,15 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase */ public function testBooleanXBooleanNonNullInDirectiveWithDefault() : void { - $this->expectPassesRule(new VariablesInAllowedPosition, ' + $this->expectPassesRule( + new VariablesInAllowedPosition(), + ' query Query($boolVar: Boolean = false) { dog @include(if: $boolVar) } - '); + ' + ); } /** @@ -229,18 +273,22 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase */ public function testIntXIntNonNull() : void { - $this->expectFailsRule(new VariablesInAllowedPosition, ' + $this->expectFailsRule( + new VariablesInAllowedPosition(), + ' query Query($intArg: Int) { complicatedArgs { nonNullIntArgField(nonNullIntArg: $intArg) } } - ', [ - FormattedError::create( - VariablesInAllowedPosition::badVarPosMessage('intArg', 'Int', 'Int!'), - [new SourceLocation(2, 19), new SourceLocation(4, 45)] - ) - ]); + ', + [ + FormattedError::create( + VariablesInAllowedPosition::badVarPosMessage('intArg', 'Int', 'Int!'), + [new SourceLocation(2, 19), new SourceLocation(4, 45)] + ), + ] + ); } /** @@ -248,7 +296,9 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase */ public function testIntXIntNonNullWithinFragment() : void { - $this->expectFailsRule(new VariablesInAllowedPosition, ' + $this->expectFailsRule( + new VariablesInAllowedPosition(), + ' fragment nonNullIntArgFieldFrag on ComplicatedArgs { nonNullIntArgField(nonNullIntArg: $intArg) } @@ -258,12 +308,14 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase ...nonNullIntArgFieldFrag } } - ', [ - FormattedError::create( - VariablesInAllowedPosition::badVarPosMessage('intArg', 'Int', 'Int!'), - [new SourceLocation(6, 19), new SourceLocation(3, 43)] - ) - ]); + ', + [ + FormattedError::create( + VariablesInAllowedPosition::badVarPosMessage('intArg', 'Int', 'Int!'), + [new SourceLocation(6, 19), new SourceLocation(3, 43)] + ), + ] + ); } /** @@ -272,7 +324,9 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase public function testIntXIntNonNullWithinNestedFragment() : void { // Int => Int! within nested fragment - $this->expectFailsRule(new VariablesInAllowedPosition, ' + $this->expectFailsRule( + new VariablesInAllowedPosition(), + ' fragment outerFrag on ComplicatedArgs { ...nonNullIntArgFieldFrag } @@ -287,12 +341,14 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase ...outerFrag } } - ', [ - FormattedError::create( - VariablesInAllowedPosition::badVarPosMessage('intArg', 'Int', 'Int!'), - [new SourceLocation(10, 19), new SourceLocation(7,43)] - ) - ]); + ', + [ + FormattedError::create( + VariablesInAllowedPosition::badVarPosMessage('intArg', 'Int', 'Int!'), + [new SourceLocation(10, 19), new SourceLocation(7, 43)] + ), + ] + ); } /** @@ -300,18 +356,22 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase */ public function testStringOverBoolean() : void { - $this->expectFailsRule(new VariablesInAllowedPosition, ' + $this->expectFailsRule( + new VariablesInAllowedPosition(), + ' query Query($stringVar: String) { complicatedArgs { booleanArgField(booleanArg: $stringVar) } } - ', [ - FormattedError::create( - VariablesInAllowedPosition::badVarPosMessage('stringVar', 'String', 'Boolean'), - [new SourceLocation(2,19), new SourceLocation(4,39)] - ) - ]); + ', + [ + FormattedError::create( + VariablesInAllowedPosition::badVarPosMessage('stringVar', 'String', 'Boolean'), + [new SourceLocation(2, 19), new SourceLocation(4, 39)] + ), + ] + ); } /** @@ -319,18 +379,22 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase */ public function testStringXListOfString() : void { - $this->expectFailsRule(new VariablesInAllowedPosition, ' + $this->expectFailsRule( + new VariablesInAllowedPosition(), + ' query Query($stringVar: String) { complicatedArgs { stringListArgField(stringListArg: $stringVar) } } - ', [ - FormattedError::create( - VariablesInAllowedPosition::badVarPosMessage('stringVar', 'String', '[String]'), - [new SourceLocation(2, 19), new SourceLocation(4,45)] - ) - ]); + ', + [ + FormattedError::create( + VariablesInAllowedPosition::badVarPosMessage('stringVar', 'String', '[String]'), + [new SourceLocation(2, 19), new SourceLocation(4, 45)] + ), + ] + ); } /** @@ -338,16 +402,20 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase */ public function testBooleanXBooleanNonNullInDirective() : void { - $this->expectFailsRule(new VariablesInAllowedPosition, ' + $this->expectFailsRule( + new VariablesInAllowedPosition(), + ' query Query($boolVar: Boolean) { dog @include(if: $boolVar) } - ', [ - FormattedError::create( - VariablesInAllowedPosition::badVarPosMessage('boolVar', 'Boolean', 'Boolean!'), - [new SourceLocation(2, 19), new SourceLocation(3,26)] - ) - ]); + ', + [ + FormattedError::create( + VariablesInAllowedPosition::badVarPosMessage('boolVar', 'Boolean', 'Boolean!'), + [new SourceLocation(2, 19), new SourceLocation(3, 26)] + ), + ] + ); } /** @@ -356,16 +424,20 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase public function testStringXBooleanNonNullInDirective() : void { // String => Boolean! in directive - $this->expectFailsRule(new VariablesInAllowedPosition, ' + $this->expectFailsRule( + new VariablesInAllowedPosition(), + ' query Query($stringVar: String) { dog @include(if: $stringVar) } - ', [ - FormattedError::create( - VariablesInAllowedPosition::badVarPosMessage('stringVar', 'String', 'Boolean!'), - [new SourceLocation(2, 19), new SourceLocation(3,26)] - ) - ]); + ', + [ + FormattedError::create( + VariablesInAllowedPosition::badVarPosMessage('stringVar', 'String', 'Boolean!'), + [new SourceLocation(2, 19), new SourceLocation(3, 26)] + ), + ] + ); } /** @@ -374,7 +446,7 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase public function testStringArrayXStringNonNullArray() : void { $this->expectFailsRule( - new VariablesInAllowedPosition, + new VariablesInAllowedPosition(), ' query Query($stringListVar: [String]) { @@ -382,11 +454,12 @@ class VariablesInAllowedPositionTest extends ValidatorTestCase stringListNonNullArgField(stringListNonNullArg: $stringListVar) } } - ', [ + ', + [ FormattedError::create( VariablesInAllowedPosition::badVarPosMessage('stringListVar', '[String]', '[String!]'), [new SourceLocation(2, 19), new SourceLocation(5, 59)] - ) + ), ] ); }