LayoutTest.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. /**
  7. * Layout integration tests
  8. *
  9. * Note that some methods are not covered here, see the \Magento\Framework\View\LayoutDirectivesTest
  10. *
  11. * @see \Magento\Framework\View\LayoutDirectivesTest
  12. */
  13. namespace Magento\Framework\View;
  14. /**
  15. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  16. */
  17. class LayoutTest extends \PHPUnit\Framework\TestCase
  18. {
  19. /**
  20. * @var \Magento\Framework\View\Layout
  21. */
  22. protected $_layout;
  23. /**
  24. * @var \Magento\Framework\View\LayoutFactory
  25. */
  26. protected $layoutFactory;
  27. protected function setUp()
  28. {
  29. $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
  30. $this->layoutFactory = $objectManager->get(\Magento\Framework\View\LayoutFactory::class);
  31. $this->_layout = $this->layoutFactory->create();
  32. $objectManager->get(\Magento\Framework\App\Cache\Type\Layout::class)->clean();
  33. }
  34. public function testConstructorStructure()
  35. {
  36. $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
  37. $structure = $objectManager->get(\Magento\Framework\View\Layout\Data\Structure::class);
  38. $structure->createElement('test.container', []);
  39. /** @var $layout \Magento\Framework\View\LayoutInterface */
  40. $layout = $this->layoutFactory->create(['structure' => $structure]);
  41. $this->assertTrue($layout->hasElement('test.container'));
  42. }
  43. /**
  44. * @magentoAppIsolation enabled
  45. * @magentoAppArea frontend
  46. */
  47. public function testDestructor()
  48. {
  49. $this->_layout->addBlock(\Magento\Framework\View\Element\Text::class, 'test');
  50. $this->assertNotEmpty($this->_layout->getAllBlocks());
  51. $this->_layout->__destruct();
  52. $this->assertEmpty($this->_layout->getAllBlocks());
  53. }
  54. public function testGetUpdate()
  55. {
  56. $this->assertInstanceOf(\Magento\Framework\View\Layout\ProcessorInterface::class, $this->_layout->getUpdate());
  57. }
  58. public function testGenerateXml()
  59. {
  60. $layoutUtility = new Utility\Layout($this);
  61. /** @var $layout \Magento\Framework\View\LayoutInterface */
  62. $layout = $this->getMockBuilder(\Magento\Framework\View\Layout::class)
  63. ->setMethods(['getUpdate'])
  64. ->setConstructorArgs($layoutUtility->getLayoutDependencies())
  65. ->getMock();
  66. $merge = $this->createPartialMock(\StdClass::class, ['asSimplexml']);
  67. $merge->expects(
  68. $this->once()
  69. )->method(
  70. 'asSimplexml'
  71. )->will(
  72. $this->returnValue(
  73. simplexml_load_string(
  74. '<layout><container name="container1"></container></layout>',
  75. \Magento\Framework\View\Layout\Element::class
  76. )
  77. )
  78. );
  79. $layout->expects($this->once())->method('getUpdate')->will($this->returnValue($merge));
  80. $this->assertEmpty($layout->getXpath('/layout/container[@name="container1"]'));
  81. $layout->generateXml();
  82. $this->assertNotEmpty($layout->getXpath('/layout/container[@name="container1"]'));
  83. }
  84. /**
  85. * A smoke test for generating elements
  86. *
  87. * See sophisticated tests at \Magento\Framework\View\LayoutDirectivesTest
  88. * @see \Magento\Framework\View\LayoutDirectivesTest
  89. * @magentoAppIsolation enabled
  90. */
  91. public function testGenerateGetAllBlocks()
  92. {
  93. $this->_layout->setXml(
  94. simplexml_load_string(
  95. '<layout>
  96. <block class="Magento\Framework\View\Element\Text" name="block1">
  97. <block class="Magento\Framework\View\Element\Text"/>
  98. </block>
  99. <block class="Magento\Framework\View\Element\Text" template="test" ttl="360"/>
  100. <block class="Magento\Framework\View\Element\Text"/>
  101. </layout>',
  102. \Magento\Framework\View\Layout\Element::class
  103. )
  104. );
  105. $this->assertEquals([], $this->_layout->getAllBlocks());
  106. $this->_layout->generateElements();
  107. $expected = ['block1', 'block1_schedule_block0', 'schedule_block1', 'schedule_block2'];
  108. $this->assertSame($expected, array_keys($this->_layout->getAllBlocks()));
  109. $child = $this->_layout->getBlock('block1_schedule_block0');
  110. $this->assertSame($this->_layout->getBlock('block1'), $child->getParentBlock());
  111. $this->assertEquals('test', $this->_layout->getBlock('schedule_block1')->getData('template'));
  112. $this->assertEquals('360', $this->_layout->getBlock('schedule_block1')->getData('ttl'));
  113. $this->assertFalse($this->_layout->getBlock('nonexisting'));
  114. }
  115. public function testGetElementProperty()
  116. {
  117. $name = 'test';
  118. $this->_layout->addContainer($name, 'Test', ['option1' => 1, 'option2' => 2]);
  119. $this->assertEquals(
  120. 'Test',
  121. $this->_layout->getElementProperty($name, \Magento\Framework\View\Layout\Element::CONTAINER_OPT_LABEL)
  122. );
  123. $this->assertEquals(
  124. \Magento\Framework\View\Layout\Element::TYPE_CONTAINER,
  125. $this->_layout->getElementProperty($name, 'type')
  126. );
  127. $this->assertSame(2, $this->_layout->getElementProperty($name, 'option2'));
  128. $this->_layout->addBlock(\Magento\Framework\View\Element\Text::class, 'text', $name);
  129. $this->assertEquals(
  130. \Magento\Framework\View\Layout\Element::TYPE_BLOCK,
  131. $this->_layout->getElementProperty('text', 'type')
  132. );
  133. $this->assertSame(
  134. ['text' => 'text'],
  135. $this->_layout->getElementProperty($name, \Magento\Framework\Data\Structure::CHILDREN)
  136. );
  137. }
  138. /**
  139. * @magentoAppIsolation enabled
  140. */
  141. public function testIsBlock()
  142. {
  143. $this->assertFalse($this->_layout->isBlock('container'));
  144. $this->assertFalse($this->_layout->isBlock('block'));
  145. $this->_layout->addContainer('container', 'Container');
  146. $this->_layout->addBlock(\Magento\Framework\View\Element\Text::class, 'block');
  147. $this->assertFalse($this->_layout->isBlock('container'));
  148. $this->assertTrue($this->_layout->isBlock('block'));
  149. }
  150. public function testSetUnsetBlock()
  151. {
  152. $expectedBlockName = 'block_' . __METHOD__;
  153. $expectedBlock = $this->_layout->createBlock(\Magento\Framework\View\Element\Text::class);
  154. $this->_layout->setBlock($expectedBlockName, $expectedBlock);
  155. $this->assertSame($expectedBlock, $this->_layout->getBlock($expectedBlockName));
  156. $this->_layout->unsetElement($expectedBlockName);
  157. $this->assertFalse($this->_layout->getBlock($expectedBlockName));
  158. $this->assertFalse($this->_layout->hasElement($expectedBlockName));
  159. }
  160. /**
  161. * @dataProvider createBlockDataProvider
  162. */
  163. public function testCreateBlock($blockType, $blockName, array $blockData, $expectedName)
  164. {
  165. $expectedData = $blockData + ['type' => $blockType];
  166. $block = $this->_layout->createBlock($blockType, $blockName, ['data' => $blockData]);
  167. $this->assertRegExp($expectedName, $block->getNameInLayout());
  168. $this->assertEquals($expectedData, $block->getData());
  169. }
  170. public function createBlockDataProvider()
  171. {
  172. return [
  173. 'named block' => [\Magento\Framework\View\Element\Template::class,
  174. 'some_block_name_full_class',
  175. ['type' => \Magento\Framework\View\Element\Template::class, 'is_anonymous' => false],
  176. '/^some_block_name_full_class$/',
  177. ],
  178. 'no name block' => [\Magento\Framework\View\Element\Text\ListText::class,
  179. '',
  180. ['type' => \Magento\Framework\View\Element\Text\ListText::class, 'key1' => 'value1'],
  181. '/text\\\\list/',
  182. ]
  183. ];
  184. }
  185. /**
  186. * @dataProvider blockNotExistsDataProvider
  187. * @expectedException \Magento\Framework\Exception\LocalizedException
  188. */
  189. public function testCreateBlockNotExists($name)
  190. {
  191. $this->_layout->createBlock($name);
  192. }
  193. public function blockNotExistsDataProvider()
  194. {
  195. return [[''], ['block_not_exists']];
  196. }
  197. public function testAddBlock()
  198. {
  199. $this->assertInstanceOf(
  200. \Magento\Framework\View\Element\Text::class,
  201. $this->_layout->addBlock(\Magento\Framework\View\Element\Text::class, 'block1')
  202. );
  203. $block2 = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
  204. \Magento\Framework\View\Element\Text::class
  205. );
  206. $block2->setNameInLayout('block2');
  207. $this->_layout->addBlock($block2, '', 'block1');
  208. $this->assertTrue($this->_layout->hasElement('block1'));
  209. $this->assertTrue($this->_layout->hasElement('block2'));
  210. $this->assertEquals('block1', $this->_layout->getParentName('block2'));
  211. }
  212. /**
  213. * @magentoAppIsolation enabled
  214. * @dataProvider addContainerDataProvider()
  215. */
  216. public function testAddContainer($htmlTag)
  217. {
  218. $this->assertFalse($this->_layout->hasElement('container'));
  219. $this->_layout->addContainer('container', 'Container', ['htmlTag' => $htmlTag]);
  220. $this->assertTrue($this->_layout->hasElement('container'));
  221. $this->assertTrue($this->_layout->isContainer('container'));
  222. $this->assertEquals($htmlTag, $this->_layout->getElementProperty('container', 'htmlTag'));
  223. $this->_layout->addContainer('container1', 'Container 1', [], 'container', 'c1');
  224. $this->assertEquals('container1', $this->_layout->getChildName('container', 'c1'));
  225. }
  226. public function addContainerDataProvider()
  227. {
  228. return [
  229. ['dd'],
  230. ['div'],
  231. ['dl'],
  232. ['fieldset'],
  233. ['header'],
  234. ['ol'],
  235. ['p'],
  236. ['section'],
  237. ['table'],
  238. ['tfoot'],
  239. ['ul']
  240. ];
  241. }
  242. /**
  243. * @magentoAppIsolation enabled
  244. */
  245. public function testAddContainerInvalidHtmlTag()
  246. {
  247. $msg = 'Html tag "span" is forbidden for usage in containers. ' .
  248. 'Consider to use one of the allowed: aside, dd, div, dl, fieldset, main, nav, ' .
  249. 'header, footer, ol, p, section, table, tfoot, ul.';
  250. $this->expectException(\Magento\Framework\Exception\LocalizedException::class);
  251. $this->expectExceptionMessage($msg);
  252. $this->_layout->addContainer('container', 'Container', ['htmlTag' => 'span']);
  253. }
  254. /**
  255. * @magentoAppIsolation enabled
  256. */
  257. public function testGetChildBlock()
  258. {
  259. $this->_layout->addContainer('parent', 'Parent');
  260. $block = $this->_layout->addBlock(
  261. \Magento\Framework\View\Element\Text::class,
  262. 'block',
  263. 'parent',
  264. 'block_alias'
  265. );
  266. $this->_layout->addContainer('container', 'Container', [], 'parent', 'container_alias');
  267. $this->assertSame($block, $this->_layout->getChildBlock('parent', 'block_alias'));
  268. $this->assertFalse($this->_layout->getChildBlock('parent', 'container_alias'));
  269. }
  270. /**
  271. * @return \Magento\Framework\View\Layout
  272. */
  273. public function testSetChild()
  274. {
  275. $this->_layout->addContainer('one', 'One');
  276. $this->_layout->addContainer('two', 'Two');
  277. $this->_layout->addContainer('three', 'Three');
  278. $this->assertSame($this->_layout, $this->_layout->setChild('one', 'two', ''));
  279. $this->_layout->setChild('one', 'three', 'three_alias');
  280. $this->assertSame(['two', 'three'], $this->_layout->getChildNames('one'));
  281. return $this->_layout;
  282. }
  283. /**
  284. * @param \Magento\Framework\View\LayoutInterface $layout
  285. * @depends testSetChild
  286. */
  287. public function testReorderChild(\Magento\Framework\View\LayoutInterface $layout)
  288. {
  289. $layout->addContainer('four', 'Four', [], 'one');
  290. // offset +1
  291. $layout->reorderChild('one', 'four', 1);
  292. $this->assertSame(['two', 'four', 'three'], $layout->getChildNames('one'));
  293. // offset -2
  294. $layout->reorderChild('one', 'three', 2, false);
  295. $this->assertSame(['two', 'three', 'four'], $layout->getChildNames('one'));
  296. // after sibling
  297. $layout->reorderChild('one', 'two', 'three');
  298. $this->assertSame(['three', 'two', 'four'], $layout->getChildNames('one'));
  299. // after everyone
  300. $layout->reorderChild('one', 'three', '-');
  301. $this->assertSame(['two', 'four', 'three'], $layout->getChildNames('one'));
  302. // before sibling
  303. $layout->reorderChild('one', 'four', 'two', false);
  304. $this->assertSame(['four', 'two', 'three'], $layout->getChildNames('one'));
  305. // before everyone
  306. $layout->reorderChild('one', 'two', '-', false);
  307. $this->assertSame(['two', 'four', 'three'], $layout->getChildNames('one'));
  308. //reorder by sibling alias
  309. $layout->reorderChild('one', 'two', 'three_alias', true);
  310. $this->assertSame(['four', 'three', 'two'], $layout->getChildNames('one'));
  311. }
  312. /**
  313. * @magentoAppIsolation enabled
  314. */
  315. public function testGetChildBlocks()
  316. {
  317. $this->_layout->addContainer('parent', 'Parent');
  318. $block1 = $this->_layout->addBlock(\Magento\Framework\View\Element\Text::class, 'block1', 'parent');
  319. $this->_layout->addContainer('container', 'Container', [], 'parent');
  320. $block2 = $this->_layout->addBlock(\Magento\Framework\View\Element\Template::class, 'block2', 'parent');
  321. $this->assertSame(['block1' => $block1, 'block2' => $block2], $this->_layout->getChildBlocks('parent'));
  322. }
  323. /**
  324. * @expectedException \Magento\Framework\Exception\LocalizedException
  325. */
  326. public function testAddBlockInvalidType()
  327. {
  328. $this->_layout->addBlock('invalid_name', 'child');
  329. }
  330. /**
  331. * @magentoAppIsolation enabled
  332. */
  333. public function testIsContainer()
  334. {
  335. $block = 'block';
  336. $container = 'container';
  337. $this->_layout->addBlock(\Magento\Framework\View\Element\Text::class, $block);
  338. $this->_layout->addContainer($container, 'Container');
  339. $this->assertFalse($this->_layout->isContainer($block));
  340. $this->assertTrue($this->_layout->isContainer($container));
  341. $this->assertFalse($this->_layout->isContainer('invalid_name'));
  342. }
  343. public function testIsManipulationAllowed()
  344. {
  345. $this->_layout->addBlock(\Magento\Framework\View\Element\Text::class, 'block1');
  346. $this->_layout->addBlock(\Magento\Framework\View\Element\Text::class, 'block2', 'block1');
  347. $this->assertFalse($this->_layout->isManipulationAllowed('block1'));
  348. $this->assertFalse($this->_layout->isManipulationAllowed('block2'));
  349. $this->_layout->addContainer('container1', 'Container 1');
  350. $this->_layout->addBlock(\Magento\Framework\View\Element\Text::class, 'block3', 'container1');
  351. $this->_layout->addContainer('container2', 'Container 2', [], 'container1');
  352. $this->assertFalse($this->_layout->isManipulationAllowed('container1'));
  353. $this->assertTrue($this->_layout->isManipulationAllowed('block3'));
  354. $this->assertTrue($this->_layout->isManipulationAllowed('container2'));
  355. }
  356. public function testRenameElement()
  357. {
  358. $blockName = 'block';
  359. $expBlockName = 'block_renamed';
  360. $containerName = 'container';
  361. $expContainerName = 'container_renamed';
  362. $block = $this->_layout->createBlock(\Magento\Framework\View\Element\Text::class, $blockName);
  363. $this->_layout->addContainer($containerName, 'Container');
  364. $this->assertEquals($block, $this->_layout->getBlock($blockName));
  365. $this->_layout->renameElement($blockName, $expBlockName);
  366. $this->assertEquals($block, $this->_layout->getBlock($expBlockName));
  367. $this->_layout->hasElement($containerName);
  368. $this->_layout->renameElement($containerName, $expContainerName);
  369. $this->_layout->hasElement($expContainerName);
  370. }
  371. public function testGetBlock()
  372. {
  373. $this->assertFalse($this->_layout->getBlock('test'));
  374. $block = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
  375. \Magento\Framework\View\Layout::class
  376. )->createBlock(
  377. \Magento\Framework\View\Element\Text::class
  378. );
  379. $this->_layout->setBlock('test', $block);
  380. $this->assertSame($block, $this->_layout->getBlock('test'));
  381. }
  382. /**
  383. * @magentoAppIsolation enabled
  384. */
  385. public function testGetParentName()
  386. {
  387. $this->_layout->addContainer('one', 'One');
  388. $this->_layout->addContainer('two', 'Two', [], 'one');
  389. $this->assertFalse($this->_layout->getParentName('one'));
  390. $this->assertEquals('one', $this->_layout->getParentName('two'));
  391. }
  392. public function testGetElementAlias()
  393. {
  394. $this->_layout->addContainer('one', 'One');
  395. $this->_layout->addContainer('two', 'One', [], 'one', '1');
  396. $this->assertFalse($this->_layout->getElementAlias('one'));
  397. $this->assertEquals('1', $this->_layout->getElementAlias('two'));
  398. }
  399. /**
  400. * @covers \Magento\Framework\View\Layout::addOutputElement
  401. * @covers \Magento\Framework\View\Layout::getOutput
  402. * @covers \Magento\Framework\View\Layout::removeOutputElement
  403. */
  404. public function testGetOutput()
  405. {
  406. $blockName = 'block_' . __METHOD__;
  407. $expectedText = "some_text_for_{$blockName}";
  408. $block = $this->_layout->addBlock(\Magento\Framework\View\Element\Text::class, $blockName);
  409. $block->setText($expectedText);
  410. $this->_layout->addOutputElement($blockName);
  411. // add the same element twice should not produce output duplicate
  412. $this->_layout->addOutputElement($blockName);
  413. $this->assertEquals($expectedText, $this->_layout->getOutput());
  414. $this->_layout->removeOutputElement($blockName);
  415. $this->assertEmpty($this->_layout->getOutput());
  416. }
  417. public function testGetMessagesBlock()
  418. {
  419. $this->assertInstanceOf(\Magento\Framework\View\Element\Messages::class, $this->_layout->getMessagesBlock());
  420. }
  421. public function testGetBlockSingleton()
  422. {
  423. $block = $this->_layout->getBlockSingleton(\Magento\Framework\View\Element\Text::class);
  424. $this->assertInstanceOf(\Magento\Framework\View\Element\Text::class, $block);
  425. $this->assertSame($block, $this->_layout->getBlockSingleton(\Magento\Framework\View\Element\Text::class));
  426. }
  427. public function testUpdateContainerAttributes()
  428. {
  429. $this->_layout->setXml(
  430. simplexml_load_file(
  431. __DIR__ . '/_files/layout/container_attributes.xml',
  432. \Magento\Framework\View\Layout\Element::class
  433. )
  434. );
  435. $this->_layout->generateElements();
  436. $result = $this->_layout->renderElement('container1', false);
  437. $this->assertEquals('<div id="container1-2" class="class12">Test11Test12</div>', $result);
  438. $result = $this->_layout->renderElement('container2', false);
  439. $this->assertEquals('<div id="container2-2" class="class22">Test21Test22</div>', $result);
  440. }
  441. public function testIsCacheable()
  442. {
  443. $this->_layout->setXml(
  444. simplexml_load_file(
  445. __DIR__ . '/_files/layout/cacheable.xml',
  446. \Magento\Framework\View\Layout\Element::class
  447. )
  448. );
  449. $this->_layout->generateElements();
  450. $this->assertTrue($this->_layout->isCacheable());
  451. }
  452. public function testIsNonCacheable()
  453. {
  454. $this->_layout->setXml(
  455. simplexml_load_file(
  456. __DIR__ . '/_files/layout/non_cacheable.xml',
  457. \Magento\Framework\View\Layout\Element::class
  458. )
  459. );
  460. $this->_layout->generateElements();
  461. $this->assertFalse($this->_layout->isCacheable());
  462. }
  463. }