diff --git a/src/Type/Introspection.php b/src/Type/Introspection.php index 7ed12fc..d670712 100644 --- a/src/Type/Introspection.php +++ b/src/Type/Introspection.php @@ -2,6 +2,7 @@ namespace GraphQL\Type; +use GraphQL\Language\Printer; use GraphQL\Schema; use GraphQL\Type\Definition\Directive; use GraphQL\Type\Definition\EnumType; @@ -16,6 +17,7 @@ use GraphQL\Type\Definition\ScalarType; use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\UnionType; use GraphQL\Type\Definition\WrappingType; +use GraphQL\Utils\AST; class TypeKind { const SCALAR = 0; @@ -42,18 +44,17 @@ class Introspection __schema { queryType { name } mutationType { name } + subscriptionType { name } types { ...FullType } directives { name description + locations args { ...InputValue } - onOperation - onFragment - onField } } } @@ -62,7 +63,7 @@ class Introspection kind name description - fields { + fields(includeDeprecated: true) { name description args { @@ -80,7 +81,7 @@ class Introspection interfaces { ...TypeRef } - enumValues { + enumValues(includeDeprecated: true) { name description isDeprecated @@ -120,17 +121,17 @@ EOD; __schema { queryType { name } mutationType { name } + subscriptionType { name } types { ...FullType } directives { name + + locations args { ...InputValue } - onOperation - onFragment - onField } } } @@ -138,8 +139,10 @@ EOD; fragment FullType on __Type { kind name - fields { + + fields(includeDeprecated: true) { name + args { ...InputValue } @@ -155,8 +158,9 @@ EOD; interfaces { ...TypeRef } - enumValues { + enumValues(includeDeprecated: true) { name + isDeprecated deprecationReason } @@ -167,6 +171,7 @@ EOD; fragment InputValue on __InputValue { name + type { ...TypeRef } defaultValue } @@ -250,130 +255,223 @@ EOD; if (!isset(self::$_map['__Directive'])) { self::$_map['__Directive'] = new ObjectType([ 'name' => '__Directive', + 'description' => 'A Directive provides a way to describe alternate runtime execution and ' . + 'type validation behavior in a GraphQL document.' . + '\n\nIn some cases, you need to provide options to alter GraphQL’s ' . + 'execution behavior in ways field arguments will not suffice, such as ' . + 'conditionally including or skipping a field. Directives provide this by ' . + 'describing additional information to the executor.', 'fields' => [ 'name' => ['type' => Type::nonNull(Type::string())], 'description' => ['type' => Type::string()], + 'locations' => [ + 'type' => Type::nonNull(Type::listOf(Type::nonNull( + self::_directiveLocation() + ))) + ], 'args' => [ 'type' => Type::nonNull(Type::listOf(Type::nonNull(self::_inputValue()))), - 'resolve' => function(Directive $directive) {return $directive->args ?: [];} + 'resolve' => function (Directive $directive) { + return $directive->args ?: []; + } ], - 'onOperation' => ['type' => Type::nonNull(Type::boolean())], - 'onFragment' => ['type' => Type::nonNull(Type::boolean())], - 'onField' => ['type' => Type::nonNull(Type::boolean())] + + // NOTE: the following three fields are deprecated and are no longer part + // of the GraphQL specification. + 'onOperation' => [ + 'deprecationReason' => 'Use `locations`.', + 'type' => Type::nonNull(Type::boolean()), + 'resolve' => function($d) { + return in_array(Directive::$directiveLocations['QUERY'], $d['locations']) || + in_array(Directive::$directiveLocations['MUTATION'], $d['locations']) || + in_array(Directive::$directiveLocations['SUBSCRIPTION'], $d['locations']); + } + ], + 'onFragment' => [ + 'deprecationReason' => 'Use `locations`.', + 'type' => Type::nonNull(Type::boolean()), + 'resolve' => function($d) { + return in_array(Directive::$directiveLocations['FRAGMENT_SPREAD'], $d['locations']) || + in_array(Directive::$directiveLocations['INLINE_FRAGMENT'], $d['locations']) || + in_array(Directive::$directiveLocations['FRAGMENT_DEFINITION'], $d['locations']); + } + ], + 'onField' => [ + 'deprecationReason' => 'Use `locations`.', + 'type' => Type::nonNull(Type::boolean()), + 'resolve' => function($d) { + return in_array(Directive::$directiveLocations['FIELD'], $d['locations']); + } + ] ] ]); } return self::$_map['__Directive']; } + public static function _directiveLocation() + { + if (!isset(self::$_map['__DirectiveLocation'])) { + self::$_map['__DirectiveLocation'] = new EnumType([ + 'name' => '__DirectiveLocation', + 'description' => + 'A Directive can be adjacent to many parts of the GraphQL language, a ' . + '__DirectiveLocation describes one such possible adjacencies.', + 'values' => [ + 'QUERY' => [ + 'value' => Directive::$directiveLocations['QUERY'], + 'description' => 'Location adjacent to a query operation.' + ], + 'MUTATION' => [ + 'value' => Directive::$directiveLocations['MUTATION'], + 'description' => 'Location adjacent to a mutation operation.' + ], + 'SUBSCRIPTION' => [ + 'value' => Directive::$directiveLocations['SUBSCRIPTION'], + 'description' => 'Location adjacent to a subscription operation.' + ], + 'FIELD' => [ + 'value' => Directive::$directiveLocations['FIELD'], + 'description' => 'Location adjacent to a field.' + ], + 'FRAGMENT_DEFINITION' => [ + 'value' => Directive::$directiveLocations['FRAGMENT_DEFINITION'], + 'description' => 'Location adjacent to a fragment definition.' + ], + 'FRAGMENT_SPREAD' => [ + 'value' => Directive::$directiveLocations['FRAGMENT_SPREAD'], + 'description' => 'Location adjacent to a fragment spread.' + ], + 'INLINE_FRAGMENT' => [ + 'value' => Directive::$directiveLocations['INLINE_FRAGMENT'], + 'description' => 'Location adjacent to an inline fragment.' + ], + ] + ]); + } + return self::$_map['__DirectiveLocation']; + } + public static function _type() { if (!isset(self::$_map['__Type'])) { self::$_map['__Type'] = new ObjectType([ 'name' => '__Type', - 'fields' => [ - 'kind' => [ - 'type' => Type::nonNull(self::_typeKind()), - 'resolve' => function (Type $type) { - switch (true) { - case $type instanceof ListOfType: - return TypeKind::LIST_KIND; - case $type instanceof NonNull: - return TypeKind::NON_NULL; - case $type instanceof ScalarType: - return TypeKind::SCALAR; - case $type instanceof ObjectType: - return TypeKind::OBJECT; - case $type instanceof EnumType: - return TypeKind::ENUM; - case $type instanceof InputObjectType: - return TypeKind::INPUT_OBJECT; - case $type instanceof InterfaceType: - return TypeKind::INTERFACE_KIND; - case $type instanceof UnionType: - return TypeKind::UNION; - default: - throw new \Exception("Unknown kind of type: " . print_r($type, true)); - } - } - ], - 'name' => ['type' => Type::string()], - 'description' => ['type' => Type::string()], - 'fields' => [ - 'type' => Type::listOf(Type::nonNull(self::_field())), - 'args' => [ - 'includeDeprecated' => ['type' => Type::boolean(), 'defaultValue' => false] - ], - 'resolve' => function (Type $type, $args) { - if ($type instanceof ObjectType || $type instanceof InterfaceType) { - $fields = $type->getFields(); - - if (empty($args['includeDeprecated'])) { - $fields = array_filter($fields, function (FieldDefinition $field) { - return !$field->deprecationReason; - }); + 'description' => + 'The fundamental unit of any GraphQL Schema is the type. There are ' . + 'many kinds of types in GraphQL as represented by the `__TypeKind` enum.' . + "\n\n". + 'Depending on the kind of a type, certain fields describe ' . + 'information about that type. Scalar types provide no information ' . + 'beyond a name and description, while Enum types provide their values. ' . + 'Object and Interface types provide the fields they describe. Abstract ' . + 'types, Union and Interface, provide the Object types possible ' . + 'at runtime. List and NonNull types compose other types.', + 'fields' => function() { + return [ + 'kind' => [ + 'type' => Type::nonNull(self::_typeKind()), + 'resolve' => function (Type $type) { + switch (true) { + case $type instanceof ListOfType: + return TypeKind::LIST_KIND; + case $type instanceof NonNull: + return TypeKind::NON_NULL; + case $type instanceof ScalarType: + return TypeKind::SCALAR; + case $type instanceof ObjectType: + return TypeKind::OBJECT; + case $type instanceof EnumType: + return TypeKind::ENUM; + case $type instanceof InputObjectType: + return TypeKind::INPUT_OBJECT; + case $type instanceof InterfaceType: + return TypeKind::INTERFACE_KIND; + case $type instanceof UnionType: + return TypeKind::UNION; + default: + throw new \Exception("Unknown kind of type: " . print_r($type, true)); } - return array_values($fields); } - return null; - } - ], - 'interfaces' => [ - 'type' => Type::listOf(Type::nonNull([__CLASS__, '_type'])), - 'resolve' => function ($type) { - if ($type instanceof ObjectType) { - return $type->getInterfaces(); - } - return null; - } - ], - 'possibleTypes' => [ - 'type' => Type::listOf(Type::nonNull([__CLASS__, '_type'])), - 'resolve' => function ($type) { - if ($type instanceof InterfaceType || $type instanceof UnionType) { - return $type->getPossibleTypes(); - } - return null; - } - ], - 'enumValues' => [ - 'type' => Type::listOf(Type::nonNull(self::_enumValue())), - 'args' => [ - 'includeDeprecated' => ['type' => Type::boolean(), 'defaultValue' => false] ], - 'resolve' => function ($type, $args) { - if ($type instanceof EnumType) { - $values = array_values($type->getValues()); + 'name' => ['type' => Type::string()], + 'description' => ['type' => Type::string()], + 'fields' => [ + 'type' => Type::listOf(Type::nonNull(self::_field())), + 'args' => [ + 'includeDeprecated' => ['type' => Type::boolean(), 'defaultValue' => false] + ], + 'resolve' => function (Type $type, $args) { + if ($type instanceof ObjectType || $type instanceof InterfaceType) { + $fields = $type->getFields(); - if (empty($args['includeDeprecated'])) { - $values = array_filter($values, function ($value) { - return !$value->deprecationReason; - }); + if (empty($args['includeDeprecated'])) { + $fields = array_filter($fields, function (FieldDefinition $field) { + return !$field->deprecationReason; + }); + } + return array_values($fields); } + return null; + } + ], + 'interfaces' => [ + 'type' => Type::listOf(Type::nonNull(self::_type())), + 'resolve' => function ($type) { + if ($type instanceof ObjectType) { + return $type->getInterfaces(); + } + return null; + } + ], + 'possibleTypes' => [ + 'type' => Type::listOf(Type::nonNull([__CLASS__, '_type'])), + 'resolve' => function ($type, $args, ResolveInfo $info) { + if ($type instanceof InterfaceType || $type instanceof UnionType) { + return $info->schema->getPossibleTypes($type); + } + return null; + } + ], + 'enumValues' => [ + 'type' => Type::listOf(Type::nonNull(self::_enumValue())), + 'args' => [ + 'includeDeprecated' => ['type' => Type::boolean(), 'defaultValue' => false] + ], + 'resolve' => function ($type, $args) { + if ($type instanceof EnumType) { + $values = array_values($type->getValues()); - return $values; + if (empty($args['includeDeprecated'])) { + $values = array_filter($values, function ($value) { + return !$value->deprecationReason; + }); + } + + return $values; + } + return null; } - return null; - } - ], - 'inputFields' => [ - 'type' => Type::listOf(Type::nonNull(self::_inputValue())), - 'resolve' => function ($type) { - if ($type instanceof InputObjectType) { - return array_values($type->getFields()); + ], + 'inputFields' => [ + 'type' => Type::listOf(Type::nonNull(self::_inputValue())), + 'resolve' => function ($type) { + if ($type instanceof InputObjectType) { + return array_values($type->getFields()); + } + return null; } - return null; - } - ], - 'ofType' => [ - 'type' => [__CLASS__, '_type'], - 'resolve' => function($type) { - if ($type instanceof WrappingType) { - return $type->getWrappedType(); - } - return null; - }] - ] + ], + 'ofType' => [ + 'type' => self::_type(), + 'resolve' => function ($type) { + if ($type instanceof WrappingType) { + return $type->getWrappedType(); + } + return null; + }] + ]; + } ]); } return self::$_map['__Type']; @@ -385,31 +483,36 @@ EOD; self::$_map['__Field'] = new ObjectType([ 'name' => '__Field', - 'fields' => [ - 'name' => ['type' => Type::nonNull(Type::string())], - 'description' => ['type' => Type::string()], - 'args' => [ - 'type' => Type::nonNull(Type::listOf(Type::nonNull(self::_inputValue()))), - 'resolve' => function (FieldDefinition $field) { - return empty($field->args) ? [] : $field->args; - } - ], - 'type' => [ - 'type' => Type::nonNull([__CLASS__, '_type']), - 'resolve' => function ($field) { - return $field->getType(); - } - ], - 'isDeprecated' => [ - 'type' => Type::nonNull(Type::boolean()), - 'resolve' => function (FieldDefinition $field) { - return !!$field->deprecationReason; - } - ], - 'deprecationReason' => [ - 'type' => Type::string() - ] - ] + 'description' => + 'Object and Interface types are described by a list of Fields, each of ' . + 'which has a name, potentially a list of arguments, and a return type.', + 'fields' => function() { + return [ + 'name' => ['type' => Type::nonNull(Type::string())], + 'description' => ['type' => Type::string()], + 'args' => [ + 'type' => Type::nonNull(Type::listOf(Type::nonNull(self::_inputValue()))), + 'resolve' => function (FieldDefinition $field) { + return empty($field->args) ? [] : $field->args; + } + ], + 'type' => [ + 'type' => Type::nonNull(self::_type()), + 'resolve' => function ($field) { + return $field->getType(); + } + ], + 'isDeprecated' => [ + 'type' => Type::nonNull(Type::boolean()), + 'resolve' => function (FieldDefinition $field) { + return !!$field->deprecationReason; + } + ], + 'deprecationReason' => [ + 'type' => Type::string() + ] + ]; + } ]); } return self::$_map['__Field']; @@ -420,22 +523,30 @@ EOD; if (!isset(self::$_map['__InputValue'])) { self::$_map['__InputValue'] = new ObjectType([ 'name' => '__InputValue', - 'fields' => [ - 'name' => ['type' => Type::nonNull(Type::string())], - 'description' => ['type' => Type::string()], - 'type' => [ - 'type' => Type::nonNull([__CLASS__, '_type']), - 'resolve' => function($value) { - return method_exists($value, 'getType') ? $value->getType() : $value->type; - } - ], - 'defaultValue' => [ - 'type' => Type::string(), - 'resolve' => function ($inputValue) { - return $inputValue->defaultValue === null ? null : json_encode($inputValue->defaultValue); - } - ] - ] + 'description' => + 'Arguments provided to Fields or Directives and the input fields of an ' . + 'InputObject are represented as Input Values which describe their type ' . + 'and optionally a default value.', + 'fields' => function() { + return [ + 'name' => ['type' => Type::nonNull(Type::string())], + 'description' => ['type' => Type::string()], + 'type' => [ + 'type' => Type::nonNull(self::_type()), + 'resolve' => function ($value) { + return method_exists($value, 'getType') ? $value->getType() : $value->type; + } + ], + 'defaultValue' => [ + 'type' => Type::string(), + 'resolve' => function ($inputValue) { + return $inputValue->defaultValue === null + ? null + : Printer::doPrint(AST::astFromValue($inputValue->defaultValue, $inputValue->getType())); + } + ] + ]; + } ]); } return self::$_map['__InputValue']; @@ -446,6 +557,10 @@ EOD; if (!isset(self::$_map['__EnumValue'])) { self::$_map['__EnumValue'] = new ObjectType([ 'name' => '__EnumValue', + 'description' => + 'One possible value for a given Enum. Enum values are unique values, not ' . + 'a placeholder for a string or numeric value. However an Enum value is ' . + 'returned in a JSON response as a string.', 'fields' => [ 'name' => ['type' => Type::nonNull(Type::string())], 'description' => ['type' => Type::string()], @@ -469,7 +584,7 @@ EOD; if (!isset(self::$_map['__TypeKind'])) { self::$_map['__TypeKind'] = new EnumType([ 'name' => '__TypeKind', - 'description' => 'An enum describing what kind of type a given __Type is', + 'description' => 'An enum describing what kind of type a given __Type is.', 'values' => [ 'SCALAR' => [ 'value' => TypeKind::SCALAR, diff --git a/tests/Type/IntrospectionTest.php b/tests/Type/IntrospectionTest.php index 76e03ea..e9a9684 100644 --- a/tests/Type/IntrospectionTest.php +++ b/tests/Type/IntrospectionTest.php @@ -14,12 +14,19 @@ use GraphQL\Validator\Rules\ProvidedNonNullArguments; class IntrospectionTest extends \PHPUnit_Framework_TestCase { + // Describe: Introspection + + /** + * @it executes an introspection query + */ function testExecutesAnIntrospectionQuery() { - $emptySchema = new Schema(new ObjectType([ - 'name' => 'QueryRoot', - 'fields' => [] - ])); + $emptySchema = new Schema([ + 'query' => new ObjectType([ + 'name' => 'QueryRoot', + 'fields' => [] + ]) + ]); $request = Introspection::getIntrospectionQuery(false); $expected = array ( @@ -28,6 +35,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase '__schema' => array ( 'mutationType' => NULL, + 'subscriptionType' => NULL, 'queryType' => array ( 'name' => 'QueryRoot', @@ -44,7 +52,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase ), 'enumValues' => NULL, 'possibleTypes' => NULL, - 'fields' => [] + 'fields' => Array () ), 1 => array ( @@ -117,13 +125,13 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase 'deprecationReason' => NULL, ), 3 => - array( + array ( 'name' => 'subscriptionType', 'args' => - array( + array ( ), 'type' => - array( + array ( 'kind' => 'OBJECT', 'name' => '__Type', 'ofType' => NULL, @@ -817,6 +825,35 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase 'deprecationReason' => NULL, ), 2 => + array ( + 'name' => 'locations', + 'args' => + array ( + ), + 'type' => + array ( + 'kind' => 'NON_NULL', + 'name' => NULL, + 'ofType' => + array ( + 'kind' => 'LIST', + 'name' => NULL, + 'ofType' => + array ( + 'kind' => 'NON_NULL', + 'name' => NULL, + 'ofType' => + array ( + 'kind' => 'ENUM', + 'name' => '__DirectiveLocation', + ), + ), + ), + ), + 'isDeprecated' => false, + 'deprecationReason' => NULL, + ), + 3 => array ( 'name' => 'args', 'args' => @@ -845,7 +882,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase 'isDeprecated' => false, 'deprecationReason' => NULL, ), - 3 => + 4 => array ( 'name' => 'onOperation', 'args' => @@ -862,10 +899,10 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase 'ofType' => NULL, ), ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, + 'isDeprecated' => true, + 'deprecationReason' => 'Use `locations`.', ), - 4 => + 5 => array ( 'name' => 'onFragment', 'args' => @@ -882,10 +919,10 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase 'ofType' => NULL, ), ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, + 'isDeprecated' => true, + 'deprecationReason' => 'Use `locations`.', ), - 5 => + 6 => array ( 'name' => 'onField', 'args' => @@ -902,8 +939,8 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase 'ofType' => NULL, ), ), - 'isDeprecated' => false, - 'deprecationReason' => NULL, + 'isDeprecated' => true, + 'deprecationReason' => 'Use `locations`.', ), ), 'inputFields' => NULL, @@ -913,39 +950,99 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase 'enumValues' => NULL, 'possibleTypes' => NULL, ), - 10 => [ + 10 => + array ( + 'kind' => 'ENUM', + 'name' => '__DirectiveLocation', + 'fields' => NULL, + 'inputFields' => NULL, + 'interfaces' => NULL, + 'enumValues' => + array ( + 0 => + array ( + 'name' => 'QUERY', + 'isDeprecated' => false, + 'deprecationReason' => null + ), + 1 => + array ( + 'name' => 'MUTATION', + 'isDeprecated' => false, + 'deprecationReason' => null + ), + 2 => + array ( + 'name' => 'SUBSCRIPTION', + 'isDeprecated' => false, + 'deprecationReason' => null + ), + 3 => + array ( + 'name' => 'FIELD', + 'isDeprecated' => false, + 'deprecationReason' => null + ), + 4 => + array ( + 'name' => 'FRAGMENT_DEFINITION', + 'isDeprecated' => false, + 'deprecationReason' => null + ), + 5 => + array ( + 'name' => 'FRAGMENT_SPREAD', + 'isDeprecated' => false, + 'deprecationReason' => null + ), + 6 => + array ( + 'name' => 'INLINE_FRAGMENT', + 'isDeprecated' => false, + 'deprecationReason' => null + ), + ), + 'possibleTypes' => NULL, + ), + 11 => array ( 'kind' => 'SCALAR', 'name' => 'ID', - 'fields' => null, - 'inputFields' => null, - 'interfaces' => null, - 'enumValues' => null, - 'possibleTypes' => null - ], - 11 => [ + 'fields' => NULL, + 'inputFields' => NULL, + 'interfaces' => NULL, + 'enumValues' => NULL, + 'possibleTypes' => NULL, + ), + 12 => array ( 'kind' => 'SCALAR', 'name' => 'Float', - 'fields' => null, - 'inputFields' => null, - 'interfaces' => null, - 'enumValues' => null, - 'possibleTypes' => null - ], - 12 => [ + 'fields' => NULL, + 'inputFields' => NULL, + 'interfaces' => NULL, + 'enumValues' => NULL, + 'possibleTypes' => NULL, + ), + 13 => array ( 'kind' => 'SCALAR', 'name' => 'Int', - 'fields' => null, - 'inputFields' => null, - 'interfaces' => null, - 'enumValues' => null, - 'possibleTypes' => null - ], + 'fields' => NULL, + 'inputFields' => NULL, + 'interfaces' => NULL, + 'enumValues' => NULL, + 'possibleTypes' => NULL, + ) ), 'directives' => array ( 0 => array ( 'name' => 'include', + 'locations' => + array ( + 0 => 'FIELD', + 1 => 'FRAGMENT_SPREAD', + 2 => 'INLINE_FRAGMENT', + ), 'args' => array ( 0 => @@ -965,13 +1062,16 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase ), ), ), - 'onOperation' => false, - 'onFragment' => true, - 'onField' => true, ), 1 => array ( 'name' => 'skip', + 'locations' => + array ( + 0 => 'FIELD', + 1 => 'FRAGMENT_SPREAD', + 2 => 'INLINE_FRAGMENT', + ), 'args' => array ( 0 => @@ -991,20 +1091,23 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase ), ), ), - 'onOperation' => false, - 'onFragment' => true, - 'onField' => true, ), ), ), - ), + ) ); $actual = GraphQL::execute($emptySchema, $request); + // print_r($actual); + // exit; + $this->assertEquals($expected, $actual); } + /** + * @it introspects on input object + */ function testIntrospectsOnInputObject() { $TestInputObject = new InputObjectType([ @@ -1028,7 +1131,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase ] ]); - $schema = new Schema($TestType); + $schema = new Schema(['query' => $TestType]); $request = ' { __schema { @@ -1088,6 +1191,9 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase $this->assertContains($expectedFragment, $result); } + /** + * @it supports the __type root field + */ public function testSupportsThe__typeRootField() { @@ -1100,7 +1206,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase ] ]); - $schema = new Schema($TestType); + $schema = new Schema(['query' => $TestType]); $request = ' { __type(name: "TestType") { @@ -1118,6 +1224,9 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expected, GraphQL::execute($schema, $request)); } + /** + * @it identifies deprecated fields + */ public function testIdentifiesDeprecatedFields() { @@ -1134,7 +1243,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase ] ]); - $schema = new Schema($TestType); + $schema = new Schema(['query' => $TestType]); $request = ' { __type(name: "TestType") { @@ -1170,6 +1279,9 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expected, GraphQL::execute($schema, $request)); } + /** + * @it respects the includeDeprecated parameter for fields + */ public function testRespectsTheIncludeDeprecatedParameterForFields() { $TestType = new ObjectType([ @@ -1185,7 +1297,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase ] ]); - $schema = new Schema($TestType); + $schema = new Schema(['query' => $TestType]); $request = ' { __type(name: "TestType") { @@ -1232,6 +1344,9 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expected, GraphQL::execute($schema, $request)); } + /** + * @it identifies deprecated enum values + */ public function testIdentifiesDeprecatedEnumValues() { $TestEnum = new EnumType([ @@ -1252,7 +1367,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase ] ]); - $schema = new Schema($TestType); + $schema = new Schema(['query' => $TestType]); $request = ' { __type(name: "TestEnum") { @@ -1293,6 +1408,9 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expected, GraphQL::execute($schema, $request)); } + /** + * @it respects the includeDeprecated parameter for enum values + */ public function testRespectsTheIncludeDeprecatedParameterForEnumValues() { $TestEnum = new EnumType([ @@ -1313,7 +1431,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase ] ]); - $schema = new Schema($TestType); + $schema = new Schema(['query' => $TestType]); $request = ' { __type(name: "TestEnum") { @@ -1353,9 +1471,11 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expected, GraphQL::execute($schema, $request)); } + /** + * @it fails as expected on the __type root field without an arg + */ public function testFailsAsExpectedOnThe__typeRootFieldWithoutAnArg() { - $TestType = new ObjectType([ 'name' => 'TestType', 'fields' => [ @@ -1365,7 +1485,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase ] ]); - $schema = new Schema($TestType); + $schema = new Schema(['query' => $TestType]); $request = ' { __type { @@ -1384,6 +1504,9 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expected, GraphQL::execute($schema, $request)); } + /** + * @it exposes descriptions on types and fields + */ public function testExposesDescriptionsOnTypesAndFields() { $QueryRoot = new ObjectType([ @@ -1391,7 +1514,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase 'fields' => [] ]); - $schema = new Schema($QueryRoot); + $schema = new Schema(['query' => $QueryRoot]); $request = ' { schemaType: __type(name: "__Schema") { @@ -1441,6 +1564,9 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expected, GraphQL::execute($schema, $request)); } + /** + * @it exposes descriptions on enums + */ public function testExposesDescriptionsOnEnums() { $QueryRoot = new ObjectType([ @@ -1448,7 +1574,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase 'fields' => [] ]); - $schema = new Schema($QueryRoot); + $schema = new Schema(['query' => $QueryRoot]); $request = ' { typeKindType: __type(name: "__TypeKind") { @@ -1465,7 +1591,7 @@ class IntrospectionTest extends \PHPUnit_Framework_TestCase 'data' => [ 'typeKindType' => [ 'name' => '__TypeKind', - 'description' => 'An enum describing what kind of type a given __Type is', + 'description' => 'An enum describing what kind of type a given __Type is.', 'enumValues' => [ [ 'description' => 'Indicates this type is a scalar.',