View source
<?php
namespace Drupal\Tests\graphql\Traits;
use Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator;
use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
use Drupal\Component\Plugin\Factory\FactoryInterface;
use Drupal\Component\Plugin\PluginInspectionInterface;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
use Drupal\graphql\Annotation\GraphQLEnum;
use Drupal\graphql\Annotation\GraphQLField;
use Drupal\graphql\Annotation\GraphQLInputType;
use Drupal\graphql\Annotation\GraphQLInterface;
use Drupal\graphql\Annotation\GraphQLMutation;
use Drupal\graphql\Annotation\GraphQLType;
use Drupal\graphql\Annotation\GraphQLUnionType;
use Drupal\graphql\Plugin\GraphQL\Enums\EnumPluginBase;
use Drupal\graphql\Plugin\GraphQL\Fields\FieldPluginBase;
use Drupal\graphql\Plugin\GraphQL\InputTypes\InputTypePluginBase;
use Drupal\graphql\Plugin\GraphQL\Interfaces\InterfacePluginBase;
use Drupal\graphql\Plugin\GraphQL\Mutations\MutationPluginBase;
use Drupal\graphql\Plugin\GraphQL\Scalars\ScalarPluginBase;
use Drupal\graphql\Plugin\GraphQL\Schemas\SchemaPluginBase;
use Drupal\graphql\Plugin\GraphQL\Subscriptions\SubscriptionPluginBase;
use Drupal\graphql\Plugin\GraphQL\Types\TypePluginBase;
use Drupal\graphql\Plugin\GraphQL\Unions\UnionTypePluginBase;
trait MockGraphQLPluginTrait {
protected $graphQLPlugins = [];
protected $graphQLPluginManagers = [];
protected $graphqlPluginDecorators = [];
protected function resetStaticCaches() {
$definitionsProperty = new \ReflectionProperty(DefaultPluginManager::class, 'definitions');
$definitionsProperty
->setAccessible(TRUE);
foreach ($this->graphQLPluginManagers as $manager) {
$definitionsProperty
->setValue($manager, NULL);
}
$deriversProperty = new \ReflectionProperty(DerivativeDiscoveryDecorator::class, 'derivers');
$deriversProperty
->setAccessible(TRUE);
foreach ($this->graphqlPluginDecorators as $decorator) {
$deriversProperty
->setValue($decorator, NULL);
}
}
protected $graphQLPluginClassMap = [
'plugin.manager.graphql.schema' => SchemaPluginBase::class,
'plugin.manager.graphql.field' => FieldPluginBase::class,
'plugin.manager.graphql.mutation' => MutationPluginBase::class,
'plugin.manager.graphql.subscription' => SubscriptionPluginBase::class,
'plugin.manager.graphql.union' => UnionTypePluginBase::class,
'plugin.manager.graphql.interface' => InterfacePluginBase::class,
'plugin.manager.graphql.type' => TypePluginBase::class,
'plugin.manager.graphql.input' => InputTypePluginBase::class,
'plugin.manager.graphql.scalar' => ScalarPluginBase::class,
'plugin.manager.graphql.enum' => EnumPluginBase::class,
];
protected function injectTypeSystemPluginManagers(ContainerBuilder $container) {
foreach ($this->graphQLPluginClassMap as $id => $class) {
$this->graphQLPlugins[$class] = [];
$manager = $container
->get($id);
$this->graphQLPluginManagers[$id] = $manager;
$factoryMethod = new \ReflectionMethod($manager, 'getFactory');
$factoryMethod
->setAccessible(TRUE);
$factoryProp = new \ReflectionProperty($manager, 'factory');
$factoryProp
->setAccessible(TRUE);
$discoveryMethod = new \ReflectionMethod($manager, 'getDiscovery');
$discoveryMethod
->setAccessible(TRUE);
$discoveryProp = new \ReflectionProperty($manager, 'discovery');
$discoveryProp
->setAccessible(TRUE);
$factory = $factoryMethod
->invoke($manager);
$discovery = $discoveryMethod
->invoke($manager);
$decoratedProp = new \ReflectionProperty(DerivativeDiscoveryDecorator::class, 'decorated');
$decoratedProp
->setAccessible(TRUE);
$unwrappedDiscovery = $decoratedProp
->getValue($discovery);
$this->graphQLPlugins[$class] = [];
$mockFactory = $this
->getMockBuilder(FactoryInterface::class)
->setMethods([
'createInstance',
])
->getMock();
$mockDiscovery = $this
->getMockBuilder(DiscoveryInterface::class)
->setMethods([
'hasDefinition',
'getDefinitions',
'getDefinition',
])
->getMock();
$decoratedDiscovery = new ContainerDerivativeDiscoveryDecorator($mockDiscovery);
$this->graphqlPluginDecorators[$id] = $decoratedDiscovery;
$mockDiscovery
->expects(static::any())
->method('getDefinitions')
->willReturnCallback(function () use ($class, $unwrappedDiscovery) {
$mockDefinitions = array_map(function ($plugin) {
return $plugin['definition'];
}, $this->graphQLPlugins[$class]);
$realDefinitions = $unwrappedDiscovery
->getDefinitions();
return array_merge($mockDefinitions, $realDefinitions);
});
$mockDiscovery
->expects(static::any())
->method('hasDefinition')
->with(static::anything())
->willReturnCallback(function ($pluginId) use ($class, $discovery) {
$basePluginId = $this
->getBasePluginId($pluginId);
return isset($this->graphQLPlugins[$class][$basePluginId]) || $discovery
->hasDefinition($pluginId);
});
$mockDiscovery
->expects(static::any())
->method('getDefinition')
->with(static::anything(), static::anything())
->willReturnCallback(function ($pluginId, $except) use ($class, $discovery) {
$basePluginId = $this
->getBasePluginId($pluginId);
if (array_key_exists($basePluginId, $this->graphQLPlugins[$class])) {
return $this->graphQLPlugins[$class][$basePluginId]['definition'];
}
return $discovery
->getDefinition($pluginId, $except);
});
$discoveryProp
->setValue($manager, $decoratedDiscovery);
$mockFactory
->expects(static::any())
->method('createInstance')
->with(static::anything(), static::anything())
->willReturnCallback(function ($pluginId, $configuration) use ($class, $factory, $decoratedDiscovery) {
$basePluginId = $this
->getBasePluginId($pluginId);
if (array_key_exists($basePluginId, $this->graphQLPlugins[$class])) {
$definition = $decoratedDiscovery
->getDefinition($pluginId);
$args = $this->graphQLPlugins[$class][$basePluginId];
$args['definition'] = $definition;
return call_user_func_array([
$this,
$definition['mock_factory'],
], $args);
}
return $factory
->createInstance($pluginId, $configuration);
});
$factoryProp
->setValue($manager, $mockFactory);
}
}
private function getBasePluginId($pluginId) {
return strpos($pluginId, ':') ? explode(':', $pluginId)[0] : $pluginId;
}
protected function getTypeSystemPluginDefinition($annotationClass, array $definition) {
return (new $annotationClass($definition))
->get();
}
protected function addTypeSystemPlugin(PluginInspectionInterface $plugin) {
foreach ($this->graphQLPluginClassMap as $id => $class) {
if ($plugin instanceof $class) {
$this->graphQLPlugins[$id][$plugin
->getPluginId()] = $plugin;
}
}
}
protected function toPromise($value) {
return $this
->returnCallback(is_callable($value) ? $value : function () use ($value) {
(yield $value);
});
}
protected function toBoundPromise($value, $scope) {
return $this
->toPromise(is_callable($value) ? \Closure::bind($value, $scope, $scope) : $value);
}
protected function mockSchema($id, $builder = NULL) {
$this->graphQLPlugins[SchemaPluginBase::class][$id] = [
'definition' => $this
->getSchemaDefinitions()[$id] + [
'mock_factory' => 'mockSchemaFactory',
],
'builder' => $builder,
];
}
protected function mockSchemaFactory($definition, $builder) {
$schema = $this
->getMockForAbstractClass(SchemaPluginBase::class, [
[],
$definition['id'],
$definition,
$this->container
->get('plugin.manager.graphql.field'),
$this->container
->get('plugin.manager.graphql.mutation'),
$this->container
->get('plugin.manager.graphql.subscription'),
$this->container
->get('graphql.type_manager_aggregator'),
$this->container
->get('graphql.query_provider'),
$this->container
->get('current_user'),
$this->container
->get('logger.channel.graphql'),
$this->container
->get('language_manager'),
$this->container
->getParameter('graphql.config'),
]);
if (is_callable($builder)) {
$builder($schema);
}
return $schema;
}
protected function mockField($id, $definition, $result = NULL, $builder = NULL) {
$definition = $this
->getTypeSystemPluginDefinition(GraphQLField::class, $definition + [
'secure' => TRUE,
'id' => $id,
'class' => FieldPluginBase::class,
'mock_factory' => 'mockFieldFactory',
]);
$this->graphQLPlugins[FieldPluginBase::class][$id] = [
'definition' => $definition,
'result' => $result,
'builder' => $builder,
];
}
protected function mockFieldFactory($definition, $result = NULL, $builder = NULL) {
$field = $this
->getMockBuilder(FieldPluginBase::class)
->setConstructorArgs([
[],
$definition['id'],
$definition,
])
->setMethods([
'resolveValues',
])
->getMock();
if (isset($result)) {
$field
->expects(static::any())
->method('resolveValues')
->with(static::anything(), static::anything(), static::anything(), static::anything())
->will($this
->toBoundPromise($result, $field));
}
if (is_callable($builder)) {
$builder($field);
}
return $field;
}
protected function mockType($id, array $definition, $applies = TRUE, $builder = NULL) {
$definition = $this
->getTypeSystemPluginDefinition(GraphQLType::class, $definition + [
'id' => $id,
'class' => TypePluginBase::class,
'mock_factory' => 'mockTypeFactory',
]);
$this->graphQLPlugins[TypePluginBase::class][$id] = [
'definition' => $definition,
'applies' => $applies,
'builder' => $builder,
];
}
protected function mockTypeFactory($definition, $applies = TRUE, $builder = NULL) {
$type = $this
->getMockBuilder(TypePluginBase::class)
->setConstructorArgs([
[],
$definition['id'],
$definition,
])
->setMethods([
'applies',
])
->getMock();
$type
->expects(static::any())
->method('applies')
->with($this
->anything(), $this
->anything())
->will($this
->toBoundPromise($applies, $type));
if (is_callable($builder)) {
$builder($type);
}
return $type;
}
protected function mockInputType($id, array $definition, $builder = NULL) {
$definition = $this
->getTypeSystemPluginDefinition(GraphQLInputType::class, $definition + [
'id' => $id,
'class' => InputTypePluginBase::class,
'mock_factory' => 'mockInputTypeFactory',
]);
$this->graphQLPlugins[InputTypePluginBase::class][$id] = [
'definition' => $definition,
'builder' => $builder,
];
}
protected function mockInputTypeFactory($definition, $builder) {
$input = $this
->getMockForAbstractClass(InputTypePluginBase::class, [
[],
$definition['id'],
$definition,
]);
if (is_callable($builder)) {
$builder($input);
}
return $input;
}
protected function mockMutation($id, array $definition, $result = NULL, $builder = NULL) {
$definition = $this
->getTypeSystemPluginDefinition(GraphQLMutation::class, $definition + [
'id' => $id,
'class' => MutationPluginBase::class,
'mock_factory' => 'mockMutationFactory',
]);
$this->graphQLPlugins[MutationPluginBase::class][$id] = [
'definition' => $definition,
'result' => $result,
'builder' => $builder,
];
}
protected function mockMutationFactory($definition, $result = NULL, $builder = NULL) {
$mutation = $this
->getMockBuilder(MutationPluginBase::class)
->setConstructorArgs([
[],
$definition['id'],
$definition,
])
->setMethods([
'resolve',
])
->getMock();
if (isset($result)) {
$mutation
->expects(static::any())
->method('resolve')
->with(static::anything(), static::anything(), static::anything(), static::anything())
->will($this
->toBoundPromise($result, $mutation));
}
if (is_callable($builder)) {
$builder($mutation);
}
return $mutation;
}
protected function mockInterface($id, array $definition, $builder = NULL) {
$definition = $this
->getTypeSystemPluginDefinition(GraphQLInterface::class, $definition + [
'id' => $id,
'class' => InterfacePluginBase::class,
'mock_factory' => 'mockInterfaceFactory',
]);
$this->graphQLPlugins[InputTypePluginBase::class][$id] = [
'definition' => $definition,
'builder' => $builder,
];
}
protected function mockInterfaceFactory($definition, $builder = NULL) {
$interface = $this
->getMockForAbstractClass(InterfacePluginBase::class, [
[],
$definition['id'],
$definition,
]);
if (is_callable($builder)) {
$builder($interface);
}
return $interface;
}
protected function mockUnion($id, array $definition, $builder = NULL) {
$definition = $this
->getTypeSystemPluginDefinition(GraphQLUnionType::class, $definition + [
'id' => $id,
'class' => UnionTypePluginBase::class,
'mock_factory' => 'mockUnionFactory',
]);
$this->graphQLPlugins[UnionTypePluginBase::class][$id] = [
'definition' => $definition,
'builder' => $builder,
];
}
protected function mockUnionFactory($definition, $builder) {
$union = $this
->getMockForAbstractClass(UnionTypePluginBase::class, [
[],
$definition['id'],
$definition,
]);
if (is_callable($union)) {
$builder($union);
}
return $union;
}
protected function mockEnum($id, array $definition, $values = [], $builder = NULL) {
$definition = $this
->getTypeSystemPluginDefinition(GraphQLEnum::class, $definition + [
'id' => $id,
'class' => EnumPluginBase::class,
'mock_factory' => 'mockEnumFactory',
]);
$this->graphQLPlugins[EnumPluginBase::class][$id] = [
'definition' => $definition,
'values' => $values,
'builder' => $builder,
];
}
protected function mockEnumFactory($definition, $values = [], $builder = NULL) {
$enum = $this
->getMockBuilder(EnumPluginBase::class)
->setConstructorArgs([
[],
$definition['id'],
$definition,
])
->setMethods([
'buildEnumValues',
])
->getMock();
$enum
->expects(static::any())
->method('buildEnumValues')
->with($this
->anything())
->will($this
->toBoundPromise($values, $enum));
if (is_callable($builder)) {
$builder($enum);
}
return $enum;
}
}