/** * @dataProvider processDataProvider */ public function testProcess($xml) { $this->element = new \Magento\Framework\View\Layout\Element($xml); $this->context->expects($this->any())->method('getScheduledStructure')->will($this->returnValue($this->scheduledStructure)); $this->scheduledStructure->expects($this->once())->method('setElementToRemoveList')->with('header'); $this->model->interpret($this->context, $this->element, $this->element); }
/** * @param array $schedule * @param array $expectedSchedule * @return void * @dataProvider processDataProvider */ public function testProcess($schedule, $expectedSchedule) { foreach ($schedule['structure'] as $structureElement) { $this->scheduledStructure->setStructureElement($structureElement, []); } $reorderMap = []; foreach ($schedule['sort'] as $elementName => $sort) { list($parentName, $sibling, $isAfter) = $sort; $this->scheduledStructure->setElementToSortList($parentName, $elementName, $sibling, $isAfter); $reorderMap[] = [$parentName, $elementName, $sibling, $isAfter]; } foreach ($schedule['move'] as $elementName => $move) { $this->scheduledStructure->setElementToMove($elementName, $move); list($destination, $sibling, $isAfter) = $move; $reorderMap[] = [$destination, $elementName, $sibling, $isAfter]; } $invocation = $this->structureMock->expects($this->any())->method('reorderChildElement'); call_user_func_array([$invocation, 'withConsecutive'], $reorderMap); foreach ($schedule['remove'] as $remove) { $this->scheduledStructure->setElementToRemoveList($remove); } $this->helperMock->expects($this->atLeastOnce())->method('scheduleElement')->with($this->scheduledStructure, $this->structureMock, $this->anything())->willReturnCallback(function ($scheduledStructure, $structure, $elementName) use($schedule) { /** * @var $scheduledStructure ScheduledStructure * @var $structure DataStructure */ $this->assertContains($elementName, $schedule['structure']); $scheduledStructure->unsetStructureElement($elementName); $scheduledStructure->setElement($elementName, []); $structure->createStructuralElement($elementName, 'block', 'someClass'); }); $this->model->process($this->readerContextMock, $this->generatorContextMock); $this->assertEquals($expectedSchedule, $this->scheduledStructure->getElements()); }
/** * Remove scheduled element * * @param ScheduledStructure $scheduledStructure * @param Structure $structure * @param string $elementName * @param bool $isChild * @return $this */ protected function removeElement(ScheduledStructure $scheduledStructure, Structure $structure, $elementName, $isChild = false) { $elementsToRemove = array_keys($structure->getChildren($elementName)); $scheduledStructure->unsetElement($elementName); foreach ($elementsToRemove as $element) { $this->removeElement($scheduledStructure, $structure, $element, true); } if (!$isChild) { $structure->unsetElement($elementName); } return $this; }
/** * Schedule structural changes for move directive * * @param \Magento\Framework\View\Layout\ScheduledStructure $scheduledStructure * @param \Magento\Framework\View\Layout\Element $currentElement * @throws \Magento\Framework\Exception\LocalizedException * @return $this */ protected function scheduleMove(Layout\ScheduledStructure $scheduledStructure, Layout\Element $currentElement) { $elementName = (string) $currentElement->getAttribute('element'); $destination = (string) $currentElement->getAttribute('destination'); $alias = (string) $currentElement->getAttribute('as') ?: ''; if ($elementName && $destination) { list($siblingName, $isAfter) = $this->beforeAfterToSibling($currentElement); $scheduledStructure->setElementToMove($elementName, [$destination, $siblingName, $isAfter, $alias]); } else { throw new \Magento\Framework\Exception\LocalizedException(new \Magento\Framework\Phrase('Element name and destination must be specified.')); } return $this; }
/** * @return void */ public function testIsListToSortEmpty() { list($parentName, $name, $sibling, $isAfter) = $this->getDataSort(); $this->assertTrue($this->model->isListToSortEmpty()); $this->model->setElementToSortList($parentName, $name, $sibling, $isAfter); $this->assertFalse($this->model->isListToSortEmpty()); }
/** * @param string $literal * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $getCondition * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $setCondition * @dataProvider processReferenceDataProvider */ public function testProcessReference($literal, $getCondition, $setCondition) { $this->context->expects($this->once())->method('getScheduledStructure')->will($this->returnValue($this->scheduledStructure)); $this->scheduledStructure->expects($getCondition)->method('getStructureElementData')->with($literal, [])->willReturn(['actions' => [['someMethod', [], 'action_config_path', 'scope']]]); $this->scheduledStructure->expects($setCondition)->method('setStructureElementData')->with($literal, ['actions' => [['someMethod', [], 'action_config_path', 'scope']], 'arguments' => []]); $this->prepareReaderPool('<' . $literal . ' name="' . $literal . '">' . '<action method="someMethod" ifconfig="action_config_path" />' . '</' . $literal . '>', $literal); /** @var \Magento\Framework\View\Layout\Reader\Block $block */ $block = $this->getBlock(['readerPool' => $this->readerPool, 'scopeType' => 'scope']); $block->interpret($this->context, $this->currentElement); }
/** * @covers \Magento\Framework\View\Layout\ScheduledStructure::flushScheduledStructure */ public function testFlushScheduledStructure() { $this->assertNotEmpty($this->_model->getPaths()); $this->assertNotEmpty($this->_model->getElements()); $this->assertNotEmpty($this->_model->getStructure()); $this->_model->flushScheduledStructure(); $this->assertEmpty($this->_model->getPaths()); $this->assertEmpty($this->_model->getElements()); $this->assertEmpty($this->_model->getStructure()); }
/** * covers \Magento\Framework\View\Layout\ScheduledStructure::setElementToBrokenParentList * covers \Magento\Framework\View\Layout\ScheduledStructure::unsetElementFromBrokenParentList */ public function testSetElementToBrokenParentList() { $element = 'element9'; $expectedToRemove = ['element2', 'element3']; $expectedToRemoveWithBroken = ['element2', 'element3']; $this->assertEquals($expectedToRemove, $this->_model->getListToRemove()); $this->_model->setElementToBrokenParentList($element); $this->assertEquals($expectedToRemoveWithBroken, $this->_model->getListToRemove()); $this->_model->unsetElementFromBrokenParentList($element); $this->assertEquals($expectedToRemove, $this->_model->getListToRemove()); }
/** * @covers Magento\Framework\View\Layout\Reader\Block::interpret() */ public function testProcessReference() { $testName = 'test_value'; $literal = 'referenceBlock'; $this->context->expects($this->once())->method('getScheduledStructure')->will($this->returnValue($this->scheduledStructure)); $this->scheduledStructure->expects($this->once())->method('getStructureElementData')->with($testName, [])->will($this->returnValue([])); $this->scheduledStructure->expects($this->once())->method('setStructureElementData')->with($testName, ['actions' => [], 'arguments' => []]); $this->prepareReaderPool('<' . $literal . ' name="' . $testName . '"/>'); /** @var \Magento\Framework\View\Layout\Reader\Block $block */ $block = $this->getBlock(['readerPool' => $this->readerPool]); $block->interpret($this->context, $this->currentElement); }
/** * Merge Container attributes * * @param \Magento\Framework\View\Layout\ScheduledStructure $scheduledStructure * @param \Magento\Framework\View\Layout\Element $currentElement * @return void */ protected function mergeContainerAttributes(Layout\ScheduledStructure $scheduledStructure, Layout\Element $currentElement) { $containerName = $currentElement->getAttribute('name'); $elementData = $scheduledStructure->getStructureElementData($containerName); if (isset($elementData['attributes'])) { $keys = array_keys($elementData['attributes']); foreach ($keys as $key) { if (isset($currentElement[$key])) { $elementData['attributes'][$key] = (string) $currentElement[$key]; } } } else { $elementData['attributes'] = [self::CONTAINER_OPT_HTML_TAG => (string) $currentElement[self::CONTAINER_OPT_HTML_TAG], self::CONTAINER_OPT_HTML_ID => (string) $currentElement[self::CONTAINER_OPT_HTML_ID], self::CONTAINER_OPT_HTML_CLASS => (string) $currentElement[self::CONTAINER_OPT_HTML_CLASS], self::CONTAINER_OPT_LABEL => (string) $currentElement[self::CONTAINER_OPT_LABEL]]; } $scheduledStructure->setStructureElementData($containerName, $elementData); }
/** * @param bool $hasParent * @param int $setAsChild * @param int $toRemoveList * @param string $siblingName * @param bool $isAfter * @param int $toSortList * @return void * * @dataProvider scheduleElementDataProvider */ public function testScheduleElement($hasParent, $setAsChild, $toRemoveList, $siblingName, $isAfter, $toSortList) { $key = 'key'; $parentName = 'parent'; $alias = 'alias'; $block = 'block'; $data = ['data']; $this->scheduledStructureMock->expects($this->any())->method('getStructureElement')->willReturnMap([[$key, null, [Layout\ScheduledStructure\Helper::SCHEDULED_STRUCTURE_INDEX_TYPE => $block, Layout\ScheduledStructure\Helper::SCHEDULED_STRUCTURE_INDEX_ALIAS => $alias, Layout\ScheduledStructure\Helper::SCHEDULED_STRUCTURE_INDEX_PARENT_NAME => $parentName, Layout\ScheduledStructure\Helper::SCHEDULED_STRUCTURE_INDEX_SIBLING_NAME => $siblingName, Layout\ScheduledStructure\Helper::SCHEDULED_STRUCTURE_INDEX_IS_AFTER => $isAfter]], [$parentName, null, []]]); $this->scheduledStructureMock->expects($this->any())->method('getStructureElementData')->willReturnMap([[$key, null, $data], [$parentName, null, $data]]); $this->scheduledStructureMock->expects($this->any())->method('hasStructureElement')->willReturn(true); $this->scheduledStructureMock->expects($this->once())->method('setElement')->with($key, [$block, $data]); $this->dataStructureMock->expects($this->once())->method('createElement')->with($key, ['type' => $block]); $this->dataStructureMock->expects($this->once())->method('hasElement')->with($parentName)->willReturn($hasParent); $this->dataStructureMock->expects($this->exactly($setAsChild))->method('setAsChild')->with($key, $parentName, $alias)->willReturn(true); $this->scheduledStructureMock->expects($this->exactly($toRemoveList))->method('setElementToBrokenParentList')->with($key); $this->scheduledStructureMock->expects($this->exactly($toSortList))->method('setElementToSortList')->with($parentName, $key, $siblingName, $isAfter); $this->helper->scheduleElement($this->scheduledStructureMock, $this->dataStructureMock, $key); }
/** * @param string $name */ protected function prepareGenerateBlock($name) { $blockClass = 'Magento\\Framework\\View\\Element\\Template'; $template = 'file.phtml'; $ttl = 100; $xmlString = '<?xml version="1.0"?><block class="' . $blockClass . '" template="' . $template . '" ttl="' . $ttl . '"></block>'; $xml = simplexml_load_string($xmlString, 'Magento\\Framework\\View\\Layout\\Element'); $elementData = [\Magento\Framework\View\Layout\Element::TYPE_BLOCK, $xml, [], []]; $this->schStructureMock->expects($this->once())->method('getElement')->with($this->equalTo($name))->will($this->returnValue($elementData)); $this->schStructureMock->expects($this->once())->method('unsetElement')->with($this->equalTo($name))->will($this->returnSelf()); $blockMock = $this->getMockBuilder('Magento\\Framework\\View\\Element\\Template')->setMethods(['setType', 'setNameInLayout', 'addData', 'setLayout', 'setTemplate', 'setTtl'])->disableOriginalConstructor()->getMock(); $blockMock->expects($this->once())->method('setTemplate')->with($this->equalTo($template))->will($this->returnSelf()); $blockMock->expects($this->once())->method('setTtl')->with($this->equalTo($ttl))->will($this->returnSelf()); $blockMock->expects($this->once())->method('setType')->with($this->equalTo(get_class($blockMock)))->will($this->returnSelf()); $blockMock->expects($this->once())->method('setNameInLayout')->with($this->equalTo($name))->will($this->returnSelf()); $blockMock->expects($this->once())->method('addData')->with($this->equalTo([]))->will($this->returnSelf()); $blockMock->expects($this->once())->method('setLayout')->with($this->equalTo($this->model))->will($this->returnSelf()); $this->blockFactoryMock->expects($this->once())->method('createBlock')->with($this->equalTo('Magento\\Framework\\View\\Element\\Template'), $this->equalTo(['data' => []]))->will($this->returnValue($blockMock)); $this->eventManagerMock->expects($this->once())->method('dispatch')->with($this->equalTo('core_layout_block_create_after'), $this->equalTo(['block' => $blockMock]))->will($this->returnSelf()); }
/** * Process queue of structural elements and actually add them to structure, and schedule elements for generation * * The catch is to populate parents first, if they are not in the structure yet. * Since layout updates could come in arbitrary order, a case is possible where an element is declared in reference, * while referenced element itself is not declared yet. * * @param \Magento\Framework\View\Layout\ScheduledStructure $scheduledStructure * @param Layout\Data\Structure $structure * @param string $key in _scheduledStructure represent element name * @return void * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function scheduleElement(Layout\ScheduledStructure $scheduledStructure, Layout\Data\Structure $structure, $key) { $row = $scheduledStructure->getStructureElement($key); $data = $scheduledStructure->getStructureElementData($key); // if we have reference container to not existed element if (!isset($row[self::SCHEDULED_STRUCTURE_INDEX_TYPE])) { $this->logger->critical("Broken reference: missing declaration of the element '{$key}'."); $scheduledStructure->unsetPathElement($key); $scheduledStructure->unsetStructureElement($key); return; } list($type, $alias, $parentName, $siblingName, $isAfter) = $row; $name = $this->_createStructuralElement($structure, $key, $type, $parentName . $alias); if ($parentName) { // recursively populate parent first if ($scheduledStructure->hasStructureElement($parentName)) { $this->scheduleElement($scheduledStructure, $structure, $parentName); } if ($structure->hasElement($parentName)) { try { $structure->setAsChild($name, $parentName, $alias); } catch (\Exception $e) { $this->logger->critical($e); } } else { $scheduledStructure->setElementToBrokenParentList($key); $this->logger->critical("Broken reference: the '{$name}' element cannot be added as child to '{$parentName}', " . 'because the latter doesn\'t exist'); } } // Move from scheduledStructure to scheduledElement $scheduledStructure->unsetStructureElement($key); $scheduledStructure->setElement($name, [$type, $data]); /** * Some elements provide info "after" or "before" which sibling they are supposed to go * Make sure to populate these siblings as well and order them correctly */ if ($siblingName) { if ($scheduledStructure->hasStructureElement($siblingName)) { $this->scheduleElement($scheduledStructure, $structure, $siblingName); } $structure->reorderChildElement($parentName, $name, $siblingName, $isAfter); } }
/** * Schedule reference data * * @param Layout\ScheduledStructure $scheduledStructure * @param Layout\Element $currentElement * @return void */ protected function scheduleReference(Layout\ScheduledStructure $scheduledStructure, Layout\Element $currentElement) { $elementName = $currentElement->getAttribute('name'); $data = $scheduledStructure->getStructureElementData($elementName, []); $this->updateScheduledData($currentElement, $data); $this->evaluateArguments($currentElement, $data); $scheduledStructure->setStructureElementData($elementName, $data); }
/** * @param \Magento\Framework\View\Layout\Element $currentElement * @param string $destination * @param string $siblingName * @param bool $isAfter * @param string $alias * @param \Magento\Framework\View\Layout\Element $parentElement * * @dataProvider processDataProvider */ public function testProcess($currentElement, $destination, $siblingName, $isAfter, $alias, $parentElement) { $this->scheduledStructureMock->expects($this->any())->method('setElementToMove')->with((string) $currentElement->getAttribute('element'), [$destination, $siblingName, $isAfter, $alias]); $this->move->interpret($this->contextMock, $currentElement, $parentElement); }
/** * Get block object by name * * @param string $name * @return \Magento\Framework\View\Element\AbstractBlock|bool */ public function getBlock($name) { if ($this->_scheduledStructure->hasElement($name)) { $this->_generateBlock($name); } if (isset($this->_blocks[$name])) { return $this->_blocks[$name]; } else { return false; } }
/** * Handling reference of container * * If attribute remove="true" then add the element to list remove, * else merge container attributes * * @param Layout\ScheduledStructure $scheduledStructure * @param Layout\Element $currentElement * @return void */ protected function containerReference(Layout\ScheduledStructure $scheduledStructure, Layout\Element $currentElement) { $containerName = $currentElement->getAttribute('name'); $containerRemove = filter_var($currentElement->getAttribute('remove'), FILTER_VALIDATE_BOOLEAN); if ($containerRemove) { $scheduledStructure->setElementToRemoveList($containerName); } else { $this->mergeContainerAttributes($scheduledStructure, $currentElement); } }
/** * Create block and set related data * * @param \Magento\Framework\View\Layout\ScheduledStructure $scheduledStructure * @param \Magento\Framework\View\Layout\Data\Structure $structure * @param string $elementName * @return \Magento\Framework\View\Element\AbstractBlock */ protected function generateBlock(Layout\ScheduledStructure $scheduledStructure, Layout\Data\Structure $structure, $elementName) { list(, $data) = $scheduledStructure->getElement($elementName); $attributes = $data['attributes']; if (!empty($attributes['group'])) { $structure->addToParentGroup($elementName, $attributes['group']); } if (!empty($attributes['display'])) { $structure->setAttribute($elementName, 'display', $attributes['display']); } // create block $className = $attributes['class']; $block = $this->createBlock($className, $elementName, ['data' => $this->evaluateArguments($data['arguments'])]); if (!empty($attributes['template'])) { $block->setTemplate($attributes['template']); } if (!empty($attributes['ttl'])) { $ttl = (int) $attributes['ttl']; $block->setTtl($ttl); } return $block; }
/** * @param array $structureElements * * @dataProvider processWithExceptionDataProvider * @expectedException \Magento\Framework\Exception\LocalizedException */ public function testProcessWithException($structureElements) { $this->scheduledStructureMock->expects($this->once())->method('getElements')->willReturn($structureElements); $this->structureMock->expects($this->once())->method('setAttribute')->willReturnSelf(); $this->container->process($this->readerContextMock, $this->generatorContextMock); }
protected function prepareScheduledStructure() { $this->scheduledStructureMock->expects($this->any())->method('getElements')->willReturn([UiComponent::TYPE => [UiComponent::TYPE, ['attributes' => ['group' => 'new_group', 'component' => 'component_name'], 'arguments' => ['attribute_1' => ['key_1' => 'value_1'], 'attribute_2' => ['key_2' => 'value_2']]]]]); }
/** * Schedule reference data * * @param Layout\ScheduledStructure $scheduledStructure * @param Layout\Element $currentElement * @return void */ protected function scheduleReference(Layout\ScheduledStructure $scheduledStructure, Layout\Element $currentElement) { $elementName = $currentElement->getAttribute('name'); $elementRemove = filter_var($currentElement->getAttribute('remove'), FILTER_VALIDATE_BOOLEAN); if ($elementRemove) { $scheduledStructure->setElementToRemoveList($elementName); } else { $data = $scheduledStructure->getStructureElementData($elementName, []); $data['attributes'] = $this->mergeBlockAttributes($data, $currentElement); $this->updateScheduledData($currentElement, $data); $this->evaluateArguments($currentElement, $data); $scheduledStructure->setStructureElementData($elementName, $data); } }