diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/AddAttributeToTemplate.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/AddAttributeToTemplate.php index 8a2b76b1bbd56212f27cb45db587e38fc2bb57c8..c338937ae1be1df330d32c4992f3cb5839ebad05 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/AddAttributeToTemplate.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/AddAttributeToTemplate.php @@ -16,7 +16,6 @@ use Magento\Eav\Api\Data\AttributeGroupInterfaceFactory; use Magento\Eav\Api\Data\AttributeInterface; use Magento\Eav\Api\Data\AttributeSetInterface; use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\Api\SortOrderBuilder; use Magento\Framework\Exception\LocalizedException; use Psr\Log\LoggerInterface; use Magento\Framework\Api\ExtensionAttributesFactory; @@ -53,11 +52,6 @@ class AddAttributeToTemplate extends \Magento\Catalog\Controller\Adminhtml\Produ */ protected $searchCriteriaBuilder; - /** - * @var SortOrderBuilder - */ - protected $sortOrderBuilder; - /** * @var AttributeGroupInterfaceFactory */ @@ -115,7 +109,6 @@ class AddAttributeToTemplate extends \Magento\Catalog\Controller\Adminhtml\Produ $attributeGroupSearchCriteria = $this->getSearchCriteriaBuilder() ->addFilter('attribute_set_id', $attributeSet->getAttributeSetId()) ->addFilter('attribute_group_code', $groupCode) - ->addSortOrder($this->getSortOrderBuilder()->setAscendingDirection()->create()) ->setPageSize(1) ->create(); @@ -252,18 +245,6 @@ class AddAttributeToTemplate extends \Magento\Catalog\Controller\Adminhtml\Produ return $this->searchCriteriaBuilder; } - /** - * @return SortOrderBuilder - */ - private function getSortOrderBuilder() - { - if (null === $this->sortOrderBuilder) { - $this->sortOrderBuilder = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\Api\SortOrderBuilder::class); - } - return $this->sortOrderBuilder; - } - /** * @return AttributeManagementInterface */ diff --git a/app/code/Magento/Catalog/Model/System/Config/Backend/Catalog/Url/Rewrite/Suffix.php b/app/code/Magento/Catalog/Model/System/Config/Backend/Catalog/Url/Rewrite/Suffix.php index 5ed18d15b2f304cdc9ca6e89a7bcc09e2a5c3590..c1f37eca1c55c7b81cf4b14a034738c5211b7085 100644 --- a/app/code/Magento/Catalog/Model/System/Config/Backend/Catalog/Url/Rewrite/Suffix.php +++ b/app/code/Magento/Catalog/Model/System/Config/Backend/Catalog/Url/Rewrite/Suffix.php @@ -40,6 +40,11 @@ class Suffix extends \Magento\Framework\App\Config\Value */ protected $resource; + /** + * @var \Magento\Framework\App\Config\ScopePool + */ + private $scopePool; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -75,6 +80,22 @@ class Suffix extends \Magento\Framework\App\Config\Value $this->resource = $appResource; } + /** + * Get instance of ScopePool + * + * @return \Magento\Framework\App\Config\ScopePool + * @deprecated + */ + private function getScopePool() + { + if ($this->scopePool === null) { + $this->scopePool = \Magento\Framework\App\ObjectManager::getInstance()->get( + \Magento\Framework\App\Config\ScopePool::class + ); + } + return $this->scopePool; + } + /** * Check url rewrite suffix - whether we can support it * @@ -103,6 +124,24 @@ class Suffix extends \Magento\Framework\App\Config\Value return parent::afterSave(); } + /** + * {@inheritdoc} + */ + public function afterDeleteCommit() + { + if ($this->isValueChanged()) { + $this->updateSuffixForUrlRewrites(); + if ($this->isCategorySuffixChanged()) { + $this->cacheTypeList->invalidate([ + \Magento\Framework\App\Cache\Type\Block::TYPE_IDENTIFIER, + \Magento\Framework\App\Cache\Type\Collection::TYPE_IDENTIFIER + ]); + } + } + + return parent::afterDeleteCommit(); + } + /** * Check is category suffix changed * @@ -135,7 +174,12 @@ class Suffix extends \Magento\Framework\App\Config\Value } $entities = $this->urlFinder->findAllByData($dataFilter); $oldSuffixPattern = '~' . preg_quote($this->getOldValue()) . '$~'; - $suffix = $this->getValue(); + if ($this->getValue() !== null) { + $suffix = $this->getValue(); + } else { + $this->getScopePool()->clean(); + $suffix = $this->_config->getValue($this->getPath()); + } foreach ($entities as $urlRewrite) { $bind = $urlRewrite->getIsAutogenerated() ? [UrlRewrite::REQUEST_PATH => preg_replace($oldSuffixPattern, $suffix, $urlRewrite->getRequestPath())] diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/AddAttributeToTemplateTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/AddAttributeToTemplateTest.php new file mode 100644 index 0000000000000000000000000000000000000000..a55766f895b62511ced21eedaacc67f6c29811ae --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/AddAttributeToTemplateTest.php @@ -0,0 +1,247 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Test\Unit\Controller\Adminhtml\Product; + +use Magento\Catalog\Controller\Adminhtml\Product\AddAttributeToTemplate; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Backend\App\Action\Context; +use Magento\Catalog\Controller\Adminhtml\Product\Builder as ProductBuilder; +use Magento\Framework\Controller\Result\JsonFactory; +use Magento\Framework\App\RequestInterface; +use Magento\Catalog\Api\AttributeSetRepositoryInterface; +use Magento\Eav\Api\Data\AttributeSetInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Api\SearchCriteria; +use Magento\Eav\Api\AttributeGroupRepositoryInterface; +use Magento\Eav\Api\Data\AttributeGroupSearchResultsInterface; +use Magento\Eav\Api\Data\AttributeGroupInterfaceFactory; +use Magento\Eav\Api\Data\AttributeGroupInterface; +use Magento\Framework\Controller\Result\Json; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class AddAttributeToTemplateTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var AddAttributeToTemplate + */ + private $controller; + + /** + * @var Context|\PHPUnit_Framework_MockObject_MockObject + */ + private $contextMock; + + /** + * @var ProductBuilder|\PHPUnit_Framework_MockObject_MockObject + */ + private $productBuilderMock; + + /** + * @var JsonFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $resultJsonFactoryMock; + + /** + * @var RequestInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $requestMock; + + /** + * @var AttributeSetRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $attributeSetRepositoryMock; + + /** + * @var AttributeSetInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $attributeSetInterfaceMock; + + /** + * @var SearchCriteriaBuilder|\PHPUnit_Framework_MockObject_MockObject + */ + private $searchCriteriaBuilderMock; + + /** + * @var SearchCriteria|\PHPUnit_Framework_MockObject_MockObject + */ + private $searchCriteriaMock; + + /** + * @var AttributeGroupRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $attributeGroupRepositoryMock; + + /** + * @var AttributeGroupSearchResultsInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $attributeGroupSearchResultsMock; + + /** + * @var AttributeGroupInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $attributeGroupInterfaceFactoryMock; + + /** + * @var AttributeGroupInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $attributeGroupInterfaceMock; + + /** + * @var Json|\PHPUnit_Framework_MockObject_MockObject + */ + private $jsonMock; + + protected function setUp() + { + $this->objectManager = new ObjectManager($this); + $this->contextMock = $this->getMockBuilder(Context::class) + ->disableOriginalConstructor() + ->getMock(); + $this->productBuilderMock = $this->getMockBuilder(ProductBuilder::class) + ->disableOriginalConstructor() + ->getMock(); + $this->resultJsonFactoryMock = $this->getMockBuilder(JsonFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) + ->setMethods(['getParam', 'setParam']) + ->getMockForAbstractClass(); + $this->contextMock->expects($this->once()) + ->method('getRequest') + ->willReturn($this->requestMock); + $this->attributeSetRepositoryMock = $this->getMockBuilder(AttributeSetRepositoryInterface::class) + ->setMethods(['get']) + ->getMockForAbstractClass(); + $this->attributeSetInterfaceMock = $this->getMockBuilder(AttributeSetInterface::class) + ->getMockForAbstractClass(); + $this->searchCriteriaBuilderMock = $this->getMockBuilder(SearchCriteriaBuilder::class) + ->disableOriginalConstructor() + ->setMethods(['addFilter', 'create', 'setPageSize']) + ->getMockForAbstractClass(); + $this->searchCriteriaMock = $this->getMockBuilder(SearchCriteria::class) + ->disableOriginalConstructor() + ->getMock(); + $this->attributeGroupRepositoryMock = $this->getMockBuilder(AttributeGroupRepositoryInterface::class) + ->setMethods(['getList']) + ->getMockForAbstractClass(); + $this->attributeGroupSearchResultsMock = $this->getMockBuilder(AttributeGroupSearchResultsInterface::class) + ->setMethods(['getItems']) + ->getMockForAbstractClass(); + $this->attributeGroupInterfaceFactoryMock = $this->getMockBuilder(AttributeGroupInterfaceFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + $this->attributeGroupInterfaceMock = $this->getMockBuilder(AttributeGroupInterface::class) + ->setMethods(['getExtensionAttributes']) + ->getMockForAbstractClass(); + $this->jsonMock = $this->getMockBuilder(Json::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->controller = $this->objectManager->getObject( + AddAttributeToTemplate::class, + [ + 'context' => $this->contextMock, + 'productBuilder' => $this->productBuilderMock, + 'resultJsonFactory' => $this->resultJsonFactoryMock, + ] + ); + + $this->objectManager->setBackwardCompatibleProperty( + $this->controller, + 'attributeSetRepository', + $this->attributeSetRepositoryMock + ); + $this->objectManager->setBackwardCompatibleProperty( + $this->controller, + 'searchCriteriaBuilder', + $this->searchCriteriaBuilderMock + ); + $this->objectManager->setBackwardCompatibleProperty( + $this->controller, + 'attributeGroupRepository', + $this->attributeGroupRepositoryMock + ); + $this->objectManager->setBackwardCompatibleProperty( + $this->controller, + 'attributeGroupFactory', + $this->attributeGroupInterfaceFactoryMock + ); + } + + public function testExecuteWithoutAttributeGroupItems() + { + $groupCode = 'attributes'; + $groupName = 'Attributes'; + $groupSortOrder = '15'; + $templateId = '4'; + $attributeIds = [ + 'selected' => ["178"], + 'total' => '1' + ]; + + $this->requestMock + ->expects($this->any()) + ->method('getParam') + ->willReturnMap( + [ + ['groupCode', null, $groupCode], + ['groupName', null, $groupName], + ['groupSortOrder', null, $groupSortOrder], + ['templateId', null, $templateId], + ['attributeIds', [], $attributeIds] + ] + ); + + $this->attributeSetRepositoryMock->expects($this->once()) + ->method('get') + ->willReturn($this->attributeSetInterfaceMock); + + $this->searchCriteriaBuilderMock->expects($this->any()) + ->method('addFilter') + ->willReturnSelf(); + $this->searchCriteriaBuilderMock->expects($this->any()) + ->method('create') + ->willReturn($this->searchCriteriaMock); + $this->searchCriteriaBuilderMock->expects($this->once()) + ->method('setPageSize') + ->willReturnSelf(); + $this->searchCriteriaBuilderMock->expects($this->never()) + ->method('addSortOrder') + ->willReturnSelf(); + + $this->attributeGroupRepositoryMock->expects($this->once()) + ->method('getList') + ->willReturn($this->attributeGroupSearchResultsMock); + $this->attributeGroupSearchResultsMock->expects($this->once()) + ->method('getItems') + ->willReturn(null); + + $this->attributeGroupInterfaceFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->attributeGroupInterfaceMock); + $this->attributeGroupInterfaceMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willThrowException(new LocalizedException(__('Could not get extension attributes'))); + + $this->resultJsonFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->jsonMock); + $this->jsonMock->expects($this->once())->method('setJsonData') + ->willReturnSelf(); + + $this->controller->execute(); + } +} diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js index ddb62ceb37b37ef6bc7735c78f81367c5c6d4343..8910e41731d11a9be99bf1777dc13e3550781976 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js @@ -179,7 +179,7 @@ define( newShippingAddress; this.source.set('params.invalid', false); - this.source.trigger('shippingAddress.data.validate'); + this.triggerShippingDataValidateEvent(); if (!this.source.get('params.invalid')) { addressData = this.source.get('shippingAddress'); @@ -254,12 +254,7 @@ define( if (this.isFormInline) { this.source.set('params.invalid', false); - this.source.trigger('shippingAddress.data.validate'); - - if (this.source.get('shippingAddress.custom_attributes')) { - this.source.trigger('shippingAddress.custom_attributes.data.validate'); - } - + this.triggerShippingDataValidateEvent(); if (emailValidationResult && this.source.get('params.invalid') || !quote.shippingMethod().method_code || @@ -304,6 +299,18 @@ define( } return true; + }, + + /** + * Trigger Shipping data Validate Event. + * + * @return {void} + */ + triggerShippingDataValidateEvent: function () { + this.source.trigger('shippingAddress.data.validate'); + if (this.source.get('shippingAddress.custom_attributes')) { + this.source.trigger('shippingAddress.custom_attributes.data.validate'); + } } }); } diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php b/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php index 9c2aeeec4abf5d2817df474c4a7c1cfbb818df6f..2b5bbc406d4690602acff51e79904a82aaad9151 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php @@ -5,6 +5,9 @@ */ namespace Magento\Eav\Model\Entity\Attribute\Source; +use Magento\Framework\App\ObjectManager; +use Magento\Store\Model\StoreManagerInterface; + class Table extends \Magento\Eav\Model\Entity\Attribute\Source\AbstractSource { /** @@ -24,6 +27,11 @@ class Table extends \Magento\Eav\Model\Entity\Attribute\Source\AbstractSource */ protected $_attrOptionFactory; + /** + * @var StoreManagerInterface + */ + private $storeManager; + /** * @param \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\CollectionFactory $attrOptionCollectionFactory * @param \Magento\Eav\Model\ResourceModel\Entity\Attribute\OptionFactory $attrOptionFactory @@ -47,24 +55,30 @@ class Table extends \Magento\Eav\Model\Entity\Attribute\Source\AbstractSource public function getAllOptions($withEmpty = true, $defaultValues = false) { $storeId = $this->getAttribute()->getStoreId(); + if ($storeId === null) { + $storeId = $this->getStoreManager()->getStore()->getId(); + } if (!is_array($this->_options)) { $this->_options = []; } if (!is_array($this->_optionsDefault)) { $this->_optionsDefault = []; } - if (!isset($this->_options[$storeId])) { + $attributeId = $this->getAttribute()->getId(); + if (!isset($this->_options[$storeId][$attributeId])) { $collection = $this->_attrOptionCollectionFactory->create()->setPositionOrder( 'asc' )->setAttributeFilter( - $this->getAttribute()->getId() + $attributeId )->setStoreFilter( - $this->getAttribute()->getStoreId() + $storeId )->load(); - $this->_options[$storeId] = $collection->toOptionArray(); - $this->_optionsDefault[$storeId] = $collection->toOptionArray('default_value'); + $this->_options[$storeId][$attributeId] = $collection->toOptionArray(); + $this->_optionsDefault[$storeId][$attributeId] = $collection->toOptionArray('default_value'); } - $options = $defaultValues ? $this->_optionsDefault[$storeId] : $this->_options[$storeId]; + $options = $defaultValues + ? $this->_optionsDefault[$storeId][$attributeId] + : $this->_options[$storeId][$attributeId]; if ($withEmpty) { $options = $this->addEmptyOption($options); } @@ -72,6 +86,20 @@ class Table extends \Magento\Eav\Model\Entity\Attribute\Source\AbstractSource return $options; } + /** + * Get StoreManager dependency + * + * @return StoreManagerInterface + * @deprecated + */ + private function getStoreManager() + { + if ($this->storeManager === null) { + $this->storeManager = ObjectManager::getInstance()->get(StoreManagerInterface::class); + } + return $this->storeManager; + } + /** * Retrieve Option values array by ids * diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/TableTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/TableTest.php index fcae389b8e2e3d6d7e312055a305b4b615734c59..9b893bf9017681b584a611939f1332ee50fbc869 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/TableTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/TableTest.php @@ -6,6 +6,11 @@ namespace Magento\Eav\Test\Unit\Model\Entity\Attribute\Source; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource; +use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection as AttributeOptionCollection; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -28,6 +33,31 @@ class TableTest extends \PHPUnit_Framework_TestCase */ private $attrOptionFactory; + /** + * @var AbstractSource | \PHPUnit_Framework_MockObject_MockObject + */ + private $sourceMock; + + /** + * @var AbstractAttribute | \PHPUnit_Framework_MockObject_MockObject + */ + private $abstractAttributeMock; + + /** + * @var StoreManagerInterface | \PHPUnit_Framework_MockObject_MockObject + */ + private $storeManagerMock; + + /** + * @var StoreInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $storeMock; + + /** + * @var AttributeOptionCollection|\PHPUnit_Framework_MockObject_MockObject + */ + private $attributeOptionCollectionMock; + protected function setUp() { $objectManager = new ObjectManager($this); @@ -48,6 +78,11 @@ class TableTest extends \PHPUnit_Framework_TestCase false ); + $this->attributeOptionCollectionMock = $this->getMockBuilder(AttributeOptionCollection::class) + ->setMethods(['toOptionArray']) + ->disableOriginalConstructor() + ->getMock(); + $this->attrOptionFactory = $this->getMockBuilder( \Magento\Eav\Model\ResourceModel\Entity\Attribute\OptionFactory::class ) @@ -55,6 +90,20 @@ class TableTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMockForAbstractClass(); + $this->sourceMock = $this->getMockBuilder(AbstractSource::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $this->abstractAttributeMock = $this->getMockBuilder(AbstractAttribute::class) + ->setMethods( + [ + 'getFrontend', 'getAttributeCode', '__wakeup', 'getStoreId', + 'getId', 'getIsRequired', 'getEntity', 'getBackend' + ] + ) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->model = $objectManager->getObject( \Magento\Eav\Model\Entity\Attribute\Source\Table::class, [ @@ -62,6 +111,16 @@ class TableTest extends \PHPUnit_Framework_TestCase 'attrOptionFactory' => $this->attrOptionFactory ] ); + $this->model->setAttribute($this->abstractAttributeMock); + + $this->storeManagerMock = $this->getMockForAbstractClass(StoreManagerInterface::class); + $this->storeMock = $this->getMockForAbstractClass(StoreInterface::class); + + $objectManager->setBackwardCompatibleProperty( + $this->model, + 'storeManager', + $this->storeManagerMock + ); } public function testGetFlatColumns() @@ -74,25 +133,8 @@ class TableTest extends \PHPUnit_Framework_TestCase false ); - $abstractAttributeMock = $this->getMock( - \Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class, - ['getFrontend', 'getAttributeCode', '__wakeup'], - [], - '', - false - ); - - $abstractAttributeMock->expects( - $this->any() - )->method( - 'getFrontend' - )->will( - $this->returnValue($abstractFrontendMock) - ); - - $abstractAttributeMock->expects($this->any())->method('getAttributeCode')->will($this->returnValue('code')); - - $this->model->setAttribute($abstractAttributeMock); + $this->abstractAttributeMock->expects($this->any())->method('getFrontend')->willReturn(($abstractFrontendMock)); + $this->abstractAttributeMock->expects($this->any())->method('getAttributeCode')->willReturn('code'); $flatColumns = $this->model->getFlatColumns(); @@ -121,25 +163,16 @@ class TableTest extends \PHPUnit_Framework_TestCase $storeId = 5; $options = [['label' => 'The label', 'value' => 'A value']]; - $attribute = $this->getMock( - \Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class, - ['getId', 'getStoreId', 'getIsRequired', '__wakeup'], - [], - '', - false - ); - $attribute->expects($this->once()) + $this->abstractAttributeMock->expects($this->once()) ->method('getId') ->willReturn($attributeId); - $attribute->expects($this->once()) + $this->abstractAttributeMock->expects($this->once()) ->method('getStoreId') ->willReturn($storeId); - $attribute->expects($this->any()) + $this->abstractAttributeMock->expects($this->any()) ->method('getIsRequired') ->willReturn(false); - $this->model->setAttribute($attribute); - $this->collectionFactory->expects($this->once()) ->method('create') ->willReturnSelf(); @@ -191,22 +224,14 @@ class TableTest extends \PHPUnit_Framework_TestCase { $attributeId = 1; $storeId = 5; - $attribute = $this->getMock( - \Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class, - ['getId', 'getStoreId', '__wakeup'], - [], - '', - false - ); - $attribute->expects($this->once()) + + $this->abstractAttributeMock->expects($this->once()) ->method('getId') ->willReturn($attributeId); - $attribute->expects($this->once()) + $this->abstractAttributeMock->expects($this->once()) ->method('getStoreId') ->willReturn($storeId); - $this->model->setAttribute($attribute); - $this->collectionFactory->expects($this->once()) ->method('create') ->willReturnSelf(); @@ -257,16 +282,13 @@ class TableTest extends \PHPUnit_Framework_TestCase ->setMethods([ 'getSelect', 'getStoreId']) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $attribute = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class) - ->setMethods(['getAttributeCode', 'getEntity', 'getBackend', 'getId']) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $attribute->expects($this->any())->method('getAttributeCode')->willReturn($attributeCode); + + $this->abstractAttributeMock->expects($this->any())->method('getAttributeCode')->willReturn($attributeCode); $entity = $this->getMockBuilder(\Magento\Eav\Model\Entity\AbstractEntity::class) ->setMethods(['getLinkField']) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $attribute->expects($this->once())->method('getEntity')->willReturn($entity); + $this->abstractAttributeMock->expects($this->once())->method('getEntity')->willReturn($entity); $entity->expects($this->once())->method('getLinkField')->willReturn('entity_id'); $select = $this->getMockBuilder(\Magento\Framework\DB\Select::class) ->setMethods(['joinLeft', 'getConnection', 'order']) @@ -278,9 +300,9 @@ class TableTest extends \PHPUnit_Framework_TestCase ->setMethods(['getTable']) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $attribute->expects($this->any())->method('getBackend')->willReturn($backend); + $this->abstractAttributeMock->expects($this->any())->method('getBackend')->willReturn($backend); $backend->expects($this->any())->method('getTable')->willReturn('table_name'); - $attribute->expects($this->any())->method('getId')->willReturn(1); + $this->abstractAttributeMock->expects($this->any())->method('getId')->willReturn(1); $collection->expects($this->once())->method('getStoreId')->willReturn(1); $connection = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) ->disableOriginalConstructor() @@ -294,11 +316,99 @@ class TableTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); $this->attrOptionFactory->expects($this->once())->method('create')->willReturn($attrOption); - $attrOption->expects($this->once())->method('addOptionValueToCollection')->with($collection, $attribute, $expr) + $attrOption->expects($this->once())->method('addOptionValueToCollection') + ->with($collection, $this->abstractAttributeMock, $expr) ->willReturnSelf(); $select->expects($this->once())->method('order')->with("{$attributeCode} {$dir}"); - $this->model->setAttribute($attribute); $this->assertEquals($this->model, $this->model->addValueSortToCollection($collection, $dir)); } + + /** + * @param bool $withEmpty + * @param bool $defaultValues + * @param array $options + * @param array $optionsDefault + * @param array $expectedResult + * @dataProvider getAllOptionsDataProvider + */ + public function testGetAllOptions( + $withEmpty, + $defaultValues, + array $options, + array $optionsDefault, + array $expectedResult + ) { + $storeId = '1'; + $attributeId = '42'; + + $this->abstractAttributeMock->expects($this->once())->method('getStoreId')->willReturn(null); + + $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($this->storeMock); + $this->storeMock->expects($this->once())->method('getId')->willReturn($storeId); + + $this->abstractAttributeMock->expects($this->once())->method('getId')->willReturn($attributeId); + + $this->collectionFactory->expects($this->once()) + ->method('create') + ->willReturnSelf(); + $this->collectionFactory->expects($this->once()) + ->method('setPositionOrder') + ->willReturnSelf(); + $this->collectionFactory->expects($this->once()) + ->method('setAttributeFilter') + ->with($attributeId) + ->willReturnSelf(); + $this->collectionFactory->expects($this->once()) + ->method('setStoreFilter') + ->with($storeId) + ->willReturnSelf(); + $this->collectionFactory->expects($this->once()) + ->method('load') + ->willReturn($this->attributeOptionCollectionMock); + $this->attributeOptionCollectionMock->expects($this->any()) + ->method('toOptionArray') + ->willReturnMap( + [ + ['value', $options], + ['default_value', $optionsDefault] + ] + ); + + $this->assertEquals($expectedResult, $this->model->getAllOptions($withEmpty, $defaultValues)); + } + + /** + * @return array + */ + public function getAllOptionsDataProvider() + { + return [ + [ + false, + false, + [['value' => '16', 'label' => 'black'], ['value' => '17', 'label' => 'white']], + [['value' => '16', 'label' => 'blck'], ['value' => '17', 'label' => 'wht']], + [['value' => '16', 'label' => 'black'], ['value' => '17', 'label' => 'white']] + ], + [ + false, + true, + [['value' => '16', 'label' => 'black'], ['value' => '17', 'label' => 'white']], + [['value' => '16', 'label' => 'blck'], ['value' => '17', 'label' => 'wht']], + [['value' => '16', 'label' => 'blck'], ['value' => '17', 'label' => 'wht']] + ], + [ + true, + false, + [['value' => '16', 'label' => 'black'], ['value' => '17', 'label' => 'white']], + [['value' => '16', 'label' => 'blck'], ['value' => '17', 'label' => 'wht']], + [ + ['label' => ' ', 'value' => ''], + ['value' => '16', 'label' => 'black'], + ['value' => '17', 'label' => 'white'] + ] + ] + ]; + } } diff --git a/app/code/Magento/Eav/etc/di.xml b/app/code/Magento/Eav/etc/di.xml index 0fcba9907cc4d46b76cf0b7e86c2e0e1bf61da5a..2de184b01d7c4673d49cf73f5fe0a35afa7149e2 100644 --- a/app/code/Magento/Eav/etc/di.xml +++ b/app/code/Magento/Eav/etc/di.xml @@ -93,9 +93,32 @@ </virtualType> <type name="Magento\Eav\Model\AttributeRepository"> <arguments> - <argument name="collectionProcessor" xsi:type="object">Magento\Framework\Api\SearchCriteria\CollectionProcessor</argument> + <argument name="collectionProcessor" xsi:type="object">Magento\Eav\Model\Api\SearchCriteria\AttributeCollectionProcessor</argument> </arguments> </type> + <virtualType name="Magento\Eav\Model\Api\SearchCriteria\AttributeCollectionProcessor" type="Magento\Framework\Api\SearchCriteria\CollectionProcessor"> + <arguments> + <argument name="processors" xsi:type="array"> + <item name="filters" xsi:type="object">Magento\Eav\Model\Api\SearchCriteria\CollectionProcessor\AttributeFilterProcessor</item> + <item name="sorting" xsi:type="object">Magento\Eav\Model\Api\SearchCriteria\CollectionProcessor\AttributeSortingProcessor</item> + <item name="pagination" xsi:type="object">Magento\Framework\Api\SearchCriteria\CollectionProcessor\PaginationProcessor</item> + </argument> + </arguments> + </virtualType> + <virtualType name="Magento\Eav\Model\Api\SearchCriteria\CollectionProcessor\AttributeFilterProcessor" type="Magento\Framework\Api\SearchCriteria\CollectionProcessor\FilterProcessor"> + <arguments> + <argument name="fieldMapping" xsi:type="array"> + <item name="attribute_id" xsi:type="string">main_table.attribute_id</item> + </argument> + </arguments> + </virtualType> + <virtualType name="Magento\Eav\Model\Api\SearchCriteria\CollectionProcessor\AttributeSortingProcessor" type="Magento\Framework\Api\SearchCriteria\CollectionProcessor\SortingProcessor"> + <arguments> + <argument name="fieldMapping" xsi:type="array"> + <item name="attribute_id" xsi:type="string">main_table.attribute_id</item> + </argument> + </arguments> + </virtualType> <virtualType name="Magento\Eav\Model\Api\SearchCriteria\CollectionProcessor\AttributeSetFilterProcessor" type="Magento\Framework\Api\SearchCriteria\CollectionProcessor\FilterProcessor"> <arguments> <argument name="customFilters" xsi:type="array"> diff --git a/app/code/Magento/Wishlist/view/frontend/layout/catalog_product_view.xml b/app/code/Magento/Wishlist/view/frontend/layout/catalog_product_view.xml index 4c01d341bb68294cae5d48f5ab2dd99d8d5b08d8..a25546b1afc902a563c296a7adc24b1a9787729b 100644 --- a/app/code/Magento/Wishlist/view/frontend/layout/catalog_product_view.xml +++ b/app/code/Magento/Wishlist/view/frontend/layout/catalog_product_view.xml @@ -16,7 +16,7 @@ </arguments> </block> <referenceBlock name="product.info.addto"> - <block class="Magento\Wishlist\Block\Catalog\Product\View\AddTo\Wishlist" name="view.addto.wishlist" after="view.addto.requisition" + <block class="Magento\Wishlist\Block\Catalog\Product\View\AddTo\Wishlist" name="view.addto.wishlist" template="Magento_Wishlist::catalog/product/view/addto/wishlist.phtml" /> </referenceBlock> </referenceContainer> diff --git a/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_configure.xml b/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_configure.xml index 50ba68940fc9d4027b359fe72ec86b305fb0f69a..02b737b7d5127320207669379ea7e3b09ddb5ac1 100644 --- a/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_configure.xml +++ b/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_configure.xml @@ -9,7 +9,7 @@ <update handle="catalog_product_view"/> <body> <referenceBlock name="product.info.addto"> - <block class="Magento\Wishlist\Block\Item\Configure" name="view.addto.wishlist" after="view.addto.requisition" + <block class="Magento\Wishlist\Block\Item\Configure" name="view.addto.wishlist" template="item/configure/addto/wishlist.phtml" /> </referenceBlock> </body> diff --git a/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_configure_type_bundle.xml b/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_configure_type_bundle.xml index b08816a6728eb54b79ec806084f4a4e684702a29..84d0429a29c99ba204ec9d4401b69d2bf68e1abf 100644 --- a/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_configure_type_bundle.xml +++ b/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_configure_type_bundle.xml @@ -20,7 +20,7 @@ <block class="Magento\Catalog\Block\Product\View" name="product.info.addtocart.bundle" as="addtocart" template="product/view/addtocart.phtml" /> <block class="Magento\Catalog\Block\Product\View" name="product.info.addto.bundle" as="addto" after="product.info.addtocart.bundle" template="Magento_Catalog::product/view/addto.phtml" cacheable="false"> - <block class="Magento\Wishlist\Block\Item\Configure" name="view.addto.wishlist.bundle" after="view.addto.requisition" + <block class="Magento\Wishlist\Block\Item\Configure" name="view.addto.wishlist.bundle" template="item/configure/addto/wishlist.phtml" /> <block class="Magento\Catalog\Block\Product\View\AddTo\Compare" name="view.addto.compare.bundle" after="view.addto.wishlist" template="Magento_Catalog::product/view/addto/compare.phtml" /> diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping.php index d29bad980faca881468ae1cf3a58523e13f7ad83..f2530c016b714bf5af16c2b6d0f0f6d690cc4af0 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping.php @@ -6,13 +6,60 @@ namespace Magento\Checkout\Test\Block\Onepage; +use Magento\Checkout\Test\Block\Onepage\Shipping\AddressModal; use Magento\Mtf\Block\Form; +use Magento\Mtf\Client\Locator; /** * Checkout shipping address block. */ class Shipping extends Form { + /** + * CSS Selector for "New Address" button + * + * @var string + */ + private $newAddressButton = '[data-bind*="isNewAddressAdded"]'; + + /** + * Wait element. + * + * @var string + */ + private $waitElement = '.loading-mask'; + + /** + * SCC Selector for Address Modal block. + * + * @var string + */ + private $addressModalBlock = '//*[@id="opc-new-shipping-address"]/../..'; + + /** + * Click on "New Address" button. + * + * @return void + */ + public function clickOnNewAddressButton() + { + $this->waitForElementNotVisible($this->waitElement); + $this->_rootElement->find($this->newAddressButton)->click(); + } + + /** + * Get Address Modal Block. + * + * @return AddressModal + */ + public function getAddressModalBlock() + { + return $this->blockFactory->create( + AddressModal::class, + ['element' => $this->browser->find($this->addressModalBlock, Locator::SELECTOR_XPATH)] + ); + } + /** * Returns form's required elements * diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping/AddressModal.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping/AddressModal.php new file mode 100644 index 0000000000000000000000000000000000000000..8a949c474bc847942f1bd5922d00514cd6133d08 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping/AddressModal.php @@ -0,0 +1,68 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Checkout\Test\Block\Onepage\Shipping; + +use Magento\Mtf\Block\Form; + +/** + * Checkout shipping address modal block. + */ +class AddressModal extends Form +{ + /** + * CSS Selector for Save button. + * + * @var string + */ + private $saveButton = '.action-save-address'; + + /** + * Selector for field's error message. + * + * @var string + */ + private $errorMessage = '.field-error'; + + /** + * Selector for error fields. + * + * @var string + */ + private $errorField = '._error'; + + /** + * Selector for field label that have error message. + * + * @var string + */ + private $fieldLabel = '.label'; + + /** + * Click on 'Save Address' button. + * + * @return void + */ + public function save() + { + $this->_rootElement->find($this->saveButton)->click(); + } + + /** + * Get Error messages for attributes. + * + * @return array + */ + public function getErrorMessages() + { + $result = []; + foreach ($this->_rootElement->getElements($this->errorField) as $item) { + $result[$item->find($this->fieldLabel)->getText()] = $item->find($this->errorMessage)->getText(); + } + + return $result; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping/AddressModal.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping/AddressModal.xml new file mode 100644 index 0000000000000000000000000000000000000000..13403b792684512c9741bca74a78f7c84036eb48 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Shipping/AddressModal.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" ?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<mapping strict="0"> + <fields> + <firstname /> + <lastname /> + <company /> + <street> + <selector>input[name="street[0]"]</selector> + </street> + <city /> + <region_id> + <input>select</input> + </region_id> + <country_id> + <input>select</input> + </country_id> + <telephone /> + <postcode /> + </fields> +</mapping> diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/AddNewShippingAddressStep.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/AddNewShippingAddressStep.php new file mode 100644 index 0000000000000000000000000000000000000000..a4ae00510a9de0b2e9e7d82ff3285e0ae780a7cc --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/AddNewShippingAddressStep.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Checkout\Test\TestStep; + +use Magento\Checkout\Test\Page\CheckoutOnepage; +use Magento\Customer\Test\Fixture\Address; +use Magento\Mtf\TestStep\TestStepInterface; + +/** + * Create customer custom attribute step. + */ +class AddNewShippingAddressStep implements TestStepInterface +{ + /** + * Checkout One page. + * + * @var CheckoutOnepage + */ + private $checkoutOnepage; + + /** + * Shipping Address fixture. + * + * @var Address + */ + private $address; + + /** + * @constructor + * @param CheckoutOnepage $checkoutOnepage + * @param Address|null $address [optional] + */ + public function __construct(CheckoutOnepage $checkoutOnepage, Address $address = null) + { + $this->checkoutOnepage = $checkoutOnepage; + $this->address = $address; + } + + /** + * Create customer account. + * + * @return void + */ + public function run() + { + $shippingBlock = $this->checkoutOnepage->getShippingBlock(); + $shippingBlock->clickOnNewAddressButton(); + if ($this->address) { + $shippingBlock->getAddressModalBlock()->fill($this->address); + } + $shippingBlock->getAddressModalBlock()->save(); + } +}