'Named', 'fields' => [ 'name' => ['type' => Type::string()] ] ]); $DogType = new ObjectType([ 'name' => 'Dog', 'interfaces' => [$NamedType], 'fields' => [ 'name' => ['type' => Type::string()], 'woofs' => ['type' => Type::boolean()] ], 'isTypeOf' => function ($value) { return $value instanceof Dog; } ]); $CatType = new ObjectType([ 'name' => 'Cat', 'interfaces' => [$NamedType], 'fields' => [ 'name' => ['type' => Type::string()], 'meows' => ['type' => Type::boolean()] ], 'isTypeOf' => function ($value) { return $value instanceof Cat; } ]); $PetType = new UnionType([ 'name' => 'Pet', 'types' => [$DogType, $CatType], 'resolveType' => function ($value) use ($DogType, $CatType) { if ($value instanceof Dog) { return $DogType; } if ($value instanceof Cat) { return $CatType; } } ]); $PersonType = new ObjectType([ 'name' => 'Person', 'interfaces' => [$NamedType], 'fields' => [ 'name' => ['type' => Type::string()], 'pets' => ['type' => Type::listOf($PetType)], 'friends' => ['type' => Type::listOf($NamedType)] ], 'isTypeOf' => function ($value) { return $value instanceof Person; } ]); $this->schema = new Schema([ 'query' => $PersonType, '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]); } // Execute: Union and intersection types /** * @it can introspect on union and intersection types */ public function testCanIntrospectOnUnionAndIntersectionTypes() { $ast = Parser::parse(' { Named: __type(name: "Named") { kind name fields { name } interfaces { name } possibleTypes { name } enumValues { name } inputFields { name } } Pet: __type(name: "Pet") { kind name fields { name } interfaces { name } possibleTypes { name } enumValues { name } inputFields { name } } } '); $expected = [ 'data' => [ 'Named' => [ 'kind' => 'INTERFACE', 'name' => 'Named', 'fields' => [ ['name' => 'name'] ], 'interfaces' => null, 'possibleTypes' => [ ['name' => 'Person'], ['name' => 'Dog'], ['name' => 'Cat'] ], 'enumValues' => null, 'inputFields' => null ], 'Pet' => [ 'kind' => 'UNION', 'name' => 'Pet', 'fields' => null, 'interfaces' => null, 'possibleTypes' => [ ['name' => 'Dog'], ['name' => 'Cat'] ], 'enumValues' => null, 'inputFields' => null ] ] ]; $this->assertEquals($expected, Executor::execute($this->schema, $ast)->toArray()); } /** * @it executes using union types */ public function testExecutesUsingUnionTypes() { // NOTE: This is an *invalid* query, but it should be an *executable* query. $ast = Parser::parse(' { __typename name pets { __typename name woofs meows } } '); $expected = [ 'data' => [ '__typename' => 'Person', 'name' => 'John', 'pets' => [ ['__typename' => 'Cat', 'name' => 'Garfield', 'meows' => false], ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true] ] ] ]; $this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray()); } /** * @it executes union types with inline fragments */ public function testExecutesUnionTypesWithInlineFragments() { // This is the valid version of the query in the above test. $ast = Parser::parse(' { __typename name pets { __typename ... on Dog { name woofs } ... on Cat { name meows } } } '); $expected = [ 'data' => [ '__typename' => 'Person', 'name' => 'John', 'pets' => [ ['__typename' => 'Cat', 'name' => 'Garfield', 'meows' => false], ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true] ] ] ]; $this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray()); } /** * @it executes using interface types */ public function testExecutesUsingInterfaceTypes() { // NOTE: This is an *invalid* query, but it should be an *executable* query. $ast = Parser::parse(' { __typename name friends { __typename name woofs meows } } '); $expected = [ 'data' => [ '__typename' => 'Person', 'name' => 'John', 'friends' => [ ['__typename' => 'Person', 'name' => 'Liz'], ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true] ] ] ]; Warning::suppress(Warning::WARNING_FULL_SCHEMA_SCAN); $this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray()); Warning::enable(Warning::WARNING_FULL_SCHEMA_SCAN); } /** * @it executes interface types with inline fragments */ public function testExecutesInterfaceTypesWithInlineFragments() { // This is the valid version of the query in the above test. $ast = Parser::parse(' { __typename name friends { __typename name ... on Dog { woofs } ... on Cat { meows } } } '); $expected = [ 'data' => [ '__typename' => 'Person', 'name' => 'John', 'friends' => [ ['__typename' => 'Person', 'name' => 'Liz'], ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true] ] ] ]; Warning::suppress(Warning::WARNING_FULL_SCHEMA_SCAN); $this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray()); Warning::enable(Warning::WARNING_FULL_SCHEMA_SCAN); } /** * @it allows fragment conditions to be abstract types */ public function testAllowsFragmentConditionsToBeAbstractTypes() { $ast = Parser::parse(' { __typename name pets { ...PetFields } friends { ...FriendFields } } fragment PetFields on Pet { __typename ... on Dog { name woofs } ... on Cat { name meows } } fragment FriendFields on Named { __typename name ... on Dog { woofs } ... on Cat { meows } } '); $expected = [ 'data' => [ '__typename' => 'Person', 'name' => 'John', 'pets' => [ ['__typename' => 'Cat', 'name' => 'Garfield', 'meows' => false], ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true] ], 'friends' => [ ['__typename' => 'Person', 'name' => 'Liz'], ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true] ] ] ]; Warning::suppress(Warning::WARNING_FULL_SCHEMA_SCAN); $this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray()); Warning::enable(Warning::WARNING_FULL_SCHEMA_SCAN); } /** * @it gets execution info in resolver */ public function testGetsExecutionInfoInResolver() { $encounteredContext = null; $encounteredSchema = null; $encounteredRootValue = null; $PersonType2 = null; $NamedType2 = new InterfaceType([ 'name' => 'Named', 'fields' => [ 'name' => ['type' => Type::string()] ], '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', 'interfaces' => [$NamedType2], 'fields' => [ 'name' => ['type' => Type::string()], 'friends' => ['type' => Type::listOf($NamedType2)], ], ]); $schema2 = new Schema([ 'query' => $PersonType2 ]); $john2 = new Person('John', [], [$this->liz]); $context = ['authToken' => '123abc']; $ast = Parser::parse('{ name, friends { name } }'); $this->assertEquals( ['data' => ['name' => 'John', 'friends' => [['name' => 'Liz']]]], GraphQL::execute($schema2, $ast, $john2, $context) ); $this->assertSame($context, $encounteredContext); $this->assertSame($schema2, $encounteredSchema); $this->assertSame($john2, $encounteredRootValue); } }