ComponentTest.php 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) Tests <https://book.cakephp.org/view/1196/Testing>
  5. * Copyright 2005-2011, Cake Software Foundation, Inc. (https://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * Redistributions of files must retain the above copyright notice
  9. *
  10. * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (https://cakefoundation.org)
  11. * @link https://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
  12. * @since 1.2.0
  13. * @license https://opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\Controller;
  16. use Cake\Controller\Component\FlashComponent;
  17. use Cake\Controller\ComponentRegistry;
  18. use Cake\Controller\Controller;
  19. use Cake\Controller\Exception\MissingComponentException;
  20. use Cake\Core\Exception\CakeException;
  21. use Cake\Event\EventManager;
  22. use Cake\Http\ServerRequest;
  23. use Cake\TestSuite\TestCase;
  24. use Exception;
  25. use TestApp\Controller\Component\AppleComponent;
  26. use TestApp\Controller\Component\BananaComponent;
  27. use TestApp\Controller\Component\ConfiguredComponent;
  28. use TestApp\Controller\Component\OrangeComponent;
  29. use TestApp\Controller\Component\SomethingWithFlashComponent;
  30. use TestApp\Controller\ComponentTestController;
  31. /**
  32. * ComponentTest class
  33. */
  34. class ComponentTest extends TestCase
  35. {
  36. /**
  37. * setUp method
  38. */
  39. public function setUp(): void
  40. {
  41. parent::setUp();
  42. static::setAppNamespace();
  43. }
  44. /**
  45. * test accessing inner components.
  46. */
  47. public function testInnerComponentConstruction(): void
  48. {
  49. $Collection = new ComponentRegistry(new Controller(new ServerRequest()));
  50. $Component = new AppleComponent($Collection);
  51. $this->assertInstanceOf(OrangeComponent::class, $Component->Orange, 'class is wrong');
  52. }
  53. /**
  54. * test component loading
  55. */
  56. public function testNestedComponentLoading(): void
  57. {
  58. $Collection = new ComponentRegistry(new Controller(new ServerRequest()));
  59. $Apple = new AppleComponent($Collection);
  60. $this->assertInstanceOf(OrangeComponent::class, $Apple->Orange, 'class is wrong');
  61. $this->assertInstanceOf(BananaComponent::class, $Apple->Orange->Banana, 'class is wrong');
  62. $this->assertEmpty($Apple->Session);
  63. $this->assertEmpty($Apple->Orange->Session);
  64. }
  65. /**
  66. * test that component components are not enabled in the collection.
  67. */
  68. public function testInnerComponentsAreNotEnabled(): void
  69. {
  70. $eventManager = new class extends EventManager
  71. {
  72. public bool $isCalled = false;
  73. public bool $isCorrectType = false;
  74. public function on($eventKey, $options = null, $callable = []): void
  75. {
  76. $this->isCalled = true;
  77. $this->isCorrectType = $eventKey instanceof AppleComponent;
  78. }
  79. };
  80. $controller = new Controller(new ServerRequest());
  81. $controller->setEventManager($eventManager);
  82. $Collection = new ComponentRegistry($controller);
  83. $Apple = $Collection->load('Apple');
  84. $this->assertInstanceOf(OrangeComponent::class, $Apple->Orange, 'class is wrong');
  85. $this->assertTrue($eventManager->isCalled, 'on() should be called');
  86. $this->assertTrue($eventManager->isCorrectType, 'on() should be called with the correct type');
  87. }
  88. /**
  89. * test a component being used more than once.
  90. */
  91. public function testMultipleComponentInitialize(): void
  92. {
  93. $Collection = new ComponentRegistry(new Controller(new ServerRequest()));
  94. $Banana = $Collection->load('Banana');
  95. $Orange = $Collection->load('Orange');
  96. $this->assertSame($Banana, $Orange->Banana, 'Should be references');
  97. $Banana->testField = 'OrangeField';
  98. $this->assertSame($Banana->testField, $Orange->Banana->testField, 'References are broken');
  99. }
  100. /**
  101. * Test a duplicate component being loaded more than once with same and differing configurations.
  102. */
  103. public function testDuplicateComponentInitialize(): void
  104. {
  105. $this->expectException(CakeException::class);
  106. $this->expectExceptionMessage('The `Banana` alias has already been loaded. The `property` key');
  107. $Collection = new ComponentRegistry(new Controller(new ServerRequest()));
  108. $Collection->load('Banana', ['property' => ['closure' => function (): void {
  109. }]]);
  110. $Collection->load('Banana', ['property' => ['closure' => function (): void {
  111. }]]);
  112. $this->assertInstanceOf(BananaComponent::class, $Collection->Banana, 'class is wrong');
  113. $Collection->load('Banana', ['property' => ['differs']]);
  114. }
  115. /**
  116. * Test mutually referencing components.
  117. */
  118. public function testSomethingReferencingFlashComponent(): void
  119. {
  120. $Controller = new ComponentTestController(new ServerRequest());
  121. $Controller->loadComponent('SomethingWithFlash');
  122. $Controller->startupProcess();
  123. $this->assertInstanceOf(SomethingWithFlashComponent::class, $Controller->SomethingWithFlash);
  124. $this->assertInstanceOf(FlashComponent::class, $Controller->SomethingWithFlash->Flash);
  125. }
  126. /**
  127. * Tests __debugInfo
  128. */
  129. public function testDebugInfo(): void
  130. {
  131. $Collection = new ComponentRegistry(new Controller(new ServerRequest()));
  132. $Component = new AppleComponent($Collection);
  133. $expected = [
  134. 'components' => [
  135. 'Orange' => [],
  136. ],
  137. 'implementedEvents' => [
  138. 'Controller.startup' => 'startup',
  139. ],
  140. '_config' => [],
  141. ];
  142. $result = $Component->__debugInfo();
  143. $this->assertEquals($expected, $result);
  144. }
  145. /**
  146. * Tests null return for unknown magic properties.
  147. */
  148. public function testMagicReturnsNull(): void
  149. {
  150. $Component = new AppleComponent(new ComponentRegistry(new Controller(new ServerRequest())));
  151. $this->assertNull($Component->ShouldBeNull);
  152. }
  153. /**
  154. * Tests config via constructor
  155. */
  156. public function testConfigViaConstructor(): void
  157. {
  158. $Component = new ConfiguredComponent(
  159. new ComponentRegistry(new Controller(new ServerRequest())),
  160. ['chicken' => 'soup']
  161. );
  162. $this->assertEquals(['chicken' => 'soup'], $Component->configCopy);
  163. $this->assertEquals(['chicken' => 'soup'], $Component->getConfig());
  164. }
  165. /**
  166. * Lazy load a component without events.
  167. */
  168. public function testLazyLoading(): void
  169. {
  170. $Component = new ConfiguredComponent(
  171. new ComponentRegistry(new Controller(new ServerRequest())),
  172. [],
  173. ['Apple', 'Banana', 'Orange']
  174. );
  175. $this->assertInstanceOf(AppleComponent::class, $Component->Apple, 'class is wrong');
  176. $this->assertInstanceOf(OrangeComponent::class, $Component->Orange, 'class is wrong');
  177. $this->assertInstanceOf(BananaComponent::class, $Component->Banana, 'class is wrong');
  178. }
  179. /**
  180. * Lazy load a component that does not exist.
  181. */
  182. public function testLazyLoadingDoesNotExists(): void
  183. {
  184. $this->expectException(MissingComponentException::class);
  185. $this->expectExceptionMessage('Component class `YouHaveNoBananasComponent` could not be found.');
  186. $Component = new ConfiguredComponent(new ComponentRegistry(new Controller(new ServerRequest())), [], ['YouHaveNoBananas']);
  187. $Component->YouHaveNoBananas;
  188. }
  189. /**
  190. * Lazy loaded components can have config options
  191. */
  192. public function testConfiguringInnerComponent(): void
  193. {
  194. $Component = new ConfiguredComponent(
  195. new ComponentRegistry(new Controller(new ServerRequest())),
  196. [],
  197. ['Configured' => ['foo' => 'bar']]
  198. );
  199. $this->assertInstanceOf(ConfiguredComponent::class, $Component->Configured, 'class is wrong');
  200. $this->assertNotSame($Component, $Component->Configured, 'Component instance was reused');
  201. $this->assertEquals(['foo' => 'bar', 'enabled' => false], $Component->Configured->getConfig());
  202. }
  203. /**
  204. * Test enabling events for lazy loaded components
  205. */
  206. public function testEventsInnerComponent(): void
  207. {
  208. $eventManager = new class extends EventManager
  209. {
  210. public bool $isCalled = false;
  211. public bool $isCorrectType = false;
  212. public function on($eventKey, $options = null, $callable = []): void
  213. {
  214. $this->isCalled = true;
  215. $this->isCorrectType = $eventKey instanceof AppleComponent;
  216. }
  217. };
  218. $controller = new Controller(new ServerRequest());
  219. $controller->setEventManager($eventManager);
  220. $Collection = new ComponentRegistry($controller);
  221. $Component = new ConfiguredComponent($Collection, [], ['Apple' => ['enabled' => true]]);
  222. $this->assertInstanceOf(AppleComponent::class, $Component->Apple, 'class is wrong');
  223. $this->assertTrue($eventManager->isCalled, 'on() should be called');
  224. $this->assertTrue($eventManager->isCorrectType, 'on() should be called with the correct type');
  225. }
  226. /**
  227. * Disabled events do not register for event listeners.
  228. */
  229. public function testNoEventsInnerComponent(): void
  230. {
  231. $eventManager = new class extends EventManager
  232. {
  233. public function on($eventKey, $options = null, $callable = []): never
  234. {
  235. throw new Exception('Should not be called');
  236. }
  237. };
  238. $controller = new Controller(new ServerRequest());
  239. $controller->setEventManager($eventManager);
  240. $Collection = new ComponentRegistry($controller);
  241. $Component = new ConfiguredComponent($Collection, [], ['Apple' => ['enabled' => false]]);
  242. $this->assertInstanceOf(AppleComponent::class, $Component->Apple, 'class is wrong');
  243. }
  244. }