diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Eav/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Product/Eav/AbstractAction.php index b6206f96b91e0..6101e5cd362e4 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Eav/AbstractAction.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Eav/AbstractAction.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Model\Indexer\Product\Eav; use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\AbstractEav; @@ -12,6 +14,11 @@ */ abstract class AbstractAction { + /** + * Config path for enable EAV indexer + */ + const ENABLE_EAV_INDEXER = 'catalog/search/enable_eav_indexer'; + /** * EAV Indexers by type * @@ -29,17 +36,27 @@ abstract class AbstractAction */ protected $_eavDecimalFactory; + /** + * @var \Magento\Framework\App\Config\ScopeConfigInterface + */ + private $scopeConfig; + /** * AbstractAction constructor. * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory $eavDecimalFactory * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory $eavSourceFactory + * @param \Magento\Framework\App\Config\ScopeConfigInterface|null $scopeConfig */ public function __construct( \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory $eavDecimalFactory, - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory $eavSourceFactory + \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory $eavSourceFactory, + \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig = null ) { $this->_eavDecimalFactory = $eavDecimalFactory; $this->_eavSourceFactory = $eavSourceFactory; + $this->scopeConfig = $scopeConfig ?: \Magento\Framework\App\ObjectManager::getInstance()->get( + \Magento\Framework\App\Config\ScopeConfigInterface::class + ); } /** @@ -92,6 +109,9 @@ public function getIndexer($type) */ public function reindex($ids = null) { + if (!$this->isEavIndexerEnabled()) { + return; + } foreach ($this->getIndexers() as $indexer) { if ($ids === null) { $indexer->reindexAll(); @@ -149,4 +169,19 @@ protected function processRelations(AbstractEav $indexer, array $ids, bool $only return array_unique(array_merge($ids, $childIds, $parentIds)); } + + /** + * Get EAV indexer status + * + * @return bool + */ + private function isEavIndexerEnabled(): bool + { + $eavIndexerStatus = $this->scopeConfig->getValue( + self::ENABLE_EAV_INDEXER, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + + return (bool)$eavIndexerStatus; + } } diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Eav/Action/Full.php b/app/code/Magento/Catalog/Model/Indexer/Product/Eav/Action/Full.php index bc747e62f641e..802176092d147 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Eav/Action/Full.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Eav/Action/Full.php @@ -3,12 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Model\Indexer\Product\Eav\Action; use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher; /** * Class Full reindex action + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Full extends \Magento\Catalog\Model\Indexer\Product\Eav\AbstractAction { @@ -32,6 +35,11 @@ class Full extends \Magento\Catalog\Model\Indexer\Product\Eav\AbstractAction */ private $activeTableSwitcher; + /** + * @var \Magento\Framework\App\Config\ScopeConfigInterface + */ + private $scopeConfig; + /** * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory $eavDecimalFactory * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory $eavSourceFactory @@ -39,6 +47,7 @@ class Full extends \Magento\Catalog\Model\Indexer\Product\Eav\AbstractAction * @param \Magento\Framework\Indexer\BatchProviderInterface|null $batchProvider * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator $batchSizeCalculator * @param ActiveTableSwitcher|null $activeTableSwitcher + * @param \Magento\Framework\App\Config\ScopeConfigInterface|null $scopeConfig */ public function __construct( \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory $eavDecimalFactory, @@ -46,9 +55,13 @@ public function __construct( \Magento\Framework\EntityManager\MetadataPool $metadataPool = null, \Magento\Framework\Indexer\BatchProviderInterface $batchProvider = null, \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator $batchSizeCalculator = null, - ActiveTableSwitcher $activeTableSwitcher = null + ActiveTableSwitcher $activeTableSwitcher = null, + \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig = null ) { - parent::__construct($eavDecimalFactory, $eavSourceFactory); + $this->scopeConfig = $scopeConfig ?: \Magento\Framework\App\ObjectManager::getInstance()->get( + \Magento\Framework\App\Config\ScopeConfigInterface::class + ); + parent::__construct($eavDecimalFactory, $eavSourceFactory, $scopeConfig); $this->metadataPool = $metadataPool ?: \Magento\Framework\App\ObjectManager::getInstance()->get( \Magento\Framework\EntityManager\MetadataPool::class ); @@ -73,6 +86,9 @@ public function __construct( */ public function execute($ids = null) { + if (!$this->isEavIndexerEnabled()) { + return; + } try { foreach ($this->getIndexers() as $indexerName => $indexer) { $connection = $indexer->getConnection(); @@ -129,4 +145,19 @@ protected function syncData($indexer, $destinationTable, $ids = null) throw $e; } } + + /** + * Get EAV indexer status + * + * @return bool + */ + private function isEavIndexerEnabled(): bool + { + $eavIndexerStatus = $this->scopeConfig->getValue( + self::ENABLE_EAV_INDEXER, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + + return (bool)$eavIndexerStatus; + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/AbstractActionTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/AbstractActionTest.php index 9d58822fb6073..6ad14af44304e 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/AbstractActionTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/AbstractActionTest.php @@ -22,6 +22,14 @@ class AbstractActionTest extends \PHPUnit\Framework\TestCase */ protected $_eavSourceFactoryMock; + /** + * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $scopeConfig; + + /** + * @return void + */ protected function setUp() { $this->_eavDecimalFactoryMock = $this->createPartialMock( @@ -32,12 +40,22 @@ protected function setUp() \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory::class, ['create'] ); + $this->scopeConfig = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); $this->_model = $this->getMockForAbstractClass( \Magento\Catalog\Model\Indexer\Product\Eav\AbstractAction::class, - [$this->_eavDecimalFactoryMock, $this->_eavSourceFactoryMock, []] + [ + $this->_eavDecimalFactoryMock, + $this->_eavSourceFactoryMock, + $this->scopeConfig + ] ); } + /** + * @return void + */ public function testGetIndexers() { $expectedIndexers = [ @@ -73,6 +91,10 @@ public function testGetIndexerWithUnknownTypeThrowsException() $this->_model->getIndexer('unknown_type'); } + /** + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + */ public function testGetIndexer() { $this->_eavSourceFactoryMock->expects($this->once()) @@ -86,6 +108,10 @@ public function testGetIndexer() $this->assertEquals('source_return_value', $this->_model->getIndexer('source')); } + /** + * @return void + * @throws \Exception + */ public function testReindexWithoutArgumentsExecutesReindexAll() { $eavSource = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\Source::class) @@ -110,6 +136,10 @@ public function testReindexWithoutArgumentsExecutesReindexAll() ->method('create') ->will($this->returnValue($eavDecimal)); + $this->scopeConfig->expects($this->once()) + ->method('getValue') + ->willReturn(1); + $this->_model->reindex(); } @@ -174,9 +204,25 @@ public function testReindexWithNotNullArgumentExecutesReindexEntities( ->method('create') ->will($this->returnValue($eavDecimal)); + $this->scopeConfig->expects($this->once()) + ->method('getValue') + ->willReturn(1); + $this->_model->reindex($ids); } + /** + * @return void + * @throws \Exception + */ + public function testReindexWithDisabledEavIndexer() + { + $this->scopeConfig->expects($this->once())->method('getValue')->willReturn(0); + $this->_eavSourceFactoryMock->expects($this->never())->method('create'); + $this->_eavDecimalFactoryMock->expects($this->never())->method('create'); + $this->_model->reindex(); + } + /** * @return array */ diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/FullTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/FullTest.php index c254557904da1..90c3f999a6a8b 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/FullTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/FullTest.php @@ -5,53 +5,87 @@ */ namespace Magento\Catalog\Test\Unit\Model\Indexer\Product\Eav\Action; +use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Framework\Indexer\BatchProviderInterface; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator; + /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class FullTest extends \PHPUnit\Framework\TestCase { - public function testExecuteWithAdapterErrorThrowsException() - { - $eavDecimalFactory = $this->createPartialMock( - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory::class, - ['create'] - ); - $eavSourceFactory = $this->createPartialMock( - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory::class, - ['create'] - ); + /** + * @var \Magento\Catalog\Model\Indexer\Product\Eav\Action\Full|\PHPUnit_Framework_MockObject_MockObject + */ + private $model; - $exceptionMessage = 'exception message'; - $exception = new \Exception($exceptionMessage); + /** + * @var DecimalFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $eavDecimalFactory; - $eavDecimalFactory->expects($this->once()) - ->method('create') - ->will($this->throwException($exception)); + /** + * @var SourceFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $eavSourceFactory; - $metadataMock = $this->createMock(\Magento\Framework\EntityManager\MetadataPool::class); - $batchProviderMock = $this->createMock(\Magento\Framework\Indexer\BatchProviderInterface::class); + /** + * @var MetadataPool|\PHPUnit_Framework_MockObject_MockObject + */ + private $metadataPool; - $batchManagementMock = $this->createMock( - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator::class - ); + /** + * @var BatchProviderInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $batchProvider; - $tableSwitcherMock = $this->getMockBuilder( - \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher::class - )->disableOriginalConstructor()->getMock(); - - $model = new \Magento\Catalog\Model\Indexer\Product\Eav\Action\Full( - $eavDecimalFactory, - $eavSourceFactory, - $metadataMock, - $batchProviderMock, - $batchManagementMock, - $tableSwitcherMock - ); + /** + * @var BatchSizeCalculator|\PHPUnit_Framework_MockObject_MockObject + */ + private $batchSizeCalculator; + + /** + * @var ActiveTableSwitcher|\PHPUnit_Framework_MockObject_MockObject + */ + private $activeTableSwitcher; + + /** + * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $scopeConfig; - $this->expectException(\Magento\Framework\Exception\LocalizedException::class); - $this->expectExceptionMessage($exceptionMessage); + /** + * @return void + */ + protected function setUp() + { + $this->eavDecimalFactory = $this->createPartialMock(DecimalFactory::class, ['create']); + $this->eavSourceFactory = $this->createPartialMock(SourceFactory::class, ['create']); + $this->metadataPool = $this->createMock(MetadataPool::class); + $this->batchProvider = $this->getMockForAbstractClass(BatchProviderInterface::class); + $this->batchSizeCalculator = $this->createMock(BatchSizeCalculator::class); + $this->activeTableSwitcher = $this->createMock(ActiveTableSwitcher::class); + $this->scopeConfig = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); - $model->execute(); + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject( + \Magento\Catalog\Model\Indexer\Product\Eav\Action\Full::class, + [ + 'eavDecimalFactory' => $this->eavDecimalFactory, + 'eavSourceFactory' => $this->eavSourceFactory, + 'metadataPool' => $this->metadataPool, + 'batchProvider' => $this->batchProvider, + 'batchSizeCalculator' => $this->batchSizeCalculator, + 'activeTableSwitcher' => $this->activeTableSwitcher, + 'scopeConfig' => $this->scopeConfig + ] + ); } /** @@ -59,14 +93,7 @@ public function testExecuteWithAdapterErrorThrowsException() */ public function testExecute() { - $eavDecimalFactory = $this->createPartialMock( - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory::class, - ['create'] - ); - $eavSourceFactory = $this->createPartialMock( - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory::class, - ['create'] - ); + $this->scopeConfig->expects($this->once())->method('getValue')->willReturn(1); $ids = [1, 2, 3]; $connectionMock = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) @@ -90,42 +117,29 @@ public function testExecute() $eavSource->expects($this->atLeastOnce())->method('getConnection')->willReturn($connectionMock); $eavDecimal->expects($this->atLeastOnce())->method('getConnection')->willReturn($connectionMock); - $eavDecimal->expects($this->once()) - ->method('reindexEntities') - ->with($ids); + $eavDecimal->expects($this->once())->method('reindexEntities')->with($ids); - $eavSource->expects($this->once()) - ->method('reindexEntities') - ->with($ids); + $eavSource->expects($this->once())->method('reindexEntities')->with($ids); - $eavDecimalFactory->expects($this->once()) - ->method('create') - ->will($this->returnValue($eavSource)); + $this->eavDecimalFactory->expects($this->once())->method('create')->will($this->returnValue($eavSource)); - $eavSourceFactory->expects($this->once()) - ->method('create') - ->will($this->returnValue($eavDecimal)); + $this->eavSourceFactory->expects($this->once())->method('create')->will($this->returnValue($eavDecimal)); - $metadataMock = $this->createMock(\Magento\Framework\EntityManager\MetadataPool::class); $entityMetadataMock = $this->getMockBuilder(\Magento\Framework\EntityManager\EntityMetadataInterface::class) ->getMockForAbstractClass(); - $metadataMock->expects($this->atLeastOnce()) + $this->metadataPool->expects($this->atLeastOnce()) ->method('getMetadata') ->with(\Magento\Catalog\Api\Data\ProductInterface::class) ->willReturn($entityMetadataMock); - $batchProviderMock = $this->createMock(\Magento\Framework\Indexer\BatchProviderInterface::class); - $batchProviderMock->expects($this->atLeastOnce()) + $this->batchProvider->expects($this->atLeastOnce()) ->method('getBatches') ->willReturn([['from' => 10, 'to' => 100]]); - $batchProviderMock->expects($this->atLeastOnce()) + $this->batchProvider->expects($this->atLeastOnce()) ->method('getBatchIds') ->willReturn($ids); - $batchManagementMock = $this->createMock( - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator::class - ); $selectMock = $this->getMockBuilder(\Magento\Framework\DB\Select::class) ->disableOriginalConstructor() ->getMock(); @@ -134,19 +148,17 @@ public function testExecute() $selectMock->expects($this->atLeastOnce())->method('distinct')->willReturnSelf(); $selectMock->expects($this->atLeastOnce())->method('from')->willReturnSelf(); - $tableSwitcherMock = $this->getMockBuilder( - \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher::class - )->disableOriginalConstructor()->getMock(); - - $model = new \Magento\Catalog\Model\Indexer\Product\Eav\Action\Full( - $eavDecimalFactory, - $eavSourceFactory, - $metadataMock, - $batchProviderMock, - $batchManagementMock, - $tableSwitcherMock - ); + $this->model->execute(); + } - $model->execute(); + /** + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function testExecuteWithDisabledEavIndexer() + { + $this->scopeConfig->expects($this->once())->method('getValue')->willReturn(0); + $this->metadataPool->expects($this->never())->method('getMetadata'); + $this->model->execute(); } } diff --git a/app/code/Magento/CatalogSearch/Plugin/EnableEavIndexer.php b/app/code/Magento/CatalogSearch/Plugin/EnableEavIndexer.php new file mode 100644 index 0000000000000..c624f9d1c2e52 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Plugin/EnableEavIndexer.php @@ -0,0 +1,33 @@ +getData(self::SEARCH_ENGINE_VALUE_PATH); + if ($searchEngine === 'mysql') { + $data = $subject->getData(); + $data['groups']['search']['fields']['enable_eav_indexer']['value'] = 1; + + $subject->setData($data); + } + } +} diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Plugin/EnableEavIndexerTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Plugin/EnableEavIndexerTest.php new file mode 100644 index 0000000000000..0eac2e3309aec --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Unit/Plugin/EnableEavIndexerTest.php @@ -0,0 +1,68 @@ +config = $this->getMockBuilder(\Magento\Config\Model\Config::class) + ->disableOriginalConstructor() + ->setMethods(['getData', 'setData']) + ->getMock(); + + $objectManagerHelper = new ObjectManagerHelper($this); + $this->model = $objectManagerHelper->getObject( + \Magento\CatalogSearch\Plugin\EnableEavIndexer::class + ); + } + + /** + * Test with other search engine (not MySQL) selected in config + * + * @return void + */ + public function testBeforeSave() + { + $this->config->expects($this->once())->method('getData')->willReturn('elasticsearch'); + $this->config->expects($this->never())->method('setData')->willReturnSelf(); + + $this->model->beforeSave($this->config); + } + + /** + * Test with MySQL search engine selected in config + * + * @return void + */ + public function testBeforeSaveMysqlSearchEngine() + { + $this->config->expects($this->at(0))->method('getData')->willReturn('mysql'); + $this->config->expects($this->at(1))->method('getData')->willReturn([]); + $this->config->expects($this->once())->method('setData')->willReturnSelf(); + + $this->model->beforeSave($this->config); + } +} diff --git a/app/code/Magento/CatalogSearch/composer.json b/app/code/Magento/CatalogSearch/composer.json index 298511ef428a9..1000e349b6f9a 100644 --- a/app/code/Magento/CatalogSearch/composer.json +++ b/app/code/Magento/CatalogSearch/composer.json @@ -9,7 +9,7 @@ "magento/framework": "*", "magento/module-backend": "*", "magento/module-catalog": "*", - "magento/module-indexer": "100.2.*", + "magento/module-indexer": "*", "magento/module-catalog-inventory": "*", "magento/module-customer": "*", "magento/module-directory": "*", @@ -19,6 +19,9 @@ "magento/module-theme": "*", "magento/module-ui": "*" }, + "suggest": { + "magento/module-config": "*" + }, "type": "magento2-module", "license": [ "OSL-3.0", diff --git a/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml b/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml index 18d2cdf542799..39235511eaeec 100644 --- a/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml +++ b/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml @@ -36,6 +36,14 @@ validate-digits + + + Enable/Disable Product EAV indexer to improve indexation speed. Make sure that indexer is not used by 3rd party extensions. + + mysql + + Magento\Config\Model\Config\Source\Yesno + diff --git a/app/code/Magento/CatalogSearch/etc/config.xml b/app/code/Magento/CatalogSearch/etc/config.xml index d2b50fe9f5336..66b79226c9f34 100644 --- a/app/code/Magento/CatalogSearch/etc/config.xml +++ b/app/code/Magento/CatalogSearch/etc/config.xml @@ -17,6 +17,7 @@ 128 100 8 + 1 diff --git a/app/code/Magento/CatalogSearch/etc/di.xml b/app/code/Magento/CatalogSearch/etc/di.xml index acec17f48211e..cc07384d4c525 100644 --- a/app/code/Magento/CatalogSearch/etc/di.xml +++ b/app/code/Magento/CatalogSearch/etc/di.xml @@ -337,4 +337,7 @@ + + + diff --git a/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php b/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php index f22879df0ae0c..29adc1816d337 100644 --- a/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php +++ b/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php @@ -101,6 +101,9 @@ public function loadAttributeOptions() /** * {@inheritdoc} + * + * @param array &$attributes + * @return void */ protected function _addSpecialAttributes(array &$attributes) { @@ -163,8 +166,6 @@ protected function addGlobalAttribute( \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute, \Magento\Catalog\Model\ResourceModel\Product\Collection $collection ) { - $storeId = $this->storeManager->getStore()->getId(); - switch ($attribute->getBackendType()) { case 'decimal': case 'datetime': @@ -173,10 +174,15 @@ protected function addGlobalAttribute( $collection->addAttributeToSelect($attribute->getAttributeCode(), 'inner'); break; default: - $alias = 'at_' . md5($this->getId()) . $attribute->getAttributeCode(); + $alias = 'at_' . sha1($this->getId()) . $attribute->getAttributeCode(); + + $connection = $this->_productResource->getConnection(); + $storeId = $connection->getIfNullSql($alias . '.store_id', $this->storeManager->getStore()->getId()); + $linkField = $attribute->getEntity()->getLinkField(); + $collection->getSelect()->join( - [$alias => $collection->getTable('catalog_product_index_eav')], - "($alias.entity_id = e.entity_id) AND ($alias.store_id = $storeId)" . + [$alias => $collection->getTable('catalog_product_entity_varchar')], + "($alias.$linkField = e.$linkField) AND ($alias.store_id = $storeId)" . " AND ($alias.attribute_id = {$attribute->getId()})", [] ); @@ -225,6 +231,8 @@ protected function addNotGlobalAttribute( /** * {@inheritdoc} + * + * @return string */ public function getMappedSqlField() { @@ -244,6 +252,9 @@ public function getMappedSqlField() /** * {@inheritdoc} + * + * @param \Magento\Catalog\Model\ResourceModel\Product\Collection $productCollection + * @return $this */ public function collectValidatedAttributes($productCollection) { diff --git a/app/code/Magento/CatalogWidget/Test/Unit/Model/Rule/Condition/ProductTest.php b/app/code/Magento/CatalogWidget/Test/Unit/Model/Rule/Condition/ProductTest.php index 09270b6b41fc7..219cae6829299 100644 --- a/app/code/Magento/CatalogWidget/Test/Unit/Model/Rule/Condition/ProductTest.php +++ b/app/code/Magento/CatalogWidget/Test/Unit/Model/Rule/Condition/ProductTest.php @@ -17,11 +17,21 @@ class ProductTest extends \PHPUnit\Framework\TestCase */ private $model; + /** + * @var \Magento\Catalog\Model\ResourceModel\Product|\PHPUnit_Framework_MockObject_MockObject + */ + private $productResource; + /** * @var \PHPUnit_Framework_MockObject_MockObject */ private $attributeMock; + /** + * @inheritdoc + * + * @return void + */ protected function setUp() { $objectManagerHelper = new ObjectManager($this); @@ -33,9 +43,9 @@ protected function setUp() $storeManager = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); $storeMock = $this->createMock(\Magento\Store\Api\Data\StoreInterface::class); $storeManager->expects($this->any())->method('getStore')->willReturn($storeMock); - $productResource = $this->createMock(\Magento\Catalog\Model\ResourceModel\Product::class); - $productResource->expects($this->once())->method('loadAllAttributes')->willReturnSelf(); - $productResource->expects($this->once())->method('getAttributesByCode')->willReturn([]); + $this->productResource = $this->createMock(\Magento\Catalog\Model\ResourceModel\Product::class); + $this->productResource->expects($this->once())->method('loadAllAttributes')->willReturnSelf(); + $this->productResource->expects($this->once())->method('getAttributesByCode')->willReturn([]); $productCategoryList = $this->getMockBuilder(\Magento\Catalog\Model\ProductCategoryList::class) ->disableOriginalConstructor() ->getMock(); @@ -45,7 +55,7 @@ protected function setUp() [ 'config' => $eavConfig, 'storeManager' => $storeManager, - 'productResource' => $productResource, + 'productResource' => $this->productResource, 'productCategoryList' => $productCategoryList, 'data' => [ 'rule' => $ruleMock, @@ -55,6 +65,11 @@ protected function setUp() ); } + /** + * Test addToCollection method. + * + * @return void + */ public function testAddToCollection() { $collectionMock = $this->createMock(\Magento\Catalog\Model\ResourceModel\Product\Collection::class); @@ -67,9 +82,22 @@ public function testAddToCollection() $this->attributeMock->expects($this->once())->method('isScopeGlobal')->willReturn(true); $this->attributeMock->expects($this->once())->method('isScopeGlobal')->willReturn(true); $this->attributeMock->expects($this->once())->method('getBackendType')->willReturn('multiselect'); + + $entityMock = $this->createMock(\Magento\Eav\Model\Entity\AbstractEntity::class); + $entityMock->expects($this->once())->method('getLinkField')->willReturn('entitiy_id'); + $this->attributeMock->expects($this->once())->method('getEntity')->willReturn($entityMock); + $connection = $this->createMock(\Magento\Framework\DB\Adapter\AdapterInterface::class); + + $this->productResource->expects($this->atLeastOnce())->method('getConnection')->willReturn($connection); + $this->model->addToCollection($collectionMock); } + /** + * Test getMappedSqlField method. + * + * @return void + */ public function testGetMappedSqlFieldSku() { $this->model->setAttribute('sku'); diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml index e7091dbfae215..602e04b138ea3 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml @@ -17,6 +17,7 @@ + diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php index b45306d670bff..25d8412c91056 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\ConfigurableProduct\Test\Unit\Block\Product\View\Type; use Magento\Customer\Model\Session; @@ -83,6 +84,9 @@ class ConfigurableTest extends \PHPUnit\Framework\TestCase */ private $variationPricesMock; + /** + * {@inheritDoc} + */ protected function setUp() { $this->mockContextObject(); @@ -174,7 +178,7 @@ protected function setUp() * * @return array */ - public function cacheKeyProvider() : array + public function cacheKeyProvider(): array { return [ 'without_currency_and_customer_group' => [ @@ -313,11 +317,7 @@ public function testGetJsonConfig() $this->localeFormat->expects($this->atLeastOnce())->method('getPriceFormat')->willReturn([]); $this->localeFormat->expects($this->any()) ->method('getNumber') - ->willReturnMap([ - [$amount, $amount], - [$priceQty, $priceQty], - [$percentage, $percentage], - ]); + ->willReturnArgument(0); $this->variationPricesMock->expects($this->once()) ->method('getFormattedPrices') @@ -349,13 +349,13 @@ public function testGetJsonConfig() /** * Retrieve array with expected parameters for method getJsonConfig() * - * @param $productId - * @param $amount - * @param $priceQty - * @param $percentage + * @param int $productId + * @param double $amount + * @param int $priceQty + * @param int $percentage * @return array */ - private function getExpectedArray($productId, $amount, $priceQty, $percentage) + private function getExpectedArray($productId, $amount, $priceQty, $percentage): array { $expectedArray = [ 'attributes' => [], diff --git a/app/code/Magento/Customer/Controller/Address/FormPost.php b/app/code/Magento/Customer/Controller/Address/FormPost.php index 21334f51b1752..60e1f6eb172f5 100644 --- a/app/code/Magento/Customer/Controller/Address/FormPost.php +++ b/app/code/Magento/Customer/Controller/Address/FormPost.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Customer\Controller\Address; use Magento\Customer\Api\AddressRepositoryInterface; @@ -197,17 +198,17 @@ public function execute() try { $address = $this->_extractAddress(); $this->_addressRepository->save($address); - $this->messageManager->addSuccess(__('You saved the address.')); + $this->messageManager->addSuccessMessage(__('You saved the address.')); $url = $this->_buildUrl('*/*/index', ['_secure' => true]); return $this->resultRedirectFactory->create()->setUrl($this->_redirect->success($url)); } catch (InputException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); foreach ($e->getErrors() as $error) { - $this->messageManager->addError($error->getMessage()); + $this->messageManager->addErrorMessage($error->getMessage()); } } catch (\Exception $e) { $redirectUrl = $this->_buildUrl('*/*/index'); - $this->messageManager->addException($e, __('We can\'t save the address.')); + $this->messageManager->addExceptionMessage($e, __('We can\'t save the address.')); } $url = $redirectUrl; diff --git a/app/code/Magento/Customer/Model/Metadata/Form/Text.php b/app/code/Magento/Customer/Model/Metadata/Form/Text.php index 9ef6df0a6d36e..c8b9a1e46a127 100644 --- a/app/code/Magento/Customer/Model/Metadata/Form/Text.php +++ b/app/code/Magento/Customer/Model/Metadata/Form/Text.php @@ -5,8 +5,10 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Customer\Model\Metadata\Form; +use Magento\Customer\Api\Data\AttributeMetadataInterface; use Magento\Framework\Api\ArrayObjectSearch; class Text extends AbstractData @@ -19,7 +21,7 @@ class Text extends AbstractData /** * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate * @param \Psr\Log\LoggerInterface $logger - * @param \Magento\Customer\Api\Data\AttributeMetadataInterface $attribute + * @param AttributeMetadataInterface $attribute * @param \Magento\Framework\Locale\ResolverInterface $localeResolver * @param string $value * @param string $entityTypeCode @@ -29,7 +31,7 @@ class Text extends AbstractData public function __construct( \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, \Psr\Log\LoggerInterface $logger, - \Magento\Customer\Api\Data\AttributeMetadataInterface $attribute, + AttributeMetadataInterface $attribute, \Magento\Framework\Locale\ResolverInterface $localeResolver, $value, $entityTypeCode, @@ -41,7 +43,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function extractValue(\Magento\Framework\App\RequestInterface $request) { @@ -49,7 +51,7 @@ public function extractValue(\Magento\Framework\App\RequestInterface $request) } /** - * {@inheritdoc} + * @inheritdoc * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ @@ -72,26 +74,7 @@ public function validateValue($value) return true; } - // validate length - $length = $this->_string->strlen(trim($value)); - - $validateRules = $attribute->getValidationRules(); - - $minTextLength = ArrayObjectSearch::getArrayElementByName( - $validateRules, - 'min_text_length' - ); - if ($minTextLength !== null && $length < $minTextLength) { - $errors[] = __('"%1" length must be equal or greater than %2 characters.', $label, $minTextLength); - } - - $maxTextLength = ArrayObjectSearch::getArrayElementByName( - $validateRules, - 'max_text_length' - ); - if ($maxTextLength !== null && $length > $maxTextLength) { - $errors[] = __('"%1" length must be equal or less than %2 characters.', $label, $maxTextLength); - } + $errors = $this->validateLength($value, $attribute, $errors); $result = $this->_validateInputRule($value); if ($result !== true) { @@ -105,7 +88,7 @@ public function validateValue($value) } /** - * {@inheritdoc} + * @inheritdoc */ public function compactValue($value) { @@ -113,7 +96,7 @@ public function compactValue($value) } /** - * {@inheritdoc} + * @inheritdoc */ public function restoreValue($value) { @@ -121,10 +104,48 @@ public function restoreValue($value) } /** - * {@inheritdoc} + * @inheritdoc */ public function outputValue($format = \Magento\Customer\Model\Metadata\ElementFactory::OUTPUT_FORMAT_TEXT) { return $this->_applyOutputFilter($this->_value); } + + /** + * Length validation + * + * @param mixed $value + * @param AttributeMetadataInterface $attribute + * @param array $errors + * @return array + */ + private function validateLength($value, AttributeMetadataInterface $attribute, array $errors): array + { + // validate length + $label = __($attribute->getStoreLabel()); + + $length = $this->_string->strlen(trim($value)); + + $validateRules = $attribute->getValidationRules(); + + if (!empty(ArrayObjectSearch::getArrayElementByName($validateRules, 'input_validation'))) { + $minTextLength = ArrayObjectSearch::getArrayElementByName( + $validateRules, + 'min_text_length' + ); + if ($minTextLength !== null && $length < $minTextLength) { + $errors[] = __('"%1" length must be equal or greater than %2 characters.', $label, $minTextLength); + } + + $maxTextLength = ArrayObjectSearch::getArrayElementByName( + $validateRules, + 'max_text_length' + ); + if ($maxTextLength !== null && $length > $maxTextLength) { + $errors[] = __('"%1" length must be equal or less than %2 characters.', $label, $maxTextLength); + } + } + + return $errors; + } } diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Address/FormPostTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Address/FormPostTest.php index 4ad1b5cbc96bd..c2a795fc95016 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Address/FormPostTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Address/FormPostTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Customer\Test\Unit\Controller\Address; use Magento\Customer\Api\AddressRepositoryInterface; @@ -162,6 +163,9 @@ class FormPostTest extends \PHPUnit\Framework\TestCase */ private $customerAddressMapper; + /** + * {@inheritDoc} + */ protected function setUp() { $this->prepareContext(); @@ -230,7 +234,10 @@ protected function setUp() ); } - protected function prepareContext() + /** + * Prepares context + */ + protected function prepareContext(): void { $this->context = $this->getMockBuilder(\Magento\Framework\App\Action\Context::class) ->disableOriginalConstructor() @@ -284,7 +291,10 @@ protected function prepareContext() ->willReturn($this->messageManager); } - protected function prepareAddress() + /** + * Prepare address + */ + protected function prepareAddress(): void { $this->addressRepository = $this->getMockBuilder(\Magento\Customer\Api\AddressRepositoryInterface::class) ->getMockForAbstractClass(); @@ -303,7 +313,10 @@ protected function prepareAddress() ->willReturn($this->addressData); } - protected function prepareRegion() + /** + * Prepare region + */ + protected function prepareRegion(): void { $this->region = $this->getMockBuilder(\Magento\Directory\Model\Region::class) ->disableOriginalConstructor() @@ -335,7 +348,10 @@ protected function prepareRegion() ->willReturn($this->regionData); } - protected function prepareForm() + /** + * Prepare form + */ + protected function prepareForm(): void { $this->form = $this->getMockBuilder(\Magento\Customer\Model\Metadata\Form::class) ->disableOriginalConstructor() @@ -346,7 +362,10 @@ protected function prepareForm() ->getMock(); } - public function testExecuteNoFormKey() + /** + * Test form without formKey + */ + public function testExecuteNoFormKey(): void { $this->formKeyValidator->expects($this->once()) ->method('validate') @@ -361,7 +380,10 @@ public function testExecuteNoFormKey() $this->assertEquals($this->resultRedirect, $this->model->execute()); } - public function testExecuteNoPostData() + /** + * Test executing without post data + */ + public function testExecuteNoPostData(): void { $postValue = 'post_value'; $url = 'url'; @@ -409,10 +431,11 @@ public function testExecuteNoPostData() } /** + * Tests executing + * * @param int $addressId * @param int $countryId * @param int $customerId - * @param bool $isRegionRequired * @param int $regionId * @param string $region * @param string $regionCode @@ -433,7 +456,7 @@ public function testExecute( $newRegionId, $newRegion, $newRegionCode - ) { + ): void { $existingAddressData = [ 'country_id' => $countryId, 'region_id' => $regionId, @@ -517,7 +540,8 @@ public function testExecute( ->willReturnMap([ [ $this->regionData, - $regionData, \Magento\Customer\Api\Data\RegionInterface::class, + $regionData, + \Magento\Customer\Api\Data\RegionInterface::class, $this->dataObjectHelper, ], [ @@ -549,7 +573,7 @@ public function testExecute( ->willReturnSelf(); $this->messageManager->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('You saved the address.')) ->willReturnSelf(); @@ -581,7 +605,7 @@ public function testExecute( /** * @return array */ - public function dataProviderTestExecute() + public function dataProviderTestExecute(): array { return [ [1, 1, 1, null, '', null, '', null, ''], @@ -612,7 +636,10 @@ public function dataProviderTestExecute() ]; } - public function testExecuteInputException() + /** + * Tests input exception + */ + public function testExecuteInputException(): void { $addressId = 1; $postValue = 'post_value'; @@ -640,7 +667,7 @@ public function testExecuteInputException() ->willThrowException(new InputException(__('InputException'))); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('InputException') ->willReturnSelf(); @@ -674,7 +701,10 @@ public function testExecuteInputException() $this->assertEquals($this->resultRedirect, $this->model->execute()); } - public function testExecuteException() + /** + * Tests exception + */ + public function testExecuteException(): void { $addressId = 1; $postValue = 'post_value'; @@ -703,7 +733,7 @@ public function testExecuteException() ->willThrowException($exception); $this->messageManager->expects($this->once()) - ->method('addException') + ->method('addExceptionMessage') ->with($exception, __('We can\'t save the address.')) ->willReturnSelf(); diff --git a/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/TextTest.php b/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/TextTest.php index b95987cba1dcf..7987bdc79ed98 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/TextTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/TextTest.php @@ -5,8 +5,10 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Customer\Test\Unit\Model\Metadata\Form; +use Magento\Customer\Api\Data\ValidationRuleInterface; use Magento\Customer\Model\Metadata\Form\Text; class TextTest extends AbstractFormTestCase @@ -14,6 +16,9 @@ class TextTest extends AbstractFormTestCase /** @var \Magento\Framework\Stdlib\StringUtils */ protected $stringHelper; + /** + * {@inheritDoc} + */ protected function setUp() { parent::setUp(); @@ -111,7 +116,7 @@ public function validateValueRequiredDataProvider() */ public function testValidateValueLength($value, $expected) { - $minTextLengthRule = $this->getMockBuilder(\Magento\Customer\Api\Data\ValidationRuleInterface::class) + $minTextLengthRule = $this->getMockBuilder(ValidationRuleInterface::class) ->disableOriginalConstructor() ->setMethods(['getName', 'getValue']) ->getMockForAbstractClass(); @@ -122,7 +127,7 @@ public function testValidateValueLength($value, $expected) ->method('getValue') ->will($this->returnValue(4)); - $maxTextLengthRule = $this->getMockBuilder(\Magento\Customer\Api\Data\ValidationRuleInterface::class) + $maxTextLengthRule = $this->getMockBuilder(ValidationRuleInterface::class) ->disableOriginalConstructor() ->setMethods(['getName', 'getValue']) ->getMockForAbstractClass(); @@ -133,7 +138,19 @@ public function testValidateValueLength($value, $expected) ->method('getValue') ->will($this->returnValue(8)); + $inputValidationRule = $this->getMockBuilder(ValidationRuleInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getName', 'getValue']) + ->getMockForAbstractClass(); + $inputValidationRule->expects($this->any()) + ->method('getName') + ->will($this->returnValue('input_validation')); + $inputValidationRule->expects($this->any()) + ->method('getValue') + ->will($this->returnValue('other')); + $validationRules = [ + 'input_validation' => $inputValidationRule, 'min_text_length' => $minTextLengthRule, 'max_text_length' => $maxTextLengthRule, ]; diff --git a/app/code/Magento/Eav/Model/Attribute/Data/Text.php b/app/code/Magento/Eav/Model/Attribute/Data/Text.php index 242f4e98108c9..f81fb2affd3b3 100644 --- a/app/code/Magento/Eav/Model/Attribute/Data/Text.php +++ b/app/code/Magento/Eav/Model/Attribute/Data/Text.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Eav\Model\Attribute\Data; use Magento\Framework\App\RequestInterface; @@ -76,19 +77,9 @@ public function validateValue($value) return true; } - // validate length - $length = $this->_string->strlen(trim($value)); - - $validateRules = $attribute->getValidateRules(); - if (!empty($validateRules['min_text_length']) && $length < $validateRules['min_text_length']) { - $label = __($attribute->getStoreLabel()); - $v = $validateRules['min_text_length']; - $errors[] = __('"%1" length must be equal or greater than %2 characters.', $label, $v); - } - if (!empty($validateRules['max_text_length']) && $length > $validateRules['max_text_length']) { - $label = __($attribute->getStoreLabel()); - $v = $validateRules['max_text_length']; - $errors[] = __('"%1" length must be equal or less than %2 characters.', $label, $v); + $result = $this->validateLength($attribute, $value); + if (count($result) !== 0) { + $errors = array_merge($errors, $result); } $result = $this->_validateInputRule($value); @@ -142,4 +133,33 @@ public function outputValue($format = \Magento\Eav\Model\AttributeDataFactory::O return $value; } + + /** + * Validates value length by attribute rules + * + * @param \Magento\Eav\Model\Attribute $attribute + * @param string $value + * @return array errors + */ + private function validateLength(\Magento\Eav\Model\Attribute $attribute, $value): array + { + $errors = []; + $length = $this->_string->strlen(trim($value)); + $validateRules = $attribute->getValidateRules(); + + if (!empty($validateRules['input_validation'])) { + if (!empty($validateRules['min_text_length']) && $length < $validateRules['min_text_length']) { + $label = __($attribute->getStoreLabel()); + $v = $validateRules['min_text_length']; + $errors[] = __('"%1" length must be equal or greater than %2 characters.', $label, $v); + } + if (!empty($validateRules['max_text_length']) && $length > $validateRules['max_text_length']) { + $label = __($attribute->getStoreLabel()); + $v = $validateRules['max_text_length']; + $errors[] = __('"%1" length must be equal or less than %2 characters.', $label, $v); + } + } + + return $errors; + } } diff --git a/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/MultilineTest.php b/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/MultilineTest.php index f628d52f6647f..bde4a3adb9de5 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/MultilineTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/MultilineTest.php @@ -3,8 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Eav\Test\Unit\Model\Attribute\Data; +use Magento\Framework\Locale\ResolverInterface; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; + class MultilineTest extends \PHPUnit\Framework\TestCase { /** @@ -13,15 +17,21 @@ class MultilineTest extends \PHPUnit\Framework\TestCase protected $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\Stdlib\StringUtils */ protected $stringMock; + /** + * {@inheritDoc} + */ protected function setUp() { - $timezoneMock = $this->createMock(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class); + /** @var TimezoneInterface $timezoneMock */ + $timezoneMock = $this->createMock(TimezoneInterface::class); + /** @var \Psr\Log\LoggerInterface $loggerMock */ $loggerMock = $this->createMock(\Psr\Log\LoggerInterface::class); - $localeResolverMock = $this->createMock(\Magento\Framework\Locale\ResolverInterface::class); + /** @var ResolverInterface $localeResolverMock */ + $localeResolverMock = $this->createMock(ResolverInterface::class); $this->stringMock = $this->createMock(\Magento\Framework\Stdlib\StringUtils::class); $this->model = new \Magento\Eav\Model\Attribute\Data\Multiline( @@ -33,7 +43,7 @@ protected function setUp() } /** - * @covers \Magento\Eav\Model\Attribute\Data\Multiline::extractValue + * @covers \Magento\Eav\Model\Attribute\Data\Multiline::extractValue * * @param mixed $param * @param mixed $expectedResult @@ -41,11 +51,15 @@ protected function setUp() */ public function testExtractValue($param, $expectedResult) { + /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\App\RequestInterface $requestMock */ $requestMock = $this->createMock(\Magento\Framework\App\RequestInterface::class); + /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Eav\Model\Attribute $attributeMock */ $attributeMock = $this->createMock(\Magento\Eav\Model\Attribute::class); $requestMock->expects($this->once())->method('getParam')->will($this->returnValue($param)); - $attributeMock->expects($this->once())->method('getAttributeCode')->will($this->returnValue('attributeCode')); + $attributeMock->expects($this->once()) + ->method('getAttributeCode') + ->will($this->returnValue('attributeCode')); $this->model->setAttribute($attributeMock); $this->assertEquals($expectedResult, $this->model->extractValue($requestMock)); @@ -69,7 +83,7 @@ public function extractValueDataProvider() } /** - * @covers \Magento\Eav\Model\Attribute\Data\Multiline::outputValue + * @covers \Magento\Eav\Model\Attribute\Data\Multiline::outputValue * * @param string $format * @param mixed $expectedResult @@ -77,9 +91,13 @@ public function extractValueDataProvider() */ public function testOutputValue($format, $expectedResult) { + /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\Model\AbstractModel $entityMock */ $entityMock = $this->createMock(\Magento\Framework\Model\AbstractModel::class); - $entityMock->expects($this->once())->method('getData')->will($this->returnValue("value1\nvalue2")); + $entityMock->expects($this->once()) + ->method('getData') + ->will($this->returnValue("value1\nvalue2")); + /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Eav\Model\Attribute $attributeMock */ $attributeMock = $this->createMock(\Magento\Eav\Model\Attribute::class); $this->model->setEntity($entityMock); @@ -113,8 +131,8 @@ public function outputValueDataProvider() } /** - * @covers \Magento\Eav\Model\Attribute\Data\Multiline::validateValue - * @covers \Magento\Eav\Model\Attribute\Data\Text::validateValue + * @covers \Magento\Eav\Model\Attribute\Data\Multiline::validateValue + * @covers \Magento\Eav\Model\Attribute\Data\Text::validateValue * * @param mixed $value * @param bool $isAttributeRequired @@ -124,14 +142,23 @@ public function outputValueDataProvider() */ public function testValidateValue($value, $isAttributeRequired, $rules, $expectedResult) { + /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\Model\AbstractModel $entityMock */ $entityMock = $this->createMock(\Magento\Framework\Model\AbstractModel::class); - $entityMock->expects($this->any())->method('getDataUsingMethod')->will($this->returnValue("value1\nvalue2")); + $entityMock->expects($this->any()) + ->method('getDataUsingMethod') + ->will($this->returnValue("value1\nvalue2")); + /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Eav\Model\Attribute $attributeMock */ $attributeMock = $this->createMock(\Magento\Eav\Model\Attribute::class); $attributeMock->expects($this->any())->method('getMultilineCount')->will($this->returnValue(2)); $attributeMock->expects($this->any())->method('getValidateRules')->will($this->returnValue($rules)); - $attributeMock->expects($this->any())->method('getStoreLabel')->will($this->returnValue('Label')); - $attributeMock->expects($this->any())->method('getIsRequired')->will($this->returnValue($isAttributeRequired)); + $attributeMock->expects($this->any()) + ->method('getStoreLabel') + ->will($this->returnValue('Label')); + + $attributeMock->expects($this->any()) + ->method('getIsRequired') + ->will($this->returnValue($isAttributeRequired)); $this->stringMock->expects($this->any())->method('strlen')->will($this->returnValue(5)); @@ -159,7 +186,7 @@ public function validateValueDataProvider() 'expectedResult' => true, ], [ - 'value' => ['value1', 'value2'], + 'value' => ['value1', 'value2'], 'isAttributeRequired' => false, 'rules' => [], 'expectedResult' => true, @@ -167,13 +194,13 @@ public function validateValueDataProvider() [ 'value' => 'value', 'isAttributeRequired' => false, - 'rules' => ['max_text_length' => 3], + 'rules' => ['input_validation' => 'other', 'max_text_length' => 3], 'expectedResult' => ['"Label" length must be equal or less than 3 characters.'], ], [ 'value' => 'value', 'isAttributeRequired' => false, - 'rules' => ['min_text_length' => 10], + 'rules' => ['input_validation' => 'other', 'min_text_length' => 10], 'expectedResult' => ['"Label" length must be equal or greater than 10 characters.'], ], [ diff --git a/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/TextTest.php b/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/TextTest.php index 36eac0bfab27b..bbbe712b2bb42 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/TextTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/TextTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Eav\Test\Unit\Model\Attribute\Data; class TextTest extends \PHPUnit\Framework\TestCase @@ -12,6 +13,9 @@ class TextTest extends \PHPUnit\Framework\TestCase */ protected $_model; + /** + * {@inheritDoc} + */ protected function setUp() { $locale = $this->createMock(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class); @@ -19,14 +23,77 @@ protected function setUp() $logger = $this->createMock(\Psr\Log\LoggerInterface::class); $helper = $this->createMock(\Magento\Framework\Stdlib\StringUtils::class); - $attributeData = [ + $this->_model = new \Magento\Eav\Model\Attribute\Data\Text($locale, $logger, $localeResolver, $helper); + $this->_model->setAttribute( + $this->createAttribute( + [ + 'store_label' => 'Test', + 'attribute_code' => 'test', + 'is_required' => 1, + 'validate_rules' => ['min_text_length' => 0, 'max_text_length' => 0, 'input_validation' => 0], + ] + ) + ); + } + + /** + * {@inheritDoc} + */ + protected function tearDown() + { + $this->_model = null; + } + + /** + * Test for string validation + */ + public function testValidateValueString(): void + { + $inputValue = '0'; + $expectedResult = true; + $this->assertEquals($expectedResult, $this->_model->validateValue($inputValue)); + } + + /** + * Test for integer validation + */ + public function testValidateValueInteger(): void + { + $inputValue = 0; + $expectedResult = ['"Test" is a required value.']; + $result = $this->_model->validateValue($inputValue); + $this->assertEquals($expectedResult, [(string)$result[0]]); + } + + /** + * Test without length validation + */ + public function testWithoutLengthValidation(): void + { + $expectedResult = true; + $defaultAttributeData = [ 'store_label' => 'Test', 'attribute_code' => 'test', 'is_required' => 1, 'validate_rules' => ['min_text_length' => 0, 'max_text_length' => 0, 'input_validation' => 0], ]; - $attributeClass = \Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class; + $defaultAttributeData['validate_rules']['min_text_length'] = 2; + $this->_model->setAttribute($this->createAttribute($defaultAttributeData)); + $this->assertEquals($expectedResult, $this->_model->validateValue('t')); + + $defaultAttributeData['validate_rules']['max_text_length'] = 3; + $this->_model->setAttribute($this->createAttribute($defaultAttributeData)); + $this->assertEquals($expectedResult, $this->_model->validateValue('test')); + } + + /** + * @param array $attributeData + * @return \Magento\Eav\Model\Attribute + */ + protected function createAttribute($attributeData): \Magento\Eav\Model\Entity\Attribute\AbstractAttribute + { + $attributeClass = \Magento\Eav\Model\Attribute::class; $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $eavTypeFactory = $this->createMock(\Magento\Eav\Model\Entity\TypeFactory::class); $arguments = $objectManagerHelper->getConstructArguments( @@ -41,27 +108,6 @@ protected function setUp() ->setMethods(['_init']) ->setConstructorArgs($arguments) ->getMock(); - $this->_model = new \Magento\Eav\Model\Attribute\Data\Text($locale, $logger, $localeResolver, $helper); - $this->_model->setAttribute($attribute); - } - - protected function tearDown() - { - $this->_model = null; - } - - public function testValidateValueString() - { - $inputValue = '0'; - $expectedResult = true; - $this->assertEquals($expectedResult, $this->_model->validateValue($inputValue)); - } - - public function testValidateValueInteger() - { - $inputValue = 0; - $expectedResult = ['"Test" is a required value.']; - $result = $this->_model->validateValue($inputValue); - $this->assertEquals($expectedResult, [(string)$result[0]]); + return $attribute; } } diff --git a/app/code/Magento/ProductVideo/Block/Product/View/Gallery.php b/app/code/Magento/ProductVideo/Block/Product/View/Gallery.php index 2277aa980f66c..45c4925640a0c 100644 --- a/app/code/Magento/ProductVideo/Block/Product/View/Gallery.php +++ b/app/code/Magento/ProductVideo/Block/Product/View/Gallery.php @@ -9,9 +9,8 @@ * * @author Magento Core Team */ -namespace Magento\ProductVideo\Block\Product\View; -use Magento\Catalog\Model\Product\Image\UrlBuilder; +namespace Magento\ProductVideo\Block\Product\View; /** * @api @@ -93,6 +92,6 @@ public function getVideoSettingsJson() */ public function getOptionsMediaGalleryDataJson() { - return $this->jsonEncoder->encode([]); + return $this->jsonEncoder->encode([]); } } diff --git a/app/code/Magento/ProductVideo/Model/Plugin/Catalog/Product/Gallery/CreateHandler.php b/app/code/Magento/ProductVideo/Model/Plugin/Catalog/Product/Gallery/CreateHandler.php index ab3c8f3c5269a..ce1493b349a85 100644 --- a/app/code/Magento/ProductVideo/Model/Plugin/Catalog/Product/Gallery/CreateHandler.php +++ b/app/code/Magento/ProductVideo/Model/Plugin/Catalog/Product/Gallery/CreateHandler.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\ProductVideo\Model\Plugin\Catalog\Product\Gallery; use Magento\ProductVideo\Model\Product\Attribute\Media\ExternalVideoEntryConverter; @@ -29,6 +30,7 @@ public function beforeExecute( \Magento\Catalog\Model\Product $product, array $arguments = [] ) { + /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute */ $attribute = $mediaGalleryCreateHandler->getAttribute(); $mediaCollection = $this->getMediaEntriesDataCollection($product, $attribute); if (!empty($mediaCollection)) { @@ -36,7 +38,7 @@ public function beforeExecute( $mediaCollection = $this->addAdditionalStoreData($mediaCollection, $storeDataCollection); $product->setData( $attribute->getAttributeCode(), - $mediaCollection + $product->getData($attribute->getAttributeCode()) + $mediaCollection ); } } @@ -56,6 +58,9 @@ public function afterExecute( ); if (!empty($mediaCollection)) { + $newVideoCollection = $this->collectNewVideos($mediaCollection); + $this->saveVideoData($newVideoCollection, 0); + $videoDataCollection = $this->collectVideoData($mediaCollection); $this->saveVideoData($videoDataCollection, $product->getStoreId()); $this->saveAdditionalStoreData($videoDataCollection); @@ -167,10 +172,7 @@ protected function collectVideoData(array $mediaCollection) { $videoDataCollection = []; foreach ($mediaCollection as $item) { - if (!empty($item['media_type']) - && empty($item['removed']) - && $item['media_type'] == ExternalVideoEntryConverter::MEDIA_TYPE_CODE - ) { + if ($this->isVideoItem($item)) { $videoData = $this->extractVideoDataFromRowData($item); $videoDataCollection[] = $videoData; } @@ -199,11 +201,7 @@ protected function collectVideoEntriesIdsToAdditionalLoad(array $mediaCollection { $ids = []; foreach ($mediaCollection as $item) { - if (!empty($item['media_type']) - && empty($item['removed']) - && $item['media_type'] == ExternalVideoEntryConverter::MEDIA_TYPE_CODE - && isset($item['save_data_from']) - ) { + if ($this->isVideoItem($item) && isset($item['save_data_from'])) { $ids[] = $item['save_data_from']; } } @@ -215,18 +213,19 @@ protected function collectVideoEntriesIdsToAdditionalLoad(array $mediaCollection * @param array $data * @return array */ - protected function addAdditionalStoreData(array $mediaCollection, array $data) + protected function addAdditionalStoreData(array $mediaCollection, array $data): array { - foreach ($mediaCollection as &$mediaItem) { + $return = []; + foreach ($mediaCollection as $key => $mediaItem) { if (!empty($mediaItem['save_data_from'])) { $additionalData = $this->createAdditionalStoreDataCollection($data, $mediaItem['save_data_from']); if (!empty($additionalData)) { $mediaItem[self::ADDITIONAL_STORE_DATA_KEY] = $additionalData; } } + $return[$key] = $mediaItem; } - - return ['images' => $mediaCollection]; + return ['images' => $return]; } /** @@ -234,7 +233,7 @@ protected function addAdditionalStoreData(array $mediaCollection, array $data) * @param int $valueId * @return array */ - protected function createAdditionalStoreDataCollection(array $storeData, $valueId) + protected function createAdditionalStoreDataCollection(array $storeData, $valueId): array { $result = []; foreach ($storeData as $item) { @@ -246,4 +245,41 @@ protected function createAdditionalStoreDataCollection(array $storeData, $valueI return $result; } + + /** + * @param array $mediaCollection + * @return array + */ + private function collectNewVideos(array $mediaCollection): array + { + $return = []; + foreach ($mediaCollection as $item) { + if ($this->isVideoItem($item) && $this->isNewVideo($item)) { + $return[] = $this->extractVideoDataFromRowData($item); + } + } + return $return; + } + + /** + * @param array $item + * @return bool + */ + private function isVideoItem(array $item): bool + { + return !empty($item['media_type']) + && empty($item['removed']) + && $item['media_type'] == ExternalVideoEntryConverter::MEDIA_TYPE_CODE; + } + + /** + * @param array $item + * @return bool + */ + private function isNewVideo(array $item): bool + { + return !isset($item['video_url_default'], $item['video_title_default']) + || empty($item['video_url_default']) + || empty($item['video_title_default']); + } } diff --git a/app/code/Magento/ProductVideo/Model/Plugin/Catalog/Product/Gallery/ReadHandler.php b/app/code/Magento/ProductVideo/Model/Plugin/Catalog/Product/Gallery/ReadHandler.php index 6c534580c39d9..31d63efcf8cb0 100644 --- a/app/code/Magento/ProductVideo/Model/Plugin/Catalog/Product/Gallery/ReadHandler.php +++ b/app/code/Magento/ProductVideo/Model/Plugin/Catalog/Product/Gallery/ReadHandler.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\ProductVideo\Model\Plugin\Catalog\Product\Gallery; use Magento\ProductVideo\Model\Product\Attribute\Media\ExternalVideoEntryConverter; @@ -55,8 +56,8 @@ protected function collectVideoEntriesIds(array $mediaCollection) { $ids = []; foreach ($mediaCollection as $item) { - if ($item['media_type'] == ExternalVideoEntryConverter::MEDIA_TYPE_CODE - && !array_key_exists('video_url', $item) + if ($item['media_type'] === ExternalVideoEntryConverter::MEDIA_TYPE_CODE + && !isset($item['video_url']) ) { $ids[] = $item['value_id']; } @@ -72,7 +73,7 @@ protected function collectVideoEntriesIds(array $mediaCollection) protected function loadVideoDataById(array $ids, $storeId = null) { $mainTableAlias = $this->resourceModel->getMainTableAlias(); - $joinConditions = $mainTableAlias.'.value_id = store_value.value_id'; + $joinConditions = $mainTableAlias . '.value_id = store_value.value_id'; if (null !== $storeId) { $joinConditions = implode( ' AND ', @@ -138,10 +139,10 @@ protected function addVideoDataToMediaEntries(array $mediaCollection, array $dat protected function substituteNullsWithDefaultValues(array $rowData) { foreach ($this->getVideoProperties(false) as $key) { - if (empty($rowData[$key]) && !empty($rowData[$key.'_default'])) { - $rowData[$key] = $rowData[$key.'_default']; + if (empty($rowData[$key]) && !empty($rowData[$key . '_default'])) { + $rowData[$key] = $rowData[$key . '_default']; } - unset($rowData[$key.'_default']); + unset($rowData[$key . '_default']); } return $rowData; @@ -154,8 +155,7 @@ protected function substituteNullsWithDefaultValues(array $rowData) protected function getVideoProperties($withDbMapping = true) { $properties = $this->videoPropertiesDbMapping; - unset($properties['value_id']); - unset($properties['store_id']); + unset($properties['value_id'], $properties['store_id']); return $withDbMapping ? $properties : array_keys($properties); } diff --git a/app/code/Magento/ProductVideo/Test/Unit/Model/Plugin/Catalog/Product/Gallery/CreateHandlerTest.php b/app/code/Magento/ProductVideo/Test/Unit/Model/Plugin/Catalog/Product/Gallery/CreateHandlerTest.php index 5770ea8b5689d..57ad71997dad7 100644 --- a/app/code/Magento/ProductVideo/Test/Unit/Model/Plugin/Catalog/Product/Gallery/CreateHandlerTest.php +++ b/app/code/Magento/ProductVideo/Test/Unit/Model/Plugin/Catalog/Product/Gallery/CreateHandlerTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\ProductVideo\Test\Unit\Model\Plugin\Catalog\Product\Gallery; /** @@ -37,6 +38,9 @@ class CreateHandlerTest extends \PHPUnit\Framework\TestCase */ protected $mediaGalleryCreateHandler; + /** + * {@inheritDoc} + */ protected function setUp() { $this->product = $this->createMock(\Magento\Catalog\Model\Product::class); @@ -62,72 +66,18 @@ protected function setUp() ); } - public function testAfterExecute() + /** + * @dataProvider provideImageForAfterExecute + * @param array $image + * @param array $expectedSave + * @param int $rowSaved + */ + public function testAfterExecute($image, $expectedSave, $rowSaved): void { - $mediaData = [ - 'images' => [ - '72mljfhmasfilp9cuq' => [ - 'position' => '3', - 'media_type' => 'external-video', - 'file' => '/i/n/index111111.jpg', - 'value_id' => '4', - 'label' => '', - 'disabled' => '0', - 'removed' => '', - 'video_provider' => 'youtube', - 'video_url' => 'https://www.youtube.com/watch?v=ab123456', - 'video_title' => 'Some second title', - 'video_description' => 'Description second', - 'video_metadata' => 'meta two', - 'role' => '', - ], - 'w596fi79hv1p6wj21u' => [ - 'position' => '4', - 'media_type' => 'image', - 'video_provider' => '', - 'file' => '/h/d/hd_image.jpg', - 'value_id' => '7', - 'label' => '', - 'disabled' => '0', - 'removed' => '', - 'video_url' => '', - 'video_title' => '', - 'video_description' => '', - 'video_metadata' => '', - 'role' => '', - ], - 'tcodwd7e0dirifr64j' => [ - 'position' => '4', - 'media_type' => 'external-video', - 'file' => '/s/a/sample_3.jpg', - 'value_id' => '5', - 'label' => '', - 'disabled' => '0', - 'removed' => '', - 'video_provider' => 'youtube', - 'video_url' => 'https://www.youtube.com/watch?v=ab123456', - 'video_title' => 'Some second title', - 'video_description' => 'Description second', - 'video_metadata' => 'meta two', - 'role' => '', - 'additional_store_data' => [ - 0 => [ - 'store_id' => '0', - 'video_provider' => null, - 'video_url' => 'https://www.youtube.com/watch?v=ab123456', - 'video_title' => 'New Title', - 'video_description' => 'New Description', - 'video_metadata' => 'New metadata', - ], - ] - ], - ], - ]; - $this->product->expects($this->once()) ->method('getData') ->with('media_gallery') - ->willReturn($mediaData); + ->willReturn(['images' => $image]); $this->product->expects($this->once()) ->method('getStoreId') ->willReturn(0); @@ -136,13 +86,150 @@ public function testAfterExecute() ->method('getAttribute') ->willReturn($this->attribute); - $this->subject->afterExecute( - $this->mediaGalleryCreateHandler, - $this->product - ); + $this->resourceModel->expects($this->exactly($rowSaved)) + ->method('saveDataRow') + ->with('catalog_product_entity_media_gallery_value_video', $expectedSave) + ->willReturn(1); + + $this->subject->afterExecute($this->mediaGalleryCreateHandler, $this->product); + } + + /** + * DataProvider for testAfterExecute + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @return array + */ + public function provideImageForAfterExecute(): array + { + return [ + 'new_video' => [ + [ + '72mljfhmasfilp9cuq' => [ + 'position' => '3', + 'media_type' => 'external-video', + 'file' => '/i/n/index111111.jpg', + 'value_id' => '4', + 'label' => '', + 'disabled' => '0', + 'removed' => '', + 'video_provider' => 'youtube', + 'video_url' => 'https://www.youtube.com/watch?v=ab123456', + 'video_title' => 'Some second title', + 'video_description' => 'Description second', + 'video_metadata' => 'meta two', + 'role' => '', + ], + ], + [ + 'value_id' => '4', + 'store_id' => 0, + 'provider' => 'youtube', + 'url' => 'https://www.youtube.com/watch?v=ab123456', + 'title' => 'Some second title', + 'description' => 'Description second', + 'metadata' => 'meta two', + ], + 2 + ], + 'image' => [ + [ + 'w596fi79hv1p6wj21u' => [ + 'position' => '4', + 'media_type' => 'image', + 'video_provider' => '', + 'file' => '/h/d/hd_image.jpg', + 'value_id' => '7', + 'label' => '', + 'disabled' => '0', + 'removed' => '', + 'video_url' => '', + 'video_title' => '', + 'video_description' => '', + 'video_metadata' => '', + 'role' => '', + ], + ], + [], + 0 + ], + 'new_video_with_additional_data' => [ + [ + 'tcodwd7e0dirifr64j' => [ + 'position' => '4', + 'media_type' => 'external-video', + 'file' => '/s/a/sample_3.jpg', + 'value_id' => '5', + 'label' => '', + 'disabled' => '0', + 'removed' => '', + 'video_provider' => 'youtube', + 'video_url' => 'https://www.youtube.com/watch?v=ab123456', + 'video_title' => 'Some second title', + 'video_description' => 'Description second', + 'video_metadata' => 'meta two', + 'role' => '', + 'additional_store_data' => [ + 0 => [ + 'store_id' => 0, + 'video_provider' => 'youtube', + 'video_url' => 'https://www.youtube.com/watch?v=ab123456', + 'video_title' => 'Some second title', + 'video_description' => 'Description second', + 'video_metadata' => 'meta two', + ], + ] + ], + ], + [ + 'value_id' => '5', + 'store_id' => 0, + 'provider' => 'youtube', + 'url' => 'https://www.youtube.com/watch?v=ab123456', + 'title' => 'Some second title', + 'description' => 'Description second', + 'metadata' => 'meta two', + ], + 3 + ], + 'not_new_video' => [ + [ + '72mljfhmasfilp9cuq' => [ + 'position' => '3', + 'media_type' => 'external-video', + 'file' => '/i/n/index111111.jpg', + 'value_id' => '4', + 'label' => '', + 'disabled' => '0', + 'removed' => '', + 'video_provider' => 'youtube', + 'video_url' => 'https://www.youtube.com/watch?v=ab123456', + 'video_url_default' => 'https://www.youtube.com/watch?v=ab123456', + 'video_title' => 'Some second title', + 'video_title_default' => 'Some second title', + 'video_description' => 'Description second', + 'video_metadata' => 'meta two', + 'role' => '', + ], + ], + [ + 'value_id' => '4', + 'store_id' => 0, + 'provider' => 'youtube', + 'url' => 'https://www.youtube.com/watch?v=ab123456', + 'title' => 'Some second title', + 'description' => 'Description second', + 'metadata' => 'meta two', + ], + 1 + ], + ]; } - public function testAfterExecuteEmpty() + /** + * Tests empty media gallery + */ + public function testAfterExecuteEmpty(): void { $this->product->expects($this->once()) ->method('getData') @@ -162,7 +249,7 @@ public function testAfterExecuteEmpty() /** * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function testBeforeExecute() + public function testBeforeExecute(): void { $mediaData = [ 'images' => [ diff --git a/app/code/Magento/Rule/Model/Condition/AbstractCondition.php b/app/code/Magento/Rule/Model/Condition/AbstractCondition.php index e3bec2d9959b1..a1987f67e47f2 100644 --- a/app/code/Magento/Rule/Model/Condition/AbstractCondition.php +++ b/app/code/Magento/Rule/Model/Condition/AbstractCondition.php @@ -355,10 +355,10 @@ public function getValueParsed() { if (!$this->hasValueParsed()) { $value = $this->getData('value'); - if (is_array($value) && isset($value[0]) && is_string($value[0])) { - $value = $value[0]; + if (is_array($value) && count($value) === 1) { + $value = reset($value); } - if ($this->isArrayOperatorType() && $value) { + if (!is_array($value) && $this->isArrayOperatorType() && $value) { $value = preg_split('#\s*[,;]\s*#', $value, null, PREG_SPLIT_NO_EMPTY); } $this->setValueParsed($value); @@ -380,7 +380,7 @@ public function isArrayOperatorType() } /** - * @return array + * @return mixed */ public function getValue() { diff --git a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php index e1c9bf99f2675..2894de0f19b87 100644 --- a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php +++ b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php @@ -163,8 +163,22 @@ protected function _getMappedSqlCondition( $this->_conditionOperatorMap[$conditionOperator] ); + $bindValue = $condition->getBindArgumentValue(); + $expression = $value . $this->_connection->quoteInto($sql, $bindValue); + + // values for multiselect attributes can be saved in comma-separated format + // below is a solution for matching such conditions with selected values + if (is_array($bindValue) && \in_array($conditionOperator, ['()', '{}'], true)) { + foreach ($bindValue as $item) { + $expression .= $this->_connection->quoteInto( + " OR (FIND_IN_SET (?, {$this->_connection->quoteIdentifier($argument)}) > 0)", + $item + ); + } + } + return $this->_expressionFactory->create( - ['expression' => $value . $this->_connection->quoteInto($sql, $condition->getBindArgumentValue())] + ['expression' => $expression] ); } @@ -174,6 +188,7 @@ protected function _getMappedSqlCondition( * @param bool $isDefaultStoreUsed * @return string * @SuppressWarnings(PHPMD.NPathComplexity) + * @throws \Magento\Framework\Exception\LocalizedException */ protected function _getMappedSqlCombination( Combine $combine, diff --git a/app/code/Magento/SalesRule/Model/Rule/Condition/Product.php b/app/code/Magento/SalesRule/Model/Rule/Condition/Product.php index 499e35db9dfd6..d2e7cabe473f4 100644 --- a/app/code/Magento/SalesRule/Model/Rule/Condition/Product.php +++ b/app/code/Magento/SalesRule/Model/Rule/Condition/Product.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\SalesRule\Model\Rule\Condition; /** @@ -51,10 +52,17 @@ public function validate(\Magento\Framework\Model\AbstractModel $model) $attrCode = $this->getAttribute(); - if ('category_ids' == $attrCode) { + if ($attrCode === 'category_ids') { return $this->validateAttribute($this->_getAvailableInCategories($product->getId())); } + if ($attrCode === 'quote_item_price') { + $numericOperations = $this->getDefaultOperatorInputByType()['numeric']; + if (in_array($this->getOperator(), $numericOperations)) { + $this->setData('value', $this->getFormattedPrice($this->getValue())); + } + } + return parent::validate($product); } @@ -79,4 +87,23 @@ public function getValueElementChooserUrl() } return $url !== false ? $this->_backendData->getUrl($url) : ''; } + + /** + * @param string $value + * @return float|null + */ + private function getFormattedPrice($value) + { + $value = preg_replace('/[^0-9^\^.,-]/m', '', $value); + + /** + * If the comma is the third symbol in the number, we consider it to be a decimal separator + */ + $separatorComa = strpos($value, ','); + $separatorDot = strpos($value, '.'); + if ($separatorComa !== false && $separatorDot === false && preg_match('/,\d{3}$/m', $value) === 1) { + $value .= '.00'; + } + return $this->_localeFormat->getNumber($value); + } } diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/ProductTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/ProductTest.php index 0bce282747b16..51c7b9ede5aa2 100644 --- a/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/ProductTest.php +++ b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/ProductTest.php @@ -3,11 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\SalesRule\Test\Unit\Model\Rule\Condition; +use Magento\Directory\Model\CurrencyFactory; +use Magento\Framework\App\ScopeResolverInterface; use \Magento\Framework\DB\Adapter\AdapterInterface; use \Magento\Framework\DB\Select; -use \Magento\Framework\Model\AbstractModel; +use Magento\Framework\Locale\Format; +use Magento\Framework\Locale\ResolverInterface; use Magento\Quote\Model\Quote\Item\AbstractItem; use \Magento\Rule\Model\Condition\Context; use \Magento\Backend\Helper\Data; @@ -50,8 +54,8 @@ class ProductTest extends \PHPUnit\Framework\TestCase /** @var Collection|\PHPUnit_Framework_MockObject_MockObject */ protected $collectionMock; - /** @var FormatInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $formatMock; + /** @var FormatInterface */ + protected $format; /** @var AttributeLoaderInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $attributeLoaderInterfaceMock; @@ -130,8 +134,12 @@ protected function setUp() $this->collectionMock = $this->getMockBuilder(Collection::class) ->disableOriginalConstructor() ->getMock(); - $this->formatMock = $this->getMockBuilder(FormatInterface::class) - ->getMockForAbstractClass(); + $this->format = new Format( + $this->getMockBuilder(ScopeResolverInterface::class)->disableOriginalConstructor()->getMock(), + $this->getMockBuilder(ResolverInterface::class)->disableOriginalConstructor()->getMock(), + $this->getMockBuilder(CurrencyFactory::class)->disableOriginalConstructor()->getMock() + ); + $this->model = new SalesRuleProduct( $this->contextMock, $this->backendHelperMock, @@ -140,7 +148,7 @@ protected function setUp() $this->productRepositoryMock, $this->productMock, $this->collectionMock, - $this->formatMock + $this->format ); } @@ -199,7 +207,10 @@ public function testGetValueElementChooserUrl($attribute, $url, $jsObject = '') $this->assertEquals($url, $this->model->getValueElementChooserUrl()); } - public function testValidateCategoriesIgnoresVisibility() + /** + * test ValidateCategoriesIgnoresVisibility + */ + public function testValidateCategoriesIgnoresVisibility(): void { /* @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $product */ $product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) @@ -231,4 +242,81 @@ public function testValidateCategoriesIgnoresVisibility() $this->model->validate($item); } + + /** + * @param boolean $isValid + * @param string $conditionValue + * @param string $operator + * @param double $productPrice + * @dataProvider localisationProvider + */ + public function testQuoteLocaleFormatPrice($isValid, $conditionValue, $operator = '>=', $productPrice = 2000.00) + { + $attr = $this->getMockBuilder(\Magento\Framework\Model\ResourceModel\Db\AbstractDb::class) + ->disableOriginalConstructor() + ->setMethods(['getAttribute']) + ->getMockForAbstractClass(); + + $attr->expects($this->any()) + ->method('getAttribute') + ->willReturn(''); + + /* @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $product */ + $product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + ->disableOriginalConstructor() + ->setMethods(['setQuoteItemPrice', 'getResource', 'hasData', 'getData',]) + ->getMock(); + + $product->expects($this->any()) + ->method('setQuoteItemPrice') + ->willReturnSelf(); + + $product->expects($this->any()) + ->method('getResource') + ->willReturn($attr); + + $product->expects($this->any()) + ->method('hasData') + ->willReturn(true); + + $product->expects($this->any()) + ->method('getData') + ->with('quote_item_price') + ->willReturn($productPrice); + + /* @var AbstractItem|\PHPUnit_Framework_MockObject_MockObject $item */ + $item = $this->getMockBuilder(AbstractItem::class) + ->disableOriginalConstructor() + ->setMethods(['getPrice', 'getProduct',]) + ->getMockForAbstractClass(); + + $item->expects($this->any()) + ->method('getPrice') + ->willReturn($productPrice); + + $item->expects($this->any()) + ->method('getProduct') + ->willReturn($product); + + $this->model->setAttribute('quote_item_price') + ->setOperator($operator); + + $this->assertEquals($isValid, $this->model->setValue($conditionValue)->validate($item)); + } + + /** + * DataProvider for testQuoteLocaleFormatPrice + * + * @return array + */ + public function localisationProvider(): array + { + return [ + 'number' => [true, 500.01], + 'locale' => [true, '1,500.03'], + 'operation' => [true, '1,500.03', '!='], + 'stringOperation' => [false, '1,500.03', '{}'], + 'smallPrice' => [false, '1,500.03', '>=', 1000], + ]; + } } diff --git a/app/code/Magento/Ui/DataProvider/EavValidationRules.php b/app/code/Magento/Ui/DataProvider/EavValidationRules.php index 12e345e1fa12c..6e2bb3866e947 100644 --- a/app/code/Magento/Ui/DataProvider/EavValidationRules.php +++ b/app/code/Magento/Ui/DataProvider/EavValidationRules.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Ui\DataProvider; use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; @@ -31,25 +32,51 @@ class EavValidationRules */ public function build(AbstractAttribute $attribute, array $data) { - $validation = []; + $validations = []; if (isset($data['required']) && $data['required'] == 1) { - $validation = array_merge($validation, ['required-entry' => true]); + $validations = array_merge($validations, ['required-entry' => true]); } if ($attribute->getFrontendInput() === 'price') { - $validation = array_merge($validation, ['validate-zero-or-greater' => true]); + $validations = array_merge($validations, ['validate-zero-or-greater' => true]); } if ($attribute->getValidateRules()) { - $validation = array_merge($validation, $attribute->getValidateRules()); + $validations = array_merge($validations, $this->clipLengthRules($attribute->getValidateRules())); } + return $this->aggregateRules($validations); + } + + /** + * @param array $validations + * @return array + */ + private function aggregateRules(array $validations): array + { $rules = []; - foreach ($validation as $type => $ruleName) { - $rule = [$type => $ruleName]; + foreach ($validations as $type => $ruleValue) { + $rule = [$type => $ruleValue]; if ($type === 'input_validation') { - $rule = isset($this->validationRules[$ruleName]) ? $this->validationRules[$ruleName] : []; + $rule = $this->validationRules[$ruleValue] ?? []; + } + if (count($rule) !== 0) { + $key = key($rule); + $rules[$key] = $rule[$key]; } - $rules = array_merge($rules, $rule); } + return $rules; + } + /** + * @param array $rules + * @return array + */ + private function clipLengthRules(array $rules): array + { + if (empty($rules['input_validation'])) { + unset( + $rules['min_text_length'], + $rules['max_text_length'] + ); + } return $rules; } } diff --git a/app/code/Magento/Ui/Test/Unit/DataProvider/EavValidationRulesTest.php b/app/code/Magento/Ui/Test/Unit/DataProvider/EavValidationRulesTest.php index debcde4765fc7..cdb62ce1db68d 100644 --- a/app/code/Magento/Ui/Test/Unit/DataProvider/EavValidationRulesTest.php +++ b/app/code/Magento/Ui/Test/Unit/DataProvider/EavValidationRulesTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Ui\Test\Unit\DataProvider; use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; @@ -29,6 +30,9 @@ class EavValidationRulesTest extends \PHPUnit\Framework\TestCase */ protected $attributeMock; + /** + * {@inheritDoc} + */ protected function setUp() { $this->objectManager = new ObjectManager($this); @@ -42,11 +46,13 @@ protected function setUp() } /** + * @param string $attributeInputType + * @param mixed $validateRules * @param array $data * @param array $expected * @dataProvider buildDataProvider */ - public function testBuild($attributeInputType, $validateRules, $data, $expected) + public function testBuild($attributeInputType, $validateRules, $data, $expected): void { $this->attributeMock->expects($this->once())->method('getFrontendInput')->willReturn($attributeInputType); $this->attributeMock->expects($this->any())->method('getValidateRules')->willReturn($validateRules); @@ -70,11 +76,25 @@ public function buildDataProvider() ['', ['input_validation' => 'email'], [], ['validate-email' => true]], ['', ['input_validation' => 'date'], [], ['validate-date' => true]], ['', ['input_validation' => 'other'], [], []], - ['', ['max_text_length' => '254'], ['required' => 1], ['max_text_length' => 254, 'required-entry' => true]], - ['', ['max_text_length' => '254', 'min_text_length' => 1], [], - ['max_text_length' => 254, 'min_text_length' => 1]], - ['', ['max_text_length' => '254', 'input_validation' => 'date'], [], - ['max_text_length' => 254, 'validate-date' => true]], + ['', ['max_text_length' => '254'], ['required' => 1], ['required-entry' => true]], + [ + '', + ['input_validation' => 'other', 'max_text_length' => '254'], + ['required' => 1], + ['max_text_length' => 254, 'required-entry' => true] + ], + [ + '', + ['input_validation' => 'other', 'max_text_length' => '254', 'min_text_length' => 1], + [], + ['max_text_length' => 254, 'min_text_length' => 1] + ], + [ + '', + ['max_text_length' => '254', 'input_validation' => 'date'], + [], + ['max_text_length' => 254, 'validate-date' => true] + ], ]; } } diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js index b729dd3127d90..6d33386fa1f1c 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js @@ -22,7 +22,7 @@ define([ opened: false, level: 0, visible: true, - initializeFieldsetDataByDefault: false, /* Data in some fieldsets should be initialized before open */ + initializeFieldsetDataByDefault: false, /* Data in some fieldsets should be initialized before open */ disabled: false, listens: { 'opened': 'onVisibilityChange' @@ -77,9 +77,9 @@ define([ elem.initContainer(this); elem.on({ - 'update': this.onChildrenUpdate, - 'loading': this.onContentLoading, - 'error': this.onChildrenError + 'update': this.onChildrenUpdate, + 'loading': this.onContentLoading, + 'error': this.onChildrenError }); if (this.disabled) { @@ -155,11 +155,42 @@ define([ * @param {String} message - error message. */ onChildrenError: function (message) { - var hasErrors = this.elems.some('error'); + var hasErrors = false; + + if (!message) { + hasErrors = this._isChildrenHasErrors(hasErrors, this); + } this.error(hasErrors || message); }, + /** + * Returns errors of children if exist + * + * @param {Boolean} hasErrors + * @param {*} container + * @return {Boolean} + * @private + */ + _isChildrenHasErrors: function (hasErrors, container) { + var self = this; + + if (hasErrors === false && container.hasOwnProperty('elems')) { + hasErrors = container.elems.some('error'); + + if (hasErrors === false && container.hasOwnProperty('_elems')) { + container._elems.forEach(function (child) { + + if (hasErrors === false) { + hasErrors = self._isChildrenHasErrors(hasErrors, child); + } + }); + } + } + + return hasErrors; + }, + /** * Callback that sets loading property to true. */ diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutTest.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutTest.xml index 9e20bbdaac1d9..5c05d4a840009 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutTest.xml +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutTest.xml @@ -49,6 +49,10 @@ + + stable:no + MAGETWO-94169: [MTF] - OnePageCheckoutUsingNonDefaultAddress_0 fails on 2.3-develop + severity:S1 diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AddressTest.php index 4c30adb6894e2..32a622d4aa654 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AddressTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AddressTest.php @@ -18,6 +18,9 @@ class AddressTest extends \Magento\TestFramework\TestCase\AbstractController /** @var FormKey */ private $formKey; + /** + * {@inheritDoc} + */ protected function setUp() { parent::setUp(); @@ -150,8 +153,8 @@ public function testFailedFormPostAction() $this->equalTo( [ 'One or more input exceptions have occurred.', - '"street" is required. Enter and try again.', - '"city" is required. Enter and try again.', + '"street" is required. Enter and try again.', + '"city" is required. Enter and try again.', ] ), \Magento\Framework\Message\MessageInterface::TYPE_ERROR diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php index b942365d64a75..6448816c9345c 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php @@ -44,6 +44,9 @@ class IndexTest extends \Magento\TestFramework\TestCase\AbstractBackendControlle /** @var \Magento\TestFramework\ObjectManager */ protected $objectManager; + /** + * {@inheritDoc} + */ protected function setUp() { parent::setUp(); @@ -67,6 +70,9 @@ protected function setUp() ); } + /** + * {@inheritDoc} + */ protected function tearDown() { /** @@ -522,7 +528,10 @@ public function testEditAction() $this->assertContains('

test firstname test lastname

', $body); } - public function testNewAction() + /** + * Tests new action + */ + public function testNewAction(): void { $this->dispatch('backend/customer/index/edit'); $body = $this->getResponse()->getBody(); @@ -717,12 +726,9 @@ public function testValidateCustomerWithAddressFailure() $this->assertContains('{"error":true,"messages":', $body); $this->assertContains('\"First Name\" is a required value', $body); - $this->assertContains('\"First Name\" length must be equal or greater than 1 characters', $body); $this->assertContains('\"Last Name\" is a required value.', $body); - $this->assertContains('\"Last Name\" length must be equal or greater than 1 characters.', $body); $this->assertContains('\"Country\" is a required value.', $body); $this->assertContains('\"Phone Number\" is a required value.', $body); - $this->assertContains('\"Phone Number\" length must be equal or greater than 1 characters.', $body); } /** diff --git a/lib/internal/Magento/Framework/Locale/Format.php b/lib/internal/Magento/Framework/Locale/Format.php index 89f6957011876..ca50cdb2440f4 100644 --- a/lib/internal/Magento/Framework/Locale/Format.php +++ b/lib/internal/Magento/Framework/Locale/Format.php @@ -65,7 +65,7 @@ public function getNumber($value) } //trim spaces and apostrophes - $value = str_replace(['\'', ' '], '', $value); + $value = preg_replace('/[^0-9^\^.,-]/m', '', $value); $separatorComa = strpos($value, ','); $separatorDot = strpos($value, '.'); diff --git a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php index 41583fd1383a5..1141f451c13a5 100644 --- a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php +++ b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php @@ -33,6 +33,9 @@ class FormatTest extends \PHPUnit\Framework\TestCase */ protected $currency; + /** + * {@inheritDoc} + */ protected function setUp() { $this->currency = $this->getMockBuilder(\Magento\Directory\Model\Currency::class) @@ -41,9 +44,7 @@ protected function setUp() $this->scope = $this->getMockBuilder(\Magento\Framework\App\ScopeInterface::class) ->setMethods(['getCurrentCurrency']) ->getMockForAbstractClass(); - $this->scope->expects($this->once()) - ->method('getCurrentCurrency') - ->willReturn($this->currency); + $this->scopeResolver = $this->getMockBuilder(\Magento\Framework\App\ScopeResolverInterface::class) ->setMethods(['getScope']) ->getMockForAbstractClass(); @@ -52,7 +53,10 @@ protected function setUp() ->willReturn($this->scope); $this->localeResolver = $this->getMockBuilder(\Magento\Framework\Locale\ResolverInterface::class) ->getMock(); + + /** @var \Magento\Directory\Model\CurrencyFactory|\PHPUnit_Framework_MockObject_MockObject $currencyFactory */ $currencyFactory = $this->getMockBuilder(\Magento\Directory\Model\CurrencyFactory::class) + ->disableOriginalConstructor() ->getMock(); $this->formatModel = new \Magento\Framework\Locale\Format( @@ -63,21 +67,26 @@ protected function setUp() } /** - * @param $localeCode - * @param $expectedResult + * @param string $localeCode + * @param array $expectedResult * @dataProvider getPriceFormatDataProvider */ - public function testGetPriceFormat($localeCode, $expectedResult) + public function testGetPriceFormat($localeCode, array $expectedResult): void { + $this->scope->expects($this->once()) + ->method('getCurrentCurrency') + ->willReturn($this->currency); + $result = $this->formatModel->getPriceFormat($localeCode); $intersection = array_intersect_assoc($result, $expectedResult); $this->assertCount(count($expectedResult), $intersection); } /** + * * @return array */ - public function getPriceFormatDataProvider() + public function getPriceFormatDataProvider(): array { return [ ['en_US', ['decimalSymbol' => '.', 'groupSymbol' => ',']], @@ -86,4 +95,35 @@ public function getPriceFormatDataProvider() ['uk_UA', ['decimalSymbol' => ',', 'groupSymbol' => ' ']] ]; } + + /** + * + * @param mixed $value + * @param float $expected + * @dataProvider provideNumbers + */ + public function testGetNumber($value, $expected): void + { + $this->assertEquals($expected, $this->formatModel->getNumber($value)); + } + + /** + * + * @return array + */ + public function provideNumbers(): array + { + return [ + [' 2345.4356,1234', 23454356.1234], + ['+23,3452.123', 233452.123], + ['12343', 12343], + ['-9456km', -9456], + ['0', 0], + ['2 054,10', 2054.1], + ['2046,45', 2046.45], + ['2 054.52', 2054.52], + ['2,46 GB', 2.46], + ['2,054.00', 2054], + ]; + } }