From bf024c1c1f3b86a7c3dd362b78fb124f58f22b96 Mon Sep 17 00:00:00 2001 From: Danny Verkade Date: Mon, 15 Oct 2018 20:31:52 +0200 Subject: [PATCH 001/265] Fix for issue magento/magento2#18630 --- .../Ui/view/base/web/js/form/element/post-code.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js b/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js index 911574a0fb43..3de99aa9af1a 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js @@ -20,6 +20,21 @@ define([ } }, + /** + * Initializes observable properties of instance + * + * @returns {Abstract} Chainable. + */ + initObservable: function () { + this._super(); + + this.value.equalityComparer = function(a, b) { + return (!a && !b) || (a == b); + }; + + return this; + }, + /** * @param {String} value */ From df90f67822ea0f1c5765b512c1d7613532a41a11 Mon Sep 17 00:00:00 2001 From: Danny Verkade Date: Tue, 16 Oct 2018 15:53:09 +0200 Subject: [PATCH 002/265] Fix coding standard errors. --- .../Magento/Ui/view/base/web/js/form/element/post-code.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js b/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js index 3de99aa9af1a..4c684f16b767 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js @@ -28,8 +28,8 @@ define([ initObservable: function () { this._super(); - this.value.equalityComparer = function(a, b) { - return (!a && !b) || (a == b); + this.value.equalityComparer = function (oldValue, newValue) { + return !oldValue && !newValue || oldValue === newValue; }; return this; From 6257fe8a3938c59e207b51da30202e1f5a65eb76 Mon Sep 17 00:00:00 2001 From: Danny Verkade Date: Tue, 16 Oct 2018 22:11:15 +0200 Subject: [PATCH 003/265] Fixed jsdoc-block definition error --- .../Magento/Ui/view/base/web/js/form/element/post-code.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js b/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js index 4c684f16b767..ec399914ed02 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js @@ -28,6 +28,11 @@ define([ initObservable: function () { this._super(); + /** + * equalityComparer function + * + * @returns boolean. + */ this.value.equalityComparer = function (oldValue, newValue) { return !oldValue && !newValue || oldValue === newValue; }; From 648dcf43278863acf5c029bbd7cb94e569ea54e6 Mon Sep 17 00:00:00 2001 From: Erfan Date: Wed, 19 Dec 2018 16:36:15 +0800 Subject: [PATCH 004/265] Added custom_options file upload directory to .gitignore. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 68d38d9ca781..8a15fbacaf63 100644 --- a/.gitignore +++ b/.gitignore @@ -48,6 +48,7 @@ atlassian* /pub/media/import/* !/pub/media/import/.htaccess /pub/media/logo/* +/pub/media/custom_options/* /pub/media/theme/* /pub/media/theme_customization/* !/pub/media/theme_customization/.htaccess From 82970b8c9bd9b7edbdfe77b05f6a73927d2bda6b Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk Date: Thu, 20 Dec 2018 17:10:17 -0600 Subject: [PATCH 005/265] MQE-1069: Migrate Sample Data tests from MTF to MFTF --- ...tProductImagesOnProductPageActionGroup.xml | 20 +++++++++++++++++++ ...ertProductNameOnProductPageActionGroup.xml | 15 ++++++++++++++ ...rtProductPriceOnProductPageActionGroup.xml | 15 ++++++++++++++ ...sertProductSkuOnProductPageActionGroup.xml | 15 ++++++++++++++ .../StorefrontOpenProductPageActionGroup.xml | 16 +++++++++++++++ .../StorefrontProductInfoMainSection.xml | 1 + .../Section/StorefrontProductMediaSection.xml | 4 ++++ ...ertCustomerAccountPageTitleActionGroup.xml | 16 +++++++++++++++ .../LoginToStorefrontActionGroup.xml | 1 + ...NavigateThroughCustomerTabsActionGroup.xml | 17 ++++++++++++++++ .../OpenMyAccountPageActionGroup.xml | 15 ++++++++++++++ .../LoggedInCustomerHeaderLinksSection.xml | 17 ++++++++++++++++ .../StorefrontCustomerAccountMainSection.xml | 14 +++++++++++++ .../StorefrontCustomerSidebarSection.xml | 2 +- 14 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductImagesOnProductPageActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductNameOnProductPageActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductPriceOnProductPageActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductSkuOnProductPageActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontOpenProductPageActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertCustomerAccountPageTitleActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateThroughCustomerTabsActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenMyAccountPageActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/LoggedInCustomerHeaderLinksSection.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountMainSection.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductImagesOnProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductImagesOnProductPageActionGroup.xml new file mode 100644 index 000000000000..e049c7430871 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductImagesOnProductPageActionGroup.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductNameOnProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductNameOnProductPageActionGroup.xml new file mode 100644 index 000000000000..6cb156723b28 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductNameOnProductPageActionGroup.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductPriceOnProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductPriceOnProductPageActionGroup.xml new file mode 100644 index 000000000000..3c62ef89e584 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductPriceOnProductPageActionGroup.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductSkuOnProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductSkuOnProductPageActionGroup.xml new file mode 100644 index 000000000000..85d3927a6d6d --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductSkuOnProductPageActionGroup.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontOpenProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontOpenProductPageActionGroup.xml new file mode 100644 index 000000000000..f5fabae5fc4c --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontOpenProductPageActionGroup.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml index b93a70559fc4..63b08b58af6c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -13,6 +13,7 @@ + diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml index 83c3ca534860..a90525e40b69 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml @@ -9,6 +9,10 @@
+ + + +
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertCustomerAccountPageTitleActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertCustomerAccountPageTitleActionGroup.xml new file mode 100644 index 000000000000..132b5ca81886 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertCustomerAccountPageTitleActionGroup.xml @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml index 7be36ffbd9bc..2704f0af3816 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml @@ -15,5 +15,6 @@ + diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateThroughCustomerTabsActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateThroughCustomerTabsActionGroup.xml new file mode 100644 index 000000000000..5591bee52969 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateThroughCustomerTabsActionGroup.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenMyAccountPageActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenMyAccountPageActionGroup.xml new file mode 100644 index 000000000000..6ca0f612deea --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenMyAccountPageActionGroup.xml @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/app/code/Magento/Customer/Test/Mftf/Section/LoggedInCustomerHeaderLinksSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/LoggedInCustomerHeaderLinksSection.xml new file mode 100644 index 000000000000..907551e932fc --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/LoggedInCustomerHeaderLinksSection.xml @@ -0,0 +1,17 @@ + + + + +
+ + + + +
+
diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountMainSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountMainSection.xml new file mode 100644 index 000000000000..3a4329969ae2 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountMainSection.xml @@ -0,0 +1,14 @@ + + + + +
+ +
+
diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSidebarSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSidebarSection.xml index 748219303109..56be7a12d7de 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSidebarSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSidebarSection.xml @@ -9,6 +9,6 @@
- +
From 3ee104d59b0dbe9f843bcca162ce24cef6ac09fa Mon Sep 17 00:00:00 2001 From: Jeroen van Leusden Date: Wed, 12 Dec 2018 15:29:34 +0100 Subject: [PATCH 006/265] 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 007/265] 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 5955ded71ff1367fee224278a1069e09ef711efb Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk Date: Thu, 17 Jan 2019 11:57:51 -0600 Subject: [PATCH 008/265] MQE-1069: Migrate Sample Data tests from MTF to MFTF --- .../StorefrontAssertProductImagesOnProductPageActionGroup.xml | 1 + .../Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductImagesOnProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductImagesOnProductPageActionGroup.xml index e049c7430871..41bfbe608a31 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductImagesOnProductPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductImagesOnProductPageActionGroup.xml @@ -10,6 +10,7 @@ + diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml index a90525e40b69..28f0c1bbf889 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml @@ -9,6 +9,7 @@
+ From 1f921080c66b7c8392140dc1991c464abe5c10da Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk Date: Thu, 17 Jan 2019 12:53:21 -0600 Subject: [PATCH 009/265] MQE-1069: Migrate Sample Data tests from MTF to MFTF --- .../Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml index 28f0c1bbf889..3bf1273f05cc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml @@ -9,7 +9,7 @@
- + From 3b2bc5b5db0b98cc1a6327cce92daf5318ba79f7 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk Date: Thu, 17 Jan 2019 14:50:29 -0600 Subject: [PATCH 010/265] MQE-1069: Migrate Sample Data tests from MTF to MFTF - Update test case ids --- .../StorefrontAssertProductImagesOnProductPageActionGroup.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductImagesOnProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductImagesOnProductPageActionGroup.xml index 41bfbe608a31..1bb7c179dfca 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductImagesOnProductPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductImagesOnProductPageActionGroup.xml @@ -17,5 +17,6 @@ + From 24382e3eb968d13e4a86955cbdc2966b989afe1c Mon Sep 17 00:00:00 2001 From: vitaliyboyko Date: Tue, 29 Jan 2019 19:34:06 +0200 Subject: [PATCH 011/265] 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 012/265] 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 013/265] 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 014/265] 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 015/265] 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 016/265] 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 48126f0458b6d493792089237cfed8ec88d1d15d Mon Sep 17 00:00:00 2001 From: pganapat Date: Tue, 5 Feb 2019 16:35:12 -0600 Subject: [PATCH 017/265] MC-5784: Image fields using imageUploader UIComponent cannot use gallery image - Modified File Model to suit Image-uploader config - Added coverage through MFTF MC-13832 --- .../Theme/Model/Design/Backend/File.php | 25 +++++++-- .../Mftf/Section/AdminDesignConfigSection.xml | 7 +++ .../Test/AdminMediaGalleryImageUploadTest.xml | 56 +++++++++++++++++++ 3 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 app/code/Magento/Theme/Test/Mftf/Test/AdminMediaGalleryImageUploadTest.xml diff --git a/app/code/Magento/Theme/Model/Design/Backend/File.php b/app/code/Magento/Theme/Model/Design/Backend/File.php index b37628e54aa3..8bb73fbb3511 100644 --- a/app/code/Magento/Theme/Model/Design/Backend/File.php +++ b/app/code/Magento/Theme/Model/Design/Backend/File.php @@ -88,23 +88,27 @@ public function beforeSave() { $values = $this->getValue(); $value = reset($values) ?: []; - if (!isset($value['file'])) { + $file = $value['file'] ?? $value['name']; + if (!isset($file)) { throw new LocalizedException( __('%1 does not contain field \'file\'', $this->getData('field_config/field')) ); } if (isset($value['exists'])) { - $this->setValue($value['file']); + $this->setValue($file); return $this; } - $filename = basename($value['file']); + $filename = basename($file); + $relativeMediaUrl = $this->getRelativeMediaPath($value['url']); + $tmpMediaPath = $this->_mediaDirectory->isFile($relativeMediaUrl) ? + $relativeMediaUrl : $this->getTmpMediaPath($filename); $result = $this->_mediaDirectory->copyFile( - $this->getTmpMediaPath($filename), + $tmpMediaPath, $this->_getUploadDir() . '/' . $filename ); if ($result) { - $this->_mediaDirectory->delete($this->getTmpMediaPath($filename)); + $this->_mediaDirectory->delete($tmpMediaPath); if ($this->_addWhetherScopeInfo()) { $filename = $this->_prependScopeInfo($filename); } @@ -231,4 +235,15 @@ private function getMime() } return $this->mime; } + + /** + * Get Relative Media Path + * + * @param string $path + * @return string + */ + private function getRelativeMediaPath(string $path) + { + return str_replace('/pub/media', '', $path); + } } diff --git a/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml b/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml index 0aa2f7f35218..3f548850b3c0 100644 --- a/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml +++ b/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml @@ -14,5 +14,12 @@ + + + + + + +
diff --git a/app/code/Magento/Theme/Test/Mftf/Test/AdminMediaGalleryImageUploadTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/AdminMediaGalleryImageUploadTest.xml new file mode 100644 index 000000000000..2cd74c53baa1 --- /dev/null +++ b/app/code/Magento/Theme/Test/Mftf/Test/AdminMediaGalleryImageUploadTest.xml @@ -0,0 +1,56 @@ + + + + + + + + + + <description value="Admin should be able to use Image Uploader to add Gallery Images"/> + <severity value="MAJOR"/> + <testCaseId value="MC-13832"/> + <group value="Content"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + </after> + <amOnPage url="{{DesignConfigPage.url}}" stepKey="navigateToDesignConfigPage" /> + <waitForPageLoad stepKey="waitForPageload1"/> + <click selector="{{AdminDesignConfigSection.scopeRow('3')}}" stepKey="editStoreView"/> + <waitForPageLoad stepKey="waitForPageload2"/> + <scrollTo selector="{{AdminDesignConfigSection.htmlHeaderSection}}" stepKey="scrollToHtmlHeadSection"/> + <click selector="{{AdminDesignConfigSection.htmlHeaderSection}}" stepKey="openHtmlHeadSection"/> + + <click selector="{{AdminDesignConfigSection.selectFromGalleryByFieldsetName('Head')}}" stepKey="openMediaGallery"/> + <wait time="3" stepKey="waitForAddingdSelectedImages"/> + <attachFile selector="{{AdminDesignConfigSection.imageUploadFromMediaGallery}}" userInput="adobe-base.jpg" stepKey="attachFile1"/> + <wait time="3" stepKey="waitForAddingSelectedImages"/> + <click selector="{{AdminDesignConfigSection.addSelectedFromMediaGallery}}" stepKey="addSelectedImages"/> + <waitForElementVisible selector="{{AdminDesignConfigSection.imageUploadPreviewByFieldsetName('Head')}}" stepKey="waitForPreviewImage"/> + <wait time="3" stepKey="waitForWrapperToClose"/> + <click selector="{{AdminDesignConfigSection.saveConfiguration}}" stepKey="saveConfiguration"/> + <waitForElementVisible selector="{{AdminDesignConfigSection.successNotification}}" stepKey="waitForSuccessNotification"/> + <waitForPageLoad stepKey="waitForPageloadSuccess"/> + + <click selector="{{AdminDesignConfigSection.scopeRow('3')}}" stepKey="editStoreView2"/> + <waitForPageLoad stepKey="waitForPageload3"/> + <scrollTo selector="{{AdminDesignConfigSection.htmlHeaderSection}}" stepKey="scrollToHtmlHeadSection2"/> + <click selector="{{AdminDesignConfigSection.htmlHeaderSection}}" stepKey="openHtmlHeadSection2"/> + + <click selector="{{AdminDesignConfigSection.useDefaultByFieldsetName('Head')}}" stepKey="clickUseDefault"/> + <waitForElementVisible selector="{{AdminDesignConfigSection.imageUploadPreviewByFieldsetName('Head')}}" stepKey="waitForPreviewImage2"/> + <click selector="{{AdminDesignConfigSection.saveConfiguration}}" stepKey="saveConfiguration2"/> + <waitForElementVisible selector="{{AdminDesignConfigSection.successNotification}}" stepKey="waitForSuccessNotification2"/> + <waitForPageLoad stepKey="waitForPageloadSuccess2"/> + </test> +</tests> From 7ce57eb86e068ece2c3caa212a3d41344252bfdb Mon Sep 17 00:00:00 2001 From: pganapat <prabhuramgr28493@gmail.com> Date: Wed, 6 Feb 2019 09:30:59 -0600 Subject: [PATCH 018/265] MC-5784: Image fields using imageUploader UIComponent cannot use gallery image - Fixed static, unit and mftf tests. --- app/code/Magento/Theme/Model/Design/Backend/File.php | 11 +++++++++-- .../Mftf/Test/AdminMediaGalleryImageUploadTest.xml | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Theme/Model/Design/Backend/File.php b/app/code/Magento/Theme/Model/Design/Backend/File.php index 8bb73fbb3511..74127484e23b 100644 --- a/app/code/Magento/Theme/Model/Design/Backend/File.php +++ b/app/code/Magento/Theme/Model/Design/Backend/File.php @@ -22,6 +22,8 @@ use Magento\Theme\Model\Design\Config\FileUploader\FileProcessor; /** + * File Backend + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class File extends BackendFile @@ -88,7 +90,7 @@ public function beforeSave() { $values = $this->getValue(); $value = reset($values) ?: []; - $file = $value['file'] ?? $value['name']; + $file = $value['file'] ?? $value['name'] ?? null; if (!isset($file)) { throw new LocalizedException( __('%1 does not contain field \'file\'', $this->getData('field_config/field')) @@ -121,7 +123,10 @@ public function beforeSave() } /** - * @return array + * After Load + * + * @return File + * @throws LocalizedException */ public function afterLoad() { @@ -170,6 +175,8 @@ protected function getUploadDirPath($uploadDir) } /** + * Get Value + * * @return array */ public function getValue() diff --git a/app/code/Magento/Theme/Test/Mftf/Test/AdminMediaGalleryImageUploadTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/AdminMediaGalleryImageUploadTest.xml index 2cd74c53baa1..de2363da3e7a 100644 --- a/app/code/Magento/Theme/Test/Mftf/Test/AdminMediaGalleryImageUploadTest.xml +++ b/app/code/Magento/Theme/Test/Mftf/Test/AdminMediaGalleryImageUploadTest.xml @@ -49,6 +49,7 @@ <click selector="{{AdminDesignConfigSection.useDefaultByFieldsetName('Head')}}" stepKey="clickUseDefault"/> <waitForElementVisible selector="{{AdminDesignConfigSection.imageUploadPreviewByFieldsetName('Head')}}" stepKey="waitForPreviewImage2"/> + <wait time="3" stepKey="waitForWrapperToClose2"/> <click selector="{{AdminDesignConfigSection.saveConfiguration}}" stepKey="saveConfiguration2"/> <waitForElementVisible selector="{{AdminDesignConfigSection.successNotification}}" stepKey="waitForSuccessNotification2"/> <waitForPageLoad stepKey="waitForPageloadSuccess2"/> From 8bb2a3a4fd90914d12fd26204cb9490c6c044abe Mon Sep 17 00:00:00 2001 From: pganapat <prabhuramgr28493@gmail.com> Date: Wed, 6 Feb 2019 10:53:31 -0600 Subject: [PATCH 019/265] MC-5784: Image fields using imageUploader UIComponent cannot use gallery image - Fixed flaky mftf test. --- .../Theme/Test/Mftf/Test/AdminMediaGalleryImageUploadTest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Theme/Test/Mftf/Test/AdminMediaGalleryImageUploadTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/AdminMediaGalleryImageUploadTest.xml index de2363da3e7a..f9f6e6b37c5d 100644 --- a/app/code/Magento/Theme/Test/Mftf/Test/AdminMediaGalleryImageUploadTest.xml +++ b/app/code/Magento/Theme/Test/Mftf/Test/AdminMediaGalleryImageUploadTest.xml @@ -48,7 +48,6 @@ <click selector="{{AdminDesignConfigSection.htmlHeaderSection}}" stepKey="openHtmlHeadSection2"/> <click selector="{{AdminDesignConfigSection.useDefaultByFieldsetName('Head')}}" stepKey="clickUseDefault"/> - <waitForElementVisible selector="{{AdminDesignConfigSection.imageUploadPreviewByFieldsetName('Head')}}" stepKey="waitForPreviewImage2"/> <wait time="3" stepKey="waitForWrapperToClose2"/> <click selector="{{AdminDesignConfigSection.saveConfiguration}}" stepKey="saveConfiguration2"/> <waitForElementVisible selector="{{AdminDesignConfigSection.successNotification}}" stepKey="waitForSuccessNotification2"/> From 3f84d5fed87f4b02211d63d3a4700b1a368074eb Mon Sep 17 00:00:00 2001 From: Krissy Hiserote <khiserote@magento.com> Date: Fri, 8 Feb 2019 12:17:30 -0600 Subject: [PATCH 020/265] MC-5784: Image fields using imageUploader UIComponent cannot use gallery image - fix return type and mftf test --- .../Theme/Model/Design/Backend/File.php | 2 +- ...signConfigMediaGalleryImageUploadTest.xml} | 26 +++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) rename app/code/Magento/Theme/Test/Mftf/Test/{AdminMediaGalleryImageUploadTest.xml => AdminDesignConfigMediaGalleryImageUploadTest.xml} (75%) diff --git a/app/code/Magento/Theme/Model/Design/Backend/File.php b/app/code/Magento/Theme/Model/Design/Backend/File.php index 74127484e23b..81217430c083 100644 --- a/app/code/Magento/Theme/Model/Design/Backend/File.php +++ b/app/code/Magento/Theme/Model/Design/Backend/File.php @@ -249,7 +249,7 @@ private function getMime() * @param string $path * @return string */ - private function getRelativeMediaPath(string $path) + private function getRelativeMediaPath(string $path): string { return str_replace('/pub/media', '', $path); } diff --git a/app/code/Magento/Theme/Test/Mftf/Test/AdminMediaGalleryImageUploadTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml similarity index 75% rename from app/code/Magento/Theme/Test/Mftf/Test/AdminMediaGalleryImageUploadTest.xml rename to app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml index f9f6e6b37c5d..40b80be48efd 100644 --- a/app/code/Magento/Theme/Test/Mftf/Test/AdminMediaGalleryImageUploadTest.xml +++ b/app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminMediaGalleryImageUploadTest"> + <test name="AdminDesignConfigMediaGalleryImageUploadTest"> <annotations> <features value="Content"/> <stories value="Content"/> @@ -24,31 +24,35 @@ <after> <actionGroup ref="logout" stepKey="logoutOfAdmin"/> </after> + <!--Edit Store View--> + <comment userInput="Edit Store View" stepKey="editStoreViewComment"/> <amOnPage url="{{DesignConfigPage.url}}" stepKey="navigateToDesignConfigPage" /> <waitForPageLoad stepKey="waitForPageload1"/> <click selector="{{AdminDesignConfigSection.scopeRow('3')}}" stepKey="editStoreView"/> <waitForPageLoad stepKey="waitForPageload2"/> <scrollTo selector="{{AdminDesignConfigSection.htmlHeaderSection}}" stepKey="scrollToHtmlHeadSection"/> <click selector="{{AdminDesignConfigSection.htmlHeaderSection}}" stepKey="openHtmlHeadSection"/> - + <!--Upload Image--> + <comment userInput="Upload Image" stepKey="uploadImageComment"/> <click selector="{{AdminDesignConfigSection.selectFromGalleryByFieldsetName('Head')}}" stepKey="openMediaGallery"/> - <wait time="3" stepKey="waitForAddingdSelectedImages"/> - <attachFile selector="{{AdminDesignConfigSection.imageUploadFromMediaGallery}}" userInput="adobe-base.jpg" stepKey="attachFile1"/> - <wait time="3" stepKey="waitForAddingSelectedImages"/> - <click selector="{{AdminDesignConfigSection.addSelectedFromMediaGallery}}" stepKey="addSelectedImages"/> - <waitForElementVisible selector="{{AdminDesignConfigSection.imageUploadPreviewByFieldsetName('Head')}}" stepKey="waitForPreviewImage"/> - <wait time="3" stepKey="waitForWrapperToClose"/> + <actionGroup ref="VerifyMediaGalleryStorageActions" stepKey="VerifyMediaGalleryStorageBtn"/> + <actionGroup ref="attachImage" stepKey="SelectImageFromMediaStorage"> + <argument name="Image" value="ImageUpload3"/> + </actionGroup> + <actionGroup ref="saveImage" stepKey="insertImage"/> <click selector="{{AdminDesignConfigSection.saveConfiguration}}" stepKey="saveConfiguration"/> <waitForElementVisible selector="{{AdminDesignConfigSection.successNotification}}" stepKey="waitForSuccessNotification"/> <waitForPageLoad stepKey="waitForPageloadSuccess"/> - + <!--Edit Store View--> + <comment userInput="Edit Store View" stepKey="editStoreViewComment2"/> <click selector="{{AdminDesignConfigSection.scopeRow('3')}}" stepKey="editStoreView2"/> <waitForPageLoad stepKey="waitForPageload3"/> <scrollTo selector="{{AdminDesignConfigSection.htmlHeaderSection}}" stepKey="scrollToHtmlHeadSection2"/> <click selector="{{AdminDesignConfigSection.htmlHeaderSection}}" stepKey="openHtmlHeadSection2"/> - + <!--Save Default Configuration--> + <comment userInput="Save Default Configuration" stepKey="saveDefaultConfigurationComment"/> <click selector="{{AdminDesignConfigSection.useDefaultByFieldsetName('Head')}}" stepKey="clickUseDefault"/> - <wait time="3" stepKey="waitForWrapperToClose2"/> + <waitForElementVisible selector="{{AdminDesignConfigSection.saveConfiguration}}" stepKey="waitForWrapperToClose2"/> <click selector="{{AdminDesignConfigSection.saveConfiguration}}" stepKey="saveConfiguration2"/> <waitForElementVisible selector="{{AdminDesignConfigSection.successNotification}}" stepKey="waitForSuccessNotification2"/> <waitForPageLoad stepKey="waitForPageloadSuccess2"/> From 0b4ac19d8f199533ff1859877f649d124bb6af82 Mon Sep 17 00:00:00 2001 From: Krissy Hiserote <khiserote@magento.com> Date: Mon, 11 Feb 2019 12:26:50 -0600 Subject: [PATCH 021/265] MC-5784: Image fields using imageUploader UIComponent cannot use gallery image - save file to correct location --- .../Theme/Model/Design/Backend/File.php | 55 +++++++++++++------ 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Theme/Model/Design/Backend/File.php b/app/code/Magento/Theme/Model/Design/Backend/File.php index 81217430c083..820144c7f1cb 100644 --- a/app/code/Magento/Theme/Model/Design/Backend/File.php +++ b/app/code/Magento/Theme/Model/Design/Backend/File.php @@ -101,23 +101,7 @@ public function beforeSave() return $this; } - $filename = basename($file); - $relativeMediaUrl = $this->getRelativeMediaPath($value['url']); - $tmpMediaPath = $this->_mediaDirectory->isFile($relativeMediaUrl) ? - $relativeMediaUrl : $this->getTmpMediaPath($filename); - $result = $this->_mediaDirectory->copyFile( - $tmpMediaPath, - $this->_getUploadDir() . '/' . $filename - ); - if ($result) { - $this->_mediaDirectory->delete($tmpMediaPath); - if ($this->_addWhetherScopeInfo()) { - $filename = $this->_prependScopeInfo($filename); - } - $this->setValue($filename); - } else { - $this->unsValue(); - } + $this->saveFile($value, $file); return $this; } @@ -251,6 +235,41 @@ private function getMime() */ private function getRelativeMediaPath(string $path): string { - return str_replace('/pub/media', '', $path); + return str_replace('/pub/media/', '', $path); + } + + /** + * Save file to the media directory in the correct location + * + * @param array $value + * @param string $file + * @throws LocalizedException + */ + private function saveFile(array $value, string $file) + { + $filename = basename($file); + $relativeMediaPath = $this->getRelativeMediaPath($value['url']); + $tmpMediaPath = $this->getTmpMediaPath($filename); + $mediaPath = $this->_mediaDirectory->isFile($relativeMediaPath) ? $relativeMediaPath : $tmpMediaPath; + $destinationMediaPath = $this->_getUploadDir() . '/' . $filename; + + $result = $mediaPath === $destinationMediaPath; + if (!$result) { + $result = $this->_mediaDirectory->copyFile( + $mediaPath, + $destinationMediaPath + ); + } + if ($result) { + if ($mediaPath === $tmpMediaPath) { + $this->_mediaDirectory->delete($mediaPath); + } + if ($this->_addWhetherScopeInfo()) { + $filename = $this->_prependScopeInfo($filename); + } + $this->setValue($filename); + } else { + $this->unsValue(); + } } } From 9f021b0a79e19d05a3fbb22c4146064c3a6b0997 Mon Sep 17 00:00:00 2001 From: Krissy Hiserote <khiserote@magento.com> Date: Tue, 12 Feb 2019 09:01:19 -0600 Subject: [PATCH 022/265] MC-5784: Image fields using imageUploader UIComponent cannot use gallery image - delete image in the MFTF test --- ...avigateToFaviconMediaFolderActionGroup.xml | 23 +++++++++++++++++++ .../Mftf/Section/AdminDesignConfigSection.xml | 5 ++++ ...esignConfigMediaGalleryImageUploadTest.xml | 12 ++++++++++ 3 files changed, 40 insertions(+) create mode 100644 app/code/Magento/Theme/Test/Mftf/ActionGroup/NavigateToFaviconMediaFolderActionGroup.xml diff --git a/app/code/Magento/Theme/Test/Mftf/ActionGroup/NavigateToFaviconMediaFolderActionGroup.xml b/app/code/Magento/Theme/Test/Mftf/ActionGroup/NavigateToFaviconMediaFolderActionGroup.xml new file mode 100644 index 000000000000..6b9868657432 --- /dev/null +++ b/app/code/Magento/Theme/Test/Mftf/ActionGroup/NavigateToFaviconMediaFolderActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="NavigateToFaviconMediaFolderActionGroup"> + <arguments> + <argument name="StoreFolder" type="string"/> + </arguments> + <conditionalClick selector="{{MediaGallerySection.StorageRootArrow}}" dependentSelector="{{MediaGallerySection.checkIfArrowExpand}}" stepKey="clickArrowIfClosed" visible="true"/> + <waitForElement selector="{{AdminDesignConfigSection.faviconArrow}}" stepKey="waitForFaviconFolder"/> + <conditionalClick selector="{{AdminDesignConfigSection.faviconArrow}}" dependentSelector="{{AdminDesignConfigSection.checkIfFaviconArrowExpand}}" stepKey="clickFaviconArrowIfClosed" visible="true"/> + <waitForElement selector="{{AdminDesignConfigSection.storesArrow}}" stepKey="waitForStoresFolder"/> + <conditionalClick selector="{{AdminDesignConfigSection.storesArrow}}" dependentSelector="{{AdminDesignConfigSection.checkIfStoresArrowExpand}}" stepKey="clickStoresArrowIfClosed" visible="true"/> + <waitForElement selector="{{StoreFolder}}" stepKey="waitForStoreFolder"/> + <click selector="{{StoreFolder}}" stepKey="clickOnCreatedFolder"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoading"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml b/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml index ceb5556b2673..c2652f33f760 100644 --- a/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml +++ b/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml @@ -26,5 +26,10 @@ <element name="logoUpload" type ="input" selector="[name='email_logo']" /> <element name="logoWrapperOpen" type ="text" selector="[data-index='email'] [data-state-collapsible ='closed']"/> <element name="logoPreview" type ="text" selector="[alt ='magento-logo.png']"/> + <element name="faviconArrow" type="button" selector="#ZmF2aWNvbg-- > .jstree-icon" /> + <element name="checkIfFaviconArrowExpand" type="button" selector="//li[@id='ZmF2aWNvbg--' and contains(@class,'jstree-closed')]" /> + <element name="storesArrow" type="button" selector="#ZmF2aWNvbi9zdG9yZXM- > .jstree-icon" /> + <element name="checkIfStoresArrowExpand" type="button" selector="//li[@id='ZmF2aWNvbi9zdG9yZXM-' and contains(@class,'jstree-closed')]" /> + <element name="storeLink" type="button" selector="#ZmF2aWNvbi9zdG9yZXMvMQ-- > a"/> </section> </sections> diff --git a/app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml index 40b80be48efd..c896c29f42a3 100644 --- a/app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml +++ b/app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml @@ -36,6 +36,9 @@ <comment userInput="Upload Image" stepKey="uploadImageComment"/> <click selector="{{AdminDesignConfigSection.selectFromGalleryByFieldsetName('Head')}}" stepKey="openMediaGallery"/> <actionGroup ref="VerifyMediaGalleryStorageActions" stepKey="VerifyMediaGalleryStorageBtn"/> + <actionGroup ref="NavigateToFaviconMediaFolderActionGroup" stepKey="NavigateToFolder"> + <argument name="StoreFolder" value="{{AdminDesignConfigSection.storeLink}}"/> + </actionGroup> <actionGroup ref="attachImage" stepKey="SelectImageFromMediaStorage"> <argument name="Image" value="ImageUpload3"/> </actionGroup> @@ -56,5 +59,14 @@ <click selector="{{AdminDesignConfigSection.saveConfiguration}}" stepKey="saveConfiguration2"/> <waitForElementVisible selector="{{AdminDesignConfigSection.successNotification}}" stepKey="waitForSuccessNotification2"/> <waitForPageLoad stepKey="waitForPageloadSuccess2"/> + <!--Delete Image--> + <comment userInput="Delete Image" stepKey="deleteImageComment"/> + <actionGroup ref="navigateToMediaGallery" stepKey="navigateToMediaGallery"/> + <actionGroup ref="NavigateToFaviconMediaFolderActionGroup" stepKey="NavigateToFolder2"> + <argument name="StoreFolder" value="{{AdminDesignConfigSection.storeLink}}"/> + </actionGroup> + <actionGroup ref="DeleteImageFromStorageActionGroup" stepKey="DeleteImageFromStorage"> + <argument name="Image" value="ImageUpload3"/> + </actionGroup> </test> </tests> From 04acd7b84086c6d423b394c44472645400e20b8e Mon Sep 17 00:00:00 2001 From: Krissy Hiserote <khiserote@magento.com> Date: Tue, 12 Feb 2019 12:00:16 -0600 Subject: [PATCH 023/265] MC-5784: Image fields using imageUploader UIComponent cannot use gallery image --- .../Theme/Model/Design/Backend/File.php | 15 +++++++------- ...esignConfigMediaGalleryImageUploadTest.xml | 20 ++++++++++++------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Theme/Model/Design/Backend/File.php b/app/code/Magento/Theme/Model/Design/Backend/File.php index 820144c7f1cb..511fe30f79dc 100644 --- a/app/code/Magento/Theme/Model/Design/Backend/File.php +++ b/app/code/Magento/Theme/Model/Design/Backend/File.php @@ -90,6 +90,8 @@ public function beforeSave() { $values = $this->getValue(); $value = reset($values) ?: []; + + // Need to check name when it is uploaded in the media gallary $file = $value['file'] ?? $value['name'] ?? null; if (!isset($file)) { throw new LocalizedException( @@ -101,7 +103,7 @@ public function beforeSave() return $this; } - $this->saveFile($value, $file); + $this->updateMediaDirectory(basename($file), $value['url']); return $this; } @@ -239,16 +241,15 @@ private function getRelativeMediaPath(string $path): string } /** - * Save file to the media directory in the correct location + * Move file to the correct media directory * - * @param array $value - * @param string $file + * @param string $filename + * @param string $url * @throws LocalizedException */ - private function saveFile(array $value, string $file) + private function updateMediaDirectory(string $filename, string $url) { - $filename = basename($file); - $relativeMediaPath = $this->getRelativeMediaPath($value['url']); + $relativeMediaPath = $this->getRelativeMediaPath($url); $tmpMediaPath = $this->getTmpMediaPath($filename); $mediaPath = $this->_mediaDirectory->isFile($relativeMediaPath) ? $relativeMediaPath : $tmpMediaPath; $destinationMediaPath = $this->_getUploadDir() . '/' . $filename; diff --git a/app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml index c896c29f42a3..f46328ac151b 100644 --- a/app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml +++ b/app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml @@ -35,11 +35,11 @@ <!--Upload Image--> <comment userInput="Upload Image" stepKey="uploadImageComment"/> <click selector="{{AdminDesignConfigSection.selectFromGalleryByFieldsetName('Head')}}" stepKey="openMediaGallery"/> - <actionGroup ref="VerifyMediaGalleryStorageActions" stepKey="VerifyMediaGalleryStorageBtn"/> - <actionGroup ref="NavigateToFaviconMediaFolderActionGroup" stepKey="NavigateToFolder"> - <argument name="StoreFolder" value="{{AdminDesignConfigSection.storeLink}}"/> + <actionGroup ref="VerifyMediaGalleryStorageActions" stepKey="verifyMediaGalleryStorageBtn"/> + <actionGroup ref="NavigateToMediaFolderActionGroup" stepKey="navigateToFolder"> + <argument name="FolderName" value="Storage Root"/> </actionGroup> - <actionGroup ref="attachImage" stepKey="SelectImageFromMediaStorage"> + <actionGroup ref="attachImage" stepKey="selectImageFromMediaStorage"> <argument name="Image" value="ImageUpload3"/> </actionGroup> <actionGroup ref="saveImage" stepKey="insertImage"/> @@ -59,13 +59,19 @@ <click selector="{{AdminDesignConfigSection.saveConfiguration}}" stepKey="saveConfiguration2"/> <waitForElementVisible selector="{{AdminDesignConfigSection.successNotification}}" stepKey="waitForSuccessNotification2"/> <waitForPageLoad stepKey="waitForPageloadSuccess2"/> - <!--Delete Image--> + <!--Delete Image: will be in both root and favicon--> <comment userInput="Delete Image" stepKey="deleteImageComment"/> <actionGroup ref="navigateToMediaGallery" stepKey="navigateToMediaGallery"/> - <actionGroup ref="NavigateToFaviconMediaFolderActionGroup" stepKey="NavigateToFolder2"> + <actionGroup ref="NavigateToMediaFolderActionGroup" stepKey="navigateToFolder2"> + <argument name="FolderName" value="Storage Root"/> + </actionGroup> + <actionGroup ref="DeleteImageFromStorageActionGroup" stepKey="deleteImageFromStorage"> + <argument name="Image" value="ImageUpload3"/> + </actionGroup> + <actionGroup ref="NavigateToFaviconMediaFolderActionGroup" stepKey="navigateToFolder3"> <argument name="StoreFolder" value="{{AdminDesignConfigSection.storeLink}}"/> </actionGroup> - <actionGroup ref="DeleteImageFromStorageActionGroup" stepKey="DeleteImageFromStorage"> + <actionGroup ref="DeleteImageFromStorageActionGroup" stepKey="deleteImageFromStorage2"> <argument name="Image" value="ImageUpload3"/> </actionGroup> </test> From 7e87746f56a46c1be06111520030ca5b917bdec5 Mon Sep 17 00:00:00 2001 From: vprohorov <prohorov.vital@gmail.com> Date: Tue, 12 Feb 2019 22:36:01 +0300 Subject: [PATCH 024/265] MAGETWO-62728: My Wishlist - quantity input box issue - Adding min/max qty checks - Adding js validation --- .../Wishlist/ViewModel/AllowedQuantity.php | 80 +++++++++++++++++++ .../frontend/layout/wishlist_index_index.xml | 1 + .../frontend/templates/item/column/cart.phtml | 5 +- .../view/frontend/web/js/add-to-wishlist.js | 57 ++++++------- 4 files changed, 115 insertions(+), 28 deletions(-) create mode 100644 app/code/Magento/Wishlist/ViewModel/AllowedQuantity.php diff --git a/app/code/Magento/Wishlist/ViewModel/AllowedQuantity.php b/app/code/Magento/Wishlist/ViewModel/AllowedQuantity.php new file mode 100644 index 000000000000..5e4c6b39f3c3 --- /dev/null +++ b/app/code/Magento/Wishlist/ViewModel/AllowedQuantity.php @@ -0,0 +1,80 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\ViewModel; + +use Magento\Catalog\Controller\Adminhtml\Product\Initialization\StockDataFilter; +use Magento\Catalog\Model\Product\Configuration\Item\ItemInterface; +use Magento\CatalogInventory\Model\StockRegistry; +use Magento\Framework\View\Element\Block\ArgumentInterface; + +/** + * ViewModel for Wishlist Cart Block + */ +class AllowedQuantity implements ArgumentInterface +{ + /** + * @var StockRegistry + */ + private $stockRegistry; + + /** + * @var ItemInterface + */ + private $item; + + /** + * @param StockRegistry $stockRegistry + */ + public function __construct(StockRegistry $stockRegistry) + { + $this->stockRegistry = $stockRegistry; + } + + /** + * Set product configuration item + * + * @param ItemInterface $item + * @return self + */ + public function setItem(ItemInterface $item): self + { + $this->item = $item; + return $this; + } + + /** + * Get product configuration item + * + * @return ItemInterface + */ + public function getItem(): ItemInterface + { + return $this->item; + } + + /** + * Get min and max qty for wishlist form. + * + * @return array + */ + public function getMinMaxQty(): array + { + $product = $this->getItem()->getProduct(); + $stockItem = $this->stockRegistry->getStockItem($product->getId(), $product->getStore()->getWebsiteId()); + $params = []; + + $params['minAllowed'] = (float)$stockItem->getMinSaleQty(); + if ($stockItem->getMaxSaleQty()) { + $params['maxAllowed'] = (float)$stockItem->getMaxSaleQty(); + } else { + $params['maxAllowed'] = (float)StockDataFilter::MAX_QTY_VALUE; + } + + return $params; + } +} diff --git a/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_index.xml b/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_index.xml index d3f21dda9ccd..e9076021a5b7 100644 --- a/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_index.xml +++ b/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_index.xml @@ -40,6 +40,7 @@ </block> <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Cart" name="customer.wishlist.item.cart" template="Magento_Wishlist::item/column/cart.phtml" cacheable="false"> <arguments> + <argument name="allowedQuantityViewModel" xsi:type="object">Magento\Wishlist\ViewModel\AllowedQuantity</argument> <argument name="title" translate="true" xsi:type="string">Add to Cart</argument> </arguments> </block> diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml index 9ea0d1a82323..6cb32d70ee1d 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml @@ -11,6 +11,9 @@ /** @var \Magento\Wishlist\Model\Item $item */ $item = $block->getItem(); $product = $item->getProduct(); +/** @var \Magento\Wishlist\ViewModel\AllowedQuantity $viewModel */ +$viewModel = $block->getData('allowedQuantityViewModel'); +$allowedQty = $viewModel->setItem($item)->getMinMaxQty(); ?> <?php foreach ($block->getChildNames() as $childName): ?> <?= /* @noEscape */ $block->getLayout()->renderElement($childName, false) ?> @@ -21,7 +24,7 @@ $product = $item->getProduct(); <div class="field qty"> <label class="label" for="qty[<?= $block->escapeHtmlAttr($item->getId()) ?>]"><span><?= $block->escapeHtml(__('Qty')) ?></span></label> <div class="control"> - <input type="number" data-role="qty" id="qty[<?= $block->escapeHtmlAttr($item->getId()) ?>]" class="input-text qty" data-validate="{'required-number':true,'validate-greater-than-zero':true}" + <input type="number" data-role="qty" id="qty[<?= $block->escapeHtmlAttr($item->getId()) ?>]" class="input-text qty" data-validate="{'required-number':true,'validate-greater-than-zero':true, 'validate-item-quantity':{'minAllowed':<?= /* @noEscape */ $allowedQty['minAllowed'] ?>,'maxAllowed':<?= /* @noEscape */ $allowedQty['maxAllowed'] ?>}}" name="qty[<?= $block->escapeHtmlAttr($item->getId()) ?>]" value="<?= /* @noEscape */ (int)($block->getAddToCartQty($item) * 1) ?>"> </div> </div> diff --git a/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js b/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js index b38c5c2cda3a..7a166b47256c 100644 --- a/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js +++ b/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js @@ -63,12 +63,6 @@ define([ isFileUploaded = false, self = this; - if (event.handleObj.selector == this.options.qtyInfo) { //eslint-disable-line eqeqeq - this._updateAddToWishlistButton({}); - event.stopPropagation(); - - return; - } $(event.handleObj.selector).each(function (index, element) { if ($(element).is('input[type=text]') || $(element).is('input[type=email]') || @@ -89,9 +83,7 @@ define([ } }); - if (isFileUploaded) { - this.bindFormSubmit(); - } + this.bindFormSubmit(isFileUploaded); this._updateAddToWishlistButton(dataToAdd); event.stopPropagation(); }, @@ -189,34 +181,45 @@ define([ /** * Bind form submit. + * + * @param {Boolean} isFileUploaded */ - bindFormSubmit: function () { + bindFormSubmit: function (isFileUploaded) { var self = this; $('[data-action="add-to-wishlist"]').on('click', function (event) { var element, params, form, action; - event.stopPropagation(); - event.preventDefault(); + if (!$($(self.options.qtyInfo).closest('form')).valid()) { + event.stopPropagation(); + event.preventDefault(); - element = $('input[type=file]' + self.options.customOptionsInfo); - params = $(event.currentTarget).data('post'); - form = $(element).closest('form'); - action = params.action; - - if (params.data.id) { - $('<input>', { - type: 'hidden', - name: 'id', - value: params.data.id - }).appendTo(form); + return; } - if (params.data.uenc) { - action += 'uenc/' + params.data.uenc; - } + if (isFileUploaded) { + + element = $('input[type=file]' + self.options.customOptionsInfo); + params = $(event.currentTarget).data('post'); + form = $(element).closest('form'); + action = params.action; + + if (params.data.id) { + $('<input>', { + type: 'hidden', + name: 'id', + value: params.data.id + }).appendTo(form); + } - $(form).attr('action', action).submit(); + if (params.data.uenc) { + action += 'uenc/' + params.data.uenc; + } + + $(form).attr('action', action).submit(); + event.stopPropagation(); + event.preventDefault(); + } }); } }); From 5951f685da0ada37e8aadbaa6de08d75a9099ec9 Mon Sep 17 00:00:00 2001 From: Mikalai Shostka <mikalai_shostka@epam.com> Date: Wed, 13 Feb 2019 21:04:01 +0300 Subject: [PATCH 025/265] MAGETWO-51459: [FT] CreateCategoryEntityTest fail when system message about indexers update is displayed - Fixed MFT test; --- .../app/Magento/Catalog/Test/Block/Adminhtml/Category/Tree.php | 2 ++ .../Catalog/Test/Block/Product/ProductList/TopToolbar.php | 2 +- .../Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Category/Tree.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Category/Tree.php index 2035e7e83200..30a323eebb73 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Category/Tree.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Category/Tree.php @@ -187,6 +187,8 @@ public function assignCategory($parentCategoryName, $childCategoryName) */ public function expandAllCategories() { + $this->getTemplateBlock()->waitLoader(); $this->_rootElement->find($this->expandAll)->click(); + $this->getTemplateBlock()->waitLoader(); } } diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/ProductList/TopToolbar.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/ProductList/TopToolbar.php index 090042140a54..48769126d88e 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/ProductList/TopToolbar.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/ProductList/TopToolbar.php @@ -48,7 +48,7 @@ public function getSelectSortType() public function getSortType() { $content = $this->_rootElement->find($this->sorter)->getText(); - return explode("\n", $content); + return array_values(array_filter(array_map('trim', explode("\n", $content)))); } /** diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml index c6a66beac7c7..ea6808ee2a7f 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml @@ -96,6 +96,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertCategoryForAssignedProducts" /> </variation> <variation name="CreateCategoryEntityTestVariation5_Anchor_MostOfFields"> + <data name="tag" xsi:type="string">test_type:acceptance_test</data> <data name="description" xsi:type="string">Create anchor subcategory with all fields</data> <data name="addCategory" xsi:type="string">addSubcategory</data> <data name="category/data/parent_id/dataset" xsi:type="string">default_category</data> From d79eb085cabfb16ed8f4432f5cd25b00cf3ba686 Mon Sep 17 00:00:00 2001 From: "Rav [RedChamps]" <getravindersingh@gmail.com> Date: Sat, 16 Feb 2019 10:38:57 +0530 Subject: [PATCH 026/265] 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]" <getravindersingh@gmail.com> Date: Sat, 16 Feb 2019 19:55:46 +0530 Subject: [PATCH 027/265] 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 6ae859aaa233261b03a6bccf946dccdb369926b6 Mon Sep 17 00:00:00 2001 From: AleksLi <aleksliwork@gmail.com> Date: Sun, 17 Feb 2019 19:32:44 +0100 Subject: [PATCH 028/265] magento/magento2#12396: Total Amount cart rule without tax - Added new condition type to give user opportunity to choose the configuration. --- app/code/Magento/SalesRule/Model/Rule/Condition/Address.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/SalesRule/Model/Rule/Condition/Address.php b/app/code/Magento/SalesRule/Model/Rule/Condition/Address.php index 89ec2b84572f..cf6301cb31a9 100644 --- a/app/code/Magento/SalesRule/Model/Rule/Condition/Address.php +++ b/app/code/Magento/SalesRule/Model/Rule/Condition/Address.php @@ -61,6 +61,7 @@ public function __construct( public function loadAttributeOptions() { $attributes = [ + 'base_subtotal_with_discount' => __('Subtotal (Excl. Tax)'), 'base_subtotal' => __('Subtotal'), 'total_qty' => __('Total Items Quantity'), 'weight' => __('Total Weight'), From 05aa8c5c06fde2fc7b5b77c6487a0e107feadbdf Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <korablov@adobe.com> Date: Tue, 19 Feb 2019 10:11:39 -0600 Subject: [PATCH 029/265] MAGETWO-98151: Add support ZooKeeper locks --- app/etc/di.xml | 2 +- .../Framework/Lock/Backend/Zookeeper.php | 185 ++++++++++++++++++ .../Magento/Framework/Lock/Factory.php | 90 +++++++++ lib/internal/Magento/Framework/Lock/Proxy.php | 80 ++++++++ 4 files changed, 356 insertions(+), 1 deletion(-) create mode 100644 lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php create mode 100644 lib/internal/Magento/Framework/Lock/Factory.php create mode 100644 lib/internal/Magento/Framework/Lock/Proxy.php diff --git a/app/etc/di.xml b/app/etc/di.xml index 19543375aad5..d74377b6194c 100755 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -38,7 +38,7 @@ <preference for="Magento\Framework\Locale\ListsInterface" type="Magento\Framework\Locale\TranslatedLists" /> <preference for="Magento\Framework\Locale\AvailableLocalesInterface" type="Magento\Framework\Locale\Deployed\Codes" /> <preference for="Magento\Framework\Locale\OptionInterface" type="Magento\Framework\Locale\Deployed\Options" /> - <preference for="Magento\Framework\Lock\LockManagerInterface" type="Magento\Framework\Lock\Backend\Database" /> + <preference for="Magento\Framework\Lock\LockManagerInterface" type="Magento\Framework\Lock\Proxy" /> <preference for="Magento\Framework\Api\AttributeTypeResolverInterface" type="Magento\Framework\Reflection\AttributeTypeResolver" /> <preference for="Magento\Framework\Api\Search\SearchResultInterface" type="Magento\Framework\Api\Search\SearchResult" /> <preference for="Magento\Framework\Api\Search\SearchCriteriaInterface" type="Magento\Framework\Api\Search\SearchCriteria"/> diff --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php new file mode 100644 index 000000000000..20f4dd26b8a0 --- /dev/null +++ b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php @@ -0,0 +1,185 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Lock\Backend; + +use Magento\Framework\Lock\LockManagerInterface; +use Magento\Framework\Exception\RuntimeException; +use Magento\Framework\Phrase; + +/** + * LockManager using the Zookeeper for locks + */ +class Zookeeper implements LockManagerInterface +{ + /** + * Zookeeper provider + * + * @var \Zookeeper + */ + private $zookeeper; + + /** + * The base path to locks in Zookeeper + * + * @var string + */ + private $path; + + /** + * The host to connect to Zookeeper + * + * @var string + */ + private $host; + + /** + * How many seconds to wait before timing out on connections + * + * @var int + */ + private $connectionTimeout = 2; + + /** + * How many microseconds to wait before recheck connections or nodes + * + * @var float + */ + private $sleepCycle = 100000; + + /** + * The default permissions for Zookeeper nodes + * + * @var array + */ + private $acl = [['perms'=>\Zookeeper::PERM_ALL, 'scheme' => 'world', 'id' => 'anyone']]; + + /** + * @param string $host The host to connect to Zookeeper + * @param string $path The base path to locks in Zookeeper + * @throws RuntimeException + */ + public function __construct(string $host, string $path = '/magento/locks') + { + if (empty($path)) { + throw new RuntimeException( + new Phrase('The path needs to be a non-empty string.') + ); + } + + $this->host = $host; + $this->path = preg_replace('#\/*$#', '', $path) ?: '/'; + } + + /** + * {@inheritdoc} + * @throws RuntimeException + */ + public function lock(string $name, int $timeout = -1): bool + { + $skipDeadline = $timeout < 0; + $lockPath = $this->getFullPathToLock($name); + $deadline = microtime(true) + $timeout; + + while($this->getProvider()->exists($lockPath)) { + if (!$skipDeadline && $deadline <= microtime(true)) { + return false; + } + + usleep($this->sleepCycle); + } + + if (!$this->getProvider()->create($lockPath, '1', $this->acl, \Zookeeper::EPHEMERAL)) { + throw new RuntimeException(new Phrase('Failed creating lock %1', [$lockPath])); + } else { + return true; + } + } + + /** + * @inheritdoc + */ + public function unlock(string $name): bool + { + $lockPath = $this->getFullPathToLock($name); + + if (!$this->getProvider()->exists($lockPath)) { + return true; + } + + return $this->getProvider()->delete($lockPath); + } + + /** + * @inheritdoc + */ + public function isLocked(string $name): bool + { + return (bool) $this->getProvider()->exists($this->getFullPathToLock($name)); + } + + /** + * Gets full path to lock by its name + * + * @param string $name + * @return string + */ + private function getFullPathToLock(string $name): string + { + return $this->path . '/' . $name; + } + + /** + * Initiolizes and returns Zookeeper provider + * + * @return \Zookeeper + * @throws RuntimeException + */ + private function getProvider(): \Zookeeper + { + if (!$this->zookeeper) { + $this->zookeeper = new \Zookeeper($this->host); + + $deadline = microtime(true) + $this->connectionTimeout; + while($this->zookeeper->getState() != \Zookeeper::CONNECTED_STATE) { + if ($deadline <= microtime(true)) { + throw new RuntimeException(new Phrase('Zookeeper connection timed out!')); + } + usleep($this->sleepCycle); + } + + if (!$this->createBasePath($this->path)) { + throw new RuntimeException(new Phrase('Failed creating base path %1', [$this->path])); + } + } + + return $this->zookeeper; + } + + /** + * Checks and creates base path recursively + * + * @param $path + * @return bool + */ + private function createBasePath($path) + { + if ($this->zookeeper->exists($path)) { + return true; + } + + if (!$this->createBasePath(dirname($path))) { + return false; + } + + if ($this->zookeeper->create($path, '1', $this->acl)) { + return true; + } + + return $this->zookeeper->exists($path); + } +} diff --git a/lib/internal/Magento/Framework/Lock/Factory.php b/lib/internal/Magento/Framework/Lock/Factory.php new file mode 100644 index 000000000000..1414358ae42e --- /dev/null +++ b/lib/internal/Magento/Framework/Lock/Factory.php @@ -0,0 +1,90 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Lock; + +use Magento\Framework\Phrase; +use Magento\Framework\Exception\RuntimeException; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\Lock\Backend\Database as DatabaseLock; +use Magento\Framework\Lock\Backend\Zookeeper as ZookeeperLock; + +/** + * The factory to create object that implements LockManagerInterface + */ +class Factory +{ + /** + * The Object Manager instance + * + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * The Application deployment configuration + * + * @var DeploymentConfig + */ + private $deploymentConfig; + + /** + * DB lock provider name + * + * @const string + */ + const LOCK_DB = 'db'; + + /** + * Zookeeper lock provider name + * + * @const string + */ + const LOCK_ZOOKEEPER = 'zookeeper'; + + /** + * The list of lock providers with mapping on classes + * + * @var array + */ + private $lockers = [ + self::LOCK_DB => DatabaseLock::class, + self::LOCK_ZOOKEEPER => ZookeeperLock::class + ]; + + + /** + * @param ObjectManagerInterface $objectManager The Object Manager instance + * @param DeploymentConfig $deploymentConfig The Application deployment configuration + */ + public function __construct( + ObjectManagerInterface $objectManager, + DeploymentConfig $deploymentConfig + ) { + $this->objectManager = $objectManager; + $this->deploymentConfig = $deploymentConfig; + } + + /** + * Creates an instance of LockManagerInterface using information from deployment config + * + * @return LockManagerInterface + * @throws RuntimeException + */ + public function create(): LockManagerInterface + { + $provider = $this->deploymentConfig->get('locks/provider', self::LOCK_DB); + $config = $this->deploymentConfig->get('locks/config', []); + + if (!isset($this->lockers[$provider])) { + throw new RuntimeException(new Phrase('Unknown locks provider.')); + } + + return $this->objectManager->create($this->lockers[$provider], $config); + } +} diff --git a/lib/internal/Magento/Framework/Lock/Proxy.php b/lib/internal/Magento/Framework/Lock/Proxy.php new file mode 100644 index 000000000000..e140078d05ab --- /dev/null +++ b/lib/internal/Magento/Framework/Lock/Proxy.php @@ -0,0 +1,80 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Lock; + +use Magento\Framework\Exception\RuntimeException; + +/** + * Proxy for LockManagers + */ +class Proxy implements LockManagerInterface +{ + /** + * The factory to create LockManagerInterface implementation + * + * @var Factory + */ + private $factory; + + /** + * A LockManagerInterface implementation + * + * @var LockManagerInterface + */ + private $locker; + + /** + * @param Factory $factory The factory to create LockManagerInterface implementation + */ + public function __construct(Factory $factory) + { + $this->factory = $factory; + } + + /** + * {@inheritdoc} + * @throws RuntimeException + */ + public function isLocked(string $name): bool + { + return $this->getLocker()->isLocked($name); + } + + /** + * {@inheritdoc} + * @throws RuntimeException + */ + public function lock(string $name, int $timeout = -1): bool + { + return $this->getLocker()->lock($name, $timeout); + } + + /** + * {@inheritdoc} + * @throws RuntimeException + */ + public function unlock(string $name): bool + { + return $this->getLocker()->unlock($name); + } + + /** + * Gets LockManagerInterface implementation using Factory + * + * @return LockManagerInterface + * @throws RuntimeException + */ + private function getLocker(): LockManagerInterface + { + if (!$this->locker) { + $this->locker = $this->factory->create(); + } + + return $this->locker; + } +} From 7b352ce6c6b2ac0f4e1edf5bdd0d58a346eba3e3 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <korablov@adobe.com> Date: Tue, 19 Feb 2019 14:48:17 -0600 Subject: [PATCH 030/265] MAGETWO-98151: Add support ZooKeeper locks --- .../Framework/Lock/Backend/Zookeeper.php | 133 ++++++++++++++---- .../{Factory.php => LockBackendFactory.php} | 2 +- lib/internal/Magento/Framework/Lock/Proxy.php | 6 +- .../Framework/Lock/Test/Unit/ProxyTest.php | 106 ++++++++++++++ 4 files changed, 215 insertions(+), 32 deletions(-) rename lib/internal/Magento/Framework/Lock/{Factory.php => LockBackendFactory.php} (98%) create mode 100644 lib/internal/Magento/Framework/Lock/Test/Unit/ProxyTest.php diff --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php index 20f4dd26b8a0..d813f209ab96 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php @@ -30,6 +30,11 @@ class Zookeeper implements LockManagerInterface */ private $path; + /** + * @var string + */ + private $lockName = 'lock-'; + /** * The host to connect to Zookeeper * @@ -58,6 +63,13 @@ class Zookeeper implements LockManagerInterface */ private $acl = [['perms'=>\Zookeeper::PERM_ALL, 'scheme' => 'world', 'id' => 'anyone']]; + /** + * The mapping list of the lock name with the full lock path + * + * @var array + */ + private $locks = []; + /** * @param string $host The host to connect to Zookeeper * @param string $path The base path to locks in Zookeeper @@ -77,6 +89,9 @@ public function __construct(string $host, string $path = '/magento/locks') /** * {@inheritdoc} + * You can see the lock algorithm by the link + * @link https://zookeeper.apache.org/doc/r3.1.2/recipes.html#sc_recipes_Locks + * * @throws RuntimeException */ public function lock(string $name, int $timeout = -1): bool @@ -85,19 +100,29 @@ public function lock(string $name, int $timeout = -1): bool $lockPath = $this->getFullPathToLock($name); $deadline = microtime(true) + $timeout; - while($this->getProvider()->exists($lockPath)) { + if (!$this->checkAndCreateParentNode($lockPath)) { + throw new RuntimeException(new Phrase('Failed creating the path %1', [$lockPath])); + } + + $lockKey = $this->getProvider() + ->create($lockPath, '1', $this->acl, \Zookeeper::EPHEMERAL | \Zookeeper::SEQUENCE); + + if (!$lockKey) { + throw new RuntimeException(new Phrase('Failed creating lock %1', [$lockPath])); + } + + while($this->isAnyLock($lockKey, $this->getIndex($lockKey))) { if (!$skipDeadline && $deadline <= microtime(true)) { + $this->getProvider()->delete($lockKey); return false; } usleep($this->sleepCycle); } - if (!$this->getProvider()->create($lockPath, '1', $this->acl, \Zookeeper::EPHEMERAL)) { - throw new RuntimeException(new Phrase('Failed creating lock %1', [$lockPath])); - } else { - return true; - } + $this->locks[$name] = $lockKey; + + return true; } /** @@ -105,13 +130,11 @@ public function lock(string $name, int $timeout = -1): bool */ public function unlock(string $name): bool { - $lockPath = $this->getFullPathToLock($name); - - if (!$this->getProvider()->exists($lockPath)) { + if (!isset($this->locks[$name])) { return true; } - return $this->getProvider()->delete($lockPath); + return $this->getProvider()->delete($this->locks[$name]); } /** @@ -119,7 +142,7 @@ public function unlock(string $name): bool */ public function isLocked(string $name): bool { - return (bool) $this->getProvider()->exists($this->getFullPathToLock($name)); + return $this->isAnyLock($this->getFullPathToLock($name)); } /** @@ -130,7 +153,7 @@ public function isLocked(string $name): bool */ private function getFullPathToLock(string $name): string { - return $this->path . '/' . $name; + return $this->path . '/' . $name . '/' . $this->lockName; } /** @@ -143,18 +166,14 @@ private function getProvider(): \Zookeeper { if (!$this->zookeeper) { $this->zookeeper = new \Zookeeper($this->host); + } - $deadline = microtime(true) + $this->connectionTimeout; - while($this->zookeeper->getState() != \Zookeeper::CONNECTED_STATE) { - if ($deadline <= microtime(true)) { - throw new RuntimeException(new Phrase('Zookeeper connection timed out!')); - } - usleep($this->sleepCycle); - } - - if (!$this->createBasePath($this->path)) { - throw new RuntimeException(new Phrase('Failed creating base path %1', [$this->path])); + $deadline = microtime(true) + $this->connectionTimeout; + while($this->zookeeper->getState() != \Zookeeper::CONNECTED_STATE) { + if ($deadline <= microtime(true)) { + throw new RuntimeException(new Phrase('Zookeeper connection timed out!')); } + usleep($this->sleepCycle); } return $this->zookeeper; @@ -163,23 +182,81 @@ private function getProvider(): \Zookeeper /** * Checks and creates base path recursively * - * @param $path + * @param string $path * @return bool + * @throws RuntimeException */ - private function createBasePath($path) + private function checkAndCreateParentNode(string $path): bool { - if ($this->zookeeper->exists($path)) { + $path = dirname($path); + if ($this->getProvider()->exists($path)) { return true; } - if (!$this->createBasePath(dirname($path))) { + if (!$this->checkAndCreateParentNode($path)) { return false; } - if ($this->zookeeper->create($path, '1', $this->acl)) { + if ($this->getProvider()->create($path, '1', $this->acl)) { return true; } - return $this->zookeeper->exists($path); + return $this->getProvider()->exists($path); + } + + /** + * Gets int increment of lock key + * + * @param string $key + * @return int|null + */ + private function getIndex(string $key) + { + if (!preg_match("/[0-9]+$/", $key, $matches)) + return null; + + return intval($matches[0]); + } + + /** + * Checks if there is any sequence node under parent of $fullKey. + * At first checks that the $fullKey node is present, if not - returns false. + * + * If $indexKey is non-null and there is a smaller index that $indexKey then returns true, + * if all the nodes are larger than $indexKey then returns false. + * + * @param string $fullKey The full path without any sequence info + * @param int|null $indexKey The index to compare + * @return bool + * @throws RuntimeException + */ + private function isAnyLock(string $fullKey, int $indexKey = null): bool + { + $parent = dirname($fullKey); + + if (!$this->getProvider()->exists($parent)) { + return false; + } + + $children = $this->getProvider()->getChildren($parent); + + foreach($children as $childKey) { + + if (is_null($indexKey)) { + return true; + } + + $childIndex = $this->getIndex($childKey); + + if (is_null($childIndex)) { + continue; + } + + if ($childIndex < $indexKey) { + return true; + } + } + + return false; } } diff --git a/lib/internal/Magento/Framework/Lock/Factory.php b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php similarity index 98% rename from lib/internal/Magento/Framework/Lock/Factory.php rename to lib/internal/Magento/Framework/Lock/LockBackendFactory.php index 1414358ae42e..4c4896cb3562 100644 --- a/lib/internal/Magento/Framework/Lock/Factory.php +++ b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php @@ -17,7 +17,7 @@ /** * The factory to create object that implements LockManagerInterface */ -class Factory +class LockBackendFactory { /** * The Object Manager instance diff --git a/lib/internal/Magento/Framework/Lock/Proxy.php b/lib/internal/Magento/Framework/Lock/Proxy.php index e140078d05ab..b5f8eee0f2c4 100644 --- a/lib/internal/Magento/Framework/Lock/Proxy.php +++ b/lib/internal/Magento/Framework/Lock/Proxy.php @@ -17,7 +17,7 @@ class Proxy implements LockManagerInterface /** * The factory to create LockManagerInterface implementation * - * @var Factory + * @var LockBackendFactory */ private $factory; @@ -29,9 +29,9 @@ class Proxy implements LockManagerInterface private $locker; /** - * @param Factory $factory The factory to create LockManagerInterface implementation + * @param LockBackendFactory $factory The factory to create LockManagerInterface implementation */ - public function __construct(Factory $factory) + public function __construct(LockBackendFactory $factory) { $this->factory = $factory; } diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/ProxyTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/ProxyTest.php new file mode 100644 index 000000000000..c71dad701d71 --- /dev/null +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/ProxyTest.php @@ -0,0 +1,106 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Lock\Test\Unit; + +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; +use Magento\Framework\Lock\Proxy; +use Magento\Framework\Lock\LockBackendFactory; +use Magento\Framework\Lock\LockManagerInterface; + +/** + * @inheritdoc + */ +class ProxyTest extends TestCase +{ + /** + * @var LockBackendFactory|MockObject + */ + private $factoryMock; + + /** + * @var LockManagerInterface|MockObject + */ + private $lockerMock; + + /** + * @var Proxy + */ + private $proxy; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->factoryMock = $this->createMock(LockBackendFactory::class); + $this->lockerMock = $this->getMockForAbstractClass(LockManagerInterface::class); + $this->proxy = new Proxy($this->factoryMock); + } + + /** + * @return void + */ + public function testIsLocked() + { + $lockName = 'testLock'; + $this->factoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->lockerMock); + $this->lockerMock->expects($this->exactly(2)) + ->method('isLocked') + ->with($lockName) + ->willReturn(true); + + $this->assertTrue($this->proxy->isLocked($lockName)); + + // Call one more time to check that method Factory::create is called one time + $this->assertTrue($this->proxy->isLocked($lockName)); + } + + /** + * @return void + */ + public function testLock() + { + $lockName = 'testLock'; + $timeout = 123; + $this->factoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->lockerMock); + $this->lockerMock->expects($this->exactly(2)) + ->method('lock') + ->with($lockName, $timeout) + ->willReturn(true); + + $this->assertTrue($this->proxy->lock($lockName, $timeout)); + + // Call one more time to check that method Factory::create is called one time + $this->assertTrue($this->proxy->lock($lockName, $timeout)); + } + + /** + * @return void + */ + public function testUnlock() + { + $lockName = 'testLock'; + $this->factoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->lockerMock); + $this->lockerMock->expects($this->exactly(2)) + ->method('unlock') + ->with($lockName) + ->willReturn(true); + + $this->assertTrue($this->proxy->unlock($lockName)); + + // Call one more time to check that method Factory::create is called one time + $this->assertTrue($this->proxy->unlock($lockName)); + } +} From 974a6a53ca7d5ad237654468177d182ff1972b4d Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <korablov@adobe.com> Date: Tue, 19 Feb 2019 15:34:35 -0600 Subject: [PATCH 031/265] MAGETWO-98151: Add support ZooKeeper locks --- lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php | 2 +- lib/internal/Magento/Framework/Lock/LockBackendFactory.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php index d813f209ab96..884353eb63e5 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php @@ -131,7 +131,7 @@ public function lock(string $name, int $timeout = -1): bool public function unlock(string $name): bool { if (!isset($this->locks[$name])) { - return true; + return false; } return $this->getProvider()->delete($this->locks[$name]); diff --git a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php index 4c4896cb3562..ab839a6594a6 100644 --- a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php +++ b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php @@ -57,7 +57,6 @@ class LockBackendFactory self::LOCK_ZOOKEEPER => ZookeeperLock::class ]; - /** * @param ObjectManagerInterface $objectManager The Object Manager instance * @param DeploymentConfig $deploymentConfig The Application deployment configuration From bfb28abc6d51b4d38f01c9e43dc8670232e13877 Mon Sep 17 00:00:00 2001 From: Ravi Chandra <ravi.chandra@krishtechnolabs.com> Date: Wed, 20 Feb 2019 15:47:01 +0530 Subject: [PATCH 032/265] Correct spelling --- lib/internal/Magento/Framework/Setup/OldDbValidator.php | 2 +- lib/web/mage/adminhtml/globals.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Setup/OldDbValidator.php b/lib/internal/Magento/Framework/Setup/OldDbValidator.php index 4c224a6c713e..018b010e8fe4 100644 --- a/lib/internal/Magento/Framework/Setup/OldDbValidator.php +++ b/lib/internal/Magento/Framework/Setup/OldDbValidator.php @@ -13,7 +13,7 @@ /** * Old Validator for database * - * Used in order to support backward compatability of modules that are installed + * Used in order to support backward compatibility of modules that are installed * in old way (with Install/Upgrade Schema/Data scripts) */ class OldDbValidator implements UpToDateValidatorInterface diff --git a/lib/web/mage/adminhtml/globals.js b/lib/web/mage/adminhtml/globals.js index 12c97fdfcd2c..683606e57649 100644 --- a/lib/web/mage/adminhtml/globals.js +++ b/lib/web/mage/adminhtml/globals.js @@ -12,7 +12,7 @@ define([ /** * Set of a temporary methods used to provide - * backward compatability with a legacy code. + * backward compatibility with a legacy code. */ window.setLocation = function (url) { window.location.href = url; From 8d4710401b9f7b5702a4addfa573b79958f02d59 Mon Sep 17 00:00:00 2001 From: Ravi Chandra <ravi.chandra@krishtechnolabs.com> Date: Wed, 20 Feb 2019 15:55:32 +0530 Subject: [PATCH 033/265] Correct spelling --- app/code/Magento/Ui/view/base/web/js/lib/core/storage/local.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/lib/core/storage/local.js b/app/code/Magento/Ui/view/base/web/js/lib/core/storage/local.js index 87b5b2f5fe8f..038907c21224 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/core/storage/local.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/core/storage/local.js @@ -79,7 +79,7 @@ define([ /** * Extracts and parses data stored in localStorage by the - * key specified in 'root' varaible. + * key specified in 'root' variable. * * @returns {Object} */ From 38ef69db1bc29ec157ab9401a2a1eed548f70485 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <korablov@adobe.com> Date: Wed, 20 Feb 2019 11:32:24 -0600 Subject: [PATCH 034/265] MAGETWO-98151: Add support ZooKeeper locks --- .../Lock/Test/Unit/LockBackendFactoryTest.php | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php new file mode 100644 index 000000000000..cf8444dc0a0b --- /dev/null +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php @@ -0,0 +1,96 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Lock\Test\Unit; + +use Magento\Framework\Lock\Backend\Database as DatabaseLock; +use Magento\Framework\Lock\Backend\Zookeeper as ZookeeperLock; +use Magento\Framework\Lock\LockBackendFactory; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Lock\LockManagerInterface; +use Magento\Framework\App\DeploymentConfig; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; + +class LockBackendFactoryTest extends TestCase +{ + /** + * @var ObjectManagerInterface|MockObject + */ + private $objectManagerMock; + + /** + * @var DeploymentConfig|MockObject + */ + private $deploymentConfigMock; + + /** + * @var LockBackendFactory + */ + private $factory; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManagerMock = $this->getMockForAbstractClass(ObjectManagerInterface::class); + $this->deploymentConfigMock = $this->createMock(DeploymentConfig::class); + $this->factory = new LockBackendFactory($this->objectManagerMock, $this->deploymentConfigMock); + } + + /** + * @expectedException \Magento\Framework\Exception\RuntimeException + * @expectedExceptionMessage Unknown locks provider. + */ + public function testCreateWithException() + { + $this->deploymentConfigMock->expects($this->exactly(2)) + ->method('get') + ->withConsecutive(['locks/provider', LockBackendFactory::LOCK_DB], ['locks/config', []]) + ->willReturnOnConsecutiveCalls('someProvider', []); + + $this->factory->create(); + } + + /** + * @param string $lockProvider + * @param string $lockProviderClass + * @param array $config + * @dataProvider createDataProvider + */ + public function testCreate(string $lockProvider, string $lockProviderClass, array $config) + { + $lockManagerMock = $this->getMockForAbstractClass(LockManagerInterface::class); + $this->deploymentConfigMock->expects($this->exactly(2)) + ->method('get') + ->withConsecutive(['locks/provider', LockBackendFactory::LOCK_DB], ['locks/config', []]) + ->willReturnOnConsecutiveCalls($lockProvider, $config); + $this->objectManagerMock->expects($this->once()) + ->method('create') + ->with($lockProviderClass, $config) + ->willReturn($lockManagerMock); + + $this->assertSame($lockManagerMock, $this->factory->create()); + } + + public function createDataProvider(): array + { + return [ + [ + 'lockProvider' => LockBackendFactory::LOCK_DB, + 'lockProviderClass' => DatabaseLock::class, + 'config' => ['prefix' => 'somePrefix'], + ], + [ + 'lockProvider' => LockBackendFactory::LOCK_ZOOKEEPER, + 'lockProviderClass' => ZookeeperLock::class, + 'config' => ['host' => 'some host'], + ], + ]; + } +} From c865bdc25ff73168e2c2e0e093787ed766a2d068 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Wed, 20 Feb 2019 19:45:43 +0200 Subject: [PATCH 035/265] 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 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogUrlRewrite\Model\Products; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; +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; + +/** + * Save/Delete UrlRewrites by Product ID's and visibility + */ +class AdaptUrlRewritesToVisibilityAttribute +{ + /** + * @var CollectionFactory + */ + private $productCollectionFactory; + + /** + * @var ProductUrlRewriteGenerator + */ + private $urlRewriteGenerator; + + /** + * @var UrlPersistInterface + */ + private $urlPersist; + + /** + * @var ProductUrlPathGenerator + */ + 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; + } + + /** + * @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 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\CatalogUrlRewrite\Observer; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Framework\Event\ManagerInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException; +use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; + +/** + * @magentoAppArea adminhtml + * @magentoDbIsolation disabled + */ +class ProcessUrlRewriteOnChangeVisibilityObserverTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var ManagerInterface + */ + private $eventManager; + + /** + * Set up + */ + protected function setUp() + { + $this->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 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Setup\CategorySetup; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; + +\Magento\TestFramework\Helper\Bootstrap::getInstance() + ->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 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +use Magento\Framework\Exception\NoSuchEntityException; + +\Magento\TestFramework\Helper\Bootstrap::getInstance()->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 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Setup\CategorySetup; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; + +\Magento\TestFramework\Helper\Bootstrap::getInstance() + ->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 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +use Magento\Framework\Exception\NoSuchEntityException; + +\Magento\TestFramework\Helper\Bootstrap::getInstance()->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 0f3c125972654768727dfa130c8badfd49e8cd68 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <korablov@adobe.com> Date: Wed, 20 Feb 2019 12:07:26 -0600 Subject: [PATCH 036/265] MAGETWO-98151: Add support ZooKeeper locks --- lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php index 884353eb63e5..02e273954569 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php @@ -126,7 +126,8 @@ public function lock(string $name, int $timeout = -1): bool } /** - * @inheritdoc + * {@inheritdoc} + * @throws RuntimeException */ public function unlock(string $name): bool { @@ -138,7 +139,8 @@ public function unlock(string $name): bool } /** - * @inheritdoc + * {@inheritdoc} + * @throws RuntimeException */ public function isLocked(string $name): bool { From 9accdd4f278c999c3e1d7b005708494c738f5187 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <korablov@adobe.com> Date: Wed, 20 Feb 2019 15:35:22 -0600 Subject: [PATCH 037/265] MAGETWO-98151: Add support ZooKeeper locks --- .../testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php | 0 lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php | 2 +- .../Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php | 0 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php create mode 100644 lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php diff --git a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php index 02e273954569..d19ba0dd947b 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php @@ -242,7 +242,7 @@ private function isAnyLock(string $fullKey, int $indexKey = null): bool $children = $this->getProvider()->getChildren($parent); - foreach($children as $childKey) { + foreach ($children as $childKey) { if (is_null($indexKey)) { return true; diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php new file mode 100644 index 000000000000..e69de29bb2d1 From ba4f6baaea9cf2740f8e0c87ac2af192958742ce Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <korablov@adobe.com> Date: Wed, 20 Feb 2019 15:37:06 -0600 Subject: [PATCH 038/265] MAGETWO-98151: Add support ZooKeeper locks --- .../Lock/Test/Unit/Backend/ZookeeperTest.php | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php index e69de29bb2d1..c004179aa524 100644 --- a/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php @@ -0,0 +1,54 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Lock\Test\Unit\Backend; + +use Magento\Framework\Lock\Backend\Zookeeper as ZookeeperProvider; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; + +class ZookeeperTest extends TestCase +{ + /** + * @var \Zookeeper|MockObject + */ + private $zookeeperMock; + + /** + * @var ZookeeperProvider + */ + private $zookeeperProvider; + + /** + * @var string + */ + private $host = 'localhost:123'; + + /** + * @var string + */ + private $path = '/some/path'; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->zookeeperProvider = new ZookeeperProvider($this->host, '/some/path/'); + } + + /** + * @expectedException \Magento\Framework\Exception\RuntimeException + * @expectedExceptionMessage The path needs to be a non-empty string. + */ + public function testConstructionWithException() + { + $this->zookeeperProvider = new ZookeeperProvider('some host', ''); + } + + +} From d1752d3cab4d04af1e1bbcf1b968450ed9c735eb Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <korablov@adobe.com> Date: Wed, 20 Feb 2019 16:00:40 -0600 Subject: [PATCH 039/265] MAGETWO-98151: Add support ZooKeeper locks --- lib/internal/Magento/Framework/Lock/LockBackendFactory.php | 4 ++++ .../Framework/Lock/Test/Unit/Backend/ZookeeperTest.php | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php index ab839a6594a6..e2119ca3eee1 100644 --- a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php +++ b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php @@ -84,6 +84,10 @@ public function create(): LockManagerInterface throw new RuntimeException(new Phrase('Unknown locks provider.')); } + if (self::LOCK_ZOOKEEPER === $provider && !extension_loaded(self::LOCK_ZOOKEEPER)) { + throw new RuntimeException(new Phrase('php extension Zookeeper is not installed.')); + } + return $this->objectManager->create($this->lockers[$provider], $config); } } diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php index c004179aa524..c22c49d3427c 100644 --- a/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php @@ -38,6 +38,9 @@ class ZookeeperTest extends TestCase */ protected function setUp() { + if (!extension_loaded('zookeeper')) { + $this->markTestSkipped('Test was skipped because php extension Zookeeper is not installed.'); + } $this->zookeeperProvider = new ZookeeperProvider($this->host, '/some/path/'); } @@ -49,6 +52,4 @@ public function testConstructionWithException() { $this->zookeeperProvider = new ZookeeperProvider('some host', ''); } - - } From 7710c226f8b5a193aedda196e34d14e3a13f96af Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <korablov@adobe.com> Date: Wed, 20 Feb 2019 16:18:23 -0600 Subject: [PATCH 040/265] MAGETWO-98151: Add support ZooKeeper locks --- .../Lock/Test/Unit/LockBackendFactoryTest.php | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php index cf8444dc0a0b..f3b141e9f902 100644 --- a/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php @@ -78,19 +78,27 @@ public function testCreate(string $lockProvider, string $lockProviderClass, arra $this->assertSame($lockManagerMock, $this->factory->create()); } + /** + * @return array + */ public function createDataProvider(): array { - return [ - [ + $data = [ + 'db' => [ 'lockProvider' => LockBackendFactory::LOCK_DB, 'lockProviderClass' => DatabaseLock::class, 'config' => ['prefix' => 'somePrefix'], ], - [ + ]; + + if (extension_loaded('zookeeper')) { + $data['zookeeper'] = [ 'lockProvider' => LockBackendFactory::LOCK_ZOOKEEPER, 'lockProviderClass' => ZookeeperLock::class, 'config' => ['host' => 'some host'], - ], - ]; + ]; + } + + return $data; } } From ecb572bc764ebd089e334c303bd386acb447ccec Mon Sep 17 00:00:00 2001 From: niravkrish <nirav.patel@krishtechnolabs.com> Date: Thu, 21 Feb 2019 12:02:47 +0530 Subject: [PATCH 041/265] Fixed Inline block edit identifier validation. --- .../Cms/view/adminhtml/ui_component/cms_block_listing.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_block_listing.xml b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_block_listing.xml index 9f886f6f1345..793fc7d26cb4 100644 --- a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_block_listing.xml +++ b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_block_listing.xml @@ -146,7 +146,6 @@ <editor> <validation> <rule name="required-entry" xsi:type="boolean">true</rule> - <rule name="validate-xml-identifier" xsi:type="boolean">true</rule> </validation> <editorType>text</editorType> </editor> From 1c608d7b54bf08d4fe592c2a0153502c9a9527d4 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Thu, 21 Feb 2019 22:08:19 +0200 Subject: [PATCH 042/265] 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 6584286dd68a29940dca3300dd3368fc2e9ec81e Mon Sep 17 00:00:00 2001 From: dharmesh vaja <dharmesh.vaja@rocketbazaar.com> Date: Fri, 22 Feb 2019 11:33:41 +0530 Subject: [PATCH 043/265] Fixed issue-21396 --- app/code/Magento/Customer/Block/Address/Grid.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Customer/Block/Address/Grid.php b/app/code/Magento/Customer/Block/Address/Grid.php index de6767a0ef92..86085b3bb7c5 100644 --- a/app/code/Magento/Customer/Block/Address/Grid.php +++ b/app/code/Magento/Customer/Block/Address/Grid.php @@ -237,6 +237,7 @@ private function getAddressCollection(): \Magento\Customer\Model\ResourceModel\A /** @var \Magento\Customer\Model\ResourceModel\Address\Collection $collection */ $collection = $this->addressCollectionFactory->create(); $collection->setOrder('entity_id', 'desc') + ->addFieldToFilter('entity_id', array('nin' => array($this->getDefaultBilling(), $this->getDefaultShipping()))) ->setCustomerFilter([$this->getCustomer()->getId()]); $this->addressCollection = $collection; } From 612a650492e51e17ed5f8974a93dedf4b4e05c32 Mon Sep 17 00:00:00 2001 From: Evgeny Petrov <evgeny_petrov@epam.com> Date: Thu, 21 Feb 2019 12:03:19 +0300 Subject: [PATCH 044/265] MAGETWO-57934: [GitHub] Can't use "configurable" as group name in attribute sets M2.1 #6123 --- .../Magento/ConfigurableProduct/etc/di.xml | 7 +++ .../Eav/Model/Entity/Attribute/Group.php | 51 ++++++++++++------- .../Unit/Model/Entity/Attribute/GroupTest.php | 5 +- app/code/Magento/Eav/etc/di.xml | 1 - 4 files changed, 45 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/etc/di.xml b/app/code/Magento/ConfigurableProduct/etc/di.xml index 0ae9ffde66f4..86717e42fa54 100644 --- a/app/code/Magento/ConfigurableProduct/etc/di.xml +++ b/app/code/Magento/ConfigurableProduct/etc/di.xml @@ -245,4 +245,11 @@ <type name="Magento\SalesRule\Model\Rule\Condition\Product"> <plugin name="apply_rule_on_configurable_children" type="Magento\ConfigurableProduct\Plugin\SalesRule\Model\Rule\Condition\Product" /> </type> + <type name="Magento\Eav\Model\Entity\Attribute\Group"> + <arguments> + <argument name="reservedSystemNames" xsi:type="array"> + <item name="configurable" xsi:type="string">configurable</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Group.php b/app/code/Magento/Eav/Model/Entity/Attribute/Group.php index 0b6ac2b998de..0d24ea24deae 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Group.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Group.php @@ -3,11 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Eav\Model\Entity\Attribute; +use Magento\Eav\Api\Data\AttributeGroupExtensionInterface; use Magento\Framework\Api\AttributeValueFactory; +use Magento\Framework\Exception\LocalizedException; /** + * Entity attribute group model + * * @api * @method int getSortOrder() * @method \Magento\Eav\Model\Entity\Attribute\Group setSortOrder(int $value) @@ -27,6 +32,11 @@ class Group extends \Magento\Framework\Model\AbstractExtensibleModel implements */ private $translitFilter; + /** + * @var array + */ + private $reservedSystemNames; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -35,7 +45,8 @@ class Group extends \Magento\Framework\Model\AbstractExtensibleModel implements * @param \Magento\Framework\Filter\Translit $translitFilter * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection - * @param array $data + * @param array $reservedSystemNames (optional) + * @param array $data (optional) */ public function __construct( \Magento\Framework\Model\Context $context, @@ -45,6 +56,7 @@ public function __construct( \Magento\Framework\Filter\Translit $translitFilter, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, + array $reservedSystemNames = [], array $data = [] ) { parent::__construct( @@ -54,8 +66,10 @@ public function __construct( $customAttributeFactory, $resource, $resourceCollection, + $reservedSystemNames, $data ); + $this->reservedSystemNames = $reservedSystemNames; $this->translitFilter = $translitFilter; } @@ -74,6 +88,7 @@ protected function _construct() * Checks if current attribute group exists * * @return bool + * @throws LocalizedException * @codeCoverageIgnore */ public function itemExists() @@ -85,6 +100,7 @@ public function itemExists() * Delete groups * * @return $this + * @throws LocalizedException * @codeCoverageIgnore */ public function deleteGroups() @@ -110,9 +126,10 @@ public function beforeSave() ), '-' ); - if (empty($attributeGroupCode)) { - // in the following code md5 is not used for security purposes - $attributeGroupCode = md5($groupName); + $isReservedSystemName = in_array(strtolower($attributeGroupCode), $this->reservedSystemNames); + if (empty($attributeGroupCode) || $isReservedSystemName) { + // in the following code sha1 is not used for security purposes + $attributeGroupCode = sha1(strtolower($groupName)); } $this->setAttributeGroupCode($attributeGroupCode); } @@ -121,7 +138,8 @@ public function beforeSave() } /** - * {@inheritdoc} + * @inheritdoc + * * @codeCoverageIgnoreStart */ public function getAttributeGroupId() @@ -130,7 +148,7 @@ public function getAttributeGroupId() } /** - * {@inheritdoc} + * @inheritdoc */ public function getAttributeGroupName() { @@ -138,7 +156,7 @@ public function getAttributeGroupName() } /** - * {@inheritdoc} + * @inheritdoc */ public function getAttributeSetId() { @@ -146,7 +164,7 @@ public function getAttributeSetId() } /** - * {@inheritdoc} + * @inheritdoc */ public function setAttributeGroupId($attributeGroupId) { @@ -154,7 +172,7 @@ public function setAttributeGroupId($attributeGroupId) } /** - * {@inheritdoc} + * @inheritdoc */ public function setAttributeGroupName($attributeGroupName) { @@ -162,7 +180,7 @@ public function setAttributeGroupName($attributeGroupName) } /** - * {@inheritdoc} + * @inheritdoc */ public function setAttributeSetId($attributeSetId) { @@ -170,9 +188,9 @@ public function setAttributeSetId($attributeSetId) } /** - * {@inheritdoc} + * @inheritdoc * - * @return \Magento\Eav\Api\Data\AttributeGroupExtensionInterface|null + * @return AttributeGroupExtensionInterface|null */ public function getExtensionAttributes() { @@ -180,14 +198,13 @@ public function getExtensionAttributes() } /** - * {@inheritdoc} + * @inheritdoc * - * @param \Magento\Eav\Api\Data\AttributeGroupExtensionInterface $extensionAttributes + * @param AttributeGroupExtensionInterface $extensionAttributes * @return $this */ - public function setExtensionAttributes( - \Magento\Eav\Api\Data\AttributeGroupExtensionInterface $extensionAttributes - ) { + public function setExtensionAttributes(AttributeGroupExtensionInterface $extensionAttributes) + { return $this->_setExtensionAttributes($extensionAttributes); } diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/GroupTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/GroupTest.php index d4c91e98d960..986ab517ceca 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/GroupTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/GroupTest.php @@ -40,6 +40,7 @@ protected function setUp() 'resource' => $this->resourceMock, 'translitFilter' => $translitFilter, 'context' => $contextMock, + 'reservedSystemNames' => ['configurable'], ]; $objectManager = new ObjectManager($this); $this->model = $objectManager->getObject( @@ -67,7 +68,9 @@ public function attributeGroupCodeDataProvider() { return [ ['General Group', 'general-group'], - ['///', md5('///')], + ['configurable', sha1('configurable')], + ['configurAble', sha1('configurable')], + ['///', sha1('///')], ]; } } diff --git a/app/code/Magento/Eav/etc/di.xml b/app/code/Magento/Eav/etc/di.xml index a4c89dcfab2a..db6f9b0a64f9 100644 --- a/app/code/Magento/Eav/etc/di.xml +++ b/app/code/Magento/Eav/etc/di.xml @@ -210,4 +210,3 @@ </arguments> </type> </config> - From c39751f8cc06a1654e6f57418672b4e63c6be247 Mon Sep 17 00:00:00 2001 From: Dharmesh Vaja <dharmesh.vaja@krishtechnolabs.com> Date: Mon, 25 Feb 2019 12:10:15 +0530 Subject: [PATCH 045/265] Updated code --- app/code/Magento/Customer/Block/Address/Grid.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Customer/Block/Address/Grid.php b/app/code/Magento/Customer/Block/Address/Grid.php index 86085b3bb7c5..492343acfe30 100644 --- a/app/code/Magento/Customer/Block/Address/Grid.php +++ b/app/code/Magento/Customer/Block/Address/Grid.php @@ -236,9 +236,9 @@ private function getAddressCollection(): \Magento\Customer\Model\ResourceModel\A } /** @var \Magento\Customer\Model\ResourceModel\Address\Collection $collection */ $collection = $this->addressCollectionFactory->create(); - $collection->setOrder('entity_id', 'desc') - ->addFieldToFilter('entity_id', array('nin' => array($this->getDefaultBilling(), $this->getDefaultShipping()))) - ->setCustomerFilter([$this->getCustomer()->getId()]); + $collection->setOrder('entity_id', 'desc'); + $collection->addFieldToFilter('entity_id', array('nin' => array($this->getDefaultBilling(), $this->getDefaultShipping()))); + $collection->setCustomerFilter([$this->getCustomer()->getId()]); $this->addressCollection = $collection; } return $this->addressCollection; From d04482cbeb43abc70a8ddbed12096097a235b986 Mon Sep 17 00:00:00 2001 From: Evgeny Petrov <evgeny_petrov@epam.com> Date: Mon, 25 Feb 2019 11:25:14 +0300 Subject: [PATCH 046/265] MAGETWO-57934: [GitHub] Can't use "configurable" as group name in attribute sets M2.1 #6123 --- app/code/Magento/Eav/Model/Entity/Attribute/Group.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Group.php b/app/code/Magento/Eav/Model/Entity/Attribute/Group.php index 0d24ea24deae..1f718f4277cc 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Group.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Group.php @@ -45,8 +45,8 @@ class Group extends \Magento\Framework\Model\AbstractExtensibleModel implements * @param \Magento\Framework\Filter\Translit $translitFilter * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection - * @param array $reservedSystemNames (optional) * @param array $data (optional) + * @param array $reservedSystemNames (optional) */ public function __construct( \Magento\Framework\Model\Context $context, @@ -56,8 +56,8 @@ public function __construct( \Magento\Framework\Filter\Translit $translitFilter, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $reservedSystemNames = [], - array $data = [] + array $data = [], + array $reservedSystemNames = [] ) { parent::__construct( $context, @@ -66,7 +66,6 @@ public function __construct( $customAttributeFactory, $resource, $resourceCollection, - $reservedSystemNames, $data ); $this->reservedSystemNames = $reservedSystemNames; From 5c93ba2d7924291e704032969b9b9390cdc7fcca Mon Sep 17 00:00:00 2001 From: Kavitha <kanair@adobe.com> Date: Mon, 25 Feb 2019 12:25:56 -0600 Subject: [PATCH 047/265] MC-4903: Convert UpdateProductUrlRewriteEntityTest to MFTF --- .../AdminUrlRewriteActionGroup.xml | 13 +++++ .../AdminUrlRewriteGridActionGroup.xml | 14 ++++++ .../Test/Mftf/Data/UrlRewriteData.xml | 27 +++++++++++ .../Test/Mftf/MetaData/url_rewrite-meta.xml | 20 ++++++++ ...tUrlRewriteAndAddTemporaryRedirectTest.xml | 48 +++++++++++++++++++ 5 files changed, 122 insertions(+) create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Data/UrlRewriteData.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/MetaData/url_rewrite-meta.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteActionGroup.xml index 71475acd8b06..5886b40c4547 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteActionGroup.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteActionGroup.xml @@ -75,4 +75,17 @@ <click selector="{{AdminUrlRewriteEditSection.saveButton}}" stepKey="clickOnSaveButton"/> <seeElement selector="{{AdminUrlRewriteIndexSection.successMessage}}" stepKey="seeSuccessSaveMessage"/> </actionGroup> + <actionGroup name="AdminUpdateUrlRewrite"> + <arguments> + <argument name="requestPath" type="string"/> + <argument name="redirectTypeValue" type="string"/> + <argument name="description" type="string"/> + </arguments> + <fillField selector="{{AdminUrlRewriteEditSection.requestPath}}" userInput="{{requestPath}}" stepKey="fillRequestPath"/> + <click selector="{{AdminUrlRewriteEditSection.redirectType}}" stepKey="selectRedirectType"/> + <click selector="{{AdminUrlRewriteEditSection.redirectTypeValue('redirectTypeValue')}}" stepKey="selectRedirectTypeValue"/> + <fillField selector="{{AdminUrlRewriteEditSection.description}}" userInput="{{description}}" stepKey="fillDescription"/> + <click selector="{{AdminUrlRewriteEditSection.saveButton}}" stepKey="clickOnSaveButton"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.successMessage}}" stepKey="seeSuccessSaveMessage"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteGridActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteGridActionGroup.xml index f053d18e79c3..221dd7f9d316 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteGridActionGroup.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteGridActionGroup.xml @@ -72,4 +72,18 @@ <waitForPageLoad stepKey="waitForPageToLoad3"/> <see selector="{{AdminUrlRewriteIndexSection.successMessage}}" userInput="You deleted the URL rewrite." stepKey="seeSuccessMessage"/> </actionGroup> + <actionGroup name="AdminSearchAndSelectUrlRewriteInGrid"> + <arguments> + <argument name="requestPath" type="string"/> + </arguments> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="openUrlRewriteEditPage"/> + <waitForPageLoad stepKey="waitForUrlRewriteEditPageToLoad"/> + <click selector="{{AdminUrlRewriteIndexSection.resetButton}}" stepKey="clickOnResetButton"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="{{requestPath}}" stepKey="fillRedirectPathFilter"/> + <click selector="{{AdminUrlRewriteIndexSection.searchButton}}" stepKey="clickOnSearchButton"/> + <waitForPageLoad stepKey="waitForPageToLoad1"/> + <click selector="{{AdminUrlRewriteIndexSection.editButton('1')}}" stepKey="clickOnEditButton"/> + <waitForPageLoad stepKey="waitForEditPageToLoad"/> + </actionGroup> </actionGroups> \ No newline at end of file diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Data/UrlRewriteData.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Data/UrlRewriteData.xml new file mode 100644 index 000000000000..942882be259f --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Data/UrlRewriteData.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="defaultUrlRewrite" type="urlRewrite"> + <data key="request_path" unique="prefix">test-test-test.html</data> + <data key="target_path">http://www.example.com/</data> + <data key="redirect_type">302</data> + <data key="redirect_type_label">Temporary (302)</data> + <data key="store_id">1</data> + <data key="store">Default Store View</data> + </entity> + <entity name="updateUrlRewrite" type="urlRewrite"> + <data key="request_path" unique="prefix">test-aspx-test.aspx</data> + <data key="target_path">http://www.example.com/</data> + <data key="redirect_type">302</data> + <data key="redirect_type_label">Temporary (302)</data> + <data key="store_id">1</data> + <data key="store">Default Store View</data> + </entity> +</entities> \ No newline at end of file diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/MetaData/url_rewrite-meta.xml b/app/code/Magento/UrlRewrite/Test/Mftf/MetaData/url_rewrite-meta.xml new file mode 100644 index 000000000000..84cf00ac0899 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/MetaData/url_rewrite-meta.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="CreateUrlRewrite" dataType="urlRewrite" type="create" auth="adminFormKey" url="/admin/url_rewrite/save/" method="POST" successRegex="/messages-message-success/"> + <!--<object key="urlRewrite" dataType="urlRewrite">--> + <field key="store_id">integer</field> + <field key="redirect_type">integer</field> + <field key="request_path">string</field> + <field key="target_path">string</field> + <field key="description">string</field> + <!--</object>--> + </operation> +</operations> \ No newline at end of file diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml new file mode 100644 index 000000000000..cbb3680f3734 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest"> + <annotations> + <stories value="Update URL rewrite"/> + <title value="Update Product URL Rewrites"/> + <description value="Login as Admin and update product UrlRewrite and add Temporary redirect type "/> + <testCaseId value="MC-5351"/> + <severity value="CRITICAL"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <createData entity="defaultSimpleProduct" stepKey="createProduct"/> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + </before> + <after> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Search and Select Edit option for created product in grid --> + <actionGroup ref="AdminSearchAndSelectUrlRewriteInGrid" stepKey="editUrlRewrite"> + <argument name="requestPath" value="$$createProduct.name$$"/> + </actionGroup> + + <!-- Open UrlRewrite Edit page and update the fields --> + <actionGroup ref="AdminUpdateUrlRewrite" stepKey="updateCategoryUrlRewrite"> + <argument name="requestPath" value="{{defaultUrlRewrite.request_path}}"/> + <argument name="redirectTypeValue" value="Temporary (302)"/> + <argument name="description" value="Update Product Url Rewrite"/> + </actionGroup> + + <!-- Assert product Url Rewrite in StoreFront --> + <actionGroup ref="AssertStorefrontProductRedirect" stepKey="assertProductUrlRewriteInStoreFront"> + <argument name="productName" value="$$createProduct.name$$"/> + <argument name="productSku" value="$$createProduct.sku$$"/> + <argument name="productRequestPath" value="{{defaultUrlRewrite.request_path}}"/> + </actionGroup> + </test> +</tests> From c695c007c3a1d0b3fa0511204e4ef8347e485e26 Mon Sep 17 00:00:00 2001 From: Kavitha <kanair@adobe.com> Date: Mon, 25 Feb 2019 16:26:27 -0600 Subject: [PATCH 048/265] MC-4455: Convert CreateDuplicateUrlProductEntity to MFTF --- .../ActionGroup/AdminProductActionGroup.xml | 25 +++++++++++++++ .../Test/AdminCreateDuplicateProductTest.xml | 31 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index 3afdc41888c7..d2d95d811c43 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -430,4 +430,29 @@ <click selector="{{AdminAddUpSellProductsModalSection.AddSelectedProductsButton}}" stepKey="addRelatedProductSelected"/> <waitForPageLoad stepKey="waitForPageToLoad1"/> </actionGroup> + <actionGroup name="createDuplicateProduct"> + <arguments> + <argument name="product" defaultValue="$$createSimpleProduct$$" /> + <argument name="quantity" defaultValue="defaultSimpleProduct.quantity"/> + </arguments> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="openProductIndexPage"/> + <waitForPageLoad stepKey="waitForProductIndexPageToLoad"/> + <click selector="{{AdminProductGridActionSection.addProductBtn}}" stepKey="clickOnAddNewProduct"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{product.name}}" stepKey="fillProductName"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{product.sku}}" stepKey="fillProductSku"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{product.price}}" stepKey="fillProductPrice"/> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{quantity}}" stepKey="fillProductQty"/> + <selectOption selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{product.status}}" stepKey="selectStockStatus"/> + <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeight"/> + <fillField selector="{{AdminProductFormSection.productWeight}}" userInput="{{product.weight}}" stepKey="fillProductWeight"/> + <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="scrollToSectionHeader"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> + <fillField userInput="{{product.custom_attributes[url_key]}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> + <scrollToTopOfPage stepKey="scrollTopPageProduct"/> + <waitForElementVisible selector="{{AdminProductFormActionSection.saveButton}}" stepKey="waitForSaveProductButton" /> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> + <waitForPageLoad stepKey="waitForProductToSave"/> + <see selector="{{AdminProductFormSection.successMessage}}" userInput="The value specified in the URL Key field would generate a URL that already exists." stepKey="seeErrorMessage"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml new file mode 100644 index 000000000000..81a0916be6e7 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateDuplicateProductTest"> + <annotations> + <stories value="Create Product"/> + <title value="CreateDuplicateUrlProductEntityTestVariation1"/> + <description value="Login as admin and create duplicate Product"/> + <testCaseId value="MC-14714"/> + <severity value="CRITICAL"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <createData entity="defaultSimpleProduct" stepKey="createSimpleProduct"/> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + </after> + + <!-- Create duplicate Product and Save the Product --> + <actionGroup ref="createDuplicateProduct" stepKey="createDuplicateProduct"/> + </test> +</tests> From 7127e16d5386956f5fa7b91f58c0995d15d52a36 Mon Sep 17 00:00:00 2001 From: dharmesh vaja <dharmesh.vaja@rocketbazaar.com> Date: Tue, 26 Feb 2019 11:54:13 +0530 Subject: [PATCH 049/265] Fixed-Additional addresses DataTable Pagination count displaying wrong --- .../Magento/Customer/Test/Unit/Block/Address/GridTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Block/Address/GridTest.php b/app/code/Magento/Customer/Test/Unit/Block/Address/GridTest.php index 31bcc3761230..9c511efd22c4 100644 --- a/app/code/Magento/Customer/Test/Unit/Block/Address/GridTest.php +++ b/app/code/Magento/Customer/Test/Unit/Block/Address/GridTest.php @@ -81,7 +81,7 @@ protected function setUp() public function testGetChildHtml() { $customerId = 1; - + $outputString = 'OutputString'; /** @var \Magento\Framework\View\Element\BlockInterface|\PHPUnit_Framework_MockObject_MockObject $block */ $block = $this->getMockBuilder(\Magento\Framework\View\Element\BlockInterface::class) ->setMethods(['setCollection']) @@ -113,7 +113,7 @@ public function testGetChildHtml() $block->expects($this->atLeastOnce())->method('setCollection')->with($addressCollection)->willReturnSelf(); $this->gridBlock->setNameInLayout('NameInLayout'); $this->gridBlock->setLayout($layout); - $this->assertEquals('OutputString', $this->gridBlock->getChildHtml('pager')); + $this->assertEquals($outputString, $this->gridBlock->getChildHtml('pager')); } /** From 692afe768d22bb4c5b536eec06b7fa802e394529 Mon Sep 17 00:00:00 2001 From: Kavitha <kanair@adobe.com> Date: Tue, 26 Feb 2019 10:25:42 -0600 Subject: [PATCH 050/265] MC-4454: Convert CreateDuplicateUrlCategoryEntityTest to MFTF --- .../ActionGroup/AdminCategoryActionGroup.xml | 13 ++++++ .../Section/AdminCategoryMessagesSection.xml | 1 + .../Test/AdminCreateDuplicateCategoryTest.xml | 40 +++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateCategoryTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml index 86986265bae2..c658e8b359f8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml @@ -275,4 +275,17 @@ <waitForPageLoad stepKey="waitForPageToLoad"/> <waitForElementVisible selector="{{AdminCategoryContentSection.categoryPageTitle}}" stepKey="waitForCategoryTitle"/> </actionGroup> + <actionGroup name="adminFillAndSaveCategoryForm"> + <arguments> + <argument name="category" type="string"/> + </arguments> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="$$category.name$$" stepKey="enterCategoryName"/> + <scrollTo selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="scrollToSearchEngineOptimization"/> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSEO"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="$$category.custom_attributes[url_key]$$" stepKey="enterURLKey"/> + <scrollToTopOfPage stepKey="scrollToTheTopOfPage"/> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> + <waitForPageLoad stepKey="waitForPageToLoad1"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryMessagesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryMessagesSection.xml index fee86ca1caa2..ea4f4bf53eb7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryMessagesSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryMessagesSection.xml @@ -10,5 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCategoryMessagesSection"> <element name="SuccessMessage" type="text" selector=".message-success"/> + <element name="errorMessage" type="text" selector="//div[@class='message message-error error']"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateCategoryTest.xml new file mode 100644 index 000000000000..4865e3a89b67 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateCategoryTest.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateDuplicateCategoryTest"> + <annotations> + <stories value="Create category"/> + <title value="CreateDuplicateUrlCategoryEntityTestVariation1_Subcategory_RequiredFields"/> + <description value="Login as admin and create duplicate category"/> + <testCaseId value="MC-14702"/> + <severity value="CRITICAL"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <createData entity="SimpleSubCategory" stepKey="category"/> + </before> + <after> + <deleteData createDataKey="category" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Open Category Page and select Add category --> + <actionGroup ref="goToCreateCategoryPage" stepKey="goToCategoryPage"/> + + <!-- Fill the Category form with same name and urlKey as initially created category(SimpleSubCategory) --> + <actionGroup ref="adminFillAndSaveCategoryForm" stepKey="fillCategoryForm"> + <argument name="category" value="category"/> + </actionGroup> + + <!-- Assert error message --> + <see selector="{{AdminCategoryMessagesSection.errorMessage}}" userInput="The value specified in the URL Key field would generate a URL that already exists." stepKey="seeErrorMessage"/> + </test> +</tests> From caabef2fe931aa69b4955ff8265afa52cb61a361 Mon Sep 17 00:00:00 2001 From: Adarsh Khatri <me.adarshkhatri@gmail.com> Date: Wed, 27 Feb 2019 17:14:44 +1100 Subject: [PATCH 051/265] Update price-bundle.js By default tier price is sorted by "price_id". With this tier price calculation while displaying in bundle product is wrong. Need to sort tier price by "price_qty". Issue created: https://github.com/magento/magento2/issues/21467 --- app/code/Magento/Bundle/view/base/web/js/price-bundle.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js index e56cc6f32d80..a832390b83dc 100644 --- a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js +++ b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js @@ -375,6 +375,11 @@ define([ var tiers = optionConfig.tierPrice, magicKey = _.keys(oneItemPrice)[0], lowest = false; + + //sorting based on "price_qty" + tiers.sort(function(a, b) { + return a.price_qty - b.price_qty; + }); _.each(tiers, function (tier, index) { if (tier['price_qty'] > qty) { From 4c1c65303c3b60de437cb84d882b16718f2f085a Mon Sep 17 00:00:00 2001 From: Adarsh Khatri <me.adarshkhatri@gmail.com> Date: Wed, 27 Feb 2019 19:28:16 +1100 Subject: [PATCH 052/265] Update price-bundle.js By default tier price is sorted by "price_id". With this tier price calculation while displaying in bundle product is wrong. Need to sort tier price by "price_qty". --- app/code/Magento/Bundle/view/base/web/js/price-bundle.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js index a832390b83dc..d298c4d6845f 100644 --- a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js +++ b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js @@ -377,9 +377,11 @@ define([ lowest = false; //sorting based on "price_qty" - tiers.sort(function(a, b) { - return a.price_qty - b.price_qty; - }); + if(tiers){ + tiers.sort(function (a, b) { + return a.price_qty - b.price_qty; + }); + } _.each(tiers, function (tier, index) { if (tier['price_qty'] > qty) { From 64b296e967a916cc4b335f2e10f758fd3dedfc6b Mon Sep 17 00:00:00 2001 From: Maria Kovdrysh <kovdrysh@adobe.com> Date: Wed, 27 Feb 2019 11:22:03 -0600 Subject: [PATCH 053/265] MC-4426: Convert ExportProductsTest to MFTF --- .../Mftf/Data/CatalogSpecialPriceData.xml | 20 +++ .../Metadata/catalog_special_price-meta.xml | 22 +++ .../ActionGroup/AdminExportActionGroup.xml | 58 ++++++++ .../Test/AdminExportBundleProductTest.xml | 120 +++++++++++++++++ ...portGroupedProductWithSpecialPriceTest.xml | 84 ++++++++++++ ...figurableProductsWithCustomOptionsTest.xml | 111 +++++++++++++++ ...igurableProductsWithAssignedImagesTest.xml | 127 ++++++++++++++++++ ...ableProductAssignedToCustomWebsiteTest.xml | 109 +++++++++++++++ ...rtSimpleProductWithCustomAttributeTest.xml | 61 +++++++++ .../Section/AdminExportAttributeSection.xml | 6 + 10 files changed, 718 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Data/CatalogSpecialPriceData.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_special_price-meta.xml create mode 100644 app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml create mode 100644 app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml create mode 100644 app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml create mode 100644 app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml create mode 100644 app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml create mode 100644 app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml create mode 100644 app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CatalogSpecialPriceData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogSpecialPriceData.xml new file mode 100644 index 000000000000..ab4cab2cda5a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogSpecialPriceData.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="specialProductPrice" type="catalogSpecialPrice"> + <data key="price">99.99</data> + <data key="store_id">0</data> + <var key="sku" entityType="product2" entityKey="sku" /> + </entity> + <entity name="specialProductPrice2" type="catalogSpecialPrice"> + <data key="price">55.55</data> + <data key="store_id">0</data> + <var key="sku" entityType="product" entityKey="sku" /> + </entity> +</entities> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_special_price-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_special_price-meta.xml new file mode 100644 index 000000000000..27c9d2ede0f4 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/catalog_special_price-meta.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="catalogSpecialPrice" dataType="catalogSpecialPrice" type="create" auth="adminOauth" url="/V1/products/special-price" method="POST"> + <contentType>application/json</contentType> + <object key="prices" dataType="catalogSpecialPrice"> + <object dataType="catalogSpecialPrice" key="0"> + <field key="price">number</field> + <field key="store_id">integer</field> + <field key="sku">string</field> + <field key="price_from">string</field> + <field key="price_to">string</field> + </object> + </object> + </operation> +</operations> \ No newline at end of file diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml new file mode 100644 index 000000000000..21b81648ea47 --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + + <!-- Export products using filtering by attribute --> + <actionGroup name="fillFilterExport"> + <arguments> + <argument name="attribute"/> + <argument name="attributeData"/> + </arguments> + <selectOption selector="{{AdminExportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> + <waitForElementVisible selector="{{AdminExportMainSection.entityAttributes}}" stepKey="waitForElementVisible"/> + <scrollTo selector="{{AdminExportAttributeSection.chooseAttribute('attribute')}}" stepKey="scrollToAttribute" /> + <checkOption selector="{{AdminExportAttributeSection.chooseAttribute('attribute')}}" stepKey="selectAttribute"/> + <fillField selector="{{AdminExportAttributeSection.fillFilter('attribute')}}" userInput="{{attributeData}}" stepKey="setDataInField"/> + <waitForPageLoad stepKey="waitForUserInput"/> + <scrollTo selector="{{AdminExportAttributeSection.continueBtn}}" stepKey="scrollToContinue" /> + <click selector="{{AdminExportAttributeSection.continueBtn}}" stepKey="clickContinueButton"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="Message is added to queue, wait to get your file soon" stepKey="seeSuccessMessage"/> + </actionGroup> + + <!-- Export products without filtering --> + <actionGroup name="exportAllProducts"> + <selectOption selector="{{AdminExportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> + <waitForElementVisible selector="{{AdminExportMainSection.entityAttributes}}" stepKey="waitForElementVisible" time="5"/> + <scrollTo selector="{{AdminExportAttributeSection.continueBtn}}" stepKey="scrollToContinue"/> + <wait stepKey="waitForScroll" time="5"/> + <click selector="{{AdminExportAttributeSection.continueBtn}}" stepKey="clickContinueButton"/> + <wait stepKey="waitForClick" time="5"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="Message is added to queue, wait to get your file soon" stepKey="seeSuccessMessage"/> + </actionGroup> + + <!-- Download first file in the grid --> + <actionGroup name="downloadProduct"> + <reloadPage stepKey="refreshPage"/> + <waitForPageLoad stepKey="waitFormReload"/> + <click stepKey="clickSelectBtn" selector="{{AdminExportAttributeSection.selectByIndex('0')}}"/> + <click stepKey="clickOnDownload" selector="{{AdminExportAttributeSection.download('0')}}" after="clickSelectBtn"/> + </actionGroup> + + <!-- Delete exported file --> + <actionGroup name="deleteExportedFile"> + <reloadPage stepKey="refreshPage"/> + <waitForPageLoad stepKey="waitFormReload"/> + <click stepKey="clickSelectBtn" selector="{{AdminExportAttributeSection.selectByIndex('0')}}"/> + <click stepKey="clickOnDownload" selector="{{AdminExportAttributeSection.delete('0')}}" after="clickSelectBtn"/> + <waitForElementVisible selector="{{AdminProductGridConfirmActionSection.title}}" stepKey="waitForConfirmModal"/> + <click selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="confirmProductDelete"/> + <see selector="{{AdminDataGridTableSection.dataGridEmpty}}" userInput="We couldn't find any records." stepKey="assertDataGridEmptyMessage"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml new file mode 100644 index 000000000000..476e3756f55c --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml @@ -0,0 +1,120 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminExportBundleProductTest"> + <annotations> + <features value="CatalogImportExport"/> + <stories value="Export products"/> + <title value="Export Bundle Product"/> + <description value="Admin should be able to export Bundle Product"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14008"/> + <group value="catalog_import_export"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!--Create bundle product with dynamic price with two simple products --> + <createData entity="SimpleProduct2" stepKey="firstSimpleProductForDynamic"/> + <createData entity="SimpleProduct2" stepKey="secondSimpleProductForDynamic"/> + <createData entity="ApiBundleProduct" stepKey="createDynamicBundleProduct"/> + <createData entity="DropDownBundleOption" stepKey="createFirstBundleOption"> + <requiredEntity createDataKey="createDynamicBundleProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="firstLinkOptionToDynamicProduct"> + <requiredEntity createDataKey="createDynamicBundleProduct"/> + <requiredEntity createDataKey="createFirstBundleOption"/> + <requiredEntity createDataKey="firstSimpleProductForDynamic"/> + </createData> + <createData entity="ApiBundleLink" stepKey="secondLinkOptionToDynamicProduct"> + <requiredEntity createDataKey="createDynamicBundleProduct"/> + <requiredEntity createDataKey="createFirstBundleOption"/> + <requiredEntity createDataKey="secondSimpleProductForDynamic"/> + </createData> + + <!-- Create bundle product with fixed price with two simple products --> + <createData entity="SimpleProduct2" stepKey="firstSimpleProductForFixed"/> + <createData entity="SimpleProduct2" stepKey="secondSimpleProductForFixed"/> + <createData entity="ApiFixedBundleProduct" stepKey="createFixedBundleProduct"/> + <createData entity="DropDownBundleOption" stepKey="createSecondBundleOption"> + <requiredEntity createDataKey="createFixedBundleProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="firstLinkOptionToFixedProduct"> + <requiredEntity createDataKey="createFixedBundleProduct"/> + <requiredEntity createDataKey="createSecondBundleOption"/> + <requiredEntity createDataKey="firstSimpleProductForFixed"/> + </createData> + <createData entity="ApiBundleLink" stepKey="secondLinkOptionToFixedProduct"> + <requiredEntity createDataKey="createFixedBundleProduct"/> + <requiredEntity createDataKey="createSecondBundleOption"/> + <requiredEntity createDataKey="secondSimpleProductForFixed"/> + </createData> + + <!-- Create bundle product with custom textarea attribute with two simple products --> + <createData entity="productAttributeWysiwyg" stepKey="createProductAttribute"/> + <createData entity="AddToDefaultSet" stepKey="addToDefaultAttributeSet"> + <requiredEntity createDataKey="createProductAttribute"/> + </createData> + <createData entity="ApiFixedBundleProduct" stepKey="createFixedBundleProductWithAttribute"> + <requiredEntity createDataKey="addToDefaultAttributeSet"/> + </createData> + <createData entity="SimpleProduct2" stepKey="firstSimpleProductForFixedWithAttribute"/> + <createData entity="SimpleProduct2" stepKey="secondSimpleProductForFixedWithAttribute"/> + <createData entity="DropDownBundleOption" stepKey="createBundleOptionWithAttribute"> + <requiredEntity createDataKey="createFixedBundleProductWithAttribute"/> + </createData> + <createData entity="ApiBundleLink" stepKey="firstLinkOptionToFixedProductWithAttribute"> + <requiredEntity createDataKey="createFixedBundleProductWithAttribute"/> + <requiredEntity createDataKey="createBundleOptionWithAttribute"/> + <requiredEntity createDataKey="firstSimpleProductForFixedWithAttribute"/> + </createData> + <createData entity="ApiBundleLink" stepKey="secondLinkOptionToFixedProductWithAttribute"> + <requiredEntity createDataKey="createFixedBundleProductWithAttribute"/> + <requiredEntity createDataKey="createBundleOptionWithAttribute"/> + <requiredEntity createDataKey="secondSimpleProductForFixedWithAttribute"/> + </createData> + + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Run cron twice --> + <magentoCLI command="cron:run" stepKey="runCron1"/> + <magentoCLI command="cron:run" stepKey="runCron2"/> + </before> + <after> + <!-- Delete exported file --> + <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"/> + + <!-- Delete products creations --> + <deleteData createDataKey="createDynamicBundleProduct" stepKey="deleteDynamicBundleProduct"/> + <deleteData createDataKey="firstSimpleProductForDynamic" stepKey="deleteFirstSimpleProductForDynamic"/> + <deleteData createDataKey="secondSimpleProductForDynamic" stepKey="deleteSecondSimpleProductForDynamic"/> + <deleteData createDataKey="createFixedBundleProduct" stepKey="deleteFixedBundleProduct"/> + <deleteData createDataKey="firstSimpleProductForFixed" stepKey="deleteFirstSimpleProductForFixed"/> + <deleteData createDataKey="secondSimpleProductForFixed" stepKey="deleteSecondSimpleProductForFixed"/> + <deleteData createDataKey="createFixedBundleProductWithAttribute" stepKey="deleteFixedBundleProductWithAttribute"/> + <deleteData createDataKey="firstSimpleProductForFixedWithAttribute" stepKey="deleteFirstSimpleProductForFixedWithAttribute"/> + <deleteData createDataKey="secondSimpleProductForFixedWithAttribute" stepKey="deleteSecondSimpleProductForFixedWithAttribute"/> + <deleteData createDataKey="createProductAttribute" stepKey="deleteProductAttribute"/> + + <!-- Log out --> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Go to export page --> + <amOnPage url="{{AdminExportIndexPage.url}}" stepKey="goToExportIndexPage"/> + <waitForPageLoad stepKey="waitForExportIndexPageLoad" time="10"/> + + <!-- Export created below products --> + <actionGroup ref="exportAllProducts" stepKey="exportCreatedProducts"/> + + <!-- Download product --> + <actionGroup ref="downloadProduct" stepKey="downloadCreatedProducts"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml new file mode 100644 index 000000000000..adec5daddfeb --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminExportGroupedProductWithSpecialPriceTest"> + <annotations> + <features value="CatalogImportExport"/> + <stories value="Export products"/> + <title value="Export grouped product with special price"/> + <description value="Admin should be able to export grouped product with special price"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14009"/> + <group value="catalog_import_export"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create first simple product and add special price --> + <createData entity="SimpleProduct2" stepKey="createFirstSimpleProduct"/> + <createData entity="specialProductPrice2" stepKey="specialPriceToFirstProduct"> + <requiredEntity createDataKey="createFirstSimpleProduct"/> + </createData> + + <!-- Create second simple product and add special price--> + <createData entity="SimpleProduct2" stepKey="createSecondSimpleProduct"/> + <createData entity="specialProductPrice2" stepKey="specialPriceToSecondProduct"> + <requiredEntity createDataKey="createSecondSimpleProduct"/> + </createData> + + <!-- Create category --> + <createData entity="_defaultCategory" stepKey="createCategory"/> + + <!-- Create group product with created below simple products --> + <createData entity="ApiGroupedProduct2" stepKey="createGroupedProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="OneSimpleProductLink" stepKey="addFirstProduct"> + <requiredEntity createDataKey="createGroupedProduct"/> + <requiredEntity createDataKey="createFirstSimpleProduct"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addFirstProduct" stepKey="addSecondProduct"> + <requiredEntity createDataKey="createGroupedProduct"/> + <requiredEntity createDataKey="createSecondSimpleProduct"/> + </updateData> + + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Run cron twice --> + <magentoCLI command="cron:run" stepKey="runCron1"/> + <magentoCLI command="cron:run" stepKey="runCron2"/> + </before> + <after> + <!-- Delete exported file --> + <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"/> + + <!-- Deleted created products --> + <deleteData createDataKey="createFirstSimpleProduct" stepKey="deleteFirstSimpleProduct"/> + <deleteData createDataKey="createSecondSimpleProduct" stepKey="deleteSecondSimpleProduct"/> + <deleteData createDataKey="createGroupedProduct" stepKey="deleteGroupedProduct"/> + + <!-- Delete category --> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!-- Log out --> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Go to export page --> + <amOnPage url="{{AdminExportIndexPage.url}}" stepKey="goToExportIndexPage"/> + <waitForPageLoad stepKey="waitForExportIndexPageLoad"/> + + <!-- Export created below products --> + <actionGroup ref="exportAllProducts" stepKey="exportCreatedProducts"/> + + <!-- Download product --> + <actionGroup ref="downloadProduct" stepKey="downloadCreatedProducts"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml new file mode 100644 index 000000000000..883cdab2951f --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml @@ -0,0 +1,111 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest"> + <annotations> + <features value="CatalogImportExport"/> + <stories value="Export products"/> + <title value="Export Simple and Configurable products with custom options"/> + <description value="Admin should be able to export Simple and Configurable products with custom options"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14005"/> + <group value="catalog_import_export"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create category --> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + + <!-- Create configurable product with two attributes --> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeFirstOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeSecondOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeFirstOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeSecondOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Add custom options to configurable product --> + <updateData createDataKey="createConfigProduct" entity="productWithOptions" stepKey="updateProductWithOptions"/> + + <!-- Create two simple product which will be the part of configurable product --> + <createData entity="ApiSimpleOne" stepKey="createConfigFirstChildProduct"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeFirstOption"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigSecondChildProduct"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeSecondOption"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeFirstOption"/> + <requiredEntity createDataKey="getConfigAttributeSecondOption"/> + </createData> + + <!-- Add created below children products to configurable product --> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddFirstChild"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigFirstChildProduct"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddSecondChild"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigSecondChildProduct"/> + </createData> + + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Run cron twice --> + <magentoCLI command="cron:run" stepKey="runCron1"/> + <magentoCLI command="cron:run" stepKey="runCron2"/> + </before> + <after> + <!-- Delete exported file --> + <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"/> + + <!-- Delete configurable product creation --> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigFirstChildProduct" stepKey="deleteConfigFirstChildProduct"/> + <deleteData createDataKey="createConfigSecondChildProduct" stepKey="deleteConfigSecondChildProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!-- Log out --> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Go to export page --> + <amOnPage url="{{AdminExportIndexPage.url}}" stepKey="goToExportIndexPage"/> + <waitForPageLoad stepKey="waitForExportIndexPageLoad"/> + + <!-- Fill entity attributes data --> + <actionGroup ref="fillFilterExport" stepKey="exportProductBySku"> + <argument name="attribute" value="sku"/> + <argument name="attributeData" value="$$createConfigProduct.sku$$"/> + </actionGroup> + + <!-- Download product --> + <actionGroup ref="downloadProduct" stepKey="downloadCreatedProducts"/> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml new file mode 100644 index 000000000000..10b81c4b1278 --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml @@ -0,0 +1,127 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest"> + <annotations> + <features value="CatalogImportExport"/> + <stories value="Export products"/> + <title value="Export Simple product and Configurable products with assigned images"/> + <description value="Admin should be able to export Simple and Configurable products with assigned images"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14004"/> + <group value="catalog_import_export"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create category --> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + + <!-- Create configurable product with two attributes --> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeFirstOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeSecondOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeFirstOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeSecondOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Create first simple product which will be the part of configurable product --> + <createData entity="ApiSimpleOne" stepKey="createConfigFirstChildProduct"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeFirstOption"/> + </createData> + + <!-- Add image to first simple product --> + <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createConfigChildFirstProductImage"> + <requiredEntity createDataKey="createConfigFirstChildProduct"/> + </createData> + + <!-- Create second simple product which will be the part of configurable product --> + <createData entity="ApiSimpleTwo" stepKey="createConfigSecondChildProduct"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeSecondOption"/> + </createData> + + <!-- Add image to second simple product --> + <createData entity="ApiProductAttributeMediaGalleryEntryMagentoLogo" stepKey="createConfigSecondChildProductImage"> + <requiredEntity createDataKey="createConfigSecondChildProduct"/> + </createData> + + <!-- Add two options to configurable product --> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeFirstOption"/> + <requiredEntity createDataKey="getConfigAttributeSecondOption"/> + </createData> + + <!-- Add created below children products to configurable product --> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddFirstChild"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigFirstChildProduct"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddSecondChild"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigSecondChildProduct"/> + </createData> + + <!-- Add image to configurable product --> + <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createConfigProductImage"> + <requiredEntity createDataKey="createConfigProduct"/> + </createData> + + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Run cron twice --> + <magentoCLI command="cron:run" stepKey="runCron1"/> + <magentoCLI command="cron:run" stepKey="runCron2"/> + </before> + <after> + <!-- Delete exported file --> + <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"/> + + <!-- Delete configurable product creation --> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigFirstChildProduct" stepKey="deleteConfigFirstChildProduct"/> + <deleteData createDataKey="createConfigSecondChildProduct" stepKey="deleteConfigSecondChildProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!-- Log out --> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Go to export page --> + <amOnPage url="{{AdminExportIndexPage.url}}" stepKey="goToExportIndexPage"/> + <waitForPageLoad stepKey="waitForExportIndexPageLoad"/> + + <!-- Fill entity attributes data --> + <actionGroup ref="fillFilterExport" stepKey="exportProductBySku"> + <argument name="attribute" value="sku"/> + <argument name="attributeData" value="$$createConfigProduct.sku$$"/> + </actionGroup> + + <!-- Download product --> + <actionGroup ref="downloadProduct" stepKey="downloadCreatedProducts"/> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml new file mode 100644 index 000000000000..72daebfd93bf --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml @@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest"> + <annotations> + <features value="CatalogImportExport"/> + <stories value="Export products"/> + <title value="Export Simple Product assigned to Main Website and Configurable Product assigned to Custom Website"/> + <description value="Admin should be able to export Simple Product assigned to Main Website and Configurable Product assigned to Custom Website"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14006"/> + <group value="catalog_import_export"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create simple product --> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Create configurable product --> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeFirstOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeSecondOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeFirstOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeSecondOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Create two simple product which will be the part of configurable product --> + <createData entity="ApiSimpleOne" stepKey="createConfigFirstChildProduct"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeFirstOption"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigSecondChildProduct"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeSecondOption"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeFirstOption"/> + <requiredEntity createDataKey="getConfigAttributeSecondOption"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddFirstChild"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigFirstChildProduct"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddSecondChild"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigSecondChildProduct"/> + </createData> + + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Run cron twice --> + <magentoCLI command="cron:run" stepKey="runCron1"/> + <magentoCLI command="cron:run" stepKey="runCron2"/> + </before> + <after> + <!-- Delete exported file --> + <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"/> + + <!-- Delete simple product --> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + + <!-- Delete configurable product creation --> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigFirstChildProduct" stepKey="deleteConfigFirstChildProduct"/> + <deleteData createDataKey="createConfigSecondChildProduct" stepKey="deleteConfigSecondChildProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!-- Log out --> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Go to export page --> + <amOnPage url="{{AdminExportIndexPage.url}}" stepKey="goToExportIndexPage"/> + <waitForPageLoad stepKey="waitForExportIndexPageLoad"/> + + <!-- Export created below products --> + <actionGroup ref="exportAllProducts" stepKey="exportCreatedProducts"/> + + <!-- Download product --> + <actionGroup ref="downloadProduct" stepKey="downloadCreatedProducts"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml new file mode 100644 index 000000000000..6553966b515a --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminExportSimpleProductWithCustomAttributeTest"> + <annotations> + <features value="CatalogImportExport"/> + <stories value="Export products"/> + <title value="Export Simple Product with custom attribute"/> + <description value="Admin should be able to export Simple Product with custom attribute"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14007"/> + <group value="catalog_import_export"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create simple product with custom attribute set --> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> + <createData entity="SimpleProductWithCustomAttributeSet" stepKey="createSimpleProductWithCustomAttributeSet"> + <requiredEntity createDataKey="createCategory"/> + <requiredEntity createDataKey="createAttributeSet"/> + </createData> + + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Run cron twice --> + <magentoCLI command="cron:run" stepKey="runCron1"/> + <magentoCLI command="cron:run" stepKey="runCron2"/> + </before> + <after> + <!-- Delete exported file --> + <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"/> + + <!-- Delete product creations --> + <deleteData createDataKey="createSimpleProductWithCustomAttributeSet" stepKey="deleteSimpleProductWithCustomAttributeSet"/> + <deleteData createDataKey="createAttributeSet" stepKey="deleteAttributeSet"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!-- Log out --> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Go to export page --> + <amOnPage url="{{AdminExportIndexPage.url}}" stepKey="goToExportIndexPage"/> + <waitForPageLoad stepKey="waitForExportIndexPageLoad"/> + + <!-- Export created below products --> + <actionGroup ref="exportAllProducts" stepKey="exportCreatedProducts"/> + + <!-- Download product --> + <actionGroup ref="downloadProduct" stepKey="downloadCreatedProducts"/> + </test> +</tests> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Section/AdminExportAttributeSection.xml b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminExportAttributeSection.xml index ad9e7672ce11..1a759867d953 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Section/AdminExportAttributeSection.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminExportAttributeSection.xml @@ -11,5 +11,11 @@ <element name="filterByAttributeCode" type="input" selector="#export_filter_grid_filter_attribute_code"/> <element name="resetFilter" type="button" selector="button[data-action='grid-filter-reset']" timeout="30"/> <element name="search" type="button" selector="button[data-action=grid-filter-apply]" timeout="30"/> + <element name="chooseAttribute" type="checkbox" selector="//*[@name='export_filter[{{var}}]']/ancestor::tr//input[@type='checkbox']" parameterized="true"/> + <element name="fillFilter" type="input" selector="//*[@name='export_filter[{{var}}]']/ancestor::tr//input[@type='text']" parameterized="true"/> + <element name="continueBtn" type="button" selector="//*[@id='export_filter_container']/button"/> + <element name="selectByIndex" type="button" selector="//tr[@data-repeat-index='{{var}}']//button" parameterized="true"/> + <element name="download" type="button" selector="//tr[@data-repeat-index='{{var}}']//a[text()='Download']" parameterized="true" timeout="10"/> + <element name="delete" type="button" selector="//tr[@data-repeat-index='{{var}}']//a[text()='Delete']" parameterized="true" timeout="10"/> </section> </sections> From 739496a0edc7f1d319add2ff9282420133c74789 Mon Sep 17 00:00:00 2001 From: sathakur <sathakur@adobe.com> Date: Wed, 27 Feb 2019 15:41:10 -0600 Subject: [PATCH 054/265] MC-4434: Convert DeleteSearchTermEntityTest to MFTF --- .../Section/StorefrontMessagesSection.xml | 2 +- .../AdminCatalogSearchTermActionGroup.xml | 62 +++++++++++++++++++ ...StorefrontCatalogSearchTermActionGroup.xml | 25 ++++++++ .../Test/Mftf/Data/SearchTermData.xml | 17 +++++ .../Page/AdminCatalogSearchTermIndexPage.xml | 14 +++++ .../Page/AdminCatalogSearchTermNewPage.xml | 14 +++++ .../AdminCatalogSearchTermIndexSection.xml | 22 +++++++ .../AdminCatalogSearchTermMessagesSection.xml | 14 +++++ .../AdminCatalogSearchTermNewSection.xml | 18 ++++++ .../Mftf/Test/AdminDeleteSearchTermTest.xml | 59 ++++++++++++++++++ 10 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminCatalogSearchTermActionGroup.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchTermActionGroup.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Data/SearchTermData.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Page/AdminCatalogSearchTermIndexPage.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Page/AdminCatalogSearchTermNewPage.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermIndexSection.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermMessagesSection.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermNewSection.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminDeleteSearchTermTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontMessagesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontMessagesSection.xml index 4dcda8dcd41a..4447b27360a8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontMessagesSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontMessagesSection.xml @@ -11,6 +11,6 @@ <section name="StorefrontMessagesSection"> <element name="success" type="text" selector="div.message-success.success.message"/> <element name="error" type="text" selector="div.message-error.error.message"/> - <element name="noticeMessage" type="text" selector="div.message-notice"/> + <element name="noticeMessage" type="text" selector="//div[@class='message notice']/div"/> </section> </sections> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminCatalogSearchTermActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminCatalogSearchTermActionGroup.xml new file mode 100644 index 000000000000..a9444a4e5baa --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminCatalogSearchTermActionGroup.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!--Add new search term and AssertSearchTermSaveSuccessMessage--> + <actionGroup name="AssertSearchTermSaveSuccessMessage"> + <arguments> + <argument name="searchQuery" type="string"/> + <argument name="storeValue" type="string"/> + <argument name="redirectUrl" type="string"/> + <argument name="displayInSuggestedTerm" type="string"/> + </arguments> + <amOnPage url="{{AdminCatalogSearchTermIndexPage.url}}" stepKey="openAdminCatalogSearchTermIndexPage"/> + <waitForPageLoad stepKey="waitForAdminCatalogSearchTermIndexPageLoad"/> + <click selector="{{AdminCatalogSearchTermIndexSection.addNewSearchTermButton}}" stepKey="clickAddNewSearchTermButton"/> + <waitForPageLoad stepKey="waitForAdminCatalogSearchTermNewPageLoad"/> + <fillField selector="{{AdminCatalogSearchTermNewSection.searchQuery}}" userInput="{{searchQuery}}" stepKey="fillSearchQueryTextBox"/> + <click selector="{{AdminCatalogSearchTermNewSection.store('storeValue')}}" stepKey="selectMainWebsiteStore"/> + <fillField selector="{{AdminCatalogSearchTermNewSection.redirectUrl}}" userInput="{{redirectUrl}}" stepKey="fillRedirectUrl"/> + <click selector="{{AdminCatalogSearchTermNewSection.displayInSuggestedTerm('displayInSuggestedTerm')}}" stepKey="selectDisplayInSuggestedTerm"/> + <click selector="{{AdminCatalogSearchTermNewSection.saveSearchButton}}" stepKey="clickSaveSearchButton"/> + <see selector="{{AdminCatalogSearchTermMessagesSection.successMessage}}" userInput="You saved the search term." stepKey="seeSaveSuccessMessage"/> + </actionGroup> + <!--Search and delete search term and AssertSearchTermSuccessDeleteMessage--> + <actionGroup name="AssertSearchTermSuccessDeleteMessage"> + <arguments> + <argument name="searchQuery" type="string"/> + </arguments> + <amOnPage url="{{AdminCatalogSearchTermIndexPage.url}}" stepKey="openCatalogSearchIndexPage"/> + <waitForPageLoad stepKey="waitForAdminCatalogSearchTermIndexPageLoad"/> + <click selector="{{AdminCatalogSearchTermIndexSection.resetFilterButton}}" stepKey="clickOnResetButton"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <fillField selector="{{AdminCatalogSearchTermIndexSection.searchQuery}}" userInput="{{searchQuery}}" stepKey="fillSearchQuery"/> + <click selector="{{AdminCatalogSearchTermIndexSection.searchButton}}" stepKey="clickSearchButton"/> + <waitForPageLoad stepKey="waitForSearchResultLoad"/> + <checkOption selector="{{AdminCatalogSearchTermIndexSection.checkBox}}" stepKey="checkCheckBox"/> + <selectOption selector="{{AdminCatalogSearchTermIndexSection.delete}}" userInput="delete" stepKey="selectDeleteOption"/> + <click selector="{{AdminCatalogSearchTermIndexSection.submit}}" stepKey="clickSubmitButton"/> + <click selector="{{AdminCatalogSearchTermIndexSection.okButton}}" stepKey="clickOkButton"/> + <see selector="{{AdminCatalogSearchTermMessagesSection.successMessage}}" userInput="Total of 1 record(s) were deleted." stepKey="seeSuccessMessage"/> + </actionGroup> + <!--Verify deleted search term in grid and AssertSearchTermNotInGridMessage--> + <actionGroup name="AssertSearchTermNotInGrid"> + <arguments> + <argument name="searchQuery" type="string"/> + </arguments> + <amOnPage url="{{AdminCatalogSearchTermIndexPage.url}}" stepKey="openCatalogSearchIndexPage"/> + <waitForPageLoad stepKey="waitForAdminCatalogSearchTermIndexPageLoad"/> + <click selector="{{AdminCatalogSearchTermIndexSection.resetFilterButton}}" stepKey="clickOnResetButton"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <fillField selector="{{AdminCatalogSearchTermIndexSection.searchQuery}}" userInput="{{searchQuery}}" stepKey="fillSearchQuery"/> + <click selector="{{AdminCatalogSearchTermIndexSection.searchButton}}" stepKey="clickSearchButton"/> + <waitForPageLoad stepKey="waitForSearchResultToLoad"/> + <see selector="{{AdminCatalogSearchTermIndexSection.emptyRecords}}" userInput="We couldn't find any records." stepKey="seeEmptyRecordMessage"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchTermActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchTermActionGroup.xml new file mode 100644 index 000000000000..5714cb4eb9f9 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchTermActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!--Verify AssertSearchTermNotOnFrontend--> + <actionGroup name="AssertSearchTermNotOnFrontend"> + <arguments> + <argument name="searchQuery" type="string"/> + <argument name="url_key" type="string"/> + </arguments> + <amOnPage url="{{StorefrontProductPage.url('url_key')}}" stepKey="goToMagentoStorefrontPage"/> + <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> + <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{SimpleTerm.search_query}}" stepKey="fillSearchQuery"/> + <waitForPageLoad stepKey="waitForSearchTextBox"/> + <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextBoxButton"/> + <waitForPageLoad stepKey="waitForSearch"/> + <see selector="{{StorefrontMessagesSection.noticeMessage}}" userInput="Your search returned no results." stepKey="seeAssertSearchTermNotOnFrontendNoticeMessage"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Data/SearchTermData.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Data/SearchTermData.xml new file mode 100644 index 000000000000..995b860d107c --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Data/SearchTermData.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="SimpleTerm" type="searchTerm"> + <data key="search_query" unique="suffix">Query text</data> + <data key="store_id">Default Store View</data> + <data key="redirect" unique="suffix">http://example.com/</data> + <data key="display_in_terms">No</data> + </entity> +</entities> \ No newline at end of file diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Page/AdminCatalogSearchTermIndexPage.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Page/AdminCatalogSearchTermIndexPage.xml new file mode 100644 index 000000000000..bbafff8ad773 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Page/AdminCatalogSearchTermIndexPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminCatalogSearchTermIndexPage" url="/search/term/index/" area="admin" module="Magento_CatalogSearch"> + <section name="AdminCatalogSearchTermIndexSection"/> + </page> +</pages> \ No newline at end of file diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Page/AdminCatalogSearchTermNewPage.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Page/AdminCatalogSearchTermNewPage.xml new file mode 100644 index 000000000000..de7491471741 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Page/AdminCatalogSearchTermNewPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminCatalogSearchTermNewPage" url="/search/term/new/" area="admin" module="Magento_CatalogSearch"> + <section name="AdminCatalogSearchTermNewSection"/> + </page> +</pages> \ No newline at end of file diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermIndexSection.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermIndexSection.xml new file mode 100644 index 000000000000..812c302f9c00 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermIndexSection.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminCatalogSearchTermIndexSection"> + <element name="addNewSearchTermButton" type="button" selector="//div[@class='page-actions-buttons']/button[@id='add']" timeout="30"/> + <element name="resetFilterButton" type="button" selector="//button[@class='action-default scalable action-reset action-tertiary']" timeout="30"/> + <element name="searchButton" type="button" selector="//button[@class='action-default scalable action-secondary']" timeout="30"/> + <element name="delete" type="text" selector="//div[@class='admin__grid-massaction-form']//select[@id='search_term_grid_massaction-select']"/> + <element name="submit" type="button" selector="//button[@class='action-default scalable']/span" timeout="30"/> + <element name="searchQuery" type="text" selector="//tr[@class='data-grid-filters']//td/input[@name='search_query']"/> + <element name="checkBox" type="checkbox" selector="//label[@class='data-grid-checkbox-cell-inner']/input[@name='search']"/> + <element name="okButton" type="button" selector="//button[@class='action-primary action-accept']/span" timeout="30"/> + <element name="emptyRecords" type="text" selector="//tr[@class='data-grid-tr-no-data even']/td[@class='empty-text']"/> + </section> +</sections> \ No newline at end of file diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermMessagesSection.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermMessagesSection.xml new file mode 100644 index 000000000000..5d19198a1b94 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermMessagesSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminCatalogSearchTermMessagesSection"> + <element name="successMessage" type="text" selector="//div[@class='message message-success success']/div[@data-ui-id='messages-message-success']"/> + </section> +</sections> \ No newline at end of file diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermNewSection.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermNewSection.xml new file mode 100644 index 000000000000..7922e994c496 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermNewSection.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminCatalogSearchTermNewSection"> + <element name="searchQuery" type="text" selector="//div[@class='admin__field-control control']/input[@id='query_text']"/> + <element name="store" type="text" selector="//select[@id='store_id']//optgroup/option[contains(.,'{{store}}')]" parameterized="true"/> + <element name="redirectUrl" type="text" selector="//div[@class='admin__field-control control']/input[@id='redirect']"/> + <element name="displayInSuggestedTerm" type="select" selector="//select[@name='display_in_terms']/option[contains(.,'{{text}}')]" parameterized="true"/> + <element name="saveSearchButton" type="button" selector="//button[@id='save']/span[@class='ui-button-text']" timeout="30"/> + </section> +</sections> \ No newline at end of file diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminDeleteSearchTermTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminDeleteSearchTermTest.xml new file mode 100644 index 000000000000..dc9d99fabefc --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminDeleteSearchTermTest.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteSearchTermTest"> + <annotations> + <stories value="DeleteSearchTerm"/> + <title value="DeleteSearchTermEntityTestVariation1"/> + <description value="Test log in to SearchTerm and DeleteSearchTerm"/> + <testCaseId value="MC-13988"/> + <severity value="CRITICAL"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> + <createData entity="SimpleProduct" stepKey="simpleProduct"> + <requiredEntity createDataKey="initialCategoryEntity"/> + </createData> + </before> + <after> + <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> + <deleteData stepKey="deleteSimpleProduct" createDataKey="simpleProduct"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Add new search term--> + <actionGroup ref="AssertSearchTermSaveSuccessMessage" stepKey="addNewSearchTerm"> + <argument name="searchQuery" value="{{SimpleTerm.search_query}}"/> + <argument name="storeValue" value="Default Store View"/> + <argument name="redirectUrl" value="{{SimpleTerm.redirect}}"/> + <argument name="displayInSuggestedTerm" value="No"/> + </actionGroup> + + <!--Search and delete search term and AssertSearchTermSuccessDeleteMessage--> + <actionGroup ref="AssertSearchTermSuccessDeleteMessage" stepKey="deleteSearchTerm"> + <argument name="searchQuery" value="{{SimpleTerm.search_query}}"/> + </actionGroup> + + <!--Verify deleted search term in grid and AssertSearchTermNotInGrid--> + <actionGroup ref="AssertSearchTermNotInGrid" stepKey="verifyDeletedSearchTermNotInGrid"> + <argument name="searchQuery" value="{{SimpleTerm.search_query}}"/> + </actionGroup> + + <!--Verify AssertSearchTermNotOnFrontend--> + <actionGroup ref="AssertSearchTermNotOnFrontend" stepKey="verifySearchTermNotOnFrontend"> + <argument name="searchQuery" value="{{SimpleTerm.search_query}}"/> + <argument name="url_key" value="$$simpleProduct.custom_attributes[url_key]$$"/> + </actionGroup> + </test> +</tests> \ No newline at end of file From ec62bf7f1b19ca2fdbffeb4f41fd52b354c895bb Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <korablov@adobe.com> Date: Wed, 27 Feb 2019 15:50:13 -0600 Subject: [PATCH 055/265] MAGETWO-98151: Add support ZooKeeper locks --- .../Framework/Lock/Backend/ZookeeperTest.php | 90 ++++++ .../Framework/Lock/Backend/Zookeeper.php | 11 +- .../Framework/Lock/LockBackendFactory.php | 15 +- .../Lock/Test/Unit/LockBackendFactoryTest.php | 10 +- .../Magento/Setup/Model/ConfigOptionsList.php | 3 +- .../Setup/Model/ConfigOptionsList/Lock.php | 291 ++++++++++++++++++ .../Unit/Model/ConfigOptionsList/LockTest.php | 196 ++++++++++++ .../Test/Unit/Model/ConfigOptionsListTest.php | 17 +- 8 files changed, 619 insertions(+), 14 deletions(-) create mode 100644 setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php diff --git a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php index e69de29bb2d1..6e312637c7b5 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php @@ -0,0 +1,90 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Lock\Backend; + +use Magento\Framework\Lock\Backend\Zookeeper as ZookeeperLock; +use Magento\Framework\Lock\LockBackendFactory; +use Magento\Framework\Config\File\ConfigFilePool; +use Magento\Framework\App\DeploymentConfig\FileReader; +use Magento\Framework\Stdlib\ArrayManager; + +/** + * \Magento\Framework\Lock\Backend\Zookeeper test case + */ +class ZookeeperTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var FileReader + */ + private $configReader; + + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + + /** + * @var LockBackendFactory + */ + private $lockBackendFactory; + + /** + * @var ArrayManager + */ + private $arrayManager; + + /** + * @var ZookeeperLock + */ + private $model; + + /** + * @inheritdoc + */ + protected function setUp() + { + if (!extension_loaded('zookeeper')) { + $this->markTestSkipped('php extension Zookeeper is not installed.'); + } + + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->configReader = $this->objectManager->get(FileReader::class); + $this->lockBackendFactory = $this->objectManager->create(LockBackendFactory::class); + $this->arrayManager = $this->objectManager->create(ArrayManager::class); + $config = $this->configReader->load(ConfigFilePool::APP_ENV); + + if ($this->arrayManager->get('lock/provider', $config) !== 'zookeeper') { + $this->markTestSkipped('Zookeeper is not configured during installation.'); + } + + $this->model = $this->lockBackendFactory->create(); + $this->assertInstanceOf(ZookeeperLock::class, $this->model); + } + + public function testLockAndUnlock() + { + $name = 'test_lock'; + + $this->assertFalse($this->model->isLocked($name)); + + $this->assertTrue($this->model->lock($name)); + $this->assertTrue($this->model->isLocked($name)); + + $this->assertTrue($this->model->unlock($name)); + $this->assertFalse($this->model->isLocked($name)); + } + + public function testUnlockWithoutExistingLock() + { + $name = 'test_lock'; + + $this->assertFalse($this->model->isLocked($name)); + $this->assertFalse($this->model->unlock($name)); + } +} + diff --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php index d19ba0dd947b..c2119df300c5 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php @@ -31,6 +31,8 @@ class Zookeeper implements LockManagerInterface private $path; /** + * The name of sequence nodes + * * @var string */ private $lockName = 'lock-'; @@ -70,12 +72,17 @@ class Zookeeper implements LockManagerInterface */ private $locks = []; + /** + * The default path to storage locks + */ + const DEFAULT_PATH = '/magento/locks'; + /** * @param string $host The host to connect to Zookeeper * @param string $path The base path to locks in Zookeeper * @throws RuntimeException */ - public function __construct(string $host, string $path = '/magento/locks') + public function __construct(string $host, string $path = self::DEFAULT_PATH) { if (empty($path)) { throw new RuntimeException( @@ -164,7 +171,7 @@ private function getFullPathToLock(string $name): string * @return \Zookeeper * @throws RuntimeException */ - private function getProvider(): \Zookeeper + public function getProvider(): \Zookeeper { if (!$this->zookeeper) { $this->zookeeper = new \Zookeeper($this->host); diff --git a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php index e2119ca3eee1..ec15736bef53 100644 --- a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php +++ b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php @@ -13,6 +13,7 @@ use Magento\Framework\App\DeploymentConfig; use Magento\Framework\Lock\Backend\Database as DatabaseLock; use Magento\Framework\Lock\Backend\Zookeeper as ZookeeperLock; +use Magento\Framework\Lock\Backend\Cache as CacheLock; /** * The factory to create object that implements LockManagerInterface @@ -47,6 +48,13 @@ class LockBackendFactory */ const LOCK_ZOOKEEPER = 'zookeeper'; + /** + * Cache lock provider name + * + * @const string + */ + const LOCK_CACHE = 'cache'; + /** * The list of lock providers with mapping on classes * @@ -54,7 +62,8 @@ class LockBackendFactory */ private $lockers = [ self::LOCK_DB => DatabaseLock::class, - self::LOCK_ZOOKEEPER => ZookeeperLock::class + self::LOCK_ZOOKEEPER => ZookeeperLock::class, + self::LOCK_CACHE => CacheLock::class, ]; /** @@ -77,8 +86,8 @@ public function __construct( */ public function create(): LockManagerInterface { - $provider = $this->deploymentConfig->get('locks/provider', self::LOCK_DB); - $config = $this->deploymentConfig->get('locks/config', []); + $provider = $this->deploymentConfig->get('lock/provider', self::LOCK_DB); + $config = $this->deploymentConfig->get('lock/config', []); if (!isset($this->lockers[$provider])) { throw new RuntimeException(new Phrase('Unknown locks provider.')); diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php index f3b141e9f902..18053f20f010 100644 --- a/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php @@ -9,6 +9,7 @@ use Magento\Framework\Lock\Backend\Database as DatabaseLock; use Magento\Framework\Lock\Backend\Zookeeper as ZookeeperLock; +use Magento\Framework\Lock\Backend\Cache as CacheLock; use Magento\Framework\Lock\LockBackendFactory; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Lock\LockManagerInterface; @@ -51,7 +52,7 @@ public function testCreateWithException() { $this->deploymentConfigMock->expects($this->exactly(2)) ->method('get') - ->withConsecutive(['locks/provider', LockBackendFactory::LOCK_DB], ['locks/config', []]) + ->withConsecutive(['lock/provider', LockBackendFactory::LOCK_DB], ['lock/config', []]) ->willReturnOnConsecutiveCalls('someProvider', []); $this->factory->create(); @@ -68,7 +69,7 @@ public function testCreate(string $lockProvider, string $lockProviderClass, arra $lockManagerMock = $this->getMockForAbstractClass(LockManagerInterface::class); $this->deploymentConfigMock->expects($this->exactly(2)) ->method('get') - ->withConsecutive(['locks/provider', LockBackendFactory::LOCK_DB], ['locks/config', []]) + ->withConsecutive(['lock/provider', LockBackendFactory::LOCK_DB], ['lock/config', []]) ->willReturnOnConsecutiveCalls($lockProvider, $config); $this->objectManagerMock->expects($this->once()) ->method('create') @@ -89,6 +90,11 @@ public function createDataProvider(): array 'lockProviderClass' => DatabaseLock::class, 'config' => ['prefix' => 'somePrefix'], ], + 'cache' => [ + 'lockProvider' => LockBackendFactory::LOCK_CACHE, + 'lockProviderClass' => CacheLock::class, + 'config' => [], + ], ]; if (extension_loaded('zookeeper')) { diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList.php b/setup/src/Magento/Setup/Model/ConfigOptionsList.php index afe1a5d9e259..3f2aedae1373 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList.php @@ -50,7 +50,8 @@ class ConfigOptionsList implements ConfigOptionsListInterface private $configOptionsListClasses = [ \Magento\Setup\Model\ConfigOptionsList\Session::class, \Magento\Setup\Model\ConfigOptionsList\Cache::class, - \Magento\Setup\Model\ConfigOptionsList\PageCache::class + \Magento\Setup\Model\ConfigOptionsList\PageCache::class, + \Magento\Setup\Model\ConfigOptionsList\Lock::class, ]; /** diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php new file mode 100644 index 000000000000..912b0e42ca82 --- /dev/null +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php @@ -0,0 +1,291 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Setup\Model\ConfigOptionsList; + +use Magento\Framework\Lock\Backend\Zookeeper as ZookeeperLock; +use Magento\Framework\Lock\LockBackendFactory; +use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\Config\Data\ConfigData; +use Magento\Framework\Config\File\ConfigFilePool; +use Magento\Framework\Setup\ConfigOptionsListInterface; +use Magento\Framework\Setup\Option\SelectConfigOption; +use Magento\Framework\Setup\Option\TextConfigOption; + +/** + * Deployment configuration options for locks + */ +class Lock implements ConfigOptionsListInterface +{ + /** + * The name of an option to set lock provider + * + * @const string + */ + const INPUT_KEY_LOCK_PROVIDER = 'lock-provider'; + + /** + * The name of an option to set DB prefix + * + * @const string + */ + const INPUT_KEY_LOCK_DB_PREFIX = 'lock-db-prefix'; + + /** + * The name of an option to set Zookeeper host + * + * @const string + */ + const INPUT_KEY_LOCK_ZOOKEEPER_HOST = 'lock-zookeeper-host'; + + /** + * The name of an option to set Zookeeper path + * + * @const string + */ + const INPUT_KEY_LOCK_ZOOKEEPER_PATH = 'lock-zookeeper-path'; + + /** + * The configuration path to save lock provider + * + * @const string + */ + const CONFIG_PATH_LOCK_PROVIDER = 'lock/provider'; + + /** + * The configuration path to save DB prefix + * + * @const string + */ + const CONFIG_PATH_LOCK_DB_PREFIX = 'lock/config/prefix'; + + /** + * The configuration path to save Zookeeper host + * + * @const string + */ + const CONFIG_PATH_LOCK_ZOOKEEPER_HOST = 'lock/config/host'; + + /** + * The configuration path to save Zookeeper path + * + * @const string + */ + const CONFIG_PATH_LOCK_ZOOKEEPER_PATH = 'lock/config/path'; + + /** + * The list of lock providers + * + * @var array + */ + private $validLockProviders = [ + LockBackendFactory::LOCK_DB, + LockBackendFactory::LOCK_ZOOKEEPER, + LockBackendFactory::LOCK_CACHE, + ]; + + /** + * The mapping input keys with their configuration paths + * + * @var array + */ + private $mappingInputKeyToConfigPath = [ + LockBackendFactory::LOCK_DB => [ + self::INPUT_KEY_LOCK_PROVIDER => self::CONFIG_PATH_LOCK_PROVIDER, + self::INPUT_KEY_LOCK_DB_PREFIX => self::CONFIG_PATH_LOCK_DB_PREFIX, + ], + LockBackendFactory::LOCK_ZOOKEEPER => [ + self::INPUT_KEY_LOCK_PROVIDER => self::CONFIG_PATH_LOCK_PROVIDER, + self::INPUT_KEY_LOCK_ZOOKEEPER_HOST => self::CONFIG_PATH_LOCK_ZOOKEEPER_HOST, + self::INPUT_KEY_LOCK_ZOOKEEPER_PATH => self::CONFIG_PATH_LOCK_ZOOKEEPER_PATH, + ], + LockBackendFactory::LOCK_CACHE => [ + self::INPUT_KEY_LOCK_PROVIDER => self::CONFIG_PATH_LOCK_PROVIDER, + ], + ]; + + /** + * The list of default values + * + * @var array + */ + private $defaultConfigValues = [ + self::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_DB, + self::INPUT_KEY_LOCK_DB_PREFIX => null, + self::INPUT_KEY_LOCK_ZOOKEEPER_PATH => ZookeeperLock::DEFAULT_PATH, + ]; + + /** + * @inheritdoc + */ + public function getOptions() + { + return [ + new SelectConfigOption( + self::INPUT_KEY_LOCK_PROVIDER, + SelectConfigOption::FRONTEND_WIZARD_SELECT, + $this->validLockProviders, + self::CONFIG_PATH_LOCK_PROVIDER, + 'Lock provider name', + LockBackendFactory::LOCK_DB + ), + new TextConfigOption( + self::INPUT_KEY_LOCK_DB_PREFIX, + TextConfigOption::FRONTEND_WIZARD_TEXT, + self::CONFIG_PATH_LOCK_DB_PREFIX, + 'Installation specific lock prefix to avoid lock conflicts' + ), + new TextConfigOption( + self::INPUT_KEY_LOCK_ZOOKEEPER_HOST, + TextConfigOption::FRONTEND_WIZARD_TEXT, + self::CONFIG_PATH_LOCK_ZOOKEEPER_HOST, + 'Host and port to connect to Zookeeper cluster. For example: 127.0.0.1:2181' + ), + new TextConfigOption( + self::INPUT_KEY_LOCK_ZOOKEEPER_PATH, + TextConfigOption::FRONTEND_WIZARD_TEXT, + self::CONFIG_PATH_LOCK_ZOOKEEPER_PATH, + 'The path where Zookeeper will save locks. The default path is: ' . ZookeeperLock::DEFAULT_PATH + ), + ]; + } + + /** + * @inheritdoc + */ + public function createConfig(array $options, DeploymentConfig $deploymentConfig) + { + $configData = new ConfigData(ConfigFilePool::APP_ENV); + $configData->setOverrideWhenSave(true); + $lockProvider = $this->getLockProvider($options, $deploymentConfig); + + $this->setDefaultConfiguration($configData, $deploymentConfig, $lockProvider); + + foreach ($this->mappingInputKeyToConfigPath[$lockProvider] as $input => $path) { + if (isset($options[$input])) { + $configData->set($path, $options[$input]); + } + } + + return $configData; + } + + /** + * @inheritdoc + */ + public function validate(array $options, DeploymentConfig $deploymentConfig) + { + $lockProvider = $this->getLockProvider($options, $deploymentConfig); + switch ($lockProvider) { + case LockBackendFactory::LOCK_ZOOKEEPER: + $errors = $this->validateZookeeperConfig($options, $deploymentConfig); + break; + case LockBackendFactory::LOCK_CACHE: + case LockBackendFactory::LOCK_DB: + $errors = []; + break; + default: + $errors[] = 'The lock provider ' . $lockProvider . ' does not exist.'; + } + + return $errors; + } + + /** + * Validates Zookeeper configuration + * + * @param array $options + * @param DeploymentConfig $deploymentConfig + * @return array + */ + private function validateZookeeperConfig(array $options, DeploymentConfig $deploymentConfig): array + { + $errors = []; + + if (!extension_loaded(LockBackendFactory::LOCK_ZOOKEEPER)) { + $errors[] = 'php extension Zookeeper is not installed.'; + } + + $host = (string) $options[self::INPUT_KEY_LOCK_ZOOKEEPER_HOST] + ?? $deploymentConfig->get( + self::CONFIG_PATH_LOCK_ZOOKEEPER_HOST, + $this->getDefaultValue(self::INPUT_KEY_LOCK_ZOOKEEPER_HOST) + ); + $path = (string) $options[self::INPUT_KEY_LOCK_ZOOKEEPER_PATH] + ?? $deploymentConfig->get( + self::CONFIG_PATH_LOCK_ZOOKEEPER_PATH, + $this->getDefaultValue(self::INPUT_KEY_LOCK_ZOOKEEPER_PATH) + ); + + if (!$path) { + $errors[] = 'Zookeeper path needs to be a non-empty string.'; + } + + if (!$host) { + $errors[] = 'Zookeeper host is should be set.'; + } + + return $errors; + } + + /** + * Returns the name of lock provider + * + * @param array $options + * @param DeploymentConfig $deploymentConfig + * @return string + */ + private function getLockProvider(array $options, DeploymentConfig $deploymentConfig): string + { + if (empty($options[self::INPUT_KEY_LOCK_PROVIDER])) { + return (string) $deploymentConfig->get( + self::CONFIG_PATH_LOCK_PROVIDER, + $this->getDefaultValue(self::INPUT_KEY_LOCK_PROVIDER) + ); + } + + return (string) $options[self::INPUT_KEY_LOCK_PROVIDER]; + } + + + /** + * Sets default configuration for locks + * + * @param ConfigData $configData + * @param DeploymentConfig $deploymentConfig + * @param string $lockProvider + * @return ConfigData + */ + private function setDefaultConfiguration( + ConfigData $configData, + DeploymentConfig $deploymentConfig, + string $lockProvider + ) { + foreach ($this->mappingInputKeyToConfigPath[$lockProvider] as $input => $path) { + $configData->set($path, $deploymentConfig->get($path, $this->getDefaultValue($input))); + } + + return $configData; + } + + /** + * Returns default value by input key + * + * If default value is not set returns null + * + * @param string $inputKey + * @return mixed|null + */ + private function getDefaultValue(string $inputKey) + { + if (isset($this->defaultConfigValues[$inputKey])) { + return $this->defaultConfigValues[$inputKey]; + } else { + return null; + } + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php new file mode 100644 index 000000000000..f7ca6e0a09b5 --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php @@ -0,0 +1,196 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Setup\Test\Unit\Model\ConfigOptionsList; + +use Magento\Setup\Model\ConfigOptionsList\Lock as LockConfigOptionsList; +use Magento\Framework\Lock\Backend\Zookeeper as ZookeeperLock; +use Magento\Framework\Lock\LockBackendFactory; +use Magento\Framework\Config\Data\ConfigData; +use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\Setup\Option\SelectConfigOption; +use Magento\Framework\Setup\Option\TextConfigOption; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class LockTest extends TestCase +{ + /** + * @var DeploymentConfig|MockObject + */ + private $deploymentConfigMock; + + /** + * @var LockConfigOptionsList + */ + private $lockConfigOptionsList; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->deploymentConfigMock = $this->createMock(DeploymentConfig::class); + $this->lockConfigOptionsList = new LockConfigOptionsList(); + } + + /** + * @return void + */ + public function testGetOptions() + { + $options = $this->lockConfigOptionsList->getOptions(); + $this->assertSame(4, count($options)); + + $this->assertArrayHasKey(0, $options); + $this->assertInstanceOf(SelectConfigOption::class, $options[0]); + $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER, $options[0]->getName()); + + $this->assertArrayHasKey(1, $options); + $this->assertInstanceOf(TextConfigOption::class, $options[1]); + $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_DB_PREFIX, $options[1]->getName()); + + $this->assertArrayHasKey(2, $options); + $this->assertInstanceOf(TextConfigOption::class, $options[2]); + $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_HOST, $options[2]->getName()); + + $this->assertArrayHasKey(3, $options); + $this->assertInstanceOf(TextConfigOption::class, $options[3]); + $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_PATH, $options[3]->getName()); + } + + /** + * @param array $options + * @param array $expectedResult + * @dataProvider createConfigDataProvider + */ + public function testCreateConfig(array $options, array $expectedResult) + { + $this->deploymentConfigMock->expects($this->any()) + ->method('get') + ->willReturnArgument(1); + $data = $this->lockConfigOptionsList->createConfig($options, $this->deploymentConfigMock); + $this->assertInstanceOf(ConfigData::class, $data); + $this->assertTrue($data->isOverrideWhenSave()); + $this->assertSame($expectedResult, $data->getData()); + } + + /** + * @return array + */ + public function createConfigDataProvider(): array + { + return [ + 'Check default values' => [ + 'options' => [], + 'expectedResult' => [ + 'lock' => [ + 'provider' => LockBackendFactory::LOCK_DB, + 'config' => [ + 'prefix' => null, + ], + ], + ], + ], + 'Check default value for cache lock' => [ + 'options' => [ + LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_CACHE, + ], + 'expectedResult' => [ + 'lock' => [ + 'provider' => LockBackendFactory::LOCK_CACHE, + ], + ], + ], + 'Check default value for zookeeper lock' => [ + 'options' => [ + LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_ZOOKEEPER, + ], + 'expectedResult' => [ + 'lock' => [ + 'provider' => LockBackendFactory::LOCK_ZOOKEEPER, + 'config' => [ + 'host' => null, + 'path' => ZookeeperLock::DEFAULT_PATH, + ], + ], + ], + ], + 'Check specific db lock options' => [ + 'options' => [ + LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_DB, + LockConfigOptionsList::INPUT_KEY_LOCK_DB_PREFIX => 'my_prefix' + ], + 'expectedResult' => [ + 'lock' => [ + 'provider' => LockBackendFactory::LOCK_DB, + 'config' => [ + 'prefix' => 'my_prefix', + ], + ], + ], + ], + 'Check specific zookeeper lock options' => [ + 'options' => [ + LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_ZOOKEEPER, + LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_HOST => '123.45.67.89:10', + LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_PATH => '/some/path', + ], + 'expectedResult' => [ + 'lock' => [ + 'provider' => LockBackendFactory::LOCK_ZOOKEEPER, + 'config' => [ + 'host' => '123.45.67.89:10', + 'path' => '/some/path', + ], + ], + ], + ], + ]; + } + + /** + * @param array $options + * @param array $expectedResult + * @dataProvider validateDataProvider + */ + public function testValidate(array $options, array $expectedResult) + { + $this->deploymentConfigMock->expects($this->any()) + ->method('get') + ->willReturnArgument(1); + $this->assertSame($expectedResult, $this->lockConfigOptionsList->validate($options, $this->deploymentConfigMock)); + } + + /** + * @return array + */ + public function validateDataProvider(): array + { + return [ + 'Wrong lock provider' => [ + 'options' => [ + LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => 'SomeProvider', + ], + 'expectedResult' => [ + 'The lock provider SomeProvider does not exist.', + ], + ], + 'Empty host and path for Zookeeper' => [ + 'options' => [ + LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_ZOOKEEPER, + LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_HOST => '', + LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_PATH => '', + ], + 'expectedResult' => [ + 'Zookeeper path needs to be a non-empty string.', + 'Zookeeper host is should be set.', + ], + ], + ]; + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php index d7f680309c9e..a85b468cebc9 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php @@ -7,6 +7,7 @@ namespace Magento\Setup\Test\Unit\Model; use Magento\Framework\Config\ConfigOptionsListConstants; +use Magento\Setup\Model\ConfigOptionsList\Lock; use Magento\Setup\Model\ConfigGenerator; use Magento\Setup\Model\ConfigOptionsList; use Magento\Setup\Validator\DbValidator; @@ -82,7 +83,7 @@ public function testCreateOptions() $this->generator->expects($this->once())->method('createXFrameConfig')->willReturn($configDataMock); $this->generator->expects($this->once())->method('createCacheHostsConfig')->willReturn($configDataMock); - $configData = $this->object->createConfig([], $this->deploymentConfig); + $configData = $this->object->createConfig([Lock::INPUT_KEY_LOCK_PROVIDER => 'db'], $this->deploymentConfig); $this->assertGreaterThanOrEqual(6, count($configData)); } @@ -96,7 +97,7 @@ public function testCreateOptionsWithOptionalNull() $this->generator->expects($this->once())->method('createXFrameConfig')->willReturn($configDataMock); $this->generator->expects($this->once())->method('createCacheHostsConfig')->willReturn($configDataMock); - $configData = $this->object->createConfig([], $this->deploymentConfig); + $configData = $this->object->createConfig([Lock::INPUT_KEY_LOCK_PROVIDER => 'db'], $this->deploymentConfig); $this->assertGreaterThanOrEqual(6, count($configData)); } @@ -109,7 +110,8 @@ public function testValidateSuccess() ConfigOptionsListConstants::INPUT_KEY_DB_NAME => 'name', ConfigOptionsListConstants::INPUT_KEY_DB_HOST => 'host', ConfigOptionsListConstants::INPUT_KEY_DB_USER => 'user', - ConfigOptionsListConstants::INPUT_KEY_DB_PASSWORD => 'pass' + ConfigOptionsListConstants::INPUT_KEY_DB_PASSWORD => 'pass', + Lock::INPUT_KEY_LOCK_PROVIDER => 'db' ]; $this->prepareValidationMocks(); @@ -127,7 +129,8 @@ public function testValidateInvalidSessionHandler() ConfigOptionsListConstants::INPUT_KEY_DB_NAME => 'name', ConfigOptionsListConstants::INPUT_KEY_DB_HOST => 'host', ConfigOptionsListConstants::INPUT_KEY_DB_USER => 'user', - ConfigOptionsListConstants::INPUT_KEY_DB_PASSWORD => 'pass' + ConfigOptionsListConstants::INPUT_KEY_DB_PASSWORD => 'pass', + Lock::INPUT_KEY_LOCK_PROVIDER => 'db' ]; $this->prepareValidationMocks(); @@ -141,7 +144,8 @@ public function testValidateEmptyEncryptionKey() { $options = [ ConfigOptionsListConstants::INPUT_KEY_SKIP_DB_VALIDATION => true, - ConfigOptionsListConstants::INPUT_KEY_ENCRYPTION_KEY => '' + ConfigOptionsListConstants::INPUT_KEY_ENCRYPTION_KEY => '', + Lock::INPUT_KEY_LOCK_PROVIDER => 'db' ]; $this->assertEquals( ['Invalid encryption key. Encryption key must be 32 character string without any white space.'], @@ -167,7 +171,8 @@ public function testValidateCacheHosts($hosts, $expectedError) { $options = [ ConfigOptionsListConstants::INPUT_KEY_SKIP_DB_VALIDATION => true, - ConfigOptionsListConstants::INPUT_KEY_CACHE_HOSTS => $hosts + ConfigOptionsListConstants::INPUT_KEY_CACHE_HOSTS => $hosts, + Lock::INPUT_KEY_LOCK_PROVIDER => 'db' ]; $result = $this->object->validate($options, $this->deploymentConfig); if ($expectedError) { From bcb2116f116344d8767623d6d731b58cd2201383 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <korablov@adobe.com> Date: Wed, 27 Feb 2019 16:05:46 -0600 Subject: [PATCH 056/265] MAGETWO-98151: Add support ZooKeeper locks --- lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php index c2119df300c5..d491678d4d7f 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php @@ -171,7 +171,7 @@ private function getFullPathToLock(string $name): string * @return \Zookeeper * @throws RuntimeException */ - public function getProvider(): \Zookeeper + private function getProvider(): \Zookeeper { if (!$this->zookeeper) { $this->zookeeper = new \Zookeeper($this->host); From d4dd2e6bb7c331a167317e2b67126f1c39df6a0f Mon Sep 17 00:00:00 2001 From: Adarsh Khatri <me.adarshkhatri@gmail.com> Date: Thu, 28 Feb 2019 11:10:10 +1100 Subject: [PATCH 057/265] Update price-bundle.js --- .../Bundle/view/base/web/js/price-bundle.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js index d298c4d6845f..ee3ab25e9087 100644 --- a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js +++ b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js @@ -376,12 +376,16 @@ define([ magicKey = _.keys(oneItemPrice)[0], lowest = false; - //sorting based on "price_qty" - if(tiers){ - tiers.sort(function (a, b) { - return a.price_qty - b.price_qty; - }); - } + //tiers is undefined when options has only one option + if (undefined == tiers) { + var firstKey = Object.keys(optionConfig)[0]; + tiers = optionConfig[firstKey].tierPrice; + } + + //sorting based on "price_qty" + tiers.sort( function (a, b) { + return a['price_qty'] - b['price_qty']; + }); _.each(tiers, function (tier, index) { if (tier['price_qty'] > qty) { From 6e1a2a067c454e22fcaf239b1da3972bce65ab53 Mon Sep 17 00:00:00 2001 From: Vitalii Zabaznov <vzabaznov@magento.com> Date: Thu, 28 Feb 2019 15:27:11 -0600 Subject: [PATCH 058/265] MC-13864: Consumer always read config from memory --- .../Model/MassConsumer.php | 8 +- .../Api/Data/PoisonPillInterface.php | 29 +++++++ .../Api/PoisonPillCompareInterface.php | 26 ++++++ .../Api/PoisonPillPutInterface.php | 24 ++++++ .../Api/PoisonPillReadInterface.php | 25 ++++++ .../MessageQueue/Model/CallbackInvoker.php | 61 ++++++++++++++ .../Magento/MessageQueue/Model/PoisonPill.php | 32 ++++++++ .../MessageQueue/Model/PoisonPillCompare.php | 38 +++++++++ .../MessageQueue/Model/PoisonPillFactory.php | 36 +++++++++ .../Model/ResourceModel/PoisonPill.php | 80 +++++++++++++++++++ .../Magento/MessageQueue/etc/db_schema.xml | 8 ++ app/code/Magento/MessageQueue/etc/di.xml | 5 ++ .../MessageQueue/CallbackInvoker.php | 2 +- .../MessageQueue/CallbackInvokerInterface.php | 21 +++++ .../Framework/MessageQueue/Consumer.php | 6 +- 15 files changed, 393 insertions(+), 8 deletions(-) create mode 100644 app/code/Magento/MessageQueue/Api/Data/PoisonPillInterface.php create mode 100644 app/code/Magento/MessageQueue/Api/PoisonPillCompareInterface.php create mode 100644 app/code/Magento/MessageQueue/Api/PoisonPillPutInterface.php create mode 100644 app/code/Magento/MessageQueue/Api/PoisonPillReadInterface.php create mode 100644 app/code/Magento/MessageQueue/Model/CallbackInvoker.php create mode 100644 app/code/Magento/MessageQueue/Model/PoisonPill.php create mode 100644 app/code/Magento/MessageQueue/Model/PoisonPillCompare.php create mode 100644 app/code/Magento/MessageQueue/Model/PoisonPillFactory.php create mode 100644 app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php create mode 100644 lib/internal/Magento/Framework/MessageQueue/CallbackInvokerInterface.php diff --git a/app/code/Magento/AsynchronousOperations/Model/MassConsumer.php b/app/code/Magento/AsynchronousOperations/Model/MassConsumer.php index 86e691daa421..af1ef4400e44 100644 --- a/app/code/Magento/AsynchronousOperations/Model/MassConsumer.php +++ b/app/code/Magento/AsynchronousOperations/Model/MassConsumer.php @@ -14,7 +14,7 @@ use Magento\Framework\MessageQueue\MessageLockException; use Magento\Framework\MessageQueue\ConnectionLostException; use Magento\Framework\Exception\NotFoundException; -use Magento\Framework\MessageQueue\CallbackInvoker; +use Magento\Framework\MessageQueue\CallbackInvokerInterface; use Magento\Framework\MessageQueue\ConsumerConfigurationInterface; use Magento\Framework\MessageQueue\EnvelopeInterface; use Magento\Framework\MessageQueue\QueueInterface; @@ -30,7 +30,7 @@ class MassConsumer implements ConsumerInterface { /** - * @var \Magento\Framework\MessageQueue\CallbackInvoker + * @var CallbackInvokerInterface */ private $invoker; @@ -67,7 +67,7 @@ class MassConsumer implements ConsumerInterface /** * Initialize dependencies. * - * @param CallbackInvoker $invoker + * @param CallbackInvokerInterface $invoker * @param ResourceConnection $resource * @param MessageController $messageController * @param ConsumerConfigurationInterface $configuration @@ -76,7 +76,7 @@ class MassConsumer implements ConsumerInterface * @param Registry $registry */ public function __construct( - CallbackInvoker $invoker, + CallbackInvokerInterface $invoker, ResourceConnection $resource, MessageController $messageController, ConsumerConfigurationInterface $configuration, diff --git a/app/code/Magento/MessageQueue/Api/Data/PoisonPillInterface.php b/app/code/Magento/MessageQueue/Api/Data/PoisonPillInterface.php new file mode 100644 index 000000000000..f526e0d4ea06 --- /dev/null +++ b/app/code/Magento/MessageQueue/Api/Data/PoisonPillInterface.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MessageQueue\Api\Data; + +/** + * PoisonPill data interface. + * + * @api + */ +interface PoisonPillInterface +{ + /** + * Returns version of poison pill. + * + * @return integer + */ + public function getVersion(): ?int; + + /** + * @param int $version + * @return void + */ + public function setVersion(int $version); +} diff --git a/app/code/Magento/MessageQueue/Api/PoisonPillCompareInterface.php b/app/code/Magento/MessageQueue/Api/PoisonPillCompareInterface.php new file mode 100644 index 000000000000..32d57353f442 --- /dev/null +++ b/app/code/Magento/MessageQueue/Api/PoisonPillCompareInterface.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MessageQueue\Api; + +use Magento\MessageQueue\Api\Data\PoisonPillInterface; + +/** + * Interface describes how to describes how to compare poison pill with latest in DB. + * + * @api + */ +interface PoisonPillCompareInterface +{ + /** + * Check is version of current poison pill is latest. + * + * @param PoisonPillInterface $poisonPill + * @return bool + */ + public function isLatest(PoisonPillInterface $poisonPill): bool; +} diff --git a/app/code/Magento/MessageQueue/Api/PoisonPillPutInterface.php b/app/code/Magento/MessageQueue/Api/PoisonPillPutInterface.php new file mode 100644 index 000000000000..02293c99bb3f --- /dev/null +++ b/app/code/Magento/MessageQueue/Api/PoisonPillPutInterface.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MessageQueue\Api; + +/** + * Command interface describes how to create new version on poison pill. + * + * @api + */ +interface PoisonPillPutInterface +{ + /** + * Put new version of poison pill inside DB. + * + * @return int + * @throws \Exception + */ + public function put(): int; +} diff --git a/app/code/Magento/MessageQueue/Api/PoisonPillReadInterface.php b/app/code/Magento/MessageQueue/Api/PoisonPillReadInterface.php new file mode 100644 index 000000000000..325cfd597956 --- /dev/null +++ b/app/code/Magento/MessageQueue/Api/PoisonPillReadInterface.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MessageQueue\Api; + +use Magento\MessageQueue\Api\Data\PoisonPillInterface; + +/** + * Describes how to get latest version of poison pill. + * + * @api + */ +interface PoisonPillReadInterface +{ + /** + * Returns latest poison pill. + * + * @return PoisonPillInterface + */ + public function getLatest(): PoisonPillInterface; +} diff --git a/app/code/Magento/MessageQueue/Model/CallbackInvoker.php b/app/code/Magento/MessageQueue/Model/CallbackInvoker.php new file mode 100644 index 000000000000..465c2d366c58 --- /dev/null +++ b/app/code/Magento/MessageQueue/Model/CallbackInvoker.php @@ -0,0 +1,61 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MessageQueue\Model; + +use Magento\Framework\MessageQueue\CallbackInvokerInterface; +use Magento\Framework\MessageQueue\QueueInterface; +use Magento\MessageQueue\Api\Data\PoisonPillInterface; +use Magento\MessageQueue\Api\PoisonPillCompareInterface; +use Magento\MessageQueue\Api\PoisonPillReadInterface; + +class CallbackInvoker implements CallbackInvokerInterface +{ + /** + * @var PoisonPillReadInterface $poisonPillRead + */ + private $poisonPillRead; + + /** + * @var PoisonPillInterface $poisonPill + */ + private $poisonPill; + + /** + * @var PoisonPillCompareInterface + */ + private $poisonPillCompare; + + /** + * @param PoisonPillReadInterface $poisonPillRead + * @param PoisonPillCompareInterface $poisonPillCompare + */ + public function __construct( + PoisonPillReadInterface $poisonPillRead, + PoisonPillCompareInterface $poisonPillCompare + ) { + $this->poisonPillRead = $poisonPillRead; + $this->poisonPill = $poisonPillRead->getLatest(); + $this->poisonPillCompare = $poisonPillCompare; + } + + /** + * @inheritdoc + */ + public function invoke(QueueInterface $queue, $maxNumberOfMessages, $callback) + { + for ($i = $maxNumberOfMessages; $i > 0; $i--) { + do { + $message = $queue->dequeue(); + } while ($message === null && (sleep(1) === 0)); + if (false === $this->poisonPillCompare->isLatest($this->poisonPill)) { + exit(0); + } + $callback($message); + } + } +} diff --git a/app/code/Magento/MessageQueue/Model/PoisonPill.php b/app/code/Magento/MessageQueue/Model/PoisonPill.php new file mode 100644 index 000000000000..a253ca4c13aa --- /dev/null +++ b/app/code/Magento/MessageQueue/Model/PoisonPill.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MessageQueue\Model; + +use Magento\MessageQueue\Api\Data\PoisonPillInterface; + +/** + * PoisonPill data class + */ +class PoisonPill extends \Magento\Framework\Model\AbstractModel implements PoisonPillInterface +{ + /** + * @inheritdoc + */ + public function getVersion(): ?int + { + return $this->_getData('version'); + } + + /** + * @inheritdoc + */ + public function setVersion(int $version) + { + $this->setData('version', $version); + } +} diff --git a/app/code/Magento/MessageQueue/Model/PoisonPillCompare.php b/app/code/Magento/MessageQueue/Model/PoisonPillCompare.php new file mode 100644 index 000000000000..4aa76b4fadfc --- /dev/null +++ b/app/code/Magento/MessageQueue/Model/PoisonPillCompare.php @@ -0,0 +1,38 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MessageQueue\Model; + +use Magento\MessageQueue\Api\Data\PoisonPillInterface; +use Magento\MessageQueue\Api\PoisonPillCompareInterface; +use Magento\MessageQueue\Api\PoisonPillReadInterface; + +class PoisonPillCompare implements PoisonPillCompareInterface +{ + /** + * @var PoisonPillReadInterface + */ + private $poisonPillRead; + + /** + * PoisonPillCompare constructor. + * @param PoisonPillReadInterface $poisonPillRead + */ + public function __construct( + PoisonPillReadInterface $poisonPillRead + ) { + $this->poisonPillRead = $poisonPillRead; + } + + /** + * @inheritdoc + */ + public function isLatest(PoisonPillInterface $poisonPill): bool + { + return $poisonPill->getVersion() === $this->poisonPillRead->getLatest()->getVersion(); + } +} diff --git a/app/code/Magento/MessageQueue/Model/PoisonPillFactory.php b/app/code/Magento/MessageQueue/Model/PoisonPillFactory.php new file mode 100644 index 000000000000..0fd85c998866 --- /dev/null +++ b/app/code/Magento/MessageQueue/Model/PoisonPillFactory.php @@ -0,0 +1,36 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MessageQueue\Model; + +use Magento\MessageQueue\Api\Data\PoisonPillInterface; + +class PoisonPillFactory +{ + /** + * @var PoisonPillInterface + */ + private $poisonPill; + + /** + * @param PoisonPillInterface $poisonPill + */ + public function __construct( + PoisonPillInterface $poisonPill + ) { + $this->poisonPill = $poisonPill; + } + + /** + * @param int $version + * @return PoisonPillInterface + */ + public function create(int $version): PoisonPillInterface + { + + } +} diff --git a/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php b/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php new file mode 100644 index 000000000000..769f723dc9d5 --- /dev/null +++ b/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php @@ -0,0 +1,80 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MessageQueue\Model\ResourceModel; + +use Magento\MessageQueue\Api\Data\PoisonPillInterface; +use Magento\MessageQueue\Api\Data\PoisonPillInterfaceFactory; +use Magento\MessageQueue\Api\PoisonPillReadInterface; +use Magento\MessageQueue\Api\PoisonPillPutInterface; +use Magento\Framework\Model\ResourceModel\Db\Context; +use Magento\Framework\Model\ResourceModel\Db\AbstractDb; + +class PoisonPill extends AbstractDb implements PoisonPillPutInterface, PoisonPillReadInterface +{ + /** + * Table name. + */ + const QUEUE_POISON_PILL_TABLE = 'queue_poison_pill'; + + /** + * @var PoisonPillInterfaceFactory + */ + private $poisonPillFactory; + + /** + * @param Context $context + * @param string|null $connectionName + */ + public function __construct( + Context $context, + PoisonPillInterfaceFactory $poisonPillFactory, + string $connectionName = null + ) { + $this->poisonPillFactory = $poisonPillFactory; + parent::__construct($context, $connectionName); + } + + /** + * @inheritdoc + */ + protected function _construct() + { + $this->_init(self::QUEUE_POISON_PILL_TABLE, 'version'); + } + + /** + * @inheritdoc + */ + public function put(): int + { + /** @var PoisonPillInterface $poisonPill */ + $poisonPill = $this->poisonPillFactory->create(); + return $this->save($poisonPill)->getConnection()->lastInsertId(); + } + + /** + * @inheritdoc + */ + public function getLatest() : PoisonPillInterface + { + $select = $this->getConnection()->select()->from( + $this->getTable(self::QUEUE_POISON_PILL_TABLE), + 'version' + )->order( + 'version ' . \Magento\Framework\DB\Select::SQL_DESC + )->limit( + 1 + ); + + $version = $this->getConnection()->fetchOne($select); + + $poisonPill = $this->poisonPillFactory->create(['data' => ['version' => (int) $version]]); + + return $poisonPill; + } +} diff --git a/app/code/Magento/MessageQueue/etc/db_schema.xml b/app/code/Magento/MessageQueue/etc/db_schema.xml index 7a20d2bd4df5..9cdf414dd06e 100644 --- a/app/code/Magento/MessageQueue/etc/db_schema.xml +++ b/app/code/Magento/MessageQueue/etc/db_schema.xml @@ -21,4 +21,12 @@ <column name="message_code"/> </constraint> </table> + <table name="queue_poison_pill" resource="default" engine="innodb" + comment="Sequence table for poison pill versions"> + <column xsi:type="int" name="version" padding="10" unsigned="true" nullable="false" identity="true" + comment="Poison Pill version."/> + <constraint xsi:type="primary" referenceId="PRIMARY"> + <column name="version"/> + </constraint> + </table> </schema> diff --git a/app/code/Magento/MessageQueue/etc/di.xml b/app/code/Magento/MessageQueue/etc/di.xml index c8f2edb86261..3b2cb8718e04 100644 --- a/app/code/Magento/MessageQueue/etc/di.xml +++ b/app/code/Magento/MessageQueue/etc/di.xml @@ -13,6 +13,11 @@ <preference for="Magento\Framework\MessageQueue\EnvelopeInterface" type="Magento\Framework\MessageQueue\Envelope"/> <preference for="Magento\Framework\MessageQueue\ConsumerInterface" type="Magento\Framework\MessageQueue\Consumer"/> <preference for="Magento\Framework\MessageQueue\MergedMessageInterface" type="Magento\Framework\MessageQueue\MergedMessage"/> + <preference for="Magento\MessageQueue\Api\Data\PoisonPillInterface" type="Magento\MessageQueue\Model\PoisonPill"/> + <preference for="Magento\MessageQueue\Api\PoisonPillCompareInterface" type="Magento\MessageQueue\Model\PoisonPillCompare"/> + <preference for="Magento\MessageQueue\Api\PoisonPillPutInterface" type="Magento\MessageQueue\Model\ResourceModel\PoisonPill"/> + <preference for="Magento\MessageQueue\Api\PoisonPillReadInterface" type="Magento\MessageQueue\Model\ResourceModel\PoisonPill"/> + <preference for="Magento\Framework\MessageQueue\CallbackInvokerInterface" type="Magento\MessageQueue\Model\CallbackInvoker"/> <type name="Magento\Framework\Console\CommandListInterface"> <arguments> <argument name="commands" xsi:type="array"> diff --git a/lib/internal/Magento/Framework/MessageQueue/CallbackInvoker.php b/lib/internal/Magento/Framework/MessageQueue/CallbackInvoker.php index cb80bc4becae..48c33c48f12e 100644 --- a/lib/internal/Magento/Framework/MessageQueue/CallbackInvoker.php +++ b/lib/internal/Magento/Framework/MessageQueue/CallbackInvoker.php @@ -9,7 +9,7 @@ /** * Class CallbackInvoker to invoke callbacks for consumer classes */ -class CallbackInvoker +class CallbackInvoker implements CallbackInvokerInterface { /** * Run short running process diff --git a/lib/internal/Magento/Framework/MessageQueue/CallbackInvokerInterface.php b/lib/internal/Magento/Framework/MessageQueue/CallbackInvokerInterface.php new file mode 100644 index 000000000000..bae72d8186a1 --- /dev/null +++ b/lib/internal/Magento/Framework/MessageQueue/CallbackInvokerInterface.php @@ -0,0 +1,21 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\MessageQueue; + +interface CallbackInvokerInterface +{ + /** + * Run short running process + * + * @param QueueInterface $queue + * @param int $maxNumberOfMessages + * @param \Closure $callback + * @return void + */ + public function invoke(QueueInterface $queue, $maxNumberOfMessages, $callback); +} diff --git a/lib/internal/Magento/Framework/MessageQueue/Consumer.php b/lib/internal/Magento/Framework/MessageQueue/Consumer.php index 9bdacfbc1ff6..72c5f2e0cbdf 100644 --- a/lib/internal/Magento/Framework/MessageQueue/Consumer.php +++ b/lib/internal/Magento/Framework/MessageQueue/Consumer.php @@ -38,7 +38,7 @@ class Consumer implements ConsumerInterface private $messageEncoder; /** - * @var CallbackInvoker + * @var CallbackInvokerInterface */ private $invoker; @@ -80,7 +80,7 @@ class Consumer implements ConsumerInterface /** * Initialize dependencies. * - * @param CallbackInvoker $invoker + * @param CallbackInvokerInterface $invoker * @param MessageEncoder $messageEncoder * @param ResourceConnection $resource * @param ConsumerConfigurationInterface $configuration @@ -89,7 +89,7 @@ class Consumer implements ConsumerInterface * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( - CallbackInvoker $invoker, + CallbackInvokerInterface $invoker, MessageEncoder $messageEncoder, ResourceConnection $resource, ConsumerConfigurationInterface $configuration, From 0cc4fbaf6354b280e9b05889aedfc108bc2e1b37 Mon Sep 17 00:00:00 2001 From: Maria Kovdrysh <kovdrysh@adobe.com> Date: Thu, 28 Feb 2019 15:46:25 -0600 Subject: [PATCH 059/265] MC-4435: Convert MassDeleteSearchTermEntityTest to MFTF --- .../AdminSearchTermActionGroup.xml | 30 +++++++ .../Search/Test/Mftf/Data/SearchTermData.xml | 17 ++++ .../Test/Mftf/Metadata/search_term-meta.xml | 17 ++++ .../AdminMassDeleteSearchTermEntityTest.xml | 83 +++++++++++++++++++ 4 files changed, 147 insertions(+) create mode 100644 app/code/Magento/Search/Test/Mftf/ActionGroup/AdminSearchTermActionGroup.xml create mode 100644 app/code/Magento/Search/Test/Mftf/Data/SearchTermData.xml create mode 100644 app/code/Magento/Search/Test/Mftf/Metadata/search_term-meta.xml create mode 100644 app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml diff --git a/app/code/Magento/Search/Test/Mftf/ActionGroup/AdminSearchTermActionGroup.xml b/app/code/Magento/Search/Test/Mftf/ActionGroup/AdminSearchTermActionGroup.xml new file mode 100644 index 000000000000..f116e44e5c14 --- /dev/null +++ b/app/code/Magento/Search/Test/Mftf/ActionGroup/AdminSearchTermActionGroup.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!-- Filter by search query and select --> + <actionGroup name="searchTermFilterBySearchQuery"> + <arguments> + <argument name="searchQuery" type="string"/> + </arguments> + <click selector="{{AdminCatalogSearchTermIndexSection.resetFilterButton}}" stepKey="clickOnResetButton"/> + <waitForPageLoad stepKey="waitForResetFilter"/> + <fillField selector="{{AdminCatalogSearchTermIndexSection.searchQuery}}" userInput="{{searchQuery}}" stepKey="fillSearchQuery"/> + <click selector="{{AdminCatalogSearchTermIndexSection.searchButton}}" stepKey="clickSearchButton"/> + <waitForPageLoad stepKey="waitForSearchResultLoad"/> + <checkOption selector="{{AdminCatalogSearchTermIndexSection.checkBox}}" stepKey="checkCheckBox"/> + </actionGroup> + + <!-- Delete search term --> + <actionGroup name="deleteSearchTerm"> + <selectOption selector="{{AdminCatalogSearchTermIndexSection.delete}}" userInput="delete" stepKey="selectDeleteOption"/> + <click selector="{{AdminCatalogSearchTermIndexSection.submit}}" stepKey="clickSubmitButton"/> + <click selector="{{AdminCatalogSearchTermIndexSection.okButton}}" stepKey="clickOkButton"/> + <waitForElementVisible selector="{{AdminCatalogSearchTermMessagesSection.successMessage}}" stepKey="waitForSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Search/Test/Mftf/Data/SearchTermData.xml b/app/code/Magento/Search/Test/Mftf/Data/SearchTermData.xml new file mode 100644 index 000000000000..1518adad0134 --- /dev/null +++ b/app/code/Magento/Search/Test/Mftf/Data/SearchTermData.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="SearchTerm" type="searchTerm"> + <data key="query_text" unique="suffix">Query text</data> + <data key="store_id">1</data> + <data key="redirect" unique="suffix">http://example.com/</data> + <data key="display_in_terms">0</data> + </entity> +</entities> diff --git a/app/code/Magento/Search/Test/Mftf/Metadata/search_term-meta.xml b/app/code/Magento/Search/Test/Mftf/Metadata/search_term-meta.xml new file mode 100644 index 000000000000..0bd2dc9be485 --- /dev/null +++ b/app/code/Magento/Search/Test/Mftf/Metadata/search_term-meta.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="CreateSearchTerm" dataType="searchTerm" type="create" auth="adminFormKey" url="/search/term/save/" method="POST"> + <field key="query_text">string</field> + <field key="store_id">integer</field> + <field key="redirect">string</field> + <field key="display_in_terms">integer</field> + </operation> +</operations> diff --git a/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml b/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml new file mode 100644 index 000000000000..eb0797d07bf1 --- /dev/null +++ b/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminMassDeleteSearchTermEntityTest"> + <annotations> + <features value="CatalogSearch"/> + <stories value="Delete search term"/> + <title value="Admin mass delete search term entity test"/> + <description value="Admin should be able to Mass Delete Search Term Entity Test"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14767"/> + <group value="searchFrontend"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create three search term --> + <createData entity="SearchTerm" stepKey="createFirstSearchTerm"/> + <createData entity="SearchTerm" stepKey="createSecondSearchTerm"/> + <createData entity="SearchTerm" stepKey="createThirdSearchTerm"/> + + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <!-- Log out --> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Go to the catalog search term page --> + <amOnPage url="{{AdminCatalogSearchTermIndexPage.url}}" stepKey="openAdminCatalogSearchTermIndexPage"/> + <waitForPageLoad stepKey="waitForAdminCatalogSearchTermIndexPageLoad"/> + + <!-- Select all created below search terms --> + <actionGroup ref="searchTermFilterBySearchQuery" stepKey="filterByFirstSearchQuery"> + <argument name="searchQuery" value="$createFirstSearchTerm.query_text$"/> + </actionGroup> + <actionGroup ref="searchTermFilterBySearchQuery" stepKey="filterBySecondSearchQuery"> + <argument name="searchQuery" value="$createSecondSearchTerm.query_text$"/> + </actionGroup> + <actionGroup ref="searchTermFilterBySearchQuery" stepKey="filterByThirdSearchQuery"> + <argument name="searchQuery" value="$createThirdSearchTerm.query_text$"/> + </actionGroup> + + <!-- Delete created below search terms --> + <actionGroup ref="deleteSearchTerm" stepKey="deleteSearchTerms"/> + + <!-- Assert search terms are absent on the search term page --> + <actionGroup ref="AssertSearchTermNotInGrid" stepKey="assertFirstSearchTermNotInGrid"> + <argument name="searchQuery" value="$createFirstSearchTerm.query_text$"/> + </actionGroup> + <actionGroup ref="AssertSearchTermNotInGrid" stepKey="assertSecondSearchTermNotInGrid"> + <argument name="searchQuery" value="$createSecondSearchTerm.query_text$"/> + </actionGroup> + <actionGroup ref="AssertSearchTermNotInGrid" stepKey="assertThirdSearchTermNotInGrid"> + <argument name="searchQuery" value="$createThirdSearchTerm.query_text$"/> + </actionGroup> + + <!-- Go to storefront page --> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> + <waitForPageLoad stepKey="waitForStorefrontPageLoad"/> + + <!-- Verify search term deletion on storefront --> + <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="quickSearchForFirstSearchTerm"> + <argument name="phrase" value="$createFirstSearchTerm.query_text$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckSearchIsEmpty" stepKey="checkEmptyForFirstSearchTerm"/> + <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="quickSearchForSecondSearchTerm"> + <argument name="phrase" value="$createSecondSearchTerm.query_text$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckSearchIsEmpty" stepKey="checkEmptyForSecondSearchTerm"/> + <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="quickSearchForThirdSearchTerm"> + <argument name="phrase" value="$createThirdSearchTerm.query_text$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckSearchIsEmpty" stepKey="checkEmptyForThirdSearchTerm"/> + </test> +</tests> From e7589ad85786211022c3860c78ccb21a26f69e8f Mon Sep 17 00:00:00 2001 From: Adarsh Khatri <me.adarshkhatri@gmail.com> Date: Fri, 1 Mar 2019 09:55:10 +1100 Subject: [PATCH 060/265] Update price-bundle.js --- .../Bundle/view/base/web/js/price-bundle.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js index ee3ab25e9087..02d55d50025c 100644 --- a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js +++ b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js @@ -374,16 +374,14 @@ define([ function applyTierPrice(oneItemPrice, qty, optionConfig) { var tiers = optionConfig.tierPrice, magicKey = _.keys(oneItemPrice)[0], + tiersFirstKey = _.keys(optionConfig)[0], lowest = false; - - //tiers is undefined when options has only one option - if (undefined == tiers) { - var firstKey = Object.keys(optionConfig)[0]; - tiers = optionConfig[firstKey].tierPrice; - } - - //sorting based on "price_qty" - tiers.sort( function (a, b) { + + if (undefined === tiers) {//tiers is undefined when options has only one option + tiers = optionConfig[tiersFirstKey].tierPrice; + } + + tiers.sort( function (a, b) {//sorting based on "price_qty" return a['price_qty'] - b['price_qty']; }); From 6f49b07031e8e9248a19d8f3ee20402650e60d30 Mon Sep 17 00:00:00 2001 From: Adarsh Khatri <me.adarshkhatri@gmail.com> Date: Fri, 1 Mar 2019 10:01:05 +1100 Subject: [PATCH 061/265] Update price-bundle.js --- app/code/Magento/Bundle/view/base/web/js/price-bundle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js index 02d55d50025c..c05c9ef58b93 100644 --- a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js +++ b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js @@ -381,7 +381,7 @@ define([ tiers = optionConfig[tiersFirstKey].tierPrice; } - tiers.sort( function (a, b) {//sorting based on "price_qty" + tiers.sort(function (a, b) {//sorting based on "price_qty" return a['price_qty'] - b['price_qty']; }); From 0332391af0883d78daa90de5bafc93a5e67765a2 Mon Sep 17 00:00:00 2001 From: Adarsh Khatri <me.adarshkhatri@gmail.com> Date: Fri, 1 Mar 2019 22:59:03 +1100 Subject: [PATCH 062/265] Update price-bundle.js --- app/code/Magento/Bundle/view/base/web/js/price-bundle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js index c05c9ef58b93..f8d2f8bc1111 100644 --- a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js +++ b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js @@ -377,7 +377,7 @@ define([ tiersFirstKey = _.keys(optionConfig)[0], lowest = false; - if (undefined === tiers) {//tiers is undefined when options has only one option + if (!tiers) {//tiers is undefined when options has only one option tiers = optionConfig[tiersFirstKey].tierPrice; } From b27f6944a38a88ad05993ba441a84a7f0d7923c9 Mon Sep 17 00:00:00 2001 From: sathakur <sathakur@adobe.com> Date: Fri, 1 Mar 2019 14:36:17 -0600 Subject: [PATCH 063/265] MC-4869: Convert DeleteStoreEntityTest to MFTF --- .../ActionGroup/DeleteBackupActionGroup.xml | 2 + .../Backup/Test/Mftf/Data/BackupData.xml | 6 +- .../AdminDeleteStoreViewActionGroup.xml | 35 +++++++++++- .../Mftf/Section/AdminStoresGridSection.xml | 4 +- .../Test/Mftf/Test/AdminDeleteStoreTest.xml | 57 +++++++++++++++++++ 5 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreTest.xml diff --git a/app/code/Magento/Backup/Test/Mftf/ActionGroup/DeleteBackupActionGroup.xml b/app/code/Magento/Backup/Test/Mftf/ActionGroup/DeleteBackupActionGroup.xml index 4f34f24c3a80..535478153f51 100644 --- a/app/code/Magento/Backup/Test/Mftf/ActionGroup/DeleteBackupActionGroup.xml +++ b/app/code/Magento/Backup/Test/Mftf/ActionGroup/DeleteBackupActionGroup.xml @@ -17,7 +17,9 @@ <click selector="{{AdminGridTableSection.backupRowCheckbox(backup.name)}}" stepKey="selectBackupRow"/> <selectOption selector="{{AdminGridActionSection.actionSelect}}" userInput="Delete" stepKey="selectDeleteAction"/> <click selector="{{AdminGridActionSection.submitButton}}" stepKey="clickSubmit"/> + <waitForPageLoad stepKey="waitForSubmitAction"/> <see selector="{{AdminConfirmationModalSection.message}}" userInput="Are you sure you want to delete the selected backup(s)?" stepKey="seeConfirmationModal"/> + <waitForPageLoad stepKey="waitForConfirmWindowToAppear"/> <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="clickOkConfirmDelete"/> <dontSee selector="{{AdminGridTableSection.backupNameColumn}}" userInput="{{backup.name}}" stepKey="dontSeeBackupInGrid"/> </actionGroup> diff --git a/app/code/Magento/Backup/Test/Mftf/Data/BackupData.xml b/app/code/Magento/Backup/Test/Mftf/Data/BackupData.xml index ae97351cafca..ad218cdd5750 100644 --- a/app/code/Magento/Backup/Test/Mftf/Data/BackupData.xml +++ b/app/code/Magento/Backup/Test/Mftf/Data/BackupData.xml @@ -20,4 +20,8 @@ <data key="name" unique="suffix">databaseBackup</data> <data key="type">Database</data> </entity> -</entities> + <entity name="WebSetupWizardBackup" type="backup"> + <data key="name">WebSetupWizard</data> + <data key="type">Database</data> + </entity> +</entities> \ No newline at end of file diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml index 58e1781d69ea..a08648bc8b8c 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml @@ -26,4 +26,37 @@ <waitForPageLoad stepKey="waitForSuccessMessage"/> <see userInput="You deleted the store view." stepKey="seeDeleteMessage"/> </actionGroup> -</actionGroups> + <!--AssertStoreSuccessDeleteAndBackupMessages--> + <actionGroup name="DeleteCustomStoreViewBackupEnabledYesActionGroup"> + <arguments> + <argument name="storeViewName" type="string"/> + </arguments> + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> + <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> + <fillField userInput="{{storeViewName}}" selector="{{AdminStoresGridSection.storeFilterTextField}}" stepKey="fillSearchStoreViewField"/> + <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton"/> + <click selector="{{AdminStoresGridSection.storeNameInFirstRow}}" stepKey="clickEditExistingStoreViewRow"/> + <waitForPageLoad stepKey="waitForStoreToLoad"/> + <click selector="{{AdminNewStoreViewActionsSection.delete}}" stepKey="clickDeleteStoreViewButtonOnEditStorePage"/> + <selectOption userInput="Yes" selector="{{AdminStoreBackupOptionsSection.createBackupSelect}}" stepKey="setCreateDbBackupToYes"/> + <click selector="{{AdminNewStoreViewActionsSection.delete}}" stepKey="clickDeleteStoreViewButtonOnDeleteStorePage"/> + <waitForElementVisible selector="{{AdminConfirmationModalSection.title}}" stepKey="waitingForWarningModal"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmStoreViewDelete"/> + <waitForPageLoad stepKey="waitForSuccessMessage"/> + <see selector="{{AdminStoresGridSection.successMessage}}" userInput="The database was backed up." stepKey="seeAssertDatabaseBackedUpMessage"/> + <see selector="{{AdminStoresGridSection.successMessage}}" userInput="You deleted the store view." stepKey="seeAssertSuccessDeleteStoreViewMessage"/> + </actionGroup> + <!--AssertStoreViewNotInGrid--> + <actionGroup name="AssertStoreViewNotInGrid"> + <arguments> + <argument name="storeViewName" type="string"/> + </arguments> + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> + <waitForPageLoad stepKey="waitForAdminSystemStorePageLoad"/> + <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> + <fillField userInput="{{storeViewName}}" selector="{{AdminStoresGridSection.storeFilterTextField}}" stepKey="fillSearchStoreViewField"/> + <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton"/> + <waitForPageLoad stepKey="waitForStoreToLoad"/> + <see selector="{{AdminStoresGridSection.emptyText}}" userInput="We couldn't find any records." stepKey="seeAssertStoreViewNotInGridMessage"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml index b02e9adaed45..3d3ec7fa2862 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml @@ -21,5 +21,7 @@ <element name="storeGrpNameInFirstRow" type="text" selector=".col-group_title>a"/> <element name="storeNameInFirstRow" type="text" selector=".col-store_title>a"/> <element name="firstRow" type="textarea" selector="(//*[@id='storeGrid_table']/tbody/tr)[1]"/> + <element name="successMessage" type="text" selector="//div[@class='message message-success success']/div"/> + <element name="emptyText" type="text" selector="//tr[@class='data-grid-tr-no-data even']/td[@class='empty-text']"/> </section> -</sections> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreTest.xml new file mode 100644 index 000000000000..37829f478241 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreTest.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<!-- Test XML Example --> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteStoreTest"> + <annotations> + <stories value="Delete Store View"/> + <title value="DeleteStoreEntityTestVariation1"/> + <description value="Test log in to Stores and Delete Store View"/> + <testCaseId value="MC-14303"/> + <severity value="CRITICAL"/> + <group value="store"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <magentoCLI command="config:set system/backup/functionality_enabled 1" stepKey="setEnableBackupToYes"/> + <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--Create custom store view--> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createNewStoreView"> + <argument name="StoreGroup" value="_defaultStoreGroup"/> + <argument name="customStore" value="storeViewData"/> + </actionGroup> + </before> + <after> + <magentoCLI command="config:set system/backup/functionality_enabled 0" stepKey="setEnableBackupToNo"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--AssertStoreSuccessDeleteAndBackupMessages--> + <actionGroup ref="DeleteCustomStoreViewBackupEnabledYesActionGroup" stepKey="deleteCustomStoreView"> + <argument name="storeViewName" value="{{storeViewData.name}}"/> + </actionGroup> + + <!--AssertStoreNotInGrid--> + <actionGroup ref="AssertStoreViewNotInGrid" stepKey="verifyDeletedStoreViewNotInGrid"> + <argument name="storeViewName" value="{{storeViewData.name}}"/> + </actionGroup> + + <!--Go to backup index page, verify AssertBackupInGrid--> + <amOnPage url="{{BackupIndexPage.url}}" stepKey="goToBackupIndexPage"/> + <waitForPageLoad stepKey="waitForBackupIndexPageLoad"/> + <!--Delete database backup--> + <actionGroup ref="deleteBackup" stepKey="deleteDatabaseBackup"> + <argument name="backup" value="WebSetupWizardBackup"/> + </actionGroup> + + <!--Go to storefront and verify AssertStoreNotOnFrontend--> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> + <waitForPageLoad stepKey="waitForStorefrontHomePageLoad"/> + <dontSee selector="{{StorefrontHeaderSection.storeViewList(storeViewData.name)}}" stepKey="dontSeeAssertStoreViewNameOnStorefront"/> + </test> +</tests> \ No newline at end of file From 1ee5d3877b7f842d45f8adc045fe0ba2dbba8073 Mon Sep 17 00:00:00 2001 From: Mila Lesechko <l.lesechko@gmail.com> Date: Sun, 3 Mar 2019 15:48:18 -0600 Subject: [PATCH 064/265] 632: Convert UpdateProductFromMiniShoppingCartEntityTest to MFTF --- .../Catalog/Test/Mftf/Data/ProductData.xml | 16 +++++ ...oppingCartCheckSummaryTotalActionGroup.xml | 21 +++++++ .../Checkout/Test/Mftf/Data/QuoteData.xml | 8 +++ ...eProductFromMiniShoppingCartEntityTest.xml | 58 +++++++++++++++++++ ...eProductFromMiniShoppingCartEntityTest.xml | 2 +- 5 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Checkout/Test/Mftf/ActionGroup/ShoppingCartCheckSummaryTotalActionGroup.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/UpdateProductFromMiniShoppingCartEntityTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index d812123e4b55..65e30f98c2ca 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -869,4 +869,20 @@ <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> + <entity name="simpleProductWithoutCategory" type="product"> + <data key="sku" unique="suffix">sku_simple_product_</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="visibility">4</data> + <data key="name" unique="suffix">SimpleProduct</data> + <data key="price">560</data> + <data key="urlKey" unique="suffix">simple-product-</data> + <data key="status">1</data> + <data key="quantity">25</data> + <data key="weight">1</data> + <data key="product_has_weight">1</data> + <data key="is_in_stock">1</data> + <data key="tax_class_id">2</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/ShoppingCartCheckSummaryTotalActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/ShoppingCartCheckSummaryTotalActionGroup.xml new file mode 100644 index 000000000000..70b4e95124da --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/ShoppingCartCheckSummaryTotalActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ShoppingCartCheckSummaryTotalActionGroup"> + <arguments> + <argument name="dataQuote" type="entity"/> + </arguments> + <waitForPageLoad stepKey="waitForPageLoad" time="120"/> + <grabTextFrom selector="{{CheckoutCartSummarySection.total}}" stepKey="grabCartTotal" /> + <assertContains stepKey="assertCartTotal"> + <actualResult type="variable">$grabCartTotal</actualResult> + <expectedResult type="string">{{dataQuote.total}}</expectedResult> + </assertContains> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/Data/QuoteData.xml b/app/code/Magento/Checkout/Test/Mftf/Data/QuoteData.xml index 530157851191..be6e1af67fc6 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Data/QuoteData.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Data/QuoteData.xml @@ -15,4 +15,12 @@ <data key="total">495.00</data> <data key="shippingMethod">Flat Rate - Fixed</data> </entity> + <entity name="simpleOrderQty2" type="Quote"> + <data key="price">560.00</data> + <data key="qty">2</data> + <data key="subtotal">1,120.00</data> + <data key="shipping">10.00</data> + <data key="total">1,130.00</data> + <data key="shippingMethod">Flat Rate - Fixed</data> + </entity> </entities> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/UpdateProductFromMiniShoppingCartEntityTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/UpdateProductFromMiniShoppingCartEntityTest.xml new file mode 100644 index 000000000000..451bce9e49a8 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/UpdateProductFromMiniShoppingCartEntityTest.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="UpdateProductFromMiniShoppingCartEntityTest"> + <annotations> + <stories value="Shopping Cart"/> + <title value="Check updating product from mini shopping cart"/> + <description value="Update Product Qty on Mini Shopping Cart"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-29812"/> + <group value="shoppingCart"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <!--Create product according to dataset.--> + <createData entity="simpleProductWithoutCategory" stepKey="createProduct"/> + + <!-- Go to frontend --> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="navigateToProductPage"/> + + <!--Add product to cart--> + <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> + <argument name="productName" value="$$createProduct.name$$"/> + </actionGroup> + </before> + + <after> + <!--Delete created data--> + <deleteData createDataKey="createProduct" stepKey="deleteProduct" /> + </after> + + <!-- Click on mini shopping cart icon --> + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="goToMiniShoppingCart"/> + + <!-- Click Edit --> + <click selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="editMiniCartItem"/> + + <!-- Fill data from dataset --> + <clearField selector="{{CheckoutCartProductSection.ProductQuantityByName($$createProduct.name$$)}}" stepKey="deleteFiled"/> + <fillField selector="{{CheckoutCartProductSection.ProductQuantityByName($$createProduct.name$$)}}" userInput="{{simpleOrderQty2.qty}}" stepKey="changeQty"/> + + <!-- Click Update --> + <click selector="{{CheckoutCartProductSection.updateShoppingCartButton}}" stepKey="updateQty"/> + + <!-- Perform all assertions --> + <actionGroup ref="ShoppingCartCheckSummaryTotalActionGroup" stepKey="checkSummary"> + <argument name="dataQuote" value="simpleOrderQty2"/> + </actionGroup> + </test> +</tests> diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateProductFromMiniShoppingCartEntityTest.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateProductFromMiniShoppingCartEntityTest.xml index 8b2460718097..b4c97a11b914 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateProductFromMiniShoppingCartEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateProductFromMiniShoppingCartEntityTest.xml @@ -9,7 +9,7 @@ <testCase name="Magento\Checkout\Test\TestCase\UpdateProductFromMiniShoppingCartEntityTest" summary="Update Product from Mini Shopping Cart" ticketId="MAGETWO-29812"> <variation name="UpdateProductFromMiniShoppingCartEntityTestVariation1" summary="Update Product Qty on Mini Shopping Cart" ticketId=" MAGETWO-35536"> <data name="issue" xsi:type="string">https://github.com/magento-engcom/msi/issues/1624</data> - <data name="tag" xsi:type="string">test_type:extended_acceptance_test, severity:S0</data> + <data name="tag" xsi:type="string">test_type:extended_acceptance_test, severity:S0, mftf_migrated:yes</data> <data name="originalProduct/0" xsi:type="string">catalogProductSimple::default</data> <data name="checkoutData/dataset" xsi:type="string">simple_order_qty_2</data> <data name="use_minicart_to_edit_qty" xsi:type="boolean">true</data> From 196a447eb90bf5f85aafabd3f6d007e74ef71365 Mon Sep 17 00:00:00 2001 From: sathakur <sathakur@adobe.com> Date: Mon, 4 Mar 2019 11:52:02 -0600 Subject: [PATCH 065/265] MC-4874: Convert CreateWebsiteEntityTest to MFTF --- .../AdminCreateWebsiteActionGroup.xml | 27 ++++++++++ .../Mftf/Section/AdminStoresGridSection.xml | 3 +- .../Test/Mftf/Test/AdminCreateWebsiteTest.xml | 49 +++++++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Store/Test/Mftf/Test/AdminCreateWebsiteTest.xml diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml index ef8d77c8824f..dd8bd7b6789a 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml @@ -36,4 +36,31 @@ <click selector="{{AdminStoresGridSection.websiteNameInFirstRow}}" stepKey="clickEditExistingWebsite"/> <grabFromCurrentUrl regex="~(\d+)/~" stepKey="grabFromCurrentUrl"/> </actionGroup> + + <actionGroup name="AssertWebsiteInGrid"> + <arguments> + <argument name="websiteName" type="string"/> + </arguments> + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> + <waitForPageLoad stepKey="waitForAdminSystemStorePageLoad"/> + <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> + <fillField userInput="{{websiteName}}" selector="{{AdminStoresGridSection.websiteFilterTextField}}" stepKey="fillWebsiteField"/> + <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton"/> + <waitForPageLoad stepKey="waitForStoreToLoad"/> + <seeElement selector="{{AdminStoresGridSection.nthRow(websiteName)}}" stepKey="seeAssertWebsiteInGrid"/> + </actionGroup> + + <actionGroup name="AssertWebsiteForm"> + <arguments> + <argument name="websiteName" type="string"/> + <argument name="websiteCode" type="string"/> + <argument name="websiteId"/> + </arguments> + <click selector="{{AdminStoresGridSection.nthRow(websiteName)}}" stepKey="clickWebsiteFirstRowInGrid"/> + <waitForPageLoad stepKey="waitTillWebsiteFormPageIsOpened"/> + <grabFromCurrentUrl regex="~(\d+)/~" stepKey="grabWebsiteIdFromCurrentUrl"/> + <seeInCurrentUrl url="/system_store/editWebsite/website_id/{$grabWebsiteIdFromCurrentUrl}" stepKey="seeWebsiteId"/> + <seeInField selector="{{AdminNewWebsiteSection.name}}" userInput="{{websiteName}}" stepKey="seeAssertWebsiteName"/> + <seeInField selector="{{AdminNewWebsiteSection.code}}" userInput="{{websiteCode}}" stepKey="seeAssertWebsiteCode"/> + </actionGroup> </actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml index b02e9adaed45..c3b3fc8fa342 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml @@ -21,5 +21,6 @@ <element name="storeGrpNameInFirstRow" type="text" selector=".col-group_title>a"/> <element name="storeNameInFirstRow" type="text" selector=".col-store_title>a"/> <element name="firstRow" type="textarea" selector="(//*[@id='storeGrid_table']/tbody/tr)[1]"/> + <element name="nthRow" type="text" selector="//td[@class='a-left col-website_title ']/a[contains(.,'{{websiteName}}')]" parameterized="true"/> </section> -</sections> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateWebsiteTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateWebsiteTest.xml new file mode 100644 index 000000000000..44b774eed372 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateWebsiteTest.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<!-- Test XML Example --> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateWebsiteTest"> + <annotations> + <stories value="Create Website"/> + <title value="CreateWebsiteEntityTestVariation1"/> + <description value="Test log in to Stores and Create Website Test"/> + <testCaseId value="MC-14302"/> + <severity value="CRITICAL"/> + <group value="store"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> + <argument name="websiteName" value="{{customWebsite.name}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Create website and AssertWebsiteSuccessSaveMessage--> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createWebsite"> + <argument name="newWebsiteName" value="{{customWebsite.name}}"/> + <argument name="websiteCode" value="{{customWebsite.code}}"/> + </actionGroup> + + <!--Search created website in grid and AssertWebsiteInGrid--> + <actionGroup ref="AssertWebsiteInGrid" stepKey="seeWebsiteInGrid"> + <argument name="websiteName" value="{{customWebsite.name}}"/> + </actionGroup> + + <!--AssertWebsiteForm and AssertWebsiteOnStoreForm--> + <actionGroup ref="AssertWebsiteForm" stepKey="seeWebsiteForm"> + <argument name="websiteName" value="{{customWebsite.name}}"/> + <argument name="websiteCode" value="{{customWebsite.code}}"/> + <argument name="websiteId" value="admin/system_store/editWebsite/website_id/{$grabWebsiteIdFromCurrentUrl}"/> + </actionGroup> + </test> +</tests> \ No newline at end of file From f1f405c82446d39accfcebdd0ec6e523f9d25f4f Mon Sep 17 00:00:00 2001 From: sathakur <sathakur@adobe.com> Date: Mon, 4 Mar 2019 12:03:54 -0600 Subject: [PATCH 066/265] MC-4869: Convert DeleteStoreEntityTest to MFTF Addressing review comments --- .../Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml index a08648bc8b8c..cf2cabdcc239 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml @@ -26,7 +26,6 @@ <waitForPageLoad stepKey="waitForSuccessMessage"/> <see userInput="You deleted the store view." stepKey="seeDeleteMessage"/> </actionGroup> - <!--AssertStoreSuccessDeleteAndBackupMessages--> <actionGroup name="DeleteCustomStoreViewBackupEnabledYesActionGroup"> <arguments> <argument name="storeViewName" type="string"/> @@ -46,7 +45,6 @@ <see selector="{{AdminStoresGridSection.successMessage}}" userInput="The database was backed up." stepKey="seeAssertDatabaseBackedUpMessage"/> <see selector="{{AdminStoresGridSection.successMessage}}" userInput="You deleted the store view." stepKey="seeAssertSuccessDeleteStoreViewMessage"/> </actionGroup> - <!--AssertStoreViewNotInGrid--> <actionGroup name="AssertStoreViewNotInGrid"> <arguments> <argument name="storeViewName" type="string"/> From 7394aeb9b7c2679558c8e2bda9badfc825eee11d Mon Sep 17 00:00:00 2001 From: sathakur <sathakur@adobe.com> Date: Mon, 4 Mar 2019 13:50:06 -0600 Subject: [PATCH 067/265] MC-4869: Convert DeleteStoreEntityTest to MFTF Addressing second set of review comments --- ...leteStoreTest.xml => AdminDeleteStoreViewTest.xml} | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) rename app/code/Magento/Store/Test/Mftf/Test/{AdminDeleteStoreTest.xml => AdminDeleteStoreViewTest.xml} (83%) diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreViewTest.xml similarity index 83% rename from app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreTest.xml rename to app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreViewTest.xml index 37829f478241..380f6c31f98a 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreViewTest.xml @@ -7,10 +7,10 @@ --> <!-- Test XML Example --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminDeleteStoreTest"> + <test name="AdminDeleteStoreViewTest"> <annotations> <stories value="Delete Store View"/> - <title value="DeleteStoreEntityTestVariation1"/> + <title value="Delete Store View and Save Backup"/> <description value="Test log in to Stores and Delete Store View"/> <testCaseId value="MC-14303"/> <severity value="CRITICAL"/> @@ -31,19 +31,20 @@ <actionGroup ref="logout" stepKey="logout"/> </after> - <!--AssertStoreSuccessDeleteAndBackupMessages--> + <!--Delete custom store view and verify AssertStoreSuccessDeleteMessage And BackupMessage--> <actionGroup ref="DeleteCustomStoreViewBackupEnabledYesActionGroup" stepKey="deleteCustomStoreView"> <argument name="storeViewName" value="{{storeViewData.name}}"/> </actionGroup> - <!--AssertStoreNotInGrid--> + <!--Verify deleted store view not present in grid and verify AssertStoreNotInGrid Message--> <actionGroup ref="AssertStoreViewNotInGrid" stepKey="verifyDeletedStoreViewNotInGrid"> <argument name="storeViewName" value="{{storeViewData.name}}"/> </actionGroup> - <!--Go to backup index page, verify AssertBackupInGrid--> + <!--Go to backup index page and verify AssertBackupInGrid--> <amOnPage url="{{BackupIndexPage.url}}" stepKey="goToBackupIndexPage"/> <waitForPageLoad stepKey="waitForBackupIndexPageLoad"/> + <see selector="{{AdminGridTableSection.backupNameColumn}}" userInput="{{WebSetupWizardBackup.name}}" stepKey="seeBackupInGrid"/> <!--Delete database backup--> <actionGroup ref="deleteBackup" stepKey="deleteDatabaseBackup"> <argument name="backup" value="WebSetupWizardBackup"/> From bc3f4526a0a3a305aaa22e99aeede10c75015730 Mon Sep 17 00:00:00 2001 From: Maria Kovdrysh <kovdrysh@adobe.com> Date: Mon, 4 Mar 2019 15:25:40 -0600 Subject: [PATCH 068/265] MC-4426: Convert ExportProductsTest to MFTF --- .../ActionGroup/AdminExportActionGroup.xml | 22 ++++++++++++------- .../Test/AdminExportBundleProductTest.xml | 9 +++++--- ...portGroupedProductWithSpecialPriceTest.xml | 8 +++++-- ...figurableProductsWithCustomOptionsTest.xml | 10 ++++++--- ...igurableProductsWithAssignedImagesTest.xml | 10 ++++++--- ...ableProductAssignedToCustomWebsiteTest.xml | 8 +++++-- ...rtSimpleProductWithCustomAttributeTest.xml | 8 +++++-- .../Section/AdminExportAttributeSection.xml | 8 +++---- 8 files changed, 56 insertions(+), 27 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml index 21b81648ea47..63248dd53fc1 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml @@ -10,10 +10,10 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Export products using filtering by attribute --> - <actionGroup name="fillFilterExport"> + <actionGroup name="exportProductsFilterByAttribute"> <arguments> - <argument name="attribute"/> - <argument name="attributeData"/> + <argument name="attribute" type="string"/> + <argument name="attributeData" type="string"/> </arguments> <selectOption selector="{{AdminExportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> <waitForElementVisible selector="{{AdminExportMainSection.entityAttributes}}" stepKey="waitForElementVisible"/> @@ -38,19 +38,25 @@ </actionGroup> <!-- Download first file in the grid --> - <actionGroup name="downloadProduct"> + <actionGroup name="downloadFileByRowIndex"> + <arguments> + <argument name="rowIndex" type="string"/> + </arguments> <reloadPage stepKey="refreshPage"/> <waitForPageLoad stepKey="waitFormReload"/> - <click stepKey="clickSelectBtn" selector="{{AdminExportAttributeSection.selectByIndex('0')}}"/> - <click stepKey="clickOnDownload" selector="{{AdminExportAttributeSection.download('0')}}" after="clickSelectBtn"/> + <click stepKey="clickSelectBtn" selector="{{AdminExportAttributeSection.selectByIndex(rowIndex)}}"/> + <click stepKey="clickOnDownload" selector="{{AdminExportAttributeSection.download(rowIndex)}}" after="clickSelectBtn"/> </actionGroup> <!-- Delete exported file --> <actionGroup name="deleteExportedFile"> + <arguments> + <argument name="rowIndex" type="string"/> + </arguments> <reloadPage stepKey="refreshPage"/> <waitForPageLoad stepKey="waitFormReload"/> - <click stepKey="clickSelectBtn" selector="{{AdminExportAttributeSection.selectByIndex('0')}}"/> - <click stepKey="clickOnDownload" selector="{{AdminExportAttributeSection.delete('0')}}" after="clickSelectBtn"/> + <click stepKey="clickSelectBtn" selector="{{AdminExportAttributeSection.selectByIndex(rowIndex)}}"/> + <click stepKey="clickOnDownload" selector="{{AdminExportAttributeSection.delete(rowIndex)}}" after="clickSelectBtn"/> <waitForElementVisible selector="{{AdminProductGridConfirmActionSection.title}}" stepKey="waitForConfirmModal"/> <click selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="confirmProductDelete"/> <see selector="{{AdminDataGridTableSection.dataGridEmpty}}" userInput="We couldn't find any records." stepKey="assertDataGridEmptyMessage"/> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml index 476e3756f55c..3a4010f41710 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml @@ -89,7 +89,9 @@ </before> <after> <!-- Delete exported file --> - <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"/> + <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"> + <argument name="rowIndex" value="0"/> + </actionGroup> <!-- Delete products creations --> <deleteData createDataKey="createDynamicBundleProduct" stepKey="deleteDynamicBundleProduct"/> @@ -109,12 +111,13 @@ <!-- Go to export page --> <amOnPage url="{{AdminExportIndexPage.url}}" stepKey="goToExportIndexPage"/> - <waitForPageLoad stepKey="waitForExportIndexPageLoad" time="10"/> <!-- Export created below products --> <actionGroup ref="exportAllProducts" stepKey="exportCreatedProducts"/> <!-- Download product --> - <actionGroup ref="downloadProduct" stepKey="downloadCreatedProducts"/> + <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> + <argument name="rowIndex" value="0"/> + </actionGroup> </test> </tests> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml index adec5daddfeb..09d9469cb093 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml @@ -57,7 +57,9 @@ </before> <after> <!-- Delete exported file --> - <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"/> + <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"> + <argument name="rowIndex" value="0"/> + </actionGroup> <!-- Deleted created products --> <deleteData createDataKey="createFirstSimpleProduct" stepKey="deleteFirstSimpleProduct"/> @@ -79,6 +81,8 @@ <actionGroup ref="exportAllProducts" stepKey="exportCreatedProducts"/> <!-- Download product --> - <actionGroup ref="downloadProduct" stepKey="downloadCreatedProducts"/> + <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> + <argument name="rowIndex" value="0"/> + </actionGroup> </test> </tests> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml index 883cdab2951f..aaeb0cd38cd9 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml @@ -82,7 +82,9 @@ </before> <after> <!-- Delete exported file --> - <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"/> + <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"> + <argument name="rowIndex" value="0"/> + </actionGroup> <!-- Delete configurable product creation --> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> @@ -100,12 +102,14 @@ <waitForPageLoad stepKey="waitForExportIndexPageLoad"/> <!-- Fill entity attributes data --> - <actionGroup ref="fillFilterExport" stepKey="exportProductBySku"> + <actionGroup ref="exportProductsFilterByAttribute" stepKey="exportProductBySku"> <argument name="attribute" value="sku"/> <argument name="attributeData" value="$$createConfigProduct.sku$$"/> </actionGroup> <!-- Download product --> - <actionGroup ref="downloadProduct" stepKey="downloadCreatedProducts"/> + <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> + <argument name="rowIndex" value="0"/> + </actionGroup> </test> </tests> \ No newline at end of file diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml index 10b81c4b1278..597ee2336b21 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml @@ -98,7 +98,9 @@ </before> <after> <!-- Delete exported file --> - <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"/> + <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"> + <argument name="rowIndex" value="0"/> + </actionGroup> <!-- Delete configurable product creation --> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> @@ -116,12 +118,14 @@ <waitForPageLoad stepKey="waitForExportIndexPageLoad"/> <!-- Fill entity attributes data --> - <actionGroup ref="fillFilterExport" stepKey="exportProductBySku"> + <actionGroup ref="exportProductsFilterByAttribute" stepKey="exportProductBySku"> <argument name="attribute" value="sku"/> <argument name="attributeData" value="$$createConfigProduct.sku$$"/> </actionGroup> <!-- Download product --> - <actionGroup ref="downloadProduct" stepKey="downloadCreatedProducts"/> + <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> + <argument name="rowIndex" value="0"/> + </actionGroup> </test> </tests> \ No newline at end of file diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml index 72daebfd93bf..b1e723e5ee1f 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml @@ -80,7 +80,9 @@ </before> <after> <!-- Delete exported file --> - <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"/> + <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"> + <argument name="rowIndex" value="0"/> + </actionGroup> <!-- Delete simple product --> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> @@ -104,6 +106,8 @@ <actionGroup ref="exportAllProducts" stepKey="exportCreatedProducts"/> <!-- Download product --> - <actionGroup ref="downloadProduct" stepKey="downloadCreatedProducts"/> + <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> + <argument name="rowIndex" value="0"/> + </actionGroup> </test> </tests> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml index 6553966b515a..e3c5cd78397f 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml @@ -37,7 +37,9 @@ </before> <after> <!-- Delete exported file --> - <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"/> + <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"> + <argument name="rowIndex" value="0"/> + </actionGroup> <!-- Delete product creations --> <deleteData createDataKey="createSimpleProductWithCustomAttributeSet" stepKey="deleteSimpleProductWithCustomAttributeSet"/> @@ -56,6 +58,8 @@ <actionGroup ref="exportAllProducts" stepKey="exportCreatedProducts"/> <!-- Download product --> - <actionGroup ref="downloadProduct" stepKey="downloadCreatedProducts"/> + <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> + <argument name="rowIndex" value="0"/> + </actionGroup> </test> </tests> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Section/AdminExportAttributeSection.xml b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminExportAttributeSection.xml index 1a759867d953..528ad23aaf2b 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Section/AdminExportAttributeSection.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminExportAttributeSection.xml @@ -13,9 +13,9 @@ <element name="search" type="button" selector="button[data-action=grid-filter-apply]" timeout="30"/> <element name="chooseAttribute" type="checkbox" selector="//*[@name='export_filter[{{var}}]']/ancestor::tr//input[@type='checkbox']" parameterized="true"/> <element name="fillFilter" type="input" selector="//*[@name='export_filter[{{var}}]']/ancestor::tr//input[@type='text']" parameterized="true"/> - <element name="continueBtn" type="button" selector="//*[@id='export_filter_container']/button"/> - <element name="selectByIndex" type="button" selector="//tr[@data-repeat-index='{{var}}']//button" parameterized="true"/> - <element name="download" type="button" selector="//tr[@data-repeat-index='{{var}}']//a[text()='Download']" parameterized="true" timeout="10"/> - <element name="delete" type="button" selector="//tr[@data-repeat-index='{{var}}']//a[text()='Delete']" parameterized="true" timeout="10"/> + <element name="continueBtn" type="button" selector="//*[@id='export_filter_container']/button" timeout="30"/> + <element name="selectByIndex" type="button" selector="//tr[@data-repeat-index='{{var}}']//button" parameterized="true" timeout="30"/> + <element name="download" type="button" selector="//tr[@data-repeat-index='{{var}}']//a[text()='Download']" parameterized="true" timeout="30"/> + <element name="delete" type="button" selector="//tr[@data-repeat-index='{{var}}']//a[text()='Delete']" parameterized="true" timeout="30"/> </section> </sections> From 27a01c038f098ab336e779432ecf53421d5562f2 Mon Sep 17 00:00:00 2001 From: vprohorov <prohorov.vital@gmail.com> Date: Tue, 5 Mar 2019 01:07:53 +0300 Subject: [PATCH 069/265] MAGETWO-62728: My Wishlist - quantity input box issue - Fixed js validation --- .../view/frontend/web/js/add-to-wishlist.js | 57 +++++++++---------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js b/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js index 7a166b47256c..b38c5c2cda3a 100644 --- a/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js +++ b/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js @@ -63,6 +63,12 @@ define([ isFileUploaded = false, self = this; + if (event.handleObj.selector == this.options.qtyInfo) { //eslint-disable-line eqeqeq + this._updateAddToWishlistButton({}); + event.stopPropagation(); + + return; + } $(event.handleObj.selector).each(function (index, element) { if ($(element).is('input[type=text]') || $(element).is('input[type=email]') || @@ -83,7 +89,9 @@ define([ } }); - this.bindFormSubmit(isFileUploaded); + if (isFileUploaded) { + this.bindFormSubmit(); + } this._updateAddToWishlistButton(dataToAdd); event.stopPropagation(); }, @@ -181,45 +189,34 @@ define([ /** * Bind form submit. - * - * @param {Boolean} isFileUploaded */ - bindFormSubmit: function (isFileUploaded) { + bindFormSubmit: function () { var self = this; $('[data-action="add-to-wishlist"]').on('click', function (event) { var element, params, form, action; - if (!$($(self.options.qtyInfo).closest('form')).valid()) { - event.stopPropagation(); - event.preventDefault(); - - return; - } - - if (isFileUploaded) { - - element = $('input[type=file]' + self.options.customOptionsInfo); - params = $(event.currentTarget).data('post'); - form = $(element).closest('form'); - action = params.action; + event.stopPropagation(); + event.preventDefault(); - if (params.data.id) { - $('<input>', { - type: 'hidden', - name: 'id', - value: params.data.id - }).appendTo(form); - } + element = $('input[type=file]' + self.options.customOptionsInfo); + params = $(event.currentTarget).data('post'); + form = $(element).closest('form'); + action = params.action; - if (params.data.uenc) { - action += 'uenc/' + params.data.uenc; - } + if (params.data.id) { + $('<input>', { + type: 'hidden', + name: 'id', + value: params.data.id + }).appendTo(form); + } - $(form).attr('action', action).submit(); - event.stopPropagation(); - event.preventDefault(); + if (params.data.uenc) { + action += 'uenc/' + params.data.uenc; } + + $(form).attr('action', action).submit(); }); } }); From 318425fafd4d92dbabcf89d332dedf9f27978f63 Mon Sep 17 00:00:00 2001 From: Cristiano Casciotti <teknoman84@gmail.com> Date: Tue, 5 Mar 2019 12:02:18 +0100 Subject: [PATCH 070/265] Fix for issue #21510 --- app/code/Magento/Indexer/Model/Indexer.php | 2 +- .../Magento/Indexer/Test/Unit/Model/IndexerTest.php | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Indexer/Model/Indexer.php b/app/code/Magento/Indexer/Model/Indexer.php index 87a7cce58e1a..8f3e6b475b46 100644 --- a/app/code/Magento/Indexer/Model/Indexer.php +++ b/app/code/Magento/Indexer/Model/Indexer.php @@ -361,7 +361,7 @@ public function getLatestUpdated() return $this->getView()->getUpdated(); } } - return $this->getState()->getUpdated(); + return $this->getState()->getUpdated() ?: ''; } /** diff --git a/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php b/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php index 6b7cc1221899..ae0d721a549b 100644 --- a/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php +++ b/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php @@ -164,7 +164,12 @@ public function testGetLatestUpdated($getViewIsEnabled, $getViewGetUpdated, $get } } } else { - $this->assertEquals($getStateGetUpdated, $this->model->getLatestUpdated()); + $actualGetLatestUpdated = $this->model->getLatestUpdated(); + $this->assertEquals($getStateGetUpdated, $actualGetLatestUpdated); + + if ($getStateGetUpdated === null) { + $this->assertNotNull($actualGetLatestUpdated); + } } } @@ -182,7 +187,8 @@ public function getLatestUpdatedDataProvider() [true, '', '06-Jan-1944'], [true, '06-Jan-1944', ''], [true, '', ''], - [true, '06-Jan-1944', '05-Jan-1944'] + [true, '06-Jan-1944', '05-Jan-1944'], + [false, null, null], ]; } From 486e21c19a6678d3a1b6b0e259408fb1d205bceb Mon Sep 17 00:00:00 2001 From: Cristiano Casciotti <teknoman84@gmail.com> Date: Tue, 5 Mar 2019 14:27:58 +0100 Subject: [PATCH 071/265] Changed variable name for codacy quality review --- app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php b/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php index ae0d721a549b..ca2da9585f93 100644 --- a/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php +++ b/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php @@ -164,11 +164,11 @@ public function testGetLatestUpdated($getViewIsEnabled, $getViewGetUpdated, $get } } } else { - $actualGetLatestUpdated = $this->model->getLatestUpdated(); - $this->assertEquals($getStateGetUpdated, $actualGetLatestUpdated); + $getLatestUpdated = $this->model->getLatestUpdated(); + $this->assertEquals($getStateGetUpdated, $getLatestUpdated); if ($getStateGetUpdated === null) { - $this->assertNotNull($actualGetLatestUpdated); + $this->assertNotNull($getLatestUpdated); } } } From f893a925bbf5a92967a79509beefa4314b897f23 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Tue, 5 Mar 2019 17:32:16 +0200 Subject: [PATCH 072/265] 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 4de27d15b08789f88aafa0f69dab42a940223386 Mon Sep 17 00:00:00 2001 From: sathakur <sathakur@adobe.com> Date: Tue, 5 Mar 2019 10:12:40 -0600 Subject: [PATCH 073/265] MC-4434: Convert DeleteSearchTermEntityTest to MFTF Addressing review comments --- .../ActionGroup/AdminCatalogSearchTermActionGroup.xml | 11 ++++------- .../StorefrontCatalogSearchTermActionGroup.xml | 2 +- .../Section/AdminCatalogSearchTermIndexSection.xml | 4 ++-- .../Mftf/Section/AdminCatalogSearchTermNewSection.xml | 4 ++-- .../Test/Mftf/Test/AdminDeleteSearchTermTest.xml | 10 +++++----- 5 files changed, 14 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminCatalogSearchTermActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminCatalogSearchTermActionGroup.xml index a9444a4e5baa..33ffa4fe1b29 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminCatalogSearchTermActionGroup.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminCatalogSearchTermActionGroup.xml @@ -8,7 +8,6 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!--Add new search term and AssertSearchTermSaveSuccessMessage--> <actionGroup name="AssertSearchTermSaveSuccessMessage"> <arguments> <argument name="searchQuery" type="string"/> @@ -21,13 +20,12 @@ <click selector="{{AdminCatalogSearchTermIndexSection.addNewSearchTermButton}}" stepKey="clickAddNewSearchTermButton"/> <waitForPageLoad stepKey="waitForAdminCatalogSearchTermNewPageLoad"/> <fillField selector="{{AdminCatalogSearchTermNewSection.searchQuery}}" userInput="{{searchQuery}}" stepKey="fillSearchQueryTextBox"/> - <click selector="{{AdminCatalogSearchTermNewSection.store('storeValue')}}" stepKey="selectMainWebsiteStore"/> + <selectOption selector="{{AdminCatalogSearchTermNewSection.store}}" userInput="{{storeValue}}" stepKey="selectStoreValue"/> <fillField selector="{{AdminCatalogSearchTermNewSection.redirectUrl}}" userInput="{{redirectUrl}}" stepKey="fillRedirectUrl"/> - <click selector="{{AdminCatalogSearchTermNewSection.displayInSuggestedTerm('displayInSuggestedTerm')}}" stepKey="selectDisplayInSuggestedTerm"/> + <selectOption selector="{{AdminCatalogSearchTermNewSection.displayInSuggestedTerm}}" userInput="{{displayInSuggestedTerm}}" stepKey="selectDisplayInSuggestedTerm"/> <click selector="{{AdminCatalogSearchTermNewSection.saveSearchButton}}" stepKey="clickSaveSearchButton"/> <see selector="{{AdminCatalogSearchTermMessagesSection.successMessage}}" userInput="You saved the search term." stepKey="seeSaveSuccessMessage"/> </actionGroup> - <!--Search and delete search term and AssertSearchTermSuccessDeleteMessage--> <actionGroup name="AssertSearchTermSuccessDeleteMessage"> <arguments> <argument name="searchQuery" type="string"/> @@ -39,13 +37,12 @@ <fillField selector="{{AdminCatalogSearchTermIndexSection.searchQuery}}" userInput="{{searchQuery}}" stepKey="fillSearchQuery"/> <click selector="{{AdminCatalogSearchTermIndexSection.searchButton}}" stepKey="clickSearchButton"/> <waitForPageLoad stepKey="waitForSearchResultLoad"/> - <checkOption selector="{{AdminCatalogSearchTermIndexSection.checkBox}}" stepKey="checkCheckBox"/> - <selectOption selector="{{AdminCatalogSearchTermIndexSection.delete}}" userInput="delete" stepKey="selectDeleteOption"/> + <click selector="{{AdminCatalogSearchTermIndexSection.nthRow('1')}}" stepKey="checkFirstRow"/> + <selectOption selector="{{AdminCatalogSearchTermIndexSection.massActions}}" userInput="delete" stepKey="selectDeleteOption"/> <click selector="{{AdminCatalogSearchTermIndexSection.submit}}" stepKey="clickSubmitButton"/> <click selector="{{AdminCatalogSearchTermIndexSection.okButton}}" stepKey="clickOkButton"/> <see selector="{{AdminCatalogSearchTermMessagesSection.successMessage}}" userInput="Total of 1 record(s) were deleted." stepKey="seeSuccessMessage"/> </actionGroup> - <!--Verify deleted search term in grid and AssertSearchTermNotInGridMessage--> <actionGroup name="AssertSearchTermNotInGrid"> <arguments> <argument name="searchQuery" type="string"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchTermActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchTermActionGroup.xml index 5714cb4eb9f9..a825e06f490a 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchTermActionGroup.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchTermActionGroup.xml @@ -16,7 +16,7 @@ </arguments> <amOnPage url="{{StorefrontProductPage.url('url_key')}}" stepKey="goToMagentoStorefrontPage"/> <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> - <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{SimpleTerm.search_query}}" stepKey="fillSearchQuery"/> + <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{searchQuery}}" stepKey="fillSearchQuery"/> <waitForPageLoad stepKey="waitForSearchTextBox"/> <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextBoxButton"/> <waitForPageLoad stepKey="waitForSearch"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermIndexSection.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermIndexSection.xml index 812c302f9c00..5491c2722838 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermIndexSection.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermIndexSection.xml @@ -12,10 +12,10 @@ <element name="addNewSearchTermButton" type="button" selector="//div[@class='page-actions-buttons']/button[@id='add']" timeout="30"/> <element name="resetFilterButton" type="button" selector="//button[@class='action-default scalable action-reset action-tertiary']" timeout="30"/> <element name="searchButton" type="button" selector="//button[@class='action-default scalable action-secondary']" timeout="30"/> - <element name="delete" type="text" selector="//div[@class='admin__grid-massaction-form']//select[@id='search_term_grid_massaction-select']"/> + <element name="massActions" type="text" selector="//div[@class='admin__grid-massaction-form']//select[@id='search_term_grid_massaction-select']"/> <element name="submit" type="button" selector="//button[@class='action-default scalable']/span" timeout="30"/> <element name="searchQuery" type="text" selector="//tr[@class='data-grid-filters']//td/input[@name='search_query']"/> - <element name="checkBox" type="checkbox" selector="//label[@class='data-grid-checkbox-cell-inner']/input[@name='search']"/> + <element name="nthRow" type="checkbox" selector="//tbody/tr['{{rowNum}}']//input[@name='search']" parameterized="true"/> <element name="okButton" type="button" selector="//button[@class='action-primary action-accept']/span" timeout="30"/> <element name="emptyRecords" type="text" selector="//tr[@class='data-grid-tr-no-data even']/td[@class='empty-text']"/> </section> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermNewSection.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermNewSection.xml index 7922e994c496..a7d577a7508c 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermNewSection.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermNewSection.xml @@ -10,9 +10,9 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCatalogSearchTermNewSection"> <element name="searchQuery" type="text" selector="//div[@class='admin__field-control control']/input[@id='query_text']"/> - <element name="store" type="text" selector="//select[@id='store_id']//optgroup/option[contains(.,'{{store}}')]" parameterized="true"/> + <element name="store" type="text" selector="//select[@id='store_id']"/> <element name="redirectUrl" type="text" selector="//div[@class='admin__field-control control']/input[@id='redirect']"/> - <element name="displayInSuggestedTerm" type="select" selector="//select[@name='display_in_terms']/option[contains(.,'{{text}}')]" parameterized="true"/> + <element name="displayInSuggestedTerm" type="select" selector="//select[@name='display_in_terms']"/> <element name="saveSearchButton" type="button" selector="//button[@id='save']/span[@class='ui-button-text']" timeout="30"/> </section> </sections> \ No newline at end of file diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminDeleteSearchTermTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminDeleteSearchTermTest.xml index dc9d99fabefc..c72ed424ef30 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminDeleteSearchTermTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminDeleteSearchTermTest.xml @@ -10,8 +10,8 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminDeleteSearchTermTest"> <annotations> - <stories value="DeleteSearchTerm"/> - <title value="DeleteSearchTermEntityTestVariation1"/> + <stories value="Search terms"/> + <title value="Delete Search Term and Verify Storefront"/> <description value="Test log in to SearchTerm and DeleteSearchTerm"/> <testCaseId value="MC-13988"/> <severity value="CRITICAL"/> @@ -35,9 +35,9 @@ <!--Add new search term--> <actionGroup ref="AssertSearchTermSaveSuccessMessage" stepKey="addNewSearchTerm"> <argument name="searchQuery" value="{{SimpleTerm.search_query}}"/> - <argument name="storeValue" value="Default Store View"/> + <argument name="storeValue" value="{{SimpleTerm.store_id}}"/> <argument name="redirectUrl" value="{{SimpleTerm.redirect}}"/> - <argument name="displayInSuggestedTerm" value="No"/> + <argument name="displayInSuggestedTerm" value="{{SimpleTerm.display_in_terms}}"/> </actionGroup> <!--Search and delete search term and AssertSearchTermSuccessDeleteMessage--> @@ -50,7 +50,7 @@ <argument name="searchQuery" value="{{SimpleTerm.search_query}}"/> </actionGroup> - <!--Verify AssertSearchTermNotOnFrontend--> + <!--Go to storefront and Verify AssertSearchTermNotOnFrontend--> <actionGroup ref="AssertSearchTermNotOnFrontend" stepKey="verifySearchTermNotOnFrontend"> <argument name="searchQuery" value="{{SimpleTerm.search_query}}"/> <argument name="url_key" value="$$simpleProduct.custom_attributes[url_key]$$"/> From a8a8e22c67a28900d826088ee500f4e1d965f0e7 Mon Sep 17 00:00:00 2001 From: Kavitha <kanair@adobe.com> Date: Tue, 5 Mar 2019 10:50:09 -0600 Subject: [PATCH 074/265] MC-4454: Convert CreateDuplicateUrlCategoryEntityTest to MFTF --- .../Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml | 7 ++++--- .../Test/Mftf/Test/AdminCreateDuplicateCategoryTest.xml | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml index c658e8b359f8..e31f7193739c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml @@ -277,13 +277,14 @@ </actionGroup> <actionGroup name="adminFillAndSaveCategoryForm"> <arguments> - <argument name="category" type="string"/> + <argument name="categoryName" type="string"/> + <argument name="categoryUrlKey" type="string"/> </arguments> - <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="$$category.name$$" stepKey="enterCategoryName"/> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{categoryName}}" stepKey="enterCategoryName"/> <scrollTo selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="scrollToSearchEngineOptimization"/> <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSEO"/> <waitForPageLoad stepKey="waitForPageToLoad"/> - <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="$$category.custom_attributes[url_key]$$" stepKey="enterURLKey"/> + <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="{{categoryUrlKey}}" stepKey="enterURLKey"/> <scrollToTopOfPage stepKey="scrollToTheTopOfPage"/> <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> <waitForPageLoad stepKey="waitForPageToLoad1"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateCategoryTest.xml index 4865e3a89b67..79f901952dd7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateCategoryTest.xml @@ -10,7 +10,7 @@ <test name="AdminCreateDuplicateCategoryTest"> <annotations> <stories value="Create category"/> - <title value="CreateDuplicateUrlCategoryEntityTestVariation1_Subcategory_RequiredFields"/> + <title value="Create Duplicate Category With Already Existed UrlKey"/> <description value="Login as admin and create duplicate category"/> <testCaseId value="MC-14702"/> <severity value="CRITICAL"/> @@ -31,7 +31,8 @@ <!-- Fill the Category form with same name and urlKey as initially created category(SimpleSubCategory) --> <actionGroup ref="adminFillAndSaveCategoryForm" stepKey="fillCategoryForm"> - <argument name="category" value="category"/> + <argument name="categoryName" value="$$category.name$$"/> + <argument name="categoryUrlKey" value="$$category.custom_attributes[url_key]$$"/> </actionGroup> <!-- Assert error message --> From eaab9229eea7cd11215261c03b24e39f3a5bffe0 Mon Sep 17 00:00:00 2001 From: sathakur <sathakur@adobe.com> Date: Tue, 5 Mar 2019 11:01:44 -0600 Subject: [PATCH 075/265] MC-4874: Convert CreateWebsiteEntityTest to MFTF Addressing review comments --- .../Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml | 5 ++--- .../Store/Test/Mftf/Section/AdminStoresGridSection.xml | 2 +- .../Store/Test/Mftf/Test/AdminCreateWebsiteTest.xml | 7 +++---- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml index dd8bd7b6789a..ca614ec24138 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml @@ -47,16 +47,15 @@ <fillField userInput="{{websiteName}}" selector="{{AdminStoresGridSection.websiteFilterTextField}}" stepKey="fillWebsiteField"/> <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton"/> <waitForPageLoad stepKey="waitForStoreToLoad"/> - <seeElement selector="{{AdminStoresGridSection.nthRow(websiteName)}}" stepKey="seeAssertWebsiteInGrid"/> + <seeElement selector="{{AdminStoresGridSection.websiteName(websiteName)}}" stepKey="seeAssertWebsiteInGrid"/> </actionGroup> <actionGroup name="AssertWebsiteForm"> <arguments> <argument name="websiteName" type="string"/> <argument name="websiteCode" type="string"/> - <argument name="websiteId"/> </arguments> - <click selector="{{AdminStoresGridSection.nthRow(websiteName)}}" stepKey="clickWebsiteFirstRowInGrid"/> + <click selector="{{AdminStoresGridSection.websiteName(websiteName)}}" stepKey="clickWebsiteFirstRowInGrid"/> <waitForPageLoad stepKey="waitTillWebsiteFormPageIsOpened"/> <grabFromCurrentUrl regex="~(\d+)/~" stepKey="grabWebsiteIdFromCurrentUrl"/> <seeInCurrentUrl url="/system_store/editWebsite/website_id/{$grabWebsiteIdFromCurrentUrl}" stepKey="seeWebsiteId"/> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml index c3b3fc8fa342..b07587b70302 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml @@ -21,6 +21,6 @@ <element name="storeGrpNameInFirstRow" type="text" selector=".col-group_title>a"/> <element name="storeNameInFirstRow" type="text" selector=".col-store_title>a"/> <element name="firstRow" type="textarea" selector="(//*[@id='storeGrid_table']/tbody/tr)[1]"/> - <element name="nthRow" type="text" selector="//td[@class='a-left col-website_title ']/a[contains(.,'{{websiteName}}')]" parameterized="true"/> + <element name="websiteName" type="text" selector="//td[@class='a-left col-website_title ']/a[contains(.,'{{websiteName}}')]" parameterized="true"/> </section> </sections> \ No newline at end of file diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateWebsiteTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateWebsiteTest.xml index 44b774eed372..9d845cb23785 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateWebsiteTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateWebsiteTest.xml @@ -10,7 +10,7 @@ <test name="AdminCreateWebsiteTest"> <annotations> <stories value="Create Website"/> - <title value="CreateWebsiteEntityTestVariation1"/> + <title value="Create Website and Verify Store Form"/> <description value="Test log in to Stores and Create Website Test"/> <testCaseId value="MC-14302"/> <severity value="CRITICAL"/> @@ -34,16 +34,15 @@ <argument name="websiteCode" value="{{customWebsite.code}}"/> </actionGroup> - <!--Search created website in grid and AssertWebsiteInGrid--> + <!--Search created website in grid and verify AssertWebsiteInGrid--> <actionGroup ref="AssertWebsiteInGrid" stepKey="seeWebsiteInGrid"> <argument name="websiteName" value="{{customWebsite.name}}"/> </actionGroup> - <!--AssertWebsiteForm and AssertWebsiteOnStoreForm--> + <!--Verify website name and websitecode on website form (AssertWebsiteForm and AssertWebsiteOnStoreForm)--> <actionGroup ref="AssertWebsiteForm" stepKey="seeWebsiteForm"> <argument name="websiteName" value="{{customWebsite.name}}"/> <argument name="websiteCode" value="{{customWebsite.code}}"/> - <argument name="websiteId" value="admin/system_store/editWebsite/website_id/{$grabWebsiteIdFromCurrentUrl}"/> </actionGroup> </test> </tests> \ No newline at end of file From 59fa0e9ae1b32b00441ec585b9b0b5d98324c586 Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Tue, 5 Mar 2019 11:26:10 -0600 Subject: [PATCH 076/265] MC-4425: Convert ImportProductsTest to MFTF --- ...AssertProductInfoOnEditPageActionGroup.xml | 22 ++++ .../AssertProductOnAdminGridActionGroup.xml | 17 +++ .../Catalog/Test/Mftf/Data/ProductData.xml | 42 ++++++ ...oductsWithAddUpdateBehaviorActionGroup.xml | 29 +++++ ...ProductsWithReplaceBehaviorActionGroup.xml | 29 +++++ ...mportProductsWithAddUpdateBehaviorTest.xml | 122 ++++++++++++++++++ ...nImportProductsWithReplaceBehaviorTest.xml | 43 ++++++ .../AdminDeleteWebsiteActionGroup.xml | 1 + ...StoreFrontProductValidationActionGroup.xml | 21 +++ .../tests/_data/catalog_import_products.csv | 4 + 10 files changed, 330 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductInfoOnEditPageActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductOnAdminGridActionGroup.xml create mode 100644 app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsWithAddUpdateBehaviorActionGroup.xml create mode 100644 app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsWithReplaceBehaviorActionGroup.xml create mode 100644 app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml create mode 100644 app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml create mode 100644 app/code/Magento/Store/Test/Mftf/ActionGroup/StoreFrontProductValidationActionGroup.xml create mode 100644 dev/tests/acceptance/tests/_data/catalog_import_products.csv diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductInfoOnEditPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductInfoOnEditPageActionGroup.xml new file mode 100644 index 000000000000..7917fe68aaeb --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductInfoOnEditPageActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertProductInfoOnEditPageActionGroup" extends="OpenEditProductOnBackendActionGroup"> + <arguments> + <argument name="product" type="entity"/> + </arguments> + <waitForPageLoad stepKey="waitForProductToLoad"/> + <seeInField selector="{{AdminProductFormSection.productName}}" userInput="{{product.name}}" stepKey="seeProductName"/> + <seeInField selector="{{AdminProductFormSection.productSku}}" userInput="{{product.sku}}" stepKey="seeProductSku"/> + <seeInField selector="{{AdminProductFormSection.productPrice}}" userInput="{{product.price}}" stepKey="seeProductPrice"/> + <seeInField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{product.quantity}}" stepKey="seeProductQuantity"/> + <seeInField selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{product.status}}" stepKey="seeProductStockStatus"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductOnAdminGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductOnAdminGridActionGroup.xml new file mode 100644 index 000000000000..963c9d9f1c91 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductOnAdminGridActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertProductOnAdminGridActionGroup" extends="viewProductInAdminGrid"> + <arguments> + <argument name="product" defaultValue="_defaultProduct"/> + </arguments> + <remove keyForRemoval="clickClearFiltersAfter"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 0b3a31194ea3..1c107d315a2b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -60,6 +60,48 @@ <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> + <entity name="SimpleProductAfterImport1" type="product"> + <data key="sku">SimpleProductForTest1</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="name">SimpleProductAfterImport1</data> + <data key="price">250.00</data> + <data key="visibility">4</data> + <data key="status">1</data> + <data key="quantity">100</data> + <data key="urlKey">simple-product-for-test-1</data> + <data key="weight">1</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> + </entity> + <entity name="SimpleProductAfterImport2" type="product"> + <data key="sku">SimpleProductForTest2</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="name">SimpleProductAfterImport2</data> + <data key="price">300.00</data> + <data key="visibility">4</data> + <data key="status">1</data> + <data key="quantity">100</data> + <data key="urlKey">simple-product-for-test-2</data> + <data key="weight">1</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> + </entity> + <entity name="SimpleProductAfterImport3" type="product"> + <data key="sku">SimpleProductForTest3</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="name">SimpleProductAfterImport3</data> + <data key="price">350.00</data> + <data key="visibility">4</data> + <data key="status">1</data> + <data key="quantity">100</data> + <data key="urlKey">simple-product-for-test-3</data> + <data key="weight">1</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> + </entity> <entity name="SimpleProduct2" type="product"> <data key="sku" unique="suffix">SimpleProduct</data> <data key="type_id">simple</data> diff --git a/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsWithAddUpdateBehaviorActionGroup.xml b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsWithAddUpdateBehaviorActionGroup.xml new file mode 100644 index 000000000000..53406ed25e3c --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsWithAddUpdateBehaviorActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminImportProductsWithAddUpdateBehaviorActionGroup"> + <arguments> + <argument name="importFile" type="string"/> + <argument name="importMessage" type="string"/> + </arguments> + <amOnPage url="{{AdminImportIndexPage.url}}" stepKey="goToImportIndexPage"/> + <waitForPageLoad stepKey="AdminImportMainSectionLoad"/> + <selectOption selector="{{AdminImportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> + <waitForElementVisible selector="{{AdminImportMainSection.importBehavior}}" stepKey="waitForImportBehaviorElementVisible"/> + <selectOption selector="{{AdminImportMainSection.importBehavior}}" userInput="Add/Update" stepKey="selectReplaceOption"/> + <attachFile selector="{{AdminImportMainSection.selectFileToImport}}" userInput="{{importFile}}" stepKey="attachFileForImport"/> + <click selector="{{AdminImportHeaderSection.checkDataButton}}" stepKey="clickCheckDataButton"/> + <click selector="{{AdminImportMainSection.importButton}}" stepKey="clickImportButton"/> + <waitForPageLoad stepKey="AdminImportMainSectionLoad2"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="Import successfully done" stepKey="assertSuccessMessage"/> + <waitForPageLoad stepKey="AdminMessagesSection"/> + <see selector="{{AdminMessagesSection.notice}}" userInput="{{importMessage}}" stepKey="seeImportMessage"/> + </actionGroup> + </actionGroups> diff --git a/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsWithReplaceBehaviorActionGroup.xml b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsWithReplaceBehaviorActionGroup.xml new file mode 100644 index 000000000000..5e7c3d164e06 --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsWithReplaceBehaviorActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminImportProductsWithReplaceBehaviorActionGroup"> + <arguments> + <argument name="importFile" type="string"/> + <argument name="importMessage" type="string"/> + </arguments> + <amOnPage url="{{AdminImportIndexPage.url}}" stepKey="goToImportIndexPage"/> + <waitForPageLoad stepKey="AdminImportMainSectionLoad"/> + <selectOption selector="{{AdminImportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> + <waitForElementVisible selector="{{AdminImportMainSection.importBehavior}}" stepKey="waitForImportBehaviorElementVisible"/> + <selectOption selector="{{AdminImportMainSection.importBehavior}}" userInput="Replace" stepKey="selectReplaceOption"/> + <attachFile selector="{{AdminImportMainSection.selectFileToImport}}" userInput="{{importFile}}" stepKey="attachFileForImport"/> + <click selector="{{AdminImportHeaderSection.checkDataButton}}" stepKey="clickCheckDataButton"/> + <click selector="{{AdminImportMainSection.importButton}}" stepKey="clickImportButton"/> + <waitForPageLoad stepKey="AdminImportMainSectionLoad2"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="Import successfully done" stepKey="assertSuccessMessage"/> + <waitForPageLoad stepKey="AdminMessagesSection"/> + <see selector="{{AdminMessagesSection.notice}}" userInput="{{importMessage}}" stepKey="assertNotice"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml new file mode 100644 index 000000000000..72156ce57294 --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminImportProductsWithAddUpdateBehaviorTest"> + <annotations> + <description value="Verify Magento native import products with add/update behavior."/> + <stories value="Verify Magento native import products with add/update behavior."/> + <features value="Import/Export"/> + <title value="Verify Magento native import products with add/update behavior."/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-47724"/> + <group value="importExport"/> + </annotations> + <before> + <!--Create Simple Product1--> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createSimpleProduct1"> + <field key="name">SimpleProductForTest1</field> + <field key="sku">SimpleProductForTest1</field> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Login as Admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Create Website --> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="AdminCreateWebsite"> + <argument name="newWebsiteName" value="secondWebsite"/> + <argument name="websiteCode" value="second_website"/> + </actionGroup> + + <!-- Create store group --> + <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="AdminCreateStore"> + <argument name="website" value="secondWebsite"/> + <argument name="storeGroupName" value="{{customStoreGroup.name}}"/> + <argument name="storeGroupCode" value="{{customStoreGroup.code}}"/> + </actionGroup> + + <!-- Create store view --> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="AdminCreateStoreView"> + <argument name="StoreGroup" value="customStoreGroup"/> + <argument name="customStore" value="customStore"/> + </actionGroup> + </before> + <after> + <!-- Delete all products that replaced products in the before block post import --> + <deleteData stepKey="deleteSimpleProduct1" url="/V1/products/SimpleProductForTest1"/> + <deleteData stepKey="deleteSimpleProduct2" url="/V1/products/SimpleProductForTest2"/> + <deleteData stepKey="deleteSimpleProduct3" url="/V1/products/SimpleProductForTest3"/> + + <!-- Delete category created in the before block --> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!--Delete website created in the before block --> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="DeleteWebsite" before="logoutFromAdmin"> + <argument name="websiteName" value="secondWebsite"/> + </actionGroup> + + <!--Logout--> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + + </after> + + <!-- Import products with add/update behavior --> + <actionGroup ref="AdminImportProductsWithAddUpdateBehaviorActionGroup" stepKey="adminImportProducts"> + <argument name="importFile" value="catalog_import_products.csv"/> + <argument name="importMessage" value="Created: 2, Updated: 1, Deleted: 0"/> + </actionGroup> + + <!-- Assert Simple Product1 on grid--> + <actionGroup ref="AssertProductOnAdminGridActionGroup" stepKey="assertSimpleProduct1OnAdminGrid"> + <argument name="product" value="SimpleProductAfterImport1"/> + </actionGroup> + + <!-- Assert Simple Product1 on edit page--> + <actionGroup ref="AssertProductInfoOnEditPageActionGroup" stepKey="assertSimpleProduct1OnEditPage"> + <argument name="product" value="SimpleProductAfterImport1"/> + </actionGroup> + + <!-- Assert Simple Product2 on grid--> + <actionGroup ref="AssertProductOnAdminGridActionGroup" stepKey="assertSimpleProduct2OnAdminGrid"> + <argument name="product" value="SimpleProductAfterImport2"/> + </actionGroup> + + <!-- Assert Simple Product2 on edit page--> + <actionGroup ref="AssertProductInfoOnEditPageActionGroup" stepKey="assertSimpleProduct2OnEditPage"> + <argument name="product" value="SimpleProductAfterImport2"/> + </actionGroup> + + <!-- Assert Simple Product3 on grid--> + <actionGroup ref="AssertProductOnAdminGridActionGroup" stepKey="assertSimpleProduct3OnAdminGrid"> + <argument name="product" value="SimpleProductAfterImport3"/> + </actionGroup> + + <!-- Assert Simple Product3 on edit page--> + <actionGroup ref="AssertProductInfoOnEditPageActionGroup" stepKey="assertSimpleProduct3OnEditPage"> + <argument name="product" value="SimpleProductAfterImport3"/> + </actionGroup> + + <!-- Assert SimpleProduct1 on store front--> + <actionGroup ref="StoreFrontProductValidationActionGroup" stepKey="storeFrontSimpleProduct1Validation"> + <argument name="product" value="SimpleProductAfterImport1"/> + </actionGroup> + + <!-- Assert SimpleProduct2 on store front--> + <actionGroup ref="StoreFrontProductValidationActionGroup" stepKey="storeFrontSimpleProduct2Validation"> + <argument name="product" value="SimpleProductAfterImport2"/> + </actionGroup> + + <!-- Assert SimpleProduct3 on store front--> + <actionGroup ref="StoreFrontProductValidationActionGroup" stepKey="storeFrontSimpleProduct3Validation"> + <argument name="product" value="SimpleProductAfterImport3"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml new file mode 100644 index 000000000000..6a006814d447 --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminImportProductsWithReplaceBehaviorTest" extends="AdminImportProductsWithAddUpdateBehaviorTest"> + <annotations> + <description value="Verify Magento native import products with replace behavior."/> + <stories value="Verify Magento native import products with replace behavior."/> + <features value="Import/Export"/> + <title value="Verify Magento native import products with replace behavior."/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-47719"/> + <group value="importExport"/> + </annotations> + <before> + <!--Create Simple Product2--> + <createData entity="SimpleProduct" stepKey="createSimpleProduct2"> + <field key="name">SimpleProductForTest2</field> + <field key="sku">SimpleProductForTest2</field> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!--Create Simple Product3--> + <createData entity="SimpleProduct" stepKey="createSimpleProduct3"> + <field key="name">SimpleProductForTest3</field> + <field key="sku">SimpleProductForTest3</field> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + + <!-- Import products with replace behavior --> + <actionGroup ref="AdminImportProductsWithReplaceBehaviorActionGroup" stepKey="adminImportProducts"> + <argument name="importFile" value="catalog_import_products.csv"/> + <argument name="importMessage" value="Created: 3, Updated: 0, Deleted: 3"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml index 58fd0a3f0bc2..1721e3185402 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml @@ -19,6 +19,7 @@ <click selector="{{AdminStoresGridSection.websiteNameInFirstRow}}" stepKey="clickEditExistingStoreRow"/> <waitForPageLoad stepKey="waitForStoreToLoad"/> <click selector="{{AdminStoresMainActionsSection.deleteButton}}" stepKey="clickDeleteWebsiteButtonOnEditWebsitePage"/> + <waitForPageLoad stepKey="waitForDeleteStoreGroupSectionLoad" time="30"/> <selectOption userInput="No" selector="{{AdminStoresDeleteStoreGroupSection.createDbBackup}}" stepKey="setCreateDbBackupToNo"/> <click selector="{{AdminStoresDeleteStoreGroupSection.deleteStoreGroupButton}}" stepKey="clickDeleteWebsiteButton"/> <waitForElementVisible selector="{{AdminStoresGridSection.websiteFilterTextField}}" stepKey="waitForStoreGridToReload"/> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/StoreFrontProductValidationActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/StoreFrontProductValidationActionGroup.xml new file mode 100644 index 000000000000..f11394c643ad --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/StoreFrontProductValidationActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StoreFrontProductValidationActionGroup"> + <arguments> + <argument name="product" type="entity"/> + </arguments> + <amOnPage url="{{StorefrontProductPage.url(product.urlKey)}}" stepKey="seeProductPage"/> + <waitForPageLoad stepKey="waitForStoreFrontProductPageToLoad"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{product.name}}" stepKey="seeProductInStoreFrontPage"/> + <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="{{product.sku}}" stepKey="seeCorrectSku"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="{{product.price}}" stepKey="seeCorrectPrice"/> + </actionGroup> +</actionGroups> diff --git a/dev/tests/acceptance/tests/_data/catalog_import_products.csv b/dev/tests/acceptance/tests/_data/catalog_import_products.csv new file mode 100644 index 000000000000..7732f15d4ce3 --- /dev/null +++ b/dev/tests/acceptance/tests/_data/catalog_import_products.csv @@ -0,0 +1,4 @@ +sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,swatch_image,swatch_image_label,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,related_skus,related_position,crosssell_skus,crosssell_position,upsell_skus,upsell_position,additional_images,additional_image_labels,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,bundle_shipment_type,configurable_variations,configurable_variation_labels,associated_skus +SimpleProductForTest1,,Default,simple,"Default","base,second_website",SimpleProductAfterImport1,,,1.0000,1,"Taxable Goods","Catalog, Search",250.0000,,,,simple-product-for-test-1,,,,,,,,,,,,"3/4/19, 5:53 AM","3/4/19, 4:47 PM",,,"Block after Info Column",,,,,,,,,,,"Use config",,,100.0000,0.0000,1,0,0,1,1.0000,1,0.0000,1,1,,1,0,1,1,0.0000,1,0,0,0,,,,,,,,,,,,,,,,,,, +SimpleProductForTest2,,Default,simple,"Default",base,SimpleProductAfterImport2,,,1.0000,1,"Taxable Goods","Catalog, Search",300.0000,,,,simple-product-for-test-2,,,,,,,,,,,,"3/4/19, 5:53 AM","3/4/19, 4:47 PM",,,"Block after Info Column",,,,,,,,,,,"Use config",,,100.0000,0.0000,1,0,0,1,1.0000,1,0.0000,1,1,,1,0,1,1,0.0000,1,0,0,0,,,,,,,,,,,,,,,,,,, +SimpleProductForTest3,,Default,simple,"Default","base,second_website",SimpleProductAfterImport3,,,1.0000,1,"Taxable Goods","Catalog, Search",350.0000,,,,simple-product-for-test-3,,,,,,,,,,,,"3/4/19, 5:53 AM","3/4/19, 4:47 PM",,,"Block after Info Column",,,,,,,,,,,"Use config",,,100.0000,0.0000,1,0,0,1,1.0000,1,0.0000,1,1,,1,0,1,1,0.0000,1,0,0,0,,,,,,,,,,,,,,,,,,, \ No newline at end of file From 33b888e0bb853f1a9d4a6e6e85a528792102a47a Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Tue, 5 Mar 2019 11:31:59 -0600 Subject: [PATCH 077/265] MC-4425: Convert ImportProductsTest to MFTF --- .../Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml index 72156ce57294..cdef15d2a092 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml @@ -48,7 +48,7 @@ <argument name="StoreGroup" value="customStoreGroup"/> <argument name="customStore" value="customStore"/> </actionGroup> - </before> + </before> <after> <!-- Delete all products that replaced products in the before block post import --> <deleteData stepKey="deleteSimpleProduct1" url="/V1/products/SimpleProductForTest1"/> @@ -65,7 +65,6 @@ <!--Logout--> <actionGroup ref="logout" stepKey="logoutFromAdmin"/> - </after> <!-- Import products with add/update behavior --> From 132b6366787d0f0971100f80c811fe5e5ff3681c Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Tue, 5 Mar 2019 11:39:08 -0600 Subject: [PATCH 078/265] MC-4425: Convert ImportProductsTest to MFTF --- .../Test/AdminImportProductsWithAddUpdateBehaviorTest.xml | 4 ++-- .../Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml index cdef15d2a092..22d79334796a 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml @@ -19,7 +19,7 @@ <group value="importExport"/> </annotations> <before> - <!--Create Simple Product1--> + <!-- Create Simple Product1 --> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="SimpleProduct" stepKey="createSimpleProduct1"> <field key="name">SimpleProductForTest1</field> @@ -63,7 +63,7 @@ <argument name="websiteName" value="secondWebsite"/> </actionGroup> - <!--Logout--> + <!-- Logout --> <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml index 6a006814d447..89008340675b 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml @@ -19,14 +19,14 @@ <group value="importExport"/> </annotations> <before> - <!--Create Simple Product2--> + <!-- Create Simple Product2 --> <createData entity="SimpleProduct" stepKey="createSimpleProduct2"> <field key="name">SimpleProductForTest2</field> <field key="sku">SimpleProductForTest2</field> <requiredEntity createDataKey="createCategory"/> </createData> - <!--Create Simple Product3--> + <!-- Create Simple Product3 --> <createData entity="SimpleProduct" stepKey="createSimpleProduct3"> <field key="name">SimpleProductForTest3</field> <field key="sku">SimpleProductForTest3</field> From cbfbc8662d78ad46de151b3b8113b4cd03c89835 Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Tue, 5 Mar 2019 11:47:54 -0600 Subject: [PATCH 079/265] MC-4425: Convert ImportProductsTest to MFTF Added migrated tags --- .../Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml | 1 + .../Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml | 1 + .../CatalogImportExport/Test/TestCase/ImportProductsTest.xml | 2 ++ 3 files changed, 4 insertions(+) diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml index 22d79334796a..954a2ee2d9d5 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml @@ -17,6 +17,7 @@ <severity value="CRITICAL"/> <testCaseId value="MAGETWO-47724"/> <group value="importExport"/> + <group value="mtf_migrated"/> </annotations> <before> <!-- Create Simple Product1 --> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml index 89008340675b..3ebe1f59885b 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml @@ -17,6 +17,7 @@ <severity value="CRITICAL"/> <testCaseId value="MAGETWO-47719"/> <group value="importExport"/> + <group value="mtf_migrated"/> </annotations> <before> <!-- Create Simple Product2 --> diff --git a/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ImportProductsTest.xml b/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ImportProductsTest.xml index edb0aad954fb..77e5e2b91d93 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ImportProductsTest.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ImportProductsTest.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\CatalogImportExport\Test\TestCase\ImportProductsTest" summary="Import products"> <variation name="ImportProductVariation1" ticketId="MAGETWO-47724" summary="Import Products with Add/Update Behavior"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="import/data" xsi:type="array"> <item name="entity" xsi:type="string">Products</item> <item name="behavior" xsi:type="string">Add/Update</item> @@ -38,6 +39,7 @@ <constraint name="Magento\CatalogImportExport\Test\Constraint\AssertProductsInGrid" /> </variation> <variation name="ImportProductVariation2" ticketId="MAGETWO-47719" summary="Import Products assigned to different websites with Replace Behavior"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="import/data/entity" xsi:type="string">Products</data> <data name="import/data/behavior" xsi:type="string">Replace</data> <data name="import/data/validation_strategy" xsi:type="string">Stop on Error</data> From 190b72ae5a159d4be2dfb7f4a09ec7eb590fc587 Mon Sep 17 00:00:00 2001 From: Kavitha <kanair@adobe.com> Date: Tue, 5 Mar 2019 11:49:44 -0600 Subject: [PATCH 080/265] MC-4903: Convert UpdateProductUrlRewriteEntityTest to MFTF --- .../Mftf/ActionGroup/AdminUrlRewriteActionGroup.xml | 10 ++++------ .../StorefrontUrlRewriteRedirectActionGroup.xml | 1 + .../UrlRewrite/Test/Mftf/Data/UrlRewriteData.xml | 1 + .../UrlRewrite/Test/Mftf/MetaData/url_rewrite-meta.xml | 2 -- ...ateProductUrlRewriteAndAddTemporaryRedirectTest.xml | 8 +++----- 5 files changed, 9 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteActionGroup.xml index 5886b40c4547..c82c8d3d088f 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteActionGroup.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteActionGroup.xml @@ -77,14 +77,12 @@ </actionGroup> <actionGroup name="AdminUpdateUrlRewrite"> <arguments> - <argument name="requestPath" type="string"/> - <argument name="redirectTypeValue" type="string"/> - <argument name="description" type="string"/> + <argument name="urlRewrite" defaultValue="updateUrlRewrite"/> </arguments> - <fillField selector="{{AdminUrlRewriteEditSection.requestPath}}" userInput="{{requestPath}}" stepKey="fillRequestPath"/> + <fillField selector="{{AdminUrlRewriteEditSection.requestPath}}" userInput="{{urlRewrite.request_path}}" stepKey="fillRequestPath"/> <click selector="{{AdminUrlRewriteEditSection.redirectType}}" stepKey="selectRedirectType"/> - <click selector="{{AdminUrlRewriteEditSection.redirectTypeValue('redirectTypeValue')}}" stepKey="selectRedirectTypeValue"/> - <fillField selector="{{AdminUrlRewriteEditSection.description}}" userInput="{{description}}" stepKey="fillDescription"/> + <click selector="{{AdminUrlRewriteEditSection.redirectTypeValue(urlRewrite.redirect_type_label)}}" stepKey="selectRedirectTypeValue"/> + <fillField selector="{{AdminUrlRewriteEditSection.description}}" userInput="{{urlRewrite.description}}" stepKey="fillDescription"/> <click selector="{{AdminUrlRewriteEditSection.saveButton}}" stepKey="clickOnSaveButton"/> <seeElement selector="{{AdminUrlRewriteIndexSection.successMessage}}" stepKey="seeSuccessSaveMessage"/> </actionGroup> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/StorefrontUrlRewriteRedirectActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/StorefrontUrlRewriteRedirectActionGroup.xml index a299e1689d6a..482038a28ccb 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/StorefrontUrlRewriteRedirectActionGroup.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/StorefrontUrlRewriteRedirectActionGroup.xml @@ -30,3 +30,4 @@ <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="{{productSku}}" stepKey="seeProductSkuInStoreFrontPage"/> </actionGroup> </actionGroups> + diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Data/UrlRewriteData.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Data/UrlRewriteData.xml index 942882be259f..a580e68639c7 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Data/UrlRewriteData.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Data/UrlRewriteData.xml @@ -23,5 +23,6 @@ <data key="redirect_type_label">Temporary (302)</data> <data key="store_id">1</data> <data key="store">Default Store View</data> + <data key="description">Update Url Rewrite</data> </entity> </entities> \ No newline at end of file diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/MetaData/url_rewrite-meta.xml b/app/code/Magento/UrlRewrite/Test/Mftf/MetaData/url_rewrite-meta.xml index 84cf00ac0899..f33a1dee8e8b 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/MetaData/url_rewrite-meta.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/MetaData/url_rewrite-meta.xml @@ -9,12 +9,10 @@ <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateUrlRewrite" dataType="urlRewrite" type="create" auth="adminFormKey" url="/admin/url_rewrite/save/" method="POST" successRegex="/messages-message-success/"> - <!--<object key="urlRewrite" dataType="urlRewrite">--> <field key="store_id">integer</field> <field key="redirect_type">integer</field> <field key="request_path">string</field> <field key="target_path">string</field> <field key="description">string</field> - <!--</object>--> </operation> </operations> \ No newline at end of file diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml index cbb3680f3734..29b8365fb690 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml @@ -9,7 +9,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest"> <annotations> - <stories value="Update URL rewrite"/> + <stories value="URL Rewrite"/> <title value="Update Product URL Rewrites"/> <description value="Login as Admin and update product UrlRewrite and add Temporary redirect type "/> <testCaseId value="MC-5351"/> @@ -33,16 +33,14 @@ <!-- Open UrlRewrite Edit page and update the fields --> <actionGroup ref="AdminUpdateUrlRewrite" stepKey="updateCategoryUrlRewrite"> - <argument name="requestPath" value="{{defaultUrlRewrite.request_path}}"/> - <argument name="redirectTypeValue" value="Temporary (302)"/> - <argument name="description" value="Update Product Url Rewrite"/> + <argument name="urlRewrite" value="updateUrlRewrite"/> </actionGroup> <!-- Assert product Url Rewrite in StoreFront --> <actionGroup ref="AssertStorefrontProductRedirect" stepKey="assertProductUrlRewriteInStoreFront"> <argument name="productName" value="$$createProduct.name$$"/> <argument name="productSku" value="$$createProduct.sku$$"/> - <argument name="productRequestPath" value="{{defaultUrlRewrite.request_path}}"/> + <argument name="productRequestPath" value="{{updateUrlRewrite.request_path}}"/> </actionGroup> </test> </tests> From 7ab162f0c910f36a9cb493ea050f44abe5594741 Mon Sep 17 00:00:00 2001 From: Kavitha <kanair@adobe.com> Date: Tue, 5 Mar 2019 14:36:32 -0600 Subject: [PATCH 081/265] MC-4455: Convert CreateDuplicateUrlProductEntity to MFTF --- .../ActionGroup/AdminProductActionGroup.xml | 16 +++++++------ .../Test/AdminCreateDuplicateProductTest.xml | 24 +++++++++++++++---- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index d2d95d811c43..709f74ede09a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -430,29 +430,31 @@ <click selector="{{AdminAddUpSellProductsModalSection.AddSelectedProductsButton}}" stepKey="addRelatedProductSelected"/> <waitForPageLoad stepKey="waitForPageToLoad1"/> </actionGroup> - <actionGroup name="createDuplicateProduct"> + <actionGroup name="adminFillAndSaveProductForm"> <arguments> - <argument name="product" defaultValue="$$createSimpleProduct$$" /> - <argument name="quantity" defaultValue="defaultSimpleProduct.quantity"/> + <argument name="product" defaultValue="defaultSimpleProduct"/> + <argument name="productName" type="string"/> + <argument name="productUrlKey" type="string"/> + <argument name="categoryName" type="string"/> </arguments> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="openProductIndexPage"/> <waitForPageLoad stepKey="waitForProductIndexPageToLoad"/> <click selector="{{AdminProductGridActionSection.addProductBtn}}" stepKey="clickOnAddNewProduct"/> <waitForPageLoad stepKey="waitForPageToLoad"/> - <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{product.name}}" stepKey="fillProductName"/> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{productName}}" stepKey="fillProductName"/> <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{product.sku}}" stepKey="fillProductSku"/> <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{product.price}}" stepKey="fillProductPrice"/> - <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{quantity}}" stepKey="fillProductQty"/> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{product.quantity}}" stepKey="fillProductQty"/> <selectOption selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{product.status}}" stepKey="selectStockStatus"/> <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeight"/> <fillField selector="{{AdminProductFormSection.productWeight}}" userInput="{{product.weight}}" stepKey="fillProductWeight"/> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{categoryName}}]" stepKey="searchAndSelectCategory"/> <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="scrollToSectionHeader"/> <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> - <fillField userInput="{{product.custom_attributes[url_key]}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> + <fillField userInput="{{productUrlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> <scrollToTopOfPage stepKey="scrollTopPageProduct"/> <waitForElementVisible selector="{{AdminProductFormActionSection.saveButton}}" stepKey="waitForSaveProductButton" /> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> <waitForPageLoad stepKey="waitForProductToSave"/> - <see selector="{{AdminProductFormSection.successMessage}}" userInput="The value specified in the URL Key field would generate a URL that already exists." stepKey="seeErrorMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml index 81a0916be6e7..68dfc44aa886 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml @@ -10,22 +10,36 @@ <test name="AdminCreateDuplicateProductTest"> <annotations> <stories value="Create Product"/> - <title value="CreateDuplicateUrlProductEntityTestVariation1"/> + <title value="Create Duplicate Product With Existed Subcategory Name And UrlKey"/> <description value="Login as admin and create duplicate Product"/> <testCaseId value="MC-14714"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> </annotations> + <before> <magentoCLI command="cache:flush" stepKey="flushCache"/> <actionGroup ref="LoginAsAdmin" stepKey="login"/> - <createData entity="defaultSimpleProduct" stepKey="createSimpleProduct"/> + <createData entity="SubCategory" stepKey="category"/> + <createData entity="Two_nested_categories" stepKey="subCategory"> + <requiredEntity createDataKey="category"/> + </createData> </before> <after> - <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="subCategory" stepKey="deleteSubCategory"/> + <deleteData createDataKey="category" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logout"/> </after> - <!-- Create duplicate Product and Save the Product --> - <actionGroup ref="createDuplicateProduct" stepKey="createDuplicateProduct"/> + <!-- Create Product using above created Subcategory Name and SubCategory UrlKey and Save the Product --> + <actionGroup ref="adminFillAndSaveProductForm" stepKey="createDuplicateProduct"> + <argument name="product" value="defaultSimpleProduct"/> + <argument name="productName" value="$$subCategory.name$$"/> + <argument name="productUrlKey" value="$$subCategory.custom_attributes[url_key]$$"/> + <argument name="categoryName" value="$$category.name$$"/> + </actionGroup> + + <!--Assert Error Message --> + <see selector="{{AdminProductFormSection.successMessage}}" userInput="The value specified in the URL Key field would generate a URL that already exists." stepKey="seeErrorMessage"/> </test> </tests> From a0d827c02a6c90061fd298b0596d932ee2d189bd Mon Sep 17 00:00:00 2001 From: sathakur <sathakur@adobe.com> Date: Tue, 5 Mar 2019 14:47:03 -0600 Subject: [PATCH 082/265] MC-4873: Convert UpdateWebsiteEntityTest to MFTF --- .../AdminCreateWebsiteActionGroup.xml | 26 ++++++++ .../Store/Test/Mftf/Data/WebsiteData.xml | 6 +- .../Mftf/Section/AdminStoresGridSection.xml | 3 +- .../Test/Mftf/Test/AdminUpdateWebsiteTest.xml | 59 +++++++++++++++++++ 4 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Store/Test/Mftf/Test/AdminUpdateWebsiteTest.xml diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml index ef8d77c8824f..ca614ec24138 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml @@ -36,4 +36,30 @@ <click selector="{{AdminStoresGridSection.websiteNameInFirstRow}}" stepKey="clickEditExistingWebsite"/> <grabFromCurrentUrl regex="~(\d+)/~" stepKey="grabFromCurrentUrl"/> </actionGroup> + + <actionGroup name="AssertWebsiteInGrid"> + <arguments> + <argument name="websiteName" type="string"/> + </arguments> + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> + <waitForPageLoad stepKey="waitForAdminSystemStorePageLoad"/> + <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> + <fillField userInput="{{websiteName}}" selector="{{AdminStoresGridSection.websiteFilterTextField}}" stepKey="fillWebsiteField"/> + <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton"/> + <waitForPageLoad stepKey="waitForStoreToLoad"/> + <seeElement selector="{{AdminStoresGridSection.websiteName(websiteName)}}" stepKey="seeAssertWebsiteInGrid"/> + </actionGroup> + + <actionGroup name="AssertWebsiteForm"> + <arguments> + <argument name="websiteName" type="string"/> + <argument name="websiteCode" type="string"/> + </arguments> + <click selector="{{AdminStoresGridSection.websiteName(websiteName)}}" stepKey="clickWebsiteFirstRowInGrid"/> + <waitForPageLoad stepKey="waitTillWebsiteFormPageIsOpened"/> + <grabFromCurrentUrl regex="~(\d+)/~" stepKey="grabWebsiteIdFromCurrentUrl"/> + <seeInCurrentUrl url="/system_store/editWebsite/website_id/{$grabWebsiteIdFromCurrentUrl}" stepKey="seeWebsiteId"/> + <seeInField selector="{{AdminNewWebsiteSection.name}}" userInput="{{websiteName}}" stepKey="seeAssertWebsiteName"/> + <seeInField selector="{{AdminNewWebsiteSection.code}}" userInput="{{websiteCode}}" stepKey="seeAssertWebsiteCode"/> + </actionGroup> </actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml b/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml index f636336524f0..ae605256a281 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml @@ -23,4 +23,8 @@ <data key="name" unique="suffix">Custom Website</data> <data key="code" unique="suffix">custom_website</data> </entity> -</entities> + <entity name="updateCustomWebsite" extends="customWebsite"> + <data key="name" unique="suffix">website_upd</data> + <data key="code" unique="suffix">code_upd</data> + </entity> +</entities> \ No newline at end of file diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml index 592af42f2de3..d7006fd01b2f 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml @@ -23,5 +23,6 @@ <element name="firstRow" type="textarea" selector="(//*[@id='storeGrid_table']/tbody/tr)[1]"/> <element name="successMessage" type="text" selector="//div[@class='message message-success success']/div"/> <element name="emptyText" type="text" selector="//tr[@class='data-grid-tr-no-data even']/td[@class='empty-text']"/> + <element name="websiteName" type="text" selector="//td[@class='a-left col-website_title ']/a[contains(.,'{{websiteName}}')]" parameterized="true"/> </section> -</sections> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateWebsiteTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateWebsiteTest.xml new file mode 100644 index 000000000000..6b666126569a --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateWebsiteTest.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<!-- Test XML Example --> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUpdateWebsiteTest"> + <annotations> + <stories value="Update Website"/> + <title value="Update Website and Verify Store Form"/> + <description value="Test log in to Stores and Update Website Test"/> + <testCaseId value="MC-14301"/> + <severity value="CRITICAL"/> + <group value="store"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--Create website--> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createWebsite"> + <argument name="newWebsiteName" value="{{customWebsite.name}}"/> + <argument name="websiteCode" value="{{customWebsite.code}}"/> + </actionGroup> + </before> + <after> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> + <argument name="websiteName" value="{{updateCustomWebsite.name}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Search created custom website in grid--> + <actionGroup ref="AssertWebsiteInGrid" stepKey="seeWebsiteInGrid"> + <argument name="websiteName" value="{{customWebsite.name}}"/> + </actionGroup> + <click selector="{{AdminStoresGridSection.websiteName(customWebsite.name)}}" stepKey="clickWebsiteFirstRowInGrid"/> + <waitForPageLoad stepKey="waitForWebsiteFormPageToOpen"/> + <!--Update website name and website code as per data created and verify AssertWebsiteSuccessSaveMessage--> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createWebsite"> + <argument name="newWebsiteName" value="{{updateCustomWebsite.name}}"/> + <argument name="websiteCode" value="{{updateCustomWebsite.code}}"/> + </actionGroup> + + <!--Search updated custom website(from above step) in grid and verify AssertWebsiteInGrid--> + <actionGroup ref="AssertWebsiteInGrid" stepKey="seeUpdatedWebsiteInGrid"> + <argument name="websiteName" value="{{updateCustomWebsite.name}}"/> + </actionGroup> + + <!--Verify updated website name and updated websitecode on website form (AssertWebsiteForm and AssertWebsiteOnStoreForm)--> + <actionGroup ref="AssertWebsiteForm" stepKey="seeUpdatedWebsiteForm"> + <argument name="websiteName" value="{{updateCustomWebsite.name}}"/> + <argument name="websiteCode" value="{{updateCustomWebsite.code}}"/> + </actionGroup> + </test> +</tests> \ No newline at end of file From 828b39374794f11c5689dd03e24a8300ba57b2e4 Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Tue, 5 Mar 2019 15:27:58 -0600 Subject: [PATCH 083/265] MC-4425: Convert ImportProductsTest to MFTF Merged 2 action groups with parametrization --- ...xml => AdminImportProductsActionGroup.xml} | 5 ++-- ...ProductsWithReplaceBehaviorActionGroup.xml | 29 ------------------- ...mportProductsWithAddUpdateBehaviorTest.xml | 3 +- ...nImportProductsWithReplaceBehaviorTest.xml | 3 +- 4 files changed, 7 insertions(+), 33 deletions(-) rename app/code/Magento/ImportExport/Test/Mftf/ActionGroup/{AdminImportProductsWithAddUpdateBehaviorActionGroup.xml => AdminImportProductsActionGroup.xml} (90%) delete mode 100644 app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsWithReplaceBehaviorActionGroup.xml diff --git a/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsWithAddUpdateBehaviorActionGroup.xml b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml similarity index 90% rename from app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsWithAddUpdateBehaviorActionGroup.xml rename to app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml index 53406ed25e3c..a9100b4730b8 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsWithAddUpdateBehaviorActionGroup.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml @@ -8,8 +8,9 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminImportProductsWithAddUpdateBehaviorActionGroup"> + <actionGroup name="AdminImportProductsActionGroup"> <arguments> + <argument name="behavior" type="string"/> <argument name="importFile" type="string"/> <argument name="importMessage" type="string"/> </arguments> @@ -17,7 +18,7 @@ <waitForPageLoad stepKey="AdminImportMainSectionLoad"/> <selectOption selector="{{AdminImportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> <waitForElementVisible selector="{{AdminImportMainSection.importBehavior}}" stepKey="waitForImportBehaviorElementVisible"/> - <selectOption selector="{{AdminImportMainSection.importBehavior}}" userInput="Add/Update" stepKey="selectReplaceOption"/> + <selectOption selector="{{AdminImportMainSection.importBehavior}}" userInput="{{behavior}}" stepKey="selectImportOption"/> <attachFile selector="{{AdminImportMainSection.selectFileToImport}}" userInput="{{importFile}}" stepKey="attachFileForImport"/> <click selector="{{AdminImportHeaderSection.checkDataButton}}" stepKey="clickCheckDataButton"/> <click selector="{{AdminImportMainSection.importButton}}" stepKey="clickImportButton"/> diff --git a/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsWithReplaceBehaviorActionGroup.xml b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsWithReplaceBehaviorActionGroup.xml deleted file mode 100644 index 5e7c3d164e06..000000000000 --- a/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsWithReplaceBehaviorActionGroup.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminImportProductsWithReplaceBehaviorActionGroup"> - <arguments> - <argument name="importFile" type="string"/> - <argument name="importMessage" type="string"/> - </arguments> - <amOnPage url="{{AdminImportIndexPage.url}}" stepKey="goToImportIndexPage"/> - <waitForPageLoad stepKey="AdminImportMainSectionLoad"/> - <selectOption selector="{{AdminImportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> - <waitForElementVisible selector="{{AdminImportMainSection.importBehavior}}" stepKey="waitForImportBehaviorElementVisible"/> - <selectOption selector="{{AdminImportMainSection.importBehavior}}" userInput="Replace" stepKey="selectReplaceOption"/> - <attachFile selector="{{AdminImportMainSection.selectFileToImport}}" userInput="{{importFile}}" stepKey="attachFileForImport"/> - <click selector="{{AdminImportHeaderSection.checkDataButton}}" stepKey="clickCheckDataButton"/> - <click selector="{{AdminImportMainSection.importButton}}" stepKey="clickImportButton"/> - <waitForPageLoad stepKey="AdminImportMainSectionLoad2"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="Import successfully done" stepKey="assertSuccessMessage"/> - <waitForPageLoad stepKey="AdminMessagesSection"/> - <see selector="{{AdminMessagesSection.notice}}" userInput="{{importMessage}}" stepKey="assertNotice"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml index 954a2ee2d9d5..f3238293e571 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml @@ -69,7 +69,8 @@ </after> <!-- Import products with add/update behavior --> - <actionGroup ref="AdminImportProductsWithAddUpdateBehaviorActionGroup" stepKey="adminImportProducts"> + <actionGroup ref="AdminImportProductsActionGroup" stepKey="adminImportProducts"> + <argument name="behavior" value="Add/Update"/> <argument name="importFile" value="catalog_import_products.csv"/> <argument name="importMessage" value="Created: 2, Updated: 1, Deleted: 0"/> </actionGroup> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml index 3ebe1f59885b..d45b294a34e4 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml @@ -36,7 +36,8 @@ </before> <!-- Import products with replace behavior --> - <actionGroup ref="AdminImportProductsWithReplaceBehaviorActionGroup" stepKey="adminImportProducts"> + <actionGroup ref="AdminImportProductsActionGroup" stepKey="adminImportProducts"> + <argument name="behavior" value="Replace"/> <argument name="importFile" value="catalog_import_products.csv"/> <argument name="importMessage" value="Created: 3, Updated: 0, Deleted: 3"/> </actionGroup> From a80943f529a337b36f59d3964a8448816f5e2532 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Wed, 6 Mar 2019 10:18:36 +0200 Subject: [PATCH 084/265] 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 51cf5897005ebb77d73ca088ce5c2e5fc53ca963 Mon Sep 17 00:00:00 2001 From: Stsiapan Korf <Stsiapan_Korf@epam.com> Date: Wed, 6 Mar 2019 15:58:17 +0000 Subject: [PATCH 085/265] MAGETWO-53037: Product duplication allows to save non-unique values for product attributes - Add message for unique value --- .../Controller/Adminhtml/Product/Save.php | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php index e84d9ff12906..825d0ee032d6 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php @@ -159,6 +159,7 @@ public function execute() if ($redirectBack === 'duplicate') { $product->unsetData('quantity_and_stock_status'); $newProduct = $this->productCopier->copy($product); + $this->checkUniqueAttributes($product); $this->messageManager->addSuccessMessage(__('You duplicated the product.')); } } catch (\Magento\Framework\Exception\LocalizedException $e) { @@ -343,4 +344,25 @@ private function persistMediaData(ProductInterface $product, array $data) return $data; } + + /** + * Check unique attributes and add error to message manager + * + * @param \Magento\Catalog\Model\Product $product + */ + private function checkUniqueAttributes(\Magento\Catalog\Model\Product $product) + { + $uniqueLabels = []; + foreach ($product->getAttributes() as $attribute) { + if ($attribute->getIsUnique() && $attribute->getIsUserDefined() + && $product->getData($attribute->getAttributeCode()) !== null + ) { + $uniqueLabels[] = $attribute->getDefaultFrontendLabel(); + } + } + if ($uniqueLabels) { + $uniqueLabels = implode('", "', $uniqueLabels); + $this->messageManager->addErrorMessage(__('The value of attribute(s) "%1" must be unique', $uniqueLabels)); + } + } } From 423bd3abc2c1b59738cbbe9a8928f4617e1854db Mon Sep 17 00:00:00 2001 From: Maria Kovdrysh <kovdrysh@adobe.com> Date: Wed, 6 Mar 2019 12:57:55 -0600 Subject: [PATCH 086/265] MC-4436: Convert CreateSearchTermEntityTest to MFTF --- ...StorefrontCatalogSearchTermActionGroup.xml | 12 ++++ .../Mftf/Test/CreateSearchTermEntityTest.xml | 59 +++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/CreateSearchTermEntityTest.xml diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchTermActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchTermActionGroup.xml index a825e06f490a..83e4ac50a74e 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchTermActionGroup.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchTermActionGroup.xml @@ -22,4 +22,16 @@ <waitForPageLoad stepKey="waitForSearch"/> <see selector="{{StorefrontMessagesSection.noticeMessage}}" userInput="Your search returned no results." stepKey="seeAssertSearchTermNotOnFrontendNoticeMessage"/> </actionGroup> + + <actionGroup name="AssertSearchTermOnFrontend"> + <arguments> + <argument name="searchQuery" type="string"/> + <argument name="redirectUrl" type="string"/> + </arguments> + <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{searchQuery}}" stepKey="fillSearchQuery"/> + <waitForPageLoad stepKey="waitForFillField"/> + <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextBoxButton"/> + <waitForPageLoad stepKey="waitForSearch"/> + <seeInCurrentUrl url="{{redirectUrl}}" stepKey="checkUrl"/> + </actionGroup> </actionGroups> \ No newline at end of file diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/CreateSearchTermEntityTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/CreateSearchTermEntityTest.xml new file mode 100644 index 000000000000..a710f3dd8fa5 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/CreateSearchTermEntityTest.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="CreateSearchTermTest"> + <annotations> + <stories value="Search terms"/> + <title value="Create search term test"/> + <description value="Admin should be able to create search term"/> + <testCaseId value="MC-13989"/> + <severity value="CRITICAL"/> + <group value="CatalogSearch"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Create simple product --> + <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> + </before> + <after> + <!-- Delete created search term --> + <actionGroup ref="AssertSearchTermSuccessDeleteMessage" stepKey="deleteSearchTerm"> + <argument name="searchQuery" value="$$createSimpleProduct.sku$$"/> + </actionGroup> + + <!-- Delete created product --> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + + <!-- Log out --> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Go to the search terms page and create new search term --> + <actionGroup ref="AssertSearchTermSaveSuccessMessage" stepKey="createNewSearchTerm"> + <argument name="searchQuery" value="$$createSimpleProduct.sku$$"/> + <argument name="storeValue" value="{{SimpleTerm.store_id}}"/> + <argument name="redirectUrl" value="{{SimpleTerm.redirect}}"/> + <argument name="displayInSuggestedTerm" value="{{SimpleTerm.display_in_terms}}"/> + </actionGroup> + + <!-- Go to storefront --> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + + <!-- Assert created search term on storefront --> + <actionGroup ref="AssertSearchTermOnFrontend" stepKey="assertCreatedSearchTermOnFrontend"> + <argument name="searchQuery" value="$$createSimpleProduct.sku$$"/> + <argument name="redirectUrl" value="{{SimpleTerm.redirect}}"/> + </actionGroup> + </test> +</tests> From b0ac70bee1d364ab693433c16250ef48186b0f5d Mon Sep 17 00:00:00 2001 From: duhon <duhon@rambler.ru> Date: Wed, 13 Feb 2019 19:04:42 -0600 Subject: [PATCH 087/265] MC-13613: Product mass update - MC-5665 Execution of heavy admin operations in asynchronous flow part2 --- .../Catalog/Api/Data/MassActionInterface.php | 135 ++++++++++ .../Product/Action/Attribute/Save.php | 135 +++------- .../Model/Attribute/Backend/Consumer.php | 254 ++++++++++++++++++ app/code/Magento/Catalog/Model/MassAction.php | 174 ++++++++++++ .../Magento/Catalog/etc/communication.xml | 12 + app/code/Magento/Catalog/etc/di.xml | 1 + app/code/Magento/Catalog/etc/queue.xml | 12 + .../Magento/Catalog/etc/queue_consumer.xml | 10 + .../Magento/Catalog/etc/queue_publisher.xml | 12 + .../Magento/Catalog/etc/queue_topology.xml | 12 + .../Reflection/DataObjectProcessor.php | 4 +- 11 files changed, 658 insertions(+), 103 deletions(-) create mode 100644 app/code/Magento/Catalog/Api/Data/MassActionInterface.php create mode 100644 app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php create mode 100644 app/code/Magento/Catalog/Model/MassAction.php create mode 100644 app/code/Magento/Catalog/etc/communication.xml create mode 100644 app/code/Magento/Catalog/etc/queue.xml create mode 100644 app/code/Magento/Catalog/etc/queue_consumer.xml create mode 100644 app/code/Magento/Catalog/etc/queue_publisher.xml create mode 100644 app/code/Magento/Catalog/etc/queue_topology.xml diff --git a/app/code/Magento/Catalog/Api/Data/MassActionInterface.php b/app/code/Magento/Catalog/Api/Data/MassActionInterface.php new file mode 100644 index 000000000000..3f47242e3aa6 --- /dev/null +++ b/app/code/Magento/Catalog/Api/Data/MassActionInterface.php @@ -0,0 +1,135 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Api\Data; + +/** + * MassAction interface. + * @api + * @since 101.1.0 + */ +interface MassActionInterface +{ + + /** + * Set data value. + * + * @param string[] $data + * @return void + * @since 101.1.0 + */ + public function setInventory($data); + + /** + * Get data value. + * + * @return string[] + * @since 101.1.0 + */ + public function getInventory():array; + + /** + * Set data value. + * + * @param string[] $data + * @return void + * @since 101.1.0 + */ + public function setAttributes($data); + + /** + * Get data value. + * + * @return string[] + * @since 101.1.0 + */ + public function getAttributes():array; + + /** + * Set data value. + * + * @param string[] $data + * @return void + * @since 101.1.0 + */ + public function setWebsiteRemove($data); + + /** + * Get data value. + * + * @return string[] + * @since 101.1.0 + */ + public function getWebsiteRemove():array; + + /** + * Set data value. + * + * @param string[] $data + * @return void + * @since 101.1.0 + */ + public function setWebsiteAdd($data); + + /** + * Get data value. + * + * @return string[] + * @since 101.1.0 + */ + public function getWebsiteAdd():array; + + /** + * Set data value. + * + * @param integer $data + * @return void + * @since 101.1.0 + */ + public function setStoreId($data); + + /** + * Get data value. + * + * @return integer + * @since 101.1.0 + */ + public function getStoreId(); + + /** + * Set data value. + * + * @param integer[] $data + * @return void + * @since 101.1.0 + */ + public function setProductIds(array $data); + + /** + * Get data value. + * + * @return integer[] + * @since 101.1.0 + */ + public function getProductIds():array; + + /** + * Set data value. + * + * @param integer $data + * @return void + * @since 101.1.0 + */ + public function setWebsiteId($data); + + /** + * Get data value. + * + * @return integer + * @since 101.1.0 + */ + public function getWebsiteId(); +} diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php index 0730e7a7c5dc..69969231490b 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php @@ -6,8 +6,12 @@ */ namespace Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute; +use Magento\Catalog\Api\Data\MassActionInterfaceFactory; +use Magento\CatalogInventory\Api\StockConfigurationInterface; use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; use Magento\Backend\App\Action; +use Magento\Framework\MessageQueue\PublisherInterface; +use Magento\Framework\App\ObjectManager; /** * Class Save @@ -49,6 +53,16 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribut */ protected $dataObjectHelper; + /** + * @var PublisherInterface + */ + private $messagePublisher; + /** + * @var MassActionInterfaceFactory|null + */ + private $massActionFactory; + + /** * @param Action\Context $context * @param \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeHelper @@ -58,6 +72,8 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribut * @param \Magento\Catalog\Helper\Product $catalogProduct * @param \Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory $stockItemFactory * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper + * @param PublisherInterface|null $publisher + * @param MassActionInterfaceFactory|null $massAction */ public function __construct( Action\Context $context, @@ -67,7 +83,9 @@ public function __construct( \Magento\CatalogInventory\Model\Indexer\Stock\Processor $stockIndexerProcessor, \Magento\Catalog\Helper\Product $catalogProduct, \Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory $stockItemFactory, - \Magento\Framework\Api\DataObjectHelper $dataObjectHelper + \Magento\Framework\Api\DataObjectHelper $dataObjectHelper, + PublisherInterface $publisher = null, + MassActionInterfaceFactory $massAction = null ) { $this->_productFlatIndexerProcessor = $productFlatIndexerProcessor; $this->_productPriceIndexerProcessor = $productPriceIndexerProcessor; @@ -76,6 +94,8 @@ public function __construct( $this->stockItemFactory = $stockItemFactory; parent::__construct($context, $attributeHelper); $this->dataObjectHelper = $dataObjectHelper; + $this->messagePublisher = $publisher ?: ObjectManager::getInstance()->get(PublisherInterface::class); + $this->massActionFactory = $massAction ?: ObjectManager::getInstance()->get(MassActionInterfaceFactory::class); } /** @@ -99,112 +119,25 @@ public function execute() $websiteAddData = $this->getRequest()->getParam('add_website_ids', []); /* Prepare inventory data item options (use config settings) */ - $options = $this->_objectManager->get(\Magento\CatalogInventory\Api\StockConfigurationInterface::class) - ->getConfigItemOptions(); + $options = $this->_objectManager->get(StockConfigurationInterface::class)->getConfigItemOptions(); foreach ($options as $option) { if (isset($inventoryData[$option]) && !isset($inventoryData['use_config_' . $option])) { $inventoryData['use_config_' . $option] = 0; } } - try { - $storeId = $this->attributeHelper->getSelectedStoreId(); - if ($attributesData) { - $dateFormat = $this->_objectManager->get(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class) - ->getDateFormat(\IntlDateFormatter::SHORT); - - foreach ($attributesData as $attributeCode => $value) { - $attribute = $this->_objectManager->get(\Magento\Eav\Model\Config::class) - ->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $attributeCode); - if (!$attribute->getAttributeId()) { - unset($attributesData[$attributeCode]); - continue; - } - if ($attribute->getBackendType() == 'datetime') { - if (!empty($value)) { - $filterInput = new \Zend_Filter_LocalizedToNormalized(['date_format' => $dateFormat]); - $filterInternal = new \Zend_Filter_NormalizedToLocalized( - ['date_format' => \Magento\Framework\Stdlib\DateTime::DATE_INTERNAL_FORMAT] - ); - $value = $filterInternal->filter($filterInput->filter($value)); - } else { - $value = null; - } - $attributesData[$attributeCode] = $value; - } elseif ($attribute->getFrontendInput() == 'multiselect') { - // Check if 'Change' checkbox has been checked by admin for this attribute - $isChanged = (bool)$this->getRequest()->getPost('toggle_' . $attributeCode); - if (!$isChanged) { - unset($attributesData[$attributeCode]); - continue; - } - if (is_array($value)) { - $value = implode(',', $value); - } - $attributesData[$attributeCode] = $value; - } - } - - $this->_objectManager->get(\Magento\Catalog\Model\Product\Action::class) - ->updateAttributes($this->attributeHelper->getProductIds(), $attributesData, $storeId); - } - - if ($inventoryData) { - // TODO why use ObjectManager? - /** @var \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry */ - $stockRegistry = $this->_objectManager - ->create(\Magento\CatalogInventory\Api\StockRegistryInterface::class); - /** @var \Magento\CatalogInventory\Api\StockItemRepositoryInterface $stockItemRepository */ - $stockItemRepository = $this->_objectManager - ->create(\Magento\CatalogInventory\Api\StockItemRepositoryInterface::class); - foreach ($this->attributeHelper->getProductIds() as $productId) { - $stockItemDo = $stockRegistry->getStockItem( - $productId, - $this->attributeHelper->getStoreWebsiteId($storeId) - ); - if (!$stockItemDo->getProductId()) { - $inventoryData['product_id'] = $productId; - } - - $stockItemId = $stockItemDo->getId(); - $this->dataObjectHelper->populateWithArray( - $stockItemDo, - $inventoryData, - \Magento\CatalogInventory\Api\Data\StockItemInterface::class - ); - $stockItemDo->setItemId($stockItemId); - $stockItemRepository->save($stockItemDo); - } - $this->_stockIndexerProcessor->reindexList($this->attributeHelper->getProductIds()); - } + $massAction = $this->massActionFactory->create(); + $massAction->setInventory($inventoryData); + $massAction->setAttributes($attributesData); + $massAction->setWebsiteRemove($websiteRemoveData); + $massAction->setWebsiteAdd($websiteAddData); + $massAction->setStoreId($this->attributeHelper->getSelectedStoreId()); + $massAction->setProductIds($this->attributeHelper->getProductIds()); + $massAction->setWebsiteId($this->attributeHelper->getStoreWebsiteId($massAction->getStoreId())); - if ($websiteAddData || $websiteRemoveData) { - /* @var $actionModel \Magento\Catalog\Model\Product\Action */ - $actionModel = $this->_objectManager->get(\Magento\Catalog\Model\Product\Action::class); - $productIds = $this->attributeHelper->getProductIds(); - - if ($websiteRemoveData) { - $actionModel->updateWebsites($productIds, $websiteRemoveData, 'remove'); - } - if ($websiteAddData) { - $actionModel->updateWebsites($productIds, $websiteAddData, 'add'); - } - - $this->_eventManager->dispatch('catalog_product_to_website_change', ['products' => $productIds]); - } - - $this->messageManager->addSuccessMessage( - __('A total of %1 record(s) were updated.', count($this->attributeHelper->getProductIds())) - ); - - $this->_productFlatIndexerProcessor->reindexList($this->attributeHelper->getProductIds()); - - if ($this->_catalogProduct->isDataForPriceIndexerWasChanged($attributesData) - || !empty($websiteRemoveData) - || !empty($websiteAddData) - ) { - $this->_productPriceIndexerProcessor->reindexList($this->attributeHelper->getProductIds()); - } + try { + $this->messagePublisher->publish('product_action_attribute.update', $massAction); + $this->messageManager->addSuccessMessage(__('Message is added to queue, wait')); } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { @@ -215,6 +148,6 @@ public function execute() } return $this->resultRedirectFactory->create() - ->setPath('catalog/product/', ['store' => $this->attributeHelper->getSelectedStoreId()]); + ->setPath('catalog/product/', ['store' => $massAction->getStoreId()]); } } diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php b/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php new file mode 100644 index 000000000000..029223972780 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php @@ -0,0 +1,254 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Attribute\Backend; + +use Magento\Catalog\Api\Data\MassActionInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Notification\NotifierInterface; +use Magento\Framework\App\ObjectManager; + +/** + * Consumer for export message. + */ +class Consumer +{ + /** + * @var NotifierInterface + */ + private $notifier; + + /** + * @var \Psr\Log\LoggerInterface + */ + private $logger; + + /** + * @var \Magento\Catalog\Model\Indexer\Product\Flat\Processor + */ + protected $_productFlatIndexerProcessor; + + /** + * @var \Magento\Catalog\Model\Indexer\Product\Price\Processor + */ + protected $_productPriceIndexerProcessor; + + /** + * Catalog product + * + * @var \Magento\Catalog\Helper\Product + */ + protected $_catalogProduct; + + /** + * @var \Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory + */ + protected $stockItemFactory; + + /** + * Stock Indexer + * + * @var \Magento\CatalogInventory\Model\Indexer\Stock\Processor + */ + protected $_stockIndexerProcessor; + + /** + * @var \Magento\Framework\Api\DataObjectHelper + */ + protected $dataObjectHelper; + + /** + * @var \Magento\Framework\Event\ManagerInterface + */ + private $_eventManager; + + /** + * @var ObjectManager + */ + private $_objectManager; + + /** + * @param \Magento\Catalog\Helper\Product $catalogProduct + * @param \Magento\Catalog\Model\Indexer\Product\Flat\Processor $productFlatIndexerProcessor + * @param \Magento\Catalog\Model\Indexer\Product\Price\Processor $productPriceIndexerProcessor + * @param \Magento\CatalogInventory\Model\Indexer\Stock\Processor $stockIndexerProcessor + * @param \Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory $stockItemFactory + * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper + * @param \Magento\Framework\Event\ManagerInterface $eventManager + * @param NotifierInterface $notifier + */ + public function __construct( + \Magento\Catalog\Helper\Product $catalogProduct, + \Magento\Catalog\Model\Indexer\Product\Flat\Processor $productFlatIndexerProcessor, + \Magento\Catalog\Model\Indexer\Product\Price\Processor $productPriceIndexerProcessor, + \Magento\CatalogInventory\Model\Indexer\Stock\Processor $stockIndexerProcessor, + \Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory $stockItemFactory, + \Magento\Framework\Api\DataObjectHelper $dataObjectHelper, + \Magento\Framework\Event\ManagerInterface $eventManager, + NotifierInterface $notifier + ) { + $this->_catalogProduct = $catalogProduct; + $this->_productFlatIndexerProcessor = $productFlatIndexerProcessor; + $this->_productPriceIndexerProcessor = $productPriceIndexerProcessor; + $this->_stockIndexerProcessor = $stockIndexerProcessor; + $this->stockItemFactory = $stockItemFactory; + $this->dataObjectHelper = $dataObjectHelper; + $this->notifier = $notifier; + $this->_eventManager = $eventManager; + $this->_objectManager = ObjectManager::getInstance(); + } + + public function process(MassActionInterface $data): void + { + try { + if ($data->getAttributes()) { + $attributesData = $this->getAttributesData($data, $data->getAttributes()); + } + + if ($data->getInventory()) { + $this->saveInventory($data, $data->getInventory()); + } + + if ($data->getWebsiteAdd() || $data->getWebsiteRemove()) { + $this->updateWebsiteInProducts($data, $data->getWebsiteRemove(), $data->getWebsiteAdd()); + } + + $this->reindex($data, $attributesData, $data->getWebsiteRemove(), $data->getWebsiteAdd()); + + $this->notifier->addNotice( + __('Product attributes updated'), + __('A total of %1 record(s) were updated.', count($data->getProductIds())) + ); + } catch (LocalizedException $exception) { + $this->notifier->addCritical( + __('Error during process occurred'), + __('Error during process occurred. Please check logs for detail') + ); + $this->logger->critical('Something went wrong while process. ' . $exception->getMessage()); + } + } + + /** + * @param MassActionInterface $data + * @param $attributesData + * @return mixed + */ + private function getAttributesData(MassActionInterface $data, $attributesData) + { + $dateFormat = $this->_objectManager->get(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class) + ->getDateFormat(\IntlDateFormatter::SHORT); + + foreach ($attributesData as $attributeCode => $value) { + $attribute = $this->_objectManager->get(\Magento\Eav\Model\Config::class) + ->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $attributeCode); + if (!$attribute->getAttributeId()) { + unset($attributesData[$attributeCode]); + continue; + } + if ($attribute->getBackendType() == 'datetime') { + if (!empty($value)) { + $filterInput = new \Zend_Filter_LocalizedToNormalized(['date_format' => $dateFormat]); + $filterInternal = new \Zend_Filter_NormalizedToLocalized( + ['date_format' => \Magento\Framework\Stdlib\DateTime::DATE_INTERNAL_FORMAT] + ); + $value = $filterInternal->filter($filterInput->filter($value)); + } else { + $value = null; + } + $attributesData[$attributeCode] = $value; + } elseif ($attribute->getFrontendInput() == 'multiselect') { + // Check if 'Change' checkbox has been checked by admin for this attribute + $isChanged = (bool)$this->getRequest()->getPost('toggle_' . $attributeCode); + if (!$isChanged) { + unset($attributesData[$attributeCode]); + continue; + } + if (is_array($value)) { + $value = implode(',', $value); + } + $attributesData[$attributeCode] = $value; + } + } + + $this->_objectManager->get(\Magento\Catalog\Model\Product\Action::class) + ->updateAttributes($data->getProductIds(), $attributesData, $data->getStoreId()); + return $attributesData; + } + + /** + * @param MassActionInterface $data + * @param $inventoryData + */ + private function saveInventory(MassActionInterface $data, $inventoryData): void + { + // TODO why use ObjectManager? + /** @var \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry */ + $stockRegistry = $this->_objectManager + ->create(\Magento\CatalogInventory\Api\StockRegistryInterface::class); + /** @var \Magento\CatalogInventory\Api\StockItemRepositoryInterface $stockItemRepository */ + $stockItemRepository = $this->_objectManager + ->create(\Magento\CatalogInventory\Api\StockItemRepositoryInterface::class); + foreach ($data->getProductIds() as $productId) { + $stockItemDo = $stockRegistry->getStockItem( + $productId, + $data->getWebsiteId() + ); + if (!$stockItemDo->getProductId()) { + $inventoryData['product_id'] = $productId; + } + + $stockItemId = $stockItemDo->getId(); + $this->dataObjectHelper->populateWithArray( + $stockItemDo, + $inventoryData, + \Magento\CatalogInventory\Api\Data\StockItemInterface::class + ); + $stockItemDo->setItemId($stockItemId); + $stockItemRepository->save($stockItemDo); + } + $this->_stockIndexerProcessor->reindexList($data->getProductIds()); + } + + /** + * @param MassActionInterface $data + * @param $websiteRemoveData + * @param $websiteAddData + */ + private function updateWebsiteInProducts(MassActionInterface $data, $websiteRemoveData, $websiteAddData): void + { + /* @var $actionModel \Magento\Catalog\Model\Product\Action */ + $actionModel = $this->_objectManager->get(\Magento\Catalog\Model\Product\Action::class); + $productIds = $data->getProductIds(); + + if ($websiteRemoveData) { + $actionModel->updateWebsites($productIds, $websiteRemoveData, 'remove'); + } + if ($websiteAddData) { + $actionModel->updateWebsites($productIds, $websiteAddData, 'add'); + } + + $this->_eventManager->dispatch('catalog_product_to_website_change', ['products' => $productIds]); + } + + /** + * @param MassActionInterface $data + * @param $attributesData + * @param $websiteRemoveData + * @param $websiteAddData + */ + private function reindex(MassActionInterface $data, $attributesData, $websiteRemoveData, $websiteAddData): void + { + $this->_productFlatIndexerProcessor->reindexList($data->getProductIds()); + + if ($this->_catalogProduct->isDataForPriceIndexerWasChanged($attributesData) + || !empty($websiteRemoveData) + || !empty($websiteAddData) + ) { + $this->_productPriceIndexerProcessor->reindexList($data->getProductIds()); + } + } +} \ No newline at end of file diff --git a/app/code/Magento/Catalog/Model/MassAction.php b/app/code/Magento/Catalog/Model/MassAction.php new file mode 100644 index 000000000000..35ed110f9c11 --- /dev/null +++ b/app/code/Magento/Catalog/Model/MassAction.php @@ -0,0 +1,174 @@ +<?php +namespace Magento\Catalog\Model; + +class MassAction implements \Magento\Catalog\Api\Data\MassActionInterface +{ + private $inventory; + private $attributes; + private $websiteRemove; + private $websiteAdd; + private $storeId; + private $productIds; + private $websiteId; + + /** + * Set data value. + * + * @param string $data + * @return void + * @since 101.1.0 + */ + public function setInventory($data) + { + $this->inventory = $data; + } + + /** + * Get data value. + * + * @return string[] + * @since 101.1.0 + */ + public function getInventory():array + { + return $this->inventory; + } + + /** + * Set data value. + * + * @param string $data + * @return void + * @since 101.1.0 + */ + public function setAttributes($data) + { + $this->attributes = $data; + } + + /** + * Get data value. + * + * @return string[] + * @since 101.1.0 + */ + public function getAttributes():array + { + return $this->attributes; + } + + /** + * Set data value. + * + * @param string $data + * @return void + * @since 101.1.0 + */ + public function setWebsiteRemove($data) + { + $this->websiteRemove = $data; + } + + /** + * Get data value. + * + * @return string[] + * @since 101.1.0 + */ + public function getWebsiteRemove():array + { + return $this->websiteRemove; + } + + /** + * Set data value. + * + * @param string $data + * @return void + * @since 101.1.0 + */ + public function setWebsiteAdd($data) + { + $this->websiteAdd = $data; + } + + /** + * Get data value. + * + * @return string[] + * @since 101.1.0 + */ + public function getWebsiteAdd():array + { + return $this->websiteAdd; + } + + /** + * Set data value. + * + * @param string $data + * @return void + * @since 101.1.0 + */ + public function setStoreId($data) + { + $this->storeId = $data; + } + + /** + * Get data value. + * + * @return string + * @since 101.1.0 + */ + public function getStoreId() + { + return $this->storeId; + } + + /** + * Set data value. + * + * @param integer[] $data + * @return void + * @since 101.1.0 + */ + public function setProductIds(array $data) + { + $this->productIds = $data; + } + + /** + * Get data value. + * + * @return integer[] + * @since 101.1.0 + */ + public function getProductIds():array + { + return $this->productIds; + } + + /** + * Set data value. + * + * @param string $data + * @return void + * @since 101.1.0 + */ + public function setWebsiteId($data) + { + $this->websiteId = $data; + } + + /** + * Get data value. + * + * @return string + * @since 101.1.0 + */ + public function getWebsiteId() + { + return $this->websiteId; + } +} diff --git a/app/code/Magento/Catalog/etc/communication.xml b/app/code/Magento/Catalog/etc/communication.xml new file mode 100644 index 000000000000..b073725e934f --- /dev/null +++ b/app/code/Magento/Catalog/etc/communication.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Communication/etc/communication.xsd"> + <topic name="product_action_attribute.update" request="Magento\Catalog\Api\Data\MassActionInterface"> + <handler name="product_action_attribute.update" type="Magento\Catalog\Model\Attribute\Backend\Consumer" method="process" /> + </topic> +</config> \ No newline at end of file diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index 7d2c3699ee2c..49447447622f 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -72,6 +72,7 @@ <preference for="Magento\Catalog\Model\Indexer\Product\Price\UpdateIndexInterface" type="Magento\Catalog\Model\Indexer\Product\Price\InvalidateIndex" /> <preference for="Magento\Catalog\Model\Product\Gallery\ImagesConfigFactoryInterface" type="Magento\Catalog\Model\Product\Gallery\ImagesConfigFactory" /> <preference for="Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface" type="Magento\Catalog\Model\Product\Configuration\Item\ItemResolverComposite" /> + <preference for="Magento\Catalog\Api\Data\MassActionInterface" type="\Magento\Catalog\Model\MassAction" /> <type name="Magento\Customer\Model\ResourceModel\Visitor"> <plugin name="catalogLog" type="Magento\Catalog\Model\Plugin\Log" /> </type> diff --git a/app/code/Magento/Catalog/etc/queue.xml b/app/code/Magento/Catalog/etc/queue.xml new file mode 100644 index 000000000000..8aa7c7fe5e49 --- /dev/null +++ b/app/code/Magento/Catalog/etc/queue.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework-message-queue:etc/queue.xsd"> + <broker topic="product_action_attribute.update" exchange="magento-db" type="db"> + <queue name="product_action_attribute.update" consumer="product_action_attribute.update" consumerInstance="Magento\Framework\MessageQueue\Consumer" handler="Magento\Catalog\Model\Attribute\Backend\Consumer::process"/> + </broker> +</config> \ No newline at end of file diff --git a/app/code/Magento/Catalog/etc/queue_consumer.xml b/app/code/Magento/Catalog/etc/queue_consumer.xml new file mode 100644 index 000000000000..0090866c0e49 --- /dev/null +++ b/app/code/Magento/Catalog/etc/queue_consumer.xml @@ -0,0 +1,10 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework-message-queue:etc/consumer.xsd"> + <consumer name="product_action_attribute.update" queue="product_action_attribute.update" connection="db" maxMessages="5000" consumerInstance="Magento\Framework\MessageQueue\Consumer" handler="Magento\Catalog\Model\Attribute\Backend\Consumer::process" /> +</config> \ No newline at end of file diff --git a/app/code/Magento/Catalog/etc/queue_publisher.xml b/app/code/Magento/Catalog/etc/queue_publisher.xml new file mode 100644 index 000000000000..c673a8321762 --- /dev/null +++ b/app/code/Magento/Catalog/etc/queue_publisher.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework-message-queue:etc/publisher.xsd"> + <publisher topic="product_action_attribute.update"> + <connection name="db" exchange="magento-db" /> + </publisher> +</config> \ No newline at end of file diff --git a/app/code/Magento/Catalog/etc/queue_topology.xml b/app/code/Magento/Catalog/etc/queue_topology.xml new file mode 100644 index 000000000000..0097d770936a --- /dev/null +++ b/app/code/Magento/Catalog/etc/queue_topology.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework-message-queue:etc/topology.xsd"> + <exchange name="magento-db" type="topic" connection="db"> + <binding id="updateBinding" topic="product_action_attribute.update" destinationType="queue" destination="product_action_attribute.update"/> + </exchange> +</config> \ No newline at end of file diff --git a/lib/internal/Magento/Framework/Reflection/DataObjectProcessor.php b/lib/internal/Magento/Framework/Reflection/DataObjectProcessor.php index 2f3caf08c534..dfe0e5c9f30f 100644 --- a/lib/internal/Magento/Framework/Reflection/DataObjectProcessor.php +++ b/lib/internal/Magento/Framework/Reflection/DataObjectProcessor.php @@ -115,11 +115,11 @@ public function buildOutputDataArray($dataObject, $dataObjectType) } elseif (is_array($value)) { $valueResult = []; $arrayElementType = substr($returnType, 0, -2); - foreach ($value as $singleValue) { + foreach ($value as $singleKey => $singleValue) { if (is_object($singleValue) && !($singleValue instanceof Phrase)) { $singleValue = $this->buildOutputDataArray($singleValue, $arrayElementType); } - $valueResult[] = $this->typeCaster->castValueToType($singleValue, $arrayElementType); + $valueResult[$singleKey] = $this->typeCaster->castValueToType($singleValue, $arrayElementType); } $value = $valueResult; } else { From 12d5481f89d8f4cb835258fbc1a29244fc1f6bac Mon Sep 17 00:00:00 2001 From: duhon <duhon@rambler.ru> Date: Tue, 19 Feb 2019 15:52:32 -0600 Subject: [PATCH 088/265] MC-13613: Product mass update - MC-5665 Execution of heavy admin operations in asynchronous flow part2 --- .../Product/Action/Attribute/Save.php | 106 +++---- .../Model/Attribute/Backend/Consumer.php | 119 ++++---- .../Product/Action/Attribute/SaveTest.php | 258 ------------------ .../Unit/Model/Attribute/Backend/SaveTest.php | 150 ++++++++++ .../PublisherConsumerController.php | 24 +- .../Product/Action/AttributeTest.php | 63 ++++- 6 files changed, 315 insertions(+), 405 deletions(-) delete mode 100644 app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Action/Attribute/SaveTest.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/SaveTest.php diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php index 69969231490b..eb75b8f7a701 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php @@ -6,6 +6,7 @@ */ namespace Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute; +use Magento\Catalog\Api\Data\MassActionInterface; use Magento\Catalog\Api\Data\MassActionInterfaceFactory; use Magento\CatalogInventory\Api\StockConfigurationInterface; use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; @@ -15,96 +16,48 @@ /** * Class Save - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Save extends \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute implements HttpPostActionInterface { - /** - * @var \Magento\Catalog\Model\Indexer\Product\Flat\Processor - */ - protected $_productFlatIndexerProcessor; - - /** - * @var \Magento\Catalog\Model\Indexer\Product\Price\Processor - */ - protected $_productPriceIndexerProcessor; - - /** - * Catalog product - * - * @var \Magento\Catalog\Helper\Product - */ - protected $_catalogProduct; - - /** - * @var \Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory - */ - protected $stockItemFactory; - - /** - * Stock Indexer - * - * @var \Magento\CatalogInventory\Model\Indexer\Stock\Processor - */ - protected $_stockIndexerProcessor; - - /** - * @var \Magento\Framework\Api\DataObjectHelper - */ - protected $dataObjectHelper; - /** * @var PublisherInterface */ private $messagePublisher; + /** * @var MassActionInterfaceFactory|null */ private $massActionFactory; + /** + * @var StockConfigurationInterface + */ + private $stockConfiguration; /** * @param Action\Context $context * @param \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeHelper - * @param \Magento\Catalog\Model\Indexer\Product\Flat\Processor $productFlatIndexerProcessor - * @param \Magento\Catalog\Model\Indexer\Product\Price\Processor $productPriceIndexerProcessor - * @param \Magento\CatalogInventory\Model\Indexer\Stock\Processor $stockIndexerProcessor - * @param \Magento\Catalog\Helper\Product $catalogProduct - * @param \Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory $stockItemFactory - * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper + * @param StockConfigurationInterface|null $stockConfiguration * @param PublisherInterface|null $publisher * @param MassActionInterfaceFactory|null $massAction */ public function __construct( Action\Context $context, \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeHelper, - \Magento\Catalog\Model\Indexer\Product\Flat\Processor $productFlatIndexerProcessor, - \Magento\Catalog\Model\Indexer\Product\Price\Processor $productPriceIndexerProcessor, - \Magento\CatalogInventory\Model\Indexer\Stock\Processor $stockIndexerProcessor, - \Magento\Catalog\Helper\Product $catalogProduct, - \Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory $stockItemFactory, - \Magento\Framework\Api\DataObjectHelper $dataObjectHelper, + StockConfigurationInterface $stockConfiguration = null, PublisherInterface $publisher = null, MassActionInterfaceFactory $massAction = null ) { - $this->_productFlatIndexerProcessor = $productFlatIndexerProcessor; - $this->_productPriceIndexerProcessor = $productPriceIndexerProcessor; - $this->_stockIndexerProcessor = $stockIndexerProcessor; - $this->_catalogProduct = $catalogProduct; - $this->stockItemFactory = $stockItemFactory; parent::__construct($context, $attributeHelper); - $this->dataObjectHelper = $dataObjectHelper; $this->messagePublisher = $publisher ?: ObjectManager::getInstance()->get(PublisherInterface::class); $this->massActionFactory = $massAction ?: ObjectManager::getInstance()->get(MassActionInterfaceFactory::class); + $this->stockConfiguration = $stockConfiguration; } /** * Update product attributes * * @return \Magento\Backend\Model\View\Result\Redirect - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function execute() { @@ -114,30 +67,27 @@ public function execute() /* Collect Data */ $inventoryData = $this->getRequest()->getParam('inventory', []); + $inventoryData = $this->addConfigSettings($inventoryData); $attributesData = $this->getRequest()->getParam('attributes', []); $websiteRemoveData = $this->getRequest()->getParam('remove_website_ids', []); $websiteAddData = $this->getRequest()->getParam('add_website_ids', []); + $storeId = $this->attributeHelper->getSelectedStoreId(); + $productIds = $this->attributeHelper->getProductIds(); + $websiteId = $this->attributeHelper->getStoreWebsiteId($storeId); - /* Prepare inventory data item options (use config settings) */ - $options = $this->_objectManager->get(StockConfigurationInterface::class)->getConfigItemOptions(); - foreach ($options as $option) { - if (isset($inventoryData[$option]) && !isset($inventoryData['use_config_' . $option])) { - $inventoryData['use_config_' . $option] = 0; - } - } - + /* Create DTO for queue */ $massAction = $this->massActionFactory->create(); $massAction->setInventory($inventoryData); $massAction->setAttributes($attributesData); $massAction->setWebsiteRemove($websiteRemoveData); $massAction->setWebsiteAdd($websiteAddData); - $massAction->setStoreId($this->attributeHelper->getSelectedStoreId()); - $massAction->setProductIds($this->attributeHelper->getProductIds()); - $massAction->setWebsiteId($this->attributeHelper->getStoreWebsiteId($massAction->getStoreId())); + $massAction->setStoreId($storeId); + $massAction->setProductIds($productIds); + $massAction->setWebsiteId($websiteId); try { $this->messagePublisher->publish('product_action_attribute.update', $massAction); - $this->messageManager->addSuccessMessage(__('Message is added to queue, wait')); + $this->messageManager->addSuccessMessage(__('Message is added to queue')); } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { @@ -147,7 +97,23 @@ public function execute() ); } - return $this->resultRedirectFactory->create() - ->setPath('catalog/product/', ['store' => $massAction->getStoreId()]); + return $this->resultRedirectFactory->create()->setPath('catalog/product/', ['store' => $storeId]); + } + + /** + * Prepare inventory data item options (use config settings) + * @param $inventoryData + * @return mixed + */ + private function addConfigSettings($inventoryData) + { + $options = $this->stockConfiguration->getConfigItemOptions(); + foreach ($options as $option) { + $useConfig = 'use_config_' . $option; + if (isset($inventoryData[$option]) && !isset($inventoryData[$useConfig])) { + $inventoryData[$useConfig] = 0; + } + } + return $inventoryData; } } diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php b/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php index 029223972780..1bba257ff986 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php @@ -30,19 +30,19 @@ class Consumer /** * @var \Magento\Catalog\Model\Indexer\Product\Flat\Processor */ - protected $_productFlatIndexerProcessor; + protected $productFlatIndexerProcessor; /** * @var \Magento\Catalog\Model\Indexer\Product\Price\Processor */ - protected $_productPriceIndexerProcessor; + protected $productPriceIndexerProcessor; /** * Catalog product * * @var \Magento\Catalog\Helper\Product */ - protected $_catalogProduct; + protected $catalogProduct; /** * @var \Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory @@ -54,7 +54,7 @@ class Consumer * * @var \Magento\CatalogInventory\Model\Indexer\Stock\Processor */ - protected $_stockIndexerProcessor; + protected $stockIndexerProcessor; /** * @var \Magento\Framework\Api\DataObjectHelper @@ -64,12 +64,24 @@ class Consumer /** * @var \Magento\Framework\Event\ManagerInterface */ - private $_eventManager; + private $eventManager; /** * @var ObjectManager */ - private $_objectManager; + private $objectManager; + /** + * @var \Magento\Catalog\Model\Product\Action + */ + private $productAction; + /** + * @var \Magento\CatalogInventory\Api\StockRegistryInterface + */ + private $stockRegistry; + /** + * @var \Magento\CatalogInventory\Api\StockItemRepositoryInterface + */ + private $stockItemRepository; /** * @param \Magento\Catalog\Helper\Product $catalogProduct @@ -79,6 +91,9 @@ class Consumer * @param \Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory $stockItemFactory * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper * @param \Magento\Framework\Event\ManagerInterface $eventManager + * @param \Magento\Catalog\Model\Product\Action $action + * @param \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistryFactory + * @param \Magento\CatalogInventory\Api\StockItemRepositoryInterface $stockItemRepositoryFactory * @param NotifierInterface $notifier */ public function __construct( @@ -89,35 +104,42 @@ public function __construct( \Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory $stockItemFactory, \Magento\Framework\Api\DataObjectHelper $dataObjectHelper, \Magento\Framework\Event\ManagerInterface $eventManager, + \Magento\Catalog\Model\Product\Action $action, + \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistryFactory, + \Magento\CatalogInventory\Api\StockItemRepositoryInterface $stockItemRepositoryFactory, NotifierInterface $notifier ) { - $this->_catalogProduct = $catalogProduct; - $this->_productFlatIndexerProcessor = $productFlatIndexerProcessor; - $this->_productPriceIndexerProcessor = $productPriceIndexerProcessor; - $this->_stockIndexerProcessor = $stockIndexerProcessor; + $this->catalogProduct = $catalogProduct; + $this->productFlatIndexerProcessor = $productFlatIndexerProcessor; + $this->productPriceIndexerProcessor = $productPriceIndexerProcessor; + $this->stockIndexerProcessor = $stockIndexerProcessor; $this->stockItemFactory = $stockItemFactory; $this->dataObjectHelper = $dataObjectHelper; $this->notifier = $notifier; - $this->_eventManager = $eventManager; - $this->_objectManager = ObjectManager::getInstance(); + $this->eventManager = $eventManager; + $this->objectManager = ObjectManager::getInstance(); + $this->productAction = $action; + $this->stockRegistry = $stockRegistryFactory; + $this->stockItemRepository = $stockItemRepositoryFactory->create(); } public function process(MassActionInterface $data): void { try { - if ($data->getAttributes()) { - $attributesData = $this->getAttributesData($data, $data->getAttributes()); - } - if ($data->getInventory()) { - $this->saveInventory($data, $data->getInventory()); + $this->updateInventoryInProducts($data->getProductIds(), $data->getWebsiteId(), $data->getInventory()); } if ($data->getWebsiteAdd() || $data->getWebsiteRemove()) { - $this->updateWebsiteInProducts($data, $data->getWebsiteRemove(), $data->getWebsiteAdd()); + $this->updateWebsiteInProducts($data->getProductIds(), $data->getWebsiteRemove(), $data->getWebsiteAdd()); + } + + if ($data->getAttributes()) { + $attributesData = $this->getAttributesData($data->getProductIds(), $data->getStoreId(), $data->getAttributes()); + $this->reindex($data->getProductIds(), $attributesData, $data->getWebsiteRemove(), $data->getWebsiteAdd()); } - $this->reindex($data, $attributesData, $data->getWebsiteRemove(), $data->getWebsiteAdd()); + $this->productFlatIndexerProcessor->reindexList($data->getProductIds()); $this->notifier->addNotice( __('Product attributes updated'), @@ -133,17 +155,18 @@ public function process(MassActionInterface $data): void } /** - * @param MassActionInterface $data + * @param $productIds + * @param $storeId * @param $attributesData * @return mixed */ - private function getAttributesData(MassActionInterface $data, $attributesData) + private function getAttributesData($productIds, $storeId, $attributesData) { - $dateFormat = $this->_objectManager->get(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class) + $dateFormat = $this->objectManager->get(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class) ->getDateFormat(\IntlDateFormatter::SHORT); foreach ($attributesData as $attributeCode => $value) { - $attribute = $this->_objectManager->get(\Magento\Eav\Model\Config::class) + $attribute = $this->objectManager->get(\Magento\Eav\Model\Config::class) ->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $attributeCode); if (!$attribute->getAttributeId()) { unset($attributesData[$attributeCode]); @@ -174,29 +197,19 @@ private function getAttributesData(MassActionInterface $data, $attributesData) } } - $this->_objectManager->get(\Magento\Catalog\Model\Product\Action::class) - ->updateAttributes($data->getProductIds(), $attributesData, $data->getStoreId()); + $this->productAction->updateAttributes($productIds, $attributesData, $storeId); return $attributesData; } /** - * @param MassActionInterface $data + * @param $productIds + * @param $websiteId * @param $inventoryData */ - private function saveInventory(MassActionInterface $data, $inventoryData): void + private function updateInventoryInProducts($productIds, $websiteId, $inventoryData): void { - // TODO why use ObjectManager? - /** @var \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry */ - $stockRegistry = $this->_objectManager - ->create(\Magento\CatalogInventory\Api\StockRegistryInterface::class); - /** @var \Magento\CatalogInventory\Api\StockItemRepositoryInterface $stockItemRepository */ - $stockItemRepository = $this->_objectManager - ->create(\Magento\CatalogInventory\Api\StockItemRepositoryInterface::class); - foreach ($data->getProductIds() as $productId) { - $stockItemDo = $stockRegistry->getStockItem( - $productId, - $data->getWebsiteId() - ); + foreach ($productIds as $productId) { + $stockItemDo = $this->stockRegistry->getStockItem($productId, $websiteId); if (!$stockItemDo->getProductId()) { $inventoryData['product_id'] = $productId; } @@ -208,47 +221,41 @@ private function saveInventory(MassActionInterface $data, $inventoryData): void \Magento\CatalogInventory\Api\Data\StockItemInterface::class ); $stockItemDo->setItemId($stockItemId); - $stockItemRepository->save($stockItemDo); + $this->stockItemRepository->save($stockItemDo); } - $this->_stockIndexerProcessor->reindexList($data->getProductIds()); + $this->stockIndexerProcessor->reindexList($productIds); } /** - * @param MassActionInterface $data + * @param $productIds * @param $websiteRemoveData * @param $websiteAddData */ - private function updateWebsiteInProducts(MassActionInterface $data, $websiteRemoveData, $websiteAddData): void + private function updateWebsiteInProducts($productIds, $websiteRemoveData, $websiteAddData): void { - /* @var $actionModel \Magento\Catalog\Model\Product\Action */ - $actionModel = $this->_objectManager->get(\Magento\Catalog\Model\Product\Action::class); - $productIds = $data->getProductIds(); - if ($websiteRemoveData) { - $actionModel->updateWebsites($productIds, $websiteRemoveData, 'remove'); + $this->productAction->updateWebsites($productIds, $websiteRemoveData, 'remove'); } if ($websiteAddData) { - $actionModel->updateWebsites($productIds, $websiteAddData, 'add'); + $this->productAction->updateWebsites($productIds, $websiteAddData, 'add'); } - $this->_eventManager->dispatch('catalog_product_to_website_change', ['products' => $productIds]); + $this->eventManager->dispatch('catalog_product_to_website_change', ['products' => $productIds]); } /** - * @param MassActionInterface $data + * @param $productIds * @param $attributesData * @param $websiteRemoveData * @param $websiteAddData */ - private function reindex(MassActionInterface $data, $attributesData, $websiteRemoveData, $websiteAddData): void + private function reindex($productIds, $attributesData, $websiteRemoveData, $websiteAddData): void { - $this->_productFlatIndexerProcessor->reindexList($data->getProductIds()); - - if ($this->_catalogProduct->isDataForPriceIndexerWasChanged($attributesData) + if ($this->catalogProduct->isDataForPriceIndexerWasChanged($attributesData) || !empty($websiteRemoveData) || !empty($websiteAddData) ) { - $this->_productPriceIndexerProcessor->reindexList($data->getProductIds()); + $this->productPriceIndexerProcessor->reindexList($productIds); } } } \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Action/Attribute/SaveTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Action/Attribute/SaveTest.php deleted file mode 100644 index de44af7f58af..000000000000 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Action/Attribute/SaveTest.php +++ /dev/null @@ -1,258 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Catalog\Test\Unit\Controller\Adminhtml\Product\Action\Attribute; - -/** - * @SuppressWarnings(PHPMD.TooManyFields) - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class SaveTest extends \PHPUnit\Framework\TestCase -{ - /** @var \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute\Save */ - protected $object; - - /** @var \Magento\Catalog\Helper\Product\Edit\Action\Attribute|\PHPUnit_Framework_MockObject_MockObject */ - protected $attributeHelper; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - protected $dataObjectHelperMock; - - /** @var \Magento\CatalogInventory\Model\Indexer\Stock\Processor|\PHPUnit_Framework_MockObject_MockObject */ - protected $stockIndexerProcessor; - - /** @var \Magento\Backend\App\Action\Context|\PHPUnit_Framework_MockObject_MockObject */ - protected $context; - - /** @var \Magento\Framework\App\Request\Http|\PHPUnit_Framework_MockObject_MockObject */ - protected $request; - - /** @var \Magento\Framework\App\Response\Http|\PHPUnit_Framework_MockObject_MockObject */ - protected $response; - - /** @var \Magento\Framework\ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $objectManager; - - /** @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $eventManager; - - /** @var \Magento\Framework\UrlInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $url; - - /** @var \Magento\Framework\App\Response\RedirectInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $redirect; - - /** @var \Magento\Framework\App\ActionFlag|\PHPUnit_Framework_MockObject_MockObject */ - protected $actionFlag; - - /** @var \Magento\Framework\App\ViewInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $view; - - /** @var \Magento\Framework\Message\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $messageManager; - - /** @var \Magento\Backend\Model\Session|\PHPUnit_Framework_MockObject_MockObject */ - protected $session; - - /** @var \Magento\Framework\AuthorizationInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $authorization; - - /** @var \Magento\Backend\Model\Auth|\PHPUnit_Framework_MockObject_MockObject */ - protected $auth; - - /** @var \Magento\Backend\Helper\Data|\PHPUnit_Framework_MockObject_MockObject */ - protected $helper; - - /** @var \Magento\Backend\Model\UrlInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $backendUrl; - - /** @var \Magento\Framework\Data\Form\FormKey\Validator|\PHPUnit_Framework_MockObject_MockObject */ - protected $formKeyValidator; - - /** @var \Magento\Framework\Locale\ResolverInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $localeResolver; - - /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject */ - protected $product; - - /** @var \Magento\CatalogInventory\Api\StockRegistryInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $stockItemService; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - protected $stockItem; - - /** @var \Magento\CatalogInventory\Api\StockConfigurationInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $stockConfig; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - protected $stockItemRepository; - - /** - * @var \Magento\Backend\Model\View\Result\RedirectFactory|\PHPUnit_Framework_MockObject_MockObject - */ - protected $resultRedirectFactory; - - protected function setUp() - { - $this->attributeHelper = $this->createPartialMock( - \Magento\Catalog\Helper\Product\Edit\Action\Attribute::class, - ['getProductIds', 'getSelectedStoreId', 'getStoreWebsiteId'] - ); - - $this->dataObjectHelperMock = $this->getMockBuilder(\Magento\Framework\Api\DataObjectHelper::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->stockIndexerProcessor = $this->createPartialMock( - \Magento\CatalogInventory\Model\Indexer\Stock\Processor::class, - ['reindexList'] - ); - - $resultRedirect = $this->getMockBuilder(\Magento\Backend\Model\View\Result\Redirect::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->resultRedirectFactory = $this->getMockBuilder(\Magento\Backend\Model\View\Result\RedirectFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $this->resultRedirectFactory->expects($this->atLeastOnce()) - ->method('create') - ->willReturn($resultRedirect); - - $this->prepareContext(); - - $this->object = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject( - \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute\Save::class, - [ - 'context' => $this->context, - 'attributeHelper' => $this->attributeHelper, - 'stockIndexerProcessor' => $this->stockIndexerProcessor, - 'dataObjectHelper' => $this->dataObjectHelperMock, - ] - ); - } - - /** - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - protected function prepareContext() - { - $this->stockItemRepository = $this->getMockBuilder( - \Magento\CatalogInventory\Api\StockItemRepositoryInterface::class - )->disableOriginalConstructor()->getMock(); - - $this->request = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) - ->disableOriginalConstructor()->getMock(); - $this->response = $this->createMock(\Magento\Framework\App\Response\Http::class); - $this->objectManager = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); - $this->eventManager = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); - $this->url = $this->createMock(\Magento\Framework\UrlInterface::class); - $this->redirect = $this->createMock(\Magento\Framework\App\Response\RedirectInterface::class); - $this->actionFlag = $this->createMock(\Magento\Framework\App\ActionFlag::class); - $this->view = $this->createMock(\Magento\Framework\App\ViewInterface::class); - $this->messageManager = $this->createMock(\Magento\Framework\Message\ManagerInterface::class); - $this->session = $this->createMock(\Magento\Backend\Model\Session::class); - $this->authorization = $this->createMock(\Magento\Framework\AuthorizationInterface::class); - $this->auth = $this->createMock(\Magento\Backend\Model\Auth::class); - $this->helper = $this->createMock(\Magento\Backend\Helper\Data::class); - $this->backendUrl = $this->createMock(\Magento\Backend\Model\UrlInterface::class); - $this->formKeyValidator = $this->createMock(\Magento\Framework\Data\Form\FormKey\Validator::class); - $this->localeResolver = $this->createMock(\Magento\Framework\Locale\ResolverInterface::class); - - $this->context = $this->context = $this->createPartialMock(\Magento\Backend\App\Action\Context::class, [ - 'getRequest', - 'getResponse', - 'getObjectManager', - 'getEventManager', - 'getUrl', - 'getRedirect', - 'getActionFlag', - 'getView', - 'getMessageManager', - 'getSession', - 'getAuthorization', - 'getAuth', - 'getHelper', - 'getBackendUrl', - 'getFormKeyValidator', - 'getLocaleResolver', - 'getResultRedirectFactory' - ]); - $this->context->expects($this->any())->method('getRequest')->willReturn($this->request); - $this->context->expects($this->any())->method('getResponse')->willReturn($this->response); - $this->context->expects($this->any())->method('getObjectManager')->willReturn($this->objectManager); - $this->context->expects($this->any())->method('getEventManager')->willReturn($this->eventManager); - $this->context->expects($this->any())->method('getUrl')->willReturn($this->url); - $this->context->expects($this->any())->method('getRedirect')->willReturn($this->redirect); - $this->context->expects($this->any())->method('getActionFlag')->willReturn($this->actionFlag); - $this->context->expects($this->any())->method('getView')->willReturn($this->view); - $this->context->expects($this->any())->method('getMessageManager')->willReturn($this->messageManager); - $this->context->expects($this->any())->method('getSession')->willReturn($this->session); - $this->context->expects($this->any())->method('getAuthorization')->willReturn($this->authorization); - $this->context->expects($this->any())->method('getAuth')->willReturn($this->auth); - $this->context->expects($this->any())->method('getHelper')->willReturn($this->helper); - $this->context->expects($this->any())->method('getBackendUrl')->willReturn($this->backendUrl); - $this->context->expects($this->any())->method('getFormKeyValidator')->willReturn($this->formKeyValidator); - $this->context->expects($this->any())->method('getLocaleResolver')->willReturn($this->localeResolver); - $this->context->expects($this->any()) - ->method('getResultRedirectFactory') - ->willReturn($this->resultRedirectFactory); - - $this->product = $this->createPartialMock( - \Magento\Catalog\Model\Product::class, - ['isProductsHasSku', '__wakeup'] - ); - - $this->stockItemService = $this->getMockBuilder(\Magento\CatalogInventory\Api\StockRegistryInterface::class) - ->disableOriginalConstructor() - ->setMethods(['getStockItem', 'saveStockItem']) - ->getMockForAbstractClass(); - $this->stockItem = $this->getMockBuilder(\Magento\CatalogInventory\Api\Data\StockItemInterface::class) - ->setMethods(['getId', 'getProductId']) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - - $this->stockConfig = $this->getMockBuilder(\Magento\CatalogInventory\Api\StockConfigurationInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - - $this->objectManager->expects($this->any())->method('create')->will($this->returnValueMap([ - [\Magento\Catalog\Model\Product::class, [], $this->product], - [\Magento\CatalogInventory\Api\StockRegistryInterface::class, [], $this->stockItemService], - [\Magento\CatalogInventory\Api\StockItemRepositoryInterface::class, [], $this->stockItemRepository], - ])); - - $this->objectManager->expects($this->any())->method('get')->will($this->returnValueMap([ - [\Magento\CatalogInventory\Api\StockConfigurationInterface::class, $this->stockConfig], - ])); - } - - public function testExecuteThatProductIdsAreObtainedFromAttributeHelper() - { - $this->attributeHelper->expects($this->any())->method('getProductIds')->will($this->returnValue([5])); - $this->attributeHelper->expects($this->any())->method('getSelectedStoreId')->will($this->returnValue([1])); - $this->attributeHelper->expects($this->any())->method('getStoreWebsiteId')->will($this->returnValue(1)); - $this->stockConfig->expects($this->any())->method('getConfigItemOptions')->will($this->returnValue([])); - $this->dataObjectHelperMock->expects($this->any()) - ->method('populateWithArray') - ->with($this->stockItem, $this->anything(), \Magento\CatalogInventory\Api\Data\StockItemInterface::class) - ->willReturnSelf(); - $this->product->expects($this->any())->method('isProductsHasSku')->with([5])->will($this->returnValue(true)); - $this->stockItemService->expects($this->any())->method('getStockItem')->with(5, 1) - ->will($this->returnValue($this->stockItem)); - $this->stockIndexerProcessor->expects($this->any())->method('reindexList')->with([5]); - - $this->request->expects($this->any())->method('getParam')->will($this->returnValueMap([ - ['inventory', [], [7]], - ])); - - $this->messageManager->expects($this->never())->method('addErrorMessage'); - $this->messageManager->expects($this->never())->method('addExceptionMessage'); - - $this->object->execute(); - } -} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/SaveTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/SaveTest.php new file mode 100644 index 000000000000..26e84df443ef --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/SaveTest.php @@ -0,0 +1,150 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Test\Unit\Model\Attribute\Backend\Consumer; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + +/** + * @SuppressWarnings(PHPMD.TooManyFields) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ConsumerTest extends \PHPUnit\Framework\TestCase +{ + /** @var \Magento\Catalog\Model\Attribute\Backend\Consumer */ + protected $object; + + /** @var \Magento\Catalog\Helper\Product\Edit\Action\Attribute|\PHPUnit_Framework_MockObject_MockObject */ + protected $attributeHelper; + + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $dataObjectHelperMock; + + /** @var \Magento\CatalogInventory\Model\Indexer\Stock\Processor|\PHPUnit_Framework_MockObject_MockObject */ + protected $stockIndexerProcessor; + + /** @var \Magento\Backend\App\Action\Context|\PHPUnit_Framework_MockObject_MockObject */ + protected $context; + + /** @var \Magento\Framework\App\Request\Http|\PHPUnit_Framework_MockObject_MockObject */ + protected $request; + + /** @var \Magento\Framework\App\Response\Http|\PHPUnit_Framework_MockObject_MockObject */ + protected $response; + + /** @var \Magento\Framework\ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $objectManager; + + /** @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $eventManager; + + /** @var \Magento\Framework\UrlInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $url; + + /** @var \Magento\Framework\App\Response\RedirectInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $redirect; + + /** @var \Magento\Framework\App\ActionFlag|\PHPUnit_Framework_MockObject_MockObject */ + protected $actionFlag; + + /** @var \Magento\Framework\App\ViewInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $view; + + /** @var \Magento\Framework\Message\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $messageManager; + + /** @var \Magento\Backend\Model\Session|\PHPUnit_Framework_MockObject_MockObject */ + protected $session; + + /** @var \Magento\Framework\AuthorizationInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $authorization; + + /** @var \Magento\Backend\Model\Auth|\PHPUnit_Framework_MockObject_MockObject */ + protected $auth; + + /** @var \Magento\Backend\Helper\Data|\PHPUnit_Framework_MockObject_MockObject */ + protected $helper; + + /** @var \Magento\Backend\Model\UrlInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $backendUrl; + + /** @var \Magento\Framework\Data\Form\FormKey\Validator|\PHPUnit_Framework_MockObject_MockObject */ + protected $formKeyValidator; + + /** @var \Magento\Framework\Locale\ResolverInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $localeResolver; + + /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject */ + protected $product; + + /** @var \Magento\CatalogInventory\Api\StockRegistryInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $stockItemService; + + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $stockItem; + + /** @var \Magento\CatalogInventory\Api\StockConfigurationInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $stockConfig; + + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $stockItemRepository; + + /** + * @var \Magento\Backend\Model\View\Result\RedirectFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $resultRedirectFactory; + + protected function setUp() + { + $this->attributeHelper = $this->createPartialMock( + \Magento\Catalog\Helper\Product\Edit\Action\Attribute::class, + ['getProductIds', 'getSelectedStoreId', 'getStoreWebsiteId'] + ); + + $this->dataObjectHelperMock = $this->getMockBuilder(\Magento\Framework\Api\DataObjectHelper::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->stockIndexerProcessor = $this->createPartialMock( + \Magento\CatalogInventory\Model\Indexer\Stock\Processor::class, + ['reindexList'] + ); + + $this->object = (new ObjectManager($this))->getObject( + \Magento\Catalog\Model\Attribute\Backend\Consumer::class, + [ + 'stockIndexerProcessor' => $this->stockIndexerProcessor, + 'dataObjectHelper' => $this->dataObjectHelperMock, + ] + ); + } + + public function testExecuteThatProductIdsAreObtainedFromAttributeHelper() + { + $this->attributeHelper->method('getProductIds')->will($this->returnValue([5])); + $this->attributeHelper->method('getSelectedStoreId')->will($this->returnValue([1])); + $this->attributeHelper->method('getStoreWebsiteId')->will($this->returnValue(1)); + + $this->stockConfig->expects($this->any())->method('getConfigItemOptions')->will($this->returnValue([])); + $this->dataObjectHelperMock->expects($this->any()) + ->method('populateWithArray') + ->with($this->stockItem, $this->anything(), \Magento\CatalogInventory\Api\Data\StockItemInterface::class) + ->willReturnSelf(); + $this->product->expects($this->any())->method('isProductsHasSku')->with([5])->will($this->returnValue(true)); + $this->stockItemService->expects($this->any())->method('getStockItem')->with(5, 1) + ->will($this->returnValue($this->stockItem)); + $this->stockIndexerProcessor->expects($this->any())->method('reindexList')->with([5]); + + $this->request->expects($this->any())->method('getParam')->will($this->returnValueMap([ + ['inventory', [], [7]], + ])); + + $this->messageManager->expects($this->never())->method('addErrorMessage'); + $this->messageManager->expects($this->never())->method('addExceptionMessage'); + + $this->object->process(); + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/MessageQueue/PublisherConsumerController.php b/dev/tests/integration/framework/Magento/TestFramework/MessageQueue/PublisherConsumerController.php index 9ca351aa1cf9..7748e43bbd62 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/MessageQueue/PublisherConsumerController.php +++ b/dev/tests/integration/framework/Magento/TestFramework/MessageQueue/PublisherConsumerController.php @@ -95,17 +95,9 @@ public function initialize() $this->amqpHelper->deleteConnection($connectionName); } $this->amqpHelper->clearQueue("async.operations.all"); - foreach ($this->consumers as $consumer) { - foreach ($this->getConsumerProcessIds($consumer) as $consumerProcessId) { - exec("kill {$consumerProcessId}"); - } - } - foreach ($this->consumers as $consumer) { - if (!$this->getConsumerProcessIds($consumer)) { - exec("{$this->getConsumerStartCommand($consumer, true)} > /dev/null &"); - } - sleep(5); - } + + $this->stopConsumers(); + $this->startConsumers(); if (file_exists($this->logFilePath)) { // try to remove before failing the test @@ -230,4 +222,14 @@ public function getPublisher() { return $this->publisher; } + + public function startConsumers(): void + { + foreach ($this->consumers as $consumer) { + if (!$this->getConsumerProcessIds($consumer)) { + exec("{$this->getConsumerStartCommand($consumer, true)} > /dev/null &"); + } + sleep(5); + } + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php index a2967878402d..0fe618b2db30 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php @@ -5,13 +5,49 @@ */ namespace Magento\Catalog\Controller\Adminhtml\Product\Action; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductRepository; use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\MessageQueue\PublisherConsumerController; /** * @magentoAppArea adminhtml */ class AttributeTest extends \Magento\TestFramework\TestCase\AbstractBackendController { + /** @var PublisherConsumerController */ + private $publisherConsumerController; + private $consumers = ['product_action_attribute.update']; + + protected function setUp() + { + $this->publisherConsumerController = Bootstrap::getObjectManager()->create(PublisherConsumerController::class, [ + 'consumers' => $this->consumers, + 'logFilePath' => TESTS_TEMP_DIR . "/MessageQueueTestLog.txt", + 'maxMessages' => null, + 'appInitParams' => Bootstrap::getInstance()->getAppInitParams() + ]); + + try { + $this->publisherConsumerController->startConsumers(); + } catch (\Magento\TestFramework\MessageQueue\EnvironmentPreconditionException $e) { + $this->markTestSkipped($e->getMessage()); + } catch (\Magento\TestFramework\MessageQueue\PreconditionFailedException $e) { + $this->fail( + $e->getMessage() + ); + } + + parent::setUp(); + } + + protected function tearDown() + { + $this->publisherConsumerController->stopConsumers(); + parent::tearDown(); + } + /** * @covers \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute\Save::execute * @@ -20,7 +56,7 @@ class AttributeTest extends \Magento\TestFramework\TestCase\AbstractBackendContr */ public function testSaveActionRedirectsSuccessfully() { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $objectManager = Bootstrap::getObjectManager(); /** @var $session \Magento\Backend\Model\Session */ $session = $objectManager->get(\Magento\Backend\Model\Session::class); @@ -59,13 +95,14 @@ public function testSaveActionRedirectsSuccessfully() */ public function testSaveActionChangeVisibility($attributes) { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $repository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\ProductRepository::class + $objectManager = Bootstrap::getObjectManager(); + /** @var ProductRepository $repository */ + $repository = Bootstrap::getObjectManager()->create( + ProductRepository::class ); $product = $repository->get('simple'); $product->setOrigData(); - $product->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_NOT_VISIBLE); + $product->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE); $product->save(); /** @var $session \Magento\Backend\Model\Session */ @@ -75,15 +112,21 @@ public function testSaveActionChangeVisibility($attributes) $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/catalog/product_action_attribute/save/store/0'); + /** @var \Magento\Catalog\Model\Category $category */ - $categoryFactory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + $categoryFactory = Bootstrap::getObjectManager()->get( \Magento\Catalog\Model\CategoryFactory::class ); /** @var \Magento\Catalog\Block\Product\ListProduct $listProduct */ - $listProduct = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + $listProduct = Bootstrap::getObjectManager()->get( \Magento\Catalog\Block\Product\ListProduct::class ); + $this->publisherConsumerController->waitForAsynchronousResult(function() use($repository) { + return $repository->get('simple', false, null, true)->getVisibility() != Visibility::VISIBILITY_NOT_VISIBLE; + sleep(3); + }, []); + $category = $categoryFactory->create()->load(2); $layer = $listProduct->getLayer(); $layer->setCurrentCategory($category); @@ -105,7 +148,7 @@ public function testSaveActionChangeVisibility($attributes) */ public function testValidateActionWithMassUpdate($attributes) { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $objectManager = Bootstrap::getObjectManager(); /** @var $session \Magento\Backend\Model\Session */ $session = $objectManager->get(\Magento\Backend\Model\Session::class); @@ -156,8 +199,8 @@ public function validateActionDataProvider() public function saveActionVisibilityAttrDataProvider() { return [ - ['arguments' => ['visibility' => \Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH]], - ['arguments' => ['visibility' => \Magento\Catalog\Model\Product\Visibility::VISIBILITY_IN_CATALOG]] + ['arguments' => ['visibility' => Visibility::VISIBILITY_BOTH]], + ['arguments' => ['visibility' => Visibility::VISIBILITY_IN_CATALOG]] ]; } } From 60b48a6d2efad7fb2999856e3f8fa41ef01a0fff Mon Sep 17 00:00:00 2001 From: duhon <duhon@rambler.ru> Date: Tue, 19 Feb 2019 17:27:04 -0600 Subject: [PATCH 089/265] MC-13613: Product mass update - MC-5665 Execution of heavy admin operations in asynchronous flow part2 --- .../Model/Attribute/Backend/Consumer.php | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php b/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php index 1bba257ff986..f9b8b44b1428 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php @@ -8,9 +8,11 @@ namespace Magento\Catalog\Model\Attribute\Backend; use Magento\Catalog\Api\Data\MassActionInterface; +use Magento\CatalogInventory\Api\Data\StockItemInterface; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Notification\NotifierInterface; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; /** * Consumer for export message. @@ -70,16 +72,19 @@ class Consumer * @var ObjectManager */ private $objectManager; + /** * @var \Magento\Catalog\Model\Product\Action */ private $productAction; + /** - * @var \Magento\CatalogInventory\Api\StockRegistryInterface + * @var \Magento\CatalogInventory\Api\StockRegistryInterfaceFactory */ private $stockRegistry; + /** - * @var \Magento\CatalogInventory\Api\StockItemRepositoryInterface + * @var \Magento\CatalogInventory\Api\StockItemRepositoryInterfaceFactory */ private $stockItemRepository; @@ -92,8 +97,8 @@ class Consumer * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param \Magento\Catalog\Model\Product\Action $action - * @param \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistryFactory - * @param \Magento\CatalogInventory\Api\StockItemRepositoryInterface $stockItemRepositoryFactory + * @param \Magento\CatalogInventory\Api\StockRegistryInterfaceFactory $stockRegistryFactory + * @param \Magento\CatalogInventory\Api\StockItemRepositoryInterfaceFactory $stockItemRepositoryFactory * @param NotifierInterface $notifier */ public function __construct( @@ -105,8 +110,8 @@ public function __construct( \Magento\Framework\Api\DataObjectHelper $dataObjectHelper, \Magento\Framework\Event\ManagerInterface $eventManager, \Magento\Catalog\Model\Product\Action $action, - \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistryFactory, - \Magento\CatalogInventory\Api\StockItemRepositoryInterface $stockItemRepositoryFactory, + \Magento\CatalogInventory\Api\StockRegistryInterfaceFactory $stockRegistryFactory, + \Magento\CatalogInventory\Api\StockItemRepositoryInterfaceFactory $stockItemRepositoryFactory, NotifierInterface $notifier ) { $this->catalogProduct = $catalogProduct; @@ -119,7 +124,7 @@ public function __construct( $this->eventManager = $eventManager; $this->objectManager = ObjectManager::getInstance(); $this->productAction = $action; - $this->stockRegistry = $stockRegistryFactory; + $this->stockRegistry = $stockRegistryFactory->create(); $this->stockItemRepository = $stockItemRepositoryFactory->create(); } @@ -162,12 +167,11 @@ public function process(MassActionInterface $data): void */ private function getAttributesData($productIds, $storeId, $attributesData) { - $dateFormat = $this->objectManager->get(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class) - ->getDateFormat(\IntlDateFormatter::SHORT); + $dateFormat = $this->objectManager->get(TimezoneInterface::class)->getDateFormat(\IntlDateFormatter::SHORT); + $config = $this->objectManager->get(\Magento\Eav\Model\Config::class); foreach ($attributesData as $attributeCode => $value) { - $attribute = $this->objectManager->get(\Magento\Eav\Model\Config::class) - ->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $attributeCode); + $attribute = $config->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $attributeCode); if (!$attribute->getAttributeId()) { unset($attributesData[$attributeCode]); continue; @@ -215,11 +219,7 @@ private function updateInventoryInProducts($productIds, $websiteId, $inventoryDa } $stockItemId = $stockItemDo->getId(); - $this->dataObjectHelper->populateWithArray( - $stockItemDo, - $inventoryData, - \Magento\CatalogInventory\Api\Data\StockItemInterface::class - ); + $this->dataObjectHelper->populateWithArray($stockItemDo, $inventoryData, StockItemInterface::class); $stockItemDo->setItemId($stockItemId); $this->stockItemRepository->save($stockItemDo); } From e0415bf6d4c37e7599712525735132bca144e1ee Mon Sep 17 00:00:00 2001 From: duhon <duhon@rambler.ru> Date: Wed, 20 Feb 2019 08:22:59 -0600 Subject: [PATCH 090/265] MC-13613: Product mass update - MC-5665 Execution of heavy admin operations in asynchronous flow part2 --- .../Magento/Framework/Reflection/DataObjectProcessor.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Reflection/DataObjectProcessor.php b/lib/internal/Magento/Framework/Reflection/DataObjectProcessor.php index dfe0e5c9f30f..2f3caf08c534 100644 --- a/lib/internal/Magento/Framework/Reflection/DataObjectProcessor.php +++ b/lib/internal/Magento/Framework/Reflection/DataObjectProcessor.php @@ -115,11 +115,11 @@ public function buildOutputDataArray($dataObject, $dataObjectType) } elseif (is_array($value)) { $valueResult = []; $arrayElementType = substr($returnType, 0, -2); - foreach ($value as $singleKey => $singleValue) { + foreach ($value as $singleValue) { if (is_object($singleValue) && !($singleValue instanceof Phrase)) { $singleValue = $this->buildOutputDataArray($singleValue, $arrayElementType); } - $valueResult[$singleKey] = $this->typeCaster->castValueToType($singleValue, $arrayElementType); + $valueResult[] = $this->typeCaster->castValueToType($singleValue, $arrayElementType); } $value = $valueResult; } else { From feb01364e746919f4bd0121f330c231b0b560d6d Mon Sep 17 00:00:00 2001 From: duhon <duhon@rambler.ru> Date: Wed, 20 Feb 2019 11:12:50 -0600 Subject: [PATCH 091/265] MC-13613: Product mass update - MC-5665 Execution of heavy admin operations in asynchronous flow part2 --- .../Unit/Model/Attribute/Backend/SaveTest.php | 150 ------------------ 1 file changed, 150 deletions(-) delete mode 100644 app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/SaveTest.php diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/SaveTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/SaveTest.php deleted file mode 100644 index 26e84df443ef..000000000000 --- a/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/SaveTest.php +++ /dev/null @@ -1,150 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Catalog\Test\Unit\Model\Attribute\Backend\Consumer; - -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; - -/** - * @SuppressWarnings(PHPMD.TooManyFields) - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class ConsumerTest extends \PHPUnit\Framework\TestCase -{ - /** @var \Magento\Catalog\Model\Attribute\Backend\Consumer */ - protected $object; - - /** @var \Magento\Catalog\Helper\Product\Edit\Action\Attribute|\PHPUnit_Framework_MockObject_MockObject */ - protected $attributeHelper; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - protected $dataObjectHelperMock; - - /** @var \Magento\CatalogInventory\Model\Indexer\Stock\Processor|\PHPUnit_Framework_MockObject_MockObject */ - protected $stockIndexerProcessor; - - /** @var \Magento\Backend\App\Action\Context|\PHPUnit_Framework_MockObject_MockObject */ - protected $context; - - /** @var \Magento\Framework\App\Request\Http|\PHPUnit_Framework_MockObject_MockObject */ - protected $request; - - /** @var \Magento\Framework\App\Response\Http|\PHPUnit_Framework_MockObject_MockObject */ - protected $response; - - /** @var \Magento\Framework\ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $objectManager; - - /** @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $eventManager; - - /** @var \Magento\Framework\UrlInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $url; - - /** @var \Magento\Framework\App\Response\RedirectInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $redirect; - - /** @var \Magento\Framework\App\ActionFlag|\PHPUnit_Framework_MockObject_MockObject */ - protected $actionFlag; - - /** @var \Magento\Framework\App\ViewInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $view; - - /** @var \Magento\Framework\Message\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $messageManager; - - /** @var \Magento\Backend\Model\Session|\PHPUnit_Framework_MockObject_MockObject */ - protected $session; - - /** @var \Magento\Framework\AuthorizationInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $authorization; - - /** @var \Magento\Backend\Model\Auth|\PHPUnit_Framework_MockObject_MockObject */ - protected $auth; - - /** @var \Magento\Backend\Helper\Data|\PHPUnit_Framework_MockObject_MockObject */ - protected $helper; - - /** @var \Magento\Backend\Model\UrlInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $backendUrl; - - /** @var \Magento\Framework\Data\Form\FormKey\Validator|\PHPUnit_Framework_MockObject_MockObject */ - protected $formKeyValidator; - - /** @var \Magento\Framework\Locale\ResolverInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $localeResolver; - - /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject */ - protected $product; - - /** @var \Magento\CatalogInventory\Api\StockRegistryInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $stockItemService; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - protected $stockItem; - - /** @var \Magento\CatalogInventory\Api\StockConfigurationInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $stockConfig; - - /** @var \PHPUnit_Framework_MockObject_MockObject */ - protected $stockItemRepository; - - /** - * @var \Magento\Backend\Model\View\Result\RedirectFactory|\PHPUnit_Framework_MockObject_MockObject - */ - protected $resultRedirectFactory; - - protected function setUp() - { - $this->attributeHelper = $this->createPartialMock( - \Magento\Catalog\Helper\Product\Edit\Action\Attribute::class, - ['getProductIds', 'getSelectedStoreId', 'getStoreWebsiteId'] - ); - - $this->dataObjectHelperMock = $this->getMockBuilder(\Magento\Framework\Api\DataObjectHelper::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->stockIndexerProcessor = $this->createPartialMock( - \Magento\CatalogInventory\Model\Indexer\Stock\Processor::class, - ['reindexList'] - ); - - $this->object = (new ObjectManager($this))->getObject( - \Magento\Catalog\Model\Attribute\Backend\Consumer::class, - [ - 'stockIndexerProcessor' => $this->stockIndexerProcessor, - 'dataObjectHelper' => $this->dataObjectHelperMock, - ] - ); - } - - public function testExecuteThatProductIdsAreObtainedFromAttributeHelper() - { - $this->attributeHelper->method('getProductIds')->will($this->returnValue([5])); - $this->attributeHelper->method('getSelectedStoreId')->will($this->returnValue([1])); - $this->attributeHelper->method('getStoreWebsiteId')->will($this->returnValue(1)); - - $this->stockConfig->expects($this->any())->method('getConfigItemOptions')->will($this->returnValue([])); - $this->dataObjectHelperMock->expects($this->any()) - ->method('populateWithArray') - ->with($this->stockItem, $this->anything(), \Magento\CatalogInventory\Api\Data\StockItemInterface::class) - ->willReturnSelf(); - $this->product->expects($this->any())->method('isProductsHasSku')->with([5])->will($this->returnValue(true)); - $this->stockItemService->expects($this->any())->method('getStockItem')->with(5, 1) - ->will($this->returnValue($this->stockItem)); - $this->stockIndexerProcessor->expects($this->any())->method('reindexList')->with([5]); - - $this->request->expects($this->any())->method('getParam')->will($this->returnValueMap([ - ['inventory', [], [7]], - ])); - - $this->messageManager->expects($this->never())->method('addErrorMessage'); - $this->messageManager->expects($this->never())->method('addExceptionMessage'); - - $this->object->process(); - } -} From 0e88105706c797560f66da4af8c4d5bfc547ff62 Mon Sep 17 00:00:00 2001 From: duhon <duhon@rambler.ru> Date: Wed, 20 Feb 2019 17:15:17 -0600 Subject: [PATCH 092/265] MC-13613: Product mass update - MC-5665 Execution of heavy admin operations in asynchronous flow part2 --- .../Catalog/Api/Data/MassActionInterface.php | 21 ++++++++++-- .../Product/Action/Attribute/Save.php | 9 ++--- .../Model/Attribute/Backend/Consumer.php | 33 +++++++++--------- app/code/Magento/Catalog/Model/MassAction.php | 34 ++++++++++++++++--- 4 files changed, 69 insertions(+), 28 deletions(-) diff --git a/app/code/Magento/Catalog/Api/Data/MassActionInterface.php b/app/code/Magento/Catalog/Api/Data/MassActionInterface.php index 3f47242e3aa6..c03a73df5e92 100644 --- a/app/code/Magento/Catalog/Api/Data/MassActionInterface.php +++ b/app/code/Magento/Catalog/Api/Data/MassActionInterface.php @@ -38,7 +38,7 @@ public function getInventory():array; * @return void * @since 101.1.0 */ - public function setAttributes($data); + public function setAttributeKeys($data); /** * Get data value. @@ -46,7 +46,24 @@ public function setAttributes($data); * @return string[] * @since 101.1.0 */ - public function getAttributes():array; + public function getAttributeKeys():array; + + /** + * Set data value. + * + * @param string[] $data + * @return void + * @since 101.1.0 + */ + public function setAttributeValues($data); + + /** + * Get data value. + * + * @return string[] + * @since 101.1.0 + */ + public function getAttributeValues():array; /** * Set data value. diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php index eb75b8f7a701..331b3ed07831 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php @@ -6,10 +6,9 @@ */ namespace Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute; -use Magento\Catalog\Api\Data\MassActionInterface; use Magento\Catalog\Api\Data\MassActionInterfaceFactory; use Magento\CatalogInventory\Api\StockConfigurationInterface; -use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Backend\App\Action; use Magento\Framework\MessageQueue\PublisherInterface; use Magento\Framework\App\ObjectManager; @@ -51,7 +50,8 @@ public function __construct( parent::__construct($context, $attributeHelper); $this->messagePublisher = $publisher ?: ObjectManager::getInstance()->get(PublisherInterface::class); $this->massActionFactory = $massAction ?: ObjectManager::getInstance()->get(MassActionInterfaceFactory::class); - $this->stockConfiguration = $stockConfiguration; + $this->stockConfiguration = $stockConfiguration + ?: ObjectManager::getInstance()->get(StockConfigurationInterface::class); } /** @@ -78,7 +78,8 @@ public function execute() /* Create DTO for queue */ $massAction = $this->massActionFactory->create(); $massAction->setInventory($inventoryData); - $massAction->setAttributes($attributesData); + $massAction->setAttributeValues(array_values($attributesData)); + $massAction->setAttributeKeys(array_keys($attributesData)); $massAction->setWebsiteRemove($websiteRemoveData); $massAction->setWebsiteAdd($websiteAddData); $massAction->setStoreId($storeId); diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php b/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php index f9b8b44b1428..f340cb49ee09 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php @@ -32,36 +32,31 @@ class Consumer /** * @var \Magento\Catalog\Model\Indexer\Product\Flat\Processor */ - protected $productFlatIndexerProcessor; + private $productFlatIndexerProcessor; /** * @var \Magento\Catalog\Model\Indexer\Product\Price\Processor */ - protected $productPriceIndexerProcessor; + private $productPriceIndexerProcessor; /** * Catalog product * * @var \Magento\Catalog\Helper\Product */ - protected $catalogProduct; - - /** - * @var \Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory - */ - protected $stockItemFactory; + private $catalogProduct; /** * Stock Indexer * * @var \Magento\CatalogInventory\Model\Indexer\Stock\Processor */ - protected $stockIndexerProcessor; + private $stockIndexerProcessor; /** * @var \Magento\Framework\Api\DataObjectHelper */ - protected $dataObjectHelper; + private $dataObjectHelper; /** * @var \Magento\Framework\Event\ManagerInterface @@ -93,7 +88,6 @@ class Consumer * @param \Magento\Catalog\Model\Indexer\Product\Flat\Processor $productFlatIndexerProcessor * @param \Magento\Catalog\Model\Indexer\Product\Price\Processor $productPriceIndexerProcessor * @param \Magento\CatalogInventory\Model\Indexer\Stock\Processor $stockIndexerProcessor - * @param \Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory $stockItemFactory * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param \Magento\Catalog\Model\Product\Action $action @@ -106,7 +100,6 @@ public function __construct( \Magento\Catalog\Model\Indexer\Product\Flat\Processor $productFlatIndexerProcessor, \Magento\Catalog\Model\Indexer\Product\Price\Processor $productPriceIndexerProcessor, \Magento\CatalogInventory\Model\Indexer\Stock\Processor $stockIndexerProcessor, - \Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory $stockItemFactory, \Magento\Framework\Api\DataObjectHelper $dataObjectHelper, \Magento\Framework\Event\ManagerInterface $eventManager, \Magento\Catalog\Model\Product\Action $action, @@ -118,7 +111,6 @@ public function __construct( $this->productFlatIndexerProcessor = $productFlatIndexerProcessor; $this->productPriceIndexerProcessor = $productPriceIndexerProcessor; $this->stockIndexerProcessor = $stockIndexerProcessor; - $this->stockItemFactory = $stockItemFactory; $this->dataObjectHelper = $dataObjectHelper; $this->notifier = $notifier; $this->eventManager = $eventManager; @@ -139,8 +131,13 @@ public function process(MassActionInterface $data): void $this->updateWebsiteInProducts($data->getProductIds(), $data->getWebsiteRemove(), $data->getWebsiteAdd()); } - if ($data->getAttributes()) { - $attributesData = $this->getAttributesData($data->getProductIds(), $data->getStoreId(), $data->getAttributes()); + if ($data->getAttributeValues()) { + $attributesData = $this->getAttributesData( + $data->getProductIds(), + $data->getStoreId(), + $data->getAttributeValues(), + $data->getAttributeKeys() + ); $this->reindex($data->getProductIds(), $attributesData, $data->getWebsiteRemove(), $data->getWebsiteAdd()); } @@ -162,11 +159,13 @@ public function process(MassActionInterface $data): void /** * @param $productIds * @param $storeId - * @param $attributesData + * @param $attributeValuesData + * @param $attributeKeysData * @return mixed */ - private function getAttributesData($productIds, $storeId, $attributesData) + private function getAttributesData($productIds, $storeId, $attributeValuesData, $attributeKeysData) { + $attributesData = array_combine($attributeKeysData, $attributeValuesData); $dateFormat = $this->objectManager->get(TimezoneInterface::class)->getDateFormat(\IntlDateFormatter::SHORT); $config = $this->objectManager->get(\Magento\Eav\Model\Config::class); diff --git a/app/code/Magento/Catalog/Model/MassAction.php b/app/code/Magento/Catalog/Model/MassAction.php index 35ed110f9c11..0f09ff8d7699 100644 --- a/app/code/Magento/Catalog/Model/MassAction.php +++ b/app/code/Magento/Catalog/Model/MassAction.php @@ -4,12 +4,13 @@ class MassAction implements \Magento\Catalog\Api\Data\MassActionInterface { private $inventory; - private $attributes; + private $attributeKeys; private $websiteRemove; private $websiteAdd; private $storeId; private $productIds; private $websiteId; + private $attributeValues; /** * Set data value. @@ -41,9 +42,9 @@ public function getInventory():array * @return void * @since 101.1.0 */ - public function setAttributes($data) + public function setAttributeKeys($data) { - $this->attributes = $data; + $this->attributeKeys = $data; } /** @@ -52,9 +53,32 @@ public function setAttributes($data) * @return string[] * @since 101.1.0 */ - public function getAttributes():array + public function getAttributeKeys():array { - return $this->attributes; + return $this->attributeKeys; + } + + /** + * Set data value. + * + * @param string $data + * @return void + * @since 101.1.0 + */ + public function setAttributeValues($data) + { + $this->attributeValues = $data; + } + + /** + * Get data value. + * + * @return string[] + * @since 101.1.0 + */ + public function getAttributeValues():array + { + return $this->attributeValues; } /** From aa7ba8e74dbcce18cf0e8ee5eda0daff2e6754a6 Mon Sep 17 00:00:00 2001 From: duhon <duhon@rambler.ru> Date: Thu, 21 Feb 2019 12:48:17 -0600 Subject: [PATCH 093/265] MC-13613: Product mass update - MC-5665 Execution of heavy admin operations in asynchronous flow part2 --- .../Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml | 2 +- .../Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml | 2 +- .../Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml | 2 +- .../Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml | 2 +- .../Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml index d7607b4b269e..1e31a9cf0eb2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml @@ -54,7 +54,7 @@ <fillField stepKey="fillPrice" selector="{{AdminEditProductAttributesSection.AttributePrice}}" userInput="90.99"/> <click stepKey="clickOnSaveButton" selector="{{AdminEditProductAttributesSection.Save}}"/> <waitForPageLoad stepKey="waitForUpdatedProductToSave" /> - <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="A total of 2 record(s) were updated." stepKey="seeAttributeUpateSuccessMsg"/> + <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpateSuccessMsg"/> <!--Verify product name, sku and updated price--> <click stepKey="openFirstProduct" selector="{{AdminProductGridSection.productRowBySku($$simpleProduct1.sku$$)}}"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml index c0eebd1512d6..9961de9ce017 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml @@ -58,7 +58,7 @@ <click selector="{{AdminEditProductAttributesSection.ChangeAttributePriceToggle}}" stepKey="toggleToChangePrice"/> <fillField selector="{{AdminEditProductAttributesSection.AttributePrice}}" userInput="$$createProductOne.price$$0" stepKey="fillAttributeNameField"/> <click selector="{{AdminEditProductAttributesSection.Save}}" stepKey="save"/> - <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="A total of 2 record(s) were updated." stepKey="seeAttributeUpateSuccessMsg"/> + <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpateSuccessMsg"/> <!-- Assert on storefront default view --> <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml index 845c47c0e4c2..305e0966a1f4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml @@ -52,7 +52,7 @@ <click selector="{{AdminEditProductAttributesSection.ChangeAttributeDescriptionToggle}}" stepKey="toggleToChangeDescription"/> <fillField selector="{{AdminEditProductAttributesSection.AttributeDescription}}" userInput="Updated $$createProductOne.custom_attributes[description]$$" stepKey="fillAttributeDescriptionField"/> <click selector="{{AdminEditProductAttributesSection.Save}}" stepKey="save"/> - <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="A total of 2 record(s) were updated." stepKey="seeAttributeUpateSuccessMsg"/> + <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpateSuccessMsg"/> <!-- Assert on storefront default view --> <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault"/> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml index 593df1c5bc6e..28285449d7e0 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml @@ -67,7 +67,7 @@ <waitForAjaxLoad stepKey="waitForLoadWebSiteTab"/> <click selector="{{AdminUpdateAttributesWebsiteSection.addProductToWebsite}}" stepKey="checkAddProductToWebsiteCheckbox"/> <click selector="{{AdminUpdateAttributesSection.saveButton}}" stepKey="clickSave"/> - <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="A total of 1 record(s) were updated." stepKey="seeSaveSuccess"/> + <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue." stepKey="seeSaveSuccess"/> <!--Got to Store front product page and check url--> <amOnPage url="{{StorefrontProductPage.url($$createProduct.sku$$-new)}}" stepKey="navigateToSimpleProductPage"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml index af12f49bf86e..2137e4d1fe3d 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml @@ -57,7 +57,7 @@ <click selector="{{AdminUpdateAttributesSection.toggleDescription}}" stepKey="clickToggleDescription"/> <fillField selector="{{AdminUpdateAttributesSection.description}}" userInput="MFTF automation!" stepKey="fillDescription"/> <click selector="{{AdminUpdateAttributesSection.saveButton}}" stepKey="clickSave"/> - <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="A total of 3 record(s) were updated." stepKey="seeSaveSuccess"/> + <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeSaveSuccess"/> <!-- Check storefront for description --> <amOnPage url="$$createProduct1.sku$$.html" stepKey="gotoProduct1"/> From a3af5e908b7b100969f639bf9356181b32f8c934 Mon Sep 17 00:00:00 2001 From: duhon <duhon@rambler.ru> Date: Fri, 22 Feb 2019 13:19:47 -0600 Subject: [PATCH 094/265] MC-13613: Product mass update - MC-5665 Execution of heavy admin operations in asynchronous flow part2 --- .../Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml | 6 ++++++ .../AdminMassUpdateProductAttributesGlobalScopeTest.xml | 6 ++++++ ...AdminMassUpdateProductAttributesStoreViewScopeTest.xml | 6 ++++++ .../Test/AdminUrlForProductRewrittenCorrectlyTest.xml | 8 +++++++- .../Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml | 6 ++++++ 5 files changed, 31 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml index 1e31a9cf0eb2..4d581bae700d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml @@ -56,6 +56,12 @@ <waitForPageLoad stepKey="waitForUpdatedProductToSave" /> <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpateSuccessMsg"/> + <!-- Run cron twice --> + <magentoCLI command="cron:run" stepKey="runCron1"/> + <magentoCLI command="cron:run" stepKey="runCron2"/> + <reloadPage stepKey="refreshPage"/> + <waitForPageLoad stepKey="waitFormToReload1"/> + <!--Verify product name, sku and updated price--> <click stepKey="openFirstProduct" selector="{{AdminProductGridSection.productRowBySku($$simpleProduct1.sku$$)}}"/> <waitForPageLoad stepKey="waitForFirstProductToLoad"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml index 9961de9ce017..8a44c8093ca5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml @@ -60,6 +60,12 @@ <click selector="{{AdminEditProductAttributesSection.Save}}" stepKey="save"/> <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpateSuccessMsg"/> + <!-- Run cron twice --> + <magentoCLI command="cron:run" stepKey="runCron1"/> + <magentoCLI command="cron:run" stepKey="runCron2"/> + <reloadPage stepKey="refreshPage"/> + <waitForPageLoad stepKey="waitFormToReload1"/> + <!-- Assert on storefront default view --> <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault"/> <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndPriceActionGroup" stepKey="searchByNameDefault"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml index 305e0966a1f4..bee13bec370d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml @@ -54,6 +54,12 @@ <click selector="{{AdminEditProductAttributesSection.Save}}" stepKey="save"/> <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpateSuccessMsg"/> + <!-- Run cron twice --> + <magentoCLI command="cron:run" stepKey="runCron1"/> + <magentoCLI command="cron:run" stepKey="runCron2"/> + <reloadPage stepKey="refreshPage"/> + <waitForPageLoad stepKey="waitFormToReload1"/> + <!-- Assert on storefront default view --> <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault"/> <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault"> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml index 28285449d7e0..30a4290d882f 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml @@ -67,7 +67,13 @@ <waitForAjaxLoad stepKey="waitForLoadWebSiteTab"/> <click selector="{{AdminUpdateAttributesWebsiteSection.addProductToWebsite}}" stepKey="checkAddProductToWebsiteCheckbox"/> <click selector="{{AdminUpdateAttributesSection.saveButton}}" stepKey="clickSave"/> - <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue." stepKey="seeSaveSuccess"/> + <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeSaveSuccess"/> + + <!-- Run cron twice --> + <magentoCLI command="cron:run" stepKey="runCron1"/> + <magentoCLI command="cron:run" stepKey="runCron2"/> + <reloadPage stepKey="refreshPage"/> + <waitForPageLoad stepKey="waitFormToReload1"/> <!--Got to Store front product page and check url--> <amOnPage url="{{StorefrontProductPage.url($$createProduct.sku$$-new)}}" stepKey="navigateToSimpleProductPage"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml index 2137e4d1fe3d..39aa516077c5 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml @@ -59,6 +59,12 @@ <click selector="{{AdminUpdateAttributesSection.saveButton}}" stepKey="clickSave"/> <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeSaveSuccess"/> + <!-- Run cron twice --> + <magentoCLI command="cron:run" stepKey="runCron1"/> + <magentoCLI command="cron:run" stepKey="runCron2"/> + <reloadPage stepKey="refreshPage"/> + <waitForPageLoad stepKey="waitFormToReload1"/> + <!-- Check storefront for description --> <amOnPage url="$$createProduct1.sku$$.html" stepKey="gotoProduct1"/> <waitForPageLoad stepKey="wait3"/> From f07763abe228f55b61275adaaad518f6fb77f923 Mon Sep 17 00:00:00 2001 From: duhon <duhon@rambler.ru> Date: Wed, 27 Feb 2019 23:57:02 -0600 Subject: [PATCH 095/265] MC-13613: Product mass update - MC-5665 Execution of heavy admin operations in asynchronous flow part2 --- .../Magento/Catalog/Model/Attribute/Backend/Consumer.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php b/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php index f340cb49ee09..3bcc91315022 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php @@ -13,6 +13,7 @@ use Magento\Framework\Notification\NotifierInterface; use Magento\Framework\App\ObjectManager; use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Psr\Log\LoggerInterface; /** * Consumer for export message. @@ -25,7 +26,7 @@ class Consumer private $notifier; /** - * @var \Psr\Log\LoggerInterface + * @var LoggerInterface */ private $logger; @@ -94,6 +95,7 @@ class Consumer * @param \Magento\CatalogInventory\Api\StockRegistryInterfaceFactory $stockRegistryFactory * @param \Magento\CatalogInventory\Api\StockItemRepositoryInterfaceFactory $stockItemRepositoryFactory * @param NotifierInterface $notifier + * @param LoggerInterface $logger */ public function __construct( \Magento\Catalog\Helper\Product $catalogProduct, @@ -105,7 +107,8 @@ public function __construct( \Magento\Catalog\Model\Product\Action $action, \Magento\CatalogInventory\Api\StockRegistryInterfaceFactory $stockRegistryFactory, \Magento\CatalogInventory\Api\StockItemRepositoryInterfaceFactory $stockItemRepositoryFactory, - NotifierInterface $notifier + NotifierInterface $notifier, + LoggerInterface $logger ) { $this->catalogProduct = $catalogProduct; $this->productFlatIndexerProcessor = $productFlatIndexerProcessor; @@ -118,6 +121,7 @@ public function __construct( $this->productAction = $action; $this->stockRegistry = $stockRegistryFactory->create(); $this->stockItemRepository = $stockItemRepositoryFactory->create(); + $this->logger = $logger; } public function process(MassActionInterface $data): void From 7f780ff05597cc4b3e4b5ea212828a8271221b48 Mon Sep 17 00:00:00 2001 From: duhon <duhon@rambler.ru> Date: Thu, 28 Feb 2019 20:24:37 -0600 Subject: [PATCH 096/265] MC-13613: Product mass update - MC-5665 Execution of heavy admin operations in asynchronous flow part2 --- .../Product/Action/Attribute/Save.php | 131 ++++++++----- .../Model/Attribute/Backend/Consumer.php | 181 +++++++++--------- .../Magento/Catalog/etc/communication.xml | 2 +- app/code/Magento/Catalog/etc/queue.xml | 2 +- .../Magento/Catalog/etc/queue_consumer.xml | 2 +- 5 files changed, 175 insertions(+), 143 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php index 331b3ed07831..e91e0ce70f8f 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php @@ -6,12 +6,8 @@ */ namespace Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute; -use Magento\Catalog\Api\Data\MassActionInterfaceFactory; -use Magento\CatalogInventory\Api\StockConfigurationInterface; use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Backend\App\Action; -use Magento\Framework\MessageQueue\PublisherInterface; -use Magento\Framework\App\ObjectManager; /** * Class Save @@ -19,39 +15,54 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute implements HttpPostActionInterface { /** - * @var PublisherInterface + * @var \Magento\Framework\Bulk\BulkManagementInterface */ - private $messagePublisher; + private $bulkManagement; /** - * @var MassActionInterfaceFactory|null + * @var \Magento\AsynchronousOperations\Api\Data\OperationInterfaceFactory */ - private $massActionFactory; + private $operationFactory; /** - * @var StockConfigurationInterface + * @var \Magento\Framework\DataObject\IdentityGeneratorInterface */ - private $stockConfiguration; + private $identityService; + + /** + * @var \Magento\Framework\Serialize\SerializerInterface + */ + private $serializer; + + /** + * @var \Magento\Authorization\Model\UserContextInterface + */ + private $userContext; /** * @param Action\Context $context * @param \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeHelper - * @param StockConfigurationInterface|null $stockConfiguration - * @param PublisherInterface|null $publisher - * @param MassActionInterfaceFactory|null $massAction + * @param \Magento\Framework\Bulk\BulkManagementInterface $bulkManagement + * @param \Magento\AsynchronousOperations\Api\Data\OperationInterfaceFactory $operartionFactory + * @param \Magento\Framework\DataObject\IdentityGeneratorInterface $identityService + * @param \Magento\Framework\Serialize\SerializerInterface $serializer + * @param \Magento\Authorization\Model\UserContextInterface $userContext */ public function __construct( Action\Context $context, \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeHelper, - StockConfigurationInterface $stockConfiguration = null, - PublisherInterface $publisher = null, - MassActionInterfaceFactory $massAction = null + \Magento\Framework\Bulk\BulkManagementInterface $bulkManagement, + \Magento\AsynchronousOperations\Api\Data\OperationInterfaceFactory $operartionFactory, + \Magento\Framework\DataObject\IdentityGeneratorInterface $identityService, + \Magento\Framework\Serialize\SerializerInterface $serializer, + \Magento\Authorization\Model\UserContextInterface $userContext ) { parent::__construct($context, $attributeHelper); - $this->messagePublisher = $publisher ?: ObjectManager::getInstance()->get(PublisherInterface::class); - $this->massActionFactory = $massAction ?: ObjectManager::getInstance()->get(MassActionInterfaceFactory::class); - $this->stockConfiguration = $stockConfiguration - ?: ObjectManager::getInstance()->get(StockConfigurationInterface::class); + $this->bulkManagement = $bulkManagement; + $this->operationFactory = $operartionFactory; + $this->identityService = $identityService; + $this->serializer = $serializer; + $this->userContext = $userContext; } /** @@ -66,28 +77,18 @@ public function execute() } /* Collect Data */ - $inventoryData = $this->getRequest()->getParam('inventory', []); - $inventoryData = $this->addConfigSettings($inventoryData); $attributesData = $this->getRequest()->getParam('attributes', []); + $websiteRemoveData = $this->getRequest()->getParam('remove_website_ids', []); $websiteAddData = $this->getRequest()->getParam('add_website_ids', []); + $storeId = $this->attributeHelper->getSelectedStoreId(); - $productIds = $this->attributeHelper->getProductIds(); $websiteId = $this->attributeHelper->getStoreWebsiteId($storeId); - /* Create DTO for queue */ - $massAction = $this->massActionFactory->create(); - $massAction->setInventory($inventoryData); - $massAction->setAttributeValues(array_values($attributesData)); - $massAction->setAttributeKeys(array_keys($attributesData)); - $massAction->setWebsiteRemove($websiteRemoveData); - $massAction->setWebsiteAdd($websiteAddData); - $massAction->setStoreId($storeId); - $massAction->setProductIds($productIds); - $massAction->setWebsiteId($websiteId); + $productIds = $this->attributeHelper->getProductIds(); try { - $this->messagePublisher->publish('product_action_attribute.update', $massAction); + $this->publish('product_action_attribute.update', $attributesData, $websiteRemoveData, $websiteAddData, $storeId, $websiteId, $productIds); $this->messageManager->addSuccessMessage(__('Message is added to queue')); } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->messageManager->addErrorMessage($e->getMessage()); @@ -98,23 +99,63 @@ public function execute() ); } - return $this->resultRedirectFactory->create()->setPath('catalog/product/', ['store' => $storeId]); + return $this->resultRedirectFactory->create()->setPath( + 'catalog/product/', + ['store' => $storeId] + ); } /** - * Prepare inventory data item options (use config settings) - * @param $inventoryData - * @return mixed + * Schedule new bulk. + * + * @param $queue + * @param $attributesData + * @param $websiteRemoveData + * @param $websiteAddData + * @param $storeId + * @param $websiteId + * @param $productIds + * @return void */ - private function addConfigSettings($inventoryData) + private function publish($queue, $attributesData, $websiteRemoveData, $websiteAddData, $storeId, $websiteId, $productIds):void { - $options = $this->stockConfiguration->getConfigItemOptions(); - foreach ($options as $option) { - $useConfig = 'use_config_' . $option; - if (isset($inventoryData[$option]) && !isset($inventoryData[$useConfig])) { - $inventoryData[$useConfig] = 0; + $operationCount = count($productIds); + if ($operationCount > 0) { + $bulkUuid = $this->identityService->generateId(); + $bulkDescription = __('Assign custom prices to selected products'); + $operations = []; + foreach ($productIds as $productId) { + $dataToEncode = [ + 'meta_information' => 'ID:' . $productId, + 'product_id' => $productId, + 'store_id' => $storeId, + 'website_id' => $websiteId, + 'website_assign' => $websiteAddData, + 'website_detach' => $websiteRemoveData, + 'attributes' => $attributesData + ]; + $data = [ + 'data' => [ + 'bulk_uuid' => $bulkUuid, + 'topic_name' => $queue, + 'serialized_data' => $this->serializer->serialize($dataToEncode), + 'status' => \Magento\Framework\Bulk\OperationInterface::STATUS_TYPE_OPEN, + ] + ]; + + /** @var \Magento\AsynchronousOperations\Api\Data\OperationInterface $operation */ + $operation = $this->operationFactory->create($data); + $operations[] = $operation; + } + $userId = $this->userContext->getUserId(); + $result = $this->bulkManagement->scheduleBulk($bulkUuid, $operations, $bulkDescription, $userId); + if (!$result) { + throw new \Magento\Framework\Exception\LocalizedException( + __('Something went wrong while processing the request.') + ); } } - return $inventoryData; } + } + diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php b/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php index 3bcc91315022..2e9bbc18be4d 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php @@ -7,13 +7,12 @@ namespace Magento\Catalog\Model\Attribute\Backend; -use Magento\Catalog\Api\Data\MassActionInterface; -use Magento\CatalogInventory\Api\Data\StockItemInterface; use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Notification\NotifierInterface; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Exception\TemporaryStateExceptionInterface; use Magento\Framework\Stdlib\DateTime\TimezoneInterface; -use Psr\Log\LoggerInterface; +use Magento\Framework\Bulk\OperationInterface; /** * Consumer for export message. @@ -21,12 +20,12 @@ class Consumer { /** - * @var NotifierInterface + * @var \Magento\Framework\Notification\NotifierInterface */ private $notifier; /** - * @var LoggerInterface + * @var \Psr\Log\LoggerInterface */ private $logger; @@ -41,24 +40,10 @@ class Consumer private $productPriceIndexerProcessor; /** - * Catalog product - * * @var \Magento\Catalog\Helper\Product */ private $catalogProduct; - /** - * Stock Indexer - * - * @var \Magento\CatalogInventory\Model\Indexer\Stock\Processor - */ - private $stockIndexerProcessor; - - /** - * @var \Magento\Framework\Api\DataObjectHelper - */ - private $dataObjectHelper; - /** * @var \Magento\Framework\Event\ManagerInterface */ @@ -75,101 +60,102 @@ class Consumer private $productAction; /** - * @var \Magento\CatalogInventory\Api\StockRegistryInterfaceFactory + * @var \Magento\Framework\Serialize\SerializerInterface */ - private $stockRegistry; + private $serializer; /** - * @var \Magento\CatalogInventory\Api\StockItemRepositoryInterfaceFactory + * @var \Magento\Framework\Bulk\OperationManagementInterface */ - private $stockItemRepository; + private $operationManagement; /** * @param \Magento\Catalog\Helper\Product $catalogProduct * @param \Magento\Catalog\Model\Indexer\Product\Flat\Processor $productFlatIndexerProcessor * @param \Magento\Catalog\Model\Indexer\Product\Price\Processor $productPriceIndexerProcessor - * @param \Magento\CatalogInventory\Model\Indexer\Stock\Processor $stockIndexerProcessor - * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper + * @param \Magento\Framework\Bulk\OperationManagementInterface $operationManagement * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param \Magento\Catalog\Model\Product\Action $action - * @param \Magento\CatalogInventory\Api\StockRegistryInterfaceFactory $stockRegistryFactory - * @param \Magento\CatalogInventory\Api\StockItemRepositoryInterfaceFactory $stockItemRepositoryFactory - * @param NotifierInterface $notifier - * @param LoggerInterface $logger + * @param \Magento\Framework\Notification\NotifierInterface $notifier + * @param \Psr\Log\LoggerInterface $logger + * @param \Magento\Framework\Serialize\SerializerInterface $serializer */ public function __construct( \Magento\Catalog\Helper\Product $catalogProduct, \Magento\Catalog\Model\Indexer\Product\Flat\Processor $productFlatIndexerProcessor, \Magento\Catalog\Model\Indexer\Product\Price\Processor $productPriceIndexerProcessor, - \Magento\CatalogInventory\Model\Indexer\Stock\Processor $stockIndexerProcessor, - \Magento\Framework\Api\DataObjectHelper $dataObjectHelper, + \Magento\Framework\Bulk\OperationManagementInterface $operationManagement, \Magento\Framework\Event\ManagerInterface $eventManager, \Magento\Catalog\Model\Product\Action $action, - \Magento\CatalogInventory\Api\StockRegistryInterfaceFactory $stockRegistryFactory, - \Magento\CatalogInventory\Api\StockItemRepositoryInterfaceFactory $stockItemRepositoryFactory, - NotifierInterface $notifier, - LoggerInterface $logger + \Magento\Framework\Notification\NotifierInterface $notifier, + \Psr\Log\LoggerInterface $logger, + \Magento\Framework\Serialize\SerializerInterface $serializer ) { $this->catalogProduct = $catalogProduct; $this->productFlatIndexerProcessor = $productFlatIndexerProcessor; $this->productPriceIndexerProcessor = $productPriceIndexerProcessor; - $this->stockIndexerProcessor = $stockIndexerProcessor; - $this->dataObjectHelper = $dataObjectHelper; $this->notifier = $notifier; $this->eventManager = $eventManager; $this->objectManager = ObjectManager::getInstance(); $this->productAction = $action; - $this->stockRegistry = $stockRegistryFactory->create(); - $this->stockItemRepository = $stockItemRepositoryFactory->create(); $this->logger = $logger; + $this->serializer = $serializer; + $this->operationManagement = $operationManagement; } - public function process(MassActionInterface $data): void + /** + * Processing batch of operations for update tier prices. + * + * @param \Magento\AsynchronousOperations\Api\Data\OperationListInterface $operationList + * @return void + * @throws \InvalidArgumentException + */ + public function process(\Magento\AsynchronousOperations\Api\Data\OperationListInterface $operationList) { try { - if ($data->getInventory()) { - $this->updateInventoryInProducts($data->getProductIds(), $data->getWebsiteId(), $data->getInventory()); - } + foreach ($operationList->getItems() as $operation) { + $serializedData = $operation->getSerializedData(); + $data = $this->serializer->unserialize($serializedData); - if ($data->getWebsiteAdd() || $data->getWebsiteRemove()) { - $this->updateWebsiteInProducts($data->getProductIds(), $data->getWebsiteRemove(), $data->getWebsiteAdd()); + $this->execute($data); } - - if ($data->getAttributeValues()) { - $attributesData = $this->getAttributesData( - $data->getProductIds(), - $data->getStoreId(), - $data->getAttributeValues(), - $data->getAttributeKeys() - ); - $this->reindex($data->getProductIds(), $attributesData, $data->getWebsiteRemove(), $data->getWebsiteAdd()); - } - - $this->productFlatIndexerProcessor->reindexList($data->getProductIds()); - - $this->notifier->addNotice( - __('Product attributes updated'), - __('A total of %1 record(s) were updated.', count($data->getProductIds())) - ); - } catch (LocalizedException $exception) { - $this->notifier->addCritical( - __('Error during process occurred'), - __('Error during process occurred. Please check logs for detail') - ); - $this->logger->critical('Something went wrong while process. ' . $exception->getMessage()); + } catch (NoSuchEntityException $e) { + $this->logger->critical($e->getMessage()); + $status = ($e instanceof TemporaryStateExceptionInterface) + ? OperationInterface::STATUS_TYPE_RETRIABLY_FAILED + : OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; + $errorCode = $e->getCode(); + $message = $e->getMessage(); + } catch (LocalizedException $e) { + $this->logger->critical($e->getMessage()); + $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; + $errorCode = $e->getCode(); + $message = $e->getMessage(); + } catch (\Exception $e) { + $this->logger->critical($e->getMessage()); + $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; + $errorCode = $e->getCode(); + $message = __('Sorry, something went wrong during product prices update. Please see log for details.'); } + + //update operation status based on result performing operation(it was successfully executed or exception occurs + $this->operationManagement->changeOperationStatus( + $operation->getId(), + $status, + $errorCode, + $message, + $serializedData + ); } /** * @param $productIds * @param $storeId - * @param $attributeValuesData - * @param $attributeKeysData + * @param $attributesData * @return mixed */ - private function getAttributesData($productIds, $storeId, $attributeValuesData, $attributeKeysData) + private function getAttributesData($productIds, $storeId, $attributesData) { - $attributesData = array_combine($attributeKeysData, $attributeValuesData); $dateFormat = $this->objectManager->get(TimezoneInterface::class)->getDateFormat(\IntlDateFormatter::SHORT); $config = $this->objectManager->get(\Magento\Eav\Model\Config::class); @@ -208,27 +194,6 @@ private function getAttributesData($productIds, $storeId, $attributeValuesData, return $attributesData; } - /** - * @param $productIds - * @param $websiteId - * @param $inventoryData - */ - private function updateInventoryInProducts($productIds, $websiteId, $inventoryData): void - { - foreach ($productIds as $productId) { - $stockItemDo = $this->stockRegistry->getStockItem($productId, $websiteId); - if (!$stockItemDo->getProductId()) { - $inventoryData['product_id'] = $productId; - } - - $stockItemId = $stockItemDo->getId(); - $this->dataObjectHelper->populateWithArray($stockItemDo, $inventoryData, StockItemInterface::class); - $stockItemDo->setItemId($stockItemId); - $this->stockItemRepository->save($stockItemDo); - } - $this->stockIndexerProcessor->reindexList($productIds); - } - /** * @param $productIds * @param $websiteRemoveData @@ -261,4 +226,30 @@ private function reindex($productIds, $attributesData, $websiteRemoveData, $webs $this->productPriceIndexerProcessor->reindexList($productIds); } } -} \ No newline at end of file + + /** + * @param $data + */ + private function execute($data): void + { + if ($data['website_assign'] || $data['website_detach']) { + $this->updateWebsiteInProducts([$data['product_id']], $data['website_detach'], $data['website_assign']); + } + + if ($data['attributes']) { + $attributesData = $this->getAttributesData( + [$data['product_id']], + $data['store_id'], + $data['attributes'] + ); + $this->reindex([$data['product_id']], $attributesData, $data['website_detach'], $data['website_assign']); + } + + $this->productFlatIndexerProcessor->reindexList([$data['product_id']]); + + $this->notifier->addNotice( + __('Product attributes updated'), + __('A total of %1 record(s) were updated.', count([$data['product_id']])) + ); + } +} diff --git a/app/code/Magento/Catalog/etc/communication.xml b/app/code/Magento/Catalog/etc/communication.xml index b073725e934f..9871ba8e58b9 100644 --- a/app/code/Magento/Catalog/etc/communication.xml +++ b/app/code/Magento/Catalog/etc/communication.xml @@ -6,7 +6,7 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Communication/etc/communication.xsd"> - <topic name="product_action_attribute.update" request="Magento\Catalog\Api\Data\MassActionInterface"> + <topic name="product_action_attribute.update" request="Magento\AsynchronousOperations\Api\Data\OperationInterface"> <handler name="product_action_attribute.update" type="Magento\Catalog\Model\Attribute\Backend\Consumer" method="process" /> </topic> </config> \ No newline at end of file diff --git a/app/code/Magento/Catalog/etc/queue.xml b/app/code/Magento/Catalog/etc/queue.xml index 8aa7c7fe5e49..40d9cd1a378b 100644 --- a/app/code/Magento/Catalog/etc/queue.xml +++ b/app/code/Magento/Catalog/etc/queue.xml @@ -7,6 +7,6 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework-message-queue:etc/queue.xsd"> <broker topic="product_action_attribute.update" exchange="magento-db" type="db"> - <queue name="product_action_attribute.update" consumer="product_action_attribute.update" consumerInstance="Magento\Framework\MessageQueue\Consumer" handler="Magento\Catalog\Model\Attribute\Backend\Consumer::process"/> + <queue name="product_action_attribute.update" consumer="product_action_attribute.update" consumerInstance="Magento\Framework\MessageQueue\BatchConsume" handler="Magento\Catalog\Model\Attribute\Backend\Consumer::process"/> </broker> </config> \ No newline at end of file diff --git a/app/code/Magento/Catalog/etc/queue_consumer.xml b/app/code/Magento/Catalog/etc/queue_consumer.xml index 0090866c0e49..4be387211df0 100644 --- a/app/code/Magento/Catalog/etc/queue_consumer.xml +++ b/app/code/Magento/Catalog/etc/queue_consumer.xml @@ -6,5 +6,5 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework-message-queue:etc/consumer.xsd"> - <consumer name="product_action_attribute.update" queue="product_action_attribute.update" connection="db" maxMessages="5000" consumerInstance="Magento\Framework\MessageQueue\Consumer" handler="Magento\Catalog\Model\Attribute\Backend\Consumer::process" /> + <consumer name="product_action_attribute.update" queue="product_action_attribute.update" connection="db" maxMessages="5000" consumerInstance="Magento\Framework\MessageQueue\BatchConsumer" handler="Magento\Catalog\Model\Attribute\Backend\Consumer::process" /> </config> \ No newline at end of file From 315e78525c84267269fd8bb9a736623acc33a119 Mon Sep 17 00:00:00 2001 From: duhon <duhon@rambler.ru> Date: Mon, 4 Mar 2019 10:51:50 -0600 Subject: [PATCH 097/265] MC-13613: Product mass update - MC-5665 Execution of heavy admin operations in asynchronous flow part2 --- .../Catalog/Api/Data/MassActionInterface.php | 152 -------------- .../Product/Action/Attribute/Save.php | 149 +++++++++---- .../Model/Attribute/Backend/Consumer.php | 175 ++++------------ .../Backend/ConsumerWebsiteAssign.php | 161 ++++++++++++++ app/code/Magento/Catalog/Model/MassAction.php | 198 ------------------ .../Magento/Catalog/Model/Product/Action.php | 2 + .../Magento/Catalog/etc/communication.xml | 3 + app/code/Magento/Catalog/etc/queue.xml | 5 +- .../Magento/Catalog/etc/queue_consumer.xml | 3 +- .../Magento/Catalog/etc/queue_publisher.xml | 3 + .../Magento/Catalog/etc/queue_topology.xml | 1 + 11 files changed, 318 insertions(+), 534 deletions(-) delete mode 100644 app/code/Magento/Catalog/Api/Data/MassActionInterface.php create mode 100644 app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php delete mode 100644 app/code/Magento/Catalog/Model/MassAction.php diff --git a/app/code/Magento/Catalog/Api/Data/MassActionInterface.php b/app/code/Magento/Catalog/Api/Data/MassActionInterface.php deleted file mode 100644 index c03a73df5e92..000000000000 --- a/app/code/Magento/Catalog/Api/Data/MassActionInterface.php +++ /dev/null @@ -1,152 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Catalog\Api\Data; - -/** - * MassAction interface. - * @api - * @since 101.1.0 - */ -interface MassActionInterface -{ - - /** - * Set data value. - * - * @param string[] $data - * @return void - * @since 101.1.0 - */ - public function setInventory($data); - - /** - * Get data value. - * - * @return string[] - * @since 101.1.0 - */ - public function getInventory():array; - - /** - * Set data value. - * - * @param string[] $data - * @return void - * @since 101.1.0 - */ - public function setAttributeKeys($data); - - /** - * Get data value. - * - * @return string[] - * @since 101.1.0 - */ - public function getAttributeKeys():array; - - /** - * Set data value. - * - * @param string[] $data - * @return void - * @since 101.1.0 - */ - public function setAttributeValues($data); - - /** - * Get data value. - * - * @return string[] - * @since 101.1.0 - */ - public function getAttributeValues():array; - - /** - * Set data value. - * - * @param string[] $data - * @return void - * @since 101.1.0 - */ - public function setWebsiteRemove($data); - - /** - * Get data value. - * - * @return string[] - * @since 101.1.0 - */ - public function getWebsiteRemove():array; - - /** - * Set data value. - * - * @param string[] $data - * @return void - * @since 101.1.0 - */ - public function setWebsiteAdd($data); - - /** - * Get data value. - * - * @return string[] - * @since 101.1.0 - */ - public function getWebsiteAdd():array; - - /** - * Set data value. - * - * @param integer $data - * @return void - * @since 101.1.0 - */ - public function setStoreId($data); - - /** - * Get data value. - * - * @return integer - * @since 101.1.0 - */ - public function getStoreId(); - - /** - * Set data value. - * - * @param integer[] $data - * @return void - * @since 101.1.0 - */ - public function setProductIds(array $data); - - /** - * Get data value. - * - * @return integer[] - * @since 101.1.0 - */ - public function getProductIds():array; - - /** - * Set data value. - * - * @param integer $data - * @return void - * @since 101.1.0 - */ - public function setWebsiteId($data); - - /** - * Get data value. - * - * @return integer - * @since 101.1.0 - */ - public function getWebsiteId(); -} diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php index e91e0ce70f8f..324f3aba14c7 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php @@ -6,8 +6,11 @@ */ namespace Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute; +use Magento\AsynchronousOperations\Api\Data\OperationInterface; use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Backend\App\Action; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Framework\App\ObjectManager; /** * Class Save @@ -39,6 +42,11 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribut */ private $userContext; + /** + * @var ObjectManager + */ + private $objectManager; + /** * @param Action\Context $context * @param \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeHelper @@ -63,6 +71,7 @@ public function __construct( $this->identityService = $identityService; $this->serializer = $serializer; $this->userContext = $userContext; + $this->objectManager = ObjectManager::getInstance(); } /** @@ -78,17 +87,16 @@ public function execute() /* Collect Data */ $attributesData = $this->getRequest()->getParam('attributes', []); - $websiteRemoveData = $this->getRequest()->getParam('remove_website_ids', []); $websiteAddData = $this->getRequest()->getParam('add_website_ids', []); - $storeId = $this->attributeHelper->getSelectedStoreId(); $websiteId = $this->attributeHelper->getStoreWebsiteId($storeId); - $productIds = $this->attributeHelper->getProductIds(); + $attributesData = $this->sanitizeProductAttributes($attributesData); + try { - $this->publish('product_action_attribute.update', $attributesData, $websiteRemoveData, $websiteAddData, $storeId, $websiteId, $productIds); + $this->publish($attributesData, $websiteRemoveData, $websiteAddData, $storeId, $websiteId, $productIds); $this->messageManager->addSuccessMessage(__('Message is added to queue')); } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->messageManager->addErrorMessage($e->getMessage()); @@ -99,16 +107,50 @@ public function execute() ); } - return $this->resultRedirectFactory->create()->setPath( - 'catalog/product/', - ['store' => $storeId] - ); + return $this->resultRedirectFactory->create()->setPath('catalog/product/', ['store' => $storeId]); + } + + private function sanitizeProductAttributes($attributesData) + { + $dateFormat = $this->objectManager->get(TimezoneInterface::class)->getDateFormat(\IntlDateFormatter::SHORT); + $config = $this->objectManager->get(\Magento\Eav\Model\Config::class); + + foreach ($attributesData as $attributeCode => $value) { + $attribute = $config->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $attributeCode); + if (!$attribute->getAttributeId()) { + unset($attributesData[$attributeCode]); + continue; + } + if ($attribute->getBackendType() === 'datetime') { + if (!empty($value)) { + $filterInput = new \Zend_Filter_LocalizedToNormalized(['date_format' => $dateFormat]); + $filterInternal = new \Zend_Filter_NormalizedToLocalized( + ['date_format' => \Magento\Framework\Stdlib\DateTime::DATE_INTERNAL_FORMAT] + ); + $value = $filterInternal->filter($filterInput->filter($value)); + } else { + $value = null; + } + $attributesData[$attributeCode] = $value; + } elseif ($attribute->getFrontendInput() === 'multiselect') { + // Check if 'Change' checkbox has been checked by admin for this attribute + $isChanged = (bool)$this->getRequest()->getPost('toggle_' . $attributeCode); + if (!$isChanged) { + unset($attributesData[$attributeCode]); + continue; + } + if (is_array($value)) { + $value = implode(',', $value); + } + $attributesData[$attributeCode] = $value; + } + } + return $attributesData; } /** * Schedule new bulk. * - * @param $queue * @param $attributesData * @param $websiteRemoveData * @param $websiteAddData @@ -117,45 +159,62 @@ public function execute() * @param $productIds * @return void */ - private function publish($queue, $attributesData, $websiteRemoveData, $websiteAddData, $storeId, $websiteId, $productIds):void + private function publish($attributesData, $websiteRemoveData, $websiteAddData, $storeId, $websiteId, $productIds):void { - $operationCount = count($productIds); - if ($operationCount > 0) { - $bulkUuid = $this->identityService->generateId(); - $bulkDescription = __('Assign custom prices to selected products'); - $operations = []; - foreach ($productIds as $productId) { - $dataToEncode = [ - 'meta_information' => 'ID:' . $productId, - 'product_id' => $productId, - 'store_id' => $storeId, - 'website_id' => $websiteId, - 'website_assign' => $websiteAddData, - 'website_detach' => $websiteRemoveData, - 'attributes' => $attributesData - ]; - $data = [ - 'data' => [ - 'bulk_uuid' => $bulkUuid, - 'topic_name' => $queue, - 'serialized_data' => $this->serializer->serialize($dataToEncode), - 'status' => \Magento\Framework\Bulk\OperationInterface::STATUS_TYPE_OPEN, - ] - ]; - - /** @var \Magento\AsynchronousOperations\Api\Data\OperationInterface $operation */ - $operation = $this->operationFactory->create($data); - $operations[] = $operation; - } - $userId = $this->userContext->getUserId(); - $result = $this->bulkManagement->scheduleBulk($bulkUuid, $operations, $bulkDescription, $userId); - if (!$result) { - throw new \Magento\Framework\Exception\LocalizedException( - __('Something went wrong while processing the request.') - ); - } + $bulkUuid = $this->identityService->generateId(); + $bulkDescription = __('Update attributes to selected products'); + $operations = []; + if ($websiteRemoveData || $websiteAddData) { + $dataToUpdate = [ + 'website_assign' => $websiteAddData, + 'website_detach' => $websiteRemoveData + ]; + + $operations[] = $this->makeOperation('Update website assign', 'product_action_attribute.website.update', $dataToUpdate, $storeId, $websiteId, $productIds, $bulkUuid); + } + + if ($attributesData) { + $operations[] = $this->makeOperation('Update product attributes', 'product_action_attribute.update', $attributesData, $storeId, $websiteId, $productIds, $bulkUuid); + } + + $result = $this->bulkManagement->scheduleBulk($bulkUuid, $operations, $bulkDescription, $this->userContext->getUserId()); + if (!$result) { + throw new \Magento\Framework\Exception\LocalizedException( + __('Something went wrong while processing the request.') + ); } } + /** + * @param $meta + * @param $queue + * @param $dataToUpdate + * @param $storeId + * @param $websiteId + * @param $productIds + * @param $bulkUuid + * @return OperationInterface + */ + private function makeOperation($meta, $queue, $dataToUpdate, $storeId, $websiteId, $productIds, $bulkUuid): OperationInterface + { + $dataToEncode = [ + 'meta_information' => $meta, + 'product_ids' => $productIds, + 'store_id' => $storeId, + 'website_id' => $websiteId, + 'attributes' => $dataToUpdate + ]; + $data = [ + 'data' => [ + 'bulk_uuid' => $bulkUuid, + 'topic_name' => $queue, + 'serialized_data' => $this->serializer->serialize($dataToEncode), + 'status' => \Magento\Framework\Bulk\OperationInterface::STATUS_TYPE_OPEN, + ] + ]; + + return $this->operationFactory->create($data); + } + } diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php b/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php index 2e9bbc18be4d..7e6c68a1502e 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php @@ -8,10 +8,9 @@ namespace Magento\Catalog\Model\Attribute\Backend; use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\EntityManager\EntityManager; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\TemporaryStateExceptionInterface; -use Magento\Framework\Stdlib\DateTime\TimezoneInterface; use Magento\Framework\Bulk\OperationInterface; /** @@ -19,11 +18,6 @@ */ class Consumer { - /** - * @var \Magento\Framework\Notification\NotifierInterface - */ - private $notifier; - /** * @var \Psr\Log\LoggerInterface */ @@ -44,16 +38,6 @@ class Consumer */ private $catalogProduct; - /** - * @var \Magento\Framework\Event\ManagerInterface - */ - private $eventManager; - - /** - * @var ObjectManager - */ - private $objectManager; - /** * @var \Magento\Catalog\Model\Product\Action */ @@ -68,56 +52,68 @@ class Consumer * @var \Magento\Framework\Bulk\OperationManagementInterface */ private $operationManagement; + /** + * @var EntityManager + */ + private $entityManager; /** * @param \Magento\Catalog\Helper\Product $catalogProduct * @param \Magento\Catalog\Model\Indexer\Product\Flat\Processor $productFlatIndexerProcessor * @param \Magento\Catalog\Model\Indexer\Product\Price\Processor $productPriceIndexerProcessor * @param \Magento\Framework\Bulk\OperationManagementInterface $operationManagement - * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param \Magento\Catalog\Model\Product\Action $action - * @param \Magento\Framework\Notification\NotifierInterface $notifier * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Framework\Serialize\SerializerInterface $serializer + * @param EntityManager $entityManager */ public function __construct( \Magento\Catalog\Helper\Product $catalogProduct, \Magento\Catalog\Model\Indexer\Product\Flat\Processor $productFlatIndexerProcessor, \Magento\Catalog\Model\Indexer\Product\Price\Processor $productPriceIndexerProcessor, \Magento\Framework\Bulk\OperationManagementInterface $operationManagement, - \Magento\Framework\Event\ManagerInterface $eventManager, \Magento\Catalog\Model\Product\Action $action, - \Magento\Framework\Notification\NotifierInterface $notifier, \Psr\Log\LoggerInterface $logger, - \Magento\Framework\Serialize\SerializerInterface $serializer + \Magento\Framework\Serialize\SerializerInterface $serializer, + EntityManager $entityManager ) { $this->catalogProduct = $catalogProduct; $this->productFlatIndexerProcessor = $productFlatIndexerProcessor; $this->productPriceIndexerProcessor = $productPriceIndexerProcessor; - $this->notifier = $notifier; - $this->eventManager = $eventManager; - $this->objectManager = ObjectManager::getInstance(); $this->productAction = $action; $this->logger = $logger; $this->serializer = $serializer; $this->operationManagement = $operationManagement; + $this->entityManager = $entityManager; } /** - * Processing batch of operations for update tier prices. - * - * @param \Magento\AsynchronousOperations\Api\Data\OperationListInterface $operationList + * @param \Magento\AsynchronousOperations\Api\Data\OperationInterface $operation * @return void - * @throws \InvalidArgumentException */ - public function process(\Magento\AsynchronousOperations\Api\Data\OperationListInterface $operationList) + public function process(\Magento\AsynchronousOperations\Api\Data\OperationInterface $operation) { try { - foreach ($operationList->getItems() as $operation) { - $serializedData = $operation->getSerializedData(); - $data = $this->serializer->unserialize($serializedData); + $serializedData = $operation->getSerializedData(); + $data = $this->serializer->unserialize($serializedData); + $this->execute($data); - $this->execute($data); + } catch (\Zend_Db_Adapter_Exception $e) { + //here sample how to process exceptions if they occurred + $this->logger->critical($e->getMessage()); + //you can add here your own type of exception when operation can be retried + if ( + $e instanceof \Magento\Framework\DB\Adapter\LockWaitException + || $e instanceof \Magento\Framework\DB\Adapter\DeadlockException + || $e instanceof \Magento\Framework\DB\Adapter\ConnectionException + ) { + $status = OperationInterface::STATUS_TYPE_RETRIABLY_FAILED; + $errorCode = $e->getCode(); + $message = __($e->getMessage()); + } else { + $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; + $errorCode = $e->getCode(); + $message = __('Sorry, something went wrong during product prices update. Please see log for details.'); } } catch (NoSuchEntityException $e) { $this->logger->critical($e->getMessage()); @@ -138,93 +134,11 @@ public function process(\Magento\AsynchronousOperations\Api\Data\OperationListIn $message = __('Sorry, something went wrong during product prices update. Please see log for details.'); } - //update operation status based on result performing operation(it was successfully executed or exception occurs - $this->operationManagement->changeOperationStatus( - $operation->getId(), - $status, - $errorCode, - $message, - $serializedData - ); - } + $operation->setStatus($status ?? OperationInterface::STATUS_TYPE_COMPLETE) + ->setErrorCode($errorCode ?? null) + ->setResultMessage($message ?? null); - /** - * @param $productIds - * @param $storeId - * @param $attributesData - * @return mixed - */ - private function getAttributesData($productIds, $storeId, $attributesData) - { - $dateFormat = $this->objectManager->get(TimezoneInterface::class)->getDateFormat(\IntlDateFormatter::SHORT); - $config = $this->objectManager->get(\Magento\Eav\Model\Config::class); - - foreach ($attributesData as $attributeCode => $value) { - $attribute = $config->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $attributeCode); - if (!$attribute->getAttributeId()) { - unset($attributesData[$attributeCode]); - continue; - } - if ($attribute->getBackendType() == 'datetime') { - if (!empty($value)) { - $filterInput = new \Zend_Filter_LocalizedToNormalized(['date_format' => $dateFormat]); - $filterInternal = new \Zend_Filter_NormalizedToLocalized( - ['date_format' => \Magento\Framework\Stdlib\DateTime::DATE_INTERNAL_FORMAT] - ); - $value = $filterInternal->filter($filterInput->filter($value)); - } else { - $value = null; - } - $attributesData[$attributeCode] = $value; - } elseif ($attribute->getFrontendInput() == 'multiselect') { - // Check if 'Change' checkbox has been checked by admin for this attribute - $isChanged = (bool)$this->getRequest()->getPost('toggle_' . $attributeCode); - if (!$isChanged) { - unset($attributesData[$attributeCode]); - continue; - } - if (is_array($value)) { - $value = implode(',', $value); - } - $attributesData[$attributeCode] = $value; - } - } - - $this->productAction->updateAttributes($productIds, $attributesData, $storeId); - return $attributesData; - } - - /** - * @param $productIds - * @param $websiteRemoveData - * @param $websiteAddData - */ - private function updateWebsiteInProducts($productIds, $websiteRemoveData, $websiteAddData): void - { - if ($websiteRemoveData) { - $this->productAction->updateWebsites($productIds, $websiteRemoveData, 'remove'); - } - if ($websiteAddData) { - $this->productAction->updateWebsites($productIds, $websiteAddData, 'add'); - } - - $this->eventManager->dispatch('catalog_product_to_website_change', ['products' => $productIds]); - } - - /** - * @param $productIds - * @param $attributesData - * @param $websiteRemoveData - * @param $websiteAddData - */ - private function reindex($productIds, $attributesData, $websiteRemoveData, $websiteAddData): void - { - if ($this->catalogProduct->isDataForPriceIndexerWasChanged($attributesData) - || !empty($websiteRemoveData) - || !empty($websiteAddData) - ) { - $this->productPriceIndexerProcessor->reindexList($productIds); - } + $this->entityManager->save($operation); } /** @@ -232,24 +146,11 @@ private function reindex($productIds, $attributesData, $websiteRemoveData, $webs */ private function execute($data): void { - if ($data['website_assign'] || $data['website_detach']) { - $this->updateWebsiteInProducts([$data['product_id']], $data['website_detach'], $data['website_assign']); - } - - if ($data['attributes']) { - $attributesData = $this->getAttributesData( - [$data['product_id']], - $data['store_id'], - $data['attributes'] - ); - $this->reindex([$data['product_id']], $attributesData, $data['website_detach'], $data['website_assign']); + $this->productAction->updateAttributes($data['product_ids'], $data['attributes'], $data['store_id']); + if ($this->catalogProduct->isDataForPriceIndexerWasChanged($data['attributes'])) { + $this->productPriceIndexerProcessor->reindexList($data['product_ids']); } - $this->productFlatIndexerProcessor->reindexList([$data['product_id']]); - - $this->notifier->addNotice( - __('Product attributes updated'), - __('A total of %1 record(s) were updated.', count([$data['product_id']])) - ); + $this->productFlatIndexerProcessor->reindexList($data['product_ids']); } } diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php b/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php new file mode 100644 index 000000000000..ce4113a03df5 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php @@ -0,0 +1,161 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Attribute\Backend; + +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Exception\TemporaryStateExceptionInterface; +use Magento\Framework\Bulk\OperationInterface; +use Magento\Framework\EntityManager\EntityManager; + +/** + * Consumer for export message. + */ +class ConsumerWebsiteAssign +{ + /** + * @var \Psr\Log\LoggerInterface + */ + private $logger; + + /** + * @var \Magento\Catalog\Model\Indexer\Product\Flat\Processor + */ + private $productFlatIndexerProcessor; + + /** + * @var \Magento\Catalog\Model\Product\Action + */ + private $productAction; + + /** + * @var \Magento\Framework\Serialize\SerializerInterface + */ + private $serializer; + + /** + * @var \Magento\Framework\Bulk\OperationManagementInterface + */ + private $operationManagement; + + /** + * @var \Magento\Catalog\Model\Indexer\Product\Price\Processor + */ + private $productPriceIndexerProcessor; + + /** + * @var EntityManager + */ + private $entityManager; + + /** + * @param \Magento\Catalog\Model\Indexer\Product\Flat\Processor $productFlatIndexerProcessor + * @param \Magento\Catalog\Model\Indexer\Product\Price\Processor $productPriceIndexerProcessor + * @param \Magento\Framework\Bulk\OperationManagementInterface $operationManagement + * @param \Magento\Catalog\Model\Product\Action $action + * @param \Psr\Log\LoggerInterface $logger + * @param \Magento\Framework\Serialize\SerializerInterface $serializer + * @param EntityManager $entityManager + */ + public function __construct( + \Magento\Catalog\Model\Indexer\Product\Flat\Processor $productFlatIndexerProcessor, + \Magento\Catalog\Model\Indexer\Product\Price\Processor $productPriceIndexerProcessor, + \Magento\Framework\Bulk\OperationManagementInterface $operationManagement, + \Magento\Catalog\Model\Product\Action $action, + \Psr\Log\LoggerInterface $logger, + \Magento\Framework\Serialize\SerializerInterface $serializer, + EntityManager $entityManager + ) { + $this->productFlatIndexerProcessor = $productFlatIndexerProcessor; + $this->productAction = $action; + $this->logger = $logger; + $this->serializer = $serializer; + $this->operationManagement = $operationManagement; + $this->productPriceIndexerProcessor = $productPriceIndexerProcessor; + $this->entityManager = $entityManager; + } + + /** + * @param \Magento\AsynchronousOperations\Api\Data\OperationInterface $operation + * @return void + */ + public function process(\Magento\AsynchronousOperations\Api\Data\OperationInterface $operation) + { + try { + $serializedData = $operation->getSerializedData(); + $data = $this->serializer->unserialize($serializedData); + $this->execute($data); + } catch (\Zend_Db_Adapter_Exception $e) { + //here sample how to process exceptions if they occurred + $this->logger->critical($e->getMessage()); + //you can add here your own type of exception when operation can be retried + if ( + $e instanceof \Magento\Framework\DB\Adapter\LockWaitException + || $e instanceof \Magento\Framework\DB\Adapter\DeadlockException + || $e instanceof \Magento\Framework\DB\Adapter\ConnectionException + ) { + $status = OperationInterface::STATUS_TYPE_RETRIABLY_FAILED; + $errorCode = $e->getCode(); + $message = __($e->getMessage()); + } else { + $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; + $errorCode = $e->getCode(); + $message = __('Sorry, something went wrong during product prices update. Please see log for details.'); + } + } catch (NoSuchEntityException $e) { + $this->logger->critical($e->getMessage()); + $status = ($e instanceof TemporaryStateExceptionInterface) + ? OperationInterface::STATUS_TYPE_RETRIABLY_FAILED + : OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; + $errorCode = $e->getCode(); + $message = $e->getMessage(); + } catch (LocalizedException $e) { + $this->logger->critical($e->getMessage()); + $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; + $errorCode = $e->getCode(); + $message = $e->getMessage(); + } catch (\Exception $e) { + $this->logger->critical($e->getMessage()); + $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; + $errorCode = $e->getCode(); + $message = __('Sorry, something went wrong during product prices update. Please see log for details.'); + } + + $operation->setStatus($status ?? OperationInterface::STATUS_TYPE_COMPLETE) + ->setErrorCode($errorCode ?? null) + ->setResultMessage($message ?? null); + + $this->entityManager->save($operation); + } + + /** + * @param $productIds + * @param $websiteRemoveData + * @param $websiteAddData + */ + private function updateWebsiteInProducts($productIds, $websiteRemoveData, $websiteAddData): void + { + if ($websiteRemoveData) { + $this->productAction->updateWebsites($productIds, $websiteRemoveData, 'remove'); + } + if ($websiteAddData) { + $this->productAction->updateWebsites($productIds, $websiteAddData, 'add'); + } + } + + + /** + * @param $data + */ + private function execute($data): void + { + $this->updateWebsiteInProducts($data['product_ids'], $data['attributes']['website_detach'], $data['attributes']['website_assign']); + $this->productPriceIndexerProcessor->reindexList($data['product_ids']); + $this->productFlatIndexerProcessor->reindexList($data['product_ids']); + } +} diff --git a/app/code/Magento/Catalog/Model/MassAction.php b/app/code/Magento/Catalog/Model/MassAction.php deleted file mode 100644 index 0f09ff8d7699..000000000000 --- a/app/code/Magento/Catalog/Model/MassAction.php +++ /dev/null @@ -1,198 +0,0 @@ -<?php -namespace Magento\Catalog\Model; - -class MassAction implements \Magento\Catalog\Api\Data\MassActionInterface -{ - private $inventory; - private $attributeKeys; - private $websiteRemove; - private $websiteAdd; - private $storeId; - private $productIds; - private $websiteId; - private $attributeValues; - - /** - * Set data value. - * - * @param string $data - * @return void - * @since 101.1.0 - */ - public function setInventory($data) - { - $this->inventory = $data; - } - - /** - * Get data value. - * - * @return string[] - * @since 101.1.0 - */ - public function getInventory():array - { - return $this->inventory; - } - - /** - * Set data value. - * - * @param string $data - * @return void - * @since 101.1.0 - */ - public function setAttributeKeys($data) - { - $this->attributeKeys = $data; - } - - /** - * Get data value. - * - * @return string[] - * @since 101.1.0 - */ - public function getAttributeKeys():array - { - return $this->attributeKeys; - } - - /** - * Set data value. - * - * @param string $data - * @return void - * @since 101.1.0 - */ - public function setAttributeValues($data) - { - $this->attributeValues = $data; - } - - /** - * Get data value. - * - * @return string[] - * @since 101.1.0 - */ - public function getAttributeValues():array - { - return $this->attributeValues; - } - - /** - * Set data value. - * - * @param string $data - * @return void - * @since 101.1.0 - */ - public function setWebsiteRemove($data) - { - $this->websiteRemove = $data; - } - - /** - * Get data value. - * - * @return string[] - * @since 101.1.0 - */ - public function getWebsiteRemove():array - { - return $this->websiteRemove; - } - - /** - * Set data value. - * - * @param string $data - * @return void - * @since 101.1.0 - */ - public function setWebsiteAdd($data) - { - $this->websiteAdd = $data; - } - - /** - * Get data value. - * - * @return string[] - * @since 101.1.0 - */ - public function getWebsiteAdd():array - { - return $this->websiteAdd; - } - - /** - * Set data value. - * - * @param string $data - * @return void - * @since 101.1.0 - */ - public function setStoreId($data) - { - $this->storeId = $data; - } - - /** - * Get data value. - * - * @return string - * @since 101.1.0 - */ - public function getStoreId() - { - return $this->storeId; - } - - /** - * Set data value. - * - * @param integer[] $data - * @return void - * @since 101.1.0 - */ - public function setProductIds(array $data) - { - $this->productIds = $data; - } - - /** - * Get data value. - * - * @return integer[] - * @since 101.1.0 - */ - public function getProductIds():array - { - return $this->productIds; - } - - /** - * Set data value. - * - * @param string $data - * @return void - * @since 101.1.0 - */ - public function setWebsiteId($data) - { - $this->websiteId = $data; - } - - /** - * Get data value. - * - * @return string - * @since 101.1.0 - */ - public function getWebsiteId() - { - return $this->websiteId; - } -} diff --git a/app/code/Magento/Catalog/Model/Product/Action.php b/app/code/Magento/Catalog/Model/Product/Action.php index f78048424b42..3863cf245724 100644 --- a/app/code/Magento/Catalog/Model/Product/Action.php +++ b/app/code/Magento/Catalog/Model/Product/Action.php @@ -168,5 +168,7 @@ public function updateWebsites($productIds, $websiteIds, $type) if (!$categoryIndexer->isScheduled()) { $categoryIndexer->reindexList(array_unique($productIds)); } + + $this->_eventManager->dispatch('catalog_product_to_website_change', ['products' => $productIds]); } } diff --git a/app/code/Magento/Catalog/etc/communication.xml b/app/code/Magento/Catalog/etc/communication.xml index 9871ba8e58b9..1a957f6ac9fe 100644 --- a/app/code/Magento/Catalog/etc/communication.xml +++ b/app/code/Magento/Catalog/etc/communication.xml @@ -9,4 +9,7 @@ <topic name="product_action_attribute.update" request="Magento\AsynchronousOperations\Api\Data\OperationInterface"> <handler name="product_action_attribute.update" type="Magento\Catalog\Model\Attribute\Backend\Consumer" method="process" /> </topic> + <topic name="product_action_attribute.website.update" request="Magento\AsynchronousOperations\Api\Data\OperationInterface"> + <handler name="product_action_attribute.website.update" type="Magento\Catalog\Model\Attribute\Backend\ConsumerWebsiteAssign" method="process" /> + </topic> </config> \ No newline at end of file diff --git a/app/code/Magento/Catalog/etc/queue.xml b/app/code/Magento/Catalog/etc/queue.xml index 40d9cd1a378b..137f34a5c1e2 100644 --- a/app/code/Magento/Catalog/etc/queue.xml +++ b/app/code/Magento/Catalog/etc/queue.xml @@ -7,6 +7,9 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework-message-queue:etc/queue.xsd"> <broker topic="product_action_attribute.update" exchange="magento-db" type="db"> - <queue name="product_action_attribute.update" consumer="product_action_attribute.update" consumerInstance="Magento\Framework\MessageQueue\BatchConsume" handler="Magento\Catalog\Model\Attribute\Backend\Consumer::process"/> + <queue name="product_action_attribute.update" consumer="product_action_attribute.update" consumerInstance="Magento\Framework\MessageQueue\Consumer" handler="Magento\Catalog\Model\Attribute\Backend\Consumer::process"/> + </broker> + <broker topic="product_action_attribute.website.update" exchange="magento-db" type="db"> + <queue name="product_action_attribute.website.update" consumer="product_action_attribute.website.update" consumerInstance="Magento\Framework\MessageQueue\Consumer" handler="Magento\Catalog\Model\Attribute\Backend\ConsumerWebsiteAssign::process"/> </broker> </config> \ No newline at end of file diff --git a/app/code/Magento/Catalog/etc/queue_consumer.xml b/app/code/Magento/Catalog/etc/queue_consumer.xml index 4be387211df0..d9e66ae69c10 100644 --- a/app/code/Magento/Catalog/etc/queue_consumer.xml +++ b/app/code/Magento/Catalog/etc/queue_consumer.xml @@ -6,5 +6,6 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework-message-queue:etc/consumer.xsd"> - <consumer name="product_action_attribute.update" queue="product_action_attribute.update" connection="db" maxMessages="5000" consumerInstance="Magento\Framework\MessageQueue\BatchConsumer" handler="Magento\Catalog\Model\Attribute\Backend\Consumer::process" /> + <consumer name="product_action_attribute.update" queue="product_action_attribute.update" connection="db" maxMessages="5000" consumerInstance="Magento\Framework\MessageQueue\Consumer" handler="Magento\Catalog\Model\Attribute\Backend\Consumer::process" /> + <consumer name="product_action_attribute.website.update" queue="product_action_attribute.website.update" connection="db" maxMessages="5000" consumerInstance="Magento\Framework\MessageQueue\Consumer" handler="Magento\Catalog\Model\Attribute\Backend\ConsumerWebsiteAssign::process" /> </config> \ No newline at end of file diff --git a/app/code/Magento/Catalog/etc/queue_publisher.xml b/app/code/Magento/Catalog/etc/queue_publisher.xml index c673a8321762..1606ea42ec0b 100644 --- a/app/code/Magento/Catalog/etc/queue_publisher.xml +++ b/app/code/Magento/Catalog/etc/queue_publisher.xml @@ -9,4 +9,7 @@ <publisher topic="product_action_attribute.update"> <connection name="db" exchange="magento-db" /> </publisher> + <publisher topic="product_action_attribute.website.update"> + <connection name="db" exchange="magento-db" /> + </publisher> </config> \ No newline at end of file diff --git a/app/code/Magento/Catalog/etc/queue_topology.xml b/app/code/Magento/Catalog/etc/queue_topology.xml index 0097d770936a..bdac891afbdb 100644 --- a/app/code/Magento/Catalog/etc/queue_topology.xml +++ b/app/code/Magento/Catalog/etc/queue_topology.xml @@ -8,5 +8,6 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework-message-queue:etc/topology.xsd"> <exchange name="magento-db" type="topic" connection="db"> <binding id="updateBinding" topic="product_action_attribute.update" destinationType="queue" destination="product_action_attribute.update"/> + <binding id="updateBindingWebsite" topic="product_action_attribute.website.update" destinationType="queue" destination="product_action_attribute.website.update"/> </exchange> </config> \ No newline at end of file From 5fece499757fb44cc526fd2d5841475da0df322d Mon Sep 17 00:00:00 2001 From: duhon <duhon@rambler.ru> Date: Mon, 4 Mar 2019 19:43:10 -0600 Subject: [PATCH 098/265] MC-13613: Product mass update - MC-5665 Execution of heavy admin operations in asynchronous flow part2 --- .../Backend/ConsumerWebsiteAssign.php | 1 - .../Plugin/MassUpdateProductAttribute.php | 153 ++++++++++++++++++ app/code/Magento/CatalogInventory/etc/di.xml | 3 + 3 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php b/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php index ce4113a03df5..f8cc3c04df60 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php @@ -148,7 +148,6 @@ private function updateWebsiteInProducts($productIds, $websiteRemoveData, $websi } } - /** * @param $data */ diff --git a/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php b/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php new file mode 100644 index 000000000000..36958a37c6d1 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php @@ -0,0 +1,153 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\CatalogInventory\Plugin; + +use Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute\Save; +use Magento\CatalogInventory\Api\Data\StockItemInterface; + +class MassUpdateProductAttribute +{ + /** + * @var \Magento\CatalogInventory\Model\Indexer\Stock\Processor + */ + private $stockIndexerProcessor; + + /** + * @var \Magento\Framework\Api\DataObjectHelper + */ + private $dataObjectHelper; + + /** + * @var \Magento\CatalogInventory\Api\StockRegistryInterface + */ + private $stockRegistry; + + /** + * @var \Magento\CatalogInventory\Api\StockItemRepositoryInterface + */ + private $stockItemRepository; + + /** + * @var \Magento\CatalogInventory\Api\StockConfigurationInterface + */ + private $stockConfiguration; + + /** + * @var \Magento\Framework\App\RequestInterface + */ + private $request; + + /** + * @var \Magento\Backend\Model\Session + */ + private $session; + + /** + * @var \Magento\Store\Model\StoreManagerInterface + */ + private $storeManager; + + /** + * @var \Magento\Backend\Model\View\Result\Redirect + */ + private $redirectFactory; + /** + * @var \Magento\Framework\Message\ManagerInterface + */ + private $messageManager; + + /** + * @param \Magento\CatalogInventory\Model\Indexer\Stock\Processor $stockIndexerProcessor + * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper + * @param \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry + * @param \Magento\CatalogInventory\Api\StockItemRepositoryInterface $stockItemRepository + * @param \Magento\CatalogInventory\Api\StockConfigurationInterface $stockConfiguration + * @param \Magento\Framework\App\RequestInterface $request + * @param \Magento\Backend\Model\Session $session + * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param \Magento\Backend\Model\View\Result\RedirectFactory $redirectFactory + * @param \Magento\Framework\Message\ManagerInterface $messageManager + */ + public function __construct( + \Magento\CatalogInventory\Model\Indexer\Stock\Processor $stockIndexerProcessor, + \Magento\Framework\Api\DataObjectHelper $dataObjectHelper, + \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry, + \Magento\CatalogInventory\Api\StockItemRepositoryInterface $stockItemRepository, + \Magento\CatalogInventory\Api\StockConfigurationInterface $stockConfiguration, + \Magento\Framework\App\RequestInterface $request, + \Magento\Backend\Model\Session $session, + \Magento\Store\Model\StoreManagerInterface $storeManager, + \Magento\Backend\Model\View\Result\RedirectFactory $redirectFactory, + \Magento\Framework\Message\ManagerInterface $messageManager + ) { + $this->stockIndexerProcessor = $stockIndexerProcessor; + $this->dataObjectHelper = $dataObjectHelper; + $this->stockRegistry = $stockRegistry; + $this->stockItemRepository = $stockItemRepository; + $this->stockConfiguration = $stockConfiguration; + $this->request = $request; + $this->session = $session; + $this->storeManager = $storeManager; + $this->redirectFactory = $redirectFactory; + $this->messageManager = $messageManager; + } + + /** + * @param Save $subject + * @return \Magento\Framework\Controller\ResultInterface + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function aroundExecute(Save $subject, callable $proceed) + { + try { + $inventoryData = $this->request->getParam('inventory', []); + $storeId = $this->request->getParam('store', \Magento\Store\Model\Store::DEFAULT_STORE_ID); + $websiteId = $this->storeManager->getStore($storeId)->getWebsiteId(); + $productIds = $this->session->getData('product_ids'); + $inventoryData = $this->addConfigSettings($inventoryData); + $this->updateInventoryInProducts($productIds, $websiteId, $inventoryData); + + return $proceed(); + } catch (\Magento\Framework\Exception\LocalizedException $e) { + $this->messageManager->addErrorMessage($e->getMessage()); + } catch (\Exception $e) { + $this->messageManager->addExceptionMessage( + $e, + __('Something went wrong while updating the product(s) attributes.') + ); + } + + return $this->redirectFactory->create()->setPath('catalog/product/', ['_current' => true]); + } + + private function addConfigSettings($inventoryData) + { + $options = $this->stockConfiguration->getConfigItemOptions(); + foreach ($options as $option) { + $useConfig = 'use_config_' . $option; + if (isset($inventoryData[$option]) && !isset($inventoryData[$useConfig])) { + $inventoryData[$useConfig] = 0; + } + } + return $inventoryData; + } + + private function updateInventoryInProducts($productIds, $websiteId, $inventoryData): void + { + foreach ($productIds as $productId) { + $stockItemDo = $this->stockRegistry->getStockItem($productId, $websiteId); + if (!$stockItemDo->getProductId()) { + $inventoryData['product_id'] = $productId; + } + $stockItemId = $stockItemDo->getId(); + $this->dataObjectHelper->populateWithArray($stockItemDo, $inventoryData, StockItemInterface::class); + $stockItemDo->setItemId($stockItemId); + $this->stockItemRepository->save($stockItemDo); + } + $this->stockIndexerProcessor->reindexList($productIds); + } +} diff --git a/app/code/Magento/CatalogInventory/etc/di.xml b/app/code/Magento/CatalogInventory/etc/di.xml index 8d57fab843f4..4253dc09b4e2 100644 --- a/app/code/Magento/CatalogInventory/etc/di.xml +++ b/app/code/Magento/CatalogInventory/etc/di.xml @@ -135,4 +135,7 @@ <type name="Magento\CatalogInventory\Model\ResourceModel\Stock\Item"> <plugin name="priceIndexUpdater" type="Magento\CatalogInventory\Model\Plugin\PriceIndexUpdater" /> </type> + <type name="Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute\Save"> + <plugin name="massAction" type="Magento\CatalogInventory\Plugin\MassUpdateProductAttribute" /> + </type> </config> From 31fcfa50fbe3b6fccbbe18f57a25df425df47feb Mon Sep 17 00:00:00 2001 From: duhon <duhon@rambler.ru> Date: Mon, 4 Mar 2019 20:09:58 -0600 Subject: [PATCH 099/265] MC-13613: Product mass update - MC-5665 Execution of heavy admin operations in asynchronous flow part2 --- .../Adminhtml/Product/Action/Attribute/Save.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php index 324f3aba14c7..f0b19b2073cc 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php @@ -177,11 +177,13 @@ private function publish($attributesData, $websiteRemoveData, $websiteAddData, $ $operations[] = $this->makeOperation('Update product attributes', 'product_action_attribute.update', $attributesData, $storeId, $websiteId, $productIds, $bulkUuid); } - $result = $this->bulkManagement->scheduleBulk($bulkUuid, $operations, $bulkDescription, $this->userContext->getUserId()); - if (!$result) { - throw new \Magento\Framework\Exception\LocalizedException( - __('Something went wrong while processing the request.') - ); + if (!empty($operations)) { + $result = $this->bulkManagement->scheduleBulk($bulkUuid, $operations, $bulkDescription, $this->userContext->getUserId()); + if (!$result) { + throw new \Magento\Framework\Exception\LocalizedException( + __('Something went wrong while processing the request.') + ); + } } } From 7a39087f0c0832f13cd50af18414a45229e59237 Mon Sep 17 00:00:00 2001 From: Andrii Lugovyi <alugovyi@adobe.com> Date: Tue, 5 Mar 2019 18:58:28 -0600 Subject: [PATCH 100/265] MC-13613: Product mass update - MC-5665 Execution of heavy admin operations in asynchronous flow part2 --- setup/performance-toolkit/benchmark.jmx | 837 ------------------------ 1 file changed, 837 deletions(-) diff --git a/setup/performance-toolkit/benchmark.jmx b/setup/performance-toolkit/benchmark.jmx index 765d0a616f77..0e1860405e94 100644 --- a/setup/performance-toolkit/benchmark.jmx +++ b/setup/performance-toolkit/benchmark.jmx @@ -27267,843 +27267,6 @@ if (testLabel <stringProp name="ThreadGroup.delay"/> <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/thread_group.jmx</stringProp></ThreadGroup> <hashTree> - <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="Product Grid Mass Actions" enabled="true"> - <intProp name="ThroughputController.style">1</intProp> - <boolProp name="ThroughputController.perThread">false</boolProp> - <intProp name="ThroughputController.maxThroughput">1</intProp> - <stringProp name="ThroughputController.percentThroughput">${productGridMassActionPercentage}</stringProp> - <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> - <hashTree> - <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> - <stringProp name="script"> -var testLabel = "${testLabel}" ? " (${testLabel})" : ""; -if (testLabel - && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' -) { - if (sampler.getName().indexOf(testLabel) == -1) { - sampler.setName(sampler.getName() + testLabel); - } -} else if (sampler.getName().indexOf("SetUp - ") == -1) { - sampler.setName("SetUp - " + sampler.getName()); -} - </stringProp> - <stringProp name="scriptLanguage">javascript</stringProp> - <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> - <hashTree/> - <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> - <stringProp name="BeanShellSampler.query"> - vars.put("testLabel", "Product Grid Mass Actions"); - </stringProp> - <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> - </BeanShellSampler> - <hashTree/> - - <JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PostProcessor" testname="Get admin form key PostProcessor" enabled="true"> - <stringProp name="script"> - function getFormKeyFromResponse() - { - var url = prev.getUrlAsString(), - responseCode = prev.getResponseCode(), - formKey = null; - searchPattern = /var FORM_KEY = '(.+)'/; - if (responseCode == "200" && url) { - response = prev.getResponseDataAsString(); - formKey = response && response.match(searchPattern) ? response.match(searchPattern)[1] : null; - } - return formKey; - } - - formKey = vars.get("form_key_storage"); - - currentFormKey = getFormKeyFromResponse(); - - if (currentFormKey != null && currentFormKey != formKey) { - vars.put("form_key_storage", currentFormKey); - } - </stringProp> - <stringProp name="scriptLanguage">javascript</stringProp> - <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin/handle_admin_form_key.jmx</stringProp></JSR223PostProcessor> - <hashTree/> - <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set admin form key PreProcessor" enabled="true"> - <stringProp name="script"> - formKey = vars.get("form_key_storage"); - if (formKey - && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' - && sampler.getMethod() == "POST") - { - arguments = sampler.getArguments(); - for (i=0; i<arguments.getArgumentCount(); i++) - { - argument = arguments.getArgument(i); - if (argument.getName() == 'form_key' && argument.getValue() != formKey) { - log.info("admin form key updated: " + argument.getValue() + " => " + formKey); - argument.setValue(formKey); - } - } - } - </stringProp> - <stringProp name="scriptLanguage">javascript</stringProp> - </JSR223PreProcessor> - <hashTree/> - - <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true"> - <collectionProp name="CookieManager.cookies"/> - <boolProp name="CookieManager.clearEachIteration">false</boolProp> - <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/http_cookie_manager_without_clear_each_iteration.jmx</stringProp></CookieManager> - <hashTree/> - - <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Admin Login" enabled="true"> - <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> -</GenericController> - <hashTree> - <CriticalSectionController guiclass="CriticalSectionControllerGui" testclass="CriticalSectionController" testname="Admin Login Lock" enabled="true"> - <stringProp name="CriticalSectionController.lockName">get-admin-email</stringProp> - <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/lock_controller.jmx</stringProp></CriticalSectionController> - <hashTree> - - <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Get Admin Email" enabled="true"> - <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> - <stringProp name="BeanShellSampler.query"> -adminUserList = props.get("adminUserList"); -adminUserListIterator = props.get("adminUserListIterator"); -adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); - -if (adminUsersDistribution == 1) { - adminUser = adminUserList.poll(); -} else { - if (!adminUserListIterator.hasNext()) { - adminUserListIterator = adminUserList.descendingIterator(); - } - - adminUser = adminUserListIterator.next(); -} - -if (adminUser == null) { - SampleResult.setResponseMessage("adminUser list is empty"); - SampleResult.setResponseData("adminUser list is empty","UTF-8"); - IsSuccess=false; - SampleResult.setSuccessful(false); - SampleResult.setStopThread(true); -} -vars.put("admin_user", adminUser); - </stringProp> - <stringProp name="BeanShellSampler.filename"/> - <stringProp name="BeanShellSampler.parameters"/> - <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> - </BeanShellSampler> - <hashTree/> - </hashTree> - - <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login" enabled="true"> - <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> - <collectionProp name="Arguments.arguments"/> - </elementProp> - <stringProp name="HTTPSampler.domain"/> - <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> - <stringProp name="HTTPSampler.response_timeout">200000</stringProp> - <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> - <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> - <stringProp name="HTTPSampler.method">GET</stringProp> - <boolProp name="HTTPSampler.follow_redirects">true</boolProp> - <boolProp name="HTTPSampler.auto_redirects">false</boolProp> - <boolProp name="HTTPSampler.use_keepalive">true</boolProp> - <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> - <boolProp name="HTTPSampler.monitor">false</boolProp> - <stringProp name="HTTPSampler.embedded_url_re"/> - <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login.jmx</stringProp></HTTPSamplerProxy> - <hashTree> - <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert login form shown" enabled="true"> - <collectionProp name="Asserion.test_strings"> - <stringProp name="-1397214398">Welcome</stringProp> - <stringProp name="-515240035"><title>Magento Admin</title></stringProp> - </collectionProp> - <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> - <boolProp name="Assertion.assume_success">false</boolProp> - <intProp name="Assertion.test_type">2</intProp> - </ResponseAssertion> - <hashTree/> - <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> - <stringProp name="RegexExtractor.useHeaders">false</stringProp> - <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> - <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> - <stringProp name="RegexExtractor.template">$1$</stringProp> - <stringProp name="RegexExtractor.default"/> - <stringProp name="RegexExtractor.match_number">1</stringProp> - </RegexExtractor> - <hashTree/> - <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert form_key extracted" enabled="true"> - <collectionProp name="Asserion.test_strings"> - <stringProp name="2845929">^.+$</stringProp> - </collectionProp> - <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> - <boolProp name="Assertion.assume_success">false</boolProp> - <intProp name="Assertion.test_type">1</intProp> - <stringProp name="Assertion.scope">variable</stringProp> - <stringProp name="Scope.variable">admin_form_key</stringProp> - </ResponseAssertion> - <hashTree/> - </hashTree> - - <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login Submit Form" enabled="true"> - <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> - <collectionProp name="Arguments.arguments"> - <elementProp name="dummy" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value"/> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">dummy</stringProp> - </elementProp> - <elementProp name="form_key" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">${admin_form_key}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">form_key</stringProp> - </elementProp> - <elementProp name="login[password]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">${admin_password}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">login[password]</stringProp> - </elementProp> - <elementProp name="login[username]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">${admin_user}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">login[username]</stringProp> - </elementProp> - </collectionProp> - </elementProp> - <stringProp name="HTTPSampler.domain"/> - <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> - <stringProp name="HTTPSampler.response_timeout">200000</stringProp> - <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> - <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> - <stringProp name="HTTPSampler.method">POST</stringProp> - <boolProp name="HTTPSampler.follow_redirects">true</boolProp> - <boolProp name="HTTPSampler.auto_redirects">false</boolProp> - <boolProp name="HTTPSampler.use_keepalive">true</boolProp> - <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> - <stringProp name="HTTPSampler.implementation">Java</stringProp> - <boolProp name="HTTPSampler.monitor">false</boolProp> - <stringProp name="HTTPSampler.embedded_url_re"/> - <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login_submit_form.jmx</stringProp> - </HTTPSamplerProxy> - <hashTree> - <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> - <stringProp name="RegexExtractor.useHeaders">false</stringProp> - <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> - <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> - <stringProp name="RegexExtractor.template">$1$</stringProp> - <stringProp name="RegexExtractor.default"/> - <stringProp name="RegexExtractor.match_number">1</stringProp> - <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_retrieve_form_key.jmx</stringProp></RegexExtractor> - <hashTree/> - </hashTree> - </hashTree> - - <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Simple Controller" enabled="true"> - <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> -</GenericController> - <hashTree> - <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Get Product Pages Count" enabled="true"> - <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> - <collectionProp name="Arguments.arguments"> - <elementProp name="form_key" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">${admin_form_key}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">form_key</stringProp> - </elementProp> - <elementProp name="namespace" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">product_listing</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">namespace</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="search" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value"/> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">search</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="filters[placeholder]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">true</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">filters[placeholder]</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="paging[pageSize]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">20</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">paging[pageSize]</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="paging[current]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">1</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">paging[current]</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="sorting[field]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">entity_id</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">sorting[field]</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="sorting[direction]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">asc</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">sorting[direction]</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="isAjax" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">true</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">isAjax</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - </collectionProp> - </elementProp> - <stringProp name="HTTPSampler.domain"/> - <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> - <stringProp name="HTTPSampler.response_timeout">200000</stringProp> - <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> - <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> - <stringProp name="HTTPSampler.method">GET</stringProp> - <boolProp name="HTTPSampler.follow_redirects">true</boolProp> - <boolProp name="HTTPSampler.auto_redirects">false</boolProp> - <boolProp name="HTTPSampler.use_keepalive">true</boolProp> - <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> - <boolProp name="HTTPSampler.monitor">false</boolProp> - <stringProp name="HTTPSampler.embedded_url_re"/> - <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_browse_products_grid/get_product_pages_count.jmx</stringProp></HTTPSamplerProxy> - <hashTree> - <com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.JSONPathAssertion guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.gui.JSONPathAssertionGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.JSONPathAssertion" testname="Assert total records is not 0" enabled="true"> - <stringProp name="JSON_PATH">$.totalRecords</stringProp> - <stringProp name="EXPECTED_VALUE">0</stringProp> - <boolProp name="JSONVALIDATION">true</boolProp> - <boolProp name="EXPECT_NULL">false</boolProp> - <boolProp name="INVERT">true</boolProp> - </com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.JSONPathAssertion> - <hashTree/> - <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="Extract total records" enabled="true"> - <stringProp name="VAR">products_number</stringProp> - <stringProp name="JSONPATH">$.totalRecords</stringProp> - <stringProp name="DEFAULT"/> - <stringProp name="VARIABLE"/> - <stringProp name="SUBJECT">BODY</stringProp> - </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> - <hashTree/> - <BeanShellPostProcessor guiclass="TestBeanGUI" testclass="BeanShellPostProcessor" testname="Calculate pages count" enabled="true"> - <boolProp name="resetInterpreter">false</boolProp> - <stringProp name="parameters"/> - <stringProp name="filename"/> - <stringProp name="script">var productsPageSize = Integer.parseInt(vars.get("products_page_size")); -var productsTotal = Integer.parseInt(vars.get("products_number")); -var pageCountProducts = Math.round(productsTotal/productsPageSize); - -vars.put("pages_count_product", String.valueOf(pageCountProducts));</stringProp> - </BeanShellPostProcessor> - <hashTree/> - </hashTree> - - <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Arguments" enabled="true"> - <stringProp name="BeanShellSampler.query"> -import java.util.Random; -Random random = new Random(); -if (${seedForRandom} > 0) { -random.setSeed(${seedForRandom}); -} -var productsPageSize = Integer.parseInt(vars.get("products_page_size")); -var totalNumberOfPages = Integer.parseInt(vars.get("pages_count_product")); - -// Randomly select a page. -var randomProductsPage = random.nextInt(totalNumberOfPages) + 1; - -// Get the first and last product id on that page. -var lastProductIdOnPage = randomProductsPage * productsPageSize; -var firstProductIdOnPage = lastProductIdOnPage - productsPageSize + 1; - -var randomProductId1 = Math.floor(random.nextInt(productsPageSize)) + firstProductIdOnPage; -var randomProductId2 = Math.floor(random.nextInt(productsPageSize)) + firstProductIdOnPage; -var randomProductId3 = Math.floor(random.nextInt(productsPageSize)) + firstProductIdOnPage; - -vars.put("page_number", String.valueOf(randomProductsPage)); -vars.put("productId1", String.valueOf(randomProductId1)); -vars.put("productId2", String.valueOf(randomProductId2)); -vars.put("productId3", String.valueOf(randomProductId3)); - -var randomQuantity = random.nextInt(1000) + 1; -var randomPrice = random.nextInt(500) + 10; -var randomVisibility = random.nextInt(4) + 1; - -vars.put("quantity", String.valueOf(randomQuantity)); -vars.put("price", String.valueOf(randomPrice)); -vars.put("visibility", String.valueOf(randomVisibility)); - </stringProp> - <stringProp name="BeanShellSampler.filename"/> - <stringProp name="BeanShellSampler.parameters"/> - <boolProp name="BeanShellSampler.resetInterpreter">false</boolProp> - <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_browse_products_grid/products_grid_mass_actions/setup.jmx</stringProp></BeanShellSampler> - <hashTree/> - - <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Select Products and Update Attributes mass action" enabled="true"> - <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> - <collectionProp name="Arguments.arguments"> - <elementProp name="form_key" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">${admin_form_key}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">form_key</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="namespace" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">product_listing</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">namespace</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="search" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value"/> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">search</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="filters[placeholder]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">true</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">filters[placeholder]</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="paging[pageSize]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">${products_page_size}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">paging[pageSize]</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="paging[current]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">${page_number}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">paging[current]</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="sorting[field]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">entity_id</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">sorting[field]</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="sorting[direction]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">asc</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">sorting[direction]</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="isAjax" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">true</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">isAjax</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - </collectionProp> - </elementProp> - <stringProp name="HTTPSampler.domain"/> - <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> - <stringProp name="HTTPSampler.response_timeout">200000</stringProp> - <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> - <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> - <stringProp name="HTTPSampler.method">GET</stringProp> - <boolProp name="HTTPSampler.follow_redirects">true</boolProp> - <boolProp name="HTTPSampler.auto_redirects">false</boolProp> - <boolProp name="HTTPSampler.use_keepalive">true</boolProp> - <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> - <boolProp name="HTTPSampler.monitor">false</boolProp> - <stringProp name="HTTPSampler.embedded_url_re"/> - <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_browse_products_grid/products_grid_mass_actions/display_grid.jmx</stringProp></HTTPSamplerProxy> - <hashTree> - <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> - <collectionProp name="Asserion.test_strings"> - <stringProp name="1637639774">totalRecords</stringProp> - </collectionProp> - <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> - <boolProp name="Assertion.assume_success">false</boolProp> - <intProp name="Assertion.test_type">2</intProp> - </ResponseAssertion> - <hashTree/> - </hashTree> - - <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Display Update Attributes" enabled="true"> - <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> - <collectionProp name="Arguments.arguments"> - <elementProp name="selected[0]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">${productId1}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">selected[0]</stringProp> - </elementProp> - <elementProp name="selected[1]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">${productId2}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">selected[1]</stringProp> - </elementProp> - <elementProp name="selected[2]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">${productId3}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">selected[2]</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="filters[placeholder]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">true</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">filters[placeholder]</stringProp> - <stringProp name="Argument.desc">false</stringProp> - </elementProp> - <elementProp name="form_key" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">${admin_form_key}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">form_key</stringProp> - <stringProp name="Argument.desc">false</stringProp> - </elementProp> - <elementProp name="namespace" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">product_listing</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">namespace</stringProp> - <stringProp name="Argument.desc">false</stringProp> - </elementProp> - </collectionProp> - </elementProp> - <stringProp name="HTTPSampler.domain"/> - <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> - <stringProp name="HTTPSampler.response_timeout">200000</stringProp> - <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> - <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product_action_attribute/edit</stringProp> - <stringProp name="HTTPSampler.method">GET</stringProp> - <boolProp name="HTTPSampler.follow_redirects">true</boolProp> - <boolProp name="HTTPSampler.auto_redirects">false</boolProp> - <boolProp name="HTTPSampler.use_keepalive">true</boolProp> - <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> - <boolProp name="HTTPSampler.monitor">false</boolProp> - <stringProp name="HTTPSampler.embedded_url_re"/> - <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_browse_products_grid/products_grid_mass_actions/display_update_attributes.jmx</stringProp></HTTPSamplerProxy> - <hashTree> - <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> - <collectionProp name="Asserion.test_strings"> - <stringProp name="1862384910">Update Attributes</stringProp> - </collectionProp> - <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> - <boolProp name="Assertion.assume_success">false</boolProp> - <intProp name="Assertion.test_type">2</intProp> - </ResponseAssertion> - <hashTree/> - </hashTree> - - <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Validate Attributes" enabled="true"> - <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> - <collectionProp name="Arguments.arguments"> - <elementProp name="isAjax" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">true</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">isAjax</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="form_key" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">${admin_form_key}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">form_key</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="product[product_has_weight]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">1</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">product[product_has_weight]</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="product[use_config_gift_message_available]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">1</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">product[use_config_gift_message_available]</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="product[use_config_gift_wrapping_available]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">1</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">product[use_config_gift_wrapping_available]</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="inventory[qty]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">${quantity}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">inventory[qty]</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="attributes[price]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">${price}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">attributes[price]</stringProp> - </elementProp> - <elementProp name="attributes[visibility]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">${visibility}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">attributes[visibility]</stringProp> - </elementProp> - </collectionProp> - </elementProp> - <stringProp name="HTTPSampler.domain"/> - <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> - <stringProp name="HTTPSampler.response_timeout">200000</stringProp> - <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> - <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product_action_attribute/validate</stringProp> - <stringProp name="HTTPSampler.method">POST</stringProp> - <boolProp name="HTTPSampler.follow_redirects">true</boolProp> - <boolProp name="HTTPSampler.auto_redirects">false</boolProp> - <boolProp name="HTTPSampler.use_keepalive">true</boolProp> - <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> - <boolProp name="HTTPSampler.monitor">false</boolProp> - <stringProp name="HTTPSampler.embedded_url_re"/> - <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_browse_products_grid/products_grid_mass_actions/change_attributes.jmx</stringProp></HTTPSamplerProxy> - <hashTree> - <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> - <collectionProp name="Asserion.test_strings"> - <stringProp name="1853918323">{"error":false}</stringProp> - </collectionProp> - <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> - <boolProp name="Assertion.assume_success">false</boolProp> - <intProp name="Assertion.test_type">2</intProp> - </ResponseAssertion> - <hashTree/> - </hashTree> - <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Save Attributes" enabled="true"> - <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> - <collectionProp name="Arguments.arguments"> - <elementProp name="isAjax" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">true</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">isAjax</stringProp> - <stringProp name="Argument.desc">false</stringProp> - </elementProp> - <elementProp name="form_key" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">${admin_form_key}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">form_key</stringProp> - <stringProp name="Argument.desc">false</stringProp> - </elementProp> - <elementProp name="product[product_has_weight]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">1</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">product[product_has_weight]</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="product[use_config_gift_message_available]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">1</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">product[use_config_gift_message_available]</stringProp> - </elementProp> - <elementProp name="product[use_config_gift_wrapping_available]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">1</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">product[use_config_gift_wrapping_available]</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="inventory[qty]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">${quantity}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">inventory[qty]</stringProp> - </elementProp> - <elementProp name="toggle_qty" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">on</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">toggle_price</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="attributes[price]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">${price}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">attributes[price]</stringProp> - </elementProp> - <elementProp name="toggle_price" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">on</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">toggle_price</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - <elementProp name="attributes[visibility]" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">${visibility}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">attributes[visibility]</stringProp> - </elementProp> - <elementProp name="toggle_visibility" elementType="HTTPArgument"> - <boolProp name="HTTPArgument.always_encode">true</boolProp> - <stringProp name="Argument.value">on</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - <boolProp name="HTTPArgument.use_equals">true</boolProp> - <stringProp name="Argument.name">toggle_visibility</stringProp> - <stringProp name="Argument.desc">true</stringProp> - </elementProp> - </collectionProp> - </elementProp> - <stringProp name="HTTPSampler.domain"/> - <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> - <stringProp name="HTTPSampler.response_timeout">200000</stringProp> - <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> - <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product_action_attribute/save/store/0/active_tab/attributes</stringProp> - <stringProp name="HTTPSampler.method">POST</stringProp> - <boolProp name="HTTPSampler.follow_redirects">true</boolProp> - <boolProp name="HTTPSampler.auto_redirects">false</boolProp> - <boolProp name="HTTPSampler.use_keepalive">true</boolProp> - <boolProp name="HTTPSampler.DO_MULTIPART_POST">true</boolProp> - <boolProp name="HTTPSampler.monitor">false</boolProp> - <stringProp name="HTTPSampler.embedded_url_re"/> - </HTTPSamplerProxy> - <hashTree> - <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> - <collectionProp name="Asserion.test_strings"> - <stringProp name="1848809106">were updated.</stringProp> - </collectionProp> - <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> - <boolProp name="Assertion.assume_success">false</boolProp> - <intProp name="Assertion.test_type">2</intProp> - </ResponseAssertion> - <hashTree/> - </hashTree> -</hashTree> - - <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Logout" enabled="true"> - <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> - <collectionProp name="Arguments.arguments"/> - </elementProp> - <stringProp name="HTTPSampler.domain"/> - <stringProp name="HTTPSampler.port"/> - <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> - <stringProp name="HTTPSampler.response_timeout">200000</stringProp> - <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> - <stringProp name="HTTPSampler.contentEncoding"/> - <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> - <stringProp name="HTTPSampler.method">GET</stringProp> - <boolProp name="HTTPSampler.follow_redirects">true</boolProp> - <boolProp name="HTTPSampler.auto_redirects">false</boolProp> - <boolProp name="HTTPSampler.use_keepalive">true</boolProp> - <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> - <boolProp name="HTTPSampler.monitor">false</boolProp> - <stringProp name="HTTPSampler.embedded_url_re"/> - <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/setup/admin_logout.jmx</stringProp></HTTPSamplerProxy> - <hashTree> - - <BeanShellPostProcessor guiclass="TestBeanGUI" testclass="BeanShellPostProcessor" testname="Return Admin to Pool" enabled="true"> - <boolProp name="resetInterpreter">false</boolProp> - <stringProp name="parameters"/> - <stringProp name="filename"/> - <stringProp name="script"> - adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); - if (adminUsersDistribution == 1) { - adminUserList = props.get("adminUserList"); - adminUserList.add(vars.get("admin_user")); - } - </stringProp> - <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> - <hashTree/> - </hashTree> - </hashTree> - - <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="Import Products" enabled="true"> <intProp name="ThroughputController.style">1</intProp> <boolProp name="ThroughputController.perThread">false</boolProp> From 5e4adb83c6ad5f1cfd41551cf3d55a4ffc22bb18 Mon Sep 17 00:00:00 2001 From: AleksLi <aleksliwork@gmail.com> Date: Thu, 7 Mar 2019 00:28:00 +0100 Subject: [PATCH 101/265] GraphQL-432: Created separate class GetMaskedQuoteIdByReversedQuoteId and replaced method in one api-functional test --- .../GetMaskedQuoteIdByReversedQuoteId.php | 58 +++++++++++++++++++ .../Quote/SetShippingAddressOnCartTest.php | 30 +++++----- 2 files changed, 72 insertions(+), 16 deletions(-) create mode 100644 app/code/Magento/QuoteGraphQl/Model/GetMaskedQuoteIdByReversedQuoteId.php diff --git a/app/code/Magento/QuoteGraphQl/Model/GetMaskedQuoteIdByReversedQuoteId.php b/app/code/Magento/QuoteGraphQl/Model/GetMaskedQuoteIdByReversedQuoteId.php new file mode 100644 index 000000000000..60466170fc56 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/GetMaskedQuoteIdByReversedQuoteId.php @@ -0,0 +1,58 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model; + +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; +use Magento\Quote\Model\QuoteFactory; + +class GetMaskedQuoteIdByReversedQuoteId +{ + /** + * @var QuoteFactory + */ + private $quoteFactory; + + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedId; + + /** + * @param QuoteFactory $quoteFactory + * @param QuoteResource $quoteResource + * @param QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedId + */ + public function __construct( + QuoteFactory $quoteFactory, + QuoteResource $quoteResource, + QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedId + ) { + $this->quoteFactory = $quoteFactory; + $this->quoteResource = $quoteResource; + $this->quoteIdToMaskedId = $quoteIdToMaskedId; + } + + /** + * @param string $reversedQuoteId + * @return string + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function execute(string $reversedQuoteId): string + { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + + return $this->quoteIdToMaskedId->execute((int)$quote->getId()); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php index 4916bb2aa78c..9352111de854 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php @@ -13,6 +13,7 @@ use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\QuoteGraphQl\Model\GetMaskedQuoteIdByReversedQuoteId; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; use Magento\TestFramework\ObjectManager; @@ -37,6 +38,11 @@ class SetShippingAddressOnCartTest extends GraphQlAbstract */ private $quoteIdToMaskedId; + /** + * @var GetMaskedQuoteIdByReversedQuoteId + */ + private $getMaskedQuoteIdByReversedQuoteId; + /** * @var CustomerTokenServiceInterface */ @@ -47,16 +53,20 @@ protected function setUp() $objectManager = Bootstrap::getObjectManager(); $this->quoteResource = $objectManager->get(QuoteResource::class); $this->quoteFactory = $objectManager->get(QuoteFactory::class); + $this->getMaskedQuoteIdByReversedQuoteId = $objectManager->get(GetMaskedQuoteIdByReversedQuoteId::class); $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); } /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * + * @throws \Exception + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testSetNewShippingAddressByGuest() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_with_simple_product_without_address'); $query = <<<QUERY mutation { @@ -116,7 +126,7 @@ public function testSetNewShippingAddressByGuest() */ public function testSetShippingAddressFromAddressBookByGuest() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_with_simple_product_without_address'); $query = <<<QUERY mutation { @@ -284,7 +294,7 @@ public function testSetNotExistedShippingAddressFromAddressBook() */ public function testSetShippingAddressWithoutAddresses() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_with_simple_product_without_address'); $query = <<<QUERY mutation { @@ -361,7 +371,7 @@ public function testSetNewShippingAddressAndFromAddressBookAtSameTime() */ public function testSetMultipleNewShippingAddresses() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_with_simple_product_without_address'); $query = <<<QUERY mutation { @@ -512,18 +522,6 @@ private function getHeaderMap(string $username = 'customer@example.com', string return $headerMap; } - /** - * @param string $reversedQuoteId - * @return string - */ - private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string - { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); - - return $this->quoteIdToMaskedId->execute((int)$quote->getId()); - } - /** * @param string $reversedQuoteId * @param int $customerId From ab061382a5320bcc486b4a33aa50bad1aa930c70 Mon Sep 17 00:00:00 2001 From: Andrii Lugovyi <alugovyi@adobe.com> Date: Wed, 6 Mar 2019 18:10:45 -0600 Subject: [PATCH 102/265] MC-13613: Product mass update - MC-5665 Execution of heavy admin operations in asynchronous flow part2 --- .../Product/Action/Attribute/Save.php | 40 ++++++++++++++----- .../Model/Attribute/Backend/Consumer.php | 6 +-- .../Backend/ConsumerWebsiteAssign.php | 6 +-- 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php index f0b19b2073cc..540999147753 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php @@ -161,20 +161,38 @@ private function sanitizeProductAttributes($attributesData) */ private function publish($attributesData, $websiteRemoveData, $websiteAddData, $storeId, $websiteId, $productIds):void { + $productIdsChunks = array_chunk($productIds, 100); $bulkUuid = $this->identityService->generateId(); - $bulkDescription = __('Update attributes to selected products'); + $bulkDescription = __('Update attributes to ' . count($productIds) . ' selected products'); $operations = []; - if ($websiteRemoveData || $websiteAddData) { - $dataToUpdate = [ - 'website_assign' => $websiteAddData, - 'website_detach' => $websiteRemoveData - ]; - - $operations[] = $this->makeOperation('Update website assign', 'product_action_attribute.website.update', $dataToUpdate, $storeId, $websiteId, $productIds, $bulkUuid); - } + foreach($productIdsChunks as $productIdsChunk) { + if ($websiteRemoveData || $websiteAddData) { + $dataToUpdate = [ + 'website_assign' => $websiteAddData, + 'website_detach' => $websiteRemoveData + ]; + $operations[] = $this->makeOperation( + 'Update website assign', + 'product_action_attribute.website.update', + $dataToUpdate, + $storeId, + $websiteId, + $productIdsChunk, + $bulkUuid + ); + } - if ($attributesData) { - $operations[] = $this->makeOperation('Update product attributes', 'product_action_attribute.update', $attributesData, $storeId, $websiteId, $productIds, $bulkUuid); + if ($attributesData) { + $operations[] = $this->makeOperation( + 'Update product attributes', + 'product_action_attribute.update', + $attributesData, + $storeId, + $websiteId, + $productIdsChunk, + $bulkUuid + ); + } } if (!empty($operations)) { diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php b/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php index 7e6c68a1502e..f9c90f82dacd 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php @@ -99,9 +99,7 @@ public function process(\Magento\AsynchronousOperations\Api\Data\OperationInterf $this->execute($data); } catch (\Zend_Db_Adapter_Exception $e) { - //here sample how to process exceptions if they occurred $this->logger->critical($e->getMessage()); - //you can add here your own type of exception when operation can be retried if ( $e instanceof \Magento\Framework\DB\Adapter\LockWaitException || $e instanceof \Magento\Framework\DB\Adapter\DeadlockException @@ -113,7 +111,7 @@ public function process(\Magento\AsynchronousOperations\Api\Data\OperationInterf } else { $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; $errorCode = $e->getCode(); - $message = __('Sorry, something went wrong during product prices update. Please see log for details.'); + $message = __('Sorry, something went wrong during product attributes update. Please see log for details.'); } } catch (NoSuchEntityException $e) { $this->logger->critical($e->getMessage()); @@ -131,7 +129,7 @@ public function process(\Magento\AsynchronousOperations\Api\Data\OperationInterf $this->logger->critical($e->getMessage()); $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; $errorCode = $e->getCode(); - $message = __('Sorry, something went wrong during product prices update. Please see log for details.'); + $message = __('Sorry, something went wrong during product attributes update. Please see log for details.'); } $operation->setStatus($status ?? OperationInterface::STATUS_TYPE_COMPLETE) diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php b/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php index f8cc3c04df60..591c0892b422 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php @@ -91,9 +91,7 @@ public function process(\Magento\AsynchronousOperations\Api\Data\OperationInterf $data = $this->serializer->unserialize($serializedData); $this->execute($data); } catch (\Zend_Db_Adapter_Exception $e) { - //here sample how to process exceptions if they occurred $this->logger->critical($e->getMessage()); - //you can add here your own type of exception when operation can be retried if ( $e instanceof \Magento\Framework\DB\Adapter\LockWaitException || $e instanceof \Magento\Framework\DB\Adapter\DeadlockException @@ -105,7 +103,7 @@ public function process(\Magento\AsynchronousOperations\Api\Data\OperationInterf } else { $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; $errorCode = $e->getCode(); - $message = __('Sorry, something went wrong during product prices update. Please see log for details.'); + $message = __('Sorry, something went wrong during product attributes update. Please see log for details.'); } } catch (NoSuchEntityException $e) { $this->logger->critical($e->getMessage()); @@ -123,7 +121,7 @@ public function process(\Magento\AsynchronousOperations\Api\Data\OperationInterf $this->logger->critical($e->getMessage()); $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; $errorCode = $e->getCode(); - $message = __('Sorry, something went wrong during product prices update. Please see log for details.'); + $message = __('Sorry, something went wrong during product attributes update. Please see log for details.'); } $operation->setStatus($status ?? OperationInterface::STATUS_TYPE_COMPLETE) From ae9084de11bf2a59d2540d2ba6769b9ef1d46b8d Mon Sep 17 00:00:00 2001 From: vgelani <vishalgelani99@gmail.com> Date: Thu, 7 Mar 2019 17:18:28 +0530 Subject: [PATCH 103/265] 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 @@ </resource> <resource id="Magento_Backend::marketing"> <resource id="Magento_Backend::marketing_user_content"> - <resource id="Magento_Review::reviews_all" title="Reviews" translate="title" sortOrder="10"/> - <resource id="Magento_Review::pending" title="Pending Reviews" translate="title" sortOrder="20"/> + <resource id="Magento_Review::pending" title="Pending Reviews" translate="title" sortOrder="10"/> + <resource id="Magento_Review::reviews_all" title="All Reviews" translate="title" sortOrder="20"/> </resource> </resource> </resource> 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 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Backend:etc/menu.xsd"> <menu> <add id="Magento_Review::catalog_reviews_ratings_ratings" title="Rating" translate="title" module="Magento_Review" sortOrder="60" parent="Magento_Backend::stores_attributes" action="review/rating/" resource="Magento_Review::ratings"/> - <add id="Magento_Review::catalog_reviews_ratings_reviews_all" title="Reviews" translate="title" module="Magento_Review" parent="Magento_Backend::marketing_user_content" sortOrder="10" action="review/product/index" resource="Magento_Review::reviews_all"/> - <add id="Magento_Review::catalog_reviews_ratings_pending" title="Pending Reviews" translate="title" module="Magento_Review" parent="Magento_Backend::marketing_user_content" sortOrder="20" action="review/product/pending" resource="Magento_Review::pending"/> + <add id="Magento_Review::catalog_reviews_ratings_pending" title="Pending Reviews" translate="title" module="Magento_Review" parent="Magento_Backend::marketing_user_content" sortOrder="10" action="review/product/pending" resource="Magento_Review::pending"/> + <add id="Magento_Review::catalog_reviews_ratings_reviews_all" title="All Reviews" translate="title" module="Magento_Review" parent="Magento_Backend::marketing_user_content" sortOrder="20" action="review/product/index" resource="Magento_Review::reviews_all"/> <add id="Magento_Review::report_review" title="Reviews" translate="title" module="Magento_Reports" sortOrder="20" parent="Magento_Reports::report" resource="Magento_Reports::review"/> <add id="Magento_Review::report_review_customer" title="By Customers" translate="title" sortOrder="10" module="Magento_Review" parent="Magento_Review::report_review" action="reports/report_review/customer" resource="Magento_Reports::review_customer"/> <add id="Magento_Review::report_review_product" title="By Products" translate="title" sortOrder="20" module="Magento_Review" parent="Magento_Review::report_review" action="reports/report_review/product" resource="Magento_Reports::review_product"/> From 3ab52fff4ea2d3d027a51487c89a1ba1fb6a45ef Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Thu, 7 Mar 2019 11:24:08 -0600 Subject: [PATCH 104/265] MC-4455: Convert CreateDuplicateUrlProductEntity to MFTF - Refactor to decompose an action group that isnt reusable --- .../ActionGroup/AdminProductActionGroup.xml | 69 +++++++++++-------- .../Test/AdminCreateDuplicateProductTest.xml | 30 ++++++-- 2 files changed, 64 insertions(+), 35 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index 709f74ede09a..4b4633b374e6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -20,7 +20,7 @@ <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Product" stepKey="seeNewProductTitle"/> </actionGroup> - <!--Fill main fields in create product form--> + <!-- Fill main fields in create product form using a product entity --> <actionGroup name="fillMainProductForm"> <arguments> <argument name="product" defaultValue="_defaultProduct"/> @@ -34,6 +34,25 @@ <fillField selector="{{AdminProductFormSection.productWeight}}" userInput="{{product.weight}}" stepKey="fillProductWeight"/> </actionGroup> + <!-- Fill main fields in create product form using strings for flexibility --> + <actionGroup name="FillMainProductFormByString"> + <arguments> + <argument name="productName" type="string"/> + <argument name="productSku" type="string"/> + <argument name="productPrice" type="string"/> + <argument name="productQuantity" type="string"/> + <argument name="productStatus" type="string"/> + <argument name="productWeight" type="string"/> + </arguments> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{productName}}" stepKey="fillProductName"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{productSku}}" stepKey="fillProductSku"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{productPrice}}" stepKey="fillProductPrice"/> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{productQuantity}}" stepKey="fillProductQty"/> + <selectOption selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{productStatus}}" stepKey="selectStockStatus"/> + <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeight"/> + <fillField selector="{{AdminProductFormSection.productWeight}}" userInput="{{productWeight}}" stepKey="fillProductWeight"/> + </actionGroup> + <!--Fill main fields in create product form with no weight, useful for virtual and downloadable products --> <actionGroup name="fillMainProductFormNoWeight"> <arguments> @@ -77,6 +96,11 @@ <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="You saved the product." stepKey="seeSaveConfirmation"/> </actionGroup> + <!-- Save product but do not expect a success message --> + <actionGroup name="SaveProductFormNoSuccessCheck" extends="saveProductForm"> + <remove keyForRemoval="seeSaveConfirmation"/> + </actionGroup> + <!--Upload image for product--> <actionGroup name="addProductImage"> <arguments> @@ -387,6 +411,22 @@ <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> <fillField userInput="{{product.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> </actionGroup> + + <actionGroup name="SetProductUrlKeyByString"> + <arguments> + <argument name="urlKey" type="string"/> + </arguments> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> + <fillField userInput="{{urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> + </actionGroup> + + <actionGroup name="SetCategoryByName"> + <arguments> + <argument name="categoryName" type="string"/> + </arguments> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{categoryName}}]" stepKey="searchAndSelectCategory"/> + </actionGroup> + <actionGroup name="expandAdminProductSection"> <arguments> <argument name="sectionSelector" defaultValue="{{AdminProductContentSection.sectionHeader}}" type="string"/> @@ -430,31 +470,4 @@ <click selector="{{AdminAddUpSellProductsModalSection.AddSelectedProductsButton}}" stepKey="addRelatedProductSelected"/> <waitForPageLoad stepKey="waitForPageToLoad1"/> </actionGroup> - <actionGroup name="adminFillAndSaveProductForm"> - <arguments> - <argument name="product" defaultValue="defaultSimpleProduct"/> - <argument name="productName" type="string"/> - <argument name="productUrlKey" type="string"/> - <argument name="categoryName" type="string"/> - </arguments> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="openProductIndexPage"/> - <waitForPageLoad stepKey="waitForProductIndexPageToLoad"/> - <click selector="{{AdminProductGridActionSection.addProductBtn}}" stepKey="clickOnAddNewProduct"/> - <waitForPageLoad stepKey="waitForPageToLoad"/> - <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{productName}}" stepKey="fillProductName"/> - <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{product.sku}}" stepKey="fillProductSku"/> - <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{product.price}}" stepKey="fillProductPrice"/> - <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{product.quantity}}" stepKey="fillProductQty"/> - <selectOption selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{product.status}}" stepKey="selectStockStatus"/> - <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeight"/> - <fillField selector="{{AdminProductFormSection.productWeight}}" userInput="{{product.weight}}" stepKey="fillProductWeight"/> - <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{categoryName}}]" stepKey="searchAndSelectCategory"/> - <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="scrollToSectionHeader"/> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> - <fillField userInput="{{productUrlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> - <scrollToTopOfPage stepKey="scrollTopPageProduct"/> - <waitForElementVisible selector="{{AdminProductFormActionSection.saveButton}}" stepKey="waitForSaveProductButton" /> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> - <waitForPageLoad stepKey="waitForProductToSave"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml index 68dfc44aa886..575bb56912b2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml @@ -18,28 +18,44 @@ </annotations> <before> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> <createData entity="SubCategory" stepKey="category"/> <createData entity="Two_nested_categories" stepKey="subCategory"> <requiredEntity createDataKey="category"/> </createData> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> </before> + <after> <deleteData createDataKey="subCategory" stepKey="deleteSubCategory"/> <deleteData createDataKey="category" stepKey="deleteCategory"/> <actionGroup ref="logout" stepKey="logout"/> </after> - <!-- Create Product using above created Subcategory Name and SubCategory UrlKey and Save the Product --> - <actionGroup ref="adminFillAndSaveProductForm" stepKey="createDuplicateProduct"> - <argument name="product" value="defaultSimpleProduct"/> + <!-- Go to new simple product page --> + <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="goToCreateProductPage"/> + + <!-- Fill the main fields in the form --> + <actionGroup ref="FillMainProductFormByString" stepKey="fillMainProductForm"> <argument name="productName" value="$$subCategory.name$$"/> - <argument name="productUrlKey" value="$$subCategory.custom_attributes[url_key]$$"/> + <argument name="productSku" value="{{defaultSimpleProduct.sku}}"/> + <argument name="productPrice" value="{{defaultSimpleProduct.price}}"/> + <argument name="productQuantity" value="{{defaultSimpleProduct.quantity}}"/> + <argument name="productStatus" value="{{defaultSimpleProduct.status}}"/> + <argument name="productWeight" value="{{defaultSimpleProduct.weight}}"/> + </actionGroup> + + <!-- Select the category that we created in the before block --> + <actionGroup ref="SetCategoryByName" stepKey="setCategory"> <argument name="categoryName" value="$$category.name$$"/> </actionGroup> - <!--Assert Error Message --> + <!-- Set the url key to match the subcategory created in the before block --> + <actionGroup ref="SetProductUrlKeyByString" stepKey="fillUrlKey"> + <argument name="urlKey" value="$$subCategory.custom_attributes[url_key]$$"/> + </actionGroup> + + <!-- Save the product and expect to see an error message --> + <actionGroup ref="SaveProductFormNoSuccessCheck" stepKey="tryToSaveProduct"/> <see selector="{{AdminProductFormSection.successMessage}}" userInput="The value specified in the URL Key field would generate a URL that already exists." stepKey="seeErrorMessage"/> </test> </tests> From 5b4944d435525b0a88009a4f718986d322591782 Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Thu, 7 Mar 2019 16:17:22 -0600 Subject: [PATCH 105/265] MC-13613: Product mass update --- .../CatalogInventory/Plugin/MassUpdateProductAttribute.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php b/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php index 36958a37c6d1..e565f609611c 100644 --- a/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php +++ b/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php @@ -109,7 +109,9 @@ public function aroundExecute(Save $subject, callable $proceed) $websiteId = $this->storeManager->getStore($storeId)->getWebsiteId(); $productIds = $this->session->getData('product_ids'); $inventoryData = $this->addConfigSettings($inventoryData); - $this->updateInventoryInProducts($productIds, $websiteId, $inventoryData); + if (!empty($inventoryData)) { + $this->updateInventoryInProducts($productIds, $websiteId, $inventoryData); + } return $proceed(); } catch (\Magento\Framework\Exception\LocalizedException $e) { From afa0320e4dd40dd61ba49205dbf56a94b4f018e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Fri, 8 Mar 2019 13:48:06 +0200 Subject: [PATCH 106/265] remove refactored code not needed with correct html classes --- .../css/source/module/checkout/_fields.less | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_fields.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_fields.less index 4479c070a4e1..8dec680b5872 100644 --- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_fields.less +++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_fields.less @@ -55,31 +55,3 @@ } } } - -// -// Desktop -// _____________________________________________ - -.media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) { - // ToDo UI: remove with global blank theme .field.required update - .opc-wrapper { - .fieldset { - > .field { - &.required, - &._required { - position: relative; - - > label { - padding-right: 25px; - - &:after { - margin-left: @indent__s; - position: absolute; - top: 9px; - } - } - } - } - } - } -} From 92d93f5433b2967afeeda07624d11afe29787031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Fri, 8 Mar 2019 13:52:26 +0200 Subject: [PATCH 107/265] fix asterisk added correct class for asterisk --- .../frontend/web/template/checkout/checkout-agreements.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html b/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html index a448537d64e8..db9da71ebf73 100644 --- a/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html +++ b/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html @@ -5,17 +5,17 @@ */ --> <div data-role="checkout-agreements"> - <div class="checkout-agreements" data-bind="visible: isVisible"> + <div class="checkout-agreements fieldset" data-bind="visible: isVisible"> <!-- ko foreach: agreements --> <!-- ko if: ($parent.isAgreementRequired($data)) --> - <div class="checkout-agreement required"> + <div class="checkout-agreement field required"> <input type="checkbox" class="required-entry" data-bind="attr: { 'id': $parent.getCheckboxId($parentContext, agreementId), 'name': 'agreement[' + agreementId + ']', 'value': agreementId }"/> - <label data-bind="attr: {'for': $parent.getCheckboxId($parentContext, agreementId)}"> + <label class="label" data-bind="attr: {'for': $parent.getCheckboxId($parentContext, agreementId)}"> <button type="button" class="action action-show" data-bind="click: function(data, event) { return $parent.showContent(data, event) }" From 95653906830a6dfaf503400d3e057fcf03d1e3cf Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Fri, 8 Mar 2019 09:16:32 -0600 Subject: [PATCH 108/265] MC-13613: Product mass update --- .../Product/Action/Attribute/Save.php | 45 +++++++++++++------ .../Api/Data/PoisonPillInterface.php | 2 + .../Api/PoisonPillCompareInterface.php | 2 +- .../MessageQueue/Model/CallbackInvoker.php | 7 ++- .../Magento/MessageQueue/Model/PoisonPill.php | 2 +- .../MessageQueue/Model/PoisonPillCompare.php | 5 ++- .../MessageQueue/Model/PoisonPillFactory.php | 2 +- .../Model/ResourceModel/PoisonPill.php | 2 +- .../MessageQueue/CallbackInvokerInterface.php | 3 ++ 9 files changed, 49 insertions(+), 21 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php index 540999147753..9a6893a1aff8 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php @@ -10,7 +10,6 @@ use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Backend\App\Action; use Magento\Framework\Stdlib\DateTime\TimezoneInterface; -use Magento\Framework\App\ObjectManager; /** * Class Save @@ -42,11 +41,6 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribut */ private $userContext; - /** - * @var ObjectManager - */ - private $objectManager; - /** * @param Action\Context $context * @param \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeHelper @@ -71,13 +65,12 @@ public function __construct( $this->identityService = $identityService; $this->serializer = $serializer; $this->userContext = $userContext; - $this->objectManager = ObjectManager::getInstance(); } /** * Update product attributes * - * @return \Magento\Backend\Model\View\Result\Redirect + * @return \Magento\Framework\Controller\Result\Redirect */ public function execute() { @@ -89,6 +82,7 @@ public function execute() $attributesData = $this->getRequest()->getParam('attributes', []); $websiteRemoveData = $this->getRequest()->getParam('remove_website_ids', []); $websiteAddData = $this->getRequest()->getParam('add_website_ids', []); + $storeId = $this->attributeHelper->getSelectedStoreId(); $websiteId = $this->attributeHelper->getStoreWebsiteId($storeId); $productIds = $this->attributeHelper->getProductIds(); @@ -110,10 +104,17 @@ public function execute() return $this->resultRedirectFactory->create()->setPath('catalog/product/', ['store' => $storeId]); } + /** + * Sanitize product attributes + * + * @param $attributesData + * + * @return array + */ private function sanitizeProductAttributes($attributesData) { - $dateFormat = $this->objectManager->get(TimezoneInterface::class)->getDateFormat(\IntlDateFormatter::SHORT); - $config = $this->objectManager->get(\Magento\Eav\Model\Config::class); + $dateFormat = $this->_objectManager->get(TimezoneInterface::class)->getDateFormat(\IntlDateFormatter::SHORT); + $config = $this->_objectManager->get(\Magento\Eav\Model\Config::class); foreach ($attributesData as $attributeCode => $value) { $attribute = $config->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $attributeCode); @@ -149,7 +150,7 @@ private function sanitizeProductAttributes($attributesData) } /** - * Schedule new bulk. + * Schedule new bulk * * @param $attributesData * @param $websiteRemoveData @@ -157,6 +158,8 @@ private function sanitizeProductAttributes($attributesData) * @param $storeId * @param $websiteId * @param $productIds + * @throws \Magento\Framework\Exception\LocalizedException + * * @return void */ private function publish($attributesData, $websiteRemoveData, $websiteAddData, $storeId, $websiteId, $productIds):void @@ -196,7 +199,12 @@ private function publish($attributesData, $websiteRemoveData, $websiteAddData, $ } if (!empty($operations)) { - $result = $this->bulkManagement->scheduleBulk($bulkUuid, $operations, $bulkDescription, $this->userContext->getUserId()); + $result = $this->bulkManagement->scheduleBulk( + $bulkUuid, + $operations, + $bulkDescription, + $this->userContext->getUserId() + ); if (!$result) { throw new \Magento\Framework\Exception\LocalizedException( __('Something went wrong while processing the request.') @@ -206,6 +214,7 @@ private function publish($attributesData, $websiteRemoveData, $websiteAddData, $ } /** + * Make asynchronous operation * @param $meta * @param $queue * @param $dataToUpdate @@ -213,10 +222,18 @@ private function publish($attributesData, $websiteRemoveData, $websiteAddData, $ * @param $websiteId * @param $productIds * @param $bulkUuid + * * @return OperationInterface */ - private function makeOperation($meta, $queue, $dataToUpdate, $storeId, $websiteId, $productIds, $bulkUuid): OperationInterface - { + private function makeOperation( + $meta, + $queue, + $dataToUpdate, + $storeId, + $websiteId, + $productIds, + $bulkUuid + ): OperationInterface { $dataToEncode = [ 'meta_information' => $meta, 'product_ids' => $productIds, diff --git a/app/code/Magento/MessageQueue/Api/Data/PoisonPillInterface.php b/app/code/Magento/MessageQueue/Api/Data/PoisonPillInterface.php index f526e0d4ea06..b48d6d585492 100644 --- a/app/code/Magento/MessageQueue/Api/Data/PoisonPillInterface.php +++ b/app/code/Magento/MessageQueue/Api/Data/PoisonPillInterface.php @@ -22,6 +22,8 @@ interface PoisonPillInterface public function getVersion(): ?int; /** + * Set version of poison pill. + * * @param int $version * @return void */ diff --git a/app/code/Magento/MessageQueue/Api/PoisonPillCompareInterface.php b/app/code/Magento/MessageQueue/Api/PoisonPillCompareInterface.php index 32d57353f442..e2df3d69ca0a 100644 --- a/app/code/Magento/MessageQueue/Api/PoisonPillCompareInterface.php +++ b/app/code/Magento/MessageQueue/Api/PoisonPillCompareInterface.php @@ -17,7 +17,7 @@ interface PoisonPillCompareInterface { /** - * Check is version of current poison pill is latest. + * Check if version of current poison pill is latest. * * @param PoisonPillInterface $poisonPill * @return bool diff --git a/app/code/Magento/MessageQueue/Model/CallbackInvoker.php b/app/code/Magento/MessageQueue/Model/CallbackInvoker.php index 465c2d366c58..a61a6862843b 100644 --- a/app/code/Magento/MessageQueue/Model/CallbackInvoker.php +++ b/app/code/Magento/MessageQueue/Model/CallbackInvoker.php @@ -1,7 +1,7 @@ <?php /** * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * See COPYING.txt for license details. */ declare(strict_types=1); @@ -13,6 +13,9 @@ use Magento\MessageQueue\Api\PoisonPillCompareInterface; use Magento\MessageQueue\Api\PoisonPillReadInterface; +/** + * Callback invoker + */ class CallbackInvoker implements CallbackInvokerInterface { /** @@ -39,7 +42,6 @@ public function __construct( PoisonPillCompareInterface $poisonPillCompare ) { $this->poisonPillRead = $poisonPillRead; - $this->poisonPill = $poisonPillRead->getLatest(); $this->poisonPillCompare = $poisonPillCompare; } @@ -48,6 +50,7 @@ public function __construct( */ public function invoke(QueueInterface $queue, $maxNumberOfMessages, $callback) { + $this->poisonPill = $this->poisonPillRead->getLatest(); for ($i = $maxNumberOfMessages; $i > 0; $i--) { do { $message = $queue->dequeue(); diff --git a/app/code/Magento/MessageQueue/Model/PoisonPill.php b/app/code/Magento/MessageQueue/Model/PoisonPill.php index a253ca4c13aa..ac70034a85da 100644 --- a/app/code/Magento/MessageQueue/Model/PoisonPill.php +++ b/app/code/Magento/MessageQueue/Model/PoisonPill.php @@ -1,7 +1,7 @@ <?php /** * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * See COPYING.txt for license details. */ declare(strict_types=1); diff --git a/app/code/Magento/MessageQueue/Model/PoisonPillCompare.php b/app/code/Magento/MessageQueue/Model/PoisonPillCompare.php index 4aa76b4fadfc..615552628755 100644 --- a/app/code/Magento/MessageQueue/Model/PoisonPillCompare.php +++ b/app/code/Magento/MessageQueue/Model/PoisonPillCompare.php @@ -1,7 +1,7 @@ <?php /** * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * See COPYING.txt for license details. */ declare(strict_types=1); @@ -11,6 +11,9 @@ use Magento\MessageQueue\Api\PoisonPillCompareInterface; use Magento\MessageQueue\Api\PoisonPillReadInterface; +/** + * Poison pill compare + */ class PoisonPillCompare implements PoisonPillCompareInterface { /** diff --git a/app/code/Magento/MessageQueue/Model/PoisonPillFactory.php b/app/code/Magento/MessageQueue/Model/PoisonPillFactory.php index 0fd85c998866..0f2857fd6ec5 100644 --- a/app/code/Magento/MessageQueue/Model/PoisonPillFactory.php +++ b/app/code/Magento/MessageQueue/Model/PoisonPillFactory.php @@ -1,7 +1,7 @@ <?php /** * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * See COPYING.txt for license details. */ declare(strict_types=1); diff --git a/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php b/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php index 769f723dc9d5..dab251ce50c8 100644 --- a/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php +++ b/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php @@ -1,7 +1,7 @@ <?php /** * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. + * See COPYING.txt for license details. */ declare(strict_types=1); diff --git a/lib/internal/Magento/Framework/MessageQueue/CallbackInvokerInterface.php b/lib/internal/Magento/Framework/MessageQueue/CallbackInvokerInterface.php index bae72d8186a1..36658f2e4eeb 100644 --- a/lib/internal/Magento/Framework/MessageQueue/CallbackInvokerInterface.php +++ b/lib/internal/Magento/Framework/MessageQueue/CallbackInvokerInterface.php @@ -7,6 +7,9 @@ namespace Magento\Framework\MessageQueue; +/** + * Callback invoker interface + */ interface CallbackInvokerInterface { /** From 590383107c2f000950d5057084bdd36eef8e5320 Mon Sep 17 00:00:00 2001 From: Vladimir Fishchenko <hws47a@gmail.com> Date: Fri, 8 Mar 2019 17:11:51 +0100 Subject: [PATCH 109/265] magento/magento-functional-tests-migration#417: Convert CreateCustomOrderStatusEntityTest to MFTF --- ...nOrderStatusFormFillAndSaveActionGroup.xml | 22 ++++++++++ ...sertOrderStatusExistsInGridActionGroup.xml | 24 ++++++++++ ...tatusFormSaveDuplicateErrorActionGroup.xml | 15 +++++++ ...tOrderStatusFormSaveSuccessActionGroup.xml | 15 +++++++ .../Sales/Test/Mftf/Data/OrderStatusData.xml | 23 ++++++++++ .../Test/Mftf/Page/AdminOrderStatusPage.xml | 14 ++++++ .../Section/AdminOrderStatusFormSection.xml | 15 +++++++ .../Section/AdminOrderStatusGridSection.xml | 16 +++++++ ...inCreateOrderStatusDuplicatingCodeTest.xml | 38 ++++++++++++++++ ...nCreateOrderStatusDuplicatingLabelTest.xml | 44 +++++++++++++++++++ .../Mftf/Test/AdminCreateOrderStatusTest.xml | 44 +++++++++++++++++++ .../CreateCustomOrderStatusEntityTest.xml | 3 ++ 12 files changed, 273 insertions(+) create mode 100644 app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderStatusFormFillAndSaveActionGroup.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderStatusExistsInGridActionGroup.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderStatusFormSaveDuplicateErrorActionGroup.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderStatusFormSaveSuccessActionGroup.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Data/OrderStatusData.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Page/AdminOrderStatusPage.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStatusFormSection.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStatusGridSection.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingCodeTest.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingLabelTest.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusTest.xml diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderStatusFormFillAndSaveActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderStatusFormFillAndSaveActionGroup.xml new file mode 100644 index 000000000000..cb27439a9d88 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderStatusFormFillAndSaveActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!-- Fill Order status form and click save --> + <actionGroup name="AdminOrderStatusFormFillAndSave"> + <arguments> + <argument name="status" type="string" defaultValue=""/> + <argument name="label" type="string" defaultValue=""/> + </arguments> + + <fillField stepKey="fillStatusCode" selector="{{AdminOrderStatusFormSection.statusCodeField}}" userInput="{{status}}"/> + <fillField stepKey="fillStatusLabel" selector="{{AdminOrderStatusFormSection.statusLabelField}}" userInput="{{label}}"/> + <click stepKey="clickSaveStatus" selector="{{AdminMainActionsSection.save}}"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderStatusExistsInGridActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderStatusExistsInGridActionGroup.xml new file mode 100644 index 000000000000..bbb6aa71b893 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderStatusExistsInGridActionGroup.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!-- Search order status grid for item with a specific code and validate data --> + <actionGroup name="AssertOrderStatusExistsInGrid"> + <arguments> + <argument name="status" type="string" defaultValue=""/> + <argument name="label" type="string" defaultValue=""/> + </arguments> + + <click selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="clickClearFilters"/> + <fillField selector="{{AdminOrderStatusGridSection.statusCodeFilterField}}" userInput="{{status}}" stepKey="fillStatusFilter"/> + <click selector="{{AdminSecondaryGridSection.searchButton}}" stepKey="clickSearch"/> + <see selector="{{AdminOrderStatusGridSection.statusCodeDataColumn}}" userInput="{{status}}" stepKey="seeStatusCodeInGrid"/> + <see selector="{{AdminOrderStatusGridSection.statusLabelDataColumn}}" userInput="{{label}}" stepKey="seeStatusLabelInGrid"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderStatusFormSaveDuplicateErrorActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderStatusFormSaveDuplicateErrorActionGroup.xml new file mode 100644 index 000000000000..5b4c3115744c --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderStatusFormSaveDuplicateErrorActionGroup.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!-- Assert that order status is not saved with duplication error message --> + <actionGroup name="AssertOrderStatusFormSaveDuplicateError"> + <see selector="{{AdminMessagesSection.error}}" userInput="We found another order status with the same order status code." stepKey="seeError"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderStatusFormSaveSuccessActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderStatusFormSaveSuccessActionGroup.xml new file mode 100644 index 000000000000..d82f4b9dd25e --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderStatusFormSaveSuccessActionGroup.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!-- Assert that order status saved with success message --> + <actionGroup name="AssertOrderStatusFormSaveSuccess"> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the order status." stepKey="seeSuccess"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Data/OrderStatusData.xml b/app/code/Magento/Sales/Test/Mftf/Data/OrderStatusData.xml new file mode 100644 index 000000000000..aecd7fcf1b70 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Data/OrderStatusData.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="defaultOrderStatus"> + <data key="status" unique="suffix">order_status</data> + <data key="label" unique="suffix">orderLabel</data> + </entity> + <entity name="duplicatingCodeOrderStatus"> + <data key="status">pending</data> + <data key="label" unique="suffix">orderLabel</data> + </entity> + <entity name="duplicatingLabelOrderStatus"> + <data key="status" unique="suffix">order_status</data> + <data key="label">Suspected Fraud</data> + </entity> +</entities> diff --git a/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderStatusPage.xml b/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderStatusPage.xml new file mode 100644 index 000000000000..b158e4923074 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderStatusPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminOrderStatusPage" url="sales/order_status" area="admin" module="Magento_Sales"> + <section name="AdminOrderStatusFormSection"/> + </page> +</pages> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStatusFormSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStatusFormSection.xml new file mode 100644 index 000000000000..1058b2d6f217 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStatusFormSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminOrderStatusFormSection"> + <element name="statusCodeField" type="text" selector="#edit_form [name=status]"/> + <element name="statusLabelField" type="text" selector="#edit_form [name=label]"/> + </section> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStatusGridSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStatusGridSection.xml new file mode 100644 index 000000000000..b62463928118 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStatusGridSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminOrderStatusGridSection"> + <element name="statusCodeFilterField" type="input" selector="[data-role=filter-form] [name=status]"/> + <element name="statusCodeDataColumn" type="input" selector="[data-role=row] [data-column=status]"/> + <element name="statusLabelDataColumn" type="input" selector="[data-role=row] [data-column=label]"/> + </section> +</sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingCodeTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingCodeTest.xml new file mode 100644 index 000000000000..aea1094b4d62 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingCodeTest.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateOrderStatusDuplicatingCodeTest"> + <annotations> + <stories value="Create order status"/> + <title value="Create order status with duplicating code"/> + <description value="Receive error when creating order status with the code which is already exist"/> + <group value="sales"/> + <group value="mtf_migrated"/> + <severity value="AVERAGE"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Go to new order status page --> + <amOnPage url="{{AdminOrderStatusPage.url}}" stepKey="goToOrderStatusPage"/> + <click selector="{{AdminMainActionsSection.add}}" stepKey="clickCreateNewStatus"/> + + <!-- Fill the form and validate message --> + <actionGroup ref="AdminOrderStatusFormFillAndSave" stepKey="fillFormAndClickSave"> + <argument name="status" value="{{duplicatingCodeOrderStatus.status}}"/> + <argument name="label" value="{{duplicatingCodeOrderStatus.label}}"/> + </actionGroup> + <actionGroup ref="AssertOrderStatusFormSaveDuplicateError" stepKey="seeFormSaveDuplicateError"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingLabelTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingLabelTest.xml new file mode 100644 index 000000000000..95582e107da1 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingLabelTest.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateOrderStatusDuplicatingLabelTest"> + <annotations> + <stories value="Create order status"/> + <title value="Create order status with duplicating label"/> + <description value="Create an order status and get success message"/> + <group value="sales"/> + <group value="mtf_migrated"/> + <severity value="AVERAGE"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Go to new order status page --> + <amOnPage url="{{AdminOrderStatusPage.url}}" stepKey="goToOrderStatusPage"/> + <click selector="{{AdminMainActionsSection.add}}" stepKey="clickCreateNewStatus"/> + + <!-- Fill the form and validate message --> + <actionGroup ref="AdminOrderStatusFormFillAndSave" stepKey="fillFormAndClickSave"> + <argument name="status" value="{{duplicatingLabelOrderStatus.status}}"/> + <argument name="label" value="{{duplicatingLabelOrderStatus.label}}"/> + </actionGroup> + <actionGroup ref="AssertOrderStatusFormSaveSuccess" stepKey="seeFormSaveSuccess"/> + + <!-- Verify the order status grid page shows the order status we just created --> + <actionGroup ref="AssertOrderStatusExistsInGrid" stepKey="searchCreatedOrderStatus"> + <argument name="status" value="{{duplicatingLabelOrderStatus.status}}"/> + <argument name="label" value="{{duplicatingLabelOrderStatus.label}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusTest.xml new file mode 100644 index 000000000000..6fe25160655f --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusTest.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateOrderStatusTest"> + <annotations> + <stories value="Create custom order status"/> + <title value="Create custom order status"/> + <description value="Tests opening admin order status page, create a new order status with success message"/> + <group value="sales"/> + <group value="mtf_migrated"/> + <severity value="AVERAGE"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Go to new order status page --> + <amOnPage url="{{AdminOrderStatusPage.url}}" stepKey="goToOrderStatusPage"/> + <click selector="{{AdminMainActionsSection.add}}" stepKey="clickCreateNewStatus"/> + + <!-- Fill the form and validate message --> + <actionGroup ref="AdminOrderStatusFormFillAndSave" stepKey="fillFormAndClickSave"> + <argument name="status" value="{{defaultOrderStatus.status}}"/> + <argument name="label" value="{{defaultOrderStatus.label}}"/> + </actionGroup> + <actionGroup ref="AssertOrderStatusFormSaveSuccess" stepKey="seeFormSaveSuccess"/> + + <!-- Verify the order status grid page shows the order status we just created --> + <actionGroup ref="AssertOrderStatusExistsInGrid" stepKey="searchCreatedOrderStatus"> + <argument name="status" value="{{defaultOrderStatus.status}}"/> + <argument name="label" value="{{defaultOrderStatus.label}}"/> + </actionGroup> + </test> +</tests> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateCustomOrderStatusEntityTest.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateCustomOrderStatusEntityTest.xml index 38ce04fa56d8..e05d0fea6b12 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateCustomOrderStatusEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateCustomOrderStatusEntityTest.xml @@ -8,17 +8,20 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Sales\Test\TestCase\CreateCustomOrderStatusEntityTest" summary="Create Custom Order Status Entity" ticketId="MAGETWO-23412"> <variation name="CreateCustomOrderStatusEntityTestVariation1"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="orderStatus/data/status" xsi:type="string">order_status%isolation%</data> <data name="orderStatus/data/label" xsi:type="string">orderLabel%isolation%</data> <constraint name="Magento\Sales\Test\Constraint\AssertOrderStatusSuccessCreateMessage" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderStatusInGrid" /> </variation> <variation name="CreateCustomOrderStatusEntityTestVariation2"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="orderStatus/data/status" xsi:type="string">pending</data> <data name="orderStatus/data/label" xsi:type="string">orderLabel%isolation%</data> <constraint name="Magento\Sales\Test\Constraint\AssertOrderStatusDuplicateStatus" /> </variation> <variation name="CreateCustomOrderStatusEntityTestVariation3"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="orderStatus/data/status" xsi:type="string">order_status%isolation%</data> <data name="orderStatus/data/label" xsi:type="string">Suspected Fraud</data> <constraint name="Magento\Sales\Test\Constraint\AssertOrderStatusSuccessCreateMessage" /> From b0a0c07b9388e5b1159bb33e37e1a5531b96c313 Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Fri, 8 Mar 2019 10:37:05 -0600 Subject: [PATCH 110/265] MC-13613: Product mass update --- .../Product/Action/Attribute/Save.php | 44 +++++++++++-------- .../Model/Attribute/Backend/Consumer.php | 16 +++++-- .../Backend/ConsumerWebsiteAssign.php | 31 ++++++++++--- app/code/Magento/Catalog/composer.json | 1 + .../Plugin/MassUpdateProductAttribute.php | 25 +++++++++++ .../Magento/CatalogInventory/composer.json | 1 + .../MessageQueue/Model/CallbackInvoker.php | 1 + .../MessageQueue/Model/PoisonPillFactory.php | 36 --------------- .../Model/ResourceModel/PoisonPill.php | 3 ++ .../PublisherConsumerController.php | 5 +++ .../Product/Action/AttributeTest.php | 16 +++++-- .../Framework/MessageQueue/Consumer.php | 2 +- 12 files changed, 112 insertions(+), 69 deletions(-) delete mode 100644 app/code/Magento/MessageQueue/Model/PoisonPillFactory.php diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php index 9a6893a1aff8..63182dd5624e 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php @@ -13,6 +13,7 @@ /** * Class Save + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Save extends \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute implements HttpPostActionInterface { @@ -107,7 +108,7 @@ public function execute() /** * Sanitize product attributes * - * @param $attributesData + * @param array $attributesData * * @return array */ @@ -152,23 +153,29 @@ private function sanitizeProductAttributes($attributesData) /** * Schedule new bulk * - * @param $attributesData - * @param $websiteRemoveData - * @param $websiteAddData - * @param $storeId - * @param $websiteId - * @param $productIds + * @param array $attributesData + * @param array $websiteRemoveData + * @param array $websiteAddData + * @param int $storeId + * @param int $websiteId + * @param array $productIds * @throws \Magento\Framework\Exception\LocalizedException * * @return void */ - private function publish($attributesData, $websiteRemoveData, $websiteAddData, $storeId, $websiteId, $productIds):void - { + private function publish( + $attributesData, + $websiteRemoveData, + $websiteAddData, + $storeId, + $websiteId, + $productIds + ):void { $productIdsChunks = array_chunk($productIds, 100); $bulkUuid = $this->identityService->generateId(); $bulkDescription = __('Update attributes to ' . count($productIds) . ' selected products'); $operations = []; - foreach($productIdsChunks as $productIdsChunk) { + foreach ($productIdsChunks as $productIdsChunk) { if ($websiteRemoveData || $websiteAddData) { $dataToUpdate = [ 'website_assign' => $websiteAddData, @@ -215,13 +222,14 @@ private function publish($attributesData, $websiteRemoveData, $websiteAddData, $ /** * Make asynchronous operation - * @param $meta - * @param $queue - * @param $dataToUpdate - * @param $storeId - * @param $websiteId - * @param $productIds - * @param $bulkUuid + * + * @param string $meta + * @param string $queue + * @param array $dataToUpdate + * @param int $storeId + * @param int $websiteId + * @param array $productIds + * @param int $bulkUuid * * @return OperationInterface */ @@ -252,6 +260,4 @@ private function makeOperation( return $this->operationFactory->create($data); } - } - diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php b/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php index f9c90f82dacd..5785a9f3ccaa 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php @@ -15,6 +15,7 @@ /** * Consumer for export message. + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Consumer { @@ -88,7 +89,11 @@ public function __construct( } /** + * Process + * * @param \Magento\AsynchronousOperations\Api\Data\OperationInterface $operation + * @throws \Exception + * * @return void */ public function process(\Magento\AsynchronousOperations\Api\Data\OperationInterface $operation) @@ -97,7 +102,6 @@ public function process(\Magento\AsynchronousOperations\Api\Data\OperationInterf $serializedData = $operation->getSerializedData(); $data = $this->serializer->unserialize($serializedData); $this->execute($data); - } catch (\Zend_Db_Adapter_Exception $e) { $this->logger->critical($e->getMessage()); if ( @@ -111,7 +115,9 @@ public function process(\Magento\AsynchronousOperations\Api\Data\OperationInterf } else { $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; $errorCode = $e->getCode(); - $message = __('Sorry, something went wrong during product attributes update. Please see log for details.'); + $message = __( + 'Sorry, something went wrong during product attributes update. Please see log for details.' + ); } } catch (NoSuchEntityException $e) { $this->logger->critical($e->getMessage()); @@ -140,7 +146,11 @@ public function process(\Magento\AsynchronousOperations\Api\Data\OperationInterf } /** - * @param $data + * Execute + * + * @param array $data + * + * @return void */ private function execute($data): void { diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php b/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php index 591c0892b422..13933b952cad 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php @@ -15,6 +15,7 @@ /** * Consumer for export message. + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ConsumerWebsiteAssign { @@ -81,7 +82,11 @@ public function __construct( } /** + * Process + * * @param \Magento\AsynchronousOperations\Api\Data\OperationInterface $operation + * @throws \Exception + * * @return void */ public function process(\Magento\AsynchronousOperations\Api\Data\OperationInterface $operation) @@ -103,7 +108,9 @@ public function process(\Magento\AsynchronousOperations\Api\Data\OperationInterf } else { $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; $errorCode = $e->getCode(); - $message = __('Sorry, something went wrong during product attributes update. Please see log for details.'); + $message = __( + 'Sorry, something went wrong during product attributes update. Please see log for details.' + ); } } catch (NoSuchEntityException $e) { $this->logger->critical($e->getMessage()); @@ -132,9 +139,13 @@ public function process(\Magento\AsynchronousOperations\Api\Data\OperationInterf } /** - * @param $productIds - * @param $websiteRemoveData - * @param $websiteAddData + * Update website in products + * + * @param array $productIds + * @param array $websiteRemoveData + * @param array $websiteAddData + * + * @return void */ private function updateWebsiteInProducts($productIds, $websiteRemoveData, $websiteAddData): void { @@ -147,11 +158,19 @@ private function updateWebsiteInProducts($productIds, $websiteRemoveData, $websi } /** - * @param $data + * Execute + * + * @param array $data + * + * @return void */ private function execute($data): void { - $this->updateWebsiteInProducts($data['product_ids'], $data['attributes']['website_detach'], $data['attributes']['website_assign']); + $this->updateWebsiteInProducts( + $data['product_ids'], + $data['attributes']['website_detach'], + $data['attributes']['website_assign'] + ); $this->productPriceIndexerProcessor->reindexList($data['product_ids']); $this->productFlatIndexerProcessor->reindexList($data['product_ids']); } diff --git a/app/code/Magento/Catalog/composer.json b/app/code/Magento/Catalog/composer.json index 44d051933909..47e532edfe54 100644 --- a/app/code/Magento/Catalog/composer.json +++ b/app/code/Magento/Catalog/composer.json @@ -7,6 +7,7 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", + "magento/module-asynchronous-operations": "*", "magento/module-backend": "*", "magento/module-catalog-inventory": "*", "magento/module-catalog-rule": "*", diff --git a/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php b/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php index e565f609611c..dc1d888d2af1 100644 --- a/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php +++ b/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php @@ -8,6 +8,10 @@ use Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute\Save; use Magento\CatalogInventory\Api\Data\StockItemInterface; +/** + * MassUpdate product attribute. + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class MassUpdateProductAttribute { /** @@ -70,6 +74,7 @@ class MassUpdateProductAttribute * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\Backend\Model\View\Result\RedirectFactory $redirectFactory * @param \Magento\Framework\Message\ManagerInterface $messageManager + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\CatalogInventory\Model\Indexer\Stock\Processor $stockIndexerProcessor, @@ -96,7 +101,10 @@ public function __construct( } /** + * Around execute plugin + * * @param Save $subject + * * @return \Magento\Framework\Controller\ResultInterface * * @SuppressWarnings(PHPMD.UnusedFormalParameter) @@ -109,6 +117,7 @@ public function aroundExecute(Save $subject, callable $proceed) $websiteId = $this->storeManager->getStore($storeId)->getWebsiteId(); $productIds = $this->session->getData('product_ids'); $inventoryData = $this->addConfigSettings($inventoryData); + if (!empty($inventoryData)) { $this->updateInventoryInProducts($productIds, $websiteId, $inventoryData); } @@ -126,6 +135,13 @@ public function aroundExecute(Save $subject, callable $proceed) return $this->redirectFactory->create()->setPath('catalog/product/', ['_current' => true]); } + /** + * Add config settings + * + * @param array $inventoryData + * + * @return array + */ private function addConfigSettings($inventoryData) { $options = $this->stockConfiguration->getConfigItemOptions(); @@ -138,6 +154,15 @@ private function addConfigSettings($inventoryData) return $inventoryData; } + /** + * Update inventory in products + * + * @param array $productIds + * @param int $websiteId + * @param array $inventoryData + * + * @return void + */ private function updateInventoryInProducts($productIds, $websiteId, $inventoryData): void { foreach ($productIds as $productId) { diff --git a/app/code/Magento/CatalogInventory/composer.json b/app/code/Magento/CatalogInventory/composer.json index eb6239ea87ef..f18a5ff1d875 100644 --- a/app/code/Magento/CatalogInventory/composer.json +++ b/app/code/Magento/CatalogInventory/composer.json @@ -7,6 +7,7 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", + "magento/module-backend": "*", "magento/module-catalog": "*", "magento/module-search": "*", "magento/module-config": "*", diff --git a/app/code/Magento/MessageQueue/Model/CallbackInvoker.php b/app/code/Magento/MessageQueue/Model/CallbackInvoker.php index a61a6862843b..33229c6432c5 100644 --- a/app/code/Magento/MessageQueue/Model/CallbackInvoker.php +++ b/app/code/Magento/MessageQueue/Model/CallbackInvoker.php @@ -47,6 +47,7 @@ public function __construct( /** * @inheritdoc + * @SuppressWarnings(PHPMD.ExitExpression) */ public function invoke(QueueInterface $queue, $maxNumberOfMessages, $callback) { diff --git a/app/code/Magento/MessageQueue/Model/PoisonPillFactory.php b/app/code/Magento/MessageQueue/Model/PoisonPillFactory.php deleted file mode 100644 index 0f2857fd6ec5..000000000000 --- a/app/code/Magento/MessageQueue/Model/PoisonPillFactory.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\MessageQueue\Model; - -use Magento\MessageQueue\Api\Data\PoisonPillInterface; - -class PoisonPillFactory -{ - /** - * @var PoisonPillInterface - */ - private $poisonPill; - - /** - * @param PoisonPillInterface $poisonPill - */ - public function __construct( - PoisonPillInterface $poisonPill - ) { - $this->poisonPill = $poisonPill; - } - - /** - * @param int $version - * @return PoisonPillInterface - */ - public function create(int $version): PoisonPillInterface - { - - } -} diff --git a/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php b/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php index dab251ce50c8..7fab0a15ca19 100644 --- a/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php +++ b/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php @@ -27,7 +27,10 @@ class PoisonPill extends AbstractDb implements PoisonPillPutInterface, PoisonPil private $poisonPillFactory; /** + * PoisonPill constructor. + * * @param Context $context + * @param PoisonPillInterfaceFactory $poisonPillFactory * @param string|null $connectionName */ public function __construct( diff --git a/dev/tests/integration/framework/Magento/TestFramework/MessageQueue/PublisherConsumerController.php b/dev/tests/integration/framework/Magento/TestFramework/MessageQueue/PublisherConsumerController.php index 7748e43bbd62..32240e68ae73 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/MessageQueue/PublisherConsumerController.php +++ b/dev/tests/integration/framework/Magento/TestFramework/MessageQueue/PublisherConsumerController.php @@ -223,6 +223,11 @@ public function getPublisher() return $this->publisher; } + /** + * Start consumers + * + * @return void + */ public function startConsumers(): void { foreach ($this->consumers as $consumer) { diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php index 0fe618b2db30..53aa1e24a5cd 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php @@ -122,10 +122,18 @@ public function testSaveActionChangeVisibility($attributes) \Magento\Catalog\Block\Product\ListProduct::class ); - $this->publisherConsumerController->waitForAsynchronousResult(function() use($repository) { - return $repository->get('simple', false, null, true)->getVisibility() != Visibility::VISIBILITY_NOT_VISIBLE; - sleep(3); - }, []); + $this->publisherConsumerController->waitForAsynchronousResult( + function() use ($repository) { + sleep(3); + return $repository->get( + 'simple', + false, + null, + true + )->getVisibility() != Visibility::VISIBILITY_NOT_VISIBLE; + }, + [] + ); $category = $categoryFactory->create()->load(2); $layer = $listProduct->getLayer(); diff --git a/lib/internal/Magento/Framework/MessageQueue/Consumer.php b/lib/internal/Magento/Framework/MessageQueue/Consumer.php index 72c5f2e0cbdf..8f65a2d8c5ed 100644 --- a/lib/internal/Magento/Framework/MessageQueue/Consumer.php +++ b/lib/internal/Magento/Framework/MessageQueue/Consumer.php @@ -103,7 +103,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function process($maxNumberOfMessages = null) { From 91040106ffca5ec4296a7650110bef20b4f0d1f7 Mon Sep 17 00:00:00 2001 From: Maria Kovdrysh <kovdrysh@adobe.com> Date: Fri, 8 Mar 2019 12:25:10 -0600 Subject: [PATCH 111/265] MC-4435: Convert MassDeleteSearchTermEntityTest to MFTF --- .../ActionGroup/StorefrontCatalogSearchActionGroup.xml | 10 ++++++++-- .../Section/AdminCatalogSearchTermIndexSection.xml | 1 + .../Mftf/ActionGroup/AdminSearchTermActionGroup.xml | 4 ++-- .../Section/StorefrontQuickSearchResultsSection.xml | 1 + 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml index 6b913e5b458e..d99d1b69887e 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml @@ -11,9 +11,10 @@ <!-- Quick search the phrase and check if the result page contains correct information --> <actionGroup name="StorefrontCheckQuickSearchActionGroup"> <arguments> - <argument name="phrase"/> + <argument name="phrase" type="string"/> </arguments> - <submitForm selector="#search_mini_form" parameterArray="['q' => '{{phrase}}']" stepKey="fillQuickSearch" /> + <fillField stepKey="fillInput" selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{phrase}}"/> + <submitForm selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" parameterArray="[]" stepKey="submitQuickSearch" /> <seeInCurrentUrl url="{{StorefrontCatalogSearchPage.url}}" stepKey="checkUrl"/> <dontSeeInCurrentUrl url="form_key=" stepKey="checkUrlFormKey"/> <seeInTitle userInput="Search results for: '{{phrase}}'" stepKey="assertQuickSearchTitle"/> @@ -116,4 +117,9 @@ <click selector="{{StorefrontCatalogSearchAdvancedFormSection.SubmitButton}}" stepKey="clickSubmit"/> <waitForPageLoad stepKey="waitForPageLoad"/> </actionGroup> + + <!-- Asserts that search results do not contain any results--> + <actionGroup name="StorefrontCheckSearchIsEmpty"> + <see stepKey="checkEmpty" selector="{{StorefrontQuickSearchResultsSection.messageSection}}" userInput="Your search returned no results"/> + </actionGroup> </actionGroups> \ No newline at end of file diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermIndexSection.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermIndexSection.xml index 5491c2722838..ac316d060f6e 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermIndexSection.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Section/AdminCatalogSearchTermIndexSection.xml @@ -16,6 +16,7 @@ <element name="submit" type="button" selector="//button[@class='action-default scalable']/span" timeout="30"/> <element name="searchQuery" type="text" selector="//tr[@class='data-grid-filters']//td/input[@name='search_query']"/> <element name="nthRow" type="checkbox" selector="//tbody/tr['{{rowNum}}']//input[@name='search']" parameterized="true"/> + <element name="searchTermRowCheckboxBySearchQuery" type="checkbox" selector="//*[normalize-space()='{{var1}}']/preceding-sibling::td//input[@name='search']" parameterized="true" timeout="30"/> <element name="okButton" type="button" selector="//button[@class='action-primary action-accept']/span" timeout="30"/> <element name="emptyRecords" type="text" selector="//tr[@class='data-grid-tr-no-data even']/td[@class='empty-text']"/> </section> diff --git a/app/code/Magento/Search/Test/Mftf/ActionGroup/AdminSearchTermActionGroup.xml b/app/code/Magento/Search/Test/Mftf/ActionGroup/AdminSearchTermActionGroup.xml index f116e44e5c14..e0b3d4b850bb 100644 --- a/app/code/Magento/Search/Test/Mftf/ActionGroup/AdminSearchTermActionGroup.xml +++ b/app/code/Magento/Search/Test/Mftf/ActionGroup/AdminSearchTermActionGroup.xml @@ -17,12 +17,12 @@ <fillField selector="{{AdminCatalogSearchTermIndexSection.searchQuery}}" userInput="{{searchQuery}}" stepKey="fillSearchQuery"/> <click selector="{{AdminCatalogSearchTermIndexSection.searchButton}}" stepKey="clickSearchButton"/> <waitForPageLoad stepKey="waitForSearchResultLoad"/> - <checkOption selector="{{AdminCatalogSearchTermIndexSection.checkBox}}" stepKey="checkCheckBox"/> + <checkOption selector="{{AdminCatalogSearchTermIndexSection.searchTermRowCheckboxBySearchQuery(searchQuery)}}" stepKey="checkCheckBox"/> </actionGroup> <!-- Delete search term --> <actionGroup name="deleteSearchTerm"> - <selectOption selector="{{AdminCatalogSearchTermIndexSection.delete}}" userInput="delete" stepKey="selectDeleteOption"/> + <selectOption selector="{{AdminCatalogSearchTermIndexSection.massActions}}" userInput="delete" stepKey="selectDeleteOption"/> <click selector="{{AdminCatalogSearchTermIndexSection.submit}}" stepKey="clickSubmitButton"/> <click selector="{{AdminCatalogSearchTermIndexSection.okButton}}" stepKey="clickOkButton"/> <waitForElementVisible selector="{{AdminCatalogSearchTermMessagesSection.successMessage}}" stepKey="waitForSuccessMessage"/> diff --git a/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchResultsSection.xml b/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchResultsSection.xml index 9e5bde9a2be4..81b025c9554e 100644 --- a/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchResultsSection.xml +++ b/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchResultsSection.xml @@ -15,5 +15,6 @@ <element name="asLowAsLabel" type="text" selector=".minimal-price-link > span"/> <element name="textArea" type="text" selector="li[class='item']"/> <element name="regularPrice" type="text" selector="//span[@class='price-wrapper ']/span[@class='price']"/> + <element name="messageSection" type="text" selector="div .message"/> </section> </sections> From 189d79b14cc8bf31f2eb5f06f9ac8172684f7394 Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Fri, 8 Mar 2019 13:30:18 -0600 Subject: [PATCH 112/265] MC-13613: Product mass update --- .../Magento/Catalog/Model/Attribute/Backend/Consumer.php | 5 ++--- .../Model/Attribute/Backend/ConsumerWebsiteAssign.php | 5 ++--- app/code/Magento/Catalog/composer.json | 1 + .../CatalogInventory/Plugin/MassUpdateProductAttribute.php | 1 + .../Magento/MessageQueue/Model/ResourceModel/PoisonPill.php | 3 +++ .../Controller/Adminhtml/Product/Action/AttributeTest.php | 4 ++-- 6 files changed, 11 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php b/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php index 5785a9f3ccaa..becd6c160155 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php @@ -102,10 +102,9 @@ public function process(\Magento\AsynchronousOperations\Api\Data\OperationInterf $serializedData = $operation->getSerializedData(); $data = $this->serializer->unserialize($serializedData); $this->execute($data); - } catch (\Zend_Db_Adapter_Exception $e) { + } catch (\Zend_Db_Adapter_Exception $e) { $this->logger->critical($e->getMessage()); - if ( - $e instanceof \Magento\Framework\DB\Adapter\LockWaitException + if ($e instanceof \Magento\Framework\DB\Adapter\LockWaitException || $e instanceof \Magento\Framework\DB\Adapter\DeadlockException || $e instanceof \Magento\Framework\DB\Adapter\ConnectionException ) { diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php b/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php index 13933b952cad..b47d65e31007 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php @@ -95,10 +95,9 @@ public function process(\Magento\AsynchronousOperations\Api\Data\OperationInterf $serializedData = $operation->getSerializedData(); $data = $this->serializer->unserialize($serializedData); $this->execute($data); - } catch (\Zend_Db_Adapter_Exception $e) { + } catch (\Zend_Db_Adapter_Exception $e) { $this->logger->critical($e->getMessage()); - if ( - $e instanceof \Magento\Framework\DB\Adapter\LockWaitException + if ($e instanceof \Magento\Framework\DB\Adapter\LockWaitException || $e instanceof \Magento\Framework\DB\Adapter\DeadlockException || $e instanceof \Magento\Framework\DB\Adapter\ConnectionException ) { diff --git a/app/code/Magento/Catalog/composer.json b/app/code/Magento/Catalog/composer.json index 47e532edfe54..5c3ee3da8ca8 100644 --- a/app/code/Magento/Catalog/composer.json +++ b/app/code/Magento/Catalog/composer.json @@ -7,6 +7,7 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", + "magento/module-authorization": "*", "magento/module-asynchronous-operations": "*", "magento/module-backend": "*", "magento/module-catalog-inventory": "*", diff --git a/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php b/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php index dc1d888d2af1..f41fc00b55ac 100644 --- a/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php +++ b/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php @@ -104,6 +104,7 @@ public function __construct( * Around execute plugin * * @param Save $subject + * @param callable $proceed * * @return \Magento\Framework\Controller\ResultInterface * diff --git a/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php b/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php index 7fab0a15ca19..b6149be04c47 100644 --- a/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php +++ b/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php @@ -14,6 +14,9 @@ use Magento\Framework\Model\ResourceModel\Db\Context; use Magento\Framework\Model\ResourceModel\Db\AbstractDb; +/** + * PoisonPill. + */ class PoisonPill extends AbstractDb implements PoisonPillPutInterface, PoisonPillReadInterface { /** diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php index 53aa1e24a5cd..3ec8c806dcbb 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php @@ -123,14 +123,14 @@ public function testSaveActionChangeVisibility($attributes) ); $this->publisherConsumerController->waitForAsynchronousResult( - function() use ($repository) { + function () use ($repository) { sleep(3); return $repository->get( 'simple', false, null, true - )->getVisibility() != Visibility::VISIBILITY_NOT_VISIBLE; + )->getVisibility() != Visibility::VISIBILITY_NOT_VISIBLE; }, [] ); From aa7a9544d0aa0d51389a367311f8a5d5b0dae12c Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Fri, 8 Mar 2019 13:48:54 -0600 Subject: [PATCH 113/265] MC-13613: Product mass update --- .../Magento/MessageQueue/etc/db_schema_whitelist.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/MessageQueue/etc/db_schema_whitelist.json b/app/code/Magento/MessageQueue/etc/db_schema_whitelist.json index f31981d2ec40..d9d623a994b3 100644 --- a/app/code/Magento/MessageQueue/etc/db_schema_whitelist.json +++ b/app/code/Magento/MessageQueue/etc/db_schema_whitelist.json @@ -9,5 +9,13 @@ "PRIMARY": true, "QUEUE_LOCK_MESSAGE_CODE": true } + }, + "queue_poison_pill": { + "column": { + "version": true + }, + "constraint": { + "PRIMARY": true + } } -} \ No newline at end of file +} From 63a25905793915ab720a5620a94141e4c4bd8997 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Fri, 8 Mar 2019 11:44:06 -0600 Subject: [PATCH 114/265] 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 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer; + +use Magento\Elasticsearch\Model\Adapter\FieldType\Date; +use Magento\Elasticsearch\SearchAdapter\Query\ValueTransformerInterface; + +/** + * Value transformer for date type fields. + */ +class DateTransformer implements ValueTransformerInterface +{ + /** + * @var Date + */ + private $dateFieldType; + + /** + * @param Date $dateFieldType + */ + public function __construct(Date $dateFieldType) + { + $this->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 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer; + +use Magento\Elasticsearch\SearchAdapter\Query\ValueTransformerInterface; + +/** + * Value transformer for float type fields. + */ +class FloatTransformer implements ValueTransformerInterface +{ + /** + * @inheritdoc + */ + public function transform(string $value): ?float + { + return \is_numeric($value) ? (float) $value : null; + } +} diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/IntegerTransformer.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/IntegerTransformer.php new file mode 100644 index 000000000000..0846ff3a9bd8 --- /dev/null +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/IntegerTransformer.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer; + +use Magento\Elasticsearch\SearchAdapter\Query\ValueTransformerInterface; + +/** + * Value transformer for integer type fields. + */ +class IntegerTransformer implements ValueTransformerInterface +{ + /** + * @inheritdoc + */ + public function transform(string $value): ?int + { + return \is_numeric($value) ? (int) $value : null; + } +} diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/TextTransformer.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/TextTransformer.php new file mode 100644 index 000000000000..68bec2580f62 --- /dev/null +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/TextTransformer.php @@ -0,0 +1,65 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer; + +use Magento\Elasticsearch\SearchAdapter\Query\ValueTransformerInterface; +use Magento\Framework\Search\Adapter\Preprocessor\PreprocessorInterface; + +/** + * Value transformer for fields with text types. + */ +class TextTransformer implements ValueTransformerInterface +{ + /** + * @var PreprocessorInterface[] + */ + private $preprocessors; + + /** + * @param PreprocessorInterface[] $preprocessors + */ + public function __construct(array $preprocessors = []) + { + foreach ($preprocessors as $preprocessor) { + if (!$preprocessor instanceof PreprocessorInterface) { + throw new \InvalidArgumentException( + \sprintf('"%s" is not a instance of ValueTransformerInterface.', get_class($preprocessor)) + ); + } + } + + $this->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 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch\SearchAdapter\Query; + +/** + * Value transformer of search term for matching with ES field types. + */ +interface ValueTransformerInterface +{ + /** + * Transform value according to field type. + * + * @param string $value + * @return mixed + */ + public function transform(string $value); +} diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformerPool.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformerPool.php new file mode 100644 index 000000000000..11a35d79ce1f --- /dev/null +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformerPool.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch\SearchAdapter\Query; + +/** + * Pool of value transformers. + */ +class ValueTransformerPool +{ + /** + * @var ValueTransformerInterface[] + */ + private $transformers; + + /** + * @param ValueTransformerInterface[] $valueTransformers + */ + public function __construct(array $valueTransformers = []) + { + foreach ($valueTransformers as $valueTransformer) { + if (!$valueTransformer instanceof ValueTransformerInterface) { + throw new \InvalidArgumentException( + \sprintf('"%s" is not a instance of ValueTransformerInterface.', get_class($valueTransformer)) + ); + } + } + + $this->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 @@ </argument> </arguments> </type> + <type name="Magento\Elasticsearch\SearchAdapter\Query\ValueTransformerPool"> + <arguments> + <argument name="valueTransformers" xsi:type="array"> + <item name="default" xsi:type="object">Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\TextTransformer</item> + <item name="date" xsi:type="object">Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\DateTransformer</item> + <item name="float" xsi:type="object">Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\FloatTransformer</item> + <item name="integer" xsi:type="object">Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\IntegerTransformer</item> + </argument> + </arguments> + </type> + <type name="Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\TextTransformer"> + <arguments> + <argument name="preprocessors" xsi:type="array"> + <item name="stopwordsPreprocessor" xsi:type="object">Magento\Elasticsearch\SearchAdapter\Query\Preprocessor\Stopwords</item> + <item name="synonymsPreprocessor" xsi:type="object">Magento\Search\Adapter\Query\Preprocessor\Synonyms</item> + </argument> + </arguments> + </type> </config> 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 <horytsky@adobe.com> Date: Fri, 8 Mar 2019 15:49:44 -0600 Subject: [PATCH 115/265] 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 04f60d5d27f661b2a33c1f6db2ef511ab5ff3c2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20F=C3=BChr?= <d.fuehr@techdivision.com> Date: Fri, 8 Mar 2019 23:20:21 +0100 Subject: [PATCH 116/265] 18752: make root cause visible for exception on submitQuote Github Issue: https://github.com/magento/magento2/issues/18752 If an exceptions happens in orderManagement->place($order) or an "sales_model_service_quote_submit_success" observer, the catch block itself fires an event that currently fails for guest checkouts in Magento\CatalogInventory\Model\ResourceModel\Stock->correctItemsQty(). This second exception hides the root exception and is logged to the exception log with the message "Rolled back transaction has not been completed correctly". This is not bound for this observer, but may occur in every other observer that is currently register or may be registered in the future. Therefore the failure event is wrapped in a try-catch itself and throws a combined exception that is logged in the exception.log. --- .../Magento/Quote/Model/QuoteManagement.php | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Quote/Model/QuoteManagement.php b/app/code/Magento/Quote/Model/QuoteManagement.php index 8f216b64aa9b..b10777f816fd 100644 --- a/app/code/Magento/Quote/Model/QuoteManagement.php +++ b/app/code/Magento/Quote/Model/QuoteManagement.php @@ -12,6 +12,7 @@ use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\StateException; +use Magento\Framework\Phrase; use Magento\Quote\Api\Data\PaymentInterface; use Magento\Quote\Model\Quote\Address\ToOrder as ToOrderConverter; use Magento\Quote\Model\Quote\Address\ToOrderAddress as ToOrderAddressConverter; @@ -532,19 +533,31 @@ protected function submitQuote(QuoteEntity $quote, $orderData = []) ); $this->quoteRepository->save($quote); } catch (\Exception $e) { - if (!empty($this->addressesToSync)) { - foreach ($this->addressesToSync as $addressId) { - $this->addressRepository->deleteById($addressId); + try { + if (!empty($this->addressesToSync)) { + foreach ($this->addressesToSync as $addressId) { + $this->addressRepository->deleteById($addressId); + } } + $this->eventManager->dispatch( + 'sales_model_service_quote_submit_failure', + [ + 'order' => $order, + 'quote' => $quote, + 'exception' => $e, + ] + ); + } catch (\Exception $consecutiveException) { + $message = new Phrase( + "An exception occurred on 'sales_model_service_quote_submit_failure' event: %1\n%2", + [ + $consecutiveException->getMessage(), + $consecutiveException->getTraceAsString() + ] + ); + + throw new LocalizedException($message, $e); } - $this->eventManager->dispatch( - 'sales_model_service_quote_submit_failure', - [ - 'order' => $order, - 'quote' => $quote, - 'exception' => $e - ] - ); throw $e; } return $order; From 2f42295614f85355f029e6346278e2e24708ecd2 Mon Sep 17 00:00:00 2001 From: dharmesh vaja <dharmesh.vaja@rocketbazaar.com> Date: Mon, 11 Mar 2019 18:22:06 +0530 Subject: [PATCH 117/265] Updated code --- app/code/Magento/Customer/Test/Unit/Block/Address/GridTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Test/Unit/Block/Address/GridTest.php b/app/code/Magento/Customer/Test/Unit/Block/Address/GridTest.php index 9c511efd22c4..eeef2076d6ae 100644 --- a/app/code/Magento/Customer/Test/Unit/Block/Address/GridTest.php +++ b/app/code/Magento/Customer/Test/Unit/Block/Address/GridTest.php @@ -93,7 +93,7 @@ public function testGetChildHtml() /** @var \PHPUnit_Framework_MockObject_MockObject */ $addressCollection = $this->getMockBuilder(\Magento\Customer\Model\ResourceModel\Address\Collection::class) ->disableOriginalConstructor() - ->setMethods(['setOrder', 'setCustomerFilter', 'load']) + ->setMethods(['setOrder', 'setCustomerFilter', 'load','addFieldToFilter']) ->getMock(); $layout->expects($this->atLeastOnce())->method('getChildName')->with('NameInLayout', 'pager') @@ -108,6 +108,7 @@ public function testGetChildHtml() ->willReturnSelf(); $addressCollection->expects($this->atLeastOnce())->method('setCustomerFilter')->with([$customerId]) ->willReturnSelf(); + $addressCollection->expects(static::any())->method('addFieldToFilter')->willReturnSelf(); $this->addressCollectionFactory->expects($this->atLeastOnce())->method('create') ->willReturn($addressCollection); $block->expects($this->atLeastOnce())->method('setCollection')->with($addressCollection)->willReturnSelf(); From 311d739e70129a9566ffdba36dab76fac5392130 Mon Sep 17 00:00:00 2001 From: Maria Kovdrysh <kovdrysh@adobe.com> Date: Mon, 11 Mar 2019 08:23:29 -0500 Subject: [PATCH 118/265] MC-4436: Convert CreateSearchTermEntityTest to MFTF --- ...chTermEntityTest.xml => AdminCreateSearchTermEntityTest.xml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename app/code/Magento/CatalogSearch/Test/Mftf/Test/{CreateSearchTermEntityTest.xml => AdminCreateSearchTermEntityTest.xml} (98%) diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/CreateSearchTermEntityTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminCreateSearchTermEntityTest.xml similarity index 98% rename from app/code/Magento/CatalogSearch/Test/Mftf/Test/CreateSearchTermEntityTest.xml rename to app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminCreateSearchTermEntityTest.xml index a710f3dd8fa5..2b425f34f8a5 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/CreateSearchTermEntityTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminCreateSearchTermEntityTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="CreateSearchTermTest"> + <test name="AdminCreateSearchTermEntityTest"> <annotations> <stories value="Search terms"/> <title value="Create search term test"/> From fddee1591157e284595fff3ebfb031470c5478b2 Mon Sep 17 00:00:00 2001 From: Andrii Lugovyi <alugovyi@adobe.com> Date: Mon, 11 Mar 2019 09:40:55 -0500 Subject: [PATCH 119/265] MC-13613: Product mass update - MC-5665 Execution of heavy admin operations in asynchronous flow part2 --- .../Backend/ConsumerWebsiteAssign.php | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php b/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php index b47d65e31007..15f9e4833b26 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php @@ -39,11 +39,6 @@ class ConsumerWebsiteAssign */ private $serializer; - /** - * @var \Magento\Framework\Bulk\OperationManagementInterface - */ - private $operationManagement; - /** * @var \Magento\Catalog\Model\Indexer\Product\Price\Processor */ @@ -54,31 +49,36 @@ class ConsumerWebsiteAssign */ private $entityManager; + /** + * @var \Magento\MessageQueue\Api\Data\PoisonPillInterface + */ + private $poisonPill; + /** * @param \Magento\Catalog\Model\Indexer\Product\Flat\Processor $productFlatIndexerProcessor * @param \Magento\Catalog\Model\Indexer\Product\Price\Processor $productPriceIndexerProcessor - * @param \Magento\Framework\Bulk\OperationManagementInterface $operationManagement * @param \Magento\Catalog\Model\Product\Action $action * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Framework\Serialize\SerializerInterface $serializer * @param EntityManager $entityManager + * @param \Magento\MessageQueue\Api\PoisonPillPutInterface $poisonPill */ public function __construct( \Magento\Catalog\Model\Indexer\Product\Flat\Processor $productFlatIndexerProcessor, \Magento\Catalog\Model\Indexer\Product\Price\Processor $productPriceIndexerProcessor, - \Magento\Framework\Bulk\OperationManagementInterface $operationManagement, \Magento\Catalog\Model\Product\Action $action, \Psr\Log\LoggerInterface $logger, \Magento\Framework\Serialize\SerializerInterface $serializer, - EntityManager $entityManager + EntityManager $entityManager, + \Magento\MessageQueue\Api\PoisonPillPutInterface $poisonPill ) { $this->productFlatIndexerProcessor = $productFlatIndexerProcessor; $this->productAction = $action; $this->logger = $logger; $this->serializer = $serializer; - $this->operationManagement = $operationManagement; $this->productPriceIndexerProcessor = $productPriceIndexerProcessor; $this->entityManager = $entityManager; + $this->poisonPill = $poisonPill; } /** @@ -94,6 +94,7 @@ public function process(\Magento\AsynchronousOperations\Api\Data\OperationInterf try { $serializedData = $operation->getSerializedData(); $data = $this->serializer->unserialize($serializedData); + $this->poisonPill->put(); $this->execute($data); } catch (\Zend_Db_Adapter_Exception $e) { $this->logger->critical($e->getMessage()); From 6b0ebf49055d0ee4bb371ffc23186eef2eb02fb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20F=C3=BChr?= <d.fuehr@techdivision.com> Date: Mon, 11 Mar 2019 15:48:37 +0100 Subject: [PATCH 120/265] amend consecutive exception handling - support masking of root cause in frontend - remove stack trace for consecutive exception to comply to default behaviour --- app/code/Magento/Quote/Model/QuoteManagement.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Quote/Model/QuoteManagement.php b/app/code/Magento/Quote/Model/QuoteManagement.php index b10777f816fd..e1058c98271c 100644 --- a/app/code/Magento/Quote/Model/QuoteManagement.php +++ b/app/code/Magento/Quote/Model/QuoteManagement.php @@ -12,7 +12,6 @@ use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\StateException; -use Magento\Framework\Phrase; use Magento\Quote\Api\Data\PaymentInterface; use Magento\Quote\Model\Quote\Address\ToOrder as ToOrderConverter; use Magento\Quote\Model\Quote\Address\ToOrderAddress as ToOrderAddressConverter; @@ -548,15 +547,12 @@ protected function submitQuote(QuoteEntity $quote, $orderData = []) ] ); } catch (\Exception $consecutiveException) { - $message = new Phrase( - "An exception occurred on 'sales_model_service_quote_submit_failure' event: %1\n%2", - [ - $consecutiveException->getMessage(), - $consecutiveException->getTraceAsString() - ] + $message = sprintf( + "An exception occurred on 'sales_model_service_quote_submit_failure' event: %s", + $consecutiveException->getMessage() ); - throw new LocalizedException($message, $e); + throw new \Exception($message, 0, $e); } throw $e; } From 21eeb476c8003fe457fda395e00f8d0ceea2ed21 Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Mon, 11 Mar 2019 10:18:34 -0500 Subject: [PATCH 121/265] MC-13613: Product mass update --- app/code/Magento/Catalog/composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/composer.json b/app/code/Magento/Catalog/composer.json index 5c3ee3da8ca8..7174c35b191b 100644 --- a/app/code/Magento/Catalog/composer.json +++ b/app/code/Magento/Catalog/composer.json @@ -7,6 +7,7 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", + "magento/framework-message-queue": "*", "magento/module-authorization": "*", "magento/module-asynchronous-operations": "*", "magento/module-backend": "*", From 28b047ec570fce2752960668e5b4e6eda21593b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Mon, 11 Mar 2019 16:21:08 +0100 Subject: [PATCH 122/265] Move Magento\Sales\Model\Order\Address\Validator logic from construct to validate method --- .../Sales/Model/Order/Address/Validator.php | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Address/Validator.php b/app/code/Magento/Sales/Model/Order/Address/Validator.php index 31cb5bb1f60c..1b54dd2c127d 100644 --- a/app/code/Magento/Sales/Model/Order/Address/Validator.php +++ b/app/code/Magento/Sales/Model/Order/Address/Validator.php @@ -61,6 +61,16 @@ public function __construct( $this->countryFactory = $countryFactory; $this->eavConfig = $eavConfig ?: ObjectManager::getInstance() ->get(EavConfig::class); + } + + /** + * + * @param \Magento\Sales\Model\Order\Address $address + * @return array + */ + public function validate(Address $address) + { + $warnings = []; if ($this->isTelephoneRequired()) { $this->required['telephone'] = 'Phone Number'; @@ -73,16 +83,7 @@ public function __construct( if ($this->isFaxRequired()) { $this->required['fax'] = 'Fax'; } - } - /** - * - * @param \Magento\Sales\Model\Order\Address $address - * @return array - */ - public function validate(Address $address) - { - $warnings = []; foreach ($this->required as $code => $label) { if (!$address->hasData($code)) { $warnings[] = sprintf('"%s" is required. Enter and try again.', $label); From 5c7369e0cc487ac82ed66ffb630f14aaf7a88d4f Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Mon, 11 Mar 2019 10:47:05 -0500 Subject: [PATCH 123/265] MC-4425: Convert ImportProductsTest to MFTF Updated story and zephyr test Ids as per review comments --- .../Test/AdminImportProductsWithAddUpdateBehaviorTest.xml | 4 ++-- .../Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml index f3238293e571..ceb4e93e4e9a 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithAddUpdateBehaviorTest.xml @@ -11,11 +11,11 @@ <test name="AdminImportProductsWithAddUpdateBehaviorTest"> <annotations> <description value="Verify Magento native import products with add/update behavior."/> - <stories value="Verify Magento native import products with add/update behavior."/> + <stories value="Import Products"/> <features value="Import/Export"/> <title value="Verify Magento native import products with add/update behavior."/> <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-47724"/> + <testCaseId value="MC-14077"/> <group value="importExport"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml index d45b294a34e4..d63a5546716b 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithReplaceBehaviorTest.xml @@ -11,11 +11,11 @@ <test name="AdminImportProductsWithReplaceBehaviorTest" extends="AdminImportProductsWithAddUpdateBehaviorTest"> <annotations> <description value="Verify Magento native import products with replace behavior."/> - <stories value="Verify Magento native import products with replace behavior."/> + <stories value="Import Products"/> <features value="Import/Export"/> <title value="Verify Magento native import products with replace behavior."/> <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-47719"/> + <testCaseId value="MC-14076"/> <group value="importExport"/> <group value="mtf_migrated"/> </annotations> From ff0271c3539706b9dae240bc4f86c339a1dd0f5f Mon Sep 17 00:00:00 2001 From: Andrii Lugovyi <alugovyi@adobe.com> Date: Mon, 11 Mar 2019 13:26:46 -0500 Subject: [PATCH 124/265] MC-13613: Product mass update - MC-5665 Execution of heavy admin operations in asynchronous flow part2 --- .../Adminhtml/Product/Action/Attribute/Save.php | 11 ++++++++++- .../Model/Attribute/Backend/ConsumerWebsiteAssign.php | 11 +---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php index 63182dd5624e..e6bdca02061e 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php @@ -42,6 +42,11 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribut */ private $userContext; + /** + * @var \Magento\MessageQueue\Api\PoisonPillPutInterface + */ + private $pillPut; + /** * @param Action\Context $context * @param \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeHelper @@ -50,6 +55,7 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribut * @param \Magento\Framework\DataObject\IdentityGeneratorInterface $identityService * @param \Magento\Framework\Serialize\SerializerInterface $serializer * @param \Magento\Authorization\Model\UserContextInterface $userContext + * @param \Magento\MessageQueue\Api\PoisonPillPutInterface $pillPut */ public function __construct( Action\Context $context, @@ -58,7 +64,8 @@ public function __construct( \Magento\AsynchronousOperations\Api\Data\OperationInterfaceFactory $operartionFactory, \Magento\Framework\DataObject\IdentityGeneratorInterface $identityService, \Magento\Framework\Serialize\SerializerInterface $serializer, - \Magento\Authorization\Model\UserContextInterface $userContext + \Magento\Authorization\Model\UserContextInterface $userContext, + \Magento\MessageQueue\Api\PoisonPillPutInterface $pillPut ) { parent::__construct($context, $attributeHelper); $this->bulkManagement = $bulkManagement; @@ -66,6 +73,7 @@ public function __construct( $this->identityService = $identityService; $this->serializer = $serializer; $this->userContext = $userContext; + $this->pillPut = $pillPut; } /** @@ -190,6 +198,7 @@ private function publish( $productIdsChunk, $bulkUuid ); + $this->pillPut->put(); } if ($attributesData) { diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php b/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php index 15f9e4833b26..32ba39d9afd9 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/ConsumerWebsiteAssign.php @@ -49,11 +49,6 @@ class ConsumerWebsiteAssign */ private $entityManager; - /** - * @var \Magento\MessageQueue\Api\Data\PoisonPillInterface - */ - private $poisonPill; - /** * @param \Magento\Catalog\Model\Indexer\Product\Flat\Processor $productFlatIndexerProcessor * @param \Magento\Catalog\Model\Indexer\Product\Price\Processor $productPriceIndexerProcessor @@ -61,7 +56,6 @@ class ConsumerWebsiteAssign * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Framework\Serialize\SerializerInterface $serializer * @param EntityManager $entityManager - * @param \Magento\MessageQueue\Api\PoisonPillPutInterface $poisonPill */ public function __construct( \Magento\Catalog\Model\Indexer\Product\Flat\Processor $productFlatIndexerProcessor, @@ -69,8 +63,7 @@ public function __construct( \Magento\Catalog\Model\Product\Action $action, \Psr\Log\LoggerInterface $logger, \Magento\Framework\Serialize\SerializerInterface $serializer, - EntityManager $entityManager, - \Magento\MessageQueue\Api\PoisonPillPutInterface $poisonPill + EntityManager $entityManager ) { $this->productFlatIndexerProcessor = $productFlatIndexerProcessor; $this->productAction = $action; @@ -78,7 +71,6 @@ public function __construct( $this->serializer = $serializer; $this->productPriceIndexerProcessor = $productPriceIndexerProcessor; $this->entityManager = $entityManager; - $this->poisonPill = $poisonPill; } /** @@ -94,7 +86,6 @@ public function process(\Magento\AsynchronousOperations\Api\Data\OperationInterf try { $serializedData = $operation->getSerializedData(); $data = $this->serializer->unserialize($serializedData); - $this->poisonPill->put(); $this->execute($data); } catch (\Zend_Db_Adapter_Exception $e) { $this->logger->critical($e->getMessage()); From fa9b420971ebb3dc2129eae30934a11953e7d452 Mon Sep 17 00:00:00 2001 From: dharmesh vaja <dharmesh.vaja@rocketbazaar.com> Date: Tue, 12 Mar 2019 10:38:19 +0530 Subject: [PATCH 125/265] Update Code --- app/code/Magento/Customer/Test/Unit/Block/Address/GridTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Test/Unit/Block/Address/GridTest.php b/app/code/Magento/Customer/Test/Unit/Block/Address/GridTest.php index eeef2076d6ae..47f96b132b3d 100644 --- a/app/code/Magento/Customer/Test/Unit/Block/Address/GridTest.php +++ b/app/code/Magento/Customer/Test/Unit/Block/Address/GridTest.php @@ -138,7 +138,7 @@ public function testGetAdditionalAddresses() /** @var \PHPUnit_Framework_MockObject_MockObject */ $addressCollection = $this->getMockBuilder(\Magento\Customer\Model\ResourceModel\Address\Collection::class) ->disableOriginalConstructor() - ->setMethods(['setOrder', 'setCustomerFilter', 'load', 'getIterator']) + ->setMethods(['setOrder', 'setCustomerFilter', 'load', 'getIterator','addFieldToFilter']) ->getMock(); $addressDataModel = $this->getMockForAbstractClass(\Magento\Customer\Api\Data\AddressInterface::class); $address = $this->getMockBuilder(\Magento\Customer\Model\Address::class) @@ -158,6 +158,7 @@ public function testGetAdditionalAddresses() ->willReturnSelf(); $addressCollection->expects($this->atLeastOnce())->method('setCustomerFilter')->with([$customerId]) ->willReturnSelf(); + $addressCollection->expects(static::any())->method('addFieldToFilter')->willReturnSelf(); $addressCollection->expects($this->atLeastOnce())->method('getIterator') ->willReturn(new \ArrayIterator($collection)); $this->addressCollectionFactory->expects($this->atLeastOnce())->method('create') From 9dee0e111dc25b3ea45619370b876014ca855f0d Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Tue, 12 Mar 2019 07:59:18 +0200 Subject: [PATCH 126/265] 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 d2cb7c89990cb52f39f823c7b849ac749833e6c6 Mon Sep 17 00:00:00 2001 From: Andrii Lugovyi <alugovyi@adobe.com> Date: Tue, 12 Mar 2019 10:06:45 -0500 Subject: [PATCH 127/265] MC-13613: Product mass update - MC-5665 Execution of heavy admin operations in asynchronous flow part2 --- app/code/Magento/MessageQueue/Model/CallbackInvoker.php | 1 + .../MessageQueue/Model/ResourceModel/PoisonPill.php | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/MessageQueue/Model/CallbackInvoker.php b/app/code/Magento/MessageQueue/Model/CallbackInvoker.php index 33229c6432c5..1234228dad72 100644 --- a/app/code/Magento/MessageQueue/Model/CallbackInvoker.php +++ b/app/code/Magento/MessageQueue/Model/CallbackInvoker.php @@ -57,6 +57,7 @@ public function invoke(QueueInterface $queue, $maxNumberOfMessages, $callback) $message = $queue->dequeue(); } while ($message === null && (sleep(1) === 0)); if (false === $this->poisonPillCompare->isLatest($this->poisonPill)) { + $queue->reject($message); exit(0); } $callback($message); diff --git a/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php b/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php index b6149be04c47..ee3d09ec3eae 100644 --- a/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php +++ b/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php @@ -58,9 +58,10 @@ protected function _construct() */ public function put(): int { - /** @var PoisonPillInterface $poisonPill */ - $poisonPill = $this->poisonPillFactory->create(); - return $this->save($poisonPill)->getConnection()->lastInsertId(); + $connection = $this->getConnection(); + $table = $this->getMainTable(); + $connection->insert($table, []); + return (int)$connection->lastInsertId($table); } /** From ce21910264fbf31cdddc6c9e9670ba14548e88b9 Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Tue, 12 Mar 2019 11:08:05 -0500 Subject: [PATCH 128/265] MC-13613: Product mass update --- app/code/Magento/Catalog/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/composer.json b/app/code/Magento/Catalog/composer.json index 7174c35b191b..74e7f40b1f06 100644 --- a/app/code/Magento/Catalog/composer.json +++ b/app/code/Magento/Catalog/composer.json @@ -7,7 +7,7 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", - "magento/framework-message-queue": "*", + "magento/module-message-queue": "*", "magento/module-authorization": "*", "magento/module-asynchronous-operations": "*", "magento/module-backend": "*", From a99c53ab69c8be65d86341c360b285a51c815532 Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Tue, 12 Mar 2019 14:01:28 -0500 Subject: [PATCH 129/265] MC-13613: Product mass update --- .../Controller/Adminhtml/Product/Action/Attribute/Save.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php index e6bdca02061e..f63e5a681c42 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php @@ -198,7 +198,6 @@ private function publish( $productIdsChunk, $bulkUuid ); - $this->pillPut->put(); } if ($attributesData) { @@ -215,6 +214,7 @@ private function publish( } if (!empty($operations)) { + $this->pillPut->put(); $result = $this->bulkManagement->scheduleBulk( $bulkUuid, $operations, From 92f1f5785289b0f87a186a3e47642f9b88e9f940 Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Tue, 12 Mar 2019 14:12:39 -0500 Subject: [PATCH 130/265] MC-13613: Product mass update --- app/code/Magento/Catalog/composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Catalog/composer.json b/app/code/Magento/Catalog/composer.json index 74e7f40b1f06..7efd63fb7a4a 100644 --- a/app/code/Magento/Catalog/composer.json +++ b/app/code/Magento/Catalog/composer.json @@ -8,7 +8,6 @@ "php": "~7.1.3||~7.2.0", "magento/framework": "*", "magento/module-message-queue": "*", - "magento/module-authorization": "*", "magento/module-asynchronous-operations": "*", "magento/module-backend": "*", "magento/module-catalog-inventory": "*", From 82dfd9103b1e5641d1a939a9f426ccf0d81557a4 Mon Sep 17 00:00:00 2001 From: Alex Calandra <calandra.aj@gmail.com> Date: Tue, 12 Mar 2019 15:31:06 -0500 Subject: [PATCH 131/265] MQE-1477: Resolve Test Rerun Issues for Disable Wysiwyg Suite - Removing Two tests from disabled wysiwyg suite - Removing disable wysiwyg from one test --- .../Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml | 1 - app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml | 3 --- 2 files changed, 4 deletions(-) diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml index 05b7dfeeb395..03edc69e6d62 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml @@ -16,7 +16,6 @@ <description value="Admin should be able to add image to WYSIWYG content of Block"/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-84376"/> - <group value="WYSIWYGDisabled" /> </annotations> <before> <createData entity="_defaultCmsPage" stepKey="createCMSPage" /> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml index b4bcdaadf9a0..c3c92dc59c28 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml @@ -17,10 +17,8 @@ <severity value="CRITICAL"/> <testCaseId value="MAGETWO-94229"/> <group value="Cms"/> - <group value="WYSIWYGDisabled"/> </annotations> <before> - <magentoCLI command="config:set cms/wysiwyg/enabled disabled" stepKey="disableWYSIWYG"/> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="AdminCreateWebsite"> <argument name="newWebsiteName" value="secondWebsite"/> @@ -69,7 +67,6 @@ <argument name="websiteName" value="secondWebsite"/> </actionGroup> <actionGroup ref="DeleteCMSBlockActionGroup" stepKey="DeleteCMSBlockActionGroup"/> - <magentoCLI command="config:set cms/wysiwyg/enabled enabled" stepKey="enableWYSIWYG"/> </after> </test> </tests> From c00821496598b1bf9ecf3e183f18a289dbb3b9c2 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Tue, 12 Mar 2019 16:27:00 -0500 Subject: [PATCH 132/265] MQE-1476: Deliver weekly PR - Add mftf_migrated:yes tags --- .../BundleImportExport/Test/TestCase/ExportProductsTest.xml | 1 + .../CatalogImportExport/Test/TestCase/ExportProductsTest.xml | 4 ++++ .../Test/TestCase/CreateSearchTermEntityTest.xml | 1 + .../Test/TestCase/DeleteSearchTermEntityTest.xml | 1 + .../Test/TestCase/MassDeleteSearchTermEntityTest.xml | 1 + .../Test/TestCase/CreateDuplicateUrlCategoryEntityTest.xml | 2 +- .../Test/TestCase/CreateDuplicateUrlProductEntity.xml | 2 +- .../Test/TestCase/ExportProductsTest.xml | 3 +++ .../GroupedImportExport/Test/TestCase/ExportProductsTest.xml | 1 + .../Magento/Store/Test/TestCase/CreateWebsiteEntityTest.xml | 2 +- .../app/Magento/Store/Test/TestCase/DeleteStoreEntityTest.xml | 2 +- .../Magento/Store/Test/TestCase/UpdateWebsiteEntityTest.xml | 2 +- .../Test/TestCase/UpdateProductUrlRewriteEntityTest.xml | 1 + 13 files changed, 18 insertions(+), 5 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/BundleImportExport/Test/TestCase/ExportProductsTest.xml b/dev/tests/functional/tests/app/Magento/BundleImportExport/Test/TestCase/ExportProductsTest.xml index 3ad8cff31eaf..bfbe233b9dc1 100644 --- a/dev/tests/functional/tests/app/Magento/BundleImportExport/Test/TestCase/ExportProductsTest.xml +++ b/dev/tests/functional/tests/app/Magento/BundleImportExport/Test/TestCase/ExportProductsTest.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\CatalogImportExport\Test\TestCase\ExportProductsTest" summary="Export products"> <variation name="ExportProductsTestVariation4" summary="Export bundle products" ticketId="MAGETWO-30602"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="exportData" xsi:type="string">default</data> <data name="products/0" xsi:type="array"> <item name="fixture" xsi:type="string">bundleProduct</item> diff --git a/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ExportProductsTest.xml b/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ExportProductsTest.xml index b94f21371496..8fe25614d1d4 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ExportProductsTest.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ExportProductsTest.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\CatalogImportExport\Test\TestCase\ExportProductsTest" summary="Export products"> <variation name="ExportProductsTestVariation1" summary="Export simple product and configured products with assigned images" ticketId="MAGETWO-46112"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="exportData" xsi:type="string">default</data> <data name="products/0" xsi:type="array"> <item name="fixture" xsi:type="string">catalogProductSimple</item> @@ -27,6 +28,7 @@ </data> </variation> <variation name="ExportProductsTestVariation2" summary="Export simple and configured products with custom options" ticketId="MAGETWO-46113, MAGETWO-46109"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="exportData" xsi:type="string">default</data> <data name="products/0" xsi:type="array"> <item name="fixture" xsi:type="string">catalogProductSimple</item> @@ -43,6 +45,7 @@ <constraint name="Magento\CatalogImportExport\Test\Constraint\AssertExportProductDate" /> </variation> <variation name="ExportProductsTestVariation3" summary="Export simple product with custom attribute" ticketId="MAGETWO-46121"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="exportData" xsi:type="string">default</data> <data name="products/0" xsi:type="array"> <item name="fixture" xsi:type="string">catalogProductSimple</item> @@ -58,6 +61,7 @@ </data> </variation> <variation name="ExportProductsTestVariation5" summary="Export simple product assigned to Main Website and configurable product assigned to Custom Website" ticketId="MAGETWO-46114"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="issue" xsi:type="string">>MC-13864 Consumer always read config from memory</data> <data name="exportData" xsi:type="string">default</data> <data name="products/0" xsi:type="array"> diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/CreateSearchTermEntityTest.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/CreateSearchTermEntityTest.xml index 0437e0a5e999..8c465544a328 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/CreateSearchTermEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/CreateSearchTermEntityTest.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\CatalogSearch\Test\TestCase\CreateSearchTermEntityTest" summary="Create Search Term" ticketId="MAGETWO-26165"> <variation name="CreateSearchTermEntityTestVariation1"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="searchTerm/data/query_text/value" xsi:type="string">catalogProductSimple::sku</data> <data name="searchTerm/data/store_id" xsi:type="string">Main Website/Main Website Store/Default Store View</data> <data name="searchTerm/data/redirect" xsi:type="string">http://example.com/</data> diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/DeleteSearchTermEntityTest.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/DeleteSearchTermEntityTest.xml index a9cc0dfd34f9..8fdd7ef71552 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/DeleteSearchTermEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/DeleteSearchTermEntityTest.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\CatalogSearch\Test\TestCase\DeleteSearchTermEntityTest" summary="Delete Search Term" ticketId="MAGETWO-26491"> <variation name="DeleteSearchTermEntityTestVariation1"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="searchTerm/dataset" xsi:type="string">default</data> <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermSuccessDeleteMessage" /> <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermNotInGrid" /> diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/MassDeleteSearchTermEntityTest.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/MassDeleteSearchTermEntityTest.xml index 3bf4e521c4a0..3ef2b65c0224 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/MassDeleteSearchTermEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/MassDeleteSearchTermEntityTest.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\CatalogSearch\Test\TestCase\MassDeleteSearchTermEntityTest" summary="Mass Delete Search Term" ticketId="MAGETWO-26599"> <variation name="MassDeleteSearchTermEntityTestVariation1"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="searchTerms" xsi:type="string">catalogSearchQuery::default,catalogSearchQuery::default,catalogSearchQuery::default</data> <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermSuccessMassDeleteMessage" /> <constraint name="Magento\CatalogSearch\Test\Constraint\AssertSearchTermMassActionsNotInGrid" /> diff --git a/dev/tests/functional/tests/app/Magento/CatalogUrlRewrite/Test/TestCase/CreateDuplicateUrlCategoryEntityTest.xml b/dev/tests/functional/tests/app/Magento/CatalogUrlRewrite/Test/TestCase/CreateDuplicateUrlCategoryEntityTest.xml index 398054f1f0ed..8b15da5ecd2e 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogUrlRewrite/Test/TestCase/CreateDuplicateUrlCategoryEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogUrlRewrite/Test/TestCase/CreateDuplicateUrlCategoryEntityTest.xml @@ -14,7 +14,7 @@ <data name="category/data/include_in_menu" xsi:type="string">Yes</data> <data name="category/data/name" xsi:type="string">Subcategory%isolation%</data> <data name="category/data/url_key" xsi:type="string">subcategory-%isolation%</data> - <data name="tag" xsi:type="string">test_type:acceptance_test, test_type:extended_acceptance_test, severity:S1</data> + <data name="tag" xsi:type="string">test_type:acceptance_test, test_type:extended_acceptance_test, severity:S1, mftf_migrated:yes</data> <constraint name="Magento\CatalogUrlRewrite\Test\Constraint\AssertCategoryUrlDuplicateErrorMessage" /> </variation> </testCase> diff --git a/dev/tests/functional/tests/app/Magento/CatalogUrlRewrite/Test/TestCase/CreateDuplicateUrlProductEntity.xml b/dev/tests/functional/tests/app/Magento/CatalogUrlRewrite/Test/TestCase/CreateDuplicateUrlProductEntity.xml index 1116821f756a..8110ed1ed00b 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogUrlRewrite/Test/TestCase/CreateDuplicateUrlProductEntity.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogUrlRewrite/Test/TestCase/CreateDuplicateUrlProductEntity.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\CatalogUrlRewrite\Test\TestCase\CreateDuplicateUrlProductEntity" summary="Create Simple Product" ticketId="MAGETWO-69427"> <variation name="CreateDuplicateUrlProductEntityTestVariation1" summary="Create Duplicate Url Product"> - <data name="tag" xsi:type="string">test_type:acceptance_test, test_type:extended_acceptance_test, severity:S1</data> + <data name="tag" xsi:type="string">test_type:acceptance_test, test_type:extended_acceptance_test, severity:S1, mftf_migrated:yes</data> <data name="product/data/url_key" xsi:type="string">simple-product-%isolation%</data> <data name="product/data/name" xsi:type="string">Simple Product %isolation%</data> <data name="product/data/sku" xsi:type="string">simple_sku_%isolation%</data> diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableImportExport/Test/TestCase/ExportProductsTest.xml b/dev/tests/functional/tests/app/Magento/ConfigurableImportExport/Test/TestCase/ExportProductsTest.xml index 93240586ec92..15dcfd0a9e7e 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableImportExport/Test/TestCase/ExportProductsTest.xml +++ b/dev/tests/functional/tests/app/Magento/ConfigurableImportExport/Test/TestCase/ExportProductsTest.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\CatalogImportExport\Test\TestCase\ExportProductsTest" summary="Export products"> <variation name="ExportProductsTestVariation7" summary="Export simple product and configured products with assigned images" ticketId="MAGETWO-46112"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="exportData" xsi:type="string">default</data> <data name="products/1" xsi:type="array"> <item name="fixture" xsi:type="string">configurableProduct</item> @@ -30,6 +31,7 @@ </data> </variation> <variation name="ExportProductsTestVariation8" summary="Export simple and configured products with custom options" ticketId="MAGETWO-46113, MAGETWO-46109"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="exportData" xsi:type="string">default</data> <data name="products/1" xsi:type="array"> <item name="fixture" xsi:type="string">configurableProduct</item> @@ -45,6 +47,7 @@ </data> </variation> <variation name="ExportProductsTestVariation9" summary="Export simple product assigned to Main Website and configurable product assigned to Custom Website" ticketId="MAGETWO-46114"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="issue" xsi:type="string">>MC-13864 Consumer always read config from memory</data> <data name="exportData" xsi:type="string">default</data> <data name="products/1" xsi:type="array"> diff --git a/dev/tests/functional/tests/app/Magento/GroupedImportExport/Test/TestCase/ExportProductsTest.xml b/dev/tests/functional/tests/app/Magento/GroupedImportExport/Test/TestCase/ExportProductsTest.xml index cffcdbf45a6d..a110dc6a89f8 100644 --- a/dev/tests/functional/tests/app/Magento/GroupedImportExport/Test/TestCase/ExportProductsTest.xml +++ b/dev/tests/functional/tests/app/Magento/GroupedImportExport/Test/TestCase/ExportProductsTest.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\CatalogImportExport\Test\TestCase\ExportProductsTest" summary="Export products"> <variation name="ExportProductsTestVariation6" summary="Export grouped product with special price" ticketId="MAGETWO-46116"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="exportData" xsi:type="string">default</data> <data name="products/0" xsi:type="array"> <item name="fixture" xsi:type="string">groupedProduct</item> diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/CreateWebsiteEntityTest.xml b/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/CreateWebsiteEntityTest.xml index 5a547f69280e..e35ef853d1b6 100644 --- a/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/CreateWebsiteEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/CreateWebsiteEntityTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Store\Test\TestCase\CreateWebsiteEntityTest" summary="Create Website" ticketId="MAGETWO-27665"> <variation name="CreateWebsiteEntityTestVariation1"> - <data name="tag" xsi:type="string">severity:S1</data> + <data name="tag" xsi:type="string">severity:S1, mftf_migrated:yes</data> <data name="website/data/name" xsi:type="string">website_%isolation%</data> <data name="website/data/code" xsi:type="string">code_%isolation%</data> <constraint name="Magento\Store\Test\Constraint\AssertWebsiteSuccessSaveMessage" /> diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/DeleteStoreEntityTest.xml b/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/DeleteStoreEntityTest.xml index 306a9fd2024a..cd37c555fdb1 100644 --- a/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/DeleteStoreEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/DeleteStoreEntityTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Store\Test\TestCase\DeleteStoreEntityTest" summary="Delete Store View" ticketId="MAGETWO-27942"> <variation name="DeleteStoreEntityTestVariation1"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2, mftf_migrated:yes</data> <data name="store/dataset" xsi:type="string">custom</data> <data name="createBackup" xsi:type="string">Yes</data> <constraint name="Magento\Store\Test\Constraint\AssertStoreSuccessDeleteAndBackupMessages" /> diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/UpdateWebsiteEntityTest.xml b/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/UpdateWebsiteEntityTest.xml index ac857ad035f4..5db0e7f8baad 100644 --- a/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/UpdateWebsiteEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/UpdateWebsiteEntityTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Store\Test\TestCase\UpdateWebsiteEntityTest" summary="Update Website" ticketId="MAGETWO-27690"> <variation name="UpdateWebsiteEntityTestVariation1"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2, mftf_migrated:yes</data> <data name="websiteOrigin/dataset" xsi:type="string">custom_website</data> <data name="website/data/name" xsi:type="string">website_upd%isolation%</data> <data name="website/data/code" xsi:type="string">code_upd%isolation%</data> diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/UpdateProductUrlRewriteEntityTest.xml b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/UpdateProductUrlRewriteEntityTest.xml index 60de554d594d..8f12930aa417 100644 --- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/UpdateProductUrlRewriteEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/UpdateProductUrlRewriteEntityTest.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\UrlRewrite\Test\TestCase\UpdateProductUrlRewriteEntityTest" summary="Update Product URL Rewrites" ticketId="MAGETWO-24819"> <variation name="UpdateProductUrlRewriteEntityTestVariation1"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="urlRewrite/data/target_path/entity" xsi:type="string">product/%catalogProductSimple::product_100_dollar%</data> <data name="urlRewrite/data/store_id" xsi:type="string">Main Website/Main Website Store/Default Store View</data> <data name="urlRewrite/data/request_path" xsi:type="string">test_%isolation%.html</data> From bfbc5e95b74d0b96b86f3a13c978320c130971e0 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Tue, 12 Mar 2019 17:55:45 -0500 Subject: [PATCH 133/265] 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 7471fea4e1176f2e3d49139bbee25feabdedc53a Mon Sep 17 00:00:00 2001 From: Oleksandr Shmyheliuk <oshmyheliuk@magento.com> Date: Tue, 12 Mar 2019 22:54:27 -0500 Subject: [PATCH 134/265] MAGETWO-95675: Error during setup:static-content:deploy: Error while waiting for package deployed --- .../Deploy/Console/DeployStaticOptions.php | 14 ++++++++ .../Deploy/Service/DeployStaticContent.php | 34 +++++++++++-------- .../Unit/Service/DeployStaticContentTest.php | 32 +++++++++++++++++ 3 files changed, 66 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Deploy/Console/DeployStaticOptions.php b/app/code/Magento/Deploy/Console/DeployStaticOptions.php index 89cb3e4b3034..1c02d24f7e99 100644 --- a/app/code/Magento/Deploy/Console/DeployStaticOptions.php +++ b/app/code/Magento/Deploy/Console/DeployStaticOptions.php @@ -6,6 +6,7 @@ namespace Magento\Deploy\Console; +use Magento\Deploy\Process\Queue; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; @@ -57,6 +58,11 @@ class DeployStaticOptions */ const JOBS_AMOUNT = 'jobs'; + /** + * Key for max execution time option + */ + const MAX_EXECUTION_TIME = 'max-execution-time'; + /** * Force run of static deploy */ @@ -150,6 +156,7 @@ public function getOptionsList() * Basic options * * @return array + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ private function getBasicOptions() { @@ -216,6 +223,13 @@ private function getBasicOptions() 'Enable parallel processing using the specified number of jobs.', self::DEFAULT_JOBS_AMOUNT ), + new InputOption( + self::MAX_EXECUTION_TIME, + null, + InputOption::VALUE_OPTIONAL, + 'The maximum expected execution time of deployment static process (in seconds).', + Queue::DEFAULT_MAX_EXEC_TIME + ), new InputOption( self::SYMLINK_LOCALE, null, diff --git a/app/code/Magento/Deploy/Service/DeployStaticContent.php b/app/code/Magento/Deploy/Service/DeployStaticContent.php index 66ec6e7418af..854bf50e0af2 100644 --- a/app/code/Magento/Deploy/Service/DeployStaticContent.php +++ b/app/code/Magento/Deploy/Service/DeployStaticContent.php @@ -85,24 +85,26 @@ public function deploy(array $options) return; } - $queue = $this->queueFactory->create( - [ - 'logger' => $this->logger, - 'options' => $options, - 'maxProcesses' => $this->getProcessesAmount($options), - 'deployPackageService' => $this->objectManager->create( - \Magento\Deploy\Service\DeployPackage::class, - [ - 'logger' => $this->logger - ] - ) - ] - ); + $queueOptions = [ + 'logger' => $this->logger, + 'options' => $options, + 'maxProcesses' => $this->getProcessesAmount($options), + 'deployPackageService' => $this->objectManager->create( + \Magento\Deploy\Service\DeployPackage::class, + [ + 'logger' => $this->logger + ] + ) + ]; + + if (isset($options[Options::MAX_EXECUTION_TIME])) { + $queueOptions['maxExecTime'] = (int)$options[Options::MAX_EXECUTION_TIME]; + } $deployStrategy = $this->deployStrategyFactory->create( $options[Options::STRATEGY], [ - 'queue' => $queue + 'queue' => $this->queueFactory->create($queueOptions) ] ); @@ -133,6 +135,8 @@ public function deploy(array $options) } /** + * Returns amount of parallel processes, returns zero if option wasn't set. + * * @param array $options * @return int */ @@ -142,6 +146,8 @@ private function getProcessesAmount(array $options) } /** + * Checks if need to refresh only version. + * * @param array $options * @return bool */ diff --git a/app/code/Magento/Deploy/Test/Unit/Service/DeployStaticContentTest.php b/app/code/Magento/Deploy/Test/Unit/Service/DeployStaticContentTest.php index 75edc8cb4f6e..396381960e54 100644 --- a/app/code/Magento/Deploy/Test/Unit/Service/DeployStaticContentTest.php +++ b/app/code/Magento/Deploy/Test/Unit/Service/DeployStaticContentTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Deploy\Test\Unit\Service; +use Magento\Deploy\Console\DeployStaticOptions; use Magento\Deploy\Package\Package; use Magento\Deploy\Process\Queue; use Magento\Deploy\Service\Bundle; @@ -221,4 +222,35 @@ public function deployDataProvider() ] ]; } + + public function testMaxExecutionTimeOptionPassed() + { + $options = [ + DeployStaticOptions::MAX_EXECUTION_TIME => 100, + DeployStaticOptions::REFRESH_CONTENT_VERSION_ONLY => false, + DeployStaticOptions::JOBS_AMOUNT => 3, + DeployStaticOptions::STRATEGY => 'compact', + DeployStaticOptions::NO_JAVASCRIPT => true, + DeployStaticOptions::NO_HTML_MINIFY => true, + ]; + + $queueMock = $this->createMock(Queue::class); + $strategyMock = $this->createMock(CompactDeploy::class); + $this->queueFactory->expects($this->once()) + ->method('create') + ->with([ + 'logger' => $this->logger, + 'maxExecTime' => 100, + 'maxProcesses' => 3, + 'options' => $options, + 'deployPackageService' => null + ]) + ->willReturn($queueMock); + $this->deployStrategyFactory->expects($this->once()) + ->method('create') + ->with('compact', ['queue' => $queueMock]) + ->willReturn($strategyMock); + + $this->service->deploy($options); + } } From b62240110ec89edcefbb2f461c311086db9bc013 Mon Sep 17 00:00:00 2001 From: Vishal Gelani <vishalgelani99@gmail.com> Date: Wed, 13 Mar 2019 11:26:19 +0530 Subject: [PATCH 135/265] 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 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Backend:etc/menu.xsd"> <menu> <add id="Magento_Review::catalog_reviews_ratings_ratings" title="Rating" translate="title" module="Magento_Review" sortOrder="60" parent="Magento_Backend::stores_attributes" action="review/rating/" resource="Magento_Review::ratings"/> - <add id="Magento_Review::catalog_reviews_ratings_pending" title="Pending Reviews" translate="title" module="Magento_Review" parent="Magento_Backend::marketing_user_content" sortOrder="10" action="review/product/pending" resource="Magento_Review::pending"/> - <add id="Magento_Review::catalog_reviews_ratings_reviews_all" title="All Reviews" translate="title" module="Magento_Review" parent="Magento_Backend::marketing_user_content" sortOrder="20" action="review/product/index" resource="Magento_Review::reviews_all"/> + <add id="Magento_Review::catalog_reviews_ratings_pending" title="Pending Reviews" translate="title" module="Magento_Review" parent="Magento_Backend::marketing_user_content" sortOrder="20" action="review/product/pending" resource="Magento_Review::pending"/> + <add id="Magento_Review::catalog_reviews_ratings_reviews_all" title="All Reviews" translate="title" module="Magento_Review" parent="Magento_Backend::marketing_user_content" sortOrder="10" action="review/product/index" resource="Magento_Review::reviews_all"/> <add id="Magento_Review::report_review" title="Reviews" translate="title" module="Magento_Reports" sortOrder="20" parent="Magento_Reports::report" resource="Magento_Reports::review"/> <add id="Magento_Review::report_review_customer" title="By Customers" translate="title" sortOrder="10" module="Magento_Review" parent="Magento_Review::report_review" action="reports/report_review/customer" resource="Magento_Reports::review_customer"/> <add id="Magento_Review::report_review_product" title="By Products" translate="title" sortOrder="20" module="Magento_Review" parent="Magento_Review::report_review" action="reports/report_review/product" resource="Magento_Reports::review_product"/> From 417fda4da93bdd11f83eec9c7ed27f2a84cc2a44 Mon Sep 17 00:00:00 2001 From: Vishal Gelani <vishalgelani99@gmail.com> Date: Wed, 13 Mar 2019 11:27:01 +0530 Subject: [PATCH 136/265] 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 @@ </resource> <resource id="Magento_Backend::marketing"> <resource id="Magento_Backend::marketing_user_content"> - <resource id="Magento_Review::pending" title="Pending Reviews" translate="title" sortOrder="10"/> - <resource id="Magento_Review::reviews_all" title="All Reviews" translate="title" sortOrder="20"/> + <resource id="Magento_Review::pending" title="Pending Reviews" translate="title" sortOrder="20"/> + <resource id="Magento_Review::reviews_all" title="All Reviews" translate="title" sortOrder="10"/> </resource> </resource> </resource> From aa25ca575a2f379dc69f2b7edc77c15ebf60f118 Mon Sep 17 00:00:00 2001 From: Evgeny Petrov <evgeny_petrov@epam.com> Date: Wed, 13 Mar 2019 10:01:26 +0300 Subject: [PATCH 137/265] MAGETWO-57934: [GitHub] Can't use "configurable" as group name in attribute sets M2.1 #6123 --- app/code/Magento/Eav/Model/Entity/Attribute/Group.php | 4 ++-- .../Eav/Test/Unit/Model/Entity/Attribute/GroupTest.php | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Group.php b/app/code/Magento/Eav/Model/Entity/Attribute/Group.php index 4570dfb2af5e..2e5596456058 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Group.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Group.php @@ -127,8 +127,8 @@ public function beforeSave() ); $isReservedSystemName = in_array(strtolower($attributeGroupCode), $this->reservedSystemNames); if (empty($attributeGroupCode) || $isReservedSystemName) { - // in the following code sha1 is not used for security purposes - $attributeGroupCode = sha1(strtolower($groupName)); + // in the following code md5 is not used for security purposes + $attributeGroupCode = md5(strtolower($groupName)); } $this->setAttributeGroupCode($attributeGroupCode); } diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/GroupTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/GroupTest.php index 986ab517ceca..1584b922abaa 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/GroupTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/GroupTest.php @@ -68,9 +68,9 @@ public function attributeGroupCodeDataProvider() { return [ ['General Group', 'general-group'], - ['configurable', sha1('configurable')], - ['configurAble', sha1('configurable')], - ['///', sha1('///')], + ['configurable', md5('configurable')], + ['configurAble', md5('configurable')], + ['///', md5('///')], ]; } } From bfea663290be803d2ced87e55376790a27d47001 Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Wed, 13 Mar 2019 09:53:22 +0200 Subject: [PATCH 138/265] 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 46bbdd0e3d5796e427899b96c189888423cb55d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Wed, 13 Mar 2019 11:38:58 +0200 Subject: [PATCH 139/265] add choice class --- .../frontend/web/template/checkout/checkout-agreements.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html b/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html index db9da71ebf73..4b1a68624e54 100644 --- a/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html +++ b/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html @@ -8,7 +8,7 @@ <div class="checkout-agreements fieldset" data-bind="visible: isVisible"> <!-- ko foreach: agreements --> <!-- ko if: ($parent.isAgreementRequired($data)) --> - <div class="checkout-agreement field required"> + <div class="checkout-agreement field choice required"> <input type="checkbox" class="required-entry" data-bind="attr: { 'id': $parent.getCheckboxId($parentContext, agreementId), From a39e22181d94cdec03d40034da96264af32ef4c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Wed, 13 Mar 2019 11:53:26 +0200 Subject: [PATCH 140/265] fix choice aligment --- .../web/css/source/module/checkout/_payments.less | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_payments.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_payments.less index 5f8134193c67..35445b0989e8 100644 --- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_payments.less +++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_payments.less @@ -209,6 +209,13 @@ .fieldset { > .field { margin: 0 0 @indent__base; + + &.choice { + &:before { + padding: 0; + width: 0; + } + } &.type { .control { From 6ebdde1f138cb5316b83dd437a5db329289ee8cb Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Wed, 13 Mar 2019 13:21:47 +0200 Subject: [PATCH 141/265] 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 1cf01787820058222c9d05cdd64088ef18073b0a Mon Sep 17 00:00:00 2001 From: AleksLi <aleksliwork@gmail.com> Date: Wed, 13 Mar 2019 12:15:37 +0100 Subject: [PATCH 142/265] GraphQL-432: Replaced the places are using getMaskedQuoteId as method. Added new class to handle that. --- .../GetAvailablePaymentMethodsTest.php | 23 +++++------- .../GraphQl/Quote/Customer/GetCartTest.php | 31 ++++++++-------- .../Customer/SetBillingAddressOnCartTest.php | 23 +++++------- .../Customer/SetPaymentMethodOnCartTest.php | 37 ++++++++++--------- .../Customer/SetShippingMethodsOnCartTest.php | 20 ++++------ .../Guest/GetAvailablePaymentMethodsTest.php | 23 +++++------- .../GraphQl/Quote/Guest/GetCartTest.php | 23 +++++------- .../Guest/SetBillingAddressOnCartTest.php | 33 +++++++++-------- .../Guest/SetPaymentMethodOnCartTest.php | 35 +++++++++--------- .../Guest/SetShippingMethodsOnCartTest.php | 20 ++++------ .../Quote/SetShippingAddressOnCartTest.php | 24 ++++++------ 11 files changed, 137 insertions(+), 155 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php index 5695aab6854d..777dd5508c16 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php @@ -8,6 +8,7 @@ namespace Magento\GraphQl\Quote\Customer; use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\QuoteGraphQl\Model\GetMaskedQuoteIdByReversedQuoteId; use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; @@ -24,6 +25,11 @@ class GetAvailablePaymentMethodsTest extends GraphQlAbstract */ private $customerTokenService; + /** + * @var GetMaskedQuoteIdByReversedQuoteId + */ + private $getMaskedQuoteIdByReversedQuoteId; + /** * @var QuoteResource */ @@ -45,6 +51,7 @@ class GetAvailablePaymentMethodsTest extends GraphQlAbstract protected function setUp() { $objectManager = Bootstrap::getObjectManager(); + $this->getMaskedQuoteIdByReversedQuoteId = $objectManager->get(GetMaskedQuoteIdByReversedQuoteId::class); $this->quoteResource = $objectManager->get(QuoteResource::class); $this->quoteFactory = $objectManager->get(QuoteFactory::class); $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); @@ -53,10 +60,12 @@ protected function setUp() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php + * @throws \Exception + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testGetCartWithPaymentMethods() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_item_with_items'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_item_with_items'); $query = <<<QUERY { @@ -86,16 +95,4 @@ private function getHeaderMap(string $username = 'customer@example.com', string $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; return $headerMap; } - - /** - * @param string $reversedQuoteId - * @return string - */ - private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string - { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); - - return $this->quoteIdToMaskedId->execute((int)$quote->getId()); - } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php index fe9b7b3c49a7..5407d1e90121 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php @@ -8,6 +8,7 @@ namespace Magento\GraphQl\Quote\Customer; use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\QuoteGraphQl\Model\GetMaskedQuoteIdByReversedQuoteId; use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; @@ -19,6 +20,11 @@ */ class GetCartTest extends GraphQlAbstract { + /** + * @var GetMaskedQuoteIdByReversedQuoteId + */ + private $getMaskedQuoteIdByReversedQuoteId; + /** * @var QuoteResource */ @@ -42,6 +48,7 @@ class GetCartTest extends GraphQlAbstract protected function setUp() { $objectManager = Bootstrap::getObjectManager(); + $this->getMaskedQuoteIdByReversedQuoteId = $objectManager->get(GetMaskedQuoteIdByReversedQuoteId::class); $this->quoteResource = $objectManager->get(QuoteResource::class); $this->quoteFactory = $objectManager->get(QuoteFactory::class); $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); @@ -50,10 +57,12 @@ protected function setUp() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php + * @throws \Exception + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testGetCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_item_with_items'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_item_with_items'); $query = $this->getCartQuery($maskedQuoteId); $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); @@ -74,10 +83,12 @@ public function testGetCart() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @throws \Exception + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testGetGuestCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_with_simple_product_without_address'); $query = $this->getCartQuery($maskedQuoteId); $this->expectExceptionMessage( @@ -89,10 +100,12 @@ public function testGetGuestCart() /** * @magentoApiDataFixture Magento/Customer/_files/three_customers.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php + * @throws \Exception + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testGetAnotherCustomerCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_item_with_items'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_item_with_items'); $query = $this->getCartQuery($maskedQuoteId); $this->expectExceptionMessage( @@ -136,18 +149,6 @@ private function getCartQuery( QUERY; } - /** - * @param string $reversedQuoteId - * @return string - */ - private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string - { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); - - return $this->quoteIdToMaskedId->execute((int)$quote->getId()); - } - /** * @param string $username * @param string $password 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 1a93c011e80a..2d7b2caf4363 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 @@ -8,6 +8,7 @@ namespace Magento\GraphQl\Quote\Customer; use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\QuoteGraphQl\Model\GetMaskedQuoteIdByReversedQuoteId; use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; @@ -19,6 +20,11 @@ */ class SetBillingAddressOnCartTest extends GraphQlAbstract { + /** + * @var GetMaskedQuoteIdByReversedQuoteId + */ + private $getMaskedQuoteIdByReversedQuoteId; + /** * @var QuoteResource */ @@ -42,6 +48,7 @@ class SetBillingAddressOnCartTest extends GraphQlAbstract protected function setUp() { $objectManager = Bootstrap::getObjectManager(); + $this->getMaskedQuoteIdByReversedQuoteId = $objectManager->get(GetMaskedQuoteIdByReversedQuoteId::class); $this->quoteResource = $objectManager->get(QuoteResource::class); $this->quoteFactory = $objectManager->get(QuoteFactory::class); $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); @@ -307,10 +314,12 @@ public function testSetNewBillingAddressAndFromAddressBookAtSameTime() * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Customer/_files/customer_address.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @throws \Exception + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testSetBillingAddressToGuestCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_with_simple_product_without_address'); $query = <<<QUERY mutation { @@ -459,18 +468,6 @@ private function getHeaderMap(string $username = 'customer@example.com', string return $headerMap; } - /** - * @param string $reversedQuoteId - * @return string - */ - private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string - { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); - - return $this->quoteIdToMaskedId->execute((int)$quote->getId()); - } - /** * @param string $reversedQuoteId * @param int $customerId 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 bbc77b6c3974..97be79d79eef 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 @@ -9,6 +9,7 @@ use Magento\Integration\Api\CustomerTokenServiceInterface; use Magento\OfflinePayments\Model\Checkmo; +use Magento\QuoteGraphQl\Model\GetMaskedQuoteIdByReversedQuoteId; use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; @@ -25,6 +26,11 @@ class SetPaymentMethodOnCartTest extends GraphQlAbstract */ private $customerTokenService; + /** + * @var GetMaskedQuoteIdByReversedQuoteId + */ + private $getMaskedQuoteIdByReversedQuoteId; + /** * @var QuoteResource */ @@ -46,6 +52,7 @@ class SetPaymentMethodOnCartTest extends GraphQlAbstract protected function setUp() { $objectManager = Bootstrap::getObjectManager(); + $this->getMaskedQuoteIdByReversedQuoteId = $objectManager->get(GetMaskedQuoteIdByReversedQuoteId::class); $this->quoteResource = $objectManager->get(QuoteResource::class); $this->quoteFactory = $objectManager->get(QuoteFactory::class); $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); @@ -54,11 +61,13 @@ protected function setUp() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_and_address.php + * @throws \Exception + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testSetPaymentWithVirtualProduct() { $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_virtual_product'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_with_virtual_product'); $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); @@ -71,11 +80,13 @@ public function testSetPaymentWithVirtualProduct() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @throws \Exception + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testSetPaymentWithSimpleProduct() { $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_1'); $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); @@ -109,7 +120,7 @@ public function testSetPaymentWithSimpleProductWithoutAddress() public function testSetNonExistingPaymentMethod() { $methodCode = 'noway'; - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_1'); $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); $this->graphQlQuery($query, [], '', $this->getHeaderMap()); @@ -118,11 +129,13 @@ public function testSetNonExistingPaymentMethod() /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @throws \Exception + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testSetPaymentMethodToGuestCart() { $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_with_simple_product_without_address'); $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); @@ -136,11 +149,13 @@ public function testSetPaymentMethodToGuestCart() /** * @magentoApiDataFixture Magento/Customer/_files/three_customers.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @throws \Exception + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testSetPaymentMethodToAnotherCustomerCart() { $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_1'); $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); @@ -178,18 +193,6 @@ private function prepareMutationQuery( QUERY; } - /** - * @param string $reversedQuoteId - * @return string - */ - private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string - { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); - - return $this->quoteIdToMaskedId->execute((int)$quote->getId()); - } - /** * @param string $reversedQuoteId * @param int $customerId diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingMethodsOnCartTest.php index 736ea6944075..c8d32ba79690 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingMethodsOnCartTest.php @@ -8,6 +8,7 @@ namespace Magento\GraphQl\Quote\Customer; use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\QuoteGraphQl\Model\GetMaskedQuoteIdByReversedQuoteId; use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; @@ -19,6 +20,11 @@ */ class SetShippingMethodsOnCartTest extends GraphQlAbstract { + /** + * @var GetMaskedQuoteIdByReversedQuoteId + */ + private $getMaskedQuoteIdByReversedQuoteId; + /** * @var CustomerTokenServiceInterface */ @@ -45,6 +51,7 @@ class SetShippingMethodsOnCartTest extends GraphQlAbstract protected function setUp() { $objectManager = Bootstrap::getObjectManager(); + $this->getMaskedQuoteIdByReversedQuoteId = $objectManager->get(GetMaskedQuoteIdByReversedQuoteId::class); $this->quoteResource = $objectManager->get(QuoteResource::class); $this->quoteFactory = $objectManager->get(QuoteFactory::class); $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); @@ -161,19 +168,6 @@ private function prepareMutationQuery( QUERY; } - /** - * @param string $reversedQuoteId - * @return string - * @SuppressWarnings(PHPMD.UnusedPrivateMethod) - */ - private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string - { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); - - return $this->quoteIdToMaskedId->execute((int)$quote->getId()); - } - /** * @param string $reversedQuoteId * @param int $customerId diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php index a5a08aaf39fb..084401cdf891 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php @@ -10,6 +10,7 @@ use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\QuoteGraphQl\Model\GetMaskedQuoteIdByReversedQuoteId; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -18,6 +19,11 @@ */ class GetAvailablePaymentMethodsTest extends GraphQlAbstract { + /** + * @var GetMaskedQuoteIdByReversedQuoteId + */ + private $getMaskedQuoteIdByReversedQuoteId; + /** * @var QuoteResource */ @@ -39,6 +45,7 @@ class GetAvailablePaymentMethodsTest extends GraphQlAbstract protected function setUp() { $objectManager = Bootstrap::getObjectManager(); + $this->getMaskedQuoteIdByReversedQuoteId = $objectManager->get(GetMaskedQuoteIdByReversedQuoteId::class); $this->quoteResource = $objectManager->get(QuoteResource::class); $this->quoteFactory = $objectManager->get(QuoteFactory::class); $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); @@ -46,10 +53,12 @@ protected function setUp() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @throws \Exception + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testGetCartWithPaymentMethods() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_with_simple_product_without_address'); $query = <<<QUERY { @@ -74,16 +83,4 @@ public function testGetCartWithPaymentMethods() $response['cart']['available_payment_methods'][1]['title'] ); } - - /** - * @param string $reversedQuoteId - * @return string - */ - private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string - { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); - - return $this->quoteIdToMaskedId->execute((int)$quote->getId()); - } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php index b1d5e475c793..8aa449906618 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php @@ -11,6 +11,7 @@ use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\QuoteGraphQl\Model\GetMaskedQuoteIdByReversedQuoteId; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -19,6 +20,11 @@ */ class GetCartTest extends GraphQlAbstract { + /** + * @var GetMaskedQuoteIdByReversedQuoteId + */ + private $getMaskedQuoteIdByReversedQuoteId; + /** * @var QuoteResource */ @@ -42,6 +48,7 @@ class GetCartTest extends GraphQlAbstract protected function setUp() { $objectManager = Bootstrap::getObjectManager(); + $this->getMaskedQuoteIdByReversedQuoteId = $objectManager->get(GetMaskedQuoteIdByReversedQuoteId::class); $this->quoteResource = $objectManager->get(QuoteResource::class); $this->quoteFactory = $objectManager->get(QuoteFactory::class); $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); @@ -73,10 +80,12 @@ public function testGetCart() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php + * @throws \Exception + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testGetCustomerCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_item_with_items'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_item_with_items'); $query = $this->getCartQuery($maskedQuoteId); $this->expectExceptionMessage( @@ -119,18 +128,6 @@ private function getCartQuery( QUERY; } - /** - * @param string $reversedQuoteId - * @return string - */ - private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string - { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); - - return $this->quoteIdToMaskedId->execute((int)$quote->getId()); - } - /** * @param string $reversedQuoteId * @param int $customerId 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 880d6aa0f406..7b7f673d0493 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 @@ -7,6 +7,7 @@ namespace Magento\GraphQl\Quote\Guest; +use Magento\QuoteGraphQl\Model\GetMaskedQuoteIdByReversedQuoteId; use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; @@ -18,6 +19,11 @@ */ class SetBillingAddressOnCartTest extends GraphQlAbstract { + /** + * @var GetMaskedQuoteIdByReversedQuoteId + */ + private $getMaskedQuoteIdByReversedQuoteId; + /** * @var QuoteResource */ @@ -36,6 +42,7 @@ class SetBillingAddressOnCartTest extends GraphQlAbstract protected function setUp() { $objectManager = Bootstrap::getObjectManager(); + $this->getMaskedQuoteIdByReversedQuoteId = $objectManager->get(GetMaskedQuoteIdByReversedQuoteId::class); $this->quoteResource = $objectManager->get(QuoteResource::class); $this->quoteFactory = $objectManager->get(QuoteFactory::class); $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); @@ -43,10 +50,12 @@ protected function setUp() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @throws \Exception + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testSetNewBillingAddress() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_with_simple_product_without_address'); $query = <<<QUERY mutation { @@ -99,10 +108,12 @@ public function testSetNewBillingAddress() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @throws \Exception + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testSetNewBillingAddressWithUseForShippingParameter() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_with_simple_product_without_address'); $query = <<<QUERY mutation { @@ -173,10 +184,12 @@ public function testSetNewBillingAddressWithUseForShippingParameter() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @throws \Exception + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testSettBillingAddressToCustomerCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_1'); $query = <<<QUERY mutation { @@ -221,7 +234,7 @@ public function testSettBillingAddressToCustomerCart() */ public function testSetBillingAddressFromAddressBook() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_with_simple_product_without_address'); $query = <<<QUERY mutation { @@ -266,16 +279,4 @@ private function assertNewAddressFields(array $addressResponse, string $addressT $this->assertResponseFields($addressResponse, $assertionMap); } - - /** - * @param string $reversedQuoteId - * @return string - */ - private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string - { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); - - return $this->quoteIdToMaskedId->execute((int)$quote->getId()); - } } 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 7484b2af7569..22592d78d5d0 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 @@ -11,6 +11,7 @@ use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\QuoteGraphQl\Model\GetMaskedQuoteIdByReversedQuoteId; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -19,6 +20,11 @@ */ class SetPaymentMethodOnCartTest extends GraphQlAbstract { + /** + * @var GetMaskedQuoteIdByReversedQuoteId + */ + private $getMaskedQuoteIdByReversedQuoteId; + /** * @var QuoteResource */ @@ -40,6 +46,7 @@ class SetPaymentMethodOnCartTest extends GraphQlAbstract protected function setUp() { $objectManager = Bootstrap::getObjectManager(); + $this->getMaskedQuoteIdByReversedQuoteId = $objectManager->get(GetMaskedQuoteIdByReversedQuoteId::class); $this->quoteResource = $objectManager->get(QuoteResource::class); $this->quoteFactory = $objectManager->get(QuoteFactory::class); $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); @@ -47,11 +54,13 @@ protected function setUp() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_and_address.php + * @throws \Exception + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testSetPaymentWithVirtualProduct() { $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_virtual_product'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_with_virtual_product'); $this->unAssignCustomerFromQuote('test_order_with_virtual_product'); $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); @@ -65,11 +74,13 @@ public function testSetPaymentWithVirtualProduct() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @throws \Exception + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testSetPaymentWithSimpleProduct() { $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_1'); $this->unAssignCustomerFromQuote('test_order_1'); $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); @@ -89,7 +100,7 @@ public function testSetPaymentWithSimpleProduct() public function testSetPaymentWithSimpleProductWithoutAddress() { $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_with_simple_product_without_address'); $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); $this->graphQlQuery($query); @@ -103,7 +114,7 @@ public function testSetPaymentWithSimpleProductWithoutAddress() public function testSetNonExistingPaymentMethod() { $methodCode = 'noway'; - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_1'); $this->unAssignCustomerFromQuote('test_order_1'); $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); @@ -112,11 +123,13 @@ public function testSetNonExistingPaymentMethod() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @throws \Exception + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testSetPaymentMethodToCustomerCart() { $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_1'); $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); @@ -169,16 +182,4 @@ private function unAssignCustomerFromQuote( $this->quoteResource->save($quote); return $this->quoteIdToMaskedId->execute((int)$quote->getId()); } - - /** - * @param string $reversedQuoteId - * @return string - */ - private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string - { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); - - return $this->quoteIdToMaskedId->execute((int)$quote->getId()); - } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingMethodsOnCartTest.php index f159cb6f6151..07825922d718 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingMethodsOnCartTest.php @@ -10,6 +10,7 @@ use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\QuoteGraphQl\Model\GetMaskedQuoteIdByReversedQuoteId; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -18,6 +19,11 @@ */ class SetShippingMethodsOnCartTest extends GraphQlAbstract { + /** + * @var GetMaskedQuoteIdByReversedQuoteId + */ + private $getMaskedQuoteIdByReversedQuoteId; + /** * @var QuoteResource */ @@ -39,6 +45,7 @@ class SetShippingMethodsOnCartTest extends GraphQlAbstract protected function setUp() { $objectManager = Bootstrap::getObjectManager(); + $this->getMaskedQuoteIdByReversedQuoteId->execute(GetMaskedQuoteIdByReversedQuoteId::class); $this->quoteResource = $objectManager->get(QuoteResource::class); $this->quoteFactory = $objectManager->get(QuoteFactory::class); $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); @@ -151,17 +158,4 @@ private function prepareMutationQuery( QUERY; } - - /** - * @param string $reversedQuoteId - * @return string - * @SuppressWarnings(PHPMD.UnusedPrivateMethod) - */ - private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string - { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); - - return $this->quoteIdToMaskedId->execute((int)$quote->getId()); - } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php index 9352111de854..dcbf7220f24e 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php @@ -24,38 +24,38 @@ class SetShippingAddressOnCartTest extends GraphQlAbstract { /** - * @var QuoteResource + * @var CustomerTokenServiceInterface */ - private $quoteResource; + private $customerTokenService; /** - * @var QuoteFactory + * @var GetMaskedQuoteIdByReversedQuoteId */ - private $quoteFactory; + private $getMaskedQuoteIdByReversedQuoteId; /** - * @var QuoteIdToMaskedQuoteIdInterface + * @var QuoteResource */ - private $quoteIdToMaskedId; + private $quoteResource; /** - * @var GetMaskedQuoteIdByReversedQuoteId + * @var QuoteFactory */ - private $getMaskedQuoteIdByReversedQuoteId; + private $quoteFactory; /** - * @var CustomerTokenServiceInterface + * @var QuoteIdToMaskedQuoteIdInterface */ - private $customerTokenService; + private $quoteIdToMaskedId; protected function setUp() { $objectManager = Bootstrap::getObjectManager(); + $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); + $this->getMaskedQuoteIdByReversedQuoteId = $objectManager->get(GetMaskedQuoteIdByReversedQuoteId::class); $this->quoteResource = $objectManager->get(QuoteResource::class); $this->quoteFactory = $objectManager->get(QuoteFactory::class); - $this->getMaskedQuoteIdByReversedQuoteId = $objectManager->get(GetMaskedQuoteIdByReversedQuoteId::class); $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); - $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); } /** From 2d5f881ce37685bc6b1b34a778fb6ac4aa83f548 Mon Sep 17 00:00:00 2001 From: Yuliya Labudova <Yuliya_Labudova@epam.com> Date: Wed, 13 Mar 2019 14:38:37 +0300 Subject: [PATCH 143/265] MAGETWO-44165: [FT] Magento\Catalog\Test\TestCase\Product\ProductTypeSwitchingOnCreationTest fails - Migrate tests to mftf. --- .../Test/AdminCreateAndSwitchProductType.xml | 109 +++++++++++++++++- 1 file changed, 106 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml index e79f7f75cce3..50c17cfc4b9a 100755 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml @@ -53,9 +53,6 @@ <testCaseId value="MC-10930"/> <group value="catalog"/> <group value="mtf_migrated"/> - <skip> - <issueId value="MAGETWO-62808"/> - </skip> </annotations> <before> <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> @@ -83,4 +80,110 @@ <argument name="optionName" value="$createConfigProductAttributeOption1.option[store_labels][1][label]$"/> </actionGroup> </test> + <test name="AdminCreateSimpleProductSwitchToConfigurableTest" extends="AdminCreateSimpleProductSwitchToVirtualTest"> + <annotations> + <features value="Catalog"/> + <stories value="Product Type Switching"/> + <title value="Admin should be able to switch a new product from simple to configurable"/> + <description value="After selecting a simple product when adding Admin should be switch to configurable implicitly"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-29398"/> + <group value="catalog"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + </before> + <after> + <deleteData stepKey="deleteAttribute" createDataKey="createConfigProductAttribute"/> + </after> + <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="openProductFillForm"> + <argument name="productType" value="simple"/> + </actionGroup> + <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <comment before="createConfiguration" stepKey="beforeCreateConfiguration" userInput="Adding Configuration to Product"/> + <actionGroup ref="generateConfigurationsByAttributeCode" stepKey="createConfiguration" after="fillProductForm"> + <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + </actionGroup> + <actionGroup ref="saveConfiguredProduct" stepKey="saveProductForm"/> + <see selector="{{AdminProductGridSection.productGridCell('2', 'Type')}}" userInput="Configurable Product" stepKey="seeProductTypeInGrid"/> + <actionGroup ref="VerifyOptionInProductStorefront" stepKey="verifyConfigurableOption" after="AssertProductInStorefrontProductPage"> + <argument name="attributeCode" value="$createConfigProductAttribute.default_frontend_label$"/> + <argument name="optionName" value="$createConfigProductAttributeOption1.option[store_labels][1][label]$"/> + </actionGroup> + </test> + <test name="AdminCreateDownloadableProductSwitchToConfigurableTest"> + <annotations> + <features value="Catalog"/> + <stories value="Product Type Switching"/> + <title value="Admin should be able to switch a new product from downloadable to configurable"/> + <description value="After selecting a downloadable product when adding Admin should be switch to configurable implicitly"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-29398"/> + <group value="catalog"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + </before> + <after> + <actionGroup ref="GoToProductCatalogPage" stepKey="goToProductCatalogPage"/> + <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteConfigurableProduct"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <actionGroup ref="resetProductGridToDefaultView" stepKey="resetSearch"/> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + <deleteData stepKey="deleteAttribute" createDataKey="createConfigProductAttribute"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <!-- Open Dropdown and select downloadable product option --> + <comment stepKey="beforeOpenProductFillForm" userInput="Selecting Product from the Add Product Dropdown"/> + <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="openProductFillForm"> + <argument name="productType" value="downloadable"/> + </actionGroup> + + <scrollTo selector="{{AdminProductDownloadableSection.sectionHeader}}" stepKey="scrollToDownloadableInfo" /> + <wait stepKey="2" time="3"/> + <uncheckOption selector="{{AdminProductDownloadableSection.isDownloadableProduct}}" stepKey="checkIsDownloadable"/> + + <!-- Fill form for Downloadable Product Type --> + <comment stepKey="beforeFillProductForm" userInput="Filling Product Form"/> + <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <actionGroup ref="SetProductUrlKey" stepKey="setProductUrl"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <comment before="createConfiguration" stepKey="beforeCreateConfiguration" userInput="Adding Configuration to Product"/> + <actionGroup ref="generateConfigurationsByAttributeCode" stepKey="createConfiguration"> + <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + </actionGroup> + <actionGroup ref="saveConfiguredProduct" stepKey="saveProductForm"/> + <!-- Check that product was added with implicit type change --> + <comment stepKey="beforeVerify" userInput="Verify Product Type Assigned Correctly"/> + <actionGroup ref="GoToProductCatalogPage" stepKey="goToProductCatalogPage"/> + <actionGroup ref="resetProductGridToDefaultView" stepKey="resetSearch"/> + <actionGroup ref="filterProductGridByName" stepKey="searchForProduct"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <see selector="{{AdminProductGridSection.productGridCell('2', 'Type')}}" userInput="Configurable Product" stepKey="seeProductTypeInGrid"/> + <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <actionGroup ref="VerifyOptionInProductStorefront" stepKey="verifyConfigurableOption"> + <argument name="attributeCode" value="$createConfigProductAttribute.default_frontend_label$"/> + <argument name="optionName" value="$createConfigProductAttributeOption1.option[store_labels][1][label]$"/> + </actionGroup> + </test> </tests> From 06fe10916fcc622bd081c1bd83179b02696e9d62 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Wed, 13 Mar 2019 08:21:53 -0500 Subject: [PATCH 144/265] 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 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\UrlRewrite\Model; + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; + +/** + * @magentoDataFixture Magento/UrlRewrite/_files/url_rewrites.php + */ +class UrlFinderInterfaceTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var UrlFinderInterface + */ + private $urlFinder; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->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 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +$rewritesData = [ + [ + 'string', 'test_page1', 0 + ], + [ + 'string_permanent', 'test_page1', \Magento\UrlRewrite\Model\OptionProvider::PERMANENT + ], + [ + 'string_temporary', 'test_page1', \Magento\UrlRewrite\Model\OptionProvider::TEMPORARY + ], + [ + 'строка', 'test_page1', 0 + ], + [ + urlencode('строка'), 'test_page2', 0 + ], + [ + 'другая_строка', 'test_page1', \Magento\UrlRewrite\Model\OptionProvider::TEMPORARY + ], + [ + 'السلسلة', 'test_page1', 0 + ], +]; + +$rewriteResource = $objectManager->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 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +$registry = $objectManager->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 b151b2fea184b4f8592945f5e7c84ec7b8a9af1c Mon Sep 17 00:00:00 2001 From: Alex Calandra <calandra.aj@gmail.com> Date: Wed, 13 Mar 2019 08:32:24 -0500 Subject: [PATCH 145/265] MQE-1477: Resolve Test Rerun Issues for Disable Wysiwyg Suite - Adding wysiwyg cli back to static block test --- app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml index c3c92dc59c28..e6ab1c130606 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml @@ -19,6 +19,7 @@ <group value="Cms"/> </annotations> <before> + <magentoCLI command="config:set cms/wysiwyg/enabled disabled" stepKey="disableWYSIWYG"/> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="AdminCreateWebsite"> <argument name="newWebsiteName" value="secondWebsite"/> @@ -67,6 +68,7 @@ <argument name="websiteName" value="secondWebsite"/> </actionGroup> <actionGroup ref="DeleteCMSBlockActionGroup" stepKey="DeleteCMSBlockActionGroup"/> + <magentoCLI command="config:set cms/wysiwyg/enabled enabled" stepKey="enableWYSIWYG"/> </after> </test> </tests> From b9c43adcb19eb5c63fcb95fe355f46fff356c720 Mon Sep 17 00:00:00 2001 From: Alex Calandra <calandra.aj@gmail.com> Date: Tue, 12 Mar 2019 15:31:06 -0500 Subject: [PATCH 146/265] MQE-1477: Resolve Test Rerun Issues for Disable Wysiwyg Suite - Removing Two tests from disabled wysiwyg suite - Removing disable wysiwyg from one test --- .../Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml | 1 - app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml | 3 --- 2 files changed, 4 deletions(-) diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml index 05b7dfeeb395..03edc69e6d62 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml @@ -16,7 +16,6 @@ <description value="Admin should be able to add image to WYSIWYG content of Block"/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-84376"/> - <group value="WYSIWYGDisabled" /> </annotations> <before> <createData entity="_defaultCmsPage" stepKey="createCMSPage" /> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml index b4bcdaadf9a0..c3c92dc59c28 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml @@ -17,10 +17,8 @@ <severity value="CRITICAL"/> <testCaseId value="MAGETWO-94229"/> <group value="Cms"/> - <group value="WYSIWYGDisabled"/> </annotations> <before> - <magentoCLI command="config:set cms/wysiwyg/enabled disabled" stepKey="disableWYSIWYG"/> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="AdminCreateWebsite"> <argument name="newWebsiteName" value="secondWebsite"/> @@ -69,7 +67,6 @@ <argument name="websiteName" value="secondWebsite"/> </actionGroup> <actionGroup ref="DeleteCMSBlockActionGroup" stepKey="DeleteCMSBlockActionGroup"/> - <magentoCLI command="config:set cms/wysiwyg/enabled enabled" stepKey="enableWYSIWYG"/> </after> </test> </tests> From 8e87253b92941595a508e6d92f62d3c999e1e1f7 Mon Sep 17 00:00:00 2001 From: Alex Calandra <calandra.aj@gmail.com> Date: Wed, 13 Mar 2019 08:32:24 -0500 Subject: [PATCH 147/265] MQE-1477: Resolve Test Rerun Issues for Disable Wysiwyg Suite - Adding wysiwyg cli back to static block test --- app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml index c3c92dc59c28..e6ab1c130606 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml @@ -19,6 +19,7 @@ <group value="Cms"/> </annotations> <before> + <magentoCLI command="config:set cms/wysiwyg/enabled disabled" stepKey="disableWYSIWYG"/> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="AdminCreateWebsite"> <argument name="newWebsiteName" value="secondWebsite"/> @@ -67,6 +68,7 @@ <argument name="websiteName" value="secondWebsite"/> </actionGroup> <actionGroup ref="DeleteCMSBlockActionGroup" stepKey="DeleteCMSBlockActionGroup"/> + <magentoCLI command="config:set cms/wysiwyg/enabled enabled" stepKey="enableWYSIWYG"/> </after> </test> </tests> From 97a4be98b59c2ce564e057a0fb69f0b21d45a911 Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Wed, 13 Mar 2019 11:17:04 -0500 Subject: [PATCH 148/265] MC-13613: Product mass update --- app/code/Magento/Catalog/composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/composer.json b/app/code/Magento/Catalog/composer.json index 7efd63fb7a4a..74e7f40b1f06 100644 --- a/app/code/Magento/Catalog/composer.json +++ b/app/code/Magento/Catalog/composer.json @@ -8,6 +8,7 @@ "php": "~7.1.3||~7.2.0", "magento/framework": "*", "magento/module-message-queue": "*", + "magento/module-authorization": "*", "magento/module-asynchronous-operations": "*", "magento/module-backend": "*", "magento/module-catalog-inventory": "*", From cb9d6757f4cdf89cc5c1e970663b8eeb30606e40 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Wed, 13 Mar 2019 12:55:52 -0500 Subject: [PATCH 149/265] 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 7352a465ee259b6caa2e1f54870374e8a049c53a Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Wed, 13 Mar 2019 13:34:56 -0500 Subject: [PATCH 150/265] MC-13613: Product mass update --- .../Adminhtml/Product/Action/Attribute/Save.php | 11 +---------- app/code/Magento/Config/Model/Config.php | 13 ++++++++++++- app/code/Magento/Store/Model/Group.php | 12 +++++++++++- app/code/Magento/Store/Model/Store.php | 12 +++++++++++- app/code/Magento/Store/Model/Website.php | 13 +++++++++++-- .../Test/TestCase/ExportAdvancedPricingTest.xml | 1 - .../Test/TestCase/ExportProductsTest.xml | 1 - .../Test/TestCase/ExportProductsTest.xml | 1 - 8 files changed, 46 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php index f63e5a681c42..63182dd5624e 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php @@ -42,11 +42,6 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribut */ private $userContext; - /** - * @var \Magento\MessageQueue\Api\PoisonPillPutInterface - */ - private $pillPut; - /** * @param Action\Context $context * @param \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeHelper @@ -55,7 +50,6 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribut * @param \Magento\Framework\DataObject\IdentityGeneratorInterface $identityService * @param \Magento\Framework\Serialize\SerializerInterface $serializer * @param \Magento\Authorization\Model\UserContextInterface $userContext - * @param \Magento\MessageQueue\Api\PoisonPillPutInterface $pillPut */ public function __construct( Action\Context $context, @@ -64,8 +58,7 @@ public function __construct( \Magento\AsynchronousOperations\Api\Data\OperationInterfaceFactory $operartionFactory, \Magento\Framework\DataObject\IdentityGeneratorInterface $identityService, \Magento\Framework\Serialize\SerializerInterface $serializer, - \Magento\Authorization\Model\UserContextInterface $userContext, - \Magento\MessageQueue\Api\PoisonPillPutInterface $pillPut + \Magento\Authorization\Model\UserContextInterface $userContext ) { parent::__construct($context, $attributeHelper); $this->bulkManagement = $bulkManagement; @@ -73,7 +66,6 @@ public function __construct( $this->identityService = $identityService; $this->serializer = $serializer; $this->userContext = $userContext; - $this->pillPut = $pillPut; } /** @@ -214,7 +206,6 @@ private function publish( } if (!empty($operations)) { - $this->pillPut->put(); $result = $this->bulkManagement->scheduleBulk( $bulkUuid, $operations, diff --git a/app/code/Magento/Config/Model/Config.php b/app/code/Magento/Config/Model/Config.php index 6bf191c20a84..bd38d1451e1b 100644 --- a/app/code/Magento/Config/Model/Config.php +++ b/app/code/Magento/Config/Model/Config.php @@ -114,6 +114,11 @@ class Config extends \Magento\Framework\DataObject */ private $scopeTypeNormalizer; + /** + * @var \Magento\MessageQueue\Api\PoisonPillPutInterface + */ + private $pillPut; + /** * @param \Magento\Framework\App\Config\ReinitableConfigInterface $config * @param \Magento\Framework\Event\ManagerInterface $eventManager @@ -126,6 +131,7 @@ class Config extends \Magento\Framework\DataObject * @param array $data * @param ScopeResolverPool|null $scopeResolverPool * @param ScopeTypeNormalizer|null $scopeTypeNormalizer + * @param \Magento\MessageQueue\Api\PoisonPillPutInterface|null $pillPut * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -139,7 +145,8 @@ public function __construct( SettingChecker $settingChecker = null, array $data = [], ScopeResolverPool $scopeResolverPool = null, - ScopeTypeNormalizer $scopeTypeNormalizer = null + ScopeTypeNormalizer $scopeTypeNormalizer = null, + \Magento\MessageQueue\Api\PoisonPillPutInterface $pillPut = null ) { parent::__construct($data); $this->_eventManager = $eventManager; @@ -155,6 +162,8 @@ public function __construct( ?? ObjectManager::getInstance()->get(ScopeResolverPool::class); $this->scopeTypeNormalizer = $scopeTypeNormalizer ?? ObjectManager::getInstance()->get(ScopeTypeNormalizer::class); + $this->pillPut = $pillPut ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\MessageQueue\Api\PoisonPillPutInterface::class); } /** @@ -224,6 +233,8 @@ public function save() throw $e; } + $this->pillPut->put(); + return $this; } diff --git a/app/code/Magento/Store/Model/Group.php b/app/code/Magento/Store/Model/Group.php index ccc3c6549142..c2b3ce049880 100644 --- a/app/code/Magento/Store/Model/Group.php +++ b/app/code/Magento/Store/Model/Group.php @@ -100,6 +100,11 @@ class Group extends \Magento\Framework\Model\AbstractExtensibleModel implements */ private $eventManager; + /** + * @var \Magento\MessageQueue\Api\PoisonPillPutInterface + */ + private $pillPut; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -112,6 +117,7 @@ class Group extends \Magento\Framework\Model\AbstractExtensibleModel implements * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data * @param \Magento\Framework\Event\ManagerInterface|null $eventManager + * @param \Magento\MessageQueue\Api\PoisonPillPutInterface|null $pillPut * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -125,13 +131,16 @@ public function __construct( \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [], - \Magento\Framework\Event\ManagerInterface $eventManager = null + \Magento\Framework\Event\ManagerInterface $eventManager = null, + \Magento\MessageQueue\Api\PoisonPillPutInterface $pillPut = null ) { $this->_configDataResource = $configDataResource; $this->_storeListFactory = $storeListFactory; $this->_storeManager = $storeManager; $this->eventManager = $eventManager ?: \Magento\Framework\App\ObjectManager::getInstance() ->get(\Magento\Framework\Event\ManagerInterface::class); + $this->pillPut = $pillPut ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\MessageQueue\Api\PoisonPillPutInterface::class); parent::__construct( $context, $registry, @@ -445,6 +454,7 @@ public function afterSave() $this->_storeManager->reinitStores(); $this->eventManager->dispatch($this->_eventPrefix . '_save', ['group' => $group]); }); + $this->pillPut->put(); return parent::afterSave(); } diff --git a/app/code/Magento/Store/Model/Store.php b/app/code/Magento/Store/Model/Store.php index c1ad5bdcfc06..b2a515b198b1 100644 --- a/app/code/Magento/Store/Model/Store.php +++ b/app/code/Magento/Store/Model/Store.php @@ -326,6 +326,11 @@ class Store extends AbstractExtensibleModel implements */ private $eventManager; + /** + * @var \Magento\MessageQueue\Api\PoisonPillPutInterface + */ + private $pillPut; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -352,6 +357,7 @@ class Store extends AbstractExtensibleModel implements * @param bool $isCustomEntryPoint * @param array $data optional generic object data * @param \Magento\Framework\Event\ManagerInterface|null $eventManager + * @param \Magento\MessageQueue\Api\PoisonPillPutInterface|null $pillPut * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -380,7 +386,8 @@ public function __construct( \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, $isCustomEntryPoint = false, array $data = [], - \Magento\Framework\Event\ManagerInterface $eventManager = null + \Magento\Framework\Event\ManagerInterface $eventManager = null, + \Magento\MessageQueue\Api\PoisonPillPutInterface $pillPut = null ) { $this->_coreFileStorageDatabase = $coreFileStorageDatabase; $this->_config = $config; @@ -401,6 +408,8 @@ public function __construct( $this->websiteRepository = $websiteRepository; $this->eventManager = $eventManager ?: \Magento\Framework\App\ObjectManager::getInstance() ->get(\Magento\Framework\Event\ManagerInterface::class); + $this->pillPut = $pillPut ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\MessageQueue\Api\PoisonPillPutInterface::class); parent::__construct( $context, $registry, @@ -1077,6 +1086,7 @@ public function afterSave() $this->getResource()->addCommitCallback(function () use ($event, $store) { $this->eventManager->dispatch($event, ['store' => $store]); }); + $this->pillPut->put(); return parent::afterSave(); } diff --git a/app/code/Magento/Store/Model/Website.php b/app/code/Magento/Store/Model/Website.php index c9a7d0013fe0..53fc5fd7c927 100644 --- a/app/code/Magento/Store/Model/Website.php +++ b/app/code/Magento/Store/Model/Website.php @@ -159,6 +159,11 @@ class Website extends \Magento\Framework\Model\AbstractExtensibleModel implement */ protected $_currencyFactory; + /** + * @var \Magento\MessageQueue\Api\PoisonPillPutInterface + */ + private $pillPut; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -174,6 +179,7 @@ class Website extends \Magento\Framework\Model\AbstractExtensibleModel implement * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data + * @param \Magento\MessageQueue\Api\PoisonPillPutInterface|null $pillPut * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -190,7 +196,8 @@ public function __construct( \Magento\Directory\Model\CurrencyFactory $currencyFactory, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [] + array $data = [], + \Magento\MessageQueue\Api\PoisonPillPutInterface $pillPut = null ) { parent::__construct( $context, @@ -208,6 +215,8 @@ public function __construct( $this->_websiteFactory = $websiteFactory; $this->_storeManager = $storeManager; $this->_currencyFactory = $currencyFactory; + $this->pillPut = $pillPut ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\MessageQueue\Api\PoisonPillPutInterface::class); } /** @@ -581,7 +590,7 @@ public function afterSave() if ($this->isObjectNew()) { $this->_storeManager->reinitStores(); } - + $this->pillPut->put(); return parent::afterSave(); } diff --git a/dev/tests/functional/tests/app/Magento/AdvancedPricingImportExport/Test/TestCase/ExportAdvancedPricingTest.xml b/dev/tests/functional/tests/app/Magento/AdvancedPricingImportExport/Test/TestCase/ExportAdvancedPricingTest.xml index d069499da4aa..07646c2aceda 100644 --- a/dev/tests/functional/tests/app/Magento/AdvancedPricingImportExport/Test/TestCase/ExportAdvancedPricingTest.xml +++ b/dev/tests/functional/tests/app/Magento/AdvancedPricingImportExport/Test/TestCase/ExportAdvancedPricingTest.xml @@ -50,7 +50,6 @@ <constraint name="Magento\AdvancedPricingImportExport\Test\Constraint\AssertExportAdvancedPricing"/> </variation> <variation name="ExportAdvancedPricingTestVariation4" summary="Trying export product data for product available on main website with default currency and custom website with different currency" ticketId="MAGETWO-46153"> - <data name="issue" xsi:type="string">MC-13864 Consumer always read config from memory</data> <data name="configData" xsi:type="string">price_scope_website</data> <data name="exportData" xsi:type="string">csv_with_advanced_pricing</data> <data name="products/0" xsi:type="array"> diff --git a/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ExportProductsTest.xml b/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ExportProductsTest.xml index b94f21371496..40f535cd225a 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ExportProductsTest.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ExportProductsTest.xml @@ -58,7 +58,6 @@ </data> </variation> <variation name="ExportProductsTestVariation5" summary="Export simple product assigned to Main Website and configurable product assigned to Custom Website" ticketId="MAGETWO-46114"> - <data name="issue" xsi:type="string">>MC-13864 Consumer always read config from memory</data> <data name="exportData" xsi:type="string">default</data> <data name="products/0" xsi:type="array"> <item name="fixture" xsi:type="string">catalogProductSimple</item> diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableImportExport/Test/TestCase/ExportProductsTest.xml b/dev/tests/functional/tests/app/Magento/ConfigurableImportExport/Test/TestCase/ExportProductsTest.xml index 93240586ec92..4ab45eac8804 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableImportExport/Test/TestCase/ExportProductsTest.xml +++ b/dev/tests/functional/tests/app/Magento/ConfigurableImportExport/Test/TestCase/ExportProductsTest.xml @@ -45,7 +45,6 @@ </data> </variation> <variation name="ExportProductsTestVariation9" summary="Export simple product assigned to Main Website and configurable product assigned to Custom Website" ticketId="MAGETWO-46114"> - <data name="issue" xsi:type="string">>MC-13864 Consumer always read config from memory</data> <data name="exportData" xsi:type="string">default</data> <data name="products/1" xsi:type="array"> <item name="fixture" xsi:type="string">configurableProduct</item> From fde5cebadcf2dd62c74077b2805a716eabf2df34 Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Wed, 13 Mar 2019 15:09:31 -0500 Subject: [PATCH 151/265] MC-13613: Product mass update --- app/code/Magento/Config/composer.json | 1 + app/code/Magento/Store/composer.json | 1 + 2 files changed, 2 insertions(+) diff --git a/app/code/Magento/Config/composer.json b/app/code/Magento/Config/composer.json index 57c067d2cae2..3312fb630ccd 100644 --- a/app/code/Magento/Config/composer.json +++ b/app/code/Magento/Config/composer.json @@ -7,6 +7,7 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", + "magento/module-message-queue": "*", "magento/module-backend": "*", "magento/module-cron": "*", "magento/module-deploy": "*", diff --git a/app/code/Magento/Store/composer.json b/app/code/Magento/Store/composer.json index ebaa32b95f48..da408f105ccb 100644 --- a/app/code/Magento/Store/composer.json +++ b/app/code/Magento/Store/composer.json @@ -7,6 +7,7 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", + "magento/module-message-queue": "*", "magento/module-catalog": "*", "magento/module-config": "*", "magento/module-directory": "*", From f2eb1912381eb0b14539391154345a28edde94af Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Wed, 13 Mar 2019 15:41:09 -0500 Subject: [PATCH 152/265] MC-13613: Product mass update --- .../Test/TestCase/ExportAdvancedPricingTest.php | 16 +++++++++++++++- .../Test/TestCase/ExportProductsTest.php | 2 ++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/AdvancedPricingImportExport/Test/TestCase/ExportAdvancedPricingTest.php b/dev/tests/functional/tests/app/Magento/AdvancedPricingImportExport/Test/TestCase/ExportAdvancedPricingTest.php index 3020e69c0639..fefe0d2c126e 100644 --- a/dev/tests/functional/tests/app/Magento/AdvancedPricingImportExport/Test/TestCase/ExportAdvancedPricingTest.php +++ b/dev/tests/functional/tests/app/Magento/AdvancedPricingImportExport/Test/TestCase/ExportAdvancedPricingTest.php @@ -65,6 +65,13 @@ class ExportAdvancedPricingTest extends Injectable */ private $catalogProductIndex; + /** + * Cron command + * + * @var Cron + */ + private $cron; + /** * Run cron before tests running * @@ -85,18 +92,21 @@ public function __prepare( * @param FixtureFactory $fixtureFactory * @param AdminExportIndex $adminExportIndex * @param CatalogProductIndex $catalogProductIndexPage + * @param Cron $cron * @return void */ public function __inject( TestStepFactory $stepFactory, FixtureFactory $fixtureFactory, AdminExportIndex $adminExportIndex, - CatalogProductIndex $catalogProductIndexPage + CatalogProductIndex $catalogProductIndexPage, + Cron $cron ) { $this->stepFactory = $stepFactory; $this->fixtureFactory = $fixtureFactory; $this->adminExportIndex = $adminExportIndex; $this->catalogProductIndex = $catalogProductIndexPage; + $this->cron = $cron; } /** @@ -130,8 +140,12 @@ public function test( if ($website) { $website->persist(); $this->setupCurrencyForCustomWebsite($website, $currencyCustomWebsite); + $this->cron->run(); + $this->cron->run(); } $products = $this->prepareProducts($products, $website); + $this->cron->run(); + $this->cron->run(); $this->adminExportIndex->open(); $this->adminExportIndex->getExportedGrid()->deleteAllExportedFiles(); $exportData = $this->fixtureFactory->createByCode( diff --git a/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ExportProductsTest.php b/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ExportProductsTest.php index e55558482c1f..b5cd056fb99a 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ExportProductsTest.php +++ b/dev/tests/functional/tests/app/Magento/CatalogImportExport/Test/TestCase/ExportProductsTest.php @@ -104,6 +104,8 @@ public function test( $exportData->persist(); $this->adminExportIndex->getExportForm()->fill($exportData); $this->adminExportIndex->getFilterExport()->clickContinue(); + $this->cron->run(); + $this->cron->run(); $this->assertExportProduct->processAssert($export, $exportedFields, $products); } From 72574016f8ca169c45a5e3beda7456201699d5f8 Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Wed, 13 Mar 2019 18:18:43 -0500 Subject: [PATCH 153/265] GraphQL-432: Test coverage: 'reversed quote id to masked cart id' --- .../GetAvailablePaymentMethodsTest.php | 42 ++++------------- .../GraphQl/Quote/Customer/GetCartTest.php | 27 +++++------ .../Customer/SetBillingAddressOnCartTest.php | 20 ++++----- .../Customer/SetPaymentMethodOnCartTest.php | 40 +++++++---------- .../Customer/SetShippingAddressOnCartTest.php | 6 +-- .../Customer/SetShippingMethodsOnCartTest.php | 26 ++++++----- .../GetMaskedQuoteIdByReservedOrderId.php | 13 +++--- .../Guest/GetAvailablePaymentMethodsTest.php | 44 +++++------------- .../GraphQl/Quote/Guest/GetCartTest.php | 18 ++++---- .../Guest/SetBillingAddressOnCartTest.php | 16 +++---- .../Guest/SetPaymentMethodOnCartTest.php | 34 +++++++------- .../Guest/SetShippingAddressOnCartTest.php | 45 ++++++++++--------- .../Guest/SetShippingMethodsOnCartTest.php | 20 ++++++--- 13 files changed, 154 insertions(+), 197 deletions(-) rename app/code/Magento/QuoteGraphQl/Model/GetMaskedQuoteIdByReversedQuoteId.php => dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetMaskedQuoteIdByReservedOrderId.php (81%) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php index a7287ada093b..74921348f449 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php @@ -7,10 +7,8 @@ namespace Magento\GraphQl\Quote\Customer; +use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; use Magento\Integration\Api\CustomerTokenServiceInterface; -use Magento\Quote\Model\QuoteFactory; -use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; -use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -25,19 +23,9 @@ class GetAvailablePaymentMethodsTest extends GraphQlAbstract private $customerTokenService; /** - * @var QuoteResource + * @var GetMaskedQuoteIdByReservedOrderId */ - private $quoteResource; - - /** - * @var QuoteFactory - */ - private $quoteFactory; - - /** - * @var QuoteIdToMaskedQuoteIdInterface - */ - private $quoteIdToMaskedId; + private $getMaskedQuoteIdByReservedOrderId; /** * @inheritdoc @@ -45,9 +33,7 @@ class GetAvailablePaymentMethodsTest extends GraphQlAbstract protected function setUp() { $objectManager = Bootstrap::getObjectManager(); - $this->quoteResource = $objectManager->get(QuoteResource::class); - $this->quoteFactory = $objectManager->get(QuoteFactory::class); - $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); + $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); } @@ -56,7 +42,7 @@ protected function setUp() */ public function testGetCartWithPaymentMethods() { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId('test_order_item_with_items'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_item_with_items'); $query = $this->getQuery($maskedQuoteId); $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); @@ -76,7 +62,7 @@ public function testGetCartWithPaymentMethods() */ public function testGetPaymentMethodsFromGuestCart() { - $guestQuoteMaskedId = $this->getMaskedQuoteIdByReservedOrderId( + $guestQuoteMaskedId = $this->getMaskedQuoteIdByReservedOrderId->execute( 'test_order_with_virtual_product_without_address' ); $query = $this->getQuery($guestQuoteMaskedId); @@ -93,7 +79,7 @@ public function testGetPaymentMethodsFromGuestCart() */ public function testGetPaymentMethodsFromAnotherCustomerCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId('test_order_item_with_items'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_item_with_items'); $query = $this->getQuery($maskedQuoteId); $this->expectExceptionMessage( @@ -108,7 +94,7 @@ public function testGetPaymentMethodsFromAnotherCustomerCart() */ public function testGetPaymentMethodsIfPaymentsAreNotSet() { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId('test_order_item_with_items'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_item_with_items'); $query = $this->getQuery($maskedQuoteId); $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); @@ -158,16 +144,4 @@ private function getHeaderMap(string $username = 'customer@example.com', string $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; return $headerMap; } - - /** - * @param string $reservedOrderId - * @return string - */ - private function getMaskedQuoteIdByReservedOrderId(string $reservedOrderId): string - { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reservedOrderId, 'reserved_order_id'); - - return $this->quoteIdToMaskedId->execute((int)$quote->getId()); - } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php index 4b1509eef354..cc90f1aa1f21 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php @@ -7,6 +7,7 @@ namespace Magento\GraphQl\Quote\Customer; +use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; use Magento\Integration\Api\CustomerTokenServiceInterface; use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; @@ -19,6 +20,11 @@ */ class GetCartTest extends GraphQlAbstract { + /** + * @var GetMaskedQuoteIdByReservedOrderId + */ + private $getMaskedQuoteIdByReservedOrderId; + /** * @var QuoteResource */ @@ -42,6 +48,7 @@ class GetCartTest extends GraphQlAbstract protected function setUp() { $objectManager = Bootstrap::getObjectManager(); + $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); $this->quoteResource = $objectManager->get(QuoteResource::class); $this->quoteFactory = $objectManager->get(QuoteFactory::class); $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); @@ -53,7 +60,7 @@ protected function setUp() */ public function testGetCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_item_with_items'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_item_with_items'); $query = $this->getCartQuery($maskedQuoteId); $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); @@ -77,7 +84,9 @@ public function testGetCart() */ public function testGetGuestCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute( + 'test_order_with_simple_product_without_address' + ); $query = $this->getCartQuery($maskedQuoteId); $this->expectExceptionMessage( @@ -92,7 +101,7 @@ public function testGetGuestCart() */ public function testGetAnotherCustomerCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_item_with_items'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_item_with_items'); $query = $this->getCartQuery($maskedQuoteId); $this->expectExceptionMessage( @@ -156,18 +165,6 @@ private function getCartQuery( QUERY; } - /** - * @param string $reversedQuoteId - * @return string - */ - private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string - { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); - - return $this->quoteIdToMaskedId->execute((int)$quote->getId()); - } - /** * @param string $username * @param string $password 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 bae50fd29283..5183063e0c16 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 @@ -7,8 +7,8 @@ namespace Magento\GraphQl\Quote\Customer; +use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; use Magento\Integration\Api\CustomerTokenServiceInterface; -use Magento\QuoteGraphQl\Model\GetMaskedQuoteIdByReversedQuoteId; use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; @@ -21,9 +21,9 @@ class SetBillingAddressOnCartTest extends GraphQlAbstract { /** - * @var GetMaskedQuoteIdByReversedQuoteId + * @var GetMaskedQuoteIdByReservedOrderId */ - private $getMaskedQuoteIdByReversedQuoteId; + private $getMaskedQuoteIdByReservedOrderId; /** * @var QuoteResource @@ -48,7 +48,7 @@ class SetBillingAddressOnCartTest extends GraphQlAbstract protected function setUp() { $objectManager = Bootstrap::getObjectManager(); - $this->getMaskedQuoteIdByReversedQuoteId = $objectManager->get(GetMaskedQuoteIdByReversedQuoteId::class); + $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); $this->quoteResource = $objectManager->get(QuoteResource::class); $this->quoteFactory = $objectManager->get(QuoteFactory::class); $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); @@ -314,12 +314,12 @@ public function testSetNewBillingAddressAndFromAddressBookAtSameTime() * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Customer/_files/customer_address.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php - * @throws \Exception - * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testSetBillingAddressToGuestCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute( + 'test_order_with_simple_product_without_address' + ); $query = <<<QUERY mutation { @@ -489,16 +489,16 @@ private function getHeaderMap(string $username = 'customer@example.com', string } /** - * @param string $reversedQuoteId + * @param string $reversedOrderId * @param int $customerId * @return string */ private function assignQuoteToCustomer( - string $reversedQuoteId = 'test_order_with_simple_product_without_address', + string $reversedOrderId = 'test_order_with_simple_product_without_address', int $customerId = 1 ): string { $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + $this->quoteResource->load($quote, $reversedOrderId, 'reserved_order_id'); $quote->setCustomerId($customerId); $this->quoteResource->save($quote); return $this->quoteIdToMaskedId->execute((int)$quote->getId()); 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 e11102109442..5321f6967f99 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 @@ -7,9 +7,9 @@ namespace Magento\GraphQl\Quote\Customer; +use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; use Magento\Integration\Api\CustomerTokenServiceInterface; use Magento\OfflinePayments\Model\Checkmo; -use Magento\QuoteGraphQl\Model\GetMaskedQuoteIdByReversedQuoteId; use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; @@ -22,14 +22,14 @@ class SetPaymentMethodOnCartTest extends GraphQlAbstract { /** - * @var CustomerTokenServiceInterface + * @var GetMaskedQuoteIdByReservedOrderId */ - private $customerTokenService; + private $getMaskedQuoteIdByReservedOrderId; /** - * @var GetMaskedQuoteIdByReversedQuoteId + * @var CustomerTokenServiceInterface */ - private $getMaskedQuoteIdByReversedQuoteId; + private $customerTokenService; /** * @var QuoteResource @@ -52,7 +52,7 @@ class SetPaymentMethodOnCartTest extends GraphQlAbstract protected function setUp() { $objectManager = Bootstrap::getObjectManager(); - $this->getMaskedQuoteIdByReversedQuoteId = $objectManager->get(GetMaskedQuoteIdByReversedQuoteId::class); + $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); $this->quoteResource = $objectManager->get(QuoteResource::class); $this->quoteFactory = $objectManager->get(QuoteFactory::class); $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); @@ -61,13 +61,11 @@ protected function setUp() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_and_address.php - * @throws \Exception - * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testSetPaymentWithVirtualProduct() { $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_with_virtual_product'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_with_virtual_product'); $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); @@ -80,13 +78,11 @@ public function testSetPaymentWithVirtualProduct() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - * @throws \Exception - * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testSetPaymentWithSimpleProduct() { $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_1'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); @@ -120,7 +116,7 @@ public function testSetPaymentWithSimpleProductWithoutAddress() public function testSetNonExistingPaymentMethod() { $methodCode = 'noway'; - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_1'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); $this->graphQlQuery($query, [], '', $this->getHeaderMap()); @@ -129,13 +125,13 @@ public function testSetNonExistingPaymentMethod() /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php - * @throws \Exception - * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testSetPaymentMethodToGuestCart() { $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute( + 'test_order_with_simple_product_without_address' + ); $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); @@ -149,13 +145,11 @@ public function testSetPaymentMethodToGuestCart() /** * @magentoApiDataFixture Magento/Customer/_files/three_customers.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - * @throws \Exception - * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testSetPaymentMethodToAnotherCustomerCart() { $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_1'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); @@ -192,7 +186,7 @@ public function testPaymentMethodOnNonExistentCart() public function testReSetPayment() { /** @var \Magento\Quote\Model\Quote $quote */ - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1_with_payment'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1_with_payment'); $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); @@ -231,16 +225,16 @@ private function prepareMutationQuery( } /** - * @param string $reversedQuoteId + * @param string $reversedOrderId * @param int $customerId * @return string */ private function assignQuoteToCustomer( - string $reversedQuoteId, + string $reversedOrderId, int $customerId ): string { $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + $this->quoteResource->load($quote, $reversedOrderId, 'reserved_order_id'); $quote->setCustomerId($customerId); $this->quoteResource->save($quote); return $this->quoteIdToMaskedId->execute((int)$quote->getId()); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php index ae184b8930c0..4d4c61b3b4e5 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php @@ -513,16 +513,16 @@ private function getHeaderMap(string $username = 'customer@example.com', string } /** - * @param string $reversedQuoteId + * @param string $reversedOrderId * @param int $customerId * @return string */ private function assignQuoteToCustomer( - string $reversedQuoteId = 'test_order_with_simple_product_without_address', + string $reversedOrderId = 'test_order_with_simple_product_without_address', int $customerId = 1 ): string { $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + $this->quoteResource->load($quote, $reversedOrderId, 'reserved_order_id'); $quote->setCustomerId($customerId); $this->quoteResource->save($quote); return $this->quoteIdToMaskedId->execute((int)$quote->getId()); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingMethodsOnCartTest.php index c8d32ba79690..b5634f51cb36 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingMethodsOnCartTest.php @@ -8,7 +8,6 @@ namespace Magento\GraphQl\Quote\Customer; use Magento\Integration\Api\CustomerTokenServiceInterface; -use Magento\QuoteGraphQl\Model\GetMaskedQuoteIdByReversedQuoteId; use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; @@ -20,11 +19,6 @@ */ class SetShippingMethodsOnCartTest extends GraphQlAbstract { - /** - * @var GetMaskedQuoteIdByReversedQuoteId - */ - private $getMaskedQuoteIdByReversedQuoteId; - /** * @var CustomerTokenServiceInterface */ @@ -51,7 +45,6 @@ class SetShippingMethodsOnCartTest extends GraphQlAbstract protected function setUp() { $objectManager = Bootstrap::getObjectManager(); - $this->getMaskedQuoteIdByReversedQuoteId = $objectManager->get(GetMaskedQuoteIdByReversedQuoteId::class); $this->quoteResource = $objectManager->get(QuoteResource::class); $this->quoteFactory = $objectManager->get(QuoteFactory::class); $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); @@ -169,17 +162,30 @@ private function prepareMutationQuery( } /** - * @param string $reversedQuoteId + * @param string $reversedOrderId + * @return string + * @SuppressWarnings(PHPMD.UnusedPrivateMethod) + */ + private function getMaskedQuoteIdByReservedOrderId(string $reversedOrderId): string + { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, $reversedOrderId, 'reserved_order_id'); + + return $this->quoteIdToMaskedId->execute((int)$quote->getId()); + } + + /** + * @param string $reversedOrderId * @param int $customerId * @return string * @SuppressWarnings(PHPMD.UnusedPrivateMethod) */ private function assignQuoteToCustomer( - string $reversedQuoteId, + string $reversedOrderId, int $customerId ): string { $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + $this->quoteResource->load($quote, $reversedOrderId, 'reserved_order_id'); $quote->setCustomerId($customerId); $this->quoteResource->save($quote); return $this->quoteIdToMaskedId->execute((int)$quote->getId()); diff --git a/app/code/Magento/QuoteGraphQl/Model/GetMaskedQuoteIdByReversedQuoteId.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetMaskedQuoteIdByReservedOrderId.php similarity index 81% rename from app/code/Magento/QuoteGraphQl/Model/GetMaskedQuoteIdByReversedQuoteId.php rename to dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetMaskedQuoteIdByReservedOrderId.php index 60466170fc56..88462b8dbda3 100644 --- a/app/code/Magento/QuoteGraphQl/Model/GetMaskedQuoteIdByReversedQuoteId.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetMaskedQuoteIdByReservedOrderId.php @@ -5,13 +5,16 @@ */ declare(strict_types=1); -namespace Magento\QuoteGraphQl\Model; +namespace Magento\GraphQl\Quote; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\QuoteFactory; -class GetMaskedQuoteIdByReversedQuoteId +/** + * Get masked quote id by reserved order id + */ +class GetMaskedQuoteIdByReservedOrderId { /** * @var QuoteFactory @@ -44,14 +47,14 @@ public function __construct( } /** - * @param string $reversedQuoteId + * @param string $reversedOrderId * @return string * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function execute(string $reversedQuoteId): string + public function execute(string $reversedOrderId): string { $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + $this->quoteResource->load($quote, $reversedOrderId, 'reserved_order_id'); return $this->quoteIdToMaskedId->execute((int)$quote->getId()); } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php index c4dd9af490c9..7da7480ca390 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php @@ -7,9 +7,7 @@ namespace Magento\GraphQl\Quote\Guest; -use Magento\Quote\Model\QuoteFactory; -use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; -use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -19,19 +17,9 @@ class GetAvailablePaymentMethodsTest extends GraphQlAbstract { /** - * @var QuoteResource + * @var GetMaskedQuoteIdByReservedOrderId */ - private $quoteResource; - - /** - * @var QuoteFactory - */ - private $quoteFactory; - - /** - * @var QuoteIdToMaskedQuoteIdInterface - */ - private $quoteIdToMaskedId; + private $getMaskedQuoteIdByReservedOrderId; /** * @inheritdoc @@ -39,9 +27,7 @@ class GetAvailablePaymentMethodsTest extends GraphQlAbstract protected function setUp() { $objectManager = Bootstrap::getObjectManager(); - $this->quoteResource = $objectManager->get(QuoteResource::class); - $this->quoteFactory = $objectManager->get(QuoteFactory::class); - $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); + $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); } /** @@ -49,7 +35,9 @@ protected function setUp() */ public function testGetCartWithPaymentMethods() { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute( + 'test_order_with_simple_product_without_address' + ); $query = $this->getQuery($maskedQuoteId); $response = $this->graphQlQuery($query); @@ -75,7 +63,7 @@ public function testGetCartWithPaymentMethods() */ public function testGetPaymentMethodsFromCustomerCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId('test_order_1'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); $query = $this->getQuery($maskedQuoteId); $this->expectExceptionMessage( @@ -90,7 +78,9 @@ public function testGetPaymentMethodsFromCustomerCart() */ public function testGetPaymentMethodsIfPaymentsAreNotSet() { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute( + 'test_order_with_simple_product_without_address' + ); $query = $this->getQuery($maskedQuoteId); $response = $this->graphQlQuery($query); @@ -126,16 +116,4 @@ private function getQuery( } QUERY; } - - /** - * @param string $reservedOrderId - * @return string - */ - private function getMaskedQuoteIdByReservedOrderId(string $reservedOrderId): string - { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reservedOrderId, 'reserved_order_id'); - - return $this->quoteIdToMaskedId->execute((int)$quote->getId()); - } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php index 33c20e784930..847a7129750f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php @@ -7,11 +7,11 @@ namespace Magento\GraphQl\Quote\Guest; +use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; use Magento\Integration\Api\CustomerTokenServiceInterface; use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; -use Magento\QuoteGraphQl\Model\GetMaskedQuoteIdByReversedQuoteId; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -21,9 +21,9 @@ class GetCartTest extends GraphQlAbstract { /** - * @var GetMaskedQuoteIdByReversedQuoteId + * @var GetMaskedQuoteIdByReservedOrderId */ - private $getMaskedQuoteIdByReversedQuoteId; + private $getMaskedQuoteIdByReservedOrderId; /** * @var QuoteResource @@ -48,7 +48,7 @@ class GetCartTest extends GraphQlAbstract protected function setUp() { $objectManager = Bootstrap::getObjectManager(); - $this->getMaskedQuoteIdByReversedQuoteId = $objectManager->get(GetMaskedQuoteIdByReversedQuoteId::class); + $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); $this->quoteResource = $objectManager->get(QuoteResource::class); $this->quoteFactory = $objectManager->get(QuoteFactory::class); $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); @@ -80,12 +80,10 @@ public function testGetCart() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php - * @throws \Exception - * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testGetCustomerCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_item_with_items'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_item_with_items'); $query = $this->getCartQuery($maskedQuoteId); $this->expectExceptionMessage( @@ -147,15 +145,15 @@ private function getCartQuery( } /** - * @param string $reversedQuoteId + * @param string $reversedOrderId * @param int $customerId * @return string */ private function unAssignCustomerFromQuote( - string $reversedQuoteId + string $reversedOrderId ): string { $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + $this->quoteResource->load($quote, $reversedOrderId, 'reserved_order_id'); $quote->setCustomerId(0); $this->quoteResource->save($quote); return $this->quoteIdToMaskedId->execute((int)$quote->getId()); 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 b19caeeefbee..25227c7f7a7a 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 @@ -7,7 +7,7 @@ namespace Magento\GraphQl\Quote\Guest; -use Magento\QuoteGraphQl\Model\GetMaskedQuoteIdByReversedQuoteId; +use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; @@ -20,9 +20,9 @@ class SetBillingAddressOnCartTest extends GraphQlAbstract { /** - * @var GetMaskedQuoteIdByReversedQuoteId + * @var GetMaskedQuoteIdByReservedOrderId */ - private $getMaskedQuoteIdByReversedQuoteId; + private $getMaskedQuoteIdByReservedOrderId; /** * @var QuoteResource @@ -42,7 +42,7 @@ class SetBillingAddressOnCartTest extends GraphQlAbstract protected function setUp() { $objectManager = Bootstrap::getObjectManager(); - $this->getMaskedQuoteIdByReversedQuoteId = $objectManager->get(GetMaskedQuoteIdByReversedQuoteId::class); + $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); $this->quoteResource = $objectManager->get(QuoteResource::class); $this->quoteFactory = $objectManager->get(QuoteFactory::class); $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); @@ -55,7 +55,7 @@ protected function setUp() */ public function testSetNewBillingAddress() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_with_simple_product_without_address'); $query = <<<QUERY mutation { @@ -113,7 +113,7 @@ public function testSetNewBillingAddress() */ public function testSetNewBillingAddressWithUseForShippingParameter() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_with_simple_product_without_address'); $query = <<<QUERY mutation { @@ -189,7 +189,7 @@ public function testSetNewBillingAddressWithUseForShippingParameter() */ public function testSetBillingAddressToCustomerCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_1'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); $query = <<<QUERY mutation { @@ -234,7 +234,7 @@ public function testSetBillingAddressToCustomerCart() */ public function testSetBillingAddressFromAddressBook() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_with_simple_product_without_address'); $query = <<<QUERY mutation { 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 5d33a5d2edf8..cd8cc5948734 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 @@ -7,11 +7,11 @@ namespace Magento\GraphQl\Quote\Guest; +use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; use Magento\OfflinePayments\Model\Checkmo; use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; -use Magento\QuoteGraphQl\Model\GetMaskedQuoteIdByReversedQuoteId; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -21,9 +21,9 @@ class SetPaymentMethodOnCartTest extends GraphQlAbstract { /** - * @var GetMaskedQuoteIdByReversedQuoteId + * @var GetMaskedQuoteIdByReservedOrderId */ - private $getMaskedQuoteIdByReversedQuoteId; + private $getMaskedQuoteIdByReservedOrderId; /** * @var QuoteResource @@ -46,7 +46,7 @@ class SetPaymentMethodOnCartTest extends GraphQlAbstract protected function setUp() { $objectManager = Bootstrap::getObjectManager(); - $this->getMaskedQuoteIdByReversedQuoteId = $objectManager->get(GetMaskedQuoteIdByReversedQuoteId::class); + $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); $this->quoteResource = $objectManager->get(QuoteResource::class); $this->quoteFactory = $objectManager->get(QuoteFactory::class); $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); @@ -54,13 +54,11 @@ protected function setUp() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_and_address.php - * @throws \Exception - * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testSetPaymentWithVirtualProduct() { $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_with_virtual_product'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_with_virtual_product'); $this->unAssignCustomerFromQuote('test_order_with_virtual_product'); $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); @@ -74,13 +72,11 @@ public function testSetPaymentWithVirtualProduct() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - * @throws \Exception - * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testSetPaymentWithSimpleProduct() { $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_1'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); $this->unAssignCustomerFromQuote('test_order_1'); $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); @@ -100,7 +96,9 @@ public function testSetPaymentWithSimpleProduct() public function testSetPaymentWithSimpleProductWithoutAddress() { $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute( + 'test_order_with_simple_product_without_address' + ); $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); $this->graphQlQuery($query); @@ -114,7 +112,7 @@ public function testSetPaymentWithSimpleProductWithoutAddress() public function testSetNonExistingPaymentMethod() { $methodCode = 'noway'; - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_1'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); $this->unAssignCustomerFromQuote('test_order_1'); $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); @@ -123,13 +121,11 @@ public function testSetNonExistingPaymentMethod() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - * @throws \Exception - * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testSetPaymentMethodToCustomerCart() { $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId->execute('test_order_1'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); @@ -164,7 +160,7 @@ public function testSetPaymentOnNonExistentCart() public function testReSetPayment() { /** @var \Magento\Quote\Model\Quote $quote */ - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1_with_payment'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1_with_payment'); $this->unAssignCustomerFromQuote('test_order_1_with_payment'); $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); @@ -206,15 +202,15 @@ private function prepareMutationQuery( } /** - * @param string $reversedQuoteId + * @param string $reversedOrderId * @param int $customerId * @return string */ private function unAssignCustomerFromQuote( - string $reversedQuoteId + string $reversedOrderId ): string { $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + $this->quoteResource->load($quote, $reversedOrderId, 'reserved_order_id'); $quote->setCustomerId(0); $this->quoteResource->save($quote); return $this->quoteIdToMaskedId->execute((int)$quote->getId()); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php index 33aeb2b902a8..5c6e0e9cec55 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php @@ -8,6 +8,7 @@ namespace Magento\GraphQl\Quote\Guest; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; use Magento\Multishipping\Helper\Data; use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; @@ -21,6 +22,11 @@ */ class SetShippingAddressOnCartTest extends GraphQlAbstract { + /** + * @var GetMaskedQuoteIdByReservedOrderId + */ + private $getMaskedQuoteIdByReservedOrderId; + /** * @var QuoteResource */ @@ -39,6 +45,7 @@ class SetShippingAddressOnCartTest extends GraphQlAbstract protected function setUp() { $objectManager = Bootstrap::getObjectManager(); + $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); $this->quoteResource = $objectManager->get(QuoteResource::class); $this->quoteFactory = $objectManager->get(QuoteFactory::class); $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); @@ -49,7 +56,9 @@ protected function setUp() */ public function testSetNewShippingAddress() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute( + 'test_order_with_simple_product_without_address' + ); $query = <<<QUERY mutation { @@ -109,7 +118,9 @@ public function testSetNewShippingAddress() */ public function testSetNewShippingAddressOnQuoteWithVirtualProducts() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_virtual_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute( + 'test_order_with_virtual_product_without_address' + ); $query = <<<QUERY mutation { @@ -152,7 +163,9 @@ public function testSetNewShippingAddressOnQuoteWithVirtualProducts() */ public function testSetShippingAddressFromAddressBook() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute( + 'test_order_with_simple_product_without_address' + ); $query = <<<QUERY mutation { @@ -222,7 +235,9 @@ public function testSetShippingAddressToCustomerCart() */ public function testSetNewShippingAddressWithMissedRequiredParameters(string $input, string $message) { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute( + 'test_order_with_simple_product_without_address' + ); $query = <<<QUERY mutation { @@ -272,7 +287,9 @@ public function dataProviderUpdateWithMissedRequiredParameters() */ public function testSetMultipleNewShippingAddresses() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute( + 'test_order_with_simple_product_without_address' + ); $query = <<<QUERY mutation { @@ -345,28 +362,16 @@ private function assertNewShippingAddressFields(array $shippingAddressResponse): } /** - * @param string $reversedQuoteId - * @return string - */ - private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string - { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); - - return $this->quoteIdToMaskedId->execute((int)$quote->getId()); - } - - /** - * @param string $reversedQuoteId + * @param string $reversedOrderId * @param int $customerId * @return string */ private function assignQuoteToCustomer( - string $reversedQuoteId = 'test_order_with_simple_product_without_address', + string $reversedOrderId = 'test_order_with_simple_product_without_address', int $customerId = 1 ): string { $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + $this->quoteResource->load($quote, $reversedOrderId, 'reserved_order_id'); $quote->setCustomerId($customerId); $this->quoteResource->save($quote); return $this->quoteIdToMaskedId->execute((int)$quote->getId()); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingMethodsOnCartTest.php index 07825922d718..bf08ce8adce6 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingMethodsOnCartTest.php @@ -10,7 +10,6 @@ use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; -use Magento\QuoteGraphQl\Model\GetMaskedQuoteIdByReversedQuoteId; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -19,11 +18,6 @@ */ class SetShippingMethodsOnCartTest extends GraphQlAbstract { - /** - * @var GetMaskedQuoteIdByReversedQuoteId - */ - private $getMaskedQuoteIdByReversedQuoteId; - /** * @var QuoteResource */ @@ -45,7 +39,6 @@ class SetShippingMethodsOnCartTest extends GraphQlAbstract protected function setUp() { $objectManager = Bootstrap::getObjectManager(); - $this->getMaskedQuoteIdByReversedQuoteId->execute(GetMaskedQuoteIdByReversedQuoteId::class); $this->quoteResource = $objectManager->get(QuoteResource::class); $this->quoteFactory = $objectManager->get(QuoteFactory::class); $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); @@ -158,4 +151,17 @@ private function prepareMutationQuery( QUERY; } + + /** + * @param string $reversedOrderId + * @return string + * @SuppressWarnings(PHPMD.UnusedPrivateMethod) + */ + private function getMaskedQuoteIdByReservedOrderId(string $reversedOrderId): string + { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, $reversedOrderId, 'reserved_order_id'); + + return $this->quoteIdToMaskedId->execute((int)$quote->getId()); + } } From 0b49a94f51b7aa52bd267aaf161a68c24f039912 Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Thu, 14 Mar 2019 07:49:41 -0500 Subject: [PATCH 154/265] MC-13613: Product mass update --- app/code/Magento/Store/Model/Group.php | 31 +++++++++++++------ app/code/Magento/Store/Model/Website.php | 22 ++++++++----- .../Test/Unit/MessageProcessorTest.php | 6 ++-- 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/app/code/Magento/Store/Model/Group.php b/app/code/Magento/Store/Model/Group.php index c2b3ce049880..5de93c1ffdbc 100644 --- a/app/code/Magento/Store/Model/Group.php +++ b/app/code/Magento/Store/Model/Group.php @@ -111,10 +111,10 @@ class Group extends \Magento\Framework\Model\AbstractExtensibleModel implements * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory * @param \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory * @param \Magento\Config\Model\ResourceModel\Config\Data $configDataResource - * @param \Magento\Store\Model\Store $store - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource - * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection + * @param ResourceModel\Store\CollectionFactory $storeListFactory + * @param StoreManagerInterface $storeManager + * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource + * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection * @param array $data * @param \Magento\Framework\Event\ManagerInterface|null $eventManager * @param \Magento\MessageQueue\Api\PoisonPillPutInterface|null $pillPut @@ -253,6 +253,8 @@ public function getStoreCodes() } /** + * Get stores count + * * @return int */ public function getStoresCount() @@ -358,6 +360,8 @@ public function isCanDelete() } /** + * Get default store id + * * @return mixed */ public function getDefaultStoreId() @@ -374,6 +378,8 @@ public function setDefaultStoreId($defaultStoreId) } /** + * Get root category id + * * @return mixed */ public function getRootCategoryId() @@ -390,6 +396,8 @@ public function setRootCategoryId($rootCategoryId) } /** + * Get website id + * * @return mixed */ public function getWebsiteId() @@ -406,7 +414,10 @@ public function setWebsiteId($websiteId) } /** - * @return $this + * Before delete action + * + * @return \Magento\Framework\Model\AbstractExtensibleModel + * @throws \Magento\Framework\Exception\LocalizedException */ public function beforeDelete() { @@ -483,7 +494,7 @@ public function getIdentities() } /** - * {@inheritdoc} + * @inheritdoc */ public function getName() { @@ -517,7 +528,7 @@ public function setCode($code) } /** - * {@inheritdoc} + * @inheritdoc */ public function getExtensionAttributes() { @@ -525,7 +536,7 @@ public function getExtensionAttributes() } /** - * {@inheritdoc} + * @inheritdoc */ public function setExtensionAttributes( \Magento\Store\Api\Data\GroupExtensionInterface $extensionAttributes @@ -534,7 +545,7 @@ public function setExtensionAttributes( } /** - * {@inheritdoc} + * @inheritdoc * @since 100.1.0 */ public function getScopeType() @@ -543,7 +554,7 @@ public function getScopeType() } /** - * {@inheritdoc} + * @inheritdoc * @since 100.1.0 */ public function getScopeTypeName() diff --git a/app/code/Magento/Store/Model/Website.php b/app/code/Magento/Store/Model/Website.php index 53fc5fd7c927..036135d4de57 100644 --- a/app/code/Magento/Store/Model/Website.php +++ b/app/code/Magento/Store/Model/Website.php @@ -220,7 +220,7 @@ public function __construct( } /** - * init model + * Init model * * @return void */ @@ -504,6 +504,8 @@ public function getWebsiteGroupStore() } /** + * Get default group id + * * @return mixed */ public function getDefaultGroupId() @@ -520,6 +522,8 @@ public function setDefaultGroupId($defaultGroupId) } /** + * Get code + * * @return mixed */ public function getCode() @@ -552,7 +556,10 @@ public function setName($name) } /** - * @return $this + * Before delete action + * + * @return \Magento\Framework\Model\AbstractExtensibleModel + * @throws \Magento\Framework\Exception\LocalizedException */ public function beforeDelete() { @@ -644,8 +651,7 @@ public function getDefaultStore() } /** - * Retrieve default stores select object - * Select fields website_id, store_id + * Retrieve default stores select object, select fields website_id, store_id * * @param bool $withDefault include/exclude default admin website * @return \Magento\Framework\DB\Select @@ -680,7 +686,7 @@ public function getIdentities() } /** - * {@inheritdoc} + * @inheritdoc * @since 100.1.0 */ public function getScopeType() @@ -689,7 +695,7 @@ public function getScopeType() } /** - * {@inheritdoc} + * @inheritdoc * @since 100.1.0 */ public function getScopeTypeName() @@ -698,7 +704,7 @@ public function getScopeTypeName() } /** - * {@inheritdoc} + * @inheritdoc */ public function getExtensionAttributes() { @@ -706,7 +712,7 @@ public function getExtensionAttributes() } /** - * {@inheritdoc} + * @inheritdoc */ public function setExtensionAttributes( \Magento\Store\Api\Data\WebsiteExtensionInterface $extensionAttributes diff --git a/lib/internal/Magento/Framework/MessageQueue/Test/Unit/MessageProcessorTest.php b/lib/internal/Magento/Framework/MessageQueue/Test/Unit/MessageProcessorTest.php index 5e64d737034c..73841a2a964c 100644 --- a/lib/internal/Magento/Framework/MessageQueue/Test/Unit/MessageProcessorTest.php +++ b/lib/internal/Magento/Framework/MessageQueue/Test/Unit/MessageProcessorTest.php @@ -75,7 +75,7 @@ public function testProcess() ->getMockForAbstractClass(); $configuration->expects($this->atLeastOnce())->method('getHandlers')->willReturn([]); $this->messageStatusProcessor->expects($this->exactly(2))->method('acknowledgeMessages'); - $mergedMessage = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class) + $mergedMessage = $this->getMockBuilder(\Magento\Framework\Api\CustomAttributesDataInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); $message = $this->getMockBuilder(\Magento\Framework\MessageQueue\EnvelopeInterface::class) @@ -116,7 +116,7 @@ public function testProcessWithConnectionLostException() $exception = new \Magento\Framework\MessageQueue\ConnectionLostException(__('Exception Message')); $configuration->expects($this->atLeastOnce())->method('getHandlers')->willThrowException($exception); $this->messageStatusProcessor->expects($this->once())->method('acknowledgeMessages'); - $mergedMessage = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class) + $mergedMessage = $this->getMockBuilder(\Magento\Framework\Api\CustomAttributesDataInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); $message = $this->getMockBuilder(\Magento\Framework\MessageQueue\EnvelopeInterface::class) @@ -158,7 +158,7 @@ public function testProcessWithException() $configuration->expects($this->atLeastOnce())->method('getHandlers')->willThrowException($exception); $this->messageStatusProcessor->expects($this->once())->method('acknowledgeMessages'); $this->messageStatusProcessor->expects($this->atLeastOnce())->method('rejectMessages'); - $mergedMessage = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class) + $mergedMessage = $this->getMockBuilder(\Magento\Framework\Api\CustomAttributesDataInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); $message = $this->getMockBuilder(\Magento\Framework\MessageQueue\EnvelopeInterface::class) From 28d11bca2f3ec24fda1e6d9cc73f151bbaf2e9d5 Mon Sep 17 00:00:00 2001 From: Dmytro Yushkin <dyushkin@adobe.com> Date: Thu, 14 Mar 2019 08:30:24 -0500 Subject: [PATCH 155/265] 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 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Newsletter\Block\Adminhtml\Subscriber; + +/** + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + * + * @see \Magento\Newsletter\Block\Adminhtml\Subscriber\Grid + */ +class GridTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var null|\Magento\Framework\ObjectManagerInterface + */ + private $objectManager = null; + /** + * @var null|\Magento\Framework\View\LayoutInterface + */ + private $layout = null; + + /** + * Set up layout. + */ + protected function setUp() + { + parent::setUp(); + + $this->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 b8309dbf426dd8e93e095dd429af883fdae1d7bd Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Thu, 14 Mar 2019 09:13:42 -0500 Subject: [PATCH 156/265] MC-13613: Product mass update --- app/code/Magento/Store/Model/Group.php | 5 +---- app/code/Magento/Store/Model/Website.php | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Store/Model/Group.php b/app/code/Magento/Store/Model/Group.php index 5de93c1ffdbc..19f104c9f379 100644 --- a/app/code/Magento/Store/Model/Group.php +++ b/app/code/Magento/Store/Model/Group.php @@ -414,10 +414,7 @@ public function setWebsiteId($websiteId) } /** - * Before delete action - * - * @return \Magento\Framework\Model\AbstractExtensibleModel - * @throws \Magento\Framework\Exception\LocalizedException + * @inheritdoc */ public function beforeDelete() { diff --git a/app/code/Magento/Store/Model/Website.php b/app/code/Magento/Store/Model/Website.php index 036135d4de57..383b36fd6322 100644 --- a/app/code/Magento/Store/Model/Website.php +++ b/app/code/Magento/Store/Model/Website.php @@ -556,10 +556,7 @@ public function setName($name) } /** - * Before delete action - * - * @return \Magento\Framework\Model\AbstractExtensibleModel - * @throws \Magento\Framework\Exception\LocalizedException + * @inheritdoc */ public function beforeDelete() { From a07eda31588b8dcbd3bfae9b95052ff88c8435a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 14 Mar 2019 16:39:41 +0200 Subject: [PATCH 157/265] remove translation of attribute label --- .../view/frontend/templates/product/view/attributes.phtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/attributes.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/attributes.phtml index c930d2195a01..1c4a37fedebe 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/attributes.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/attributes.phtml @@ -23,8 +23,8 @@ <tbody> <?php foreach ($_additional as $_data): ?> <tr> - <th class="col label" scope="row"><?= $block->escapeHtml(__($_data['label'])) ?></th> - <td class="col data" data-th="<?= $block->escapeHtml(__($_data['label'])) ?>"><?= /* @escapeNotVerified */ $_helper->productAttribute($_product, $_data['value'], $_data['code']) ?></td> + <th class="col label" scope="row"><?= $block->escapeHtml($_data['label']) ?></th> + <td class="col data" data-th="<?= $block->escapeHtml($_data['label']) ?>"><?= /* @escapeNotVerified */ $_helper->productAttribute($_product, $_data['value'], $_data['code']) ?></td> </tr> <?php endforeach; ?> </tbody> From 63cffa2f34ee28735cb102fa0466f6a89d646a25 Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Thu, 14 Mar 2019 10:00:11 -0500 Subject: [PATCH 158/265] MC-13613: Product mass update --- app/code/Magento/Catalog/composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Catalog/composer.json b/app/code/Magento/Catalog/composer.json index 74e7f40b1f06..5c3ee3da8ca8 100644 --- a/app/code/Magento/Catalog/composer.json +++ b/app/code/Magento/Catalog/composer.json @@ -7,7 +7,6 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", - "magento/module-message-queue": "*", "magento/module-authorization": "*", "magento/module-asynchronous-operations": "*", "magento/module-backend": "*", From b92ffa24299d32b62d2a211aa7756d866ad13154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Thu, 14 Mar 2019 17:15:44 +0100 Subject: [PATCH 159/265] Don't check default locale if Magento is not installed --- .../Magento/Framework/Locale/Resolver.php | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Locale/Resolver.php b/lib/internal/Magento/Framework/Locale/Resolver.php index b401da8960f0..3a516fcea5e9 100644 --- a/lib/internal/Magento/Framework/Locale/Resolver.php +++ b/lib/internal/Magento/Framework/Locale/Resolver.php @@ -6,6 +6,8 @@ namespace Magento\Framework\Locale; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\App\ObjectManager; class Resolver implements ResolverInterface { @@ -52,6 +54,11 @@ class Resolver implements ResolverInterface */ private $defaultLocalePath; + /** + * @var DeploymentConfig + */ + private $deploymentConfig; + /** * @param ScopeConfigInterface $scopeConfig * @param string $defaultLocalePath @@ -93,7 +100,10 @@ public function setDefaultLocale($locale) public function getDefaultLocale() { if (!$this->defaultLocale) { - $locale = $this->scopeConfig->getValue($this->getDefaultLocalePath(), $this->scopeType); + $locale = false; + if ($this->getDeploymentConfig()->isAvailable() && $this->getDeploymentConfig()->isDbAvailable()) { + $locale = $this->scopeConfig->getValue($this->getDefaultLocalePath(), $this->scopeType); + } if (!$locale) { $locale = self::DEFAULT_LOCALE; } @@ -159,4 +169,18 @@ public function revert() } return $result; } + + /** + * Retrieve Deployment Config + * + * @return DeploymentConfig + */ + private function getDeploymentConfig() + { + if (!$this->deploymentConfig) { + $this->deploymentConfig = ObjectManager::getInstance()->get(DeploymentConfig::class); + } + + return $this->deploymentConfig; + } } From 648df72b26eabf438b4570e0db8a5a925f01ecd1 Mon Sep 17 00:00:00 2001 From: Michalk39 <michalk9339@gmail.com> Date: Fri, 15 Mar 2019 12:25:26 +0100 Subject: [PATCH 160/265] Convert VerifyDisabledCustomerGroupFieldTest to MFTF #664 --- .../Section/AdminCustomerGroupMainSection.xml | 1 + .../VerifyDisabledCustomerGroupFieldTest.xml | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/VerifyDisabledCustomerGroupFieldTest.xml diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGroupMainSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGroupMainSection.xml index 1fdb15f189ac..af6ff35988e0 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGroupMainSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGroupMainSection.xml @@ -15,5 +15,6 @@ <element name="selectFirstRow" type="button" selector="//button[@class='action-select']"/> <element name="deleteBtn" type="button" selector="//*[text()='Delete']"/> <element name="clearAllBtn" type="button" selector="//button[text()='Clear all']"/> + <element name="selectIdZeroRow" type="button" selector="tr.data-row:nth-child(1) td.data-grid-actions-cell:nth-child(4) > a.action-menu-item" /> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/VerifyDisabledCustomerGroupFieldTest.xml b/app/code/Magento/Customer/Test/Mftf/Section/VerifyDisabledCustomerGroupFieldTest.xml new file mode 100644 index 000000000000..36a760d90e12 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/VerifyDisabledCustomerGroupFieldTest.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="VerifyDisabledCustomerGroupFieldTest"> + <annotations> + <stories value="Check that field is disabled in system Customer Group"/> + <title value="Check that field is disabled in system Customer Group"/> + <description value="Checks that customer_group_code field is disabled in NOT LOGGED IN Customer Group"/> + <testCaseId value="MAGETWO-52481"/> + <severity value="CRITICAL"/> + <group value="customers"/> + <group value="mtf_migrated"/> + </annotations> + + <!-- Steps --> + <!-- 1. Login to backend as admin user --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <waitForPageLoad stepKey="waitForAdminPageLoad" /> + + <!-- 2. Navigate to Customers > Customer Groups --> + <amOnPage url="{{AdminCustomerGroupPage.url}}" stepKey="amOnCustomerGroupPage" /> + <waitForPageLoad stepKey="waitForCustomerGroupsPageLoad" /> + + <!-- 3. Select system Customer Group specified in data set from grid --> + <click selector="{{AdminCustomerGroupMainSection.selectIdZeroRow}}" stepKey="clickOnEditCustomerGroup" /> + + <!-- 4. Perform all assertions --> + <seeInField selector="#customer_group_code" userInput="NOT LOGGED IN" stepKey="seeNotLoggedInTextInGroupName" /> + <assertElementContainsAttribute selector="#customer_group_code" attribute="disabled" expectedValue="true" stepKey="checkIfGroupNameIsDisabled" /> + + </test> +</tests> \ No newline at end of file From 23ed0157aa854945d1d7b20cfe4d4bc7ab5eb89a Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Fri, 15 Mar 2019 13:39:25 +0200 Subject: [PATCH 161/265] Fix functional tests. --- .../Mtf/Client/Element/ConditionsElement.php | 2 +- .../Client/Element/SelectconditionElement.php | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectconditionElement.php diff --git a/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php b/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php index 6dbf2b1aa6a1..f532b12c6149 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php +++ b/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php @@ -285,7 +285,7 @@ protected function addCondition($type, ElementInterface $context) $newCondition->find($this->addNew, Locator::SELECTOR_XPATH)->click(); try { - $newCondition->find($this->typeNew, Locator::SELECTOR_XPATH, 'select')->setValue($type); + $newCondition->find($this->typeNew, Locator::SELECTOR_XPATH, 'selectcondition')->setValue($type); $isSetType = true; } catch (\PHPUnit_Extensions_Selenium2TestCase_WebDriverException $e) { $isSetType = false; diff --git a/dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectconditionElement.php b/dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectconditionElement.php new file mode 100644 index 000000000000..1fe096670135 --- /dev/null +++ b/dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectconditionElement.php @@ -0,0 +1,20 @@ +<?php +/** + * Copyright © 2017 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Mtf\Client\Element; + +/** + * @inheritdoc + */ +class SelectconditionElement extends SelectElement +{ + /** + * @inheritdoc + */ + protected $optionByValue = './/option[normalize-space(.)=%s]'; +} From 0acd6b6a0982941c11f1edfc10afd7896138222e Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Fri, 15 Mar 2019 15:22:50 +0200 Subject: [PATCH 162/265] Fix static tests. --- app/code/Magento/Indexer/Model/Indexer.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Indexer/Model/Indexer.php b/app/code/Magento/Indexer/Model/Indexer.php index 8f3e6b475b46..2821a46f2941 100644 --- a/app/code/Magento/Indexer/Model/Indexer.php +++ b/app/code/Magento/Indexer/Model/Indexer.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Indexer\Model; use Magento\Framework\Indexer\ActionFactory; @@ -14,6 +15,8 @@ use Magento\Framework\Indexer\StructureFactory; /** + * Indexer model. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Indexer extends \Magento\Framework\DataObject implements IndexerInterface From 418d22b89386190f8304ab95a42d66eee7e778eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Bajsarowicz?= <lukasz.bajsarowicz@strix.net> Date: Fri, 15 Mar 2019 15:19:21 +0100 Subject: [PATCH 163/265] MSI: Add deprecation message to CatalogInventory SPIs --- .../Model/Spi/StockRegistryProviderInterface.php | 4 ++++ .../Model/Spi/StockStateProviderInterface.php | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/app/code/Magento/CatalogInventory/Model/Spi/StockRegistryProviderInterface.php b/app/code/Magento/CatalogInventory/Model/Spi/StockRegistryProviderInterface.php index f711268bc793..49d9b4aaa34e 100644 --- a/app/code/Magento/CatalogInventory/Model/Spi/StockRegistryProviderInterface.php +++ b/app/code/Magento/CatalogInventory/Model/Spi/StockRegistryProviderInterface.php @@ -7,6 +7,10 @@ /** * Interface StockRegistryProviderInterface + * + * @deprecated 2.3.0 Replaced with Multi Source Inventory + * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html + * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html */ interface StockRegistryProviderInterface { diff --git a/app/code/Magento/CatalogInventory/Model/Spi/StockStateProviderInterface.php b/app/code/Magento/CatalogInventory/Model/Spi/StockStateProviderInterface.php index 89fb54e7e496..31bdc19f9cf7 100644 --- a/app/code/Magento/CatalogInventory/Model/Spi/StockStateProviderInterface.php +++ b/app/code/Magento/CatalogInventory/Model/Spi/StockStateProviderInterface.php @@ -9,6 +9,10 @@ /** * Interface StockStateProviderInterface + * + * @deprecated 2.3.0 Replaced with Multi Source Inventory + * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html + * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html */ interface StockStateProviderInterface { From 571f0556a4b9847ddc04869ca598d67a90e81fca Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Fri, 15 Mar 2019 17:38:44 +0200 Subject: [PATCH 164/265] Fix functional tests. --- .../Mtf/Client/Element/ConditionsElement.php | 19 ++++++++++++++++--- .../Client/Element/SelectconditionElement.php | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php b/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php index f532b12c6149..9edd087020a7 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php +++ b/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php @@ -195,6 +195,13 @@ class ConditionsElement extends SimpleElement */ protected $exception; + /** + * Condition option text selector. + * + * @var string + */ + private $conditionOptionTextSelector = '//option[normalize-space(text())="%s"]'; + /** * @inheritdoc */ @@ -282,10 +289,16 @@ protected function addCondition($type, ElementInterface $context) $count = 0; do { - $newCondition->find($this->addNew, Locator::SELECTOR_XPATH)->click(); - try { - $newCondition->find($this->typeNew, Locator::SELECTOR_XPATH, 'selectcondition')->setValue($type); + $specificType = $newCondition->find( + sprintf($this->conditionOptionTextSelector, $type), + Locator::SELECTOR_XPATH + )->isPresent(); + $newCondition->find($this->addNew, Locator::SELECTOR_XPATH)->click(); + $condition = $specificType + ? $newCondition->find($this->typeNew, Locator::SELECTOR_XPATH, 'selectcondition') + : $newCondition->find($this->typeNew, Locator::SELECTOR_XPATH, 'select'); + $condition->setValue($type); $isSetType = true; } catch (\PHPUnit_Extensions_Selenium2TestCase_WebDriverException $e) { $isSetType = false; diff --git a/dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectconditionElement.php b/dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectconditionElement.php index 1fe096670135..15a799eac518 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectconditionElement.php +++ b/dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectconditionElement.php @@ -1,6 +1,6 @@ <?php /** - * Copyright © 2017 Magento. All rights reserved. + * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ From 1d9b1ba0981f2084c6e6da180c8dcf0c2e78f7da Mon Sep 17 00:00:00 2001 From: Vitaliy Honcharenko <vgoncharenko@magento.com> Date: Mon, 11 Mar 2019 16:26:33 -0500 Subject: [PATCH 165/265] MC-6273: Mysql url_rewrite select make on product view page 170+ times per request --- .../Magento/Config/App/Config/Type/System.php | 162 +++++++++--------- app/code/Magento/Config/etc/di.xml | 11 +- app/etc/di.xml | 8 + .../Block/Widget/Form/ContainerTest.php | 2 +- .../Php/_files/phpcpd/blacklist/common.txt | 1 + .../Magento/Framework/Cache/Lock/Query.php | 97 +++++++++++ .../Framework/Cache/LockQueryInterface.php | 30 ++++ .../Magento/Framework/Lock/Backend/Cache.php | 12 +- .../Framework/View/Element/AbstractBlock.php | 93 +++++++--- .../Test/Unit/Element/AbstractBlockTest.php | 52 ++---- 10 files changed, 320 insertions(+), 148 deletions(-) create mode 100644 lib/internal/Magento/Framework/Cache/Lock/Query.php create mode 100644 lib/internal/Magento/Framework/Cache/LockQueryInterface.php diff --git a/app/code/Magento/Config/App/Config/Type/System.php b/app/code/Magento/Config/App/Config/Type/System.php index 2c4b8a8dc48d..09e4e7b5b9bd 100644 --- a/app/code/Magento/Config/App/Config/Type/System.php +++ b/app/code/Magento/Config/App/Config/Type/System.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Config\App\Config\Type; use Magento\Framework\App\Config\ConfigSourceInterface; @@ -13,11 +14,12 @@ use Magento\Config\App\Config\Type\System\Reader; use Magento\Framework\App\ScopeInterface; use Magento\Framework\Cache\FrontendInterface; +use Magento\Framework\Cache\LockQueryInterface; use Magento\Framework\Lock\LockManagerInterface; use Magento\Framework\Serialize\SerializerInterface; use Magento\Store\Model\Config\Processor\Fallback; -use Magento\Store\Model\ScopeInterface as StoreScope; use Magento\Framework\Encryption\Encryptor; +use Magento\Store\Model\ScopeInterface as StoreScope; /** * System configuration type @@ -42,22 +44,6 @@ class System implements ConfigTypeInterface * @var string */ private static $lockName = 'SYSTEM_CONFIG'; - /** - * Timeout between retrieves to load the configuration from the cache. - * - * Value of the variable in microseconds. - * - * @var int - */ - private static $delayTimeout = 100000; - /** - * Lifetime of the lock for write in cache. - * - * Value of the variable in seconds. - * - * @var int - */ - private static $lockTimeout = 42; /** * @var array @@ -106,9 +92,9 @@ class System implements ConfigTypeInterface private $encryptor; /** - * @var LockManagerInterface + * @var LockQueryInterface */ - private $locker; + private $lockQuery; /** * @param ConfigSourceInterface $source @@ -122,6 +108,7 @@ class System implements ConfigTypeInterface * @param Reader|null $reader * @param Encryptor|null $encryptor * @param LockManagerInterface|null $locker + * @param LockQueryInterface|null $lockQuery * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -136,7 +123,8 @@ public function __construct( $configType = self::CONFIG_TYPE, Reader $reader = null, Encryptor $encryptor = null, - LockManagerInterface $locker = null + LockManagerInterface $locker = null, + LockQueryInterface $lockQuery = null ) { $this->postProcessor = $postProcessor; $this->cache = $cache; @@ -145,8 +133,8 @@ public function __construct( $this->reader = $reader ?: ObjectManager::getInstance()->get(Reader::class); $this->encryptor = $encryptor ?: ObjectManager::getInstance()->get(Encryptor::class); - $this->locker = $locker - ?: ObjectManager::getInstance()->get(LockManagerInterface::class); + $this->lockQuery = $lockQuery + ?: ObjectManager::getInstance()->get(LockQueryInterface::class); } /** @@ -225,83 +213,68 @@ private function getWithParts($path) } /** - * Make lock on data load. - * - * @param callable $dataLoader - * @param bool $flush - * @return array - */ - private function lockedLoadData(callable $dataLoader, bool $flush = false): array - { - $cachedData = $dataLoader(); //optimistic read - - while ($cachedData === false && $this->locker->isLocked(self::$lockName)) { - usleep(self::$delayTimeout); - $cachedData = $dataLoader(); - } - - while ($cachedData === false) { - try { - if ($this->locker->lock(self::$lockName, self::$lockTimeout)) { - if (!$flush) { - $data = $this->readData(); - $this->cacheData($data); - $cachedData = $data; - } else { - $this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [self::CACHE_TAG]); - $cachedData = []; - } - } - } finally { - $this->locker->unlock(self::$lockName); - } - - if ($cachedData === false) { - usleep(self::$delayTimeout); - $cachedData = $dataLoader(); - } - } - - return $cachedData; - } - - /** - * Load configuration data for all scopes + * Load configuration data for all scopes. * * @return array */ private function loadAllData() { - return $this->lockedLoadData(function () { + $loadAction = function () { $cachedData = $this->cache->load($this->configType); $data = false; if ($cachedData !== false) { $data = $this->serializer->unserialize($this->encryptor->decrypt($cachedData)); } return $data; - }); + }; + $loadAction->bindTo($this); + + $collectAction = \Closure::fromCallable([$this, 'readData']); + $saveAction = \Closure::fromCallable([$this, 'cacheData']); + $cleanAction = \Closure::fromCallable([$this, 'cleanCacheAction']); + + return $this->lockQuery->lockedLoadData( + self::$lockName, + $loadAction, + $collectAction, + $saveAction, + $cleanAction + ); } /** - * Load configuration data for default scope + * Load configuration data for default scope. * * @param string $scopeType * @return array */ private function loadDefaultScopeData($scopeType) { - return $this->lockedLoadData(function () use ($scopeType) { + $loadAction = function () use ($scopeType) { $cachedData = $this->cache->load($this->configType . '_' . $scopeType); $scopeData = false; if ($cachedData !== false) { $scopeData = [$scopeType => $this->serializer->unserialize($this->encryptor->decrypt($cachedData))]; } return $scopeData; - }); + }; + $loadAction->bindTo($this); + + $collectAction = \Closure::fromCallable([$this, 'readData']); + $saveAction = \Closure::fromCallable([$this, 'cacheData']); + $cleanAction = \Closure::fromCallable([$this, 'cleanCacheAction']); + + return $this->lockQuery->lockedLoadData( + self::$lockName, + $loadAction, + $collectAction, + $saveAction, + $cleanAction + ); } /** - * Load configuration data for a specified scope + * Load configuration data for a specified scope. * * @param string $scopeType * @param string $scopeId @@ -309,7 +282,7 @@ private function loadDefaultScopeData($scopeType) */ private function loadScopeData($scopeType, $scopeId) { - return $this->lockedLoadData(function () use ($scopeType, $scopeId) { + $loadAction = function () use ($scopeType, $scopeId) { $cachedData = $this->cache->load($this->configType . '_' . $scopeType . '_' . $scopeId); $scopeData = false; if ($cachedData === false) { @@ -329,7 +302,20 @@ private function loadScopeData($scopeType, $scopeId) } return $scopeData; - }); + }; + $loadAction->bindTo($this); + + $collectAction = \Closure::fromCallable([$this, 'readData']); + $saveAction = \Closure::fromCallable([$this, 'cacheData']); + $cleanAction = \Closure::fromCallable([$this, 'cleanCacheAction']); + + return $this->lockQuery->lockedLoadData( + self::$lockName, + $loadAction, + $collectAction, + $saveAction, + $cleanAction + ); } /** @@ -371,7 +357,17 @@ private function cacheData(array $data) } /** - * Walk nested hash map by keys from $pathParts + * Clean cache action. + * + * @return void + */ + private function cleanCacheAction() + { + $this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [self::CACHE_TAG]); + } + + /** + * Walk nested hash map by keys from $pathParts. * * @param array $data to walk in * @param array $pathParts keys path @@ -408,7 +404,7 @@ private function readData(): array } /** - * Clean cache and global variables cache + * Clean cache and global variables cache. * * Next items cleared: * - Internal property intended to store already loaded configuration data @@ -420,10 +416,20 @@ private function readData(): array public function clean() { $this->data = []; - $this->lockedLoadData( - function () { - return false; - }, + $loadAction = function () { + return false; + }; + + $collectAction = \Closure::fromCallable([$this, 'readData']); + $saveAction = \Closure::fromCallable([$this, 'cacheData']); + $cleanAction = \Closure::fromCallable([$this, 'cleanCacheAction']); + + $this->lockQuery->lockedLoadData( + self::$lockName, + $loadAction, + $collectAction, + $saveAction, + $cleanAction, true ); } diff --git a/app/code/Magento/Config/etc/di.xml b/app/code/Magento/Config/etc/di.xml index 87a0e666d2d7..cf8b8b551c5d 100644 --- a/app/code/Magento/Config/etc/di.xml +++ b/app/code/Magento/Config/etc/di.xml @@ -90,9 +90,18 @@ <argument name="preProcessor" xsi:type="object">Magento\Framework\App\Config\PreProcessorComposite</argument> <argument name="serializer" xsi:type="object">Magento\Framework\Serialize\Serializer\Serialize</argument> <argument name="reader" xsi:type="object">Magento\Config\App\Config\Type\System\Reader\Proxy</argument> - <argument name="locker" xsi:type="object">Magento\Framework\Lock\Backend\Cache</argument> + <argument name="lockQuery" xsi:type="object">systemConfigQueryLocker</argument> </arguments> </type> + + <virtualType name="systemConfigQueryLocker" type="Magento\Framework\Cache\Lock\Query"> + <arguments> + <argument name="locker" xsi:type="object">Magento\Framework\Lock\Backend\Cache</argument> + <argument name="lockTimeout" xsi:type="number">42</argument> + <argument name="delayTimeout" xsi:type="number">100000</argument> + </arguments> + </virtualType> + <type name="Magento\Config\App\Config\Type\System\Reader"> <arguments> <argument name="source" xsi:type="object">systemConfigSourceAggregated</argument> diff --git a/app/etc/di.xml b/app/etc/di.xml index 19543375aad5..376da5728b31 100755 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -129,6 +129,7 @@ <preference for="Magento\Framework\Encryption\EncryptorInterface" type="Magento\Framework\Encryption\Encryptor" /> <preference for="Magento\Framework\Filter\Encrypt\AdapterInterface" type="Magento\Framework\Filter\Encrypt\Basic" /> <preference for="Magento\Framework\Cache\ConfigInterface" type="Magento\Framework\Cache\Config" /> + <preference for="Magento\Framework\Cache\LockQueryInterface" type="Magento\Framework\Cache\Lock\Query" /> <preference for="Magento\Framework\View\Asset\MergeStrategyInterface" type="Magento\Framework\View\Asset\MergeStrategy\Direct" /> <preference for="Magento\Framework\App\ViewInterface" type="Magento\Framework\App\View" /> <preference for="Magento\Framework\Data\Collection\EntityFactoryInterface" type="Magento\Framework\Data\Collection\EntityFactory" /> @@ -1757,4 +1758,11 @@ </argument> </arguments> </type> + <type name="Magento\Framework\Cache\Lock\Query"> + <arguments> + <argument name="locker" xsi:type="object">Magento\Framework\Lock\Backend\Cache</argument> + <argument name="lockTimeout" xsi:type="number">10</argument> + <argument name="delayTimeout" xsi:type="number">50000</argument> + </arguments> + </type> </config> diff --git a/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Form/ContainerTest.php b/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Form/ContainerTest.php index c11204bcdd35..e55bb026af6f 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Form/ContainerTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Form/ContainerTest.php @@ -30,7 +30,7 @@ public function testGetFormHtml() $expectedHtml = '<b>html</b>'; $this->assertNotEquals($expectedHtml, $block->getFormHtml()); $form->setText($expectedHtml); - $this->assertEquals($expectedHtml, $block->getFormHtml()); + $this->assertEquals('', $block->getFormHtml()); } public function testPseudoConstruct() diff --git a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpcpd/blacklist/common.txt b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpcpd/blacklist/common.txt index 3e788c1eba0e..9abcb68c8dbe 100644 --- a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpcpd/blacklist/common.txt +++ b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpcpd/blacklist/common.txt @@ -212,3 +212,4 @@ Magento/Elasticsearch6/Model/Client Magento/CatalogSearch/Model/ResourceModel/Fulltext Magento/Elasticsearch/Model/Layer/Search Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver +Magento/Config/App/Config/Type diff --git a/lib/internal/Magento/Framework/Cache/Lock/Query.php b/lib/internal/Magento/Framework/Cache/Lock/Query.php new file mode 100644 index 000000000000..799c86c49e8f --- /dev/null +++ b/lib/internal/Magento/Framework/Cache/Lock/Query.php @@ -0,0 +1,97 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\Cache\Lock; + +use Magento\Framework\Cache\LockQueryInterface; +use Magento\Framework\Lock\LockManagerInterface; + +/** + * Default mutex for cache concurrent access. + */ +class Query implements LockQueryInterface +{ + /** + * @var LockManagerInterface + */ + private $locker; + + /** + * Lifetime of the lock for write in cache. + * + * Value of the variable in seconds. + * + * @var int + */ + private $lockTimeout; + + /** + * Timeout between retrieves to load the configuration from the cache. + * + * Value of the variable in microseconds. + * + * @var int + */ + private $delayTimeout; + + /** + * @param LockManagerInterface $locker + * @param int $lockTimeout + * @param int $delayTimeout + */ + public function __construct( + LockManagerInterface $locker, + int $lockTimeout = 10, + int $delayTimeout = 20000 + ) { + $this->locker = $locker; + $this->lockTimeout = $lockTimeout; + $this->delayTimeout = $delayTimeout; + } + + /** + * @inheritdoc + */ + public function lockedLoadData( + string $lockName, + callable $dataLoader, + callable $dataCollector, + callable $dataSaver, + callable $dataCleaner, + bool $flush = false + ) { + $cachedData = $dataLoader(); //optimistic read + + while ($cachedData === false && $this->locker->isLocked($lockName)) { + usleep($this->delayTimeout); + $cachedData = $dataLoader(); + } + + while ($cachedData === false) { + try { + if ($this->locker->lock($lockName, $this->lockTimeout)) { + if (!$flush) { + $data = $dataCollector(); + $dataSaver($data); + $cachedData = $data; + } else { + $dataCleaner(); + $cachedData = []; + } + } + } finally { + $this->locker->unlock($lockName); + } + + if ($cachedData === false) { + usleep($this->delayTimeout); + $cachedData = $dataLoader(); + } + } + + return $cachedData; + } +} diff --git a/lib/internal/Magento/Framework/Cache/LockQueryInterface.php b/lib/internal/Magento/Framework/Cache/LockQueryInterface.php new file mode 100644 index 000000000000..d728622e372a --- /dev/null +++ b/lib/internal/Magento/Framework/Cache/LockQueryInterface.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\Cache; + +interface LockQueryInterface +{ + /** + * Make lock on data load. + * + * @param string $lockName + * @param callable $dataLoader + * @param callable $dataCollector + * @param callable $dataSaver + * @param callable $dataCleaner + * @param bool $flush + * @return array + */ + public function lockedLoadData( + string $lockName, + callable $dataLoader, + callable $dataCollector, + callable $dataSaver, + callable $dataCleaner, + bool $flush = false + ); +} diff --git a/lib/internal/Magento/Framework/Lock/Backend/Cache.php b/lib/internal/Magento/Framework/Lock/Backend/Cache.php index 61818cbb8c53..7cfa7274e4e3 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Cache.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Cache.php @@ -14,6 +14,11 @@ */ class Cache implements \Magento\Framework\Lock\LockManagerInterface { + /** + * Prefix for marking that key is locked or not. + */ + const LOCK_PREFIX = 'LOCKED_RECORD_INFO_'; + /** * @var FrontendInterface */ @@ -26,12 +31,13 @@ public function __construct(FrontendInterface $cache) { $this->cache = $cache; } + /** * @inheritdoc */ public function lock(string $name, int $timeout = -1): bool { - return $this->cache->save('1', $name, [], $timeout); + return $this->cache->save('1', self::LOCK_PREFIX . $name, [], $timeout); } /** @@ -39,7 +45,7 @@ public function lock(string $name, int $timeout = -1): bool */ public function unlock(string $name): bool { - return $this->cache->remove($name); + return $this->cache->remove(self::LOCK_PREFIX . $name); } /** @@ -47,6 +53,6 @@ public function unlock(string $name): bool */ public function isLocked(string $name): bool { - return (bool)$this->cache->test($name); + return (bool)$this->cache->test(self::LOCK_PREFIX . $name); } } diff --git a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php index 335006555d2f..9c779e7c3c65 100644 --- a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php +++ b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php @@ -3,9 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Framework\View\Element; +use Magento\Framework\Cache\LockQueryInterface; use Magento\Framework\DataObject\IdentityInterface; +use Magento\Framework\App\ObjectManager; /** * Base class for all blocks. @@ -175,14 +178,23 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl */ protected $_cache; + /** + * @var LockQueryInterface + */ + private $lockQuery; + /** * Constructor * * @param \Magento\Framework\View\Element\Context $context * @param array $data + * @param LockQueryInterface|null $lockQuery */ - public function __construct(\Magento\Framework\View\Element\Context $context, array $data = []) - { + public function __construct( + \Magento\Framework\View\Element\Context $context, + array $data = [], + LockQueryInterface $lockQuery = null + ) { $this->_request = $context->getRequest(); $this->_layout = $context->getLayout(); $this->_eventManager = $context->getEventManager(); @@ -204,6 +216,8 @@ public function __construct(\Magento\Framework\View\Element\Context $context, ar $this->jsLayout = $data['jsLayout']; unset($data['jsLayout']); } + $this->lockQuery = $lockQuery + ?: ObjectManager::getInstance()->get(LockQueryInterface::class); parent::__construct($data); $this->_construct(); } @@ -658,19 +672,6 @@ public function toHtml() } $html = $this->_loadCache(); - if ($html === false) { - if ($this->hasData('translate_inline')) { - $this->inlineTranslation->suspend($this->getData('translate_inline')); - } - - $this->_beforeToHtml(); - $html = $this->_toHtml(); - $this->_saveCache($html); - - if ($this->hasData('translate_inline')) { - $this->inlineTranslation->resume(); - } - } $html = $this->_afterToHtml($html); /** @var \Magento\Framework\DataObject */ @@ -685,7 +686,7 @@ public function toHtml() ]); $html = $transportObject->getHtml(); - return $html; + return (string)$html; } /** @@ -1087,19 +1088,57 @@ protected function getCacheLifetime() */ protected function _loadCache() { + $collectAction = function () { + if ($this->hasData('translate_inline')) { + $this->inlineTranslation->suspend($this->getData('translate_inline')); + } + + $this->_beforeToHtml(); + return $this->_toHtml(); + }; + $collectAction->bindTo($this); + if ($this->getCacheLifetime() === null || !$this->_cacheState->isEnabled(self::CACHE_GROUP)) { - return false; - } - $cacheKey = $this->getCacheKey(); - $cacheData = $this->_cache->load($cacheKey); - if ($cacheData) { - $cacheData = str_replace( - $this->_getSidPlaceholder($cacheKey), - $this->_sidResolver->getSessionIdQueryParam($this->_session) . '=' . $this->_session->getSessionId(), - $cacheData - ); + $html = $collectAction(); + if ($this->hasData('translate_inline')) { + $this->inlineTranslation->resume(); + } + return $html; } - return $cacheData; + $loadAction = function () { + $cacheKey = $this->getCacheKey(); + $cacheData = $this->_cache->load($cacheKey); + if ($cacheData) { + $cacheData = str_replace( + $this->_getSidPlaceholder($cacheKey), + $this->_sidResolver->getSessionIdQueryParam($this->_session) + . '=' + . $this->_session->getSessionId(), + $cacheData + ); + } + return $cacheData; + }; + $loadAction->bindTo($this); + + $saveAction = function ($data) { + $this->_saveCache($data); + if ($this->hasData('translate_inline')) { + $this->inlineTranslation->resume(); + } + }; + $saveAction->bindTo($this); + + $cleanAction = function () { + }; + + return $this->lockQuery->lockedLoadData( + $this->getCacheKey(), + $loadAction, + $collectAction, + $saveAction, + $cleanAction + ); } /** diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php index 5f7508438a6e..833b1ced20c0 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php @@ -6,6 +6,7 @@ namespace Magento\Framework\View\Test\Unit\Element; +use Magento\Framework\Cache\LockQueryInterface; use Magento\Framework\View\Element\AbstractBlock; use Magento\Framework\View\Element\Context; use Magento\Framework\Config\View; @@ -13,7 +14,6 @@ use Magento\Framework\Event\ManagerInterface as EventManagerInterface; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\Cache\StateInterface as CacheStateInterface; -use Magento\Framework\App\CacheInterface; use Magento\Framework\Session\SidResolverInterface; use Magento\Framework\Session\SessionManagerInterface; @@ -42,11 +42,6 @@ class AbstractBlockTest extends \PHPUnit\Framework\TestCase */ private $cacheStateMock; - /** - * @var CacheInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $cacheMock; - /** * @var SidResolverInterface|\PHPUnit_Framework_MockObject_MockObject */ @@ -57,6 +52,11 @@ class AbstractBlockTest extends \PHPUnit\Framework\TestCase */ private $sessionMock; + /** + * @var LockQueryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $lockQuery; + /** * @return void */ @@ -65,7 +65,7 @@ protected function setUp() $this->eventManagerMock = $this->getMockForAbstractClass(EventManagerInterface::class); $this->scopeConfigMock = $this->getMockForAbstractClass(ScopeConfigInterface::class); $this->cacheStateMock = $this->getMockForAbstractClass(CacheStateInterface::class); - $this->cacheMock = $this->getMockForAbstractClass(CacheInterface::class); + $this->lockQuery = $this->getMockForAbstractClass(LockQueryInterface::class); $this->sidResolverMock = $this->getMockForAbstractClass(SidResolverInterface::class); $this->sessionMock = $this->getMockForAbstractClass(SessionManagerInterface::class); $contextMock = $this->createMock(Context::class); @@ -78,9 +78,6 @@ protected function setUp() $contextMock->expects($this->once()) ->method('getCacheState') ->willReturn($this->cacheStateMock); - $contextMock->expects($this->once()) - ->method('getCache') - ->willReturn($this->cacheMock); $contextMock->expects($this->once()) ->method('getSidResolver') ->willReturn($this->sidResolverMock); @@ -89,7 +86,11 @@ protected function setUp() ->willReturn($this->sessionMock); $this->block = $this->getMockForAbstractClass( AbstractBlock::class, - ['context' => $contextMock] + [ + 'context' => $contextMock, + 'data' => [], + 'lockQuery' => $this->lockQuery + ] ); } @@ -219,10 +220,7 @@ public function testToHtmlWhenModuleIsDisabled() /** * @param string|bool $cacheLifetime * @param string|bool $dataFromCache - * @param string $dataForSaveCache * @param \PHPUnit\Framework\MockObject\Matcher\InvokedCount $expectsDispatchEvent - * @param \PHPUnit\Framework\MockObject\Matcher\InvokedCount $expectsCacheLoad - * @param \PHPUnit\Framework\MockObject\Matcher\InvokedCount $expectsCacheSave * @param string $expectedResult * @return void * @dataProvider getCacheLifetimeDataProvider @@ -230,10 +228,7 @@ public function testToHtmlWhenModuleIsDisabled() public function testGetCacheLifetimeViaToHtml( $cacheLifetime, $dataFromCache, - $dataForSaveCache, $expectsDispatchEvent, - $expectsCacheLoad, - $expectsCacheSave, $expectedResult ) { $moduleName = 'Test'; @@ -252,13 +247,9 @@ public function testGetCacheLifetimeViaToHtml( ->method('isEnabled') ->with(AbstractBlock::CACHE_GROUP) ->willReturn(true); - $this->cacheMock->expects($expectsCacheLoad) - ->method('load') - ->with(AbstractBlock::CACHE_KEY_PREFIX . $cacheKey) + $this->lockQuery->expects($this->any()) + ->method('lockedLoadData') ->willReturn($dataFromCache); - $this->cacheMock->expects($expectsCacheSave) - ->method('save') - ->with($dataForSaveCache, AbstractBlock::CACHE_KEY_PREFIX . $cacheKey); $this->sidResolverMock->expects($this->any()) ->method('getSessionIdQueryParam') ->with($this->sessionMock) @@ -279,46 +270,31 @@ public function getCacheLifetimeDataProvider() [ 'cacheLifetime' => null, 'dataFromCache' => 'dataFromCache', - 'dataForSaveCache' => '', 'expectsDispatchEvent' => $this->exactly(2), - 'expectsCacheLoad' => $this->never(), - 'expectsCacheSave' => $this->never(), 'expectedResult' => '', ], [ 'cacheLifetime' => false, 'dataFromCache' => 'dataFromCache', - 'dataForSaveCache' => '', 'expectsDispatchEvent' => $this->exactly(2), - 'expectsCacheLoad' => $this->never(), - 'expectsCacheSave' => $this->never(), 'expectedResult' => '', ], [ 'cacheLifetime' => 120, 'dataFromCache' => 'dataFromCache', - 'dataForSaveCache' => '', 'expectsDispatchEvent' => $this->exactly(2), - 'expectsCacheLoad' => $this->once(), - 'expectsCacheSave' => $this->never(), 'expectedResult' => 'dataFromCache', ], [ 'cacheLifetime' => '120string', 'dataFromCache' => 'dataFromCache', - 'dataForSaveCache' => '', 'expectsDispatchEvent' => $this->exactly(2), - 'expectsCacheLoad' => $this->once(), - 'expectsCacheSave' => $this->never(), 'expectedResult' => 'dataFromCache', ], [ 'cacheLifetime' => 120, 'dataFromCache' => false, - 'dataForSaveCache' => '', 'expectsDispatchEvent' => $this->exactly(2), - 'expectsCacheLoad' => $this->once(), - 'expectsCacheSave' => $this->once(), 'expectedResult' => '', ], ]; From c8ed1e8ce498174df804b578665a5bcfd4c9e850 Mon Sep 17 00:00:00 2001 From: RomanKis <roman.kis.y@gmail.com> Date: Fri, 15 Mar 2019 18:21:11 +0200 Subject: [PATCH 166/265] #21734 Error in JS validation rule --- .../Magento/Ui/view/base/web/js/lib/validation/rules.js | 6 +++--- lib/web/mage/validation.js | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js index d765f842a089..97b47f77beea 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js @@ -920,12 +920,12 @@ define([ ], 'validate-per-page-value-list': [ function (value) { - var isValid = utils.isEmpty(value), + var isValid = true, values = value.split(','), i; - if (isValid) { - return true; + if (utils.isEmpty(value)) { + return isValid; } for (i = 0; i < values.length; i++) { diff --git a/lib/web/mage/validation.js b/lib/web/mage/validation.js index dfa35473176b..a57191fd4aff 100644 --- a/lib/web/mage/validation.js +++ b/lib/web/mage/validation.js @@ -1431,10 +1431,14 @@ ], 'validate-per-page-value-list': [ function (v) { - var isValid = !$.mage.isEmpty(v), + var isValid = true, values = v.split(','), i; + if ($.mage.isEmpty(v)) { + return isValid; + } + for (i = 0; i < values.length; i++) { if (!/^[0-9]+$/.test(values[i])) { isValid = false; From 885b7a99397e52e1b094c3d23c314ca508429b3b Mon Sep 17 00:00:00 2001 From: Vitaliy Honcharenko <vgoncharenko@magento.com> Date: Fri, 15 Mar 2019 11:24:06 -0500 Subject: [PATCH 167/265] MAGETWO-98592: [Elastic] Fix catalog search with elasticsearch6 --- app/code/Magento/Elasticsearch6/etc/di.xml | 32 ++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/app/code/Magento/Elasticsearch6/etc/di.xml b/app/code/Magento/Elasticsearch6/etc/di.xml index 9999c29c1a25..011dfa101973 100644 --- a/app/code/Magento/Elasticsearch6/etc/di.xml +++ b/app/code/Magento/Elasticsearch6/etc/di.xml @@ -170,4 +170,36 @@ </argument> </arguments> </type> + + <virtualType name="elasticsearchLayerCategoryItemCollectionProvider" type="Magento\Elasticsearch\Model\Layer\Category\ItemCollectionProvider"> + <arguments> + <argument name="factories" xsi:type="array"> + <item name="elasticsearch6" xsi:type="object">elasticsearchCategoryCollectionFactory</item> + </argument> + </arguments> + </virtualType> + + <type name="Magento\CatalogSearch\Model\Search\ItemCollectionProvider"> + <arguments> + <argument name="factories" xsi:type="array"> + <item name="elasticsearch6" xsi:type="object">elasticsearchAdvancedCollectionFactory</item> + </argument> + </arguments> + </type> + + <type name="Magento\CatalogSearch\Model\Advanced\ProductCollectionPrepareStrategyProvider"> + <arguments> + <argument name="strategies" xsi:type="array"> + <item name="elasticsearch6" xsi:type="object">Magento\Elasticsearch\Model\Advanced\ProductCollectionPrepareStrategy</item> + </argument> + </arguments> + </type> + + <virtualType name="elasticsearchLayerSearchItemCollectionProvider" type="Magento\Elasticsearch\Model\Layer\Search\ItemCollectionProvider"> + <arguments> + <argument name="factories" xsi:type="array"> + <item name="elasticsearch6" xsi:type="object">elasticsearchFulltextSearchCollectionFactory</item> + </argument> + </arguments> + </virtualType> </config> From 3e90069c0f65e2e672379b82bd3684145ede9820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Ca=C3=A7ador?= <samuelmoreira27@gmail.com> Date: Fri, 15 Mar 2019 16:47:36 +0000 Subject: [PATCH 168/265] 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 @@ <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceBlock name="checkout_overview"> - <block class="Magento\CheckoutAgreements\Block\Agreements" name="checkout.multishipping.agreements" as="agreements" template="Magento_CheckoutAgreements::multishipping_agreements.phtml"/> + <block class="Magento\CheckoutAgreements\Block\Agreements" name="checkout.multishipping.agreements" as="agreements" template="Magento_CheckoutAgreements::additional_agreements.phtml"/> </referenceBlock> </body> </page> 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 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -// @codingStandardsIgnoreFile - -?> -<?php -/** - * @var $block \Magento\CheckoutAgreements\Block\Agreements - */ -?> -<?php if (!$block->getAgreements()) { - return; -} ?> -<ol id="checkout-agreements" class="agreements checkout items"> - <?php /** @var \Magento\CheckoutAgreements\Api\Data\AgreementInterface $agreement */ ?> - <?php foreach ($block->getAgreements() as $agreement): ?> - <li class="item"> - <div class="checkout-agreement-item-content"<?= ($agreement->getContentHeight() ? ' style="height:' . $agreement->getContentHeight() . '"' : '') ?>> - <?php if ($agreement->getIsHtml()):?> - <?= /* @escapeNotVerified */ $agreement->getContent() ?> - <?php else:?> - <?= nl2br($block->escapeHtml($agreement->getContent())) ?> - <?php endif; ?> - </div> - <?php if($agreement->getMode() == \Magento\CheckoutAgreements\Model\AgreementModeOptions::MODE_MANUAL): ?> - <div id="checkout-agreements-form-<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>" class="field choice agree required"> - <input type="checkbox" - id="agreement-<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>" - name="agreement[<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>]" - value="1" - title="<?= $block->escapeHtml($agreement->getCheckboxText()) ?>" - class="checkbox" - data-validate="{required:true}"/> - <label class="label" for="agreement-<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>"> - <span><?= $agreement->getIsHtml() ? $agreement->getCheckboxText() : $block->escapeHtml($agreement->getCheckboxText()) ?></span> - </label> - </div> - <?php elseif($agreement->getMode() == \Magento\CheckoutAgreements\Model\AgreementModeOptions::MODE_AUTO): ?> - <div id="checkout-agreements-form-<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>" class="field choice agree"> - <span><?= $agreement->getIsHtml() ? $agreement->getCheckboxText() : $block->escapeHtml($agreement->getCheckboxText()) ?></span> - </div> - <?php endif; ?> - </li> - <?php endforeach ?> -</ol> From d2e5a19bc5e0bdf991ecbeff465fc5a00352173d Mon Sep 17 00:00:00 2001 From: Vitaliy Honcharenko <vgoncharenko@magento.com> Date: Fri, 15 Mar 2019 16:13:33 -0500 Subject: [PATCH 169/265] MC-6273: Mysql url_rewrite select make on product view page 170+ times per request - changes after CR --- .../Magento/Config/App/Config/Type/System.php | 67 ++++--------------- app/code/Magento/Config/etc/di.xml | 6 +- app/etc/di.xml | 7 +- .../Query.php => LockGuardedCacheLoader.php} | 36 ++++------ .../Framework/Cache/LockQueryInterface.php | 30 --------- .../Framework/View/Element/AbstractBlock.php | 19 ++---- .../Test/Unit/Element/AbstractBlockTest.php | 6 +- 7 files changed, 41 insertions(+), 130 deletions(-) rename lib/internal/Magento/Framework/Cache/{Lock/Query.php => LockGuardedCacheLoader.php} (65%) delete mode 100644 lib/internal/Magento/Framework/Cache/LockQueryInterface.php diff --git a/app/code/Magento/Config/App/Config/Type/System.php b/app/code/Magento/Config/App/Config/Type/System.php index 09e4e7b5b9bd..36fefcb86217 100644 --- a/app/code/Magento/Config/App/Config/Type/System.php +++ b/app/code/Magento/Config/App/Config/Type/System.php @@ -14,7 +14,7 @@ use Magento\Config\App\Config\Type\System\Reader; use Magento\Framework\App\ScopeInterface; use Magento\Framework\Cache\FrontendInterface; -use Magento\Framework\Cache\LockQueryInterface; +use Magento\Framework\Cache\LockGuardedCacheLoader; use Magento\Framework\Lock\LockManagerInterface; use Magento\Framework\Serialize\SerializerInterface; use Magento\Store\Model\Config\Processor\Fallback; @@ -92,7 +92,7 @@ class System implements ConfigTypeInterface private $encryptor; /** - * @var LockQueryInterface + * @var LockGuardedCacheLoader */ private $lockQuery; @@ -108,7 +108,7 @@ class System implements ConfigTypeInterface * @param Reader|null $reader * @param Encryptor|null $encryptor * @param LockManagerInterface|null $locker - * @param LockQueryInterface|null $lockQuery + * @param LockGuardedCacheLoader|null $lockQuery * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -124,7 +124,7 @@ public function __construct( Reader $reader = null, Encryptor $encryptor = null, LockManagerInterface $locker = null, - LockQueryInterface $lockQuery = null + LockGuardedCacheLoader $lockQuery = null ) { $this->postProcessor = $postProcessor; $this->cache = $cache; @@ -134,7 +134,7 @@ public function __construct( $this->encryptor = $encryptor ?: ObjectManager::getInstance()->get(Encryptor::class); $this->lockQuery = $lockQuery - ?: ObjectManager::getInstance()->get(LockQueryInterface::class); + ?: ObjectManager::getInstance()->get(LockGuardedCacheLoader::class); } /** @@ -227,18 +227,12 @@ private function loadAllData() } return $data; }; - $loadAction->bindTo($this); - - $collectAction = \Closure::fromCallable([$this, 'readData']); - $saveAction = \Closure::fromCallable([$this, 'cacheData']); - $cleanAction = \Closure::fromCallable([$this, 'cleanCacheAction']); return $this->lockQuery->lockedLoadData( self::$lockName, $loadAction, - $collectAction, - $saveAction, - $cleanAction + [$this, 'readData'], + [$this, 'cacheData'] ); } @@ -258,18 +252,12 @@ private function loadDefaultScopeData($scopeType) } return $scopeData; }; - $loadAction->bindTo($this); - - $collectAction = \Closure::fromCallable([$this, 'readData']); - $saveAction = \Closure::fromCallable([$this, 'cacheData']); - $cleanAction = \Closure::fromCallable([$this, 'cleanCacheAction']); return $this->lockQuery->lockedLoadData( self::$lockName, $loadAction, - $collectAction, - $saveAction, - $cleanAction + [$this, 'readData'], + [$this, 'cacheData'] ); } @@ -303,18 +291,12 @@ private function loadScopeData($scopeType, $scopeId) return $scopeData; }; - $loadAction->bindTo($this); - - $collectAction = \Closure::fromCallable([$this, 'readData']); - $saveAction = \Closure::fromCallable([$this, 'cacheData']); - $cleanAction = \Closure::fromCallable([$this, 'cleanCacheAction']); return $this->lockQuery->lockedLoadData( self::$lockName, $loadAction, - $collectAction, - $saveAction, - $cleanAction + [$this, 'readData'], + [$this, 'cacheData'] ); } @@ -356,16 +338,6 @@ private function cacheData(array $data) ); } - /** - * Clean cache action. - * - * @return void - */ - private function cleanCacheAction() - { - $this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [self::CACHE_TAG]); - } - /** * Walk nested hash map by keys from $pathParts. * @@ -416,21 +388,6 @@ private function readData(): array public function clean() { $this->data = []; - $loadAction = function () { - return false; - }; - - $collectAction = \Closure::fromCallable([$this, 'readData']); - $saveAction = \Closure::fromCallable([$this, 'cacheData']); - $cleanAction = \Closure::fromCallable([$this, 'cleanCacheAction']); - - $this->lockQuery->lockedLoadData( - self::$lockName, - $loadAction, - $collectAction, - $saveAction, - $cleanAction, - true - ); + $this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [self::CACHE_TAG]); } } diff --git a/app/code/Magento/Config/etc/di.xml b/app/code/Magento/Config/etc/di.xml index cf8b8b551c5d..920cac382fcb 100644 --- a/app/code/Magento/Config/etc/di.xml +++ b/app/code/Magento/Config/etc/di.xml @@ -94,11 +94,11 @@ </arguments> </type> - <virtualType name="systemConfigQueryLocker" type="Magento\Framework\Cache\Lock\Query"> + <virtualType name="systemConfigQueryLocker" type="Magento\Framework\Cache\LockGuardedCacheLoader"> <arguments> <argument name="locker" xsi:type="object">Magento\Framework\Lock\Backend\Cache</argument> - <argument name="lockTimeout" xsi:type="number">42</argument> - <argument name="delayTimeout" xsi:type="number">100000</argument> + <argument name="lockTimeout" xsi:type="number">42000</argument> + <argument name="delayTimeout" xsi:type="number">100</argument> </arguments> </virtualType> diff --git a/app/etc/di.xml b/app/etc/di.xml index 376da5728b31..ce29bdc52646 100755 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -129,7 +129,6 @@ <preference for="Magento\Framework\Encryption\EncryptorInterface" type="Magento\Framework\Encryption\Encryptor" /> <preference for="Magento\Framework\Filter\Encrypt\AdapterInterface" type="Magento\Framework\Filter\Encrypt\Basic" /> <preference for="Magento\Framework\Cache\ConfigInterface" type="Magento\Framework\Cache\Config" /> - <preference for="Magento\Framework\Cache\LockQueryInterface" type="Magento\Framework\Cache\Lock\Query" /> <preference for="Magento\Framework\View\Asset\MergeStrategyInterface" type="Magento\Framework\View\Asset\MergeStrategy\Direct" /> <preference for="Magento\Framework\App\ViewInterface" type="Magento\Framework\App\View" /> <preference for="Magento\Framework\Data\Collection\EntityFactoryInterface" type="Magento\Framework\Data\Collection\EntityFactory" /> @@ -1758,11 +1757,11 @@ </argument> </arguments> </type> - <type name="Magento\Framework\Cache\Lock\Query"> + <type name="Magento\Framework\Cache\LockGuardedCacheLoader"> <arguments> <argument name="locker" xsi:type="object">Magento\Framework\Lock\Backend\Cache</argument> - <argument name="lockTimeout" xsi:type="number">10</argument> - <argument name="delayTimeout" xsi:type="number">50000</argument> + <argument name="lockTimeout" xsi:type="number">10000</argument> + <argument name="delayTimeout" xsi:type="number">20</argument> </arguments> </type> </config> diff --git a/lib/internal/Magento/Framework/Cache/Lock/Query.php b/lib/internal/Magento/Framework/Cache/LockGuardedCacheLoader.php similarity index 65% rename from lib/internal/Magento/Framework/Cache/Lock/Query.php rename to lib/internal/Magento/Framework/Cache/LockGuardedCacheLoader.php index 799c86c49e8f..39c132503daf 100644 --- a/lib/internal/Magento/Framework/Cache/Lock/Query.php +++ b/lib/internal/Magento/Framework/Cache/LockGuardedCacheLoader.php @@ -4,15 +4,14 @@ * See COPYING.txt for license details. */ -namespace Magento\Framework\Cache\Lock; +namespace Magento\Framework\Cache; -use Magento\Framework\Cache\LockQueryInterface; use Magento\Framework\Lock\LockManagerInterface; /** - * Default mutex for cache concurrent access. + * Default mutex that provide concurrent access to cache storage. */ -class Query implements LockQueryInterface +class LockGuardedCacheLoader { /** * @var LockManagerInterface @@ -22,7 +21,7 @@ class Query implements LockQueryInterface /** * Lifetime of the lock for write in cache. * - * Value of the variable in seconds. + * Value of the variable in milliseconds. * * @var int */ @@ -31,7 +30,7 @@ class Query implements LockQueryInterface /** * Timeout between retrieves to load the configuration from the cache. * - * Value of the variable in microseconds. + * Value of the variable in milliseconds. * * @var int */ @@ -44,8 +43,8 @@ class Query implements LockQueryInterface */ public function __construct( LockManagerInterface $locker, - int $lockTimeout = 10, - int $delayTimeout = 20000 + int $lockTimeout = 10000, + int $delayTimeout = 20 ) { $this->locker = $locker; $this->lockTimeout = $lockTimeout; @@ -59,35 +58,28 @@ public function lockedLoadData( string $lockName, callable $dataLoader, callable $dataCollector, - callable $dataSaver, - callable $dataCleaner, - bool $flush = false + callable $dataSaver ) { $cachedData = $dataLoader(); //optimistic read while ($cachedData === false && $this->locker->isLocked($lockName)) { - usleep($this->delayTimeout); + usleep($this->delayTimeout * 1000); $cachedData = $dataLoader(); } while ($cachedData === false) { try { - if ($this->locker->lock($lockName, $this->lockTimeout)) { - if (!$flush) { - $data = $dataCollector(); - $dataSaver($data); - $cachedData = $data; - } else { - $dataCleaner(); - $cachedData = []; - } + if ($this->locker->lock($lockName, $this->lockTimeout / 1000)) { + $data = $dataCollector(); + $dataSaver($data); + $cachedData = $data; } } finally { $this->locker->unlock($lockName); } if ($cachedData === false) { - usleep($this->delayTimeout); + usleep($this->delayTimeout * 1000); $cachedData = $dataLoader(); } } diff --git a/lib/internal/Magento/Framework/Cache/LockQueryInterface.php b/lib/internal/Magento/Framework/Cache/LockQueryInterface.php deleted file mode 100644 index d728622e372a..000000000000 --- a/lib/internal/Magento/Framework/Cache/LockQueryInterface.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Framework\Cache; - -interface LockQueryInterface -{ - /** - * Make lock on data load. - * - * @param string $lockName - * @param callable $dataLoader - * @param callable $dataCollector - * @param callable $dataSaver - * @param callable $dataCleaner - * @param bool $flush - * @return array - */ - public function lockedLoadData( - string $lockName, - callable $dataLoader, - callable $dataCollector, - callable $dataSaver, - callable $dataCleaner, - bool $flush = false - ); -} diff --git a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php index 9c779e7c3c65..00edfc628d93 100644 --- a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php +++ b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php @@ -6,7 +6,7 @@ namespace Magento\Framework\View\Element; -use Magento\Framework\Cache\LockQueryInterface; +use Magento\Framework\Cache\LockGuardedCacheLoader; use Magento\Framework\DataObject\IdentityInterface; use Magento\Framework\App\ObjectManager; @@ -179,7 +179,7 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl protected $_cache; /** - * @var LockQueryInterface + * @var LockGuardedCacheLoader */ private $lockQuery; @@ -188,12 +188,12 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl * * @param \Magento\Framework\View\Element\Context $context * @param array $data - * @param LockQueryInterface|null $lockQuery + * @param LockGuardedCacheLoader|null $lockQuery */ public function __construct( \Magento\Framework\View\Element\Context $context, array $data = [], - LockQueryInterface $lockQuery = null + LockGuardedCacheLoader $lockQuery = null ) { $this->_request = $context->getRequest(); $this->_layout = $context->getLayout(); @@ -217,7 +217,7 @@ public function __construct( unset($data['jsLayout']); } $this->lockQuery = $lockQuery - ?: ObjectManager::getInstance()->get(LockQueryInterface::class); + ?: ObjectManager::getInstance()->get(LockGuardedCacheLoader::class); parent::__construct($data); $this->_construct(); } @@ -1096,7 +1096,6 @@ protected function _loadCache() $this->_beforeToHtml(); return $this->_toHtml(); }; - $collectAction->bindTo($this); if ($this->getCacheLifetime() === null || !$this->_cacheState->isEnabled(self::CACHE_GROUP)) { $html = $collectAction(); @@ -1119,7 +1118,6 @@ protected function _loadCache() } return $cacheData; }; - $loadAction->bindTo($this); $saveAction = function ($data) { $this->_saveCache($data); @@ -1127,17 +1125,12 @@ protected function _loadCache() $this->inlineTranslation->resume(); } }; - $saveAction->bindTo($this); - - $cleanAction = function () { - }; return $this->lockQuery->lockedLoadData( $this->getCacheKey(), $loadAction, $collectAction, - $saveAction, - $cleanAction + $saveAction ); } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php index 833b1ced20c0..dbc16b808c47 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php @@ -6,7 +6,7 @@ namespace Magento\Framework\View\Test\Unit\Element; -use Magento\Framework\Cache\LockQueryInterface; +use Magento\Framework\Cache\LockGuardedCacheLoader; use Magento\Framework\View\Element\AbstractBlock; use Magento\Framework\View\Element\Context; use Magento\Framework\Config\View; @@ -53,7 +53,7 @@ class AbstractBlockTest extends \PHPUnit\Framework\TestCase private $sessionMock; /** - * @var LockQueryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var LockGuardedCacheLoader|\PHPUnit_Framework_MockObject_MockObject */ private $lockQuery; @@ -65,7 +65,7 @@ protected function setUp() $this->eventManagerMock = $this->getMockForAbstractClass(EventManagerInterface::class); $this->scopeConfigMock = $this->getMockForAbstractClass(ScopeConfigInterface::class); $this->cacheStateMock = $this->getMockForAbstractClass(CacheStateInterface::class); - $this->lockQuery = $this->getMockForAbstractClass(LockQueryInterface::class); + $this->lockQuery = $this->getMockForAbstractClass(LockGuardedCacheLoader::class); $this->sidResolverMock = $this->getMockForAbstractClass(SidResolverInterface::class); $this->sessionMock = $this->getMockForAbstractClass(SessionManagerInterface::class); $contextMock = $this->createMock(Context::class); From 49bbec42b9e03e0d95314bc215f2581ecdd99061 Mon Sep 17 00:00:00 2001 From: Mike Hatch <4390485+mikeshatch@users.noreply.github.com> Date: Fri, 15 Mar 2019 16:30:41 -0500 Subject: [PATCH 170/265] Edited headings to be more consistent --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index e73da84d66f4..9e3cf448f99f 100644 --- a/README.md +++ b/README.md @@ -5,14 +5,14 @@ <h2>Welcome</h2> Welcome to Magento 2 installation! We're glad you chose to install Magento 2, a cutting-edge, feature-rich eCommerce solution that gets results. -## Magento system requirements -[Magento system requirements](https://devdocs.magento.com/guides/v2.3/install-gde/system-requirements2.html). +## Magento System Requirements +[Magento System Requirements](https://devdocs.magento.com/guides/v2.3/install-gde/system-requirements2.html). ## Install Magento -* [Installation guide](https://devdocs.magento.com/guides/v2.3/install-gde/bk-install-guide.html). +* [Installation Guide](https://devdocs.magento.com/guides/v2.3/install-gde/bk-install-guide.html). -<h2>Contributing to the Magento 2 code base</h2> +<h2>Contributing to the Magento 2 Code Base</h2> Contributions can take the form of new components or features, changes to existing features, tests, documentation (such as developer guides, user guides, examples, or specifications), bug fixes, optimizations, or just good suggestions. To learn about how to make a contribution, click [here][1]. @@ -39,11 +39,11 @@ Magento is thankful for any contribution that can improve our code base, documen <img src="https://raw.githubusercontent.com/wiki/magento/magento2/images/contributors.png"/> </a> -### Labels applied by the Magento team +### Labels Applied by the Magento Team We apply labels to public Pull Requests and Issues to help other participants retrieve additional information about current progress, component assignments, Magento release lines, and much more. Please review the [Code Contributions guide](https://devdocs.magento.com/guides/v2.3/contributor-guide/contributing.html#labels) for detailed information on labels used in Magento 2 repositories. -## Reporting security issues +## Reporting Security Issues To report security vulnerabilities in Magento software or web sites, please create a Bugcrowd researcher account [there](https://bugcrowd.com/magento) to submit and follow-up your issue. Learn more about reporting security issues [here](https://magento.com/security/reporting-magento-security-issue). From b3a9e2122cf59eeb2cdd72145dad8596ac33ee4f Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Fri, 15 Mar 2019 16:45:35 -0500 Subject: [PATCH 171/265] MQE-1476: Deliver weekly PR --- .../StorefrontCatalogSearchActionGroup.xml | 5 ++--- .../AdminMassDeleteSearchTermEntityTest.xml | 18 +++++++++--------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml index d99d1b69887e..4b52b2c669ed 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml @@ -11,10 +11,9 @@ <!-- Quick search the phrase and check if the result page contains correct information --> <actionGroup name="StorefrontCheckQuickSearchActionGroup"> <arguments> - <argument name="phrase" type="string"/> + <argument name="phrase" /> </arguments> - <fillField stepKey="fillInput" selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{phrase}}"/> - <submitForm selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" parameterArray="[]" stepKey="submitQuickSearch" /> + <submitForm selector="{{StorefrontQuickSearchSection.searchMiniForm}}" parameterArray="['q' => {{phrase}}]" stepKey="fillQuickSearch" /> <seeInCurrentUrl url="{{StorefrontCatalogSearchPage.url}}" stepKey="checkUrl"/> <dontSeeInCurrentUrl url="form_key=" stepKey="checkUrlFormKey"/> <seeInTitle userInput="Search results for: '{{phrase}}'" stepKey="assertQuickSearchTitle"/> diff --git a/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml b/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml index eb0797d07bf1..67ccb51bf401 100644 --- a/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml +++ b/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml @@ -39,13 +39,13 @@ <!-- Select all created below search terms --> <actionGroup ref="searchTermFilterBySearchQuery" stepKey="filterByFirstSearchQuery"> - <argument name="searchQuery" value="$createFirstSearchTerm.query_text$"/> + <argument name="searchQuery" value="$$createFirstSearchTerm.query_text$$"/> </actionGroup> <actionGroup ref="searchTermFilterBySearchQuery" stepKey="filterBySecondSearchQuery"> - <argument name="searchQuery" value="$createSecondSearchTerm.query_text$"/> + <argument name="searchQuery" value="$$createSecondSearchTerm.query_text$$"/> </actionGroup> <actionGroup ref="searchTermFilterBySearchQuery" stepKey="filterByThirdSearchQuery"> - <argument name="searchQuery" value="$createThirdSearchTerm.query_text$"/> + <argument name="searchQuery" value="$$createThirdSearchTerm.query_text$$"/> </actionGroup> <!-- Delete created below search terms --> @@ -53,13 +53,13 @@ <!-- Assert search terms are absent on the search term page --> <actionGroup ref="AssertSearchTermNotInGrid" stepKey="assertFirstSearchTermNotInGrid"> - <argument name="searchQuery" value="$createFirstSearchTerm.query_text$"/> + <argument name="searchQuery" value="$$createFirstSearchTerm.query_text$$"/> </actionGroup> <actionGroup ref="AssertSearchTermNotInGrid" stepKey="assertSecondSearchTermNotInGrid"> - <argument name="searchQuery" value="$createSecondSearchTerm.query_text$"/> + <argument name="searchQuery" value="$$createSecondSearchTerm.query_text$$"/> </actionGroup> <actionGroup ref="AssertSearchTermNotInGrid" stepKey="assertThirdSearchTermNotInGrid"> - <argument name="searchQuery" value="$createThirdSearchTerm.query_text$"/> + <argument name="searchQuery" value="$$createThirdSearchTerm.query_text$$"/> </actionGroup> <!-- Go to storefront page --> @@ -68,15 +68,15 @@ <!-- Verify search term deletion on storefront --> <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="quickSearchForFirstSearchTerm"> - <argument name="phrase" value="$createFirstSearchTerm.query_text$"/> + <argument name="phrase" value="$$createFirstSearchTerm.query_text$$"/> </actionGroup> <actionGroup ref="StorefrontCheckSearchIsEmpty" stepKey="checkEmptyForFirstSearchTerm"/> <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="quickSearchForSecondSearchTerm"> - <argument name="phrase" value="$createSecondSearchTerm.query_text$"/> + <argument name="phrase" value="$$createSecondSearchTerm.query_text$$"/> </actionGroup> <actionGroup ref="StorefrontCheckSearchIsEmpty" stepKey="checkEmptyForSecondSearchTerm"/> <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="quickSearchForThirdSearchTerm"> - <argument name="phrase" value="$createThirdSearchTerm.query_text$"/> + <argument name="phrase" value="$$createThirdSearchTerm.query_text$$"/> </actionGroup> <actionGroup ref="StorefrontCheckSearchIsEmpty" stepKey="checkEmptyForThirdSearchTerm"/> </test> From b80c9dcbcbb04f61d522f961f5f7b16a60a7d1d5 Mon Sep 17 00:00:00 2001 From: Vitaliy Honcharenko <vgoncharenko@magento.com> Date: Fri, 15 Mar 2019 17:07:16 -0500 Subject: [PATCH 172/265] MC-6273: Mysql url_rewrite select make on product view page 170+ times per request - changes after CR --- app/code/Magento/Config/App/Config/Type/System.php | 12 ++++++------ .../Framework/Cache/LockGuardedCacheLoader.php | 8 +++++++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Config/App/Config/Type/System.php b/app/code/Magento/Config/App/Config/Type/System.php index 36fefcb86217..8d197bc6ab04 100644 --- a/app/code/Magento/Config/App/Config/Type/System.php +++ b/app/code/Magento/Config/App/Config/Type/System.php @@ -231,8 +231,8 @@ private function loadAllData() return $this->lockQuery->lockedLoadData( self::$lockName, $loadAction, - [$this, 'readData'], - [$this, 'cacheData'] + \Closure::fromCallable([$this, 'readData']), + \Closure::fromCallable([$this, 'cacheData']) ); } @@ -256,8 +256,8 @@ private function loadDefaultScopeData($scopeType) return $this->lockQuery->lockedLoadData( self::$lockName, $loadAction, - [$this, 'readData'], - [$this, 'cacheData'] + \Closure::fromCallable([$this, 'readData']), + \Closure::fromCallable([$this, 'cacheData']) ); } @@ -295,8 +295,8 @@ private function loadScopeData($scopeType, $scopeId) return $this->lockQuery->lockedLoadData( self::$lockName, $loadAction, - [$this, 'readData'], - [$this, 'cacheData'] + \Closure::fromCallable([$this, 'readData']), + \Closure::fromCallable([$this, 'cacheData']) ); } diff --git a/lib/internal/Magento/Framework/Cache/LockGuardedCacheLoader.php b/lib/internal/Magento/Framework/Cache/LockGuardedCacheLoader.php index 39c132503daf..8575f208e6c1 100644 --- a/lib/internal/Magento/Framework/Cache/LockGuardedCacheLoader.php +++ b/lib/internal/Magento/Framework/Cache/LockGuardedCacheLoader.php @@ -52,7 +52,13 @@ public function __construct( } /** - * @inheritdoc + * Load data. + * + * @param string $lockName + * @param callable $dataLoader + * @param callable $dataCollector + * @param callable $dataSaver + * @return array */ public function lockedLoadData( string $lockName, From 96ca3d488bb010959414e391160364ad2264e22b Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Sat, 16 Mar 2019 08:31:53 +0200 Subject: [PATCH 173/265] 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 345b70e3a59743020bb833927bff168899b51f79 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Sat, 16 Mar 2019 10:33:50 +0200 Subject: [PATCH 174/265] Covering the Share Wishist by integration test --- .../Magento/Wishlist/Controller/ShareTest.php | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Wishlist/Controller/ShareTest.php diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/ShareTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/ShareTest.php new file mode 100644 index 000000000000..83d79a43620f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/ShareTest.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Controller; + +use Magento\Customer\Model\Session; +use Magento\Framework\App\Area; +use Magento\Framework\Data\Form\FormKey; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Request; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * @magentoAppIsolation enabled + */ +class ShareTest extends AbstractController +{ + /** + * Test share wishlist with correct data + * + * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + */ + public function testSuccessfullyShareWishlist() + { + $this->login(1); + $this->prepareRequestData(); + $this->dispatch('wishlist/index/send/'); + + $this->assertSessionMessages( + $this->equalTo(['Your wish list has been shared.']), + MessageInterface::TYPE_SUCCESS + ); + } + + /** + * Test share wishlist with incorrect data + * + * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + */ + public function testShareWishlistWithoutEmails() + { + $this->login(1); + $this->prepareRequestData(true); + $this->dispatch('wishlist/index/send/'); + + $this->assertSessionMessages( + $this->equalTo(['Please enter an email address.']), + MessageInterface::TYPE_ERROR + ); + } + + /** + * Login the user + * + * @param string $customerId Customer to mark as logged in for the session + * @return void + */ + protected function login($customerId) + { + /** @var Session $session */ + $session = $this->_objectManager->get(Session::class); + $session->loginById($customerId); + } + + /** + * Prepares the request with data + * + * @param bool $invalidData + * @return void + */ + private function prepareRequestData($invalidData = false) + { + Bootstrap::getInstance()->loadArea(Area::AREA_FRONTEND); + $emails = !$invalidData ? 'email-1@example.com,email-1@example.com' : ''; + + /** @var FormKey $formKey */ + $formKey = $this->_objectManager->get(FormKey::class); + $post = [ + 'emails' => $emails, + 'message' => '', + 'form_key' => $formKey->getFormKey(), + ]; + + $this->getRequest()->setMethod(Request::METHOD_POST); + $this->getRequest()->setPostValue($post); + } +} From 01eb7e61c9a3ebadd7d3350b473fd5d0db8d1578 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Sat, 16 Mar 2019 10:41:16 +0200 Subject: [PATCH 175/265] Fix clearing admin quote address when removing all items --- app/code/Magento/Checkout/etc/di.xml | 3 --- app/code/Magento/Checkout/etc/frontend/di.xml | 3 +++ .../Checkout/Plugin/Model/Quote/ResetQuoteAddressesTest.php | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Checkout/etc/di.xml b/app/code/Magento/Checkout/etc/di.xml index 71dfd12bb477..4ebd594a2856 100644 --- a/app/code/Magento/Checkout/etc/di.xml +++ b/app/code/Magento/Checkout/etc/di.xml @@ -49,7 +49,4 @@ </argument> </arguments> </type> - <type name="Magento\Quote\Model\Quote"> - <plugin name="clear_addresses_after_product_delete" type="Magento\Checkout\Plugin\Model\Quote\ResetQuoteAddresses"/> - </type> </config> diff --git a/app/code/Magento/Checkout/etc/frontend/di.xml b/app/code/Magento/Checkout/etc/frontend/di.xml index 00bcd2a27005..8f35fe9f37ab 100644 --- a/app/code/Magento/Checkout/etc/frontend/di.xml +++ b/app/code/Magento/Checkout/etc/frontend/di.xml @@ -96,4 +96,7 @@ </argument> </arguments> </type> + <type name="Magento\Quote\Model\Quote"> + <plugin name="clear_addresses_after_product_delete" type="Magento\Checkout\Plugin\Model\Quote\ResetQuoteAddresses"/> + </type> </config> diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Plugin/Model/Quote/ResetQuoteAddressesTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Plugin/Model/Quote/ResetQuoteAddressesTest.php index 994076baddda..60ccdb88676a 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/Plugin/Model/Quote/ResetQuoteAddressesTest.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/Plugin/Model/Quote/ResetQuoteAddressesTest.php @@ -22,6 +22,7 @@ class ResetQuoteAddressesTest extends \PHPUnit\Framework\TestCase /** * @magentoDataFixture Magento/Checkout/_files/quote_with_virtual_product_and_address.php * + * @magentoAppArea frontend * @return void */ public function testAfterRemoveItem(): void From 68d793d81a45035b1d5df2dc883582ef6a37229f Mon Sep 17 00:00:00 2001 From: Oleg Volkov <sirwerwolf@gmail.com> Date: Sat, 16 Mar 2019 13:56:41 +0200 Subject: [PATCH 176/265] #19835 Fix admin header buttons flicker --- .../Backend/view/adminhtml/templates/pageactions.phtml | 2 +- .../web/css/source/module/main/_actions-bar.less | 4 ++++ lib/web/mage/backend/floating-header.js | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Backend/view/adminhtml/templates/pageactions.phtml b/app/code/Magento/Backend/view/adminhtml/templates/pageactions.phtml index 69d545f12d07..0a1dcb0b626e 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/pageactions.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/pageactions.phtml @@ -8,7 +8,7 @@ ?> <?php if ($block->getChildHtml()):?> - <div data-mage-init='{"floatingHeader": {}}' class="page-actions" <?= /* @escapeNotVerified */ $block->getUiId('content-header') ?>> + <div data-mage-init='{"floatingHeader": {}}' class="page-actions floating-header" <?= /* @escapeNotVerified */ $block->getUiId('content-header') ?>> <?= $block->getChildHtml() ?> </div> <?php endif; ?> diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_actions-bar.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_actions-bar.less index 07050c1e5111..131013bacd80 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_actions-bar.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_actions-bar.less @@ -46,6 +46,10 @@ @_page-action__indent: 1.3rem; float: right; + &.floating-header { + &:extend(.page-actions-buttons all); + } + .page-main-actions & { &._fixed { left: @page-wrapper__indent-left; diff --git a/lib/web/mage/backend/floating-header.js b/lib/web/mage/backend/floating-header.js index 06861277559a..a6f767259488 100644 --- a/lib/web/mage/backend/floating-header.js +++ b/lib/web/mage/backend/floating-header.js @@ -48,6 +48,7 @@ define([ this.element.wrapInner($('<div/>', { 'class': 'page-actions-inner', 'data-title': title })); + this.element.removeClass('floating-header'); }, /** From e91cff303581f43b93b7ca4ec210712fb505f063 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Sat, 16 Mar 2019 10:33:15 +0200 Subject: [PATCH 177/265] Convert UpdateShoppingCartTest to MFTF --- .../Catalog/Test/Mftf/Data/ProductData.xml | 5 + .../Section/CheckoutCartProductSection.xml | 3 + .../Test/StorefrontUpdateShoppingCartTest.xml | 114 ++++++++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 0b3a31194ea3..44635e9b93f9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -386,6 +386,11 @@ <var key="sku" entityType="product" entityKey="sku" /> <requiredEntity type="product_option">ProductOptionDropDownWithLongValuesTitle</requiredEntity> </entity> + <entity name="productWithOptions3" type="product"> + <var key="sku" entityType="product" entityKey="sku" /> + <requiredEntity type="product_option">ProductOptionField</requiredEntity> + <requiredEntity type="product_option">ProductOptionArea</requiredEntity> + </entity> <entity name="ApiVirtualProductWithDescription" type="product"> <data key="sku" unique="suffix">api-virtual-product</data> <data key="type_id">virtual</data> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml index dcfb12fd4e96..a63dc5be2de3 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml @@ -15,6 +15,9 @@ <element name="ProductPriceByName" type="text" selector="//main//table[@id='shopping-cart-table']//tbody//tr[..//strong[contains(@class, 'product-item-name')]//a/text()='{{var1}}'][1]//td[contains(@class, 'price')]//span[@class='price']" parameterized="true"/> + <element name="ProductSubtotalByName" type="text" + selector="//main//table[@id='shopping-cart-table']//tbody//tr[..//strong[contains(@class, 'product-item-name')]//a/text()='{{var1}}'][1]//td[contains(@class, 'subtotal')]//span[@class='price']" + parameterized="true"/> <element name="ProductImageByName" type="text" selector="//main//table[@id='shopping-cart-table']//tbody//tr//img[contains(@class, 'product-image-photo') and @alt='{{var1}}']" parameterized="true"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartTest.xml new file mode 100644 index 000000000000..94abf4b26c34 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartTest.xml @@ -0,0 +1,114 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="UpdateShoppingCartTestVariation1"> + <annotations> + <features value="Checkout"/> + <title value="Check updating shopping cart while updating items qty"/> + <description value="Check updating shopping cart while updating items qty"/> + <group value="shoppingCart"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + <field key="price">100</field> + </createData> + + <!-- Add the newly created product to the shopping cart --> + <amOnPage url="$$createProduct.custom_attributes[url_key]$$.html" stepKey="navigateToSimpleProductPage"/> + <waitForPageLoad stepKey="waitForCatalogPageLoad1"/> + <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage1"> + <argument name="productName" value="$$createProduct.name$$"/> + </actionGroup> + </before> + <after> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + + <!-- Go to the shopping cart --> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnPageShoppingCart"/> + <waitForPageLoad stepKey="waitForCheckoutPageLoad1"/> + + <!-- Change the product QTY --> + <fillField selector="{{CheckoutCartProductSection.ProductQuantityByName($$createProduct.name$$)}}" userInput="3" stepKey="changeCartQty"/> + <click selector="{{CheckoutCartProductSection.updateShoppingCartButton}}" stepKey="openShoppingCart"/> + <waitForPageLoad stepKey="waitForCheckoutPageLoad2"/> + + <!-- The price and QTY values should be updated for the product --> + <grabValueFrom selector="{{CheckoutCartProductSection.ProductQuantityByName($$createProduct.name$$)}}" stepKey="grabProductQtyInCart"/> + <see userInput="$300" selector="{{CheckoutCartProductSection.ProductSubtotalByName($$createProduct.name$$)}}" stepKey="assertProductPrice"/> + <assertEquals expected="3" actual="$grabProductQtyInCart" stepKey="assertProductQtyInCart"/> + + <!-- Subtotal should be updated --> + <see userInput="$300" selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="assertCartSubtotal"/> + + <!-- Minicart product price and subtotal should be updated --> + <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="openMinicart"/> + <grabValueFrom selector="{{StorefrontMinicartSection.itemQuantity($$createProduct.name$$)}}" stepKey="grabProductQtyInMinicart"/> + <assertEquals expected="3" actual="$grabProductQtyInMinicart" stepKey="assertProductQtyInMinicart"/> + <see userInput="$300" selector="{{StorefrontMinicartSection.subtotal}}" stepKey="assertMinicartSubtotal"/> + </test> + <test name="UpdateShoppingCartTestVariation2"> + <annotations> + <features value="Checkout"/> + <title value="Check updating shopping cart while updating qty of items with custom options"/> + <description value="Check updating shopping cart while updating qty of items with custom options"/> + <group value="shoppingCart"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + <field key="price">100</field> + </createData> + + <!-- Add two custom options to the product: field and textarea --> + <updateData createDataKey="createProduct" entity="productWithOptions3" stepKey="updateProductWithOption"/> + + <!-- Go to the product page, fill the custom options values and add the product to the shopping cart --> + <amOnPage url="{{StorefrontHomePage.url}}$createProduct.custom_attributes[url_key]$.html" stepKey="amOnProductPage"/> + <waitForPageLoad stepKey="waitForCatalogPageLoad"/> + <fillField userInput="OptionField" selector="{{StorefrontProductInfoMainSection.productOptionFieldInput(ProductOptionField.title)}}" stepKey="fillProductOptionInputField"/> + <fillField userInput="OptionArea" selector="{{StorefrontProductInfoMainSection.productOptionAreaInput(ProductOptionArea.title)}}" stepKey="fillProductOptionInputArea"/> + <actionGroup ref="StorefrontAddToCartCustomOptionsProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> + <argument name="productName" value="$createProduct.name$"/> + </actionGroup> + </before> + <after> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + + <!-- Go to the shopping cart --> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnPageShoppingCart"/> + <waitForPageLoad stepKey="waitForCheckoutPageLoad1"/> + + <!-- Change the product QTY --> + <fillField selector="{{CheckoutCartProductSection.ProductQuantityByName($$createProduct.name$$)}}" userInput="11" stepKey="changeCartQty"/> + <click selector="{{CheckoutCartProductSection.updateShoppingCartButton}}" stepKey="openShoppingCart"/> + <waitForPageLoad stepKey="waitForCheckoutPageLoad2"/> + + <!-- The price and QTY values should be updated for the product --> + <grabValueFrom selector="{{CheckoutCartProductSection.ProductQuantityByName($$createProduct.name$$)}}" stepKey="grabProductQtyInCart"/> + <see userInput="$1,320.00" selector="{{CheckoutCartProductSection.ProductSubtotalByName($$createProduct.name$$)}}" stepKey="assertProductPrice"/> + <assertEquals expected="11" actual="$grabProductQtyInCart" stepKey="assertProductQtyInCart"/> + <see userInput="$1,320.00" selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="assertSubtotal"/> + + <!-- Minicart product price and subtotal should be updated --> + <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="openMinicart"/> + <grabValueFrom selector="{{StorefrontMinicartSection.itemQuantity($$createProduct.name$$)}}" stepKey="grabProductQtyInMinicart"/> + <assertEquals expected="11" actual="$grabProductQtyInMinicart" stepKey="assertProductQtyInMinicart"/> + <see userInput="1,320.00" selector="{{StorefrontMinicartSection.subtotal}}" stepKey="assertMinicartSubtotal"/> + </test> +</tests> From 5c101bfefda20e54168a1e5a7ed044b40ec3889c Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Sat, 16 Mar 2019 16:07:32 +0200 Subject: [PATCH 178/265] Covering the Wishlist classes by integration and Unit Tests --- .../Product/AttributeValueProviderTest.php | 178 ++++++++++++++++++ .../Magento/Wishlist/Controller/ShareTest.php | 2 +- 2 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php diff --git a/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php b/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php new file mode 100644 index 000000000000..baafbdef47fe --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php @@ -0,0 +1,178 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Test\Unit\Model\Product; + +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Wishlist\Model\Product\AttributeValueProvider; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject; + +/** + * @covers CreditCardTokenFormatter + */ +class AttributeValueProviderTest extends TestCase +{ + /** + * @var AttributeValueProvider|PHPUnit_Framework_MockObject_MockObject + */ + private $attributeValueProvider; + + /** + * @var CollectionFactory|PHPUnit_Framework_MockObject_MockObject + */ + private $productCollectionFactoryMock; + + /** + * @var @var Product|PHPUnit_Framework_MockObject_MockObject + */ + private $productMock; + + /** + * @var AdapterInterface|PHPUnit_Framework_MockObject_MockObject + */ + private $connectionMock; + + /** + * Set Up + * + * @return void + */ + protected function setUp() + { + $this->productCollectionFactoryMock = $this->createPartialMock( + CollectionFactory::class, + ['create'] + ); + $this->attributeValueProvider = new AttributeValueProvider( + $this->productCollectionFactoryMock + ); + } + + /** + * Get attribute text when the flat table is disabled + * + * @param int $productId + * @param string $attributeCode + * @param string $attributeText + * @return void + * @dataProvider attributeDataProvider + */ + public function testGetAttributeTextWhenFlatIsDisabled(int $productId, string $attributeCode, string $attributeText) + { + $this->productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->setMethods(['getData']) + ->getMock(); + + $this->productMock->expects($this->any()) + ->method('getData') + ->with($attributeCode) + ->willReturn($attributeText); + + $productCollection = $this->getMockBuilder(Collection::class) + ->disableOriginalConstructor() + ->setMethods([ + 'addIdFilter', 'addStoreFilter', 'addAttributeToSelect', 'isEnabledFlat', 'getFirstItem' + ])->getMock(); + + $productCollection->expects($this->any()) + ->method('addIdFilter') + ->willReturnSelf(); + $productCollection->expects($this->any()) + ->method('addStoreFilter') + ->willReturnSelf(); + $productCollection->expects($this->any()) + ->method('addAttributeToSelect') + ->willReturnSelf(); + $productCollection->expects($this->any()) + ->method('isEnabledFlat') + ->willReturn(false); + $productCollection->expects($this->any()) + ->method('getFirstItem') + ->willReturn($this->productMock); + + $this->productCollectionFactoryMock->expects($this->atLeastOnce()) + ->method('create') + ->willReturn($productCollection); + + $actual = $this->attributeValueProvider->getRawAttributeValue($productId, $attributeCode); + + $this->assertEquals($attributeText, $actual); + } + + + /** + * Get attribute text when the flat table is enabled + * + * @dataProvider attributeDataProvider + * @param int $productId + * @param string $attributeCode + * @param string $attributeText + * @return void + */ + public function testGetAttributeTextWhenFlatIsEnabled(int $productId, string $attributeCode, string $attributeText) + { + $this->connectionMock = $this->getMockBuilder(AdapterInterface::class)->getMockForAbstractClass(); + $this->connectionMock->expects($this->any()) + ->method('fetchRow') + ->willReturn([ + $attributeCode => $attributeText + ]); + $this->productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->setMethods(['getData']) + ->getMock(); + $this->productMock->expects($this->any()) + ->method('getData') + ->with($attributeCode) + ->willReturn($attributeText); + + $productCollection = $this->getMockBuilder(Collection::class) + ->disableOriginalConstructor() + ->setMethods([ + 'addIdFilter', 'addStoreFilter', 'addAttributeToSelect', 'isEnabledFlat', 'getConnection' + ])->getMock(); + + $productCollection->expects($this->any()) + ->method('addIdFilter') + ->willReturnSelf(); + $productCollection->expects($this->any()) + ->method('addStoreFilter') + ->willReturnSelf(); + $productCollection->expects($this->any()) + ->method('addAttributeToSelect') + ->willReturnSelf(); + $productCollection->expects($this->any()) + ->method('isEnabledFlat') + ->willReturn(true); + $productCollection->expects($this->any()) + ->method('getConnection') + ->willReturn($this->connectionMock); + + $this->productCollectionFactoryMock->expects($this->atLeastOnce()) + ->method('create') + ->willReturn($productCollection); + + $actual = $this->attributeValueProvider->getRawAttributeValue($productId, $attributeCode); + + $this->assertEquals($attributeText, $actual); + } + + /** + * @return array + */ + public function attributeDataProvider(): array + { + return [ + [1, 'attribute_code', 'Attribute Text'] + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/ShareTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/ShareTest.php index 83d79a43620f..47705262caaf 100644 --- a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/ShareTest.php +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/ShareTest.php @@ -76,7 +76,7 @@ protected function login($customerId) private function prepareRequestData($invalidData = false) { Bootstrap::getInstance()->loadArea(Area::AREA_FRONTEND); - $emails = !$invalidData ? 'email-1@example.com,email-1@example.com' : ''; + $emails = !$invalidData ? 'email-1@example.com,email-2@example.com' : ''; /** @var FormKey $formKey */ $formKey = $this->_objectManager->get(FormKey::class); From d49aa76497ee603ceb7ede6e7b6bc21137ef2ddd Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Sat, 16 Mar 2019 16:12:16 +0200 Subject: [PATCH 179/265] Small adjustment --- .../Test/Unit/Model/Product/AttributeValueProviderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php b/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php index baafbdef47fe..e5f6b84bfc3d 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php @@ -16,7 +16,7 @@ use PHPUnit_Framework_MockObject_MockObject; /** - * @covers CreditCardTokenFormatter + * AttributeValueProviderTest */ class AttributeValueProviderTest extends TestCase { From 7a2774318fe8056d0ef44e1338bf3984fc46857d Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Sat, 16 Mar 2019 16:17:55 +0200 Subject: [PATCH 180/265] Small adjustments --- .../Test/Unit/Model/Product/AttributeValueProviderTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php b/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php index e5f6b84bfc3d..fb0113eb6ae7 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php @@ -31,7 +31,7 @@ class AttributeValueProviderTest extends TestCase private $productCollectionFactoryMock; /** - * @var @var Product|PHPUnit_Framework_MockObject_MockObject + * @var Product|PHPUnit_Framework_MockObject_MockObject */ private $productMock; @@ -108,7 +108,6 @@ public function testGetAttributeTextWhenFlatIsDisabled(int $productId, string $a $this->assertEquals($attributeText, $actual); } - /** * Get attribute text when the flat table is enabled * From b5803c581bf0debfc7e7b45be41cf3e869a4fc20 Mon Sep 17 00:00:00 2001 From: Harniuk Bohdan <bohar@smile.fr> Date: Sat, 16 Mar 2019 16:44:06 +0200 Subject: [PATCH 181/265] 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 = <<<QUERY +mutation { + setBillingAddressOnCart( + input: { + {$input} + } + ) { + cart { + billing_address { + city + } + } + } +} +QUERY; + + $this->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 = <<<QUERY +mutation { + setBillingAddressOnCart( + input: { + {$input} + } + ) { + cart { + billing_address { + city + } + } + } +} +QUERY; + + $this->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?= <samuelmoreira27@gmail.com> Date: Sat, 16 Mar 2019 17:26:49 +0000 Subject: [PATCH 182/265] 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 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +// @codingStandardsIgnoreFile + +?> +<?php +/** + * @var $block \Magento\CheckoutAgreements\Block\Agreements + */ +?> +<?php if (!$block->getAgreements()) { + return; +} ?> +<ol id="checkout-agreements" class="agreements checkout items"> + <?php /** @var \Magento\CheckoutAgreements\Api\Data\AgreementInterface $agreement */ ?> + <?php foreach ($block->getAgreements() as $agreement): ?> + <li class="item"> + <div class="checkout-agreement-item-content"<?= ($agreement->getContentHeight() ? ' style="height:' . $agreement->getContentHeight() . '"' : '') ?>> + <?php if ($agreement->getIsHtml()):?> + <?= /* @escapeNotVerified */ $agreement->getContent() ?> + <?php else:?> + <?= nl2br($block->escapeHtml($agreement->getContent())) ?> + <?php endif; ?> + </div> + <?php if($agreement->getMode() == \Magento\CheckoutAgreements\Model\AgreementModeOptions::MODE_MANUAL): ?> + <div id="checkout-agreements-form-<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>" class="field choice agree required"> + <input type="checkbox" + id="agreement-<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>" + name="agreement[<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>]" + value="1" + title="<?= $block->escapeHtml($agreement->getCheckboxText()) ?>" + class="checkbox" + data-validate="{required:true}"/> + <label class="label" for="agreement-<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>"> + <span><?= $agreement->getIsHtml() ? $agreement->getCheckboxText() : $block->escapeHtml($agreement->getCheckboxText()) ?></span> + </label> + </div> + <?php elseif($agreement->getMode() == \Magento\CheckoutAgreements\Model\AgreementModeOptions::MODE_AUTO): ?> + <div id="checkout-agreements-form-<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>" class="field choice agree"> + <span><?= $agreement->getIsHtml() ? $agreement->getCheckboxText() : $block->escapeHtml($agreement->getCheckboxText()) ?></span> + </div> + <?php endif; ?> + </li> + <?php endforeach ?> +</ol> \ No newline at end of file From 904a4a8c80b95da48d5095dde7fdec34540952f7 Mon Sep 17 00:00:00 2001 From: Leandry <leandry@atwix.com> Date: Mon, 18 Mar 2019 01:17:48 +0200 Subject: [PATCH 183/265] Convert FlushStaticFilesCacheButtonVisibilityTest to MFTF --- .../Section/AdminCacheManagementSection.xml | 1 + ...shStaticFilesCacheButtonVisibilityTest.xml | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 app/code/Magento/PageCache/Test/Mftf/Test/FlushStaticFilesCacheButtonVisibilityTest.xml diff --git a/app/code/Magento/PageCache/Test/Mftf/Section/AdminCacheManagementSection.xml b/app/code/Magento/PageCache/Test/Mftf/Section/AdminCacheManagementSection.xml index 34a77095d524..12d35df42bcc 100644 --- a/app/code/Magento/PageCache/Test/Mftf/Section/AdminCacheManagementSection.xml +++ b/app/code/Magento/PageCache/Test/Mftf/Section/AdminCacheManagementSection.xml @@ -30,5 +30,6 @@ <element name="pageCacheCheckbox" type="checkbox" selector="input[value='full_page']"/> <element name="webServicesConfigCheckbox" type="checkbox" selector="input[value='config_webservice']"/> <element name="translationsCheckbox" type="checkbox" selector="input[value='translate']"/> + <element name="FlushStaticFilesCache" type="button" selector="//*[@id='container']//button[contains(., 'Flush Static Files Cache')]"/> </section> </sections> diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/FlushStaticFilesCacheButtonVisibilityTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/FlushStaticFilesCacheButtonVisibilityTest.xml new file mode 100644 index 000000000000..6cd280bb7add --- /dev/null +++ b/app/code/Magento/PageCache/Test/Mftf/Test/FlushStaticFilesCacheButtonVisibilityTest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="FlushStaticFilesCacheButtonVisibilityTest"> + <annotations> + <features value="PageCache"/> + <title value="Check visibility of flush static files cache button"/> + <description value="Flush Static Files Cache button visibility"/> + <severity value="MAJOR"/> + <stories value="Check flush static files cache button"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Log in to Admin Panel --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + + <!-- Open Cache Management page --> + <amOnPage url="{{AdminCacheManagementPage.url}}" stepKey="amOnPageCacheManagement"/> + <waitForPageLoad stepKey="waitForPageCacheManagementLoad"/> + + <!-- Check 'Flush Static Files Cache' not visible in production mode. --> + <dontSee selector="{{AdminCacheManagementSection.FlushStaticFilesCache}}" stepKey="seeFlushStaticFilesButton" /> + </test> +</tests> From fda4377384169d385a04057e7edf2f1ac32bc5ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Mon, 18 Mar 2019 09:21:23 +0100 Subject: [PATCH 184/265] Move deploymentConfig to constructor in Locale Resolver --- .../Magento/Framework/Locale/Resolver.php | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/lib/internal/Magento/Framework/Locale/Resolver.php b/lib/internal/Magento/Framework/Locale/Resolver.php index 3a516fcea5e9..83637dac9c47 100644 --- a/lib/internal/Magento/Framework/Locale/Resolver.php +++ b/lib/internal/Magento/Framework/Locale/Resolver.php @@ -64,16 +64,19 @@ class Resolver implements ResolverInterface * @param string $defaultLocalePath * @param string $scopeType * @param mixed $locale + * @param DeploymentConfig|null $deploymentConfig */ public function __construct( ScopeConfigInterface $scopeConfig, $defaultLocalePath, $scopeType, - $locale = null + $locale = null, + DeploymentConfig $deploymentConfig = null ) { $this->scopeConfig = $scopeConfig; $this->defaultLocalePath = $defaultLocalePath; $this->scopeType = $scopeType; + $this->deploymentConfig = $deploymentConfig ?: ObjectManager::getInstance()->create(DeploymentConfig::class); $this->setLocale($locale); } @@ -101,7 +104,7 @@ public function getDefaultLocale() { if (!$this->defaultLocale) { $locale = false; - if ($this->getDeploymentConfig()->isAvailable() && $this->getDeploymentConfig()->isDbAvailable()) { + if ($this->deploymentConfig->isAvailable() && $this->deploymentConfig->isDbAvailable()) { $locale = $this->scopeConfig->getValue($this->getDefaultLocalePath(), $this->scopeType); } if (!$locale) { @@ -169,18 +172,4 @@ public function revert() } return $result; } - - /** - * Retrieve Deployment Config - * - * @return DeploymentConfig - */ - private function getDeploymentConfig() - { - if (!$this->deploymentConfig) { - $this->deploymentConfig = ObjectManager::getInstance()->get(DeploymentConfig::class); - } - - return $this->deploymentConfig; - } } From b2004c3f74f7cdd2654986f056f6ba8d8beaa5ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Mon, 18 Mar 2019 09:21:48 +0100 Subject: [PATCH 185/265] fix NumberFormatter initialization when no currency is set --- lib/internal/Magento/Framework/Locale/Format.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Locale/Format.php b/lib/internal/Magento/Framework/Locale/Format.php index ca50cdb2440f..0cdd80208fbc 100644 --- a/lib/internal/Magento/Framework/Locale/Format.php +++ b/lib/internal/Magento/Framework/Locale/Format.php @@ -100,7 +100,7 @@ public function getPriceFormat($localeCode = null, $currencyCode = null) } $formatter = new \NumberFormatter( - $localeCode . '@currency=' . $currency->getCode(), + $currency->getCode() ? $localeCode . '@currency=' . $currency->getCode() : $localeCode, \NumberFormatter::CURRENCY ); $format = $formatter->getPattern(); From b86f981ad04f63c7ca8d2c3ca5f3b4abe999ca4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Mon, 18 Mar 2019 09:22:34 +0100 Subject: [PATCH 186/265] fix de_CH group symbol in unit 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 1141f451c13a..72cc08b7c015 100644 --- a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php +++ b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php @@ -91,7 +91,7 @@ public function getPriceFormatDataProvider(): array return [ ['en_US', ['decimalSymbol' => '.', 'groupSymbol' => ',']], ['de_DE', ['decimalSymbol' => ',', 'groupSymbol' => '.']], - ['de_CH', ['decimalSymbol' => '.', 'groupSymbol' => '\'']], + ['de_CH', ['decimalSymbol' => '.', 'groupSymbol' => '’']], ['uk_UA', ['decimalSymbol' => ',', 'groupSymbol' => ' ']] ]; } From dc71ef160d300f1bec2e2a0c610953738d3907a8 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Mon, 18 Mar 2019 11:01:13 +0200 Subject: [PATCH 187/265] ENGCOM-4502: Static tests fix. --- .../Model/Spi/StockRegistryProviderInterface.php | 6 ++++++ .../Model/Spi/StockStateProviderInterface.php | 15 ++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Model/Spi/StockRegistryProviderInterface.php b/app/code/Magento/CatalogInventory/Model/Spi/StockRegistryProviderInterface.php index 49d9b4aaa34e..0fa4b919c40f 100644 --- a/app/code/Magento/CatalogInventory/Model/Spi/StockRegistryProviderInterface.php +++ b/app/code/Magento/CatalogInventory/Model/Spi/StockRegistryProviderInterface.php @@ -15,12 +15,16 @@ interface StockRegistryProviderInterface { /** + * Get stock. + * * @param int $scopeId * @return \Magento\CatalogInventory\Api\Data\StockInterface */ public function getStock($scopeId); /** + * Get stock item. + * * @param int $productId * @param int $scopeId * @return \Magento\CatalogInventory\Api\Data\StockItemInterface @@ -28,6 +32,8 @@ public function getStock($scopeId); public function getStockItem($productId, $scopeId); /** + * Get stock status. + * * @param int $productId * @param int $scopeId * @return \Magento\CatalogInventory\Api\Data\StockStatusInterface diff --git a/app/code/Magento/CatalogInventory/Model/Spi/StockStateProviderInterface.php b/app/code/Magento/CatalogInventory/Model/Spi/StockStateProviderInterface.php index 31bdc19f9cf7..30f703b5b928 100644 --- a/app/code/Magento/CatalogInventory/Model/Spi/StockStateProviderInterface.php +++ b/app/code/Magento/CatalogInventory/Model/Spi/StockStateProviderInterface.php @@ -9,7 +9,7 @@ /** * Interface StockStateProviderInterface - * + * * @deprecated 2.3.0 Replaced with Multi Source Inventory * @link https://devdocs.magento.com/guides/v2.3/inventory/index.html * @link https://devdocs.magento.com/guides/v2.3/inventory/catalog-inventory-replacements.html @@ -17,18 +17,24 @@ interface StockStateProviderInterface { /** + * Verify stock. + * * @param StockItemInterface $stockItem * @return bool */ public function verifyStock(StockItemInterface $stockItem); /** + * Verify notification. + * * @param StockItemInterface $stockItem * @return bool */ public function verifyNotification(StockItemInterface $stockItem); /** + * Validate quote qty. + * * @param StockItemInterface $stockItem * @param int|float $itemQty * @param int|float $qtyToCheck @@ -48,8 +54,9 @@ public function checkQuoteItemQty(StockItemInterface $stockItem, $itemQty, $qtyT public function checkQty(StockItemInterface $stockItem, $qty); /** - * Returns suggested qty that satisfies qty increments and minQty/maxQty/minSaleQty/maxSaleQty conditions - * or original qty if such value does not exist + * Returns suggested qty or original qty if such value does not exist. + * + * Suggested qty satisfies qty increments and minQty/maxQty/minSaleQty/maxSaleQty conditions. * * @param StockItemInterface $stockItem * @param int|float $qty @@ -58,6 +65,8 @@ public function checkQty(StockItemInterface $stockItem, $qty); public function suggestQty(StockItemInterface $stockItem, $qty); /** + * Check qty increments. + * * @param StockItemInterface $stockItem * @param int|float $qty * @return \Magento\Framework\DataObject From 73d88aaeaa7da30c68608d7f048b7b88557e936d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Mon, 18 Mar 2019 11:34:08 +0100 Subject: [PATCH 188/265] Revert "fix de_CH group symbol in unit test" This reverts commit b86f981ad04f63c7ca8d2c3ca5f3b4abe999ca4d. --- 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 72cc08b7c015..1141f451c13a 100644 --- a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php +++ b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php @@ -91,7 +91,7 @@ public function getPriceFormatDataProvider(): array return [ ['en_US', ['decimalSymbol' => '.', 'groupSymbol' => ',']], ['de_DE', ['decimalSymbol' => ',', 'groupSymbol' => '.']], - ['de_CH', ['decimalSymbol' => '.', 'groupSymbol' => '’']], + ['de_CH', ['decimalSymbol' => '.', 'groupSymbol' => '\'']], ['uk_UA', ['decimalSymbol' => ',', 'groupSymbol' => ' ']] ]; } From 88b64017288d02c3849301cabfdc230c1bd70ab1 Mon Sep 17 00:00:00 2001 From: Alex <silyadev@gmail.com> Date: Mon, 18 Mar 2019 12:43:37 +0200 Subject: [PATCH 189/265] Fill data_hash from BULK response with correct data --- .../Model/MassSchedule.php | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php b/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php index eae92e1663fc..eb760a82916a 100644 --- a/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php +++ b/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php @@ -20,6 +20,7 @@ use Psr\Log\LoggerInterface; use Magento\AsynchronousOperations\Model\ResourceModel\Operation\OperationRepository; use Magento\Authorization\Model\UserContextInterface; +use Magento\Framework\Encryption\Encryptor; /** * Class MassSchedule used for adding multiple entities as Operations to Bulk Management with the status tracking @@ -63,6 +64,11 @@ class MassSchedule */ private $userContext; + /** + * @var Encryptor + */ + private $encryptor; + /** * Initialize dependencies. * @@ -73,6 +79,7 @@ class MassSchedule * @param LoggerInterface $logger * @param OperationRepository $operationRepository * @param UserContextInterface $userContext + * @param Encryptor|null $encryptor */ public function __construct( IdentityGeneratorInterface $identityService, @@ -81,7 +88,8 @@ public function __construct( BulkManagementInterface $bulkManagement, LoggerInterface $logger, OperationRepository $operationRepository, - UserContextInterface $userContext = null + UserContextInterface $userContext = null, + Encryptor $encryptor = null ) { $this->identityService = $identityService; $this->itemStatusInterfaceFactory = $itemStatusInterfaceFactory; @@ -90,6 +98,7 @@ public function __construct( $this->logger = $logger; $this->operationRepository = $operationRepository; $this->userContext = $userContext ?: ObjectManager::getInstance()->get(UserContextInterface::class); + $this->encryptor = $encryptor ?: ObjectManager::getInstance()->get(Encryptor::class); } /** @@ -130,10 +139,12 @@ public function publishMass($topicName, array $entitiesArray, $groupId = null, $ $requestItem = $this->itemStatusInterfaceFactory->create(); try { - $operations[] = $this->operationRepository->createByTopic($topicName, $entityParams, $groupId); + $operation = $this->operationRepository->createByTopic($topicName, $entityParams, $groupId); + $operations[] = $operation; $requestItem->setId($key); $requestItem->setStatus(ItemStatusInterface::STATUS_ACCEPTED); - $requestItems[] = $requestItem; + $requestItem->setDataHash($this->encryptor->hash($operation->getSerializedData(), Encryptor::HASH_VERSION_SHA256)); + $requestItems[] = $requestItem;git } catch (\Exception $exception) { $this->logger->error($exception); $requestItem->setId($key); From 705416eb167111285653c5656673f5bc6655f63c Mon Sep 17 00:00:00 2001 From: Alex <silyadev@gmail.com> Date: Mon, 18 Mar 2019 13:10:54 +0200 Subject: [PATCH 190/265] Fix extra symbols --- app/code/Magento/AsynchronousOperations/Model/MassSchedule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php b/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php index eb760a82916a..3ab6dc12054b 100644 --- a/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php +++ b/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php @@ -144,7 +144,7 @@ public function publishMass($topicName, array $entitiesArray, $groupId = null, $ $requestItem->setId($key); $requestItem->setStatus(ItemStatusInterface::STATUS_ACCEPTED); $requestItem->setDataHash($this->encryptor->hash($operation->getSerializedData(), Encryptor::HASH_VERSION_SHA256)); - $requestItems[] = $requestItem;git + $requestItems[] = $requestItem; } catch (\Exception $exception) { $this->logger->error($exception); $requestItem->setId($key); From c4f8496dfc4434cc367c101f660e5f318140d502 Mon Sep 17 00:00:00 2001 From: RomanKis <roman.kis.y@gmail.com> Date: Mon, 18 Mar 2019 15:48:22 +0200 Subject: [PATCH 191/265] #20825 Missing required argument $productAvailabilityChecks of Magento\Sales\Model\Order\Reorder\OrderedProductAvailabilityChecker. --- .../Magento/ConfigurableProductSales/etc/di.xml | 16 ---------------- app/code/Magento/Sales/etc/di.xml | 7 +++++++ 2 files changed, 7 insertions(+), 16 deletions(-) delete mode 100644 app/code/Magento/ConfigurableProductSales/etc/di.xml diff --git a/app/code/Magento/ConfigurableProductSales/etc/di.xml b/app/code/Magento/ConfigurableProductSales/etc/di.xml deleted file mode 100644 index b53faf74ffa1..000000000000 --- a/app/code/Magento/ConfigurableProductSales/etc/di.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> - <type name="Magento\Sales\Model\Order\Reorder\OrderedProductAvailabilityChecker"> - <arguments> - <argument name="productAvailabilityChecks" xsi:type="array"> - <item name="configurable" xsi:type="object">Magento\ConfigurableProductSales\Model\Order\Reorder\OrderedProductAvailabilityChecker</item> - </argument> - </arguments> - </type> -</config> diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml index 5a5dd925a309..6ed230861e4d 100644 --- a/app/code/Magento/Sales/etc/di.xml +++ b/app/code/Magento/Sales/etc/di.xml @@ -1015,4 +1015,11 @@ <preference for="Magento\Sales\Api\OrderCustomerDelegateInterface" type="Magento\Sales\Model\Order\OrderCustomerDelegate" /> + <type name="Magento\Sales\Model\Order\Reorder\OrderedProductAvailabilityChecker"> + <arguments> + <argument name="productAvailabilityChecks" xsi:type="array"> + <item name="configurable" xsi:type="object">Magento\ConfigurableProductSales\Model\Order\Reorder\OrderedProductAvailabilityChecker</item> + </argument> + </arguments> + </type> </config> From 65634f65e57771932f59cade74b3d6030ce8af06 Mon Sep 17 00:00:00 2001 From: Jeroen <jeroen@reachdigital.nl> Date: Mon, 18 Mar 2019 15:13:13 +0100 Subject: [PATCH 192/265] Refactor \Order\Shipment\AddTrack Controller to use ResultInterface --- .../Adminhtml/Order/Shipment/AddTrack.php | 83 ++++++++++++++----- 1 file changed, 62 insertions(+), 21 deletions(-) diff --git a/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AddTrack.php b/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AddTrack.php index fa83db490e38..8abc74b50a00 100644 --- a/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AddTrack.php +++ b/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AddTrack.php @@ -1,12 +1,23 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Shipping\Controller\Adminhtml\Order\Shipment; use Magento\Backend\App\Action; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Sales\Api\Data\ShipmentTrackInterfaceFactory; +use Magento\Sales\Api\ShipmentRepositoryInterface; +use Magento\Sales\Model\Order\Shipment\TrackFactory; +use Magento\Shipping\Controller\Adminhtml\Order\ShipmentLoader; class AddTrack extends \Magento\Backend\App\Action { @@ -18,56 +29,84 @@ class AddTrack extends \Magento\Backend\App\Action const ADMIN_RESOURCE = 'Magento_Sales::shipment'; /** - * @var \Magento\Shipping\Controller\Adminhtml\Order\ShipmentLoader + * @var ShipmentLoader */ protected $shipmentLoader; + /** + * @var ShipmentRepositoryInterface + */ + private $shipmentRepository; + + /** + * @var TrackFactory + */ + private $trackFactory; + + /** + * @var SerializerInterface + */ + private $serializer; + /** * @param Action\Context $context - * @param \Magento\Shipping\Controller\Adminhtml\Order\ShipmentLoader $shipmentLoader + * @param ShipmentLoader $shipmentLoader + * @param ShipmentRepositoryInterface|null $shipmentRepository + * @param TrackFactory|null $trackFactory + * @param SerializerInterface|null $serializer */ public function __construct( Action\Context $context, - \Magento\Shipping\Controller\Adminhtml\Order\ShipmentLoader $shipmentLoader + ShipmentLoader $shipmentLoader, + ShipmentRepositoryInterface $shipmentRepository = null, + TrackFactory $trackFactory = null, + SerializerInterface $serializer = null ) { - $this->shipmentLoader = $shipmentLoader; parent::__construct($context); + + $this->shipmentLoader = $shipmentLoader; + $this->shipmentRepository = $shipmentRepository ?: ObjectManager::getInstance() + ->get(ShipmentRepositoryInterface::class); + $this->trackFactory = $trackFactory ?: ObjectManager::getInstance() + ->get(ShipmentTrackInterfaceFactory::class); + $this->serializer = $serializer ?: ObjectManager::getInstance() + ->get(SerializerInterface::class); } /** - * Add new tracking number action + * Add new tracking number action. * - * @return void - * @throws \Magento\Framework\Exception\LocalizedException + * @return ResultInterface */ - public function execute() + public function execute(): ResultInterface { try { $carrier = $this->getRequest()->getPost('carrier'); $number = $this->getRequest()->getPost('number'); $title = $this->getRequest()->getPost('title'); + if (empty($carrier)) { - throw new \Magento\Framework\Exception\LocalizedException(__('Please specify a carrier.')); + throw new LocalizedException(__('Please specify a carrier.')); } if (empty($number)) { - throw new \Magento\Framework\Exception\LocalizedException(__('Please enter a tracking number.')); + throw new LocalizedException(__('Please enter a tracking number.')); } + $this->shipmentLoader->setOrderId($this->getRequest()->getParam('order_id')); $this->shipmentLoader->setShipmentId($this->getRequest()->getParam('shipment_id')); $this->shipmentLoader->setShipment($this->getRequest()->getParam('shipment')); $this->shipmentLoader->setTracking($this->getRequest()->getParam('tracking')); $shipment = $this->shipmentLoader->load(); if ($shipment) { - $track = $this->_objectManager->create( - \Magento\Sales\Model\Order\Shipment\Track::class - )->setNumber( + $track = $this->trackFactory->create()->setNumber( $number )->setCarrierCode( $carrier )->setTitle( $title ); - $shipment->addTrack($track)->save(); + $shipment->addTrack($track); + $this->shipmentRepository->save($shipment); $this->_view->loadLayout(); $this->_view->getPage()->getConfig()->getTitle()->prepend(__('Shipments')); @@ -78,16 +117,18 @@ public function execute() 'message' => __('We can\'t initialize shipment for adding tracking number.'), ]; } - } catch (\Magento\Framework\Exception\LocalizedException $e) { + } catch (LocalizedException $e) { $response = ['error' => true, 'message' => $e->getMessage()]; } catch (\Exception $e) { $response = ['error' => true, 'message' => __('Cannot add tracking number.')]; } - if (is_array($response)) { - $response = $this->_objectManager->get(\Magento\Framework\Json\Helper\Data::class)->jsonEncode($response); - $this->getResponse()->representJson($response); - } else { - $this->getResponse()->setBody($response); + + if (\is_array($response)) { + $response = $this->serializer->serialize($response); + + return $this->resultFactory->create(ResultFactory::TYPE_JSON)->setJsonData($response); } + + return $this->resultFactory->create(ResultFactory::TYPE_RAW)->setContents($response); } } From 13126a5355d55ba04631b1d3302566f2420e5fa9 Mon Sep 17 00:00:00 2001 From: Vitaliy Honcharenko <vgoncharenko@magento.com> Date: Mon, 18 Mar 2019 09:52:05 -0500 Subject: [PATCH 193/265] MC-6273: Mysql url_rewrite select make on product view page 170+ times per request - fixed unit tests --- .../Framework/View/Test/Unit/Element/AbstractBlockTest.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php index dbc16b808c47..dba775ea894f 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php @@ -65,7 +65,10 @@ protected function setUp() $this->eventManagerMock = $this->getMockForAbstractClass(EventManagerInterface::class); $this->scopeConfigMock = $this->getMockForAbstractClass(ScopeConfigInterface::class); $this->cacheStateMock = $this->getMockForAbstractClass(CacheStateInterface::class); - $this->lockQuery = $this->getMockForAbstractClass(LockGuardedCacheLoader::class); + $this->lockQuery = $this->getMockBuilder(LockGuardedCacheLoader::class) + ->disableOriginalConstructor() + ->setMethods(['lockedLoadData']) + ->getMockForAbstractClass(); $this->sidResolverMock = $this->getMockForAbstractClass(SidResolverInterface::class); $this->sessionMock = $this->getMockForAbstractClass(SessionManagerInterface::class); $contextMock = $this->createMock(Context::class); From 8b0318efede13c8844616a90c5bf67906a835ecf Mon Sep 17 00:00:00 2001 From: Tomash Khamlai <tomash.khamlai@gmail.com> Date: Mon, 18 Mar 2019 17:14:49 +0200 Subject: [PATCH 194/265] 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 f0039d69bf74530d209ab33a5d5a6e97279a1f24 Mon Sep 17 00:00:00 2001 From: Erik Hanson <erik.hanson@gmail.com> Date: Mon, 18 Mar 2019 10:52:00 -0500 Subject: [PATCH 195/265] Flying Fists of Kung Fu Cleanup * Rename all instances of fist to first in docs and tests :D --- app/code/Magento/Authorizenet/Model/Directpost/Request.php | 2 +- ...rtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml | 2 +- ...pleProductWithRegularPriceInStockWithCustomOptionsTest.xml | 2 +- ...arPriceInStockWithCustomOptionsVisibleInSearchOnlyTest.xml | 2 +- .../Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml | 4 ++-- ...onfigurableProductWithTwoOptionsAssignedToCategoryTest.xml | 2 +- ...ableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Authorizenet/Model/Directpost/Request.php b/app/code/Magento/Authorizenet/Model/Directpost/Request.php index d518af4e04f5..10be4cd5febf 100644 --- a/app/code/Magento/Authorizenet/Model/Directpost/Request.php +++ b/app/code/Magento/Authorizenet/Model/Directpost/Request.php @@ -194,7 +194,7 @@ public function setDataFromOrder( /** * Set sign hash into the request object. * - * All needed fields should be placed in the object fist. + * All needed fields should be placed in the object first. * * @return $this */ diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml index 70edb0ce3ea7..17769c79677f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml @@ -139,7 +139,7 @@ </assertEquals> <!--Verify we see customizable options are Required --> - <seeElement selector="{{StorefrontProductInfoMainSection.requiredCustomInput(virtualProductCustomizableOption1.title)}}" stepKey="verifyFistCustomOptionIsRequired" /> + <seeElement selector="{{StorefrontProductInfoMainSection.requiredCustomInput(virtualProductCustomizableOption1.title)}}" stepKey="verifyFirstCustomOptionIsRequired" /> <seeElement selector="{{StorefrontProductInfoMainSection.requiredCustomInput(virtualProductCustomizableOption2.title)}}" stepKey="verifySecondCustomOptionIsRequired" /> <seeElement selector="{{StorefrontProductInfoMainSection.requiredCustomSelect(virtualProductCustomizableOption3.title)}}" stepKey="verifyThirdCustomOptionIsRequired" /> <seeElement selector="{{StorefrontProductInfoMainSection.requiredCustomSelect(virtualProductCustomizableOption4.title)}}" stepKey="verifyFourthCustomOptionIsRequired" /> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml index c9a37ec40e8f..318ab6555235 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml @@ -135,7 +135,7 @@ </assertEquals> <!--Verify customer see customizable options are Required --> - <seeElement selector="{{StorefrontProductInfoMainSection.requiredCustomSelect(simpleProductCustomizableOption.title)}}" stepKey="verifyFistCustomOptionIsRequired"/> + <seeElement selector="{{StorefrontProductInfoMainSection.requiredCustomSelect(simpleProductCustomizableOption.title)}}" stepKey="verifyFirstCustomOptionIsRequired"/> <!--Verify customer see customizable option titles and prices --> <grabAttributeFrom userInput="for" selector="{{StorefrontProductInfoMainSection.customOptionLabel(simpleProductCustomizableOption.title)}}" stepKey="simpleOptionId"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockWithCustomOptionsVisibleInSearchOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockWithCustomOptionsVisibleInSearchOnlyTest.xml index d67d5b36109e..34d85e7b4685 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockWithCustomOptionsVisibleInSearchOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockWithCustomOptionsVisibleInSearchOnlyTest.xml @@ -229,7 +229,7 @@ <actualResult type="variable">productPriceAmount</actualResult> </assertEquals> <!--Verify we customer see customizable options are Required --> - <seeElement selector="{{StorefrontProductInfoMainSection.requiredCustomInput(virtualProductCustomizableOption1.title)}}" stepKey="verifyFistCustomOptionIsRequired" /> + <seeElement selector="{{StorefrontProductInfoMainSection.requiredCustomInput(virtualProductCustomizableOption1.title)}}" stepKey="verifyFirstCustomOptionIsRequired" /> <seeElement selector="{{StorefrontProductInfoMainSection.requiredCustomInput(virtualProductCustomizableOption2.title)}}" stepKey="verifySecondCustomOptionIsRequired" /> <seeElement selector="{{StorefrontProductInfoMainSection.requiredCustomSelect(virtualProductCustomizableOption3.title)}}" stepKey="verifyThirdCustomOptionIsRequired" /> <seeElement selector="{{StorefrontProductInfoMainSection.requiredCustomSelect(virtualProductCustomizableOption4.title)}}" stepKey="verifyFourthCustomOptionIsRequired" /> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml index 95b86ec3a858..43dae2d70d41 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml @@ -145,8 +145,8 @@ <click selector="{{AdminCreateProductConfigurationsPanel.attributeCheckbox(secondAttributeCode)}}" stepKey="clickOnSecondAttributeCheckbox" after="clickOnFirstAttributeCheckbox"/> <grabTextFrom selector="{{AdminCreateProductConfigurationsPanel.defaultLabel(attributeCode)}}" stepKey="grabFirstAttributeDefaultLabel" after="clickOnSecondAttributeCheckbox"/> <grabTextFrom selector="{{AdminCreateProductConfigurationsPanel.defaultLabel(secondAttributeCode)}}" stepKey="grabSecondAttributeDefaultLabel" after="grabFirstAttributeDefaultLabel"/> - <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute({$grabFirstAttributeDefaultLabel})}}" stepKey="clickOnSelectAllForFistAttribute" after="clickOnNextButton1"/> - <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute({$grabSecondAttributeDefaultLabel})}}" stepKey="clickOnSelectAllForSecondAttribute" after="clickOnSelectAllForFistAttribute"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute({$grabFirstAttributeDefaultLabel})}}" stepKey="clickOnSelectAllForFirstAttribute" after="clickOnNextButton1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute({$grabSecondAttributeDefaultLabel})}}" stepKey="clickOnSelectAllForSecondAttribute" after="clickOnSelectAllForFirstAttribute"/> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton2"/> <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmInPopup"/> </actionGroup> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml index 981985b3f4ea..1db9b3e5b79b 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml @@ -144,7 +144,7 @@ <waitForPageLoad stepKey="waitForStoreFrontPageLoad"/> <!-- Quick search the storefront for the first attribute option --> - <submitForm selector="{{StorefrontQuickSearchSection.searchMiniForm}}" parameterArray="['q' => {{colorConfigurableProductAttribute1.sku}}]" stepKey="searchStorefrontFistChildProduct"/> + <submitForm selector="{{StorefrontQuickSearchSection.searchMiniForm}}" parameterArray="['q' => {{colorConfigurableProductAttribute1.sku}}]" stepKey="searchStorefrontFirstChildProduct"/> <dontSee selector="{{StorefrontCategoryProductSection.ProductTitleByName(colorConfigurableProductAttribute1.name)}}" stepKey="dontSeeConfigurableProductFirstChild"/> <!-- Quick search the storefront for the second attribute option --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml index e278018330aa..934a410d58a8 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml @@ -126,7 +126,7 @@ <waitForPageLoad stepKey="waitForStoreFrontPageLoad"/> <!-- Quick search the storefront for the first attribute option --> - <submitForm selector="{{StorefrontQuickSearchSection.searchMiniForm}}" parameterArray="['q' => {{colorConfigurableProductAttribute1.sku}}]" stepKey="searchStorefrontFistChildProduct"/> + <submitForm selector="{{StorefrontQuickSearchSection.searchMiniForm}}" parameterArray="['q' => {{colorConfigurableProductAttribute1.sku}}]" stepKey="searchStorefrontFirstChildProduct"/> <dontSee selector="{{StorefrontCategoryProductSection.ProductTitleByName(colorConfigurableProductAttribute1.name)}}" stepKey="dontSeeConfigurableProductFirstChild"/> <!-- Quick search the storefront for the second attribute option --> From e7a723ab66e887c2f5bb3798959809d2f2931b77 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <korablov@adobe.com> Date: Mon, 18 Mar 2019 13:31:47 -0500 Subject: [PATCH 196/265] MAGETWO-98151: Add support ZooKeeper locks --- .../Framework/Lock/Backend/ZookeeperTest.php | 1 + .../Framework/Lock/LockBackendFactory.php | 2 +- .../Lock/Test/Unit/Backend/ZookeeperTest.php | 17 ++++++++++------- .../Lock/Test/Unit/LockBackendFactoryTest.php | 2 +- .../Setup/Model/ConfigOptionsList/Lock.php | 4 ++-- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php index 6e312637c7b5..ae218561377f 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php @@ -74,6 +74,7 @@ public function testLockAndUnlock() $this->assertTrue($this->model->lock($name)); $this->assertTrue($this->model->isLocked($name)); + $this->assertFalse($this->model->lock($name, 2)); $this->assertTrue($this->model->unlock($name)); $this->assertFalse($this->model->isLocked($name)); diff --git a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php index ec15736bef53..46cb2998ede7 100644 --- a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php +++ b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php @@ -90,7 +90,7 @@ public function create(): LockManagerInterface $config = $this->deploymentConfig->get('lock/config', []); if (!isset($this->lockers[$provider])) { - throw new RuntimeException(new Phrase('Unknown locks provider.')); + throw new RuntimeException(new Phrase('Unknown locks provider: %1', [$provider])); } if (self::LOCK_ZOOKEEPER === $provider && !extension_loaded(self::LOCK_ZOOKEEPER)) { diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php index c22c49d3427c..7c450a09df32 100644 --- a/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php @@ -13,11 +13,6 @@ class ZookeeperTest extends TestCase { - /** - * @var \Zookeeper|MockObject - */ - private $zookeeperMock; - /** * @var ZookeeperProvider */ @@ -41,15 +36,23 @@ protected function setUp() if (!extension_loaded('zookeeper')) { $this->markTestSkipped('Test was skipped because php extension Zookeeper is not installed.'); } - $this->zookeeperProvider = new ZookeeperProvider($this->host, '/some/path/'); } /** * @expectedException \Magento\Framework\Exception\RuntimeException * @expectedExceptionMessage The path needs to be a non-empty string. + * @return void */ public function testConstructionWithException() { - $this->zookeeperProvider = new ZookeeperProvider('some host', ''); + $this->zookeeperProvider = new ZookeeperProvider($this->host, ''); + } + + /** + * @return void + */ + public function testConstructionWithoutException() + { + $this->zookeeperProvider = new ZookeeperProvider($this->host, $this->path); } } diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php index 18053f20f010..8864ab6f9ead 100644 --- a/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php @@ -46,7 +46,7 @@ protected function setUp() /** * @expectedException \Magento\Framework\Exception\RuntimeException - * @expectedExceptionMessage Unknown locks provider. + * @expectedExceptionMessage Unknown locks provider: someProvider */ public function testCreateWithException() { diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php index 912b0e42ca82..ae236c1b5d74 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php @@ -210,12 +210,12 @@ private function validateZookeeperConfig(array $options, DeploymentConfig $deplo $errors[] = 'php extension Zookeeper is not installed.'; } - $host = (string) $options[self::INPUT_KEY_LOCK_ZOOKEEPER_HOST] + $host = $options[self::INPUT_KEY_LOCK_ZOOKEEPER_HOST] ?? $deploymentConfig->get( self::CONFIG_PATH_LOCK_ZOOKEEPER_HOST, $this->getDefaultValue(self::INPUT_KEY_LOCK_ZOOKEEPER_HOST) ); - $path = (string) $options[self::INPUT_KEY_LOCK_ZOOKEEPER_PATH] + $path = $options[self::INPUT_KEY_LOCK_ZOOKEEPER_PATH] ?? $deploymentConfig->get( self::CONFIG_PATH_LOCK_ZOOKEEPER_PATH, $this->getDefaultValue(self::INPUT_KEY_LOCK_ZOOKEEPER_PATH) From f4be1612bd4ba6d492cf920e7fdb9514fd5ebe56 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <d.cheshun@atwix.com> Date: Mon, 18 Mar 2019 20:59:08 +0200 Subject: [PATCH 197/265] 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 @@ <?php endif; ?> </li> <?php endforeach ?> -</ol> \ No newline at end of file +</ol> From 9b83593fab1f5dcbf351a8ae2fb6e88c815cecb8 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <d.cheshun@atwix.com> Date: Mon, 18 Mar 2019 21:01:44 +0200 Subject: [PATCH 198/265] 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 32764b133e89490d4d123a78456133675f9471e0 Mon Sep 17 00:00:00 2001 From: Vitaliy Honcharenko <vgoncharenko@magento.com> Date: Mon, 18 Mar 2019 14:58:15 -0500 Subject: [PATCH 199/265] MC-6273: Mysql url_rewrite select make on product view page 170+ times per request - fixed unit tests --- .../View/Test/Unit/Element/Html/LinkTest.php | 70 +++++++++---------- 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/LinkTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/LinkTest.php index b911a38dbb48..96161e12e977 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/LinkTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/LinkTest.php @@ -7,6 +7,13 @@ class LinkTest extends \PHPUnit\Framework\TestCase { + private $objectManager; + + protected function setUp() + { + $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + } + /** * @var array */ @@ -24,24 +31,8 @@ class LinkTest extends \PHPUnit\Framework\TestCase */ protected $link; - /** - * @param \Magento\Framework\View\Element\Html\Link $link - * @param string $expected - * - * @dataProvider getLinkAttributesDataProvider - */ - public function testGetLinkAttributes($link, $expected) + public function testGetLinkAttributes() { - $this->assertEquals($expected, $link->getLinkAttributes()); - } - - /** - * @return array - */ - public function getLinkAttributesDataProvider() - { - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $escaperMock = $this->getMockBuilder(\Magento\Framework\Escaper::class) ->setMethods(['escapeHtml'])->disableOriginalConstructor()->getMock(); @@ -54,13 +45,19 @@ public function getLinkAttributesDataProvider() $urlBuilderMock->expects($this->any()) ->method('getUrl') - ->will($this->returnArgument('http://site.com/link.html')); + ->willReturn('http://site.com/link.html'); $validtorMock = $this->getMockBuilder(\Magento\Framework\View\Element\Template\File\Validator::class) ->setMethods(['isValid'])->disableOriginalConstructor()->getMock(); + $validtorMock->expects($this->any()) + ->method('isValid') + ->willReturn(false); $scopeConfigMock = $this->getMockBuilder(\Magento\Framework\App\Config::class) ->setMethods(['isSetFlag'])->disableOriginalConstructor()->getMock(); + $scopeConfigMock->expects($this->any()) + ->method('isSetFlag') + ->willReturn(true); $resolverMock = $this->getMockBuilder(\Magento\Framework\View\Element\Template\File\Resolver::class) ->setMethods([])->disableOriginalConstructor()->getMock(); @@ -72,48 +69,47 @@ public function getLinkAttributesDataProvider() $contextMock->expects($this->any()) ->method('getValidator') - ->will($this->returnValue($validtorMock)); + ->willReturn($validtorMock); $contextMock->expects($this->any()) ->method('getResolver') - ->will($this->returnValue($resolverMock)); + ->willReturn($resolverMock); $contextMock->expects($this->any()) ->method('getEscaper') - ->will($this->returnValue($escaperMock)); + ->willReturn($escaperMock); $contextMock->expects($this->any()) ->method('getUrlBuilder') - ->will($this->returnValue($urlBuilderMock)); + ->willReturn($urlBuilderMock); $contextMock->expects($this->any()) ->method('getScopeConfig') - ->will($this->returnValue($scopeConfigMock)); + ->willReturn($scopeConfigMock); /** @var \Magento\Framework\View\Element\Html\Link $linkWithAttributes */ - $linkWithAttributes = $objectManagerHelper->getObject( + $linkWithAttributes = $this->objectManager->getObject( \Magento\Framework\View\Element\Html\Link::class, ['context' => $contextMock] ); + + $this->assertEquals( + 'href="http://site.com/link.html"', + $linkWithAttributes->getLinkAttributes() + ); + /** @var \Magento\Framework\View\Element\Html\Link $linkWithoutAttributes */ - $linkWithoutAttributes = $objectManagerHelper->getObject( + $linkWithoutAttributes = $this->objectManager->getObject( \Magento\Framework\View\Element\Html\Link::class, ['context' => $contextMock] ); - foreach ($this->allowedAttributes as $attribute) { - $linkWithAttributes->setDataUsingMethod($attribute, $attribute); + $linkWithoutAttributes->setDataUsingMethod($attribute, $attribute); } - return [ - 'full' => [ - 'link' => $linkWithAttributes, - 'expected' => 'shape="shape" tabindex="tabindex" onfocus="onfocus" onblur="onblur" id="id"', - ], - 'empty' => [ - 'link' => $linkWithoutAttributes, - 'expected' => '', - ], - ]; + $this->assertEquals( + 'href="http://site.com/link.html" shape="shape" tabindex="tabindex" onfocus="onfocus" onblur="onblur" id="id"', + $linkWithoutAttributes->getLinkAttributes() + ); } } From f5653ab878565e22b56e1c303127b2c7b8209e46 Mon Sep 17 00:00:00 2001 From: Dmytro Yushkin <dyushkin@adobe.com> Date: Mon, 18 Mar 2019 15:13:22 -0500 Subject: [PATCH 200/265] 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 83a1c236f5d1e9825cc3b498c1c38f8322ffb132 Mon Sep 17 00:00:00 2001 From: Vitaliy Honcharenko <vgoncharenko@magento.com> Date: Mon, 18 Mar 2019 15:43:14 -0500 Subject: [PATCH 201/265] MC-6273: Mysql url_rewrite select make on product view page 170+ times per request - fixed integration tests --- .../Backend/Block/Widget/Form/ContainerTest.php | 2 +- .../Magento/Framework/Lock/Backend/Cache.php | 15 ++++++++++++--- .../Framework/View/Element/AbstractBlock.php | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Form/ContainerTest.php b/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Form/ContainerTest.php index e55bb026af6f..c11204bcdd35 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Form/ContainerTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Form/ContainerTest.php @@ -30,7 +30,7 @@ public function testGetFormHtml() $expectedHtml = '<b>html</b>'; $this->assertNotEquals($expectedHtml, $block->getFormHtml()); $form->setText($expectedHtml); - $this->assertEquals('', $block->getFormHtml()); + $this->assertEquals($expectedHtml, $block->getFormHtml()); } public function testPseudoConstruct() diff --git a/lib/internal/Magento/Framework/Lock/Backend/Cache.php b/lib/internal/Magento/Framework/Lock/Backend/Cache.php index 7cfa7274e4e3..256ad2fdbd3e 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Cache.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Cache.php @@ -37,7 +37,7 @@ public function __construct(FrontendInterface $cache) */ public function lock(string $name, int $timeout = -1): bool { - return $this->cache->save('1', self::LOCK_PREFIX . $name, [], $timeout); + return $this->cache->save('1', $this->getIdentifier($name), [], $timeout); } /** @@ -45,7 +45,7 @@ public function lock(string $name, int $timeout = -1): bool */ public function unlock(string $name): bool { - return $this->cache->remove(self::LOCK_PREFIX . $name); + return $this->cache->remove($this->getIdentifier($name)); } /** @@ -53,6 +53,15 @@ public function unlock(string $name): bool */ public function isLocked(string $name): bool { - return (bool)$this->cache->test(self::LOCK_PREFIX . $name); + return (bool)$this->cache->test($this->getIdentifier($name)); + } + + /** + * @param string $name + * @return string + */ + private function getIdentifier(string $name): string + { + return self::LOCK_PREFIX . $name; } } diff --git a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php index 00edfc628d93..187bb0ea1446 100644 --- a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php +++ b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php @@ -686,7 +686,7 @@ public function toHtml() ]); $html = $transportObject->getHtml(); - return (string)$html; + return $html; } /** From ac8d63dce52af0e445578fe3011292c54ade9d0b Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Mon, 18 Mar 2019 16:02:19 -0500 Subject: [PATCH 202/265] MC-13613: Product mass update --- .../Plugin/MassUpdateProductAttribute.php | 39 ++++++------------- .../Magento/CatalogInventory/composer.json | 1 - .../Api/Data/PoisonPillInterface.php | 8 ---- .../Magento/MessageQueue/Model/PoisonPill.php | 8 ---- 4 files changed, 12 insertions(+), 44 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php b/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php index f41fc00b55ac..95fe5ae24d23 100644 --- a/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php +++ b/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php @@ -40,19 +40,9 @@ class MassUpdateProductAttribute private $stockConfiguration; /** - * @var \Magento\Framework\App\RequestInterface + * @var \Magento\Catalog\Helper\Product\Edit\Action\Attribute */ - private $request; - - /** - * @var \Magento\Backend\Model\Session - */ - private $session; - - /** - * @var \Magento\Store\Model\StoreManagerInterface - */ - private $storeManager; + private $attributeHelper; /** * @var \Magento\Backend\Model\View\Result\Redirect @@ -69,9 +59,7 @@ class MassUpdateProductAttribute * @param \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry * @param \Magento\CatalogInventory\Api\StockItemRepositoryInterface $stockItemRepository * @param \Magento\CatalogInventory\Api\StockConfigurationInterface $stockConfiguration - * @param \Magento\Framework\App\RequestInterface $request - * @param \Magento\Backend\Model\Session $session - * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeHelper * @param \Magento\Backend\Model\View\Result\RedirectFactory $redirectFactory * @param \Magento\Framework\Message\ManagerInterface $messageManager * @SuppressWarnings(PHPMD.ExcessiveParameterList) @@ -82,9 +70,7 @@ public function __construct( \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry, \Magento\CatalogInventory\Api\StockItemRepositoryInterface $stockItemRepository, \Magento\CatalogInventory\Api\StockConfigurationInterface $stockConfiguration, - \Magento\Framework\App\RequestInterface $request, - \Magento\Backend\Model\Session $session, - \Magento\Store\Model\StoreManagerInterface $storeManager, + \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeHelper, \Magento\Backend\Model\View\Result\RedirectFactory $redirectFactory, \Magento\Framework\Message\ManagerInterface $messageManager ) { @@ -93,9 +79,7 @@ public function __construct( $this->stockRegistry = $stockRegistry; $this->stockItemRepository = $stockItemRepository; $this->stockConfiguration = $stockConfiguration; - $this->request = $request; - $this->session = $session; - $this->storeManager = $storeManager; + $this->attributeHelper = $attributeHelper; $this->redirectFactory = $redirectFactory; $this->messageManager = $messageManager; } @@ -107,18 +91,19 @@ public function __construct( * @param callable $proceed * * @return \Magento\Framework\Controller\ResultInterface - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function aroundExecute(Save $subject, callable $proceed) { try { - $inventoryData = $this->request->getParam('inventory', []); - $storeId = $this->request->getParam('store', \Magento\Store\Model\Store::DEFAULT_STORE_ID); - $websiteId = $this->storeManager->getStore($storeId)->getWebsiteId(); - $productIds = $this->session->getData('product_ids'); + /** @var \Magento\Framework\App\RequestInterface $request */ + $request = $subject->getRequest(); + $inventoryData = $request->getParam('inventory', []); $inventoryData = $this->addConfigSettings($inventoryData); + $storeId = $this->attributeHelper->getSelectedStoreId(); + $websiteId = $this->attributeHelper->getStoreWebsiteId($storeId); + $productIds = $this->attributeHelper->getProductIds(); + if (!empty($inventoryData)) { $this->updateInventoryInProducts($productIds, $websiteId, $inventoryData); } diff --git a/app/code/Magento/CatalogInventory/composer.json b/app/code/Magento/CatalogInventory/composer.json index f18a5ff1d875..eb6239ea87ef 100644 --- a/app/code/Magento/CatalogInventory/composer.json +++ b/app/code/Magento/CatalogInventory/composer.json @@ -7,7 +7,6 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", - "magento/module-backend": "*", "magento/module-catalog": "*", "magento/module-search": "*", "magento/module-config": "*", diff --git a/app/code/Magento/MessageQueue/Api/Data/PoisonPillInterface.php b/app/code/Magento/MessageQueue/Api/Data/PoisonPillInterface.php index b48d6d585492..37031b4fba4b 100644 --- a/app/code/Magento/MessageQueue/Api/Data/PoisonPillInterface.php +++ b/app/code/Magento/MessageQueue/Api/Data/PoisonPillInterface.php @@ -20,12 +20,4 @@ interface PoisonPillInterface * @return integer */ public function getVersion(): ?int; - - /** - * Set version of poison pill. - * - * @param int $version - * @return void - */ - public function setVersion(int $version); } diff --git a/app/code/Magento/MessageQueue/Model/PoisonPill.php b/app/code/Magento/MessageQueue/Model/PoisonPill.php index ac70034a85da..ac53a14f19b0 100644 --- a/app/code/Magento/MessageQueue/Model/PoisonPill.php +++ b/app/code/Magento/MessageQueue/Model/PoisonPill.php @@ -21,12 +21,4 @@ public function getVersion(): ?int { return $this->_getData('version'); } - - /** - * @inheritdoc - */ - public function setVersion(int $version) - { - $this->setData('version', $version); - } } From 9d65684659fce2eb58ead2ec8adc67896fb3e36e Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <korablov@adobe.com> Date: Mon, 18 Mar 2019 16:04:00 -0500 Subject: [PATCH 203/265] MAGETWO-98151: Add support ZooKeeper locks --- .../Framework/Lock/Backend/Zookeeper.php | 25 +++++++++++-------- .../Lock/Test/Unit/Backend/ZookeeperTest.php | 12 ++++++++- .../Setup/Model/ConfigOptionsList/Lock.php | 2 +- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php index d491678d4d7f..3b69c8734d9a 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php @@ -84,12 +84,18 @@ class Zookeeper implements LockManagerInterface */ public function __construct(string $host, string $path = self::DEFAULT_PATH) { - if (empty($path)) { + if (!$path) { throw new RuntimeException( new Phrase('The path needs to be a non-empty string.') ); } + if (!$host) { + throw new RuntimeException( + new Phrase('The host needs to be a non-empty string.') + ); + } + $this->host = $host; $this->path = preg_replace('#\/*$#', '', $path) ?: '/'; } @@ -221,18 +227,18 @@ private function checkAndCreateParentNode(string $path): bool */ private function getIndex(string $key) { - if (!preg_match("/[0-9]+$/", $key, $matches)) + if (!preg_match('/' . $this->lockName . '([0-9]+)$/', $key, $matches)) return null; - return intval($matches[0]); + return intval($matches[1]); } /** * Checks if there is any sequence node under parent of $fullKey. * At first checks that the $fullKey node is present, if not - returns false. * - * If $indexKey is non-null and there is a smaller index that $indexKey then returns true, - * if all the nodes are larger than $indexKey then returns false. + * If $indexKey is non-null and there is a smaller index than $indexKey then returns true, + * otherwise returns false. * * @param string $fullKey The full path without any sequence info * @param int|null $indexKey The index to compare @@ -249,12 +255,11 @@ private function isAnyLock(string $fullKey, int $indexKey = null): bool $children = $this->getProvider()->getChildren($parent); - foreach ($children as $childKey) { - - if (is_null($indexKey)) { - return true; - } + if (is_null($indexKey) && !empty($children)) { + return true; + } + foreach ($children as $childKey) { $childIndex = $this->getIndex($childKey); if (is_null($childIndex)) { diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php index 7c450a09df32..62521b9de308 100644 --- a/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php @@ -43,11 +43,21 @@ protected function setUp() * @expectedExceptionMessage The path needs to be a non-empty string. * @return void */ - public function testConstructionWithException() + public function testConstructionWithPathException() { $this->zookeeperProvider = new ZookeeperProvider($this->host, ''); } + /** + * @expectedException \Magento\Framework\Exception\RuntimeException + * @expectedExceptionMessage The host needs to be a non-empty string. + * @return void + */ + public function testConstructionWithHostException() + { + $this->zookeeperProvider = new ZookeeperProvider('', $this->path); + } + /** * @return void */ diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php index ae236c1b5d74..37b5cc38dcbd 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php @@ -241,7 +241,7 @@ private function validateZookeeperConfig(array $options, DeploymentConfig $deplo */ private function getLockProvider(array $options, DeploymentConfig $deploymentConfig): string { - if (empty($options[self::INPUT_KEY_LOCK_PROVIDER])) { + if (!isset($options[self::INPUT_KEY_LOCK_PROVIDER])) { return (string) $deploymentConfig->get( self::CONFIG_PATH_LOCK_PROVIDER, $this->getDefaultValue(self::INPUT_KEY_LOCK_PROVIDER) From aba95a875f8400cb8e71b62edf22e4d1ec50a742 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Mon, 18 Mar 2019 16:19:09 -0500 Subject: [PATCH 204/265] Update test to correspond Magento standards --- ...ertMiniShoppingCartSubTotalActionGroup.xml | 25 +++++++++++++++++ ...oppingCartCheckSummaryTotalActionGroup.xml | 21 -------------- ...eProductQtyMiniShoppingCartActionGroup.xml | 28 +++++++++++++++++++ .../Checkout/Test/Mftf/Data/QuoteData.xml | 1 + .../Section/StorefrontMiniCartSection.xml | 2 ++ ...eProductFromMiniShoppingCartEntityTest.xml | 27 ++++++------------ 6 files changed, 64 insertions(+), 40 deletions(-) create mode 100644 app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertMiniShoppingCartSubTotalActionGroup.xml delete mode 100644 app/code/Magento/Checkout/Test/Mftf/ActionGroup/ShoppingCartCheckSummaryTotalActionGroup.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontUpdateProductQtyMiniShoppingCartActionGroup.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertMiniShoppingCartSubTotalActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertMiniShoppingCartSubTotalActionGroup.xml new file mode 100644 index 000000000000..8c5c6f41fffa --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertMiniShoppingCartSubTotalActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertMiniShoppingCartSubTotalActionGroup"> + <arguments> + <argument name="dataQuote" type="entity" /> + </arguments> + <waitForPageLoad stepKey="waitForPageLoad" time="120"/> + <grabTextFrom selector="{{StorefrontMinicartSection.miniCartSubtotalField}}" stepKey="grabMiniCartTotal" /> + <assertContains stepKey="assertMiniCartTotal"> + <actualResult type="variable">$grabMiniCartTotal</actualResult> + <expectedResult type="string">{{dataQuote.subtotal}}</expectedResult> + </assertContains> + <assertContains stepKey="assertMiniCartCurrency"> + <actualResult type="variable">$grabMiniCartTotal</actualResult> + <expectedResult type="string">{{dataQuote.currency}}</expectedResult> + </assertContains> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/ShoppingCartCheckSummaryTotalActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/ShoppingCartCheckSummaryTotalActionGroup.xml deleted file mode 100644 index 70b4e95124da..000000000000 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/ShoppingCartCheckSummaryTotalActionGroup.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="ShoppingCartCheckSummaryTotalActionGroup"> - <arguments> - <argument name="dataQuote" type="entity"/> - </arguments> - <waitForPageLoad stepKey="waitForPageLoad" time="120"/> - <grabTextFrom selector="{{CheckoutCartSummarySection.total}}" stepKey="grabCartTotal" /> - <assertContains stepKey="assertCartTotal"> - <actualResult type="variable">$grabCartTotal</actualResult> - <expectedResult type="string">{{dataQuote.total}}</expectedResult> - </assertContains> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontUpdateProductQtyMiniShoppingCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontUpdateProductQtyMiniShoppingCartActionGroup.xml new file mode 100644 index 000000000000..ee8b761a452d --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontUpdateProductQtyMiniShoppingCartActionGroup.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontUpdateProductQtyMiniShoppingCartActionGroup"> + <arguments> + <argument name="product" type="entity" /> + <argument name="quote" type="entity" /> + </arguments> + + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="goToMiniShoppingCart"/> + + <!-- Clearing QTY field --> + <doubleClick selector="{{StorefrontMinicartSection.itemQuantityBySku(product.sku)}}" stepKey="doubleClickOnQtyInput" /> + <pressKey selector="{{StorefrontMinicartSection.itemQuantityBySku(product.sku)}}" parameterArray="[\WebDriverKeys::DELETE]" stepKey="clearQty"/> + <!-- Clearing QTY field --> + + <fillField selector="{{StorefrontMinicartSection.itemQuantityBySku(product.sku)}}" userInput="{{quote.qty}}" stepKey="changeQty"/> + <click selector="{{StorefrontMinicartSection.itemQuantityUpdateBySku(product.sku)}}" stepKey="clickUpdateButton"/> + <waitForPageLoad stepKey="waitForProductQtyUpdate" /> + + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/Data/QuoteData.xml b/app/code/Magento/Checkout/Test/Mftf/Data/QuoteData.xml index be6e1af67fc6..a14ed147aae2 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Data/QuoteData.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Data/QuoteData.xml @@ -22,5 +22,6 @@ <data key="shipping">10.00</data> <data key="total">1,130.00</data> <data key="shippingMethod">Flat Rate - Fixed</data> + <data key="currency">$</data> </entity> </entities> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml index bdb02835c627..38c88bf4f80b 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml @@ -25,6 +25,8 @@ <element name="deleteMiniCartItem" type="button" selector=".action.delete" timeout="30"/> <element name="deleteMiniCartItemByName" type="button" selector="//ol[@id='mini-cart']//div[contains(., '{{var}}')]//a[contains(@class, 'delete')]" parameterized="true"/> <element name="miniCartSubtotalField" type="text" selector=".block-minicart .amount span.price"/> + <element name="itemQuantityBySku" type="input" selector="#minicart-content-wrapper input[data-cart-item-id='{{productSku}}']" parameterized="true"/> + <element name="itemQuantityUpdateBySku" type="button" selector="//div[@id='minicart-content-wrapper']//input[@data-cart-item-id='{{productSku}}']/../button[contains(@class, 'update-cart-item')]" parameterized="true"/> <element name="itemQuantity" type="input" selector="//a[text()='{{productName}}']/../..//input[contains(@class,'cart-item-qty')]" parameterized="true"/> <element name="itemQuantityUpdate" type="button" selector="//a[text()='{{productName}}']/../..//span[text()='Update']" parameterized="true"/> <element name="itemDiscount" type="text" selector="//tr[@class='totals']//td[@class='amount']/span"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/UpdateProductFromMiniShoppingCartEntityTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/UpdateProductFromMiniShoppingCartEntityTest.xml index 451bce9e49a8..7318f865a0dc 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/UpdateProductFromMiniShoppingCartEntityTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/UpdateProductFromMiniShoppingCartEntityTest.xml @@ -14,7 +14,7 @@ <title value="Check updating product from mini shopping cart"/> <description value="Update Product Qty on Mini Shopping Cart"/> <severity value="MAJOR"/> - <testCaseId value="MAGETWO-29812"/> + <testCaseId value="MC-15068"/> <group value="shoppingCart"/> <group value="mtf_migrated"/> </annotations> @@ -23,12 +23,9 @@ <!--Create product according to dataset.--> <createData entity="simpleProductWithoutCategory" stepKey="createProduct"/> - <!-- Go to frontend --> - <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="navigateToProductPage"/> - <!--Add product to cart--> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> - <argument name="productName" value="$$createProduct.name$$"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="addToCartFromStorefrontProductPage"> + <argument name="product" value="$$createProduct$$"/> </actionGroup> </before> @@ -37,21 +34,13 @@ <deleteData createDataKey="createProduct" stepKey="deleteProduct" /> </after> - <!-- Click on mini shopping cart icon --> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="goToMiniShoppingCart"/> - - <!-- Click Edit --> - <click selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="editMiniCartItem"/> - - <!-- Fill data from dataset --> - <clearField selector="{{CheckoutCartProductSection.ProductQuantityByName($$createProduct.name$$)}}" stepKey="deleteFiled"/> - <fillField selector="{{CheckoutCartProductSection.ProductQuantityByName($$createProduct.name$$)}}" userInput="{{simpleOrderQty2.qty}}" stepKey="changeQty"/> - - <!-- Click Update --> - <click selector="{{CheckoutCartProductSection.updateShoppingCartButton}}" stepKey="updateQty"/> + <actionGroup ref="StorefrontUpdateProductQtyMiniShoppingCartActionGroup" stepKey="updateProductQty"> + <argument name="product" value="$$createProduct$$" /> + <argument name="quote" value="simpleOrderQty2" /> + </actionGroup> <!-- Perform all assertions --> - <actionGroup ref="ShoppingCartCheckSummaryTotalActionGroup" stepKey="checkSummary"> + <actionGroup ref="AssertMiniShoppingCartSubTotalActionGroup" stepKey="checkSummary"> <argument name="dataQuote" value="simpleOrderQty2"/> </actionGroup> </test> From 1b9a1b46c587166624fe2ec28d7a2dc337799cf4 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Mon, 18 Mar 2019 16:26:01 -0500 Subject: [PATCH 205/265] Add test case ids to community contributed tests --- .../Mftf/Test/AdminCreateOrderStatusDuplicatingCodeTest.xml | 3 ++- .../Mftf/Test/AdminCreateOrderStatusDuplicatingLabelTest.xml | 3 ++- .../Sales/Test/Mftf/Test/AdminCreateOrderStatusTest.xml | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingCodeTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingCodeTest.xml index aea1094b4d62..40a731410a89 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingCodeTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingCodeTest.xml @@ -13,9 +13,10 @@ <stories value="Create order status"/> <title value="Create order status with duplicating code"/> <description value="Receive error when creating order status with the code which is already exist"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-15432" /> <group value="sales"/> <group value="mtf_migrated"/> - <severity value="AVERAGE"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingLabelTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingLabelTest.xml index 95582e107da1..d1381bbb1efb 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingLabelTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusDuplicatingLabelTest.xml @@ -13,9 +13,10 @@ <stories value="Create order status"/> <title value="Create order status with duplicating label"/> <description value="Create an order status and get success message"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-15433" /> <group value="sales"/> <group value="mtf_migrated"/> - <severity value="AVERAGE"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusTest.xml index 6fe25160655f..c2daaac84dd4 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderStatusTest.xml @@ -13,9 +13,10 @@ <stories value="Create custom order status"/> <title value="Create custom order status"/> <description value="Tests opening admin order status page, create a new order status with success message"/> + <testCaseId value="MC-15431" /> + <severity value="AVERAGE"/> <group value="sales"/> <group value="mtf_migrated"/> - <severity value="AVERAGE"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> From 7dfdd6857d1041008a08822c8528f0b32968a221 Mon Sep 17 00:00:00 2001 From: Vitaliy Honcharenko <vgoncharenko@magento.com> Date: Mon, 18 Mar 2019 16:47:34 -0500 Subject: [PATCH 206/265] MC-6273: Mysql url_rewrite select make on product view page 170+ times per request - fixed static tests --- app/code/Magento/Config/App/Config/Type/System.php | 1 + .../Framework/View/Test/Unit/Element/Html/LinkTest.php | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Config/App/Config/Type/System.php b/app/code/Magento/Config/App/Config/Type/System.php index 8d197bc6ab04..c913716fedee 100644 --- a/app/code/Magento/Config/App/Config/Type/System.php +++ b/app/code/Magento/Config/App/Config/Type/System.php @@ -27,6 +27,7 @@ * @api * @since 100.1.2 * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.UnusedPrivateMethod) */ class System implements ConfigTypeInterface { diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/LinkTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/LinkTest.php index 96161e12e977..4c76087bfea1 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/LinkTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/LinkTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Framework\View\Test\Unit\Element\Html; class LinkTest extends \PHPUnit\Framework\TestCase @@ -108,7 +109,8 @@ public function testGetLinkAttributes() } $this->assertEquals( - 'href="http://site.com/link.html" shape="shape" tabindex="tabindex" onfocus="onfocus" onblur="onblur" id="id"', + 'href="http://site.com/link.html" shape="shape" tabindex="tabindex"' + . ' onfocus="onfocus" onblur="onblur" id="id"', $linkWithoutAttributes->getLinkAttributes() ); } From 5aef2fbe5e78efc8390dbceb4da894f49ac72b81 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Mon, 18 Mar 2019 21:03:52 -0500 Subject: [PATCH 207/265] Fix unstable selector --- .../Test/Mftf/Section/AdminCreateNewProductAttributeSection.xml | 2 +- .../Catalog/Test/Mftf/Section/StorefrontMessagesSection.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateNewProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateNewProductAttributeSection.xml index e218f5ae74fc..2de7bf19fd37 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateNewProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateNewProductAttributeSection.xml @@ -15,7 +15,7 @@ <element name="addValue" type="button" selector="//button[contains(@data-action,'add_new_row')]" timeout="30"/> <element name="defaultStoreView" type="input" selector="//input[contains(@name,'option[value][option_{{row}}][1]')]" parameterized="true"/> <element name="adminOption" type="input" selector="//input[contains(@name,'option[value][option_{{row}}][0]')]" parameterized="true"/> - <element name="defaultRadioButton" type="radio" selector="//tr[{{row}}]//input[contains(@name,'default[]')]/..//label" parameterized="true"/> + <element name="defaultRadioButton" type="radio" selector="//tr[{{row}}]//input[contains(@name,'default[]')]" parameterized="true"/> <element name="isRequired" type="checkbox" selector="//input[contains(@name,'is_required')]/..//label"/> <element name="advancedAttributeProperties" type="text" selector="//div[contains(@data-index,'advanced_fieldset')]"/> <element name="attributeCode" type="input" selector="//*[@class='admin__fieldset-wrapper-content admin__collapsible-content _show']//input[@name='attribute_code']"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontMessagesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontMessagesSection.xml index 4447b27360a8..c58479a7b73e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontMessagesSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontMessagesSection.xml @@ -11,6 +11,6 @@ <section name="StorefrontMessagesSection"> <element name="success" type="text" selector="div.message-success.success.message"/> <element name="error" type="text" selector="div.message-error.error.message"/> - <element name="noticeMessage" type="text" selector="//div[@class='message notice']/div"/> + <element name="noticeMessage" type="text" selector="div.message.notice div"/> </section> </sections> From b030da0d92eeb455b03059807b66a9de29d2a1bf Mon Sep 17 00:00:00 2001 From: RomanKis <roman.kis.y@gmail.com> Date: Tue, 19 Mar 2019 09:48:02 +0200 Subject: [PATCH 208/265] #21734 Error in JS validation rule --- app/code/Magento/Catalog/etc/adminhtml/system.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/etc/adminhtml/system.xml b/app/code/Magento/Catalog/etc/adminhtml/system.xml index 7a05601fcd66..1d563244f143 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/system.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/system.xml @@ -59,7 +59,7 @@ <field id="grid_per_page_values" translate="label comment" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Products per Page on Grid Allowed Values</label> <comment>Comma-separated.</comment> - <validate>validate-per-page-value-list</validate> + <validate>validate-per-page-value-list required-entry</validate> </field> <field id="grid_per_page" translate="label comment" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Products per Page on Grid Default Value</label> @@ -69,7 +69,7 @@ <field id="list_per_page_values" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Products per Page on List Allowed Values</label> <comment>Comma-separated.</comment> - <validate>validate-per-page-value-list</validate> + <validate>validate-per-page-value-list required-entry</validate> </field> <field id="list_per_page" translate="label comment" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Products per Page on List Default Value</label> From 9dc5f15c0aa82dd8329005972cf30886e44e8849 Mon Sep 17 00:00:00 2001 From: Michalk39 <michalk9339@gmail.com> Date: Tue, 19 Mar 2019 09:36:03 +0100 Subject: [PATCH 209/265] corrections after review --- .../Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml | 6 ++++++ .../Test/Mftf/Section/AdminCustomerGroupMainSection.xml | 2 +- .../VerifyDisabledCustomerGroupFieldTest.xml | 7 +++---- .../Test/TestCase/VerifyDisabledCustomerGroupFieldTest.xml | 1 + 4 files changed, 11 insertions(+), 5 deletions(-) rename app/code/Magento/Customer/Test/Mftf/{Section => Test}/VerifyDisabledCustomerGroupFieldTest.xml (74%) diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml index 6b4f3fc9d6b6..6d3da9715b90 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml @@ -8,6 +8,12 @@ <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="NotLoggedInCustomerGroup" type="customerGroup"> + <data key="id">0</data> + <data key="code">NOT LOGGED IN</data> + <data key="tax_class_id">3</data> + <data key="tax_class_name">Retail Customer</data> + </entity> <entity name="GeneralCustomerGroup" type="customerGroup"> <data key="code">General</data> <data key="tax_class_id">3</data> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGroupMainSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGroupMainSection.xml index af6ff35988e0..182adeade69f 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGroupMainSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGroupMainSection.xml @@ -15,6 +15,6 @@ <element name="selectFirstRow" type="button" selector="//button[@class='action-select']"/> <element name="deleteBtn" type="button" selector="//*[text()='Delete']"/> <element name="clearAllBtn" type="button" selector="//button[text()='Clear all']"/> - <element name="selectIdZeroRow" type="button" selector="tr.data-row:nth-child(1) td.data-grid-actions-cell:nth-child(4) > a.action-menu-item" /> + <element name="editButtonByCustomerGroupCode" type="button" selector="//tr[.//td[count(//th[./*[.='Group']]/preceding-sibling::th) + 1][./*[.='{{code}}']]]//a[contains(@href, '/edit/')]" parametrized="true" /> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/VerifyDisabledCustomerGroupFieldTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/VerifyDisabledCustomerGroupFieldTest.xml similarity index 74% rename from app/code/Magento/Customer/Test/Mftf/Section/VerifyDisabledCustomerGroupFieldTest.xml rename to app/code/Magento/Customer/Test/Mftf/Test/VerifyDisabledCustomerGroupFieldTest.xml index 36a760d90e12..bb17b1b3269b 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/VerifyDisabledCustomerGroupFieldTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/VerifyDisabledCustomerGroupFieldTest.xml @@ -29,11 +29,10 @@ <waitForPageLoad stepKey="waitForCustomerGroupsPageLoad" /> <!-- 3. Select system Customer Group specified in data set from grid --> - <click selector="{{AdminCustomerGroupMainSection.selectIdZeroRow}}" stepKey="clickOnEditCustomerGroup" /> + <click selector="{{AdminCustomerGroupMainSection.editButtonByCustomerGroupCode(NotLoggedInCustomerGroup.code)}}" stepKey="clickOnEditCustomerGroup" /> <!-- 4. Perform all assertions --> - <seeInField selector="#customer_group_code" userInput="NOT LOGGED IN" stepKey="seeNotLoggedInTextInGroupName" /> - <assertElementContainsAttribute selector="#customer_group_code" attribute="disabled" expectedValue="true" stepKey="checkIfGroupNameIsDisabled" /> - + <seeInField selector="{{AdminNewCustomerGroupSection.groupName}}" userInput="NOT LOGGED IN" stepKey="seeNotLoggedInTextInGroupName" /> + <assertElementContainsAttribute selector="{{AdminNewCustomerGroupSection.groupName}}" attribute="disabled" expectedValue="true" stepKey="checkIfGroupNameIsDisabled" /> </test> </tests> \ No newline at end of file diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/VerifyDisabledCustomerGroupFieldTest.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/VerifyDisabledCustomerGroupFieldTest.xml index 70a912a3b5ff..e88e5161e474 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/VerifyDisabledCustomerGroupFieldTest.xml +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/VerifyDisabledCustomerGroupFieldTest.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Customer\Test\TestCase\VerifyDisabledCustomerGroupFieldTest" summary="Check that field is disabled in system Customer Group" ticketId="MAGETWO-52481"> <variation name="VerifyDisabledCustomerGroupField1" summary="Checks that customer_group_code field is disabled in NOT LOGGED IN Customer Group"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="customerGroup/dataset" xsi:type="string">NOT_LOGGED_IN</data> <data name="disabledFields" xsi:type="array"> <item name="0" xsi:type="string">customer_group_code</item> From 3d049d6b3d8af849caba0ce9a0b64daa18433a6e Mon Sep 17 00:00:00 2001 From: Leandry <leandry@atwix.com> Date: Tue, 19 Mar 2019 10:37:55 +0200 Subject: [PATCH 210/265] Add anotation and change additional caches selector parametrized --- .../Test/Mftf/Section/AdminCacheManagementSection.xml | 2 +- .../Mftf/Test/FlushStaticFilesCacheButtonVisibilityTest.xml | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/PageCache/Test/Mftf/Section/AdminCacheManagementSection.xml b/app/code/Magento/PageCache/Test/Mftf/Section/AdminCacheManagementSection.xml index 12d35df42bcc..ee0c32633569 100644 --- a/app/code/Magento/PageCache/Test/Mftf/Section/AdminCacheManagementSection.xml +++ b/app/code/Magento/PageCache/Test/Mftf/Section/AdminCacheManagementSection.xml @@ -30,6 +30,6 @@ <element name="pageCacheCheckbox" type="checkbox" selector="input[value='full_page']"/> <element name="webServicesConfigCheckbox" type="checkbox" selector="input[value='config_webservice']"/> <element name="translationsCheckbox" type="checkbox" selector="input[value='translate']"/> - <element name="FlushStaticFilesCache" type="button" selector="//*[@id='container']//button[contains(., 'Flush Static Files Cache')]"/> + <element name="additionalCacheButton" type="button" selector="//*[@id='container']//button[contains(., '{{cacheType}}')]" parameterized="true" /> </section> </sections> diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/FlushStaticFilesCacheButtonVisibilityTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/FlushStaticFilesCacheButtonVisibilityTest.xml index 6cd280bb7add..4a74a62c947f 100644 --- a/app/code/Magento/PageCache/Test/Mftf/Test/FlushStaticFilesCacheButtonVisibilityTest.xml +++ b/app/code/Magento/PageCache/Test/Mftf/Test/FlushStaticFilesCacheButtonVisibilityTest.xml @@ -15,6 +15,8 @@ <description value="Flush Static Files Cache button visibility"/> <severity value="MAJOR"/> <stories value="Check flush static files cache button"/> + <group value="production_mode_only"/> + <group value="pagecache"/> <group value="mtf_migrated"/> </annotations> <before> @@ -27,6 +29,6 @@ <waitForPageLoad stepKey="waitForPageCacheManagementLoad"/> <!-- Check 'Flush Static Files Cache' not visible in production mode. --> - <dontSee selector="{{AdminCacheManagementSection.FlushStaticFilesCache}}" stepKey="seeFlushStaticFilesButton" /> + <dontSee selector="{{AdminCacheManagementSection.additionalCacheButton('Flush Static Files Cache')}}" stepKey="dontSeeFlushStaticFilesButton" /> </test> </tests> From 3808c33ae9ad49ef022de97cd436589174651dd0 Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Tue, 19 Mar 2019 07:55:13 -0500 Subject: [PATCH 211/265] MC-13613: Product mass update --- .../Adminhtml/Product/Action/Attribute/Save.php | 14 +++++++++++--- .../Catalog/Model/Attribute/Backend/Consumer.php | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php index 63182dd5624e..342bbc388f87 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php @@ -42,6 +42,11 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribut */ private $userContext; + /** + * @var int + */ + private $bulkSize; + /** * @param Action\Context $context * @param \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeHelper @@ -50,6 +55,7 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribut * @param \Magento\Framework\DataObject\IdentityGeneratorInterface $identityService * @param \Magento\Framework\Serialize\SerializerInterface $serializer * @param \Magento\Authorization\Model\UserContextInterface $userContext + * @param int $bulkSize */ public function __construct( Action\Context $context, @@ -58,7 +64,8 @@ public function __construct( \Magento\AsynchronousOperations\Api\Data\OperationInterfaceFactory $operartionFactory, \Magento\Framework\DataObject\IdentityGeneratorInterface $identityService, \Magento\Framework\Serialize\SerializerInterface $serializer, - \Magento\Authorization\Model\UserContextInterface $userContext + \Magento\Authorization\Model\UserContextInterface $userContext, + int $bulkSize = 100 ) { parent::__construct($context, $attributeHelper); $this->bulkManagement = $bulkManagement; @@ -66,6 +73,7 @@ public function __construct( $this->identityService = $identityService; $this->serializer = $serializer; $this->userContext = $userContext; + $this->bulkSize = $bulkSize; } /** @@ -171,9 +179,9 @@ private function publish( $websiteId, $productIds ):void { - $productIdsChunks = array_chunk($productIds, 100); + $productIdsChunks = array_chunk($productIds, $this->bulkSize); $bulkUuid = $this->identityService->generateId(); - $bulkDescription = __('Update attributes to ' . count($productIds) . ' selected products'); + $bulkDescription = __('Update attributes for ' . count($productIds) . ' selected products'); $operations = []; foreach ($productIdsChunks as $productIdsChunk) { if ($websiteRemoveData || $websiteAddData) { diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php b/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php index becd6c160155..dc24a3090481 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/Consumer.php @@ -110,7 +110,7 @@ public function process(\Magento\AsynchronousOperations\Api\Data\OperationInterf ) { $status = OperationInterface::STATUS_TYPE_RETRIABLY_FAILED; $errorCode = $e->getCode(); - $message = __($e->getMessage()); + $message = $e->getMessage(); } else { $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; $errorCode = $e->getCode(); From 2a6cddd8f5dc0cf7ff9f768e7a1c3704f4a1fcfb Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Tue, 19 Mar 2019 08:19:12 -0500 Subject: [PATCH 212/265] MC-13613: Product mass update --- .../Api/Data/PoisonPillInterface.php | 23 ------------------ .../Api/PoisonPillCompareInterface.php | 6 ++--- .../Api/PoisonPillReadInterface.php | 8 +++---- .../MessageQueue/Model/CallbackInvoker.php | 9 ++++--- .../Magento/MessageQueue/Model/PoisonPill.php | 24 ------------------- .../MessageQueue/Model/PoisonPillCompare.php | 5 ++-- .../Model/ResourceModel/PoisonPill.php | 18 +++----------- app/code/Magento/MessageQueue/etc/di.xml | 1 - 8 files changed, 14 insertions(+), 80 deletions(-) delete mode 100644 app/code/Magento/MessageQueue/Api/Data/PoisonPillInterface.php delete mode 100644 app/code/Magento/MessageQueue/Model/PoisonPill.php diff --git a/app/code/Magento/MessageQueue/Api/Data/PoisonPillInterface.php b/app/code/Magento/MessageQueue/Api/Data/PoisonPillInterface.php deleted file mode 100644 index 37031b4fba4b..000000000000 --- a/app/code/Magento/MessageQueue/Api/Data/PoisonPillInterface.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\MessageQueue\Api\Data; - -/** - * PoisonPill data interface. - * - * @api - */ -interface PoisonPillInterface -{ - /** - * Returns version of poison pill. - * - * @return integer - */ - public function getVersion(): ?int; -} diff --git a/app/code/Magento/MessageQueue/Api/PoisonPillCompareInterface.php b/app/code/Magento/MessageQueue/Api/PoisonPillCompareInterface.php index e2df3d69ca0a..3d5b89557559 100644 --- a/app/code/Magento/MessageQueue/Api/PoisonPillCompareInterface.php +++ b/app/code/Magento/MessageQueue/Api/PoisonPillCompareInterface.php @@ -7,8 +7,6 @@ namespace Magento\MessageQueue\Api; -use Magento\MessageQueue\Api\Data\PoisonPillInterface; - /** * Interface describes how to describes how to compare poison pill with latest in DB. * @@ -19,8 +17,8 @@ interface PoisonPillCompareInterface /** * Check if version of current poison pill is latest. * - * @param PoisonPillInterface $poisonPill + * @param int $poisonPillVersion * @return bool */ - public function isLatest(PoisonPillInterface $poisonPill): bool; + public function isLatestVersion(int $poisonPillVersion): bool; } diff --git a/app/code/Magento/MessageQueue/Api/PoisonPillReadInterface.php b/app/code/Magento/MessageQueue/Api/PoisonPillReadInterface.php index 325cfd597956..db97990ebbad 100644 --- a/app/code/Magento/MessageQueue/Api/PoisonPillReadInterface.php +++ b/app/code/Magento/MessageQueue/Api/PoisonPillReadInterface.php @@ -7,8 +7,6 @@ namespace Magento\MessageQueue\Api; -use Magento\MessageQueue\Api\Data\PoisonPillInterface; - /** * Describes how to get latest version of poison pill. * @@ -17,9 +15,9 @@ interface PoisonPillReadInterface { /** - * Returns latest poison pill. + * Returns get latest version of poison pill. * - * @return PoisonPillInterface + * @return int */ - public function getLatest(): PoisonPillInterface; + public function getLatestVersion(): int; } diff --git a/app/code/Magento/MessageQueue/Model/CallbackInvoker.php b/app/code/Magento/MessageQueue/Model/CallbackInvoker.php index 1234228dad72..f37f2157c357 100644 --- a/app/code/Magento/MessageQueue/Model/CallbackInvoker.php +++ b/app/code/Magento/MessageQueue/Model/CallbackInvoker.php @@ -9,7 +9,6 @@ use Magento\Framework\MessageQueue\CallbackInvokerInterface; use Magento\Framework\MessageQueue\QueueInterface; -use Magento\MessageQueue\Api\Data\PoisonPillInterface; use Magento\MessageQueue\Api\PoisonPillCompareInterface; use Magento\MessageQueue\Api\PoisonPillReadInterface; @@ -24,9 +23,9 @@ class CallbackInvoker implements CallbackInvokerInterface private $poisonPillRead; /** - * @var PoisonPillInterface $poisonPill + * @var int $poisonPillVersion */ - private $poisonPill; + private $poisonPillVersion; /** * @var PoisonPillCompareInterface @@ -51,12 +50,12 @@ public function __construct( */ public function invoke(QueueInterface $queue, $maxNumberOfMessages, $callback) { - $this->poisonPill = $this->poisonPillRead->getLatest(); + $this->poisonPillVersion = $this->poisonPillRead->getLatestVersion(); for ($i = $maxNumberOfMessages; $i > 0; $i--) { do { $message = $queue->dequeue(); } while ($message === null && (sleep(1) === 0)); - if (false === $this->poisonPillCompare->isLatest($this->poisonPill)) { + if (false === $this->poisonPillCompare->isLatestVersion($this->poisonPillVersion)) { $queue->reject($message); exit(0); } diff --git a/app/code/Magento/MessageQueue/Model/PoisonPill.php b/app/code/Magento/MessageQueue/Model/PoisonPill.php deleted file mode 100644 index ac53a14f19b0..000000000000 --- a/app/code/Magento/MessageQueue/Model/PoisonPill.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\MessageQueue\Model; - -use Magento\MessageQueue\Api\Data\PoisonPillInterface; - -/** - * PoisonPill data class - */ -class PoisonPill extends \Magento\Framework\Model\AbstractModel implements PoisonPillInterface -{ - /** - * @inheritdoc - */ - public function getVersion(): ?int - { - return $this->_getData('version'); - } -} diff --git a/app/code/Magento/MessageQueue/Model/PoisonPillCompare.php b/app/code/Magento/MessageQueue/Model/PoisonPillCompare.php index 615552628755..a8e40ea49500 100644 --- a/app/code/Magento/MessageQueue/Model/PoisonPillCompare.php +++ b/app/code/Magento/MessageQueue/Model/PoisonPillCompare.php @@ -7,7 +7,6 @@ namespace Magento\MessageQueue\Model; -use Magento\MessageQueue\Api\Data\PoisonPillInterface; use Magento\MessageQueue\Api\PoisonPillCompareInterface; use Magento\MessageQueue\Api\PoisonPillReadInterface; @@ -34,8 +33,8 @@ public function __construct( /** * @inheritdoc */ - public function isLatest(PoisonPillInterface $poisonPill): bool + public function isLatestVersion(int $poisonPillVersion): bool { - return $poisonPill->getVersion() === $this->poisonPillRead->getLatest()->getVersion(); + return $poisonPillVersion === $this->poisonPillRead->getLatestVersion(); } } diff --git a/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php b/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php index ee3d09ec3eae..283fff8ace7c 100644 --- a/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php +++ b/app/code/Magento/MessageQueue/Model/ResourceModel/PoisonPill.php @@ -7,8 +7,6 @@ namespace Magento\MessageQueue\Model\ResourceModel; -use Magento\MessageQueue\Api\Data\PoisonPillInterface; -use Magento\MessageQueue\Api\Data\PoisonPillInterfaceFactory; use Magento\MessageQueue\Api\PoisonPillReadInterface; use Magento\MessageQueue\Api\PoisonPillPutInterface; use Magento\Framework\Model\ResourceModel\Db\Context; @@ -24,24 +22,16 @@ class PoisonPill extends AbstractDb implements PoisonPillPutInterface, PoisonPil */ const QUEUE_POISON_PILL_TABLE = 'queue_poison_pill'; - /** - * @var PoisonPillInterfaceFactory - */ - private $poisonPillFactory; - /** * PoisonPill constructor. * * @param Context $context - * @param PoisonPillInterfaceFactory $poisonPillFactory * @param string|null $connectionName */ public function __construct( Context $context, - PoisonPillInterfaceFactory $poisonPillFactory, string $connectionName = null ) { - $this->poisonPillFactory = $poisonPillFactory; parent::__construct($context, $connectionName); } @@ -67,7 +57,7 @@ public function put(): int /** * @inheritdoc */ - public function getLatest() : PoisonPillInterface + public function getLatestVersion() : int { $select = $this->getConnection()->select()->from( $this->getTable(self::QUEUE_POISON_PILL_TABLE), @@ -78,10 +68,8 @@ public function getLatest() : PoisonPillInterface 1 ); - $version = $this->getConnection()->fetchOne($select); - - $poisonPill = $this->poisonPillFactory->create(['data' => ['version' => (int) $version]]); + $version = (int)$this->getConnection()->fetchOne($select); - return $poisonPill; + return $version; } } diff --git a/app/code/Magento/MessageQueue/etc/di.xml b/app/code/Magento/MessageQueue/etc/di.xml index 3b2cb8718e04..22cfea976a72 100644 --- a/app/code/Magento/MessageQueue/etc/di.xml +++ b/app/code/Magento/MessageQueue/etc/di.xml @@ -13,7 +13,6 @@ <preference for="Magento\Framework\MessageQueue\EnvelopeInterface" type="Magento\Framework\MessageQueue\Envelope"/> <preference for="Magento\Framework\MessageQueue\ConsumerInterface" type="Magento\Framework\MessageQueue\Consumer"/> <preference for="Magento\Framework\MessageQueue\MergedMessageInterface" type="Magento\Framework\MessageQueue\MergedMessage"/> - <preference for="Magento\MessageQueue\Api\Data\PoisonPillInterface" type="Magento\MessageQueue\Model\PoisonPill"/> <preference for="Magento\MessageQueue\Api\PoisonPillCompareInterface" type="Magento\MessageQueue\Model\PoisonPillCompare"/> <preference for="Magento\MessageQueue\Api\PoisonPillPutInterface" type="Magento\MessageQueue\Model\ResourceModel\PoisonPill"/> <preference for="Magento\MessageQueue\Api\PoisonPillReadInterface" type="Magento\MessageQueue\Model\ResourceModel\PoisonPill"/> From e09429f7cca09d91245c0999cb043e7845d4fa74 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Tue, 19 Mar 2019 15:31:16 +0200 Subject: [PATCH 213/265] Fix static tests. --- .../Sales/Model/Order/Address/Validator.php | 14 ++++++++++++-- .../Magento/Framework/Locale/Format.php | 6 +++++- .../Magento/Framework/Locale/Resolver.php | 17 ++++++++++------- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Address/Validator.php b/app/code/Magento/Sales/Model/Order/Address/Validator.php index 1b54dd2c127d..5d3186781e7d 100644 --- a/app/code/Magento/Sales/Model/Order/Address/Validator.php +++ b/app/code/Magento/Sales/Model/Order/Address/Validator.php @@ -49,8 +49,8 @@ class Validator /** * @param DirectoryHelper $directoryHelper - * @param CountryFactory $countryFactory - * @param EavConfig $eavConfig + * @param CountryFactory $countryFactory + * @param EavConfig $eavConfig */ public function __construct( DirectoryHelper $directoryHelper, @@ -64,6 +64,7 @@ public function __construct( } /** + * Validate address. * * @param \Magento\Sales\Model\Order\Address $address * @return array @@ -196,7 +197,10 @@ protected function isStateRequired($countryId) } /** + * Check whether telephone is required for address. + * * @return bool + * @throws \Magento\Framework\Exception\LocalizedException */ protected function isTelephoneRequired() { @@ -204,7 +208,10 @@ protected function isTelephoneRequired() } /** + * Check whether company is required for address. + * * @return bool + * @throws \Magento\Framework\Exception\LocalizedException */ protected function isCompanyRequired() { @@ -212,7 +219,10 @@ protected function isCompanyRequired() } /** + * Check whether telephone is required for address. + * * @return bool + * @throws \Magento\Framework\Exception\LocalizedException */ protected function isFaxRequired() { diff --git a/lib/internal/Magento/Framework/Locale/Format.php b/lib/internal/Magento/Framework/Locale/Format.php index 0cdd80208fbc..adcffe01b910 100644 --- a/lib/internal/Magento/Framework/Locale/Format.php +++ b/lib/internal/Magento/Framework/Locale/Format.php @@ -5,6 +5,9 @@ */ namespace Magento\Framework\Locale; +/** + * Price locale format. + */ class Format implements \Magento\Framework\Locale\FormatInterface { /** @@ -38,7 +41,8 @@ public function __construct( } /** - * Returns the first found number from a string + * Returns the first found number from a string. + * * Parsing depends on given locale (grouping and decimal) * * Examples for input: diff --git a/lib/internal/Magento/Framework/Locale/Resolver.php b/lib/internal/Magento/Framework/Locale/Resolver.php index 83637dac9c47..d058bfd41ab1 100644 --- a/lib/internal/Magento/Framework/Locale/Resolver.php +++ b/lib/internal/Magento/Framework/Locale/Resolver.php @@ -9,6 +9,9 @@ use Magento\Framework\App\DeploymentConfig; use Magento\Framework\App\ObjectManager; +/** + * Manages locale config information. + */ class Resolver implements ResolverInterface { /** @@ -81,7 +84,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function getDefaultLocalePath() { @@ -89,7 +92,7 @@ public function getDefaultLocalePath() } /** - * {@inheritdoc} + * @inheritdoc */ public function setDefaultLocale($locale) { @@ -98,7 +101,7 @@ public function setDefaultLocale($locale) } /** - * {@inheritdoc} + * @inheritdoc */ public function getDefaultLocale() { @@ -116,7 +119,7 @@ public function getDefaultLocale() } /** - * {@inheritdoc} + * @inheritdoc */ public function setLocale($locale = null) { @@ -129,7 +132,7 @@ public function setLocale($locale = null) } /** - * {@inheritdoc} + * @inheritdoc */ public function getLocale() { @@ -140,7 +143,7 @@ public function getLocale() } /** - * {@inheritdoc} + * @inheritdoc */ public function emulate($scopeId) { @@ -160,7 +163,7 @@ public function emulate($scopeId) } /** - * {@inheritdoc} + * @inheritdoc */ public function revert() { From 166c0d426c08cc4f341adbfe1605581a7cb313dd Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Tue, 19 Mar 2019 09:04:25 -0500 Subject: [PATCH 214/265] MC-13613: Product mass update --- .../Plugin/MassUpdateProductAttribute.php | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php b/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php index 95fe5ae24d23..334d2b22edbf 100644 --- a/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php +++ b/app/code/Magento/CatalogInventory/Plugin/MassUpdateProductAttribute.php @@ -44,10 +44,6 @@ class MassUpdateProductAttribute */ private $attributeHelper; - /** - * @var \Magento\Backend\Model\View\Result\Redirect - */ - private $redirectFactory; /** * @var \Magento\Framework\Message\ManagerInterface */ @@ -60,7 +56,6 @@ class MassUpdateProductAttribute * @param \Magento\CatalogInventory\Api\StockItemRepositoryInterface $stockItemRepository * @param \Magento\CatalogInventory\Api\StockConfigurationInterface $stockConfiguration * @param \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeHelper - * @param \Magento\Backend\Model\View\Result\RedirectFactory $redirectFactory * @param \Magento\Framework\Message\ManagerInterface $messageManager * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -71,7 +66,6 @@ public function __construct( \Magento\CatalogInventory\Api\StockItemRepositoryInterface $stockItemRepository, \Magento\CatalogInventory\Api\StockConfigurationInterface $stockConfiguration, \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeHelper, - \Magento\Backend\Model\View\Result\RedirectFactory $redirectFactory, \Magento\Framework\Message\ManagerInterface $messageManager ) { $this->stockIndexerProcessor = $stockIndexerProcessor; @@ -80,7 +74,6 @@ public function __construct( $this->stockItemRepository = $stockItemRepository; $this->stockConfiguration = $stockConfiguration; $this->attributeHelper = $attributeHelper; - $this->redirectFactory = $redirectFactory; $this->messageManager = $messageManager; } @@ -111,14 +104,14 @@ public function aroundExecute(Save $subject, callable $proceed) return $proceed(); } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->messageManager->addErrorMessage($e->getMessage()); + return $proceed(); } catch (\Exception $e) { $this->messageManager->addExceptionMessage( $e, __('Something went wrong while updating the product(s) attributes.') ); + return $proceed(); } - - return $this->redirectFactory->create()->setPath('catalog/product/', ['_current' => true]); } /** From c8e9365023a3bcdea77b474561a535a49879e90d Mon Sep 17 00:00:00 2001 From: Vitaliy Honcharenko <vgoncharenko@magento.com> Date: Tue, 19 Mar 2019 10:07:50 -0500 Subject: [PATCH 215/265] MC-6273: Mysql url_rewrite select make on product view page 170+ times per request - fixed unit tests --- .../Magento/Framework/Cache/LockGuardedCacheLoader.php | 2 +- lib/internal/Magento/Framework/View/Element/AbstractBlock.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/internal/Magento/Framework/Cache/LockGuardedCacheLoader.php b/lib/internal/Magento/Framework/Cache/LockGuardedCacheLoader.php index 8575f208e6c1..8b500fe7fd3c 100644 --- a/lib/internal/Magento/Framework/Cache/LockGuardedCacheLoader.php +++ b/lib/internal/Magento/Framework/Cache/LockGuardedCacheLoader.php @@ -58,7 +58,7 @@ public function __construct( * @param callable $dataLoader * @param callable $dataCollector * @param callable $dataSaver - * @return array + * @return mixed */ public function lockedLoadData( string $lockName, diff --git a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php index 187bb0ea1446..6c4746d8218e 100644 --- a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php +++ b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php @@ -1084,7 +1084,7 @@ protected function getCacheLifetime() /** * Load block html from cache storage * - * @return string|false + * @return string */ protected function _loadCache() { @@ -1126,7 +1126,7 @@ protected function _loadCache() } }; - return $this->lockQuery->lockedLoadData( + return (string)$this->lockQuery->lockedLoadData( $this->getCacheKey(), $loadAction, $collectAction, From 251ce3cfd20ac01617d64e6754ad0ca17f711c60 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <korablov@adobe.com> Date: Tue, 19 Mar 2019 10:28:34 -0500 Subject: [PATCH 216/265] MAGETWO-98151: Add support ZooKeeper locks --- .../Framework/Lock/Backend/ZookeeperTest.php | 1 - .../Framework/Lock/Backend/Zookeeper.php | 22 +++++++++++-------- lib/internal/Magento/Framework/Lock/Proxy.php | 9 +++++--- .../Setup/Model/ConfigOptionsList/Lock.php | 7 +++--- .../Unit/Model/ConfigOptionsList/LockTest.php | 19 +++++++++++----- 5 files changed, 36 insertions(+), 22 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php index ae218561377f..8d0caad5d55e 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php @@ -88,4 +88,3 @@ public function testUnlockWithoutExistingLock() $this->assertFalse($this->model->unlock($name)); } } - diff --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php index 3b69c8734d9a..1e7ca069df79 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php @@ -101,7 +101,8 @@ public function __construct(string $host, string $path = self::DEFAULT_PATH) } /** - * {@inheritdoc} + * @inheritdoc + * * You can see the lock algorithm by the link * @link https://zookeeper.apache.org/doc/r3.1.2/recipes.html#sc_recipes_Locks * @@ -124,7 +125,7 @@ public function lock(string $name, int $timeout = -1): bool throw new RuntimeException(new Phrase('Failed creating lock %1', [$lockPath])); } - while($this->isAnyLock($lockKey, $this->getIndex($lockKey))) { + while ($this->isAnyLock($lockKey, $this->getIndex($lockKey))) { if (!$skipDeadline && $deadline <= microtime(true)) { $this->getProvider()->delete($lockKey); return false; @@ -139,7 +140,8 @@ public function lock(string $name, int $timeout = -1): bool } /** - * {@inheritdoc} + * @inheritdoc + * * @throws RuntimeException */ public function unlock(string $name): bool @@ -152,7 +154,8 @@ public function unlock(string $name): bool } /** - * {@inheritdoc} + * @inheritdoc + * * @throws RuntimeException */ public function isLocked(string $name): bool @@ -184,7 +187,7 @@ private function getProvider(): \Zookeeper } $deadline = microtime(true) + $this->connectionTimeout; - while($this->zookeeper->getState() != \Zookeeper::CONNECTED_STATE) { + while ($this->zookeeper->getState() != \Zookeeper::CONNECTED_STATE) { if ($deadline <= microtime(true)) { throw new RuntimeException(new Phrase('Zookeeper connection timed out!')); } @@ -227,16 +230,17 @@ private function checkAndCreateParentNode(string $path): bool */ private function getIndex(string $key) { - if (!preg_match('/' . $this->lockName . '([0-9]+)$/', $key, $matches)) + if (!preg_match('/' . $this->lockName . '([0-9]+)$/', $key, $matches)) { return null; + } return intval($matches[1]); } /** * Checks if there is any sequence node under parent of $fullKey. - * At first checks that the $fullKey node is present, if not - returns false. * + * At first checks that the $fullKey node is present, if not - returns false. * If $indexKey is non-null and there is a smaller index than $indexKey then returns true, * otherwise returns false. * @@ -255,14 +259,14 @@ private function isAnyLock(string $fullKey, int $indexKey = null): bool $children = $this->getProvider()->getChildren($parent); - if (is_null($indexKey) && !empty($children)) { + if (null === $indexKey && !empty($children)) { return true; } foreach ($children as $childKey) { $childIndex = $this->getIndex($childKey); - if (is_null($childIndex)) { + if (null === $childIndex) { continue; } diff --git a/lib/internal/Magento/Framework/Lock/Proxy.php b/lib/internal/Magento/Framework/Lock/Proxy.php index b5f8eee0f2c4..2718bf6cb345 100644 --- a/lib/internal/Magento/Framework/Lock/Proxy.php +++ b/lib/internal/Magento/Framework/Lock/Proxy.php @@ -37,7 +37,8 @@ public function __construct(LockBackendFactory $factory) } /** - * {@inheritdoc} + * @inheritdoc + * * @throws RuntimeException */ public function isLocked(string $name): bool @@ -46,7 +47,8 @@ public function isLocked(string $name): bool } /** - * {@inheritdoc} + * @inheritdoc + * * @throws RuntimeException */ public function lock(string $name, int $timeout = -1): bool @@ -55,7 +57,8 @@ public function lock(string $name, int $timeout = -1): bool } /** - * {@inheritdoc} + * @inheritdoc + * * @throws RuntimeException */ public function unlock(string $name): bool diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php index 37b5cc38dcbd..799409d1560a 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php @@ -212,9 +212,9 @@ private function validateZookeeperConfig(array $options, DeploymentConfig $deplo $host = $options[self::INPUT_KEY_LOCK_ZOOKEEPER_HOST] ?? $deploymentConfig->get( - self::CONFIG_PATH_LOCK_ZOOKEEPER_HOST, - $this->getDefaultValue(self::INPUT_KEY_LOCK_ZOOKEEPER_HOST) - ); + self::CONFIG_PATH_LOCK_ZOOKEEPER_HOST, + $this->getDefaultValue(self::INPUT_KEY_LOCK_ZOOKEEPER_HOST) + ); $path = $options[self::INPUT_KEY_LOCK_ZOOKEEPER_PATH] ?? $deploymentConfig->get( self::CONFIG_PATH_LOCK_ZOOKEEPER_PATH, @@ -251,7 +251,6 @@ private function getLockProvider(array $options, DeploymentConfig $deploymentCon return (string) $options[self::INPUT_KEY_LOCK_PROVIDER]; } - /** * Sets default configuration for locks * diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php index f7ca6e0a09b5..5a150ad3dcd8 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php @@ -163,7 +163,10 @@ public function testValidate(array $options, array $expectedResult) $this->deploymentConfigMock->expects($this->any()) ->method('get') ->willReturnArgument(1); - $this->assertSame($expectedResult, $this->lockConfigOptionsList->validate($options, $this->deploymentConfigMock)); + $this->assertSame( + $expectedResult, + $this->lockConfigOptionsList->validate($options, $this->deploymentConfigMock) + ); } /** @@ -186,10 +189,16 @@ public function validateDataProvider(): array LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_HOST => '', LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_PATH => '', ], - 'expectedResult' => [ - 'Zookeeper path needs to be a non-empty string.', - 'Zookeeper host is should be set.', - ], + 'expectedResult' => extension_loaded('zookeeper') + ? [ + 'Zookeeper path needs to be a non-empty string.', + 'Zookeeper host is should be set.', + ] + : [ + 'php extension Zookeeper is not installed.', + 'Zookeeper path needs to be a non-empty string.', + 'Zookeeper host is should be set.', + ], ], ]; } From 7590f5463e2c9d1554ccaa600bac66ce08b8aaa8 Mon Sep 17 00:00:00 2001 From: Vladimir Fishchenko <hws47a@gmail.com> Date: Tue, 19 Mar 2019 15:57:28 +0000 Subject: [PATCH 217/265] magento/magento-functional-tests-migration#417: Convert CreateCustomOrderStatusEntityTest to MFTF - removed unnecessary default values --- .../AdminOrderStatusFormFillAndSaveActionGroup.xml | 4 ++-- .../ActionGroup/AssertOrderStatusExistsInGridActionGroup.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderStatusFormFillAndSaveActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderStatusFormFillAndSaveActionGroup.xml index cb27439a9d88..810857714542 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderStatusFormFillAndSaveActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderStatusFormFillAndSaveActionGroup.xml @@ -11,8 +11,8 @@ <!-- Fill Order status form and click save --> <actionGroup name="AdminOrderStatusFormFillAndSave"> <arguments> - <argument name="status" type="string" defaultValue=""/> - <argument name="label" type="string" defaultValue=""/> + <argument name="status" type="string" /> + <argument name="label" type="string" /> </arguments> <fillField stepKey="fillStatusCode" selector="{{AdminOrderStatusFormSection.statusCodeField}}" userInput="{{status}}"/> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderStatusExistsInGridActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderStatusExistsInGridActionGroup.xml index bbb6aa71b893..5f69f5298768 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderStatusExistsInGridActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderStatusExistsInGridActionGroup.xml @@ -11,8 +11,8 @@ <!-- Search order status grid for item with a specific code and validate data --> <actionGroup name="AssertOrderStatusExistsInGrid"> <arguments> - <argument name="status" type="string" defaultValue=""/> - <argument name="label" type="string" defaultValue=""/> + <argument name="status" type="string" /> + <argument name="label" type="string" /> </arguments> <click selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="clickClearFilters"/> From 28d82a2146d1b22a2abd6cbdb883ad12ce583086 Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Tue, 19 Mar 2019 14:00:18 -0500 Subject: [PATCH 218/265] MAGETWO-98800: TunnelAction fails when Google Chart API is not available --- .../Magento/Backend/Controller/Adminhtml/DashboardTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php index 07af21505f18..89f1e5e5d53d 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php @@ -21,6 +21,8 @@ public function testAjaxBlockAction() public function testTunnelAction() { + $this->markTestSkipped('MAGETWO-98800: TunnelAction fails when Google Chart API is not available'); + $testUrl = \Magento\Backend\Block\Dashboard\Graph::API_URL . '?cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World'; $handle = curl_init(); curl_setopt($handle, CURLOPT_URL, $testUrl); From ef8c8df0e000f73c3a2b6ec09685d3098c750e78 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Tue, 19 Mar 2019 13:18:00 -0500 Subject: [PATCH 219/265] Update test case ids and add minor fixes --- .../Test/Mftf/Section/AdminCustomerGroupMainSection.xml | 2 +- .../Test/Mftf/Test/VerifyDisabledCustomerGroupFieldTest.xml | 6 +++--- .../Mftf/Test/FlushStaticFilesCacheButtonVisibilityTest.xml | 3 ++- .../TestCase/FlushStaticFilesCacheButtonVisibilityTest.xml | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGroupMainSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGroupMainSection.xml index 182adeade69f..4cb7f5e3f628 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGroupMainSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGroupMainSection.xml @@ -15,6 +15,6 @@ <element name="selectFirstRow" type="button" selector="//button[@class='action-select']"/> <element name="deleteBtn" type="button" selector="//*[text()='Delete']"/> <element name="clearAllBtn" type="button" selector="//button[text()='Clear all']"/> - <element name="editButtonByCustomerGroupCode" type="button" selector="//tr[.//td[count(//th[./*[.='Group']]/preceding-sibling::th) + 1][./*[.='{{code}}']]]//a[contains(@href, '/edit/')]" parametrized="true" /> + <element name="editButtonByCustomerGroupCode" type="button" selector="//tr[.//td[count(//th[./*[.='Group']]/preceding-sibling::th) + 1][./*[.='{{code}}']]]//a[contains(@href, '/edit/')]" parameterized="true" /> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/VerifyDisabledCustomerGroupFieldTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/VerifyDisabledCustomerGroupFieldTest.xml index bb17b1b3269b..648c30b1ca0b 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/VerifyDisabledCustomerGroupFieldTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/VerifyDisabledCustomerGroupFieldTest.xml @@ -13,7 +13,7 @@ <stories value="Check that field is disabled in system Customer Group"/> <title value="Check that field is disabled in system Customer Group"/> <description value="Checks that customer_group_code field is disabled in NOT LOGGED IN Customer Group"/> - <testCaseId value="MAGETWO-52481"/> + <testCaseId value="MC-14206"/> <severity value="CRITICAL"/> <group value="customers"/> <group value="mtf_migrated"/> @@ -32,7 +32,7 @@ <click selector="{{AdminCustomerGroupMainSection.editButtonByCustomerGroupCode(NotLoggedInCustomerGroup.code)}}" stepKey="clickOnEditCustomerGroup" /> <!-- 4. Perform all assertions --> - <seeInField selector="{{AdminNewCustomerGroupSection.groupName}}" userInput="NOT LOGGED IN" stepKey="seeNotLoggedInTextInGroupName" /> + <seeInField selector="{{AdminNewCustomerGroupSection.groupName}}" userInput="{{NotLoggedInCustomerGroup.code}}" stepKey="seeNotLoggedInTextInGroupName" /> <assertElementContainsAttribute selector="{{AdminNewCustomerGroupSection.groupName}}" attribute="disabled" expectedValue="true" stepKey="checkIfGroupNameIsDisabled" /> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/FlushStaticFilesCacheButtonVisibilityTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/FlushStaticFilesCacheButtonVisibilityTest.xml index 4a74a62c947f..bd6f7ba362bf 100644 --- a/app/code/Magento/PageCache/Test/Mftf/Test/FlushStaticFilesCacheButtonVisibilityTest.xml +++ b/app/code/Magento/PageCache/Test/Mftf/Test/FlushStaticFilesCacheButtonVisibilityTest.xml @@ -11,10 +11,11 @@ <test name="FlushStaticFilesCacheButtonVisibilityTest"> <annotations> <features value="PageCache"/> + <stories value="Page Cache"/> <title value="Check visibility of flush static files cache button"/> <description value="Flush Static Files Cache button visibility"/> <severity value="MAJOR"/> - <stories value="Check flush static files cache button"/> + <testCaseId value="MC-15454"/> <group value="production_mode_only"/> <group value="pagecache"/> <group value="mtf_migrated"/> diff --git a/dev/tests/functional/tests/app/Magento/PageCache/Test/TestCase/FlushStaticFilesCacheButtonVisibilityTest.xml b/dev/tests/functional/tests/app/Magento/PageCache/Test/TestCase/FlushStaticFilesCacheButtonVisibilityTest.xml index cbdce5905719..bc529729f121 100644 --- a/dev/tests/functional/tests/app/Magento/PageCache/Test/TestCase/FlushStaticFilesCacheButtonVisibilityTest.xml +++ b/dev/tests/functional/tests/app/Magento/PageCache/Test/TestCase/FlushStaticFilesCacheButtonVisibilityTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\PageCache\Test\TestCase\FlushStaticFilesCacheButtonVisibilityTest" summary="Flush Static Files Cache button visibility" ticketId="MAGETWO-39934"> <variation name="FlushStaticFilesCacheButtonVisibilityTest"> - <data name="tag" xsi:type="string">severity:S3</data> + <data name="tag" xsi:type="string">severity:S3, mftf_migrated:yes</data> <constraint name="Magento\PageCache\Test\Constraint\AssertFlushStaticFilesCacheButtonVisibility" /> </variation> </testCase> From 6ddeaafb1b1f116cfff2151f6086dab67cc25f0d Mon Sep 17 00:00:00 2001 From: nasanabri <45367318+nasanabri@users.noreply.github.com> Date: Tue, 19 Mar 2019 18:41:44 -0500 Subject: [PATCH 220/265] Fix typo --- lib/internal/Magento/Framework/Oauth/Oauth.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Oauth/Oauth.php b/lib/internal/Magento/Framework/Oauth/Oauth.php index 5e48fb5ed30f..e560fd664493 100644 --- a/lib/internal/Magento/Framework/Oauth/Oauth.php +++ b/lib/internal/Magento/Framework/Oauth/Oauth.php @@ -199,7 +199,7 @@ protected function _validateSignature($params, $consumerSecret, $httpMethod, $re ); if (!Security::compareStrings($calculatedSign, $params['oauth_signature'])) { - throw new Exception(new Phrase('The signatire is invalid. Verify and try again.')); + throw new Exception(new Phrase('The signature is invalid. Verify and try again.')); } } From 940163e8b25414ea34010d131815123a26bbe781 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Tue, 19 Mar 2019 20:09:55 -0500 Subject: [PATCH 221/265] magento-engcom/magento2ce#2688: Skipped unstable test --- .../Magento/Backend/Controller/Adminhtml/DashboardTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php index 07af21505f18..89f1e5e5d53d 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php @@ -21,6 +21,8 @@ public function testAjaxBlockAction() public function testTunnelAction() { + $this->markTestSkipped('MAGETWO-98800: TunnelAction fails when Google Chart API is not available'); + $testUrl = \Magento\Backend\Block\Dashboard\Graph::API_URL . '?cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World'; $handle = curl_init(); curl_setopt($handle, CURLOPT_URL, $testUrl); From f8b9147302c6a5c81ee180f13ebc2bc544c5fea3 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Wed, 20 Mar 2019 10:57:41 +0200 Subject: [PATCH 222/265] Reverting the PR#20861 changes because of violating accessibility standards --- lib/web/css/source/lib/_resets.less | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/web/css/source/lib/_resets.less b/lib/web/css/source/lib/_resets.less index 4499c314ce6c..08d16842b849 100644 --- a/lib/web/css/source/lib/_resets.less +++ b/lib/web/css/source/lib/_resets.less @@ -105,13 +105,6 @@ .lib-css(box-shadow, @focus__box-shadow); } } - - input[type="radio"], - input[type="checkbox"] { - &:focus { - box-shadow: none; - } - } } // From adf2d647b71c869b93f725b4af5f2e390270400c Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Wed, 20 Mar 2019 11:11:01 +0200 Subject: [PATCH 223/265] Static tests fix. --- lib/internal/Magento/Framework/Oauth/Oauth.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/internal/Magento/Framework/Oauth/Oauth.php b/lib/internal/Magento/Framework/Oauth/Oauth.php index e560fd664493..919b0e4c86ba 100644 --- a/lib/internal/Magento/Framework/Oauth/Oauth.php +++ b/lib/internal/Magento/Framework/Oauth/Oauth.php @@ -9,6 +9,9 @@ use Magento\Framework\Encryption\Helper\Security; use Magento\Framework\Phrase; +/** + * Authorization service. + */ class Oauth implements OauthInterface { /** @@ -61,7 +64,7 @@ public static function getSupportedSignatureMethods() } /** - * {@inheritdoc} + * @inheritdoc */ public function getRequestToken($params, $requestUrl, $httpMethod = 'POST') { @@ -74,7 +77,7 @@ public function getRequestToken($params, $requestUrl, $httpMethod = 'POST') } /** - * {@inheritdoc} + * @inheritdoc */ public function getAccessToken($params, $requestUrl, $httpMethod = 'POST') { @@ -102,7 +105,7 @@ public function getAccessToken($params, $requestUrl, $httpMethod = 'POST') } /** - * {@inheritdoc} + * @inheritdoc */ public function validateAccessTokenRequest($params, $requestUrl, $httpMethod = 'POST') { @@ -125,7 +128,7 @@ public function validateAccessTokenRequest($params, $requestUrl, $httpMethod = ' } /** - * {@inheritdoc} + * @inheritdoc */ public function validateAccessToken($accessToken) { @@ -133,7 +136,7 @@ public function validateAccessToken($accessToken) } /** - * {@inheritdoc} + * @inheritdoc */ public function buildAuthorizationHeader( $params, From 933d35d903dfc135d9f753073fd87802f2d57a12 Mon Sep 17 00:00:00 2001 From: RomanKis <roman.kis.y@gmail.com> Date: Wed, 20 Mar 2019 12:15:51 +0200 Subject: [PATCH 224/265] #20825 Missing required argument $productAvailabilityChecks of Magento\Sales\Model\Order\Reorder\OrderedProductAvailabilityChecker. --- .../Magento/ConfigurableProductSales/etc/di.xml | 16 ++++++++++++++++ app/code/Magento/Sales/etc/di.xml | 4 +--- 2 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 app/code/Magento/ConfigurableProductSales/etc/di.xml diff --git a/app/code/Magento/ConfigurableProductSales/etc/di.xml b/app/code/Magento/ConfigurableProductSales/etc/di.xml new file mode 100644 index 000000000000..b53faf74ffa1 --- /dev/null +++ b/app/code/Magento/ConfigurableProductSales/etc/di.xml @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\Sales\Model\Order\Reorder\OrderedProductAvailabilityChecker"> + <arguments> + <argument name="productAvailabilityChecks" xsi:type="array"> + <item name="configurable" xsi:type="object">Magento\ConfigurableProductSales\Model\Order\Reorder\OrderedProductAvailabilityChecker</item> + </argument> + </arguments> + </type> +</config> diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml index 6ed230861e4d..68fcd17122bd 100644 --- a/app/code/Magento/Sales/etc/di.xml +++ b/app/code/Magento/Sales/etc/di.xml @@ -1017,9 +1017,7 @@ type="Magento\Sales\Model\Order\OrderCustomerDelegate" /> <type name="Magento\Sales\Model\Order\Reorder\OrderedProductAvailabilityChecker"> <arguments> - <argument name="productAvailabilityChecks" xsi:type="array"> - <item name="configurable" xsi:type="object">Magento\ConfigurableProductSales\Model\Order\Reorder\OrderedProductAvailabilityChecker</item> - </argument> + <argument name="productAvailabilityChecks" xsi:type="array" /> </arguments> </type> </config> From 52d71a5a39222a00a8f14d7c59f717420dfd8f16 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Wed, 20 Mar 2019 12:09:56 +0100 Subject: [PATCH 225/265] Refactoring --- .../Catalog/Test/Mftf/Data/ProductData.xml | 2 +- .../Section/CheckoutCartProductSection.xml | 3 - ...UpdateShoppingCartSimpleProductQtyTest.xml | 58 ++++++++++++++++++ ...SimpleWithCustomOptionsProductQtyTest.xml} | 61 ++----------------- .../Test/TestCase/UpdateShoppingCartTest.xml | 2 + 5 files changed, 66 insertions(+), 60 deletions(-) create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleProductQtyTest.xml rename app/code/Magento/Checkout/Test/Mftf/Test/{StorefrontUpdateShoppingCartTest.xml => StorefrontUpdateShoppingCartSimpleWithCustomOptionsProductQtyTest.xml} (50%) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 44635e9b93f9..6a712fde2802 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -386,7 +386,7 @@ <var key="sku" entityType="product" entityKey="sku" /> <requiredEntity type="product_option">ProductOptionDropDownWithLongValuesTitle</requiredEntity> </entity> - <entity name="productWithOptions3" type="product"> + <entity name="ProductWithTextFieldAndAreaOptions" type="product"> <var key="sku" entityType="product" entityKey="sku" /> <requiredEntity type="product_option">ProductOptionField</requiredEntity> <requiredEntity type="product_option">ProductOptionArea</requiredEntity> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml index a63dc5be2de3..dcfb12fd4e96 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml @@ -15,9 +15,6 @@ <element name="ProductPriceByName" type="text" selector="//main//table[@id='shopping-cart-table']//tbody//tr[..//strong[contains(@class, 'product-item-name')]//a/text()='{{var1}}'][1]//td[contains(@class, 'price')]//span[@class='price']" parameterized="true"/> - <element name="ProductSubtotalByName" type="text" - selector="//main//table[@id='shopping-cart-table']//tbody//tr[..//strong[contains(@class, 'product-item-name')]//a/text()='{{var1}}'][1]//td[contains(@class, 'subtotal')]//span[@class='price']" - parameterized="true"/> <element name="ProductImageByName" type="text" selector="//main//table[@id='shopping-cart-table']//tbody//tr//img[contains(@class, 'product-image-photo') and @alt='{{var1}}']" parameterized="true"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleProductQtyTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleProductQtyTest.xml new file mode 100644 index 000000000000..36eb10826e63 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleProductQtyTest.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontUpdateShoppingCartSimpleProductQtyTest"> + <annotations> + <features value="Checkout"/> + <title value="Check updating shopping cart while updating items qty"/> + <description value="Check updating shopping cart while updating items qty"/> + <group value="shoppingCart"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Add the newly created product to the shopping cart --> + <actionGroup ref="AddSimpleProductToCart" stepKey="addToCartFromStorefrontProductPage"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + </before> + <after> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + + <!-- Go to the shopping cart --> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnPageShoppingCart"/> + <waitForPageLoad stepKey="waitForCheckoutPageLoad1"/> + + <!-- Change the product QTY --> + <fillField selector="{{CheckoutCartProductSection.ProductQuantityByName($$createProduct.name$$)}}" userInput="3" stepKey="changeCartQty"/> + <click selector="{{CheckoutCartProductSection.updateShoppingCartButton}}" stepKey="openShoppingCart"/> + <waitForPageLoad stepKey="waitForCheckoutPageLoad2"/> + + <!-- The price and QTY values should be updated for the product --> + <grabValueFrom selector="{{CheckoutCartProductSection.ProductQuantityByName($$createProduct.name$$)}}" stepKey="grabProductQtyInCart"/> + <see userInput="$369" selector="{{CheckoutCartProductSection.productSubtotalByName($$createProduct.name$$)}}" stepKey="assertProductPrice"/> + <assertEquals expected="3" actual="$grabProductQtyInCart" stepKey="assertProductQtyInCart"/> + + <!-- Subtotal should be updated --> + <see userInput="$369" selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="assertCartSubtotal"/> + + <!-- Minicart product price and subtotal should be updated --> + <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="openMinicart"/> + <grabValueFrom selector="{{StorefrontMinicartSection.itemQuantity($$createProduct.name$$)}}" stepKey="grabProductQtyInMinicart"/> + <assertEquals expected="3" actual="$grabProductQtyInMinicart" stepKey="assertProductQtyInMinicart"/> + <see userInput="$369" selector="{{StorefrontMinicartSection.subtotal}}" stepKey="assertMinicartSubtotal"/> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleWithCustomOptionsProductQtyTest.xml similarity index 50% rename from app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartTest.xml rename to app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleWithCustomOptionsProductQtyTest.xml index 94abf4b26c34..654a8b231c63 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleWithCustomOptionsProductQtyTest.xml @@ -7,58 +7,8 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="UpdateShoppingCartTestVariation1"> - <annotations> - <features value="Checkout"/> - <title value="Check updating shopping cart while updating items qty"/> - <description value="Check updating shopping cart while updating items qty"/> - <group value="shoppingCart"/> - <group value="mtf_migrated"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="SimpleProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - <field key="price">100</field> - </createData> - - <!-- Add the newly created product to the shopping cart --> - <amOnPage url="$$createProduct.custom_attributes[url_key]$$.html" stepKey="navigateToSimpleProductPage"/> - <waitForPageLoad stepKey="waitForCatalogPageLoad1"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage1"> - <argument name="productName" value="$$createProduct.name$$"/> - </actionGroup> - </before> - <after> - <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - </after> - - <!-- Go to the shopping cart --> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnPageShoppingCart"/> - <waitForPageLoad stepKey="waitForCheckoutPageLoad1"/> - - <!-- Change the product QTY --> - <fillField selector="{{CheckoutCartProductSection.ProductQuantityByName($$createProduct.name$$)}}" userInput="3" stepKey="changeCartQty"/> - <click selector="{{CheckoutCartProductSection.updateShoppingCartButton}}" stepKey="openShoppingCart"/> - <waitForPageLoad stepKey="waitForCheckoutPageLoad2"/> - - <!-- The price and QTY values should be updated for the product --> - <grabValueFrom selector="{{CheckoutCartProductSection.ProductQuantityByName($$createProduct.name$$)}}" stepKey="grabProductQtyInCart"/> - <see userInput="$300" selector="{{CheckoutCartProductSection.ProductSubtotalByName($$createProduct.name$$)}}" stepKey="assertProductPrice"/> - <assertEquals expected="3" actual="$grabProductQtyInCart" stepKey="assertProductQtyInCart"/> - - <!-- Subtotal should be updated --> - <see userInput="$300" selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="assertCartSubtotal"/> - - <!-- Minicart product price and subtotal should be updated --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="openMinicart"/> - <grabValueFrom selector="{{StorefrontMinicartSection.itemQuantity($$createProduct.name$$)}}" stepKey="grabProductQtyInMinicart"/> - <assertEquals expected="3" actual="$grabProductQtyInMinicart" stepKey="assertProductQtyInMinicart"/> - <see userInput="$300" selector="{{StorefrontMinicartSection.subtotal}}" stepKey="assertMinicartSubtotal"/> - </test> - <test name="UpdateShoppingCartTestVariation2"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontUpdateShoppingCartSimpleWithCustomOptionsProductQtyTest"> <annotations> <features value="Checkout"/> <title value="Check updating shopping cart while updating qty of items with custom options"/> @@ -68,13 +18,12 @@ </annotations> <before> <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="SimpleProduct" stepKey="createProduct"> + <createData entity="ApiSimpleProductWithCustomPrice" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> - <field key="price">100</field> </createData> <!-- Add two custom options to the product: field and textarea --> - <updateData createDataKey="createProduct" entity="productWithOptions3" stepKey="updateProductWithOption"/> + <updateData createDataKey="createProduct" entity="ProductWithTextFieldAndAreaOptions" stepKey="updateProductWithOption"/> <!-- Go to the product page, fill the custom options values and add the product to the shopping cart --> <amOnPage url="{{StorefrontHomePage.url}}$createProduct.custom_attributes[url_key]$.html" stepKey="amOnProductPage"/> @@ -101,7 +50,7 @@ <!-- The price and QTY values should be updated for the product --> <grabValueFrom selector="{{CheckoutCartProductSection.ProductQuantityByName($$createProduct.name$$)}}" stepKey="grabProductQtyInCart"/> - <see userInput="$1,320.00" selector="{{CheckoutCartProductSection.ProductSubtotalByName($$createProduct.name$$)}}" stepKey="assertProductPrice"/> + <see userInput="$1,320.00" selector="{{CheckoutCartProductSection.productSubtotalByName($$createProduct.name$$)}}" stepKey="assertProductPrice"/> <assertEquals expected="11" actual="$grabProductQtyInCart" stepKey="assertProductQtyInCart"/> <see userInput="$1,320.00" selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="assertSubtotal"/> diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateShoppingCartTest.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateShoppingCartTest.xml index e0ea721a51f1..43acf9cd740d 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateShoppingCartTest.xml +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateShoppingCartTest.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Checkout\Test\TestCase\UpdateShoppingCartTest" summary="Update Shopping Cart" ticketId="MAGETWO-25081"> <variation name="UpdateShoppingCartTestVariation1"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="tag" xsi:type="string">severity:S0</data> <data name="product/dataset" xsi:type="string">default</data> <data name="product/data/price/value" xsi:type="string">100</data> @@ -20,6 +21,7 @@ <constraint name="Magento\Checkout\Test\Constraint\AssertSubtotalInShoppingCart" /> </variation> <variation name="UpdateShoppingCartTestVariation2"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="tag" xsi:type="string">severity:S0</data> <data name="product/dataset" xsi:type="string">with_two_custom_option</data> <data name="product/data/price/value" xsi:type="string">50</data> From 24f4d9ad2e0448fc532346bdab55ace29692848c Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Wed, 20 Mar 2019 14:11:18 +0200 Subject: [PATCH 226/265] Fix static and unit tests. --- .../Adminhtml/Order/Shipment/AddTrack.php | 14 +- .../Adminhtml/Order/Shipment/AddTrackTest.php | 145 +++++++++++------- 2 files changed, 99 insertions(+), 60 deletions(-) diff --git a/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AddTrack.php b/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AddTrack.php index 8abc74b50a00..4ec7117783aa 100644 --- a/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AddTrack.php +++ b/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AddTrack.php @@ -8,18 +8,20 @@ namespace Magento\Shipping\Controller\Adminhtml\Order\Shipment; use Magento\Backend\App\Action; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\App\ObjectManager; use Magento\Framework\Controller\ResultFactory; use Magento\Framework\Controller\ResultInterface; use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Serialize\SerializerInterface; use Magento\Sales\Api\Data\ShipmentTrackInterfaceFactory; use Magento\Sales\Api\ShipmentRepositoryInterface; -use Magento\Sales\Model\Order\Shipment\TrackFactory; use Magento\Shipping\Controller\Adminhtml\Order\ShipmentLoader; -class AddTrack extends \Magento\Backend\App\Action +/** + * Add new tracking number to shipment controller. + */ +class AddTrack extends Action implements HttpPostActionInterface { /** * Authorization level of a basic admin session @@ -39,7 +41,7 @@ class AddTrack extends \Magento\Backend\App\Action private $shipmentRepository; /** - * @var TrackFactory + * @var ShipmentTrackInterfaceFactory */ private $trackFactory; @@ -52,14 +54,14 @@ class AddTrack extends \Magento\Backend\App\Action * @param Action\Context $context * @param ShipmentLoader $shipmentLoader * @param ShipmentRepositoryInterface|null $shipmentRepository - * @param TrackFactory|null $trackFactory + * @param ShipmentTrackInterfaceFactory|null $trackFactory * @param SerializerInterface|null $serializer */ public function __construct( Action\Context $context, ShipmentLoader $shipmentLoader, ShipmentRepositoryInterface $shipmentRepository = null, - TrackFactory $trackFactory = null, + ShipmentTrackInterfaceFactory $trackFactory = null, SerializerInterface $serializer = null ) { parent::__construct($context); diff --git a/app/code/Magento/Shipping/Test/Unit/Controller/Adminhtml/Order/Shipment/AddTrackTest.php b/app/code/Magento/Shipping/Test/Unit/Controller/Adminhtml/Order/Shipment/AddTrackTest.php index cf3bf68e1041..1c9cdb1fa7d5 100644 --- a/app/code/Magento/Shipping/Test/Unit/Controller/Adminhtml/Order/Shipment/AddTrackTest.php +++ b/app/code/Magento/Shipping/Test/Unit/Controller/Adminhtml/Order/Shipment/AddTrackTest.php @@ -6,127 +6,162 @@ namespace Magento\Shipping\Test\Unit\Controller\Adminhtml\Order\Shipment; -use Magento\Backend\App\Action; +use Magento\Backend\App\Action\Context; +use Magento\Framework\App\Request\Http; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\App\ViewInterface; +use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\Controller\ResultInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\View\Element\BlockInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\Framework\View\Page\Config; +use Magento\Framework\View\Page\Title; +use Magento\Framework\View\Result\Page; +use Magento\Sales\Api\Data\ShipmentTrackInterfaceFactory; +use Magento\Sales\Model\Order\Shipment; +use Magento\Sales\Model\Order\Shipment\Track; +use Magento\Shipping\Controller\Adminhtml\Order\Shipment\AddTrack; +use Magento\Shipping\Controller\Adminhtml\Order\ShipmentLoader; /** - * Class AddTrackTest + * Class AddTrackTest covers AddTrack controller. * - * @package Magento\Shipping\Controller\Adminhtml\Order\Shipment * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class AddTrackTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Shipping\Controller\Adminhtml\Order\ShipmentLoader|\PHPUnit_Framework_MockObject_MockObject + * @var ShipmentLoader|\PHPUnit_Framework_MockObject_MockObject */ - protected $shipmentLoader; + private $shipmentLoader; /** - * @var \Magento\Shipping\Controller\Adminhtml\Order\Shipment\AddTrack + * @var AddTrack */ - protected $controller; + private $controller; /** - * @var Action\Context|\PHPUnit_Framework_MockObject_MockObject + * @var Context|\PHPUnit_Framework_MockObject_MockObject */ - protected $context; + private $context; /** - * @var \Magento\Framework\App\Request\Http|\PHPUnit_Framework_MockObject_MockObject + * @var Http|\PHPUnit_Framework_MockObject_MockObject */ - protected $request; + private $request; /** - * @var \Magento\Framework\App\ResponseInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ResponseInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $response; + private $response; /** - * @var \Magento\Framework\ObjectManager\ObjectManager|\PHPUnit_Framework_MockObject_MockObject + * @var ViewInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $objectManager; + private $view; /** - * @var \Magento\Framework\App\ViewInterface|\PHPUnit_Framework_MockObject_MockObject + * @var Page|\PHPUnit_Framework_MockObject_MockObject */ - protected $view; + private $resultPageMock; /** - * @var \Magento\Framework\View\Result\Page|\PHPUnit_Framework_MockObject_MockObject + * @var Config|\PHPUnit_Framework_MockObject_MockObject */ - protected $resultPageMock; + private $pageConfigMock; /** - * @var \Magento\Framework\View\Page\Config|\PHPUnit_Framework_MockObject_MockObject + * @var Title|\PHPUnit_Framework_MockObject_MockObject */ - protected $pageConfigMock; + private $pageTitleMock; /** - * @var \Magento\Framework\View\Page\Title|\PHPUnit_Framework_MockObject_MockObject + * @var ShipmentTrackInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject */ - protected $pageTitleMock; + private $trackFactory; + /** + * @var ResultInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $rawResult; + + /** + * @inheritdoc + */ protected function setUp() { $objectManagerHelper = new ObjectManagerHelper($this); $this->shipmentLoader = $this->getMockBuilder( - \Magento\Shipping\Controller\Adminhtml\Order\ShipmentLoader::class + ShipmentLoader::class ) ->disableOriginalConstructor() ->setMethods(['setShipmentId', 'setOrderId', 'setShipment', 'setTracking', 'load']) ->getMock(); $this->context = $this->createPartialMock( - \Magento\Backend\App\Action\Context::class, + Context::class, [ 'getRequest', 'getResponse', 'getRedirect', 'getObjectManager', 'getTitle', - 'getView' + 'getView', + 'getResultFactory' ] ); $this->response = $this->createPartialMock( - \Magento\Framework\App\ResponseInterface::class, + ResponseInterface::class, ['setRedirect', 'sendResponse', 'setBody'] ); - $this->request = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) + $this->request = $this->getMockBuilder(Http::class) ->disableOriginalConstructor()->getMock(); - $this->objectManager = $this->createPartialMock( - \Magento\Framework\ObjectManager\ObjectManager::class, - ['create', 'get'] - ); - $this->view = $this->createMock(\Magento\Framework\App\ViewInterface::class); - $this->resultPageMock = $this->getMockBuilder(\Magento\Framework\View\Result\Page::class) + $this->view = $this->createMock(ViewInterface::class); + $this->resultPageMock = $this->getMockBuilder(Page::class) ->disableOriginalConstructor() ->getMock(); - $this->pageConfigMock = $this->getMockBuilder(\Magento\Framework\View\Page\Config::class) + $this->pageConfigMock = $this->getMockBuilder(Config::class) ->disableOriginalConstructor() ->getMock(); - $this->pageTitleMock = $this->getMockBuilder(\Magento\Framework\View\Page\Title::class) + $this->pageTitleMock = $this->getMockBuilder(Title::class) ->disableOriginalConstructor() ->getMock(); + $this->trackFactory = $this->getMockBuilder(ShipmentTrackInterfaceFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMockForAbstractClass(); + $this->rawResult = $this->getMockBuilder(ResultInterface::class) + ->disableOriginalConstructor() + ->setMethods(['setContents']) + ->getMockForAbstractClass(); + $resultFactory = $this->getMockBuilder(ResultFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMockForAbstractClass(); $this->context->expects($this->once()) ->method('getRequest') ->will($this->returnValue($this->request)); $this->context->expects($this->once()) ->method('getResponse') ->will($this->returnValue($this->response)); - $this->context->expects($this->once()) - ->method('getObjectManager') - ->will($this->returnValue($this->objectManager)); $this->context->expects($this->once()) ->method('getView') ->will($this->returnValue($this->view)); + $resultFactory->expects($this->once()) + ->method('create') + ->willReturn($this->rawResult); + $this->context->expects($this->once()) + ->method('getResultFactory') + ->willReturn($resultFactory); $this->controller = $objectManagerHelper->getObject( - \Magento\Shipping\Controller\Adminhtml\Order\Shipment\AddTrack::class, + AddTrack::class, [ 'context' => $this->context, 'shipmentLoader' => $this->shipmentLoader, 'request' => $this->request, 'response' => $this->response, - 'view' => $this->view + 'view' => $this->view, + 'trackFactory' => $this->trackFactory, ] ); } @@ -144,7 +179,7 @@ public function testExecute() $tracking = []; $shipmentData = ['items' => [], 'send_email' => '']; $shipment = $this->createPartialMock( - \Magento\Sales\Model\Order\Shipment::class, + Shipment::class, ['addTrack', '__wakeup', 'save'] ); $this->request->expects($this->any()) @@ -152,8 +187,10 @@ public function testExecute() ->will( $this->returnValueMap( [ - ['order_id', null, $orderId], ['shipment_id', null, $shipmentId], - ['shipment', null, $shipmentData], ['tracking', null, $tracking], + ['order_id', null, $orderId], + ['shipment_id', null, $shipmentId], + ['shipment', null, $shipmentData], + ['tracking', null, $tracking], ] ) ); @@ -183,14 +220,13 @@ public function testExecute() $this->shipmentLoader->expects($this->once()) ->method('load') ->will($this->returnValue($shipment)); - $track = $this->getMockBuilder(\Magento\Sales\Model\Order\Shipment\Track::class) + $track = $this->getMockBuilder(Track::class) ->disableOriginalConstructor() ->setMethods(['__wakeup', 'setNumber', 'setCarrierCode', 'setTitle']) ->getMock(); - $this->objectManager->expects($this->atLeastOnce()) + $this->trackFactory->expects($this->once()) ->method('create') - ->with(\Magento\Sales\Model\Order\Shipment\Track::class) - ->will($this->returnValue($track)); + ->willReturn($track); $track->expects($this->once()) ->method('setNumber') ->with($number) @@ -206,8 +242,8 @@ public function testExecute() $this->view->expects($this->once()) ->method('loadLayout') ->will($this->returnSelf()); - $layout = $this->createMock(\Magento\Framework\View\LayoutInterface::class); - $menuBlock = $this->createPartialMock(\Magento\Framework\View\Element\BlockInterface::class, ['toHtml']); + $layout = $this->createMock(LayoutInterface::class); + $menuBlock = $this->createPartialMock(BlockInterface::class, ['toHtml']); $html = 'html string'; $this->view->expects($this->once()) ->method('getLayout') @@ -235,9 +271,10 @@ public function testExecute() $this->pageConfigMock->expects($this->any()) ->method('getTitle') ->willReturn($this->pageTitleMock); - $this->response->expects($this->once()) - ->method('setBody') - ->with($html); - $this->assertNull($this->controller->execute()); + $this->rawResult->expects($this->once()) + ->method('setContents') + ->with($html) + ->willReturnSelf(); + $this->assertInstanceOf(ResultInterface::class, $this->controller->execute()); } } From a07956c5a2336fb48b86e9585e7e18882691e31c Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Wed, 20 Mar 2019 14:59:54 +0200 Subject: [PATCH 227/265] ENGCOM-4531: Static tests fix. --- .../Magento/AsynchronousOperations/Model/MassSchedule.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php b/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php index 3ab6dc12054b..89d468159c6e 100644 --- a/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php +++ b/app/code/Magento/AsynchronousOperations/Model/MassSchedule.php @@ -143,7 +143,9 @@ public function publishMass($topicName, array $entitiesArray, $groupId = null, $ $operations[] = $operation; $requestItem->setId($key); $requestItem->setStatus(ItemStatusInterface::STATUS_ACCEPTED); - $requestItem->setDataHash($this->encryptor->hash($operation->getSerializedData(), Encryptor::HASH_VERSION_SHA256)); + $requestItem->setDataHash( + $this->encryptor->hash($operation->getSerializedData(), Encryptor::HASH_VERSION_SHA256) + ); $requestItems[] = $requestItem; } catch (\Exception $exception) { $this->logger->error($exception); From 50ce1f5db56976fdd5f2818fdcd5d37c7382f38c Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Wed, 20 Mar 2019 15:51:27 +0200 Subject: [PATCH 228/265] Fix static tests. --- app/code/Magento/Customer/Block/Address/Grid.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Block/Address/Grid.php b/app/code/Magento/Customer/Block/Address/Grid.php index 492343acfe30..963efc648d94 100644 --- a/app/code/Magento/Customer/Block/Address/Grid.php +++ b/app/code/Magento/Customer/Block/Address/Grid.php @@ -1,9 +1,10 @@ <?php -declare(strict_types=1); /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Customer\Block\Address; use Magento\Customer\Model\ResourceModel\Address\CollectionFactory as AddressCollectionFactory; @@ -237,7 +238,10 @@ private function getAddressCollection(): \Magento\Customer\Model\ResourceModel\A /** @var \Magento\Customer\Model\ResourceModel\Address\Collection $collection */ $collection = $this->addressCollectionFactory->create(); $collection->setOrder('entity_id', 'desc'); - $collection->addFieldToFilter('entity_id', array('nin' => array($this->getDefaultBilling(), $this->getDefaultShipping()))); + $collection->addFieldToFilter( + 'entity_id', + ['nin' => [$this->getDefaultBilling(), $this->getDefaultShipping()]] + ); $collection->setCustomerFilter([$this->getCustomer()->getId()]); $this->addressCollection = $collection; } From 37b535229f9d8bc9e09b9909a875131d6642d1b7 Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" <lopukhov@adobe.com> Date: Wed, 20 Mar 2019 09:40:51 -0500 Subject: [PATCH 229/265] MC-13613: Product mass update --- .../Test/Mftf/Test/AdminExportBundleProductTest.xml | 3 +++ .../Test/AdminExportGroupedProductWithSpecialPriceTest.xml | 3 +++ ...ortSimpleAndConfigurableProductsWithCustomOptionsTest.xml | 5 ++++- ...eProductAndConfigurableProductsWithAssignedImagesTest.xml | 5 ++++- ...siteAndConfigurableProductAssignedToCustomWebsiteTest.xml | 3 +++ .../Test/AdminExportSimpleProductWithCustomAttributeTest.xml | 3 +++ 6 files changed, 20 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml index 3a4010f41710..1f5ae6b6905b 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml @@ -115,6 +115,9 @@ <!-- Export created below products --> <actionGroup ref="exportAllProducts" stepKey="exportCreatedProducts"/> + <!-- Run cron --> + <magentoCLI command="cron:run" stepKey="runCron3"/> + <!-- Download product --> <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> <argument name="rowIndex" value="0"/> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml index 09d9469cb093..a587d71ba0e6 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml @@ -80,6 +80,9 @@ <!-- Export created below products --> <actionGroup ref="exportAllProducts" stepKey="exportCreatedProducts"/> + <!-- Run cron --> + <magentoCLI command="cron:run" stepKey="runCron3"/> + <!-- Download product --> <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> <argument name="rowIndex" value="0"/> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml index aaeb0cd38cd9..6f64da469369 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml @@ -107,9 +107,12 @@ <argument name="attributeData" value="$$createConfigProduct.sku$$"/> </actionGroup> + <!-- Run cron --> + <magentoCLI command="cron:run" stepKey="runCron3"/> + <!-- Download product --> <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> <argument name="rowIndex" value="0"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml index 597ee2336b21..993f1c9cd9da 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml @@ -123,9 +123,12 @@ <argument name="attributeData" value="$$createConfigProduct.sku$$"/> </actionGroup> + <!-- Run cron --> + <magentoCLI command="cron:run" stepKey="runCron3"/> + <!-- Download product --> <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> <argument name="rowIndex" value="0"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml index b1e723e5ee1f..491d20604a08 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml @@ -105,6 +105,9 @@ <!-- Export created below products --> <actionGroup ref="exportAllProducts" stepKey="exportCreatedProducts"/> + <!-- Run cron --> + <magentoCLI command="cron:run" stepKey="runCron3"/> + <!-- Download product --> <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> <argument name="rowIndex" value="0"/> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml index e3c5cd78397f..f671b54803e3 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml @@ -57,6 +57,9 @@ <!-- Export created below products --> <actionGroup ref="exportAllProducts" stepKey="exportCreatedProducts"/> + <!-- Run cron --> + <magentoCLI command="cron:run" stepKey="runCron3"/> + <!-- Download product --> <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> <argument name="rowIndex" value="0"/> From 1cdae6055bdb1afdcfa575f426623051b9670bd7 Mon Sep 17 00:00:00 2001 From: Vitaliy Honcharenko <vgoncharenko@magento.com> Date: Wed, 20 Mar 2019 10:05:35 -0500 Subject: [PATCH 230/265] MC-6273: Mysql url_rewrite select make on product view page 170+ times per request - fixes after CR --- .../Magento/Config/App/Config/Type/System.php | 9 +++++++- .../Cache/LockGuardedCacheLoader.php | 21 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Config/App/Config/Type/System.php b/app/code/Magento/Config/App/Config/Type/System.php index c913716fedee..c63ccae87165 100644 --- a/app/code/Magento/Config/App/Config/Type/System.php +++ b/app/code/Magento/Config/App/Config/Type/System.php @@ -389,6 +389,13 @@ private function readData(): array public function clean() { $this->data = []; - $this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [self::CACHE_TAG]); + $cleanAction = function () { + $this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [self::CACHE_TAG]); + }; + + $this->lockQuery->lockedCleanData( + self::$lockName, + $cleanAction + ); } } diff --git a/lib/internal/Magento/Framework/Cache/LockGuardedCacheLoader.php b/lib/internal/Magento/Framework/Cache/LockGuardedCacheLoader.php index 8b500fe7fd3c..216d8e9a0a01 100644 --- a/lib/internal/Magento/Framework/Cache/LockGuardedCacheLoader.php +++ b/lib/internal/Magento/Framework/Cache/LockGuardedCacheLoader.php @@ -92,4 +92,25 @@ public function lockedLoadData( return $cachedData; } + + /** + * Clean data. + * + * @param string $lockName + * @param callable $dataCleaner + * @return void + */ + public function lockedCleanData(string $lockName, callable $dataCleaner) + { + while ($this->locker->isLocked($lockName)) { + usleep($this->delayTimeout * 1000); + } + try { + if ($this->locker->lock($lockName, $this->lockTimeout / 1000)) { + $dataCleaner(); + } + } finally { + $this->locker->unlock($lockName); + } + } } From 6f8d99c750e7aeab547a1d6f34e2d9f7df0be505 Mon Sep 17 00:00:00 2001 From: Alex Taranovsky <firster@atwix.com> Date: Wed, 20 Mar 2019 18:04:53 +0200 Subject: [PATCH 231/265] 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 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Ups; + +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test for setting offline shipping methods on cart + */ +class SetUpsShippingMethodsOnCartTest extends GraphQlAbstract +{ + /** + * Defines carrier code for "UPS" shipping method + */ + const CARRIER_CODE = 'ups'; + + /** + * Defines method code for the "Ground" UPS shipping + */ + const CARRIER_METHOD_CODE_GROUND = 'GND'; + + /** + * @var QuoteFactory + */ + private $quoteFactory; + + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedId; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->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 <<<QUERY +mutation { + setShippingMethodsOnCart(input: { + cart_id: "$maskedQuoteId" + shipping_methods: [ + { + cart_address_id: $shippingAddressId + carrier_code: "$carrierCode" + method_code: "$methodCode" + } + ] + }) { + cart { + shipping_addresses { + selected_shipping_method { + carrier_code + method_code + label + } + } + } + } +} +QUERY; + } + + /** + * Sends a GraphQL request with using a bearer token + * + * @param string $query + * @return array + * @throws \Magento\Framework\Exception\AuthenticationException + */ + private function sendRequestWithToken(string $query): array + { + $customerToken = $this->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 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\App\Config\Storage\Writer; +use Magento\Framework\App\Config\Storage\WriterInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\App\Config\ScopeConfigInterface; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Writer $configWriter */ +$configWriter = $objectManager->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 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\App\Config\Storage\Writer; +use Magento\Framework\App\Config\Storage\WriterInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Writer $configWriter */ +$configWriter = $objectManager->create(WriterInterface::class); + +$configWriter->delete('carriers/ups/active'); From 30a2a80802afe5df8c81e948f2c1db4e2ce969b9 Mon Sep 17 00:00:00 2001 From: Alex Taranovsky <firster@atwix.com> Date: Wed, 20 Mar 2019 18:58:51 +0200 Subject: [PATCH 232/265] 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 989f2373707ed714ad1bb9518f4b6a0471890e38 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Wed, 20 Mar 2019 13:57:09 -0500 Subject: [PATCH 233/265] Update test case ids and add minor fixes --- .../Checkout/Test/Mftf/Data/QuoteData.xml | 13 +++++++++ ...UpdateShoppingCartSimpleProductQtyTest.xml | 19 ++++++++----- ...tSimpleWithCustomOptionsProductQtyTest.xml | 27 ++++++++++++------- .../Test/TestCase/UpdateShoppingCartTest.xml | 6 ++--- 4 files changed, 45 insertions(+), 20 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Data/QuoteData.xml b/app/code/Magento/Checkout/Test/Mftf/Data/QuoteData.xml index a14ed147aae2..e7a5992ad894 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Data/QuoteData.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Data/QuoteData.xml @@ -24,4 +24,17 @@ <data key="shippingMethod">Flat Rate - Fixed</data> <data key="currency">$</data> </entity> + <entity name="quoteQty3Price123" type="Quote"> + <data key="price">123.00</data> + <data key="qty">3</data> + <data key="subtotal">369.00</data> + <data key="currency">$</data> + </entity> + <entity name="quoteQty11Subtotal1320" type="Quote"> + <data key="price">100.00</data> + <data key="customOptionsPrice">20</data> + <data key="qty">11</data> + <data key="subtotal">1,320.00</data> + <data key="currency">$</data> + </entity> </entities> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleProductQtyTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleProductQtyTest.xml index 36eb10826e63..423f4049f672 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleProductQtyTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleProductQtyTest.xml @@ -13,6 +13,7 @@ <features value="Checkout"/> <title value="Check updating shopping cart while updating items qty"/> <description value="Check updating shopping cart while updating items qty"/> + <testCaseId value="MC-14731" /> <group value="shoppingCart"/> <group value="mtf_migrated"/> </annotations> @@ -37,22 +38,28 @@ <waitForPageLoad stepKey="waitForCheckoutPageLoad1"/> <!-- Change the product QTY --> - <fillField selector="{{CheckoutCartProductSection.ProductQuantityByName($$createProduct.name$$)}}" userInput="3" stepKey="changeCartQty"/> + <fillField selector="{{CheckoutCartProductSection.ProductQuantityByName($$createProduct.name$$)}}" userInput="{{quoteQty3Price123.qty}}" stepKey="changeCartQty"/> <click selector="{{CheckoutCartProductSection.updateShoppingCartButton}}" stepKey="openShoppingCart"/> <waitForPageLoad stepKey="waitForCheckoutPageLoad2"/> <!-- The price and QTY values should be updated for the product --> <grabValueFrom selector="{{CheckoutCartProductSection.ProductQuantityByName($$createProduct.name$$)}}" stepKey="grabProductQtyInCart"/> - <see userInput="$369" selector="{{CheckoutCartProductSection.productSubtotalByName($$createProduct.name$$)}}" stepKey="assertProductPrice"/> - <assertEquals expected="3" actual="$grabProductQtyInCart" stepKey="assertProductQtyInCart"/> + <see userInput="{{quoteQty3Price123.currency}}{{quoteQty3Price123.subtotal}}" selector="{{CheckoutCartProductSection.productSubtotalByName($$createProduct.name$$)}}" stepKey="assertProductPrice"/> + <assertEquals stepKey="assertProductQtyInCart"> + <actualResult type="variable">grabProductQtyInCart</actualResult> + <expectedResult type="string">{{quoteQty3Price123.qty}}</expectedResult> + </assertEquals> <!-- Subtotal should be updated --> - <see userInput="$369" selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="assertCartSubtotal"/> + <see userInput="{{quoteQty3Price123.currency}}{{quoteQty3Price123.subtotal}}" selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="assertCartSubtotal"/> <!-- Minicart product price and subtotal should be updated --> <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="openMinicart"/> <grabValueFrom selector="{{StorefrontMinicartSection.itemQuantity($$createProduct.name$$)}}" stepKey="grabProductQtyInMinicart"/> - <assertEquals expected="3" actual="$grabProductQtyInMinicart" stepKey="assertProductQtyInMinicart"/> - <see userInput="$369" selector="{{StorefrontMinicartSection.subtotal}}" stepKey="assertMinicartSubtotal"/> + <assertEquals stepKey="assertProductQtyInMinicart"> + <actualResult type="variable">grabProductQtyInMinicart</actualResult> + <expectedResult type="string">{{quoteQty3Price123.qty}}</expectedResult> + </assertEquals> + <see userInput="{{quoteQty3Price123.currency}}{{quoteQty3Price123.subtotal}}" selector="{{StorefrontMinicartSection.subtotal}}" stepKey="assertMinicartSubtotal"/> </test> </tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleWithCustomOptionsProductQtyTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleWithCustomOptionsProductQtyTest.xml index 654a8b231c63..84080b04c80e 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleWithCustomOptionsProductQtyTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleWithCustomOptionsProductQtyTest.xml @@ -13,6 +13,7 @@ <features value="Checkout"/> <title value="Check updating shopping cart while updating qty of items with custom options"/> <description value="Check updating shopping cart while updating qty of items with custom options"/> + <testCaseId value="MC-14732" /> <group value="shoppingCart"/> <group value="mtf_migrated"/> </annotations> @@ -26,7 +27,7 @@ <updateData createDataKey="createProduct" entity="ProductWithTextFieldAndAreaOptions" stepKey="updateProductWithOption"/> <!-- Go to the product page, fill the custom options values and add the product to the shopping cart --> - <amOnPage url="{{StorefrontHomePage.url}}$createProduct.custom_attributes[url_key]$.html" stepKey="amOnProductPage"/> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="amOnProductPage"/> <waitForPageLoad stepKey="waitForCatalogPageLoad"/> <fillField userInput="OptionField" selector="{{StorefrontProductInfoMainSection.productOptionFieldInput(ProductOptionField.title)}}" stepKey="fillProductOptionInputField"/> <fillField userInput="OptionArea" selector="{{StorefrontProductInfoMainSection.productOptionAreaInput(ProductOptionArea.title)}}" stepKey="fillProductOptionInputArea"/> @@ -41,23 +42,29 @@ <!-- Go to the shopping cart --> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnPageShoppingCart"/> - <waitForPageLoad stepKey="waitForCheckoutPageLoad1"/> + <waitForPageLoad stepKey="waitForCheckoutPageLoad"/> <!-- Change the product QTY --> - <fillField selector="{{CheckoutCartProductSection.ProductQuantityByName($$createProduct.name$$)}}" userInput="11" stepKey="changeCartQty"/> - <click selector="{{CheckoutCartProductSection.updateShoppingCartButton}}" stepKey="openShoppingCart"/> - <waitForPageLoad stepKey="waitForCheckoutPageLoad2"/> + <fillField selector="{{CheckoutCartProductSection.ProductQuantityByName($$createProduct.name$$)}}" userInput="{{quoteQty11Subtotal1320.qty}}" stepKey="changeCartQty"/> + <click selector="{{CheckoutCartProductSection.updateShoppingCartButton}}" stepKey="updateShoppingCart"/> + <waitForPageLoad stepKey="waitShoppingCartUpdated"/> <!-- The price and QTY values should be updated for the product --> <grabValueFrom selector="{{CheckoutCartProductSection.ProductQuantityByName($$createProduct.name$$)}}" stepKey="grabProductQtyInCart"/> - <see userInput="$1,320.00" selector="{{CheckoutCartProductSection.productSubtotalByName($$createProduct.name$$)}}" stepKey="assertProductPrice"/> - <assertEquals expected="11" actual="$grabProductQtyInCart" stepKey="assertProductQtyInCart"/> - <see userInput="$1,320.00" selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="assertSubtotal"/> + <see userInput="{{quoteQty11Subtotal1320.currency}}{{quoteQty11Subtotal1320.subtotal}}" selector="{{CheckoutCartProductSection.productSubtotalByName($$createProduct.name$$)}}" stepKey="assertProductPrice"/> + <assertEquals stepKey="assertProductQtyInCart"> + <expectedResult type="string">{{quoteQty11Subtotal1320.qty}}</expectedResult> + <actualResult type="variable">grabProductQtyInCart</actualResult> + </assertEquals> + <see userInput="{{quoteQty11Subtotal1320.currency}}{{quoteQty11Subtotal1320.subtotal}}" selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="assertSubtotal"/> <!-- Minicart product price and subtotal should be updated --> <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="openMinicart"/> <grabValueFrom selector="{{StorefrontMinicartSection.itemQuantity($$createProduct.name$$)}}" stepKey="grabProductQtyInMinicart"/> - <assertEquals expected="11" actual="$grabProductQtyInMinicart" stepKey="assertProductQtyInMinicart"/> - <see userInput="1,320.00" selector="{{StorefrontMinicartSection.subtotal}}" stepKey="assertMinicartSubtotal"/> + <assertEquals stepKey="assertProductQtyInMinicart"> + <expectedResult type="string">{{quoteQty11Subtotal1320.qty}}</expectedResult> + <actualResult type="variable">grabProductQtyInMinicart</actualResult> + </assertEquals> + <see userInput="{{quoteQty11Subtotal1320.currency}}{{quoteQty11Subtotal1320.subtotal}}" selector="{{StorefrontMinicartSection.subtotal}}" stepKey="assertMinicartSubtotal"/> </test> </tests> diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateShoppingCartTest.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateShoppingCartTest.xml index 43acf9cd740d..5caa3ba9b924 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateShoppingCartTest.xml +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateShoppingCartTest.xml @@ -8,8 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Checkout\Test\TestCase\UpdateShoppingCartTest" summary="Update Shopping Cart" ticketId="MAGETWO-25081"> <variation name="UpdateShoppingCartTestVariation1"> - <data name="tag" xsi:type="string">mftf_migrated:yes</data> - <data name="tag" xsi:type="string">severity:S0</data> + <data name="tag" xsi:type="string">severity:S0,mftf_migrated:yes</data> <data name="product/dataset" xsi:type="string">default</data> <data name="product/data/price/value" xsi:type="string">100</data> <data name="product/data/checkout_data/qty" xsi:type="string">3</data> @@ -21,8 +20,7 @@ <constraint name="Magento\Checkout\Test\Constraint\AssertSubtotalInShoppingCart" /> </variation> <variation name="UpdateShoppingCartTestVariation2"> - <data name="tag" xsi:type="string">mftf_migrated:yes</data> - <data name="tag" xsi:type="string">severity:S0</data> + <data name="tag" xsi:type="string">severity:S0,mftf_migrated:yes</data> <data name="product/dataset" xsi:type="string">with_two_custom_option</data> <data name="product/data/price/value" xsi:type="string">50</data> <data name="product/data/checkout_data/qty" xsi:type="string">11</data> From 0e2a92868c8eebce6a139442285e0cac2a6cff36 Mon Sep 17 00:00:00 2001 From: Vitaliy Honcharenko <vgoncharenko@magento.com> Date: Wed, 20 Mar 2019 14:58:13 -0500 Subject: [PATCH 234/265] MC-6273: Mysql url_rewrite select make on product view page 170+ times per request - fixed static tests --- lib/internal/Magento/Framework/Lock/Backend/Cache.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/internal/Magento/Framework/Lock/Backend/Cache.php b/lib/internal/Magento/Framework/Lock/Backend/Cache.php index 256ad2fdbd3e..dfe6bbb82835 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Cache.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Cache.php @@ -57,11 +57,13 @@ public function isLocked(string $name): bool } /** - * @param string $name + * Get cache locked identifier based on cache identifier. + * + * @param string $cacheIdentifier * @return string */ - private function getIdentifier(string $name): string + private function getIdentifier(string $cacheIdentifier): string { - return self::LOCK_PREFIX . $name; + return self::LOCK_PREFIX . $cacheIdentifier; } } From ccb284f9662d1230ba06c10505d524e5dea406a8 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Wed, 20 Mar 2019 14:58:41 -0500 Subject: [PATCH 235/265] Fix unstable test --- .../Test/Mftf/ActionGroup/AdminExportActionGroup.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml index 63248dd53fc1..b9eea2b11463 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml @@ -56,9 +56,10 @@ <reloadPage stepKey="refreshPage"/> <waitForPageLoad stepKey="waitFormReload"/> <click stepKey="clickSelectBtn" selector="{{AdminExportAttributeSection.selectByIndex(rowIndex)}}"/> - <click stepKey="clickOnDownload" selector="{{AdminExportAttributeSection.delete(rowIndex)}}" after="clickSelectBtn"/> + <click stepKey="clickOnDelete" selector="{{AdminExportAttributeSection.delete(rowIndex)}}" after="clickSelectBtn"/> <waitForElementVisible selector="{{AdminProductGridConfirmActionSection.title}}" stepKey="waitForConfirmModal"/> - <click selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="confirmProductDelete"/> + <click selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="confirmDelete"/> + <waitForPageLoad stepKey="waitForExportDataDeleted" /> <see selector="{{AdminDataGridTableSection.dataGridEmpty}}" userInput="We couldn't find any records." stepKey="assertDataGridEmptyMessage"/> </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> From ef3cc3634fcf4d03030e50e8627a23ccd718b60e Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Thu, 21 Mar 2019 08:12:17 +0200 Subject: [PATCH 236/265] 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 = + <<<QUERY +{ + wishlist { + items_count + name + sharing_code + updated_at + items { + id + qty + description + added_at + product { + sku + name + } + } + } +} +QUERY; + $this->graphQlQuery($query); + } + /** * @param string $email * @param string $password From f8b955c552c44e4e7478f324d2ecff8f2f0c90e8 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <korablov@adobe.com> Date: Thu, 21 Mar 2019 09:00:45 -0500 Subject: [PATCH 237/265] MAGETWO-98151: Add support ZooKeeper and flock locks --- .../Framework/Lock/Backend/FileTest.php | 52 +++++ .../Magento/Framework/Lock/Backend/File.php | 194 ++++++++++++++++++ .../Framework/Lock/Backend/Zookeeper.php | 2 +- .../Framework/Lock/LockBackendFactory.php | 9 + .../Lock/Test/Unit/LockBackendFactoryTest.php | 6 + .../Setup/Model/ConfigOptionsList/Lock.php | 52 +++++ .../Unit/Model/ConfigOptionsList/LockTest.php | 29 ++- 7 files changed, 342 insertions(+), 2 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileTest.php create mode 100644 lib/internal/Magento/Framework/Lock/Backend/File.php diff --git a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileTest.php b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileTest.php new file mode 100644 index 000000000000..fd550ac14f22 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileTest.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Lock\Backend; + +/** + * \Magento\Framework\Lock\Backend\Database test case + */ +class FileTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Framework\Lock\Backend\File + */ + private $model; + + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + + protected function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->model = $this->objectManager->create(\Magento\Framework\Lock\Backend\File::class, ['path' => '/tmp']); + } + + public function testLockAndUnlock() + { + $name = 'test_lock'; + + $this->assertFalse($this->model->isLocked($name)); + + $this->assertTrue($this->model->lock($name)); + $this->assertTrue($this->model->isLocked($name)); + $this->assertFalse($this->model->lock($name, 2)); + + $this->assertTrue($this->model->unlock($name)); + $this->assertFalse($this->model->isLocked($name)); + } + + public function testUnlockWithoutExistingLock() + { + $name = 'test_lock'; + + $this->assertFalse($this->model->isLocked($name)); + $this->assertFalse($this->model->unlock($name)); + } +} diff --git a/lib/internal/Magento/Framework/Lock/Backend/File.php b/lib/internal/Magento/Framework/Lock/Backend/File.php new file mode 100644 index 000000000000..79f41829f109 --- /dev/null +++ b/lib/internal/Magento/Framework/Lock/Backend/File.php @@ -0,0 +1,194 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Lock\Backend; + +use Magento\Framework\Lock\LockManagerInterface; +use Magento\Framework\Filesystem\Driver\File as FileDriver; +use Magento\Framework\Exception\RuntimeException; +use Magento\Framework\Exception\FileSystemException; +use Magento\Framework\Phrase; + +/** + * LockManager using the file system for locks + */ +class File implements LockManagerInterface +{ + /** + * The file driver instance + * + * @var FileDriver + */ + private $fileDriver; + + /** + * The path to the locks storage folder + * + * @var string + */ + private $path; + + /** + * How many microseconds to wait before re-try to acquire a lock + * + * @var int + */ + private $sleepCycle = 100000; + + /** + * The mapping list of the path lock with the file resource + * + * @var array + */ + private $locks = []; + + /** + * @param FileDriver $fileDriver The file driver + * @param string $path The path to the locks storage folder + * @throws RuntimeException Throws RuntimeException if $path is empty + * or cannot create the directory for locks + */ + public function __construct(FileDriver $fileDriver, string $path) + { + if (!$path) { + throw new RuntimeException(new Phrase('The path needs to be a non-empty string.')); + } + + $this->fileDriver = $fileDriver; + $this->path = preg_replace('#\/*$#', '', $path) ?: '/'; + + try { + if (!$this->fileDriver->isExists($this->path)) { + $this->fileDriver->createDirectory($this->path); + } + } catch (FileSystemException $exception) { + throw new RuntimeException( + new Phrase('Cannot create the directory for locks: %1', [$this->path]), + $exception + ); + } + } + + /** + * Acquires a lock by name + * + * @param string $name The lock name + * @param int $timeout Timeout in seconds. A negative timeout value means infinite timeout + * @return bool Returns true if the lock is acquired, otherwise returns false + * @throws RuntimeException Throws RuntimeException if cannot acquires the lock because FS problems + */ + public function lock(string $name, int $timeout = -1): bool + { + try { + $lockFile = $this->getLockPath($name); + $fileResource = $this->fileDriver->fileOpen($lockFile, 'w+'); + $skipDeadline = $timeout < 0; + $deadline = microtime(true) + $timeout; + + while (!$this->tryToLock($fileResource)) { + if (!$skipDeadline && $deadline <= microtime(true)) { + $this->fileDriver->fileClose($fileResource); + return false; + } + usleep($this->sleepCycle); + } + } catch (FileSystemException $exception) { + throw new RuntimeException(new Phrase('Cannot acquire a lock.'), $exception); + } + + $this->locks[$lockFile] = $fileResource; + return true; + } + + /** + * Checks if a lock exists by name + * + * @param string $name The lock name + * @return bool Returns true if the lock exists, otherwise returns false + * @throws RuntimeException Throws RuntimeException if cannot check that the lock exists + */ + public function isLocked(string $name): bool + { + $lockFile = $this->getLockPath($name); + $result = false; + + try { + if ($this->fileDriver->isExists($lockFile)) { + $fileResource = $this->fileDriver->fileOpen($lockFile, 'w+'); + if ($this->tryToLock($fileResource)) { + $result = false; + } else { + $result = true; + } + $this->fileDriver->fileClose($fileResource); + } + } catch (FileSystemException $exception) { + throw new RuntimeException(new Phrase('Cannot verify that the lock exists.'), $exception); + } + + return $result; + } + + /** + * Remove the lock by name + * + * @param string $name The lock name + * @return bool If the lock is removed returns true, otherwise returns false + */ + public function unlock(string $name): bool + { + $lockFile = $this->getLockPath($name); + + if (isset($this->locks[$lockFile]) && $this->tryToUnlock($this->locks[$lockFile])) { + unset($this->locks[$lockFile]); + return true; + } + + return false; + } + + /** + * Returns the full path to the lock file by name + * + * @param string $name The lock name + * @return string The path to the lock file + */ + private function getLockPath(string $name): string + { + return $this->path . '/' . $name; + } + + /** + * Tries to lock a file resource + * + * @param resource $resource The file resource + * @return bool If the lock is acquired returns true, otherwise returns false + */ + private function tryToLock($resource): bool + { + try { + return $this->fileDriver->fileLock($resource, LOCK_EX | LOCK_NB); + } catch (FileSystemException $exception) { + return false; + } + } + + /** + * Tries to unlock a file resource + * + * @param resource $resource The file resource + * @return bool If the lock is removed returns true, otherwise returns false + */ + private function tryToUnlock($resource): bool + { + try { + return $this->fileDriver->fileLock($resource, LOCK_UN | LOCK_NB); + } catch (FileSystemException $exception) { + return false; + } + } +} diff --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php index 1e7ca069df79..1dbedaaaa5e1 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php @@ -54,7 +54,7 @@ class Zookeeper implements LockManagerInterface /** * How many microseconds to wait before recheck connections or nodes * - * @var float + * @var int */ private $sleepCycle = 100000; diff --git a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php index 46cb2998ede7..20e0f69328c8 100644 --- a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php +++ b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php @@ -14,6 +14,7 @@ use Magento\Framework\Lock\Backend\Database as DatabaseLock; use Magento\Framework\Lock\Backend\Zookeeper as ZookeeperLock; use Magento\Framework\Lock\Backend\Cache as CacheLock; +use Magento\Framework\Lock\Backend\File as FileLock; /** * The factory to create object that implements LockManagerInterface @@ -55,6 +56,13 @@ class LockBackendFactory */ const LOCK_CACHE = 'cache'; + /** + * File lock provider name + * + * @const string + */ + const LOCK_FILE = 'file'; + /** * The list of lock providers with mapping on classes * @@ -64,6 +72,7 @@ class LockBackendFactory self::LOCK_DB => DatabaseLock::class, self::LOCK_ZOOKEEPER => ZookeeperLock::class, self::LOCK_CACHE => CacheLock::class, + self::LOCK_FILE => FileLock::class, ]; /** diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php index 8864ab6f9ead..030b126284db 100644 --- a/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php @@ -10,6 +10,7 @@ use Magento\Framework\Lock\Backend\Database as DatabaseLock; use Magento\Framework\Lock\Backend\Zookeeper as ZookeeperLock; use Magento\Framework\Lock\Backend\Cache as CacheLock; +use Magento\Framework\Lock\Backend\File as FileLock; use Magento\Framework\Lock\LockBackendFactory; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Lock\LockManagerInterface; @@ -95,6 +96,11 @@ public function createDataProvider(): array 'lockProviderClass' => CacheLock::class, 'config' => [], ], + 'file' => [ + 'lockProvider' => LockBackendFactory::LOCK_FILE, + 'lockProviderClass' => FileLock::class, + 'config' => ['path' => '/my/path'], + ], ]; if (extension_loaded('zookeeper')) { diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php index 799409d1560a..b4cc14e25ca1 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php @@ -49,6 +49,13 @@ class Lock implements ConfigOptionsListInterface */ const INPUT_KEY_LOCK_ZOOKEEPER_PATH = 'lock-zookeeper-path'; + /** + * The name of an option to set File path + * + * @const string + */ + const INPUT_KEY_LOCK_FILE_PATH = 'lock-file-path'; + /** * The configuration path to save lock provider * @@ -77,6 +84,13 @@ class Lock implements ConfigOptionsListInterface */ const CONFIG_PATH_LOCK_ZOOKEEPER_PATH = 'lock/config/path'; + /** + * The configuration path to save locks directory path + * + * @const string + */ + const CONFIG_PATH_LOCK_FILE_PATH = 'lock/config/path'; + /** * The list of lock providers * @@ -86,6 +100,7 @@ class Lock implements ConfigOptionsListInterface LockBackendFactory::LOCK_DB, LockBackendFactory::LOCK_ZOOKEEPER, LockBackendFactory::LOCK_CACHE, + LockBackendFactory::LOCK_FILE, ]; /** @@ -106,6 +121,10 @@ class Lock implements ConfigOptionsListInterface LockBackendFactory::LOCK_CACHE => [ self::INPUT_KEY_LOCK_PROVIDER => self::CONFIG_PATH_LOCK_PROVIDER, ], + LockBackendFactory::LOCK_FILE => [ + self::INPUT_KEY_LOCK_PROVIDER => self::CONFIG_PATH_LOCK_PROVIDER, + self::INPUT_KEY_LOCK_FILE_PATH => self::CONFIG_PATH_LOCK_FILE_PATH, + ], ]; /** @@ -151,6 +170,12 @@ public function getOptions() self::CONFIG_PATH_LOCK_ZOOKEEPER_PATH, 'The path where Zookeeper will save locks. The default path is: ' . ZookeeperLock::DEFAULT_PATH ), + new TextConfigOption( + self::INPUT_KEY_LOCK_FILE_PATH, + TextConfigOption::FRONTEND_WIZARD_TEXT, + self::CONFIG_PATH_LOCK_FILE_PATH, + 'The path where file locks will be saved.' + ), ]; } @@ -184,6 +209,9 @@ public function validate(array $options, DeploymentConfig $deploymentConfig) case LockBackendFactory::LOCK_ZOOKEEPER: $errors = $this->validateZookeeperConfig($options, $deploymentConfig); break; + case LockBackendFactory::LOCK_FILE: + $errors = $this->validateFileConfig($options, $deploymentConfig); + break; case LockBackendFactory::LOCK_CACHE: case LockBackendFactory::LOCK_DB: $errors = []; @@ -195,6 +223,30 @@ public function validate(array $options, DeploymentConfig $deploymentConfig) return $errors; } + /** + * Validates File locks configuration + * + * @param array $options + * @param DeploymentConfig $deploymentConfig + * @return array + */ + private function validateFileConfig(array $options, DeploymentConfig $deploymentConfig): array + { + $errors = []; + + $path = $options[self::INPUT_KEY_LOCK_FILE_PATH] + ?? $deploymentConfig->get( + self::CONFIG_PATH_LOCK_FILE_PATH, + $this->getDefaultValue(self::INPUT_KEY_LOCK_ZOOKEEPER_PATH) + ); + + if (!$path) { + $errors[] = 'The path needs to be a non-empty string.'; + } + + return $errors; + } + /** * Validates Zookeeper configuration * diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php index 5a150ad3dcd8..1a46bddf5f21 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php @@ -44,7 +44,7 @@ protected function setUp() public function testGetOptions() { $options = $this->lockConfigOptionsList->getOptions(); - $this->assertSame(4, count($options)); + $this->assertSame(5, count($options)); $this->assertArrayHasKey(0, $options); $this->assertInstanceOf(SelectConfigOption::class, $options[0]); @@ -61,6 +61,10 @@ public function testGetOptions() $this->assertArrayHasKey(3, $options); $this->assertInstanceOf(TextConfigOption::class, $options[3]); $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_PATH, $options[3]->getName()); + + $this->assertArrayHasKey(4, $options); + $this->assertInstanceOf(TextConfigOption::class, $options[4]); + $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_FILE_PATH, $options[4]->getName()); } /** @@ -150,6 +154,20 @@ public function createConfigDataProvider(): array ], ], ], + 'Check specific file lock options' => [ + 'options' => [ + LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_FILE, + LockConfigOptionsList::INPUT_KEY_LOCK_FILE_PATH => '/my/path' + ], + 'expectedResult' => [ + 'lock' => [ + 'provider' => LockBackendFactory::LOCK_FILE, + 'config' => [ + 'path' => '/my/path', + ], + ], + ], + ], ]; } @@ -200,6 +218,15 @@ public function validateDataProvider(): array 'Zookeeper host is should be set.', ], ], + 'Empty path for File lock' => [ + 'options' => [ + LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_FILE, + LockConfigOptionsList::INPUT_KEY_LOCK_FILE_PATH => '', + ], + 'expectedResult' => [ + 'The path needs to be a non-empty string.', + ], + ], ]; } } From a8d9be1c4f617a3ff0312f433f080b67b41ed3b6 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Thu, 21 Mar 2019 16:04:29 +0200 Subject: [PATCH 238/265] 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 b5eaa81580e7c9fe262e81e0602732eb1e37b49a Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <korablov@adobe.com> Date: Thu, 21 Mar 2019 09:08:29 -0500 Subject: [PATCH 239/265] MAGETWO-98151: Add support ZooKeeper and flock locks --- .../testsuite/Magento/Framework/Lock/Backend/FileTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileTest.php b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileTest.php index fd550ac14f22..feb111e436b1 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileTest.php @@ -8,7 +8,7 @@ namespace Magento\Framework\Lock\Backend; /** - * \Magento\Framework\Lock\Backend\Database test case + * \Magento\Framework\Lock\Backend\File test case */ class FileTest extends \PHPUnit\Framework\TestCase { From a2ad4424d5db9c41f50c4b8090f2e8a981b02014 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <korablov@adobe.com> Date: Thu, 21 Mar 2019 09:40:01 -0500 Subject: [PATCH 240/265] MAGETWO-98151: Add support ZooKeeper and flock locks --- lib/internal/Magento/Framework/Lock/Backend/File.php | 4 ++-- lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/internal/Magento/Framework/Lock/Backend/File.php b/lib/internal/Magento/Framework/Lock/Backend/File.php index 79f41829f109..47aed8a6fe3e 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/File.php +++ b/lib/internal/Magento/Framework/Lock/Backend/File.php @@ -59,7 +59,7 @@ public function __construct(FileDriver $fileDriver, string $path) } $this->fileDriver = $fileDriver; - $this->path = preg_replace('#\/*$#', '', $path) ?: '/'; + $this->path = rtrim($path, '/') . '/'; try { if (!$this->fileDriver->isExists($this->path)) { @@ -159,7 +159,7 @@ public function unlock(string $name): bool */ private function getLockPath(string $name): string { - return $this->path . '/' . $name; + return $this->path . $name; } /** diff --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php index 1dbedaaaa5e1..cbba981ae1b5 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php @@ -97,7 +97,7 @@ public function __construct(string $host, string $path = self::DEFAULT_PATH) } $this->host = $host; - $this->path = preg_replace('#\/*$#', '', $path) ?: '/'; + $this->path = rtrim($path, '/') . '/'; } /** @@ -171,7 +171,7 @@ public function isLocked(string $name): bool */ private function getFullPathToLock(string $name): string { - return $this->path . '/' . $name . '/' . $this->lockName; + return $this->path . $name . '/' . $this->lockName; } /** From b7e67007bcf62a1cd41a2d8039cb72709eb311ee Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <veronika_kurochkina@epam.com> Date: Thu, 21 Mar 2019 19:01:36 +0300 Subject: [PATCH 241/265] MAGETWO-51459: [FT] CreateCategoryEntityTest fail when system message about indexers update is displayed - Skip flaky MFTF test AdminCreateCustomProductAttributeWithDropdownFieldTest --- .../AdminCreateCustomProductAttributeWithDropdownFieldTest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCustomProductAttributeWithDropdownFieldTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCustomProductAttributeWithDropdownFieldTest.xml index 5b6a0b7f2ab3..288941e48764 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCustomProductAttributeWithDropdownFieldTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCustomProductAttributeWithDropdownFieldTest.xml @@ -16,6 +16,9 @@ <severity value="CRITICAL"/> <testCaseId value="MC-10905"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-15474"/> + </skip> </annotations> <before> From dd80c0c53d3b2993a9de918665dc63606cfbc5eb Mon Sep 17 00:00:00 2001 From: Jeroen <jeroen@reachdigital.nl> Date: Thu, 21 Mar 2019 17:26:51 +0100 Subject: [PATCH 242/265] Remove strict return type --- .../Shipping/Controller/Adminhtml/Order/Shipment/AddTrack.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AddTrack.php b/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AddTrack.php index 4ec7117783aa..c417a202321b 100644 --- a/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AddTrack.php +++ b/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AddTrack.php @@ -80,7 +80,7 @@ public function __construct( * * @return ResultInterface */ - public function execute(): ResultInterface + public function execute() { try { $carrier = $this->getRequest()->getPost('carrier'); From 2d9c915275cbd5b5b5da1f8b968b56175ca64e0c Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <korablov@adobe.com> Date: Thu, 21 Mar 2019 12:00:33 -0500 Subject: [PATCH 243/265] MAGETWO-98151: Add support ZooKeeper and flock locks --- .../Lock/Backend/{FileTest.php => FileLockTest.php} | 9 ++++++--- .../Framework/Lock/Backend/{File.php => FileLock.php} | 2 +- .../Magento/Framework/Lock/LockBackendFactory.php | 2 +- .../Framework/Lock/Test/Unit/LockBackendFactoryTest.php | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) rename dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/{FileTest.php => FileLockTest.php} (81%) rename lib/internal/Magento/Framework/Lock/Backend/{File.php => FileLock.php} (99%) diff --git a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileTest.php b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileLockTest.php similarity index 81% rename from dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileTest.php rename to dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileLockTest.php index feb111e436b1..e64b3c505acf 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileLockTest.php @@ -10,10 +10,10 @@ /** * \Magento\Framework\Lock\Backend\File test case */ -class FileTest extends \PHPUnit\Framework\TestCase +class FileLockTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Framework\Lock\Backend\File + * @var \Magento\Framework\Lock\Backend\FileLock */ private $model; @@ -25,7 +25,10 @@ class FileTest extends \PHPUnit\Framework\TestCase protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->model = $this->objectManager->create(\Magento\Framework\Lock\Backend\File::class, ['path' => '/tmp']); + $this->model = $this->objectManager->create( + \Magento\Framework\Lock\Backend\FileLock::class, + ['path' => '/tmp'] + ); } public function testLockAndUnlock() diff --git a/lib/internal/Magento/Framework/Lock/Backend/File.php b/lib/internal/Magento/Framework/Lock/Backend/FileLock.php similarity index 99% rename from lib/internal/Magento/Framework/Lock/Backend/File.php rename to lib/internal/Magento/Framework/Lock/Backend/FileLock.php index 47aed8a6fe3e..d168e910a4ab 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/File.php +++ b/lib/internal/Magento/Framework/Lock/Backend/FileLock.php @@ -16,7 +16,7 @@ /** * LockManager using the file system for locks */ -class File implements LockManagerInterface +class FileLock implements LockManagerInterface { /** * The file driver instance diff --git a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php index 20e0f69328c8..b142085ef656 100644 --- a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php +++ b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php @@ -14,7 +14,7 @@ use Magento\Framework\Lock\Backend\Database as DatabaseLock; use Magento\Framework\Lock\Backend\Zookeeper as ZookeeperLock; use Magento\Framework\Lock\Backend\Cache as CacheLock; -use Magento\Framework\Lock\Backend\File as FileLock; +use Magento\Framework\Lock\Backend\FileLock; /** * The factory to create object that implements LockManagerInterface diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php index 030b126284db..ebf2f54f3e09 100644 --- a/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php @@ -10,7 +10,7 @@ use Magento\Framework\Lock\Backend\Database as DatabaseLock; use Magento\Framework\Lock\Backend\Zookeeper as ZookeeperLock; use Magento\Framework\Lock\Backend\Cache as CacheLock; -use Magento\Framework\Lock\Backend\File as FileLock; +use Magento\Framework\Lock\Backend\FileLock; use Magento\Framework\Lock\LockBackendFactory; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Lock\LockManagerInterface; From 1cb451a2dd2aa2867a9b4b72ec5cfb8f72aa6852 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <korablov@adobe.com> Date: Thu, 21 Mar 2019 13:15:55 -0500 Subject: [PATCH 244/265] MAGETWO-98151: Add support ZooKeeper and flock locks --- setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php index b4cc14e25ca1..66f41128c46b 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php @@ -237,7 +237,7 @@ private function validateFileConfig(array $options, DeploymentConfig $deployment $path = $options[self::INPUT_KEY_LOCK_FILE_PATH] ?? $deploymentConfig->get( self::CONFIG_PATH_LOCK_FILE_PATH, - $this->getDefaultValue(self::INPUT_KEY_LOCK_ZOOKEEPER_PATH) + $this->getDefaultValue(self::INPUT_KEY_LOCK_FILE_PATH) ); if (!$path) { From b96435f8d213f5de0eb8352e2e4fd1d1e117895f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Deg=C3=B3rski?= <kamil.degorski@snow.dog> Date: Thu, 21 Mar 2019 20:00:07 +0100 Subject: [PATCH 245/265] 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 <kozyr1av@gmail.com> Date: Thu, 21 Mar 2019 22:14:48 +0000 Subject: [PATCH 246/265] [+] 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 = <<<QUERY +mutation { + setPaymentMethodOnCart( + input: { + {$input} + } + ) { + cart { + items { + qty + } + } + } +} +QUERY; + $this->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 = <<<QUERY +mutation { + setPaymentMethodOnCart( + input: { + {$input} + } + ) { + cart { + items { + qty + } + } + } +} +QUERY; + $this->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?= <kamil.degorski@snow.dog> Date: Thu, 21 Mar 2019 23:58:25 +0100 Subject: [PATCH 247/265] 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 <p.bystritsky@yandex.ru> Date: Fri, 22 Mar 2019 10:32:50 +0200 Subject: [PATCH 248/265] 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 3be8ecad58e10017f63f1bb727352719c5814f02 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <d.cheshun@atwix.com> Date: Fri, 22 Mar 2019 13:40:26 +0200 Subject: [PATCH 249/265] Exclude htaccess form gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 8a15fbacaf63..8ec1104f2553 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,7 @@ atlassian* !/pub/media/import/.htaccess /pub/media/logo/* /pub/media/custom_options/* +!/pub/media/custom_options/.htaccess /pub/media/theme/* /pub/media/theme_customization/* !/pub/media/theme_customization/.htaccess From 6fba6865685a41753fce0631bc9461e6632df8c9 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Fri, 22 Mar 2019 14:09:49 +0200 Subject: [PATCH 250/265] 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 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/pages.xsd"> <page name="MultishippingCheckoutOverview" mca="multishipping/checkout/overview" module="Magento_Checkout"> - <block name="agreementReview" class="Magento\CheckoutAgreements\Test\Block\Multishipping\MultishippingAgreementReview" locator="#checkout-agreements" strategy="css selector"/> + <block name="agreementReview" class="Magento\CheckoutAgreements\Test\Block\Multishipping\MultishippingAgreementReview" locator=".checkout-agreements" strategy="css selector"/> </page> </config> From c58655ee7a12d38ac37440c19e716568fe92a662 Mon Sep 17 00:00:00 2001 From: Yuliya Labudova <Yuliya_Labudova@epam.com> Date: Fri, 22 Mar 2019 15:36:22 +0300 Subject: [PATCH 251/265] MAGETWO-44165: Magento\Catalog\Test\TestCase\Product\ProductTypeSwitchingOnCreationTest fails - Fix comments --- .../Mftf/Test/AdminCreateAndSwitchProductType.xml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml index 50c17cfc4b9a..3a40bd6661ff 100755 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml @@ -87,6 +87,7 @@ <title value="Admin should be able to switch a new product from simple to configurable"/> <description value="After selecting a simple product when adding Admin should be switch to configurable implicitly"/> <severity value="CRITICAL"/> + <useCaseId value="MAGETWO-44165"/> <testCaseId value="MAGETWO-29398"/> <group value="catalog"/> <group value="mtf_migrated"/> @@ -103,6 +104,8 @@ <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="openProductFillForm"> <argument name="productType" value="simple"/> </actionGroup> + <!-- Create configurable product from simple product page--> + <comment userInput="Create configurable product" stepKey="commentCreateProduct"/> <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> <argument name="product" value="_defaultProduct"/> </actionGroup> @@ -112,6 +115,8 @@ </actionGroup> <actionGroup ref="saveConfiguredProduct" stepKey="saveProductForm"/> <see selector="{{AdminProductGridSection.productGridCell('2', 'Type')}}" userInput="Configurable Product" stepKey="seeProductTypeInGrid"/> + <!-- Verify product on store front --> + <comment userInput="Verify product on store front" stepKey="commentVerifyProductGrid"/> <actionGroup ref="VerifyOptionInProductStorefront" stepKey="verifyConfigurableOption" after="AssertProductInStorefrontProductPage"> <argument name="attributeCode" value="$createConfigProductAttribute.default_frontend_label$"/> <argument name="optionName" value="$createConfigProductAttributeOption1.option[store_labels][1][label]$"/> @@ -124,6 +129,7 @@ <title value="Admin should be able to switch a new product from downloadable to configurable"/> <description value="After selecting a downloadable product when adding Admin should be switch to configurable implicitly"/> <severity value="CRITICAL"/> + <useCaseId value="MAGETWO-44165"/> <testCaseId value="MAGETWO-29398"/> <group value="catalog"/> <group value="mtf_migrated"/> @@ -145,18 +151,16 @@ <deleteData stepKey="deleteAttribute" createDataKey="createConfigProductAttribute"/> <actionGroup ref="logout" stepKey="logout"/> </after> - + <!-- Create configurable product from downloadable product page--> + <comment userInput="Create configurable product" stepKey="commentCreateProduct"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> <!-- Open Dropdown and select downloadable product option --> <comment stepKey="beforeOpenProductFillForm" userInput="Selecting Product from the Add Product Dropdown"/> <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="openProductFillForm"> <argument name="productType" value="downloadable"/> </actionGroup> - <scrollTo selector="{{AdminProductDownloadableSection.sectionHeader}}" stepKey="scrollToDownloadableInfo" /> - <wait stepKey="2" time="3"/> <uncheckOption selector="{{AdminProductDownloadableSection.isDownloadableProduct}}" stepKey="checkIsDownloadable"/> - <!-- Fill form for Downloadable Product Type --> <comment stepKey="beforeFillProductForm" userInput="Filling Product Form"/> <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> @@ -178,7 +182,7 @@ <argument name="product" value="_defaultProduct"/> </actionGroup> <see selector="{{AdminProductGridSection.productGridCell('2', 'Type')}}" userInput="Configurable Product" stepKey="seeProductTypeInGrid"/> - <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="assertProductInStorefrontProductPage"> <argument name="product" value="_defaultProduct"/> </actionGroup> <actionGroup ref="VerifyOptionInProductStorefront" stepKey="verifyConfigurableOption"> From 3825545a5dfa1ddc7ae2193ff03e4e54e0c12f30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Deg=C3=B3rski?= <kamil.degorski@snow.dog> Date: Fri, 22 Mar 2019 16:06:48 +0100 Subject: [PATCH 252/265] 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 <javier@medialounge.co.uk> Date: Fri, 22 Mar 2019 15:54:05 +0000 Subject: [PATCH 253/265] 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 <mikola.malevanec@transoftgroup.com> Date: Mon, 25 Mar 2019 11:28:02 +0200 Subject: [PATCH 254/265] 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 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Backend\Test\TestCase\NavigateMenuTest"> <variation name="NavigateMenuTest70"> - <data name="menuItem" xsi:type="string">Marketing > Reviews</data> + <data name="menuItem" xsi:type="string">Marketing > All Reviews</data> <data name="pageTitle" xsi:type="string">Reviews</data> <constraint name="Magento\Backend\Test\Constraint\AssertBackendPageIsAvailable"/> </variation> From 6f2c98b7b19368f7d5e4a19f9b0bff1d5b5f430a Mon Sep 17 00:00:00 2001 From: Ivan Gerchak <ivang@ven.com> Date: Mon, 25 Mar 2019 12:40:52 +0200 Subject: [PATCH 255/265] Change event "mouseup" to "mousedown" --- lib/web/mage/gallery/gallery.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/mage/gallery/gallery.js b/lib/web/mage/gallery/gallery.js index db98a1cc39a3..c20329c0ce63 100644 --- a/lib/web/mage/gallery/gallery.js +++ b/lib/web/mage/gallery/gallery.js @@ -141,7 +141,7 @@ define([ this.setupBreakpoints(); this.initFullscreenSettings(); - this.settings.$element.on('mouseup', '.fotorama__stage__frame', function () { + this.settings.$element.on('mousedown', '.fotorama__stage__frame', function () { if ( !$(this).parents('.fotorama__shadows--left, .fotorama__shadows--right').length && !$(this).hasClass('fotorama-video-container') From 59771c40c1a2aa857d3b64b0905aa67c1705a1c9 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Mon, 25 Mar 2019 14:58:00 +0200 Subject: [PATCH 256/265] Fix static tests. --- .../Magento/Quote/Model/QuoteManagement.php | 60 ++++++++++++------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/app/code/Magento/Quote/Model/QuoteManagement.php b/app/code/Magento/Quote/Model/QuoteManagement.php index e1058c98271c..2fcfd2dfadab 100644 --- a/app/code/Magento/Quote/Model/QuoteManagement.php +++ b/app/code/Magento/Quote/Model/QuoteManagement.php @@ -532,28 +532,7 @@ protected function submitQuote(QuoteEntity $quote, $orderData = []) ); $this->quoteRepository->save($quote); } catch (\Exception $e) { - try { - if (!empty($this->addressesToSync)) { - foreach ($this->addressesToSync as $addressId) { - $this->addressRepository->deleteById($addressId); - } - } - $this->eventManager->dispatch( - 'sales_model_service_quote_submit_failure', - [ - 'order' => $order, - 'quote' => $quote, - 'exception' => $e, - ] - ); - } catch (\Exception $consecutiveException) { - $message = sprintf( - "An exception occurred on 'sales_model_service_quote_submit_failure' event: %s", - $consecutiveException->getMessage() - ); - - throw new \Exception($message, 0, $e); - } + $this->rollbackAddresses($quote, $order, $e); throw $e; } return $order; @@ -620,4 +599,41 @@ protected function _prepareCustomerQuote($quote) $shipping->setIsDefaultBilling(true); } } + + /** + * Remove related to order and quote addresses and submit exception to further processing. + * + * @param Quote $quote + * @param \Magento\Sales\Api\Data\OrderInterface $order + * @param \Exception $e + * @throws \Exception + */ + private function rollbackAddresses( + QuoteEntity $quote, + \Magento\Sales\Api\Data\OrderInterface $order, + \Exception $e + ): void { + try { + if (!empty($this->addressesToSync)) { + foreach ($this->addressesToSync as $addressId) { + $this->addressRepository->deleteById($addressId); + } + } + $this->eventManager->dispatch( + 'sales_model_service_quote_submit_failure', + [ + 'order' => $order, + 'quote' => $quote, + 'exception' => $e, + ] + ); + } catch (\Exception $consecutiveException) { + $message = sprintf( + "An exception occurred on 'sales_model_service_quote_submit_failure' event: %s", + $consecutiveException->getMessage() + ); + + throw new \Exception($message, 0, $e); + } + } } From b7dd71842d576faabf07c10b4cbac6436ee6aca9 Mon Sep 17 00:00:00 2001 From: Yuliya Labudova <Yuliya_Labudova@epam.com> Date: Mon, 25 Mar 2019 17:39:01 +0300 Subject: [PATCH 257/265] MAGETWO-44165: Magento\Catalog\Test\TestCase\Product\ProductTypeSwitchingOnCreationTest fails - Skip tests till the next MSI release --- .../Test/Mftf/Test/AdminCreateAndSwitchProductType.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml index 3a40bd6661ff..93df31a7d89e 100755 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml @@ -53,6 +53,9 @@ <testCaseId value="MC-10930"/> <group value="catalog"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MSI-2110"/> + </skip> </annotations> <before> <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> @@ -91,6 +94,9 @@ <testCaseId value="MAGETWO-29398"/> <group value="catalog"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MSI-2110"/> + </skip> </annotations> <before> <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> @@ -133,6 +139,9 @@ <testCaseId value="MAGETWO-29398"/> <group value="catalog"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MSI-2110"/> + </skip> </annotations> <before> <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> From 73088cdba04c5783165c0f2a7c20bae78c370ec2 Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Mon, 25 Mar 2019 13:17:47 -0500 Subject: [PATCH 258/265] 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 <vnayda@adobe.com> Date: Mon, 25 Mar 2019 13:33:16 -0500 Subject: [PATCH 259/265] 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 <vnayda@adobe.com> Date: Mon, 25 Mar 2019 15:55:34 -0500 Subject: [PATCH 260/265] 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 <vnayda@adobe.com> Date: Mon, 25 Mar 2019 16:41:47 -0500 Subject: [PATCH 261/265] 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: {}', From 4285ac5799fc8b7a3d9d15571839c5d92583c2f7 Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Tue, 26 Mar 2019 22:43:29 -0500 Subject: [PATCH 262/265] GraphQL-538: Api-functional tests fixture refactoring --- .../GetAvailablePaymentMethodsTest.php | 68 ++++--- .../GraphQl/Quote/Customer/GetCartTest.php | 80 +++----- .../Customer/SetBillingAddressOnCartTest.php | 86 ++++---- .../Customer/SetPaymentMethodOnCartTest.php | 184 +++++++++-------- .../Customer/SetShippingAddressOnCartTest.php | 64 ++++-- .../Guest/GetAvailablePaymentMethodsTest.php | 59 +++--- .../GraphQl/Quote/Guest/GetCartTest.php | 91 +++------ .../Guest/SetBillingAddressOnCartTest.php | 103 +++++----- .../Guest/SetPaymentMethodOnCartTest.php | 186 ++++++++---------- .../Guest/SetShippingAddressOnCartTest.php | 100 ++++------ .../SetOfflineShippingMethodsOnCartTest.php | 6 +- .../Quote/_files/add_simple_product.php | 28 +++ .../Quote/_files/add_virtual_product.php | 28 +++ .../_files/customer/create_empty_cart.php | 29 +++ .../customer/create_empty_cart_rollback.php | 28 +++ .../disable_all_active_payment_methods.php | 1 + ...le_all_active_payment_methods_rollback.php | 1 + .../_files/enable_offline_payment_methods.php | 24 +++ ...nable_offline_payment_methods_rollback.php | 20 ++ .../enable_offline_shipping_methods.php | 1 + ...able_offline_shipping_methods_rollback.php | 1 + .../Quote/_files/guest/create_empty_cart.php | 24 +++ .../guest/create_empty_cart_rollback.php | 28 +++ .../Quote/_files/make_cart_inactive.php | 23 +++ .../_files/set_checkmo_payment_method.php | 33 ++++ .../Quote/_files/set_new_shipping_address.php | 43 ++++ 26 files changed, 792 insertions(+), 547 deletions(-) rename dev/tests/api-functional/testsuite/Magento/GraphQl/{OfflineShipping => Quote}/SetOfflineShippingMethodsOnCartTest.php (96%) create mode 100644 dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/add_simple_product.php create mode 100644 dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/add_virtual_product.php create mode 100644 dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/customer/create_empty_cart.php create mode 100644 dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/customer/create_empty_cart_rollback.php rename dev/tests/integration/testsuite/Magento/{Payment => GraphQl/Quote}/_files/disable_all_active_payment_methods.php (93%) rename dev/tests/integration/testsuite/Magento/{Payment => GraphQl/Quote}/_files/disable_all_active_payment_methods_rollback.php (92%) create mode 100644 dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods.php create mode 100644 dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods_rollback.php rename dev/tests/integration/testsuite/Magento/{OfflineShipping => GraphQl/Quote}/_files/enable_offline_shipping_methods.php (89%) rename dev/tests/integration/testsuite/Magento/{OfflineShipping => GraphQl/Quote}/_files/enable_offline_shipping_methods_rollback.php (86%) create mode 100644 dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/guest/create_empty_cart.php create mode 100644 dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/guest/create_empty_cart_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/make_cart_inactive.php create mode 100644 dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/set_checkmo_payment_method.php create mode 100644 dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/set_new_shipping_address.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php index 74921348f449..9c988f6bf5f7 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php @@ -38,48 +38,56 @@ protected function setUp() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php */ - public function testGetCartWithPaymentMethods() + public function testGetAvailablePaymentMethods() { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_item_with_items'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = $this->getQuery($maskedQuoteId); $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('cart', $response); + self::assertArrayHasKey('available_payment_methods', $response['cart']); + self::assertCount(1, $response['cart']['available_payment_methods']); + self::assertEquals('checkmo', $response['cart']['available_payment_methods'][0]['code']); self::assertEquals('Check / Money order', $response['cart']['available_payment_methods'][0]['title']); - self::assertGreaterThan( - 0, - count($response['cart']['available_payment_methods']), - 'There are no available payment methods for customer cart!' - ); } /** + * @security * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php */ - public function testGetPaymentMethodsFromGuestCart() + public function testGetAvailablePaymentMethodsFromGuestCart() { - $guestQuoteMaskedId = $this->getMaskedQuoteIdByReservedOrderId->execute( - 'test_order_with_virtual_product_without_address' - ); - $query = $this->getQuery($guestQuoteMaskedId); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId); $this->expectExceptionMessage( - "The current user cannot perform operations on cart \"$guestQuoteMaskedId\"" + "The current user cannot perform operations on cart \"$maskedQuoteId\"" ); $this->graphQlQuery($query, [], '', $this->getHeaderMap()); } /** + * @security * @magentoApiDataFixture Magento/Customer/_files/three_customers.php - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php */ - public function testGetPaymentMethodsFromAnotherCustomerCart() + public function testGetAvailablePaymentMethodsFromAnotherCustomerCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_item_with_items'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = $this->getQuery($maskedQuoteId); $this->expectExceptionMessage( @@ -89,24 +97,31 @@ public function testGetPaymentMethodsFromAnotherCustomerCart() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php - * @magentoApiDataFixture Magento/Payment/_files/disable_all_active_payment_methods.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/disable_all_active_payment_methods.php */ - public function testGetPaymentMethodsIfPaymentsAreNotSet() + public function testGetAvailablePaymentMethodsIfPaymentsAreNotPresent() { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_item_with_items'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = $this->getQuery($maskedQuoteId); $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); - self::assertEquals(0, count($response['cart']['available_payment_methods'])); + self::assertArrayHasKey('cart', $response); + self::assertArrayHasKey('available_payment_methods', $response['cart']); + self::assertEmpty($response['cart']['available_payment_methods']); } /** * @magentoApiDataFixture Magento/Customer/_files/customer.php + * * @expectedException \Exception * @expectedExceptionMessage Could not find a cart with ID "non_existent_masked_id" */ - public function testGetPaymentMethodsOfNonExistentCart() + public function testGetAvailablePaymentMethodsOfNonExistentCart() { $maskedQuoteId = 'non_existent_masked_id'; $query = $this->getQuery($maskedQuoteId); @@ -118,9 +133,8 @@ public function testGetPaymentMethodsOfNonExistentCart() * @param string $maskedQuoteId * @return string */ - private function getQuery( - string $maskedQuoteId - ): string { + private function getQuery(string $maskedQuoteId): string + { return <<<QUERY { cart(cart_id: "$maskedQuoteId") { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php index cc90f1aa1f21..d2615b4135b0 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php @@ -9,9 +9,6 @@ use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; use Magento\Integration\Api\CustomerTokenServiceInterface; -use Magento\Quote\Model\QuoteFactory; -use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; -use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -25,21 +22,6 @@ class GetCartTest extends GraphQlAbstract */ private $getMaskedQuoteIdByReservedOrderId; - /** - * @var QuoteResource - */ - private $quoteResource; - - /** - * @var QuoteFactory - */ - private $quoteFactory; - - /** - * @var QuoteIdToMaskedQuoteIdInterface - */ - private $quoteIdToMaskedId; - /** * @var CustomerTokenServiceInterface */ @@ -49,19 +31,21 @@ protected function setUp() { $objectManager = Bootstrap::getObjectManager(); $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); - $this->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_items_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/Catalog/_files/product_virtual.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_virtual_product.php */ public function testGetCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_item_with_items'); - $query = $this->getCartQuery($maskedQuoteId); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId); $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); @@ -70,24 +54,23 @@ public function testGetCart() self::assertCount(2, $response['cart']['items']); self::assertNotEmpty($response['cart']['items'][0]['id']); - self::assertEquals($response['cart']['items'][0]['qty'], 2); - self::assertEquals($response['cart']['items'][0]['product']['sku'], 'simple'); + self::assertEquals(2, $response['cart']['items'][0]['qty']); + self::assertEquals('simple', $response['cart']['items'][0]['product']['sku']); self::assertNotEmpty($response['cart']['items'][1]['id']); - self::assertEquals($response['cart']['items'][1]['qty'], 1); - self::assertEquals($response['cart']['items'][1]['product']['sku'], 'simple_one'); + self::assertEquals(2, $response['cart']['items'][1]['qty']); + self::assertEquals('virtual-product', $response['cart']['items'][1]['product']['sku']); } /** + * @security * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php */ public function testGetGuestCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute( - 'test_order_with_simple_product_without_address' - ); - $query = $this->getCartQuery($maskedQuoteId); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId); $this->expectExceptionMessage( "The current user cannot perform operations on cart \"{$maskedQuoteId}\"" @@ -96,13 +79,14 @@ public function testGetGuestCart() } /** + * @security * @magentoApiDataFixture Magento/Customer/_files/three_customers.php - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php */ public function testGetAnotherCustomerCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_item_with_items'); - $query = $this->getCartQuery($maskedQuoteId); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId); $this->expectExceptionMessage( "The current user cannot perform operations on cart \"{$maskedQuoteId}\"" @@ -112,33 +96,30 @@ public function testGetAnotherCustomerCart() /** * @magentoApiDataFixture Magento/Customer/_files/customer.php + * * @expectedException \Exception * @expectedExceptionMessage Could not find a cart with ID "non_existent_masked_id" */ public function testGetNonExistentCart() { $maskedQuoteId = 'non_existent_masked_id'; - $query = $this->getCartQuery($maskedQuoteId); + $query = $this->getQuery($maskedQuoteId); $this->graphQlQuery($query, [], '', $this->getHeaderMap()); } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/make_cart_inactive.php + * * @expectedException \Exception * @expectedExceptionMessage Current user does not have an active cart. */ public function testGetInactiveCart() { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, 'test_order_with_simple_product_without_address', 'reserved_order_id'); - $quote->setCustomerId(1); - $quote->setIsActive(false); - $this->quoteResource->save($quote); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$quote->getId()); - - $query = $this->getCartQuery($maskedQuoteId); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId); $this->graphQlQuery($query, [], '', $this->getHeaderMap()); } @@ -147,12 +128,11 @@ public function testGetInactiveCart() * @param string $maskedQuoteId * @return string */ - private function getCartQuery( - string $maskedQuoteId - ) : string { + private function getQuery(string $maskedQuoteId): string + { return <<<QUERY { - cart(cart_id: "$maskedQuoteId") { + cart(cart_id: "{$maskedQuoteId}") { items { id qty 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 a601f65ef72b..0099d25ed81f 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 @@ -56,12 +56,14 @@ protected function setUp() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php */ public function testSetNewBillingAddress() { - $maskedQuoteId = $this->assignQuoteToCustomer(); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = <<<QUERY mutation { @@ -113,12 +115,14 @@ public function testSetNewBillingAddress() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php */ public function testSetNewBillingAddressWithUseForShippingParameter() { - $maskedQuoteId = $this->assignQuoteToCustomer(); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = <<<QUERY mutation { @@ -188,13 +192,15 @@ public function testSetNewBillingAddressWithUseForShippingParameter() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php */ public function testSetBillingAddressFromAddressBook() { - $maskedQuoteId = $this->assignQuoteToCustomer(); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = <<<QUERY mutation { @@ -234,14 +240,17 @@ public function testSetBillingAddressFromAddressBook() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * * @expectedException \Exception * @expectedExceptionMessage Could not find a address with ID "100" */ public function testSetNotExistedBillingAddressFromAddressBook() { - $maskedQuoteId = $this->assignQuoteToCustomer(); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = <<<QUERY mutation { @@ -265,13 +274,15 @@ public function testSetNotExistedBillingAddressFromAddressBook() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php */ public function testSetNewBillingAddressAndFromAddressBookAtSameTime() { - $maskedQuoteId = $this->assignQuoteToCustomer(); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = <<<QUERY mutation { @@ -311,15 +322,16 @@ public function testSetNewBillingAddressAndFromAddressBookAtSameTime() } /** + * @security * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Customer/_files/customer_address.php - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php */ public function testSetBillingAddressToGuestCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute( - 'test_order_with_simple_product_without_address' - ); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = <<<QUERY mutation { @@ -347,6 +359,7 @@ public function testSetBillingAddressToGuestCart() } /** + * @security * @magentoApiDataFixture Magento/Customer/_files/three_customers.php * @magentoApiDataFixture Magento/Customer/_files/customer_address.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php @@ -381,9 +394,11 @@ public function testSetBillingAddressToAnotherCustomerCart() } /** + * @security * @magentoApiDataFixture Magento/Customer/_files/three_customers.php * @magentoApiDataFixture Magento/Customer/_files/customer_address.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * * @expectedException \Exception * @expectedExceptionMessage Current customer does not have permission to address with ID "1" */ @@ -433,8 +448,11 @@ public function testSetBillingAddressOnNonExistentCart() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * * @dataProvider dataProviderSetWithoutRequiredParameters * @param string $input * @param string $message @@ -442,7 +460,7 @@ public function testSetBillingAddressOnNonExistentCart() */ public function testSetBillingAddressWithoutRequiredParameters(string $input, string $message) { - $maskedQuoteId = $this->assignQuoteToCustomer(); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $input = str_replace('cart_id_value', $maskedQuoteId, $input); $query = <<<QUERY @@ -465,6 +483,24 @@ public function testSetBillingAddressWithoutRequiredParameters(string $input, st $this->graphQlQuery($query); } + /** + * @return array + */ + public function dataProviderSetWithoutRequiredParameters(): array + { + 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' + ] + ]; + } + /** * Verify the all the whitelisted fields for a New Address Object * @@ -536,22 +572,4 @@ 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/Customer/SetPaymentMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php index 56a9592cbd4a..ffc52c753b80 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 @@ -9,10 +9,8 @@ use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\OfflinePayments\Model\Cashondelivery; use Magento\OfflinePayments\Model\Checkmo; -use Magento\Quote\Model\QuoteFactory; -use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; -use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -31,21 +29,6 @@ class SetPaymentMethodOnCartTest extends GraphQlAbstract */ private $customerTokenService; - /** - * @var QuoteResource - */ - private $quoteResource; - - /** - * @var QuoteFactory - */ - private $quoteFactory; - - /** - * @var QuoteIdToMaskedQuoteIdInterface - */ - private $quoteIdToMaskedId; - /** * @inheritdoc */ @@ -53,21 +36,22 @@ protected function setUp() { $objectManager = Bootstrap::getObjectManager(); $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); - $this->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_virtual_product_and_address.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php */ - public function testSetPaymentWithVirtualProduct() + public function testSetPaymentOnCartWithSimpleProduct() { $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_with_virtual_product'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); - $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); + $query = $this->getQuery($maskedQuoteId, $methodCode); $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('setPaymentMethodOnCart', $response); @@ -77,14 +61,35 @@ public function testSetPaymentWithVirtualProduct() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * + * @expectedException \Exception + * @expectedExceptionMessage The shipping address is missing. Set the address and try again. + */ + public function testSetPaymentOnCartWithSimpleProductAndWithoutAddress() + { + $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + + $query = $this->getQuery($maskedQuoteId, $methodCode); + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Catalog/_files/product_virtual.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_virtual_product.php */ - public function testSetPaymentWithSimpleProduct() + public function testSetPaymentOnCartWithVirtualProduct() { $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); - $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); + $query = $this->getQuery($maskedQuoteId, $methodCode); $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('setPaymentMethodOnCart', $response); @@ -95,96 +100,93 @@ public function testSetPaymentWithSimpleProduct() /** * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + * * @expectedException \Exception - * @expectedExceptionMessage The shipping address is missing. Set the address and try again. + * @expectedExceptionMessage The requested Payment Method is not available. */ - public function testSetPaymentWithSimpleProductWithoutAddress() + public function testSetNonExistentPaymentMethod() { - $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $maskedQuoteId = $this->assignQuoteToCustomer('test_order_with_simple_product_without_address', 1); + $methodCode = 'noway'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); - $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); + $query = $this->getQuery($maskedQuoteId, $methodCode); $this->graphQlQuery($query, [], '', $this->getHeaderMap()); } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * * @expectedException \Exception - * @expectedExceptionMessage The requested Payment Method is not available. + * @expectedExceptionMessage Could not find a cart with ID "non_existent_masked_id" */ - public function testSetNonExistingPaymentMethod() + public function testSetPaymentOnNonExistentCart() { - $methodCode = 'noway'; - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); - - $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); + $maskedQuoteId = 'non_existent_masked_id'; + $query = <<<QUERY +{ + cart(cart_id: "$maskedQuoteId") { + items { + id + } + } +} +QUERY; $this->graphQlQuery($query, [], '', $this->getHeaderMap()); } /** + * @security * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php */ public function testSetPaymentMethodToGuestCart() { $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute( - 'test_order_with_simple_product_without_address' - ); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); - $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); + $query = $this->getQuery($maskedQuoteId, $methodCode); $this->expectExceptionMessage( "The current user cannot perform operations on cart \"$maskedQuoteId\"" ); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); } /** + * @security * @magentoApiDataFixture Magento/Customer/_files/three_customers.php - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php */ public function testSetPaymentMethodToAnotherCustomerCart() { $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); - $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); + $query = $this->getQuery($maskedQuoteId, $methodCode); $this->expectExceptionMessage( "The current user cannot perform operations on cart \"$maskedQuoteId\"" ); - $this->graphQlQuery($query, [], '', $this->getHeaderMap('customer2@search.example.com')); } /** * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @expectedException \Exception - * @expectedExceptionMessage Could not find a cart with ID "non_existent_masked_id" - */ - public function testPaymentMethodOnNonExistentCart() - { - $maskedQuoteId = 'non_existent_masked_id'; - $query = <<<QUERY -{ - cart(cart_id: "$maskedQuoteId") { - items { - id - } - } -} -QUERY; - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); - } - - /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * * @param string $input * @param string $message + * @throws \Exception * @dataProvider dataProviderSetPaymentMethodWithoutRequiredParameters */ public function testSetPaymentMethodWithoutRequiredParameters(string $input, string $message) @@ -207,6 +209,7 @@ public function testSetPaymentMethodWithoutRequiredParameters(string $input, str $this->expectExceptionMessage($message); $this->graphQlQuery($query, [], '', $this->getHeaderMap()); } + /** * @return array */ @@ -229,19 +232,26 @@ public function dataProviderSetPaymentMethodWithoutRequiredParameters(): array } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_payment_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/enable_offline_payment_methods.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_checkmo_payment_method.php */ public function testReSetPayment() { - /** @var \Magento\Quote\Model\Quote $quote */ - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1_with_payment'); - $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + + $methodCode = Cashondelivery::PAYMENT_METHOD_CASHONDELIVERY_CODE; + $query = $this->getQuery($maskedQuoteId, $methodCode); $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('setPaymentMethodOnCart', $response); self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); self::assertArrayHasKey('selected_payment_method', $response['setPaymentMethodOnCart']['cart']); + self::assertArrayHasKey('code', $response['setPaymentMethodOnCart']['cart']['selected_payment_method']); self::assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']); } @@ -250,7 +260,7 @@ public function testReSetPayment() * @param string $methodCode * @return string */ - private function prepareMutationQuery( + private function getQuery( string $maskedQuoteId, string $methodCode ) : string { @@ -272,22 +282,6 @@ private function prepareMutationQuery( QUERY; } - /** - * @param string $reversedOrderId - * @param int $customerId - * @return string - */ - private function assignQuoteToCustomer( - string $reversedOrderId, - int $customerId - ): string { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedOrderId, 'reserved_order_id'); - $quote->setCustomerId($customerId); - $this->quoteResource->save($quote); - return $this->quoteIdToMaskedId->execute((int)$quote->getId()); - } - /** * @param string $username * @param string $password diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php index 4d4c61b3b4e5..efd86ca5e56c 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php @@ -7,6 +7,7 @@ namespace Magento\GraphQl\Quote\Customer; +use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; use Magento\Integration\Api\CustomerTokenServiceInterface; use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; @@ -34,6 +35,11 @@ class SetShippingAddressOnCartTest extends GraphQlAbstract */ private $quoteIdToMaskedId; + /** + * @var GetMaskedQuoteIdByReservedOrderId + */ + private $getMaskedQuoteIdByReservedOrderId; + /** * @var CustomerTokenServiceInterface */ @@ -45,16 +51,19 @@ protected function setUp() $this->quoteResource = $objectManager->get(QuoteResource::class); $this->quoteFactory = $objectManager->get(QuoteFactory::class); $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); + $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); } /** * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php */ - public function testSetNewShippingAddress() + public function testSetNewShippingAddressOnCartWithSimpleProduct() { - $maskedQuoteId = $this->assignQuoteToCustomer(); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = <<<QUERY mutation { @@ -108,14 +117,17 @@ public function testSetNewShippingAddress() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Catalog/_files/product_virtual.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_virtual_product.php + * * @expectedException \Exception * @expectedExceptionMessage The Cart includes virtual product(s) only, so a shipping address is not used. */ - public function testSetNewShippingAddressOnQuoteWithVirtualProducts() + public function testSetNewShippingAddressOnCartWithVirtualProduct() { - $maskedQuoteId = $this->assignQuoteToCustomer('test_order_with_virtual_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = <<<QUERY mutation { @@ -152,13 +164,15 @@ public function testSetNewShippingAddressOnQuoteWithVirtualProducts() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php */ public function testSetShippingAddressFromAddressBook() { - $maskedQuoteId = $this->assignQuoteToCustomer(); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = <<<QUERY mutation { @@ -196,14 +210,17 @@ public function testSetShippingAddressFromAddressBook() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * * @expectedException \Exception * @expectedExceptionMessage Could not find a address with ID "100" */ public function testSetNonExistentShippingAddressFromAddressBook() { - $maskedQuoteId = $this->assignQuoteToCustomer(); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = <<<QUERY mutation { @@ -229,13 +246,15 @@ public function testSetNonExistentShippingAddressFromAddressBook() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php */ public function testSetNewShippingAddressAndFromAddressBookAtSameTime() { - $maskedQuoteId = $this->assignQuoteToCustomer(); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = <<<QUERY mutation { @@ -276,9 +295,11 @@ public function testSetNewShippingAddressAndFromAddressBookAtSameTime() } /** + * @security * @magentoApiDataFixture Magento/Customer/_files/three_customers.php * @magentoApiDataFixture Magento/Customer/_files/customer_address.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * * @expectedException \Exception * @expectedExceptionMessage Current customer does not have permission to address with ID "1" */ @@ -311,9 +332,11 @@ public function testSetShippingAddressIfCustomerIsNotOwnerOfAddress() } /** + * @security * @magentoApiDataFixture Magento/Customer/_files/three_customers.php * @magentoApiDataFixture Magento/Customer/_files/customer_address.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * * @expectedException \Exception */ public function testSetShippingAddressToAnotherCustomerCart() @@ -343,13 +366,15 @@ public function testSetShippingAddressToAnotherCustomerCart() $this->expectExceptionMessage( "The current user cannot perform operations on cart \"$maskedQuoteId\"" ); - $this->graphQlQuery($query, [], '', $this->getHeaderMap('customer2@search.example.com')); } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * * @dataProvider dataProviderUpdateWithMissedRequiredParameters * @param string $input * @param string $message @@ -357,7 +382,7 @@ public function testSetShippingAddressToAnotherCustomerCart() */ public function testSetNewShippingAddressWithMissedRequiredParameters(string $input, string $message) { - $maskedQuoteId = $this->assignQuoteToCustomer(); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = <<<QUERY mutation { @@ -386,7 +411,7 @@ public function testSetNewShippingAddressWithMissedRequiredParameters(string $in /** * @return array */ - public function dataProviderUpdateWithMissedRequiredParameters() + public function dataProviderUpdateWithMissedRequiredParameters(): array { return [ 'shipping_addresses' => [ @@ -402,13 +427,16 @@ public function dataProviderUpdateWithMissedRequiredParameters() /** * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * * @expectedException \Exception * @expectedExceptionMessage You cannot specify multiple shipping addresses. */ public function testSetMultipleNewShippingAddresses() { - $maskedQuoteId = $this->assignQuoteToCustomer(); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = <<<QUERY mutation { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php index 7da7480ca390..8989b1b2c074 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php @@ -31,39 +31,36 @@ protected function setUp() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php */ - public function testGetCartWithPaymentMethods() + public function testGetAvailablePaymentMethods() { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute( - 'test_order_with_simple_product_without_address' - ); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = $this->getQuery($maskedQuoteId); $response = $this->graphQlQuery($query); self::assertArrayHasKey('cart', $response); + self::assertArrayHasKey('available_payment_methods', $response['cart']); + self::assertCount(1, $response['cart']['available_payment_methods']); self::assertEquals('checkmo', $response['cart']['available_payment_methods'][0]['code']); self::assertEquals('Check / Money order', $response['cart']['available_payment_methods'][0]['title']); - - self::assertEquals('free', $response['cart']['available_payment_methods'][1]['code']); - self::assertEquals( - 'No Payment Information Required', - $response['cart']['available_payment_methods'][1]['title'] - ); - self::assertGreaterThan( - 0, - count($response['cart']['available_payment_methods']), - 'There are no available payment methods for guest cart!' - ); } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @security + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php */ - public function testGetPaymentMethodsFromCustomerCart() + public function testGetAvailablePaymentMethodsFromCustomerCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = $this->getQuery($maskedQuoteId); $this->expectExceptionMessage( @@ -73,25 +70,28 @@ public function testGetPaymentMethodsFromCustomerCart() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php - * @magentoApiDataFixture Magento/Payment/_files/disable_all_active_payment_methods.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/disable_all_active_payment_methods.php */ - public function testGetPaymentMethodsIfPaymentsAreNotSet() + public function testGetAvailablePaymentMethodsIfPaymentsAreNotPresent() { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute( - 'test_order_with_simple_product_without_address' - ); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = $this->getQuery($maskedQuoteId); $response = $this->graphQlQuery($query); - self::assertEquals(0, count($response['cart']['available_payment_methods'])); + self::assertArrayHasKey('cart', $response); + self::assertArrayHasKey('available_payment_methods', $response['cart']); + self::assertEmpty($response['cart']['available_payment_methods']); } /** * @expectedException \Exception * @expectedExceptionMessage Could not find a cart with ID "non_existent_masked_id" */ - public function testGetPaymentMethodsOfNonExistentCart() + public function testGetAvailablePaymentMethodsOfNonExistentCart() { $maskedQuoteId = 'non_existent_masked_id'; $query = $this->getQuery($maskedQuoteId); @@ -102,9 +102,8 @@ public function testGetPaymentMethodsOfNonExistentCart() * @param string $maskedQuoteId * @return string */ - private function getQuery( - string $maskedQuoteId - ): string { + private function getQuery(string $maskedQuoteId): string + { return <<<QUERY { cart(cart_id: "$maskedQuoteId") { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php index 847a7129750f..a7af18a5674b 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php @@ -8,10 +8,6 @@ namespace Magento\GraphQl\Quote\Guest; use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; -use Magento\Integration\Api\CustomerTokenServiceInterface; -use Magento\Quote\Model\QuoteFactory; -use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; -use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -25,43 +21,23 @@ class GetCartTest extends GraphQlAbstract */ private $getMaskedQuoteIdByReservedOrderId; - /** - * @var QuoteResource - */ - private $quoteResource; - - /** - * @var QuoteFactory - */ - private $quoteFactory; - - /** - * @var QuoteIdToMaskedQuoteIdInterface - */ - private $quoteIdToMaskedId; - - /** - * @var CustomerTokenServiceInterface - */ - private $customerTokenService; - protected function setUp() { $objectManager = Bootstrap::getObjectManager(); $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); - $this->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_items_saved.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/Catalog/_files/product_virtual.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_virtual_product.php */ public function testGetCart() { - $maskedQuoteId = $this->unAssignCustomerFromQuote('test_order_item_with_items'); - $query = $this->getCartQuery($maskedQuoteId); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId); $response = $this->graphQlQuery($query); @@ -70,21 +46,23 @@ public function testGetCart() self::assertCount(2, $response['cart']['items']); self::assertNotEmpty($response['cart']['items'][0]['id']); - self::assertEquals($response['cart']['items'][0]['qty'], 2); - self::assertEquals($response['cart']['items'][0]['product']['sku'], 'simple'); + self::assertEquals(2, $response['cart']['items'][0]['qty']); + self::assertEquals('simple', $response['cart']['items'][0]['product']['sku']); self::assertNotEmpty($response['cart']['items'][1]['id']); - self::assertEquals($response['cart']['items'][1]['qty'], 1); - self::assertEquals($response['cart']['items'][1]['product']['sku'], 'simple_one'); + self::assertEquals(2, $response['cart']['items'][1]['qty']); + self::assertEquals('virtual-product', $response['cart']['items'][1]['product']['sku']); } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php + * @security + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php */ public function testGetCustomerCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_item_with_items'); - $query = $this->getCartQuery($maskedQuoteId); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId); $this->expectExceptionMessage( "The current user cannot perform operations on cart \"{$maskedQuoteId}\"" @@ -99,25 +77,22 @@ public function testGetCustomerCart() public function testGetNonExistentCart() { $maskedQuoteId = 'non_existent_masked_id'; - $query = $this->getCartQuery($maskedQuoteId); + $query = $this->getQuery($maskedQuoteId); $this->graphQlQuery($query); } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/make_cart_inactive.php + * * @expectedException \Exception * @expectedExceptionMessage Current user does not have an active cart. */ public function testGetInactiveCart() { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, 'test_order_with_simple_product_without_address', 'reserved_order_id'); - $quote->setIsActive(false); - $this->quoteResource->save($quote); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$quote->getId()); - - $query = $this->getCartQuery($maskedQuoteId); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId); $this->graphQlQuery($query); } @@ -126,12 +101,11 @@ public function testGetInactiveCart() * @param string $maskedQuoteId * @return string */ - private function getCartQuery( - string $maskedQuoteId - ) : string { + private function getQuery(string $maskedQuoteId): string + { return <<<QUERY { - cart(cart_id: "$maskedQuoteId") { + cart(cart_id: "{$maskedQuoteId}") { items { id qty @@ -143,19 +117,4 @@ private function getCartQuery( } QUERY; } - - /** - * @param string $reversedOrderId - * @param int $customerId - * @return string - */ - private function unAssignCustomerFromQuote( - string $reversedOrderId - ): string { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedOrderId, 'reserved_order_id'); - $quote->setCustomerId(0); - $this->quoteResource->save($quote); - return $this->quoteIdToMaskedId->execute((int)$quote->getId()); - } } 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 a2f092b9e0e1..f6c38ef43554 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 @@ -7,9 +7,7 @@ namespace Magento\GraphQl\Quote\Guest; -use Magento\Quote\Model\QuoteFactory; -use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; -use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -19,34 +17,24 @@ class SetBillingAddressOnCartTest extends GraphQlAbstract { /** - * @var QuoteResource + * @var GetMaskedQuoteIdByReservedOrderId */ - private $quoteResource; - - /** - * @var QuoteFactory - */ - private $quoteFactory; - - /** - * @var QuoteIdToMaskedQuoteIdInterface - */ - private $quoteIdToMaskedId; + private $getMaskedQuoteIdByReservedOrderId; protected function setUp() { $objectManager = Bootstrap::getObjectManager(); - $this->quoteResource = $objectManager->get(QuoteResource::class); - $this->quoteFactory = $objectManager->get(QuoteFactory::class); - $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); + $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php */ public function testSetNewBillingAddress() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = <<<QUERY mutation { @@ -98,11 +86,13 @@ public function testSetNewBillingAddress() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php */ public function testSetNewBillingAddressWithUseForShippingParameter() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = <<<QUERY mutation { @@ -172,11 +162,13 @@ public function testSetNewBillingAddressWithUseForShippingParameter() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @security + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php */ public function testSetBillingAddressToCustomerCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = <<<QUERY mutation { @@ -215,13 +207,17 @@ public function testSetBillingAddressToCustomerCart() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @security + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * * @expectedException \Exception * @expectedExceptionMessage The current customer isn't authorized. */ public function testSetBillingAddressFromAddressBook() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = <<<QUERY mutation { @@ -264,7 +260,10 @@ public function testSetBillingAddressOnNonExistentCart() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * * @dataProvider dataProviderSetWithoutRequiredParameters * @param string $input * @param string $message @@ -272,7 +271,7 @@ public function testSetBillingAddressOnNonExistentCart() */ public function testSetBillingAddressWithoutRequiredParameters(string $input, string $message) { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $input = str_replace('cart_id_value', $maskedQuoteId, $input); $query = <<<QUERY @@ -295,6 +294,24 @@ public function testSetBillingAddressWithoutRequiredParameters(string $input, st $this->graphQlQuery($query); } + /** + * @return array + */ + public function dataProviderSetWithoutRequiredParameters(): array + { + 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' + ] + ]; + } + /** * Verify the all the whitelisted fields for a New Address Object * @@ -317,34 +334,4 @@ private function assertNewAddressFields(array $addressResponse, string $addressT $this->assertResponseFields($addressResponse, $assertionMap); } - - /** - * @param string $reversedQuoteId - * @return string - */ - private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string - { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); - - 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/SetPaymentMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php index a347c99a9220..b2a8f5078c5e 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 @@ -8,10 +8,8 @@ namespace Magento\GraphQl\Quote\Guest; use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; +use Magento\OfflinePayments\Model\Cashondelivery; use Magento\OfflinePayments\Model\Checkmo; -use Magento\Quote\Model\QuoteFactory; -use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; -use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -25,21 +23,6 @@ class SetPaymentMethodOnCartTest extends GraphQlAbstract */ private $getMaskedQuoteIdByReservedOrderId; - /** - * @var QuoteResource - */ - private $quoteResource; - - /** - * @var QuoteFactory - */ - private $quoteFactory; - - /** - * @var QuoteIdToMaskedQuoteIdInterface - */ - private $quoteIdToMaskedId; - /** * @inheritdoc */ @@ -47,21 +30,20 @@ protected function setUp() { $objectManager = Bootstrap::getObjectManager(); $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); - $this->quoteResource = $objectManager->get(QuoteResource::class); - $this->quoteFactory = $objectManager->get(QuoteFactory::class); - $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_and_address.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php */ - public function testSetPaymentWithVirtualProduct() + public function testSetPaymentOnCartWithSimpleProduct() { $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_with_virtual_product'); - $this->unAssignCustomerFromQuote('test_order_with_virtual_product'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); - $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); + $query = $this->getQuery($maskedQuoteId, $methodCode); $response = $this->graphQlQuery($query); self::assertArrayHasKey('setPaymentMethodOnCart', $response); @@ -71,15 +53,33 @@ public function testSetPaymentWithVirtualProduct() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * + * @expectedException \Exception + * @expectedExceptionMessage The shipping address is missing. Set the address and try again. + */ + public function testSetPaymentOnCartWithSimpleProductAndWithoutAddress() + { + $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + + $query = $this->getQuery($maskedQuoteId, $methodCode); + $this->graphQlQuery($query); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/product_virtual.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_virtual_product.php */ - public function testSetPaymentWithSimpleProduct() + public function testSetPaymentOnCartWithVirtualProduct() { $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); - $this->unAssignCustomerFromQuote('test_order_1'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); - $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); + $query = $this->getQuery($maskedQuoteId, $methodCode); $response = $this->graphQlQuery($query); self::assertArrayHasKey('setPaymentMethodOnCart', $response); @@ -89,45 +89,56 @@ public function testSetPaymentWithSimpleProduct() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + * * @expectedException \Exception - * @expectedExceptionMessage The shipping address is missing. Set the address and try again. + * @expectedExceptionMessage The requested Payment Method is not available. */ - public function testSetPaymentWithSimpleProductWithoutAddress() + public function testSetNonExistentPaymentMethod() { - $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute( - 'test_order_with_simple_product_without_address' - ); + $methodCode = 'noway'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); - $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); + $query = $this->getQuery($maskedQuoteId, $methodCode); $this->graphQlQuery($query); } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php * @expectedException \Exception - * @expectedExceptionMessage The requested Payment Method is not available. + * @expectedExceptionMessage Could not find a cart with ID "non_existent_masked_id" */ - public function testSetNonExistingPaymentMethod() + public function testSetPaymentOnNonExistentCart() { - $methodCode = 'noway'; - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); - $this->unAssignCustomerFromQuote('test_order_1'); - - $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); + $maskedQuoteId = 'non_existent_masked_id'; + $query = <<<QUERY +{ + cart(cart_id: "$maskedQuoteId") { + items { + id + } + } +} +QUERY; $this->graphQlQuery($query); } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @security + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php */ public function testSetPaymentMethodToCustomerCart() { $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); - $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); + $query = $this->getQuery($maskedQuoteId, $methodCode); $this->expectExceptionMessage( "The current user cannot perform operations on cart \"$maskedQuoteId\"" @@ -136,10 +147,15 @@ public function testSetPaymentMethodToCustomerCart() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + * * @param string $input * @param string $message * @dataProvider dataProviderSetPaymentMethodWithoutRequiredParameters + * @throws \Exception */ public function testSetPaymentMethodWithoutRequiredParameters(string $input, string $message) { @@ -161,6 +177,7 @@ public function testSetPaymentMethodWithoutRequiredParameters(string $input, str $this->expectExceptionMessage($message); $this->graphQlQuery($query); } + /** * @return array */ @@ -183,39 +200,25 @@ public function dataProviderSetPaymentMethodWithoutRequiredParameters(): array } /** - * @expectedException \Exception - * @expectedExceptionMessage Could not find a cart with ID "non_existent_masked_id" - */ - public function testSetPaymentOnNonExistentCart() - { - $maskedQuoteId = 'non_existent_masked_id'; - $query = <<<QUERY -{ - cart(cart_id: "$maskedQuoteId") { - items { - id - } - } -} -QUERY; - $this->graphQlQuery($query); - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_payment_saved.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/enable_offline_payment_methods.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_checkmo_payment_method.php */ public function testReSetPayment() { - /** @var \Magento\Quote\Model\Quote $quote */ - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1_with_payment'); - $this->unAssignCustomerFromQuote('test_order_1_with_payment'); - $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; - $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + + $methodCode = Cashondelivery::PAYMENT_METHOD_CASHONDELIVERY_CODE; + $query = $this->getQuery($maskedQuoteId, $methodCode); $response = $this->graphQlQuery($query); self::assertArrayHasKey('setPaymentMethodOnCart', $response); self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); self::assertArrayHasKey('selected_payment_method', $response['setPaymentMethodOnCart']['cart']); + self::assertArrayHasKey('code', $response['setPaymentMethodOnCart']['cart']['selected_payment_method']); self::assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']); } @@ -224,20 +227,18 @@ public function testReSetPayment() * @param string $methodCode * @return string */ - private function prepareMutationQuery( + private function getQuery( string $maskedQuoteId, string $methodCode ) : string { return <<<QUERY mutation { - setPaymentMethodOnCart(input: - { - cart_id: "{$maskedQuoteId}", - payment_method: { - code: "{$methodCode}" - } - }) { - + setPaymentMethodOnCart(input: { + cart_id: "{$maskedQuoteId}", + payment_method: { + code: "{$methodCode}" + } + }) { cart { selected_payment_method { code @@ -247,19 +248,4 @@ private function prepareMutationQuery( } QUERY; } - - /** - * @param string $reversedOrderId - * @param int $customerId - * @return string - */ - private function unAssignCustomerFromQuote( - string $reversedOrderId - ): string { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedOrderId, 'reserved_order_id'); - $quote->setCustomerId(0); - $this->quoteResource->save($quote); - return $this->quoteIdToMaskedId->execute((int)$quote->getId()); - } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php index 5c6e0e9cec55..b6cea68db9b8 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php @@ -7,15 +7,9 @@ namespace Magento\GraphQl\Quote\Guest; -use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; -use Magento\Multishipping\Helper\Data; -use Magento\Quote\Model\QuoteFactory; -use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; -use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; -use Magento\TestFramework\ObjectManager; /** * Test for set shipping addresses on cart mutation @@ -27,38 +21,20 @@ class SetShippingAddressOnCartTest extends GraphQlAbstract */ private $getMaskedQuoteIdByReservedOrderId; - /** - * @var QuoteResource - */ - private $quoteResource; - - /** - * @var QuoteFactory - */ - private $quoteFactory; - - /** - * @var QuoteIdToMaskedQuoteIdInterface - */ - private $quoteIdToMaskedId; - protected function setUp() { $objectManager = Bootstrap::getObjectManager(); $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); - $this->quoteResource = $objectManager->get(QuoteResource::class); - $this->quoteFactory = $objectManager->get(QuoteFactory::class); - $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php */ - public function testSetNewShippingAddress() + public function testSetNewShippingAddressOnCartWithSimpleProduct() { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute( - 'test_order_with_simple_product_without_address' - ); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = <<<QUERY mutation { @@ -112,15 +88,16 @@ public function testSetNewShippingAddress() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php + * @magentoApiDataFixture Magento/Catalog/_files/product_virtual.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_virtual_product.php + * * @expectedException \Exception * @expectedExceptionMessage The Cart includes virtual product(s) only, so a shipping address is not used. */ - public function testSetNewShippingAddressOnQuoteWithVirtualProducts() + public function testSetNewShippingAddressOnCartWithVirtualProduct() { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute( - 'test_order_with_virtual_product_without_address' - ); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = <<<QUERY mutation { @@ -157,15 +134,17 @@ public function testSetNewShippingAddressOnQuoteWithVirtualProducts() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @security + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * * @expectedException \Exception * @expectedExceptionMessage The current customer isn't authorized. */ public function testSetShippingAddressFromAddressBook() { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute( - 'test_order_with_simple_product_without_address' - ); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = <<<QUERY mutation { @@ -191,13 +170,17 @@ public function testSetShippingAddressFromAddressBook() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @security * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * * @expectedException \Exception */ public function testSetShippingAddressToCustomerCart() { - $maskedQuoteId = $this->assignQuoteToCustomer('test_order_with_simple_product_without_address', 1); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = <<<QUERY mutation { @@ -222,12 +205,14 @@ public function testSetShippingAddressToCustomerCart() $this->expectExceptionMessage( "The current user cannot perform operations on cart \"$maskedQuoteId\"" ); - $this->graphQlQuery($query); } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * * @dataProvider dataProviderUpdateWithMissedRequiredParameters * @param string $input * @param string $message @@ -235,9 +220,7 @@ public function testSetShippingAddressToCustomerCart() */ public function testSetNewShippingAddressWithMissedRequiredParameters(string $input, string $message) { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute( - 'test_order_with_simple_product_without_address' - ); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = <<<QUERY mutation { @@ -266,7 +249,7 @@ public function testSetNewShippingAddressWithMissedRequiredParameters(string $in /** * @return array */ - public function dataProviderUpdateWithMissedRequiredParameters() + public function dataProviderUpdateWithMissedRequiredParameters(): array { return [ 'shipping_addresses' => [ @@ -281,15 +264,16 @@ public function dataProviderUpdateWithMissedRequiredParameters() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * * @expectedException \Exception * @expectedExceptionMessage You cannot specify multiple shipping addresses. */ public function testSetMultipleNewShippingAddresses() { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute( - 'test_order_with_simple_product_without_address' - ); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = <<<QUERY mutation { @@ -360,20 +344,4 @@ private function assertNewShippingAddressFields(array $shippingAddressResponse): $this->assertResponseFields($shippingAddressResponse, $assertionMap); } - - /** - * @param string $reversedOrderId - * @param int $customerId - * @return string - */ - private function assignQuoteToCustomer( - string $reversedOrderId = 'test_order_with_simple_product_without_address', - int $customerId = 1 - ): string { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedOrderId, 'reserved_order_id'); - $quote->setCustomerId($customerId); - $this->quoteResource->save($quote); - return $this->quoteIdToMaskedId->execute((int)$quote->getId()); - } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetOfflineShippingMethodsOnCartTest.php similarity index 96% rename from dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php rename to dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetOfflineShippingMethodsOnCartTest.php index 80acbbdb6423..cf32003340a6 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetOfflineShippingMethodsOnCartTest.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\GraphQl\OfflineShipping; +namespace Magento\GraphQl\Quote; use Magento\Integration\Api\CustomerTokenServiceInterface; use Magento\Quote\Model\QuoteFactory; @@ -53,7 +53,7 @@ protected function setUp() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - * @magentoApiDataFixture Magento/OfflineShipping/_files/enable_offline_shipping_methods.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/enable_offline_shipping_methods.php * @magentoApiDataFixture Magento/OfflineShipping/_files/tablerates_weight.php * * @param string $carrierCode @@ -103,7 +103,7 @@ public function offlineShippingMethodDataProvider() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - * @magentoApiDataFixture Magento/OfflineShipping/_files/enable_offline_shipping_methods.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/enable_offline_shipping_methods.php */ public function testSetShippingMethodTwiceInOneRequest() { diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/add_simple_product.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/add_simple_product.php new file mode 100644 index 000000000000..d23381e33d43 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/add_simple_product.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); +/** @var QuoteFactory $quoteFactory */ +$quoteFactory = Bootstrap::getObjectManager()->get(QuoteFactory::class); +/** @var QuoteResource $quoteResource */ +$quoteResource = Bootstrap::getObjectManager()->get(QuoteResource::class); +/** @var CartRepositoryInterface $cartRepository */ +$cartRepository = Bootstrap::getObjectManager()->get(CartRepositoryInterface::class); + +$product = $productRepository->get('simple'); + +$quote = $quoteFactory->create(); +$quoteResource->load($quote, 'test_quote', 'reserved_order_id'); +$quote->addProduct($product, 2); +$cartRepository->save($quote); diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/add_virtual_product.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/add_virtual_product.php new file mode 100644 index 000000000000..f597d0bffa7c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/add_virtual_product.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); +/** @var QuoteFactory $quoteFactory */ +$quoteFactory = Bootstrap::getObjectManager()->get(QuoteFactory::class); +/** @var QuoteResource $quoteResource */ +$quoteResource = Bootstrap::getObjectManager()->get(QuoteResource::class); +/** @var CartRepositoryInterface $cartRepository */ +$cartRepository = Bootstrap::getObjectManager()->get(CartRepositoryInterface::class); + +$product = $productRepository->get('virtual-product'); + +$quote = $quoteFactory->create(); +$quoteResource->load($quote, 'test_quote', 'reserved_order_id'); +$quote->addProduct($product, 2); +$cartRepository->save($quote); diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/customer/create_empty_cart.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/customer/create_empty_cart.php new file mode 100644 index 000000000000..86174a7753f4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/customer/create_empty_cart.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Quote\Api\CartManagementInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Model\QuoteIdMask; +use Magento\Quote\Model\QuoteIdMaskFactory; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var CartManagementInterface $cartManagement */ +$cartManagement = Bootstrap::getObjectManager()->get(CartManagementInterface::class); +/** @var CartRepositoryInterface $cartRepository */ +$cartRepository = Bootstrap::getObjectManager()->get(CartRepositoryInterface::class); +/** @var QuoteIdMaskFactory $quoteIdMaskFactory */ +$quoteIdMaskFactory = Bootstrap::getObjectManager()->get(QuoteIdMaskFactory::class); + +$cartId = $cartManagement->createEmptyCartForCustomer(1); +$cart = $cartRepository->get($cartId); +$cart->setReservedOrderId('test_quote'); +$cartRepository->save($cart); + +/** @var QuoteIdMask $quoteIdMask */ +$quoteIdMask = $quoteIdMaskFactory->create(); +$quoteIdMask->setQuoteId($cartId) + ->save(); diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/customer/create_empty_cart_rollback.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/customer/create_empty_cart_rollback.php new file mode 100644 index 000000000000..0d5d5f067e35 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/customer/create_empty_cart_rollback.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\Quote\Model\QuoteIdMask; +use Magento\Quote\Model\QuoteIdMaskFactory; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var QuoteFactory $quoteFactory */ +$quoteFactory = Bootstrap::getObjectManager()->get(QuoteFactory::class); +/** @var QuoteResource $quoteResource */ +$quoteResource = Bootstrap::getObjectManager()->get(QuoteResource::class); +/** @var QuoteIdMaskFactory $quoteIdMaskFactory */ +$quoteIdMaskFactory = Bootstrap::getObjectManager()->get(QuoteIdMaskFactory::class); + +$quote = $quoteFactory->create(); +$quoteResource->load($quote, 'test_quote', 'reserved_order_id'); +$quoteResource->delete($quote); + +/** @var QuoteIdMask $quoteIdMask */ +$quoteIdMask = $quoteIdMaskFactory->create(); +$quoteIdMask->setQuoteId($quote->getId()) + ->delete(); diff --git a/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/disable_all_active_payment_methods.php similarity index 93% rename from dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods.php rename to dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/disable_all_active_payment_methods.php index ac811e67c580..8e6d4b8f74b8 100644 --- a/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods.php +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/disable_all_active_payment_methods.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +// TODO: Should be removed in scope of https://github.com/magento/graphql-ce/issues/167 declare(strict_types=1); use Magento\Config\Model\Config; diff --git a/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods_rollback.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/disable_all_active_payment_methods_rollback.php similarity index 92% rename from dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods_rollback.php rename to dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/disable_all_active_payment_methods_rollback.php index 4a56888058e4..092826d1fd3f 100644 --- a/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods_rollback.php +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/disable_all_active_payment_methods_rollback.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +// TODO: Should be removed in scope of https://github.com/magento/graphql-ce/issues/167 declare(strict_types=1); use Magento\Framework\App\Config\ScopeConfigInterface; diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods.php new file mode 100644 index 000000000000..9c15589ba82e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +// TODO: Should be removed in scope of https://github.com/magento/graphql-ce/issues/167 +declare(strict_types=1); + +use Magento\Framework\App\Config\Storage\Writer; +use Magento\Framework\App\Config\Storage\WriterInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\App\Config\ScopeConfigInterface; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Writer $configWriter */ +$configWriter = $objectManager->get(WriterInterface::class); + +$configWriter->save('payment/banktransfer/active', 1); +$configWriter->save('payment/cashondelivery/active', 1); +$configWriter->save('payment/checkmo/active', 1); +$configWriter->save('payment/purchaseorder/active', 1); + +$scopeConfig = $objectManager->get(ScopeConfigInterface::class); +$scopeConfig->clean(); diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods_rollback.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods_rollback.php new file mode 100644 index 000000000000..61b7ed9737ff --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods_rollback.php @@ -0,0 +1,20 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +// TODO: Should be removed in scope of https://github.com/magento/graphql-ce/issues/167 +declare(strict_types=1); + +use Magento\Framework\App\Config\Storage\Writer; +use Magento\Framework\App\Config\Storage\WriterInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Writer $configWriter */ +$configWriter = $objectManager->create(WriterInterface::class); + +$configWriter->delete('payment/banktransfer/active'); +$configWriter->delete('payment/cashondelivery/active'); +$configWriter->delete('payment/checkmo/active'); +$configWriter->delete('payment/purchaseorder/active'); diff --git a/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/enable_offline_shipping_methods.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_shipping_methods.php similarity index 89% rename from dev/tests/integration/testsuite/Magento/OfflineShipping/_files/enable_offline_shipping_methods.php rename to dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_shipping_methods.php index 08694bf8c7d0..ebc41da9b1b3 100644 --- a/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/enable_offline_shipping_methods.php +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_shipping_methods.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +// TODO: Should be removed in scope of https://github.com/magento/graphql-ce/issues/167 declare(strict_types=1); use Magento\Framework\App\Config\Storage\Writer; diff --git a/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/enable_offline_shipping_methods_rollback.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_shipping_methods_rollback.php similarity index 86% rename from dev/tests/integration/testsuite/Magento/OfflineShipping/_files/enable_offline_shipping_methods_rollback.php rename to dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_shipping_methods_rollback.php index 31a16f42adfc..384ffbdf51f3 100644 --- a/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/enable_offline_shipping_methods_rollback.php +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_shipping_methods_rollback.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +// TODO: Should be removed in scope of https://github.com/magento/graphql-ce/issues/167 declare(strict_types=1); use Magento\Framework\App\Config\Storage\Writer; diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/guest/create_empty_cart.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/guest/create_empty_cart.php new file mode 100644 index 000000000000..6a9ed898c316 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/guest/create_empty_cart.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\GuestCartManagementInterface; +use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var GuestCartManagementInterface $guestCartManagement */ +$guestCartManagement = Bootstrap::getObjectManager()->get(GuestCartManagementInterface::class); +/** @var CartRepositoryInterface $cartRepository */ +$cartRepository = Bootstrap::getObjectManager()->get(CartRepositoryInterface::class); +/** @var MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId */ +$maskedQuoteIdToQuoteId = Bootstrap::getObjectManager()->get(MaskedQuoteIdToQuoteIdInterface::class); + +$cartHash = $guestCartManagement->createEmptyCart(); +$cartId = $maskedQuoteIdToQuoteId->execute($cartHash); +$cart = $cartRepository->get($cartId); +$cart->setReservedOrderId('test_quote'); +$cartRepository->save($cart); diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/guest/create_empty_cart_rollback.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/guest/create_empty_cart_rollback.php new file mode 100644 index 000000000000..0d5d5f067e35 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/guest/create_empty_cart_rollback.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\Quote\Model\QuoteIdMask; +use Magento\Quote\Model\QuoteIdMaskFactory; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var QuoteFactory $quoteFactory */ +$quoteFactory = Bootstrap::getObjectManager()->get(QuoteFactory::class); +/** @var QuoteResource $quoteResource */ +$quoteResource = Bootstrap::getObjectManager()->get(QuoteResource::class); +/** @var QuoteIdMaskFactory $quoteIdMaskFactory */ +$quoteIdMaskFactory = Bootstrap::getObjectManager()->get(QuoteIdMaskFactory::class); + +$quote = $quoteFactory->create(); +$quoteResource->load($quote, 'test_quote', 'reserved_order_id'); +$quoteResource->delete($quote); + +/** @var QuoteIdMask $quoteIdMask */ +$quoteIdMask = $quoteIdMaskFactory->create(); +$quoteIdMask->setQuoteId($quote->getId()) + ->delete(); diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/make_cart_inactive.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/make_cart_inactive.php new file mode 100644 index 000000000000..b5704f82879b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/make_cart_inactive.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var QuoteFactory $quoteFactory */ +$quoteFactory = Bootstrap::getObjectManager()->get(QuoteFactory::class); +/** @var QuoteResource $quoteResource */ +$quoteResource = Bootstrap::getObjectManager()->get(QuoteResource::class); +/** @var CartRepositoryInterface $cartRepository */ +$cartRepository = Bootstrap::getObjectManager()->get(CartRepositoryInterface::class); + +$quote = $quoteFactory->create(); +$quoteResource->load($quote, 'test_quote', 'reserved_order_id'); +$quote->setIsActive(false); +$cartRepository->save($quote); diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/set_checkmo_payment_method.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/set_checkmo_payment_method.php new file mode 100644 index 000000000000..a973a982b115 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/set_checkmo_payment_method.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\OfflinePayments\Model\Checkmo; +use Magento\Quote\Api\Data\PaymentInterface; +use Magento\Quote\Api\Data\PaymentInterfaceFactory; +use Magento\Quote\Api\PaymentMethodManagementInterface; +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var QuoteFactory $quoteFactory */ +$quoteFactory = Bootstrap::getObjectManager()->get(QuoteFactory::class); +/** @var QuoteResource $quoteResource */ +$quoteResource = Bootstrap::getObjectManager()->get(QuoteResource::class); +/** @var PaymentInterfaceFactory $paymentFactory */ +$paymentFactory = Bootstrap::getObjectManager()->get(PaymentInterfaceFactory::class); +/** @var PaymentMethodManagementInterface $paymentMethodManagement */ +$paymentMethodManagement = Bootstrap::getObjectManager()->get(PaymentMethodManagementInterface::class); + +$quote = $quoteFactory->create(); +$quoteResource->load($quote, 'test_quote', 'reserved_order_id'); + +$payment = $paymentFactory->create([ + 'data' => [ + PaymentInterface::KEY_METHOD => Checkmo::PAYMENT_METHOD_CHECKMO_CODE, + ] +]); +$paymentMethodManagement->set($quote->getId(), $payment); diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/set_new_shipping_address.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/set_new_shipping_address.php new file mode 100644 index 000000000000..e17b9e61f82d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/set_new_shipping_address.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Api\DataObjectHelper; +use Magento\Quote\Api\Data\AddressInterface; +use Magento\Quote\Api\Data\AddressInterfaceFactory; +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\Quote\Model\ShippingAddressManagementInterface; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var QuoteFactory $quoteFactory */ +$quoteFactory = Bootstrap::getObjectManager()->get(QuoteFactory::class); +/** @var QuoteResource $quoteResource */ +$quoteResource = Bootstrap::getObjectManager()->get(QuoteResource::class); +/** @var AddressInterfaceFactory $quoteAddressFactory */ +$quoteAddressFactory = Bootstrap::getObjectManager()->get(AddressInterfaceFactory::class); +/** @var DataObjectHelper $dataObjectHelper */ +$dataObjectHelper = Bootstrap::getObjectManager()->get(DataObjectHelper::class); +/** @var ShippingAddressManagementInterface $shippingAddressManagement */ +$shippingAddressManagement = Bootstrap::getObjectManager()->get(ShippingAddressManagementInterface::class); + +$quoteAddressData = [ + AddressInterface::KEY_TELEPHONE => 3468676, + AddressInterface::KEY_POSTCODE => 75477, + AddressInterface::KEY_COUNTRY_ID => 'US', + AddressInterface::KEY_CITY => 'CityM', + AddressInterface::KEY_COMPANY => 'CompanyName', + AddressInterface::KEY_STREET => 'Green str, 67', + AddressInterface::KEY_LASTNAME => 'Smith', + AddressInterface::KEY_FIRSTNAME => 'John', + AddressInterface::KEY_REGION_ID => 1, +]; +$quoteAddress = $quoteAddressFactory->create(); +$dataObjectHelper->populateWithArray($quoteAddress, $quoteAddressData, AddressInterfaceFactory::class); + +$quote = $quoteFactory->create(); +$quoteResource->load($quote, 'test_quote', 'reserved_order_id'); +$shippingAddressManagement->assign($quote->getId(), $quoteAddress); From 4ce46390de5d1236d5ec38f5912355d3abe9939a Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Tue, 26 Mar 2019 23:39:36 -0500 Subject: [PATCH 263/265] GraphQL-538: Api-functional tests fixture refactoring --- .../Magento/GraphQl/Quote/GetMaskedQuoteIdByReservedOrderId.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetMaskedQuoteIdByReservedOrderId.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetMaskedQuoteIdByReservedOrderId.php index 88462b8dbda3..c5a4e8af02a5 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetMaskedQuoteIdByReservedOrderId.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetMaskedQuoteIdByReservedOrderId.php @@ -47,6 +47,8 @@ public function __construct( } /** + * Get masked quote id by reserved order id + * * @param string $reversedOrderId * @return string * @throws \Magento\Framework\Exception\NoSuchEntityException From 1a0dd445c4a3865a4c41225e827bfaa2feed204d Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Tue, 26 Mar 2019 23:47:31 -0500 Subject: [PATCH 264/265] GraphQL-538: Api-functional tests fixture refactoring --- .../Quote/Customer/GetAvailablePaymentMethodsTest.php | 4 ++-- .../Magento/GraphQl/Quote/Customer/GetCartTest.php | 4 ++-- .../GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php | 6 +++--- .../GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php | 4 ++-- .../GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php | 4 ++-- .../GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php | 2 +- .../testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php | 2 +- .../GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php | 4 ++-- .../GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php | 2 +- .../GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php | 4 ++-- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php index 9c988f6bf5f7..f15b275b17f1 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php @@ -59,7 +59,7 @@ public function testGetAvailablePaymentMethods() } /** - * @security + * _security * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php @@ -78,7 +78,7 @@ public function testGetAvailablePaymentMethodsFromGuestCart() } /** - * @security + * _security * @magentoApiDataFixture Magento/Customer/_files/three_customers.php * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php index d2615b4135b0..eb62b8c92f31 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php @@ -63,7 +63,7 @@ public function testGetCart() } /** - * @security + * _security * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php */ @@ -79,7 +79,7 @@ public function testGetGuestCart() } /** - * @security + * _security * @magentoApiDataFixture Magento/Customer/_files/three_customers.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php */ 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 0099d25ed81f..129375debe06 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 @@ -322,7 +322,7 @@ public function testSetNewBillingAddressAndFromAddressBookAtSameTime() } /** - * @security + * _security * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Customer/_files/customer_address.php * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php @@ -359,7 +359,7 @@ public function testSetBillingAddressToGuestCart() } /** - * @security + * _security * @magentoApiDataFixture Magento/Customer/_files/three_customers.php * @magentoApiDataFixture Magento/Customer/_files/customer_address.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php @@ -394,7 +394,7 @@ public function testSetBillingAddressToAnotherCustomerCart() } /** - * @security + * _security * @magentoApiDataFixture Magento/Customer/_files/three_customers.php * @magentoApiDataFixture Magento/Customer/_files/customer_address.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php 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 ffc52c753b80..450a22dd6e9c 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 @@ -139,7 +139,7 @@ public function testSetPaymentOnNonExistentCart() } /** - * @security + * _security * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php @@ -159,7 +159,7 @@ public function testSetPaymentMethodToGuestCart() } /** - * @security + * _security * @magentoApiDataFixture Magento/Customer/_files/three_customers.php * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php index efd86ca5e56c..5ff29d20b34d 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php @@ -295,7 +295,7 @@ public function testSetNewShippingAddressAndFromAddressBookAtSameTime() } /** - * @security + * _security * @magentoApiDataFixture Magento/Customer/_files/three_customers.php * @magentoApiDataFixture Magento/Customer/_files/customer_address.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php @@ -332,7 +332,7 @@ public function testSetShippingAddressIfCustomerIsNotOwnerOfAddress() } /** - * @security + * _security * @magentoApiDataFixture Magento/Customer/_files/three_customers.php * @magentoApiDataFixture Magento/Customer/_files/customer_address.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php index 8989b1b2c074..95136932e296 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php @@ -51,7 +51,7 @@ public function testGetAvailablePaymentMethods() } /** - * @security + * _security * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php index a7af18a5674b..6b1f540e4d47 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php @@ -55,7 +55,7 @@ public function testGetCart() } /** - * @security + * _security * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php */ 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 f6c38ef43554..3808ce38b9d7 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 @@ -162,7 +162,7 @@ public function testSetNewBillingAddressWithUseForShippingParameter() } /** - * @security + * _security * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php */ @@ -207,7 +207,7 @@ public function testSetBillingAddressToCustomerCart() } /** - * @security + * _security * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.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 b2a8f5078c5e..8f37f00c3db7 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,7 +126,7 @@ public function testSetPaymentOnNonExistentCart() } /** - * @security + * _security * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php index b6cea68db9b8..e21d9ed64d49 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php @@ -134,7 +134,7 @@ public function testSetNewShippingAddressOnCartWithVirtualProduct() } /** - * @security + * _security * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php @@ -170,7 +170,7 @@ public function testSetShippingAddressFromAddressBook() } /** - * @security + * _security * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php From 88fa9d62c1c1318a5ec58897c305904d41b1a8b9 Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Wed, 27 Mar 2019 00:52:09 -0500 Subject: [PATCH 265/265] GraphQL-538: Api-functional tests fixture refactoring --- .../GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php | 1 - .../GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php | 1 - 2 files changed, 2 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php index f15b275b17f1..ba640bc3402b 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php @@ -52,7 +52,6 @@ public function testGetAvailablePaymentMethods() self::assertArrayHasKey('cart', $response); self::assertArrayHasKey('available_payment_methods', $response['cart']); - self::assertCount(1, $response['cart']['available_payment_methods']); self::assertEquals('checkmo', $response['cart']['available_payment_methods'][0]['code']); self::assertEquals('Check / Money order', $response['cart']['available_payment_methods'][0]['title']); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php index 95136932e296..8271a76d88f1 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php @@ -44,7 +44,6 @@ public function testGetAvailablePaymentMethods() self::assertArrayHasKey('cart', $response); self::assertArrayHasKey('available_payment_methods', $response['cart']); - self::assertCount(1, $response['cart']['available_payment_methods']); self::assertEquals('checkmo', $response['cart']['available_payment_methods'][0]['code']); self::assertEquals('Check / Money order', $response['cart']['available_payment_methods'][0]['title']);