4e2a79f19d50a2cfcda78860ccf9c1bcce0cd1ec
[yaffs-website] / web / core / tests / Drupal / Tests / Core / Entity / EntityTypeManagerTest.php
1 <?php
2
3 /**
4  * @file
5  * Contains \Drupal\Tests\Core\Entity\EntityTypeManagerTest.
6  */
7
8 namespace Drupal\Tests\Core\Entity;
9
10 use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
11 use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
12 use Drupal\Component\Plugin\Exception\PluginNotFoundException;
13 use Drupal\Core\Cache\CacheBackendInterface;
14 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
15 use Drupal\Core\Entity\EntityHandlerBase;
16 use Drupal\Core\Entity\EntityInterface;
17 use Drupal\Core\Entity\EntityManagerInterface;
18 use Drupal\Core\Entity\EntityTypeInterface;
19 use Drupal\Core\Entity\EntityTypeManager;
20 use Drupal\Core\Entity\EntityTypeManagerInterface;
21 use Drupal\Core\Entity\Exception\InvalidLinkTemplateException;
22 use Drupal\Core\Extension\ModuleHandlerInterface;
23 use Drupal\Core\StringTranslation\TranslationInterface;
24 use Drupal\Tests\UnitTestCase;
25 use Prophecy\Argument;
26 use Symfony\Component\DependencyInjection\ContainerInterface;
27
28 /**
29  * @coversDefaultClass \Drupal\Core\Entity\EntityTypeManager
30  * @group Entity
31  */
32 class EntityTypeManagerTest extends UnitTestCase {
33
34   /**
35    * The entity type manager under test.
36    *
37    * @var \Drupal\Core\Entity\EntityTypeManager
38    */
39   protected $entityTypeManager;
40
41   /**
42    * The translation manager.
43    *
44    * @var \Drupal\Core\StringTranslation\TranslationInterface|\Prophecy\Prophecy\ProphecyInterface
45    */
46   protected $translationManager;
47
48   /**
49    * The plugin discovery.
50    *
51    * @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface|\Prophecy\Prophecy\ProphecyInterface
52    */
53   protected $discovery;
54
55   /**
56    * The module handler.
57    *
58    * @var \Drupal\Core\Extension\ModuleHandlerInterface|\Prophecy\Prophecy\ProphecyInterface
59    */
60   protected $moduleHandler;
61
62   /**
63    * The cache backend.
64    *
65    * @var \Drupal\Core\Cache\CacheBackendInterface|\Prophecy\Prophecy\ProphecyInterface
66    */
67   protected $cacheBackend;
68
69   /**
70    * {@inheritdoc}
71    */
72   protected function setUp() {
73     parent::setUp();
74
75     $this->moduleHandler = $this->prophesize(ModuleHandlerInterface::class);
76     $this->moduleHandler->getImplementations('entity_type_build')->willReturn([]);
77     $this->moduleHandler->alter('entity_type', Argument::type('array'))->willReturn(NULL);
78
79     $this->cacheBackend = $this->prophesize(CacheBackendInterface::class);
80     $this->translationManager = $this->prophesize(TranslationInterface::class);
81
82     $this->entityTypeManager = new TestEntityTypeManager(new \ArrayObject(), $this->moduleHandler->reveal(), $this->cacheBackend->reveal(), $this->translationManager->reveal(), $this->getClassResolverStub());
83     $this->discovery = $this->prophesize(DiscoveryInterface::class);
84     $this->entityTypeManager->setDiscovery($this->discovery->reveal());
85   }
86
87   /**
88    * Sets up the entity type manager to be tested.
89    *
90    * @param \Drupal\Core\Entity\EntityTypeInterface[]|\Prophecy\Prophecy\ProphecyInterface[] $definitions
91    *   (optional) An array of entity type definitions.
92    */
93   protected function setUpEntityTypeDefinitions($definitions = []) {
94     $class = $this->getMockClass(EntityInterface::class);
95     foreach ($definitions as $key => $entity_type) {
96       // \Drupal\Core\Entity\EntityTypeInterface::getLinkTemplates() is called
97       // by \Drupal\Core\Entity\EntityManager::processDefinition() so it must
98       // always be mocked.
99       $entity_type->getLinkTemplates()->willReturn([]);
100
101       // Give the entity type a legitimate class to return.
102       $entity_type->getClass()->willReturn($class);
103       $entity_type->setClass($class)->willReturn($entity_type->reveal());
104
105       $definitions[$key] = $entity_type->reveal();
106     }
107
108     $this->discovery->getDefinition(Argument::cetera())
109       ->will(function ($args) use ($definitions) {
110         $entity_type_id = $args[0];
111         $exception_on_invalid = $args[1];
112         if (isset($definitions[$entity_type_id])) {
113           return $definitions[$entity_type_id];
114         }
115         elseif (!$exception_on_invalid) {
116           return NULL;
117         }
118         else throw new PluginNotFoundException($entity_type_id);
119       });
120     $this->discovery->getDefinitions()->willReturn($definitions);
121
122   }
123
124   /**
125    * Tests the hasHandler() method.
126    *
127    * @covers ::hasHandler
128    *
129    * @dataProvider providerTestHasHandler
130    */
131   public function testHasHandler($entity_type_id, $expected) {
132     $apple = $this->prophesize(EntityTypeInterface::class);
133     $apple->hasHandlerClass('storage')->willReturn(TRUE);
134
135     $banana = $this->prophesize(EntityTypeInterface::class);
136     $banana->hasHandlerClass('storage')->willReturn(FALSE);
137
138     $this->setUpEntityTypeDefinitions([
139       'apple' => $apple,
140       'banana' => $banana,
141     ]);
142
143     $entity_type = $this->entityTypeManager->hasHandler($entity_type_id, 'storage');
144     $this->assertSame($expected, $entity_type);
145   }
146
147   /**
148    * Provides test data for testHasHandler().
149    *
150    * @return array
151    *   Test data.
152    */
153   public function providerTestHasHandler() {
154     return [
155       ['apple', TRUE],
156       ['banana', FALSE],
157       ['pear', FALSE],
158     ];
159   }
160
161   /**
162    * Tests the getStorage() method.
163    *
164    * @covers ::getStorage
165    */
166   public function testGetStorage() {
167     $class = $this->getTestHandlerClass();
168     $entity = $this->prophesize(EntityTypeInterface::class);
169     $entity->getHandlerClass('storage')->willReturn($class);
170     $this->setUpEntityTypeDefinitions(['test_entity_type' => $entity]);
171
172     $this->assertInstanceOf($class, $this->entityTypeManager->getStorage('test_entity_type'));
173   }
174
175   /**
176    * Tests the getListBuilder() method.
177    *
178    * @covers ::getListBuilder
179    */
180   public function testGetListBuilder() {
181     $class = $this->getTestHandlerClass();
182     $entity = $this->prophesize(EntityTypeInterface::class);
183     $entity->getHandlerClass('list_builder')->willReturn($class);
184     $this->setUpEntityTypeDefinitions(['test_entity_type' => $entity]);
185
186     $this->assertInstanceOf($class, $this->entityTypeManager->getListBuilder('test_entity_type'));
187   }
188
189   /**
190    * Tests the getViewBuilder() method.
191    *
192    * @covers ::getViewBuilder
193    */
194   public function testGetViewBuilder() {
195     $class = $this->getTestHandlerClass();
196     $entity = $this->prophesize(EntityTypeInterface::class);
197     $entity->getHandlerClass('view_builder')->willReturn($class);
198     $this->setUpEntityTypeDefinitions(['test_entity_type' => $entity]);
199
200     $this->assertInstanceOf($class, $this->entityTypeManager->getViewBuilder('test_entity_type'));
201   }
202
203   /**
204    * Tests the getAccessControlHandler() method.
205    *
206    * @covers ::getAccessControlHandler
207    */
208   public function testGetAccessControlHandler() {
209     $class = $this->getTestHandlerClass();
210     $entity = $this->prophesize(EntityTypeInterface::class);
211     $entity->getHandlerClass('access')->willReturn($class);
212     $this->setUpEntityTypeDefinitions(['test_entity_type' => $entity]);
213
214     $this->assertInstanceOf($class, $this->entityTypeManager->getAccessControlHandler('test_entity_type'));
215   }
216
217   /**
218    * Tests the getFormObject() method.
219    *
220    * @covers ::getFormObject
221    */
222   public function testGetFormObject() {
223     $entity_manager = $this->prophesize(EntityManagerInterface::class);
224     $container = $this->prophesize(ContainerInterface::class);
225     $container->get('entity.manager')->willReturn($entity_manager->reveal());
226     \Drupal::setContainer($container->reveal());
227
228     $apple = $this->prophesize(EntityTypeInterface::class);
229     $apple->getFormClass('default')->willReturn(TestEntityForm::class);
230
231     $banana = $this->prophesize(EntityTypeInterface::class);
232     $banana->getFormClass('default')->willReturn(TestEntityFormInjected::class);
233
234     $this->setUpEntityTypeDefinitions([
235       'apple' => $apple,
236       'banana' => $banana,
237     ]);
238
239     $apple_form = $this->entityTypeManager->getFormObject('apple', 'default');
240     $this->assertInstanceOf(TestEntityForm::class, $apple_form);
241     $this->assertAttributeInstanceOf(ModuleHandlerInterface::class, 'moduleHandler', $apple_form);
242     $this->assertAttributeInstanceOf(TranslationInterface::class, 'stringTranslation', $apple_form);
243
244     $banana_form = $this->entityTypeManager->getFormObject('banana', 'default');
245     $this->assertInstanceOf(TestEntityFormInjected::class, $banana_form);
246     $this->assertAttributeEquals('yellow', 'color', $banana_form);
247
248   }
249
250   /**
251    * Tests the getFormObject() method with an invalid operation.
252    *
253    * @covers ::getFormObject
254    */
255   public function testGetFormObjectInvalidOperation() {
256     $entity = $this->prophesize(EntityTypeInterface::class);
257     $entity->getFormClass('edit')->willReturn('');
258     $this->setUpEntityTypeDefinitions(['test_entity_type' => $entity]);
259
260     $this->setExpectedException(InvalidPluginDefinitionException::class);
261     $this->entityTypeManager->getFormObject('test_entity_type', 'edit');
262   }
263
264   /**
265    * Tests the getHandler() method.
266    *
267    * @covers ::getHandler
268    */
269   public function testGetHandler() {
270     $class = $this->getTestHandlerClass();
271     $apple = $this->prophesize(EntityTypeInterface::class);
272     $apple->getHandlerClass('storage')->willReturn($class);
273
274     $this->setUpEntityTypeDefinitions([
275       'apple' => $apple,
276     ]);
277
278     $apple_controller = $this->entityTypeManager->getHandler('apple', 'storage');
279     $this->assertInstanceOf($class, $apple_controller);
280     $this->assertAttributeInstanceOf(ModuleHandlerInterface::class, 'moduleHandler', $apple_controller);
281     $this->assertAttributeInstanceOf(TranslationInterface::class, 'stringTranslation', $apple_controller);
282   }
283
284   /**
285    * Tests the getHandler() method when no controller is defined.
286    *
287    * @covers ::getHandler
288    */
289   public function testGetHandlerMissingHandler() {
290     $entity = $this->prophesize(EntityTypeInterface::class);
291     $entity->getHandlerClass('storage')->willReturn('');
292     $this->setUpEntityTypeDefinitions(['test_entity_type' => $entity]);
293     $this->setExpectedException(InvalidPluginDefinitionException::class);
294     $this->entityTypeManager->getHandler('test_entity_type', 'storage');
295   }
296
297   /**
298    * @covers ::getRouteProviders
299    */
300   public function testGetRouteProviders() {
301     $apple = $this->prophesize(EntityTypeInterface::class);
302     $apple->getRouteProviderClasses()->willReturn(['default' => TestRouteProvider::class]);
303
304     $this->setUpEntityTypeDefinitions([
305       'apple' => $apple,
306     ]);
307
308     $apple_route_provider = $this->entityTypeManager->getRouteProviders('apple');
309     $this->assertInstanceOf(TestRouteProvider::class, $apple_route_provider['default']);
310     $this->assertAttributeInstanceOf(ModuleHandlerInterface::class, 'moduleHandler', $apple_route_provider['default']);
311     $this->assertAttributeInstanceOf(TranslationInterface::class, 'stringTranslation', $apple_route_provider['default']);
312   }
313
314   /**
315    * Tests the processDefinition() method.
316    *
317    * @covers ::processDefinition
318    */
319   public function testProcessDefinition() {
320     $apple = $this->prophesize(EntityTypeInterface::class);
321     $this->setUpEntityTypeDefinitions(['apple' => $apple]);
322
323     $apple->getLinkTemplates()->willReturn(['canonical' => 'path/to/apple']);
324
325     $definition = $apple->reveal();
326     $this->setExpectedException(InvalidLinkTemplateException::class, "Link template 'canonical' for entity type 'apple' must start with a leading slash, the current link template is 'path/to/apple'");
327     $this->entityTypeManager->processDefinition($definition, 'apple');
328   }
329
330   /**
331    * Tests the getDefinition() method.
332    *
333    * @covers ::getDefinition
334    *
335    * @dataProvider providerTestGetDefinition
336    */
337   public function testGetDefinition($entity_type_id, $expected) {
338     $entity = $this->prophesize(EntityTypeInterface::class);
339
340     $this->setUpEntityTypeDefinitions([
341       'apple' => $entity,
342       'banana' => $entity,
343     ]);
344
345     $entity_type = $this->entityTypeManager->getDefinition($entity_type_id, FALSE);
346     if ($expected) {
347       $this->assertInstanceOf(EntityTypeInterface::class, $entity_type);
348     }
349     else {
350       $this->assertNull($entity_type);
351     }
352   }
353
354   /**
355    * Provides test data for testGetDefinition().
356    *
357    * @return array
358    *   Test data.
359    */
360   public function providerTestGetDefinition() {
361     return [
362       ['apple', TRUE],
363       ['banana', TRUE],
364       ['pear', FALSE],
365     ];
366   }
367
368   /**
369    * Tests the getDefinition() method with an invalid definition.
370    *
371    * @covers ::getDefinition
372    */
373   public function testGetDefinitionInvalidException() {
374     $this->setUpEntityTypeDefinitions();
375
376     $this->setExpectedException(PluginNotFoundException::class, 'The "pear" entity type does not exist.');
377     $this->entityTypeManager->getDefinition('pear', TRUE);
378   }
379
380   /**
381    * Gets a mock controller class name.
382    *
383    * @return string
384    *   A mock controller class name.
385    */
386   protected function getTestHandlerClass() {
387     return get_class($this->getMockForAbstractClass(EntityHandlerBase::class));
388   }
389
390 }
391
392 class TestEntityTypeManager extends EntityTypeManager {
393
394   /**
395    * Sets the discovery for the manager.
396    *
397    * @param \Drupal\Component\Plugin\Discovery\DiscoveryInterface $discovery
398    *   The discovery object.
399    */
400   public function setDiscovery(DiscoveryInterface $discovery) {
401     $this->discovery = $discovery;
402   }
403
404 }
405
406 /**
407  * Provides a test entity form.
408  */
409 class TestEntityForm extends EntityHandlerBase {
410
411   /**
412    * The entity manager.
413    *
414    * @var \Drupal\Core\Entity\EntityManagerInterface
415    */
416   protected $entityManager;
417
418   /**
419    * The entity type manager.
420    *
421    * @var \Drupal\Core\Entity\EntityTypeManagerInterface
422    */
423   protected $entityTypeManager;
424
425   /**
426    * {@inheritdoc}
427    */
428   public function getBaseFormId() {
429     return 'the_base_form_id';
430   }
431
432   /**
433    * {@inheritdoc}
434    */
435   public function getFormId() {
436     return 'the_form_id';
437   }
438
439   /**
440    * {@inheritdoc}
441    */
442   public function setEntity(EntityInterface $entity) {
443     return $this;
444   }
445
446   /**
447    * {@inheritdoc}
448    */
449   public function setOperation($operation) {
450     return $this;
451   }
452
453   /**
454    * {@inheritdoc}
455    */
456   public function setEntityManager(EntityManagerInterface $entity_manager) {
457     $this->entityManager = $entity_manager;
458     return $this;
459   }
460
461   /**
462    * {@inheritdoc}
463    */
464   public function setEntityTypeManager(EntityTypeManagerInterface $entity_type_manager) {
465     $this->entityTypeManager = $entity_type_manager;
466     return $this;
467   }
468
469 }
470
471 /**
472  * Provides a test entity form that uses injection.
473  */
474 class TestEntityFormInjected extends TestEntityForm implements ContainerInjectionInterface {
475
476   /**
477    * The color of the entity type.
478    *
479    * @var string
480    */
481   protected $color;
482
483   /**
484    * Constructs a new TestEntityFormInjected.
485    *
486    * @param string $color
487    *   The color of the entity type.
488    */
489   public function __construct($color) {
490     $this->color = $color;
491   }
492
493   /**
494    * {@inheritdoc}
495    */
496   public static function create(ContainerInterface $container) {
497     return new static('yellow');
498   }
499
500 }
501
502 /**
503  * Provides a test entity route provider.
504  */
505 class TestRouteProvider extends EntityHandlerBase {
506
507 }