From 1a969c028cdf3ea1391f7607f8f09e4ae27a4ebf Mon Sep 17 00:00:00 2001 From: Roger <rogerdz88@hotmail.com> Date: Fri, 5 Jan 2024 17:10:43 +0000 Subject: [PATCH 1/3] Columns functionality for \Magento\Theme\Block\Html\Topmenu seems broken --- app/code/Magento/Theme/Block/Html/Topmenu.php | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Theme/Block/Html/Topmenu.php b/app/code/Magento/Theme/Block/Html/Topmenu.php index f8460b43ba2ff..2ce3b54ffd7d0 100644 --- a/app/code/Magento/Theme/Block/Html/Topmenu.php +++ b/app/code/Magento/Theme/Block/Html/Topmenu.php @@ -141,7 +141,7 @@ protected function _columnBrake($items, $limit) { $total = $this->_countItems($items); if ($total <= $limit) { - return; + return []; } $result[] = ['total' => $total, 'max' => (int)ceil($total / ceil($total / $limit))]; @@ -151,12 +151,9 @@ protected function _columnBrake($items, $limit) foreach ($items as $item) { $place = $this->_countItems($item->getChildren()) + 1; - $count += $place; + $count++; - if ($place >= $limit) { - $colbrake = !$firstCol; - $count = 0; - } elseif ($count >= $limit) { + if ($count > $limit) { $colbrake = !$firstCol; $count = $place; } else { @@ -242,7 +239,7 @@ protected function _getHtml( } if ($this->shouldAddNewColumn($colBrakes, $counter)) { - $html .= '</ul></li><li class="column"><ul>'; + continue; } $html .= '<li ' . $this->_getRenderedMenuItemAttributes($child) . '>'; @@ -257,10 +254,6 @@ protected function _getHtml( $counter++; } - if (is_array($colBrakes) && !empty($colBrakes) && $limit) { - $html = '<li class="column"><ul>' . $html . '</ul></li>'; - } - return $html; } From 85683bdf583a9f7f731dac1a278242cb8f3da45b Mon Sep 17 00:00:00 2001 From: engcom-Dash <grp-engcom-vendorworker-Dash@adobe.com> Date: Mon, 9 Sep 2024 17:59:07 +0530 Subject: [PATCH 2/3] 38326: Add unit test and update return type of _columnBrake function --- app/code/Magento/Theme/Block/Html/Topmenu.php | 2 +- .../Test/Unit/Block/Html/TopmenuTest.php | 216 ++++++++++++++++-- 2 files changed, 203 insertions(+), 15 deletions(-) mode change 100644 => 100755 app/code/Magento/Theme/Test/Unit/Block/Html/TopmenuTest.php diff --git a/app/code/Magento/Theme/Block/Html/Topmenu.php b/app/code/Magento/Theme/Block/Html/Topmenu.php index 7094a424dae6a..88ec35c552b44 100644 --- a/app/code/Magento/Theme/Block/Html/Topmenu.php +++ b/app/code/Magento/Theme/Block/Html/Topmenu.php @@ -133,7 +133,7 @@ protected function _countItems($items) * * @param Menu $items * @param int $limit - * @return array|void + * @return array * * @todo: Add Depth Level limit, and better logic for columns */ diff --git a/app/code/Magento/Theme/Test/Unit/Block/Html/TopmenuTest.php b/app/code/Magento/Theme/Test/Unit/Block/Html/TopmenuTest.php old mode 100644 new mode 100755 index 5bc0f70dd7703..b2337e652b90f --- a/app/code/Magento/Theme/Test/Unit/Block/Html/TopmenuTest.php +++ b/app/code/Magento/Theme/Test/Unit/Block/Html/TopmenuTest.php @@ -23,6 +23,8 @@ use Magento\Framework\View\Element\Template\Context; use Magento\Store\Model\StoreManagerInterface; use Magento\Theme\Block\Html\Topmenu; +use Magento\Backend\Model\Menu; +use Magento\Backend\Model\Menu\Item as MenuItem; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -76,6 +78,21 @@ class TopmenuTest extends TestCase */ private $requestMock; + /** + * @var Menu + */ + private $menuMock; + + /** + * @var MenuItem + */ + private $menuItemMock; + + /** + * @var Node + */ + private $nodeMock; + // @codingStandardsIgnoreStart /** @var string */ private $navigationMenuHtml = <<<HTML @@ -107,6 +124,21 @@ protected function setUp(): void ->disableOriginalConstructor() ->getMock(); + $this->menuMock = $this->getMockBuilder(Menu::class) + ->onlyMethods(['count', 'getIterator']) + ->disableOriginalConstructor() + ->getMock(); + + $this->menuItemMock = $this->getMockBuilder(MenuItem::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->nodeMock = $this->getMockBuilder(Node::class) + ->disableOriginalConstructor() + ->addMethods(['getClass']) + ->onlyMethods(['getChildren', 'hasChildren', '__call']) + ->getMock(); + $objectManager = new ObjectManager($this); $this->context = $objectManager->getObject( Context::class, @@ -265,14 +297,10 @@ private function buildTree(bool $isCurrentItem): MockObject $children->expects($this->once())->method('count')->willReturn(10); - $nodeMock = $this->getMockBuilder(Node::class) - ->disableOriginalConstructor() - ->onlyMethods(['getChildren', '__call']) - ->getMock(); - $nodeMock->expects($this->once()) + $this->nodeMock->expects($this->once()) ->method('getChildren') ->willReturn($children); - $nodeMock + $this->nodeMock ->method('__call') ->willReturnCallback(function ($arg1, $arg2) { if ($arg1 == 'setOutermostClass') { @@ -291,13 +319,13 @@ private function buildTree(bool $isCurrentItem): MockObject $this->nodeFactory->expects($this->any()) ->method('create') ->with($nodeMockData) - ->willReturn($nodeMock); + ->willReturn($this->nodeMock); $this->treeFactory->expects($this->once()) ->method('create') ->willReturn($treeMock); - return $nodeMock; + return $this->nodeMock; } /** @@ -315,20 +343,180 @@ public function testGetMenu(): void 'tree' => $treeMock ]; - $nodeMock = $this->getMockBuilder(Node::class) - ->disableOriginalConstructor() - ->getMock(); - $this->nodeFactory->expects($this->any()) ->method('create') ->with($nodeMockData) - ->willReturn($nodeMock); + ->willReturn($this->nodeMock); $this->treeFactory->expects($this->once()) ->method('create') ->willReturn($treeMock); $topmenuBlock = $this->getTopmenu(); - $this->assertEquals($nodeMock, $topmenuBlock->getMenu()); + $this->assertEquals($this->nodeMock, $topmenuBlock->getMenu()); + } + + /** + * Test counting items when there are no children. + * @return void + */ + public function testCountItemsNoChildren():void + { + $this->menuMock->expects($this->any()) + ->method('count') + ->willReturn(5); + $this->menuMock->expects($this->any()) + ->method('getIterator') + ->willReturn(new \ArrayIterator([$this->menuItemMock])); + + $this->menuItemMock->expects($this->any()) + ->method('hasChildren') + ->willReturn(false); + + $method = new \ReflectionMethod( + Topmenu::class, + '_countItems' + ); + $method->setAccessible(true); + + $this->assertEquals(5, $method->invoke($this->getTopmenu(), $this->menuMock)); + } + + /** + * Test counting items when there are children. + * @return void + */ + public function testCountItemsWithChildren(): void + { + // Setup child menu mock + $childMenuMock = $this->createMock(Menu::class); + $childMenuMock->expects($this->any()) + ->method('count') + ->willReturn(3); + $childMenuMock->expects($this->any()) + ->method('getIterator') + ->willReturn(new \ArrayIterator([])); + + $this->menuItemMock->expects($this->any()) + ->method('hasChildren') + ->willReturn(true); + $this->menuItemMock->expects($this->any()) + ->method('getChildren') + ->willReturn($childMenuMock); + + // Setup menu mock + $this->menuMock->expects($this->any()) + ->method('count') + ->willReturn(2); + $this->menuMock->expects($this->any()) + ->method('getIterator') + ->willReturn(new \ArrayIterator([$this->menuItemMock, $this->menuItemMock])); + + $method = new \ReflectionMethod( + Topmenu::class, + '_countItems' + ); + $method->setAccessible(true); + + // Total should be 2 (top level) + 2 * 3 (children) = 8 + $this->assertEquals(8, $method->invoke($this->getTopmenu(), $this->menuMock)); + } + + /** + * @return void + * @throws \ReflectionException + */ + public function testColumnBrakeEmptyArray(): void + { + $this->testCountItemsNoChildren(); + + $method = new \ReflectionMethod( + Topmenu::class, + '_columnBrake' + ); + $method->setAccessible(true); + + $this->assertEquals([], $method->invoke($this->getTopmenu(), $this->menuMock, 5)); + } + + /** + * @return void + * @throws \ReflectionException + */ + public function testColumnBrakeWithoutItem(): void + { + $result = [ + [ 'total' => 8, + 'max' => 2 + ], + [ + 'place' => 4, + 'colbrake' => false + ], + [ + 'place' => 4, + 'colbrake' => true + ] + ]; + + $this->testCountItemsWithChildren(); + + $method = new \ReflectionMethod( + Topmenu::class, + '_columnBrake' + ); + $method->setAccessible(true); + + $this->assertEquals($result, $method->invoke($this->getTopmenu(), $this->menuMock, 2)); + } + + /** + * @return void + */ + public function testAddSubMenu(): void + { + $container = $this->createMock(CategoryTree::class); + + $children = $this->getMockBuilder(Collection::class) + ->onlyMethods(['count']) + ->setConstructorArgs(['container' => $container]) + ->getMock(); + + $this->nodeMock->expects($this->atLeastOnce()) + ->method('hasChildren') + ->willReturn(true); + + $this->nodeMock->expects($this->any()) + ->method('getChildren') + ->willReturn($children); + + $method = new \ReflectionMethod( + Topmenu::class, + '_addSubMenu' + ); + $method->setAccessible(true); + + $this->assertEquals( + '<ul class="level0 "></ul>', + $method->invoke($this->getTopmenu(), $this->nodeMock, 0, '', 2) + ); + } + + /** + * @return void + */ + public function testSetCurrentClass(): void + { + $this->nodeMock->expects($this->once()) + ->method('getClass') + ->willReturn(null); + + $method = new \ReflectionMethod( + Topmenu::class, + 'setCurrentClass' + ); + $method->setAccessible(true); + + $method->invoke($this->getTopmenu(), $this->nodeMock, ''); } } From 49a1dd7fa3b770b1d99b959a8e51d2a45b0a07a5 Mon Sep 17 00:00:00 2001 From: engcom-Dash <grp-engcom-vendorworker-Dash@adobe.com> Date: Tue, 10 Sep 2024 11:25:02 +0530 Subject: [PATCH 3/3] 32386: Fix unit test failure for testColumnBrakeWithoutItem method --- app/code/Magento/Theme/Test/Unit/Block/Html/TopmenuTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Theme/Test/Unit/Block/Html/TopmenuTest.php b/app/code/Magento/Theme/Test/Unit/Block/Html/TopmenuTest.php index b2337e652b90f..53368d379d05f 100755 --- a/app/code/Magento/Theme/Test/Unit/Block/Html/TopmenuTest.php +++ b/app/code/Magento/Theme/Test/Unit/Block/Html/TopmenuTest.php @@ -455,7 +455,7 @@ public function testColumnBrakeWithoutItem(): void ], [ 'place' => 4, - 'colbrake' => true + 'colbrake' => false ] ];