From 3ee104d59b0dbe9f843bcca162ce24cef6ac09fa Mon Sep 17 00:00:00 2001 From: Jeroen van Leusden Date: Wed, 12 Dec 2018 15:29:34 +0100 Subject: [PATCH 01/50] Use repository to load order when manually creating an invoice ENGCOM-3679: Static test fix. ENGCOM-3679: Unit test fix. --- .../Adminhtml/Order/Invoice/NewAction.php | 28 +++++++---- .../Adminhtml/Order/Invoice/NewActionTest.php | 47 +++++++++---------- 2 files changed, 41 insertions(+), 34 deletions(-) diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/NewAction.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/NewAction.php index 3295b244f323..ceb231248ef5 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/NewAction.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/NewAction.php @@ -1,18 +1,21 @@ registry = $registry; $this->resultPageFactory = $resultPageFactory; - parent::__construct($context); $this->invoiceService = $invoiceService; + $this->orderRepository = $orderRepository ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(OrderRepositoryInterface::class); } /** @@ -78,14 +91,11 @@ public function execute() { $orderId = $this->getRequest()->getParam('order_id'); $invoiceData = $this->getRequest()->getParam('invoice', []); - $invoiceItems = isset($invoiceData['items']) ? $invoiceData['items'] : []; + $invoiceItems = $invoiceData['items'] ?? []; try { /** @var \Magento\Sales\Model\Order $order */ - $order = $this->_objectManager->create(\Magento\Sales\Model\Order::class)->load($orderId); - if (!$order->getId()) { - throw new \Magento\Framework\Exception\LocalizedException(__('The order no longer exists.')); - } + $order = $this->orderRepository->get($orderId); if (!$order->canInvoice()) { throw new \Magento\Framework\Exception\LocalizedException( diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/NewActionTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/NewActionTest.php index 05c99c9f9ef9..87e27fdb2206 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/NewActionTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/NewActionTest.php @@ -6,12 +6,14 @@ namespace Magento\Sales\Test\Unit\Controller\Adminhtml\Order\Invoice; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Sales\Api\OrderRepositoryInterface; /** * Class NewActionTest * @package Magento\Sales\Controller\Adminhtml\Order\Invoice * @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.TooManyFields) */ class NewActionTest extends \PHPUnit\Framework\TestCase { @@ -90,6 +92,11 @@ class NewActionTest extends \PHPUnit\Framework\TestCase */ protected $invoiceServiceMock; + /** + * @var OrderRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $orderRepositoryMock; + protected function setUp() { $objectManager = new ObjectManager($this); @@ -215,12 +222,15 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); + $this->orderRepositoryMock = $this->createMock(OrderRepositoryInterface::class); + $this->controller = $objectManager->getObject( \Magento\Sales\Controller\Adminhtml\Order\Invoice\NewAction::class, [ 'context' => $contextMock, 'resultPageFactory' => $this->resultPageFactoryMock, - 'invoiceService' => $this->invoiceServiceMock + 'invoiceService' => $this->invoiceServiceMock, + 'orderRepository' => $this->orderRepositoryMock ] ); } @@ -250,19 +260,17 @@ public function testExecute() $orderMock = $this->getMockBuilder(\Magento\Sales\Model\Order::class) ->disableOriginalConstructor() - ->setMethods(['load', 'getId', 'canInvoice']) + ->setMethods(['load', 'canInvoice']) ->getMock(); - $orderMock->expects($this->once()) - ->method('load') - ->with($orderId) - ->willReturnSelf(); - $orderMock->expects($this->once()) - ->method('getId') - ->willReturn($orderId); $orderMock->expects($this->once()) ->method('canInvoice') ->willReturn(true); + $this->orderRepositoryMock->expects($this->once()) + ->method('get') + ->with($orderId) + ->willReturn($orderMock); + $this->invoiceServiceMock->expects($this->once()) ->method('prepareInvoice') ->with($orderMock, []) @@ -285,11 +293,7 @@ public function testExecute() ->with(true) ->will($this->returnValue($commentText)); - $this->objectManagerMock->expects($this->at(0)) - ->method('create') - ->with(\Magento\Sales\Model\Order::class) - ->willReturn($orderMock); - $this->objectManagerMock->expects($this->at(1)) + $this->objectManagerMock->expects($this->once()) ->method('get') ->with(\Magento\Backend\Model\Session::class) ->will($this->returnValue($this->sessionMock)); @@ -318,19 +322,12 @@ public function testExecuteNoOrder() $orderMock = $this->getMockBuilder(\Magento\Sales\Model\Order::class) ->disableOriginalConstructor() - ->setMethods(['load', 'getId', 'canInvoice']) + ->setMethods(['canInvoice']) ->getMock(); - $orderMock->expects($this->once()) - ->method('load') - ->with($orderId) - ->willReturnSelf(); - $orderMock->expects($this->once()) - ->method('getId') - ->willReturn(null); - $this->objectManagerMock->expects($this->at(0)) - ->method('create') - ->with(\Magento\Sales\Model\Order::class) + $this->orderRepositoryMock->expects($this->once()) + ->method('get') + ->with($orderId) ->willReturn($orderMock); $resultRedirect = $this->getMockBuilder(\Magento\Backend\Model\View\Result\Redirect::class) From a0566abffe872cca0a7e06eb2b4d08093f15fbda Mon Sep 17 00:00:00 2001 From: Fabian Schmengler Date: Fri, 11 Jan 2019 11:18:28 +0100 Subject: [PATCH 02/50] Deny access to XML and PHTML files in pub/errors For apache via .htaccess and in nginx sample configuration --- nginx.conf.sample | 7 ++++++- pub/errors/.htaccess | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/nginx.conf.sample b/nginx.conf.sample index 90604808f6ec..80cc88431eff 100644 --- a/nginx.conf.sample +++ b/nginx.conf.sample @@ -159,6 +159,11 @@ location /media/downloadable/ { location /media/import/ { deny all; } +location /errors/ { + location ~* \.xml$ { + deny all; + } +} # PHP entry point for main application location ~ ^/(index|get|static|errors/report|errors/404|errors/503|health_check)\.php$ { @@ -198,6 +203,6 @@ gzip_types gzip_vary on; # Banned locations (only reached if the earlier PHP entry point regexes don't match) -location ~* (\.php$|\.htaccess$|\.git) { +location ~* (\.php$|\.phtml$|\.htaccess$|\.git) { deny all; } diff --git a/pub/errors/.htaccess b/pub/errors/.htaccess index 3692dd439e2f..a7b9cbda0589 100644 --- a/pub/errors/.htaccess +++ b/pub/errors/.htaccess @@ -1,4 +1,7 @@ Options None + + Deny from all + RewriteEngine Off From 24382e3eb968d13e4a86955cbdc2966b989afe1c Mon Sep 17 00:00:00 2001 From: vitaliyboyko Date: Tue, 29 Jan 2019 19:34:06 +0200 Subject: [PATCH 03/50] ENGCOM-20434: Consider deleting/creating url rewrites on change the product attribute visibility via mass action --- ...writeOnChangeProductVisibilityObserver.php | 106 ++++++++++++++++++ ...ProductProcessUrlRewriteSavingObserver.php | 39 ++++++- ...uctProcessUrlRewriteSavingObserverTest.php | 52 +++++++-- .../Magento/CatalogUrlRewrite/etc/events.xml | 3 + .../product_configurable_not_available.php | 2 +- ...uctProcessUrlRewriteSavingObserverTest.php | 30 ++++- .../_files/configurable_products.php | 2 +- .../Framework/DB/Adapter/Pdo/Mysql.php | 4 - 8 files changed, 213 insertions(+), 25 deletions(-) create mode 100644 app/code/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeProductVisibilityObserver.php diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeProductVisibilityObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeProductVisibilityObserver.php new file mode 100644 index 000000000000..16d12bd4c7e4 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeProductVisibilityObserver.php @@ -0,0 +1,106 @@ +productCollectionFactory = $collectionFactory; + $this->urlRewriteGenerator = $urlRewriteGenerator; + $this->urlPersist = $urlPersist; + $this->urlPathGenerator = $urlPathGenerator; + } + /** + * Generate urls for UrlRewrites and save it in storage + * + * @param Observer $observer + * @return array + * @throws UrlAlreadyExistsException + */ + public function execute(Observer $observer) + { + $event = $observer->getEvent(); + $attrData = $event->getAttributesData(); + $productIds = $event->getProductIds(); + $storeId = $event->getStoreId(); + $visibility = $attrData[ProductInterface::VISIBILITY] ?? null; + + if (!$visibility) { + return [$attrData, $productIds, $storeId]; + } + + $productCollection = $this->productCollectionFactory->create(); + $productCollection->addAttributeToSelect(ProductInterface::VISIBILITY); + $productCollection->addAttributeToSelect('url_key'); + $productCollection->addFieldToFilter( + 'entity_id', + ['in' => array_unique($productIds)] + ); + + foreach ($productCollection as $product) { + if ($visibility == Visibility::VISIBILITY_NOT_VISIBLE) { + $this->urlPersist->deleteByData( + [ + UrlRewrite::ENTITY_ID => $product->getId(), + UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, + ] + ); + } elseif ($visibility !== Visibility::VISIBILITY_NOT_VISIBLE) { + $product->setVisibility($visibility); + $productUrlPath = $this->urlPathGenerator->getUrlPath($product); + $productUrlRewrite = $this->urlRewriteGenerator->generate($product); + $product->unsUrlPath(); + $product->setUrlPath($productUrlPath); + $this->urlPersist->replace($productUrlRewrite); + } + } + + return [$attrData, $productIds, $storeId]; + } +} diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php index 6eda8dd0b61e..c5a1c1e429bf 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php @@ -3,14 +3,18 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\CatalogUrlRewrite\Observer; use Magento\Catalog\Model\Product; use Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; use Magento\Framework\App\ObjectManager; +use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException; use Magento\UrlRewrite\Model\UrlPersistInterface; use Magento\Framework\Event\ObserverInterface; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; /** * Class ProductProcessUrlRewriteSavingObserver @@ -32,20 +36,29 @@ class ProductProcessUrlRewriteSavingObserver implements ObserverInterface */ private $productUrlPathGenerator; + /** + * @var CollectionFactory + */ + private $collectionFactory; + /** * @param ProductUrlRewriteGenerator $productUrlRewriteGenerator * @param UrlPersistInterface $urlPersist - * @param ProductUrlPathGenerator|null $productUrlPathGenerator + * @param ProductUrlPathGenerator|null $urlPathGenerator + * @param CollectionFactory|null $collectionFactory */ public function __construct( ProductUrlRewriteGenerator $productUrlRewriteGenerator, UrlPersistInterface $urlPersist, - ProductUrlPathGenerator $productUrlPathGenerator = null + ProductUrlPathGenerator $urlPathGenerator = null, + CollectionFactory $collectionFactory = null ) { $this->productUrlRewriteGenerator = $productUrlRewriteGenerator; $this->urlPersist = $urlPersist; - $this->productUrlPathGenerator = $productUrlPathGenerator ?: ObjectManager::getInstance() + $this->productUrlPathGenerator = $urlPathGenerator ?: ObjectManager::getInstance() ->get(ProductUrlPathGenerator::class); + $this->collectionFactory = $collectionFactory ?: ObjectManager::getInstance() + ->get(CollectionFactory::class); } /** @@ -69,7 +82,27 @@ public function execute(\Magento\Framework\Event\Observer $observer) $product->unsUrlPath(); $product->setUrlPath($this->productUrlPathGenerator->getUrlPath($product)); $this->urlPersist->replace($this->productUrlRewriteGenerator->generate($product)); + return; } } + $this->validateUrlKey($product); + } + + /** + * @param Product $product + * @throws UrlAlreadyExistsException + */ + private function validateUrlKey(Product $product) + { + $productCollection = $this->collectionFactory->create(); + $productCollection->addFieldToFilter( + 'url_key', + ['in' => $product->getUrlKey()] + ); + $productCollection->getSelect()->where('e.entity_id != ?', $product->getId()); + + if ($productCollection->getItems()) { + throw new UrlAlreadyExistsException(); + } } } diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php index 39317b42af98..3df850f51e69 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php @@ -6,9 +6,7 @@ namespace Magento\CatalogUrlRewrite\Test\Unit\Observer; -use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; /** * Class ProductProcessUrlRewriteSavingObserverTest @@ -48,6 +46,21 @@ class ProductProcessUrlRewriteSavingObserverTest extends \PHPUnit\Framework\Test */ protected $objectManager; + /** + * @var \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory + */ + private $collectionFactory; + + /** + * @var \Magento\Catalog\Model\ResourceModel\Product\Collection + */ + private $productCollection; + + /** + * @var \Magento\Framework\DB\Select + */ + private $select; + /** * @var \Magento\CatalogUrlRewrite\Observer\ProductProcessUrlRewriteSavingObserver */ @@ -60,13 +73,13 @@ protected function setUp() { $this->urlPersist = $this->createMock(\Magento\UrlRewrite\Model\UrlPersistInterface::class); $this->product = $this->createPartialMock(\Magento\Catalog\Model\Product::class, [ - 'getId', - 'dataHasChangedFor', - 'isVisibleInSiteVisibility', - 'getIsChangedWebsites', - 'getIsChangedCategories', - 'getStoreId' - ]); + 'getId', + 'dataHasChangedFor', + 'isVisibleInSiteVisibility', + 'getIsChangedWebsites', + 'getIsChangedCategories', + 'getStoreId' + ]); $this->product->expects($this->any())->method('getId')->will($this->returnValue(3)); $this->event = $this->createPartialMock(\Magento\Framework\Event::class, ['getProduct']); $this->event->expects($this->any())->method('getProduct')->willReturn($this->product); @@ -79,12 +92,28 @@ protected function setUp() $this->productUrlRewriteGenerator->expects($this->any()) ->method('generate') ->will($this->returnValue([3 => 'rewrite'])); + $this->collectionFactory = $this->createMock( + \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory::class + ); + $this->productCollection = $this->createMock( + \Magento\Catalog\Model\ResourceModel\Product\Collection::class + ); + $this->select = $this->createMock( + \Magento\Framework\DB\Select::class + ); + $this->collectionFactory->expects($this->any()) + ->method('create') + ->will($this->returnValue($this->productCollection)); + $this->productCollection->expects($this->any()) + ->method('getSelect') + ->will($this->returnValue($this->select)); $this->objectManager = new ObjectManager($this); $this->model = $this->objectManager->getObject( \Magento\CatalogUrlRewrite\Observer\ProductProcessUrlRewriteSavingObserver::class, [ 'productUrlRewriteGenerator' => $this->productUrlRewriteGenerator, - 'urlPersist' => $this->urlPersist + 'urlPersist' => $this->urlPersist, + 'collectionFactory' => $this->collectionFactory ] ); } @@ -103,8 +132,7 @@ public function urlKeyDataProvider() 'isChangedWebsites' => false, 'isChangedCategories' => false, 'visibilityResult' => true, - 'expectedReplaceCount' => 1, - + 'expectedReplaceCount' => 1 ], 'no chnages' => [ 'isChangedUrlKey' => false, diff --git a/app/code/Magento/CatalogUrlRewrite/etc/events.xml b/app/code/Magento/CatalogUrlRewrite/etc/events.xml index cc558fe81f16..728442acf7a4 100644 --- a/app/code/Magento/CatalogUrlRewrite/etc/events.xml +++ b/app/code/Magento/CatalogUrlRewrite/etc/events.xml @@ -27,6 +27,9 @@ + + + diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_configurable_not_available.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_configurable_not_available.php index 73467cd8d063..87290e0b8259 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_configurable_not_available.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_configurable_not_available.php @@ -43,7 +43,7 @@ $productSku = array_shift($productsSku); $product->setTypeId(Type::TYPE_SIMPLE) ->setAttributeSetId($attributeSetId) - ->setName('Configurable Option' . $option->getLabel()) + ->setName('Configurable Option Not Available' . $option->getLabel()) ->setSku('simple_not_avalilable_' . $productSku) ->setPrice(11) ->setTestConfigurable($option->getValue()) diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php index c72a58197b1f..f902ee993b84 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php @@ -5,9 +5,11 @@ */ namespace Magento\CatalogUrlRewrite\Observer; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Visibility; use Magento\Store\Model\StoreManagerInterface; +use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; -use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator; /** * @magentoAppArea adminhtml @@ -18,12 +20,16 @@ class ProductProcessUrlRewriteSavingObserverTest extends \PHPUnit\Framework\Test /** @var \Magento\Framework\ObjectManagerInterface */ protected $objectManager; + /** @var ProductRepositoryInterface */ + private $productRepository; + /** * Set up */ protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); } /** @@ -136,10 +142,8 @@ public function testUrlKeyHasChangedInGlobalContext() */ public function testUrlKeyHasChangedInStoreviewContextWithPermanentRedirection() { - /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository*/ - $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); /** @var \Magento\Catalog\Model\Product $product*/ - $product = $productRepository->get('product1'); + $product = $this->productRepository->get('product1'); /** @var StoreManagerInterface $storeManager */ $storeManager = $this->objectManager->get(StoreManagerInterface::class); @@ -234,4 +238,22 @@ public function testUrlKeyHasChangedInStoreviewContextWithoutPermanentRedirectio $this->assertContains($row, $actual); } } + + /** + * @magentoDataFixture Magento/CatalogUrlRewrite/_files/product_simple.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoAppIsolation enabled + */ + public function testDuplicatedUrlKeyInvisibleProduct() + { + $this->expectException( + UrlAlreadyExistsException::class, + 'URL key for specified store already exists' + ); + /** @var \Magento\Catalog\Model\Product $product*/ + $product = $this->productRepository->get('simple2'); + $product->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE); + $product->setUrlKey('simple_product'); + $product->save(); + } } diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_products.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_products.php index 985c77dba662..afebcab773d9 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_products.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_products.php @@ -107,7 +107,7 @@ ->setId($productId) ->setAttributeSetId($attributeSetId) ->setWebsiteIds([1]) - ->setName('Configurable Option' . $option->getLabel()) + ->setName('Configurable Option 12345' . $option->getLabel()) ->setSku('simple_' . $productId) ->setPrice($productId) ->setTestConfigurable($option->getValue()) diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php index 86266ec23fe4..dfa30e1964e3 100644 --- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php +++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php @@ -384,10 +384,6 @@ protected function _connect() throw new \Zend_Db_Adapter_Exception('No host configured to connect'); } - if (isset($this->_config['port'])) { - throw new \Zend_Db_Adapter_Exception('Port must be configured within host parameter (like localhost:3306'); - } - unset($this->_config['port']); if (strpos($this->_config['host'], '/') !== false) { From 4d18c9a92cd8f1acdd780ce933f2860e7d092f7f Mon Sep 17 00:00:00 2001 From: vitaliyboyko Date: Tue, 29 Jan 2019 19:36:12 +0200 Subject: [PATCH 04/50] ENGCOM-20434: Added accidentally removed code --- lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php index dfa30e1964e3..86266ec23fe4 100644 --- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php +++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php @@ -384,6 +384,10 @@ protected function _connect() throw new \Zend_Db_Adapter_Exception('No host configured to connect'); } + if (isset($this->_config['port'])) { + throw new \Zend_Db_Adapter_Exception('Port must be configured within host parameter (like localhost:3306'); + } + unset($this->_config['port']); if (strpos($this->_config['host'], '/') !== false) { From 6a5776c46396810875f46e986e56594e8e5ea30c Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko Date: Sat, 2 Feb 2019 20:02:54 +0200 Subject: [PATCH 05/50] ENGCOM-20434: Removed url key validation for invisible products --- ...ProductProcessUrlRewriteSavingObserver.php | 39 ++----------------- 1 file changed, 3 insertions(+), 36 deletions(-) diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php index c5a1c1e429bf..6eda8dd0b61e 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php @@ -3,18 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -declare(strict_types=1); - namespace Magento\CatalogUrlRewrite\Observer; use Magento\Catalog\Model\Product; use Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; use Magento\Framework\App\ObjectManager; -use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException; use Magento\UrlRewrite\Model\UrlPersistInterface; use Magento\Framework\Event\ObserverInterface; -use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; /** * Class ProductProcessUrlRewriteSavingObserver @@ -36,29 +32,20 @@ class ProductProcessUrlRewriteSavingObserver implements ObserverInterface */ private $productUrlPathGenerator; - /** - * @var CollectionFactory - */ - private $collectionFactory; - /** * @param ProductUrlRewriteGenerator $productUrlRewriteGenerator * @param UrlPersistInterface $urlPersist - * @param ProductUrlPathGenerator|null $urlPathGenerator - * @param CollectionFactory|null $collectionFactory + * @param ProductUrlPathGenerator|null $productUrlPathGenerator */ public function __construct( ProductUrlRewriteGenerator $productUrlRewriteGenerator, UrlPersistInterface $urlPersist, - ProductUrlPathGenerator $urlPathGenerator = null, - CollectionFactory $collectionFactory = null + ProductUrlPathGenerator $productUrlPathGenerator = null ) { $this->productUrlRewriteGenerator = $productUrlRewriteGenerator; $this->urlPersist = $urlPersist; - $this->productUrlPathGenerator = $urlPathGenerator ?: ObjectManager::getInstance() + $this->productUrlPathGenerator = $productUrlPathGenerator ?: ObjectManager::getInstance() ->get(ProductUrlPathGenerator::class); - $this->collectionFactory = $collectionFactory ?: ObjectManager::getInstance() - ->get(CollectionFactory::class); } /** @@ -82,27 +69,7 @@ public function execute(\Magento\Framework\Event\Observer $observer) $product->unsUrlPath(); $product->setUrlPath($this->productUrlPathGenerator->getUrlPath($product)); $this->urlPersist->replace($this->productUrlRewriteGenerator->generate($product)); - return; } } - $this->validateUrlKey($product); - } - - /** - * @param Product $product - * @throws UrlAlreadyExistsException - */ - private function validateUrlKey(Product $product) - { - $productCollection = $this->collectionFactory->create(); - $productCollection->addFieldToFilter( - 'url_key', - ['in' => $product->getUrlKey()] - ); - $productCollection->getSelect()->where('e.entity_id != ?', $product->getId()); - - if ($productCollection->getItems()) { - throw new UrlAlreadyExistsException(); - } } } From f2088d9b59d0fcf6d521d510fcc37d11e3308f03 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko Date: Sat, 2 Feb 2019 20:04:24 +0200 Subject: [PATCH 06/50] ENGCOM-20434: Removed url key validation for invisible products (revert tests) --- .../product_configurable_not_available.php | 2 +- ...uctProcessUrlRewriteSavingObserverTest.php | 30 +++---------------- .../_files/configurable_products.php | 2 +- 3 files changed, 6 insertions(+), 28 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_configurable_not_available.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_configurable_not_available.php index 87290e0b8259..73467cd8d063 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_configurable_not_available.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_configurable_not_available.php @@ -43,7 +43,7 @@ $productSku = array_shift($productsSku); $product->setTypeId(Type::TYPE_SIMPLE) ->setAttributeSetId($attributeSetId) - ->setName('Configurable Option Not Available' . $option->getLabel()) + ->setName('Configurable Option' . $option->getLabel()) ->setSku('simple_not_avalilable_' . $productSku) ->setPrice(11) ->setTestConfigurable($option->getValue()) diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php index f902ee993b84..c72a58197b1f 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php @@ -5,11 +5,9 @@ */ namespace Magento\CatalogUrlRewrite\Observer; -use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Catalog\Model\Product\Visibility; use Magento\Store\Model\StoreManagerInterface; -use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; +use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator; /** * @magentoAppArea adminhtml @@ -20,16 +18,12 @@ class ProductProcessUrlRewriteSavingObserverTest extends \PHPUnit\Framework\Test /** @var \Magento\Framework\ObjectManagerInterface */ protected $objectManager; - /** @var ProductRepositoryInterface */ - private $productRepository; - /** * Set up */ protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); } /** @@ -142,8 +136,10 @@ public function testUrlKeyHasChangedInGlobalContext() */ public function testUrlKeyHasChangedInStoreviewContextWithPermanentRedirection() { + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository*/ + $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); /** @var \Magento\Catalog\Model\Product $product*/ - $product = $this->productRepository->get('product1'); + $product = $productRepository->get('product1'); /** @var StoreManagerInterface $storeManager */ $storeManager = $this->objectManager->get(StoreManagerInterface::class); @@ -238,22 +234,4 @@ public function testUrlKeyHasChangedInStoreviewContextWithoutPermanentRedirectio $this->assertContains($row, $actual); } } - - /** - * @magentoDataFixture Magento/CatalogUrlRewrite/_files/product_simple.php - * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php - * @magentoAppIsolation enabled - */ - public function testDuplicatedUrlKeyInvisibleProduct() - { - $this->expectException( - UrlAlreadyExistsException::class, - 'URL key for specified store already exists' - ); - /** @var \Magento\Catalog\Model\Product $product*/ - $product = $this->productRepository->get('simple2'); - $product->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE); - $product->setUrlKey('simple_product'); - $product->save(); - } } diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_products.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_products.php index afebcab773d9..985c77dba662 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_products.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_products.php @@ -107,7 +107,7 @@ ->setId($productId) ->setAttributeSetId($attributeSetId) ->setWebsiteIds([1]) - ->setName('Configurable Option 12345' . $option->getLabel()) + ->setName('Configurable Option' . $option->getLabel()) ->setSku('simple_' . $productId) ->setPrice($productId) ->setTestConfigurable($option->getValue()) From 4edec382f3517f64284ba45fb341e4bcce1745ab Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko Date: Sat, 2 Feb 2019 20:06:54 +0200 Subject: [PATCH 07/50] ENGCOM-20434: Removed url key validation for invisible products (revert unit test) --- ...uctProcessUrlRewriteSavingObserverTest.php | 52 +++++-------------- 1 file changed, 12 insertions(+), 40 deletions(-) diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php index 3df850f51e69..39317b42af98 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php @@ -6,7 +6,9 @@ namespace Magento\CatalogUrlRewrite\Test\Unit\Observer; +use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; /** * Class ProductProcessUrlRewriteSavingObserverTest @@ -46,21 +48,6 @@ class ProductProcessUrlRewriteSavingObserverTest extends \PHPUnit\Framework\Test */ protected $objectManager; - /** - * @var \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory - */ - private $collectionFactory; - - /** - * @var \Magento\Catalog\Model\ResourceModel\Product\Collection - */ - private $productCollection; - - /** - * @var \Magento\Framework\DB\Select - */ - private $select; - /** * @var \Magento\CatalogUrlRewrite\Observer\ProductProcessUrlRewriteSavingObserver */ @@ -73,13 +60,13 @@ protected function setUp() { $this->urlPersist = $this->createMock(\Magento\UrlRewrite\Model\UrlPersistInterface::class); $this->product = $this->createPartialMock(\Magento\Catalog\Model\Product::class, [ - 'getId', - 'dataHasChangedFor', - 'isVisibleInSiteVisibility', - 'getIsChangedWebsites', - 'getIsChangedCategories', - 'getStoreId' - ]); + 'getId', + 'dataHasChangedFor', + 'isVisibleInSiteVisibility', + 'getIsChangedWebsites', + 'getIsChangedCategories', + 'getStoreId' + ]); $this->product->expects($this->any())->method('getId')->will($this->returnValue(3)); $this->event = $this->createPartialMock(\Magento\Framework\Event::class, ['getProduct']); $this->event->expects($this->any())->method('getProduct')->willReturn($this->product); @@ -92,28 +79,12 @@ protected function setUp() $this->productUrlRewriteGenerator->expects($this->any()) ->method('generate') ->will($this->returnValue([3 => 'rewrite'])); - $this->collectionFactory = $this->createMock( - \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory::class - ); - $this->productCollection = $this->createMock( - \Magento\Catalog\Model\ResourceModel\Product\Collection::class - ); - $this->select = $this->createMock( - \Magento\Framework\DB\Select::class - ); - $this->collectionFactory->expects($this->any()) - ->method('create') - ->will($this->returnValue($this->productCollection)); - $this->productCollection->expects($this->any()) - ->method('getSelect') - ->will($this->returnValue($this->select)); $this->objectManager = new ObjectManager($this); $this->model = $this->objectManager->getObject( \Magento\CatalogUrlRewrite\Observer\ProductProcessUrlRewriteSavingObserver::class, [ 'productUrlRewriteGenerator' => $this->productUrlRewriteGenerator, - 'urlPersist' => $this->urlPersist, - 'collectionFactory' => $this->collectionFactory + 'urlPersist' => $this->urlPersist ] ); } @@ -132,7 +103,8 @@ public function urlKeyDataProvider() 'isChangedWebsites' => false, 'isChangedCategories' => false, 'visibilityResult' => true, - 'expectedReplaceCount' => 1 + 'expectedReplaceCount' => 1, + ], 'no chnages' => [ 'isChangedUrlKey' => false, From a8d1dc042d663adf6b1667192ba3f044c57d51f1 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko Date: Sat, 2 Feb 2019 20:09:02 +0200 Subject: [PATCH 08/50] ENGCOM-20434: static fixes --- .../ProcessUrlRewriteOnChangeProductVisibilityObserver.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeProductVisibilityObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeProductVisibilityObserver.php index 16d12bd4c7e4..6006f7503540 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeProductVisibilityObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeProductVisibilityObserver.php @@ -27,18 +27,22 @@ class ProcessUrlRewriteOnChangeProductVisibilityObserver implements ObserverInte * @var CollectionFactory */ private $productCollectionFactory; + /** * @var ProductUrlRewriteGenerator */ private $urlRewriteGenerator; + /** * @var UrlPersistInterface */ private $urlPersist; + /** * @var ProductUrlPathGenerator */ private $urlPathGenerator; + /** * @param CollectionFactory $collectionFactory * @param ProductUrlRewriteGenerator $urlRewriteGenerator @@ -56,6 +60,7 @@ public function __construct( $this->urlPersist = $urlPersist; $this->urlPathGenerator = $urlPathGenerator; } + /** * Generate urls for UrlRewrites and save it in storage * From d79eb085cabfb16ed8f4432f5cd25b00cf3ba686 Mon Sep 17 00:00:00 2001 From: "Rav [RedChamps]" Date: Sat, 16 Feb 2019 10:38:57 +0530 Subject: [PATCH 09/50] Added refunded discount amount to the total revenue calculation and fixed Negative order amount in dashboard when viewing particular store data --- .../Reports/Model/ResourceModel/Order/Collection.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php index fd9adbe73410..5c92fc4c3cdb 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php @@ -773,6 +773,7 @@ public function addRevenueToSelect($convertCurrency = false) !$convertCurrency, $this->getConnection()->getIfNullSql('main_table.base_subtotal_refunded', 0), $this->getConnection()->getIfNullSql('main_table.base_subtotal_canceled', 0), + $this->getConnection()->getIfNullSql('main_table.base_discount_refunded', 0), $this->getConnection()->getIfNullSql('main_table.base_discount_canceled', 0) ); $this->getSelect()->columns(['revenue' => $expr]); @@ -795,6 +796,7 @@ public function addSumAvgTotals($storeId = 0) $storeId, $this->getConnection()->getIfNullSql('main_table.base_subtotal_refunded', 0), $this->getConnection()->getIfNullSql('main_table.base_subtotal_canceled', 0), + $this->getConnection()->getIfNullSql('main_table.base_discount_refunded', 0), $this->getConnection()->getIfNullSql('main_table.base_discount_canceled', 0) ); @@ -813,6 +815,7 @@ public function addSumAvgTotals($storeId = 0) * @param int $storeId * @param string $baseSubtotalRefunded * @param string $baseSubtotalCanceled + * @param string $baseDiscountRefunded * @param string $baseDiscountCanceled * @return string */ @@ -820,13 +823,14 @@ protected function getTotalsExpression( $storeId, $baseSubtotalRefunded, $baseSubtotalCanceled, + $baseDiscountRefunded, $baseDiscountCanceled ) { $template = ($storeId != 0) - ? '(main_table.base_subtotal - %2$s - %1$s - ABS(main_table.base_discount_amount) - %3$s)' - : '((main_table.base_subtotal - %1$s - %2$s - ABS(main_table.base_discount_amount) + %3$s) ' + ? '(main_table.base_subtotal - %2$s - %1$s - ABS(main_table.base_discount_amount) + %3$s + %4$s)' + : '((main_table.base_subtotal - %1$s - %2$s - ABS(main_table.base_discount_amount) + %3$s + %4$s) ' . ' * main_table.base_to_global_rate)'; - return sprintf($template, $baseSubtotalRefunded, $baseSubtotalCanceled, $baseDiscountCanceled); + return sprintf($template, $baseSubtotalRefunded, $baseSubtotalCanceled, $baseDiscountRefunded, $baseDiscountCanceled); } /** From d1e71b96056f2b83d182c92120008837cd665b9f Mon Sep 17 00:00:00 2001 From: "Rav [RedChamps]" Date: Sat, 16 Feb 2019 19:55:46 +0530 Subject: [PATCH 10/50] Fixed bug that total amount is showing negative when order with discount is partially refunded --- .../Reports/Model/ResourceModel/Order/Collection.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php index 5c92fc4c3cdb..19549f7181aa 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php @@ -773,8 +773,8 @@ public function addRevenueToSelect($convertCurrency = false) !$convertCurrency, $this->getConnection()->getIfNullSql('main_table.base_subtotal_refunded', 0), $this->getConnection()->getIfNullSql('main_table.base_subtotal_canceled', 0), - $this->getConnection()->getIfNullSql('main_table.base_discount_refunded', 0), - $this->getConnection()->getIfNullSql('main_table.base_discount_canceled', 0) + $this->getConnection()->getIfNullSql('ABS(main_table.base_discount_refunded)', 0), + $this->getConnection()->getIfNullSql('ABS(main_table.base_discount_canceled)', 0) ); $this->getSelect()->columns(['revenue' => $expr]); @@ -796,8 +796,8 @@ public function addSumAvgTotals($storeId = 0) $storeId, $this->getConnection()->getIfNullSql('main_table.base_subtotal_refunded', 0), $this->getConnection()->getIfNullSql('main_table.base_subtotal_canceled', 0), - $this->getConnection()->getIfNullSql('main_table.base_discount_refunded', 0), - $this->getConnection()->getIfNullSql('main_table.base_discount_canceled', 0) + $this->getConnection()->getIfNullSql('ABS(main_table.base_discount_refunded)', 0), + $this->getConnection()->getIfNullSql('ABS(main_table.base_discount_canceled)', 0) ); $this->getSelect()->columns( @@ -827,8 +827,8 @@ protected function getTotalsExpression( $baseDiscountCanceled ) { $template = ($storeId != 0) - ? '(main_table.base_subtotal - %2$s - %1$s - ABS(main_table.base_discount_amount) + %3$s + %4$s)' - : '((main_table.base_subtotal - %1$s - %2$s - ABS(main_table.base_discount_amount) + %3$s + %4$s) ' + ? '(main_table.base_subtotal - %2$s - %1$s - (ABS(main_table.base_discount_amount) - %3$s - %4$s))' + : '((main_table.base_subtotal - %1$s - %2$s - (ABS(main_table.base_discount_amount) - %3$s - %4$s)) ' . ' * main_table.base_to_global_rate)'; return sprintf($template, $baseSubtotalRefunded, $baseSubtotalCanceled, $baseDiscountRefunded, $baseDiscountCanceled); } From c865bdc25ff73168e2c2e0e093787ed766a2d068 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko Date: Wed, 20 Feb 2019 19:45:43 +0200 Subject: [PATCH 11/50] ENGCOM-20434: refactoring, test coverage --- .../AdaptUrlRewritesToVisibilityAttribute.php | 122 +++++++++++ ...writeOnChangeProductVisibilityObserver.php | 85 ++------ ...lRewriteOnChangeVisibilityObserverTest.php | 194 ++++++++++++++++++ .../_files/product_invisible_multistore.php | 40 ++++ .../product_invisible_multistore_rollback.php | 33 +++ .../_files/products_invisible.php | 38 ++++ .../_files/products_invisible_rollback.php | 37 ++++ 7 files changed, 478 insertions(+), 71 deletions(-) create mode 100644 app/code/Magento/CatalogUrlRewrite/Model/Products/AdaptUrlRewritesToVisibilityAttribute.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeVisibilityObserverTest.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_invisible_multistore.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_invisible_multistore_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/products_invisible.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/products_invisible_rollback.php diff --git a/app/code/Magento/CatalogUrlRewrite/Model/Products/AdaptUrlRewritesToVisibilityAttribute.php b/app/code/Magento/CatalogUrlRewrite/Model/Products/AdaptUrlRewritesToVisibilityAttribute.php new file mode 100644 index 000000000000..0cadbd0acf0c --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Model/Products/AdaptUrlRewritesToVisibilityAttribute.php @@ -0,0 +1,122 @@ +productCollectionFactory = $collectionFactory; + $this->urlRewriteGenerator = $urlRewriteGenerator; + $this->urlPersist = $urlPersist; + $this->urlPathGenerator = $urlPathGenerator; + } + + /** + * @param array $productIds + * @param int $visibility + * @throws UrlAlreadyExistsException + */ + public function execute(array $productIds, int $visibility): void + { + $products = $this->getProductsByIds($productIds); + + /** @var Product $product */ + foreach ($products as $product) { + if ($visibility == Visibility::VISIBILITY_NOT_VISIBLE) { + $this->urlPersist->deleteByData( + [ + UrlRewrite::ENTITY_ID => $product->getId(), + UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, + ] + ); + } elseif ($visibility !== Visibility::VISIBILITY_NOT_VISIBLE) { + $product->setVisibility($visibility); + $productUrlPath = $this->urlPathGenerator->getUrlPath($product); + $productUrlRewrite = $this->urlRewriteGenerator->generate($product); + $product->unsUrlPath(); + $product->setUrlPath($productUrlPath); + + try { + $this->urlPersist->replace($productUrlRewrite); + } catch (UrlAlreadyExistsException $e) { + throw new UrlAlreadyExistsException( + __( + 'Can not change the visibility of the product with SKU equals "%1". URL key "%2" for specified store already exists.', + $product->getSku(), + $product->getUrlKey() + ), + $e, + $e->getCode(), + $e->getUrls() + ); + } + } + } + } + + /** + * @param array $productIds + * @return array + */ + protected function getProductsByIds(array $productIds): array + { + $productCollection = $this->productCollectionFactory->create(); + $productCollection->addAttributeToSelect(ProductInterface::VISIBILITY); + $productCollection->addAttributeToSelect('url_key'); + $productCollection->addFieldToFilter( + 'entity_id', + ['in' => array_unique($productIds)] + ); + + return $productCollection->getItems(); + } +} diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeProductVisibilityObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeProductVisibilityObserver.php index 6006f7503540..56393c619f4e 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeProductVisibilityObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeProductVisibilityObserver.php @@ -8,64 +8,34 @@ namespace Magento\CatalogUrlRewrite\Observer; use Magento\Catalog\Api\Data\ProductInterface; -use Magento\Catalog\Model\Product\Visibility; -use Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator; -use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; -use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException; -use Magento\UrlRewrite\Model\UrlPersistInterface; -use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; -use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; +use Magento\CatalogUrlRewrite\Model\Products\AdaptUrlRewritesToVisibilityAttribute; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; +use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException; /** - * Class ProductProcessUrlRewriteSavingObserver + * Consider URL rewrites on change product visibility via mass action */ class ProcessUrlRewriteOnChangeProductVisibilityObserver implements ObserverInterface { /** - * @var CollectionFactory - */ - private $productCollectionFactory; - - /** - * @var ProductUrlRewriteGenerator - */ - private $urlRewriteGenerator; - - /** - * @var UrlPersistInterface + * @var AdaptUrlRewritesToVisibilityAttribute */ - private $urlPersist; + private $adaptUrlRewritesToVisibility; /** - * @var ProductUrlPathGenerator + * @param AdaptUrlRewritesToVisibilityAttribute $adaptUrlRewritesToVisibility */ - private $urlPathGenerator; - - /** - * @param CollectionFactory $collectionFactory - * @param ProductUrlRewriteGenerator $urlRewriteGenerator - * @param UrlPersistInterface $urlPersist - * @param ProductUrlPathGenerator|null $urlPathGenerator - */ - public function __construct( - CollectionFactory $collectionFactory, - ProductUrlRewriteGenerator $urlRewriteGenerator, - UrlPersistInterface $urlPersist, - ProductUrlPathGenerator $urlPathGenerator - ) { - $this->productCollectionFactory = $collectionFactory; - $this->urlRewriteGenerator = $urlRewriteGenerator; - $this->urlPersist = $urlPersist; - $this->urlPathGenerator = $urlPathGenerator; + public function __construct(AdaptUrlRewritesToVisibilityAttribute $adaptUrlRewritesToVisibility) + { + $this->adaptUrlRewritesToVisibility = $adaptUrlRewritesToVisibility; } /** * Generate urls for UrlRewrites and save it in storage * * @param Observer $observer - * @return array + * @return void * @throws UrlAlreadyExistsException */ public function execute(Observer $observer) @@ -73,39 +43,12 @@ public function execute(Observer $observer) $event = $observer->getEvent(); $attrData = $event->getAttributesData(); $productIds = $event->getProductIds(); - $storeId = $event->getStoreId(); - $visibility = $attrData[ProductInterface::VISIBILITY] ?? null; - - if (!$visibility) { - return [$attrData, $productIds, $storeId]; - } - - $productCollection = $this->productCollectionFactory->create(); - $productCollection->addAttributeToSelect(ProductInterface::VISIBILITY); - $productCollection->addAttributeToSelect('url_key'); - $productCollection->addFieldToFilter( - 'entity_id', - ['in' => array_unique($productIds)] - ); + $visibility = $attrData[ProductInterface::VISIBILITY] ?? 0; - foreach ($productCollection as $product) { - if ($visibility == Visibility::VISIBILITY_NOT_VISIBLE) { - $this->urlPersist->deleteByData( - [ - UrlRewrite::ENTITY_ID => $product->getId(), - UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, - ] - ); - } elseif ($visibility !== Visibility::VISIBILITY_NOT_VISIBLE) { - $product->setVisibility($visibility); - $productUrlPath = $this->urlPathGenerator->getUrlPath($product); - $productUrlRewrite = $this->urlRewriteGenerator->generate($product); - $product->unsUrlPath(); - $product->setUrlPath($productUrlPath); - $this->urlPersist->replace($productUrlRewrite); - } + if (!$visibility || !$productIds) { + return; } - return [$attrData, $productIds, $storeId]; + $this->adaptUrlRewritesToVisibility->execute($productIds, $visibility); } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeVisibilityObserverTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeVisibilityObserverTest.php new file mode 100644 index 000000000000..af9979309078 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeVisibilityObserverTest.php @@ -0,0 +1,194 @@ +objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->eventManager = $this->objectManager->create(ManagerInterface::class); + } + + /** + * @magentoDataFixture Magento/CatalogUrlRewrite/_files/product_rewrite_multistore.php + * @magentoAppIsolation enabled + */ + public function testMakeProductInvisibleViaMassAction() + { + /** @var \Magento\Catalog\Model\Product $product*/ + $product = $this->productRepository->get('product1'); + + /** @var StoreManagerInterface $storeManager */ + $storeManager = $this->objectManager->get(StoreManagerInterface::class); + $storeManager->setCurrentStore(0); + + $testStore = $storeManager->getStore('test'); + $productFilter = [ + UrlRewrite::ENTITY_TYPE => 'product', + ]; + + $expected = [ + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => '1', + ], + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore->getId(), + ] + ]; + + $actual = $this->getActualResults($productFilter); + foreach ($expected as $row) { + $this->assertContains($row, $actual); + } + + $this->eventManager->dispatch( + 'catalog_product_attribute_update_before', + [ + 'attributes_data' => [ ProductInterface::VISIBILITY => Visibility::VISIBILITY_NOT_VISIBLE ], + 'product_ids' => [$product->getId()] + ] + ); + + $actual = $this->getActualResults($productFilter); + $this->assertCount(0, $actual); + } + + /** + * @magentoDataFixture Magento/CatalogUrlRewrite/_files/product_invisible_multistore.php + * @magentoAppIsolation enabled + */ + public function testMakeProductVisibleViaMassAction() + { + /** @var \Magento\Catalog\Model\Product $product*/ + $product = $this->productRepository->get('product1'); + + /** @var StoreManagerInterface $storeManager */ + $storeManager = $this->objectManager->get(StoreManagerInterface::class); + $storeManager->setCurrentStore(0); + + $testStore = $storeManager->getStore('test'); + $productFilter = [ + UrlRewrite::ENTITY_TYPE => 'product', + ]; + + $actual = $this->getActualResults($productFilter); + $this->assertCount(0, $actual); + + $this->eventManager->dispatch( + 'catalog_product_attribute_update_before', + [ + 'attributes_data' => [ ProductInterface::VISIBILITY => Visibility::VISIBILITY_BOTH ], + 'product_ids' => [$product->getId()] + ] + ); + + $expected = [ + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => '1', + ], + [ + 'request_path' => "product-1.html", + 'target_path' => "catalog/product/view/id/" . $product->getId(), + 'is_auto_generated' => 1, + 'redirect_type' => 0, + 'store_id' => $testStore->getId(), + ] + ]; + + $actual = $this->getActualResults($productFilter); + foreach ($expected as $row) { + $this->assertContains($row, $actual); + } + } + + /** + * @magentoDataFixture Magento/CatalogUrlRewrite/_files/products_invisible.php + * @magentoAppIsolation enabled + */ + public function testErrorOnDuplicatedUrlKey() + { + $skus = ['product1', 'product2']; + foreach ($skus as $sku) { + /** @var \Magento\Catalog\Model\Product $product */ + $productIds[] = $this->productRepository->get($sku)->getId(); + } + $this->expectException(UrlAlreadyExistsException::class); + $this->expectExceptionMessage('Can not change the visibility of the product with SKU equals "product2". URL key "product-1" for specified store already exists.'); + + $this->eventManager->dispatch( + 'catalog_product_attribute_update_before', + [ + 'attributes_data' => [ ProductInterface::VISIBILITY => Visibility::VISIBILITY_BOTH ], + 'product_ids' => $productIds + ] + ); + } + + /** + * @param array $filter + * @return array + */ + private function getActualResults(array $filter) + { + /** @var \Magento\UrlRewrite\Model\UrlFinderInterface $urlFinder */ + $urlFinder = $this->objectManager->get(\Magento\UrlRewrite\Model\UrlFinderInterface::class); + $actualResults = []; + foreach ($urlFinder->findAllByData($filter) as $url) { + $actualResults[] = [ + 'request_path' => $url->getRequestPath(), + 'target_path' => $url->getTargetPath(), + 'is_auto_generated' => (int)$url->getIsAutogenerated(), + 'redirect_type' => $url->getRedirectType(), + 'store_id' => $url->getStoreId() + ]; + } + return $actualResults; + } +} diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_invisible_multistore.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_invisible_multistore.php new file mode 100644 index 000000000000..d50b29383b1e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_invisible_multistore.php @@ -0,0 +1,40 @@ +loadArea(\Magento\Backend\App\Area\FrontNameResolver::AREA_CODE); + +require __DIR__ . '/../../Store/_files/store.php'; + +/** @var $installer CategorySetup */ +$objectManager = Bootstrap::getObjectManager(); +$installer = $objectManager->create(CategorySetup::class); +$storeManager = $objectManager->get(StoreManagerInterface::class); +$storeManager->setCurrentStore(0); + +/** @var $product \Magento\Catalog\Model\Product */ +$product = $objectManager->create(\Magento\Catalog\Model\Product::class); +$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setAttributeSetId($installer->getAttributeSetId('catalog_product', 'Default')) + ->setStoreId(0) + ->setWebsiteIds([1]) + ->setName('Product1') + ->setSku('product1') + ->setPrice(10) + ->setWeight(18) + ->setStockData(['use_config_manage_stock' => 0]) + ->setUrlKey('product-1') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_NOT_VISIBLE) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED); + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_invisible_multistore_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_invisible_multistore_rollback.php new file mode 100644 index 000000000000..bcf399cb5e55 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_invisible_multistore_rollback.php @@ -0,0 +1,33 @@ +getInstance()->reinitialize(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); +try { + $product = $productRepository->get('product1', true); + if ($product->getId()) { + $productRepository->delete($product); + } +} catch (NoSuchEntityException $e) { +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/../../Store/_files/store_rollback.php'; +require __DIR__ . '/../../Store/_files/second_store_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/products_invisible.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/products_invisible.php new file mode 100644 index 000000000000..c72d9c8284db --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/products_invisible.php @@ -0,0 +1,38 @@ +loadArea(\Magento\Backend\App\Area\FrontNameResolver::AREA_CODE); + +/** @var $installer CategorySetup */ +$objectManager = Bootstrap::getObjectManager(); +$installer = $objectManager->create(CategorySetup::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +$skus = ['product1', 'product2']; +foreach ($skus as $sku) { + /** @var $product \Magento\Catalog\Model\Product */ + $product = $objectManager->create(\Magento\Catalog\Model\Product::class); + $product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setAttributeSetId($installer->getAttributeSetId('catalog_product', 'Default')) + ->setStoreId(0) + ->setWebsiteIds([1]) + ->setName('Product1') + ->setSku($sku) + ->setPrice(10) + ->setWeight(18) + ->setStockData(['use_config_manage_stock' => 0]) + ->setUrlKey('product-1') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_NOT_VISIBLE) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED); + $productRepository->save($product); +} diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/products_invisible_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/products_invisible_rollback.php new file mode 100644 index 000000000000..d3d17542aa6a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/products_invisible_rollback.php @@ -0,0 +1,37 @@ +getInstance()->reinitialize(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); + +$skus = ['product1', 'product2']; +foreach ($skus as $sku) { + try { + $product = $productRepository->get($sku, true); + if ($product->getId()) { + $productRepository->delete($product); + } + } catch (NoSuchEntityException $e) { + } +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/../../Store/_files/store_rollback.php'; +require __DIR__ . '/../../Store/_files/second_store_rollback.php'; From 1c608d7b54bf08d4fe592c2a0153502c9a9527d4 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko Date: Thu, 21 Feb 2019 22:08:19 +0200 Subject: [PATCH 12/50] ENGCOM-20434: refactoring, doc blocks adjustments --- .../Products/AdaptUrlRewritesToVisibilityAttribute.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogUrlRewrite/Model/Products/AdaptUrlRewritesToVisibilityAttribute.php b/app/code/Magento/CatalogUrlRewrite/Model/Products/AdaptUrlRewritesToVisibilityAttribute.php index 0cadbd0acf0c..cf1a7ec76f01 100644 --- a/app/code/Magento/CatalogUrlRewrite/Model/Products/AdaptUrlRewritesToVisibilityAttribute.php +++ b/app/code/Magento/CatalogUrlRewrite/Model/Products/AdaptUrlRewritesToVisibilityAttribute.php @@ -61,6 +61,8 @@ public function __construct( } /** + * Process Url Rewrites according to the products visibility attribute + * * @param array $productIds * @param int $visibility * @throws UrlAlreadyExistsException @@ -104,10 +106,12 @@ public function execute(array $productIds, int $visibility): void } /** + * Get Product Models by Id's + * * @param array $productIds * @return array */ - protected function getProductsByIds(array $productIds): array + private function getProductsByIds(array $productIds): array { $productCollection = $this->productCollectionFactory->create(); $productCollection->addAttributeToSelect(ProductInterface::VISIBILITY); From f893a925bbf5a92967a79509beefa4314b897f23 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky Date: Tue, 5 Mar 2019 17:32:16 +0200 Subject: [PATCH 13/50] ENGCOM-4302: Static test fix. --- .../Reports/Model/ResourceModel/Order/Collection.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php index 19549f7181aa..33dfdcffc9a4 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php @@ -830,7 +830,13 @@ protected function getTotalsExpression( ? '(main_table.base_subtotal - %2$s - %1$s - (ABS(main_table.base_discount_amount) - %3$s - %4$s))' : '((main_table.base_subtotal - %1$s - %2$s - (ABS(main_table.base_discount_amount) - %3$s - %4$s)) ' . ' * main_table.base_to_global_rate)'; - return sprintf($template, $baseSubtotalRefunded, $baseSubtotalCanceled, $baseDiscountRefunded, $baseDiscountCanceled); + return sprintf( + $template, + $baseSubtotalRefunded, + $baseSubtotalCanceled, + $baseDiscountRefunded, + $baseDiscountCanceled + ); } /** From a80943f529a337b36f59d3964a8448816f5e2532 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko Date: Wed, 6 Mar 2019 10:18:36 +0200 Subject: [PATCH 14/50] ENGCOM-20434: fixed type inconsistency --- .../ProcessUrlRewriteOnChangeProductVisibilityObserver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeProductVisibilityObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeProductVisibilityObserver.php index 56393c619f4e..2337bb3646bd 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeProductVisibilityObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeProductVisibilityObserver.php @@ -49,6 +49,6 @@ public function execute(Observer $observer) return; } - $this->adaptUrlRewritesToVisibility->execute($productIds, $visibility); + $this->adaptUrlRewritesToVisibility->execute($productIds, (int)$visibility); } } From ae9084de11bf2a59d2540d2ba6769b9ef1d46b8d Mon Sep 17 00:00:00 2001 From: vgelani Date: Thu, 7 Mar 2019 17:18:28 +0530 Subject: [PATCH 15/50] Updated review text in admin menu --- app/code/Magento/Review/etc/acl.xml | 4 ++-- app/code/Magento/Review/etc/adminhtml/menu.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Review/etc/acl.xml b/app/code/Magento/Review/etc/acl.xml index 09b80750da14..6932f2535090 100644 --- a/app/code/Magento/Review/etc/acl.xml +++ b/app/code/Magento/Review/etc/acl.xml @@ -16,8 +16,8 @@ - - + + diff --git a/app/code/Magento/Review/etc/adminhtml/menu.xml b/app/code/Magento/Review/etc/adminhtml/menu.xml index 0a2e49450e0c..72499b416cd1 100644 --- a/app/code/Magento/Review/etc/adminhtml/menu.xml +++ b/app/code/Magento/Review/etc/adminhtml/menu.xml @@ -8,8 +8,8 @@ - - + + From 63a25905793915ab720a5620a94141e4c4bd8997 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Fri, 8 Mar 2019 11:44:06 -0600 Subject: [PATCH 16/50] MAGETWO-97830: Elasticsearch date field crash quicksearch --- .../Product/FieldProvider/DynamicField.php | 2 +- .../Aggregation/Builder/Term.php | 4 +- .../SearchAdapter/Query/Builder/Match.php | 67 ++++++++-- .../ValueTransformer/DateTransformer.php | 44 +++++++ .../ValueTransformer/FloatTransformer.php | 24 ++++ .../ValueTransformer/IntegerTransformer.php | 24 ++++ .../ValueTransformer/TextTransformer.php | 65 ++++++++++ .../Query/ValueTransformerInterface.php | 22 ++++ .../Query/ValueTransformerPool.php | 46 +++++++ .../SearchAdapter/Query/Builder/MatchTest.php | 117 ++++++++++-------- app/code/Magento/Elasticsearch/etc/di.xml | 18 +++ .../Model/ResourceModel/SynonymReader.php | 14 ++- ...hp => product_text_attribute_rollback.php} | 6 +- .../_files/product_export_data_rollback.php | 8 +- ...uct_export_data_special_chars_rollback.php | 8 +- .../SearchAdapter/AdapterTest.php | 14 +++ .../Search/_files/filterable_attributes.php | 33 ++++- .../_files/filterable_attributes_rollback.php | 12 +- .../Search/Model/SynonymReaderTest.php | 17 ++- 19 files changed, 465 insertions(+), 80 deletions(-) create mode 100644 app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/DateTransformer.php create mode 100644 app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/FloatTransformer.php create mode 100644 app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/IntegerTransformer.php create mode 100644 app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/TextTransformer.php create mode 100644 app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformerInterface.php create mode 100644 app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformerPool.php rename dev/tests/integration/testsuite/Magento/Catalog/_files/{text_attribute_rollback.php => product_text_attribute_rollback.php} (84%) diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/DynamicField.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/DynamicField.php index 181cbd4dfd4b..7fa460fbb396 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/DynamicField.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/DynamicField.php @@ -140,7 +140,7 @@ public function getFields(array $context = []): array foreach ($groups as $group) { $groupPriceKey = $this->fieldNameResolver->getFieldName( $priceAttribute, - ['customerGroupId' => $group->getId(), 'websiteId' => $context['websiteId']] + array_merge($context, ['customerGroupId' => $group->getId()]) ); $allAttributes[$groupPriceKey] = [ 'type' => $this->fieldTypeConverter->convert(FieldTypeConverterInterface::INTERNAL_DATA_TYPE_FLOAT), diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Term.php b/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Term.php index 0c03a9df18dc..eeb48f805bcc 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Term.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Term.php @@ -22,13 +22,15 @@ public function build( array $queryResult, DataProviderInterface $dataProvider ) { + $buckets = $queryResult['aggregations'][$bucket->getName()]['buckets'] ?? []; $values = []; - foreach ($queryResult['aggregations'][$bucket->getName()]['buckets'] as $resultBucket) { + foreach ($buckets as $resultBucket) { $values[$resultBucket['key']] = [ 'value' => $resultBucket['key'], 'count' => $resultBucket['doc_count'], ]; } + return $values; } } diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php index aaa9d8a88382..64a82e131aad 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php @@ -5,6 +5,11 @@ */ namespace Magento\Elasticsearch\SearchAdapter\Query\Builder; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ResolverInterface + as FieldTypeResolver; +use Magento\Elasticsearch\SearchAdapter\Query\ValueTransformerPool; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Search\Request\Query\BoolExpression; use Magento\Framework\Search\Request\QueryInterface as RequestQueryInterface; use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; @@ -26,20 +31,49 @@ class Match implements QueryInterface private $fieldMapper; /** + * @deprecated + * @see \Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\TextTransformer * @var PreprocessorInterface[] */ protected $preprocessorContainer; + /** + * @var AttributeProvider + */ + private $attributeProvider; + + /** + * @var FieldTypeResolver + */ + private $fieldTypeResolver; + + /** + * @var ValueTransformerPool + */ + private $valueTransformerPool; + /** * @param FieldMapperInterface $fieldMapper * @param PreprocessorInterface[] $preprocessorContainer + * @param AttributeProvider|null $attributeProvider + * @param FieldTypeResolver|null $fieldTypeResolver + * @param ValueTransformerPool|null $valueTransformerPool */ public function __construct( FieldMapperInterface $fieldMapper, - array $preprocessorContainer + array $preprocessorContainer, + AttributeProvider $attributeProvider = null, + FieldTypeResolver $fieldTypeResolver = null, + ValueTransformerPool $valueTransformerPool = null ) { $this->fieldMapper = $fieldMapper; $this->preprocessorContainer = $preprocessorContainer; + $this->attributeProvider = $attributeProvider ?? ObjectManager::getInstance() + ->get(AttributeProvider::class); + $this->fieldTypeResolver = $fieldTypeResolver ?? ObjectManager::getInstance() + ->get(FieldTypeResolver::class); + $this->valueTransformerPool = $valueTransformerPool ?? ObjectManager::getInstance() + ->get(ValueTransformerPool::class); } /** @@ -72,10 +106,6 @@ public function build(array $selectQuery, RequestQueryInterface $requestQuery, $ */ protected function prepareQuery($queryValue, $conditionType) { - $queryValue = $this->escape($queryValue); - foreach ($this->preprocessorContainer as $preprocessor) { - $queryValue = $preprocessor->process($queryValue); - } $condition = $conditionType === BoolExpression::QUERY_CONDITION_NOT ? self::QUERY_CONDITION_MUST_NOT : $conditionType; return [ @@ -104,10 +134,24 @@ protected function buildQueries(array $matches, array $queryValue) // Checking for quoted phrase \"phrase test\", trim escaped surrounding quotes if found $count = 0; - $value = preg_replace('#^\\\\"(.*)\\\\"$#m', '$1', $queryValue['value'], -1, $count); + $value = preg_replace('#^"(.*)"$#m', '$1', $queryValue['value'], -1, $count); $condition = ($count) ? 'match_phrase' : 'match'; + $transformedTypes = []; foreach ($matches as $match) { + $attributeAdapter = $this->attributeProvider->getByAttributeCode($match['field']); + $fieldType = $this->fieldTypeResolver->getFieldType($attributeAdapter); + $valueTransformer = $this->valueTransformerPool->get($fieldType ?? 'text'); + $valueTransformerHash = \spl_object_hash($valueTransformer); + if (!isset($transformedTypes[$valueTransformerHash])) { + $transformedTypes[$valueTransformerHash] = $valueTransformer->transform($value); + } + $transformedValue = $transformedTypes[$valueTransformerHash]; + if (null === $transformedValue) { + //Value is incompatible with this field type. + continue; + } + $resolvedField = $this->fieldMapper->getFieldName( $match['field'], ['type' => FieldMapperInterface::TYPE_QUERY] @@ -117,8 +161,8 @@ protected function buildQueries(array $matches, array $queryValue) 'body' => [ $condition => [ $resolvedField => [ - 'query' => $value, - 'boost' => isset($match['boost']) ? $match['boost'] : 1, + 'query' => $transformedValue, + 'boost' => $match['boost'] ?? 1, ], ], ], @@ -131,16 +175,13 @@ protected function buildQueries(array $matches, array $queryValue) /** * Escape a value for special query characters such as ':', '(', ')', '*', '?', etc. * - * Cut trailing plus or minus sign, and @ symbol, using of which causes InnoDB to report a syntax error. - * https://dev.mysql.com/doc/refman/5.7/en/fulltext-boolean.html Fulltext-boolean search docs. - * + * @deprecated + * @see \Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\TextTransformer * @param string $value * @return string */ protected function escape($value) { - $value = preg_replace('/@+|[@+-]+$/', '', $value); - $pattern = '/(\+|-|&&|\|\||!|\(|\)|\{|}|\[|]|\^|"|~|\*|\?|:|\\\)/'; $replace = '\\\$1'; diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/DateTransformer.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/DateTransformer.php new file mode 100644 index 000000000000..49eca6e9d82a --- /dev/null +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/DateTransformer.php @@ -0,0 +1,44 @@ +dateFieldType = $dateFieldType; + } + + /** + * @inheritdoc + */ + public function transform(string $value): ?string + { + try { + $formattedDate = $this->dateFieldType->formatDate(null, $value); + } catch (\Exception $e) { + $formattedDate = null; + } + + return $formattedDate; + } +} diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/FloatTransformer.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/FloatTransformer.php new file mode 100644 index 000000000000..5e330076d3df --- /dev/null +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/FloatTransformer.php @@ -0,0 +1,24 @@ +preprocessors = $preprocessors; + } + + /** + * @inheritdoc + */ + public function transform(string $value): string + { + $value = $this->escape($value); + foreach ($this->preprocessors as $preprocessor) { + $value = $preprocessor->process($value); + } + + return $value; + } + + /** + * Escape a value for special query characters such as ':', '(', ')', '*', '?', etc. + * + * @param string $value + * @return string + */ + private function escape(string $value): string + { + $pattern = '/(\+|-|&&|\|\||!|\(|\)|\{|}|\[|]|\^|"|~|\*|\?|:|\\\)/'; + $replace = '\\\$1'; + + return preg_replace($pattern, $replace, $value); + } +} diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformerInterface.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformerInterface.php new file mode 100644 index 000000000000..c84ddc69cc7a --- /dev/null +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformerInterface.php @@ -0,0 +1,22 @@ +transformers = $valueTransformers; + } + + /** + * Get value transformer related to field type. + * + * @param string $fieldType + * @return ValueTransformerInterface + */ + public function get(string $fieldType): ValueTransformerInterface + { + return $this->transformers[$fieldType] ?? $this->transformers['default']; + } +} diff --git a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Query/Builder/MatchTest.php b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Query/Builder/MatchTest.php index 8114feb09d35..f3e2b0e9e0fa 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Query/Builder/MatchTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Query/Builder/MatchTest.php @@ -5,14 +5,30 @@ */ namespace Magento\Elasticsearch\Test\Unit\SearchAdapter\Query\Builder; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeAdapter; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ResolverInterface + as FieldTypeResolver; use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; use Magento\Elasticsearch\SearchAdapter\Query\Builder\Match as MatchQueryBuilder; +use Magento\Elasticsearch\SearchAdapter\Query\ValueTransformerInterface; +use Magento\Elasticsearch\SearchAdapter\Query\ValueTransformerPool; use Magento\Framework\Search\Request\Query\Match as MatchRequestQuery; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use PHPUnit_Framework_MockObject_MockObject as MockObject; +use PHPUnit\Framework\MockObject\MockObject as MockObject; class MatchTest extends \PHPUnit\Framework\TestCase { + /** + * @var AttributeProvider|MockObject + */ + private $attributeProvider; + + /** + * @var FieldTypeResolver|MockObject + */ + private $fieldTypeResolver; + /** * @var MatchQueryBuilder */ @@ -23,46 +39,63 @@ class MatchTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { + $this->attributeProvider = $this->createMock(AttributeProvider::class); + $this->fieldTypeResolver = $this->createMock(FieldTypeResolver::class); + + $valueTransformerPoolMock = $this->createMock(ValueTransformerPool::class); + $valueTransformerMock = $this->createMock(ValueTransformerInterface::class); + $valueTransformerPoolMock->method('get') + ->willReturn($valueTransformerMock); + $valueTransformerMock->method('transform') + ->willReturnArgument(0); + $this->matchQueryBuilder = (new ObjectManager($this))->getObject( MatchQueryBuilder::class, [ 'fieldMapper' => $this->getFieldMapper(), 'preprocessorContainer' => [], + 'attributeProvider' => $this->attributeProvider, + 'fieldTypeResolver' => $this->fieldTypeResolver, + 'valueTransformerPool' => $valueTransformerPoolMock, ] ); } /** * Tests that method constructs a correct select query. - * @see MatchQueryBuilder::build - * - * @dataProvider queryValuesInvariantsProvider * - * @param string $rawQueryValue - * @param string $errorMessage + * @see MatchQueryBuilder::build */ - public function testBuild($rawQueryValue, $errorMessage) + public function testBuild() { - $this->assertSelectQuery( - $this->matchQueryBuilder->build([], $this->getMatchRequestQuery($rawQueryValue), 'not'), - $errorMessage - ); - } + $attributeAdapter = $this->createMock(AttributeAdapter::class); + $this->attributeProvider->expects($this->once()) + ->method('getByAttributeCode') + ->with('some_field') + ->willReturn($attributeAdapter); + $this->fieldTypeResolver->expects($this->once()) + ->method('getFieldType') + ->with($attributeAdapter) + ->willReturn('text'); + + $rawQueryValue = 'query_value'; + $selectQuery = $this->matchQueryBuilder->build([], $this->getMatchRequestQuery($rawQueryValue), 'not'); - /** - * @link https://dev.mysql.com/doc/refman/5.7/en/fulltext-boolean.html Fulltext-boolean search docs. - * - * @return array - */ - public function queryValuesInvariantsProvider() - { - return [ - ['query_value', 'Select query field must match simple raw query value.'], - ['query_value+', 'Specifying a trailing plus sign causes InnoDB to report a syntax error.'], - ['query_value-', 'Specifying a trailing minus sign causes InnoDB to report a syntax error.'], - ['query_@value', 'The @ symbol is reserved for use by the @distance proximity search operator.'], - ['query_value+@', 'The @ symbol is reserved for use by the @distance proximity search operator.'], + $expectedSelectQuery = [ + 'bool' => [ + 'must_not' => [ + [ + 'match' => [ + 'some_field' => [ + 'query' => $rawQueryValue, + 'boost' => 43, + ], + ], + ], + ], + ], ]; + $this->assertEquals($expectedSelectQuery, $selectQuery); } /** @@ -76,6 +109,16 @@ public function queryValuesInvariantsProvider() */ public function testBuildMatchQuery($rawQueryValue, $queryValue, $match) { + $attributeAdapter = $this->createMock(AttributeAdapter::class); + $this->attributeProvider->expects($this->once()) + ->method('getByAttributeCode') + ->with('some_field') + ->willReturn($attributeAdapter); + $this->fieldTypeResolver->expects($this->once()) + ->method('getFieldType') + ->with($attributeAdapter) + ->willReturn('text'); + $query = $this->matchQueryBuilder->build([], $this->getMatchRequestQuery($rawQueryValue), 'should'); $expectedSelectQuery = [ @@ -111,30 +154,6 @@ public function matchProvider() ]; } - /** - * @param array $selectQuery - * @param string $errorMessage - */ - private function assertSelectQuery($selectQuery, $errorMessage) - { - $expectedSelectQuery = [ - 'bool' => [ - 'must_not' => [ - [ - 'match' => [ - 'some_field' => [ - 'query' => 'query_value', - 'boost' => 43, - ], - ], - ], - ], - ], - ]; - - $this->assertEquals($expectedSelectQuery, $selectQuery, $errorMessage); - } - /** * Gets fieldMapper mock object. * diff --git a/app/code/Magento/Elasticsearch/etc/di.xml b/app/code/Magento/Elasticsearch/etc/di.xml index 9796dc93858f..83dfd17a61d8 100644 --- a/app/code/Magento/Elasticsearch/etc/di.xml +++ b/app/code/Magento/Elasticsearch/etc/di.xml @@ -532,4 +532,22 @@ + + + + Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\TextTransformer + Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\DateTransformer + Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\FloatTransformer + Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\IntegerTransformer + + + + + + + Magento\Elasticsearch\SearchAdapter\Query\Preprocessor\Stopwords + Magento\Search\Adapter\Query\Preprocessor\Synonyms + + + diff --git a/app/code/Magento/Search/Model/ResourceModel/SynonymReader.php b/app/code/Magento/Search/Model/ResourceModel/SynonymReader.php index 46e794a1954c..45eee0a4001d 100644 --- a/app/code/Magento/Search/Model/ResourceModel/SynonymReader.php +++ b/app/code/Magento/Search/Model/ResourceModel/SynonymReader.php @@ -87,7 +87,7 @@ private function queryByPhrase($phrase) { $matchQuery = $this->fullTextSelect->getMatchQuery( ['synonyms' => 'synonyms'], - $phrase, + $this->escapePhrase($phrase), Fulltext::FULLTEXT_MODE_BOOLEAN ); $query = $this->getConnection()->select()->from( @@ -97,6 +97,18 @@ private function queryByPhrase($phrase) return $this->getConnection()->fetchAll($query); } + /** + * Cut trailing plus or minus sign, and @ symbol, using of which causes InnoDB to report a syntax error. + * + * @see https://dev.mysql.com/doc/refman/5.7/en/fulltext-boolean.html + * @param string $phrase + * @return string + */ + private function escapePhrase(string $phrase): string + { + return preg_replace('/@+|[@+-]+$/', '', $phrase); + } + /** * A private helper function to retrieve matching synonym groups per scope * diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/text_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_text_attribute_rollback.php similarity index 84% rename from dev/tests/integration/testsuite/Magento/Catalog/_files/text_attribute_rollback.php rename to dev/tests/integration/testsuite/Magento/Catalog/_files/product_text_attribute_rollback.php index cbc0476efd1b..a9ab0e11312b 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/text_attribute_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_text_attribute_rollback.php @@ -3,13 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + /* Delete attribute with text_attribute code */ -$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get('Magento\Framework\Registry'); +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); $registry->unregister('isSecureArea'); $registry->register('isSecureArea', true); + /** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ $attribute = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - 'Magento\Catalog\Model\ResourceModel\Eav\Attribute' + \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class ); $attribute->load('text_attribute', 'attribute_code'); $attribute->delete(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_rollback.php index 168073bc6ab7..c57c7c3fd6a9 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_rollback.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_rollback.php @@ -5,10 +5,10 @@ */ /** Delete all products */ -require dirname(dirname(__DIR__)) . '/Catalog/_files/products_with_multiselect_attribute_rollback.php'; +include dirname(dirname(__DIR__)) . '/Catalog/_files/products_with_multiselect_attribute_rollback.php'; /** Delete text attribute */ -require dirname(dirname(__DIR__)) . '/Catalog/_files/text_attribute_rollback.php'; +include dirname(dirname(__DIR__)) . '/Catalog/_files/product_text_attribute_rollback.php'; -require dirname(dirname(__DIR__)) . '/Store/_files/second_store_rollback.php'; +include dirname(dirname(__DIR__)) . '/Store/_files/second_store_rollback.php'; -require dirname(dirname(__DIR__)) . '/Catalog/_files/category_rollback.php'; +include dirname(dirname(__DIR__)) . '/Catalog/_files/category_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_special_chars_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_special_chars_rollback.php index 168073bc6ab7..c57c7c3fd6a9 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_special_chars_rollback.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_special_chars_rollback.php @@ -5,10 +5,10 @@ */ /** Delete all products */ -require dirname(dirname(__DIR__)) . '/Catalog/_files/products_with_multiselect_attribute_rollback.php'; +include dirname(dirname(__DIR__)) . '/Catalog/_files/products_with_multiselect_attribute_rollback.php'; /** Delete text attribute */ -require dirname(dirname(__DIR__)) . '/Catalog/_files/text_attribute_rollback.php'; +include dirname(dirname(__DIR__)) . '/Catalog/_files/product_text_attribute_rollback.php'; -require dirname(dirname(__DIR__)) . '/Store/_files/second_store_rollback.php'; +include dirname(dirname(__DIR__)) . '/Store/_files/second_store_rollback.php'; -require dirname(dirname(__DIR__)) . '/Catalog/_files/category_rollback.php'; +include dirname(dirname(__DIR__)) . '/Catalog/_files/category_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php index dc288a18fadb..8d80fd8533d6 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php @@ -369,4 +369,18 @@ public function dateDataProvider() [['from' => '2000-02-01T00:00:00Z', 'to' => ''], 0], ]; } + + public function filterByAttributeValuesDataProvider() + { + $variations = parent::filterByAttributeValuesDataProvider(); + + $variations['quick search by date'] = [ + 'quick_search_container', + [ + 'search_term' => '2000-10-30', + ], + ]; + + return $variations; + } } diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes.php index b09af48b5f94..f4f3337a253c 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes.php @@ -20,6 +20,10 @@ CategorySetup::class, ['resourceName' => 'catalog_setup'] ); +$productEntityTypeId = $installer->getEntityTypeId( + \Magento\Catalog\Api\Data\ProductAttributeInterface::ENTITY_TYPE_CODE +); + $selectOptions = []; $selectAttributes = []; foreach (range(1, 2) as $index) { @@ -30,7 +34,7 @@ $selectAttribute->setData( [ 'attribute_code' => 'select_attribute_' . $index, - 'entity_type_id' => $installer->getEntityTypeId('catalog_product'), + 'entity_type_id' => $productEntityTypeId, 'is_global' => 1, 'is_user_defined' => 1, 'frontend_input' => 'select', @@ -56,7 +60,8 @@ ); $selectAttribute->save(); /* Assign attribute to attribute set */ - $installer->addAttributeToGroup('catalog_product', 'Default', 'General', $selectAttribute->getId()); + $installer->addAttributeToGroup($productEntityTypeId, 'Default', 'General', $selectAttribute->getId()); + /** @var $selectOptions Collection */ $selectOption = Bootstrap::getObjectManager()->create( Collection::class @@ -65,6 +70,26 @@ $selectAttributes[$index] = $selectAttribute; $selectOptions[$index] = $selectOption; } + +$dateAttribute = Bootstrap::getObjectManager()->create(Attribute::class); +$dateAttribute->setData( + [ + 'attribute_code' => 'date_attribute', + 'entity_type_id' => $productEntityTypeId, + 'is_global' => 1, + 'is_filterable' => 1, + 'backend_type' => 'datetime', + 'frontend_input' => 'date', + 'frontend_label' => 'Test Date', + 'is_searchable' => 1, + 'is_filterable_in_search' => 1, + ] +); +$dateAttribute->save(); +/* Assign attribute to attribute set */ +$installer->addAttributeToGroup($productEntityTypeId, 'Default', 'General', $dateAttribute->getId()); + +$productAttributeSetId = $installer->getAttributeSetId($productEntityTypeId, 'Default'); /* Create simple products per each first attribute option */ foreach ($selectOptions[1] as $option) { /** @var $product Product */ @@ -74,7 +99,7 @@ $product->setTypeId( Type::TYPE_SIMPLE )->setAttributeSetId( - $installer->getAttributeSetId('catalog_product', 'Default') + $productAttributeSetId )->setWebsiteIds( [1] )->setName( @@ -92,6 +117,7 @@ )->setStockData( ['use_config_manage_stock' => 1, 'qty' => 5, 'is_in_stock' => 1] )->save(); + Bootstrap::getObjectManager()->get( Action::class )->updateAttributes( @@ -99,6 +125,7 @@ [ $selectAttributes[1]->getAttributeCode() => $option->getId(), $selectAttributes[2]->getAttributeCode() => $selectOptions[2]->getLastItem()->getId(), + $dateAttribute->getAttributeCode() => '10/30/2000', ], $product->getStoreId() ); diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes_rollback.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes_rollback.php index 18a5372d06d9..fd413726b263 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes_rollback.php @@ -13,6 +13,7 @@ $registry = Bootstrap::getObjectManager()->get(Registry::class); $registry->unregister('isSecureArea'); $registry->register('isSecureArea', true); + /** @var $productCollection \Magento\Catalog\Model\ResourceModel\Product\Collection */ $productCollection = Bootstrap::getObjectManager() ->create(Product::class) @@ -20,17 +21,26 @@ foreach ($productCollection as $product) { $product->delete(); } + /** @var $attribute Attribute */ $attribute = Bootstrap::getObjectManager()->create( Attribute::class ); /** @var $installer CategorySetup */ $installer = Bootstrap::getObjectManager()->create(CategorySetup::class); +$productEntityTypeId = $installer->getEntityTypeId( + \Magento\Catalog\Api\Data\ProductAttributeInterface::ENTITY_TYPE_CODE +); foreach (range(1, 2) as $index) { - $attribute->loadByCode($installer->getEntityTypeId('catalog_product'), 'select_attribute_' . $index); + $attribute->loadByCode($productEntityTypeId, 'select_attribute_' . $index); if ($attribute->getId()) { $attribute->delete(); } } +$attribute->loadByCode($productEntityTypeId, 'date_attribute'); +if ($attribute->getId()) { + $attribute->delete(); +} + $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Search/Model/SynonymReaderTest.php b/dev/tests/integration/testsuite/Magento/Search/Model/SynonymReaderTest.php index b9ba89ba5314..2d0020ba2268 100644 --- a/dev/tests/integration/testsuite/Magento/Search/Model/SynonymReaderTest.php +++ b/dev/tests/integration/testsuite/Magento/Search/Model/SynonymReaderTest.php @@ -48,7 +48,22 @@ public static function loadByPhraseDataProvider() ['synonyms' => 'queen,monarch', 'store_id' => 1, 'website_id' => 0], ['synonyms' => 'british,english', 'store_id' => 1, 'website_id' => 0] ] - ] + ], + [ + 'query_value', [] + ], + [ + 'query_value+', [] + ], + [ + 'query_value-', [] + ], + [ + 'query_@value', [] + ], + [ + 'query_value+@', [] + ], ]; } From 9f93a5e01785ce3ab143e561ff9204609e430c6d Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Fri, 8 Mar 2019 15:49:44 -0600 Subject: [PATCH 17/50] MAGETWO-97830: Elasticsearch date field crash quicksearch --- .../SearchAdapter/Query/Builder/Match.php | 11 +++++------ .../Unit/SearchAdapter/Query/Builder/MatchTest.php | 7 +++---- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php index 64a82e131aad..afd383c13421 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php @@ -6,8 +6,7 @@ namespace Magento\Elasticsearch\SearchAdapter\Query\Builder; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; -use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ResolverInterface - as FieldTypeResolver; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ResolverInterface as TypeResolver; use Magento\Elasticsearch\SearchAdapter\Query\ValueTransformerPool; use Magento\Framework\App\ObjectManager; use Magento\Framework\Search\Request\Query\BoolExpression; @@ -43,7 +42,7 @@ class Match implements QueryInterface private $attributeProvider; /** - * @var FieldTypeResolver + * @var TypeResolver */ private $fieldTypeResolver; @@ -56,14 +55,14 @@ class Match implements QueryInterface * @param FieldMapperInterface $fieldMapper * @param PreprocessorInterface[] $preprocessorContainer * @param AttributeProvider|null $attributeProvider - * @param FieldTypeResolver|null $fieldTypeResolver + * @param TypeResolver|null $fieldTypeResolver * @param ValueTransformerPool|null $valueTransformerPool */ public function __construct( FieldMapperInterface $fieldMapper, array $preprocessorContainer, AttributeProvider $attributeProvider = null, - FieldTypeResolver $fieldTypeResolver = null, + TypeResolver $fieldTypeResolver = null, ValueTransformerPool $valueTransformerPool = null ) { $this->fieldMapper = $fieldMapper; @@ -71,7 +70,7 @@ public function __construct( $this->attributeProvider = $attributeProvider ?? ObjectManager::getInstance() ->get(AttributeProvider::class); $this->fieldTypeResolver = $fieldTypeResolver ?? ObjectManager::getInstance() - ->get(FieldTypeResolver::class); + ->get(TypeResolver::class); $this->valueTransformerPool = $valueTransformerPool ?? ObjectManager::getInstance() ->get(ValueTransformerPool::class); } diff --git a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Query/Builder/MatchTest.php b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Query/Builder/MatchTest.php index f3e2b0e9e0fa..d0ffc6debcd8 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Query/Builder/MatchTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Query/Builder/MatchTest.php @@ -7,8 +7,7 @@ use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeAdapter; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; -use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ResolverInterface - as FieldTypeResolver; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ResolverInterface as TypeResolver; use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; use Magento\Elasticsearch\SearchAdapter\Query\Builder\Match as MatchQueryBuilder; use Magento\Elasticsearch\SearchAdapter\Query\ValueTransformerInterface; @@ -25,7 +24,7 @@ class MatchTest extends \PHPUnit\Framework\TestCase private $attributeProvider; /** - * @var FieldTypeResolver|MockObject + * @var TypeResolver|MockObject */ private $fieldTypeResolver; @@ -40,7 +39,7 @@ class MatchTest extends \PHPUnit\Framework\TestCase protected function setUp() { $this->attributeProvider = $this->createMock(AttributeProvider::class); - $this->fieldTypeResolver = $this->createMock(FieldTypeResolver::class); + $this->fieldTypeResolver = $this->createMock(TypeResolver::class); $valueTransformerPoolMock = $this->createMock(ValueTransformerPool::class); $valueTransformerMock = $this->createMock(ValueTransformerInterface::class); From 9dee0e111dc25b3ea45619370b876014ca855f0d Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun Date: Tue, 12 Mar 2019 07:59:18 +0200 Subject: [PATCH 18/50] Currency misspelled in graphql attributes --- app/code/Magento/DirectoryGraphQl/etc/schema.graphqls | 4 ++-- .../testsuite/Magento/GraphQl/Directory/CurrencyTest.php | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/DirectoryGraphQl/etc/schema.graphqls b/app/code/Magento/DirectoryGraphQl/etc/schema.graphqls index 40ef6975fad8..f2bc576f95e8 100644 --- a/app/code/Magento/DirectoryGraphQl/etc/schema.graphqls +++ b/app/code/Magento/DirectoryGraphQl/etc/schema.graphqls @@ -10,8 +10,8 @@ type Query { type Currency { base_currency_code: String base_currency_symbol: String - default_display_currecy_code: String - default_display_currecy_symbol: String + default_display_currency_code: String + default_display_currency_symbol: String available_currency_codes: [String] exchange_rates: [ExchangeRate] } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Directory/CurrencyTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Directory/CurrencyTest.php index 1ff0b53dda0b..ad5d71cb0860 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Directory/CurrencyTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Directory/CurrencyTest.php @@ -21,8 +21,8 @@ public function testGetCurrency() currency { base_currency_code base_currency_symbol - default_display_currecy_code - default_display_currecy_symbol + default_display_currency_code + default_display_currency_symbol available_currency_codes exchange_rates { currency_to @@ -36,8 +36,8 @@ public function testGetCurrency() $this->assertArrayHasKey('currency', $result); $this->assertArrayHasKey('base_currency_code', $result['currency']); $this->assertArrayHasKey('base_currency_symbol', $result['currency']); - $this->assertArrayHasKey('default_display_currecy_code', $result['currency']); - $this->assertArrayHasKey('default_display_currecy_symbol', $result['currency']); + $this->assertArrayHasKey('default_display_currency_code', $result['currency']); + $this->assertArrayHasKey('default_display_currency_symbol', $result['currency']); $this->assertArrayHasKey('available_currency_codes', $result['currency']); $this->assertArrayHasKey('exchange_rates', $result['currency']); } From bfbc5e95b74d0b96b86f3a13c978320c130971e0 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Tue, 12 Mar 2019 17:55:45 -0500 Subject: [PATCH 19/50] MAGETWO-98621: Special price not removing at website scope --- .../Eav/Model/Entity/AbstractEntity.php | 8 +++-- .../Model/ResourceModel/ProductTest.php | 34 ++++++++++++++++--- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php index d0a5e8de53ae..1fd71e446e6b 100644 --- a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php +++ b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php @@ -1683,14 +1683,16 @@ public function saveAttribute(DataObject $object, $attributeCode) $connection->beginTransaction(); try { - $select = $connection->select()->from($table, 'value_id')->where($where); - $origValueId = $connection->fetchOne($select); + $select = $connection->select()->from($table, ['value_id', 'value'])->where($where); + $origRow = $connection->fetchRow($select); + $origValueId = $origRow['value_id'] ?? false; + $origValue = $origRow['value'] ?? null; if ($origValueId === false && $newValue !== null) { $this->_insertAttribute($object, $attribute, $newValue); } elseif ($origValueId !== false && $newValue !== null) { $this->_updateAttribute($object, $attribute, $origValueId, $newValue); - } elseif ($origValueId !== false && $newValue === null) { + } elseif ($origValueId !== false && $newValue === null && $origValue !== null) { $connection->delete($table, $where); } $this->_processAttributeValues(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/ProductTest.php index 7954e2c36227..476f01eb277d 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/ProductTest.php @@ -12,6 +12,11 @@ class ProductTest extends TestCase { + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + /** * @var Product */ @@ -29,7 +34,8 @@ protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); - $this->model = $this->objectManager->get(Product::class); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->model = $this->objectManager->create(Product::class); } /** @@ -42,11 +48,29 @@ public function testGetAttributeRawValue() $sku = 'simple'; $attribute = 'name'; - /** @var ProductRepositoryInterface $productRepository */ - $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); - $product = $productRepository->get($sku); - + $product = $this->productRepository->get($sku); $actual = $this->model->getAttributeRawValue($product->getId(), $attribute, null); self::assertEquals($product->getName(), $actual); } + + /** + * @magentoAppArea adminhtml + * @magentoDataFixture Magento/Catalog/_files/product_special_price.php + * @magentoAppIsolation enabled + * @magentoConfigFixture default_store catalog/price/scope 1 + */ + public function testUpdateStoreSpecificSpecialPrice() + { + /** @var \Magento\Catalog\Model\Product $product */ + $product = $this->productRepository->get('simple', true, 1); + $this->assertEquals(5.99, $product->getSpecialPrice()); + + $product->setSpecialPrice(''); + $this->model->save($product); + $product = $this->productRepository->get('simple', false, 1, true); + $this->assertEmpty($product->getSpecialPrice()); + + $product = $this->productRepository->get('simple', false, 0, true); + $this->assertEquals(5.99, $product->getSpecialPrice()); + } } From b62240110ec89edcefbb2f461c311086db9bc013 Mon Sep 17 00:00:00 2001 From: Vishal Gelani Date: Wed, 13 Mar 2019 11:26:19 +0530 Subject: [PATCH 20/50] Updated sort order --- app/code/Magento/Review/etc/adminhtml/menu.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Review/etc/adminhtml/menu.xml b/app/code/Magento/Review/etc/adminhtml/menu.xml index 72499b416cd1..737632947192 100644 --- a/app/code/Magento/Review/etc/adminhtml/menu.xml +++ b/app/code/Magento/Review/etc/adminhtml/menu.xml @@ -8,8 +8,8 @@ - - + + From 417fda4da93bdd11f83eec9c7ed27f2a84cc2a44 Mon Sep 17 00:00:00 2001 From: Vishal Gelani Date: Wed, 13 Mar 2019 11:27:01 +0530 Subject: [PATCH 21/50] Update acl.xml --- app/code/Magento/Review/etc/acl.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Review/etc/acl.xml b/app/code/Magento/Review/etc/acl.xml index 6932f2535090..46fdb20dee4a 100644 --- a/app/code/Magento/Review/etc/acl.xml +++ b/app/code/Magento/Review/etc/acl.xml @@ -16,8 +16,8 @@ - - + + From bfea663290be803d2ced87e55376790a27d47001 Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun Date: Wed, 13 Mar 2019 09:53:22 +0200 Subject: [PATCH 22/50] Currency misspelled in graphql attributes --- app/code/Magento/DirectoryGraphQl/etc/schema.graphqls | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/DirectoryGraphQl/etc/schema.graphqls b/app/code/Magento/DirectoryGraphQl/etc/schema.graphqls index f2bc576f95e8..59c6a35e9a1a 100644 --- a/app/code/Magento/DirectoryGraphQl/etc/schema.graphqls +++ b/app/code/Magento/DirectoryGraphQl/etc/schema.graphqls @@ -10,8 +10,10 @@ type Query { type Currency { base_currency_code: String base_currency_symbol: String + default_display_currecy_code: String @deprecated(reason: "Symbol was missed. Use `default_display_currency_code`.") default_display_currency_code: String - default_display_currency_symbol: String + default_display_currecy_symbol: String @deprecated(reason: "Symbol was missed. Use `default_display_currency_symbol`.") + default_display_currency_symbol available_currency_codes: [String] exchange_rates: [ExchangeRate] } From 6ebdde1f138cb5316b83dd437a5db329289ee8cb Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun Date: Wed, 13 Mar 2019 13:21:47 +0200 Subject: [PATCH 23/50] Currency misspelled in graphql attributes --- app/code/Magento/DirectoryGraphQl/etc/schema.graphqls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/DirectoryGraphQl/etc/schema.graphqls b/app/code/Magento/DirectoryGraphQl/etc/schema.graphqls index 59c6a35e9a1a..8da1920f9a44 100644 --- a/app/code/Magento/DirectoryGraphQl/etc/schema.graphqls +++ b/app/code/Magento/DirectoryGraphQl/etc/schema.graphqls @@ -13,7 +13,7 @@ type Currency { default_display_currecy_code: String @deprecated(reason: "Symbol was missed. Use `default_display_currency_code`.") default_display_currency_code: String default_display_currecy_symbol: String @deprecated(reason: "Symbol was missed. Use `default_display_currency_symbol`.") - default_display_currency_symbol + default_display_currency_symbol: String available_currency_codes: [String] exchange_rates: [ExchangeRate] } From 06fe10916fcc622bd081c1bd83179b02696e9d62 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Wed, 13 Mar 2019 08:21:53 -0500 Subject: [PATCH 24/50] MAGETWO-98643: [Magento Cloud] Translated Arabic URL's are returning 404's --- .../UrlRewrite/Model/Storage/DbStorage.php | 109 +++++++++++++----- .../Model/UrlFinderInterfaceTest.php | 71 ++++++++++++ .../UrlRewrite/_files/url_rewrites.php | 42 +++++++ .../_files/url_rewrites_rollback.php | 20 ++++ 4 files changed, 210 insertions(+), 32 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/UrlRewrite/Model/UrlFinderInterfaceTest.php create mode 100644 dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites.php create mode 100644 dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites_rollback.php diff --git a/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php b/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php index d8ceb16d71fd..2ac1bdd71211 100644 --- a/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php +++ b/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php @@ -105,42 +105,18 @@ protected function doFindOneByData(array $data) $result = null; $requestPath = $data[UrlRewrite::REQUEST_PATH]; - - $data[UrlRewrite::REQUEST_PATH] = [ + $decodedRequestPath = urldecode($requestPath); + $data[UrlRewrite::REQUEST_PATH] = array_unique([ rtrim($requestPath, '/'), rtrim($requestPath, '/') . '/', - ]; + rtrim($decodedRequestPath, '/'), + rtrim($decodedRequestPath, '/') . '/', + ]); $resultsFromDb = $this->connection->fetchAll($this->prepareSelect($data)); - - if (count($resultsFromDb) === 1) { - $resultFromDb = current($resultsFromDb); - $redirectTypes = [OptionProvider::TEMPORARY, OptionProvider::PERMANENT]; - - // If request path matches the DB value or it's redirect - we can return result from DB - $canReturnResultFromDb = ($resultFromDb[UrlRewrite::REQUEST_PATH] === $requestPath - || in_array((int)$resultFromDb[UrlRewrite::REDIRECT_TYPE], $redirectTypes, true)); - - // Otherwise return 301 redirect to request path from DB results - $result = $canReturnResultFromDb ? $resultFromDb : [ - UrlRewrite::ENTITY_TYPE => 'custom', - UrlRewrite::ENTITY_ID => '0', - UrlRewrite::REQUEST_PATH => $requestPath, - UrlRewrite::TARGET_PATH => $resultFromDb[UrlRewrite::REQUEST_PATH], - UrlRewrite::REDIRECT_TYPE => OptionProvider::PERMANENT, - UrlRewrite::STORE_ID => $resultFromDb[UrlRewrite::STORE_ID], - UrlRewrite::DESCRIPTION => null, - UrlRewrite::IS_AUTOGENERATED => '0', - UrlRewrite::METADATA => null, - ]; - } else { - // If we have 2 results - return the row that matches request path - foreach ($resultsFromDb as $resultFromDb) { - if ($resultFromDb[UrlRewrite::REQUEST_PATH] === $requestPath) { - $result = $resultFromDb; - break; - } - } + if ($resultsFromDb) { + $urlRewrite = $this->extractMostRelevantUrlRewrite($requestPath, $resultsFromDb); + $result = $this->prepareUrlRewrite($requestPath, $urlRewrite); } return $result; @@ -149,6 +125,75 @@ protected function doFindOneByData(array $data) return $this->connection->fetchRow($this->prepareSelect($data)); } + /** + * Extract most relevant url rewrite from url rewrites list + * + * @param string $requestPath + * @param array $urlRewrites + * @return array|null + */ + private function extractMostRelevantUrlRewrite(string $requestPath, array $urlRewrites): ?array + { + $prioritizedUrlRewrites = []; + foreach ($urlRewrites as $urlRewrite) { + switch (true) { + case $urlRewrite[UrlRewrite::REQUEST_PATH] === $requestPath: + $priority = 1; + break; + case $urlRewrite[UrlRewrite::REQUEST_PATH] === urldecode($requestPath): + $priority = 2; + break; + case rtrim($urlRewrite[UrlRewrite::REQUEST_PATH], '/') === rtrim($requestPath, '/'): + $priority = 3; + break; + case rtrim($urlRewrite[UrlRewrite::REQUEST_PATH], '/') === rtrim(urldecode($requestPath), '/'): + $priority = 4; + break; + default: + $priority = 5; + break; + } + $prioritizedUrlRewrites[$priority] = $urlRewrite; + } + ksort($prioritizedUrlRewrites); + + return array_shift($prioritizedUrlRewrites); + } + + /** + * Prepare url rewrite + * + * If request path matches the DB value or it's redirect - we can return result from DB + * Otherwise return 301 redirect to request path from DB results + * + * @param string $requestPath + * @param array $urlRewrite + * @return array + */ + private function prepareUrlRewrite(string $requestPath, array $urlRewrite): array + { + $redirectTypes = [OptionProvider::TEMPORARY, OptionProvider::PERMANENT]; + $canReturnResultFromDb = ( + in_array($urlRewrite[UrlRewrite::REQUEST_PATH], [$requestPath, urldecode($requestPath)], true) + || in_array((int) $urlRewrite[UrlRewrite::REDIRECT_TYPE], $redirectTypes, true) + ); + if (!$canReturnResultFromDb) { + $urlRewrite = [ + UrlRewrite::ENTITY_TYPE => 'custom', + UrlRewrite::ENTITY_ID => '0', + UrlRewrite::REQUEST_PATH => $requestPath, + UrlRewrite::TARGET_PATH => $urlRewrite[UrlRewrite::REQUEST_PATH], + UrlRewrite::REDIRECT_TYPE => OptionProvider::PERMANENT, + UrlRewrite::STORE_ID => $urlRewrite[UrlRewrite::STORE_ID], + UrlRewrite::DESCRIPTION => null, + UrlRewrite::IS_AUTOGENERATED => '0', + UrlRewrite::METADATA => null, + ]; + } + + return $urlRewrite; + } + /** * Delete old URLs from DB. * diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/UrlFinderInterfaceTest.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/UrlFinderInterfaceTest.php new file mode 100644 index 000000000000..b6055f14e79d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/UrlFinderInterfaceTest.php @@ -0,0 +1,71 @@ +urlFinder = Bootstrap::getObjectManager()->create(UrlFinderInterface::class); + } + + /** + * @dataProvider findOneDataProvider + * @param string $requestPath + * @param string $targetPath + * @param int $redirectType + */ + public function testFindOneByData(string $requestPath, string $targetPath, int $redirectType) + { + $data = [ + UrlRewrite::REQUEST_PATH => $requestPath, + ]; + $urlRewrite = $this->urlFinder->findOneByData($data); + $this->assertEquals($targetPath, $urlRewrite->getTargetPath()); + $this->assertEquals($redirectType, $urlRewrite->getRedirectType()); + } + + /** + * @return array + */ + public function findOneDataProvider(): array + { + return [ + ['string', 'test_page1', 0], + ['string/', 'string', 301], + ['string_permanent', 'test_page1', 301], + ['string_permanent/', 'test_page1', 301], + ['string_temporary', 'test_page1', 302], + ['string_temporary/', 'test_page1', 302], + ['строка', 'test_page1', 0], + ['строка/', 'строка', 301], + [urlencode('строка'), 'test_page2', 0], + [urlencode('строка') . '/', urlencode('строка'), 301], + ['другая_строка', 'test_page1', 302], + ['другая_строка/', 'test_page1', 302], + [urlencode('другая_строка'), 'test_page1', 302], + [urlencode('другая_строка') . '/', 'test_page1', 302], + ['السلسلة', 'test_page1', 0], + [urlencode('السلسلة'), 'test_page1', 0], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites.php new file mode 100644 index 000000000000..9edc6507308e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites.php @@ -0,0 +1,42 @@ +create(\Magento\UrlRewrite\Model\ResourceModel\UrlRewrite::class); +foreach ($rewritesData as $rewriteData) { + list ($requestPath, $targetPath, $redirectType) = $rewriteData; + $rewrite = $objectManager->create(\Magento\UrlRewrite\Model\UrlRewrite::class); + $rewrite->setEntityType('custom') + ->setRequestPath($requestPath) + ->setTargetPath($targetPath) + ->setRedirectType($redirectType); + $rewriteResource->save($rewrite); +} diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites_rollback.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites_rollback.php new file mode 100644 index 000000000000..a98f947d614e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites_rollback.php @@ -0,0 +1,20 @@ +get(\Magento\Framework\Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +$urlRewriteCollection = $objectManager->create(\Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection::class); +$collection = $urlRewriteCollection + ->addFieldToFilter('target_path', ['test_page1', 'test_page2']) + ->load() + ->walk('delete'); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); From cb9d6757f4cdf89cc5c1e970663b8eeb30606e40 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Wed, 13 Mar 2019 12:55:52 -0500 Subject: [PATCH 25/50] MAGETWO-98665: [Magento cloud] Media directories not sorted --- app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php | 3 ++- .../Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php b/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php index b2ef78bab990..ca563bd9d8f6 100644 --- a/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php +++ b/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php @@ -270,7 +270,8 @@ public function getDirsCollection($path) $collection = $this->getCollection($path) ->setCollectDirs(true) ->setCollectFiles(false) - ->setCollectRecursively(false); + ->setCollectRecursively(false) + ->setOrder('basename', \Magento\Framework\Data\Collection\Filesystem::SORT_ORDER_ASC); $conditions = $this->getConditionsForExcludeDirs(); diff --git a/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php b/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php index 309f08a54aab..7bec1e360146 100644 --- a/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php +++ b/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php @@ -417,6 +417,10 @@ protected function generalTestGetDirsCollection($path, $collectionArray = [], $e ->method('setCollectRecursively') ->with(false) ->willReturnSelf(); + $storageCollectionMock->expects($this->once()) + ->method('setOrder') + ->with('basename', \Magento\Framework\Data\Collection\Filesystem::SORT_ORDER_ASC) + ->willReturnSelf(); $storageCollectionMock->expects($this->once()) ->method('getIterator') ->willReturn(new \ArrayIterator($collectionArray)); From 28d11bca2f3ec24fda1e6d9cc73f151bbaf2e9d5 Mon Sep 17 00:00:00 2001 From: Dmytro Yushkin Date: Thu, 14 Mar 2019 08:30:24 -0500 Subject: [PATCH 26/50] MAGETWO-98617: Pager does not work on Newsletter Subscribers Admin page --- .../Grid/Massaction/AbstractMassaction.php | 28 +++-- .../Unit/Block/Widget/Grid/MassactionTest.php | 56 ---------- .../Block/Widget/Grid/MassactionTest.php | 52 --------- .../Block/Adminhtml/Subscriber/GridTest.php | 103 ++++++++++++++++++ 4 files changed, 116 insertions(+), 123 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Newsletter/Block/Adminhtml/Subscriber/GridTest.php diff --git a/app/code/Magento/Backend/Block/Widget/Grid/Massaction/AbstractMassaction.php b/app/code/Magento/Backend/Block/Widget/Grid/Massaction/AbstractMassaction.php index 9890a10a4ceb..891b2a3ada72 100644 --- a/app/code/Magento/Backend/Block/Widget/Grid/Massaction/AbstractMassaction.php +++ b/app/code/Magento/Backend/Block/Widget/Grid/Massaction/AbstractMassaction.php @@ -282,25 +282,23 @@ public function getGridIdsJson() if (!$this->getUseSelectAll()) { return ''; } - /** @var \Magento\Framework\Data\Collection $allIdsCollection */ - $allIdsCollection = clone $this->getParentBlock()->getCollection(); - if ($this->getMassactionIdField()) { - $massActionIdField = $this->getMassactionIdField(); + /** @var \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection $collection */ + $collection = clone $this->getParentBlock()->getCollection(); + + if ($collection instanceof AbstractDb) { + $idsSelect = clone $collection->getSelect(); + $idsSelect->reset(\Magento\Framework\DB\Select::ORDER); + $idsSelect->reset(\Magento\Framework\DB\Select::LIMIT_COUNT); + $idsSelect->reset(\Magento\Framework\DB\Select::LIMIT_OFFSET); + $idsSelect->reset(\Magento\Framework\DB\Select::COLUMNS); + $idsSelect->columns($this->getMassactionIdField(), 'main_table'); + $idList = $collection->getConnection()->fetchCol($idsSelect); } else { - $massActionIdField = $this->getParentBlock()->getMassactionIdField(); + $idList = $collection->setPageSize(0)->getColumnValues($this->getMassactionIdField()); } - if ($allIdsCollection instanceof AbstractDb) { - $allIdsCollection->getSelect()->limit(); - $allIdsCollection->clear(); - } - - $gridIds = $allIdsCollection->setPageSize(0)->getColumnValues($massActionIdField); - if (!empty($gridIds)) { - return join(",", $gridIds); - } - return ''; + return implode(',', $idList); } /** diff --git a/app/code/Magento/Backend/Test/Unit/Block/Widget/Grid/MassactionTest.php b/app/code/Magento/Backend/Test/Unit/Block/Widget/Grid/MassactionTest.php index e8143b5f6b43..e62b73f39241 100644 --- a/app/code/Magento/Backend/Test/Unit/Block/Widget/Grid/MassactionTest.php +++ b/app/code/Magento/Backend/Test/Unit/Block/Widget/Grid/MassactionTest.php @@ -269,62 +269,6 @@ public function testGetGridIdsJsonWithoutUseSelectAll() $this->assertEmpty($this->_block->getGridIdsJson()); } - /** - * @param array $items - * @param string $result - * - * @dataProvider dataProviderGetGridIdsJsonWithUseSelectAll - */ - public function testGetGridIdsJsonWithUseSelectAll(array $items, $result) - { - $this->_block->setUseSelectAll(true); - - if ($this->_block->getMassactionIdField()) { - $massActionIdField = $this->_block->getMassactionIdField(); - } else { - $massActionIdField = $this->_block->getParentBlock()->getMassactionIdField(); - } - - $collectionMock = $this->getMockBuilder(\Magento\Framework\Data\Collection::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->_gridMock->expects($this->once()) - ->method('getCollection') - ->willReturn($collectionMock); - $collectionMock->expects($this->once()) - ->method('setPageSize') - ->with(0) - ->willReturnSelf(); - $collectionMock->expects($this->once()) - ->method('getColumnValues') - ->with($massActionIdField) - ->willReturn($items); - - $this->assertEquals($result, $this->_block->getGridIdsJson()); - } - - /** - * @return array - */ - public function dataProviderGetGridIdsJsonWithUseSelectAll() - { - return [ - [ - [], - '', - ], - [ - [1], - '1', - ], - [ - [1, 2, 3], - '1,2,3', - ], - ]; - } - /** * @param string $itemId * @param array|\Magento\Framework\DataObject $item diff --git a/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Grid/MassactionTest.php b/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Grid/MassactionTest.php index 8aeee9cf1249..e11c5ce5d9cf 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Grid/MassactionTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Grid/MassactionTest.php @@ -87,41 +87,6 @@ public function testMassactionDefaultValues() $this->assertFalse($blockEmpty->isAvailable()); } - public function testGetJavaScript() - { - $this->loadLayout(); - - $javascript = $this->_block->getJavaScript(); - - $expectedItemFirst = '#"option_id1":{"label":"Option One",' . - '"url":"http:\\\/\\\/localhost\\\/index\.php\\\/(?:key\\\/([\w\d]+)\\\/)?",' . - '"complete":"Test","id":"option_id1"}#'; - $this->assertRegExp($expectedItemFirst, $javascript); - - $expectedItemSecond = '#"option_id2":{"label":"Option Two",' . - '"url":"http:\\\/\\\/localhost\\\/index\.php\\\/(?:key\\\/([\w\d]+)\\\/)?",' . - '"confirm":"Are you sure\?","id":"option_id2"}#'; - $this->assertRegExp($expectedItemSecond, $javascript); - } - - public function testGetJavaScriptWithAddedItem() - { - $this->loadLayout(); - - $input = [ - 'id' => 'option_id3', - 'label' => 'Option Three', - 'url' => '*/*/option3', - 'block_name' => 'admin.test.grid.massaction.option3', - ]; - $expected = '#"option_id3":{"id":"option_id3","label":"Option Three",' . - '"url":"http:\\\/\\\/localhost\\\/index\.php\\\/(?:key\\\/([\w\d]+)\\\/)?",' . - '"block_name":"admin.test.grid.massaction.option3"}#'; - - $this->_block->addItem($input['id'], $input); - $this->assertRegExp($expected, $this->_block->getJavaScript()); - } - /** * @param string $mageMode * @param int $expectedCount @@ -213,21 +178,4 @@ public function getItemsDataProvider() ] ]; } - - public function testGridContainsMassactionColumn() - { - $this->loadLayout(); - $this->_layout->getBlock('admin.test.grid')->toHtml(); - - $gridMassactionColumn = $this->_layout->getBlock('admin.test.grid') - ->getColumnSet() - ->getChildBlock('massaction'); - - $this->assertNotNull($gridMassactionColumn, 'Massaction column does not exist in the grid column set'); - $this->assertInstanceOf( - \Magento\Backend\Block\Widget\Grid\Column::class, - $gridMassactionColumn, - 'Massaction column is not an instance of \Magento\Backend\Block\Widget\Column' - ); - } } diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Block/Adminhtml/Subscriber/GridTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Block/Adminhtml/Subscriber/GridTest.php new file mode 100644 index 000000000000..ea361a643dee --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Block/Adminhtml/Subscriber/GridTest.php @@ -0,0 +1,103 @@ +objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + + $this->layout = $this->objectManager->create(\Magento\Framework\View\LayoutInterface::class); + $this->layout->getUpdate()->load('newsletter_subscriber_grid'); + $this->layout->generateXml(); + $this->layout->generateElements(); + } + + /** + * Check if mass action block exists. + */ + public function testMassActionBlockExists() + { + $this->assertNotFalse( + $this->getMassActionBlock(), + 'Mass action block does not exist in the grid, or it name was changed.' + ); + } + + /** + * Check if mass action id field is correct. + */ + public function testMassActionFieldIdIsCorrect() + { + $this->assertEquals( + 'subscriber_id', + $this->getMassActionBlock()->getMassactionIdField(), + 'Mass action id field is incorrect.' + ); + } + + /** + * Check if function returns correct result. + * + * @magentoDataFixture Magento/Newsletter/_files/subscribers.php + */ + public function testMassActionBlockContainsCorrectIdList() + { + $this->assertEquals( + implode(',', $this->getAllSubscriberIdList()), + $this->getMassActionBlock()->getGridIdsJson(), + 'Function returns incorrect result.' + ); + } + + /** + * Retrieve mass action block. + * + * @return bool|\Magento\Backend\Block\Widget\Grid\Massaction + */ + private function getMassActionBlock() + { + return $this->layout->getBlock('adminhtml.newslettrer.subscriber.grid.massaction'); + } + + /** + * Retrieve list of id of all subscribers. + * + * @return array + */ + private function getAllSubscriberIdList() + { + /** @var \Magento\Framework\App\ResourceConnection $resourceConnection */ + $resourceConnection = $this->objectManager->get(\Magento\Framework\App\ResourceConnection::class); + $select = $resourceConnection->getConnection() + ->select() + ->from($resourceConnection->getTableName('newsletter_subscriber')) + ->columns(['subscriber_id' => 'subscriber_id']); + + return $resourceConnection->getConnection()->fetchCol($select); + } +} From 3e90069c0f65e2e672379b82bd3684145ede9820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Ca=C3=A7ador?= Date: Fri, 15 Mar 2019 16:47:36 +0000 Subject: [PATCH 27/50] Multishipping checkout agreements now are the same as default checkout agreements --- .../multishipping_checkout_overview.xml | 2 +- .../templates/multishipping_agreements.phtml | 49 ------------------- 2 files changed, 1 insertion(+), 50 deletions(-) delete mode 100644 app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml diff --git a/app/code/Magento/CheckoutAgreements/view/frontend/layout/multishipping_checkout_overview.xml b/app/code/Magento/CheckoutAgreements/view/frontend/layout/multishipping_checkout_overview.xml index 3f742de0177d..122160f1a10c 100644 --- a/app/code/Magento/CheckoutAgreements/view/frontend/layout/multishipping_checkout_overview.xml +++ b/app/code/Magento/CheckoutAgreements/view/frontend/layout/multishipping_checkout_overview.xml @@ -8,7 +8,7 @@ - + diff --git a/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml b/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml deleted file mode 100644 index 3400770f5cee..000000000000 --- a/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml +++ /dev/null @@ -1,49 +0,0 @@ - - -getAgreements()) { - return; -} ?> -
    - - getAgreements() as $agreement): ?> -
  1. -
    getContentHeight() ? ' style="height:' . $agreement->getContentHeight() . '"' : '') ?>> - getIsHtml()):?> - getContent() ?> - - escapeHtml($agreement->getContent())) ?> - -
    - getMode() == \Magento\CheckoutAgreements\Model\AgreementModeOptions::MODE_MANUAL): ?> -
    - - -
    - getMode() == \Magento\CheckoutAgreements\Model\AgreementModeOptions::MODE_AUTO): ?> -
    - getIsHtml() ? $agreement->getCheckboxText() : $block->escapeHtml($agreement->getCheckboxText()) ?> -
    - -
  2. - -
From 96ca3d488bb010959414e391160364ad2264e22b Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun Date: Sat, 16 Mar 2019 08:31:53 +0200 Subject: [PATCH 28/50] While adding the product to Cart when the requested Qty is zero or less then zero, one was unexpectedly added --- .../Model/Cart/AddSimpleProductToCart.php | 5 +++++ .../GraphQl/Quote/AddSimpleProductToCartTest.php | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php index 1b32866ed883..6868ce3f7f1f 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php @@ -67,6 +67,11 @@ public function execute(Quote $cart, array $cartItemData): void { $sku = $this->extractSku($cartItemData); $qty = $this->extractQty($cartItemData); + if ($qty <= 0) { + throw new GraphQlInputException( + __('Please enter a number greater than 0 in this field.') + ); + } $customizableOptions = $this->extractCustomizableOptions($cartItemData); try { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartTest.php index 1e92a2e497be..d9ab8db62a19 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartTest.php @@ -59,6 +59,22 @@ public function testAddSimpleProductToCart() self::assertEquals($sku, $response['addSimpleProductsToCart']['cart']['items'][0]['product']['sku']); } + /** + * @magentoApiDataFixture Magento/Catalog/_files/products.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + * @expectedException \Exception + * @expectedExceptionMessage Please enter a number greater than 0 in this field. + */ + public function testAddSimpleProductToCartWithNegativeQty() + { + $sku = 'simple'; + $qty = -2; + $maskedQuoteId = $this->getMaskedQuoteId(); + + $query = $this->getAddSimpleProductQuery($maskedQuoteId, $sku, $qty); + $this->graphQlQuery($query); + } + /** * @return string */ From b5803c581bf0debfc7e7b45be41cf3e869a4fc20 Mon Sep 17 00:00:00 2001 From: Harniuk Bohdan Date: Sat, 16 Mar 2019 16:44:06 +0200 Subject: [PATCH 29/50] magento/graphql-ce#482: [Test Coverage] 'SetBillingAddressOnCart' functionality --- .../Customer/SetBillingAddressOnCartTest.php | 50 +++++++++++++++++++ .../Guest/SetBillingAddressOnCartTest.php | 49 ++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php index 67a086311d71..02cd428767cc 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php @@ -423,6 +423,39 @@ public function testSetBillingAddressOnNonExistentCart() $this->graphQlQuery($query, [], '', $this->getHeaderMap()); } + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @dataProvider dataProviderSetWithoutRequiredParameters + * @param string $input + * @param string $message + * @throws \Exception + */ + public function testSetBillingAddressWithoutRequiredParameters(string $input, string $message) + { + $maskedQuoteId = $this->assignQuoteToCustomer(); + $input = str_replace('cart_id_value', $maskedQuoteId, $input); + + $query = <<expectExceptionMessage($message); + $this->graphQlQuery($query); + } + /** * Verify the all the whitelisted fields for a New Address Object * @@ -506,4 +539,21 @@ private function assignQuoteToCustomer( $this->quoteResource->save($quote); return $this->quoteIdToMaskedId->execute((int)$quote->getId()); } + + /** + * @return array + */ + public function dataProviderSetWithoutRequiredParameters() + { + return [ + 'missed_billing_address' => [ + 'cart_id: "cart_id_value"', + 'Field SetBillingAddressOnCartInput.billing_address of required type BillingAddressInput! was not provided.', + ], + 'missed_cart_id' => [ + 'billing_address: {}', + 'Required parameter "cart_id" is missing' + ] + ]; + } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php index 27de0d12e413..de07b80b3959 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php @@ -263,6 +263,38 @@ public function testSetBillingAddressOnNonExistentCart() $this->graphQlQuery($query); } + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @dataProvider dataProviderSetWithoutRequiredParameters + * @param string $input + * @param string $message + * @throws \Exception + */ + public function testSetBillingAddressWithoutRequiredParameters(string $input, string $message) + { + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $input = str_replace('cart_id_value', $maskedQuoteId, $input); + + $query = <<expectExceptionMessage($message); + $this->graphQlQuery($query); + } + /** * Verify the all the whitelisted fields for a New Address Object * @@ -297,4 +329,21 @@ private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): str return $this->quoteIdToMaskedId->execute((int)$quote->getId()); } + + /** + * @return array + */ + public function dataProviderSetWithoutRequiredParameters() + { + return [ + 'missed_billing_address' => [ + 'cart_id: "cart_id_value"', + 'Field SetBillingAddressOnCartInput.billing_address of required type BillingAddressInput! was not provided.', + ], + 'missed_cart_id' => [ + 'billing_address: {}', + 'Required parameter "cart_id" is missing' + ] + ]; + } } From 7d998a75d37f1098ab0528e7d5d50736584941cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Ca=C3=A7ador?= Date: Sat, 16 Mar 2019 17:26:49 +0000 Subject: [PATCH 30/50] Restore multishipping_agreements file for backward compatibility --- .../templates/multishipping_agreements.phtml | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml diff --git a/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml b/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml new file mode 100644 index 000000000000..2d0667ee27cf --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml @@ -0,0 +1,49 @@ + + +getAgreements()) { + return; +} ?> +
    + + getAgreements() as $agreement): ?> +
  1. +
    getContentHeight() ? ' style="height:' . $agreement->getContentHeight() . '"' : '') ?>> + getIsHtml()):?> + getContent() ?> + + escapeHtml($agreement->getContent())) ?> + +
    + getMode() == \Magento\CheckoutAgreements\Model\AgreementModeOptions::MODE_MANUAL): ?> +
    + + +
    + getMode() == \Magento\CheckoutAgreements\Model\AgreementModeOptions::MODE_AUTO): ?> +
    + getIsHtml() ? $agreement->getCheckboxText() : $block->escapeHtml($agreement->getCheckboxText()) ?> +
    + +
  2. + +
\ No newline at end of file From 8b0318efede13c8844616a90c5bf67906a835ecf Mon Sep 17 00:00:00 2001 From: Tomash Khamlai Date: Mon, 18 Mar 2019 17:14:49 +0200 Subject: [PATCH 31/50] When setting `background` for labels explicitly the labels in admin will overlap the `::before` pseudo-element. I suggest to remove explicit setting for `label` element or replace `@color-white` to `transparent`. In some cases developers try to hide elements by setting white background and higher z-index for the element with white background. But the proper way of hiding elements is to override templates config or remove elements by JavaScript. --- .../adminhtml/Magento/backend/web/css/source/forms/_fields.less | 1 - 1 file changed, 1 deletion(-) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less index d8fd1db5ced4..5698afdaac7a 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less @@ -545,7 +545,6 @@ & > .admin__field-label { #mix-grid .column(@field-label-grid__column, @field-grid__columns); cursor: pointer; - background: @color-white; left: 0; position: absolute; top: 0; From f4be1612bd4ba6d492cf920e7fdb9514fd5ebe56 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun Date: Mon, 18 Mar 2019 20:59:08 +0200 Subject: [PATCH 32/50] Fix phpcs issue: restore new line at the end of file --- .../view/frontend/templates/multishipping_agreements.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml b/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml index 2d0667ee27cf..3400770f5cee 100644 --- a/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml +++ b/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml @@ -46,4 +46,4 @@ - \ No newline at end of file + From 9b83593fab1f5dcbf351a8ae2fb6e88c815cecb8 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun Date: Mon, 18 Mar 2019 21:01:44 +0200 Subject: [PATCH 33/50] Add deprecated tag for legacy template --- .../view/frontend/templates/multishipping_agreements.phtml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml b/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml index 3400770f5cee..33227f0cdce3 100644 --- a/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml +++ b/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml @@ -4,6 +4,7 @@ * See COPYING.txt for license details. */ +// @deprecated // @codingStandardsIgnoreFile ?> From f5653ab878565e22b56e1c303127b2c7b8209e46 Mon Sep 17 00:00:00 2001 From: Dmytro Yushkin Date: Mon, 18 Mar 2019 15:13:22 -0500 Subject: [PATCH 34/50] MAGETWO-98617: Pager does not work on Newsletter Subscribers Admin page - Added strict type declaration --- .../Magento/Newsletter/Block/Adminhtml/Subscriber/GridTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Block/Adminhtml/Subscriber/GridTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Block/Adminhtml/Subscriber/GridTest.php index ea361a643dee..48d3356525f4 100644 --- a/dev/tests/integration/testsuite/Magento/Newsletter/Block/Adminhtml/Subscriber/GridTest.php +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Block/Adminhtml/Subscriber/GridTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Newsletter\Block\Adminhtml\Subscriber; /** From 6f8d99c750e7aeab547a1d6f34e2d9f7df0be505 Mon Sep 17 00:00:00 2001 From: Alex Taranovsky Date: Wed, 20 Mar 2019 18:04:53 +0200 Subject: [PATCH 35/50] 281 - [Shipping methods] Support of UPS shipping method 1. Test coverage for UPS "Ground" method --- .../Ups/SetUpsShippingMethodsOnCartTest.php | 147 ++++++++++++++++++ .../Ups/_files/enable_ups_shipping_method.php | 20 +++ .../enable_ups_shipping_method_rollback.php | 16 ++ 3 files changed, 183 insertions(+) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Ups/SetUpsShippingMethodsOnCartTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Ups/_files/enable_ups_shipping_method.php create mode 100644 dev/tests/integration/testsuite/Magento/Ups/_files/enable_ups_shipping_method_rollback.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Ups/SetUpsShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Ups/SetUpsShippingMethodsOnCartTest.php new file mode 100644 index 000000000000..ed2e73946b52 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Ups/SetUpsShippingMethodsOnCartTest.php @@ -0,0 +1,147 @@ +quoteResource = $objectManager->get(QuoteResource::class); + $this->quoteFactory = $objectManager->get(QuoteFactory::class); + $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); + $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/Ups/_files/enable_ups_shipping_method.php + */ + public function testSetUpsShippingMethod() + { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, 'test_order_1', 'reserved_order_id'); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$quote->getId()); + $shippingAddressId = (int)$quote->getShippingAddress()->getId(); + + $query = $this->getAddUpsShippingMethodQuery( + $maskedQuoteId, + $shippingAddressId, + self::CARRIER_CODE, + self::CARRIER_METHOD_CODE_GROUND + ); + + $response = $this->sendRequestWithToken($query); + $addressesInformation = $response['setShippingMethodsOnCart']['cart']['shipping_addresses']; + $expectedResult = [ + 'carrier_code' => self::CARRIER_CODE, + 'method_code' => self::CARRIER_METHOD_CODE_GROUND, + 'label' => 'United Parcel Service - Ground', + ]; + self::assertEquals($addressesInformation[0]['selected_shipping_method'], $expectedResult); + } + + /** + * Generates query for setting the specified shipping method on cart + * + * @param int $shippingAddressId + * @param string $maskedQuoteId + * @param string $carrierCode + * @param string $methodCode + * @return string + */ + private function getAddUpsShippingMethodQuery( + string $maskedQuoteId, + int $shippingAddressId, + string $carrierCode, + string $methodCode + ): string { + return <<customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + + return $this->graphQlQuery($query, [], '', $headerMap); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Ups/_files/enable_ups_shipping_method.php b/dev/tests/integration/testsuite/Magento/Ups/_files/enable_ups_shipping_method.php new file mode 100644 index 000000000000..5c6c60866faf --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Ups/_files/enable_ups_shipping_method.php @@ -0,0 +1,20 @@ +get(WriterInterface::class); + +$configWriter->save('carriers/ups/active', 1); + +$scopeConfig = $objectManager->get(ScopeConfigInterface::class); +$scopeConfig->clean(); diff --git a/dev/tests/integration/testsuite/Magento/Ups/_files/enable_ups_shipping_method_rollback.php b/dev/tests/integration/testsuite/Magento/Ups/_files/enable_ups_shipping_method_rollback.php new file mode 100644 index 000000000000..6d7894879f97 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Ups/_files/enable_ups_shipping_method_rollback.php @@ -0,0 +1,16 @@ +create(WriterInterface::class); + +$configWriter->delete('carriers/ups/active'); From 30a2a80802afe5df8c81e948f2c1db4e2ce969b9 Mon Sep 17 00:00:00 2001 From: Alex Taranovsky Date: Wed, 20 Mar 2019 18:58:51 +0200 Subject: [PATCH 36/50] magento/graphql-ce#281: [Shipping methods] Support of UPS shipping method --- .../Magento/GraphQl/Ups/SetUpsShippingMethodsOnCartTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Ups/SetUpsShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Ups/SetUpsShippingMethodsOnCartTest.php index ed2e73946b52..af85b616be69 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Ups/SetUpsShippingMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Ups/SetUpsShippingMethodsOnCartTest.php @@ -15,7 +15,7 @@ use Magento\TestFramework\TestCase\GraphQlAbstract; /** - * Test for setting offline shipping methods on cart + * Test for setting "UPS" shipping method on cart */ class SetUpsShippingMethodsOnCartTest extends GraphQlAbstract { From ef3cc3634fcf4d03030e50e8627a23ccd718b60e Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun Date: Thu, 21 Mar 2019 08:12:17 +0200 Subject: [PATCH 37/50] Missing key model in Wishlist value data --- .../Model/Resolver/WishlistResolver.php | 5 ++++ .../Magento/GraphQl/Wishlist/WishlistTest.php | 30 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistResolver.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistResolver.php index e3a788af2ea7..792928ab61aa 100644 --- a/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistResolver.php +++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistResolver.php @@ -13,6 +13,7 @@ use Magento\Wishlist\Model\ResourceModel\Wishlist as WishlistResourceModel; use Magento\Wishlist\Model\Wishlist; use Magento\Wishlist\Model\WishlistFactory; +use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; /** * Fetches the Wishlist data according to the GraphQL schema @@ -51,6 +52,10 @@ public function resolve( ) { $customerId = $context->getUserId(); + /* Guest checking */ + if (!$customerId && 0 === $customerId) { + throw new GraphQlAuthorizationException(__('The current user cannot perform operations on wishlist')); + } /** @var Wishlist $wishlist */ $wishlist = $this->wishlistFactory->create(); $this->wishlistResource->load($wishlist, $customerId, 'customer_id'); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/WishlistTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/WishlistTest.php index d570fc09b771..4aac5d944593 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/WishlistTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/WishlistTest.php @@ -93,6 +93,36 @@ public function testGetCustomerWishlist(): void $this->assertEquals($wishlistItemProduct->getName(), $response['wishlist']['items'][0]['product']['name']); } + /** + * @expectedException \Exception + * @expectedExceptionMessage The current user cannot perform operations on wishlist + */ + public function testGetGuestWishlist() + { + $query = + <<graphQlQuery($query); + } + /** * @param string $email * @param string $password From a8d9be1c4f617a3ff0312f433f080b67b41ed3b6 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky Date: Thu, 21 Mar 2019 16:04:29 +0200 Subject: [PATCH 38/50] ENGCOM-4357: Static tests fix. --- .../Model/Products/AdaptUrlRewritesToVisibilityAttribute.php | 3 ++- .../ProcessUrlRewriteOnChangeVisibilityObserverTest.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogUrlRewrite/Model/Products/AdaptUrlRewritesToVisibilityAttribute.php b/app/code/Magento/CatalogUrlRewrite/Model/Products/AdaptUrlRewritesToVisibilityAttribute.php index cf1a7ec76f01..f5851cf1e11b 100644 --- a/app/code/Magento/CatalogUrlRewrite/Model/Products/AdaptUrlRewritesToVisibilityAttribute.php +++ b/app/code/Magento/CatalogUrlRewrite/Model/Products/AdaptUrlRewritesToVisibilityAttribute.php @@ -92,7 +92,8 @@ public function execute(array $productIds, int $visibility): void } catch (UrlAlreadyExistsException $e) { throw new UrlAlreadyExistsException( __( - 'Can not change the visibility of the product with SKU equals "%1". URL key "%2" for specified store already exists.', + 'Can not change the visibility of the product with SKU equals "%1". ' + . 'URL key "%2" for specified store already exists.', $product->getSku(), $product->getUrlKey() ), diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeVisibilityObserverTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeVisibilityObserverTest.php index af9979309078..d3f0e9fa2a5a 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeVisibilityObserverTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeVisibilityObserverTest.php @@ -160,7 +160,8 @@ public function testErrorOnDuplicatedUrlKey() $productIds[] = $this->productRepository->get($sku)->getId(); } $this->expectException(UrlAlreadyExistsException::class); - $this->expectExceptionMessage('Can not change the visibility of the product with SKU equals "product2". URL key "product-1" for specified store already exists.'); + $this->expectExceptionMessage('Can not change the visibility of the product with SKU equals "product2". ' + . 'URL key "product-1" for specified store already exists.'); $this->eventManager->dispatch( 'catalog_product_attribute_update_before', From b96435f8d213f5de0eb8352e2e4fd1d1e117895f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Deg=C3=B3rski?= Date: Thu, 21 Mar 2019 20:00:07 +0100 Subject: [PATCH 39/50] magento/magento2#21001 - fix unit tests, by passing currency to number formatter, then it behaves as was expected in test. --- .../Framework/Locale/Test/Unit/FormatTest.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php index 1141f451c13a..526702afb87b 100644 --- a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php +++ b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php @@ -68,15 +68,17 @@ protected function setUp() /** * @param string $localeCode + * @param string $currencyCode * @param array $expectedResult * @dataProvider getPriceFormatDataProvider */ - public function testGetPriceFormat($localeCode, array $expectedResult): void + public function testGetPriceFormat($localeCode, $currencyCode, array $expectedResult): void { $this->scope->expects($this->once()) ->method('getCurrentCurrency') ->willReturn($this->currency); + $this->currency->method('getCode')->willReturn($currencyCode); $result = $this->formatModel->getPriceFormat($localeCode); $intersection = array_intersect_assoc($result, $expectedResult); $this->assertCount(count($expectedResult), $intersection); @@ -89,17 +91,17 @@ public function testGetPriceFormat($localeCode, array $expectedResult): void public function getPriceFormatDataProvider(): array { return [ - ['en_US', ['decimalSymbol' => '.', 'groupSymbol' => ',']], - ['de_DE', ['decimalSymbol' => ',', 'groupSymbol' => '.']], - ['de_CH', ['decimalSymbol' => '.', 'groupSymbol' => '\'']], - ['uk_UA', ['decimalSymbol' => ',', 'groupSymbol' => ' ']] + ['en_US', 'USD', ['decimalSymbol' => '.', 'groupSymbol' => ',']], + ['de_DE', 'EUR', ['decimalSymbol' => ',', 'groupSymbol' => '.']], + ['de_CH', 'CHF', ['decimalSymbol' => '.', 'groupSymbol' => '’']], + ['uk_UA', 'UAH', ['decimalSymbol' => ',', 'groupSymbol' => ' ']] ]; } /** * - * @param mixed $value - * @param float $expected + * @param mixed $value + * @param float $expected * @dataProvider provideNumbers */ public function testGetNumber($value, $expected): void From e0248ea3ce6416c9fea185a958278dd7bda1a951 Mon Sep 17 00:00:00 2001 From: AlexandrKozyr Date: Thu, 21 Mar 2019 22:14:48 +0000 Subject: [PATCH 40/50] [+] added tests for SetPaymentMethodOnCart functionality --- .../Model/Resolver/SetPaymentMethodOnCart.php | 4 +- .../Customer/SetPaymentMethodOnCartTest.php | 48 +++++++++++++++++++ .../Guest/SetPaymentMethodOnCartTest.php | 47 ++++++++++++++++++ 3 files changed, 97 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php index a93c8032c996..d1dcb4a48a76 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php @@ -60,12 +60,12 @@ public function __construct( public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { if (!isset($args['input']['cart_id']) || empty($args['input']['cart_id'])) { - throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); + throw new GraphQlInputException(__('Required parameter "cart_id" is missing.')); } $maskedCartId = $args['input']['cart_id']; if (!isset($args['input']['payment_method']['code']) || empty($args['input']['payment_method']['code'])) { - throw new GraphQlInputException(__('Required parameter "payment_method" is missing')); + throw new GraphQlInputException(__('Required parameter "code" for "payment_method" is missing.')); } $paymentMethodCode = $args['input']['payment_method']['code']; diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php index c7da2144adb9..dc970c3ab589 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php @@ -171,6 +171,54 @@ public function testPaymentMethodOnNonExistentCart() $this->graphQlQuery($query, [], '', $this->getHeaderMap()); } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @param string $input + * @param string $message + * @dataProvider dataProviderSetPaymentMethodWithoutRequiredParameters + */ + public function testSetPaymentMethodWithoutRequiredParameters(string $input, string $message) + { + $query = <<expectExceptionMessage($message); + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + } + /** + * @return array + */ + public function dataProviderSetPaymentMethodWithoutRequiredParameters(): array + { + return [ + 'missed_cart_id' => [ + 'payment_method: {code: "'.Checkmo::PAYMENT_METHOD_CHECKMO_CODE.'"}', + 'Required parameter "cart_id" is missing.' + ], + 'missed_payment_method' => [ + 'cart_id: "test"', + 'Required parameter "code" for "payment_method" is missing.' + ], + 'missed_payment_method_code' => [ + 'cart_id: "test",payment_method: {code: ""}', + 'Required parameter "code" for "payment_method" is missing.' + ], + ]; + } + /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_payment_saved.php */ diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php index 182bbaf61850..6b6bf04b837d 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php @@ -126,6 +126,53 @@ public function testSetPaymentMethodToCustomerCart() $this->graphQlQuery($query); } + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @param string $input + * @param string $message + * @dataProvider dataProviderSetPaymentMethodWithoutRequiredParameters + */ + public function testSetPaymentMethodWithoutRequiredParameters(string $input, string $message) + { + $query = <<expectExceptionMessage($message); + $this->graphQlQuery($query); + } + /** + * @return array + */ + public function dataProviderSetPaymentMethodWithoutRequiredParameters(): array + { + return [ + 'missed_cart_id' => [ + 'payment_method: {code: "'.Checkmo::PAYMENT_METHOD_CHECKMO_CODE.'"}', + 'Required parameter "cart_id" is missing.' + ], + 'missed_payment_method' => [ + 'cart_id: "test"', + 'Required parameter "code" for "payment_method" is missing.' + ], + 'missed_payment_method_code' => [ + 'cart_id: "test",payment_method: {code: ""}', + 'Required parameter "code" for "payment_method" is missing.' + ], + ]; + } + /** * @expectedException \Exception * @expectedExceptionMessage Could not find a cart with ID "non_existent_masked_id" From 8d7b964943eae2996e9542dbfcef87e1044df825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Deg=C3=B3rski?= Date: Thu, 21 Mar 2019 23:58:25 +0100 Subject: [PATCH 41/50] magento/magento2#21001 - fix unit tests, by passing currency to number formatter, then it behaves as was expected in test. --- lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php index 526702afb87b..d5297b1d0c61 100644 --- a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php +++ b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php @@ -93,7 +93,7 @@ public function getPriceFormatDataProvider(): array return [ ['en_US', 'USD', ['decimalSymbol' => '.', 'groupSymbol' => ',']], ['de_DE', 'EUR', ['decimalSymbol' => ',', 'groupSymbol' => '.']], - ['de_CH', 'CHF', ['decimalSymbol' => '.', 'groupSymbol' => '’']], + ['de_CH', 'CHF', ['decimalSymbol' => '.', 'groupSymbol' => '\'']], ['uk_UA', 'UAH', ['decimalSymbol' => ',', 'groupSymbol' => ' ']] ]; } From 57c4db41be4281541fc0745bcfd4f6cbca64ef86 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky Date: Fri, 22 Mar 2019 10:32:50 +0200 Subject: [PATCH 42/50] ENGCOM-4302: Backward compatibility fix. --- .../Model/ResourceModel/Order/Collection.php | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php index 33dfdcffc9a4..f5e73e2cdff2 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php @@ -769,7 +769,7 @@ public function addOrdersCount() */ public function addRevenueToSelect($convertCurrency = false) { - $expr = $this->getTotalsExpression( + $expr = $this->getTotalsExpressionWithDiscountRefunded( !$convertCurrency, $this->getConnection()->getIfNullSql('main_table.base_subtotal_refunded', 0), $this->getConnection()->getIfNullSql('main_table.base_subtotal_canceled', 0), @@ -792,7 +792,7 @@ public function addSumAvgTotals($storeId = 0) /** * calculate average and total amount */ - $expr = $this->getTotalsExpression( + $expr = $this->getTotalsExpressionWithDiscountRefunded( $storeId, $this->getConnection()->getIfNullSql('main_table.base_subtotal_refunded', 0), $this->getConnection()->getIfNullSql('main_table.base_subtotal_canceled', 0), @@ -810,16 +810,40 @@ public function addSumAvgTotals($storeId = 0) } /** - * Get SQL expression for totals + * Get SQL expression for totals. * * @param int $storeId * @param string $baseSubtotalRefunded * @param string $baseSubtotalCanceled - * @param string $baseDiscountRefunded * @param string $baseDiscountCanceled * @return string + * @deprecated + * @see getTotalsExpressionWithDiscountRefunded */ protected function getTotalsExpression( + $storeId, + $baseSubtotalRefunded, + $baseSubtotalCanceled, + $baseDiscountCanceled + ) { + $template = ($storeId != 0) + ? '(main_table.base_subtotal - %2$s - %1$s - ABS(main_table.base_discount_amount) - %3$s)' + : '((main_table.base_subtotal - %1$s - %2$s - ABS(main_table.base_discount_amount) + %3$s) ' + . ' * main_table.base_to_global_rate)'; + return sprintf($template, $baseSubtotalRefunded, $baseSubtotalCanceled, $baseDiscountCanceled); + } + + /** + * Get SQL expression for totals with discount refunded. + * + * @param int $storeId + * @param string $baseSubtotalRefunded + * @param string $baseSubtotalCanceled + * @param string $baseDiscountRefunded + * @param string $baseDiscountCanceled + * @return string + */ + private function getTotalsExpressionWithDiscountRefunded( $storeId, $baseSubtotalRefunded, $baseSubtotalCanceled, From 6fba6865685a41753fce0631bc9461e6632df8c9 Mon Sep 17 00:00:00 2001 From: nmalevanec Date: Fri, 22 Mar 2019 14:09:49 +0200 Subject: [PATCH 43/50] Fix functional test. --- app/code/Magento/Multishipping/view/frontend/web/js/overview.js | 2 +- .../Test/Page/MultishippingCheckoutOverview.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Multishipping/view/frontend/web/js/overview.js b/app/code/Magento/Multishipping/view/frontend/web/js/overview.js index 9b867cd7217b..3a6d73e30497 100644 --- a/app/code/Magento/Multishipping/view/frontend/web/js/overview.js +++ b/app/code/Magento/Multishipping/view/frontend/web/js/overview.js @@ -15,7 +15,7 @@ define([ opacity: 0.5, // CSS opacity for the 'Place Order' button when it's clicked and then disabled. pleaseWaitLoader: 'span.please-wait', // 'Submitting order information...' Ajax loader. placeOrderSubmit: 'button[type="submit"]', // The 'Place Order' button. - agreements: '#checkout-agreements' // Container for all of the checkout agreements and terms/conditions + agreements: '.checkout-agreements' // Container for all of the checkout agreements and terms/conditions }, /** diff --git a/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/Page/MultishippingCheckoutOverview.xml b/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/Page/MultishippingCheckoutOverview.xml index d304d305a726..a266b09278dd 100644 --- a/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/Page/MultishippingCheckoutOverview.xml +++ b/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/Page/MultishippingCheckoutOverview.xml @@ -7,6 +7,6 @@ --> - + From 3825545a5dfa1ddc7ae2193ff03e4e54e0c12f30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Deg=C3=B3rski?= Date: Fri, 22 Mar 2019 16:06:48 +0100 Subject: [PATCH 44/50] magento/magento2#21001 - make swiss group sympbol icu version dependent --- lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php index d5297b1d0c61..73a029a5a141 100644 --- a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php +++ b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php @@ -90,10 +90,11 @@ public function testGetPriceFormat($localeCode, $currencyCode, array $expectedRe */ public function getPriceFormatDataProvider(): array { + $swissGroupSymbol = INTL_ICU_VERSION >= 59.1 ? '’' : '\''; return [ ['en_US', 'USD', ['decimalSymbol' => '.', 'groupSymbol' => ',']], ['de_DE', 'EUR', ['decimalSymbol' => ',', 'groupSymbol' => '.']], - ['de_CH', 'CHF', ['decimalSymbol' => '.', 'groupSymbol' => '\'']], + ['de_CH', 'CHF', ['decimalSymbol' => '.', 'groupSymbol' => $swissGroupSymbol]], ['uk_UA', 'UAH', ['decimalSymbol' => ',', 'groupSymbol' => ' ']] ]; } From 6e80c692bdd0e7af35e4aee738923edf083559d3 Mon Sep 17 00:00:00 2001 From: Javier Villanueva Date: Fri, 22 Mar 2019 15:54:05 +0000 Subject: [PATCH 45/50] Trigger contentUpdate on reviews load --- app/code/Magento/Review/view/frontend/web/js/process-reviews.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Review/view/frontend/web/js/process-reviews.js b/app/code/Magento/Review/view/frontend/web/js/process-reviews.js index d1c40959e3ec..88c61fa38af3 100644 --- a/app/code/Magento/Review/view/frontend/web/js/process-reviews.js +++ b/app/code/Magento/Review/view/frontend/web/js/process-reviews.js @@ -20,7 +20,7 @@ define([ showLoader: false, loaderContext: $('.product.data.items') }).done(function (data) { - $('#product-review-container').html(data); + $('#product-review-container').html(data).trigger('contentUpdated'); $('[data-role="product-review"] .pages a').each(function (index, element) { $(element).click(function (event) { //eslint-disable-line max-nested-callbacks processReviews($(element).attr('href'), true); From 44ddbab42b47a16bf3f1491a5658c2ba255c81cf Mon Sep 17 00:00:00 2001 From: nmalevanec Date: Mon, 25 Mar 2019 11:28:02 +0200 Subject: [PATCH 46/50] Fix functional test. --- .../tests/app/Magento/Review/Test/TestCase/NavigateMenuTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/Review/Test/TestCase/NavigateMenuTest.xml b/dev/tests/functional/tests/app/Magento/Review/Test/TestCase/NavigateMenuTest.xml index 8445a21604cd..4d96ebd2edbe 100644 --- a/dev/tests/functional/tests/app/Magento/Review/Test/TestCase/NavigateMenuTest.xml +++ b/dev/tests/functional/tests/app/Magento/Review/Test/TestCase/NavigateMenuTest.xml @@ -8,7 +8,7 @@ - Marketing > Reviews + Marketing > All Reviews Reviews From 73088cdba04c5783165c0f2a7c20bae78c370ec2 Mon Sep 17 00:00:00 2001 From: Valerii Naida Date: Mon, 25 Mar 2019 13:17:47 -0500 Subject: [PATCH 47/50] GraphQL-281: [Shipping methods] Support of UPS shipping method --- .../Magento/GraphQl/Ups/SetUpsShippingMethodsOnCartTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Ups/SetUpsShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Ups/SetUpsShippingMethodsOnCartTest.php index af85b616be69..463f2c4af101 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Ups/SetUpsShippingMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Ups/SetUpsShippingMethodsOnCartTest.php @@ -83,8 +83,8 @@ public function testSetUpsShippingMethod() $addressesInformation = $response['setShippingMethodsOnCart']['cart']['shipping_addresses']; $expectedResult = [ 'carrier_code' => self::CARRIER_CODE, - 'method_code' => self::CARRIER_METHOD_CODE_GROUND, - 'label' => 'United Parcel Service - Ground', + 'method_code' => self::CARRIER_METHOD_CODE_GROUND, + 'label' => 'United Parcel Service - Ground', ]; self::assertEquals($addressesInformation[0]['selected_shipping_method'], $expectedResult); } From 5aa062cb4801d4311ddcde2c59b63c2f4dc16e49 Mon Sep 17 00:00:00 2001 From: Valerii Naida Date: Mon, 25 Mar 2019 13:33:16 -0500 Subject: [PATCH 48/50] GraphQL-483: [Test Coverage] 'SetPaymentMethodOnCart' functionality --- .../GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php | 4 ++-- .../GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php index dc970c3ab589..51c48f5041dd 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php @@ -205,7 +205,7 @@ public function dataProviderSetPaymentMethodWithoutRequiredParameters(): array { return [ 'missed_cart_id' => [ - 'payment_method: {code: "'.Checkmo::PAYMENT_METHOD_CHECKMO_CODE.'"}', + 'payment_method: {code: "' . Checkmo::PAYMENT_METHOD_CHECKMO_CODE . '"}', 'Required parameter "cart_id" is missing.' ], 'missed_payment_method' => [ @@ -213,7 +213,7 @@ public function dataProviderSetPaymentMethodWithoutRequiredParameters(): array 'Required parameter "code" for "payment_method" is missing.' ], 'missed_payment_method_code' => [ - 'cart_id: "test",payment_method: {code: ""}', + 'cart_id: "test", payment_method: {code: ""}', 'Required parameter "code" for "payment_method" is missing.' ], ]; diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php index 6b6bf04b837d..017b85ba17b9 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php @@ -159,7 +159,7 @@ public function dataProviderSetPaymentMethodWithoutRequiredParameters(): array { return [ 'missed_cart_id' => [ - 'payment_method: {code: "'.Checkmo::PAYMENT_METHOD_CHECKMO_CODE.'"}', + 'payment_method: {code: "' . Checkmo::PAYMENT_METHOD_CHECKMO_CODE . '"}', 'Required parameter "cart_id" is missing.' ], 'missed_payment_method' => [ @@ -167,7 +167,7 @@ public function dataProviderSetPaymentMethodWithoutRequiredParameters(): array 'Required parameter "code" for "payment_method" is missing.' ], 'missed_payment_method_code' => [ - 'cart_id: "test",payment_method: {code: ""}', + 'cart_id: "test", payment_method: {code: ""}', 'Required parameter "code" for "payment_method" is missing.' ], ]; From 18cafdc0aaee73fe6320e0ad8e736d563d40ca51 Mon Sep 17 00:00:00 2001 From: Valerii Naida Date: Mon, 25 Mar 2019 15:55:34 -0500 Subject: [PATCH 49/50] GraphQL-482: [Test Coverage] 'SetBillingAddressOnCart' functionality --- .../GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php | 3 ++- .../GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php index 02cd428767cc..f79e6b8211b0 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php @@ -548,7 +548,8 @@ public function dataProviderSetWithoutRequiredParameters() return [ 'missed_billing_address' => [ 'cart_id: "cart_id_value"', - 'Field SetBillingAddressOnCartInput.billing_address of required type BillingAddressInput! was not provided.', + 'Field SetBillingAddressOnCartInput.billing_address of required type BillingAddressInput!' + . 'was not provided.', ], 'missed_cart_id' => [ 'billing_address: {}', diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php index de07b80b3959..81969cf610f7 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php @@ -338,7 +338,8 @@ public function dataProviderSetWithoutRequiredParameters() return [ 'missed_billing_address' => [ 'cart_id: "cart_id_value"', - 'Field SetBillingAddressOnCartInput.billing_address of required type BillingAddressInput! was not provided.', + 'Field SetBillingAddressOnCartInput.billing_address of required type BillingAddressInput!' + . 'was not provided.', ], 'missed_cart_id' => [ 'billing_address: {}', From 594e5563d671eb34409b7da35e80f4d35bf7ced9 Mon Sep 17 00:00:00 2001 From: Valerii Naida Date: Mon, 25 Mar 2019 16:41:47 -0500 Subject: [PATCH 50/50] GraphQL-482: [Test Coverage] 'SetBillingAddressOnCart' functionality --- .../GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php | 2 +- .../Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php index f79e6b8211b0..55a32f7cdf65 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php @@ -549,7 +549,7 @@ public function dataProviderSetWithoutRequiredParameters() 'missed_billing_address' => [ 'cart_id: "cart_id_value"', 'Field SetBillingAddressOnCartInput.billing_address of required type BillingAddressInput!' - . 'was not provided.', + . ' was not provided.', ], 'missed_cart_id' => [ 'billing_address: {}', diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php index 81969cf610f7..a2f092b9e0e1 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php @@ -339,7 +339,7 @@ public function dataProviderSetWithoutRequiredParameters() 'missed_billing_address' => [ 'cart_id: "cart_id_value"', 'Field SetBillingAddressOnCartInput.billing_address of required type BillingAddressInput!' - . 'was not provided.', + . ' was not provided.', ], 'missed_cart_id' => [ 'billing_address: {}',