From f0e02b1b3b9c6694bbcb9676dc399dbfae27329b Mon Sep 17 00:00:00 2001 From: Matthias Herold Date: Mon, 15 Jan 2018 16:46:51 +0100 Subject: [PATCH 001/345] [FEATURE] \Magento\Framework\Config\Dom -> added ability to create default/fixed value nodes during XSD Schema Validation with flag 'LIBXML_SCHEMA_CREATE' --- lib/internal/Magento/Framework/Config/Dom.php | 2 +- .../Framework/Config/Test/Unit/DomTest.php | 42 +++++++++++++++++++ .../Config/Test/Unit/_files/sample.xsd | 1 + 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Config/Dom.php b/lib/internal/Magento/Framework/Config/Dom.php index 35d9626c90a94..f0805ae4a1694 100644 --- a/lib/internal/Magento/Framework/Config/Dom.php +++ b/lib/internal/Magento/Framework/Config/Dom.php @@ -313,7 +313,7 @@ public static function validateDomDocument( libxml_set_external_entity_loader([self::$urnResolver, 'registerEntityLoader']); $errors = []; try { - $result = $dom->schemaValidate($schema); + $result = $dom->schemaValidate($schema, LIBXML_SCHEMA_CREATE); if (!$result) { $errors = self::getXmlErrors($errorFormat); } diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/DomTest.php b/lib/internal/Magento/Framework/Config/Test/Unit/DomTest.php index 5c8f66683877c..0508b5e4fb359 100644 --- a/lib/internal/Magento/Framework/Config/Test/Unit/DomTest.php +++ b/lib/internal/Magento/Framework/Config/Test/Unit/DomTest.php @@ -135,6 +135,48 @@ public function validateDataProvider() ]; } + /** + * @param string $xml + * @param string $expectedValue + * @dataProvider validateWithDefaultValueDataProvider + */ + public function testValidateWithDefaultValue($xml, $expectedValue) + { + if (!function_exists('libxml_set_external_entity_loader')) { + $this->markTestSkipped('Skipped on HHVM. Will be fixed in MAGETWO-45033'); + } + + $actualErrors = []; + + $dom = new \Magento\Framework\Config\Dom($xml, $this->validationStateMock); + $dom->validate(__DIR__ . '/_files/sample.xsd', $actualErrors); + + $actualValue = $dom->getDom() + ->getElementsByTagName('root')->item(0) + ->getElementsByTagName('node')->item(0) + ->getAttribute('attribute_with_default_value'); + + $this->assertEmpty($actualErrors); + $this->assertEquals($expectedValue, $actualValue); + } + + /** + * @return array + */ + public function validateWithDefaultValueDataProvider() + { + return [ + 'default_value' => [ + '', + 'default_value' + ], + 'custom_value' => [ + '', + 'non_default_value' + ], + ]; + } + public function testValidateCustomErrorFormat() { $xml = ''; diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/_files/sample.xsd b/lib/internal/Magento/Framework/Config/Test/Unit/_files/sample.xsd index 1f635b7081e05..701a2eb18c2a1 100644 --- a/lib/internal/Magento/Framework/Config/Test/Unit/_files/sample.xsd +++ b/lib/internal/Magento/Framework/Config/Test/Unit/_files/sample.xsd @@ -21,6 +21,7 @@ + From bcd4322f9074570d7e912e0a714b1ffb32053266 Mon Sep 17 00:00:00 2001 From: Matthias Herold Date: Mon, 15 Jan 2018 16:48:13 +0100 Subject: [PATCH 002/345] Fixed expectation dev/tests/integration/testsuite/Magento/Framework/Search/_files/search_request_merged.php for \Magento\Framework\Search\Request\Config\FileSystemReaderTest::testRead --- .../Framework/Search/_files/search_request_merged.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/search_request_merged.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/search_request_merged.php index 8586f47a0f7fa..0aaa3f4e15bda 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/search_request_merged.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/search_request_merged.php @@ -35,6 +35,7 @@ 'match_query' => [ 'value' => '$match_term_override$', 'name' => 'match_query', + 'boost' => '1', 'match' => [ 0 => [ 'field' => 'match_field', @@ -50,6 +51,7 @@ ], 'must_query' => [ 'name' => 'must_query', + 'boost' => '1', 'filterReference' => [ 0 => [ 'clause' => 'must', @@ -60,6 +62,7 @@ ], 'should_query' => [ 'name' => 'should_query', + 'boost' => '1', 'filterReference' => [ 0 => [ 'clause' => 'should', @@ -70,6 +73,7 @@ ], 'not_query' => [ 'name' => 'not_query', + 'boost' => '1', 'filterReference' => [ 0 => [ 'clause' => 'not', @@ -80,6 +84,7 @@ ], 'match_query_2' => [ 'value' => '$match_term_override$', + 'boost' => '1', 'name' => 'match_query_2', 'match' => [ 0 => [ @@ -163,6 +168,7 @@ 'queries' => [ 'filter_query' => [ 'name' => 'filter_query', + 'boost' => '1', 'filterReference' => [ 0 => [ @@ -230,6 +236,7 @@ 'new_match_query' => [ 'value' => '$match_term$', 'name' => 'new_match_query', + 'boost' => '1', 'match' => [ 0 => [ From bf024c1c1f3b86a7c3dd362b78fb124f58f22b96 Mon Sep 17 00:00:00 2001 From: Danny Verkade Date: Mon, 15 Oct 2018 20:31:52 +0200 Subject: [PATCH 003/345] 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 911574a0fb438..3de99aa9af1a3 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 004/345] 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 3de99aa9af1a3..4c684f16b7678 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 005/345] 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 4c684f16b7678..ec399914ed02e 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 b92bbc41c6f76e50b02922067d3df208501a56f6 Mon Sep 17 00:00:00 2001 From: niravkrish Date: Sat, 8 Dec 2018 12:00:48 +0530 Subject: [PATCH 006/345] Fixed-#18017 magento 2.3 --- .../Swatches/view/frontend/web/js/swatch-renderer.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js index 0a8675067ea5d..cfb29b27bdef4 100644 --- a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js +++ b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js @@ -1223,7 +1223,15 @@ define([ } imagesToUpdate = this._setImageIndex(imagesToUpdate); - gallery.updateData(imagesToUpdate); + + if (gallery === undefined) { + context.find(this.options.mediaGallerySelector).on('gallery:loaded', function (loadedGallery) { + loadedGallery = context.find(this.options.mediaGallerySelector).data('gallery'); + loadedGallery.updateData(imagesToUpdate); + }.bind(this)); + } else { + gallery.updateData(imagesToUpdate); + } if (isInitial) { $(this.options.mediaGallerySelector).AddFotoramaVideoEvents(); From 95d5f44f6654a3db1ad6eb2b0b17b007f67bc3de Mon Sep 17 00:00:00 2001 From: niravkrish Date: Sat, 8 Dec 2018 18:06:04 +0530 Subject: [PATCH 007/345] Fixed Code Improovement --- .../Swatches/view/frontend/web/js/swatch-renderer.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js index cfb29b27bdef4..77c1f208f68c9 100644 --- a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js +++ b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js @@ -1223,7 +1223,6 @@ define([ } imagesToUpdate = this._setImageIndex(imagesToUpdate); - if (gallery === undefined) { context.find(this.options.mediaGallerySelector).on('gallery:loaded', function (loadedGallery) { loadedGallery = context.find(this.options.mediaGallerySelector).data('gallery'); @@ -1242,7 +1241,14 @@ define([ }); } - gallery.first(); + if (gallery === undefined) { + context.find(this.options.mediaGallerySelector).on('gallery:loaded', function (loadedGallery) { + loadedGallery = context.find(this.options.mediaGallerySelector).data('gallery'); + loadedGallery.first(); + }.bind(this)); + } else { + gallery.first(); + } } else if (justAnImage && justAnImage.img) { context.find('.product-image-photo').attr('src', justAnImage.img); From 30c3eac83641969f6a38fdf1f52eac903520ea52 Mon Sep 17 00:00:00 2001 From: niravkrish Date: Mon, 10 Dec 2018 10:02:27 +0530 Subject: [PATCH 008/345] Code Improovement and changes as suggested --- .../Magento/Swatches/view/frontend/web/js/swatch-renderer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js index 77c1f208f68c9..45cd85b32d579 100644 --- a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js +++ b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js @@ -1223,7 +1223,7 @@ define([ } imagesToUpdate = this._setImageIndex(imagesToUpdate); - if (gallery === undefined) { + if (typeof gallery === undefined) { context.find(this.options.mediaGallerySelector).on('gallery:loaded', function (loadedGallery) { loadedGallery = context.find(this.options.mediaGallerySelector).data('gallery'); loadedGallery.updateData(imagesToUpdate); @@ -1241,7 +1241,7 @@ define([ }); } - if (gallery === undefined) { + if (typeof gallery === undefined) { context.find(this.options.mediaGallerySelector).on('gallery:loaded', function (loadedGallery) { loadedGallery = context.find(this.options.mediaGallerySelector).data('gallery'); loadedGallery.first(); From ee64ecdf5e03deae2aa8734892cd51aff398543c Mon Sep 17 00:00:00 2001 From: niravkrish Date: Mon, 10 Dec 2018 10:35:24 +0530 Subject: [PATCH 009/345] code changes as per PSR4 --- .../Magento/Swatches/view/frontend/web/js/swatch-renderer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js index 45cd85b32d579..8b8b76accd12f 100644 --- a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js +++ b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js @@ -1223,6 +1223,7 @@ define([ } imagesToUpdate = this._setImageIndex(imagesToUpdate); + if (typeof gallery === undefined) { context.find(this.options.mediaGallerySelector).on('gallery:loaded', function (loadedGallery) { loadedGallery = context.find(this.options.mediaGallerySelector).data('gallery'); From a3f022e9667747f58aec6d81d366b097149af66b Mon Sep 17 00:00:00 2001 From: Wouter Samaey Date: Tue, 18 Dec 2018 11:21:15 +0100 Subject: [PATCH 010/345] MUI controller lacks JSON return, instead returns status 200 with empty body --- app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php b/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php index b983e56b8aee2..25c916550a850 100644 --- a/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php +++ b/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php @@ -86,6 +86,14 @@ public function execute() $contentType = $this->contentTypeResolver->resolve($component->getContext()); $this->getResponse()->setHeader('Content-Type', $contentType, true); + }else { + /** @var \Magento\Framework\Controller\Result\Json $resultJson */ + $resultJson = $this->resultJsonFactory->create(); + $resultJson->setStatusHeader( + \Zend\Http\Response::STATUS_CODE_403, + \Zend\Http\AbstractMessage::VERSION_11, + 'Forbidden' + ); } } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->logger->critical($e); From 0a0d65ea78edde0206b819b6131aa91a94374292 Mon Sep 17 00:00:00 2001 From: Wouter Samaey Date: Tue, 18 Dec 2018 11:21:15 +0100 Subject: [PATCH 011/345] MUI controller lacks JSON return, instead returns status 200 with empty body --- .../Magento/Ui/Controller/Adminhtml/Index/Render.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php b/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php index b983e56b8aee2..ebca3aef7ddf3 100644 --- a/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php +++ b/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php @@ -86,6 +86,18 @@ public function execute() $contentType = $this->contentTypeResolver->resolve($component->getContext()); $this->getResponse()->setHeader('Content-Type', $contentType, true); + }else { + /** @var \Magento\Framework\Controller\Result\Json $resultJson */ + $resultJson = $this->resultJsonFactory->create(); + $resultJson->setStatusHeader( + \Zend\Http\Response::STATUS_CODE_403, + \Zend\Http\AbstractMessage::VERSION_11, + 'Forbidden' + ); + return $resultJson->setData([ + 'error' => $this->escaper->escapeHtml('Forbidden'), + 'errorcode' => 403] + ); } } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->logger->critical($e); From d3bc83dbed34680467c41c36951db1979750f5e8 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko Date: Thu, 20 Dec 2018 14:50:21 +0100 Subject: [PATCH 012/345] Update app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php Co-Authored-By: woutersamaey --- app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php b/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php index ebca3aef7ddf3..84bfa56f25655 100644 --- a/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php +++ b/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php @@ -86,7 +86,7 @@ public function execute() $contentType = $this->contentTypeResolver->resolve($component->getContext()); $this->getResponse()->setHeader('Content-Type', $contentType, true); - }else { + } else { /** @var \Magento\Framework\Controller\Result\Json $resultJson */ $resultJson = $this->resultJsonFactory->create(); $resultJson->setStatusHeader( From 3c9c884e157157550131ca6008e21bc475a1acc7 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko Date: Thu, 20 Dec 2018 14:50:31 +0100 Subject: [PATCH 013/345] Update app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php Co-Authored-By: woutersamaey --- app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php b/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php index 84bfa56f25655..e620222a642b7 100644 --- a/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php +++ b/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php @@ -96,7 +96,7 @@ public function execute() ); return $resultJson->setData([ 'error' => $this->escaper->escapeHtml('Forbidden'), - 'errorcode' => 403] + 'errorcode' => 403 ); } } catch (\Magento\Framework\Exception\LocalizedException $e) { From f9911bdb77283cd670d25e4ace8b89668d153225 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko Date: Thu, 20 Dec 2018 14:50:44 +0100 Subject: [PATCH 014/345] Update app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php Co-Authored-By: woutersamaey --- app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php b/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php index e620222a642b7..b06c655939b1c 100644 --- a/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php +++ b/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php @@ -97,7 +97,7 @@ public function execute() return $resultJson->setData([ 'error' => $this->escaper->escapeHtml('Forbidden'), 'errorcode' => 403 - ); + ]); } } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->logger->critical($e); From 82970b8c9bd9b7edbdfe77b05f6a73927d2bda6b Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk Date: Thu, 20 Dec 2018 17:10:17 -0600 Subject: [PATCH 015/345] 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 0000000000000..e049c7430871f --- /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 0000000000000..6cb156723b286 --- /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 0000000000000..3c62ef89e584b --- /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 0000000000000..85d3927a6d6d0 --- /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 0000000000000..f5fabae5fc4ce --- /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 b93a70559fc4a..63b08b58af6ca 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 83c3ca5348606..a90525e40b695 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 0000000000000..132b5ca81886f --- /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 7be36ffbd9bc4..2704f0af38165 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 0000000000000..5591bee529690 --- /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 0000000000000..6ca0f612deeaa --- /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 0000000000000..907551e932fcf --- /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 0000000000000..3a4329969ae2b --- /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 7482193031091..56be7a12d7de7 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 eeb40d82fab691bd4ed817ff9728696efcb8d472 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky Date: Fri, 21 Dec 2018 16:36:29 +0200 Subject: [PATCH 016/345] ENGCOM-3725: Unit test fix. --- .../Controller/Adminhtml/Index/RenderTest.php | 49 +++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Ui/Test/Unit/Controller/Adminhtml/Index/RenderTest.php b/app/code/Magento/Ui/Test/Unit/Controller/Adminhtml/Index/RenderTest.php index 05b35fb017b4b..2bba8686490b6 100644 --- a/app/code/Magento/Ui/Test/Unit/Controller/Adminhtml/Index/RenderTest.php +++ b/app/code/Magento/Ui/Test/Unit/Controller/Adminhtml/Index/RenderTest.php @@ -3,12 +3,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Ui\Test\Unit\Controller\Adminhtml\Index; +use Magento\Framework\Controller\Result\Json; +use Magento\Framework\Escaper; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Ui\Controller\Adminhtml\Index\Render; use Magento\Ui\Model\UiComponentTypeResolver; -use Magento\Framework\View\Element\UiComponent\ContextInterface; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Zend\Http\AbstractMessage; +use Zend\Http\Response; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -97,6 +102,11 @@ class RenderTest extends \PHPUnit\Framework\TestCase */ private $loggerMock; + /** + * @var Escaper|\PHPUnit_Framework_MockObject_MockObject + */ + private $escaperMock; + protected function setUp() { $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) @@ -170,6 +180,10 @@ protected function setUp() $this->uiComponentTypeResolverMock = $this->getMockBuilder(UiComponentTypeResolver::class) ->disableOriginalConstructor() ->getMock(); + $this->escaperMock = $this->createMock(Escaper::class); + $this->escaperMock->expects($this->any()) + ->method('escapeHtml') + ->willReturnArgument(0); $this->objectManagerHelper = new ObjectManagerHelper($this); @@ -181,6 +195,7 @@ protected function setUp() 'contentTypeResolver' => $this->uiComponentTypeResolverMock, 'resultJsonFactory' => $this->resultJsonFactoryMock, 'logger' => $this->loggerMock, + 'escaper' => $this->escaperMock, ] ); } @@ -201,7 +216,7 @@ public function testExecuteAjaxRequestException() ->method('appendBody') ->willThrowException(new \Exception('exception')); - $jsonResultMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Json::class) + $jsonResultMock = $this->getMockBuilder(Json::class) ->disableOriginalConstructor() ->setMethods(['setData']) ->getMock(); @@ -290,6 +305,34 @@ public function testExecuteAjaxRequestWithoutPermissions(array $dataProviderConf $name = 'test-name'; $renderedData = 'data'; + if (false === $isAllowed) { + $jsonResultMock = $this->getMockBuilder(Json::class) + ->disableOriginalConstructor() + ->setMethods(['setStatusHeader', 'setData']) + ->getMock(); + + $jsonResultMock->expects($this->at(0)) + ->method('setStatusHeader') + ->with( + Response::STATUS_CODE_403, + AbstractMessage::VERSION_11, + 'Forbidden' + ) + ->willReturnSelf(); + + $jsonResultMock->expects($this->at(1)) + ->method('setData') + ->with([ + 'error' => 'Forbidden', + 'errorcode' => 403 + ]) + ->willReturnSelf(); + + $this->resultJsonFactoryMock->expects($this->any()) + ->method('create') + ->willReturn($jsonResultMock); + } + $this->requestMock->expects($this->any()) ->method('getParam') ->with('namespace') From 7feaeddc33995e2a0abe121e24854e02d7bb83f7 Mon Sep 17 00:00:00 2001 From: niravkrish Date: Fri, 28 Dec 2018 13:03:55 +0530 Subject: [PATCH 017/345] Code Improvement suggested by orlangur --- .../Magento/Swatches/view/frontend/web/js/swatch-renderer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js index 8b8b76accd12f..3261f8fcf96e6 100644 --- a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js +++ b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js @@ -1224,7 +1224,7 @@ define([ imagesToUpdate = this._setImageIndex(imagesToUpdate); - if (typeof gallery === undefined) { + if (typeof gallery == 'undefined') { context.find(this.options.mediaGallerySelector).on('gallery:loaded', function (loadedGallery) { loadedGallery = context.find(this.options.mediaGallerySelector).data('gallery'); loadedGallery.updateData(imagesToUpdate); @@ -1242,7 +1242,7 @@ define([ }); } - if (typeof gallery === undefined) { + if (typeof gallery == 'undefined') { context.find(this.options.mediaGallerySelector).on('gallery:loaded', function (loadedGallery) { loadedGallery = context.find(this.options.mediaGallerySelector).data('gallery'); loadedGallery.first(); From 0df72432ecf611c8c0f54b0b88bbbca485d4046f Mon Sep 17 00:00:00 2001 From: Aditi Singh <36220507+aditisinghcedcoss@users.noreply.github.com> Date: Fri, 28 Dec 2018 13:09:54 +0530 Subject: [PATCH 018/345] Update AbstactSource.php Issue #13612 fixed. --- .../Eav/Model/Entity/Attribute/Source/AbstractSource.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Source/AbstractSource.php b/app/code/Magento/Eav/Model/Entity/Attribute/Source/AbstractSource.php index 0991b3f9f4b23..8d5b501654749 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Source/AbstractSource.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Source/AbstractSource.php @@ -73,7 +73,7 @@ public function getOptionText($value) } } // End - if (isset($options[$value])) { + if (is_scalar($value) && isset($options[$value])) { return $options[$value]; } return false; From d41c407f62dffedd3e3be3f2dc27ed51084d0887 Mon Sep 17 00:00:00 2001 From: Dusan Lukic Date: Thu, 3 Jan 2019 15:28:01 +0100 Subject: [PATCH 019/345] Fixed non existant category with products issue --- .../Model/Resolver/CategoryTree.php | 2 +- .../Magento/GraphQl/Catalog/CategoryTest.php | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php index 4e3a8403f3132..7e7b0db8240af 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php @@ -72,7 +72,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $rootCategoryId = $this->getCategoryId($args); $categoriesTree = $this->categoryTree->getTree($info, $rootCategoryId); - if (!empty($categoriesTree)) { + if (!empty($categoriesTree) && ($categoriesTree->count() > 0)) { $result = $this->extractDataFromCategoryTree->execute($categoriesTree); return current($result); } else { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php index 54e98367ab8ca..17a7bec180585 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php @@ -112,6 +112,21 @@ public function testCategoriesTree() ); } + public function testNonExistentCategoryWithProductCount() + { + $query = <<graphQlQuery($query); + $expectedResponse = ['category' => null]; + $this->assertEquals($expectedResponse, $response); + } + /** * @magentoApiDataFixture Magento/Catalog/_files/categories.php * @SuppressWarnings(PHPMD.ExcessiveMethodLength) From bbceaab96e7f9440528944a41d141e78ee6c3b26 Mon Sep 17 00:00:00 2001 From: niravkrish Date: Tue, 8 Jan 2019 10:45:52 +0530 Subject: [PATCH 020/345] resolve conflicts --- .../Magento/Swatches/view/frontend/web/js/swatch-renderer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js index 3261f8fcf96e6..3693f86984602 100644 --- a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js +++ b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js @@ -1224,7 +1224,7 @@ define([ imagesToUpdate = this._setImageIndex(imagesToUpdate); - if (typeof gallery == 'undefined') { + if (typeof gallery === 'undefined') { context.find(this.options.mediaGallerySelector).on('gallery:loaded', function (loadedGallery) { loadedGallery = context.find(this.options.mediaGallerySelector).data('gallery'); loadedGallery.updateData(imagesToUpdate); @@ -1242,7 +1242,7 @@ define([ }); } - if (typeof gallery == 'undefined') { + if (typeof gallery === 'undefined') { context.find(this.options.mediaGallerySelector).on('gallery:loaded', function (loadedGallery) { loadedGallery = context.find(this.options.mediaGallerySelector).data('gallery'); loadedGallery.first(); From 5955ded71ff1367fee224278a1069e09ef711efb Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk Date: Thu, 17 Jan 2019 11:57:51 -0600 Subject: [PATCH 021/345] 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 e049c7430871f..41bfbe608a31d 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 a90525e40b695..28f0c1bbf8890 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 022/345] 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 28f0c1bbf8890..3bf1273f05cc2 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 023/345] 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 41bfbe608a31d..1bb7c179dfca8 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 0dfe0f0980a8be7f5ab62d5572126a0a8802ea21 Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun Date: Wed, 23 Jan 2019 13:38:28 +0200 Subject: [PATCH 024/345] Sorting by Websites not working in product grid in backoffice #20511 --- .../Ui/Component/Listing/Columns/Websites.php | 58 ++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php index 5af0d71dc246c..f67585c6401f8 100644 --- a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php +++ b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php @@ -1,4 +1,4 @@ -storeManager = $storeManager; + $this->_resourceHelper = $resourceHelper; } /** @@ -83,4 +97,46 @@ public function prepare() $this->_data['config']['componentDisabled'] = true; } } + + /** + * Apply sorting + * + * @return void + */ + protected function applySorting() + { + $sorting = $this->getContext()->getRequestParam('sorting'); + $isSortable = $this->getData('config/sortable'); + if ($isSortable !== false + && !empty($sorting['field']) + && !empty($sorting['direction']) + && $sorting['field'] === $this->getName() + ) { + $collection = $this->getContext()->getDataProvider()->getCollection(); + $collection + ->joinField( + 'websites_ids', + 'catalog_product_website', + 'website_id', + 'product_id=entity_id', + null, + 'left' + ) + ->joinTable( + 'store_website', + 'website_id = websites_ids', + ['name'], + null, + 'left' + ) + ->groupByAttribute('entity_id'); + $this->_resourceHelper->addGroupConcatColumn( + $collection->getSelect(), + self::WEBSITE_NAMES, + 'name' + ); + + $collection->getSelect()->order(self::WEBSITE_NAMES . ' ' . $sorting['direction']); + } + } } From 887902bdf7bbf524ef4b852cd8c6efb0775dd1bf Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky Date: Fri, 25 Jan 2019 13:24:27 +0200 Subject: [PATCH 025/345] ENGCOM-3810: Static test fix. --- .../Eav/Model/Entity/Attribute/Source/AbstractSource.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Source/AbstractSource.php b/app/code/Magento/Eav/Model/Entity/Attribute/Source/AbstractSource.php index 8d5b501654749..36ad026029056 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Source/AbstractSource.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Source/AbstractSource.php @@ -80,6 +80,8 @@ public function getOptionText($value) } /** + * Get option id. + * * @param string $value * @return null|string */ From 70fb3da4f8dda65a2ffcf6ed20e57f63cc3133ee Mon Sep 17 00:00:00 2001 From: Rik Willems Date: Wed, 30 Jan 2019 09:07:09 +0200 Subject: [PATCH 026/345] Use batches and direct queries to fix sales address upgrade --- .../FillQuoteAddressIdInSalesOrderAddress.php | 130 ++++++++++++------ 1 file changed, 90 insertions(+), 40 deletions(-) diff --git a/app/code/Magento/Sales/Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php b/app/code/Magento/Sales/Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php index 2716e860243bf..e1b517befccb4 100644 --- a/app/code/Magento/Sales/Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php +++ b/app/code/Magento/Sales/Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php @@ -9,6 +9,7 @@ use Magento\Eav\Model\Config; use Magento\Framework\App\State; use Magento\Quote\Model\QuoteFactory; +use Magento\Sales\Model\Order\Address; use Magento\Sales\Model\OrderFactory; use Magento\Sales\Model\ResourceModel\Order\Address\CollectionFactory as AddressCollectionFactory; use Magento\Framework\App\ResourceConnection; @@ -39,23 +40,11 @@ class FillQuoteAddressIdInSalesOrderAddress implements DataPatchInterface, Patch */ private $eavConfig; - /** - * @var AddressCollectionFactory - */ - private $addressCollectionFactory; - - /** - * @var OrderFactory - */ - private $orderFactory; - - /** - * @var QuoteFactory - */ - private $quoteFactory; - /** * PatchInitial constructor. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * * @param ModuleDataSetupInterface $moduleDataSetup */ public function __construct( @@ -71,9 +60,6 @@ public function __construct( $this->salesSetupFactory = $salesSetupFactory; $this->state = $state; $this->eavConfig = $eavConfig; - $this->addressCollectionFactory = $addressCollectionFactory; - $this->orderFactory = $orderFactory; - $this->quoteFactory = $quoteFactory; } /** @@ -96,28 +82,8 @@ public function apply() */ public function fillQuoteAddressIdInSalesOrderAddress(ModuleDataSetupInterface $setup) { - $addressTable = $setup->getTable('sales_order_address'); - $updateOrderAddress = $setup->getConnection() - ->select() - ->joinInner( - ['sales_order' => $setup->getTable('sales_order')], - $addressTable . '.parent_id = sales_order.entity_id', - ['quote_address_id' => 'quote_address.address_id'] - ) - ->joinInner( - ['quote_address' => $setup->getTable('quote_address')], - 'sales_order.quote_id = quote_address.quote_id - AND ' . $addressTable . '.address_type = quote_address.address_type', - [] - ) - ->where( - $addressTable . '.quote_address_id IS NULL' - ); - $updateOrderAddress = $setup->getConnection()->updateFromSelect( - $updateOrderAddress, - $addressTable - ); - $setup->getConnection()->query($updateOrderAddress); + $this->fillQuoteAddressIdInSalesOrderAddressByType($setup, Address::TYPE_SHIPPING); + $this->fillQuoteAddressIdInSalesOrderAddressByType($setup, Address::TYPE_BILLING); } /** @@ -145,4 +111,88 @@ public function getAliases() { return []; } + + /** + * @param ModuleDataSetupInterface $setup + * @param string $addressType + */ + private function fillQuoteAddressIdInSalesOrderAddressByType(ModuleDataSetupInterface $setup, $addressType) + { + $salesConnection = $setup->getConnection('sales'); + + $orderTable = $setup->getTable('sales_order', 'sales'); + $orderAddressTable = $setup->getTable('sales_order_address', 'sales'); + + $query = $salesConnection + ->select() + ->from( + ['sales_order_address' => $orderAddressTable], + ['entity_id', 'address_type'] + ) + ->joinInner( + ['sales_order' => $orderTable], + 'sales_order_address.parent_id = sales_order.entity_id', + ['quote_id' => 'sales_order.quote_id'] + ) + ->where('sales_order_address.quote_address_id IS NULL') + ->where('sales_order_address.address_type = ?', $addressType) + ->order('sales_order_address.entity_id'); + + $batchSize = 5000; + $result = $salesConnection->query($query); + $count = $result->rowCount(); + $batches = ceil($count / $batchSize); + + for ($batch = $batches; $batch > 0; $batch--) { + $query->limitPage($batch, $batchSize); + $result = $salesConnection->fetchAssoc($query); + + $this->fillQuoteAddressIdInSalesOrderAddressProcessBatch($setup, $result, $addressType); + } + } + + /** + * @param ModuleDataSetupInterface $setup + * @param array $orderAddresses + * @param string $addressType + */ + private function fillQuoteAddressIdInSalesOrderAddressProcessBatch( + ModuleDataSetupInterface $setup, + array $orderAddresses, + $addressType + ) { + $salesConnection = $setup->getConnection('sales'); + $quoteConnection = $setup->getConnection('checkout'); + + $quoteAddressTable = $setup->getTable('quote_address', 'checkout'); + $quoteTable = $setup->getTable('quote', 'checkout'); + $salesOrderAddressTable = $setup->getTable('sales_order_address', 'sales'); + + $query = $quoteConnection + ->select() + ->from( + ['quote_address' => $quoteAddressTable], + ['quote_id', 'address_id'] + ) + ->joinInner( + ['quote' => $quoteTable], + 'quote_address.quote_id = quote.entity_id', + [] + ) + ->where('quote.entity_id in (?)', array_column($orderAddresses, 'quote_id')) + ->where('address_type = ?', $addressType); + + $quoteAddresses = $quoteConnection->fetchAssoc($query); + + foreach ($orderAddresses as $orderAddress) { + $bind = [ + 'quote_address_id' => $quoteAddresses[$orderAddress['quote_id']]['address_id'] ?? null, + ]; + $where = [ + 'orderAddressId' => $orderAddress['entity_id'] + ]; + + $salesConnection->update($salesOrderAddressTable, $bind, $where); + } + } } From 54469ce4307776cf5e5beedb002ef8b5775da51e Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun Date: Thu, 31 Jan 2019 10:53:54 +0200 Subject: [PATCH 027/345] magento:magento2 Missed form validation in Admin Order Address Edit route sales/order/address --- .../Order/Create/Form/AbstractForm.php | 50 ++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php index d15c218a60b47..b5761bc0ad733 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php @@ -176,8 +176,8 @@ protected function _addAttributesToForm($attributes, \Magento\Framework\Data\For [ 'name' => $attribute->getAttributeCode(), 'label' => __($attribute->getStoreLabel()), - 'class' => $attribute->getFrontendClass(), - 'required' => $attribute->isRequired() + 'class' => $this->getValidationClasses($attribute), + 'required' => $attribute->isRequired(), ] ); if ($inputType == 'multiline') { @@ -227,4 +227,50 @@ public function getFormValues() { return []; } + + /** + * Retrieve frontend classes according validation rules + * + * @param \Magento\Customer\Api\Data\AttributeMetadataInterface $attribute + * + * @return string + */ + private function getValidationClasses(\Magento\Customer\Api\Data\AttributeMetadataInterface $attribute) : string + { + $out = []; + $out[] = $attribute->getFrontendClass(); + + $textLengthValidateClasses = $this->getTextLengthValidateClasses($attribute); + if (!empty($textLengthValidateClasses)) { + $out = array_merge($out, $textLengthValidateClasses); + } + + $out = !empty($out) ? implode(' ', array_unique(array_filter($out))) : ''; + return $out; + } + + /** + * Retrieve validation classes by min_text_length and max_text_length rules + * + * @param \Magento\Customer\Api\Data\AttributeMetadataInterface $attribute + * + * @return array + */ + private function getTextLengthValidateClasses(\Magento\Customer\Api\Data\AttributeMetadataInterface $attribute) : array + { + $classes = []; + + $validateRules = $attribute->getValidationRules(); + if (!empty($validateRules['min_text_length'])) { + $classes[] = 'minimum-length-' . $validateRules['min_text_length']; + } + if (!empty($validateRules['max_text_length'])) { + $classes[] = 'maximum-length-' . $validateRules['max_text_length']; + } + if (!empty($classes)) { + $classes[] = 'validate-length'; + } + + return $classes; + } } From 464e9817dee6dd9c0f15d3b7823e70a84ff6e2b4 Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun Date: Fri, 1 Feb 2019 12:12:15 +0200 Subject: [PATCH 028/345] magento:magento2 Missed form validation in Admin Order Address Edit route sales/order/address --- .../Block/Adminhtml/Order/Create/Form/AbstractForm.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php index b5761bc0ad733..b4442458e801b 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php @@ -240,9 +240,9 @@ private function getValidationClasses(\Magento\Customer\Api\Data\AttributeMetada $out = []; $out[] = $attribute->getFrontendClass(); - $textLengthValidateClasses = $this->getTextLengthValidateClasses($attribute); - if (!empty($textLengthValidateClasses)) { - $out = array_merge($out, $textLengthValidateClasses); + $textClasses = $this->getTextLengthValidateClasses($attribute); + if (!empty($textClasses)) { + $out = array_merge($out, $textClasses); } $out = !empty($out) ? implode(' ', array_unique(array_filter($out))) : ''; From facfa44aa6f5163a1d78f501c94a8a4303b6b631 Mon Sep 17 00:00:00 2001 From: Ananth Iyer Date: Sun, 3 Feb 2019 15:50:41 +0530 Subject: [PATCH 029/345] Fixed Massaction design with submenu on grid pages --- .../Magento/backend/web/css/source/actions/_actions-select.less | 1 + 1 file changed, 1 insertion(+) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-select.less b/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-select.less index 1cc61bef5da07..e2e1e301d8ee1 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-select.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-select.less @@ -114,6 +114,7 @@ .action-submenu { position: absolute; + right: -100%; } } } From 7b8e969747b4ecefbfcf407cfabe11a4ff08e3c7 Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun Date: Mon, 4 Feb 2019 12:06:51 +0200 Subject: [PATCH 030/345] magento:magento2 Missed form validation in Admin Order Address Edit route sales/order/address --- .../Order/Create/Form/AbstractForm.php | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php index b4442458e801b..5ea77b0f71811 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php @@ -261,14 +261,22 @@ private function getTextLengthValidateClasses(\Magento\Customer\Api\Data\Attribu $classes = []; $validateRules = $attribute->getValidationRules(); - if (!empty($validateRules['min_text_length'])) { - $classes[] = 'minimum-length-' . $validateRules['min_text_length']; - } - if (!empty($validateRules['max_text_length'])) { - $classes[] = 'maximum-length-' . $validateRules['max_text_length']; - } - if (!empty($classes)) { - $classes[] = 'validate-length'; + if(!empty($validateRules)) { + foreach ($validateRules as $rule) { + switch ($rule->getName()) { + case 'min_text_length' : + $classes[] = 'minimum-length-' . $rule->getValue(); + break; + + case 'max_text_length' : + $classes[] = 'maximum-length-' . $rule->getValue(); + break; + } + } + + if (!empty($classes)) { + $classes[] = 'validate-length'; + } } return $classes; From 48126f0458b6d493792089237cfed8ec88d1d15d Mon Sep 17 00:00:00 2001 From: pganapat Date: Tue, 5 Feb 2019 16:35:12 -0600 Subject: [PATCH 031/345] 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 b37628e54aa30..8bb73fbb35116 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 0aa2f7f35218a..3f548850b3c06 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 0000000000000..2cd74c53baa13 --- /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 314d3a8ef24d49f5a55cf6ca3efe5bdfef025b4d Mon Sep 17 00:00:00 2001 From: Ananth Iyer <iyerananth3@gmail.com> Date: Wed, 6 Feb 2019 14:34:02 +0530 Subject: [PATCH 032/345] Fixed mass action design issue #2 --- .../backend/web/css/source/actions/_actions-select.less | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-select.less b/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-select.less index e2e1e301d8ee1..544ab2fadd624 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-select.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-select.less @@ -108,10 +108,6 @@ min-width: 100%; position: static; - ._parent._visible { - position: relative; - } - .action-submenu { position: absolute; right: -100%; From 06113942bfd95b25efd94c2f3626526549aade1a Mon Sep 17 00:00:00 2001 From: niravkrish <nirav.patel@krishtechnolabs.com> Date: Wed, 6 Feb 2019 15:25:56 +0530 Subject: [PATCH 033/345] reolve comflict and remove unncessary code using compare with magento 2 core file --- .../Swatches/view/frontend/web/js/swatch-renderer.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js index 3693f86984602..220b06fea1dc0 100644 --- a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js +++ b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js @@ -1242,15 +1242,6 @@ define([ }); } - if (typeof gallery === 'undefined') { - context.find(this.options.mediaGallerySelector).on('gallery:loaded', function (loadedGallery) { - loadedGallery = context.find(this.options.mediaGallerySelector).data('gallery'); - loadedGallery.first(); - }.bind(this)); - } else { - gallery.first(); - } - } else if (justAnImage && justAnImage.img) { context.find('.product-image-photo').attr('src', justAnImage.img); } 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 034/345] 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 8bb73fbb35116..74127484e23b4 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 2cd74c53baa13..de2363da3e7a5 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 035/345] 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 de2363da3e7a5..f9f6e6b37c5dd 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 fd4ed41edfd534a1037f13c21aa9d16a8d07c37a Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Thu, 7 Feb 2019 13:06:30 +0200 Subject: [PATCH 036/345] magento:magento2 Missed form validation in Admin Order Address Edit route sales/order/address --- ...rderWithAndWithoutFieldsValidationTest.xml | 79 +++++++++++++++++++ .../Customer/Test/Repository/Address.xml | 15 ++++ 2 files changed, 94 insertions(+) create mode 100644 app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutFieldsValidationTest.xml diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutFieldsValidationTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutFieldsValidationTest.xml new file mode 100644 index 0000000000000..83c9072721836 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutFieldsValidationTest.xml @@ -0,0 +1,79 @@ +<?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="AdminSubmitsOrderWithAndWithoutFieldsValidationTest"> + <annotations> + <features value="Sales"/> + <stories value="Create orders"/> + <title value="Fields validation is required to create an order from Admin Panel"/> + <description value="Admin should not be able to submit orders without invalid address fields"/> + <group value="sales"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + </after> + <!--Create order via Admin--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <comment userInput="Admin creates order" stepKey="adminCreateOrderComment"/> + <!--<actionGroup ref="navigateToNewOrderPageNewCustomer" stepKey="navigateToNewOrderPage"/>--> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderIndexPage"/> + <waitForPageLoad stepKey="waitForIndexPageLoad"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Orders" stepKey="seeIndexPageTitle"/> + <click selector="{{AdminOrdersGridSection.createNewOrder}}" stepKey="clickCreateNewOrder"/> + <click selector="{{AdminOrderFormActionSection.CreateNewCustomer}}" stepKey="clickCreateCustomer"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Create New Order" stepKey="seeNewOrderPageTitle"/> + + <!--Check if order can be submitted without the required fields including email address--> + <actionGroup ref="checkRequiredFieldsNewOrderForm" stepKey="checkRequiredFieldsNewOrder" after="seeNewOrderPageTitle"/> + <scrollToTopOfPage stepKey="scrollToTopOfOrderFormPage" after="checkRequiredFieldsNewOrder"/> + <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToOrder" after="scrollToTopOfOrderFormPage"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + + <!--Fill customer group and customer email--> + <selectOption selector="{{AdminOrderFormAccountSection.group}}" userInput="{{GeneralCustomerGroup.code}}" stepKey="selectCustomerGroup" after="addSimpleProductToOrder"/> + <fillField selector="{{AdminOrderFormAccountSection.email}}" userInput="{{Simple_US_Customer.email}}" stepKey="fillCustomerEmail" after="selectCustomerGroup"/> + + <!--Fill wrong customer address information--> + <actionGroup ref="fillOrderCustomerInformation" stepKey="fillCustomerAddress" after="fillCustomerEmail"> + <argument name="customer" value="Simple_US_Customer"/> + <argument name="address" value="US_address_TX_Wrong_Validation"/> + </actionGroup> + <!-- Select shipping --> + <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRateShipping" after="fillCustomerAddress"/> + + <!--Verify totals on Order page--> + <see selector="{{AdminOrderFormTotalSection.total('Subtotal')}}" userInput="${{AdminOrderSimpleProduct.subtotal}}" stepKey="seeOrderSubTotal" after="selectFlatRateShipping"/> + <see selector="{{AdminOrderFormTotalSection.total('Shipping')}}" userInput="${{AdminOrderSimpleProduct.shipping}}" stepKey="seeOrderShipping" after="seeOrderSubTotal"/> + <scrollTo selector="{{AdminOrderFormTotalSection.grandTotal}}" stepKey="scrollToOrderGrandTotal"/> + <see selector="{{AdminOrderFormTotalSection.grandTotal}}" userInput="${{AdminOrderSimpleProduct.grandTotal}}" stepKey="seeCorrectGrandTotal" after="scrollToOrderGrandTotal"/> + + <!--Submit Order and verify information--> + <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="clickSubmitOrder" after="seeCorrectGrandTotal"/> + <see selector="{{AdminOrderFormBillingAddressSection.firstNameError}}" userInput="Please enter less or equal than 255 symbols." stepKey="firstNameError" after="clickSubmitOrder"/> + + <!--Fill correct customer address information--> + <actionGroup ref="fillOrderCustomerInformation" stepKey="fillCustomerAddress" after="firstNameError"> + <argument name="customer" value="Simple_US_Customer"/> + <argument name="address" value="US_address_TX"/> + </actionGroup> + <!--Submit Order and verify information--> + <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="clickSubmitOrder" after="fillCustomerAddress"/> + <seeInCurrentUrl url="{{AdminOrderDetailsPage.url}}" stepKey="seeViewOrderPage" after="clickSubmitOrder"/> + <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the order." stepKey="seeSuccessMessage" after="seeViewOrderPage"/> + </test> + </tests> \ No newline at end of file diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Repository/Address.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/Repository/Address.xml index 32f5d54340026..c182f59c2474b 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/Repository/Address.xml +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Repository/Address.xml @@ -118,6 +118,21 @@ <field name="default_shipping" xsi:type="string">Yes</field> </dataset> + <dataset name="US_address_TX_Wrong_Validation"> + <field name="firstname" xsi:type="string">LoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsum</field> + <field name="lastname" xsi:type="string">Doe</field> + <field name="email" xsi:type="string">John.Doe%isolation%@example.com</field> + <field name="company" xsi:type="string">Magento %isolation%</field> + <field name="street" xsi:type="string">7700 W. Parmer Lane Bldg. D</field> + <field name="city" xsi:type="string">Austin</field> + <field name="region_id" xsi:type="string">Texas</field> + <field name="postcode" xsi:type="string">78729</field> + <field name="country_id" xsi:type="string">United States</field> + <field name="telephone" xsi:type="string">512-691-4400</field> + <field name="default_billing" xsi:type="string">Yes</field> + <field name="default_shipping" xsi:type="string">Yes</field> + </dataset> + <dataset name="US_address_NY"> <field name="firstname" xsi:type="string">John</field> <field name="lastname" xsi:type="string">Doe</field> From 40d44c2884cfb56850ce929b1c9e6577b489efe8 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Fri, 25 Jan 2019 16:23:29 +0200 Subject: [PATCH 037/345] ENGCOM-3725: MTF test fix. --- .../Backend/Test/Page/Adminhtml/Dashboard.xml | 1 + .../AssertUserRoleRestrictedAccess.php | 5 +- ...ssertUserRoleRestrictedAccessWithError.php | 17 ++++++ .../Constraint/AssertUserSuccessLogin.php | 10 +++- .../AssertUserSuccessLoginWithError.php | 20 +++++++ .../TestCase/UpdateAdminUserEntityTest.xml | 2 +- .../UpdateAdminUserRoleEntityTest.php | 5 ++ .../UpdateAdminUserRoleEntityTest.xml | 4 +- .../Test/TestStep/CloseErrorAlertStep.php | 53 +++++++++++++++++++ .../Test/TestStep/LoginUserOnBackendStep.php | 4 +- .../LoginUserOnBackendWithErrorStep.php | 53 +++++++++++++++++++ .../Test/TestStep/LogoutUserOnBackendStep.php | 1 - .../LogoutUserOnBackendWithErrorStep.php | 40 ++++++++++++++ .../tests/app/Magento/User/Test/etc/di.xml | 14 +++++ 14 files changed, 219 insertions(+), 10 deletions(-) create mode 100644 dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserRoleRestrictedAccessWithError.php create mode 100644 dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserSuccessLoginWithError.php create mode 100644 dev/tests/functional/tests/app/Magento/User/Test/TestStep/CloseErrorAlertStep.php create mode 100644 dev/tests/functional/tests/app/Magento/User/Test/TestStep/LoginUserOnBackendWithErrorStep.php create mode 100644 dev/tests/functional/tests/app/Magento/User/Test/TestStep/LogoutUserOnBackendWithErrorStep.php create mode 100644 dev/tests/functional/tests/app/Magento/User/Test/etc/di.xml diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Page/Adminhtml/Dashboard.xml b/dev/tests/functional/tests/app/Magento/Backend/Test/Page/Adminhtml/Dashboard.xml index adae65a1d06d6..799f9e30fd972 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Page/Adminhtml/Dashboard.xml +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Page/Adminhtml/Dashboard.xml @@ -17,5 +17,6 @@ <block name="accessDeniedBlock" class="Magento\Backend\Test\Block\Denied" locator="#anchor-content" strategy="css selector" /> <block name="systemMessageDialog" class="Magento\AdminNotification\Test\Block\System\Messages" locator='.ui-popup-message .modal-inner-wrap' strategy="css selector" /> <block name="applicationVersion" class="Magento\Backend\Test\Block\Version" locator="body" strategy="css selector" /> + <block name="modalMessage" class="Magento\Ui\Test\Block\Adminhtml\Modal" locator=".modal-popup>.modal-inner-wrap" strategy="css selector" /> </page> </config> diff --git a/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserRoleRestrictedAccess.php b/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserRoleRestrictedAccess.php index f7c56ae1b9653..ecfbc8d353888 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserRoleRestrictedAccess.php +++ b/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserRoleRestrictedAccess.php @@ -10,6 +10,7 @@ use Magento\Mtf\Client\BrowserInterface; use Magento\Mtf\Constraint\AbstractConstraint; use Magento\User\Test\Fixture\User; +use Magento\User\Test\TestStep\LoginUserOnBackendWithErrorStep; /** * Asserts that user has only related permissions. @@ -18,6 +19,8 @@ class AssertUserRoleRestrictedAccess extends AbstractConstraint { const DENIED_ACCESS = 'Sorry, you need permissions to view this content.'; + protected $loginStep = 'Magento\User\Test\TestStep\LoginUserOnBackendStep'; + /** * Asserts that user has only related permissions. * @@ -36,7 +39,7 @@ public function processAssert( $denyUrl ) { $this->objectManager->create( - \Magento\User\Test\TestStep\LoginUserOnBackendStep::class, + $this->loginStep, ['user' => $user] )->run(); diff --git a/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserRoleRestrictedAccessWithError.php b/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserRoleRestrictedAccessWithError.php new file mode 100644 index 0000000000000..b001893abb4c4 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserRoleRestrictedAccessWithError.php @@ -0,0 +1,17 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\User\Test\Constraint; + +/** + * @inheritdoc + */ +class AssertUserRoleRestrictedAccessWithError extends AssertUserRoleRestrictedAccess +{ + protected $loginStep = 'Magento\User\Test\TestStep\LoginUserOnBackendWithErrorStep'; +} diff --git a/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserSuccessLogin.php b/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserSuccessLogin.php index c0c04628f744d..c4645e6e8916a 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserSuccessLogin.php +++ b/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserSuccessLogin.php @@ -7,14 +7,20 @@ namespace Magento\User\Test\Constraint; use Magento\Backend\Test\Page\Adminhtml\Dashboard; -use Magento\User\Test\Fixture\User; use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\User\Test\Fixture\User; +use Magento\User\Test\TestStep\LoginUserOnBackendStep; /** * Verify whether customer has logged in to the Backend. */ class AssertUserSuccessLogin extends AbstractConstraint { + /** + * @var string + */ + protected $loginStep = LoginUserOnBackendStep::class; + /** * Verify whether customer has logged in to the Backend. * @@ -25,7 +31,7 @@ class AssertUserSuccessLogin extends AbstractConstraint public function processAssert(User $user, Dashboard $dashboard) { $this->objectManager->create( - \Magento\User\Test\TestStep\LoginUserOnBackendStep::class, + $this->loginStep, ['user' => $user] )->run(); \PHPUnit\Framework\Assert::assertTrue( diff --git a/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserSuccessLoginWithError.php b/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserSuccessLoginWithError.php new file mode 100644 index 0000000000000..9fed1f4df8573 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/User/Test/Constraint/AssertUserSuccessLoginWithError.php @@ -0,0 +1,20 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\User\Test\Constraint; + +use Magento\User\Test\TestStep\LoginUserOnBackendWithErrorStep; + +/** + * Verify whether customer has logged in to the Backend with error alert. + */ +class AssertUserSuccessLoginWithError extends AssertUserSuccessLogin +{ + /** + * @var string + */ + protected $loginStep = LoginUserOnBackendWithErrorStep::class; +} diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserEntityTest.xml b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserEntityTest.xml index f7de667cf17ac..a89d1ede80112 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserEntityTest.xml @@ -32,7 +32,7 @@ <constraint name="Magento\User\Test\Constraint\AssertUserInGrid" /> <constraint name="Magento\User\Test\Constraint\AssertUserSuccessLogOut" /> <constraint name="Magento\User\Test\Constraint\AssertUserSuccessLogin" /> - <constraint name="Magento\User\Test\Constraint\AssertUserRoleRestrictedAccess" /> + <constraint name="Magento\User\Test\Constraint\AssertUserRoleRestrictedAccessWithError" /> </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserRoleEntityTest.php b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserRoleEntityTest.php index cc1d0fc980fbf..58450abc71633 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserRoleEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserRoleEntityTest.php @@ -121,6 +121,11 @@ public function testUpdateAdminUserRolesEntity( */ public function tearDown() { + sleep(3); + $modalMessage = $this->dashboard->getModalMessage(); + if ($modalMessage->isVisible()) { + $modalMessage->acceptAlert(); + } $this->dashboard->getAdminPanelHeader()->logOut(); } } diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserRoleEntityTest.xml b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserRoleEntityTest.xml index 224ccbce10f96..db6a13d0f3551 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserRoleEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserRoleEntityTest.xml @@ -29,8 +29,8 @@ <constraint name="Magento\User\Test\Constraint\AssertRoleSuccessSaveMessage" /> <constraint name="Magento\User\Test\Constraint\AssertRoleInGrid" /> <constraint name="Magento\User\Test\Constraint\AssertUserSuccessLogOut" /> - <constraint name="Magento\User\Test\Constraint\AssertUserSuccessLogin" /> - <constraint name="Magento\User\Test\Constraint\AssertUserRoleRestrictedAccess" /> + <constraint name="Magento\User\Test\Constraint\AssertUserSuccessLoginWithError"/> + <constraint name="Magento\User\Test\Constraint\AssertUserRoleRestrictedAccessWithError" /> </variation> <variation name="UpdateAdminUserRoleEntityTestVariation3"> <data name="user/dataset" xsi:type="string">custom_admin_with_default_role</data> diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestStep/CloseErrorAlertStep.php b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/CloseErrorAlertStep.php new file mode 100644 index 0000000000000..b8a3fce5de214 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/CloseErrorAlertStep.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\User\Test\TestStep; + +use Magento\Backend\Test\Page\Adminhtml\Dashboard; +use Magento\Mtf\Client\BrowserInterface; +use Magento\Mtf\TestStep\TestStepInterface; + +/** + * Close access error modal message. + */ +class CloseErrorAlertStep implements TestStepInterface +{ + /** + * @var Dashboard + */ + private $dashboard; + + /** + * @var BrowserInterface + */ + private $browser; + + /** + * @param Dashboard $dashboard + * @param BrowserInterface $browser + */ + public function __construct( + Dashboard $dashboard, + BrowserInterface $browser + ) { + $this->dashboard = $dashboard; + $this->browser = $browser; + } + + /** + * @inheritdoc + */ + public function run() + { + $modalMessage = $this->dashboard->getModalMessage(); + $this->browser->waitUntil( + function () use ($modalMessage) { + return $modalMessage->isVisible() ? true : null; + } + ); + $modalMessage->acceptAlert(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LoginUserOnBackendStep.php b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LoginUserOnBackendStep.php index 4f7e6deed7a85..c244e27d42899 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LoginUserOnBackendStep.php +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LoginUserOnBackendStep.php @@ -50,7 +50,7 @@ class LoginUserOnBackendStep implements TestStepInterface * * @var BrowserInterface */ - private $browser; + protected $browser; /** * Array of error messages on admin login form. @@ -108,8 +108,6 @@ public function run() } } } - - $this->dashboard->getSystemMessageDialog()->closePopup(); } /** diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LoginUserOnBackendWithErrorStep.php b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LoginUserOnBackendWithErrorStep.php new file mode 100644 index 0000000000000..094f90d0a5d70 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LoginUserOnBackendWithErrorStep.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\User\Test\TestStep; + +use Magento\Backend\Test\Page\AdminAuthLogin; +use Magento\Backend\Test\Page\Adminhtml\Dashboard; +use Magento\Mtf\Client\BrowserInterface; +use Magento\User\Test\Fixture\User; + +/** + * Login user on backend with access error. + */ +class LoginUserOnBackendWithErrorStep extends LoginUserOnBackendStep +{ + /** + * @var CloseErrorAlertStep + */ + private $closeErrorAlertStep; + + /** + * @param LogoutUserOnBackendStep $logoutUserOnBackendStep + * @param AdminAuthLogin $adminAuth + * @param User $user + * @param Dashboard $dashboard + * @param BrowserInterface $browser + */ + public function __construct( + LogoutUserOnBackendStep $logoutUserOnBackendStep, + AdminAuthLogin $adminAuth, + User $user, + Dashboard $dashboard, + BrowserInterface $browser, + CloseErrorAlertStep $closeErrorAlertStep + ) { + parent::__construct($logoutUserOnBackendStep, $adminAuth, $user, $dashboard, $browser); + $this->closeErrorAlertStep = $closeErrorAlertStep; + } + + /** + * Run step flow. + * + * @return void + */ + public function run() + { + parent::run(); + $this->closeErrorAlertStep->run(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LogoutUserOnBackendStep.php b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LogoutUserOnBackendStep.php index 70a4080a0b4d5..7f366312bba24 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LogoutUserOnBackendStep.php +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LogoutUserOnBackendStep.php @@ -48,7 +48,6 @@ public function __construct(AdminAuthLogin $adminAuth, Dashboard $dashboard) public function run() { $this->adminAuth->open(); - $this->dashboard->getSystemMessageDialog()->closePopup(); $this->dashboard->getAdminPanelHeader()->logOut(); } } diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LogoutUserOnBackendWithErrorStep.php b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LogoutUserOnBackendWithErrorStep.php new file mode 100644 index 0000000000000..ce49e86afc065 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LogoutUserOnBackendWithErrorStep.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\User\Test\TestStep; + +use Magento\Backend\Test\Page\AdminAuthLogin; +use Magento\Backend\Test\Page\Adminhtml\Dashboard; + +/** + * Logout user on backend with access error. + */ +class LogoutUserOnBackendWithErrorStep extends LogoutUserOnBackendStep +{ + /** + * @var CloseErrorAlertStep + */ + private $closeErrorAlertStep; + + public function __construct( + AdminAuthLogin $adminAuth, + Dashboard $dashboard, + CloseErrorAlertStep $closeErrorAlertStep + ) { + parent::__construct($adminAuth, $dashboard); + $this->closeErrorAlertStep = $closeErrorAlertStep; + } + + /** + * @inheritdoc + */ + public function run() + { + $this->adminAuth->open(); + $this->closeErrorAlertStep->run(); + $this->dashboard->getAdminPanelHeader()->logOut(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/User/Test/etc/di.xml b/dev/tests/functional/tests/app/Magento/User/Test/etc/di.xml new file mode 100644 index 0000000000000..1298bd56a8fb0 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/User/Test/etc/di.xml @@ -0,0 +1,14 @@ +<?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\User\Test\TestStep\LoginUserOnBackendWithErrorStep"> + <arguments> + <argument name="logoutUserOnBackendStep" xsi:type="object">\Magento\User\Test\TestStep\LogoutUserOnBackendWithErrorStep</argument> + </arguments> + </type> +</config> From 669980aab826a151ff3c100352294460d84c3df1 Mon Sep 17 00:00:00 2001 From: suryakant <suryakant.makwana@krishtechnolabs.com> Date: Fri, 8 Feb 2019 18:10:28 +0530 Subject: [PATCH 038/345] Added space above error message. --- .../Magento/luma/Magento_Catalog/web/css/source/_module.less | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less index 501a1d2918d6a..ba30763110d67 100644 --- a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less @@ -391,6 +391,8 @@ .box-tocart { &:extend(.abs-box-tocart all); + margin-top: @indent__s; + .field.qty { } 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 039/345] 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 74127484e23b4..81217430c083f 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 f9f6e6b37c5dd..40b80be48efd3 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 7d6cca6c944eb694d40ae169fe8443bbd75486c8 Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Mon, 11 Feb 2019 07:52:36 +0200 Subject: [PATCH 040/345] magento:magento2 Missed form validation in Admin Order Address Edit route sales/order/address --- .../Customer/Test/Mftf/Data/AddressData.xml | 16 ++++++++++++++++ .../Magento/Customer/Test/Repository/Address.xml | 15 --------------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml index d090620145105..93556bc2bff2f 100755 --- a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml @@ -49,6 +49,22 @@ <data key="default_shipping">Yes</data> <requiredEntity type="region">RegionTX</requiredEntity> </entity> + <entity name="US_address_TX_Wrong_Validation" type="address"> + <data key="firstname">LoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsum</data> + <data key="lastname">Doe</data> + <data key="company">Magento</data> + <array key="street"> + <item>7700 West Parmer Lane</item> + </array> + <data key="city">Austin</data> + <data key="state">Texas</data> + <data key="country_id">US</data> + <data key="postcode">78729</data> + <data key="telephone">512-345-6789</data> + <data key="default_billing">Yes</data> + <data key="default_shipping">Yes</data> + <requiredEntity type="region">RegionTX</requiredEntity> + </entity> <entity name="US_Address_NY" type="address"> <data key="firstname">John</data> <data key="lastname">Doe</data> diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Repository/Address.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/Repository/Address.xml index c182f59c2474b..32f5d54340026 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/Repository/Address.xml +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Repository/Address.xml @@ -118,21 +118,6 @@ <field name="default_shipping" xsi:type="string">Yes</field> </dataset> - <dataset name="US_address_TX_Wrong_Validation"> - <field name="firstname" xsi:type="string">LoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsum</field> - <field name="lastname" xsi:type="string">Doe</field> - <field name="email" xsi:type="string">John.Doe%isolation%@example.com</field> - <field name="company" xsi:type="string">Magento %isolation%</field> - <field name="street" xsi:type="string">7700 W. Parmer Lane Bldg. D</field> - <field name="city" xsi:type="string">Austin</field> - <field name="region_id" xsi:type="string">Texas</field> - <field name="postcode" xsi:type="string">78729</field> - <field name="country_id" xsi:type="string">United States</field> - <field name="telephone" xsi:type="string">512-691-4400</field> - <field name="default_billing" xsi:type="string">Yes</field> - <field name="default_shipping" xsi:type="string">Yes</field> - </dataset> - <dataset name="US_address_NY"> <field name="firstname" xsi:type="string">John</field> <field name="lastname" xsi:type="string">Doe</field> 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 041/345] 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 81217430c083f..820144c7f1cb1 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 042/345] 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 0000000000000..6b98686574321 --- /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 ceb5556b2673d..c2652f33f7606 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 40b80be48efd3..c896c29f42a30 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 043/345] 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 820144c7f1cb1..511fe30f79dcd 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 c896c29f42a30..f46328ac151b1 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 044/345] 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 0000000000000..5e4c6b39f3c36 --- /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 d3f21dda9ccde..e9076021a5b73 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 9ea0d1a823235..6cb32d70ee1d8 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 b38c5c2cda3ad..7a166b47256cb 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 fa49579fe1bf893349d78f5bc1c9661043784590 Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Wed, 13 Feb 2019 08:00:06 +0200 Subject: [PATCH 045/345] Sorting by Websites not working in product grid in backoffice #20511 --- .../Catalog/Ui/Component/Listing/Columns/Websites.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php index f67585c6401f8..5dd1fc578d15f 100644 --- a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php +++ b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php @@ -50,13 +50,14 @@ public function __construct( ContextInterface $context, UiComponentFactory $uiComponentFactory, StoreManagerInterface $storeManager, - Helper $resourceHelper, + Helper $resourceHelper = null, array $components = [], array $data = [] ) { parent::__construct($context, $uiComponentFactory, $components, $data); + $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); $this->storeManager = $storeManager; - $this->_resourceHelper = $resourceHelper; + $this->_resourceHelper = $resourceHelper ?: $objectManager->get(Helper::class); } /** From 8170bd78fc71757f78abb5a1a0bb06f8f9d9c621 Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Wed, 13 Feb 2019 11:03:50 +0200 Subject: [PATCH 046/345] Sorting by Websites not working in product grid in backoffice #20511 --- .../Catalog/Ui/Component/Listing/Columns/Websites.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php index 5dd1fc578d15f..5571f6e70fa74 100644 --- a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php +++ b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php @@ -8,7 +8,7 @@ use Magento\Framework\View\Element\UiComponentFactory; use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Store\Model\StoreManagerInterface; -use \Magento\Framework\DB\Helper; +use Magento\Framework\DB\Helper; /** * @api @@ -42,17 +42,17 @@ class Websites extends \Magento\Ui\Component\Listing\Columns\Column * @param ContextInterface $context * @param UiComponentFactory $uiComponentFactory * @param StoreManagerInterface $storeManager - * @param Helper $resourceHelper * @param array $components * @param array $data + * @param Helper $resourceHelper */ public function __construct( ContextInterface $context, UiComponentFactory $uiComponentFactory, StoreManagerInterface $storeManager, - Helper $resourceHelper = null, array $components = [], - array $data = [] + array $data = [], + Helper $resourceHelper = null ) { parent::__construct($context, $uiComponentFactory, $components, $data); $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); From d09b512a284ea8c52cc2dcec8ca8350869a997e9 Mon Sep 17 00:00:00 2001 From: Oscar Recio <osrecio@gmail.com> Date: Wed, 13 Feb 2019 10:22:14 +0100 Subject: [PATCH 047/345] Add Mexico Regions --- .../Setup/Patch/Data/AddDataForIndia.php | 2 +- .../Setup/Patch/Data/AddDataForMexico.php | 127 ++++++++++++++++++ 2 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Directory/Setup/Patch/Data/AddDataForMexico.php diff --git a/app/code/Magento/Directory/Setup/Patch/Data/AddDataForIndia.php b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForIndia.php index 69d500960d3f0..8337d17051efa 100644 --- a/app/code/Magento/Directory/Setup/Patch/Data/AddDataForIndia.php +++ b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForIndia.php @@ -29,7 +29,7 @@ class AddDataForIndia implements DataPatchInterface, PatchVersionInterface private $dataInstallerFactory; /** - * AddDataForCroatia constructor. + * AddDataForIndia constructor. * * @param ModuleDataSetupInterface $moduleDataSetup * @param \Magento\Directory\Setup\DataInstallerFactory $dataInstallerFactory diff --git a/app/code/Magento/Directory/Setup/Patch/Data/AddDataForMexico.php b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForMexico.php new file mode 100644 index 0000000000000..32bdf90800d6b --- /dev/null +++ b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForMexico.php @@ -0,0 +1,127 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Directory\Setup\Patch\Data; + +use Magento\Directory\Setup\DataInstaller; +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; +use Magento\Framework\Setup\Patch\PatchVersionInterface; + +/** + * Adds Mexican States + */ +class AddDataForMexico implements DataPatchInterface, PatchVersionInterface +{ + /** + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + + /** + * @var \Magento\Directory\Setup\DataInstallerFactory + */ + private $dataInstallerFactory; + + /** + * @param ModuleDataSetupInterface $moduleDataSetup + * @param \Magento\Directory\Setup\DataInstallerFactory $dataInstallerFactory + */ + public function __construct( + ModuleDataSetupInterface $moduleDataSetup, + \Magento\Directory\Setup\DataInstallerFactory $dataInstallerFactory + ) { + $this->moduleDataSetup = $moduleDataSetup; + $this->dataInstallerFactory = $dataInstallerFactory; + } + + /** + * @inheritdoc + */ + public function apply() + { + /** @var DataInstaller $dataInstaller */ + $dataInstaller = $this->dataInstallerFactory->create(); + $dataInstaller->addCountryRegions( + $this->moduleDataSetup->getConnection(), + $this->getDataForMexico() + ); + } + + /** + * Mexican states data. + * + * @return array + */ + private function getDataForMexico() + { + return [ + ['MX', 'AGU', 'Aguascalientes'], + ['MX', 'BCN', 'Baja California'], + ['MX', 'BCS', 'Baja California Sur'], + ['MX', 'CAM', 'Campeche'], + ['MX', 'CHP', 'Chiapas'], + ['MX', 'CHH', 'Chihuahua'], + ['MX', 'CMX', 'Ciudad de México'], + ['MX', 'COA', 'Coahuila'], + ['MX', 'COL', 'Colima'], + ['MX', 'DUR', 'Durango'], + ['MX', 'MEX', 'Estado de México'], + ['MX', 'GUA', 'Guanajuato'], + ['MX', 'GRO', 'Guerrero'], + ['MX', 'HID', 'Hidalgo'], + ['MX', 'JAL', 'Jalisco'], + ['MX', 'MIC', 'Michoacán'], + ['MX', 'MOR', 'Morelos'], + ['MX', 'NAY', 'Nayarit'], + ['MX', 'NLE', 'Nuevo León'], + ['MX', 'OAX', 'Oaxaca'], + ['MX', 'PUE', 'Puebla'], + ['MX', 'QUE', 'Querétaro'], + ['MX', 'ROO', 'Quintana Roo'], + ['MX', 'SLP', 'San Luis Potosí'], + ['MX', 'SIN', 'Sinaloa'], + ['MX', 'SON', 'Sonora'], + ['MX', 'TAB', 'Tabasco'], + ['MX', 'TAM', 'Tamaulipas'], + ['MX', 'TLA', 'Tlaxcala'], + ['MX', 'VER', 'Veracruz'], + ['MX', 'YUC', 'Yucatán'], + ['MX', 'ZAC', 'Zacatecas'] + ]; + } + + /** + * @inheritdoc + */ + public static function getDependencies() + { + return [ + InitializeDirectoryData::class, + AddDataForAustralia::class, + AddDataForCroatia::class, + AddDataForIndia::class, + ]; + } + + /** + * @inheritdoc + */ + public static function getVersion() + { + return '2.0.4'; + } + + /** + * @inheritdoc + */ + public function getAliases() + { + return []; + } +} From a572ae2a1c5c86519ac3e0fd1eaf4d3157e80ef7 Mon Sep 17 00:00:00 2001 From: Denys Saltanahmedov <d.saltanakhmedov@atwix.com> Date: Wed, 13 Feb 2019 14:53:09 +0200 Subject: [PATCH 048/345] Fixing a bug with replace method in Advanced Prices in CSV Import #18761 --- .../Model/Import/AdvancedPricing.php | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing.php b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing.php index 2e17e734b1e60..b29b8fdecf897 100644 --- a/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing.php +++ b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing.php @@ -368,8 +368,8 @@ protected function saveAndReplaceAdvancedPrices() $this->_cachedSkuToDelete = null; } $listSku = []; + $tierPrices = []; while ($bunch = $this->_dataSourceModel->getNextBunch()) { - $tierPrices = []; foreach ($bunch as $rowNum => $rowData) { if (!$this->validateRow($rowData, $rowNum)) { $this->addRowError(ValidatorInterface::ERROR_SKU_IS_EMPTY, $rowNum); @@ -397,15 +397,8 @@ protected function saveAndReplaceAdvancedPrices() ]; } } - if (\Magento\ImportExport\Model\Import::BEHAVIOR_REPLACE == $behavior) { - if ($listSku) { - $this->processCountNewPrices($tierPrices); - if ($this->deleteProductTierPrices(array_unique($listSku), self::TABLE_TIER_PRICE)) { - $this->saveProductPrices($tierPrices, self::TABLE_TIER_PRICE); - $this->setUpdatedAt($listSku); - } - } - } elseif (\Magento\ImportExport\Model\Import::BEHAVIOR_APPEND == $behavior) { + + if (\Magento\ImportExport\Model\Import::BEHAVIOR_APPEND == $behavior) { $this->processCountExistingPrices($tierPrices, self::TABLE_TIER_PRICE) ->processCountNewPrices($tierPrices); $this->saveProductPrices($tierPrices, self::TABLE_TIER_PRICE); @@ -414,6 +407,17 @@ protected function saveAndReplaceAdvancedPrices() } } } + + if (\Magento\ImportExport\Model\Import::BEHAVIOR_REPLACE == $behavior) { + if ($listSku) { + $this->processCountNewPrices($tierPrices); + if ($this->deleteProductTierPrices(array_unique($listSku), self::TABLE_TIER_PRICE)) { + $this->saveProductPrices($tierPrices, self::TABLE_TIER_PRICE); + $this->setUpdatedAt($listSku); + } + } + } + return $this; } From 6692e087dd2d5f2dcc4d16f35407ae6630ea5b2b Mon Sep 17 00:00:00 2001 From: Denys Saltanahmedov <d.saltanakhmedov@atwix.com> Date: Wed, 13 Feb 2019 16:53:35 +0200 Subject: [PATCH 049/345] fixing static tests --- .../Model/Import/AdvancedPricing.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing.php b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing.php index b29b8fdecf897..88bb8d4e3608f 100644 --- a/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing.php +++ b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing.php @@ -185,6 +185,7 @@ class AdvancedPricing extends \Magento\ImportExport\Model\Import\Entity\Abstract * @param AdvancedPricing\Validator\Website $websiteValidator * @param AdvancedPricing\Validator\TierPrice $tierPriceValidator * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @throws \Exception */ public function __construct( \Magento\Framework\Json\Helper\Data $jsonHelper, @@ -255,6 +256,7 @@ public function getEntityTypeCode() * @param array $rowData * @param int $rowNum * @return bool + * @throws \Zend_Validate_Exception */ public function validateRow(array $rowData, $rowNum) { @@ -308,6 +310,7 @@ protected function _importData() * Save advanced pricing * * @return $this + * @throws \Exception */ public function saveAdvancedPricing() { @@ -319,6 +322,7 @@ public function saveAdvancedPricing() * Deletes Advanced price data from raw data. * * @return $this + * @throws \Exception */ public function deleteAdvancedPricing() { @@ -347,6 +351,7 @@ public function deleteAdvancedPricing() * Replace advanced pricing * * @return $this + * @throws \Exception */ public function replaceAdvancedPricing() { @@ -360,6 +365,7 @@ public function replaceAdvancedPricing() * @return $this * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) + * @throws \Exception */ protected function saveAndReplaceAdvancedPrices() { @@ -427,6 +433,7 @@ protected function saveAndReplaceAdvancedPrices() * @param array $priceData * @param string $table * @return $this + * @throws \Exception */ protected function saveProductPrices(array $priceData, $table) { @@ -458,6 +465,7 @@ protected function saveProductPrices(array $priceData, $table) * @param array $listSku * @param string $table * @return boolean + * @throws \Exception */ protected function deleteProductTierPrices(array $listSku, $table) { @@ -535,6 +543,7 @@ protected function getCustomerGroupId($customerGroup) * Retrieve product skus * * @return array + * @throws \Exception */ protected function retrieveOldSkus() { @@ -555,6 +564,7 @@ protected function retrieveOldSkus() * @param array $prices * @param string $table * @return $this + * @throws \Exception */ protected function processCountExistingPrices($prices, $table) { From a03aa67e100535f74c1a7ec502a1499f67ea3632 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Wed, 13 Feb 2019 17:20:54 +0200 Subject: [PATCH 050/345] MC-13950: Fix MFTF test --- .../Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml index eed9f80c251c8..a116a23dc02cd 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml @@ -14,7 +14,6 @@ <argument name="orderId" type="string"/> </arguments> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderGridPage"/> - <waitForPageLoad stepKey="waitForOrderGridLoad"/> <conditionalClick selector="{{AdminOrdersGridSection.clearFilters}}" dependentSelector="{{AdminOrdersGridSection.clearFilters}}" visible="true" stepKey="clearExistingOrderFilters"/> <click selector="{{AdminOrdersGridSection.filters}}" stepKey="openOrderGridFilters"/> <fillField selector="{{AdminOrdersGridSection.idFilter}}" userInput="{{orderId}}" stepKey="fillOrderIdFilter"/> @@ -74,4 +73,9 @@ <waitForPageLoad stepKey="waitForPageToLoad"/> <conditionalClick selector="{{AdminOrdersGridSection.clearFilters}}" dependentSelector="{{AdminOrdersGridSection.enabledFilters}}" visible="true" stepKey="clickOnButtonToRemoveFiltersIfPresent"/> </actionGroup> + + <actionGroup name="OpenOrderById" extends="filterOrderGridById"> + <click selector="{{AdminDataGridTableSection.firstRow}}" after="clickOrderApplyFilters" stepKey="openOrderViewPage"/> + <waitForPageLoad after="openOrderViewPage" stepKey="waitForOrderViewPageOpened"/> + </actionGroup> </actionGroups> From f0d1bf7a3ae6c259a5ddc6ac2949ad079f0ef50c Mon Sep 17 00:00:00 2001 From: suryakant <suryakant.makwana@krishtechnolabs.com> Date: Thu, 14 Feb 2019 17:08:38 +0530 Subject: [PATCH 051/345] Fixed for only group product. --- .../luma/Magento_Catalog/web/css/source/_module.less | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less index ba30763110d67..48fa5ecde302b 100644 --- a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less @@ -293,6 +293,12 @@ } } + .page-product-grouped { + .box-tocart { + margin-top: @indent__s; + } + } + .product-options-wrapper { .fieldset-product-options-inner { .legend { @@ -391,7 +397,6 @@ .box-tocart { &:extend(.abs-box-tocart all); - margin-top: @indent__s; .field.qty { } From 5bf736481300478ed6ce2dfd3c3ac5dd6f19a5a9 Mon Sep 17 00:00:00 2001 From: Dusan Lukic <ldusan84@gmail.com> Date: Thu, 14 Feb 2019 21:31:49 +0100 Subject: [PATCH 052/345] Throw an exception where category doesn't exist --- .../CatalogGraphQl/Model/Resolver/CategoryTree.php | 12 +++++++----- .../Magento/GraphQl/Catalog/CategoryTest.php | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php index 7e7b0db8240af..1783a5cd9a7e5 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php @@ -11,6 +11,7 @@ use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\GraphQl\Query\ResolverInterface; /** @@ -72,11 +73,12 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $rootCategoryId = $this->getCategoryId($args); $categoriesTree = $this->categoryTree->getTree($info, $rootCategoryId); - if (!empty($categoriesTree) && ($categoriesTree->count() > 0)) { - $result = $this->extractDataFromCategoryTree->execute($categoriesTree); - return current($result); - } else { - return null; + + if (empty($categoriesTree) || ($categoriesTree->count() == 0)) { + throw new GraphQlNoSuchEntityException(__('Category doesn\'t exist')); } + + $result = $this->extractDataFromCategoryTree->execute($categoriesTree); + return current($result); } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php index 17a7bec180585..c586665a16d51 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php @@ -123,7 +123,7 @@ public function testNonExistentCategoryWithProductCount() QUERY; $response = $this->graphQlQuery($query); - $expectedResponse = ['category' => null]; + $expectedResponse = ['errors' => ['message' => 'Category doesn\'t exist']]; $this->assertEquals($expectedResponse, $response); } From 8d828dafbc1206a71a4e28c6d61df9103c474edc Mon Sep 17 00:00:00 2001 From: Dusan Lukic <ldusan84@gmail.com> Date: Thu, 14 Feb 2019 23:17:51 +0100 Subject: [PATCH 053/345] Fixed api-functional test --- .../testsuite/Magento/GraphQl/Catalog/CategoryTest.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php index c586665a16d51..187d3b80e4126 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php @@ -10,11 +10,13 @@ use Magento\Catalog\Api\Data\CategoryInterface; use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection; use Magento\Framework\DataObject; +use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\TestFramework\TestCase\GraphQlAbstract; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\TestFramework\ObjectManager; + class CategoryTest extends GraphQlAbstract { /** @@ -122,9 +124,9 @@ public function testNonExistentCategoryWithProductCount() } QUERY; - $response = $this->graphQlQuery($query); - $expectedResponse = ['errors' => ['message' => 'Category doesn\'t exist']]; - $this->assertEquals($expectedResponse, $response); + $this->expectException(GraphQlNoSuchEntityException::class); + $this->expectExceptionMessage('GraphQL response contains errors: Category doesn\'t exist'); + $this->graphQlQuery($query); } /** From 62ee4c55d8d8b26e5a7f0aefffc659554d0a3086 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 15 Feb 2019 10:46:17 +0200 Subject: [PATCH 054/345] MC-14938: MFTF test fix --- .../Mftf/ActionGroup/LoginToStorefrontActionGroup.xml | 10 ++++++---- .../OpenEditCustomerFromAdminActionGroup.xml | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml index 7be36ffbd9bc4..90544dc2673a3 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml @@ -11,9 +11,11 @@ <arguments> <argument name="Customer"/> </arguments> - <amOnPage stepKey="amOnSignInPage" url="{{StorefrontCustomerSignInPage.url}}"/> - <fillField stepKey="fillEmail" userInput="{{Customer.email}}" selector="{{StorefrontCustomerSignInFormSection.emailField}}"/> - <fillField stepKey="fillPassword" userInput="{{Customer.password}}" selector="{{StorefrontCustomerSignInFormSection.passwordField}}"/> - <click stepKey="clickSignInAccountButton" selector="{{StorefrontCustomerSignInFormSection.signInAccountButton}}"/> + <amOnPage url="{{StorefrontCustomerSignInPage.url}}" stepKey="amOnSignInPage"/> + <waitForPageLoad time="30" stepKey="waitPageFullyLoaded"/> + <waitForElementVisible selector="{{StorefrontCustomerSignInFormSection.emailField}}" stepKey="waitFormAppears"/> + <fillField userInput="{{Customer.email}}" selector="{{StorefrontCustomerSignInFormSection.emailField}}" stepKey="fillEmail"/> + <fillField userInput="{{Customer.password}}" selector="{{StorefrontCustomerSignInFormSection.passwordField}}" stepKey="fillPassword"/> + <click selector="{{StorefrontCustomerSignInFormSection.signInAccountButton}}" stepKey="clickSignInAccountButton"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml index af918e8208566..e4c7161784c9e 100755 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml @@ -12,7 +12,7 @@ <argument name="customer"/> </arguments> <amOnPage url="{{AdminCustomerPage.url}}" stepKey="navigateToCustomers"/> - <waitForPageLoad stepKey="waitForPageLoad1" /> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingOrderFilters"/> <click selector="{{AdminCustomerFiltersSection.filtersButton}}" stepKey="openFilter"/> <fillField userInput="{{customer.email}}" selector="{{AdminCustomerFiltersSection.emailInput}}" stepKey="filterEmail"/> <click selector="{{AdminCustomerFiltersSection.apply}}" stepKey="applyFilter"/> From c0f2632115e7058c034b4585e9d3281b424ed936 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Fri, 15 Feb 2019 15:02:45 +0200 Subject: [PATCH 055/345] Fix stastic test. --- .../Directory/Setup/Patch/Data/AddDataForIndia.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Directory/Setup/Patch/Data/AddDataForIndia.php b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForIndia.php index 8337d17051efa..47f4fb0a6c7f3 100644 --- a/app/code/Magento/Directory/Setup/Patch/Data/AddDataForIndia.php +++ b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForIndia.php @@ -13,8 +13,7 @@ use Magento\Framework\Setup\Patch\PatchVersionInterface; /** - * Class AddDataForIndia - * @package Magento\Directory\Setup\Patch\Data + * Add Regions for India. */ class AddDataForIndia implements DataPatchInterface, PatchVersionInterface { @@ -43,7 +42,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function apply() { @@ -103,7 +102,7 @@ private function getDataForIndia() } /** - * {@inheritdoc} + * @inheritdoc */ public static function getDependencies() { @@ -113,7 +112,7 @@ public static function getDependencies() } /** - * {@inheritdoc} + * @inheritdoc */ public static function getVersion() { @@ -121,7 +120,7 @@ public static function getVersion() } /** - * {@inheritdoc} + * @inheritdoc */ public function getAliases() { From 996ba90910de26c707a54f3fdd7b77b854f8c0e7 Mon Sep 17 00:00:00 2001 From: Dusan Lukic <ldusan84@gmail.com> Date: Fri, 15 Feb 2019 14:53:58 +0100 Subject: [PATCH 056/345] Small api-functional test fix --- .../testsuite/Magento/GraphQl/Catalog/CategoryTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php index 187d3b80e4126..4808f5d9b5374 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php @@ -10,7 +10,7 @@ use Magento\Catalog\Api\Data\CategoryInterface; use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection; use Magento\Framework\DataObject; -use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; +use Magento\TestFramework\TestCase\GraphQl\ResponseContainsErrorsException; use Magento\TestFramework\TestCase\GraphQlAbstract; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; @@ -124,7 +124,7 @@ public function testNonExistentCategoryWithProductCount() } QUERY; - $this->expectException(GraphQlNoSuchEntityException::class); + $this->expectException(ResponseContainsErrorsException::class); $this->expectExceptionMessage('GraphQL response contains errors: Category doesn\'t exist'); $this->graphQlQuery($query); } 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 057/345] 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 89ec2b84572fc..cf6301cb31a9c 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 058/345] 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 19543375aad58..d74377b6194c3 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 0000000000000..20f4dd26b8a08 --- /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 0000000000000..1414358ae42e9 --- /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 0000000000000..e140078d05ab8 --- /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 059/345] 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 20f4dd26b8a08..d813f209ab96e 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 1414358ae42e9..4c4896cb35623 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 e140078d05ab8..b5f8eee0f2c4f 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 0000000000000..c71dad701d715 --- /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 060/345] 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 d813f209ab96e..884353eb63e5f 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 4c4896cb35623..ab839a6594a67 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 f4d1442ead35e67ef7a49e07fdc186a034752338 Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Wed, 20 Feb 2019 11:53:03 +0200 Subject: [PATCH 061/345] Sorting by Websites not working in product grid in backoffice --- .../Mftf/Test/AdminSortingByWebsitesTest.xml | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml new file mode 100644 index 0000000000000..54c107990c3c7 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml @@ -0,0 +1,94 @@ +<?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="AdminSortingByWebsitesTest"> + <annotations> + <stories value="View sorting by websites"/> + <title value="Sorting by websites in Admin"/> + <description value="Sorting products by websites in Admin"/> + </annotations> + <before> + <actionGroup ref="LoginActionGroup" stepKey="loginAsAdmin"/> + + <!--Create Website --> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createAdditionalWebsite"> + <argument name="newWebsiteName" value="Second Website"/> + <argument name="websiteCode" value="second_website"/> + </actionGroup> + + <!--Create Store --> + <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createNewStore"> + <argument name="website" value="Second Website"/> + <argument name="storeGroupName" value="Second Store"/> + <argument name="storeGroupCode" value="second_store"/> + </actionGroup> + + <!--Create Store view --> + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> + <waitForPageLoad stepKey="waitForSystemStorePage"/> + <click selector="{{AdminStoresMainActionsSection.createStoreViewButton}}" stepKey="createStoreViewButton"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <waitForElementVisible selector="//legend[contains(., 'Store View Information')]" stepKey="waitForNewStorePageToOpen"/> + <selectOption userInput="Second Store" selector="{{AdminNewStoreSection.storeGrpDropdown}}" stepKey="selectStoreGroup"/> + <fillField userInput="Second Store View" selector="{{AdminNewStoreSection.storeNameTextField}}" stepKey="fillStoreViewName"/> + <fillField userInput="second_store_view" selector="{{AdminNewStoreSection.storeCodeTextField}}" stepKey="fillStoreViewCode"/> + <selectOption selector="{{AdminNewStoreSection.statusDropdown}}" userInput="1" stepKey="enableStoreViewStatus"/> + <click selector="{{AdminNewStoreViewActionsSection.saveButton}}" stepKey="clickSaveStoreView" /> + <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForModal" /> + <see selector="{{AdminConfirmationModalSection.title}}" userInput="Warning message" stepKey="seeWarning" /> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="dismissModal" /> + <waitForPageLoad stepKey="waitForPageLoad2" time="180" /> + <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" time="150" stepKey="waitForPageReolad"/> + <see userInput="You saved the store view." stepKey="seeSavedMessage" /> + + <!--Create a Simple Product 1 --> + <actionGroup ref="createSimpleProductAndAddToWebsite" stepKey="createSimpleProduct1"> + <argument name="product" value="simpleProductForMassUpdate"/> + <argument name="website" value="Second Website"/> + </actionGroup> + + <!--Create a Simple Product 2 --> + <actionGroup ref="createSimpleProductAndAddToWebsite" stepKey="createSimpleProduct2"> + <argument name="product" value="simpleProductForMassUpdate2"/> + <argument name="website" value="Second Website"/> + </actionGroup> + </before> + <after> + <!--Delete website --> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteSecondWebsite"> + <argument name="websiteName" value="Second Website"/> + </actionGroup> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + + <!--Delete Products --> + <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct1"> + <argument name="productName" value="simpleProductForMassUpdate.name"/> + </actionGroup> + <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct2"> + <argument name="productName" value="simpleProductForMassUpdate2.name"/> + </actionGroup> + <actionGroup ref="logout" stepKey="amOnLogoutPage"/> + </after> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToCatalogProductGrid"/> + <waitForPageLoad stepKey="waitForCatalogProductGrid"/> + + <!--Sort Ascending--> + <click selector="{{AdminProductGridSection.columnHeader('Websites')}}" stepKey="clickWebsitesHeaderToSortAsc"/> + <grabTextFrom selector="{{AdminProductGridSection.productGridCell('Ukraine', 'Country of Manufacture')}}" stepKey="getFirstWebsitesSortAsc"/> + <grabTextFrom selector="{{AdminProductGridSection.productGridCell('Cameroon', 'Country of Manufacture')}}" stepKey="getSecondWebsitesSortAsc"/> + <assertLessThanOrEqual expected="$getSecondPriceSortAsc" actual="$getFirstPriceSortAsc" stepKey="checkPriceAscSortCorrect"/> + <!--Sort Descending--> + <click selector="{{AdminProductGridSection.columnHeader('Country of Manufacture')}}" stepKey="clickWebsitesHeaderToSortDesc"/> + <grabTextFrom selector="{{AdminProductGridSection.productGridCell('Ukraine', 'Country of Manufacture')}}" stepKey="getFirstWebsitesSortDesc"/> + <grabTextFrom selector="{{AdminProductGridSection.productGridCell('Cameroon', 'Country of Manufacture')}}" stepKey="getSecondWebsitesSortDesc"/> + <assertGreaterThanOrEqual expected="$getSecondWebsitesSortDesc" actual="$getFirstWebsitesSortDesc" stepKey="checkWebsitesDescSortCorrect"/> + </test> +</tests> From cfa2e3ef16b8d215ecab208a830766c3c79830c1 Mon Sep 17 00:00:00 2001 From: Ravi Chandra <ravi.chandra@krishtechnolabs.com> Date: Wed, 20 Feb 2019 15:25:27 +0530 Subject: [PATCH 062/345] Correct spelling --- .../Block/Adminhtml/Product/Attribute/Edit/Tab/Advanced.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Advanced.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Advanced.php index dd09e40ac5b35..564969d9bb913 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Advanced.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Advanced.php @@ -255,7 +255,7 @@ protected function _prepareForm() } /** - * Initialize form fileds values + * Initialize form fields values * * @return $this */ 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 063/345] 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 4c224a6c713ef..018b010e8fe4a 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 12c97fdfcd2c5..683606e576497 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 93da772ece99f9b332829a08e39c7f4dc9ceb92e Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Wed, 20 Feb 2019 13:01:32 +0200 Subject: [PATCH 064/345] Sorting by Websites not working in product grid in backoffice --- .../Test/Mftf/Test/AdminSortingByWebsitesTest.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml index 54c107990c3c7..450d1f1cca39f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml @@ -82,13 +82,13 @@ <!--Sort Ascending--> <click selector="{{AdminProductGridSection.columnHeader('Websites')}}" stepKey="clickWebsitesHeaderToSortAsc"/> - <grabTextFrom selector="{{AdminProductGridSection.productGridCell('Ukraine', 'Country of Manufacture')}}" stepKey="getFirstWebsitesSortAsc"/> - <grabTextFrom selector="{{AdminProductGridSection.productGridCell('Cameroon', 'Country of Manufacture')}}" stepKey="getSecondWebsitesSortAsc"/> + <grabTextFrom selector="{{AdminProductGridSection.productGridCell('Main Website', 'Country of Manufacture')}}" stepKey="getFirstWebsitesSortAsc"/> + <grabTextFrom selector="{{AdminProductGridSection.productGridCell('Second Website', 'Country of Manufacture')}}" stepKey="getSecondWebsitesSortAsc"/> <assertLessThanOrEqual expected="$getSecondPriceSortAsc" actual="$getFirstPriceSortAsc" stepKey="checkPriceAscSortCorrect"/> <!--Sort Descending--> - <click selector="{{AdminProductGridSection.columnHeader('Country of Manufacture')}}" stepKey="clickWebsitesHeaderToSortDesc"/> - <grabTextFrom selector="{{AdminProductGridSection.productGridCell('Ukraine', 'Country of Manufacture')}}" stepKey="getFirstWebsitesSortDesc"/> - <grabTextFrom selector="{{AdminProductGridSection.productGridCell('Cameroon', 'Country of Manufacture')}}" stepKey="getSecondWebsitesSortDesc"/> + <click selector="{{AdminProductGridSection.columnHeader('Websites')}}" stepKey="clickWebsitesHeaderToSortDesc"/> + <grabTextFrom selector="{{AdminProductGridSection.productGridCell('Main Website', 'Country of Manufacture')}}" stepKey="getFirstWebsitesSortDesc"/> + <grabTextFrom selector="{{AdminProductGridSection.productGridCell('Second Website', 'Country of Manufacture')}}" stepKey="getSecondWebsitesSortDesc"/> <assertGreaterThanOrEqual expected="$getSecondWebsitesSortDesc" actual="$getFirstWebsitesSortDesc" stepKey="checkWebsitesDescSortCorrect"/> </test> </tests> 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 065/345] 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 0000000000000..cf8444dc0a0ba --- /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 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 066/345] 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 884353eb63e5f..02e2739545696 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 4e348fd0a595579a4be08795c18f13c71ebecea0 Mon Sep 17 00:00:00 2001 From: Oleksandr Miroshnichenko <omiroshnichenko@magento.com> Date: Wed, 20 Feb 2019 14:51:32 -0600 Subject: [PATCH 067/345] Fix Admin Customizable Options Dropdown sort_order issue --- .../Backend/view/adminhtml/web/template/dynamic-rows/grid.html | 2 +- .../Ui/DataProvider/Product/Form/Modifier/CustomOptions.php | 1 + .../Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js | 1 + app/code/Magento/Ui/view/base/web/js/dynamic-rows/record.js | 2 +- .../view/base/web/templates/dynamic-rows/templates/default.html | 2 +- .../Ui/view/base/web/templates/dynamic-rows/templates/grid.html | 2 +- 6 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Backend/view/adminhtml/web/template/dynamic-rows/grid.html b/app/code/Magento/Backend/view/adminhtml/web/template/dynamic-rows/grid.html index fe30ca7e83f19..0033f4c071e42 100644 --- a/app/code/Magento/Backend/view/adminhtml/web/template/dynamic-rows/grid.html +++ b/app/code/Magento/Backend/view/adminhtml/web/template/dynamic-rows/grid.html @@ -69,7 +69,7 @@ <!-- ko foreach: { data: $record().elems(), as: 'elem'} --> <td if="elem.template" - visible="elem.visible" + visible="elem.visible() && elem.formElement !== 'hidden'" disable="elem.disabled" css="$parent.setClasses(elem)" template="elem.template" diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CustomOptions.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CustomOptions.php index f8f82511cc12f..af43c84501f65 100755 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CustomOptions.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CustomOptions.php @@ -871,6 +871,7 @@ protected function getPositionFieldConfig($sortOrder) 'formElement' => Hidden::NAME, 'dataScope' => static::FIELD_SORT_ORDER_NAME, 'dataType' => Number::NAME, + 'visible' => false, 'sortOrder' => $sortOrder, ], ], diff --git a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js index 1d52fc78d7a85..cbbfbdb127ad7 100644 --- a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js +++ b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js @@ -543,6 +543,7 @@ define([ data = this.createHeaderTemplate(cell.config); cell.config.labelVisible = false; _.extend(data, { + defaultLabelVisible: data.visible(), label: cell.config.label, name: cell.name, required: !!cell.config.validation, diff --git a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/record.js b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/record.js index 3987507ece54f..9a9d478904775 100644 --- a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/record.js +++ b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/record.js @@ -245,7 +245,7 @@ define([ label = _.findWhere(this.parentComponent().labels(), { name: index }); - label.visible() !== state ? label.visible(state) : false; + label.defaultLabelVisible && label.visible(state); } else { elems[curElem].visible(state); } diff --git a/app/code/Magento/Ui/view/base/web/templates/dynamic-rows/templates/default.html b/app/code/Magento/Ui/view/base/web/templates/dynamic-rows/templates/default.html index 1a21e1b2f1c71..6da4f82fa8b9e 100644 --- a/app/code/Magento/Ui/view/base/web/templates/dynamic-rows/templates/default.html +++ b/app/code/Magento/Ui/view/base/web/templates/dynamic-rows/templates/default.html @@ -41,7 +41,7 @@ <!-- ko foreach: { data: $record().elems(), as: 'elem'} --> <td if="elem.template" css="$parent.setClasses(elem)" - visible="elem.visible" + visible="elem.visible() && elem.formElement !== 'hidden'" disable="elem.disabled" template="elem.template"/> <!-- /ko --> diff --git a/app/code/Magento/Ui/view/base/web/templates/dynamic-rows/templates/grid.html b/app/code/Magento/Ui/view/base/web/templates/dynamic-rows/templates/grid.html index d0b12549bd66d..e5d73a62b329e 100644 --- a/app/code/Magento/Ui/view/base/web/templates/dynamic-rows/templates/grid.html +++ b/app/code/Magento/Ui/view/base/web/templates/dynamic-rows/templates/grid.html @@ -58,7 +58,7 @@ <!-- ko foreach: { data: $record().elems(), as: 'elem'} --> <td if="elem.template" - visible="elem.visible" + visible="elem.visible() && elem.formElement !== 'hidden'" disable="elem.disabled" css="$parent.setClasses(elem)" template="elem.template" 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 068/345] 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 0000000000000..e69de29bb2d1d diff --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php index 02e2739545696..d19ba0dd947b0 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 0000000000000..e69de29bb2d1d 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 069/345] 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 e69de29bb2d1d..c004179aa524a 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 070/345] 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 ab839a6594a67..e2119ca3eee11 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 c004179aa524a..c22c49d3427c5 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 071/345] 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 cf8444dc0a0ba..f3b141e9f902b 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 072/345] 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 9f886f6f1345e..793fc7d26cb4a 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 992fa047536e4546cb0cfc672d99823c15f836ff Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 21 Feb 2019 17:46:39 +0200 Subject: [PATCH 073/345] MC-14943: MFTF test fix --- .../Test/Mftf/ActionGroup/AdminProductActionGroup.xml | 10 ++++++---- .../Mftf/ActionGroup/AdminSwitchWebsiteActionGroup.xml | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index 3afdc41888c79..a4bad1e817735 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -180,12 +180,14 @@ <arguments> <argument name="website" type="string"/> </arguments> - <scrollTo selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="ScrollToWebsites"/> - <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="ClickTpOpenProductInWebsite"/> + <scrollTo selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="scrollToWebsites"/> + <conditionalClick selector="{{ProductInWebsitesSection.sectionHeader}}" dependentSelector="{{ProductInWebsitesSection.website(website.name)}}" visible="false" stepKey="clickToOpenProductInWebsite"/> <waitForPageLoad stepKey="waitForPageOpened"/> - <click selector="{{ProductInWebsitesSection.website(website)}}" stepKey="SelectWebsite"/> + <click selector="{{ProductInWebsitesSection.website(website)}}" stepKey="selectWebsite"/> <click selector="{{AdminProductFormAdvancedPricingSection.save}}" stepKey="clickSaveProduct"/> - <waitForPageLoad time='60' stepKey="waitForPageOpened1"/> + <waitForPageLoad time='60' stepKey="waitForProducrSaved"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSaveSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the product." stepKey="seeSaveSuccessMessage"/> </actionGroup> <actionGroup name="ProductSetAdvancedPricing"> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchWebsiteActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchWebsiteActionGroup.xml index cfb2c7e6347c3..0960dfb47c368 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchWebsiteActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchWebsiteActionGroup.xml @@ -11,6 +11,7 @@ <arguments> <argument name="website"/> </arguments> + <scrollToTopOfPage stepKey="scrollToTop"/> <click selector="{{AdminMainActionsSection.storeViewDropdown}}" stepKey="clickWebsiteSwitchDropdown"/> <waitForElementVisible selector="{{AdminMainActionsSection.websiteByName('Main Website')}}" stepKey="waitForWebsiteAreVisible"/> <click selector="{{AdminMainActionsSection.websiteByName(website.name)}}" stepKey="clickWebsiteByName"/> 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 074/345] 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 de6767a0ef92a..86085b3bb7c5b 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 075/345] 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 0ae9ffde66f43..86717e42fa540 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 0b6ac2b998de7..0d24ea24deaec 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 d4c91e98d9608..986ab517ceca1 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 a4c89dcfab2af..db6f9b0a64f9f 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 4703a2afeee0de44835c726c5824e6578cc91bd6 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 22 Feb 2019 10:56:46 +0200 Subject: [PATCH 076/345] MC-14943: MFTF test fix --- .../Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index a4bad1e817735..3402b82fa6dc8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -181,7 +181,7 @@ <argument name="website" type="string"/> </arguments> <scrollTo selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="scrollToWebsites"/> - <conditionalClick selector="{{ProductInWebsitesSection.sectionHeader}}" dependentSelector="{{ProductInWebsitesSection.website(website.name)}}" visible="false" stepKey="clickToOpenProductInWebsite"/> + <conditionalClick selector="{{ProductInWebsitesSection.sectionHeader}}" dependentSelector="{{ProductInWebsitesSection.website(website)}}" visible="false" stepKey="clickToOpenProductInWebsite"/> <waitForPageLoad stepKey="waitForPageOpened"/> <click selector="{{ProductInWebsitesSection.website(website)}}" stepKey="selectWebsite"/> <click selector="{{AdminProductFormAdvancedPricingSection.save}}" stepKey="clickSaveProduct"/> From 5362c5a76d7b6dac663addada2aee67987cdf945 Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Fri, 22 Feb 2019 14:50:27 +0200 Subject: [PATCH 077/345] Sorting by Websites not working in product grid in backoffice #20511 --- .../Mftf/Test/AdminSortingByWebsitesTest.xml | 111 ++++++++---------- 1 file changed, 51 insertions(+), 60 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml index 450d1f1cca39f..354955ba54402 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml @@ -15,80 +15,71 @@ <description value="Sorting products by websites in Admin"/> </annotations> <before> - <actionGroup ref="LoginActionGroup" stepKey="loginAsAdmin"/> - - <!--Create Website --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--Create new website --> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createAdditionalWebsite"> <argument name="newWebsiteName" value="Second Website"/> <argument name="websiteCode" value="second_website"/> </actionGroup> - - <!--Create Store --> - <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createNewStore"> - <argument name="website" value="Second Website"/> - <argument name="storeGroupName" value="Second Store"/> - <argument name="storeGroupCode" value="second_store"/> - </actionGroup> - - <!--Create Store view --> - <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> - <waitForPageLoad stepKey="waitForSystemStorePage"/> - <click selector="{{AdminStoresMainActionsSection.createStoreViewButton}}" stepKey="createStoreViewButton"/> - <waitForPageLoad stepKey="waitForProductPageLoad"/> - <waitForElementVisible selector="//legend[contains(., 'Store View Information')]" stepKey="waitForNewStorePageToOpen"/> - <selectOption userInput="Second Store" selector="{{AdminNewStoreSection.storeGrpDropdown}}" stepKey="selectStoreGroup"/> - <fillField userInput="Second Store View" selector="{{AdminNewStoreSection.storeNameTextField}}" stepKey="fillStoreViewName"/> - <fillField userInput="second_store_view" selector="{{AdminNewStoreSection.storeCodeTextField}}" stepKey="fillStoreViewCode"/> - <selectOption selector="{{AdminNewStoreSection.statusDropdown}}" userInput="1" stepKey="enableStoreViewStatus"/> - <click selector="{{AdminNewStoreViewActionsSection.saveButton}}" stepKey="clickSaveStoreView" /> - <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForModal" /> - <see selector="{{AdminConfirmationModalSection.title}}" userInput="Warning message" stepKey="seeWarning" /> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="dismissModal" /> - <waitForPageLoad stepKey="waitForPageLoad2" time="180" /> - <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" time="150" stepKey="waitForPageReolad"/> - <see userInput="You saved the store view." stepKey="seeSavedMessage" /> - - <!--Create a Simple Product 1 --> - <actionGroup ref="createSimpleProductAndAddToWebsite" stepKey="createSimpleProduct1"> - <argument name="product" value="simpleProductForMassUpdate"/> - <argument name="website" value="Second Website"/> - </actionGroup> - - <!--Create a Simple Product 2 --> - <actionGroup ref="createSimpleProductAndAddToWebsite" stepKey="createSimpleProduct2"> - <argument name="product" value="simpleProductForMassUpdate2"/> - <argument name="website" value="Second Website"/> - </actionGroup> </before> <after> - <!--Delete website --> - <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteSecondWebsite"> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteTestWebsite"> <argument name="websiteName" value="Second Website"/> </actionGroup> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - - <!--Delete Products --> - <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct1"> - <argument name="productName" value="simpleProductForMassUpdate.name"/> - </actionGroup> - <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct2"> - <argument name="productName" value="simpleProductForMassUpdate2.name"/> - </actionGroup> - <actionGroup ref="logout" stepKey="amOnLogoutPage"/> + <actionGroup ref="logout" stepKey="logout"/> </after> + <actionGroup ref="EnableWebUrlOptions" stepKey="addStoreCodeToUrls"/> + <!--Create a Simple Product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToCatalogProductGrid"/> <waitForPageLoad stepKey="waitForCatalogProductGrid"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> + <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickAddSimpleProduct"/> + <fillField userInput="{{_defaultProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> + <fillField userInput="{{_defaultProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillSKU"/> + <fillField userInput="{{_defaultProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> + <fillField userInput="{{_defaultProduct.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> + + <!--Save the product --> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> + <waitForLoadingMaskToDisappear stepKey="waitProductPageSave"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeProductSavedMessage"/> + + <!-- Add this product to second website --> + <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="openProductInWebsitesSection1"/> + <click selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="selectSecondWebsite"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> + <waitForLoadingMaskToDisappear stepKey="waitForProductPagetoSaveAgain"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessageAgain"/> - <!--Sort Ascending--> + <!--Create a Simple Product 2 --> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="openProductCatalogPage"/> + <waitForPageLoad stepKey="waitForProductCatalogPage"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductToggle"/> + <waitForPageLoad stepKey="waitForProductToggleToSelectSimpleProduct"/> + <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickSimpleProductFromDropDownList"/> + + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{nameAndAttributeSkuMaskSimpleProduct.name}}" stepKey="fillSimpleProductName"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{nameAndAttributeSkuMaskSimpleProduct.price}}" stepKey="fillSimpleProductPrice"/> + <fillField selector="{{AdminProductFormSection.productWeight}}" userInput="{{nameAndAttributeSkuMaskSimpleProduct.weight}}" stepKey="fillSimpleProductWeight"/> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{nameAndAttributeSkuMaskSimpleProduct.quantity}}" stepKey="fillSimpleProductQuantity"/> + <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton2"/> + <waitForPageLoad stepKey="waitForSimpleProductToSave"/> + <!-- Verify customer see success message --> + <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToCatalogProductGrid2"/> + <waitForPageLoad stepKey="waitForCatalogProductGrid2"/> + + <!--Sorting works (By Websites) ASC--> + <actionGroup ref="resetProductGridToDefaultView" stepKey="setProductGridToDefaultSortingWebsites"/> <click selector="{{AdminProductGridSection.columnHeader('Websites')}}" stepKey="clickWebsitesHeaderToSortAsc"/> - <grabTextFrom selector="{{AdminProductGridSection.productGridCell('Main Website', 'Country of Manufacture')}}" stepKey="getFirstWebsitesSortAsc"/> - <grabTextFrom selector="{{AdminProductGridSection.productGridCell('Second Website', 'Country of Manufacture')}}" stepKey="getSecondWebsitesSortAsc"/> - <assertLessThanOrEqual expected="$getSecondPriceSortAsc" actual="$getFirstPriceSortAsc" stepKey="checkPriceAscSortCorrect"/> - <!--Sort Descending--> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Main Website" stepKey="checkIfProduct1WebsitesAsc"/> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('2')}}" userInput="Second Website" stepKey="checkIfProduct2WebsitesAsc"/> + + <!--Sorting works (By Websites) DESC--> <click selector="{{AdminProductGridSection.columnHeader('Websites')}}" stepKey="clickWebsitesHeaderToSortDesc"/> - <grabTextFrom selector="{{AdminProductGridSection.productGridCell('Main Website', 'Country of Manufacture')}}" stepKey="getFirstWebsitesSortDesc"/> - <grabTextFrom selector="{{AdminProductGridSection.productGridCell('Second Website', 'Country of Manufacture')}}" stepKey="getSecondWebsitesSortDesc"/> - <assertGreaterThanOrEqual expected="$getSecondWebsitesSortDesc" actual="$getFirstWebsitesSortDesc" stepKey="checkWebsitesDescSortCorrect"/> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Second Website" stepKey="checkIfProduct1WebsitesDesc"/> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('2')}}" userInput="Main Website" stepKey="checkIfProduct2WebsitesDesc"/> </test> </tests> From 4cb3e37fc2a427ab1f0508a47d25803bb8b84c60 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 22 Feb 2019 15:21:54 +0200 Subject: [PATCH 078/345] MC-13948: [FT] [MFTF] AdminCartRulesAppliedForProductInCartTest fails because of bad design --- .../ActionGroup/AddProductToCartActionGroup.xml | 10 +++++++--- .../Mftf/ActionGroup/AdminProductActionGroup.xml | 2 +- .../ActionGroup/AdminProductGridActionGroup.xml | 7 ++++--- .../AdminCartRulesAppliedForProductInCartTest.xml | 14 ++++++++++---- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml index 692487c1d60cd..c06ecc689a089 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml @@ -11,9 +11,13 @@ <arguments> <argument name="product" defaultValue="product"/> </arguments> - <amOnPage url="/{{product.name}}.html" stepKey="navigateProductPage"/> + <amOnPage url="{{StorefrontProductPage.url($$product.custom_attributes[url_key]$$)}}" stepKey="goToProductPage"/> <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="addToCart"/> - <waitForElementVisible selector="{{StorefrontProductPageSection.successMsg}}" time="30" stepKey="waitForProductAdded"/> - <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added {{product.name}} to your shopping cart." stepKey="seeAddedToCartMessage"/> + <waitForElementNotVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAdding}}" stepKey="waitForElementNotVisibleAddToCartButtonTitleIsAdding"/> + <waitForElementNotVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAdded}}" stepKey="waitForElementNotVisibleAddToCartButtonTitleIsAdded"/> + <waitForElementVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAddToCart}}" stepKey="waitForElementVisibleAddToCartButtonTitleIsAddToCart"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" time="30" stepKey="waitForProductAddedMessage"/> + <see selector="{{StorefrontMessagesSection.success}}" userInput="You added {{product.name}} to your shopping cart." stepKey="seeAddToCartSuccessMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index 3afdc41888c79..9068fb56801c9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -15,7 +15,7 @@ <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductToggle"/> <waitForElementVisible selector="{{AdminProductGridActionSection.addTypeProduct(product.type_id)}}" stepKey="waitForAddProductDropdown" time="30"/> <click selector="{{AdminProductGridActionSection.addTypeProduct(product.type_id)}}" stepKey="clickAddProductType"/> - <waitForPageLoad stepKey="waitForCreateProductPageLoad"/> + <waitForPageLoad time="30" stepKey="waitForCreateProductPageLoad"/> <seeInCurrentUrl url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId, product.type_id)}}" stepKey="seeNewProductUrl"/> <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Product" stepKey="seeNewProductTitle"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml index f0367fb72c6a2..da0e748a3d214 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml @@ -192,7 +192,7 @@ </arguments> <!--TODO use other action group for filtering grid when MQE-539 is implemented --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{sku}}" stepKey="fillProductSkuFilter"/> <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> @@ -201,8 +201,9 @@ <click selector="{{AdminProductGridSection.multicheckOption('Select All')}}" stepKey="selectAllProductInFilteredGrid"/> <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> <click selector="{{AdminProductGridSection.bulkActionOption('Delete')}}" stepKey="clickDeleteAction"/> - <waitForElementVisible selector="{{AdminProductGridConfirmActionSection.title}}" stepKey="waitForConfirmModal"/> - <click selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="confirmProductDelete"/> + <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForConfirmModal"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmProductDelete"/> + <see selector="{{AdminMessagesSection.success}}" userInput="record(s) have been deleted." stepKey="seeSuccessMessage"/> </actionGroup> <actionGroup name="deleteProductByName" extends="deleteProductBySku"> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml index fbcc871a69b97..ab085dc5ae137 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml @@ -21,26 +21,30 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!--Create category and product--> <createData entity="_defaultCategory" stepKey="defaultCategory"/> <createData entity="SimpleProduct2" stepKey="simpleProduct"> <field key="price">200</field> <field key="quantity">500</field> </createData> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> + <after> <!--Delete created data--> <deleteData createDataKey="defaultCategory" stepKey="deleteCategory"/> <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup stepKey="deleteProduct1" ref="deleteProductBySku"> + <actionGroup ref="deleteProductBySku" stepKey="deleteBundleProduct"> <argument name="sku" value="{{BundleProduct.sku}}"/> </actionGroup> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFilters"/> + <actionGroup ref="DeleteCartPriceRuleByName" stepKey="deleteCartPriceRule"> <argument name="ruleName" value="{{PriceRuleWithCondition.name}}"/> </actionGroup> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFilters1"/> <actionGroup ref="logout" stepKey="logout"/> </after> @@ -57,12 +61,14 @@ <pressKey selector="{{AdminProductFormSection.productSku}}" parameterArray="[\Facebook\WebDriver\WebDriverKeys::ENTER]" stepKey="enter"/> <!--Off dynamic price and set value--> + <scrollToTopOfPage stepKey="scrollToTopOfThePageToSeePriceTypeElement"/> <click selector="{{AdminProductFormBundleSection.dynamicPrice}}" stepKey="offDynamicPrice"/> <fillField selector="{{AdminProductFormBundleSection.priceField}}" userInput="0" stepKey="setProductPrice"/> <!-- Add category to product --> <click selector="{{AdminProductFormBundleSection.categoriesDropDown}}" stepKey="dropDownCategories"/> <fillField selector="{{AdminProductFormBundleSection.searchForCategory}}" userInput="$$defaultCategory.name$$" stepKey="searchForCategory"/> + <waitForElementVisible selector="{{AdminProductFormBundleSection.selectCategory}}" stepKey="waitForElementLoaded"/> <click selector="{{AdminProductFormBundleSection.selectCategory}}" stepKey="selectCategory"/> <click selector="{{AdminProductFormBundleSection.categoriesLabel}}" stepKey="clickOnCategoriesLabelToCloseOptions"/> @@ -92,7 +98,7 @@ </actionGroup> <!--Go to Storefront and add product to cart and checkout from cart--> - <amOnPage url="/$$simpleProduct.name$$.html" stepKey="GoToProduct"/> + <amOnPage url="{{StorefrontProductPage.url($$simpleProduct.custom_attributes[url_key]$$)}}" stepKey="goToProductPage"/> <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="2" stepKey="setQuantity"/> <actionGroup ref="StorefrontAddToCartCustomOptionsProductPageActionGroup" stepKey="AddProductToCard"> <argument name="productName" value="$$simpleProduct.name$$"/> From e65ab0f1ea6b67e943187f474a1aa8d672b6ca5f Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 22 Feb 2019 17:32:22 +0200 Subject: [PATCH 079/345] MC-13948: [FT] [MFTF] AdminCartRulesAppliedForProductInCartTest fails because of bad design --- .../Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml index c06ecc689a089..a544be434f9c5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml @@ -11,7 +11,7 @@ <arguments> <argument name="product" defaultValue="product"/> </arguments> - <amOnPage url="{{StorefrontProductPage.url($$product.custom_attributes[url_key]$$)}}" stepKey="goToProductPage"/> + <amOnPage url="{{StorefrontProductPage.url(product.custom_attributes[url_key])}}" stepKey="goToProductPage"/> <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="addToCart"/> <waitForElementNotVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAdding}}" stepKey="waitForElementNotVisibleAddToCartButtonTitleIsAdding"/> <waitForElementNotVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAdded}}" stepKey="waitForElementNotVisibleAddToCartButtonTitleIsAdded"/> 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 080/345] 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 86085b3bb7c5b..492343acfe306 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 081/345] 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 0d24ea24deaec..1f718f4277cc5 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 a168c95b1cd71448bb001d0378caf9998628044f Mon Sep 17 00:00:00 2001 From: Denis Kopylov <dkopylov@magenius.team> Date: Mon, 25 Feb 2019 19:06:22 +0300 Subject: [PATCH 082/345] [FIX] Add review summary to wishlist (https://github.com/magento/magento2/issues/21419) --- .../Block/Customer/Wishlist/Item/Column/Actions.php | 1 + .../Block/Customer/Wishlist/Item/Column/Comment.php | 1 + .../Block/Customer/Wishlist/Item/Column/Edit.php | 1 + .../Block/Customer/Wishlist/Item/Column/Info.php | 1 + .../Block/Customer/Wishlist/Item/Column/Remove.php | 1 + .../view/frontend/layout/wishlist_index_index.xml | 13 +++++++------ .../frontend/templates/item/column/actions.phtml | 2 +- .../frontend/templates/item/column/comment.phtml | 2 +- .../view/frontend/templates/item/column/edit.phtml | 2 +- .../view/frontend/templates/item/column/name.phtml | 2 +- .../frontend/templates/item/column/remove.phtml | 2 +- .../frontend/templates/item/column/review.phtml | 10 ++++++++++ 12 files changed, 27 insertions(+), 11 deletions(-) create mode 100644 app/code/Magento/Wishlist/view/frontend/templates/item/column/review.phtml diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php index cafb6a5291481..0cf6b6a9867d1 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php @@ -13,6 +13,7 @@ /** * @api + * @deprecated Empty class * @since 100.0.2 */ class Actions extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php index 2d75956858a0a..5f40e96bfa6ff 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php @@ -13,6 +13,7 @@ /** * @api + * @deprecated Empty class * @since 100.0.2 */ class Comment extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php index 53ca78c63524d..c4e94ec503175 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php @@ -13,6 +13,7 @@ /** * @api + * @deprecated Empty class * @since 100.0.2 */ class Edit extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php index 33fb0f7325cdd..f83c122910ebd 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php @@ -13,6 +13,7 @@ /** * @api + * @deprecated Empty class * @since 100.0.2 */ class Info extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php index 57703b9300db8..3e6f225842e01 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php @@ -13,6 +13,7 @@ /** * @api + * @deprecated Empty class * @since 100.0.2 */ class Remove extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column 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 d3f21dda9ccde..8696d178d89d7 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 @@ -17,7 +17,8 @@ <block class="Magento\Wishlist\Block\Rss\Link" name="wishlist.rss.link" template="Magento_Wishlist::rss/wishlist.phtml"/> <block class="Magento\Wishlist\Block\Customer\Wishlist\Items" name="customer.wishlist.items" as="items" template="Magento_Wishlist::item/list.phtml" cacheable="false"> <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Image" name="customer.wishlist.item.image" template="Magento_Wishlist::item/column/image.phtml" cacheable="false"/> - <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Info" name="customer.wishlist.item.name" template="Magento_Wishlist::item/column/name.phtml" cacheable="false"/> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.name" template="Magento_Wishlist::item/column/name.phtml" cacheable="false"/> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.review" template="Magento_Wishlist::item/column/review.phtml" cacheable="false"/> <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Cart" name="customer.wishlist.item.price" template="Magento_Wishlist::item/column/price.phtml" cacheable="false"> <block class="Magento\Catalog\Pricing\Render" name="product.price.render.wishlist"> <arguments> @@ -29,11 +30,11 @@ </block> <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Options" name="customer.wishlist.item.options" cacheable="false"/> </block> - <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Actions" name="customer.wishlist.item.inner" template="Magento_Wishlist::item/column/actions.phtml" cacheable="false"> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.inner" template="Magento_Wishlist::item/column/actions.phtml" cacheable="false"> <arguments> <argument name="css_class" xsi:type="string">product-item-inner</argument> </arguments> - <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Comment" name="customer.wishlist.item.comment" template="Magento_Wishlist::item/column/comment.phtml" cacheable="false"> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.comment" template="Magento_Wishlist::item/column/comment.phtml" cacheable="false"> <arguments> <argument name="title" translate="true" xsi:type="string">Product Details and Comment</argument> </arguments> @@ -44,12 +45,12 @@ </arguments> </block> - <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Actions" name="customer.wishlist.item.actions" template="Magento_Wishlist::item/column/actions.phtml" cacheable="false"> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.actions" template="Magento_Wishlist::item/column/actions.phtml" cacheable="false"> <arguments> <argument name="css_class" xsi:type="string">product-item-actions</argument> </arguments> - <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Edit" name="customer.wishlist.item.edit" template="Magento_Wishlist::item/column/edit.phtml" before="-" cacheable="false"/> - <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Remove" name="customer.wishlist.item.remove" template="Magento_Wishlist::item/column/remove.phtml" cacheable="false"/> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.edit" template="Magento_Wishlist::item/column/edit.phtml" before="-" cacheable="false"/> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.remove" template="Magento_Wishlist::item/column/remove.phtml" cacheable="false"/> </block> </block> </block> diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/actions.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/actions.phtml index 0b50df8c59fbb..7fa55a839b2ea 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/actions.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/actions.phtml @@ -6,7 +6,7 @@ // @codingStandardsIgnoreFile -/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Actions $block */ +/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column $block */ ?> <?php $children = $block->getChildNames(); ?> <?php if ($children): ?> diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/comment.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/comment.phtml index 17e2404ee23cf..45bd7494fed4f 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/comment.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/comment.phtml @@ -6,7 +6,7 @@ // @codingStandardsIgnoreFile -/* @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Comment $block */ +/* @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column $block */ /* @var \Magento\Wishlist\Model\Item $item */ $item = $block->getItem(); diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/edit.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/edit.phtml index 1898ef6fc0d66..81d770d556734 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/edit.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/edit.phtml @@ -6,7 +6,7 @@ // @codingStandardsIgnoreFile -/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Edit $block */ +/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column $block */ /** @var \Magento\Wishlist\Model\Item $item */ $item = $block->getItem(); diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/name.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/name.phtml index 265f4635a6be3..53c5f35f886ea 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/name.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/name.phtml @@ -6,7 +6,7 @@ // @codingStandardsIgnoreFile -/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Info $block */ +/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column $block */ /** @var \Magento\Wishlist\Model\Item $item */ $item = $block->getItem(); diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/remove.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/remove.phtml index 57ae439d9995b..0bd78ba38ba58 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/remove.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/remove.phtml @@ -6,7 +6,7 @@ // @codingStandardsIgnoreFile -/* @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Remove $block */ +/* @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column $block */ ?> <a href="#" data-role="remove" data-post-remove='<?= /* @noEscape */ $block->getItemRemoveParams($block->getItem()) ?>' title="<?= $block->escapeHtmlAttr(__('Remove Item')) ?>" class="btn-remove action delete"> diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/review.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/review.phtml new file mode 100644 index 0000000000000..9120cc9fa684e --- /dev/null +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/review.phtml @@ -0,0 +1,10 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column $block */ +$product = $block->getItem()->getProduct(); +?> +<?= $block->getReviewsSummaryHtml($product, 'short') ?> From dda8a1e9ddff1c4850187600ed14d056e6d6d872 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Mon, 25 Feb 2019 18:09:22 +0200 Subject: [PATCH 083/345] MC-13848: [FT] [MFTF] StorefrontPurchaseProductWithCustomOptionsTest fails because of bad design --- ...ntPurchaseProductWithCustomOptionsTest.xml | 108 +++++++++--------- 1 file changed, 53 insertions(+), 55 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml index 951afa2ddb68b..a3bce2d4fe2f2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml @@ -20,30 +20,36 @@ </annotations> <before> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <!--Create Simple Product with Custom Options--> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + <field key="price">17</field> + </createData> + <updateData createDataKey="createProduct" entity="productWithOptions" stepKey="updateProductWithOption"/> + <!-- Logout customer before in case of it logged in from previous test --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogoutStorefront"/> </before> <after> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <!-- Delete product and category --> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearOrderListingFilters"/> + <actionGroup ref="logout" stepKey="logoutAdmin"/> + <!-- Logout customer --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogoutStorefront"/> </after> - <!--Create Simple Product with Custom Options--> + <!-- Login Customer Storefront --> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - <field key="price">17</field> - </createData> - <updateData createDataKey="createProduct" entity="productWithOptions" stepKey="updateProductWithOption"/> - - <!-- Login Customer Storeront --> - - <amOnPage url="{{StorefrontCustomerSignInPage.url}}" stepKey="amOnSignInPage"/> - <fillField userInput="$$createCustomer.email$$" selector="{{StorefrontCustomerSignInFormSection.emailField}}" stepKey="fillEmail"/> - <fillField userInput="$$createCustomer.password$$" selector="{{StorefrontCustomerSignInFormSection.passwordField}}" stepKey="fillPassword"/> - <click selector="{{StorefrontCustomerSignInFormSection.signInAccountButton}}" stepKey="clickSignInAccountButton"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginCustomerOnStorefront"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> <!-- Checking the correctness of displayed prices for user parameters --> - <amOnPage url="{{StorefrontHomePage.url}}$createProduct.custom_attributes[url_key]$.html" stepKey="amOnProduct3Page"/> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToProductPage"/> <seeElement selector="{{StorefrontProductInfoMainSection.productAttributeOptionsPrice(ProductOptionField.title, ProductOptionField.price)}}" stepKey="checkFieldProductOption"/> <seeElement selector="{{StorefrontProductInfoMainSection.productAttributeOptionsPrice(ProductOptionArea.title, '1.7')}}" stepKey="checkAreaProductOption"/> @@ -80,7 +86,7 @@ <grabTextFrom selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="finalProductPrice"/> <actionGroup ref="StorefrontAddToCartCustomOptionsProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> - <argument name="productName" value="$createProduct.name$"/> + <argument name="productName" value="$$createProduct.name$$"/> </actionGroup> <!-- Checking the correctness of displayed custom options for user parameters on checkout --> @@ -92,21 +98,21 @@ <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskForCartItem"/> <waitForElement selector="{{CheckoutPaymentSection.cartItemsAreaActive}}" time="30" stepKey="waitForCartItemsAreaActive"/> - <see selector="{{CheckoutPaymentSection.cartItems}}" userInput="$createProduct.name$" stepKey="seeProductInCart"/> - - <conditionalClick selector="{{CheckoutPaymentSection.ProductOptionsByProductItemName($createProduct.name$)}}" dependentSelector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($createProduct.name$)}}" visible="false" stepKey="exposeProductOptions"/> - - <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($createProduct.name$)}}" userInput="{{ProductOptionField.title}}" stepKey="seeProductOptionFieldInput1"/> - <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($createProduct.name$)}}" userInput="{{ProductOptionArea.title}}" stepKey="seeProductOptionAreaInput1"/> - <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($createProduct.name$)}}" userInput="{{productWithOptions.file}}" stepKey="seeProductOptionFileInput1"/> - <seeElement selector="{{CheckoutPaymentSection.ProductOptionLinkActiveByProductItemName($createProduct.name$, productWithOptions.file)}}" stepKey="seeProductOptionFileInputLink1"/> - <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($createProduct.name$)}}" userInput="{{ProductOptionValueDropdown1.title}}" stepKey="seeProductOptionValueDropdown1Input1"/> - <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($createProduct.name$)}}" userInput="{{ProductOptionValueRadioButtons1.title}}" stepKey="seeProductOptionValueRadioButtons1Input1"/> - <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($createProduct.name$)}}" userInput="{{ProductOptionValueCheckbox.title}}" stepKey="seeProductOptionValueCheckboxInput1" /> - <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($createProduct.name$)}}" userInput="{{ProductOptionValueMultiSelect1.title}}" stepKey="seeproductAttributeOptionsMultiselect1Input1" /> - <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($createProduct.name$)}}" userInput="Jan 1, $year" stepKey="seeProductOptionDateAndTimeInput" /> - <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($createProduct.name$)}}" userInput="1/1/$shortYear, 1:00 AM" stepKey="seeProductOptionDataInput" /> - <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($createProduct.name$)}}" userInput="1:00 AM" stepKey="seeProductOptionTimeInput" /> + <see selector="{{CheckoutPaymentSection.cartItems}}" userInput="$$createProduct.name$$" stepKey="seeProductInCart"/> + + <conditionalClick selector="{{CheckoutPaymentSection.ProductOptionsByProductItemName($$createProduct.name$$)}}" dependentSelector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($$createProduct.name$$)}}" visible="false" stepKey="exposeProductOptions"/> + + <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="{{ProductOptionField.title}}" stepKey="seeProductOptionFieldInput1"/> + <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="{{ProductOptionArea.title}}" stepKey="seeProductOptionAreaInput1"/> + <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="{{productWithOptions.file}}" stepKey="seeProductOptionFileInput1"/> + <seeElement selector="{{CheckoutPaymentSection.ProductOptionLinkActiveByProductItemName($$createProduct.name$$, productWithOptions.file)}}" stepKey="seeProductOptionFileInputLink1"/> + <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="{{ProductOptionValueDropdown1.title}}" stepKey="seeProductOptionValueDropdown1Input1"/> + <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="{{ProductOptionValueRadioButtons1.title}}" stepKey="seeProductOptionValueRadioButtons1Input1"/> + <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="{{ProductOptionValueCheckbox.title}}" stepKey="seeProductOptionValueCheckboxInput1" /> + <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="{{ProductOptionValueMultiSelect1.title}}" stepKey="seeproductAttributeOptionsMultiselect1Input1" /> + <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="Jan 1, $year" stepKey="seeProductOptionDateAndTimeInput" /> + <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="1/1/$shortYear, 1:00 AM" stepKey="seeProductOptionDataInput" /> + <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="1:00 AM" stepKey="seeProductOptionTimeInput" /> <!--Select shipping method--> <actionGroup ref="CheckoutSelectFlatRateShippingMethodActionGroup" stepKey="selectFlatRateShippingMethod"/> <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/> @@ -124,13 +130,11 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnOrdersPage"/> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearGridFilter"/> - <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="fillOrderNum"/> - <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchOrderNum"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnSearch"/> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> + <actionGroup ref="filterOrderGridById" stepKey="filterByOrderId"> + <argument name="orderId" value="$grabOrderNumber"/> + </actionGroup> + <click selector="{{AdminDataGridTableSection.firstRow}}" stepKey="clickOrderRow"/> + <waitForPageLoad stepKey="waitForOrderPageOpened"/> <!-- Checking the correctness of displayed custom options for user parameters on Order --> @@ -168,24 +172,18 @@ <!-- Go to Customer Order Page and Checking the correctness of displayed custom options for user parameters on Order --> - <amOnPage url="{{StorefrontCustomerOrderViewPage.url({$grabOrderNumber})}}" stepKey="amOnProduct4Page"/> + <amOnPage url="{{StorefrontCustomerOrderViewPage.url({$grabOrderNumber})}}" stepKey="amOnOrderPage"/> - <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($createProduct.name$, ProductOptionField.title, ProductOptionField.title)}}" userInput="{{ProductOptionField.title}}" stepKey="seeStorefontOrderProductOptionField1" /> - <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($createProduct.name$, ProductOptionArea.title, ProductOptionArea.title)}}" userInput="{{ProductOptionArea.title}}" stepKey="seeStorefontOrderProductOptionArea1"/> - <see selector="{{StorefrontCustomerOrderSection.productCustomOptionsFile($createProduct.name$, ProductOptionFile.title, productWithOptions.file)}}" userInput="{{productWithOptions.file}}" stepKey="seeStorefontOrderProductOptionFile1"/> + <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($$createProduct.name$$, ProductOptionField.title, ProductOptionField.title)}}" userInput="{{ProductOptionField.title}}" stepKey="seeStorefontOrderProductOptionField1" /> + <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($$createProduct.name$$, ProductOptionArea.title, ProductOptionArea.title)}}" userInput="{{ProductOptionArea.title}}" stepKey="seeStorefontOrderProductOptionArea1"/> + <see selector="{{StorefrontCustomerOrderSection.productCustomOptionsFile($$createProduct.name$$, ProductOptionFile.title, productWithOptions.file)}}" userInput="{{productWithOptions.file}}" stepKey="seeStorefontOrderProductOptionFile1"/> <seeElement selector="{{StorefrontCustomerOrderSection.productCustomOptionsLink($createProduct.name$, ProductOptionFile.title, productWithOptions.file)}}" stepKey="seeStorefontOrderProductOptionFileLink1"/> - <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($createProduct.name$, ProductOptionDropDown.title, ProductOptionValueDropdown1.title)}}" userInput="{{ProductOptionValueDropdown1.title}}" stepKey="seeStorefontOrderProductOptionValueDropdown11"/> - <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($createProduct.name$, ProductOptionRadiobutton.title, ProductOptionValueRadioButtons1.title)}}" userInput="{{ProductOptionValueRadioButtons1.title}}" stepKey="seeStorefontOrderProductOptionValueRadioButtons11"/> - <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($createProduct.name$, ProductOptionCheckbox.title, ProductOptionValueCheckbox.title)}}" userInput="{{ProductOptionValueCheckbox.title}}" stepKey="seeStorefontOrderProductOptionValueCheckbox1" /> - <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($createProduct.name$, ProductOptionMultiSelect.title, ProductOptionValueMultiSelect1.title)}}" userInput="{{ProductOptionValueMultiSelect1.title}}" stepKey="seeStorefontOrderproductAttributeOptionsMultiselect11" /> - <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($createProduct.name$, ProductOptionDate.title, 'Jan 1, $year')}}" userInput="Jan 1, $year" stepKey="seeStorefontOrderProductOptionDateAndTime1" /> - <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($createProduct.name$, ProductOptionDateTime.title, '1/1/$shortYear, 1:00 AM')}}" userInput="1/1/$shortYear, 1:00 AM" stepKey="seeStorefontOrderProductOptionData1" /> - <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($createProduct.name$, ProductOptionTime.title, '1:00 AM')}}" userInput="1:00 AM" stepKey="seeStorefontOrderProductOptionTime1" /> - - <!-- Delete product and category --> - - <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - + <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($$createProduct.name$$, ProductOptionDropDown.title, ProductOptionValueDropdown1.title)}}" userInput="{{ProductOptionValueDropdown1.title}}" stepKey="seeStorefontOrderProductOptionValueDropdown11"/> + <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($$createProduct.name$$, ProductOptionRadiobutton.title, ProductOptionValueRadioButtons1.title)}}" userInput="{{ProductOptionValueRadioButtons1.title}}" stepKey="seeStorefontOrderProductOptionValueRadioButtons11"/> + <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($$createProduct.name$$, ProductOptionCheckbox.title, ProductOptionValueCheckbox.title)}}" userInput="{{ProductOptionValueCheckbox.title}}" stepKey="seeStorefontOrderProductOptionValueCheckbox1" /> + <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($$createProduct.name$$, ProductOptionMultiSelect.title, ProductOptionValueMultiSelect1.title)}}" userInput="{{ProductOptionValueMultiSelect1.title}}" stepKey="seeStorefontOrderproductAttributeOptionsMultiselect11" /> + <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($$createProduct.name$$, ProductOptionDate.title, 'Jan 1, $year')}}" userInput="Jan 1, $year" stepKey="seeStorefontOrderProductOptionDateAndTime1" /> + <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($$createProduct.name$$, ProductOptionDateTime.title, '1/1/$shortYear, 1:00 AM')}}" userInput="1/1/$shortYear, 1:00 AM" stepKey="seeStorefontOrderProductOptionData1" /> + <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($$createProduct.name$$, ProductOptionTime.title, '1:00 AM')}}" userInput="1:00 AM" stepKey="seeStorefontOrderProductOptionTime1" /> </test> </tests> From a5bdc38d3531bf93cd46fdc4e5e4f6bbe5056b32 Mon Sep 17 00:00:00 2001 From: Ananth Iyer <iyerananth3@gmail.com> Date: Mon, 25 Feb 2019 23:48:59 +0530 Subject: [PATCH 084/345] Disable dropdown in JavaScript and CSS Settings in developer configuration --- app/code/Magento/Backend/etc/adminhtml/system.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Backend/etc/adminhtml/system.xml b/app/code/Magento/Backend/etc/adminhtml/system.xml index 0fb7d89f924de..c48e4a2b0aa9d 100644 --- a/app/code/Magento/Backend/etc/adminhtml/system.xml +++ b/app/code/Magento/Backend/etc/adminhtml/system.xml @@ -169,15 +169,15 @@ </group> <group id="js" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="1"> <label>JavaScript Settings</label> - <field id="merge_files" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="merge_files" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Merge JavaScript Files</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="enable_js_bundling" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="enable_js_bundling" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Enable JavaScript Bundling</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="minify_files" translate="label comment" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="minify_files" translate="label comment" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Minify JavaScript Files</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Minification is not applied in developer mode.</comment> @@ -185,11 +185,11 @@ </group> <group id="css" translate="label" type="text" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="1"> <label>CSS Settings</label> - <field id="merge_css_files" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="merge_css_files" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Merge CSS Files</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="minify_files" translate="label comment" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="minify_files" translate="label comment" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Minify CSS Files</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Minification is not applied in developer mode.</comment> 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 085/345] 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 71475acd8b066..5886b40c4547b 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 f053d18e79c3e..221dd7f9d316e 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 0000000000000..942882be259f9 --- /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 0000000000000..84cf00ac08999 --- /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 0000000000000..cbb3680f37345 --- /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 086/345] 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 3afdc41888c79..d2d95d811c43d 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 0000000000000..81a0916be6e77 --- /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 087/345] 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 31bcc37612302..9c511efd22c4b 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 6a87bfe6787842550e33a9465c2750238c7a7da0 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 26 Feb 2019 12:52:43 +0200 Subject: [PATCH 088/345] MC-13844: [FT] [MFTF] AdminAvailabilityCreditMemoWithNoPaymentTest fails because of bad design --- .../ProductsQtyReturnAfterOrderCancelTest.xml | 1 - .../ActionGroup/AdminInvoiceActionGroup.xml | 15 +++++-- .../ActionGroup/AdminOrderActionGroup.xml | 3 +- .../AdminInvoiceMainActionsSection.xml | 2 +- ...vailabilityCreditMemoWithNoPaymentTest.xml | 44 ++++++++----------- ...ectnessInvoicedItemInBundleProductTest.xml | 2 +- .../Test/Mftf/Test/AdminCreateInvoiceTest.xml | 1 - .../Test/Mftf/Test/AdminTaxReportGridTest.xml | 3 +- 8 files changed, 34 insertions(+), 37 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml index 03e1d1b260ffd..456be43f80b8d 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml @@ -76,7 +76,6 @@ <click selector="{{AdminInvoiceItemsSection.updateQty}}" stepKey="updateQunatity"/> <waitForPageLoad stepKey="waitPageToBeLoaded"/> <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> - <waitForPageLoad stepKey="waitForSuccessMessageLoad"/> <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShipAction"/> <waitForPageLoad stepKey="waitOrderDetailToLoad"/> <fillField selector="{{AdminShipmentItemsSection.itemQtyToShip('1')}}" userInput="1" stepKey="changeItemQtyToShip"/> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml index c814a886a2b33..0061411d576e2 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml @@ -44,7 +44,6 @@ <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoiceButton"/> <waitForPageLoad stepKey="waitForNewInvoicePageLoad"/> <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> - <waitForPageLoad stepKey="waitForSuccessMessageLoad"/> <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="seeSuccessMessage"/> <click selector="{{AdminOrderDetailsOrderViewSection.invoices}}" stepKey="clickInvoices"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask5" /> @@ -58,9 +57,17 @@ <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Invoice" stepKey="seePageNameNewInvoicePage"/> </actionGroup> - <actionGroup name="submitInvoiceIntoOrder"> + <actionGroup name="StartCreateInvoiceFromOrderPage"> + <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoiceAction"/> + <seeInCurrentUrl url="{{AdminInvoiceNewPage.url}}" stepKey="seeNewInvoiceUrl"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Invoice" stepKey="seeNewInvoicePageTitle"/> + </actionGroup> + + <actionGroup name="SubmitInvoice"> <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> - <seeInCurrentUrl url="{{AdminOrderDetailsPage.url}}" stepKey="seeViewOrderPageInvoice"/> - <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="seeInvoiceCreateSuccess"/> + <waitForElementVisible selector="{{AdminMessagesSection.successMessage}}" stepKey="waitForMessageAppears"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="seeInvoiceCreateSuccess"/> + <grabFromCurrentUrl regex="~/order_id/(\d+)/~" stepKey="grabOrderId"/> + <seeInCurrentUrl url="{{AdminOrderDetailsPage.url('$grabOrderId')}}" stepKey="seeViewOrderPageInvoice"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml index aea04c8abfa60..0e09f3933c1aa 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml @@ -18,7 +18,8 @@ <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Orders" stepKey="seeIndexPageTitle"/> <click selector="{{AdminOrdersGridSection.createNewOrder}}" stepKey="clickCreateNewOrder"/> <click selector="{{AdminOrderFormActionSection.CreateNewCustomer}}" stepKey="clickCreateCustomer"/> - <click selector="{{AdminOrderStoreScopeTreeSection.storeOption(storeView.name)}}" stepKey="selectDefaultStoreView"/> + <conditionalClick selector="{{AdminOrderStoreScopeTreeSection.storeOption(storeView.name)}}" dependentSelector="{{AdminOrderStoreScopeTreeSection.storeOption(storeView.name)}}" visible="true" stepKey="selectStoreViewIfAppears"/> + <waitForPageLoad stepKey="waitForCreateOrderPageLoadAfterStoreSelect" /> <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Create New Order" stepKey="seeNewOrderPageTitle"/> </actionGroup> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceMainActionsSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceMainActionsSection.xml index bc7fc8145af33..011500fac3f69 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceMainActionsSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceMainActionsSection.xml @@ -9,7 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminInvoiceMainActionsSection"> - <element name="submitInvoice" type="button" selector=".action-default.scalable.save.submit-button.primary"/> + <element name="submitInvoice" type="button" selector=".action-default.scalable.save.submit-button.primary" timeout="60"/> <element name="openNewCreditMemoFromInvoice" type="button" selector=".action-default.scalable.credit-memo"/> <element name="submitNewRefundFromInvoice" type="button" selector=".action-default.scalable.save.submit-button refund primary"/> </section> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminAvailabilityCreditMemoWithNoPaymentTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminAvailabilityCreditMemoWithNoPaymentTest.xml index 9180636db7821..e405173429b2c 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminAvailabilityCreditMemoWithNoPaymentTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminAvailabilityCreditMemoWithNoPaymentTest.xml @@ -25,6 +25,7 @@ </createData> <!-- Enable *Free Shipping* --> <createData entity="FreeShippingMethodsSettingConfig" stepKey="freeShippingMethodsSettingConfig"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> @@ -33,62 +34,53 @@ <createData entity="DisableFreeShippingConfig" stepKey="disableFreeShippingConfig"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="AdminDeleteCustomerActionGroup" stepKey="deleteCustomer"> + <argument name="customerEmail" value="Simple_US_Customer.email"/> + </actionGroup> + <magentoCLI command="cache:flush" stepKey="flushCache"/> <actionGroup ref="logout" stepKey="logOut"/> </after> - <!-- Flush Magento Cache --> - <magentoCLI stepKey="flushCache" command="cache:flush"/> - <!--Proceed to Admin panel > SALES > Orders. Created order should be in Processing status--> - <amOnPage url="{{AdminOrderCreatePage.url}}" stepKey="navigateToSalesOrderPage"/> - <waitForPageLoad stepKey="waitForSalesOrderPageLoaded"/> - - <click selector="{{AdminOrderFormActionSection.CreateNewCustomer}}" stepKey="clickCreateCustomer"/> - <waitForElementVisible stepKey="waitForNewOrderPageOpened" selector="{{NewOrderSection.submitOrder}}"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Create New Order" stepKey="seeNewOrderPageTitle"/> + <actionGroup ref="navigateToNewOrderPageNewCustomer" stepKey="navigateToNewOrderPage"/> <!--Check if order can be submitted without the required fields including email address--> - <scrollToTopOfPage stepKey="scrollToTopOfOrderFormPage" after="seeNewOrderPageTitle"/> - <actionGroup ref="addSimpleProductToOrder" stepKey="addFirstProductToOrder" after="scrollToTopOfOrderFormPage"> + <scrollToTopOfPage stepKey="scrollToTopOfOrderFormPage"/> + <actionGroup ref="addSimpleProductToOrder" stepKey="addFirstProductToOrder"> <argument name="product" value="$$createProduct$$"/> </actionGroup> <!--Click *Custom Price* link, enter 0 and click *Update Items and Quantities* button--> <click selector="{{AdminOrderFormItemsSection.customPriceCheckbox}}" stepKey="clickCustomPriceCheckbox"/> - <waitForElementVisible stepKey="waitForPriceFieldAppears" selector="{{AdminOrderFormItemsSection.customPriceField}}"/> + <waitForElementVisible selector="{{AdminOrderFormItemsSection.customPriceField}}" stepKey="waitForPriceFieldAppears"/> <fillField selector="{{AdminOrderFormItemsSection.customPriceField}}" userInput="0" stepKey="fillCustomPriceField"/> <click selector="{{AdminOrderFormItemsSection.updateItemsAndQuantities}}" stepKey="clickUpdateItemsAndQuantitiesButton"/> <!--Fill customer group and customer email--> - <selectOption selector="{{AdminOrderFormAccountSection.group}}" userInput="{{GeneralCustomerGroup.code}}" stepKey="selectCustomerGroup" after="clickUpdateItemsAndQuantitiesButton"/> - <fillField selector="{{AdminOrderFormAccountSection.email}}" userInput="{{Simple_US_Customer.email}}" stepKey="fillCustomerEmail" after="selectCustomerGroup"/> + <selectOption selector="{{AdminOrderFormAccountSection.group}}" userInput="{{GeneralCustomerGroup.code}}" stepKey="selectCustomerGroup"/> + <fillField selector="{{AdminOrderFormAccountSection.email}}" userInput="{{Simple_US_Customer.email}}" stepKey="fillCustomerEmail"/> <!--Fill customer address information--> - <actionGroup ref="fillOrderCustomerInformation" stepKey="fillCustomerAddress" after="fillCustomerEmail"> + <actionGroup ref="fillOrderCustomerInformation" stepKey="fillCustomerAddress"> <argument name="customer" value="Simple_US_Customer"/> <argument name="address" value="US_Address_TX"/> </actionGroup> <!-- Select Free shipping --> - <actionGroup ref="orderSelectFreeShipping" stepKey="selectFreeShippingOption" after="fillCustomerAddress"/> + <actionGroup ref="orderSelectFreeShipping" stepKey="selectFreeShippingOption"/> <!--Click *Submit Order* button--> - <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="clickSubmitOrder" after="selectFreeShippingOption"/> + <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="clickSubmitOrder"/> <!--Click *Invoice* button--> - <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoiceButton"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Invoice" stepKey="seeNewInvoiceInPageTitle" after="clickInvoiceButton"/> - <waitForPageLoad stepKey="waitForInvoicePageOpened"/> - - <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> - <waitForPageLoad stepKey="waitForInvoiceSaved"/> - <see userInput="The invoice has been created." stepKey="seeCorrectMessage"/> + <actionGroup ref="StartCreateInvoiceFromOrderPage" stepKey="startCreateInvoice"/> + <actionGroup ref="SubmitInvoice" stepKey="submitInvoice"/> <!--Verify that *Credit Memo* button is displayed--> <seeElement selector="{{AdminOrderFormItemsSection.creditMemo}}" stepKey="seeCreditMemo"/> <click selector="{{AdminOrderFormItemsSection.creditMemo}}" stepKey="clickCreditMemoItem"/> <waitForPageLoad stepKey="waitForCreditMemoPageLoaded"/> - <see stepKey="seeNewMemoPage" userInput="New Memo"/> - <seeInCurrentUrl url="{{AdminCreditMemoNewPage.url}}" stepKey="seeUrlOnPage"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Memo" stepKey="seeNewMemoPageTitle"/> + <seeInCurrentUrl url="{{AdminCreditMemoNewPage.url}}" stepKey="seeNewMemoUrlOnPage"/> </test> </tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml index 7c83f35468ce6..f869841153aea 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml @@ -81,7 +81,7 @@ <fillField selector="{{AdminInvoiceItemsSection.qtyToInvoiceColumn}}" userInput="5" stepKey="ChangeQtyToInvoice"/> <click selector="{{AdminInvoiceItemsSection.updateQty}}" stepKey="updateQunatity"/> <waitForPageLoad stepKey="waitPageToBeLoaded"/> - <actionGroup ref="submitInvoiceIntoOrder" stepKey="submitInvoice"/> + <actionGroup ref="SubmitInvoice" stepKey="submitInvoice"/> <!--Verify invoiced items qty in ship tab--> <actionGroup ref="goToShipmentIntoOrder" stepKey="goToShipment"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml index 099cf7fbce914..ce66409ed9b3c 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml @@ -71,7 +71,6 @@ <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoice"/> <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> - <waitForPageLoad stepKey="waitForInvoicePageLoad"/> <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="seeSuccessMessage"/> <click selector="{{AdminOrderDetailsOrderViewSection.invoices}}" stepKey="clickInvoices"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask5" /> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxReportGridTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxReportGridTest.xml index 4741898b0ab86..628d189823a52 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxReportGridTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxReportGridTest.xml @@ -136,9 +136,8 @@ <waitForPageLoad stepKey="waitForInvoicePageOpened"/> <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> - <waitForPageLoad stepKey="waitForInvoiceSaved"/> - <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShipAction" after="waitForInvoiceSaved"/> + <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShipAction"/> <seeInCurrentUrl url="{{AdminShipmentNewPage.url}}" stepKey="seeOrderShipmentUrl" after="clickShipAction"/> <!--Submit Shipment--> <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" stepKey="clickSubmitShipment" after="seeOrderShipmentUrl"/> 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 089/345] 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 86986265bae2c..c658e8b359f81 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 fee86ca1caa29..ea4f4bf53eb71 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 0000000000000..4865e3a89b67a --- /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 0def6c20ad6ae7ed847cafd75b2cbdf8b651e25f Mon Sep 17 00:00:00 2001 From: Denis Kopylov <dkopylov@magenius.team> Date: Tue, 26 Feb 2019 19:31:34 +0300 Subject: [PATCH 090/345] [FIX] Revert usage of deprecate marked classes in layout --- .../view/frontend/layout/wishlist_index_index.xml | 12 ++++++------ .../frontend/templates/item/column/actions.phtml | 2 +- .../frontend/templates/item/column/comment.phtml | 2 +- .../view/frontend/templates/item/column/edit.phtml | 2 +- .../view/frontend/templates/item/column/name.phtml | 2 +- .../view/frontend/templates/item/column/remove.phtml | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) 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 8696d178d89d7..b20bc6a4e00ba 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 @@ -17,7 +17,7 @@ <block class="Magento\Wishlist\Block\Rss\Link" name="wishlist.rss.link" template="Magento_Wishlist::rss/wishlist.phtml"/> <block class="Magento\Wishlist\Block\Customer\Wishlist\Items" name="customer.wishlist.items" as="items" template="Magento_Wishlist::item/list.phtml" cacheable="false"> <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Image" name="customer.wishlist.item.image" template="Magento_Wishlist::item/column/image.phtml" cacheable="false"/> - <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.name" template="Magento_Wishlist::item/column/name.phtml" cacheable="false"/> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Info" name="customer.wishlist.item.name" template="Magento_Wishlist::item/column/name.phtml" cacheable="false"/> <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.review" template="Magento_Wishlist::item/column/review.phtml" cacheable="false"/> <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Cart" name="customer.wishlist.item.price" template="Magento_Wishlist::item/column/price.phtml" cacheable="false"> <block class="Magento\Catalog\Pricing\Render" name="product.price.render.wishlist"> @@ -30,11 +30,11 @@ </block> <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Options" name="customer.wishlist.item.options" cacheable="false"/> </block> - <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.inner" template="Magento_Wishlist::item/column/actions.phtml" cacheable="false"> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Actions" name="customer.wishlist.item.inner" template="Magento_Wishlist::item/column/actions.phtml" cacheable="false"> <arguments> <argument name="css_class" xsi:type="string">product-item-inner</argument> </arguments> - <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.comment" template="Magento_Wishlist::item/column/comment.phtml" cacheable="false"> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Comment" name="customer.wishlist.item.comment" template="Magento_Wishlist::item/column/comment.phtml" cacheable="false"> <arguments> <argument name="title" translate="true" xsi:type="string">Product Details and Comment</argument> </arguments> @@ -45,12 +45,12 @@ </arguments> </block> - <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.actions" template="Magento_Wishlist::item/column/actions.phtml" cacheable="false"> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Actions" name="customer.wishlist.item.actions" template="Magento_Wishlist::item/column/actions.phtml" cacheable="false"> <arguments> <argument name="css_class" xsi:type="string">product-item-actions</argument> </arguments> - <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.edit" template="Magento_Wishlist::item/column/edit.phtml" before="-" cacheable="false"/> - <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.remove" template="Magento_Wishlist::item/column/remove.phtml" cacheable="false"/> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Edit" name="customer.wishlist.item.edit" template="Magento_Wishlist::item/column/edit.phtml" before="-" cacheable="false"/> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Remove" name="customer.wishlist.item.remove" template="Magento_Wishlist::item/column/remove.phtml" cacheable="false"/> </block> </block> </block> diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/actions.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/actions.phtml index 7fa55a839b2ea..0b50df8c59fbb 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/actions.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/actions.phtml @@ -6,7 +6,7 @@ // @codingStandardsIgnoreFile -/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column $block */ +/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Actions $block */ ?> <?php $children = $block->getChildNames(); ?> <?php if ($children): ?> diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/comment.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/comment.phtml index 45bd7494fed4f..17e2404ee23cf 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/comment.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/comment.phtml @@ -6,7 +6,7 @@ // @codingStandardsIgnoreFile -/* @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column $block */ +/* @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Comment $block */ /* @var \Magento\Wishlist\Model\Item $item */ $item = $block->getItem(); diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/edit.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/edit.phtml index 81d770d556734..1898ef6fc0d66 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/edit.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/edit.phtml @@ -6,7 +6,7 @@ // @codingStandardsIgnoreFile -/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column $block */ +/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Edit $block */ /** @var \Magento\Wishlist\Model\Item $item */ $item = $block->getItem(); diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/name.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/name.phtml index 53c5f35f886ea..265f4635a6be3 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/name.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/name.phtml @@ -6,7 +6,7 @@ // @codingStandardsIgnoreFile -/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column $block */ +/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Info $block */ /** @var \Magento\Wishlist\Model\Item $item */ $item = $block->getItem(); diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/remove.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/remove.phtml index 0bd78ba38ba58..57ae439d9995b 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/remove.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/remove.phtml @@ -6,7 +6,7 @@ // @codingStandardsIgnoreFile -/* @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column $block */ +/* @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Remove $block */ ?> <a href="#" data-role="remove" data-post-remove='<?= /* @noEscape */ $block->getItemRemoveParams($block->getItem()) ?>' title="<?= $block->escapeHtmlAttr(__('Remove Item')) ?>" class="btn-remove action delete"> From 95ba7aec2559a9364d92bcce4fe4e8a851b7cc8a Mon Sep 17 00:00:00 2001 From: mageprince <mail.mageprince@gmail.com> Date: Wed, 27 Feb 2019 11:22:46 +0530 Subject: [PATCH 091/345] fix admin pages break --- .../web/css/source/module/header/_headings-group.less | 1 + 1 file changed, 1 insertion(+) diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/header/_headings-group.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/header/_headings-group.less index 832c66b7988e0..bf7ee7850f9d0 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/header/_headings-group.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/header/_headings-group.less @@ -42,4 +42,5 @@ color: @page-title__color; font-size: @page-title__font-size; margin-bottom: 0; + word-break: break-all; } 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 092/345] 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 e56cc6f32d804..a832390b83dc7 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 093/345] 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 a832390b83dc7..d298c4d6845f0 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 094/345] 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 0000000000000..ab4cab2cda5aa --- /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 0000000000000..27c9d2ede0f4b --- /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 0000000000000..21b81648ea479 --- /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 0000000000000..476e3756f55ca --- /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 0000000000000..adec5daddfeb0 --- /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 0000000000000..883cdab2951fd --- /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 0000000000000..10b81c4b1278a --- /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 0000000000000..72daebfd93bf7 --- /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 0000000000000..6553966b515a4 --- /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 ad9e7672ce11a..1a759867d9535 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 095/345] 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 4dcda8dcd41ae..4447b27360a80 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 0000000000000..a9444a4e5baa6 --- /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 0000000000000..5714cb4eb9f9c --- /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 0000000000000..995b860d107ca --- /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 0000000000000..bbafff8ad7739 --- /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 0000000000000..de7491471741c --- /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 0000000000000..812c302f9c00a --- /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 0000000000000..5d19198a1b94c --- /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 0000000000000..7922e994c4965 --- /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 0000000000000..dc9d99fabefc6 --- /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 096/345] 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 e69de29bb2d1d..6e312637c7b5a 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 d19ba0dd947b0..c2119df300c52 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 e2119ca3eee11..ec15736bef534 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 f3b141e9f902b..18053f20f010c 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 afe1a5d9e2591..3f2aedae1373c 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 0000000000000..912b0e42ca822 --- /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 0000000000000..f7ca6e0a09b5a --- /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 d7f680309c9ef..a85b468cebc92 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 097/345] 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 c2119df300c52..d491678d4d7fc 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 098/345] 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 d298c4d6845f0..ee3ab25e90875 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 099/345] 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 86e691daa4213..af1ef4400e442 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 0000000000000..f526e0d4ea067 --- /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 0000000000000..32d57353f4426 --- /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 0000000000000..02293c99bb3f4 --- /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 0000000000000..325cfd597956a --- /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 0000000000000..465c2d366c582 --- /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 0000000000000..a253ca4c13aa0 --- /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 0000000000000..4aa76b4fadfcc --- /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 0000000000000..0fd85c998866b --- /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 0000000000000..769f723dc9d5a --- /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 7a20d2bd4df5d..9cdf414dd06e1 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 c8f2edb862613..3b2cb8718e041 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 cb80bc4becaec..48c33c48f12e6 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 0000000000000..bae72d8186a18 --- /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 9bdacfbc1ff6c..72c5f2e0cbdfc 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 100/345] 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 0000000000000..f116e44e5c141 --- /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 0000000000000..1518adad01347 --- /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 0000000000000..0bd2dc9be4855 --- /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 0000000000000..eb0797d07bf18 --- /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 101/345] 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 ee3ab25e90875..02d55d50025cc 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 102/345] 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 02d55d50025cc..c05c9ef58b938 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 103/345] 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 c05c9ef58b938..f8d2f8bc11116 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 795d5329c18dd7a732fc71261ee5b74398882b1f Mon Sep 17 00:00:00 2001 From: Oleksandr Miroshnichenko <omiroshnichenko@magento.com> Date: Fri, 1 Mar 2019 09:53:11 -0600 Subject: [PATCH 104/345] Fix Admin Customizable Options Dropdown sort_order issue - fix test --- .../code/Magento/Ui/base/js/dynamic-rows/dynamic-rows.test.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/dynamic-rows/dynamic-rows.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/dynamic-rows/dynamic-rows.test.js index 2e238eb993029..fc60fbb0bdccc 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/dynamic-rows/dynamic-rows.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/dynamic-rows/dynamic-rows.test.js @@ -151,12 +151,14 @@ define([ } }], result = [{ + defaultLabelVisible: true, label: 'Label 2', name: 'Name 2', required: false, columnsHeaderClasses: '', sortOrder: 5 }, { + defaultLabelVisible: true, label: 'Label 1', name: 'Name 1', required: false, 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 105/345] 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 4f34f24c3a806..535478153f517 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 ae97351cafcaf..ad218cdd57500 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 58e1781d69eab..a08648bc8b8c6 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 b02e9adaed45e..3d3ec7fa28629 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 0000000000000..37829f4782413 --- /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 f57e5e883f3b948813f02aa6226b47e6bf58eb9f Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Sat, 2 Mar 2019 00:15:13 +0200 Subject: [PATCH 106/345] Sorting by Websites not working in product grid in backoffice #20511 --- .../Test/Mftf/Test/AdminSortingByWebsitesTest.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml index 354955ba54402..c4b08bc55d98a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml @@ -18,13 +18,13 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!--Create new website --> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createAdditionalWebsite"> - <argument name="newWebsiteName" value="Second Website"/> - <argument name="websiteCode" value="second_website"/> + <argument name="newWebsiteName" value="{{customWebsite.name}}"/> + <argument name="websiteCode" value="{{customWebsite.code}}"/> </actionGroup> </before> <after> <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteTestWebsite"> - <argument name="websiteName" value="Second Website"/> + <argument name="websiteName" value="{{customWebsite.name}}"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> </after> @@ -47,7 +47,7 @@ <!-- Add this product to second website --> <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="openProductInWebsitesSection1"/> - <click selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="selectSecondWebsite"/> + <click selector="{{ProductInWebsitesSection.website('{{customWebsite.name}}')}}" stepKey="selectSecondWebsite"/> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> <waitForLoadingMaskToDisappear stepKey="waitForProductPagetoSaveAgain"/> <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessageAgain"/> @@ -75,11 +75,11 @@ <actionGroup ref="resetProductGridToDefaultView" stepKey="setProductGridToDefaultSortingWebsites"/> <click selector="{{AdminProductGridSection.columnHeader('Websites')}}" stepKey="clickWebsitesHeaderToSortAsc"/> <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Main Website" stepKey="checkIfProduct1WebsitesAsc"/> - <see selector="{{AdminProductGridSection.productGridContentsOnRow('2')}}" userInput="Second Website" stepKey="checkIfProduct2WebsitesAsc"/> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('2')}}" userInput="{{customWebsite.name}}" stepKey="checkIfProduct2WebsitesAsc"/> <!--Sorting works (By Websites) DESC--> <click selector="{{AdminProductGridSection.columnHeader('Websites')}}" stepKey="clickWebsitesHeaderToSortDesc"/> - <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Second Website" stepKey="checkIfProduct1WebsitesDesc"/> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="{{customWebsite.name}}" stepKey="checkIfProduct1WebsitesDesc"/> <see selector="{{AdminProductGridSection.productGridContentsOnRow('2')}}" userInput="Main Website" stepKey="checkIfProduct2WebsitesDesc"/> </test> </tests> 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 107/345] 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 d812123e4b553..65e30f98c2ca5 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 0000000000000..70b4e95124da8 --- /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 530157851191f..be6e1af67fc63 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 0000000000000..451bce9e49a8f --- /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 8b2460718097c..b4c97a11b9145 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 d45bd6555d48b32ffb32a22c6668887c2bf68f86 Mon Sep 17 00:00:00 2001 From: suryakant <suryakant.makwana@krishtechnolabs.com> Date: Mon, 4 Mar 2019 12:43:50 +0530 Subject: [PATCH 108/345] Added space above error message. --- .../luma/Magento_Catalog/web/css/source/_module.less | 6 +----- .../luma/Magento_GroupedProduct/web/css/source/_module.less | 3 +++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less index 48fa5ecde302b..fc59d530fd16c 100644 --- a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less @@ -293,11 +293,7 @@ } } - .page-product-grouped { - .box-tocart { - margin-top: @indent__s; - } - } + .product-options-wrapper { .fieldset-product-options-inner { diff --git a/app/design/frontend/Magento/luma/Magento_GroupedProduct/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_GroupedProduct/web/css/source/_module.less index 088372808aa6a..92a67df1d26c3 100644 --- a/app/design/frontend/Magento/luma/Magento_GroupedProduct/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_GroupedProduct/web/css/source/_module.less @@ -70,6 +70,9 @@ clear: left; } } + .box-tocart { + margin-top: @indent__s; + } } } From 64f270a2738f9795eb0048f331bcb22a9b1e6f54 Mon Sep 17 00:00:00 2001 From: suryakant <suryakant.makwana@krishtechnolabs.com> Date: Mon, 4 Mar 2019 14:08:08 +0530 Subject: [PATCH 109/345] Added space above error message. --- .../Magento/luma/Magento_Catalog/web/css/source/_module.less | 2 -- .../luma/Magento_GroupedProduct/web/css/source/_module.less | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less index fc59d530fd16c..33b1ff43fe046 100644 --- a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less @@ -293,8 +293,6 @@ } } - - .product-options-wrapper { .fieldset-product-options-inner { .legend { diff --git a/app/design/frontend/Magento/luma/Magento_GroupedProduct/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_GroupedProduct/web/css/source/_module.less index 92a67df1d26c3..550f8718ddead 100644 --- a/app/design/frontend/Magento/luma/Magento_GroupedProduct/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_GroupedProduct/web/css/source/_module.less @@ -70,6 +70,7 @@ clear: left; } } + .box-tocart { margin-top: @indent__s; } 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 110/345] 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 ef8d77c8824ff..dd8bd7b6789a2 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 b02e9adaed45e..c3b3fc8fa342d 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 0000000000000..44b774eed372f --- /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 111/345] 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 a08648bc8b8c6..cf2cabdcc2399 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 112/345] 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 37829f4782413..380f6c31f98a7 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 113/345] 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 21b81648ea479..63248dd53fc1b 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 476e3756f55ca..3a4010f417104 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 adec5daddfeb0..09d9469cb093d 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 883cdab2951fd..aaeb0cd38cd99 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 10b81c4b1278a..597ee2336b21f 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 72daebfd93bf7..b1e723e5ee1ff 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 6553966b515a4..e3c5cd78397f6 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 1a759867d9535..528ad23aaf2bf 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 114/345] 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 7a166b47256cb..b38c5c2cda3ad 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 473f9a5438ba08dc850d624e08f4f28f896af790 Mon Sep 17 00:00:00 2001 From: Denis Kopylov <dkopylov@magenius.team> Date: Tue, 5 Mar 2019 09:42:36 +0300 Subject: [PATCH 115/345] [FIX] Change deprecation annotation (https://github.com/magento/magento2/issues/21419) --- .../Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php | 2 +- .../Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php | 2 +- .../Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php | 2 +- .../Wishlist/Block/Customer/Wishlist/Item/Column/Info.php | 2 +- .../Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php index 0cf6b6a9867d1..41ce705c77607 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php @@ -13,7 +13,7 @@ /** * @api - * @deprecated Empty class + * @deprecated * @since 100.0.2 */ class Actions extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php index 5f40e96bfa6ff..f63d083bf56b6 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php @@ -13,7 +13,7 @@ /** * @api - * @deprecated Empty class + * @deprecated * @since 100.0.2 */ class Comment extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php index c4e94ec503175..1fc73c05f6f86 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php @@ -13,7 +13,7 @@ /** * @api - * @deprecated Empty class + * @deprecated * @since 100.0.2 */ class Edit extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php index f83c122910ebd..e6067392bae48 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php @@ -13,7 +13,7 @@ /** * @api - * @deprecated Empty class + * @deprecated * @since 100.0.2 */ class Info extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php index 3e6f225842e01..724ffd90f87db 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php @@ -13,7 +13,7 @@ /** * @api - * @deprecated Empty class + * @deprecated * @since 100.0.2 */ class Remove extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column From c105a2fec056ebf9d7aef32310f0a82218b5230c Mon Sep 17 00:00:00 2001 From: mageprince <mail.mageprince@gmail.com> Date: Tue, 5 Mar 2019 14:40:34 +0530 Subject: [PATCH 116/345] Fix admin search filter page break --- .../module/data-grid/data-grid-header/_data-grid-filters.less | 1 + 1 file changed, 1 insertion(+) diff --git a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/data-grid-header/_data-grid-filters.less b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/data-grid-header/_data-grid-filters.less index 6e03e1d0cebaa..ef4481bdd520b 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/data-grid-header/_data-grid-filters.less +++ b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/data-grid-header/_data-grid-filters.less @@ -354,6 +354,7 @@ .admin__current-filters-list-wrap { width: 100%; + word-break: break-all; } .admin__current-filters-list { 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 117/345] 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 87a7cce58e1a5..8f3e6b475b466 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 6b7cc12218990..ae0d721a549bd 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 118/345] 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 ae0d721a549bd..ca2da9585f934 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 f4d4d9b7fbe6ab7b0b4721d476b46239d44a2b4d Mon Sep 17 00:00:00 2001 From: Nainesh <nainesh@twojay.in> Date: Tue, 5 Mar 2019 19:25:10 +0530 Subject: [PATCH 119/345] fixes-for-#20414::Recent orders grid not aligned from left in mobile as all content aligned from left --- .../luma/Magento_Sales/web/css/source/_module.less | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_module.less index 1e4a92fa0701f..f49c588f16f9f 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_module.less @@ -392,6 +392,17 @@ &.orders-recent { &:extend(.abs-account-table-margin-mobile all); &:extend(.abs-no-border-top all); + .table-order-items { + &.table { + tbody { + > tr { + > td.col { + padding-left: 0; + } + } + } + } + } } } From 6c820d361171287a89a1f89c0202867c02df2044 Mon Sep 17 00:00:00 2001 From: Nazarn96 <nazarn96@gmail.com> Date: Fri, 1 Mar 2019 15:55:16 +0200 Subject: [PATCH 120/345] Remove Environment emulation to better performance --- .../Controller/Adminhtml/Sitemap/Generate.php | 12 +++--------- app/code/Magento/Sitemap/Model/Observer.php | 19 +------------------ .../Model/ResourceModel/Catalog/Product.php | 1 + .../Sitemap/Test/Unit/Model/ObserverTest.php | 13 +------------ 4 files changed, 6 insertions(+), 39 deletions(-) diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php index 9592ab6f57c55..c8edefa00cc86 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php @@ -10,6 +10,9 @@ use Magento\Store\Model\App\Emulation; use Magento\Framework\App\ObjectManager; +/** + * Generate sitemap file + */ class Generate extends \Magento\Sitemap\Controller\Adminhtml\Sitemap { /** @var \Magento\Store\Model\App\Emulation $appEmulation */ @@ -44,14 +47,7 @@ public function execute() // if sitemap record exists if ($sitemap->getId()) { try { - //We need to emulate to get the correct frontend URL for the product images - $this->appEmulation->startEnvironmentEmulation( - $sitemap->getStoreId(), - \Magento\Framework\App\Area::AREA_FRONTEND, - true - ); $sitemap->generateXml(); - $this->messageManager->addSuccessMessage( __('The sitemap "%1" has been generated.', $sitemap->getSitemapFilename()) ); @@ -59,8 +55,6 @@ public function execute() $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { $this->messageManager->addExceptionMessage($e, __('We can\'t generate the sitemap right now.')); - } finally { - $this->appEmulation->stopEnvironmentEmulation(); } } else { $this->messageManager->addErrorMessage(__('We can\'t find a sitemap to generate.')); diff --git a/app/code/Magento/Sitemap/Model/Observer.php b/app/code/Magento/Sitemap/Model/Observer.php index bd7a84f601b77..ce74d738c4bc3 100644 --- a/app/code/Magento/Sitemap/Model/Observer.php +++ b/app/code/Magento/Sitemap/Model/Observer.php @@ -5,7 +5,6 @@ */ namespace Magento\Sitemap\Model; -use Magento\Store\Model\App\Emulation; use Magento\Sitemap\Model\EmailNotification as SitemapEmail; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Sitemap\Model\ResourceModel\Sitemap\CollectionFactory; @@ -57,11 +56,6 @@ class Observer */ private $collectionFactory; - /** - * @var Emulation - */ - private $appEmulation; - /** * @var $emailNotification */ @@ -72,17 +66,14 @@ class Observer * @param ScopeConfigInterface $scopeConfig * @param CollectionFactory $collectionFactory * @param EmailNotification $emailNotification - * @param Emulation $appEmulation */ public function __construct( ScopeConfigInterface $scopeConfig, CollectionFactory $collectionFactory, - SitemapEmail $emailNotification, - Emulation $appEmulation + SitemapEmail $emailNotification ) { $this->scopeConfig = $scopeConfig; $this->collectionFactory = $collectionFactory; - $this->appEmulation = $appEmulation; $this->emailNotification = $emailNotification; } @@ -114,17 +105,9 @@ public function scheduledGenerateSitemaps() foreach ($collection as $sitemap) { /* @var $sitemap \Magento\Sitemap\Model\Sitemap */ try { - $this->appEmulation->startEnvironmentEmulation( - $sitemap->getStoreId(), - \Magento\Framework\App\Area::AREA_FRONTEND, - true - ); - $sitemap->generateXml(); } catch (\Exception $e) { $errors[] = $e->getMessage(); - } finally { - $this->appEmulation->stopEnvironmentEmulation(); } } if ($errors && $recipient) { diff --git a/app/code/Magento/Sitemap/Model/ResourceModel/Catalog/Product.php b/app/code/Magento/Sitemap/Model/ResourceModel/Catalog/Product.php index 82024b3b015e5..4d678f5c1cb94 100644 --- a/app/code/Magento/Sitemap/Model/ResourceModel/Catalog/Product.php +++ b/app/code/Magento/Sitemap/Model/ResourceModel/Catalog/Product.php @@ -389,6 +389,7 @@ protected function _prepareProduct(array $productRow, $storeId) */ protected function _loadProductImages($product, $storeId) { + $this->_storeManager->setCurrentStore($storeId); /** @var $helper \Magento\Sitemap\Helper\Data */ $helper = $this->_sitemapData; $imageIncludePolicy = $helper->getProductImageIncludePolicy($storeId); diff --git a/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php b/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php index 5fae54ff3c5d0..09f5418bbd762 100644 --- a/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php +++ b/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php @@ -118,7 +118,7 @@ public function testScheduledGenerateSitemapsSendsExceptionEmail() ->method('getStoreId') ->willReturn($storeId); - $this->sitemapMock->expects($this->at(1)) + $this->sitemapMock->expects($this->once()) ->method('generateXml') ->willThrowException(new \Exception($exception)); @@ -130,17 +130,6 @@ public function testScheduledGenerateSitemapsSendsExceptionEmail() ) ->willReturn('error-recipient@example.com'); - $this->appEmulationMock->expects($this->at(0)) - ->method('startEnvironmentEmulation') - ->with( - $storeId, - Area::AREA_FRONTEND, - true - ); - - $this->appEmulationMock->expects($this->at(1)) - ->method('stopEnvironmentEmulation'); - $this->observer->scheduledGenerateSitemaps(); } } 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 121/345] 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 a9444a4e5baa6..33ffa4fe1b296 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 5714cb4eb9f9c..a825e06f490a5 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 812c302f9c00a..5491c27228387 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 7922e994c4965..a7d577a7508c0 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 dc9d99fabefc6..c72ed424ef307 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 122/345] 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 c658e8b359f81..e31f7193739cb 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 4865e3a89b67a..79f901952dd75 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 123/345] 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 dd8bd7b6789a2..ca614ec24138c 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 c3b3fc8fa342d..b07587b70302e 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 44b774eed372f..9d845cb237852 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 124/345] 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 0000000000000..7917fe68aaebc --- /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 0000000000000..963c9d9f1c911 --- /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 0b3a31194ea36..1c107d315a2b9 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 0000000000000..53406ed25e3c0 --- /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 0000000000000..5e7c3d164e067 --- /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 0000000000000..72156ce57294f --- /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 0000000000000..6a006814d4471 --- /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 58fd0a3f0bc2b..1721e3185402e 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 0000000000000..f11394c643ad7 --- /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 0000000000000..7732f15d4ce3a --- /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 125/345] 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 72156ce57294f..cdef15d2a0924 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 126/345] 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 cdef15d2a0924..22d79334796af 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 6a006814d4471..89008340675bb 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 127/345] 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 22d79334796af..954a2ee2d9d5d 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 89008340675bb..3ebe1f59885ba 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 edb0aad954fbb..77e5e2b91d93f 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 128/345] 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 5886b40c4547b..c82c8d3d088f3 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 a299e1689d6a7..482038a28ccb9 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 942882be259f9..a580e68639c7e 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 84cf00ac08999..f33a1dee8e8bd 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 cbb3680f37345..29b8365fb690a 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 129/345] 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 d2d95d811c43d..709f74ede09aa 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 81a0916be6e77..68dfc44aa886a 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 130/345] 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 ef8d77c8824ff..ca614ec24138c 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 f636336524f01..ae605256a2819 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 592af42f2de30..d7006fd01b2ff 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 0000000000000..6b666126569ae --- /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 131/345] 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 53406ed25e3c0..a9100b4730b8c 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 5e7c3d164e067..0000000000000 --- 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 954a2ee2d9d5d..f3238293e5713 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 3ebe1f59885ba..d45b294a34e4f 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 9ab463fda7bab8d58c791dd4ba07fe4ec8407943 Mon Sep 17 00:00:00 2001 From: amol2jcommerce <amol@twojay.in> Date: Wed, 6 Mar 2019 11:18:04 +0530 Subject: [PATCH 132/345] changes for Whitespace-issues-for-related-cross-and-upsell-grids --- .../luma/Magento_Catalog/web/css/source/module/_listings.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less index d477c08fc9553..e5915969c91b9 100644 --- a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less +++ b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less @@ -397,7 +397,7 @@ .page-products.page-layout-3columns { .products-grid { .product-item { - margin-left: 2%; + margin-left: 0; width: calc(~'(100% - 4%) / 3'); &:nth-child(3n + 1) { From a04f8f8446921d4312a4cf753335b90c6463c33d Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Wed, 6 Mar 2019 09:18:20 +0100 Subject: [PATCH 133/345] Test coverage: Set OfflineShipping methods on Cart --- .../SetOfflineShippingMethodsOnCartTest.php | 352 ++++++++++++++++++ 1 file changed, 352 insertions(+) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php new file mode 100644 index 0000000000000..99661e4cd342f --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php @@ -0,0 +1,352 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote; + +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\Quote\Model\Quote; +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 SetOfflineShippingOnCartTest extends GraphQlAbstract +{ + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var Quote + */ + private $quote; + + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedId; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->quoteResource = $objectManager->create(QuoteResource::class); + $this->quote = $objectManager->create(Quote::class); + $this->quoteIdToMaskedId = $objectManager->create(QuoteIdToMaskedQuoteIdInterface::class); + $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); + } + + /** + * Test for general routine of setting a shipping method on shopping cart + * + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/Checkout/_files/enable_all_shipping_methods.php + */ + public function testSetShippingMethodOnCart() + { + $shippingCarrierCode = 'flatrate'; + $shippingMethodCode = 'flatrate'; + $this->quoteResource->load( + $this->quote, + 'test_order_1', + 'reserved_order_id' + ); + $shippingAddress = $this->quote->getShippingAddress(); + $shippingAddressId = $shippingAddress->getId(); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + + $query = $this->prepareMutationQuery( + $maskedQuoteId, + $shippingMethodCode, + $shippingCarrierCode, + $shippingAddressId + ); + + $response = $this->sendRequestWithToken($query); + + self::assertArrayHasKey('setShippingMethodsOnCart', $response); + self::assertArrayHasKey('cart', $response['setShippingMethodsOnCart']); + $addressesInformation = $response['setShippingMethodsOnCart']['cart']['shipping_addresses']; + self::assertCount(1, $addressesInformation); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/Checkout/_files/enable_all_shipping_methods.php + */ + public function testSetFlatrateOnCart() + { + $this->setShippingMethodAndCheckResponse( + 'flatrate', + 'flatrate', + '10', + 'Flat Rate - Fixed' + ); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/OfflineShipping/_files/tablerates_weight.php + * @magentoApiDataFixture Magento/Checkout/_files/enable_all_shipping_methods.php + */ + public function testSetTableRatesOnCart() + { + $this->setShippingMethodAndCheckResponse( + 'tablerate', + 'bestway', + '10', + 'Best Way - Table Rate' + ); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/Checkout/_files/enable_all_shipping_methods.php + */ + public function testSetFreeShippingOnCart() + { + $this->setShippingMethodAndCheckResponse( + 'freeshipping', + 'freeshipping', + '0', + 'Free Shipping - Free' + ); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/Checkout/_files/enable_all_shipping_methods.php + */ + public function testSetUpsOnCart() + { + $this->setShippingMethodAndCheckResponse( + 'ups', + 'GND', + '15.61', + 'United Parcel Service - Ground' + ); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/Checkout/_files/enable_all_shipping_methods.php + */ + public function testSetShippingMethodWithWrongCartId() + { + $shippingCarrierCode = 'flatrate'; + $shippingMethodCode = 'flatrate'; + $shippingAddressId = '1'; + $maskedQuoteId = 'invalid'; + + $query = $this->prepareMutationQuery( + $maskedQuoteId, + $shippingMethodCode, + $shippingCarrierCode, + $shippingAddressId + ); + + self::expectExceptionMessage("Could not find a cart with ID \"$maskedQuoteId\""); + $this->sendRequestWithToken($query); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/Checkout/_files/enable_all_shipping_methods.php + */ + public function testSetNonExistingShippingMethod() + { + $shippingCarrierCode = 'non'; + $shippingMethodCode = 'existing'; + $this->quoteResource->load( + $this->quote, + 'test_order_1', + 'reserved_order_id' + ); + $shippingAddress = $this->quote->getShippingAddress(); + $shippingAddressId = $shippingAddress->getId(); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + + $query = $this->prepareMutationQuery( + $maskedQuoteId, + $shippingMethodCode, + $shippingCarrierCode, + $shippingAddressId + ); + + self::expectExceptionMessage("Carrier with such method not found: $shippingCarrierCode, $shippingMethodCode"); + $this->sendRequestWithToken($query); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/Checkout/_files/enable_all_shipping_methods.php + */ + public function testSetShippingMethodWithNonExistingAddress() + { + $shippingCarrierCode = 'flatrate'; + $shippingMethodCode = 'flatrate'; + $this->quoteResource->load( + $this->quote, + 'test_order_1', + 'reserved_order_id' + ); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + $shippingAddressId = '-20'; + + $query = $this->prepareMutationQuery( + $maskedQuoteId, + $shippingMethodCode, + $shippingCarrierCode, + $shippingAddressId + ); + + self::expectExceptionMessage("Could not find a cart address with ID \"$shippingAddressId\""); + $this->sendRequestWithToken($query); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/Checkout/_files/enable_all_shipping_methods.php + */ + public function testSetShippingMethodByGuestToCustomerCart() + { + $shippingCarrierCode = 'flatrate'; + $shippingMethodCode = 'flatrate'; + $this->quoteResource->load( + $this->quote, + 'test_order_1', + 'reserved_order_id' + ); + $shippingAddress = $this->quote->getShippingAddress(); + $shippingAddressId = $shippingAddress->getId(); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + + $query = $this->prepareMutationQuery( + $maskedQuoteId, + $shippingMethodCode, + $shippingCarrierCode, + $shippingAddressId + ); + + self::expectExceptionMessage( + "The current user cannot perform operations on cart \"$maskedQuoteId\"" + ); + + $this->graphQlQuery($query); + } + + /** + * Send request for setting the requested shipping method and check the output + * + * @param string $shippingCarrierCode + * @param string $shippingMethodCode + * @param string $shippingAmount + * @param string $shippingLabel + * @throws \Magento\Framework\Exception\AuthenticationException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function setShippingMethodAndCheckResponse( + string $shippingCarrierCode, + string $shippingMethodCode, + string $shippingAmount, + string $shippingLabel + ) { + $this->quoteResource->load( + $this->quote, + 'test_order_1', + 'reserved_order_id' + ); + $shippingAddress = $this->quote->getShippingAddress(); + $shippingAddressId = $shippingAddress->getId(); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + + $query = $this->prepareMutationQuery( + $maskedQuoteId, + $shippingMethodCode, + $shippingCarrierCode, + $shippingAddressId + ); + + $response = $this->sendRequestWithToken($query); + + $addressesInformation = $response['setShippingMethodsOnCart']['cart']['shipping_addresses']; + self::assertEquals($addressesInformation[0]['selected_shipping_method']['carrier_code'], $shippingCarrierCode); + self::assertEquals($addressesInformation[0]['selected_shipping_method']['method_code'], $shippingMethodCode); + self::assertEquals($addressesInformation[0]['selected_shipping_method']['amount'], $shippingAmount); + self::assertEquals($addressesInformation[0]['selected_shipping_method']['label'], $shippingLabel); + } + + /** + * Generates query for setting the specified shipping method on cart + * + * @param string $maskedQuoteId + * @param string $shippingMethodCode + * @param string $shippingCarrierCode + * @param string $shippingAddressId + * @return string + */ + private function prepareMutationQuery( + string $maskedQuoteId, + string $shippingMethodCode, + string $shippingCarrierCode, + string $shippingAddressId + ) : string { + return <<<QUERY +mutation { + setShippingMethodsOnCart(input: + { + cart_id: "$maskedQuoteId", + shipping_methods: [{ + cart_address_id: $shippingAddressId + method_code: "$shippingMethodCode" + carrier_code: "$shippingCarrierCode" + }] + }) { + + cart { + shipping_addresses { + selected_shipping_method { + carrier_code + method_code + label + amount + } + } + } + } +} + +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); + } +} \ No newline at end of file From 990a373803fc004b5da6bd20ca9d3149744a9f42 Mon Sep 17 00:00:00 2001 From: Alex Taranovsky <firster@atwix.com> Date: Wed, 6 Mar 2019 12:38:37 +0200 Subject: [PATCH 134/345] 429 - Test coverage: nonExistentCart 1. Add none existing cart test - Could not find a cart with ID "non_existent_masked_id" --- .../Customer/SetBillingAddressOnCartTest.php | 34 +++++++++++++++++++ .../Customer/SetPaymentMethodOnCartTest.php | 34 +++++++++++++++++++ .../Guest/SetBillingAddressOnCartTest.php | 33 ++++++++++++++++++ .../Guest/SetPaymentMethodOnCartTest.php | 33 ++++++++++++++++++ 4 files changed, 134 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 1a93c011e80a8..06926e0a0b2e9 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 @@ -403,6 +403,40 @@ public function testSetBillingAddressIfCustomerIsNotOwnerOfAddress() $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 testSetBillingAddressOnNonExistentCart() + { + $maskedQuoteId = 'non_existent_masked_id'; + $query = $this->getCartQuery($maskedQuoteId); + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + } + + /** + * @param string $maskedQuoteId + * @return string + */ + private function getCartQuery( + string $maskedQuoteId + ) : string { + return <<<QUERY +{ + cart(cart_id: "$maskedQuoteId") { + items { + id + qty + product { + sku + } + } + } +} +QUERY; + } + /** * Verify the all the whitelisted fields for a New Address Object * 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 bbc77b6c39740..a2bbbfe135fa1 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 @@ -151,6 +151,40 @@ public function testSetPaymentMethodToAnotherCustomerCart() $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 = $this->getCartQuery($maskedQuoteId); + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + } + + /** + * @param string $maskedQuoteId + * @return string + */ + private function getCartQuery( + string $maskedQuoteId + ) : string { + return <<<QUERY +{ + cart(cart_id: "$maskedQuoteId") { + items { + id + qty + product { + sku + } + } + } +} +QUERY; + } + /** * @param string $maskedQuoteId * @param string $methodCode 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 880d6aa0f406f..0bb7c9741197f 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 @@ -244,6 +244,39 @@ public function testSetBillingAddressFromAddressBook() $this->graphQlQuery($query); } + /** + * @expectedException \Exception + * @expectedExceptionMessage Could not find a cart with ID "non_existent_masked_id" + */ + public function testSetBillingAddressOnNonExistentCart() + { + $maskedQuoteId = 'non_existent_masked_id'; + $query = $this->getCartQuery($maskedQuoteId); + $this->graphQlQuery($query); + } + + /** + * @param string $maskedQuoteId + * @return string + */ + private function getCartQuery( + string $maskedQuoteId + ) : string { + return <<<QUERY +{ + cart(cart_id: "$maskedQuoteId") { + items { + id + qty + product { + sku + } + } + } +} +QUERY; + } + /** * Verify the all the whitelisted fields for a New Address Object * 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 7484b2af7569d..5117fce04a915 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,17 @@ public function testSetPaymentMethodToCustomerCart() $this->graphQlQuery($query); } + /** + * @expectedException \Exception + * @expectedExceptionMessage Could not find a cart with ID "non_existent_masked_id" + */ + public function testSetPaymentOnNonExistentCart() + { + $maskedQuoteId = 'non_existent_masked_id'; + $query = $this->getCartQuery($maskedQuoteId); + $this->graphQlQuery($query); + } + /** * @param string $maskedQuoteId * @param string $methodCode @@ -155,6 +166,28 @@ private function prepareMutationQuery( QUERY; } + /** + * @param string $maskedQuoteId + * @return string + */ + private function getCartQuery( + string $maskedQuoteId + ) : string { + return <<<QUERY +{ + cart(cart_id: "$maskedQuoteId") { + items { + id + qty + product { + sku + } + } + } +} +QUERY; + } + /** * @param string $reversedQuoteId * @param int $customerId From e1d5544a5a09d44420df6a787f54774e1072ecdb Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Wed, 6 Mar 2019 14:27:21 +0200 Subject: [PATCH 135/345] graphQl-427: adjusted code coverage of set shipping address feature --- .../SetShippingAddressOnCartTest.php | 195 +++----- .../Guest/SetShippingAddressOnCartTest.php | 442 ++++++++++++++++++ 2 files changed, 496 insertions(+), 141 deletions(-) rename dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/{ => Customer}/SetShippingAddressOnCartTest.php (71%) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php similarity index 71% rename from dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php rename to dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php index 4916bb2aa78c3..1d381bc944ec1 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php @@ -5,17 +5,14 @@ */ declare(strict_types=1); -namespace Magento\GraphQl\Quote; +namespace Magento\GraphQl\Quote\Customer; -use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Integration\Api\CustomerTokenServiceInterface; -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 @@ -53,10 +50,11 @@ protected function setUp() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php */ - public function testSetNewShippingAddressByGuest() + public function testSetNewShippingAddress() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->assignQuoteToCustomer(); $query = <<<QUERY mutation { @@ -91,8 +89,8 @@ public function testSetNewShippingAddressByGuest() postcode telephone country { - code label + code } address_type } @@ -100,7 +98,7 @@ public function testSetNewShippingAddressByGuest() } } QUERY; - $response = $this->graphQlQuery($query); + $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('cart', $response['setShippingAddressesOnCart']); $cartResponse = $response['setShippingAddressesOnCart']['cart']; @@ -111,12 +109,15 @@ public function testSetNewShippingAddressByGuest() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php - * @expectedException \Exception - * @expectedExceptionMessage The current customer isn't authorized. + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @dataProvider requestWithoutRequiredParamsDataProvider + * @param string $params + * @param string $expectedException + * @throws \Exception */ - public function testSetShippingAddressFromAddressBookByGuest() + public function testSetNewShippingAddressWithEmptyRequiredParams(string $params, string $expectedException) { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->assignQuoteToCustomer(); $query = <<<QUERY mutation { @@ -125,7 +126,9 @@ public function testSetShippingAddressFromAddressBookByGuest() cart_id: "$maskedQuoteId" shipping_addresses: [ { - customer_address_id: 1 + address: { + $params + } } ] } @@ -138,16 +141,21 @@ public function testSetShippingAddressFromAddressBookByGuest() } } QUERY; - $this->graphQlQuery($query); + $this->expectExceptionMessage( + $expectedException + ); + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @expectedException \Exception + * @expectedExceptionMessage The Cart includes virtual product(s) only, so a shipping address is not used. */ - public function testSetNewShippingAddressByRegisteredCustomer() + public function testSetNewShippingAddressOnQuoteWithVirtualProducts() { - $maskedQuoteId = $this->assignQuoteToCustomer(); + $maskedQuoteId = $this->assignQuoteToCustomer('test_order_with_virtual_product_without_address'); $query = <<<QUERY mutation { @@ -174,30 +182,13 @@ public function testSetNewShippingAddressByRegisteredCustomer() ) { cart { shipping_addresses { - firstname - lastname - company - street city - postcode - telephone - country { - label - code - } - address_type } } } } QUERY; - $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); - - self::assertArrayHasKey('cart', $response['setShippingAddressesOnCart']); - $cartResponse = $response['setShippingAddressesOnCart']['cart']; - self::assertArrayHasKey('shipping_addresses', $cartResponse); - $shippingAddressResponse = current($cartResponse['shipping_addresses']); - $this->assertNewShippingAddressFields($shippingAddressResponse); + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); } /** @@ -205,7 +196,7 @@ public function testSetNewShippingAddressByRegisteredCustomer() * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php */ - public function testSetShippingAddressFromAddressBookByRegisteredCustomer() + public function testSetShippingAddressFromAddressBook() { $maskedQuoteId = $this->assignQuoteToCustomer(); @@ -277,36 +268,6 @@ public function testSetNotExistedShippingAddressFromAddressBook() $this->graphQlQuery($query, [], '', $this->getHeaderMap()); } - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php - * @expectedException \Exception - * @expectedExceptionMessage The shipping address must contain either "customer_address_id" or "address". - */ - public function testSetShippingAddressWithoutAddresses() - { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); - - $query = <<<QUERY -mutation { - setShippingAddressesOnCart( - input: { - cart_id: "$maskedQuoteId" - shipping_addresses: [ - {} - ] - } - ) { - cart { - shipping_addresses { - city - } - } - } -} -QUERY; - $this->graphQlQuery($query); - } - /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @magentoApiDataFixture Magento/Customer/_files/customer.php @@ -355,13 +316,15 @@ public function testSetNewShippingAddressAndFromAddressBookAtSameTime() } /** + * @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 You cannot specify multiple shipping addresses. + * @expectedExceptionMessage The current user cannot use address with ID "1" */ - public function testSetMultipleNewShippingAddresses() + public function testSetShippingAddressIfCustomerIsNotOwnerOfAddress() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->assignQuoteToCustomer('test_order_with_simple_product_without_address', 2); $query = <<<QUERY mutation { @@ -370,57 +333,21 @@ public function testSetMultipleNewShippingAddresses() cart_id: "$maskedQuoteId" shipping_addresses: [ { - address: { - firstname: "test firstname" - lastname: "test lastname" - company: "test company" - street: ["test street 1", "test street 2"] - city: "test city" - region: "test region" - postcode: "887766" - country_code: "US" - telephone: "88776655" - save_in_address_book: false - } - }, - { - address: { - firstname: "test firstname 2" - lastname: "test lastname 2" - company: "test company 2" - street: ["test street 1", "test street 2"] - city: "test city" - region: "test region" - postcode: "887766" - country_code: "US" - telephone: "88776655" - save_in_address_book: false - } + customer_address_id: 1 } ] } ) { cart { shipping_addresses { - city + postcode } } } } QUERY; - /** @var \Magento\Config\Model\ResourceModel\Config $config */ - $config = ObjectManager::getInstance()->get(\Magento\Config\Model\ResourceModel\Config::class); - $config->saveConfig( - Data::XML_PATH_CHECKOUT_MULTIPLE_AVAILABLE, - null, - ScopeConfigInterface::SCOPE_TYPE_DEFAULT, - 0 - ); - /** @var \Magento\Framework\App\Config\ReinitableConfigInterface $config */ - $config = ObjectManager::getInstance()->get(\Magento\Framework\App\Config\ReinitableConfigInterface::class); - $config->reinit(); - $this->graphQlQuery($query); + $this->graphQlQuery($query, [], '', $this->getHeaderMap('customer2@search.example.com')); } /** @@ -428,11 +355,10 @@ public function testSetMultipleNewShippingAddresses() * @magentoApiDataFixture Magento/Customer/_files/customer_address.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @expectedException \Exception - * @expectedExceptionMessage The current user cannot use address with ID "1" */ - public function testSetShippingAddressIfCustomerIsNotOwnerOfAddress() + public function testSetShippingAddressIfCustomerIsNotOwnerOfCart() { - $maskedQuoteId = $this->assignQuoteToCustomer('test_order_with_simple_product_without_address', 2); + $maskedQuoteId = $this->assignQuoteToCustomer('test_order_with_simple_product_without_address', 1); $query = <<<QUERY mutation { @@ -454,10 +380,27 @@ public function testSetShippingAddressIfCustomerIsNotOwnerOfAddress() } } QUERY; + $this->expectExceptionMessage( + "The current user cannot perform operations on cart \"$maskedQuoteId\"" + ); $this->graphQlQuery($query, [], '', $this->getHeaderMap('customer2@search.example.com')); } + /** + * TODO: currently only the city param is required, do we need to add at least ZIP code? + * @return array + */ + public function requestWithoutRequiredParamsDataProvider() + { + return [ + [ + 'save_in_address_book: false', + 'Field CartAddressInput.city of required type String! was not provided' + ] + ]; + } + /** * Verify the all the whitelisted fields for a New Address Object * @@ -512,18 +455,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 @@ -539,22 +470,4 @@ private function assignQuoteToCustomer( $this->quoteResource->save($quote); return $this->quoteIdToMaskedId->execute((int)$quote->getId()); } - - public function tearDown() - { - /** @var \Magento\Config\Model\ResourceModel\Config $config */ - $config = ObjectManager::getInstance()->get(\Magento\Config\Model\ResourceModel\Config::class); - - //default state of multishipping config - $config->saveConfig( - Data::XML_PATH_CHECKOUT_MULTIPLE_AVAILABLE, - 1, - ScopeConfigInterface::SCOPE_TYPE_DEFAULT, - 0 - ); - - /** @var \Magento\Framework\App\Config\ReinitableConfigInterface $config */ - $config = ObjectManager::getInstance()->get(\Magento\Framework\App\Config\ReinitableConfigInterface::class); - $config->reinit(); - } } 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 new file mode 100644 index 0000000000000..522091c1eb2bf --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php @@ -0,0 +1,442 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote\Guest; + +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Integration\Api\CustomerTokenServiceInterface; +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 + */ +class SetShippingAddressOnCartTest extends GraphQlAbstract +{ + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var QuoteFactory + */ + private $quoteFactory; + + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedId; + + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + 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_simple_product_saved.php + */ + public function testSetNewShippingAddress() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + { + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + region: "test region" + postcode: "887766" + country_code: "US" + telephone: "88776655" + save_in_address_book: false + } + } + ] + } + ) { + cart { + shipping_addresses { + firstname + lastname + company + street + city + postcode + telephone + country { + code + label + } + address_type + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + + self::assertArrayHasKey('cart', $response['setShippingAddressesOnCart']); + $cartResponse = $response['setShippingAddressesOnCart']['cart']; + self::assertArrayHasKey('shipping_addresses', $cartResponse); + $shippingAddressResponse = current($cartResponse['shipping_addresses']); + $this->assertNewShippingAddressFields($shippingAddressResponse); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php + * @expectedException \Exception + * @expectedExceptionMessage The Cart includes virtual product(s) only, so a shipping address is not used. + */ + public function testSetNewShippingAddressOnQuoteWithVirtualProducts() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_virtual_product_without_address'); + + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + { + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + region: "test region" + postcode: "887766" + country_code: "US" + telephone: "88776655" + save_in_address_book: false + } + } + ] + } + ) { + cart { + shipping_addresses { + city + } + } + } +} +QUERY; + $this->graphQlQuery($query); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @expectedException \Exception + * @expectedExceptionMessage The current customer isn't authorized. + */ + public function testSetShippingAddressFromAddressBook() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + { + customer_address_id: 1 + } + ] + } + ) { + cart { + shipping_addresses { + city + } + } + } +} +QUERY; + $this->graphQlQuery($query); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @expectedException \Exception + * @expectedExceptionMessage The shipping address must contain either "customer_address_id" or "address". + */ + public function testSetShippingAddressWithoutAddresses() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + {} + ] + } + ) { + cart { + shipping_addresses { + city + } + } + } +} +QUERY; + $this->graphQlQuery($query); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @dataProvider requestWithoutRequiredParamsDataProvider + * @param string $params + * @param string $expectedException + * @throws \Exception + */ + public function testSetNewShippingAddressWithEmptyRequiredParams(string $params, string $expectedException) + { + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + { + address: { + $params + } + } + ] + } + ) { + cart { + shipping_addresses { + city + } + } + } +} +QUERY; + $this->expectExceptionMessage( + $expectedException + ); + $this->graphQlQuery($query); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @expectedException \Exception + * @expectedExceptionMessage You cannot specify multiple shipping addresses. + */ + public function testSetMultipleNewShippingAddresses() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + { + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + region: "test region" + postcode: "887766" + country_code: "US" + telephone: "88776655" + save_in_address_book: false + } + }, + { + address: { + firstname: "test firstname 2" + lastname: "test lastname 2" + company: "test company 2" + street: ["test street 1", "test street 2"] + city: "test city" + region: "test region" + postcode: "887766" + country_code: "US" + telephone: "88776655" + save_in_address_book: false + } + } + ] + } + ) { + cart { + shipping_addresses { + city + } + } + } +} +QUERY; + /** @var \Magento\Config\Model\ResourceModel\Config $config */ + $config = ObjectManager::getInstance()->get(\Magento\Config\Model\ResourceModel\Config::class); + $config->saveConfig( + Data::XML_PATH_CHECKOUT_MULTIPLE_AVAILABLE, + null, + ScopeConfigInterface::SCOPE_TYPE_DEFAULT, + 0 + ); + /** @var \Magento\Framework\App\Config\ReinitableConfigInterface $config */ + $config = ObjectManager::getInstance()->get(\Magento\Framework\App\Config\ReinitableConfigInterface::class); + $config->reinit(); + + $this->graphQlQuery($query); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @expectedException \Exception + */ + public function testSetShippingAddressIfCustomerIsNotOwnerOfCart() + { + $maskedQuoteId = $this->assignQuoteToCustomer('test_order_with_simple_product_without_address', 1); + + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + { + customer_address_id: 1 + } + ] + } + ) { + cart { + shipping_addresses { + postcode + } + } + } +} +QUERY; + $this->expectExceptionMessage( + "The current user cannot perform operations on cart \"$maskedQuoteId\"" + ); + + $this->graphQlQuery($query); + } + + /** + * Verify the all the whitelisted fields for a New Address Object + * + * @param array $shippingAddressResponse + */ + private function assertNewShippingAddressFields(array $shippingAddressResponse): void + { + $assertionMap = [ + ['response_field' => 'firstname', 'expected_value' => 'test firstname'], + ['response_field' => 'lastname', 'expected_value' => 'test lastname'], + ['response_field' => 'company', 'expected_value' => 'test company'], + ['response_field' => 'street', 'expected_value' => [0 => 'test street 1', 1 => 'test street 2']], + ['response_field' => 'city', 'expected_value' => 'test city'], + ['response_field' => 'postcode', 'expected_value' => '887766'], + ['response_field' => 'telephone', 'expected_value' => '88776655'], + ['response_field' => 'country', 'expected_value' => ['code' => 'US', 'label' => 'US']], + ['response_field' => 'address_type', 'expected_value' => 'SHIPPING'] + ]; + + $this->assertResponseFields($shippingAddressResponse, $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()); + } + + /** + * @param string $reversedQuoteId + * @param int $customerId + * @return string + */ + private function assignQuoteToCustomer( + string $reversedQuoteId = 'test_order_with_simple_product_without_address', + int $customerId = 1 + ): string { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + $quote->setCustomerId($customerId); + $this->quoteResource->save($quote); + return $this->quoteIdToMaskedId->execute((int)$quote->getId()); + } + + /** + * TODO: currently only the city param is required, do we need to add at least ZIP code? + * @return array + */ + public function requestWithoutRequiredParamsDataProvider() + { + return [ + [ + 'save_in_address_book: false', + 'Field CartAddressInput.city of required type String! was not provided' + ] + ]; + } + + public function tearDown() + { + /** @var \Magento\Config\Model\ResourceModel\Config $config */ + $config = ObjectManager::getInstance()->get(\Magento\Config\Model\ResourceModel\Config::class); + + //default state of multishipping config + $config->saveConfig( + Data::XML_PATH_CHECKOUT_MULTIPLE_AVAILABLE, + 1, + ScopeConfigInterface::SCOPE_TYPE_DEFAULT, + 0 + ); + + /** @var \Magento\Framework\App\Config\ReinitableConfigInterface $config */ + $config = ObjectManager::getInstance()->get(\Magento\Framework\App\Config\ReinitableConfigInterface::class); + $config->reinit(); + } +} From 40500a7793797e489e3b132e12fe8e3a47c2c050 Mon Sep 17 00:00:00 2001 From: Yogesh Suhagiya <yksuhagiya@gmail.com> Date: Wed, 6 Mar 2019 19:06:14 +0530 Subject: [PATCH 136/345] Fixed typo mistake --- lib/internal/Magento/Framework/App/FrontControllerInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/App/FrontControllerInterface.php b/lib/internal/Magento/Framework/App/FrontControllerInterface.php index a552d88e68f50..afd3091097d19 100644 --- a/lib/internal/Magento/Framework/App/FrontControllerInterface.php +++ b/lib/internal/Magento/Framework/App/FrontControllerInterface.php @@ -8,7 +8,7 @@ /** * Application front controller responsible for dispatching application requests. * Front controller contains logic common for all actions. - * Evary application area has own front controller + * Every application area has own front controller. * * @api */ From 4e8a9bf545f00e0c3e2f5b5c20f6248302c2ac95 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Wed, 6 Mar 2019 16:50:27 +0100 Subject: [PATCH 137/345] Namespace fix --- .../OfflineShipping/SetOfflineShippingMethodsOnCartTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php index 99661e4cd342f..e01dcc4bcd6eb 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\GraphQl\Quote; +namespace Magento\GraphQl\OfflineShipping; use Magento\Integration\Api\CustomerTokenServiceInterface; use Magento\Quote\Model\Quote; From 9b2be29006afb5c30e386144a3ef4315c7539538 Mon Sep 17 00:00:00 2001 From: Alex Taranovsky <firster@atwix.com> Date: Wed, 6 Mar 2019 18:44:27 +0200 Subject: [PATCH 138/345] 420 - Test coverage: GetAvailablePaymentMethodsTest for Guest 1. Add - testGetPaymentMethodsFromGuestCart() - testGetPaymentMethodsFromAnotherCustomerCart() - testGetPaymentMethodsIfPaymentsAreNotSet() - testGetPaymentMethodsOfNonExistentCart() to \Magento\GraphQl\Quote\\Guest\GetAvailablePaymentMethodsTest --- .../Guest/GetAvailablePaymentMethodsTest.php | 78 +++++++++++++++---- 1 file changed, 65 insertions(+), 13 deletions(-) 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 a5a08aaf39fb1..2f96ddc4e3824 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 @@ -47,21 +47,11 @@ protected function setUp() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php */ - public function testGetCartWithPaymentMethods() + public function testGetPaymentMethodsFromGuestCart() { $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); - - $query = <<<QUERY -{ - cart(cart_id: "$maskedQuoteId") { - available_payment_methods { - code - title - } - } -} -QUERY; - $response = $this->graphQlQuery($query); + $query = $this->getCartAvailablePaymentMethodsQuery($maskedQuoteId); + $response = $this->graphQlQuery($query); self::assertArrayHasKey('cart', $response); @@ -73,6 +63,68 @@ public function testGetCartWithPaymentMethods() '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 + */ + public function testGetPaymentMethodsFromAnotherCustomerCart() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); + $query = $this->getCartAvailablePaymentMethodsQuery($maskedQuoteId); + + $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/Payment/_files/disable_all_payment_methods.php + */ + public function testGetPaymentMethodsIfPaymentsAreNotSet() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $query = $this->getCartAvailablePaymentMethodsQuery($maskedQuoteId); + $response = $this->graphQlQuery($query); + + self::assertEquals(0, count($response['cart']['available_payment_methods'])); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Could not find a cart with ID "non_existent_masked_id" + */ + public function testGetPaymentMethodsOfNonExistentCart() + { + $maskedQuoteId = 'non_existent_masked_id'; + $query = $this->getCartAvailablePaymentMethodsQuery($maskedQuoteId); + $this->graphQlQuery($query); + } + + /** + * @param string $maskedQuoteId + * @return string + */ + private function getCartAvailablePaymentMethodsQuery( + string $maskedQuoteId + ) : string { + return <<<QUERY +{ + cart(cart_id: "$maskedQuoteId") { + available_payment_methods { + code + title + } + } +} +QUERY; } /** 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 139/345] 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 a825e06f490a5..83e4ac50a74e6 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 0000000000000..a710f3dd8fa5c --- /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 a2688893e7a690ded6524b4ef382fd729d712236 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Wed, 6 Mar 2019 15:03:24 -0600 Subject: [PATCH 140/345] MC-15289: Customer account group cannot be selected while creating a new customer in order --- .../Adminhtml/Order/Create/Form/Account.php | 24 ++----------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php index bb24d2ae15a34..5663c35ba8ac8 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php @@ -133,8 +133,7 @@ protected function _prepareForm() $this->_addAttributesToForm($attributes, $fieldset); $this->_form->addFieldNameSuffix('order[account]'); - $storeId = (int)$this->_sessionQuote->getStoreId(); - $this->_form->setValues($this->extractValuesFromAttributes($attributes, $storeId)); + $this->_form->setValues($this->extractValuesFromAttributes($attributes)); return $this; } @@ -192,10 +191,9 @@ public function getFormValues() * Extract the form values from attributes. * * @param array $attributes - * @param int $storeId * @return array */ - private function extractValuesFromAttributes(array $attributes, int $storeId): array + private function extractValuesFromAttributes(array $attributes): array { $formValues = $this->getFormValues(); foreach ($attributes as $code => $attribute) { @@ -203,26 +201,8 @@ private function extractValuesFromAttributes(array $attributes, int $storeId): a if (isset($defaultValue) && !isset($formValues[$code])) { $formValues[$code] = $defaultValue; } - if ($code === 'group_id' && empty($defaultValue)) { - $formValues[$code] = $this->getDefaultCustomerGroup($storeId); - } } return $formValues; } - - /** - * Gets default customer group. - * - * @param int $storeId - * @return string|null - */ - private function getDefaultCustomerGroup(int $storeId): ?string - { - return $this->_scopeConfig->getValue( - 'customer/create_account/default_group', - ScopeInterface::SCOPE_STORE, - $storeId - ); - } } From 5d8807a6042e3e900720610cafae299c6e2008a5 Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Wed, 6 Mar 2019 15:12:40 -0600 Subject: [PATCH 141/345] GraphQL-424: Test coverage: Set OfflineShipping methods on Cart --- .../SetOfflineShippingMethodsOnCartTest.php | 147 +----------------- 1 file changed, 2 insertions(+), 145 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php index e01dcc4bcd6eb..e65ac0275206b 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php @@ -51,40 +51,6 @@ protected function setUp() $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); } - /** - * Test for general routine of setting a shipping method on shopping cart - * - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - * @magentoApiDataFixture Magento/Checkout/_files/enable_all_shipping_methods.php - */ - public function testSetShippingMethodOnCart() - { - $shippingCarrierCode = 'flatrate'; - $shippingMethodCode = 'flatrate'; - $this->quoteResource->load( - $this->quote, - 'test_order_1', - 'reserved_order_id' - ); - $shippingAddress = $this->quote->getShippingAddress(); - $shippingAddressId = $shippingAddress->getId(); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - - $query = $this->prepareMutationQuery( - $maskedQuoteId, - $shippingMethodCode, - $shippingCarrierCode, - $shippingAddressId - ); - - $response = $this->sendRequestWithToken($query); - - self::assertArrayHasKey('setShippingMethodsOnCart', $response); - self::assertArrayHasKey('cart', $response['setShippingMethodsOnCart']); - $addressesInformation = $response['setShippingMethodsOnCart']['cart']['shipping_addresses']; - self::assertCount(1, $addressesInformation); - } - /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php * @magentoApiDataFixture Magento/Checkout/_files/enable_all_shipping_methods.php @@ -142,114 +108,6 @@ public function testSetUpsOnCart() ); } - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - * @magentoApiDataFixture Magento/Checkout/_files/enable_all_shipping_methods.php - */ - public function testSetShippingMethodWithWrongCartId() - { - $shippingCarrierCode = 'flatrate'; - $shippingMethodCode = 'flatrate'; - $shippingAddressId = '1'; - $maskedQuoteId = 'invalid'; - - $query = $this->prepareMutationQuery( - $maskedQuoteId, - $shippingMethodCode, - $shippingCarrierCode, - $shippingAddressId - ); - - self::expectExceptionMessage("Could not find a cart with ID \"$maskedQuoteId\""); - $this->sendRequestWithToken($query); - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - * @magentoApiDataFixture Magento/Checkout/_files/enable_all_shipping_methods.php - */ - public function testSetNonExistingShippingMethod() - { - $shippingCarrierCode = 'non'; - $shippingMethodCode = 'existing'; - $this->quoteResource->load( - $this->quote, - 'test_order_1', - 'reserved_order_id' - ); - $shippingAddress = $this->quote->getShippingAddress(); - $shippingAddressId = $shippingAddress->getId(); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - - $query = $this->prepareMutationQuery( - $maskedQuoteId, - $shippingMethodCode, - $shippingCarrierCode, - $shippingAddressId - ); - - self::expectExceptionMessage("Carrier with such method not found: $shippingCarrierCode, $shippingMethodCode"); - $this->sendRequestWithToken($query); - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - * @magentoApiDataFixture Magento/Checkout/_files/enable_all_shipping_methods.php - */ - public function testSetShippingMethodWithNonExistingAddress() - { - $shippingCarrierCode = 'flatrate'; - $shippingMethodCode = 'flatrate'; - $this->quoteResource->load( - $this->quote, - 'test_order_1', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - $shippingAddressId = '-20'; - - $query = $this->prepareMutationQuery( - $maskedQuoteId, - $shippingMethodCode, - $shippingCarrierCode, - $shippingAddressId - ); - - self::expectExceptionMessage("Could not find a cart address with ID \"$shippingAddressId\""); - $this->sendRequestWithToken($query); - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - * @magentoApiDataFixture Magento/Checkout/_files/enable_all_shipping_methods.php - */ - public function testSetShippingMethodByGuestToCustomerCart() - { - $shippingCarrierCode = 'flatrate'; - $shippingMethodCode = 'flatrate'; - $this->quoteResource->load( - $this->quote, - 'test_order_1', - 'reserved_order_id' - ); - $shippingAddress = $this->quote->getShippingAddress(); - $shippingAddressId = $shippingAddress->getId(); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - - $query = $this->prepareMutationQuery( - $maskedQuoteId, - $shippingMethodCode, - $shippingCarrierCode, - $shippingAddressId - ); - - self::expectExceptionMessage( - "The current user cannot perform operations on cart \"$maskedQuoteId\"" - ); - - $this->graphQlQuery($query); - } - /** * Send request for setting the requested shipping method and check the output * @@ -275,7 +133,7 @@ private function setShippingMethodAndCheckResponse( $shippingAddressId = $shippingAddress->getId(); $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - $query = $this->prepareMutationQuery( + $query = $this->getQuery( $maskedQuoteId, $shippingMethodCode, $shippingCarrierCode, @@ -300,7 +158,7 @@ private function setShippingMethodAndCheckResponse( * @param string $shippingAddressId * @return string */ - private function prepareMutationQuery( + private function getQuery( string $maskedQuoteId, string $shippingMethodCode, string $shippingCarrierCode, @@ -343,7 +201,6 @@ private function prepareMutationQuery( */ private function sendRequestWithToken(string $query): array { - $customerToken = $this->customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; From 4a7d8b1fd1892f34e63da0ff95e173beeb48d323 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Wed, 6 Mar 2019 15:32:44 -0600 Subject: [PATCH 142/345] MQE-1436: Remove composer.json from dev/tests/acceptance - composer/lock file removed, robofile removed. --- dev/tests/acceptance/RoboFile.php | 175 -- dev/tests/acceptance/composer.json | 25 - dev/tests/acceptance/composer.lock | 3534 ---------------------------- 3 files changed, 3734 deletions(-) delete mode 100644 dev/tests/acceptance/RoboFile.php delete mode 100755 dev/tests/acceptance/composer.json delete mode 100644 dev/tests/acceptance/composer.lock diff --git a/dev/tests/acceptance/RoboFile.php b/dev/tests/acceptance/RoboFile.php deleted file mode 100644 index e6e9e591bbd8b..0000000000000 --- a/dev/tests/acceptance/RoboFile.php +++ /dev/null @@ -1,175 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -use Symfony\Component\Yaml\Yaml; - -/** This is project's console commands configuration for Robo task runner. - * - * @codingStandardsIgnoreStart - * @see http://robo.li/ - */ -class RoboFile extends \Robo\Tasks -{ - use Robo\Task\Base\loadShortcuts; - - /** - * Duplicate the Example configuration files for the Project. - * Build the Codeception project. - * - * @return void - */ - function buildProject() - { - passthru($this->getBaseCmd("build:project")); - } - - /** - * Generate all Tests in PHP OR Generate set of tests via passing array of tests - * - * @param array $tests - * @param array $opts - * @return \Robo\Result - */ - function generateTests(array $tests, $opts = [ - 'config' => null, - 'force' => false, - 'nodes' => null, - 'lines' => null, - 'tests' => null - ]) - { - $baseCmd = $this->getBaseCmd("generate:tests"); - - $mftfArgNames = ['config', 'nodes', 'lines', 'tests']; - // append arguments to the end of the command - foreach ($opts as $argName => $argValue) { - if (in_array($argName, $mftfArgNames) && $argValue !== null) { - $baseCmd .= " --$argName $argValue"; - } - } - - // use a separate conditional for the force flag (casting bool to string in php is hard) - if ($opts['force']) { - $baseCmd .= ' --force'; - } - - return $this->taskExec($baseCmd)->args($tests)->run(); - } - - /** - * Generate a suite based on name(s) passed in as args. - * - * @param array $args - * @throws Exception - * @return \Robo\Result - */ - function generateSuite(array $args) - { - if (empty($args)) { - throw new Exception("Please provide suite name(s) after generate:suite command"); - } - $baseCmd = $this->getBaseCmd("generate:suite"); - return $this->taskExec($baseCmd)->args($args)->run(); - } - - /** - * Run all Tests with the specified @group tag'. - * - * @param array $args - * @return \Robo\Result - */ - function group(array $args) - { - $args = array_merge($args, ['-k']); - $baseCmd = $this->getBaseCmd("run:group"); - return $this->taskExec($baseCmd)->args($args)->run(); - } - - /** - * Generate the HTML for the Allure report based on the Test XML output - Allure v1.4.X - * - * @return \Robo\Result - */ - function allure1Generate() - { - return $this->_exec('allure generate tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-results'. DIRECTORY_SEPARATOR .' -o tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-report'. DIRECTORY_SEPARATOR .''); - } - - /** - * Generate the HTML for the Allure report based on the Test XML output - Allure v2.3.X - * - * @return \Robo\Result - */ - function allure2Generate() - { - return $this->_exec('allure generate tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-results'. DIRECTORY_SEPARATOR .' --output tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-report'. DIRECTORY_SEPARATOR .' --clean'); - } - - /** - * Open the HTML Allure report - Allure v1.4.X - * - * @return \Robo\Result - */ - function allure1Open() - { - return $this->_exec('allure report open --report-dir tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-report'. DIRECTORY_SEPARATOR .''); - } - - /** - * Open the HTML Allure report - Allure v2.3.X - * - * @return \Robo\Result - */ - function allure2Open() - { - return $this->_exec('allure open --port 0 tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-report'. DIRECTORY_SEPARATOR .''); - } - - /** - * Generate and open the HTML Allure report - Allure v1.4.X - * - * @return \Robo\Result - */ - function allure1Report() - { - $result1 = $this->allure1Generate(); - - if ($result1->wasSuccessful()) { - return $this->allure1Open(); - } else { - return $result1; - } - } - - /** - * Generate and open the HTML Allure report - Allure v2.3.X - * - * @return \Robo\Result - */ - function allure2Report() - { - $result1 = $this->allure2Generate(); - - if ($result1->wasSuccessful()) { - return $this->allure2Open(); - } else { - return $result1; - } - } - - /** - * Private function for returning the formatted command for the passthru to mftf bin execution. - * - * @param string $command - * @return string - */ - private function getBaseCmd($command) - { - $this->writeln("\033[01;31m Use of robo will be deprecated with next major release, please use <root>/vendor/bin/mftf $command \033[0m"); - chdir(__DIR__); - return realpath('../../../vendor/bin/mftf') . " $command"; - } -} \ No newline at end of file diff --git a/dev/tests/acceptance/composer.json b/dev/tests/acceptance/composer.json deleted file mode 100755 index 83cad123f8568..0000000000000 --- a/dev/tests/acceptance/composer.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "description": "Magento 2 (Open Source) Functional Tests", - "type": "project", - "version": "1.0.0-dev", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.1.3||~7.2.0", - "codeception/codeception": "~2.3.4 || ~2.4.0", - "consolidation/robo": "^1.0.0", - "vlucas/phpdotenv": "^2.4" - }, - "autoload": { - "psr-4": { - "Magento\\": "tests/functional/Magento" - }, - "files": ["tests/_bootstrap.php"] - }, - "prefer-stable": true -} diff --git a/dev/tests/acceptance/composer.lock b/dev/tests/acceptance/composer.lock deleted file mode 100644 index 5d2c2c5eb1769..0000000000000 --- a/dev/tests/acceptance/composer.lock +++ /dev/null @@ -1,3534 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "b93d599d375af66b29edfd8a35875e69", - "packages": [ - { - "name": "behat/gherkin", - "version": "v4.6.0", - "source": { - "type": "git", - "url": "https://github.com/Behat/Gherkin.git", - "reference": "ab0a02ea14893860bca00f225f5621d351a3ad07" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/ab0a02ea14893860bca00f225f5621d351a3ad07", - "reference": "ab0a02ea14893860bca00f225f5621d351a3ad07", - "shasum": "" - }, - "require": { - "php": ">=5.3.1" - }, - "require-dev": { - "phpunit/phpunit": "~4.5|~5", - "symfony/phpunit-bridge": "~2.7|~3|~4", - "symfony/yaml": "~2.3|~3|~4" - }, - "suggest": { - "symfony/yaml": "If you want to parse features, represented in YAML files" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.4-dev" - } - }, - "autoload": { - "psr-0": { - "Behat\\Gherkin": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - } - ], - "description": "Gherkin DSL parser for PHP 5.3", - "homepage": "http://behat.org/", - "keywords": [ - "BDD", - "Behat", - "Cucumber", - "DSL", - "gherkin", - "parser" - ], - "time": "2019-01-16T14:22:17+00:00" - }, - { - "name": "codeception/codeception", - "version": "2.4.5", - "source": { - "type": "git", - "url": "https://github.com/Codeception/Codeception.git", - "reference": "5fee32d5c82791548931cbc34806b4de6aa1abfc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/Codeception/zipball/5fee32d5c82791548931cbc34806b4de6aa1abfc", - "reference": "5fee32d5c82791548931cbc34806b4de6aa1abfc", - "shasum": "" - }, - "require": { - "behat/gherkin": "^4.4.0", - "codeception/phpunit-wrapper": "^6.0.9|^7.0.6", - "codeception/stub": "^2.0", - "ext-json": "*", - "ext-mbstring": "*", - "facebook/webdriver": ">=1.1.3 <2.0", - "guzzlehttp/guzzle": ">=4.1.4 <7.0", - "guzzlehttp/psr7": "~1.0", - "php": ">=5.6.0 <8.0", - "symfony/browser-kit": ">=2.7 <5.0", - "symfony/console": ">=2.7 <5.0", - "symfony/css-selector": ">=2.7 <5.0", - "symfony/dom-crawler": ">=2.7 <5.0", - "symfony/event-dispatcher": ">=2.7 <5.0", - "symfony/finder": ">=2.7 <5.0", - "symfony/yaml": ">=2.7 <5.0" - }, - "require-dev": { - "codeception/specify": "~0.3", - "facebook/graph-sdk": "~5.3", - "flow/jsonpath": "~0.2", - "monolog/monolog": "~1.8", - "pda/pheanstalk": "~3.0", - "php-amqplib/php-amqplib": "~2.4", - "predis/predis": "^1.0", - "squizlabs/php_codesniffer": "~2.0", - "symfony/process": ">=2.7 <5.0", - "vlucas/phpdotenv": "^2.4.0" - }, - "suggest": { - "aws/aws-sdk-php": "For using AWS Auth in REST module and Queue module", - "codeception/phpbuiltinserver": "Start and stop PHP built-in web server for your tests", - "codeception/specify": "BDD-style code blocks", - "codeception/verify": "BDD-style assertions", - "flow/jsonpath": "For using JSONPath in REST module", - "league/factory-muffin": "For DataFactory module", - "league/factory-muffin-faker": "For Faker support in DataFactory module", - "phpseclib/phpseclib": "for SFTP option in FTP Module", - "stecman/symfony-console-completion": "For BASH autocompletion", - "symfony/phpunit-bridge": "For phpunit-bridge support" - }, - "bin": [ - "codecept" - ], - "type": "library", - "extra": { - "branch-alias": [] - }, - "autoload": { - "psr-4": { - "Codeception\\": "src\\Codeception", - "Codeception\\Extension\\": "ext" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Bodnarchuk", - "email": "davert@mail.ua", - "homepage": "http://codegyre.com" - } - ], - "description": "BDD-style testing framework", - "homepage": "http://codeception.com/", - "keywords": [ - "BDD", - "TDD", - "acceptance testing", - "functional testing", - "unit testing" - ], - "time": "2018-08-01T07:21:49+00:00" - }, - { - "name": "codeception/phpunit-wrapper", - "version": "7.6.1", - "source": { - "type": "git", - "url": "https://github.com/Codeception/phpunit-wrapper.git", - "reference": "ed4b12beb167dc2ecea293b4f6df6c20ce8d280f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/ed4b12beb167dc2ecea293b4f6df6c20ce8d280f", - "reference": "ed4b12beb167dc2ecea293b4f6df6c20ce8d280f", - "shasum": "" - }, - "require": { - "phpunit/php-code-coverage": "^6.0", - "phpunit/phpunit": ">=7.1 <7.6", - "sebastian/comparator": "^3.0", - "sebastian/diff": "^3.0" - }, - "require-dev": { - "codeception/specify": "*", - "vlucas/phpdotenv": "^2.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Codeception\\PHPUnit\\": "src\\" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Davert", - "email": "davert.php@resend.cc" - } - ], - "description": "PHPUnit classes used by Codeception", - "time": "2019-01-13T10:34:39+00:00" - }, - { - "name": "codeception/stub", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/Codeception/Stub.git", - "reference": "f50bc271f392a2836ff80690ce0c058efe1ae03e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/Stub/zipball/f50bc271f392a2836ff80690ce0c058efe1ae03e", - "reference": "f50bc271f392a2836ff80690ce0c058efe1ae03e", - "shasum": "" - }, - "require": { - "phpunit/phpunit": ">=4.8 <8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Codeception\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", - "time": "2018-07-26T11:55:37+00:00" - }, - { - "name": "consolidation/annotated-command", - "version": "2.11.2", - "source": { - "type": "git", - "url": "https://github.com/consolidation/annotated-command.git", - "reference": "004af26391cd7d1cd04b0ac736dc1324d1b4f572" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/004af26391cd7d1cd04b0ac736dc1324d1b4f572", - "reference": "004af26391cd7d1cd04b0ac736dc1324d1b4f572", - "shasum": "" - }, - "require": { - "consolidation/output-formatters": "^3.4", - "php": ">=5.4.5", - "psr/log": "^1", - "symfony/console": "^2.8|^3|^4", - "symfony/event-dispatcher": "^2.5|^3|^4", - "symfony/finder": "^2.5|^3|^4" - }, - "require-dev": { - "g1a/composer-test-scenarios": "^3", - "php-coveralls/php-coveralls": "^1", - "phpunit/phpunit": "^6", - "squizlabs/php_codesniffer": "^2.7" - }, - "type": "library", - "extra": { - "scenarios": { - "symfony4": { - "require": { - "symfony/console": "^4.0" - }, - "config": { - "platform": { - "php": "7.1.3" - } - } - }, - "symfony2": { - "require": { - "symfony/console": "^2.8" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.36" - }, - "remove": [ - "php-coveralls/php-coveralls" - ], - "config": { - "platform": { - "php": "5.4.8" - } - }, - "scenario-options": { - "create-lockfile": "false" - } - }, - "phpunit4": { - "require-dev": { - "phpunit/phpunit": "^4.8.36" - }, - "remove": [ - "php-coveralls/php-coveralls" - ], - "config": { - "platform": { - "php": "5.4.8" - } - } - } - }, - "branch-alias": { - "dev-master": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "Consolidation\\AnnotatedCommand\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Greg Anderson", - "email": "greg.1.anderson@greenknowe.org" - } - ], - "description": "Initialize Symfony Console commands from annotated command class methods.", - "time": "2019-02-02T02:29:53+00:00" - }, - { - "name": "consolidation/config", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/consolidation/config.git", - "reference": "925231dfff32f05b787e1fddb265e789b939cf4c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consolidation/config/zipball/925231dfff32f05b787e1fddb265e789b939cf4c", - "reference": "925231dfff32f05b787e1fddb265e789b939cf4c", - "shasum": "" - }, - "require": { - "dflydev/dot-access-data": "^1.1.0", - "grasmash/expander": "^1", - "php": ">=5.4.0" - }, - "require-dev": { - "g1a/composer-test-scenarios": "^1", - "phpunit/phpunit": "^5", - "satooshi/php-coveralls": "^1.0", - "squizlabs/php_codesniffer": "2.*", - "symfony/console": "^2.5|^3|^4", - "symfony/yaml": "^2.8.11|^3|^4" - }, - "suggest": { - "symfony/yaml": "Required to use Consolidation\\Config\\Loader\\YamlConfigLoader" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Consolidation\\Config\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Greg Anderson", - "email": "greg.1.anderson@greenknowe.org" - } - ], - "description": "Provide configuration services for a commandline tool.", - "time": "2018-10-24T17:55:35+00:00" - }, - { - "name": "consolidation/log", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/consolidation/log.git", - "reference": "b2e887325ee90abc96b0a8b7b474cd9e7c896e3a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consolidation/log/zipball/b2e887325ee90abc96b0a8b7b474cd9e7c896e3a", - "reference": "b2e887325ee90abc96b0a8b7b474cd9e7c896e3a", - "shasum": "" - }, - "require": { - "php": ">=5.4.5", - "psr/log": "^1.0", - "symfony/console": "^2.8|^3|^4" - }, - "require-dev": { - "g1a/composer-test-scenarios": "^3", - "php-coveralls/php-coveralls": "^1", - "phpunit/phpunit": "^6", - "squizlabs/php_codesniffer": "^2" - }, - "type": "library", - "extra": { - "scenarios": { - "symfony4": { - "require": { - "symfony/console": "^4.0" - }, - "config": { - "platform": { - "php": "7.1.3" - } - } - }, - "symfony2": { - "require": { - "symfony/console": "^2.8" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.36" - }, - "remove": [ - "php-coveralls/php-coveralls" - ], - "config": { - "platform": { - "php": "5.4.8" - } - } - }, - "phpunit4": { - "require-dev": { - "phpunit/phpunit": "^4.8.36" - }, - "remove": [ - "php-coveralls/php-coveralls" - ], - "config": { - "platform": { - "php": "5.4.8" - } - } - } - }, - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Consolidation\\Log\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Greg Anderson", - "email": "greg.1.anderson@greenknowe.org" - } - ], - "description": "Improved Psr-3 / Psr\\Log logger based on Symfony Console components.", - "time": "2019-01-01T17:30:51+00:00" - }, - { - "name": "consolidation/output-formatters", - "version": "3.4.0", - "source": { - "type": "git", - "url": "https://github.com/consolidation/output-formatters.git", - "reference": "a942680232094c4a5b21c0b7e54c20cce623ae19" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/a942680232094c4a5b21c0b7e54c20cce623ae19", - "reference": "a942680232094c4a5b21c0b7e54c20cce623ae19", - "shasum": "" - }, - "require": { - "dflydev/dot-access-data": "^1.1.0", - "php": ">=5.4.0", - "symfony/console": "^2.8|^3|^4", - "symfony/finder": "^2.5|^3|^4" - }, - "require-dev": { - "g1a/composer-test-scenarios": "^2", - "phpunit/phpunit": "^5.7.27", - "satooshi/php-coveralls": "^2", - "squizlabs/php_codesniffer": "^2.7", - "symfony/console": "3.2.3", - "symfony/var-dumper": "^2.8|^3|^4", - "victorjonsson/markdowndocs": "^1.3" - }, - "suggest": { - "symfony/var-dumper": "For using the var_dump formatter" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Consolidation\\OutputFormatters\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Greg Anderson", - "email": "greg.1.anderson@greenknowe.org" - } - ], - "description": "Format text by applying transformations provided by plug-in formatters.", - "time": "2018-10-19T22:35:38+00:00" - }, - { - "name": "consolidation/robo", - "version": "1.4.4", - "source": { - "type": "git", - "url": "https://github.com/consolidation/Robo.git", - "reference": "8bec6a6ea54a7d03d56552a4250c49dec3b3083d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consolidation/Robo/zipball/8bec6a6ea54a7d03d56552a4250c49dec3b3083d", - "reference": "8bec6a6ea54a7d03d56552a4250c49dec3b3083d", - "shasum": "" - }, - "require": { - "consolidation/annotated-command": "^2.10.2", - "consolidation/config": "^1.0.10", - "consolidation/log": "~1", - "consolidation/output-formatters": "^3.1.13", - "consolidation/self-update": "^1", - "grasmash/yaml-expander": "^1.3", - "league/container": "^2.2", - "php": ">=5.5.0", - "symfony/console": "^2.8|^3|^4", - "symfony/event-dispatcher": "^2.5|^3|^4", - "symfony/filesystem": "^2.5|^3|^4", - "symfony/finder": "^2.5|^3|^4", - "symfony/process": "^2.5|^3|^4" - }, - "replace": { - "codegyre/robo": "< 1.0" - }, - "require-dev": { - "codeception/aspect-mock": "^1|^2.1.1", - "codeception/base": "^2.3.7", - "codeception/verify": "^0.3.2", - "g1a/composer-test-scenarios": "^3", - "goaop/framework": "~2.1.2", - "goaop/parser-reflection": "^1.1.0", - "natxet/cssmin": "3.0.4", - "nikic/php-parser": "^3.1.5", - "patchwork/jsqueeze": "~2", - "pear/archive_tar": "^1.4.4", - "php-coveralls/php-coveralls": "^1", - "phpunit/php-code-coverage": "~2|~4", - "squizlabs/php_codesniffer": "^2.8" - }, - "suggest": { - "henrikbjorn/lurker": "For monitoring filesystem changes in taskWatch", - "natxet/CssMin": "For minifying CSS files in taskMinify", - "patchwork/jsqueeze": "For minifying JS files in taskMinify", - "pear/archive_tar": "Allows tar archives to be created and extracted in taskPack and taskExtract, respectively." - }, - "bin": [ - "robo" - ], - "type": "library", - "extra": { - "scenarios": { - "symfony4": { - "require": { - "symfony/console": "^4" - }, - "config": { - "platform": { - "php": "7.1.3" - } - } - }, - "symfony2": { - "require": { - "symfony/console": "^2.8" - }, - "remove": [ - "goaop/framework" - ], - "config": { - "platform": { - "php": "5.5.9" - } - }, - "scenario-options": { - "create-lockfile": "false" - } - } - }, - "branch-alias": { - "dev-master": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "Robo\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Davert", - "email": "davert.php@resend.cc" - } - ], - "description": "Modern task runner", - "time": "2019-02-08T20:59:23+00:00" - }, - { - "name": "consolidation/self-update", - "version": "1.1.5", - "source": { - "type": "git", - "url": "https://github.com/consolidation/self-update.git", - "reference": "a1c273b14ce334789825a09d06d4c87c0a02ad54" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consolidation/self-update/zipball/a1c273b14ce334789825a09d06d4c87c0a02ad54", - "reference": "a1c273b14ce334789825a09d06d4c87c0a02ad54", - "shasum": "" - }, - "require": { - "php": ">=5.5.0", - "symfony/console": "^2.8|^3|^4", - "symfony/filesystem": "^2.5|^3|^4" - }, - "bin": [ - "scripts/release" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "SelfUpdate\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Greg Anderson", - "email": "greg.1.anderson@greenknowe.org" - }, - { - "name": "Alexander Menk", - "email": "menk@mestrona.net" - } - ], - "description": "Provides a self:update command for Symfony Console applications.", - "time": "2018-10-28T01:52:03+00:00" - }, - { - "name": "container-interop/container-interop", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/container-interop/container-interop.git", - "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8", - "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8", - "shasum": "" - }, - "require": { - "psr/container": "^1.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Interop\\Container\\": "src/Interop/Container/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Promoting the interoperability of container objects (DIC, SL, etc.)", - "homepage": "https://github.com/container-interop/container-interop", - "time": "2017-02-14T19:40:03+00:00" - }, - { - "name": "dflydev/dot-access-data", - "version": "v1.1.0", - "source": { - "type": "git", - "url": "https://github.com/dflydev/dflydev-dot-access-data.git", - "reference": "3fbd874921ab2c041e899d044585a2ab9795df8a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/3fbd874921ab2c041e899d044585a2ab9795df8a", - "reference": "3fbd874921ab2c041e899d044585a2ab9795df8a", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "Dflydev\\DotAccessData": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dragonfly Development Inc.", - "email": "info@dflydev.com", - "homepage": "http://dflydev.com" - }, - { - "name": "Beau Simensen", - "email": "beau@dflydev.com", - "homepage": "http://beausimensen.com" - }, - { - "name": "Carlos Frutos", - "email": "carlos@kiwing.it", - "homepage": "https://github.com/cfrutos" - } - ], - "description": "Given a deep data structure, access data by dot notation.", - "homepage": "https://github.com/dflydev/dflydev-dot-access-data", - "keywords": [ - "access", - "data", - "dot", - "notation" - ], - "time": "2017-01-20T21:14:22+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", - "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "athletic/athletic": "~0.1.8", - "ext-pdo": "*", - "ext-phar": "*", - "phpunit/phpunit": "^6.2.3", - "squizlabs/php_codesniffer": "^3.0.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://github.com/doctrine/instantiator", - "keywords": [ - "constructor", - "instantiate" - ], - "time": "2017-07-22T11:58:36+00:00" - }, - { - "name": "facebook/webdriver", - "version": "1.6.0", - "source": { - "type": "git", - "url": "https://github.com/facebook/php-webdriver.git", - "reference": "bd8c740097eb9f2fc3735250fc1912bc811a954e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/facebook/php-webdriver/zipball/bd8c740097eb9f2fc3735250fc1912bc811a954e", - "reference": "bd8c740097eb9f2fc3735250fc1912bc811a954e", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-json": "*", - "ext-mbstring": "*", - "ext-zip": "*", - "php": "^5.6 || ~7.0", - "symfony/process": "^2.8 || ^3.1 || ^4.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.0", - "jakub-onderka/php-parallel-lint": "^0.9.2", - "php-coveralls/php-coveralls": "^2.0", - "php-mock/php-mock-phpunit": "^1.1", - "phpunit/phpunit": "^5.7", - "sebastian/environment": "^1.3.4 || ^2.0 || ^3.0", - "squizlabs/php_codesniffer": "^2.6", - "symfony/var-dumper": "^3.3 || ^4.0" - }, - "suggest": { - "ext-SimpleXML": "For Firefox profile creation" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-community": "1.5-dev" - } - }, - "autoload": { - "psr-4": { - "Facebook\\WebDriver\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "description": "A PHP client for Selenium WebDriver", - "homepage": "https://github.com/facebook/php-webdriver", - "keywords": [ - "facebook", - "php", - "selenium", - "webdriver" - ], - "time": "2018-05-16T17:37:13+00:00" - }, - { - "name": "grasmash/expander", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/grasmash/expander.git", - "reference": "95d6037344a4be1dd5f8e0b0b2571a28c397578f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/grasmash/expander/zipball/95d6037344a4be1dd5f8e0b0b2571a28c397578f", - "reference": "95d6037344a4be1dd5f8e0b0b2571a28c397578f", - "shasum": "" - }, - "require": { - "dflydev/dot-access-data": "^1.1.0", - "php": ">=5.4" - }, - "require-dev": { - "greg-1-anderson/composer-test-scenarios": "^1", - "phpunit/phpunit": "^4|^5.5.4", - "satooshi/php-coveralls": "^1.0.2|dev-master", - "squizlabs/php_codesniffer": "^2.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Grasmash\\Expander\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthew Grasmick" - } - ], - "description": "Expands internal property references in PHP arrays file.", - "time": "2017-12-21T22:14:55+00:00" - }, - { - "name": "grasmash/yaml-expander", - "version": "1.4.0", - "source": { - "type": "git", - "url": "https://github.com/grasmash/yaml-expander.git", - "reference": "3f0f6001ae707a24f4d9733958d77d92bf9693b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/grasmash/yaml-expander/zipball/3f0f6001ae707a24f4d9733958d77d92bf9693b1", - "reference": "3f0f6001ae707a24f4d9733958d77d92bf9693b1", - "shasum": "" - }, - "require": { - "dflydev/dot-access-data": "^1.1.0", - "php": ">=5.4", - "symfony/yaml": "^2.8.11|^3|^4" - }, - "require-dev": { - "greg-1-anderson/composer-test-scenarios": "^1", - "phpunit/phpunit": "^4.8|^5.5.4", - "satooshi/php-coveralls": "^1.0.2|dev-master", - "squizlabs/php_codesniffer": "^2.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Grasmash\\YamlExpander\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthew Grasmick" - } - ], - "description": "Expands internal property references in a yaml file.", - "time": "2017-12-16T16:06:03+00:00" - }, - { - "name": "guzzlehttp/guzzle", - "version": "6.3.3", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", - "shasum": "" - }, - "require": { - "guzzlehttp/promises": "^1.0", - "guzzlehttp/psr7": "^1.4", - "php": ">=5.5" - }, - "require-dev": { - "ext-curl": "*", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", - "psr/log": "^1.0" - }, - "suggest": { - "psr/log": "Required for using the Log middleware" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.3-dev" - } - }, - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "GuzzleHttp\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Guzzle is a PHP HTTP client library", - "homepage": "http://guzzlephp.org/", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "rest", - "web service" - ], - "time": "2018-04-22T15:46:56+00:00" - }, - { - "name": "guzzlehttp/promises", - "version": "v1.3.1", - "source": { - "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", - "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", - "shasum": "" - }, - "require": { - "php": ">=5.5.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Guzzle promises library", - "keywords": [ - "promise" - ], - "time": "2016-12-20T10:07:11+00:00" - }, - { - "name": "guzzlehttp/psr7", - "version": "1.5.2", - "source": { - "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "9f83dded91781a01c63574e387eaa769be769115" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/9f83dded91781a01c63574e387eaa769be769115", - "reference": "9f83dded91781a01c63574e387eaa769be769115", - "shasum": "" - }, - "require": { - "php": ">=5.4.0", - "psr/http-message": "~1.0", - "ralouphie/getallheaders": "^2.0.5" - }, - "provide": { - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.5-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Tobias Schultze", - "homepage": "https://github.com/Tobion" - } - ], - "description": "PSR-7 message implementation that also provides common utility methods", - "keywords": [ - "http", - "message", - "psr-7", - "request", - "response", - "stream", - "uri", - "url" - ], - "time": "2018-12-04T20:46:45+00:00" - }, - { - "name": "league/container", - "version": "2.4.1", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/container.git", - "reference": "43f35abd03a12977a60ffd7095efd6a7808488c0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/container/zipball/43f35abd03a12977a60ffd7095efd6a7808488c0", - "reference": "43f35abd03a12977a60ffd7095efd6a7808488c0", - "shasum": "" - }, - "require": { - "container-interop/container-interop": "^1.2", - "php": "^5.4.0 || ^7.0" - }, - "provide": { - "container-interop/container-interop-implementation": "^1.2", - "psr/container-implementation": "^1.0" - }, - "replace": { - "orno/di": "~2.0" - }, - "require-dev": { - "phpunit/phpunit": "4.*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev", - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "League\\Container\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Phil Bennett", - "email": "philipobenito@gmail.com", - "homepage": "http://www.philipobenito.com", - "role": "Developer" - } - ], - "description": "A fast and intuitive dependency injection container.", - "homepage": "https://github.com/thephpleague/container", - "keywords": [ - "container", - "dependency", - "di", - "injection", - "league", - "provider", - "service" - ], - "time": "2017-05-10T09:20:27+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.8.1", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", - "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "replace": { - "myclabs/deep-copy": "self.version" - }, - "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^7.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, - "files": [ - "src/DeepCopy/deep_copy.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "time": "2018-06-11T23:09:50+00:00" - }, - { - "name": "phar-io/manifest", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "phar-io/version": "^2.0", - "php": "^5.6 || ^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2018-07-08T19:23:20+00:00" - }, - { - "name": "phar-io/version", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "time": "2018-07-08T19:19:57+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "shasum": "" - }, - "require": { - "php": ">=5.5" - }, - "require-dev": { - "phpunit/phpunit": "^4.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "time": "2017-09-11T18:02:19+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "4.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08", - "shasum": "" - }, - "require": { - "php": "^7.0", - "phpdocumentor/reflection-common": "^1.0.0", - "phpdocumentor/type-resolver": "^0.4.0", - "webmozart/assert": "^1.0" - }, - "require-dev": { - "doctrine/instantiator": "~1.0.5", - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^6.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-11-30T07:14:17+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "0.4.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", - "shasum": "" - }, - "require": { - "php": "^5.5 || ^7.0", - "phpdocumentor/reflection-common": "^1.0" - }, - "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "time": "2017-07-14T14:27:02+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "1.8.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", - "sebastian/comparator": "^1.1|^2.0|^3.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0" - }, - "require-dev": { - "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8.x-dev" - } - }, - "autoload": { - "psr-0": { - "Prophecy\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "time": "2018-08-05T17:53:17+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "6.1.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", - "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-xmlwriter": "*", - "php": "^7.1", - "phpunit/php-file-iterator": "^2.0", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^3.0", - "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.1 || ^4.0", - "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "suggest": { - "ext-xdebug": "^2.6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "time": "2018-10-31T16:06:48+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "050bedf145a257b1ff02746c31894800e5122946" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", - "reference": "050bedf145a257b1ff02746c31894800e5122946", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "time": "2018-09-13T20:33:42+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "time": "2015-06-21T13:50:34+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "8b8454ea6958c3dee38453d3bd571e023108c91f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/8b8454ea6958c3dee38453d3bd571e023108c91f", - "reference": "8b8454ea6958c3dee38453d3bd571e023108c91f", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "time": "2018-02-01T13:07:23+00:00" - }, - { - "name": "phpunit/php-token-stream", - "version": "3.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/c99e3be9d3e85f60646f152f9002d46ed7770d18", - "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": "^7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", - "keywords": [ - "tokenizer" - ], - "time": "2018-10-30T05:52:18+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "7.5.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "2896657da5fb237bc316bdfc18c2650efeee0dc0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2896657da5fb237bc316bdfc18c2650efeee0dc0", - "reference": "2896657da5fb237bc316bdfc18c2650efeee0dc0", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.1", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "myclabs/deep-copy": "^1.7", - "phar-io/manifest": "^1.0.2", - "phar-io/version": "^2.0", - "php": "^7.1", - "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^6.0.7", - "phpunit/php-file-iterator": "^2.0.1", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^2.0", - "sebastian/comparator": "^3.0", - "sebastian/diff": "^3.0", - "sebastian/environment": "^4.0", - "sebastian/exporter": "^3.1", - "sebastian/global-state": "^2.0", - "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^2.0", - "sebastian/version": "^2.0.1" - }, - "conflict": { - "phpunit/phpunit-mock-objects": "*" - }, - "require-dev": { - "ext-pdo": "*" - }, - "suggest": { - "ext-soap": "*", - "ext-xdebug": "*", - "phpunit/php-invoker": "^2.0" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "7.5-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "time": "2019-02-07T14:15:04+00:00" - }, - { - "name": "psr/container", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "time": "2017-02-14T16:28:37+00:00" - }, - { - "name": "psr/http-message", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", - "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" - ], - "time": "2016-08-06T14:39:51+00:00" - }, - { - "name": "psr/log", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", - "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "time": "2018-11-20T15:27:04+00:00" - }, - { - "name": "ralouphie/getallheaders", - "version": "2.0.5", - "source": { - "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/5601c8a83fbba7ef674a7369456d12f1e0d0eafa", - "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa", - "shasum": "" - }, - "require": { - "php": ">=5.3" - }, - "require-dev": { - "phpunit/phpunit": "~3.7.0", - "satooshi/php-coveralls": ">=1.0" - }, - "type": "library", - "autoload": { - "files": [ - "src/getallheaders.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } - ], - "description": "A polyfill for getallheaders.", - "time": "2016-02-11T07:05:27+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04T06:30:41+00:00" - }, - { - "name": "sebastian/comparator", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", - "shasum": "" - }, - "require": { - "php": "^7.1", - "sebastian/diff": "^3.0", - "sebastian/exporter": "^3.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "time": "2018-07-12T15:12:46+00:00" - }, - { - "name": "sebastian/diff", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", - "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.5 || ^8.0", - "symfony/process": "^2 || ^3.3 || ^4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], - "time": "2019-02-04T06:01:07+00:00" - }, - { - "name": "sebastian/environment", - "version": "4.1.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "6fda8ce1974b62b14935adc02a9ed38252eca656" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6fda8ce1974b62b14935adc02a9ed38252eca656", - "reference": "6fda8ce1974b62b14935adc02a9ed38252eca656", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.5" - }, - "suggest": { - "ext-posix": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "time": "2019-02-01T05:27:49+00:00" - }, - { - "name": "sebastian/exporter", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", - "shasum": "" - }, - "require": { - "php": "^7.0", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "time": "2017-04-03T13:19:02+00:00" - }, - { - "name": "sebastian/global-state", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "time": "2017-04-27T15:39:26+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "shasum": "" - }, - "require": { - "php": "^7.0", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-08-03T12:35:26+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "773f97c67f28de00d397be301821b06708fca0be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", - "reference": "773f97c67f28de00d397be301821b06708fca0be", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "time": "2017-03-29T09:07:27+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2017-03-03T06:23:57+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", - "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2018-10-04T04:07:39+00:00" - }, - { - "name": "sebastian/version", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "time": "2016-10-03T07:35:21+00:00" - }, - { - "name": "symfony/browser-kit", - "version": "v4.2.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/browser-kit.git", - "reference": "ee4462581eb54bf34b746e4a5d522a4f21620160" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/ee4462581eb54bf34b746e4a5d522a4f21620160", - "reference": "ee4462581eb54bf34b746e4a5d522a4f21620160", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "symfony/dom-crawler": "~3.4|~4.0" - }, - "require-dev": { - "symfony/css-selector": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0" - }, - "suggest": { - "symfony/process": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\BrowserKit\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony BrowserKit Component", - "homepage": "https://symfony.com", - "time": "2019-01-16T21:31:25+00:00" - }, - { - "name": "symfony/console", - "version": "v4.2.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "1f0ad51dfde4da8a6070f06adc58b4e37cbb37a4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/1f0ad51dfde4da8a6070f06adc58b4e37cbb37a4", - "reference": "1f0ad51dfde4da8a6070f06adc58b4e37cbb37a4", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "symfony/contracts": "^1.0", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/dependency-injection": "<3.4", - "symfony/process": "<3.3" - }, - "provide": { - "psr/log-implementation": "1.0" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~3.4|~4.0", - "symfony/lock": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Console Component", - "homepage": "https://symfony.com", - "time": "2019-01-25T14:35:16+00:00" - }, - { - "name": "symfony/contracts", - "version": "v1.0.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/contracts.git", - "reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/contracts/zipball/1aa7ab2429c3d594dd70689604b5cf7421254cdf", - "reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf", - "shasum": "" - }, - "require": { - "php": "^7.1.3" - }, - "require-dev": { - "psr/cache": "^1.0", - "psr/container": "^1.0" - }, - "suggest": { - "psr/cache": "When using the Cache contracts", - "psr/container": "When using the Service contracts", - "symfony/cache-contracts-implementation": "", - "symfony/service-contracts-implementation": "", - "symfony/translation-contracts-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\": "" - }, - "exclude-from-classmap": [ - "**/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A set of abstractions extracted out of the Symfony components", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "time": "2018-12-05T08:06:11+00:00" - }, - { - "name": "symfony/css-selector", - "version": "v4.2.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/css-selector.git", - "reference": "48eddf66950fa57996e1be4a55916d65c10c604a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/48eddf66950fa57996e1be4a55916d65c10c604a", - "reference": "48eddf66950fa57996e1be4a55916d65c10c604a", - "shasum": "" - }, - "require": { - "php": "^7.1.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\CssSelector\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jean-François Simon", - "email": "jeanfrancois.simon@sensiolabs.com" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony CssSelector Component", - "homepage": "https://symfony.com", - "time": "2019-01-16T20:31:39+00:00" - }, - { - "name": "symfony/dom-crawler", - "version": "v4.2.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/dom-crawler.git", - "reference": "d8476760b04cdf7b499c8718aa437c20a9155103" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/d8476760b04cdf7b499c8718aa437c20a9155103", - "reference": "d8476760b04cdf7b499c8718aa437c20a9155103", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0" - }, - "require-dev": { - "symfony/css-selector": "~3.4|~4.0" - }, - "suggest": { - "symfony/css-selector": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\DomCrawler\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony DomCrawler Component", - "homepage": "https://symfony.com", - "time": "2019-01-16T20:35:37+00:00" - }, - { - "name": "symfony/event-dispatcher", - "version": "v4.2.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "bd09ad265cd50b2b9d09d65ce6aba2d29bc81fe1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/bd09ad265cd50b2b9d09d65ce6aba2d29bc81fe1", - "reference": "bd09ad265cd50b2b9d09d65ce6aba2d29bc81fe1", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "symfony/contracts": "^1.0" - }, - "conflict": { - "symfony/dependency-injection": "<3.4" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/expression-language": "~3.4|~4.0", - "symfony/stopwatch": "~3.4|~4.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony EventDispatcher Component", - "homepage": "https://symfony.com", - "time": "2019-01-16T20:35:37+00:00" - }, - { - "name": "symfony/filesystem", - "version": "v4.2.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "7c16ebc2629827d4ec915a52ac809768d060a4ee" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/7c16ebc2629827d4ec915a52ac809768d060a4ee", - "reference": "7c16ebc2629827d4ec915a52ac809768d060a4ee", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "symfony/polyfill-ctype": "~1.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Filesystem Component", - "homepage": "https://symfony.com", - "time": "2019-01-16T20:35:37+00:00" - }, - { - "name": "symfony/finder", - "version": "v4.2.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "ef71816cbb264988bb57fe6a73f610888b9aa70c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/ef71816cbb264988bb57fe6a73f610888b9aa70c", - "reference": "ef71816cbb264988bb57fe6a73f610888b9aa70c", - "shasum": "" - }, - "require": { - "php": "^7.1.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Finder Component", - "homepage": "https://symfony.com", - "time": "2019-01-16T20:35:37+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.10.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "e3d826245268269cd66f8326bd8bc066687b4a19" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19", - "reference": "e3d826245268269cd66f8326bd8bc066687b4a19", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.9-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - }, - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "time": "2018-08-06T14:22:27+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.10.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "c79c051f5b3a46be09205c73b80b346e4153e494" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494", - "reference": "c79c051f5b3a46be09205c73b80b346e4153e494", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.9-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "time": "2018-09-21T13:07:52+00:00" - }, - { - "name": "symfony/process", - "version": "v4.2.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "6c05edb11fbeff9e2b324b4270ecb17911a8b7ad" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/6c05edb11fbeff9e2b324b4270ecb17911a8b7ad", - "reference": "6c05edb11fbeff9e2b324b4270ecb17911a8b7ad", - "shasum": "" - }, - "require": { - "php": "^7.1.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Process Component", - "homepage": "https://symfony.com", - "time": "2019-01-24T22:05:03+00:00" - }, - { - "name": "symfony/yaml", - "version": "v4.2.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "d461670ee145092b7e2a56c1da7118f19cadadb0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/d461670ee145092b7e2a56c1da7118f19cadadb0", - "reference": "d461670ee145092b7e2a56c1da7118f19cadadb0", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "symfony/polyfill-ctype": "~1.8" - }, - "conflict": { - "symfony/console": "<3.4" - }, - "require-dev": { - "symfony/console": "~3.4|~4.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Yaml Component", - "homepage": "https://symfony.com", - "time": "2019-01-16T20:35:37+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b", - "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "time": "2017-04-07T12:08:54+00:00" - }, - { - "name": "vlucas/phpdotenv", - "version": "v2.6.1", - "source": { - "type": "git", - "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "2a7dcf7e3e02dc5e701004e51a6f304b713107d5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/2a7dcf7e3e02dc5e701004e51a6f304b713107d5", - "reference": "2a7dcf7e3e02dc5e701004e51a6f304b713107d5", - "shasum": "" - }, - "require": { - "php": ">=5.3.9", - "symfony/polyfill-ctype": "^1.9" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6-dev" - } - }, - "autoload": { - "psr-4": { - "Dotenv\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Vance Lucas", - "email": "vance@vancelucas.com", - "homepage": "http://www.vancelucas.com" - } - ], - "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", - "keywords": [ - "dotenv", - "env", - "environment" - ], - "time": "2019-01-29T11:11:52+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.4.0", - "source": { - "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9", - "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0", - "symfony/polyfill-ctype": "^1.8" - }, - "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "time": "2018-12-25T11:19:39+00:00" - } - ], - "packages-dev": [], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": true, - "prefer-lowest": false, - "platform": { - "php": "~7.1.3||~7.2.0" - }, - "platform-dev": [] -} 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 143/345] 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 0000000000000..3f47242e3aa61 --- /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 0730e7a7c5dc1..69969231490b7 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 0000000000000..029223972780a --- /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 0000000000000..35ed110f9c11a --- /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 0000000000000..b073725e934f0 --- /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 7d2c3699ee2c2..49447447622f9 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 0000000000000..8aa7c7fe5e49e --- /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 0000000000000..0090866c0e490 --- /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 0000000000000..c673a8321762a --- /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 0000000000000..0097d770936a3 --- /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 2f3caf08c534e..dfe0e5c9f30f3 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 144/345] 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 69969231490b7..eb75b8f7a701d 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 029223972780a..1bba257ff9864 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 de44af7f58afc..0000000000000 --- 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 0000000000000..26e84df443ef8 --- /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 9ca351aa1cf98..7748e43bbd621 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 a2967878402d0..0fe618b2db304 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 145/345] 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 1bba257ff9864..f9b8b44b14281 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 146/345] 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 dfe0e5c9f30f3..2f3caf08c534e 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 147/345] 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 26e84df443ef8..0000000000000 --- 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 148/345] 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 3f47242e3aa61..c03a73df5e92d 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 eb75b8f7a701d..331b3ed07831f 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 f9b8b44b14281..f340cb49ee099 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 35ed110f9c11a..0f09ff8d7699c 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 149/345] 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 d7607b4b269e8..1e31a9cf0eb23 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 c0eebd1512d6d..9961de9ce0171 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 845c47c0e4c20..305e0966a1f4f 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 593df1c5bc6e1..28285449d7e0d 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 af12f49bf86ea..2137e4d1fe3dd 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 150/345] 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 1e31a9cf0eb23..4d581bae700d7 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 9961de9ce0171..8a44c8093ca5e 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 305e0966a1f4f..bee13bec370da 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 28285449d7e0d..30a4290d882fb 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 2137e4d1fe3dd..39aa516077c56 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 151/345] 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 f340cb49ee099..3bcc91315022b 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 152/345] 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 331b3ed07831f..e91e0ce70f8fd 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 3bcc91315022b..2e9bbc18be4da 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 b073725e934f0..9871ba8e58b9f 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 8aa7c7fe5e49e..40d9cd1a378b0 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 0090866c0e490..4be387211df04 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 153/345] 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 c03a73df5e92d..0000000000000 --- 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 e91e0ce70f8fd..324f3aba14c7a 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 2e9bbc18be4da..7e6c68a1502e0 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 0000000000000..ce4113a03df59 --- /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 0f09ff8d7699c..0000000000000 --- 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 f78048424b42c..3863cf2457247 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 9871ba8e58b9f..1a957f6ac9fe5 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 40d9cd1a378b0..137f34a5c1e25 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 4be387211df04..d9e66ae69c10c 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 c673a8321762a..1606ea42ec0b3 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 0097d770936a3..bdac891afbdb8 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 154/345] 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 ce4113a03df59..f8cc3c04df605 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 0000000000000..36958a37c6d1a --- /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 8d57fab843f4c..4253dc09b4e29 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 155/345] 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 324f3aba14c7a..f0b19b2073cc3 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 156/345] 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 765d0a616f77c..0e1860405e946 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 25ea720c478f7cc9475a05fe17242b9973920d54 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Wed, 6 Mar 2019 17:05:27 -0600 Subject: [PATCH 157/345] MC-15289: Customer account group cannot be selected while creating a new customer in order - Added functional test --- .../AdminChangeCustomerGroupInNewOrder.xml | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 app/code/Magento/Sales/Test/Mftf/Test/AdminChangeCustomerGroupInNewOrder.xml diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminChangeCustomerGroupInNewOrder.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminChangeCustomerGroupInNewOrder.xml new file mode 100644 index 0000000000000..85ef563e10db7 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminChangeCustomerGroupInNewOrder.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="AdminChangeCustomerGroupInNewOrder"> + <annotations> + <title value="Customer account group cannot be selected while creating a new customer in order"/> + <stories value="MC-15290: Customer account group cannot be selected while creating a new customer in order"/> + <description value="Customer account group cannot be selected while creating a new customer in order"/> + <severity value="MAJOR"/> + <testCaseId value="MC-15290"/> + <useCaseId value="MC-15289"/> + <group value="sales"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <actionGroup ref="navigateToNewOrderPageNewCustomerSingleStore" stepKey="openNewOrder"/> + <selectOption selector="{{AdminOrderFormAccountSection.group}}" userInput="Retailer" stepKey="selectCustomerGroup"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <grabValueFrom selector="{{AdminOrderFormAccountSection.group}}" stepKey="grabGroupValue"/> + <assertEquals stepKey="assertValueIsStillSelected"> + <actualResult type="variable">$grabGroupValue</actualResult> + <expectedResult type="string">3</expectedResult> + </assertEquals> + </test> +</tests> 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 158/345] 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 f0b19b2073cc3..5409991477539 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 7e6c68a1502e0..f9c90f82dacd4 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 f8cc3c04df605..591c0892b4228 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 13991bbefd2ad895c4d6047d8c0e4675130ca8f6 Mon Sep 17 00:00:00 2001 From: amol2jcommerce <amol@twojay.in> Date: Thu, 7 Mar 2019 12:08:31 +0530 Subject: [PATCH 159/345] changes for advanced-Search-layout-not-proper-1 --- .../Magento_Customer/web/css/source/_module.less | 16 ++++++++++++++++ .../Magento_Customer/web/css/source/_module.less | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less index 3ffaeb82cdc2a..0da95b0789674 100644 --- a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less @@ -388,6 +388,17 @@ position: relative; } } + + .form.search.advanced { + .field.price { + .with-addon { + .input-text { + width: 100%; + flex-basis: auto; + } + } + } + } } // @@ -456,6 +467,11 @@ width: 50%; } + .form.search.advanced { + float: none; + width: 50%; + } + // // My account // --------------------------------------------- diff --git a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less index d7ae6c3b28f4a..555da7f7a2ebe 100755 --- a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less @@ -407,6 +407,11 @@ width: 50%; } + .form.search.advanced { + float: none; + width: 50%; + } + // My account .account.page-layout-2columns-left { .sidebar-main, @@ -589,4 +594,15 @@ position: relative; } } + + .form.search.advanced { + .field.price { + .with-addon { + .input-text { + width: 100%; + flex-basis: auto; + } + } + } + } } From 1f0f7a1b920a1289bffa5c1ffa9a415c264bef61 Mon Sep 17 00:00:00 2001 From: amol2jcommerce <amol@twojay.in> Date: Thu, 7 Mar 2019 13:13:51 +0530 Subject: [PATCH 160/345] changes for advanced-Search-layout-not-proper-1 --- .../blank/Magento_Customer/web/css/source/_module.less | 8 ++------ .../luma/Magento_Customer/web/css/source/_module.less | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less index 0da95b0789674..645c7cbc12d5d 100644 --- a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less @@ -462,13 +462,9 @@ .form.password.reset, .form.send.confirmation, .form.password.forget, - .form.create.account { - min-width: 600px; - width: 50%; - } - + .form.create.account, .form.search.advanced { - float: none; + min-width: 600px; width: 50%; } diff --git a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less index 555da7f7a2ebe..cc90f7542414b 100755 --- a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less @@ -402,13 +402,9 @@ .form.password.reset, .form.send.confirmation, .form.password.forget, - .form.create.account { - min-width: 600px; - width: 50%; - } - + .form.create.account, .form.search.advanced { - float: none; + min-width: 600px; width: 50%; } From 5867a48a5e7ab3ac7e5bad35e98db1b2b4d8b35c Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Thu, 14 Feb 2019 16:21:23 +0200 Subject: [PATCH 161/345] ENGCOM-4238: MFTF test fix. --- .../Customer/Test/Mftf/Data/AddressData.xml | 17 ----------- .../Customer/Test/Mftf/Data/CustomerData.xml | 13 +++++++++ ...rderWithAndWithoutFieldsValidationTest.xml | 29 ++++++++++++------- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml index 8fa058dc2b574..da36cf722325e 100755 --- a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml @@ -51,23 +51,6 @@ <data key="default_shipping">Yes</data> <requiredEntity type="region">RegionTX</requiredEntity> </entity> - <entity name="US_address_TX_Wrong_Validation" type="address"> - <data key="firstname">LoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsum</data> - <data key="lastname">Doe</data> - <data key="company">Magento</data> - <array key="street"> - <item>7700 West Parmer Lane</item> - </array> - <data key="city">Austin</data> - <data key="state">Texas</data> - <data key="country_id">US</data> - <data key="country">United States</data> - <data key="postcode">78729</data> - <data key="telephone">512-345-6789</data> - <data key="default_billing">Yes</data> - <data key="default_shipping">Yes</data> - <requiredEntity type="region">RegionTX</requiredEntity> - </entity> <entity name="US_Address_TX_Default_Billing" type="address"> <data key="firstname">John</data> <data key="lastname">Doe</data> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml index b3c0d8d9e0047..670ca1cef027a 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml @@ -45,6 +45,19 @@ <data key="website_id">0</data> <requiredEntity type="address">US_Address_TX</requiredEntity> </entity> + <entity name="Simple_US_Customer_Incorrect_Name" type="customer"> + <data key="group_id">1</data> + <data key="default_billing">true</data> + <data key="default_shipping">true</data> + <data key="email" unique="prefix">John.Doe@example.com</data> + <data key="firstname">LoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsum</data> + <data key="lastname">Doe</data> + <data key="fullname">John Doe</data> + <data key="password">pwdTest123!</data> + <data key="store_id">0</data> + <data key="website_id">0</data> + <requiredEntity type="address">US_Address_TX</requiredEntity> + </entity> <entity name="Simple_Customer_Without_Address" type="customer"> <data key="group_id">1</data> <data key="email" unique="prefix">John.Doe@example.com</data> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutFieldsValidationTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutFieldsValidationTest.xml index 83c9072721836..d418751c736e1 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutFieldsValidationTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutFieldsValidationTest.xml @@ -49,12 +49,13 @@ <fillField selector="{{AdminOrderFormAccountSection.email}}" userInput="{{Simple_US_Customer.email}}" stepKey="fillCustomerEmail" after="selectCustomerGroup"/> <!--Fill wrong customer address information--> - <actionGroup ref="fillOrderCustomerInformation" stepKey="fillCustomerAddress" after="fillCustomerEmail"> - <argument name="customer" value="Simple_US_Customer"/> - <argument name="address" value="US_address_TX_Wrong_Validation"/> + <actionGroup ref="fillOrderCustomerInformation" stepKey="fillWrongCustomerAddress" after="fillCustomerEmail"> + <argument name="customer" value="Simple_US_Customer_Incorrect_Name"/> + <argument name="address" value="US_Address_TX"/> </actionGroup> <!-- Select shipping --> - <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRateShipping" after="fillCustomerAddress"/> + <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRateShipping" + after="fillWrongCustomerAddress"/> <!--Verify totals on Order page--> <see selector="{{AdminOrderFormTotalSection.total('Subtotal')}}" userInput="${{AdminOrderSimpleProduct.subtotal}}" stepKey="seeOrderSubTotal" after="selectFlatRateShipping"/> @@ -63,17 +64,25 @@ <see selector="{{AdminOrderFormTotalSection.grandTotal}}" userInput="${{AdminOrderSimpleProduct.grandTotal}}" stepKey="seeCorrectGrandTotal" after="scrollToOrderGrandTotal"/> <!--Submit Order and verify information--> - <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="clickSubmitOrder" after="seeCorrectGrandTotal"/> - <see selector="{{AdminOrderFormBillingAddressSection.firstNameError}}" userInput="Please enter less or equal than 255 symbols." stepKey="firstNameError" after="clickSubmitOrder"/> + <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="clickSubmitOrderWrong" + after="seeCorrectGrandTotal"/> + <see selector="{{AdminOrderFormBillingAddressSection.firstNameError}}" + userInput="Please enter less or equal than 255 symbols." stepKey="firstNameError" + after="clickSubmitOrderWrong"/> <!--Fill correct customer address information--> <actionGroup ref="fillOrderCustomerInformation" stepKey="fillCustomerAddress" after="firstNameError"> <argument name="customer" value="Simple_US_Customer"/> - <argument name="address" value="US_address_TX"/> + <argument name="address" value="US_Address_TX"/> </actionGroup> + + <!-- Select shipping --> + <click selector="{{AdminOrderFormPaymentSection.getShippingMethods}}" stepKey="clickShipping" after="fillCustomerAddress"/> + <click selector="{{AdminOrderFormPaymentSection.getShippingMethods}}" stepKey="selectShipping" after="clickShipping"/> + <!--Submit Order and verify information--> - <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="clickSubmitOrder" after="fillCustomerAddress"/> + <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="clickSubmitOrder" after="selectShipping"/> <seeInCurrentUrl url="{{AdminOrderDetailsPage.url}}" stepKey="seeViewOrderPage" after="clickSubmitOrder"/> <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the order." stepKey="seeSuccessMessage" after="seeViewOrderPage"/> - </test> - </tests> \ No newline at end of file + </test> +</tests> From 9395e76f7a268c7c875140f184a9f7b24bdfe19b Mon Sep 17 00:00:00 2001 From: Alex Taranovsky <firster@atwix.com> Date: Thu, 7 Mar 2019 14:11:53 +0200 Subject: [PATCH 162/345] 420 - Test coverage: GetAvailablePaymentMethodsTest for Guest 1. Add Magento/Payment/_files/disable_all_active_payment_methods.php --- .../Guest/GetAvailablePaymentMethodsTest.php | 2 +- .../disable_all_active_payment_methods.php | 37 +++++++++++++++++++ ...le_all_active_payment_methods_rollback.php | 33 +++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods.php create mode 100644 dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods_rollback.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 2f96ddc4e3824..09d5c419b5d68 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 @@ -86,7 +86,7 @@ public function testGetPaymentMethodsFromAnotherCustomerCart() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php - * @magentoApiDataFixture Magento/Payment/_files/disable_all_payment_methods.php + * @magentoApiDataFixture Magento/Payment/_files/disable_all_active_payment_methods.php */ public function testGetPaymentMethodsIfPaymentsAreNotSet() { diff --git a/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods.php b/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods.php new file mode 100644 index 0000000000000..762ea672614a8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Config\Model\Config; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$processConfigData = function (Config $config, array $data) { + foreach ($data as $key => $value) { + $config->setDataByPath($key, $value); + $config->save(); + } +}; + +$objectManager = Bootstrap::getObjectManager(); +$paymentMethodList = $objectManager->get(\Magento\Payment\Api\PaymentMethodListInterface::class); +$rollbackConfigKey = 'test/payment/disabled_payment_methods'; +$configData = []; +$disabledPaymentMethods = []; + +// Get all active Payment Methods +foreach ($paymentMethodList->getActiveList(\Magento\Store\Model\Store::DEFAULT_STORE_ID) as $paymentMethod) { + $configData['payment/' . $paymentMethod->getCode() . '/active'] = 0; + $disabledPaymentMethods[] = $paymentMethod->getCode(); +} +// Remember all manually disabled Payment Methods for rollback +$configData[$rollbackConfigKey] = implode(',', $disabledPaymentMethods); + +/** @var Config $defConfig */ +$defConfig = $objectManager->create(Config::class); +$defConfig->setScope(ScopeConfigInterface::SCOPE_TYPE_DEFAULT); + +$processConfigData($defConfig, $configData); diff --git a/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods_rollback.php b/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods_rollback.php new file mode 100644 index 0000000000000..ee81af139f4b4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods_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\App\Config\ScopeConfigInterface; +use Magento\Framework\App\Config\Storage\WriterInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +$rollbackConfigKey = 'test/payment/disabled_payment_methods'; + +$configWriter = $objectManager->create(WriterInterface::class); +$rollbackConfigValue = $objectManager->get(\Magento\Store\Model\StoreManagerInterface::class) + ->getStore(\Magento\Store\Model\Store::DEFAULT_STORE_ID) + ->getConfig($rollbackConfigKey); + +$disabledPaymentMethods = []; +if (!empty($rollbackConfigValue)) { + $disabledPaymentMethods = explode(',', $rollbackConfigValue); +} + +if (count($disabledPaymentMethods)) { + foreach ($disabledPaymentMethods as $keyToRemove) { + $configWriter->delete(sprintf('payment/%s/active', $keyToRemove)); + } +} +$configWriter->delete($rollbackConfigKey); + +$scopeConfig = $objectManager->get(ScopeConfigInterface::class); +$scopeConfig->clean(); From 44e24e37e1678f072de4e075f47a21f0f1b93b46 Mon Sep 17 00:00:00 2001 From: Alex Taranovsky <firster@atwix.com> Date: Thu, 7 Mar 2019 15:30:17 +0200 Subject: [PATCH 163/345] 419 - Test coverage: GetAvailablePaymentMethodsTest for Customer 1. Add - testGetPaymentMethodsFromCustomerCart - testGetPaymentMethodsFromAnotherCustomerCart - testGetPaymentMethodsOfNonExistentCart --- .../GetAvailablePaymentMethodsTest.php | 65 +++++++++++++++---- 1 file changed, 54 insertions(+), 11 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 5695aab6854d4..4cc72a8089b94 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 @@ -54,11 +54,59 @@ protected function setUp() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php */ - public function testGetCartWithPaymentMethods() + public function testGetPaymentMethodsFromCustomerCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_item_with_items'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId('test_order_item_with_items'); + $query = $this->getCartAvailablePaymentMethodsQuery($maskedQuoteId); + $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); - $query = <<<QUERY + self::assertArrayHasKey('cart', $response); + 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!' + ); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/three_customers.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php + */ + public function testGetPaymentMethodsFromAnotherCustomerCart() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId('test_order_item_with_items'); + $query = $this->getCartAvailablePaymentMethodsQuery($maskedQuoteId); + + $this->expectExceptionMessage( + "The current user cannot perform operations on cart \"$maskedQuoteId\"" + ); + $this->graphQlQuery($query, [], '', $this->getHeaderMap('customer3@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 testGetPaymentMethodsOfNonExistentCart() + { + $maskedQuoteId = 'non_existent_masked_id'; + $query = $this->getCartAvailablePaymentMethodsQuery($maskedQuoteId); + + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + } + + /** + * @param string $maskedQuoteId + * @return string + */ + private function getCartAvailablePaymentMethodsQuery( + string $maskedQuoteId + ) : string { + return <<<QUERY { cart(cart_id: "$maskedQuoteId") { available_payment_methods { @@ -68,11 +116,6 @@ public function testGetCartWithPaymentMethods() } } QUERY; - $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); - - self::assertArrayHasKey('cart', $response); - self::assertEquals('checkmo', $response['cart']['available_payment_methods'][0]['code']); - self::assertEquals('Check / Money order', $response['cart']['available_payment_methods'][0]['title']); } /** @@ -88,13 +131,13 @@ private function getHeaderMap(string $username = 'customer@example.com', string } /** - * @param string $reversedQuoteId + * @param string $reservedOrderId * @return string */ - private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string + private function getMaskedQuoteIdByReservedOrderId(string $reservedOrderId): string { $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + $this->quoteResource->load($quote, $reservedOrderId, 'reserved_order_id'); return $this->quoteIdToMaskedId->execute((int)$quote->getId()); } From 8ab0ab3585c72037e06fbf6ca1c6cb4161e59a04 Mon Sep 17 00:00:00 2001 From: Alex Taranovsky <firster@atwix.com> Date: Thu, 7 Mar 2019 15:33:51 +0200 Subject: [PATCH 164/345] 420 - Test coverage: GetAvailablePaymentMethodsTest for Guest 1. Fix typo errors --- .../Quote/Guest/GetAvailablePaymentMethodsTest.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 09d5c419b5d68..ff2e79b61a3ab 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 @@ -49,7 +49,7 @@ protected function setUp() */ public function testGetPaymentMethodsFromGuestCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId('test_order_with_simple_product_without_address'); $query = $this->getCartAvailablePaymentMethodsQuery($maskedQuoteId); $response = $this->graphQlQuery($query); @@ -75,7 +75,7 @@ public function testGetPaymentMethodsFromGuestCart() */ public function testGetPaymentMethodsFromAnotherCustomerCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId('test_order_1'); $query = $this->getCartAvailablePaymentMethodsQuery($maskedQuoteId); $this->expectExceptionMessage( @@ -90,7 +90,7 @@ public function testGetPaymentMethodsFromAnotherCustomerCart() */ public function testGetPaymentMethodsIfPaymentsAreNotSet() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId('test_order_with_simple_product_without_address'); $query = $this->getCartAvailablePaymentMethodsQuery($maskedQuoteId); $response = $this->graphQlQuery($query); @@ -128,13 +128,13 @@ private function getCartAvailablePaymentMethodsQuery( } /** - * @param string $reversedQuoteId + * @param string $reservedOrderId * @return string */ - private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string + private function getMaskedQuoteIdByReservedOrderId(string $reservedOrderId): string { $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + $this->quoteResource->load($quote, $reservedOrderId, 'reserved_order_id'); return $this->quoteIdToMaskedId->execute((int)$quote->getId()); } From 8786fbfa171adb26931d90e8f3d3790a5ddcb63c Mon Sep 17 00:00:00 2001 From: Alex Taranovsky <firster@atwix.com> Date: Thu, 7 Mar 2019 15:55:27 +0200 Subject: [PATCH 165/345] 419 - Test coverage: GetAvailablePaymentMethodsTest for Customer 1. Add testGetPaymentMethodsIfPaymentsAreNotSet and appropriate fixtures --- .../GetAvailablePaymentMethodsTest.php | 12 ++++++ .../disable_all_active_payment_methods.php | 37 +++++++++++++++++++ ...le_all_active_payment_methods_rollback.php | 33 +++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods.php create mode 100644 dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods_rollback.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 4cc72a8089b94..e5027119cbc6c 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 @@ -85,6 +85,18 @@ public function testGetPaymentMethodsFromAnotherCustomerCart() $this->graphQlQuery($query, [], '', $this->getHeaderMap('customer3@search.example.com')); } + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php + * @magentoApiDataFixture Magento/Payment/_files/disable_all_active_payment_methods.php + */ + public function testGetPaymentMethodsIfPaymentsAreNotSet() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId('test_order_item_with_items'); + $query = $this->getCartAvailablePaymentMethodsQuery($maskedQuoteId); + $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + + self::assertEquals(0, count($response['cart']['available_payment_methods'])); + } /** * @magentoApiDataFixture Magento/Customer/_files/customer.php diff --git a/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods.php b/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods.php new file mode 100644 index 0000000000000..762ea672614a8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Config\Model\Config; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$processConfigData = function (Config $config, array $data) { + foreach ($data as $key => $value) { + $config->setDataByPath($key, $value); + $config->save(); + } +}; + +$objectManager = Bootstrap::getObjectManager(); +$paymentMethodList = $objectManager->get(\Magento\Payment\Api\PaymentMethodListInterface::class); +$rollbackConfigKey = 'test/payment/disabled_payment_methods'; +$configData = []; +$disabledPaymentMethods = []; + +// Get all active Payment Methods +foreach ($paymentMethodList->getActiveList(\Magento\Store\Model\Store::DEFAULT_STORE_ID) as $paymentMethod) { + $configData['payment/' . $paymentMethod->getCode() . '/active'] = 0; + $disabledPaymentMethods[] = $paymentMethod->getCode(); +} +// Remember all manually disabled Payment Methods for rollback +$configData[$rollbackConfigKey] = implode(',', $disabledPaymentMethods); + +/** @var Config $defConfig */ +$defConfig = $objectManager->create(Config::class); +$defConfig->setScope(ScopeConfigInterface::SCOPE_TYPE_DEFAULT); + +$processConfigData($defConfig, $configData); diff --git a/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods_rollback.php b/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods_rollback.php new file mode 100644 index 0000000000000..ee81af139f4b4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods_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\App\Config\ScopeConfigInterface; +use Magento\Framework\App\Config\Storage\WriterInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +$rollbackConfigKey = 'test/payment/disabled_payment_methods'; + +$configWriter = $objectManager->create(WriterInterface::class); +$rollbackConfigValue = $objectManager->get(\Magento\Store\Model\StoreManagerInterface::class) + ->getStore(\Magento\Store\Model\Store::DEFAULT_STORE_ID) + ->getConfig($rollbackConfigKey); + +$disabledPaymentMethods = []; +if (!empty($rollbackConfigValue)) { + $disabledPaymentMethods = explode(',', $rollbackConfigValue); +} + +if (count($disabledPaymentMethods)) { + foreach ($disabledPaymentMethods as $keyToRemove) { + $configWriter->delete(sprintf('payment/%s/active', $keyToRemove)); + } +} +$configWriter->delete($rollbackConfigKey); + +$scopeConfig = $objectManager->get(ScopeConfigInterface::class); +$scopeConfig->clean(); From 40f547bd13f4c6e93c934f1bb51c1b208aa8c608 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Thu, 7 Mar 2019 16:34:22 +0100 Subject: [PATCH 166/345] Test coverage optimisation --- .../SetOfflineShippingMethodsOnCartTest.php | 88 +++++++------------ 1 file changed, 33 insertions(+), 55 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php index e65ac0275206b..8a6476329177d 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php @@ -8,7 +8,7 @@ namespace Magento\GraphQl\OfflineShipping; use Magento\Integration\Api\CustomerTokenServiceInterface; -use Magento\Quote\Model\Quote; +use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; use Magento\TestFramework\Helper\Bootstrap; @@ -19,6 +19,11 @@ */ class SetOfflineShippingOnCartTest extends GraphQlAbstract { + /** + * @var QuoteFactory + */ + private $quoteFactory; + /** * @var CustomerTokenServiceInterface */ @@ -29,11 +34,6 @@ class SetOfflineShippingOnCartTest extends GraphQlAbstract */ private $quoteResource; - /** - * @var Quote - */ - private $quote; - /** * @var QuoteIdToMaskedQuoteIdInterface */ @@ -45,67 +45,44 @@ class SetOfflineShippingOnCartTest extends GraphQlAbstract protected function setUp() { $objectManager = Bootstrap::getObjectManager(); - $this->quoteResource = $objectManager->create(QuoteResource::class); - $this->quote = $objectManager->create(Quote::class); - $this->quoteIdToMaskedId = $objectManager->create(QuoteIdToMaskedQuoteIdInterface::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_address_saved.php - * @magentoApiDataFixture Magento/Checkout/_files/enable_all_shipping_methods.php - */ - public function testSetFlatrateOnCart() - { - $this->setShippingMethodAndCheckResponse( - 'flatrate', - 'flatrate', - '10', - 'Flat Rate - Fixed' - ); - } - /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php * @magentoApiDataFixture Magento/OfflineShipping/_files/tablerates_weight.php * @magentoApiDataFixture Magento/Checkout/_files/enable_all_shipping_methods.php + * @dataProvider offlineShippingMethodDataProvider() + * @param string $carrier + * @param string $method + * @param float $amount + * @param string $label */ - public function testSetTableRatesOnCart() + public function testSetOfflineShippingMethod(string $carrier, string $method, float $amount, string $label) { $this->setShippingMethodAndCheckResponse( - 'tablerate', - 'bestway', - '10', - 'Best Way - Table Rate' + $carrier, + $method, + $amount, + $label ); } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - * @magentoApiDataFixture Magento/Checkout/_files/enable_all_shipping_methods.php - */ - public function testSetFreeShippingOnCart() - { - $this->setShippingMethodAndCheckResponse( - 'freeshipping', - 'freeshipping', - '0', - 'Free Shipping - Free' - ); - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - * @magentoApiDataFixture Magento/Checkout/_files/enable_all_shipping_methods.php + * Data provider for base offline shipping methods + * + * @return array */ - public function testSetUpsOnCart() + public function offlineShippingMethodDataProvider() { - $this->setShippingMethodAndCheckResponse( - 'ups', - 'GND', - '15.61', - 'United Parcel Service - Ground' - ); + return [ + ['flatrate', 'flatrate', 10, 'Flat Rate - Fixed'], + ['tablerate', 'bestway', 10, 'Best Way - Table Rate'], + ['freeshipping', 'freeshipping', 0, 'Free Shipping - Free'] + ]; } /** @@ -121,17 +98,18 @@ public function testSetUpsOnCart() private function setShippingMethodAndCheckResponse( string $shippingCarrierCode, string $shippingMethodCode, - string $shippingAmount, + float $shippingAmount, string $shippingLabel ) { + $quote = $this->quoteFactory->create(); $this->quoteResource->load( - $this->quote, + $quote, 'test_order_1', 'reserved_order_id' ); - $shippingAddress = $this->quote->getShippingAddress(); + $shippingAddress = $quote->getShippingAddress(); $shippingAddressId = $shippingAddress->getId(); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$quote->getId()); $query = $this->getQuery( $maskedQuoteId, 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 167/345] 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 709f74ede09aa..4b4633b374e60 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 68dfc44aa886a..575bb56912b25 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 11c681ee7bb78d814bc4ec10ba649dc7d7320f22 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Thu, 7 Mar 2019 13:17:23 -0600 Subject: [PATCH 168/345] MC-15292: Disabled attribute for the table does not work with dependent tables --- .../without_setup_version/module.xml | 10 +++++ .../db_schema.xml | 11 ++++++ .../without_setup_version/module.xml | 10 +++++ .../Setup/DeclarativeInstallerTest.php | 39 +++++++++++++++++++ .../Schema/Declaration/SchemaBuilder.php | 7 ++++ 5 files changed, 77 insertions(+) create mode 100644 dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule1/revisions/without_setup_version/module.xml create mode 100644 dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule3/revisions/drop_table_with_external_dependency/db_schema.xml create mode 100644 dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule3/revisions/without_setup_version/module.xml diff --git a/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule1/revisions/without_setup_version/module.xml b/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule1/revisions/without_setup_version/module.xml new file mode 100644 index 0000000000000..ed831eb6e9354 --- /dev/null +++ b/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule1/revisions/without_setup_version/module.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:Module/etc/module.xsd"> + <module name="Magento_TestSetupDeclarationModule1"/> +</config> diff --git a/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule3/revisions/drop_table_with_external_dependency/db_schema.xml b/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule3/revisions/drop_table_with_external_dependency/db_schema.xml new file mode 100644 index 0000000000000..c22c41b7a5c03 --- /dev/null +++ b/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule3/revisions/drop_table_with_external_dependency/db_schema.xml @@ -0,0 +1,11 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd"> + <table name="reference_table" disabled="true"/> +</schema> diff --git a/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule3/revisions/without_setup_version/module.xml b/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule3/revisions/without_setup_version/module.xml new file mode 100644 index 0000000000000..27e21dc3fe898 --- /dev/null +++ b/dev/tests/setup-integration/_files/Magento/TestSetupDeclarationModule3/revisions/without_setup_version/module.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:Module/etc/module.xsd"> + <module name="Magento_TestSetupDeclarationModule3"/> +</config> diff --git a/dev/tests/setup-integration/testsuite/Magento/Setup/DeclarativeInstallerTest.php b/dev/tests/setup-integration/testsuite/Magento/Setup/DeclarativeInstallerTest.php index 6097348d4fabc..e8698965de007 100644 --- a/dev/tests/setup-integration/testsuite/Magento/Setup/DeclarativeInstallerTest.php +++ b/dev/tests/setup-integration/testsuite/Magento/Setup/DeclarativeInstallerTest.php @@ -249,6 +249,45 @@ public function testInstallationWithDroppingTables() self::assertEquals($this->getData(), $shardData); } + /** + * @moduleName Magento_TestSetupDeclarationModule1 + * @moduleName Magento_TestSetupDeclarationModule3 + */ + public function testInstallationWithDroppingTablesFromSecondaryModule() + { + $modules = [ + 'Magento_TestSetupDeclarationModule1', + 'Magento_TestSetupDeclarationModule3', + ]; + + $this->moduleManager->updateRevision( + 'Magento_TestSetupDeclarationModule3', + 'drop_table_with_external_dependency', + 'db_schema.xml', + 'etc' + ); + + foreach ($modules as $moduleName) { + $this->moduleManager->updateRevision( + $moduleName, + 'without_setup_version', + 'module.xml', + 'etc' + ); + } + + try { + $this->cliCommand->install($modules); + } catch (\Exception $e) { + $installException = $e->getPrevious(); + self::assertSame(1, $installException->getCode()); + self::assertContains( + 'The reference table named "reference_table" is disabled', + $installException->getMessage() + ); + } + } + /** * @moduleName Magento_TestSetupDeclarationModule1 * @dataProviderFromFile Magento/TestSetupDeclarationModule1/fixture/declarative_installer/rollback.php diff --git a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Declaration/SchemaBuilder.php b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Declaration/SchemaBuilder.php index 34a99f26a4ef1..4c65d8a70bed5 100644 --- a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Declaration/SchemaBuilder.php +++ b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Declaration/SchemaBuilder.php @@ -350,6 +350,13 @@ private function processConstraints(array $tableData, string $resource, Schema $ if ($constraintData['type'] === 'foreign') { $constraintData['column'] = $this->getColumnByName($constraintData['column'], $table); $referenceTableData = $this->tablesData[$constraintData['referenceTable']]; + + if ($this->isDisabled($referenceTableData)) { + throw new \LogicException( + sprintf('The reference table named "%s" is disabled', $referenceTableData['name']) + ); + } + //If we are referenced to the same table we need to specify it //Get table name from resource connection regarding prefix settings $refTableName = $this->resourceConnection->getTableName($referenceTableData['name']); From 068e76a2dc964efe3019cdd699774a0154ad3215 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Thu, 7 Mar 2019 13:53:58 -0600 Subject: [PATCH 169/345] MC-15293: Bundle product with special price not getting added to cart - reverted change of ENGCOM-3450 with commits: 8f463db4808afc9f8f4908a4dee65f3abe981eba 63c9ea46673e58f2712da5a768d0840ece184152 77492c1c08695753b3a274b02552fab7be9c64c0 d0ba9973dffac27560364906b22786d70410d69d 0a12f2facafd2eaea06a208173cf1e564e107a19 --- .../Model/Product/Attribute/Backend/Tierprice.php | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php index e346c912dccaa..db967052cb7a5 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php @@ -165,19 +165,6 @@ protected function modifyPriceData($object, $data) /** @var \Magento\Catalog\Model\Product $object */ $data = parent::modifyPriceData($object, $data); $price = $object->getPrice(); - - $specialPrice = $object->getSpecialPrice(); - $specialPriceFromDate = $object->getSpecialFromDate(); - $specialPriceToDate = $object->getSpecialToDate(); - $today = time(); - - if ($specialPrice && ($object->getPrice() > $object->getFinalPrice())) { - if ($today >= strtotime($specialPriceFromDate) && $today <= strtotime($specialPriceToDate) || - $today >= strtotime($specialPriceFromDate) && $specialPriceToDate === null) { - $price = $specialPrice; - } - } - foreach ($data as $key => $tierPrice) { $percentageValue = $this->getPercentage($tierPrice); if ($percentageValue) { From dc91429398946e3977039ce7ce732797dfd3e88b Mon Sep 17 00:00:00 2001 From: Oleksandr Miroshnichenko <omiroshnichenko@magento.com> Date: Thu, 7 Mar 2019 14:57:21 -0600 Subject: [PATCH 170/345] MC-15305: Unable to do a product search via graphql with elastic search --- .../FieldMapper/Product/FieldProvider/DynamicField.php | 4 +++- app/code/Magento/Elasticsearch6/etc/di.xml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) 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 c7e2a4beabb5c..e30ecce247a2c 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 @@ -123,10 +123,12 @@ public function getFields(array $context = []): array $groups = $this->groupRepository->getList($searchCriteria)->getItems(); $priceAttribute = $this->attributeAdapterProvider->getByAttributeCode('price'); + $ctx = isset($context['websiteId']) ? ['websiteId' => $context['websiteId']] : []; foreach ($groups as $group) { + $ctx['customerGroupId'] = $group->getId(); $groupPriceKey = $this->fieldNameResolver->getFieldName( $priceAttribute, - ['customerGroupId' => $group->getId(), 'websiteId' => $context['websiteId']] + $ctx ); $allAttributes[$groupPriceKey] = [ 'type' => $this->fieldTypeConverter->convert(FieldTypeConverterInterface::INTERNAL_DATA_TYPE_FLOAT), diff --git a/app/code/Magento/Elasticsearch6/etc/di.xml b/app/code/Magento/Elasticsearch6/etc/di.xml index 25eff42fd3442..df71f3c3158d3 100644 --- a/app/code/Magento/Elasticsearch6/etc/di.xml +++ b/app/code/Magento/Elasticsearch6/etc/di.xml @@ -158,7 +158,7 @@ <type name="Magento\Search\Model\Search\PageSizeProvider"> <arguments> <argument name="pageSizeBySearchEngine" xsi:type="array"> - <item name="elasticsearch6" xsi:type="number">2147483647</item> + <item name="elasticsearch6" xsi:type="number">10000</item> </argument> </arguments> </type> 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 171/345] 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 36958a37c6d1a..e565f609611c2 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 bc364ac0a46aa7e8182e71447398d3e5fcf68bdf Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Thu, 7 Mar 2019 19:50:34 -0600 Subject: [PATCH 172/345] Elasticsearch6 integration tests adjustment --- .../Magento/Elasticsearch/Model/Config.php | 16 ++++-- app/code/Magento/Elasticsearch/etc/di.xml | 8 +++ .../Magento/Elasticsearch6/Model/Config.php | 56 ------------------- .../Model/DataProvider/Suggestions.php | 2 +- .../Model/DataProvider/SuggestionsTest.php | 2 +- app/code/Magento/Elasticsearch6/etc/di.xml | 8 +++ .../SearchAdapter/AdapterTest.php | 4 +- .../Model/Client/ElasticsearchTest.php | 10 ++-- .../Model/Indexer/IndexHandlerTest.php | 12 ++-- .../Model/Indexer/ReindexAllTest.php | 4 +- .../SearchAdapter/AdapterTest.php | 48 ++++++++-------- 11 files changed, 67 insertions(+), 103 deletions(-) delete mode 100644 app/code/Magento/Elasticsearch6/Model/Config.php diff --git a/app/code/Magento/Elasticsearch/Model/Config.php b/app/code/Magento/Elasticsearch/Model/Config.php index dc08a72a9feb3..387db07c62f90 100644 --- a/app/code/Magento/Elasticsearch/Model/Config.php +++ b/app/code/Magento/Elasticsearch/Model/Config.php @@ -25,8 +25,6 @@ class Config implements ClientOptionsInterface */ const ENGINE_NAME = 'elasticsearch'; - private const ENGINE_NAME_5 = 'elasticsearch5'; - /** * Elasticsearch Entity type */ @@ -64,23 +62,31 @@ class Config implements ClientOptionsInterface private $engineResolver; /** - * Constructor + * Available Elasticsearch engines. * + * @var array + */ + private $engineList; + + /** * @param ScopeConfigInterface $scopeConfig * @param ClientResolver|null $clientResolver * @param EngineResolverInterface|null $engineResolver * @param string|null $prefix + * @param array $engineList */ public function __construct( ScopeConfigInterface $scopeConfig, ClientResolver $clientResolver = null, EngineResolverInterface $engineResolver = null, - $prefix = null + $prefix = null, + $engineList = [] ) { $this->scopeConfig = $scopeConfig; $this->clientResolver = $clientResolver ?: ObjectManager::getInstance()->get(ClientResolver::class); $this->engineResolver = $engineResolver ?: ObjectManager::getInstance()->get(EngineResolverInterface::class); $this->prefix = $prefix ?: $this->clientResolver->getCurrentEngine(); + $this->engineList = $engineList; } /** @@ -138,7 +144,7 @@ public function getSearchConfigData($field, $storeId = null) */ public function isElasticsearchEnabled() { - return in_array($this->engineResolver->getCurrentSearchEngine(), [self::ENGINE_NAME, self::ENGINE_NAME_5]); + return in_array($this->engineResolver->getCurrentSearchEngine(), $this->engineList); } /** diff --git a/app/code/Magento/Elasticsearch/etc/di.xml b/app/code/Magento/Elasticsearch/etc/di.xml index 7e219bb2f918f..23a1e76a30cfe 100644 --- a/app/code/Magento/Elasticsearch/etc/di.xml +++ b/app/code/Magento/Elasticsearch/etc/di.xml @@ -13,6 +13,14 @@ <preference for="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldIndex\ConverterInterface" type="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldIndex\Converter" /> <preference for="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ConverterInterface" type="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\Converter" /> <preference for="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProviderInterface" type="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\CompositeFieldProvider" /> + <type name="Magento\Elasticsearch\Model\Config"> + <arguments> + <argument name="engineList" xsi:type="array"> + <item name="elasticsearch" xsi:type="string">elasticsearch</item> + <item name="elasticsearch5" xsi:type="string">elasticsearch5</item> + </argument> + </arguments> + </type> <type name="Magento\Elasticsearch\Model\Adapter\FieldMapper\FieldMapperResolver"> <arguments> <argument name="fieldMappers" xsi:type="array"> diff --git a/app/code/Magento/Elasticsearch6/Model/Config.php b/app/code/Magento/Elasticsearch6/Model/Config.php deleted file mode 100644 index 1a989e2705fdd..0000000000000 --- a/app/code/Magento/Elasticsearch6/Model/Config.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Elasticsearch6\Model; - -use Magento\Framework\Search\EngineResolverInterface; -use Magento\Framework\App\ObjectManager; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\AdvancedSearch\Model\Client\ClientResolver; - -/** - * Elasticsearch6 config model - */ -class Config extends \Magento\Elasticsearch\Model\Config -{ - /** - * Search engine name - */ - private const ENGINE_NAME_6 = 'elasticsearch6'; - - /** - * @var EngineResolverInterface - */ - private $engineResolver; - - /** - * Constructor - * - * @param ScopeConfigInterface $scopeConfig - * @param ClientResolver|null $clientResolver - * @param EngineResolverInterface|null $engineResolver - * @param string|null $prefix - */ - public function __construct( - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\AdvancedSearch\Model\Client\ClientResolver $clientResolver, - \Magento\Framework\Search\EngineResolverInterface $engineResolver, - $prefix = null - ) { - parent::__construct($scopeConfig, $clientResolver, $engineResolver, $prefix); - $this->engineResolver = $engineResolver; - } - - /** - * Return true if third party search engine is used - * - * @return bool - */ - public function isElasticsearchEnabled() - { - return in_array($this->engineResolver->getCurrentSearchEngine(), [self::ENGINE_NAME_6]); - } -} diff --git a/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php b/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php index 77e1270f54fc2..d05471734bb8f 100644 --- a/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php +++ b/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php @@ -9,7 +9,7 @@ use Magento\Store\Model\ScopeInterface; use Magento\Search\Model\QueryInterface; use Magento\AdvancedSearch\Model\SuggestedQueriesInterface; -use Magento\Elasticsearch6\Model\Config; +use Magento\Elasticsearch\Model\Config; use Magento\Elasticsearch\SearchAdapter\ConnectionManager; use Magento\Search\Model\QueryResultFactory; use Magento\Framework\App\Config\ScopeConfigInterface; diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php b/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php index 957edc559fdcb..b3c60b70ffa8e 100644 --- a/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php +++ b/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php @@ -67,7 +67,7 @@ class SuggestionsTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { - $this->config = $this->getMockBuilder(\Magento\Elasticsearch6\Model\Config::class) + $this->config = $this->getMockBuilder(\Magento\Elasticsearch\Model\Config::class) ->disableOriginalConstructor() ->setMethods(['isElasticsearchEnabled']) ->getMock(); diff --git a/app/code/Magento/Elasticsearch6/etc/di.xml b/app/code/Magento/Elasticsearch6/etc/di.xml index 25eff42fd3442..f9ee035972a35 100644 --- a/app/code/Magento/Elasticsearch6/etc/di.xml +++ b/app/code/Magento/Elasticsearch6/etc/di.xml @@ -6,6 +6,14 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\Elasticsearch\Model\Config"> + <arguments> + <argument name="engineList" xsi:type="array"> + <item name="elasticsearch6" xsi:type="string">elasticsearch6</item> + </argument> + </arguments> + </type> + <type name="Magento\Search\Model\Adminhtml\System\Config\Source\Engine"> <arguments> <argument name="engines" xsi:type="array"> diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/AdapterTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/AdapterTest.php index 978815f665341..a52c5bb9e21b7 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/AdapterTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/AdapterTest.php @@ -43,7 +43,7 @@ protected function setUp() $contentManager = $this->getMockBuilder(\Magento\Elasticsearch\SearchAdapter\ConnectionManager::class) ->disableOriginalConstructor() ->getMock(); - $this->clientMock = $this->getMockBuilder(\Magento\Elasticsearch\Model\Client\Elasticsearch::class) + $this->clientMock = $this->getMockBuilder(\Magento\Elasticsearch6\Model\Client\Elasticsearch::class) ->disableOriginalConstructor() ->getMock(); $contentManager @@ -78,7 +78,7 @@ protected function setUp() /** * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest * @return void */ diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Client/ElasticsearchTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Client/ElasticsearchTest.php index 61add5f7d0ea7..3eea2497daa1f 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Client/ElasticsearchTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Client/ElasticsearchTest.php @@ -10,7 +10,7 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\Store\Model\StoreManagerInterface; use Magento\Elasticsearch\SearchAdapter\ConnectionManager; -use Magento\Elasticsearch\Model\Client\Elasticsearch as ElasticsearchClient; +use Magento\Elasticsearch6\Model\Client\Elasticsearch as ElasticsearchClient; use Magento\Elasticsearch\Model\Config; use Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver; @@ -95,7 +95,7 @@ private function search($text) } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix composite_product_search */ public function testSearchConfigurableProductBySimpleProductName() @@ -104,7 +104,7 @@ public function testSearchConfigurableProductBySimpleProductName() } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix composite_product_search */ public function testSearchConfigurableProductBySimpleProductAttributeMultiselect() @@ -113,7 +113,7 @@ public function testSearchConfigurableProductBySimpleProductAttributeMultiselect } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix composite_product_search */ public function testSearchConfigurableProductBySimpleProductAttributeSelect() @@ -122,7 +122,7 @@ public function testSearchConfigurableProductBySimpleProductAttributeSelect() } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix composite_product_search */ public function testSearchConfigurableProductBySimpleProductAttributeShortDescription() diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php index 014aaf7679bc9..77533e83b719c 100755 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php @@ -13,7 +13,7 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\Store\Model\StoreManagerInterface; use Magento\Elasticsearch\SearchAdapter\ConnectionManager; -use Magento\Elasticsearch\Model\Client\Elasticsearch as ElasticsearchClient; +use Magento\Elasticsearch6\Model\Client\Elasticsearch as ElasticsearchClient; use Magento\Elasticsearch\Model\Config; use Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver; use Magento\Indexer\Model\Indexer; @@ -87,7 +87,7 @@ protected function setUp() } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest * @return void */ @@ -106,7 +106,7 @@ public function testReindexAll(): void /** * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest * @return void */ @@ -131,7 +131,7 @@ public function testReindexRowAfterEdit(): void } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest * @return void */ @@ -170,7 +170,7 @@ public function testReindexRowAfterMassAction(): void } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest * @magentoAppArea adminhtml * @return void @@ -192,7 +192,7 @@ public function testReindexRowAfterDelete(): void /** * @magentoDbIsolation enabled * @magentoAppArea adminhtml - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest * @magentoDataFixture Magento/Elasticsearch/_files/configurable_products.php * @return void diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/ReindexAllTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/ReindexAllTest.php index d40ce9e8a0706..7d4aa8e005e4e 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/ReindexAllTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/ReindexAllTest.php @@ -68,7 +68,7 @@ protected function setUp() /** * Test search of all products after full reindex * - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest_configurable * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_products.php */ @@ -82,7 +82,7 @@ public function testSearchAll() /** * Test search of specific product after full reindex * - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest_configurable * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_products.php */ diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php index dc288a18fadb7..6bb7d6ac568fc 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php @@ -5,8 +5,6 @@ */ namespace Magento\Elasticsearch\SearchAdapter; -use Magento\Elasticsearch\Model\Config; - /** * Class AdapterTest * @@ -26,7 +24,7 @@ class AdapterTest extends \Magento\Framework\Search\Adapter\Mysql\AdapterTest /** * @var string */ - protected $searchEngine = Config::ENGINE_NAME; + protected $searchEngine = 'elasticsearch6'; /** * Get request config path @@ -43,12 +41,12 @@ protected function getRequestConfigPath() */ protected function createAdapter() { - return $this->objectManager->create(\Magento\Elasticsearch\SearchAdapter\Adapter::class); + return $this->objectManager->create(\Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Adapter::class); } /** * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testMatchQuery() @@ -58,7 +56,7 @@ public function testMatchQuery() /** * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testMatchOrderedQuery() @@ -70,7 +68,7 @@ public function testMatchOrderedQuery() /** * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testAggregationsQuery() @@ -80,7 +78,7 @@ public function testAggregationsQuery() /** * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testMatchQueryFilters() @@ -92,7 +90,7 @@ public function testMatchQueryFilters() * Range filter test with all fields filled * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testRangeFilterWithAllFields() @@ -104,7 +102,7 @@ public function testRangeFilterWithAllFields() * Range filter test with all fields filled * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testRangeFilterWithoutFromField() @@ -116,7 +114,7 @@ public function testRangeFilterWithoutFromField() * Range filter test with all fields filled * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testRangeFilterWithoutToField() @@ -128,7 +126,7 @@ public function testRangeFilterWithoutToField() * Term filter test * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testTermFilter() @@ -140,7 +138,7 @@ public function testTermFilter() * Term filter test * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testTermFilterArray() @@ -152,7 +150,7 @@ public function testTermFilterArray() * Term filter test * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testWildcardFilter() @@ -164,7 +162,7 @@ public function testWildcardFilter() * Request limits test * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testSearchLimit() @@ -176,7 +174,7 @@ public function testSearchLimit() * Bool filter test * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testBoolFilter() @@ -188,7 +186,7 @@ public function testBoolFilter() * Test bool filter with nested negative bool filter * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testBoolFilterWithNestedNegativeBoolFilter() @@ -200,7 +198,7 @@ public function testBoolFilterWithNestedNegativeBoolFilter() * Test range inside nested negative bool filter * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testBoolFilterWithNestedRangeInNegativeBoolFilter() @@ -213,7 +211,7 @@ public function testBoolFilterWithNestedRangeInNegativeBoolFilter() * * @dataProvider elasticSearchAdvancedSearchDataProvider * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest * @param string $nameQuery * @param string $descriptionQuery @@ -259,7 +257,7 @@ public function elasticSearchAdvancedSearchDataProvider() /** * @magentoAppIsolation enabled * @magentoDataFixture Magento/Framework/Search/_files/filterable_attribute.php - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testCustomFilterableAttribute() @@ -274,7 +272,7 @@ public function testCustomFilterableAttribute() * * @magentoAppIsolation enabled * @magentoDataFixture Magento/Framework/Search/_files/filterable_attributes.php - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest * @dataProvider filterByAttributeValuesDataProvider * @param string $requestName @@ -294,7 +292,7 @@ public function testFilterByAttributeValues($requestName, $additionalData) * @param $rangeFilter * @param $expectedRecordsCount * @magentoDataFixture Magento/Framework/Search/_files/date_attribute.php - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest * @magentoAppIsolation enabled * @dataProvider dateDataProvider @@ -309,7 +307,7 @@ public function testAdvancedSearchDateField($rangeFilter, $expectedRecordsCount) /** * @magentoDataFixture Magento/Framework/Search/_files/product_configurable.php * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testAdvancedSearchCompositeProductWithOutOfStockOption() @@ -320,7 +318,7 @@ public function testAdvancedSearchCompositeProductWithOutOfStockOption() /** * @magentoDataFixture Magento/Framework/Search/_files/product_configurable_with_disabled_child.php * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testAdvancedSearchCompositeProductWithDisabledChild() @@ -333,7 +331,7 @@ public function testAdvancedSearchCompositeProductWithDisabledChild() /** * @magentoDataFixture Magento/Framework/Search/_files/search_weight_products.php * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testSearchQueryBoost() From a21901aac61365d1ceb8f58b77246abe3a6a8cd3 Mon Sep 17 00:00:00 2001 From: amol2jcommerce <amol@twojay.in> Date: Fri, 8 Mar 2019 10:58:54 +0530 Subject: [PATCH 173/345] changes for Minicart-search-logo-not-vertically-aligned-1 --- .../luma/Magento_CatalogSearch/web/css/source/_module.less | 2 +- .../luma/Magento_Checkout/web/css/source/module/_minicart.less | 2 +- .../Magento/luma/Magento_Theme/web/css/source/_module.less | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less index f785dd74d900e..5680aab107104 100644 --- a/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less @@ -199,7 +199,7 @@ // Mobile // _____________________________________________ -.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__s) { +.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { .block-search { margin-top: @indent__s; } diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less index 9576004421e48..4fb0f52894971 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less @@ -400,7 +400,7 @@ } } -.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__s) { +.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { .minicart-wrapper { margin-top: @indent__s; } diff --git a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less index bafe1be57ee35..4cabd6ec94d92 100644 --- a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less @@ -422,7 +422,7 @@ } } -.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__s) { +.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { .logo { margin-bottom: 13px; margin-top: 4px; From e4315560018174b9d1cae692d76b4a8dd727a9b3 Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Fri, 8 Mar 2019 08:04:59 +0200 Subject: [PATCH 174/345] mutations add<Product Type>ProductsToCart dosn't check if the cart is active --- .../Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php index 21df2271cc7f3..33c91b03ee375 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php @@ -84,6 +84,13 @@ public function execute(string $cartHash, ?int $customerId): Quote ) ); } - return $cart; + + try { + return $this->cartRepository->getActiveForCustomer($customerId); + } catch (\Exception $e) { + throw new GraphQlNoSuchEntityException( + __('Current customer does not have an active cart.') + ); + } } } From c82cbbdb53064d560082ef65b8296cc60b8cd87b Mon Sep 17 00:00:00 2001 From: Dave Macaulay <macaulay@adobe.com> Date: Fri, 8 Mar 2019 12:14:01 +0100 Subject: [PATCH 175/345] MC-14937: Complete Page Builder Analytics data collection - Add checks on analytics.xml for dependency test --- .../Magento/CatalogAnalytics/composer.json | 3 +- .../Magento/CustomerAnalytics/composer.json | 3 +- app/code/Magento/QuoteAnalytics/composer.json | 3 +- .../Magento/ReviewAnalytics/composer.json | 3 +- app/code/Magento/SalesAnalytics/composer.json | 3 +- .../Magento/WishlistAnalytics/composer.json | 3 +- .../Dependency/AnalyticsConfigRule.php | 43 +++++++++++++++++++ .../Magento/Test/Integrity/DependencyTest.php | 28 ++++++++++++ 8 files changed, 83 insertions(+), 6 deletions(-) create mode 100644 dev/tests/static/framework/Magento/TestFramework/Dependency/AnalyticsConfigRule.php diff --git a/app/code/Magento/CatalogAnalytics/composer.json b/app/code/Magento/CatalogAnalytics/composer.json index 5c97261d483d8..805be8a17765f 100644 --- a/app/code/Magento/CatalogAnalytics/composer.json +++ b/app/code/Magento/CatalogAnalytics/composer.json @@ -4,7 +4,8 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", - "magento/module-catalog": "*" + "magento/module-catalog": "*", + "magento/module-analytics": "*" }, "type": "magento2-module", "license": [ diff --git a/app/code/Magento/CustomerAnalytics/composer.json b/app/code/Magento/CustomerAnalytics/composer.json index 7dec4279ee280..3840c534b1964 100644 --- a/app/code/Magento/CustomerAnalytics/composer.json +++ b/app/code/Magento/CustomerAnalytics/composer.json @@ -4,7 +4,8 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", - "magento/module-customer": "*" + "magento/module-customer": "*", + "magento/module-analytics": "*" }, "type": "magento2-module", "license": [ diff --git a/app/code/Magento/QuoteAnalytics/composer.json b/app/code/Magento/QuoteAnalytics/composer.json index 90dae1ec2adca..706bed674b4a9 100644 --- a/app/code/Magento/QuoteAnalytics/composer.json +++ b/app/code/Magento/QuoteAnalytics/composer.json @@ -4,7 +4,8 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", - "magento/module-quote": "*" + "magento/module-quote": "*", + "magento/module-analytics": "*" }, "type": "magento2-module", "license": [ diff --git a/app/code/Magento/ReviewAnalytics/composer.json b/app/code/Magento/ReviewAnalytics/composer.json index 73f534451580c..a82d4328ca159 100644 --- a/app/code/Magento/ReviewAnalytics/composer.json +++ b/app/code/Magento/ReviewAnalytics/composer.json @@ -4,7 +4,8 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", - "magento/module-review": "*" + "magento/module-review": "*", + "magento/module-analytics": "*" }, "type": "magento2-module", "license": [ diff --git a/app/code/Magento/SalesAnalytics/composer.json b/app/code/Magento/SalesAnalytics/composer.json index 64424c8f5bc61..b77dcd7e71c65 100644 --- a/app/code/Magento/SalesAnalytics/composer.json +++ b/app/code/Magento/SalesAnalytics/composer.json @@ -4,7 +4,8 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", - "magento/module-sales": "*" + "magento/module-sales": "*", + "magento/module-analytics": "*" }, "type": "magento2-module", "license": [ diff --git a/app/code/Magento/WishlistAnalytics/composer.json b/app/code/Magento/WishlistAnalytics/composer.json index fc69afe2907ab..747f2a4baaaa9 100644 --- a/app/code/Magento/WishlistAnalytics/composer.json +++ b/app/code/Magento/WishlistAnalytics/composer.json @@ -4,7 +4,8 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", - "magento/module-wishlist": "*" + "magento/module-wishlist": "*", + "magento/module-analytics": "*" }, "type": "magento2-module", "license": [ diff --git a/dev/tests/static/framework/Magento/TestFramework/Dependency/AnalyticsConfigRule.php b/dev/tests/static/framework/Magento/TestFramework/Dependency/AnalyticsConfigRule.php new file mode 100644 index 0000000000000..b1a6da5e43822 --- /dev/null +++ b/dev/tests/static/framework/Magento/TestFramework/Dependency/AnalyticsConfigRule.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\TestFramework\Dependency; + +/** + * Class provides dependency rule for analytics.xml config file. + */ +class AnalyticsConfigRule implements RuleInterface +{ + /** + * @inheritdoc + */ + public function getDependencyInfo($currentModule, $fileType, $file, &$contents) + { + if ('config' != $fileType || !preg_match('#.*/analytics\.xml$#', $file)) { + return []; + } + + $dependenciesInfo = []; + if (preg_match_all('#<[customProvider|reportProvider][^>]*class=[\'"]([^\'"]+)[\'"]#i', $contents, $matches)) { + $classes = array_pop($matches); + foreach ($classes as $class) { + $classParts = explode('\\', $class); + $module = implode('\\', array_slice($classParts, 0, 2)); + if (strtolower($currentModule) !== strtolower($module)) { + $dependenciesInfo[] = [ + 'module' => $module, + 'type' => RuleInterface::TYPE_HARD, + 'source' => $file, + ]; + } + } + } + + return $dependenciesInfo; + } +} diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php index a4113abed8030..e2e0357a38f77 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php @@ -16,6 +16,7 @@ use Magento\TestFramework\Dependency\LayoutRule; use Magento\TestFramework\Dependency\PhpRule; use Magento\TestFramework\Dependency\ReportsConfigRule; +use Magento\TestFramework\Dependency\AnalyticsConfigRule; use Magento\TestFramework\Dependency\VirtualType\VirtualTypeMapper; /** @@ -78,6 +79,17 @@ class DependencyTest extends \PHPUnit\Framework\TestCase */ protected static $_listRoutesXml = []; + /** + * List of analytics.xml + * + * Format: array( + * '{Module_Name}' => '{Filename}' + * ) + * + * @var array + */ + protected static $_listAnalyticsXml = []; + /** * List of routers * @@ -176,6 +188,7 @@ public static function setUpBeforeClass() self::_prepareListConfigXml(); self::_prepareListDbSchemaXml(); self::_prepareListRoutesXml(); + self::_prepareListAnalyticsXml(); self::_prepareMapRouters(); self::_prepareMapLayoutBlocks(); @@ -240,6 +253,7 @@ protected static function _initRules() ), new DiRule(new VirtualTypeMapper()), new ReportsConfigRule($dbRuleTables), + new AnalyticsConfigRule(), ]; } @@ -571,6 +585,20 @@ protected static function _prepareListRoutesXml() } } + /** + * Prepare list of analytics.xml files + */ + protected static function _prepareListAnalyticsXml() + { + $files = Files::init()->getDbSchemaFiles('analytics.xml', [], false); + foreach ($files as $file) { + if (preg_match('/(?<namespace>[A-Z][a-z]+)[_\/\\\\](?<module>[A-Z][a-zA-Z]+)/', $file, $matches)) { + $module = $matches['namespace'] . '\\' . $matches['module']; + self::$_listAnalyticsXml[$module] = $file; + } + } + } + /** * Prepare map of routers */ 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 176/345] 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 4479c070a4e17..8dec680b58726 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 7409f92b62113b81e5713877a518bf50fb70cb85 Mon Sep 17 00:00:00 2001 From: Dave Macaulay <macaulay@adobe.com> Date: Fri, 8 Mar 2019 12:51:01 +0100 Subject: [PATCH 177/345] MC-14937: Complete Page Builder Analytics data collection - Move gift message di.xml entries into root di.xml --- app/code/Magento/GiftMessage/etc/di.xml | 7 +++++++ app/code/Magento/GiftMessage/etc/frontend/di.xml | 7 ------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/GiftMessage/etc/di.xml b/app/code/Magento/GiftMessage/etc/di.xml index 1d03849d978b8..af42eff5d934c 100644 --- a/app/code/Magento/GiftMessage/etc/di.xml +++ b/app/code/Magento/GiftMessage/etc/di.xml @@ -13,6 +13,13 @@ </argument> </arguments> </type> + <type name="Magento\GiftMessage\Model\CompositeConfigProvider"> + <arguments> + <argument name="configProviders" xsi:type="array"> + <item name="gift_message_config_provider" xsi:type="object">Magento\GiftMessage\Model\GiftMessageConfigProvider</item> + </argument> + </arguments> + </type> <preference for="Magento\GiftMessage\Api\CartRepositoryInterface" type="Magento\GiftMessage\Model\CartRepository"/> <preference for="Magento\GiftMessage\Api\ItemRepositoryInterface" type="Magento\GiftMessage\Model\ItemRepository"/> <preference for="Magento\GiftMessage\Api\GuestCartRepositoryInterface" type="Magento\GiftMessage\Model\GuestCartRepository"/> diff --git a/app/code/Magento/GiftMessage/etc/frontend/di.xml b/app/code/Magento/GiftMessage/etc/frontend/di.xml index a4837e0180c0b..3680a7b521217 100644 --- a/app/code/Magento/GiftMessage/etc/frontend/di.xml +++ b/app/code/Magento/GiftMessage/etc/frontend/di.xml @@ -29,13 +29,6 @@ <type name="Magento\Multishipping\Model\Checkout\Type\Multishipping"> <plugin name="save_gift_messages" type="Magento\GiftMessage\Model\Type\Plugin\Multishipping"/> </type> - <type name="Magento\GiftMessage\Model\CompositeConfigProvider"> - <arguments> - <argument name="configProviders" xsi:type="array"> - <item name="gift_message_config_provider" xsi:type="object">Magento\GiftMessage\Model\GiftMessageConfigProvider</item> - </argument> - </arguments> - </type> <type name="Magento\GiftMessage\Block\Cart\Item\Renderer\Actions\GiftOptions"> <arguments> <argument name="layoutProcessors" xsi:type="array"> 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 178/345] 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 a448537d64e83..db9da71ebf739 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 4488e63648ba75b3f47e34eda5e0ca6191289534 Mon Sep 17 00:00:00 2001 From: Dave Macaulay <macaulay@adobe.com> Date: Fri, 8 Mar 2019 13:27:23 +0100 Subject: [PATCH 179/345] MC-14937: Complete Page Builder Analytics data collection - Revert gift message changes --- app/code/Magento/GiftMessage/etc/di.xml | 7 ------- app/code/Magento/GiftMessage/etc/frontend/di.xml | 7 +++++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/GiftMessage/etc/di.xml b/app/code/Magento/GiftMessage/etc/di.xml index af42eff5d934c..1d03849d978b8 100644 --- a/app/code/Magento/GiftMessage/etc/di.xml +++ b/app/code/Magento/GiftMessage/etc/di.xml @@ -13,13 +13,6 @@ </argument> </arguments> </type> - <type name="Magento\GiftMessage\Model\CompositeConfigProvider"> - <arguments> - <argument name="configProviders" xsi:type="array"> - <item name="gift_message_config_provider" xsi:type="object">Magento\GiftMessage\Model\GiftMessageConfigProvider</item> - </argument> - </arguments> - </type> <preference for="Magento\GiftMessage\Api\CartRepositoryInterface" type="Magento\GiftMessage\Model\CartRepository"/> <preference for="Magento\GiftMessage\Api\ItemRepositoryInterface" type="Magento\GiftMessage\Model\ItemRepository"/> <preference for="Magento\GiftMessage\Api\GuestCartRepositoryInterface" type="Magento\GiftMessage\Model\GuestCartRepository"/> diff --git a/app/code/Magento/GiftMessage/etc/frontend/di.xml b/app/code/Magento/GiftMessage/etc/frontend/di.xml index 3680a7b521217..a4837e0180c0b 100644 --- a/app/code/Magento/GiftMessage/etc/frontend/di.xml +++ b/app/code/Magento/GiftMessage/etc/frontend/di.xml @@ -29,6 +29,13 @@ <type name="Magento\Multishipping\Model\Checkout\Type\Multishipping"> <plugin name="save_gift_messages" type="Magento\GiftMessage\Model\Type\Plugin\Multishipping"/> </type> + <type name="Magento\GiftMessage\Model\CompositeConfigProvider"> + <arguments> + <argument name="configProviders" xsi:type="array"> + <item name="gift_message_config_provider" xsi:type="object">Magento\GiftMessage\Model\GiftMessageConfigProvider</item> + </argument> + </arguments> + </type> <type name="Magento\GiftMessage\Block\Cart\Item\Renderer\Actions\GiftOptions"> <arguments> <argument name="layoutProcessors" xsi:type="array"> From cf51a02dd373f03fb9577e11d1f66eb71812f168 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Fri, 8 Mar 2019 14:31:39 +0100 Subject: [PATCH 180/345] Added test case for multiple shipping methods for one address --- .../SetOfflineShippingMethodsOnCartTest.php | 56 ++++++++++++++++++- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php index 8a6476329177d..b053820c8a7a7 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php @@ -71,6 +71,59 @@ public function testSetOfflineShippingMethod(string $carrier, string $method, fl ); } + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/Checkout/_files/enable_all_shipping_methods.php + */ + public function testSetShippingMethodTwiceInOneRequest() + { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load( + $quote, + 'test_order_1', + 'reserved_order_id' + ); + $shippingAddress = $quote->getShippingAddress(); + $shippingAddressId = $shippingAddress->getId(); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$quote->getId()); + + $query = <<<QUERY +mutation { + setShippingMethodsOnCart(input: + { + cart_id: "$maskedQuoteId", + shipping_methods: [ + { + cart_address_id: $shippingAddressId + method_code: "flatrate" + carrier_code: "flatrate" + } + { + cart_address_id: $shippingAddressId + method_code: "freeshipping" + carrier_code: "freeshipping" + } + ] + }) { + + cart { + shipping_addresses { + selected_shipping_method { + carrier_code + method_code + label + amount + } + } + } + } +} +QUERY; + + self::expectExceptionMessage('You cannot specify multiple shipping methods.'); + $this->sendRequestWithToken($query); + } + /** * Data provider for base offline shipping methods * @@ -90,7 +143,7 @@ public function offlineShippingMethodDataProvider() * * @param string $shippingCarrierCode * @param string $shippingMethodCode - * @param string $shippingAmount + * @param float $shippingAmount * @param string $shippingLabel * @throws \Magento\Framework\Exception\AuthenticationException * @throws \Magento\Framework\Exception\NoSuchEntityException @@ -166,7 +219,6 @@ private function getQuery( } } } - QUERY; } 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 181/345] 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 5409991477539..9a6893a1aff83 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 f526e0d4ea067..b48d6d585492a 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 32d57353f4426..e2df3d69ca0aa 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 465c2d366c582..a61a6862843b3 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 a253ca4c13aa0..ac70034a85da5 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 4aa76b4fadfcc..6155526287555 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 0fd85c998866b..0f2857fd6ec5c 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 769f723dc9d5a..dab251ce50c83 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 bae72d8186a18..36658f2e4eebe 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 976803b0618c9983f51d28c789e046c07718dd25 Mon Sep 17 00:00:00 2001 From: Dave Macaulay <macaulay@adobe.com> Date: Fri, 8 Mar 2019 16:34:02 +0100 Subject: [PATCH 182/345] MC-14937: Complete Page Builder Analytics data collection - Add defaults to configProviders --- .../Magento/GiftMessage/Model/CompositeConfigProvider.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/GiftMessage/Model/CompositeConfigProvider.php b/app/code/Magento/GiftMessage/Model/CompositeConfigProvider.php index 0fdce9e9090ac..cb370c27863ca 100644 --- a/app/code/Magento/GiftMessage/Model/CompositeConfigProvider.php +++ b/app/code/Magento/GiftMessage/Model/CompositeConfigProvider.php @@ -7,6 +7,9 @@ use Magento\Checkout\Model\ConfigProviderInterface; +/** + * Class CompositeConfigProvider + */ class CompositeConfigProvider implements ConfigProviderInterface { /** @@ -18,13 +21,13 @@ class CompositeConfigProvider implements ConfigProviderInterface * @param ConfigProviderInterface[] $configProviders */ public function __construct( - array $configProviders + array $configProviders = [] ) { $this->configProviders = $configProviders; } /** - * {@inheritdoc} + * @inheritdoc */ public function getConfig() { 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 183/345] 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 0000000000000..cb27439a9d886 --- /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 0000000000000..bbb6aa71b8938 --- /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 0000000000000..5b4c3115744c9 --- /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 0000000000000..d82f4b9dd25e8 --- /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 0000000000000..aecd7fcf1b703 --- /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 0000000000000..b158e4923074a --- /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 0000000000000..1058b2d6f2177 --- /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 0000000000000..b624639281187 --- /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 0000000000000..aea1094b4d629 --- /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 0000000000000..95582e107da19 --- /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 0000000000000..6fe25160655fc --- /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 38ce04fa56d81..e05d0fea6b129 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 81ed92762db527a7a84cb4908169c99433356e47 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Fri, 8 Mar 2019 10:22:31 -0600 Subject: [PATCH 184/345] MC-15215: MFTF - DeleteCustomWebsiteActionGroup Is Flaky --- .../Test/Mftf/ActionGroup/DeleteCustomWebsiteActionGroup.xml | 4 +--- .../Test/Mftf/Section/AdminStoresDeleteWebsiteSection.xml | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomWebsiteActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomWebsiteActionGroup.xml index cc6a1fb62ea5f..32441a1864254 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomWebsiteActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomWebsiteActionGroup.xml @@ -13,13 +13,11 @@ </arguments> <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnTheStorePage"/> <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="clickOnResetButton"/> - <waitForPageLoad stepKey="waitForPageLoadAfterResetButtonClicked" time="10"/> <fillField userInput="{{websiteName}}" selector="{{AdminStoresGridSection.websiteFilterTextField}}" stepKey="fillSearchWebsiteField"/> <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton" /> - <waitForPageLoad stepKey="waitForPageLoadAfterSearch" time="10"/> <see userInput="{{websiteName}}" selector="{{AdminStoresGridSection.websiteNameInFirstRow}}" stepKey="verifyThatCorrectWebsiteFound"/> <click selector="{{AdminStoresGridSection.websiteNameInFirstRow}}" stepKey="clickEditExistingWebsite"/> - + <waitForPageLoad stepKey="waitForPageLoadAfterWebsiteSelected" time="30"/> <click selector="{{AdminStoresMainActionsSection.deleteButton}}" stepKey="clickDeleteWebsiteButtonOnEditStorePage"/> <selectOption userInput="No" selector="{{AdminStoresDeleteWebsiteSection.createDbBackup}}" stepKey="setCreateDbBackupToNo"/> <click selector="{{AdminStoresDeleteWebsiteSection.deleteButton}}" stepKey="clickDeleteButtonOnDeleteWebsitePage"/> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresDeleteWebsiteSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresDeleteWebsiteSection.xml index fea7dc07c8287..b8df919446e07 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresDeleteWebsiteSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresDeleteWebsiteSection.xml @@ -8,6 +8,6 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminStoresDeleteWebsiteSection"> <element name="createDbBackup" type="select" selector="#store_create_backup"/> - <element name="deleteButton" type="button" selector="#delete" timeout="30"/> + <element name="deleteButton" type="button" selector="#delete" timeout="90"/> </section> </sections> 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 185/345] 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 9a6893a1aff83..63182dd5624e6 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 f9c90f82dacd4..5785a9f3ccaa7 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 591c0892b4228..13933b952cad4 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 44d051933909b..47e532edfe548 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 e565f609611c2..dc1d888d2af19 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 eb6239ea87ef0..f18a5ff1d875a 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 a61a6862843b3..33229c6432c5a 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 0f2857fd6ec5c..0000000000000 --- 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 dab251ce50c83..7fab0a15ca19f 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 7748e43bbd621..32240e68ae73e 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 0fe618b2db304..53aa1e24a5cd2 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 72c5f2e0cbdfc..8f65a2d8c5ed2 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 f6949d306a1aa4795b5e9cdfe0f4e5e451440bb4 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Fri, 8 Mar 2019 10:52:46 -0600 Subject: [PATCH 186/345] Travis CI env updated with Elasticsearch 6 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d29aa241b15b6..e75e8f1a52dd1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -54,7 +54,7 @@ cache: - $HOME/node_modules - $HOME/yarn.lock before_install: - - curl -O https://download.elastic.co/elasticsearch/release/org/elasticsearch/distribution/deb/elasticsearch/2.3.0/elasticsearch-2.3.0.deb && sudo dpkg -i --force-confnew elasticsearch-2.3.0.deb && sudo service elasticsearch restart + - curl -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.6.1.deb && sudo dpkg -i --force-confnew elasticsearch-6.6.1.deb && sudo service elasticsearch restart - ./dev/travis/before_install.sh install: composer install --no-interaction before_script: ./dev/travis/before_script.sh 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 187/345] 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 6b913e5b458e6..d99d1b69887ed 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 5491c27228387..ac316d060f6e9 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 f116e44e5c141..e0b3d4b850bbb 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 9e5bde9a2be49..81b025c9554e2 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 223a712697ef9e967312d1872f1e7867d34952d0 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Wed, 13 Feb 2019 20:51:05 +0200 Subject: [PATCH 188/345] graphQl-360(361): unmarked tests --- .../Magento/GraphQl/Catalog/CategoryProductsVariantsTest.php | 2 -- .../GraphQl/ConfigurableProduct/ConfigurableProductViewTest.php | 2 -- 2 files changed, 4 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryProductsVariantsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryProductsVariantsTest.php index 1419aff867d2d..098c53b430b88 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryProductsVariantsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryProductsVariantsTest.php @@ -24,8 +24,6 @@ class CategoryProductsVariantsTest extends GraphQlAbstract */ public function testGetSimpleProductsFromCategory() { - $this->markTestIncomplete('https://github.com/magento/graphql-ce/issues/360'); - $query = <<<QUERY { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/ConfigurableProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/ConfigurableProductViewTest.php index c25eed1fd6511..da5410384627c 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/ConfigurableProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/ConfigurableProductViewTest.php @@ -28,8 +28,6 @@ class ConfigurableProductViewTest extends GraphQlAbstract */ public function testQueryConfigurableProductLinks() { - $this->markTestIncomplete('https://github.com/magento/graphql-ce/issues/361'); - $productSku = 'configurable'; $query From 7547a8f6da2d988c6e6174ad18b8c25223e0b4b0 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Fri, 8 Mar 2019 12:53:58 -0600 Subject: [PATCH 189/345] MC-15215: MFTF - DeleteCustomWebsiteActionGroup Is Flaky --- .../Test/Mftf/ActionGroup/DeleteCustomWebsiteActionGroup.xml | 1 + .../Store/Test/Mftf/Section/AdminStoresDeleteWebsiteSection.xml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomWebsiteActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomWebsiteActionGroup.xml index 32441a1864254..da3ce02a80f28 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomWebsiteActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomWebsiteActionGroup.xml @@ -21,5 +21,6 @@ <click selector="{{AdminStoresMainActionsSection.deleteButton}}" stepKey="clickDeleteWebsiteButtonOnEditStorePage"/> <selectOption userInput="No" selector="{{AdminStoresDeleteWebsiteSection.createDbBackup}}" stepKey="setCreateDbBackupToNo"/> <click selector="{{AdminStoresDeleteWebsiteSection.deleteButton}}" stepKey="clickDeleteButtonOnDeleteWebsitePage"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="You deleted the website." stepKey="checkSuccessMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresDeleteWebsiteSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresDeleteWebsiteSection.xml index b8df919446e07..1bdf7f0c22c4e 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresDeleteWebsiteSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresDeleteWebsiteSection.xml @@ -8,6 +8,6 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminStoresDeleteWebsiteSection"> <element name="createDbBackup" type="select" selector="#store_create_backup"/> - <element name="deleteButton" type="button" selector="#delete" timeout="90"/> + <element name="deleteButton" type="button" selector="#delete" timeout="120"/> </section> </sections> From 7b1fc55e5a047dfdd63fc39d91d1fa86188fb434 Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Fri, 8 Mar 2019 12:54:49 -0600 Subject: [PATCH 190/345] GraphQL-360: Unskip and fix testGetSimpleProductsFromCategory --- .../Model/Variant/Collection.php | 13 +++---- .../Catalog/CategoryProductsVariantsTest.php | 1 - .../Magento/GraphQl/Catalog/CategoryTest.php | 37 +++---------------- 3 files changed, 12 insertions(+), 39 deletions(-) diff --git a/app/code/Magento/ConfigurableProductGraphQl/Model/Variant/Collection.php b/app/code/Magento/ConfigurableProductGraphQl/Model/Variant/Collection.php index 9fda4ec0173ec..12571602878d1 100644 --- a/app/code/Magento/ConfigurableProductGraphQl/Model/Variant/Collection.php +++ b/app/code/Magento/ConfigurableProductGraphQl/Model/Variant/Collection.php @@ -90,14 +90,17 @@ public function __construct( */ public function addParentProduct(Product $product) : void { - if (isset($this->parentProducts[$product->getId()])) { + $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); + $productId = $product->getData($linkField); + + if (isset($this->parentProducts[$productId])) { return; } if (!empty($this->childrenMap)) { $this->childrenMap = []; } - $this->parentProducts[$product->getId()] = $product; + $this->parentProducts[$productId] = $product; } /** @@ -140,16 +143,12 @@ private function fetch() : array return $this->childrenMap; } - $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); foreach ($this->parentProducts as $product) { $attributeData = $this->getAttributesCodes($product); /** @var ChildCollection $childCollection */ $childCollection = $this->childCollectionFactory->create(); - $childCollection->addAttributeToSelect($attributeData); - - /** @var Product $product */ - $product->setData($linkField, $product->getId()); $childCollection->setProductFilter($product); + $childCollection->addAttributeToSelect($attributeData); /** @var Product $childProduct */ foreach ($childCollection->getItems() as $childProduct) { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryProductsVariantsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryProductsVariantsTest.php index 098c53b430b88..f62be7328481c 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryProductsVariantsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryProductsVariantsTest.php @@ -18,7 +18,6 @@ class CategoryProductsVariantsTest extends GraphQlAbstract { /** - * * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable.php * @throws \Magento\Framework\Exception\NoSuchEntityException */ diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php index c5fd2c49b9924..b20d8d21b66aa 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php @@ -28,7 +28,6 @@ protected function setUp() } /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Catalog/_files/categories.php * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ @@ -70,14 +69,7 @@ public function testCategoriesTree() } } QUERY; - // get customer ID token - /** @var \Magento\Integration\Api\CustomerTokenServiceInterface $customerTokenService */ - $customerTokenService = $this->objectManager->create( - \Magento\Integration\Api\CustomerTokenServiceInterface::class - ); - $customerToken = $customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); - $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; - $response = $this->graphQlQuery($query, [], '', $headerMap); + $response = $this->graphQlQuery($query); $responseDataObject = new DataObject($response); //Some sort of smoke testing self::assertEquals( @@ -111,39 +103,22 @@ public function testCategoriesTree() } /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Catalog/_files/categories.php - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function testGetCategoryById() { - $rootCategoryId = 13; + $categoryId = 13; $query = <<<QUERY { - category(id: {$rootCategoryId}) { + category(id: {$categoryId}) { id name } } QUERY; - // get customer ID token - /** @var \Magento\Integration\Api\CustomerTokenServiceInterface $customerTokenService */ - $customerTokenService = $this->objectManager->create( - \Magento\Integration\Api\CustomerTokenServiceInterface::class - ); - $customerToken = $customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); - $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; - $response = $this->graphQlQuery($query, [], '', $headerMap); - $responseDataObject = new DataObject($response); - //Some sort of smoke testing - self::assertEquals( - 'Category 1.2', - $responseDataObject->getData('category/name') - ); - self::assertEquals( - 13, - $responseDataObject->getData('category/id') - ); + $response = $this->graphQlQuery($query); + self::assertEquals('Category 1.2', $response['category']['name']); + self::assertEquals(13, $response['category']['id']); } /** 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 191/345] 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 5785a9f3ccaa7..becd6c160155c 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 13933b952cad4..b47d65e310070 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 47e532edfe548..5c3ee3da8ca81 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 dc1d888d2af19..f41fc00b55acb 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 7fab0a15ca19f..b6149be04c471 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 53aa1e24a5cd2..3ec8c806dcbb1 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 192/345] 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 f31981d2ec40f..d9d623a994b37 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 fa60f8b9e471abcffab9c3173e17587cdb6db218 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Fri, 8 Mar 2019 16:18:01 -0600 Subject: [PATCH 193/345] ENGCOM-4389: Elasticsearch6 implementation --- .travis.yml | 2 +- .../Magento/Elasticsearch/Model/Config.php | 16 ++++-- app/code/Magento/Elasticsearch/composer.json | 2 +- app/code/Magento/Elasticsearch/etc/di.xml | 8 +++ .../Magento/Elasticsearch6/Model/Config.php | 56 ------------------- .../Model/DataProvider/Suggestions.php | 2 +- .../Model/DataProvider/SuggestionsTest.php | 2 +- app/code/Magento/Elasticsearch6/composer.json | 2 +- app/code/Magento/Elasticsearch6/etc/di.xml | 8 +++ .../SearchAdapter/AdapterTest.php | 4 +- .../Model/Client/ElasticsearchTest.php | 10 ++-- .../Model/Indexer/IndexHandlerTest.php | 12 ++-- .../Model/Indexer/ReindexAllTest.php | 4 +- .../SearchAdapter/AdapterTest.php | 48 ++++++++-------- .../Php/_files/phpcpd/blacklist/common.txt | 1 + 15 files changed, 71 insertions(+), 106 deletions(-) delete mode 100644 app/code/Magento/Elasticsearch6/Model/Config.php diff --git a/.travis.yml b/.travis.yml index d29aa241b15b6..e75e8f1a52dd1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -54,7 +54,7 @@ cache: - $HOME/node_modules - $HOME/yarn.lock before_install: - - curl -O https://download.elastic.co/elasticsearch/release/org/elasticsearch/distribution/deb/elasticsearch/2.3.0/elasticsearch-2.3.0.deb && sudo dpkg -i --force-confnew elasticsearch-2.3.0.deb && sudo service elasticsearch restart + - curl -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.6.1.deb && sudo dpkg -i --force-confnew elasticsearch-6.6.1.deb && sudo service elasticsearch restart - ./dev/travis/before_install.sh install: composer install --no-interaction before_script: ./dev/travis/before_script.sh diff --git a/app/code/Magento/Elasticsearch/Model/Config.php b/app/code/Magento/Elasticsearch/Model/Config.php index dc08a72a9feb3..387db07c62f90 100644 --- a/app/code/Magento/Elasticsearch/Model/Config.php +++ b/app/code/Magento/Elasticsearch/Model/Config.php @@ -25,8 +25,6 @@ class Config implements ClientOptionsInterface */ const ENGINE_NAME = 'elasticsearch'; - private const ENGINE_NAME_5 = 'elasticsearch5'; - /** * Elasticsearch Entity type */ @@ -64,23 +62,31 @@ class Config implements ClientOptionsInterface private $engineResolver; /** - * Constructor + * Available Elasticsearch engines. * + * @var array + */ + private $engineList; + + /** * @param ScopeConfigInterface $scopeConfig * @param ClientResolver|null $clientResolver * @param EngineResolverInterface|null $engineResolver * @param string|null $prefix + * @param array $engineList */ public function __construct( ScopeConfigInterface $scopeConfig, ClientResolver $clientResolver = null, EngineResolverInterface $engineResolver = null, - $prefix = null + $prefix = null, + $engineList = [] ) { $this->scopeConfig = $scopeConfig; $this->clientResolver = $clientResolver ?: ObjectManager::getInstance()->get(ClientResolver::class); $this->engineResolver = $engineResolver ?: ObjectManager::getInstance()->get(EngineResolverInterface::class); $this->prefix = $prefix ?: $this->clientResolver->getCurrentEngine(); + $this->engineList = $engineList; } /** @@ -138,7 +144,7 @@ public function getSearchConfigData($field, $storeId = null) */ public function isElasticsearchEnabled() { - return in_array($this->engineResolver->getCurrentSearchEngine(), [self::ENGINE_NAME, self::ENGINE_NAME_5]); + return in_array($this->engineResolver->getCurrentSearchEngine(), $this->engineList); } /** diff --git a/app/code/Magento/Elasticsearch/composer.json b/app/code/Magento/Elasticsearch/composer.json index a821506f5ef6e..c6ac38c1e4005 100644 --- a/app/code/Magento/Elasticsearch/composer.json +++ b/app/code/Magento/Elasticsearch/composer.json @@ -12,7 +12,7 @@ "magento/module-store": "*", "magento/module-catalog-inventory": "*", "magento/framework": "*", - "elasticsearch/elasticsearch": "~2.0|~5.1" + "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1" }, "suggest": { "magento/module-config": "*" diff --git a/app/code/Magento/Elasticsearch/etc/di.xml b/app/code/Magento/Elasticsearch/etc/di.xml index 9796dc93858f1..4a354a2ea528f 100644 --- a/app/code/Magento/Elasticsearch/etc/di.xml +++ b/app/code/Magento/Elasticsearch/etc/di.xml @@ -13,6 +13,14 @@ <preference for="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldIndex\ConverterInterface" type="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldIndex\Converter" /> <preference for="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ConverterInterface" type="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\Converter" /> <preference for="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProviderInterface" type="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\CompositeFieldProvider" /> + <type name="Magento\Elasticsearch\Model\Config"> + <arguments> + <argument name="engineList" xsi:type="array"> + <item name="elasticsearch" xsi:type="string">elasticsearch</item> + <item name="elasticsearch5" xsi:type="string">elasticsearch5</item> + </argument> + </arguments> + </type> <virtualType name="Magento\Elasticsearch\Model\Layer\Search\Context" type="Magento\Catalog\Model\Layer\Search\Context"> <arguments> diff --git a/app/code/Magento/Elasticsearch6/Model/Config.php b/app/code/Magento/Elasticsearch6/Model/Config.php deleted file mode 100644 index 1a989e2705fdd..0000000000000 --- a/app/code/Magento/Elasticsearch6/Model/Config.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Elasticsearch6\Model; - -use Magento\Framework\Search\EngineResolverInterface; -use Magento\Framework\App\ObjectManager; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\AdvancedSearch\Model\Client\ClientResolver; - -/** - * Elasticsearch6 config model - */ -class Config extends \Magento\Elasticsearch\Model\Config -{ - /** - * Search engine name - */ - private const ENGINE_NAME_6 = 'elasticsearch6'; - - /** - * @var EngineResolverInterface - */ - private $engineResolver; - - /** - * Constructor - * - * @param ScopeConfigInterface $scopeConfig - * @param ClientResolver|null $clientResolver - * @param EngineResolverInterface|null $engineResolver - * @param string|null $prefix - */ - public function __construct( - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\AdvancedSearch\Model\Client\ClientResolver $clientResolver, - \Magento\Framework\Search\EngineResolverInterface $engineResolver, - $prefix = null - ) { - parent::__construct($scopeConfig, $clientResolver, $engineResolver, $prefix); - $this->engineResolver = $engineResolver; - } - - /** - * Return true if third party search engine is used - * - * @return bool - */ - public function isElasticsearchEnabled() - { - return in_array($this->engineResolver->getCurrentSearchEngine(), [self::ENGINE_NAME_6]); - } -} diff --git a/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php b/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php index 77e1270f54fc2..d05471734bb8f 100644 --- a/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php +++ b/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php @@ -9,7 +9,7 @@ use Magento\Store\Model\ScopeInterface; use Magento\Search\Model\QueryInterface; use Magento\AdvancedSearch\Model\SuggestedQueriesInterface; -use Magento\Elasticsearch6\Model\Config; +use Magento\Elasticsearch\Model\Config; use Magento\Elasticsearch\SearchAdapter\ConnectionManager; use Magento\Search\Model\QueryResultFactory; use Magento\Framework\App\Config\ScopeConfigInterface; diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php b/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php index 957edc559fdcb..b3c60b70ffa8e 100644 --- a/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php +++ b/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php @@ -67,7 +67,7 @@ class SuggestionsTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { - $this->config = $this->getMockBuilder(\Magento\Elasticsearch6\Model\Config::class) + $this->config = $this->getMockBuilder(\Magento\Elasticsearch\Model\Config::class) ->disableOriginalConstructor() ->setMethods(['isElasticsearchEnabled']) ->getMock(); diff --git a/app/code/Magento/Elasticsearch6/composer.json b/app/code/Magento/Elasticsearch6/composer.json index c289d8cd3e4e4..26b6c8c678ade 100644 --- a/app/code/Magento/Elasticsearch6/composer.json +++ b/app/code/Magento/Elasticsearch6/composer.json @@ -9,7 +9,7 @@ "magento/module-search": "*", "magento/module-store": "*", "magento/module-elasticsearch": "*", - "elasticsearch/elasticsearch": "~6.1" + "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1" }, "suggest": { "magento/module-config": "*" diff --git a/app/code/Magento/Elasticsearch6/etc/di.xml b/app/code/Magento/Elasticsearch6/etc/di.xml index 25eff42fd3442..f9ee035972a35 100644 --- a/app/code/Magento/Elasticsearch6/etc/di.xml +++ b/app/code/Magento/Elasticsearch6/etc/di.xml @@ -6,6 +6,14 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\Elasticsearch\Model\Config"> + <arguments> + <argument name="engineList" xsi:type="array"> + <item name="elasticsearch6" xsi:type="string">elasticsearch6</item> + </argument> + </arguments> + </type> + <type name="Magento\Search\Model\Adminhtml\System\Config\Source\Engine"> <arguments> <argument name="engines" xsi:type="array"> diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/AdapterTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/AdapterTest.php index 978815f665341..a52c5bb9e21b7 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/AdapterTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Elasticsearch5/SearchAdapter/AdapterTest.php @@ -43,7 +43,7 @@ protected function setUp() $contentManager = $this->getMockBuilder(\Magento\Elasticsearch\SearchAdapter\ConnectionManager::class) ->disableOriginalConstructor() ->getMock(); - $this->clientMock = $this->getMockBuilder(\Magento\Elasticsearch\Model\Client\Elasticsearch::class) + $this->clientMock = $this->getMockBuilder(\Magento\Elasticsearch6\Model\Client\Elasticsearch::class) ->disableOriginalConstructor() ->getMock(); $contentManager @@ -78,7 +78,7 @@ protected function setUp() /** * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest * @return void */ diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Client/ElasticsearchTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Client/ElasticsearchTest.php index 61add5f7d0ea7..3eea2497daa1f 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Client/ElasticsearchTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Client/ElasticsearchTest.php @@ -10,7 +10,7 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\Store\Model\StoreManagerInterface; use Magento\Elasticsearch\SearchAdapter\ConnectionManager; -use Magento\Elasticsearch\Model\Client\Elasticsearch as ElasticsearchClient; +use Magento\Elasticsearch6\Model\Client\Elasticsearch as ElasticsearchClient; use Magento\Elasticsearch\Model\Config; use Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver; @@ -95,7 +95,7 @@ private function search($text) } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix composite_product_search */ public function testSearchConfigurableProductBySimpleProductName() @@ -104,7 +104,7 @@ public function testSearchConfigurableProductBySimpleProductName() } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix composite_product_search */ public function testSearchConfigurableProductBySimpleProductAttributeMultiselect() @@ -113,7 +113,7 @@ public function testSearchConfigurableProductBySimpleProductAttributeMultiselect } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix composite_product_search */ public function testSearchConfigurableProductBySimpleProductAttributeSelect() @@ -122,7 +122,7 @@ public function testSearchConfigurableProductBySimpleProductAttributeSelect() } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix composite_product_search */ public function testSearchConfigurableProductBySimpleProductAttributeShortDescription() diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php index 014aaf7679bc9..77533e83b719c 100755 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php @@ -13,7 +13,7 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\Store\Model\StoreManagerInterface; use Magento\Elasticsearch\SearchAdapter\ConnectionManager; -use Magento\Elasticsearch\Model\Client\Elasticsearch as ElasticsearchClient; +use Magento\Elasticsearch6\Model\Client\Elasticsearch as ElasticsearchClient; use Magento\Elasticsearch\Model\Config; use Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver; use Magento\Indexer\Model\Indexer; @@ -87,7 +87,7 @@ protected function setUp() } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest * @return void */ @@ -106,7 +106,7 @@ public function testReindexAll(): void /** * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest * @return void */ @@ -131,7 +131,7 @@ public function testReindexRowAfterEdit(): void } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest * @return void */ @@ -170,7 +170,7 @@ public function testReindexRowAfterMassAction(): void } /** - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest * @magentoAppArea adminhtml * @return void @@ -192,7 +192,7 @@ public function testReindexRowAfterDelete(): void /** * @magentoDbIsolation enabled * @magentoAppArea adminhtml - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest * @magentoDataFixture Magento/Elasticsearch/_files/configurable_products.php * @return void diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/ReindexAllTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/ReindexAllTest.php index d40ce9e8a0706..7d4aa8e005e4e 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/ReindexAllTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/ReindexAllTest.php @@ -68,7 +68,7 @@ protected function setUp() /** * Test search of all products after full reindex * - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest_configurable * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_products.php */ @@ -82,7 +82,7 @@ public function testSearchAll() /** * Test search of specific product after full reindex * - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest_configurable * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_products.php */ diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php index dc288a18fadb7..6bb7d6ac568fc 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php @@ -5,8 +5,6 @@ */ namespace Magento\Elasticsearch\SearchAdapter; -use Magento\Elasticsearch\Model\Config; - /** * Class AdapterTest * @@ -26,7 +24,7 @@ class AdapterTest extends \Magento\Framework\Search\Adapter\Mysql\AdapterTest /** * @var string */ - protected $searchEngine = Config::ENGINE_NAME; + protected $searchEngine = 'elasticsearch6'; /** * Get request config path @@ -43,12 +41,12 @@ protected function getRequestConfigPath() */ protected function createAdapter() { - return $this->objectManager->create(\Magento\Elasticsearch\SearchAdapter\Adapter::class); + return $this->objectManager->create(\Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Adapter::class); } /** * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testMatchQuery() @@ -58,7 +56,7 @@ public function testMatchQuery() /** * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testMatchOrderedQuery() @@ -70,7 +68,7 @@ public function testMatchOrderedQuery() /** * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testAggregationsQuery() @@ -80,7 +78,7 @@ public function testAggregationsQuery() /** * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testMatchQueryFilters() @@ -92,7 +90,7 @@ public function testMatchQueryFilters() * Range filter test with all fields filled * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testRangeFilterWithAllFields() @@ -104,7 +102,7 @@ public function testRangeFilterWithAllFields() * Range filter test with all fields filled * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testRangeFilterWithoutFromField() @@ -116,7 +114,7 @@ public function testRangeFilterWithoutFromField() * Range filter test with all fields filled * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testRangeFilterWithoutToField() @@ -128,7 +126,7 @@ public function testRangeFilterWithoutToField() * Term filter test * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testTermFilter() @@ -140,7 +138,7 @@ public function testTermFilter() * Term filter test * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testTermFilterArray() @@ -152,7 +150,7 @@ public function testTermFilterArray() * Term filter test * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testWildcardFilter() @@ -164,7 +162,7 @@ public function testWildcardFilter() * Request limits test * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testSearchLimit() @@ -176,7 +174,7 @@ public function testSearchLimit() * Bool filter test * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testBoolFilter() @@ -188,7 +186,7 @@ public function testBoolFilter() * Test bool filter with nested negative bool filter * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testBoolFilterWithNestedNegativeBoolFilter() @@ -200,7 +198,7 @@ public function testBoolFilterWithNestedNegativeBoolFilter() * Test range inside nested negative bool filter * * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testBoolFilterWithNestedRangeInNegativeBoolFilter() @@ -213,7 +211,7 @@ public function testBoolFilterWithNestedRangeInNegativeBoolFilter() * * @dataProvider elasticSearchAdvancedSearchDataProvider * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest * @param string $nameQuery * @param string $descriptionQuery @@ -259,7 +257,7 @@ public function elasticSearchAdvancedSearchDataProvider() /** * @magentoAppIsolation enabled * @magentoDataFixture Magento/Framework/Search/_files/filterable_attribute.php - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testCustomFilterableAttribute() @@ -274,7 +272,7 @@ public function testCustomFilterableAttribute() * * @magentoAppIsolation enabled * @magentoDataFixture Magento/Framework/Search/_files/filterable_attributes.php - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest * @dataProvider filterByAttributeValuesDataProvider * @param string $requestName @@ -294,7 +292,7 @@ public function testFilterByAttributeValues($requestName, $additionalData) * @param $rangeFilter * @param $expectedRecordsCount * @magentoDataFixture Magento/Framework/Search/_files/date_attribute.php - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest * @magentoAppIsolation enabled * @dataProvider dateDataProvider @@ -309,7 +307,7 @@ public function testAdvancedSearchDateField($rangeFilter, $expectedRecordsCount) /** * @magentoDataFixture Magento/Framework/Search/_files/product_configurable.php * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testAdvancedSearchCompositeProductWithOutOfStockOption() @@ -320,7 +318,7 @@ public function testAdvancedSearchCompositeProductWithOutOfStockOption() /** * @magentoDataFixture Magento/Framework/Search/_files/product_configurable_with_disabled_child.php * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testAdvancedSearchCompositeProductWithDisabledChild() @@ -333,7 +331,7 @@ public function testAdvancedSearchCompositeProductWithDisabledChild() /** * @magentoDataFixture Magento/Framework/Search/_files/search_weight_products.php * @magentoAppIsolation enabled - * @magentoConfigFixture default/catalog/search/engine elasticsearch + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix adaptertest */ public function testSearchQueryBoost() 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 3e788c1eba0ee..96854aa76281f 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/Elasticsearch6/Model/Client From ce9b0970193cd12d69c8240b802b04bd88777df9 Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Fri, 8 Mar 2019 17:16:08 -0600 Subject: [PATCH 194/345] MC-15325: Unable to add Giftcards during BrainTree(Paypal) purchase --- .../Braintree/Controller/Paypal/Review.php | 3 +- .../Controller/Paypal/ReviewTest.php | 43 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 dev/tests/integration/testsuite/Magento/Braintree/Controller/Paypal/ReviewTest.php diff --git a/app/code/Magento/Braintree/Controller/Paypal/Review.php b/app/code/Magento/Braintree/Controller/Paypal/Review.php index 14ec829d98024..eb2de7c7b6e39 100644 --- a/app/code/Magento/Braintree/Controller/Paypal/Review.php +++ b/app/code/Magento/Braintree/Controller/Paypal/Review.php @@ -13,11 +13,12 @@ use Magento\Braintree\Model\Paypal\Helper\QuoteUpdater; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\App\Action\HttpGetActionInterface; /** * Class Review */ -class Review extends AbstractAction implements HttpPostActionInterface +class Review extends AbstractAction implements HttpPostActionInterface, HttpGetActionInterface { /** * @var QuoteUpdater diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Controller/Paypal/ReviewTest.php b/dev/tests/integration/testsuite/Magento/Braintree/Controller/Paypal/ReviewTest.php new file mode 100644 index 0000000000000..fc79048f15f45 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Controller/Paypal/ReviewTest.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Braintree\Controller\Paypal; + +use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * ReviewTest + */ +class ReviewTest extends AbstractController +{ + /** + * @var Review + */ + private $controller; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->controller = $this->_objectManager->create(Review::class); + } + + /** + * Test controller implements correct interfaces + * + */ + public function testInterfaceImplementation() + { + $this->assertInstanceOf(HttpGetActionInterface::class, $this->controller); + $this->assertInstanceOf(HttpPostActionInterface::class, $this->controller); + } +} From c2012388eb962a0e8d27278766ed87bb4192f3dc Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Fri, 8 Mar 2019 18:58:08 -0600 Subject: [PATCH 195/345] GraphQL-458: CustomerGraphQl module refactoring --- .../Address/CreateCustomerAddress.php | 115 ++++++++++++++++++ .../CustomerAddressCreateDataValidator.php | 56 --------- .../CustomerAddressUpdateDataValidator.php | 56 --------- .../Address/DeleteCustomerAddress.php | 60 +++++++++ ...der.php => ExtractCustomerAddressData.php} | 29 +++-- .../Customer/Address}/GetCustomerAddress.php | 4 +- .../Address/GetCustomerAddressForUser.php | 61 ---------- .../Address/UpdateCustomerAddress.php | 111 +++++++++++++++++ ...eAccount.php => CreateCustomerAccount.php} | 45 +++++-- ...taProvider.php => ExtractCustomerData.php} | 39 +----- ...eckCustomerAccount.php => GetCustomer.php} | 33 ++--- .../Model/Customer/SetUpUserContext.php | 30 ----- ...omerData.php => UpdateCustomerAccount.php} | 46 +++++-- .../Model/Resolver/ChangePassword.php | 42 +++---- .../Model/Resolver/CreateCustomer.php | 66 +++------- .../Model/Resolver/CreateCustomerAddress.php | 100 ++++----------- .../Model/Resolver/Customer.php | 33 +++-- .../Model/Resolver/CustomerAddresses.php | 71 +++++++++++ .../Model/Resolver/DeleteCustomerAddress.php | 75 ++++-------- .../Model/Resolver/IsEmailAvailable.php | 11 +- .../Model/Resolver/IsSubscribed.php | 19 ++- .../Model/Resolver/RevokeCustomerToken.php | 19 ++- .../Model/Resolver/UpdateCustomer.php | 60 ++++----- .../Model/Resolver/UpdateCustomerAddress.php | 102 ++++++---------- .../CustomerGraphQl/Test/Mftf/README.md | 3 - .../CustomerGraphQl/etc/schema.graphqls | 2 +- ...ddress.php => ExtractQuoteAddressData.php} | 4 +- .../Model/Cart/SetBillingAddressOnCart.php | 17 +-- .../Model/Cart/SetShippingAddressesOnCart.php | 17 +-- .../Model/Resolver/BillingAddress.php | 16 +-- .../Model/Resolver/ShippingAddresses.php | 16 +-- .../SalesGraphQl/Model/Resolver/Orders.php | 17 ++- .../Model/Resolver/DeletePaymentToken.php | 19 ++- .../Model/Resolver/PaymentTokens.php | 20 ++- .../Customer/ChangeCustomerPasswordTest.php | 1 - .../Customer/DeleteCustomerAddressTest.php | 2 +- .../GraphQl/Customer/UpdateCustomerTest.php | 3 +- .../Customer/SetBillingAddressOnCartTest.php | 2 +- .../Quote/SetShippingAddressOnCartTest.php | 2 +- 39 files changed, 719 insertions(+), 705 deletions(-) create mode 100644 app/code/Magento/CustomerGraphQl/Model/Customer/Address/CreateCustomerAddress.php delete mode 100644 app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressCreateDataValidator.php delete mode 100644 app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressUpdateDataValidator.php create mode 100644 app/code/Magento/CustomerGraphQl/Model/Customer/Address/DeleteCustomerAddress.php rename app/code/Magento/CustomerGraphQl/Model/Customer/Address/{CustomerAddressDataProvider.php => ExtractCustomerAddressData.php} (80%) rename app/code/Magento/{QuoteGraphQl/Model/Cart => CustomerGraphQl/Model/Customer/Address}/GetCustomerAddress.php (92%) delete mode 100644 app/code/Magento/CustomerGraphQl/Model/Customer/Address/GetCustomerAddressForUser.php create mode 100644 app/code/Magento/CustomerGraphQl/Model/Customer/Address/UpdateCustomerAddress.php rename app/code/Magento/CustomerGraphQl/Model/Customer/{CreateAccount.php => CreateCustomerAccount.php} (62%) rename app/code/Magento/CustomerGraphQl/Model/Customer/{CustomerDataProvider.php => ExtractCustomerData.php} (72%) rename app/code/Magento/CustomerGraphQl/Model/Customer/{CheckCustomerAccount.php => GetCustomer.php} (79%) delete mode 100644 app/code/Magento/CustomerGraphQl/Model/Customer/SetUpUserContext.php rename app/code/Magento/CustomerGraphQl/Model/Customer/{UpdateCustomerData.php => UpdateCustomerAccount.php} (72%) create mode 100644 app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerAddresses.php delete mode 100644 app/code/Magento/CustomerGraphQl/Test/Mftf/README.md rename app/code/Magento/QuoteGraphQl/Model/Cart/{ExtractDataFromAddress.php => ExtractQuoteAddressData.php} (96%) diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CreateCustomerAddress.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CreateCustomerAddress.php new file mode 100644 index 0000000000000..388b6dc2ea943 --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CreateCustomerAddress.php @@ -0,0 +1,115 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CustomerGraphQl\Model\Customer\Address; + +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Api\Data\AddressInterfaceFactory; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\Api\DataObjectHelper; + +/** + * Create customer address + */ +class CreateCustomerAddress +{ + /** + * @var GetAllowedAddressAttributes + */ + private $getAllowedAddressAttributes; + + /** + * @var AddressInterfaceFactory + */ + private $addressFactory; + + /** + * @var AddressRepositoryInterface + */ + private $addressRepository; + + /** + * @var DataObjectHelper + */ + private $dataObjectHelper; + + /** + * @param GetAllowedAddressAttributes $getAllowedAddressAttributes + * @param AddressInterfaceFactory $addressFactory + * @param AddressRepositoryInterface $addressRepository + * @param DataObjectHelper $dataObjectHelper + */ + public function __construct( + GetAllowedAddressAttributes $getAllowedAddressAttributes, + AddressInterfaceFactory $addressFactory, + AddressRepositoryInterface $addressRepository, + DataObjectHelper $dataObjectHelper + ) { + $this->getAllowedAddressAttributes = $getAllowedAddressAttributes; + $this->addressFactory = $addressFactory; + $this->addressRepository = $addressRepository; + $this->dataObjectHelper = $dataObjectHelper; + } + + /** + * Create customer address + * + * @param int $customerId + * @param array $data + * @return AddressInterface + * @throws GraphQlInputException + */ + public function execute(int $customerId, array $data): AddressInterface + { + $this->validateData($data); + + /** @var AddressInterface $address */ + $address = $this->addressFactory->create(); + $this->dataObjectHelper->populateWithArray($address, $data, AddressInterface::class); + + if (isset($data['region']['region_id'])) { + $address->setRegionId($address->getRegion()->getRegionId()); + } + $address->setCustomerId($customerId); + + try { + $this->addressRepository->save($address); + } catch (LocalizedException $e) { + throw new GraphQlInputException(__($e->getMessage()), $e); + } + return $address; + } + + /** + * Validate customer address create data + * + * @param array $addressData + * @return void + * @throws GraphQlInputException + */ + public function validateData(array $addressData): void + { + $attributes = $this->getAllowedAddressAttributes->execute(); + $errorInput = []; + + foreach ($attributes as $attributeName => $attributeInfo) { + if ($attributeInfo->getIsRequired() + && (!isset($addressData[$attributeName]) || empty($addressData[$attributeName])) + ) { + $errorInput[] = $attributeName; + } + } + + if ($errorInput) { + throw new GraphQlInputException( + __('Required parameters are missing: %1', [implode(', ', $errorInput)]) + ); + } + } +} diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressCreateDataValidator.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressCreateDataValidator.php deleted file mode 100644 index 65672bcd3503b..0000000000000 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressCreateDataValidator.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\CustomerGraphQl\Model\Customer\Address; - -use Magento\Framework\GraphQl\Exception\GraphQlInputException; - -/** - * Customer address create data validator - */ -class CustomerAddressCreateDataValidator -{ - /** - * @var GetAllowedAddressAttributes - */ - private $getAllowedAddressAttributes; - - /** - * @param GetAllowedAddressAttributes $getAllowedAddressAttributes - */ - public function __construct(GetAllowedAddressAttributes $getAllowedAddressAttributes) - { - $this->getAllowedAddressAttributes = $getAllowedAddressAttributes; - } - - /** - * Validate customer address create data - * - * @param array $addressData - * @return void - * @throws GraphQlInputException - */ - public function validate(array $addressData): void - { - $attributes = $this->getAllowedAddressAttributes->execute(); - $errorInput = []; - - foreach ($attributes as $attributeName => $attributeInfo) { - if ($attributeInfo->getIsRequired() - && (!isset($addressData[$attributeName]) || empty($addressData[$attributeName])) - ) { - $errorInput[] = $attributeName; - } - } - - if ($errorInput) { - throw new GraphQlInputException( - __('Required parameters are missing: %1', [implode(', ', $errorInput)]) - ); - } - } -} diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressUpdateDataValidator.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressUpdateDataValidator.php deleted file mode 100644 index 13716b491fddf..0000000000000 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressUpdateDataValidator.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\CustomerGraphQl\Model\Customer\Address; - -use Magento\Framework\GraphQl\Exception\GraphQlInputException; - -/** - * Customer address update data validator. Patch update is allowed - */ -class CustomerAddressUpdateDataValidator -{ - /** - * @var GetAllowedAddressAttributes - */ - private $getAllowedAddressAttributes; - - /** - * @param GetAllowedAddressAttributes $getAllowedAddressAttributes - */ - public function __construct(GetAllowedAddressAttributes $getAllowedAddressAttributes) - { - $this->getAllowedAddressAttributes = $getAllowedAddressAttributes; - } - - /** - * Validate customer address update data - * - * @param array $addressData - * @return void - * @throws GraphQlInputException - */ - public function validate(array $addressData): void - { - $attributes = $this->getAllowedAddressAttributes->execute(); - $errorInput = []; - - foreach ($attributes as $attributeName => $attributeInfo) { - if ($attributeInfo->getIsRequired() - && (isset($addressData[$attributeName]) && empty($addressData[$attributeName])) - ) { - $errorInput[] = $attributeName; - } - } - - if ($errorInput) { - throw new GraphQlInputException( - __('Required parameters are missing: %1', [implode(', ', $errorInput)]) - ); - } - } -} diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/DeleteCustomerAddress.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/DeleteCustomerAddress.php new file mode 100644 index 0000000000000..586fbebde703f --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/DeleteCustomerAddress.php @@ -0,0 +1,60 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CustomerGraphQl\Model\Customer\Address; + +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; + +/** + * Delete customer address + */ +class DeleteCustomerAddress +{ + /** + * @var AddressRepositoryInterface + */ + private $addressRepository; + + /** + * @param AddressRepositoryInterface $addressRepository + */ + public function __construct( + AddressRepositoryInterface $addressRepository + ) { + $this->addressRepository = $addressRepository; + } + + /** + * Delete customer address + * + * @param AddressInterface $address + * @return void + * @throws GraphQlInputException + */ + public function execute(AddressInterface $address): void + { + if ($address->isDefaultBilling()) { + throw new GraphQlInputException( + __('Customer Address %1 is set as default billing address and can not be deleted', [$address->getId()]) + ); + } + if ($address->isDefaultShipping()) { + throw new GraphQlInputException( + __('Customer Address %1 is set as default shipping address and can not be deleted', [$address->getId()]) + ); + } + + try { + $this->addressRepository->delete($address); + } catch (LocalizedException $e) { + throw new GraphQlInputException(__($e->getMessage()), $e); + } + } +} diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressDataProvider.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/ExtractCustomerAddressData.php similarity index 80% rename from app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressDataProvider.php rename to app/code/Magento/CustomerGraphQl/Model/Customer/Address/ExtractCustomerAddressData.php index 9640953032ac6..a4649bccc02e8 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CustomerAddressDataProvider.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/ExtractCustomerAddressData.php @@ -17,9 +17,9 @@ use Magento\Framework\Serialize\SerializerInterface; /** - * Customer Address field data provider, used for GraphQL request processing. + * Transform single customer address data from object to in array format */ -class CustomerAddressDataProvider +class ExtractCustomerAddressData { /** * @var ServiceOutputProcessor @@ -82,24 +82,27 @@ private function curateAddressDefaultValues(array $address, AddressInterface $ad /** * Transform single customer address data from object to in array format * - * @param AddressInterface $addressObject + * @param AddressInterface $address * @return array */ - public function getAddressData(AddressInterface $addressObject): array + public function execute(AddressInterface $address): array { - $address = $this->serviceOutputProcessor->process( - $addressObject, + $addressData = $this->serviceOutputProcessor->process( + $address, AddressRepositoryInterface::class, 'getById' ); - $address = $this->curateAddressDefaultValues($address, $addressObject); + $addressData = $this->curateAddressDefaultValues($addressData, $address); - if (isset($address[CustomAttributesDataInterface::EXTENSION_ATTRIBUTES_KEY])) { - $address = array_merge($address, $address[CustomAttributesDataInterface::EXTENSION_ATTRIBUTES_KEY]); + if (isset($addressData[CustomAttributesDataInterface::EXTENSION_ATTRIBUTES_KEY])) { + $addressData = array_merge( + $addressData, + $addressData[CustomAttributesDataInterface::EXTENSION_ATTRIBUTES_KEY] + ); } $customAttributes = []; - if (isset($address[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES])) { - foreach ($address[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES] as $attribute) { + if (isset($addressData[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES])) { + foreach ($addressData[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES] as $attribute) { $isArray = false; if (is_array($attribute['value'])) { $isArray = true; @@ -120,8 +123,8 @@ public function getAddressData(AddressInterface $addressObject): array $customAttributes[$attribute['attribute_code']] = $attribute['value']; } } - $address = array_merge($address, $customAttributes); + $addressData = array_merge($addressData, $customAttributes); - return $address; + return $addressData; } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCustomerAddress.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/GetCustomerAddress.php similarity index 92% rename from app/code/Magento/QuoteGraphQl/Model/Cart/GetCustomerAddress.php rename to app/code/Magento/CustomerGraphQl/Model/Customer/Address/GetCustomerAddress.php index 039324abf6854..7258f2e726fd7 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCustomerAddress.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/GetCustomerAddress.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\QuoteGraphQl\Model\Cart; +namespace Magento\CustomerGraphQl\Model\Customer\Address; use Magento\Customer\Api\AddressRepositoryInterface; use Magento\Customer\Api\Data\AddressInterface; @@ -58,7 +58,7 @@ public function execute(int $addressId, int $customerId): AddressInterface if ((int)$customerAddress->getCustomerId() !== $customerId) { throw new GraphQlAuthorizationException( __( - 'The current user cannot use address with ID "%address_id"', + 'Current customer does not have permission to address with ID "%address_id"', ['address_id' => $addressId] ) ); diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/GetCustomerAddressForUser.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/GetCustomerAddressForUser.php deleted file mode 100644 index f7323402a6c62..0000000000000 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/GetCustomerAddressForUser.php +++ /dev/null @@ -1,61 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\CustomerGraphQl\Model\Customer\Address; - -use Magento\Customer\Api\AddressRepositoryInterface; -use Magento\Customer\Api\Data\AddressInterface; -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; -use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; - -/** - * Get customer address for user - */ -class GetCustomerAddressForUser -{ - /** - * @var AddressRepositoryInterface - */ - private $addressRepository; - - /** - * @param AddressRepositoryInterface $addressRepository - */ - public function __construct(AddressRepositoryInterface $addressRepository) - { - $this->addressRepository = $addressRepository; - } - - /** - * Get customer address for user - * - * @param int $addressId - * @param int $userId - * @return AddressInterface - * @throws GraphQlAuthorizationException - * @throws GraphQlNoSuchEntityException - */ - public function execute(int $addressId, int $userId): AddressInterface - { - try { - /** @var AddressInterface $address */ - $address = $this->addressRepository->getById($addressId); - } catch (NoSuchEntityException $e) { - throw new GraphQlNoSuchEntityException( - __('Address id %1 does not exist.', [$addressId]) - ); - } - - if ($address->getCustomerId() != $userId) { - throw new GraphQlAuthorizationException( - __('Current customer does not have permission to address id %1', [$addressId]) - ); - } - return $address; - } -} diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/UpdateCustomerAddress.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/UpdateCustomerAddress.php new file mode 100644 index 0000000000000..65745a20bc8eb --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/UpdateCustomerAddress.php @@ -0,0 +1,111 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CustomerGraphQl\Model\Customer\Address; + +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\Api\DataObjectHelper; + +/** + * Update customer address + */ +class UpdateCustomerAddress +{ + /** + * @var GetAllowedAddressAttributes + */ + private $getAllowedAddressAttributes; + + /** + * @var AddressRepositoryInterface + */ + private $addressRepository; + + /** + * @var DataObjectHelper + */ + private $dataObjectHelper; + + /** + * @var array + */ + private $restrictedKeys; + + /** + * @param GetAllowedAddressAttributes $getAllowedAddressAttributes + * @param AddressRepositoryInterface $addressRepository + * @param DataObjectHelper $dataObjectHelper + * @param array $restrictedKeys + */ + public function __construct( + GetAllowedAddressAttributes $getAllowedAddressAttributes, + AddressRepositoryInterface $addressRepository, + DataObjectHelper $dataObjectHelper, + array $restrictedKeys = [] + ) { + $this->getAllowedAddressAttributes = $getAllowedAddressAttributes; + $this->addressRepository = $addressRepository; + $this->dataObjectHelper = $dataObjectHelper; + $this->restrictedKeys = $restrictedKeys; + } + + /** + * Update customer address + * + * @param AddressInterface $address + * @param array $data + * @return void + * @throws GraphQlInputException + */ + public function execute(AddressInterface $address, array $data): void + { + $this->validateData($data); + + $filteredData = array_diff_key($data, array_flip($this->restrictedKeys)); + $this->dataObjectHelper->populateWithArray($address, $filteredData, AddressInterface::class); + + if (isset($data['region']['region_id'])) { + $address->setRegionId($address->getRegion()->getRegionId()); + } + + try { + $this->addressRepository->save($address); + } catch (LocalizedException $e) { + throw new GraphQlInputException(__($e->getMessage()), $e); + } + } + + /** + * Validate customer address update data + * + * @param array $addressData + * @return void + * @throws GraphQlInputException + */ + public function validateData(array $addressData): void + { + $attributes = $this->getAllowedAddressAttributes->execute(); + $errorInput = []; + + foreach ($attributes as $attributeName => $attributeInfo) { + if ($attributeInfo->getIsRequired() + && (isset($addressData[$attributeName]) && empty($addressData[$attributeName])) + ) { + $errorInput[] = $attributeName; + } + } + + if ($errorInput) { + throw new GraphQlInputException( + __('Required parameters are missing: %1', [implode(', ', $errorInput)]) + ); + } + } +} diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/CreateAccount.php b/app/code/Magento/CustomerGraphQl/Model/Customer/CreateCustomerAccount.php similarity index 62% rename from app/code/Magento/CustomerGraphQl/Model/Customer/CreateAccount.php rename to app/code/Magento/CustomerGraphQl/Model/Customer/CreateCustomerAccount.php index 4a4b5c863528b..75371b805e023 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/CreateAccount.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/CreateCustomerAccount.php @@ -12,13 +12,13 @@ use Magento\Customer\Api\Data\CustomerInterfaceFactory; use Magento\Framework\Api\DataObjectHelper; use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Store\Model\StoreManagerInterface; /** - * Class CreateAccount creates new customer account + * Create new customer account */ -class CreateAccount +class CreateCustomerAccount { /** * @var DataObjectHelper @@ -40,46 +40,71 @@ class CreateAccount */ private $storeManager; + /** + * @var ChangeSubscriptionStatus + */ + private $changeSubscriptionStatus; + /** * @param DataObjectHelper $dataObjectHelper * @param CustomerInterfaceFactory $customerFactory * @param StoreManagerInterface $storeManager * @param AccountManagementInterface $accountManagement + * @param ChangeSubscriptionStatus $changeSubscriptionStatus */ public function __construct( DataObjectHelper $dataObjectHelper, CustomerInterfaceFactory $customerFactory, StoreManagerInterface $storeManager, - AccountManagementInterface $accountManagement + AccountManagementInterface $accountManagement, + ChangeSubscriptionStatus $changeSubscriptionStatus ) { $this->dataObjectHelper = $dataObjectHelper; $this->customerFactory = $customerFactory; $this->accountManagement = $accountManagement; $this->storeManager = $storeManager; + $this->changeSubscriptionStatus = $changeSubscriptionStatus; } /** * Creates new customer account * - * @param array $args + * @param array $data + * @return CustomerInterface + * @throws GraphQlInputException + */ + public function execute(array $data): CustomerInterface + { + try { + $customer = $this->createAccount($data); + } catch (LocalizedException $e) { + throw new GraphQlInputException(__($e->getMessage())); + } + + if (isset($data['is_subscribed'])) { + $this->changeSubscriptionStatus->execute((int)$customer->getId(), (bool)$data['is_subscribed']); + } + return $customer; + } + + /** + * @param array $data * @return CustomerInterface * @throws LocalizedException - * @throws NoSuchEntityException */ - public function execute(array $args): CustomerInterface + private function createAccount(array $data): CustomerInterface { $customerDataObject = $this->customerFactory->create(); $this->dataObjectHelper->populateWithArray( $customerDataObject, - $args['input'], + $data, CustomerInterface::class ); $store = $this->storeManager->getStore(); $customerDataObject->setWebsiteId($store->getWebsiteId()); $customerDataObject->setStoreId($store->getId()); - $password = array_key_exists('password', $args['input']) ? $args['input']['password'] : null; - + $password = array_key_exists('password', $data) ? $data['password'] : null; return $this->accountManagement->createAccount($customerDataObject, $password); } } diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/CustomerDataProvider.php b/app/code/Magento/CustomerGraphQl/Model/Customer/ExtractCustomerData.php similarity index 72% rename from app/code/Magento/CustomerGraphQl/Model/Customer/CustomerDataProvider.php rename to app/code/Magento/CustomerGraphQl/Model/Customer/ExtractCustomerData.php index c8382593eab23..abe028051a07e 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/CustomerDataProvider.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/ExtractCustomerData.php @@ -9,22 +9,15 @@ use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\Webapi\ServiceOutputProcessor; use Magento\Customer\Api\Data\CustomerInterface; /** - * Customer field data provider, used for GraphQL request processing. + * Transform single customer data from object to in array format */ -class CustomerDataProvider +class ExtractCustomerData { - /** - * @var CustomerRepositoryInterface - */ - private $customerRepository; - /** * @var ServiceOutputProcessor */ @@ -36,47 +29,24 @@ class CustomerDataProvider private $serializer; /** - * @param CustomerRepositoryInterface $customerRepository * @param ServiceOutputProcessor $serviceOutputProcessor * @param SerializerInterface $serializer */ public function __construct( - CustomerRepositoryInterface $customerRepository, ServiceOutputProcessor $serviceOutputProcessor, SerializerInterface $serializer ) { - $this->customerRepository = $customerRepository; $this->serviceOutputProcessor = $serviceOutputProcessor; $this->serializer = $serializer; } - /** - * Get customer data by Id or empty array - * - * @param int $customerId - * @return array - * @throws NoSuchEntityException|LocalizedException - */ - public function getCustomerById(int $customerId): array - { - try { - $customer = $this->customerRepository->getById($customerId); - } catch (NoSuchEntityException $e) { - throw new GraphQlNoSuchEntityException( - __('Customer id "%customer_id" does not exist.', ['customer_id' => $customerId]), - $e - ); - } - return $this->processCustomer($customer); - } - /** * Curate default shipping and default billing keys * * @param array $arrayAddress * @return array */ - private function curateAddressData(array $arrayAddress) : array + private function curateAddressData(array $arrayAddress): array { foreach ($arrayAddress as $key => $address) { if (!isset($address['default_shipping'])) { @@ -94,8 +64,9 @@ private function curateAddressData(array $arrayAddress) : array * * @param CustomerInterface $customer * @return array + * @throws LocalizedException */ - private function processCustomer(CustomerInterface $customer): array + public function execute(CustomerInterface $customer): array { $customerData = $this->serviceOutputProcessor->process( $customer, diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/CheckCustomerAccount.php b/app/code/Magento/CustomerGraphQl/Model/Customer/GetCustomer.php similarity index 79% rename from app/code/Magento/CustomerGraphQl/Model/Customer/CheckCustomerAccount.php rename to app/code/Magento/CustomerGraphQl/Model/Customer/GetCustomer.php index b2f524c877fd6..8bd5c9157493c 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/CheckCustomerAccount.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/GetCustomer.php @@ -10,6 +10,7 @@ use Magento\Authorization\Model\UserContextInterface; use Magento\Customer\Api\AccountManagementInterface; use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Model\AuthenticationInterface; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; @@ -17,11 +18,12 @@ use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; /** - * Check customer account + * Get customer */ -class CheckCustomerAccount +class GetCustomer { /** * @var AuthenticationInterface @@ -54,39 +56,41 @@ public function __construct( } /** - * Check customer account + * Get customer * - * @param int|null $customerId - * @param int|null $customerType - * @return void + * @param ContextInterface $context + * @return CustomerInterface + * @throws GraphQlAuthenticationException * @throws GraphQlAuthorizationException * @throws GraphQlInputException * @throws GraphQlNoSuchEntityException - * @throws GraphQlAuthenticationException */ - public function execute(?int $customerId, ?int $customerType): void + public function execute(ContextInterface $context): CustomerInterface { - if (true === $this->isCustomerGuest($customerId, $customerType)) { + $currentUserId = $context->getUserId(); + $currentUserType = $context->getUserType(); + + if (true === $this->isUserGuest($currentUserId, $currentUserType)) { throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); } try { - $this->customerRepository->getById($customerId); + $customer = $this->customerRepository->getById($currentUserId); } catch (NoSuchEntityException $e) { throw new GraphQlNoSuchEntityException( - __('Customer with id "%customer_id" does not exist.', ['customer_id' => $customerId]), + __('Customer with id "%customer_id" does not exist.', ['customer_id' => $currentUserId]), $e ); } catch (LocalizedException $e) { throw new GraphQlInputException(__($e->getMessage())); } - if (true === $this->authentication->isLocked($customerId)) { + if (true === $this->authentication->isLocked($currentUserId)) { throw new GraphQlAuthenticationException(__('The account is locked.')); } try { - $confirmationStatus = $this->accountManagement->getConfirmationStatus($customerId); + $confirmationStatus = $this->accountManagement->getConfirmationStatus($currentUserId); } catch (LocalizedException $e) { throw new GraphQlInputException(__($e->getMessage())); } @@ -94,6 +98,7 @@ public function execute(?int $customerId, ?int $customerType): void if ($confirmationStatus === AccountManagementInterface::ACCOUNT_CONFIRMATION_REQUIRED) { throw new GraphQlAuthenticationException(__("This account isn't confirmed. Verify and try again.")); } + return $customer; } /** @@ -103,7 +108,7 @@ public function execute(?int $customerId, ?int $customerType): void * @param int|null $customerType * @return bool */ - private function isCustomerGuest(?int $customerId, ?int $customerType): bool + private function isUserGuest(?int $customerId, ?int $customerType): bool { if (null === $customerId || null === $customerType) { return true; diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/SetUpUserContext.php b/app/code/Magento/CustomerGraphQl/Model/Customer/SetUpUserContext.php deleted file mode 100644 index 1fcf1c0d7c1c3..0000000000000 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/SetUpUserContext.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\CustomerGraphQl\Model\Customer; - -use Magento\Customer\Api\Data\CustomerInterface; -use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; -use Magento\Authorization\Model\UserContextInterface; - -/** - * Set up user context after creating new customer account - */ -class SetUpUserContext -{ - /** - * Set up user context after creating new customer account - * - * @param ContextInterface $context - * @param CustomerInterface $customer - */ - public function execute(ContextInterface $context, CustomerInterface $customer) - { - $context->setUserId((int)$customer->getId()); - $context->setUserType(UserContextInterface::USER_TYPE_CUSTOMER); - } -} diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateCustomerData.php b/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateCustomerAccount.php similarity index 72% rename from app/code/Magento/CustomerGraphQl/Model/Customer/UpdateCustomerData.php rename to app/code/Magento/CustomerGraphQl/Model/Customer/UpdateCustomerAccount.php index 33f16f3d94b7d..b58ea8f0d21ab 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateCustomerData.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateCustomerAccount.php @@ -18,9 +18,9 @@ use Magento\Framework\Api\DataObjectHelper; /** - * Update customer data + * Update customer account data */ -class UpdateCustomerData +class UpdateCustomerAccount { /** * @var CustomerRepositoryInterface @@ -42,6 +42,11 @@ class UpdateCustomerData */ private $dataObjectHelper; + /** + * @var ChangeSubscriptionStatus + */ + private $changeSubscriptionStatus; + /** * @var array */ @@ -52,6 +57,7 @@ class UpdateCustomerData * @param StoreManagerInterface $storeManager * @param CheckCustomerPassword $checkCustomerPassword * @param DataObjectHelper $dataObjectHelper + * @param ChangeSubscriptionStatus $changeSubscriptionStatus * @param array $restrictedKeys */ public function __construct( @@ -59,6 +65,7 @@ public function __construct( StoreManagerInterface $storeManager, CheckCustomerPassword $checkCustomerPassword, DataObjectHelper $dataObjectHelper, + ChangeSubscriptionStatus $changeSubscriptionStatus, array $restrictedKeys = [] ) { $this->customerRepository = $customerRepository; @@ -66,34 +73,53 @@ public function __construct( $this->checkCustomerPassword = $checkCustomerPassword; $this->dataObjectHelper = $dataObjectHelper; $this->restrictedKeys = $restrictedKeys; + $this->changeSubscriptionStatus = $changeSubscriptionStatus; } /** - * Update account information + * Update customer account data * - * @param int $customerId + * @param CustomerInterface $customer * @param array $data * @return void * @throws GraphQlAlreadyExistsException - * @throws GraphQlInputException * @throws GraphQlAuthenticationException + * @throws GraphQlInputException */ - public function execute(int $customerId, array $data): void + public function execute(CustomerInterface $customer, array $data): void { - $customer = $this->customerRepository->getById($customerId); + try { + $this->updateCustomer($customer, $data); + } catch (LocalizedException $e) { + throw new GraphQlInputException(__($e->getMessage())); + } - $filteredData = array_diff_key($data, array_flip($this->restrictedKeys)); - $this->dataObjectHelper->populateWithArray($customer, $filteredData, CustomerInterface::class); + if (isset($data['is_subscribed'])) { + $this->changeSubscriptionStatus->execute((int)$customer->getId(), (bool)$data['is_subscribed']); + } + } + /** + * @param CustomerInterface $customer + * @param array $data + * @throws GraphQlAlreadyExistsException + * @throws GraphQlAuthenticationException + * @throws GraphQlInputException + */ + private function updateCustomer(CustomerInterface $customer, array $data): void + { if (isset($data['email']) && $customer->getEmail() !== $data['email']) { if (!isset($data['password']) || empty($data['password'])) { throw new GraphQlInputException(__('Provide the current "password" to change "email".')); } - $this->checkCustomerPassword->execute($data['password'], $customerId); + $this->checkCustomerPassword->execute($data['password'], (int)$customer->getId()); $customer->setEmail($data['email']); } + $filteredData = array_diff_key($data, array_flip($this->restrictedKeys)); + $this->dataObjectHelper->populateWithArray($customer, $filteredData, CustomerInterface::class); + $customer->setStoreId($this->storeManager->getStore()->getId()); try { diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/ChangePassword.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/ChangePassword.php index 78fa852a7ac59..b80c8a99ec59e 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/ChangePassword.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/ChangePassword.php @@ -9,8 +9,8 @@ use Magento\Customer\Api\AccountManagementInterface; use Magento\CustomerGraphQl\Model\Customer\CheckCustomerPassword; -use Magento\CustomerGraphQl\Model\Customer\CustomerDataProvider; -use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccount; +use Magento\CustomerGraphQl\Model\Customer\ExtractCustomerData; +use Magento\CustomerGraphQl\Model\Customer\GetCustomer; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; @@ -22,9 +22,9 @@ class ChangePassword implements ResolverInterface { /** - * @var CheckCustomerAccount + * @var GetCustomer */ - private $checkCustomerAccount; + private $getCustomer; /** * @var CheckCustomerPassword @@ -37,26 +37,26 @@ class ChangePassword implements ResolverInterface private $accountManagement; /** - * @var CustomerDataProvider + * @var ExtractCustomerData */ - private $customerDataProvider; + private $extractCustomerData; /** - * @param CheckCustomerAccount $checkCustomerAccount + * @param GetCustomer $getCustomer * @param CheckCustomerPassword $checkCustomerPassword * @param AccountManagementInterface $accountManagement - * @param CustomerDataProvider $customerDataProvider + * @param ExtractCustomerData $extractCustomerData */ public function __construct( - CheckCustomerAccount $checkCustomerAccount, + GetCustomer $getCustomer, CheckCustomerPassword $checkCustomerPassword, AccountManagementInterface $accountManagement, - CustomerDataProvider $customerDataProvider + ExtractCustomerData $extractCustomerData ) { - $this->checkCustomerAccount = $checkCustomerAccount; + $this->getCustomer = $getCustomer; $this->checkCustomerPassword = $checkCustomerPassword; $this->accountManagement = $accountManagement; - $this->customerDataProvider = $customerDataProvider; + $this->extractCustomerData = $extractCustomerData; } /** @@ -69,24 +69,20 @@ public function resolve( array $value = null, array $args = null ) { - if (!isset($args['currentPassword'])) { + if (!isset($args['currentPassword']) || '' == trim($args['currentPassword'])) { throw new GraphQlInputException(__('Specify the "currentPassword" value.')); } - if (!isset($args['newPassword'])) { + if (!isset($args['newPassword']) || '' == trim($args['newPassword'])) { throw new GraphQlInputException(__('Specify the "newPassword" value.')); } - $currentUserId = $context->getUserId(); - $currentUserType = $context->getUserType(); - $this->checkCustomerAccount->execute($currentUserId, $currentUserType); + $customer = $this->getCustomer->execute($context); + $customerId = (int)$customer->getId(); - $currentUserId = (int)$currentUserId; - $this->checkCustomerPassword->execute($args['currentPassword'], $currentUserId); + $this->checkCustomerPassword->execute($args['currentPassword'], $customerId); + $this->accountManagement->changePasswordById($customerId, $args['currentPassword'], $args['newPassword']); - $this->accountManagement->changePasswordById($currentUserId, $args['currentPassword'], $args['newPassword']); - - $data = $this->customerDataProvider->getCustomerById($currentUserId); - return $data; + return $this->extractCustomerData->execute($customer); } } diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php index 299045c6b62b0..1ae22bcc12792 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php @@ -7,16 +7,13 @@ namespace Magento\CustomerGraphQl\Model\Resolver; -use Magento\CustomerGraphQl\Model\Customer\ChangeSubscriptionStatus; -use Magento\CustomerGraphQl\Model\Customer\CreateAccount; -use Magento\CustomerGraphQl\Model\Customer\CustomerDataProvider; -use Magento\CustomerGraphQl\Model\Customer\SetUpUserContext; -use Magento\Framework\Exception\State\InputMismatchException; +use Magento\Authorization\Model\UserContextInterface; +use Magento\CustomerGraphQl\Model\Customer\CreateCustomerAccount; +use Magento\CustomerGraphQl\Model\Customer\ExtractCustomerData; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\Framework\Validator\Exception as ValidatorException; /** * Create customer account resolver @@ -24,41 +21,25 @@ class CreateCustomer implements ResolverInterface { /** - * @var CustomerDataProvider + * @var ExtractCustomerData */ - private $customerDataProvider; + private $extractCustomerData; /** - * @var ChangeSubscriptionStatus + * @var CreateCustomerAccount */ - private $changeSubscriptionStatus; + private $createCustomerAccount; /** - * @var CreateAccount - */ - private $createAccount; - - /** - * @var SetUpUserContext - */ - private $setUpUserContext; - - /** - * @param CustomerDataProvider $customerDataProvider - * @param ChangeSubscriptionStatus $changeSubscriptionStatus - * @param SetUpUserContext $setUpUserContext - * @param CreateAccount $createAccount + * @param ExtractCustomerData $extractCustomerData + * @param CreateCustomerAccount $createCustomerAccount */ public function __construct( - CustomerDataProvider $customerDataProvider, - ChangeSubscriptionStatus $changeSubscriptionStatus, - SetUpUserContext $setUpUserContext, - CreateAccount $createAccount + ExtractCustomerData $extractCustomerData, + CreateCustomerAccount $createCustomerAccount ) { - $this->customerDataProvider = $customerDataProvider; - $this->changeSubscriptionStatus = $changeSubscriptionStatus; - $this->createAccount = $createAccount; - $this->setUpUserContext = $setUpUserContext; + $this->extractCustomerData = $extractCustomerData; + $this->createCustomerAccount = $createCustomerAccount; } /** @@ -74,22 +55,13 @@ public function resolve( if (!isset($args['input']) || !is_array($args['input']) || empty($args['input'])) { throw new GraphQlInputException(__('"input" value should be specified')); } - try { - $customer = $this->createAccount->execute($args); - $customerId = (int)$customer->getId(); - $this->setUpUserContext->execute($context, $customer); - if (array_key_exists('is_subscribed', $args['input'])) { - if ($args['input']['is_subscribed']) { - $this->changeSubscriptionStatus->execute($customerId, true); - } - } - $data = $this->customerDataProvider->getCustomerById($customerId); - } catch (ValidatorException $e) { - throw new GraphQlInputException(__($e->getMessage())); - } catch (InputMismatchException $e) { - throw new GraphQlInputException(__($e->getMessage())); - } + $customer = $this->createCustomerAccount->execute($args['input']); + + $context->setUserId((int)$customer->getId()); + $context->setUserType(UserContextInterface::USER_TYPE_CUSTOMER); + + $data = $this->extractCustomerData->execute($customer); return ['customer' => $data]; } } diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomerAddress.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomerAddress.php index 823444e5a2d7d..fd8122de961ee 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomerAddress.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomerAddress.php @@ -7,14 +7,9 @@ namespace Magento\CustomerGraphQl\Model\Resolver; -use Magento\Customer\Api\AddressRepositoryInterface; -use Magento\Customer\Api\Data\AddressInterfaceFactory; -use Magento\Customer\Api\Data\AddressInterface; -use Magento\CustomerGraphQl\Model\Customer\Address\CustomerAddressCreateDataValidator; -use Magento\CustomerGraphQl\Model\Customer\Address\CustomerAddressDataProvider; -use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccount; -use Magento\Framework\Api\DataObjectHelper; -use Magento\Framework\Exception\InputException; +use Magento\CustomerGraphQl\Model\Customer\Address\CreateCustomerAddress as CreateCustomerAddressModel; +use Magento\CustomerGraphQl\Model\Customer\Address\ExtractCustomerAddressData; +use Magento\CustomerGraphQl\Model\Customer\GetCustomer; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\GraphQl\Config\Element\Field; @@ -26,57 +21,33 @@ class CreateCustomerAddress implements ResolverInterface { /** - * @var CheckCustomerAccount + * @var GetCustomer */ - private $checkCustomerAccount; + private $getCustomer; /** - * @var AddressRepositoryInterface + * @var CreateCustomerAddressModel */ - private $addressRepository; + private $createCustomerAddress; /** - * @var AddressInterfaceFactory + * @var ExtractCustomerAddressData */ - private $addressInterfaceFactory; + private $extractCustomerAddressData; /** - * @var CustomerAddressDataProvider - */ - private $customerAddressDataProvider; - - /** - * @var DataObjectHelper - */ - private $dataObjectHelper; - - /** - * @var CustomerAddressCreateDataValidator - */ - private $customerAddressCreateDataValidator; - - /** - * @param CheckCustomerAccount $checkCustomerAccount - * @param AddressRepositoryInterface $addressRepository - * @param AddressInterfaceFactory $addressInterfaceFactory - * @param CustomerAddressDataProvider $customerAddressDataProvider - * @param DataObjectHelper $dataObjectHelper - * @param CustomerAddressCreateDataValidator $customerAddressCreateDataValidator + * @param GetCustomer $getCustomer + * @param CreateCustomerAddressModel $createCustomerAddress + * @param ExtractCustomerAddressData $extractCustomerAddressData */ public function __construct( - CheckCustomerAccount $checkCustomerAccount, - AddressRepositoryInterface $addressRepository, - AddressInterfaceFactory $addressInterfaceFactory, - CustomerAddressDataProvider $customerAddressDataProvider, - DataObjectHelper $dataObjectHelper, - CustomerAddressCreateDataValidator $customerAddressCreateDataValidator + GetCustomer $getCustomer, + CreateCustomerAddressModel $createCustomerAddress, + ExtractCustomerAddressData $extractCustomerAddressData ) { - $this->checkCustomerAccount = $checkCustomerAccount; - $this->addressRepository = $addressRepository; - $this->addressInterfaceFactory = $addressInterfaceFactory; - $this->customerAddressDataProvider = $customerAddressDataProvider; - $this->dataObjectHelper = $dataObjectHelper; - $this->customerAddressCreateDataValidator = $customerAddressCreateDataValidator; + $this->getCustomer = $getCustomer; + $this->createCustomerAddress = $createCustomerAddress; + $this->extractCustomerAddressData = $extractCustomerAddressData; } /** @@ -89,36 +60,13 @@ public function resolve( array $value = null, array $args = null ) { - $currentUserId = $context->getUserId(); - $currentUserType = $context->getUserType(); - - $this->checkCustomerAccount->execute($currentUserId, $currentUserType); - $this->customerAddressCreateDataValidator->validate($args['input']); - - $address = $this->createCustomerAddress((int)$currentUserId, $args['input']); - return $this->customerAddressDataProvider->getAddressData($address); - } + if (!isset($args['input']) || !is_array($args['input']) || empty($args['input'])) { + throw new GraphQlInputException(__('"input" value should be specified')); + } - /** - * Create customer address - * - * @param int $customerId - * @param array $addressData - * @return AddressInterface - * @throws GraphQlInputException - */ - private function createCustomerAddress(int $customerId, array $addressData) : AddressInterface - { - /** @var AddressInterface $address */ - $address = $this->addressInterfaceFactory->create(); - $this->dataObjectHelper->populateWithArray($address, $addressData, AddressInterface::class); - $address->setCustomerId($customerId); + $customer = $this->getCustomer->execute($context); - try { - $address = $this->addressRepository->save($address); - } catch (InputException $e) { - throw new GraphQlInputException(__($e->getMessage()), $e); - } - return $address; + $address = $this->createCustomerAddress->execute((int)$customer->getId(), $args['input']); + return $this->extractCustomerAddressData->execute($address); } } diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer.php index c3c78a1004da6..91048d4836c80 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer.php @@ -7,9 +7,9 @@ namespace Magento\CustomerGraphQl\Model\Resolver; -use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccount; +use Magento\CustomerGraphQl\Model\Customer\GetCustomer; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\CustomerGraphQl\Model\Customer\CustomerDataProvider; +use Magento\CustomerGraphQl\Model\Customer\ExtractCustomerData; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; @@ -19,25 +19,25 @@ class Customer implements ResolverInterface { /** - * @var CheckCustomerAccount + * @var GetCustomer */ - private $checkCustomerAccount; + private $getCustomer; /** - * @var CustomerDataProvider + * @var ExtractCustomerData */ - private $customerDataProvider; + private $extractCustomerData; /** - * @param CheckCustomerAccount $checkCustomerAccount - * @param CustomerDataProvider $customerDataProvider + * @param GetCustomer $getCustomer + * @param ExtractCustomerData $extractCustomerData */ public function __construct( - CheckCustomerAccount $checkCustomerAccount, - CustomerDataProvider $customerDataProvider + GetCustomer $getCustomer, + ExtractCustomerData $extractCustomerData ) { - $this->checkCustomerAccount = $checkCustomerAccount; - $this->customerDataProvider = $customerDataProvider; + $this->getCustomer = $getCustomer; + $this->extractCustomerData = $extractCustomerData; } /** @@ -50,13 +50,8 @@ public function resolve( array $value = null, array $args = null ) { - $currentUserId = $context->getUserId(); - $currentUserType = $context->getUserType(); + $customer = $this->getCustomer->execute($context); - $this->checkCustomerAccount->execute($currentUserId, $currentUserType); - - $currentUserId = (int)$currentUserId; - $data = $this->customerDataProvider->getCustomerById($currentUserId); - return $data; + return $this->extractCustomerData->execute($customer); } } diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerAddresses.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerAddresses.php new file mode 100644 index 0000000000000..e6e3887de423c --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerAddresses.php @@ -0,0 +1,71 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CustomerGraphQl\Model\Resolver; + +use Magento\Customer\Model\Customer; +use Magento\CustomerGraphQl\Model\Customer\GetCustomer; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\CustomerGraphQl\Model\Customer\Address\ExtractCustomerAddressData; + +/** + * Customers addresses field resolver + */ +class CustomerAddresses implements ResolverInterface +{ + /** + * @var GetCustomer + */ + private $getCustomer; + + /** + * @var ExtractCustomerAddressData + */ + private $extractCustomerAddressData; + + /** + * @param GetCustomer $getCustomer + * @param ExtractCustomerAddressData $extractCustomerAddressData + */ + public function __construct( + GetCustomer $getCustomer, + ExtractCustomerAddressData $extractCustomerAddressData + ) { + $this->getCustomer = $getCustomer; + $this->extractCustomerAddressData = $extractCustomerAddressData; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + if (!isset($value['model'])) { + throw new LocalizedException(__('"model" value should be specified')); + } + /** @var Customer $customer */ + $customer = $value['model']; + + $addressesData = []; + $addresses = $customer->getAddresses(); + + if (count($addresses)) { + foreach ($addresses as $address) { + $addressesData[] = $this->extractCustomerAddressData->execute($address); + } + } + return $addressesData; + } +} diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/DeleteCustomerAddress.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/DeleteCustomerAddress.php index 084857c84d5a4..08e82d930f268 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/DeleteCustomerAddress.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/DeleteCustomerAddress.php @@ -7,14 +7,13 @@ namespace Magento\CustomerGraphQl\Model\Resolver; -use Magento\Customer\Api\AddressRepositoryInterface; -use Magento\CustomerGraphQl\Model\Customer\Address\GetCustomerAddressForUser; -use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccount; +use Magento\CustomerGraphQl\Model\Customer\Address\DeleteCustomerAddress as DeleteCustomerAddressModel; +use Magento\CustomerGraphQl\Model\Customer\Address\GetCustomerAddress; +use Magento\CustomerGraphQl\Model\Customer\GetCustomer; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; -use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; -use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; /** * Customers address delete, used for GraphQL request processing. @@ -22,33 +21,33 @@ class DeleteCustomerAddress implements ResolverInterface { /** - * @var CheckCustomerAccount + * @var GetCustomer */ - private $checkCustomerAccount; + private $getCustomer; /** - * @var AddressRepositoryInterface + * @var GetCustomerAddress */ - private $addressRepository; + private $getCustomerAddress; /** - * @var GetCustomerAddressForUser + * @var DeleteCustomerAddressModel */ - private $getCustomerAddressForUser; + private $deleteCustomerAddress; /** - * @param CheckCustomerAccount $checkCustomerAccount - * @param AddressRepositoryInterface $addressRepository - * @param GetCustomerAddressForUser $getCustomerAddressForUser + * @param GetCustomer $getCustomer + * @param GetCustomerAddress $getCustomerAddress + * @param DeleteCustomerAddressModel $deleteCustomerAddress */ public function __construct( - CheckCustomerAccount $checkCustomerAccount, - AddressRepositoryInterface $addressRepository, - GetCustomerAddressForUser $getCustomerAddressForUser + GetCustomer $getCustomer, + GetCustomerAddress $getCustomerAddress, + DeleteCustomerAddressModel $deleteCustomerAddress ) { - $this->checkCustomerAccount = $checkCustomerAccount; - $this->addressRepository = $addressRepository; - $this->getCustomerAddressForUser = $getCustomerAddressForUser; + $this->getCustomer = $getCustomer; + $this->getCustomerAddress = $getCustomerAddress; + $this->deleteCustomerAddress = $deleteCustomerAddress; } /** @@ -61,36 +60,14 @@ public function resolve( array $value = null, array $args = null ) { - $currentUserId = $context->getUserId(); - $currentUserType = $context->getUserType(); - - $this->checkCustomerAccount->execute($currentUserId, $currentUserType); + if (!isset($args['id']) || empty($args['id'])) { + throw new GraphQlInputException(__('Address "id" value should be specified')); + } - return $this->deleteCustomerAddress((int)$currentUserId, (int)$args['id']); - } + $customer = $this->getCustomer->execute($context); + $address = $this->getCustomerAddress->execute((int)$args['id'], (int)$customer->getId()); - /** - * Delete customer address - * - * @param int $customerId - * @param int $addressId - * @return bool - * @throws GraphQlAuthorizationException - * @throws GraphQlNoSuchEntityException - */ - private function deleteCustomerAddress($customerId, $addressId) - { - $address = $this->getCustomerAddressForUser->execute($addressId, $customerId); - if ($address->isDefaultBilling()) { - throw new GraphQlAuthorizationException( - __('Customer Address %1 is set as default billing address and can not be deleted', [$addressId]) - ); - } - if ($address->isDefaultShipping()) { - throw new GraphQlAuthorizationException( - __('Customer Address %1 is set as default shipping address and can not be deleted', [$addressId]) - ); - } - return $this->addressRepository->delete($address); + $this->deleteCustomerAddress->execute($address); + return true; } } diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/IsEmailAvailable.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/IsEmailAvailable.php index 11ad0f77f8949..ddf1aec275ece 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/IsEmailAvailable.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/IsEmailAvailable.php @@ -8,6 +8,7 @@ namespace Magento\CustomerGraphQl\Model\Resolver; use Magento\Customer\Api\AccountManagementInterface; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; @@ -42,11 +43,15 @@ public function resolve( array $value = null, array $args = null ) { - $email = $args['email'] ?? null; - if (!$email) { + if (!isset($args['email']) || empty($args['email'])) { throw new GraphQlInputException(__('"Email should be specified')); } - $isEmailAvailable = $this->accountManagement->isEmailAvailable($email); + + try { + $isEmailAvailable = $this->accountManagement->isEmailAvailable($args['email']); + } catch (LocalizedException $e) { + throw new GraphQlInputException(__($e->getMessage()), $e); + } return [ 'is_email_available' => $isEmailAvailable diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php index c0bd864b3ee09..fc5691d97cbfe 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php @@ -7,7 +7,7 @@ namespace Magento\CustomerGraphQl\Model\Resolver; -use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccount; +use Magento\CustomerGraphQl\Model\Customer\GetCustomer; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; @@ -19,9 +19,9 @@ class IsSubscribed implements ResolverInterface { /** - * @var CheckCustomerAccount + * @var GetCustomer */ - private $checkCustomerAccount; + private $getCustomer; /** * @var SubscriberFactory @@ -29,14 +29,14 @@ class IsSubscribed implements ResolverInterface private $subscriberFactory; /** - * @param CheckCustomerAccount $checkCustomerAccount + * @param GetCustomer $getCustomer * @param SubscriberFactory $subscriberFactory */ public function __construct( - CheckCustomerAccount $checkCustomerAccount, + GetCustomer $getCustomer, SubscriberFactory $subscriberFactory ) { - $this->checkCustomerAccount = $checkCustomerAccount; + $this->getCustomer = $getCustomer; $this->subscriberFactory = $subscriberFactory; } @@ -50,12 +50,9 @@ public function resolve( array $value = null, array $args = null ) { - $currentUserId = $context->getUserId(); - $currentUserType = $context->getUserType(); + $customer = $this->getCustomer->execute($context); - $this->checkCustomerAccount->execute($currentUserId, $currentUserType); - - $status = $this->subscriberFactory->create()->loadByCustomerId((int)$currentUserId)->isSubscribed(); + $status = $this->subscriberFactory->create()->loadByCustomerId((int)$customer->getId())->isSubscribed(); return (bool)$status; } } diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/RevokeCustomerToken.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/RevokeCustomerToken.php index 3301162dc0088..92779597e5afa 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/RevokeCustomerToken.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/RevokeCustomerToken.php @@ -7,7 +7,7 @@ namespace Magento\CustomerGraphQl\Model\Resolver; -use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccount; +use Magento\CustomerGraphQl\Model\Customer\GetCustomer; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; @@ -19,9 +19,9 @@ class RevokeCustomerToken implements ResolverInterface { /** - * @var CheckCustomerAccount + * @var GetCustomer */ - private $checkCustomerAccount; + private $getCustomer; /** * @var CustomerTokenServiceInterface @@ -29,14 +29,14 @@ class RevokeCustomerToken implements ResolverInterface private $customerTokenService; /** - * @param CheckCustomerAccount $checkCustomerAccount + * @param GetCustomer $getCustomer * @param CustomerTokenServiceInterface $customerTokenService */ public function __construct( - CheckCustomerAccount $checkCustomerAccount, + GetCustomer $getCustomer, CustomerTokenServiceInterface $customerTokenService ) { - $this->checkCustomerAccount = $checkCustomerAccount; + $this->getCustomer = $getCustomer; $this->customerTokenService = $customerTokenService; } @@ -50,11 +50,8 @@ public function resolve( array $value = null, array $args = null ) { - $currentUserId = $context->getUserId(); - $currentUserType = $context->getUserType(); + $customer = $this->getCustomer->execute($context); - $this->checkCustomerAccount->execute($currentUserId, $currentUserType); - - return ['result' => $this->customerTokenService->revokeCustomerAccessToken((int)$currentUserId)]; + return ['result' => $this->customerTokenService->revokeCustomerAccessToken((int)$customer->getId())]; } } diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomer.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomer.php index 50760d2e2e31c..7e06a2a063b4b 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomer.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomer.php @@ -7,12 +7,11 @@ namespace Magento\CustomerGraphQl\Model\Resolver; -use Magento\CustomerGraphQl\Model\Customer\ChangeSubscriptionStatus; -use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccount; -use Magento\CustomerGraphQl\Model\Customer\UpdateCustomerData; +use Magento\CustomerGraphQl\Model\Customer\GetCustomer; +use Magento\CustomerGraphQl\Model\Customer\UpdateCustomerAccount; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\CustomerGraphQl\Model\Customer\CustomerDataProvider; +use Magento\CustomerGraphQl\Model\Customer\ExtractCustomerData; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; @@ -22,41 +21,33 @@ class UpdateCustomer implements ResolverInterface { /** - * @var CheckCustomerAccount + * @var GetCustomer */ - private $checkCustomerAccount; + private $getCustomer; /** - * @var UpdateCustomerData + * @var UpdateCustomerAccount */ - private $updateCustomerData; + private $updateCustomerAccount; /** - * @var ChangeSubscriptionStatus + * @var ExtractCustomerData */ - private $changeSubscriptionStatus; + private $extractCustomerData; /** - * @var CustomerDataProvider - */ - private $customerDataProvider; - - /** - * @param CheckCustomerAccount $checkCustomerAccount - * @param UpdateCustomerData $updateCustomerData - * @param ChangeSubscriptionStatus $changeSubscriptionStatus - * @param CustomerDataProvider $customerDataProvider + * @param GetCustomer $getCustomer + * @param UpdateCustomerAccount $updateCustomerAccount + * @param ExtractCustomerData $extractCustomerData */ public function __construct( - CheckCustomerAccount $checkCustomerAccount, - UpdateCustomerData $updateCustomerData, - ChangeSubscriptionStatus $changeSubscriptionStatus, - CustomerDataProvider $customerDataProvider + GetCustomer $getCustomer, + UpdateCustomerAccount $updateCustomerAccount, + ExtractCustomerData $extractCustomerData ) { - $this->checkCustomerAccount = $checkCustomerAccount; - $this->updateCustomerData = $updateCustomerData; - $this->changeSubscriptionStatus = $changeSubscriptionStatus; - $this->customerDataProvider = $customerDataProvider; + $this->getCustomer = $getCustomer; + $this->updateCustomerAccount = $updateCustomerAccount; + $this->extractCustomerData = $extractCustomerData; } /** @@ -73,19 +64,10 @@ public function resolve( throw new GraphQlInputException(__('"input" value should be specified')); } - $currentUserId = $context->getUserId(); - $currentUserType = $context->getUserType(); - - $this->checkCustomerAccount->execute($currentUserId, $currentUserType); - - $currentUserId = (int)$currentUserId; - $this->updateCustomerData->execute($currentUserId, $args['input']); - - if (isset($args['input']['is_subscribed'])) { - $this->changeSubscriptionStatus->execute($currentUserId, (bool)$args['input']['is_subscribed']); - } + $customer = $this->getCustomer->execute($context); + $this->updateCustomerAccount->execute($customer, $args['input']); - $data = $this->customerDataProvider->getCustomerById($currentUserId); + $data = $this->extractCustomerData->execute($customer); return ['customer' => $data]; } } diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomerAddress.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomerAddress.php index 833ab2e450280..bf41b7ddd10c9 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomerAddress.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomerAddress.php @@ -7,13 +7,11 @@ namespace Magento\CustomerGraphQl\Model\Resolver; -use Magento\Customer\Api\AddressRepositoryInterface; -use Magento\Customer\Api\Data\AddressInterface; -use Magento\CustomerGraphQl\Model\Customer\Address\CustomerAddressDataProvider; -use Magento\CustomerGraphQl\Model\Customer\Address\CustomerAddressUpdateDataValidator; -use Magento\CustomerGraphQl\Model\Customer\Address\GetCustomerAddressForUser; -use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccount; -use Magento\Framework\Api\DataObjectHelper; +use Magento\CustomerGraphQl\Model\Customer\Address\ExtractCustomerAddressData; +use Magento\CustomerGraphQl\Model\Customer\Address\GetCustomerAddress; +use Magento\CustomerGraphQl\Model\Customer\Address\UpdateCustomerAddress as UpdateCustomerAddressModel; +use Magento\CustomerGraphQl\Model\Customer\GetCustomer; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; @@ -24,57 +22,41 @@ class UpdateCustomerAddress implements ResolverInterface { /** - * @var CheckCustomerAccount + * @var GetCustomer */ - private $checkCustomerAccount; + private $getCustomer; /** - * @var AddressRepositoryInterface + * @var GetCustomerAddress */ - private $addressRepository; + private $getCustomerAddress; /** - * @var CustomerAddressDataProvider + * @var UpdateCustomerAddressModel */ - private $customerAddressDataProvider; + private $updateCustomerAddress; /** - * @var DataObjectHelper + * @var ExtractCustomerAddressData */ - private $dataObjectHelper; + private $extractCustomerAddressData; /** - * @var CustomerAddressUpdateDataValidator - */ - private $customerAddressUpdateDataValidator; - - /** - * @var GetCustomerAddressForUser - */ - private $getCustomerAddressForUser; - - /** - * @param CheckCustomerAccount $checkCustomerAccount - * @param AddressRepositoryInterface $addressRepository - * @param CustomerAddressDataProvider $customerAddressDataProvider - * @param DataObjectHelper $dataObjectHelper - * @param CustomerAddressUpdateDataValidator $customerAddressUpdateDataValidator - * @param GetCustomerAddressForUser $getCustomerAddressForUser + * @param GetCustomer $getCustomer + * @param GetCustomerAddress $getCustomerAddress + * @param UpdateCustomerAddressModel $updateCustomerAddress + * @param ExtractCustomerAddressData $extractCustomerAddressData */ public function __construct( - CheckCustomerAccount $checkCustomerAccount, - AddressRepositoryInterface $addressRepository, - CustomerAddressDataProvider $customerAddressDataProvider, - DataObjectHelper $dataObjectHelper, - CustomerAddressUpdateDataValidator $customerAddressUpdateDataValidator, - GetCustomerAddressForUser $getCustomerAddressForUser + GetCustomer $getCustomer, + GetCustomerAddress $getCustomerAddress, + UpdateCustomerAddressModel $updateCustomerAddress, + ExtractCustomerAddressData $extractCustomerAddressData ) { - $this->checkCustomerAccount = $checkCustomerAccount; - $this->addressRepository = $addressRepository; - $this->customerAddressDataProvider = $customerAddressDataProvider; - $this->dataObjectHelper = $dataObjectHelper; - $this->customerAddressUpdateDataValidator = $customerAddressUpdateDataValidator; - $this->getCustomerAddressForUser = $getCustomerAddressForUser; + $this->getCustomer = $getCustomer; + $this->getCustomerAddress = $getCustomerAddress; + $this->updateCustomerAddress = $updateCustomerAddress; + $this->extractCustomerAddressData = $extractCustomerAddressData; } /** @@ -87,32 +69,18 @@ public function resolve( array $value = null, array $args = null ) { - $currentUserId = $context->getUserId(); - $currentUserType = $context->getUserType(); - - $this->checkCustomerAccount->execute($currentUserId, $currentUserType); - $this->customerAddressUpdateDataValidator->validate($args['input']); - - $address = $this->updateCustomerAddress((int)$currentUserId, (int)$args['id'], $args['input']); - return $this->customerAddressDataProvider->getAddressData($address); - } + if (!isset($args['id']) || empty($args['id'])) { + throw new GraphQlInputException(__('Address "id" value should be specified')); + } - /** - * Update customer address - * - * @param int $customerId - * @param int $addressId - * @param array $addressData - * @return AddressInterface - */ - private function updateCustomerAddress(int $customerId, int $addressId, array $addressData) - { - $address = $this->getCustomerAddressForUser->execute($addressId, $customerId); - $this->dataObjectHelper->populateWithArray($address, $addressData, AddressInterface::class); - if (isset($addressData['region']['region_id'])) { - $address->setRegionId($address->getRegion()->getRegionId()); + if (!isset($args['input']) || !is_array($args['input']) || empty($args['input'])) { + throw new GraphQlInputException(__('"input" value should be specified')); } - return $this->addressRepository->save($address); + $customer = $this->getCustomer->execute($context); + $address = $this->getCustomerAddress->execute((int)$args['id'], (int)$customer->getId()); + + $this->updateCustomerAddress->execute($address, $args['input']); + return $this->extractCustomerAddressData->execute($address); } } diff --git a/app/code/Magento/CustomerGraphQl/Test/Mftf/README.md b/app/code/Magento/CustomerGraphQl/Test/Mftf/README.md deleted file mode 100644 index ae023224f4d9b..0000000000000 --- a/app/code/Magento/CustomerGraphQl/Test/Mftf/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Customer Graph Ql Functional Tests - -The Functional Test Module for **Magento Customer Graph Ql** module. diff --git a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls index c5860005c6e0e..4e4fd1d0fa8ad 100644 --- a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls @@ -91,7 +91,7 @@ type Customer @doc(description: "Customer defines the customer name and address taxvat: String @doc(description: "The customer's Tax/VAT number (for corporate customers)") id: Int @doc(description: "The ID assigned to the customer") is_subscribed: Boolean @doc(description: "Indicates whether the customer is subscribed to the company's newsletter") @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\IsSubscribed") - addresses: [CustomerAddress] @doc(description: "An array containing the customer's shipping and billing addresses") + addresses: [CustomerAddress] @doc(description: "An array containing the customer's shipping and billing addresses") @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\CustomerAddresses") gender: Int @doc(description: "The customer's gender(Male - 1, Female - 2)") } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractDataFromAddress.php b/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractQuoteAddressData.php similarity index 96% rename from app/code/Magento/QuoteGraphQl/Model/Cart/ExtractDataFromAddress.php rename to app/code/Magento/QuoteGraphQl/Model/Cart/ExtractQuoteAddressData.php index 20212d3412595..840dedb4f274e 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractDataFromAddress.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractQuoteAddressData.php @@ -13,9 +13,9 @@ use Magento\Quote\Model\Quote\Address as QuoteAddress; /** - * Extract the necessary address fields from an Address model + * Extract address fields from an Quote Address model */ -class ExtractDataFromAddress +class ExtractQuoteAddressData { /** * @var ExtensibleDataObjectConverter diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index cf277c729cdfd..0aafe0e7882d7 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -7,7 +7,8 @@ namespace Magento\QuoteGraphQl\Model\Cart; -use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccount; +use Magento\CustomerGraphQl\Model\Customer\Address\GetCustomerAddress; +use Magento\CustomerGraphQl\Model\Customer\GetCustomer; use Magento\Framework\GraphQl\Exception\GraphQlAuthenticationException; use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; @@ -26,9 +27,9 @@ class SetBillingAddressOnCart private $quoteAddressFactory; /** - * @var CheckCustomerAccount + * @var GetCustomer */ - private $checkCustomerAccount; + private $getCustomer; /** * @var GetCustomerAddress @@ -42,18 +43,18 @@ class SetBillingAddressOnCart /** * @param QuoteAddressFactory $quoteAddressFactory - * @param CheckCustomerAccount $checkCustomerAccount + * @param GetCustomer $getCustomer * @param GetCustomerAddress $getCustomerAddress * @param AssignBillingAddressToCart $assignBillingAddressToCart */ public function __construct( QuoteAddressFactory $quoteAddressFactory, - CheckCustomerAccount $checkCustomerAccount, + GetCustomer $getCustomer, GetCustomerAddress $getCustomerAddress, AssignBillingAddressToCart $assignBillingAddressToCart ) { $this->quoteAddressFactory = $quoteAddressFactory; - $this->checkCustomerAccount = $checkCustomerAccount; + $this->getCustomer = $getCustomer; $this->getCustomerAddress = $getCustomerAddress; $this->assignBillingAddressToCart = $assignBillingAddressToCart; } @@ -99,8 +100,8 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b if (null === $customerAddressId) { $billingAddress = $this->quoteAddressFactory->createBasedOnInputData($addressInput); } else { - $this->checkCustomerAccount->execute($context->getUserId(), $context->getUserType()); - $customerAddress = $this->getCustomerAddress->execute((int)$customerAddressId, (int)$context->getUserId()); + $customer = $this->getCustomer->execute($context); + $customerAddress = $this->getCustomerAddress->execute((int)$customerAddressId, (int)$customer->getId()); $billingAddress = $this->quoteAddressFactory->createBasedOnCustomerAddress($customerAddress); } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php index 8e54ab0d3feef..bbca7721754cb 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php @@ -7,7 +7,8 @@ namespace Magento\QuoteGraphQl\Model\Cart; -use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccount; +use Magento\CustomerGraphQl\Model\Customer\Address\GetCustomerAddress; +use Magento\CustomerGraphQl\Model\Customer\GetCustomer; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; use Magento\Quote\Api\Data\CartInterface; @@ -23,9 +24,9 @@ class SetShippingAddressesOnCart implements SetShippingAddressesOnCartInterface private $quoteAddressFactory; /** - * @var CheckCustomerAccount + * @var GetCustomer */ - private $checkCustomerAccount; + private $getCustomer; /** * @var GetCustomerAddress @@ -39,18 +40,18 @@ class SetShippingAddressesOnCart implements SetShippingAddressesOnCartInterface /** * @param QuoteAddressFactory $quoteAddressFactory - * @param CheckCustomerAccount $checkCustomerAccount + * @param GetCustomer $getCustomer * @param GetCustomerAddress $getCustomerAddress * @param AssignShippingAddressToCart $assignShippingAddressToCart */ public function __construct( QuoteAddressFactory $quoteAddressFactory, - CheckCustomerAccount $checkCustomerAccount, + GetCustomer $getCustomer, GetCustomerAddress $getCustomerAddress, AssignShippingAddressToCart $assignShippingAddressToCart ) { $this->quoteAddressFactory = $quoteAddressFactory; - $this->checkCustomerAccount = $checkCustomerAccount; + $this->getCustomer = $getCustomer; $this->getCustomerAddress = $getCustomerAddress; $this->assignShippingAddressToCart = $assignShippingAddressToCart; } @@ -84,8 +85,8 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s if (null === $customerAddressId) { $shippingAddress = $this->quoteAddressFactory->createBasedOnInputData($addressInput); } else { - $this->checkCustomerAccount->execute($context->getUserId(), $context->getUserType()); - $customerAddress = $this->getCustomerAddress->execute((int)$customerAddressId, (int)$context->getUserId()); + $customer = $this->getCustomer->execute($context); + $customerAddress = $this->getCustomerAddress->execute((int)$customerAddressId, (int)$customer->getId()); $shippingAddress = $this->quoteAddressFactory->createBasedOnCustomerAddress($customerAddress); } diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/BillingAddress.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/BillingAddress.php index a03533ecefffa..a6bb0b0d04df1 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/BillingAddress.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/BillingAddress.php @@ -11,7 +11,8 @@ use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\QuoteGraphQl\Model\Cart\ExtractDataFromAddress; +use Magento\Quote\Api\Data\CartInterface; +use Magento\QuoteGraphQl\Model\Cart\ExtractQuoteAddressData; /** * @inheritdoc @@ -19,16 +20,16 @@ class BillingAddress implements ResolverInterface { /** - * @var ExtractDataFromAddress + * @var ExtractQuoteAddressData */ - private $extractDataFromAddress; + private $extractQuoteAddressData; /** - * @param ExtractDataFromAddress $extractDataFromAddress + * @param ExtractQuoteAddressData $extractQuoteAddressData */ - public function __construct(ExtractDataFromAddress $extractDataFromAddress) + public function __construct(ExtractQuoteAddressData $extractQuoteAddressData) { - $this->extractDataFromAddress = $extractDataFromAddress; + $this->extractQuoteAddressData = $extractQuoteAddressData; } /** @@ -39,6 +40,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value if (!isset($value['model'])) { throw new LocalizedException(__('"model" value should be specified')); } + /** @var CartInterface $cart */ $cart = $value['model']; $billingAddress = $cart->getBillingAddress(); @@ -46,7 +48,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value return null; } - $addressData = $this->extractDataFromAddress->execute($billingAddress); + $addressData = $this->extractQuoteAddressData->execute($billingAddress); return $addressData; } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddresses.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddresses.php index 3a55ef9ae25a8..eb3b0966740eb 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddresses.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddresses.php @@ -11,7 +11,8 @@ use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\QuoteGraphQl\Model\Cart\ExtractDataFromAddress; +use Magento\Quote\Model\Quote; +use Magento\QuoteGraphQl\Model\Cart\ExtractQuoteAddressData; /** * @inheritdoc @@ -19,16 +20,16 @@ class ShippingAddresses implements ResolverInterface { /** - * @var ExtractDataFromAddress + * @var ExtractQuoteAddressData */ - private $extractDataFromAddress; + private $extractQuoteAddressData; /** - * @param ExtractDataFromAddress $extractDataFromAddress + * @param ExtractQuoteAddressData $extractQuoteAddressData */ - public function __construct(ExtractDataFromAddress $extractDataFromAddress) + public function __construct(ExtractQuoteAddressData $extractQuoteAddressData) { - $this->extractDataFromAddress = $extractDataFromAddress; + $this->extractQuoteAddressData = $extractQuoteAddressData; } /** @@ -39,6 +40,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value if (!isset($value['model'])) { throw new LocalizedException(__('"model" value should be specified')); } + /** @var Quote $cart */ $cart = $value['model']; $addressesData = []; @@ -46,7 +48,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value if (count($shippingAddresses)) { foreach ($shippingAddresses as $shippingAddress) { - $addressesData[] = $this->extractDataFromAddress->execute($shippingAddress); + $addressesData[] = $this->extractQuoteAddressData->execute($shippingAddress); } } return $addressesData; diff --git a/app/code/Magento/SalesGraphQl/Model/Resolver/Orders.php b/app/code/Magento/SalesGraphQl/Model/Resolver/Orders.php index 5802115d44b5e..3e592cf061dfc 100644 --- a/app/code/Magento/SalesGraphQl/Model/Resolver/Orders.php +++ b/app/code/Magento/SalesGraphQl/Model/Resolver/Orders.php @@ -11,7 +11,7 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Sales\Model\ResourceModel\Order\CollectionFactoryInterface; -use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccount; +use Magento\CustomerGraphQl\Model\Customer\GetCustomer; /** * Orders data reslover @@ -24,20 +24,20 @@ class Orders implements ResolverInterface private $collectionFactory; /** - * @var CheckCustomerAccount + * @var GetCustomer */ - private $checkCustomerAccount; + private $getCustomer; /** * @param CollectionFactoryInterface $collectionFactory - * @param CheckCustomerAccount $checkCustomerAccount + * @param GetCustomer $getCustomer */ public function __construct( CollectionFactoryInterface $collectionFactory, - CheckCustomerAccount $checkCustomerAccount + GetCustomer $getCustomer ) { $this->collectionFactory = $collectionFactory; - $this->checkCustomerAccount = $checkCustomerAccount; + $this->getCustomer = $getCustomer; } /** @@ -50,11 +50,10 @@ public function resolve( array $value = null, array $args = null ) { - $customerId = $context->getUserId(); - $this->checkCustomerAccount->execute($customerId, $context->getUserType()); + $customer = $this->getCustomer->execute($context); $items = []; - $orders = $this->collectionFactory->create($customerId); + $orders = $this->collectionFactory->create($customer->getId()); /** @var \Magento\Sales\Model\Order $order */ foreach ($orders as $order) { diff --git a/app/code/Magento/VaultGraphQl/Model/Resolver/DeletePaymentToken.php b/app/code/Magento/VaultGraphQl/Model/Resolver/DeletePaymentToken.php index 696dbf166fc38..cbdbbdcf010b6 100644 --- a/app/code/Magento/VaultGraphQl/Model/Resolver/DeletePaymentToken.php +++ b/app/code/Magento/VaultGraphQl/Model/Resolver/DeletePaymentToken.php @@ -14,7 +14,7 @@ use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Vault\Api\PaymentTokenManagementInterface; use Magento\Vault\Api\PaymentTokenRepositoryInterface; -use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccount; +use Magento\CustomerGraphQl\Model\Customer\GetCustomer; /** * Delete Payment Token resolver, used for GraphQL mutation processing. @@ -22,9 +22,9 @@ class DeletePaymentToken implements ResolverInterface { /** - * @var CheckCustomerAccount + * @var GetCustomer */ - private $checkCustomerAccount; + private $getCustomer; /** * @var PaymentTokenManagementInterface @@ -37,16 +37,16 @@ class DeletePaymentToken implements ResolverInterface private $paymentTokenRepository; /** - * @param CheckCustomerAccount $checkCustomerAccount + * @param GetCustomer $getCustomer * @param PaymentTokenManagementInterface $paymentTokenManagement * @param PaymentTokenRepositoryInterface $paymentTokenRepository */ public function __construct( - CheckCustomerAccount $checkCustomerAccount, + GetCustomer $getCustomer, PaymentTokenManagementInterface $paymentTokenManagement, PaymentTokenRepositoryInterface $paymentTokenRepository ) { - $this->checkCustomerAccount = $checkCustomerAccount; + $this->getCustomer = $getCustomer; $this->paymentTokenManagement = $paymentTokenManagement; $this->paymentTokenRepository = $paymentTokenRepository; } @@ -65,12 +65,9 @@ public function resolve( throw new GraphQlInputException(__('Specify the "public_hash" value.')); } - $currentUserId = $context->getUserId(); - $currentUserType = $context->getUserType(); + $customer = $this->getCustomer->execute($context); - $this->checkCustomerAccount->execute($currentUserId, $currentUserType); - - $token = $this->paymentTokenManagement->getByPublicHash($args['public_hash'], $currentUserId); + $token = $this->paymentTokenManagement->getByPublicHash($args['public_hash'], $customer->getId()); if (!$token) { throw new GraphQlNoSuchEntityException( __('Could not find a token using public hash: %1', $args['public_hash']) diff --git a/app/code/Magento/VaultGraphQl/Model/Resolver/PaymentTokens.php b/app/code/Magento/VaultGraphQl/Model/Resolver/PaymentTokens.php index 80e81037bb5dd..1563eaedf6b9b 100644 --- a/app/code/Magento/VaultGraphQl/Model/Resolver/PaymentTokens.php +++ b/app/code/Magento/VaultGraphQl/Model/Resolver/PaymentTokens.php @@ -11,7 +11,7 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Vault\Model\PaymentTokenManagement; -use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccount; +use Magento\CustomerGraphQl\Model\Customer\GetCustomer; /** * Customers Payment Tokens resolver, used for GraphQL request processing. @@ -24,20 +24,20 @@ class PaymentTokens implements ResolverInterface private $paymentTokenManagement; /** - * @var CheckCustomerAccount + * @var GetCustomer */ - private $checkCustomerAccount; + private $getCustomer; /** * @param PaymentTokenManagement $paymentTokenManagement - * @param CheckCustomerAccount $checkCustomerAccount + * @param GetCustomer $getCustomer */ public function __construct( PaymentTokenManagement $paymentTokenManagement, - CheckCustomerAccount $checkCustomerAccount + GetCustomer $getCustomer ) { $this->paymentTokenManagement = $paymentTokenManagement; - $this->checkCustomerAccount = $checkCustomerAccount; + $this->getCustomer = $getCustomer; } /** @@ -50,12 +50,9 @@ public function resolve( array $value = null, array $args = null ) { - $currentUserId = $context->getUserId(); - $currentUserType = $context->getUserType(); + $customer = $this->getCustomer->execute($context); - $this->checkCustomerAccount->execute($currentUserId, $currentUserType); - - $tokens = $this->paymentTokenManagement->getVisibleAvailableTokens($currentUserId); + $tokens = $this->paymentTokenManagement->getVisibleAvailableTokens($customer->getId()); $result = []; foreach ($tokens as $token) { @@ -66,7 +63,6 @@ public function resolve( 'details' => $token->getTokenDetails(), ]; } - return ['items' => $result]; } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/ChangeCustomerPasswordTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/ChangeCustomerPasswordTest.php index 66b171740ccab..84c111bd25fd4 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/ChangeCustomerPasswordTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/ChangeCustomerPasswordTest.php @@ -77,7 +77,6 @@ public function testChangePasswordIfUserIsNotAuthorizedTest() */ public function testChangeWeakPassword() { - $this->markTestIncomplete('https://github.com/magento/graphql-ce/issues/190'); $customerEmail = 'customer@example.com'; $oldCustomerPassword = 'password'; $newCustomerPassword = 'weakpass'; diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/DeleteCustomerAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/DeleteCustomerAddressTest.php index ba0232020298f..1153b9662b41a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/DeleteCustomerAddressTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/DeleteCustomerAddressTest.php @@ -132,7 +132,7 @@ public function testDeleteDefaultBillingCustomerAddress() * @magentoApiDataFixture Magento/Customer/_files/customer.php * * @expectedException \Exception - * @expectedExceptionMessage Address id 9999 does not exist. + * @expectedExceptionMessage Could not find a address with ID "9999" */ public function testDeleteNonExistCustomerAddress() { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerTest.php index ee8fabc43c901..df45e1de771d9 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerTest.php @@ -94,8 +94,7 @@ public function testUpdateCustomer() $this->assertEquals($newMiddlename, $response['updateCustomer']['customer']['middlename']); $this->assertEquals($newLastname, $response['updateCustomer']['customer']['lastname']); $this->assertEquals($newSuffix, $response['updateCustomer']['customer']['suffix']); - $newDobDate = new \DateTime($newDob); - $this->assertEquals($newDobDate->format('Y-m-d'), $response['updateCustomer']['customer']['dob']); + $this->assertEquals($newDob, $response['updateCustomer']['customer']['dob']); $this->assertEquals($newTaxVat, $response['updateCustomer']['customer']['taxvat']); $this->assertEquals($newEmail, $response['updateCustomer']['customer']['email']); $this->assertEquals($newGender, $response['updateCustomer']['customer']['gender']); 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 1a93c011e80a8..d37e04838ab37 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 @@ -376,7 +376,7 @@ public function testSetBillingAddressToAnotherCustomerCart() * @magentoApiDataFixture Magento/Customer/_files/customer_address.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @expectedException \Exception - * @expectedExceptionMessage The current user cannot use address with ID "1" + * @expectedExceptionMessage Current customer does not have permission to address with ID "1" */ public function testSetBillingAddressIfCustomerIsNotOwnerOfAddress() { 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 4916bb2aa78c3..00332284d16df 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php @@ -428,7 +428,7 @@ public function testSetMultipleNewShippingAddresses() * @magentoApiDataFixture Magento/Customer/_files/customer_address.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @expectedException \Exception - * @expectedExceptionMessage The current user cannot use address with ID "1" + * @expectedExceptionMessage Current customer does not have permission to address with ID "1" */ public function testSetShippingAddressIfCustomerIsNotOwnerOfAddress() { From 07c0fbecda260f97ba4c8eb5298ff99ed63e12a7 Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Mon, 11 Mar 2019 10:57:54 +0200 Subject: [PATCH 196/345] Test coverage: Add product to Cart with wrong cart hash --- .../Quote/AddSimpleProductToCartTest.php | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) 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 d5bdb942e9b2c..34e73070945d6 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartTest.php @@ -81,6 +81,45 @@ public function testAddProductIfQuantityIsNotAvailable() } } } +QUERY; + + $this->graphQlQuery($query); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/products.php + * @expectedException \Exception + * @expectedExceptionMessage Could not find a cart with ID "wrong_cart_hash" + */ + public function testAddProductWithWrongCartHash() + { + $sku = 'simple'; + $qty = 1; + + $maskedQuoteId = 'wrong_cart_hash'; + + $query = <<<QUERY +mutation { + addSimpleProductsToCart( + input: { + cart_id: "{$maskedQuoteId}" + cartItems: [ + { + data: { + qty: $qty + sku: "$sku" + } + } + ] + } + ) { + cart { + items { + qty + } + } + } +} QUERY; $this->graphQlQuery($query); From 348eeb6d5769882b91c90cc0fe40c49c95af9279 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Mon, 11 Mar 2019 12:30:48 +0200 Subject: [PATCH 197/345] ENGCOM-4438: Static test fix. --- .../Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php index c8edefa00cc86..c8e2d356b3ffb 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Generate.php @@ -7,13 +7,15 @@ namespace Magento\Sitemap\Controller\Adminhtml\Sitemap; use Magento\Backend\App\Action; +use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Sitemap\Controller\Adminhtml\Sitemap; use Magento\Store\Model\App\Emulation; use Magento\Framework\App\ObjectManager; /** * Generate sitemap file */ -class Generate extends \Magento\Sitemap\Controller\Adminhtml\Sitemap +class Generate extends Sitemap implements HttpGetActionInterface { /** @var \Magento\Store\Model\App\Emulation $appEmulation */ private $appEmulation; From f46d5b0fe00356cb2d16ab5cffe8fd9eebd86fdb Mon Sep 17 00:00:00 2001 From: Ansari <ziyaurrahman@krishtechnolabs.com> Date: Mon, 11 Mar 2019 16:29:05 +0530 Subject: [PATCH 198/345] Spelling Correction --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b86c7b79a0cbd..671071828b494 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -489,7 +489,7 @@ Tests: * Fixed an issue where found records in global search in Backend could not be selected via keyboard * Fixed an issue where Category menu items went out of screen when page side was reached * Fixed an issue where subcategories in menu were shown instantly when user moved mouse quickly - * Fixed an issue where popup header was our of window range while creating group product + * Fixed an issue where popup header was out of window range while creating group product * Fixed an issue where region field was absent in customer address form on backend for "United Kingdom" country * Fixed an ability to edit the Order from Admin panel * Fixed an issue where email could not be retrieved from \Magento\Quote\Api\Data\AddressInterface after adding an address on OnePageCheckout From c26ce0e8290bfffe9e4257355be106f8b0e7b60e Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <d.cheshun@atwix.com> Date: Mon, 11 Mar 2019 13:07:44 +0200 Subject: [PATCH 199/345] Fix static test --- .../Adminhtml/Product/Attribute/Edit/Tab/Advanced.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Advanced.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Advanced.php index 564969d9bb913..1b6756968662f 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Advanced.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Advanced.php @@ -4,11 +4,6 @@ * See COPYING.txt for license details. */ -/** - * Product attribute add/edit form main tab - * - * @author Magento Core Team <core@magentocommerce.com> - */ namespace Magento\Catalog\Block\Adminhtml\Product\Attribute\Edit\Tab; use Magento\Backend\Block\Widget\Form\Generic; @@ -18,6 +13,8 @@ use Magento\Framework\App\ObjectManager; /** + * Product attribute add/edit form main tab + * * @api * @since 100.0.2 */ @@ -73,6 +70,7 @@ public function __construct( * Adding product form elements for editing attribute * * @return $this + * @throws \Magento\Framework\Exception\LocalizedException * @SuppressWarnings(PHPMD) */ protected function _prepareForm() 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 200/345] 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 9c511efd22c4b..eeef2076d6ae4 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 201/345] 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 a710f3dd8fa5c..2b425f34f8a5b 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 2363896edb0a8d7707a2f71862cf8d0de6423f4a Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Mon, 11 Mar 2019 15:34:43 +0200 Subject: [PATCH 202/345] mutations add<Product Type>ProductsToCart dosn't check if the cart is active --- .../QuoteGraphQl/Model/Cart/GetCartForUser.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php index 33c91b03ee375..fa2b924d44bf5 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php @@ -85,12 +85,15 @@ public function execute(string $cartHash, ?int $customerId): Quote ); } - try { - return $this->cartRepository->getActiveForCustomer($customerId); - } catch (\Exception $e) { - throw new GraphQlNoSuchEntityException( - __('Current customer does not have an active cart.') - ); + if ($customerId) { + try { + return $this->cartRepository->getActiveForCustomer($customerId); + } catch (\Exception $e) { + throw new GraphQlNoSuchEntityException( + __($e->getMessage()) + ); + } } + return $cart; } } From e3b875b4796616f164ba98fead8c741cf37c3ae3 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Mon, 11 Mar 2019 16:08:58 +0200 Subject: [PATCH 203/345] Fix static test. --- .../module/data-grid/data-grid-header/_data-grid-filters.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/data-grid-header/_data-grid-filters.less b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/data-grid-header/_data-grid-filters.less index ef4481bdd520b..e37e08f3b667d 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/data-grid-header/_data-grid-filters.less +++ b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/data-grid/data-grid-header/_data-grid-filters.less @@ -99,7 +99,7 @@ } .action-menu { - max-height: 3.85rem * @data-grid-search-control-action-menu-item__quantity; // ToDo UI: change static item height + max-height: 3.85rem * @data-grid-search-control-action-menu-item__quantity; // @todo: change static item height overflow-y: auto; z-index: @data-grid-search-menu__z-index; } 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 204/345] 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 b47d65e310070..15f9e4833b269 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 02f8c3834e8d3fbc4bf760711ec784eb1f7b9553 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Mon, 11 Mar 2019 17:08:50 +0200 Subject: [PATCH 205/345] Fix functional test. --- .../User/Test/TestStep/CloseErrorAlertStep.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestStep/CloseErrorAlertStep.php b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/CloseErrorAlertStep.php index b8a3fce5de214..51d48058c8ae5 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/TestStep/CloseErrorAlertStep.php +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/CloseErrorAlertStep.php @@ -43,11 +43,15 @@ public function __construct( public function run() { $modalMessage = $this->dashboard->getModalMessage(); - $this->browser->waitUntil( - function () use ($modalMessage) { - return $modalMessage->isVisible() ? true : null; - } - ); - $modalMessage->acceptAlert(); + try { + $this->browser->waitUntil( + function () use ($modalMessage) { + return $modalMessage->isVisible() ? true : null; + } + ); + $modalMessage->acceptAlert(); + } catch (\PHPUnit_Extensions_Selenium2TestCase_WebDriverException $e) { + //There is no modal to accept. + } } } 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 206/345] 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 5c3ee3da8ca81..7174c35b191b6 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 207/345] 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 31cb5bb1f60ca..1b54dd2c127db 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 53bbfd22cf7242197b93f53189839f2f5018c7bd Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Mon, 11 Mar 2019 14:59:54 +0200 Subject: [PATCH 208/345] ENGCOM-4071: Static test fix. --- .../FillQuoteAddressIdInSalesOrderAddress.php | 44 ++++++++----------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/app/code/Magento/Sales/Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php b/app/code/Magento/Sales/Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php index e1b517befccb4..f04dcb9ec2d49 100644 --- a/app/code/Magento/Sales/Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php +++ b/app/code/Magento/Sales/Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php @@ -8,16 +8,15 @@ use Magento\Eav\Model\Config; use Magento\Framework\App\State; -use Magento\Quote\Model\QuoteFactory; -use Magento\Sales\Model\Order\Address; -use Magento\Sales\Model\OrderFactory; -use Magento\Sales\Model\ResourceModel\Order\Address\CollectionFactory as AddressCollectionFactory; -use Magento\Framework\App\ResourceConnection; -use Magento\Sales\Setup\SalesSetupFactory; +use Magento\Framework\Setup\ModuleDataSetupInterface; use Magento\Framework\Setup\Patch\DataPatchInterface; use Magento\Framework\Setup\Patch\PatchVersionInterface; -use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Sales\Model\Order\Address; +use Magento\Sales\Setup\SalesSetupFactory; +/** + * Fills quote_address_id in table sales_order_address if it is empty. + */ class FillQuoteAddressIdInSalesOrderAddress implements DataPatchInterface, PatchVersionInterface { /** @@ -25,11 +24,6 @@ class FillQuoteAddressIdInSalesOrderAddress implements DataPatchInterface, Patch */ private $moduleDataSetup; - /** - * @var SalesSetupFactory - */ - private $salesSetupFactory; - /** * @var State */ @@ -41,29 +35,22 @@ class FillQuoteAddressIdInSalesOrderAddress implements DataPatchInterface, Patch private $eavConfig; /** - * PatchInitial constructor. - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * * @param ModuleDataSetupInterface $moduleDataSetup + * @param State $state + * @param Config $eavConfig */ public function __construct( ModuleDataSetupInterface $moduleDataSetup, - SalesSetupFactory $salesSetupFactory, State $state, - Config $eavConfig, - AddressCollectionFactory $addressCollectionFactory, - OrderFactory $orderFactory, - QuoteFactory $quoteFactory + Config $eavConfig ) { $this->moduleDataSetup = $moduleDataSetup; - $this->salesSetupFactory = $salesSetupFactory; $this->state = $state; $this->eavConfig = $eavConfig; } /** - * {@inheritdoc} + * @inheritdoc */ public function apply() { @@ -87,7 +74,7 @@ public function fillQuoteAddressIdInSalesOrderAddress(ModuleDataSetupInterface $ } /** - * {@inheritdoc} + * @inheritdoc */ public static function getDependencies() { @@ -97,7 +84,7 @@ public static function getDependencies() } /** - * {@inheritdoc} + * @inheritdoc */ public static function getVersion() { @@ -105,7 +92,7 @@ public static function getVersion() } /** - * {@inheritdoc} + * @inheritdoc */ public function getAliases() { @@ -113,8 +100,11 @@ public function getAliases() } /** + * Fill quote_address_id in sales_order_address by type. + * * @param ModuleDataSetupInterface $setup * @param string $addressType + * @throws \Zend_Db_Statement_Exception */ private function fillQuoteAddressIdInSalesOrderAddressByType(ModuleDataSetupInterface $setup, $addressType) { @@ -152,6 +142,8 @@ private function fillQuoteAddressIdInSalesOrderAddressByType(ModuleDataSetupInte } /** + * Process filling quote_address_id in sales_order_address in batch. + * * @param ModuleDataSetupInterface $setup * @param array $orderAddresses * @param string $addressType 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 209/345] 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 f3238293e5713..ceb4e93e4e9aa 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 d45b294a34e4f..d63a5546716b1 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 210/345] 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 63182dd5624e6..e6bdca02061ef 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 15f9e4833b269..32ba39d9afd98 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 924dca297c1ac696e1362ec2e76a23676d87efa0 Mon Sep 17 00:00:00 2001 From: tuyennn <thinghost76@gmail.com> Date: Tue, 12 Mar 2019 09:57:52 +0700 Subject: [PATCH 211/345] issues-21521: Fix Broken Tax Rate Search Filter Admin grid --- app/code/Magento/Tax/etc/di.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Tax/etc/di.xml b/app/code/Magento/Tax/etc/di.xml index 096f8359fadd3..3b46b0f9e258c 100644 --- a/app/code/Magento/Tax/etc/di.xml +++ b/app/code/Magento/Tax/etc/di.xml @@ -143,6 +143,7 @@ <arguments> <argument name="fieldMapping" xsi:type="array"> <item name="id" xsi:type="string">tax_calculation_rule_id</item> + <item name="code" xsi:type="string">main_table.code</item> <item name="tax_rate_ids" xsi:type="string">tax_calculation_rate_id</item> <item name="customer_tax_class_ids" xsi:type="string">cd.customer_tax_class_id</item> <item name="product_tax_class_ids" xsi:type="string">cd.product_tax_class_id</item> @@ -154,6 +155,7 @@ <arguments> <argument name="fieldMapping" xsi:type="array"> <item name="id" xsi:type="string">tax_calculation_rule_id</item> + <item name="code" xsi:type="string">main_table.code</item> <item name="tax_rate_ids" xsi:type="string">tax_calculation_rate_id</item> <item name="customer_tax_class_ids" xsi:type="string">cd.customer_tax_class_id</item> <item name="product_tax_class_ids" xsi:type="string">cd.product_tax_class_id</item> 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 212/345] 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 eeef2076d6ae4..47f96b132b3db 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 649fad680d12c1b4a8f0248dbf84293a5de344e2 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Tue, 12 Mar 2019 11:38:10 +0200 Subject: [PATCH 213/345] ENGCOM-4071: Upgrade fail fix. --- .../Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php b/app/code/Magento/Sales/Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php index f04dcb9ec2d49..c05d2d01a1f14 100644 --- a/app/code/Magento/Sales/Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php +++ b/app/code/Magento/Sales/Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php @@ -181,7 +181,7 @@ private function fillQuoteAddressIdInSalesOrderAddressProcessBatch( 'quote_address_id' => $quoteAddresses[$orderAddress['quote_id']]['address_id'] ?? null, ]; $where = [ - 'orderAddressId' => $orderAddress['entity_id'] + 'entity_id' => $orderAddress['entity_id'] ]; $salesConnection->update($salesOrderAddressTable, $bind, $where); From f406c28b8d4a9a86320ca9874973f74b917afc6f Mon Sep 17 00:00:00 2001 From: Yogesh Suhagiya <yksuhagiya@gmail.com> Date: Tue, 12 Mar 2019 15:13:55 +0530 Subject: [PATCH 214/345] Corrected test case method name --- .../Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 880d6aa0f406f..1d937251dcb40 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 @@ -174,7 +174,7 @@ public function testSetNewBillingAddressWithUseForShippingParameter() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php */ - public function testSettBillingAddressToCustomerCart() + public function testSetBillingAddressToCustomerCart() { $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); From 524eef39230cd60d2a4f32f6827c6f238d574957 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Tue, 12 Mar 2019 12:20:49 +0200 Subject: [PATCH 215/345] Fix static tests. --- .../web/css/source/actions/_actions-select.less | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-select.less b/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-select.less index 544ab2fadd624..1c45fe6946ba0 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-select.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-select.less @@ -7,6 +7,14 @@ // Actions -> Action select // _____________________________________________ +// +// Variables +// _____________________________________________ + +@_dropdown__padding-right: @action__height; +@_triangle__height: @button-marker-triangle__height; +@_triangle__width: @button-marker-triangle__width; + // Action select have the same visual styles and functionality as native <select> .action-select-wrap { @_action-select__border-color: @button__border-color; @@ -18,9 +26,9 @@ .action-select { .action-toggle-triangle( - @_dropdown__padding-right: @_action-select-toggle__size; - @_triangle__height: @button-marker-triangle__height; - @_triangle__width: @button-marker-triangle__width; + @_dropdown__padding-right; + @_triangle__height; + @_triangle__width; ); .lib-text-overflow-ellipsis(); From 7c55811a43ae0a9d80cc3c6544c42088bfe0d790 Mon Sep 17 00:00:00 2001 From: Ronak Patel <ronak2ram@gmail.com> Date: Tue, 12 Mar 2019 17:22:27 +0530 Subject: [PATCH 216/345] Resolve Issue : Search REST API returns wrong total_count --- .../Search/Adapter/Mysql/Adapter.php | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Adapter.php b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Adapter.php index f4d83ece134cf..501136f76752f 100644 --- a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Adapter.php +++ b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Adapter.php @@ -49,6 +49,16 @@ class Adapter implements AdapterInterface */ private $temporaryStorageFactory; + /** + * Query Select Parts to be skipped when prepare query for count + * + * @var array + */ + private $countSqlSkipParts = [ + \Magento\Framework\DB\Select::LIMIT_COUNT => true, + \Magento\Framework\DB\Select::LIMIT_OFFSET => true, + ]; + /** * @param Mapper $mapper * @param ResponseFactory $responseFactory @@ -86,7 +96,7 @@ public function query(RequestInterface $request) $response = [ 'documents' => $documents, 'aggregations' => $aggregations, - 'total' => count($documents) + 'total' => $this->getSize($query) ]; return $this->responseFactory->create($response); } @@ -115,4 +125,36 @@ private function getConnection() { return $this->resource->getConnection(); } + + /** + * Get rows size + * + * @return int + */ + private function getSize($query) + { + $sql = $this->getSelectCountSql($query); + $parentSelect = $this->getConnection()->select(); + $parentSelect->from(['core_select' => $sql]); + $parentSelect->reset(\Magento\Framework\DB\Select::COLUMNS); + $parentSelect->columns('COUNT(*)'); + $totalRecords = $this->getConnection()->fetchOne($parentSelect); + return intval($totalRecords); + } + + /** + * Reset limit and offset + * + * @return Select + */ + private function getSelectCountSql($query) + { + foreach ($this->countSqlSkipParts as $part => $toSkip) { + if ($toSkip) { + $query->reset($part); + } + } + return $query; + } + } From 23e7e36ca80b59899fc67a5288c3860d13dfca67 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Tue, 12 Mar 2019 14:01:07 +0200 Subject: [PATCH 217/345] ENGCOM-4461: Static test fix. --- .../Magento/blank/Magento_Customer/web/css/source/_module.less | 2 +- .../Magento/luma/Magento_Customer/web/css/source/_module.less | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less index f847b8aff9206..54ba530092cc0 100644 --- a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less @@ -393,8 +393,8 @@ .field.price { .with-addon { .input-text { - width: 100%; flex-basis: auto; + width: 100%; } } } diff --git a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less index 6d832e93a1df3..bd8ddde98c506 100755 --- a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less @@ -614,8 +614,8 @@ .field.price { .with-addon { .input-text { - width: 100%; flex-basis: auto; + width: 100%; } } } From a7a14d040a3fcfbb8245585cbe851ec1f83d0029 Mon Sep 17 00:00:00 2001 From: Eduard Chitoraga <e.chitoraga@atwix.com> Date: Tue, 12 Mar 2019 15:13:29 +0200 Subject: [PATCH 218/345] Update Mysql.php --- lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php index 86266ec23fe47..59d9ec169309b 100644 --- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php +++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php @@ -2154,7 +2154,6 @@ public function createTable(Table $table) */ public function createTemporaryTable(\Magento\Framework\DB\Ddl\Table $table) { - $columns = $table->getColumns(); $sqlFragment = array_merge( $this->_getColumnsDefinition($table), $this->_getIndexesDefinition($table), From 55d65923ea48d0647bc4b9782ef1c5e3fc80e3e8 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Tue, 12 Mar 2019 09:35:42 -0500 Subject: [PATCH 219/345] ENGCOM-4389: Elasticsearch6 implementation - Travis CI Stabilized --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e75e8f1a52dd1..76885ebab2896 100644 --- a/.travis.yml +++ b/.travis.yml @@ -54,7 +54,6 @@ cache: - $HOME/node_modules - $HOME/yarn.lock before_install: - - curl -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.6.1.deb && sudo dpkg -i --force-confnew elasticsearch-6.6.1.deb && sudo service elasticsearch restart - ./dev/travis/before_install.sh install: composer install --no-interaction before_script: ./dev/travis/before_script.sh From a7d0cd284b398dc9feadb458b546853157ab0549 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Tue, 12 Mar 2019 09:35:42 -0500 Subject: [PATCH 220/345] ENGCOM-4389: Elasticsearch6 implementation - Travis CI Stabilized --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e75e8f1a52dd1..76885ebab2896 100644 --- a/.travis.yml +++ b/.travis.yml @@ -54,7 +54,6 @@ cache: - $HOME/node_modules - $HOME/yarn.lock before_install: - - curl -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.6.1.deb && sudo dpkg -i --force-confnew elasticsearch-6.6.1.deb && sudo service elasticsearch restart - ./dev/travis/before_install.sh install: composer install --no-interaction before_script: ./dev/travis/before_script.sh From 05019081918c5c6ce1b91e8a3cd75a57591cc731 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Tue, 12 Mar 2019 17:01:01 +0200 Subject: [PATCH 221/345] ENGCOM-4460: Static test fix. --- .../Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php | 4 ++-- .../Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php | 4 ++-- .../Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php | 4 ++-- .../Wishlist/Block/Customer/Wishlist/Item/Column/Info.php | 4 ++-- .../Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php index 41ce705c77607..40882ae00dae1 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php @@ -5,13 +5,13 @@ */ /** - * Wishlist for item column in customer wishlist - * * @author Magento Core Team <core@magentocommerce.com> */ namespace Magento\Wishlist\Block\Customer\Wishlist\Item\Column; /** + * Model for item column in customer wishlist. + * * @api * @deprecated * @since 100.0.2 diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php index f63d083bf56b6..53f67626e956d 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php @@ -5,13 +5,13 @@ */ /** - * Wishlist block customer item cart column - * * @author Magento Core Team <core@magentocommerce.com> */ namespace Magento\Wishlist\Block\Customer\Wishlist\Item\Column; /** + * Wishlist block customer item cart column. + * * @api * @deprecated * @since 100.0.2 diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php index 1fc73c05f6f86..c4c786961694b 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php @@ -5,13 +5,13 @@ */ /** - * Edit item in customer wishlist table - * * @author Magento Core Team <core@magentocommerce.com> */ namespace Magento\Wishlist\Block\Customer\Wishlist\Item\Column; /** + * Edit item in customer wishlist table. + * * @api * @deprecated * @since 100.0.2 diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php index e6067392bae48..b7eaf53fc23b5 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php @@ -5,13 +5,13 @@ */ /** - * Wishlist block customer item cart column - * * @author Magento Core Team <core@magentocommerce.com> */ namespace Magento\Wishlist\Block\Customer\Wishlist\Item\Column; /** + * Wishlist block customer item cart column. + * * @api * @deprecated * @since 100.0.2 diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php index 724ffd90f87db..09f5014edead6 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php @@ -5,13 +5,13 @@ */ /** - * Delete item column in customer wishlist table - * * @author Magento Core Team <core@magentocommerce.com> */ namespace Magento\Wishlist\Block\Customer\Wishlist\Item\Column; /** + * Delete item column in customer wishlist table + * * @api * @deprecated * @since 100.0.2 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 222/345] 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 33229c6432c5a..1234228dad72d 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 b6149be04c471..ee3d09ec3eaed 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 223/345] 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 7174c35b191b6..74e7f40b1f062 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 f9a88cb28316605184a4dcc268cff3366151ae2a Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Tue, 12 Mar 2019 11:59:59 -0500 Subject: [PATCH 224/345] ENGCOM-4389: Elasticsearch6 implementation - Fixed removing out of stock items from ES index --- .../Magento/Elasticsearch/Model/Config.php | 16 ++++-- app/code/Magento/Elasticsearch/etc/di.xml | 8 +++ .../Magento/Elasticsearch6/Model/Config.php | 56 ------------------- .../Model/DataProvider/Suggestions.php | 2 +- .../Model/DataProvider/SuggestionsTest.php | 2 +- app/code/Magento/Elasticsearch6/etc/di.xml | 8 +++ 6 files changed, 29 insertions(+), 63 deletions(-) delete mode 100644 app/code/Magento/Elasticsearch6/Model/Config.php diff --git a/app/code/Magento/Elasticsearch/Model/Config.php b/app/code/Magento/Elasticsearch/Model/Config.php index dc08a72a9feb3..387db07c62f90 100644 --- a/app/code/Magento/Elasticsearch/Model/Config.php +++ b/app/code/Magento/Elasticsearch/Model/Config.php @@ -25,8 +25,6 @@ class Config implements ClientOptionsInterface */ const ENGINE_NAME = 'elasticsearch'; - private const ENGINE_NAME_5 = 'elasticsearch5'; - /** * Elasticsearch Entity type */ @@ -64,23 +62,31 @@ class Config implements ClientOptionsInterface private $engineResolver; /** - * Constructor + * Available Elasticsearch engines. * + * @var array + */ + private $engineList; + + /** * @param ScopeConfigInterface $scopeConfig * @param ClientResolver|null $clientResolver * @param EngineResolverInterface|null $engineResolver * @param string|null $prefix + * @param array $engineList */ public function __construct( ScopeConfigInterface $scopeConfig, ClientResolver $clientResolver = null, EngineResolverInterface $engineResolver = null, - $prefix = null + $prefix = null, + $engineList = [] ) { $this->scopeConfig = $scopeConfig; $this->clientResolver = $clientResolver ?: ObjectManager::getInstance()->get(ClientResolver::class); $this->engineResolver = $engineResolver ?: ObjectManager::getInstance()->get(EngineResolverInterface::class); $this->prefix = $prefix ?: $this->clientResolver->getCurrentEngine(); + $this->engineList = $engineList; } /** @@ -138,7 +144,7 @@ public function getSearchConfigData($field, $storeId = null) */ public function isElasticsearchEnabled() { - return in_array($this->engineResolver->getCurrentSearchEngine(), [self::ENGINE_NAME, self::ENGINE_NAME_5]); + return in_array($this->engineResolver->getCurrentSearchEngine(), $this->engineList); } /** diff --git a/app/code/Magento/Elasticsearch/etc/di.xml b/app/code/Magento/Elasticsearch/etc/di.xml index 7e219bb2f918f..23a1e76a30cfe 100644 --- a/app/code/Magento/Elasticsearch/etc/di.xml +++ b/app/code/Magento/Elasticsearch/etc/di.xml @@ -13,6 +13,14 @@ <preference for="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldIndex\ConverterInterface" type="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldIndex\Converter" /> <preference for="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ConverterInterface" type="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\Converter" /> <preference for="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProviderInterface" type="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\CompositeFieldProvider" /> + <type name="Magento\Elasticsearch\Model\Config"> + <arguments> + <argument name="engineList" xsi:type="array"> + <item name="elasticsearch" xsi:type="string">elasticsearch</item> + <item name="elasticsearch5" xsi:type="string">elasticsearch5</item> + </argument> + </arguments> + </type> <type name="Magento\Elasticsearch\Model\Adapter\FieldMapper\FieldMapperResolver"> <arguments> <argument name="fieldMappers" xsi:type="array"> diff --git a/app/code/Magento/Elasticsearch6/Model/Config.php b/app/code/Magento/Elasticsearch6/Model/Config.php deleted file mode 100644 index 1a989e2705fdd..0000000000000 --- a/app/code/Magento/Elasticsearch6/Model/Config.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Elasticsearch6\Model; - -use Magento\Framework\Search\EngineResolverInterface; -use Magento\Framework\App\ObjectManager; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\AdvancedSearch\Model\Client\ClientResolver; - -/** - * Elasticsearch6 config model - */ -class Config extends \Magento\Elasticsearch\Model\Config -{ - /** - * Search engine name - */ - private const ENGINE_NAME_6 = 'elasticsearch6'; - - /** - * @var EngineResolverInterface - */ - private $engineResolver; - - /** - * Constructor - * - * @param ScopeConfigInterface $scopeConfig - * @param ClientResolver|null $clientResolver - * @param EngineResolverInterface|null $engineResolver - * @param string|null $prefix - */ - public function __construct( - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\AdvancedSearch\Model\Client\ClientResolver $clientResolver, - \Magento\Framework\Search\EngineResolverInterface $engineResolver, - $prefix = null - ) { - parent::__construct($scopeConfig, $clientResolver, $engineResolver, $prefix); - $this->engineResolver = $engineResolver; - } - - /** - * Return true if third party search engine is used - * - * @return bool - */ - public function isElasticsearchEnabled() - { - return in_array($this->engineResolver->getCurrentSearchEngine(), [self::ENGINE_NAME_6]); - } -} diff --git a/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php b/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php index 77e1270f54fc2..d05471734bb8f 100644 --- a/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php +++ b/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php @@ -9,7 +9,7 @@ use Magento\Store\Model\ScopeInterface; use Magento\Search\Model\QueryInterface; use Magento\AdvancedSearch\Model\SuggestedQueriesInterface; -use Magento\Elasticsearch6\Model\Config; +use Magento\Elasticsearch\Model\Config; use Magento\Elasticsearch\SearchAdapter\ConnectionManager; use Magento\Search\Model\QueryResultFactory; use Magento\Framework\App\Config\ScopeConfigInterface; diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php b/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php index 957edc559fdcb..b3c60b70ffa8e 100644 --- a/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php +++ b/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php @@ -67,7 +67,7 @@ class SuggestionsTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { - $this->config = $this->getMockBuilder(\Magento\Elasticsearch6\Model\Config::class) + $this->config = $this->getMockBuilder(\Magento\Elasticsearch\Model\Config::class) ->disableOriginalConstructor() ->setMethods(['isElasticsearchEnabled']) ->getMock(); diff --git a/app/code/Magento/Elasticsearch6/etc/di.xml b/app/code/Magento/Elasticsearch6/etc/di.xml index df71f3c3158d3..4532e5020b665 100644 --- a/app/code/Magento/Elasticsearch6/etc/di.xml +++ b/app/code/Magento/Elasticsearch6/etc/di.xml @@ -6,6 +6,14 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\Elasticsearch\Model\Config"> + <arguments> + <argument name="engineList" xsi:type="array"> + <item name="elasticsearch6" xsi:type="string">elasticsearch6</item> + </argument> + </arguments> + </type> + <type name="Magento\Search\Model\Adminhtml\System\Config\Source\Engine"> <arguments> <argument name="engines" xsi:type="array"> From f40faed9c2128a0018a50c9605b69e455d150138 Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Tue, 12 Mar 2019 12:45:35 -0500 Subject: [PATCH 225/345] GraphQL-458: CustomerGraphQl module refactoring --- .../Model/Customer/CreateCustomerAccount.php | 2 + .../Model/Customer/SaveCustomer.php | 59 +++++++++++++++++++ .../Model/Customer/UpdateCustomerAccount.php | 46 +++------------ .../Model/Cart/SetBillingAddressOnCart.php | 4 -- 4 files changed, 70 insertions(+), 41 deletions(-) create mode 100644 app/code/Magento/CustomerGraphQl/Model/Customer/SaveCustomer.php diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/CreateCustomerAccount.php b/app/code/Magento/CustomerGraphQl/Model/Customer/CreateCustomerAccount.php index 75371b805e023..b7b66df042467 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/CreateCustomerAccount.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/CreateCustomerAccount.php @@ -88,6 +88,8 @@ public function execute(array $data): CustomerInterface } /** + * Create account + * * @param array $data * @return CustomerInterface * @throws LocalizedException diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/SaveCustomer.php b/app/code/Magento/CustomerGraphQl/Model/Customer/SaveCustomer.php new file mode 100644 index 0000000000000..f8b7947f09ba7 --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/SaveCustomer.php @@ -0,0 +1,59 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CustomerGraphQl\Model\Customer; + +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Framework\Exception\AlreadyExistsException; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Exception\GraphQlAlreadyExistsException; +use Magento\Framework\GraphQl\Exception\GraphQlAuthenticationException; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Customer\Api\Data\CustomerInterface; + +/** + * Save customer + */ +class SaveCustomer +{ + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * @param CustomerRepositoryInterface $customerRepository + */ + public function __construct( + CustomerRepositoryInterface $customerRepository + ) { + $this->customerRepository = $customerRepository; + } + + /** + * Save customer + * + * @param CustomerInterface $customer + * @param array $data + * @throws GraphQlAlreadyExistsException + * @throws GraphQlAuthenticationException + * @throws GraphQlInputException + */ + public function execute(CustomerInterface $customer): void + { + try { + $this->customerRepository->save($customer); + } catch (AlreadyExistsException $e) { + throw new GraphQlAlreadyExistsException( + __('A customer with the same email address already exists in an associated website.'), + $e + ); + } catch (LocalizedException $e) { + throw new GraphQlInputException(__($e->getMessage()), $e); + } + } +} diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateCustomerAccount.php b/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateCustomerAccount.php index b58ea8f0d21ab..8601d586b3c95 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateCustomerAccount.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateCustomerAccount.php @@ -7,9 +7,6 @@ namespace Magento\CustomerGraphQl\Model\Customer; -use Magento\Customer\Api\CustomerRepositoryInterface; -use Magento\Framework\Exception\AlreadyExistsException; -use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Exception\GraphQlAlreadyExistsException; use Magento\Framework\GraphQl\Exception\GraphQlAuthenticationException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; @@ -23,9 +20,9 @@ class UpdateCustomerAccount { /** - * @var CustomerRepositoryInterface + * @var SaveCustomer */ - private $customerRepository; + private $saveCustomer; /** * @var StoreManagerInterface @@ -53,7 +50,7 @@ class UpdateCustomerAccount private $restrictedKeys; /** - * @param CustomerRepositoryInterface $customerRepository + * @param SaveCustomer $saveCustomer * @param StoreManagerInterface $storeManager * @param CheckCustomerPassword $checkCustomerPassword * @param DataObjectHelper $dataObjectHelper @@ -61,14 +58,14 @@ class UpdateCustomerAccount * @param array $restrictedKeys */ public function __construct( - CustomerRepositoryInterface $customerRepository, + SaveCustomer $saveCustomer, StoreManagerInterface $storeManager, CheckCustomerPassword $checkCustomerPassword, DataObjectHelper $dataObjectHelper, ChangeSubscriptionStatus $changeSubscriptionStatus, array $restrictedKeys = [] ) { - $this->customerRepository = $customerRepository; + $this->saveCustomer = $saveCustomer; $this->storeManager = $storeManager; $this->checkCustomerPassword = $checkCustomerPassword; $this->dataObjectHelper = $dataObjectHelper; @@ -87,26 +84,6 @@ public function __construct( * @throws GraphQlInputException */ public function execute(CustomerInterface $customer, array $data): void - { - try { - $this->updateCustomer($customer, $data); - } catch (LocalizedException $e) { - throw new GraphQlInputException(__($e->getMessage())); - } - - if (isset($data['is_subscribed'])) { - $this->changeSubscriptionStatus->execute((int)$customer->getId(), (bool)$data['is_subscribed']); - } - } - - /** - * @param CustomerInterface $customer - * @param array $data - * @throws GraphQlAlreadyExistsException - * @throws GraphQlAuthenticationException - * @throws GraphQlInputException - */ - private function updateCustomer(CustomerInterface $customer, array $data): void { if (isset($data['email']) && $customer->getEmail() !== $data['email']) { if (!isset($data['password']) || empty($data['password'])) { @@ -122,15 +99,10 @@ private function updateCustomer(CustomerInterface $customer, array $data): void $customer->setStoreId($this->storeManager->getStore()->getId()); - try { - $this->customerRepository->save($customer); - } catch (AlreadyExistsException $e) { - throw new GraphQlAlreadyExistsException( - __('A customer with the same email address already exists in an associated website.'), - $e - ); - } catch (LocalizedException $e) { - throw new GraphQlInputException(__($e->getMessage()), $e); + $this->saveCustomer->execute($customer); + + if (isset($data['is_subscribed'])) { + $this->changeSubscriptionStatus->execute((int)$customer->getId(), (bool)$data['is_subscribed']); } } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index 0aafe0e7882d7..31524ea023222 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -9,8 +9,6 @@ use Magento\CustomerGraphQl\Model\Customer\Address\GetCustomerAddress; use Magento\CustomerGraphQl\Model\Customer\GetCustomer; -use Magento\Framework\GraphQl\Exception\GraphQlAuthenticationException; -use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; @@ -67,8 +65,6 @@ public function __construct( * @param array $billingAddressInput * @return void * @throws GraphQlInputException - * @throws GraphQlAuthenticationException - * @throws GraphQlAuthorizationException * @throws GraphQlNoSuchEntityException */ public function execute(ContextInterface $context, CartInterface $cart, array $billingAddressInput): void From d3482b7724b6f1e82378a87c0344058af65b06d9 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> Date: Tue, 12 Mar 2019 20:06:51 +0200 Subject: [PATCH 226/345] magento/magento2#20785 Use batches and direct queries to fix sales address upgrade --- .../Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php b/app/code/Magento/Sales/Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php index c05d2d01a1f14..a75690536e760 100644 --- a/app/code/Magento/Sales/Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php +++ b/app/code/Magento/Sales/Setup/Patch/Data/FillQuoteAddressIdInSalesOrderAddress.php @@ -181,7 +181,7 @@ private function fillQuoteAddressIdInSalesOrderAddressProcessBatch( 'quote_address_id' => $quoteAddresses[$orderAddress['quote_id']]['address_id'] ?? null, ]; $where = [ - 'entity_id' => $orderAddress['entity_id'] + 'entity_id = ?' => $orderAddress['entity_id'] ]; $salesConnection->update($salesOrderAddressTable, $bind, $where); 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 227/345] 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 e6bdca02061ef..f63e5a681c42b 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 228/345] 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 74e7f40b1f062..7efd63fb7a4aa 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 229/345] 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 05b7dfeeb3953..03edc69e6d625 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 b4bcdaadf9a09..c3c92dc59c288 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 a93688a779b72724c1354013d749acf933499505 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Tue, 12 Mar 2019 15:52:23 -0500 Subject: [PATCH 230/345] ENGCOM-4389: Elasticsearch6 implementation - Fixed removing out of stock items from ES index --- app/code/Magento/Elasticsearch6/etc/di.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Elasticsearch6/etc/di.xml b/app/code/Magento/Elasticsearch6/etc/di.xml index 4532e5020b665..9999c29c1a257 100644 --- a/app/code/Magento/Elasticsearch6/etc/di.xml +++ b/app/code/Magento/Elasticsearch6/etc/di.xml @@ -52,7 +52,7 @@ <item name="elasticsearch6" xsi:type="string">\Magento\Elasticsearch6\Model\Client\ElasticsearchFactory</item> </argument> <argument name="clientOptions" xsi:type="array"> - <item name="elasticsearch6" xsi:type="string">\Magento\Elasticsearch6\Model\Config</item> + <item name="elasticsearch6" xsi:type="string">\Magento\Elasticsearch\Model\Config</item> </argument> </arguments> </type> From 13aaa6814223f6a1a478686bc3238a38c623bddc Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Tue, 12 Mar 2019 15:54:23 -0500 Subject: [PATCH 231/345] ENGCOM-4389: Elasticsearch6 implementation - Removed usage of unused class --- app/code/Magento/Elasticsearch6/etc/di.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Elasticsearch6/etc/di.xml b/app/code/Magento/Elasticsearch6/etc/di.xml index f9ee035972a35..4ece66ea1bbf3 100644 --- a/app/code/Magento/Elasticsearch6/etc/di.xml +++ b/app/code/Magento/Elasticsearch6/etc/di.xml @@ -52,7 +52,7 @@ <item name="elasticsearch6" xsi:type="string">\Magento\Elasticsearch6\Model\Client\ElasticsearchFactory</item> </argument> <argument name="clientOptions" xsi:type="array"> - <item name="elasticsearch6" xsi:type="string">\Magento\Elasticsearch6\Model\Config</item> + <item name="elasticsearch6" xsi:type="string">\Magento\Elasticsearch\Model\Config</item> </argument> </arguments> </type> From 6a9e0ceb3a769bc878872213b27d10aeb0f385c4 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Tue, 12 Mar 2019 15:54:23 -0500 Subject: [PATCH 232/345] ENGCOM-4389: Elasticsearch6 implementation - Removed usage of unused class --- app/code/Magento/Elasticsearch6/etc/di.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Elasticsearch6/etc/di.xml b/app/code/Magento/Elasticsearch6/etc/di.xml index 4532e5020b665..9999c29c1a257 100644 --- a/app/code/Magento/Elasticsearch6/etc/di.xml +++ b/app/code/Magento/Elasticsearch6/etc/di.xml @@ -52,7 +52,7 @@ <item name="elasticsearch6" xsi:type="string">\Magento\Elasticsearch6\Model\Client\ElasticsearchFactory</item> </argument> <argument name="clientOptions" xsi:type="array"> - <item name="elasticsearch6" xsi:type="string">\Magento\Elasticsearch6\Model\Config</item> + <item name="elasticsearch6" xsi:type="string">\Magento\Elasticsearch\Model\Config</item> </argument> </arguments> </type> From 4de33372b198ef49f757decda398c12c72e1336e Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Tue, 12 Mar 2019 16:03:35 -0500 Subject: [PATCH 233/345] GraphQL-458: CustomerGraphQl module refactoring --- .../CustomerGraphQl/Model/Customer/ExtractCustomerData.php | 1 + .../CustomerGraphQl/Model/Resolver/ChangePassword.php | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/ExtractCustomerData.php b/app/code/Magento/CustomerGraphQl/Model/Customer/ExtractCustomerData.php index abe028051a07e..de37482aca056 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/ExtractCustomerData.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/ExtractCustomerData.php @@ -102,6 +102,7 @@ public function execute(CustomerInterface $customer): array } $customerData = array_merge($customerData, $customAttributes); + $customerData['model'] = $customer; return $customerData; } } diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/ChangePassword.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/ChangePassword.php index b80c8a99ec59e..317b7725b0265 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/ChangePassword.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/ChangePassword.php @@ -11,6 +11,7 @@ use Magento\CustomerGraphQl\Model\Customer\CheckCustomerPassword; use Magento\CustomerGraphQl\Model\Customer\ExtractCustomerData; use Magento\CustomerGraphQl\Model\Customer\GetCustomer; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; @@ -81,8 +82,12 @@ public function resolve( $customerId = (int)$customer->getId(); $this->checkCustomerPassword->execute($args['currentPassword'], $customerId); - $this->accountManagement->changePasswordById($customerId, $args['currentPassword'], $args['newPassword']); + try { + $this->accountManagement->changePasswordById($customerId, $args['currentPassword'], $args['newPassword']); + } catch (LocalizedException $e) { + throw new GraphQlInputException(__($e->getMessage()), $e); + } return $this->extractCustomerData->execute($customer); } } 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 234/345] 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 3ad8cff31eaf8..bfbe233b9dc1b 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 b94f21371496a..8fe25614d1d42 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 0437e0a5e999b..8c465544a3283 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 a9cc0dfd34f9f..8fdd7ef715521 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 3bf4e521c4a04..3ef2b65c0224b 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 398054f1f0ed3..8b15da5ecd2ef 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 1116821f756a9..8110ed1ed00b1 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 93240586ec92c..15dcfd0a9e7e7 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 cffcdbf45a6dc..a110dc6a89f8c 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 5a547f69280e1..e35ef853d1b68 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 306a9fd2024a4..cd37c555fdb1d 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 ac857ad035f44..5db0e7f8baad4 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 60de554d594d2..8f12930aa417b 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 b8006ee5e1403a6f2da76d7634b128f93397ff60 Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Tue, 12 Mar 2019 17:11:25 -0500 Subject: [PATCH 235/345] GraphQL-419: Test coverage: GetAvailablePaymentMethodsTest for Customer --- .../GetAvailablePaymentMethodsTest.php | 36 ++++++++++++++----- .../GraphQl/Quote/Customer/GetCartTest.php | 2 +- .../Quote/Customer/RemoveItemFromCartTest.php | 2 +- .../Quote/Customer/UpdateCartItemsTest.php | 2 +- .../disable_all_active_payment_methods.php | 24 ++++++------- ...le_all_active_payment_methods_rollback.php | 4 +-- 6 files changed, 43 insertions(+), 27 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 e5027119cbc6c..34cd6ae503c22 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 @@ -54,11 +54,11 @@ protected function setUp() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php */ - public function testGetPaymentMethodsFromCustomerCart() + public function testGetCartWithPaymentMethods() { $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId('test_order_item_with_items'); - $query = $this->getCartAvailablePaymentMethodsQuery($maskedQuoteId); - $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $query = $this->getQuery($maskedQuoteId); + $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('cart', $response); self::assertEquals('checkmo', $response['cart']['available_payment_methods'][0]['code']); @@ -70,6 +70,23 @@ public function testGetPaymentMethodsFromCustomerCart() ); } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php + */ + public function testGetPaymentMethodsFromGuestCart() + { + $guestQuoteMaskedId = $this->getMaskedQuoteIdByReservedOrderId( + 'test_order_with_virtual_product_without_address' + ); + $query = $this->getQuery($guestQuoteMaskedId); + + $this->expectExceptionMessage( + "The current user cannot perform operations on cart \"$guestQuoteMaskedId\"" + ); + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + } + /** * @magentoApiDataFixture Magento/Customer/_files/three_customers.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php @@ -77,7 +94,7 @@ public function testGetPaymentMethodsFromCustomerCart() public function testGetPaymentMethodsFromAnotherCustomerCart() { $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId('test_order_item_with_items'); - $query = $this->getCartAvailablePaymentMethodsQuery($maskedQuoteId); + $query = $this->getQuery($maskedQuoteId); $this->expectExceptionMessage( "The current user cannot perform operations on cart \"$maskedQuoteId\"" @@ -92,8 +109,8 @@ public function testGetPaymentMethodsFromAnotherCustomerCart() public function testGetPaymentMethodsIfPaymentsAreNotSet() { $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId('test_order_item_with_items'); - $query = $this->getCartAvailablePaymentMethodsQuery($maskedQuoteId); - $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $query = $this->getQuery($maskedQuoteId); + $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); self::assertEquals(0, count($response['cart']['available_payment_methods'])); } @@ -106,7 +123,7 @@ public function testGetPaymentMethodsIfPaymentsAreNotSet() public function testGetPaymentMethodsOfNonExistentCart() { $maskedQuoteId = 'non_existent_masked_id'; - $query = $this->getCartAvailablePaymentMethodsQuery($maskedQuoteId); + $query = $this->getQuery($maskedQuoteId); $this->graphQlQuery($query, [], '', $this->getHeaderMap()); } @@ -115,9 +132,10 @@ public function testGetPaymentMethodsOfNonExistentCart() * @param string $maskedQuoteId * @return string */ - private function getCartAvailablePaymentMethodsQuery( + private function getQuery( string $maskedQuoteId - ) : string { + ): 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 fe9b7b3c49a7c..286742936da8e 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 @@ -72,8 +72,8 @@ public function testGetCart() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php */ public function testGetGuestCart() { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveItemFromCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveItemFromCartTest.php index 70ec646c10008..e80a2127ad420 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveItemFromCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveItemFromCartTest.php @@ -129,7 +129,7 @@ public function testRemoveItemIfItemIsNotBelongToCart() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php */ public function testRemoveItemFromGuestCart() diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/UpdateCartItemsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/UpdateCartItemsTest.php index 632ee839834e0..74e7aa8b5d0a4 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/UpdateCartItemsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/UpdateCartItemsTest.php @@ -156,7 +156,7 @@ public function testUpdateItemIfItemIsNotBelongToCart() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php */ public function testUpdateItemInGuestCart() diff --git a/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods.php b/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods.php index 762ea672614a8..37b67fbf6c44d 100644 --- a/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods.php +++ b/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods.php @@ -7,23 +7,17 @@ use Magento\Config\Model\Config; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Model\Store; use Magento\TestFramework\Helper\Bootstrap; -$processConfigData = function (Config $config, array $data) { - foreach ($data as $key => $value) { - $config->setDataByPath($key, $value); - $config->save(); - } -}; - -$objectManager = Bootstrap::getObjectManager(); -$paymentMethodList = $objectManager->get(\Magento\Payment\Api\PaymentMethodListInterface::class); -$rollbackConfigKey = 'test/payment/disabled_payment_methods'; -$configData = []; +$objectManager = Bootstrap::getObjectManager(); +$paymentMethodList = $objectManager->get(\Magento\Payment\Api\PaymentMethodListInterface::class); +$rollbackConfigKey = 'test/payment/disabled_payment_methods'; +$configData = []; $disabledPaymentMethods = []; // Get all active Payment Methods -foreach ($paymentMethodList->getActiveList(\Magento\Store\Model\Store::DEFAULT_STORE_ID) as $paymentMethod) { +foreach ($paymentMethodList->getActiveList(Store::DEFAULT_STORE_ID) as $paymentMethod) { $configData['payment/' . $paymentMethod->getCode() . '/active'] = 0; $disabledPaymentMethods[] = $paymentMethod->getCode(); } @@ -34,4 +28,8 @@ $defConfig = $objectManager->create(Config::class); $defConfig->setScope(ScopeConfigInterface::SCOPE_TYPE_DEFAULT); -$processConfigData($defConfig, $configData); +foreach ($configData as $key => $value) { + $defConfig->setDataByPath($key, $value); + $defConfig->save(); +} + diff --git a/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods_rollback.php b/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods_rollback.php index ee81af139f4b4..4a56888058e4d 100644 --- a/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods_rollback.php @@ -9,10 +9,10 @@ use Magento\Framework\App\Config\Storage\WriterInterface; use Magento\TestFramework\Helper\Bootstrap; -$objectManager = Bootstrap::getObjectManager(); +$objectManager = Bootstrap::getObjectManager(); $rollbackConfigKey = 'test/payment/disabled_payment_methods'; -$configWriter = $objectManager->create(WriterInterface::class); +$configWriter = $objectManager->create(WriterInterface::class); $rollbackConfigValue = $objectManager->get(\Magento\Store\Model\StoreManagerInterface::class) ->getStore(\Magento\Store\Model\Store::DEFAULT_STORE_ID) ->getConfig($rollbackConfigKey); From 350d1f8783128719ce7d2cfb83197ee382ecbf4f Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Tue, 12 Mar 2019 17:25:53 -0500 Subject: [PATCH 236/345] GraphQL-429: Test coverage: nonExistentCart --- .../Customer/SetBillingAddressOnCartTest.php | 18 ++--------- .../Customer/SetPaymentMethodOnCartTest.php | 18 ++--------- .../Guest/SetBillingAddressOnCartTest.php | 18 ++--------- .../Guest/SetPaymentMethodOnCartTest.php | 32 ++++++------------- 4 files changed, 15 insertions(+), 71 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 06926e0a0b2e9..ccd328cea12ed 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 @@ -411,30 +411,16 @@ public function testSetBillingAddressIfCustomerIsNotOwnerOfAddress() public function testSetBillingAddressOnNonExistentCart() { $maskedQuoteId = 'non_existent_masked_id'; - $query = $this->getCartQuery($maskedQuoteId); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); - } - - /** - * @param string $maskedQuoteId - * @return string - */ - private function getCartQuery( - string $maskedQuoteId - ) : string { - return <<<QUERY + $query = <<<QUERY { cart(cart_id: "$maskedQuoteId") { items { id - qty - product { - sku - } } } } QUERY; + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); } /** 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 a2bbbfe135fa1..91c98d2f13f47 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 @@ -159,30 +159,16 @@ public function testSetPaymentMethodToAnotherCustomerCart() public function testPaymentMethodOnNonExistentCart() { $maskedQuoteId = 'non_existent_masked_id'; - $query = $this->getCartQuery($maskedQuoteId); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); - } - - /** - * @param string $maskedQuoteId - * @return string - */ - private function getCartQuery( - string $maskedQuoteId - ) : string { - return <<<QUERY + $query = <<<QUERY { cart(cart_id: "$maskedQuoteId") { items { id - qty - product { - sku - } } } } QUERY; + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); } /** 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 0bb7c9741197f..8f8e5419a9ff2 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 @@ -251,30 +251,16 @@ public function testSetBillingAddressFromAddressBook() public function testSetBillingAddressOnNonExistentCart() { $maskedQuoteId = 'non_existent_masked_id'; - $query = $this->getCartQuery($maskedQuoteId); - $this->graphQlQuery($query); - } - - /** - * @param string $maskedQuoteId - * @return string - */ - private function getCartQuery( - string $maskedQuoteId - ) : string { - return <<<QUERY + $query = <<<QUERY { cart(cart_id: "$maskedQuoteId") { items { id - qty - product { - sku - } } } } QUERY; + $this->graphQlQuery($query); } /** 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 5117fce04a915..fbfa53b7d6434 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 @@ -133,7 +133,15 @@ public function testSetPaymentMethodToCustomerCart() public function testSetPaymentOnNonExistentCart() { $maskedQuoteId = 'non_existent_masked_id'; - $query = $this->getCartQuery($maskedQuoteId); + $query = <<<QUERY +{ + cart(cart_id: "$maskedQuoteId") { + items { + id + } + } +} +QUERY; $this->graphQlQuery($query); } @@ -166,28 +174,6 @@ private function prepareMutationQuery( QUERY; } - /** - * @param string $maskedQuoteId - * @return string - */ - private function getCartQuery( - string $maskedQuoteId - ) : string { - return <<<QUERY -{ - cart(cart_id: "$maskedQuoteId") { - items { - id - qty - product { - sku - } - } - } -} -QUERY; - } - /** * @param string $reversedQuoteId * @param int $customerId From 18ccbdd248f423cfb77b47cf14e20c9dd5ad0910 Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Tue, 12 Mar 2019 17:50:23 -0500 Subject: [PATCH 237/345] GraphQL-458: CustomerGraphQl module refactoring --- app/code/Magento/CustomerGraphQl/Model/Customer/SaveCustomer.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/SaveCustomer.php b/app/code/Magento/CustomerGraphQl/Model/Customer/SaveCustomer.php index f8b7947f09ba7..1605c63b62f4c 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/SaveCustomer.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/SaveCustomer.php @@ -38,7 +38,6 @@ public function __construct( * Save customer * * @param CustomerInterface $customer - * @param array $data * @throws GraphQlAlreadyExistsException * @throws GraphQlAuthenticationException * @throws GraphQlInputException From 5f3d0760a59e000df24b09111c845440f83b7128 Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Tue, 12 Mar 2019 19:21:32 -0500 Subject: [PATCH 238/345] GraphQL-427: Test coverage: SetShippingAddressOnCartTest --- .../Customer/SetShippingAddressOnCartTest.php | 151 ++++++++++++------ .../Guest/SetShippingAddressOnCartTest.php | 138 ++++------------ 2 files changed, 139 insertions(+), 150 deletions(-) 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 1d381bc944ec1..9e13476c57c32 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 @@ -49,8 +49,8 @@ protected function setUp() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php */ public function testSetNewShippingAddress() { @@ -107,46 +107,6 @@ public function testSetNewShippingAddress() $this->assertNewShippingAddressFields($shippingAddressResponse); } - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @dataProvider requestWithoutRequiredParamsDataProvider - * @param string $params - * @param string $expectedException - * @throws \Exception - */ - public function testSetNewShippingAddressWithEmptyRequiredParams(string $params, string $expectedException) - { - $maskedQuoteId = $this->assignQuoteToCustomer(); - - $query = <<<QUERY -mutation { - setShippingAddressesOnCart( - input: { - cart_id: "$maskedQuoteId" - shipping_addresses: [ - { - address: { - $params - } - } - ] - } - ) { - cart { - shipping_addresses { - city - } - } - } -} -QUERY; - $this->expectExceptionMessage( - $expectedException - ); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); - } - /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php * @magentoApiDataFixture Magento/Customer/_files/customer.php @@ -241,7 +201,7 @@ public function testSetShippingAddressFromAddressBook() * @expectedException \Exception * @expectedExceptionMessage Could not find a address with ID "100" */ - public function testSetNotExistedShippingAddressFromAddressBook() + public function testSetNonExistentShippingAddressFromAddressBook() { $maskedQuoteId = $this->assignQuoteToCustomer(); @@ -356,7 +316,7 @@ public function testSetShippingAddressIfCustomerIsNotOwnerOfAddress() * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @expectedException \Exception */ - public function testSetShippingAddressIfCustomerIsNotOwnerOfCart() + public function testSetShippingAddressToAnotherCustomerCart() { $maskedQuoteId = $this->assignQuoteToCustomer('test_order_with_simple_product_without_address', 1); @@ -388,19 +348,116 @@ public function testSetShippingAddressIfCustomerIsNotOwnerOfCart() } /** - * TODO: currently only the city param is required, do we need to add at least ZIP code? + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @dataProvider dataProviderUpdateWithMissedRequiredParameters + * @param string $input + * @param string $message + * @throws \Exception + */ + public function testSetNewShippingAddressWithMissedRequiredParameters(string $input, string $message) + { + $maskedQuoteId = $this->assignQuoteToCustomer(); + + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "{$maskedQuoteId}" + shipping_addresses: [ + { + {$input} + } + ] + } + ) { + cart { + shipping_addresses { + city + } + } + } +} +QUERY; + $this->expectExceptionMessage($message); + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + } + + /** * @return array */ - public function requestWithoutRequiredParamsDataProvider() + public function dataProviderUpdateWithMissedRequiredParameters() { return [ - [ - 'save_in_address_book: false', + 'shipping_addresses' => [ + '', + 'The shipping address must contain either "customer_address_id" or "address".', + ], + 'missed_city' => [ + 'address: { save_in_address_book: false }', 'Field CartAddressInput.city of required type String! was not provided' ] ]; } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @expectedException \Exception + * @expectedExceptionMessage You cannot specify multiple shipping addresses. + */ + public function testSetMultipleNewShippingAddresses() + { + $maskedQuoteId = $this->assignQuoteToCustomer(); + + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + { + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + region: "test region" + postcode: "887766" + country_code: "US" + telephone: "88776655" + save_in_address_book: false + } + }, + { + address: { + firstname: "test firstname 2" + lastname: "test lastname 2" + company: "test company 2" + street: ["test street 1", "test street 2"] + city: "test city" + region: "test region" + postcode: "887766" + country_code: "US" + telephone: "88776655" + save_in_address_book: false + } + } + ] + } + ) { + cart { + shipping_addresses { + city + } + } + } +} +QUERY; + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + } + /** * Verify the all the whitelisted fields for a New Address Object * 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 522091c1eb2bf..33aeb2b902a8f 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,7 +8,6 @@ namespace Magento\GraphQl\Quote\Guest; use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Integration\Api\CustomerTokenServiceInterface; use Magento\Multishipping\Helper\Data; use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; @@ -37,18 +36,12 @@ class SetShippingAddressOnCartTest extends GraphQlAbstract */ private $quoteIdToMaskedId; - /** - * @var CustomerTokenServiceInterface - */ - private $customerTokenService; - 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); } /** @@ -186,12 +179,12 @@ public function testSetShippingAddressFromAddressBook() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php * @expectedException \Exception - * @expectedExceptionMessage The shipping address must contain either "customer_address_id" or "address". */ - public function testSetShippingAddressWithoutAddresses() + public function testSetShippingAddressToCustomerCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->assignQuoteToCustomer('test_order_with_simple_product_without_address', 1); $query = <<<QUERY mutation { @@ -199,29 +192,35 @@ public function testSetShippingAddressWithoutAddresses() input: { cart_id: "$maskedQuoteId" shipping_addresses: [ - {} + { + customer_address_id: 1 + } ] } ) { cart { shipping_addresses { - city + postcode } } } } QUERY; + $this->expectExceptionMessage( + "The current user cannot perform operations on cart \"$maskedQuoteId\"" + ); + $this->graphQlQuery($query); } /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php - * @dataProvider requestWithoutRequiredParamsDataProvider - * @param string $params - * @param string $expectedException + * @dataProvider dataProviderUpdateWithMissedRequiredParameters + * @param string $input + * @param string $message * @throws \Exception */ - public function testSetNewShippingAddressWithEmptyRequiredParams(string $params, string $expectedException) + public function testSetNewShippingAddressWithMissedRequiredParameters(string $input, string $message) { $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); @@ -229,12 +228,10 @@ public function testSetNewShippingAddressWithEmptyRequiredParams(string $params, mutation { setShippingAddressesOnCart( input: { - cart_id: "$maskedQuoteId" + cart_id: "{$maskedQuoteId}" shipping_addresses: [ { - address: { - $params - } + {$input} } ] } @@ -247,12 +244,27 @@ public function testSetNewShippingAddressWithEmptyRequiredParams(string $params, } } QUERY; - $this->expectExceptionMessage( - $expectedException - ); + $this->expectExceptionMessage($message); $this->graphQlQuery($query); } + /** + * @return array + */ + public function dataProviderUpdateWithMissedRequiredParameters() + { + return [ + 'shipping_addresses' => [ + '', + 'The shipping address must contain either "customer_address_id" or "address".', + ], + 'missed_city' => [ + 'address: { save_in_address_book: false }', + 'Field CartAddressInput.city of required type String! was not provided' + ] + ]; + } + /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @expectedException \Exception @@ -307,54 +319,6 @@ public function testSetMultipleNewShippingAddresses() } } QUERY; - /** @var \Magento\Config\Model\ResourceModel\Config $config */ - $config = ObjectManager::getInstance()->get(\Magento\Config\Model\ResourceModel\Config::class); - $config->saveConfig( - Data::XML_PATH_CHECKOUT_MULTIPLE_AVAILABLE, - null, - ScopeConfigInterface::SCOPE_TYPE_DEFAULT, - 0 - ); - /** @var \Magento\Framework\App\Config\ReinitableConfigInterface $config */ - $config = ObjectManager::getInstance()->get(\Magento\Framework\App\Config\ReinitableConfigInterface::class); - $config->reinit(); - - $this->graphQlQuery($query); - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @expectedException \Exception - */ - public function testSetShippingAddressIfCustomerIsNotOwnerOfCart() - { - $maskedQuoteId = $this->assignQuoteToCustomer('test_order_with_simple_product_without_address', 1); - - $query = <<<QUERY -mutation { - setShippingAddressesOnCart( - input: { - cart_id: "$maskedQuoteId" - shipping_addresses: [ - { - customer_address_id: 1 - } - ] - } - ) { - cart { - shipping_addresses { - postcode - } - } - } -} -QUERY; - $this->expectExceptionMessage( - "The current user cannot perform operations on cart \"$maskedQuoteId\"" - ); - $this->graphQlQuery($query); } @@ -407,36 +371,4 @@ private function assignQuoteToCustomer( $this->quoteResource->save($quote); return $this->quoteIdToMaskedId->execute((int)$quote->getId()); } - - /** - * TODO: currently only the city param is required, do we need to add at least ZIP code? - * @return array - */ - public function requestWithoutRequiredParamsDataProvider() - { - return [ - [ - 'save_in_address_book: false', - 'Field CartAddressInput.city of required type String! was not provided' - ] - ]; - } - - public function tearDown() - { - /** @var \Magento\Config\Model\ResourceModel\Config $config */ - $config = ObjectManager::getInstance()->get(\Magento\Config\Model\ResourceModel\Config::class); - - //default state of multishipping config - $config->saveConfig( - Data::XML_PATH_CHECKOUT_MULTIPLE_AVAILABLE, - 1, - ScopeConfigInterface::SCOPE_TYPE_DEFAULT, - 0 - ); - - /** @var \Magento\Framework\App\Config\ReinitableConfigInterface $config */ - $config = ObjectManager::getInstance()->get(\Magento\Framework\App\Config\ReinitableConfigInterface::class); - $config->reinit(); - } } From 31166c95234c3fb5fce096ea5328a3fe854bb730 Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Tue, 12 Mar 2019 19:25:39 -0500 Subject: [PATCH 239/345] GraphQL-419: Test coverage: GetAvailablePaymentMethodsTest for Customer --- .../GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php | 3 +-- .../Payment/_files/disable_all_active_payment_methods.php | 1 - 2 files changed, 1 insertion(+), 3 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 34cd6ae503c22..a7287ada093b8 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 @@ -134,8 +134,7 @@ public function testGetPaymentMethodsOfNonExistentCart() */ private function getQuery( string $maskedQuoteId - ): string - { + ): string { return <<<QUERY { cart(cart_id: "$maskedQuoteId") { diff --git a/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods.php b/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods.php index 37b67fbf6c44d..ac811e67c580a 100644 --- a/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods.php +++ b/dev/tests/integration/testsuite/Magento/Payment/_files/disable_all_active_payment_methods.php @@ -32,4 +32,3 @@ $defConfig->setDataByPath($key, $value); $defConfig->save(); } - From 6df79895e723c6cc203a8caf485a1b8504923acd Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Tue, 12 Mar 2019 20:23:54 -0500 Subject: [PATCH 240/345] GraphQL-360: Unskip and fix testGetSimpleProductsFromCategory --- .../testsuite/Magento/GraphQl/Catalog/CategoryTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php index 3edc4a797b3e5..cbce53325e9e6 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryTest.php @@ -16,7 +16,6 @@ use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\TestFramework\ObjectManager; - class CategoryTest extends GraphQlAbstract { /** From fb4f930be721474c7aea8f16c59505e8685d1f62 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Tue, 12 Mar 2019 20:41:50 -0500 Subject: [PATCH 241/345] MC-15331: Unable to place order with authorize.net direct post credit card --- .../Magento/Authorizenet/Model/Directpost.php | 9 +- .../Authorizenet/Model/Directpost/Request.php | 119 +++++++++++++++--- .../Model/Directpost/Response.php | 72 +++++++++-- .../Unit/Model/Directpost/RequestTest.php | 80 ++++++++++++ .../Unit/Model/Directpost/ResponseTest.php | 78 +++++------- .../Authorizenet/etc/adminhtml/system.xml | 4 + app/code/Magento/Authorizenet/etc/config.xml | 1 + 7 files changed, 292 insertions(+), 71 deletions(-) create mode 100644 app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/RequestTest.php diff --git a/app/code/Magento/Authorizenet/Model/Directpost.php b/app/code/Magento/Authorizenet/Model/Directpost.php index 5bc9335d24439..946ec8ba01a0e 100644 --- a/app/code/Magento/Authorizenet/Model/Directpost.php +++ b/app/code/Magento/Authorizenet/Model/Directpost.php @@ -546,15 +546,16 @@ public function setResponseData(array $postData) public function validateResponse() { $response = $this->getResponse(); - //md5 check - if (!$this->getConfigData('trans_md5') - || !$this->getConfigData('login') - || !$response->isValidHash($this->getConfigData('trans_md5'), $this->getConfigData('login')) + $hashConfigKey = !empty($response->getData('x_SHA2_Hash')) ? 'signature_key' : 'trans_md5'; + + //hash check + if (!$response->isValidHash($this->getConfigData($hashConfigKey), $this->getConfigData('login')) ) { throw new \Magento\Framework\Exception\LocalizedException( __('The transaction was declined because the response hash validation failed.') ); } + return true; } diff --git a/app/code/Magento/Authorizenet/Model/Directpost/Request.php b/app/code/Magento/Authorizenet/Model/Directpost/Request.php index 357385e5c8c79..d518af4e04f55 100644 --- a/app/code/Magento/Authorizenet/Model/Directpost/Request.php +++ b/app/code/Magento/Authorizenet/Model/Directpost/Request.php @@ -8,6 +8,8 @@ namespace Magento\Authorizenet\Model\Directpost; use Magento\Authorizenet\Model\Request as AuthorizenetRequest; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Intl\DateTimeFactory; /** * Authorize.net request model for DirectPost model @@ -20,10 +22,35 @@ class Request extends AuthorizenetRequest */ protected $_transKey = null; + /** + * Hexadecimal signature key. + * + * @var string + */ + private $signatureKey = ''; + + /** + * @var DateTimeFactory + */ + private $dateTimeFactory; + + /** + * @param array $data + * @param DateTimeFactory $dateTimeFactory + */ + public function __construct( + array $data = [], + DateTimeFactory $dateTimeFactory = null + ) { + $this->dateTimeFactory = $dateTimeFactory ?? ObjectManager::getInstance() + ->get(DateTimeFactory::class); + parent::__construct($data); + } + /** * Return merchant transaction key. * - * Needed to generate sign. + * Needed to generate MD5 sign. * * @return string */ @@ -35,7 +62,7 @@ protected function _getTransactionKey() /** * Set merchant transaction key. * - * Needed to generate sign. + * Needed to generate MD5 sign. * * @param string $transKey * @return $this @@ -47,7 +74,7 @@ protected function _setTransactionKey($transKey) } /** - * Generates the fingerprint for request. + * Generates the MD5 fingerprint for request. * * @param string $merchantApiLoginId * @param string $merchantTransactionKey @@ -67,7 +94,7 @@ public function generateRequestSign( ) { return hash_hmac( "md5", - $merchantApiLoginId . "^" . $fpSequence . "^" . $fpTimestamp . "^" . $amount . "^" . $currencyCode, + $merchantApiLoginId . '^' . $fpSequence . '^' . $fpTimestamp . '^' . $amount . '^' . $currencyCode, $merchantTransactionKey ); } @@ -82,7 +109,7 @@ public function setConstantData(\Magento\Authorizenet\Model\Directpost $paymentM { $this->setXVersion('3.1')->setXDelimData('FALSE')->setXRelayResponse('TRUE'); - $this->setXTestRequest($paymentMethod->getConfigData('test') ? 'TRUE' : 'FALSE'); + $this->setSignatureKey($paymentMethod->getConfigData('signature_key')); $this->setXLogin($paymentMethod->getConfigData('login')) ->setXMethod(\Magento\Authorizenet\Model\Authorizenet::REQUEST_METHOD_CC) @@ -173,17 +200,81 @@ public function setDataFromOrder( */ public function signRequestData() { - $fpTimestamp = time(); - $hash = $this->generateRequestSign( - $this->getXLogin(), - $this->_getTransactionKey(), - $this->getXAmount(), - $this->getXCurrencyCode(), - $this->getXFpSequence(), - $fpTimestamp - ); + $fpDate = $this->dateTimeFactory->create('now', new \DateTimeZone('UTC')); + $fpTimestamp = $fpDate->getTimestamp(); + + if (!empty($this->getSignatureKey())) { + $hash = $this->generateSha2RequestSign( + (string)$this->getXLogin(), + (string)$this->getSignatureKey(), + (string)$this->getXAmount(), + (string)$this->getXCurrencyCode(), + (string)$this->getXFpSequence(), + $fpTimestamp + ); + } else { + $hash = $this->generateRequestSign( + $this->getXLogin(), + $this->_getTransactionKey(), + $this->getXAmount(), + $this->getXCurrencyCode(), + $this->getXFpSequence(), + $fpTimestamp + ); + } + $this->setXFpTimestamp($fpTimestamp); $this->setXFpHash($hash); + return $this; } + + /** + * Generates the SHA2 fingerprint for request. + * + * @param string $merchantApiLoginId + * @param string $merchantSignatureKey + * @param string $amount + * @param string $currencyCode + * @param string $fpSequence An invoice number or random number. + * @param int $fpTimestamp + * @return string The fingerprint. + */ + private function generateSha2RequestSign( + string $merchantApiLoginId, + string $merchantSignatureKey, + string $amount, + string $currencyCode, + string $fpSequence, + int $fpTimestamp + ): string { + $message = $merchantApiLoginId . '^' . $fpSequence . '^' . $fpTimestamp . '^' . $amount . '^' . $currencyCode; + + return strtoupper(hash_hmac('sha512', $message, pack('H*', $merchantSignatureKey))); + } + + /** + * Return merchant hexadecimal signature key. + * + * Needed to generate SHA2 sign. + * + * @return string + */ + private function getSignatureKey(): string + { + return $this->signatureKey; + } + + /** + * Set merchant hexadecimal signature key. + * + * Needed to generate SHA2 sign. + * + * @param string $signatureKey + * @return void + */ + private function setSignatureKey(string $signatureKey) + { + $this->signatureKey = $signatureKey; + } } diff --git a/app/code/Magento/Authorizenet/Model/Directpost/Response.php b/app/code/Magento/Authorizenet/Model/Directpost/Response.php index 1c713a159c3ad..b5604a78cb9cd 100644 --- a/app/code/Magento/Authorizenet/Model/Directpost/Response.php +++ b/app/code/Magento/Authorizenet/Model/Directpost/Response.php @@ -27,25 +27,31 @@ class Response extends AuthorizenetResponse */ public function generateHash($merchantMd5, $merchantApiLogin, $amount, $transactionId) { - if (!$amount) { - $amount = '0.00'; - } - return strtoupper(md5($merchantMd5 . $merchantApiLogin . $transactionId . $amount)); } /** * Return if is valid order id. * - * @param string $merchantMd5 + * @param string $storedHash * @param string $merchantApiLogin * @return bool */ - public function isValidHash($merchantMd5, $merchantApiLogin) + public function isValidHash($storedHash, $merchantApiLogin) { - $hash = $this->generateHash($merchantMd5, $merchantApiLogin, $this->getXAmount(), $this->getXTransId()); + if (empty($this->getData('x_amount'))) { + $this->setData('x_amount', '0.00'); + } - return Security::compareStrings($hash, $this->getData('x_MD5_Hash')); + if (!empty($this->getData('x_SHA2_Hash'))) { + $hash = $this->generateSha2Hash($storedHash); + return Security::compareStrings($hash, $this->getData('x_SHA2_Hash')); + } elseif (!empty($this->getData('x_MD5_Hash'))) { + $hash = $this->generateHash($storedHash, $merchantApiLogin, $this->getXAmount(), $this->getXTransId()); + return Security::compareStrings($hash, $this->getData('x_MD5_Hash')); + } + + return false; } /** @@ -57,4 +63,54 @@ public function isApproved() { return $this->getXResponseCode() == \Magento\Authorizenet\Model\Directpost::RESPONSE_CODE_APPROVED; } + + /** + * Generates an SHA2 hash to compare against AuthNet's. + * + * @param string $signatureKey + * @return string + * @see https://support.authorize.net/s/article/MD5-Hash-End-of-Life-Signature-Key-Replacement + */ + private function generateSha2Hash(string $signatureKey): string + { + $hashFields = [ + 'x_trans_id', + 'x_test_request', + 'x_response_code', + 'x_auth_code', + 'x_cvv2_resp_code', + 'x_cavv_response', + 'x_avs_code', + 'x_method', + 'x_account_number', + 'x_amount', + 'x_company', + 'x_first_name', + 'x_last_name', + 'x_address', + 'x_city', + 'x_state', + 'x_zip', + 'x_country', + 'x_phone', + 'x_fax', + 'x_email', + 'x_ship_to_company', + 'x_ship_to_first_name', + 'x_ship_to_last_name', + 'x_ship_to_address', + 'x_ship_to_city', + 'x_ship_to_state', + 'x_ship_to_zip', + 'x_ship_to_country', + 'x_invoice_num', + ]; + + $message = '^'; + foreach ($hashFields as $field) { + $message .= ($this->getData($field) ?? '') . '^'; + } + + return strtoupper(hash_hmac('sha512', $message, pack('H*', $signatureKey))); + } } diff --git a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/RequestTest.php b/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/RequestTest.php new file mode 100644 index 0000000000000..94d8f3a0d27a7 --- /dev/null +++ b/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/RequestTest.php @@ -0,0 +1,80 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Authorizenet\Test\Unit\Model\Directpost; + +use Magento\Authorizenet\Model\Directpost\Request; +use Magento\Framework\Intl\DateTimeFactory; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +class RequestTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var DateTimeFactory|MockObject + */ + private $dateTimeFactory; + + /** + * @var Request + */ + private $requestModel; + + protected function setUp() + { + $this->dateTimeFactory = $this->getMockBuilder(DateTimeFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $dateTime = new \DateTime('2016-07-05 00:00:00', new \DateTimeZone('UTC')); + $this->dateTimeFactory->method('create') + ->willReturn($dateTime); + + $this->requestModel = new Request([], $this->dateTimeFactory); + } + + /** + * @param string $signatureKey + * @param string $expectedHash + * @dataProvider signRequestDataProvider + */ + public function testSignRequestData(string $signatureKey, string $expectedHash) + { + /** @var \Magento\Authorizenet\Model\Directpost $paymentMethod */ + $paymentMethod = $this->createMock(\Magento\Authorizenet\Model\Directpost::class); + $paymentMethod->method('getConfigData') + ->willReturnMap( + [ + ['test', null, true], + ['login', null, 'login'], + ['trans_key', null, 'trans_key'], + ['signature_key', null, $signatureKey], + ] + ); + + $this->requestModel->setConstantData($paymentMethod); + $this->requestModel->signRequestData(); + $signHash = $this->requestModel->getXFpHash(); + + $this->assertEquals($expectedHash, $signHash); + } + + /** + * @return array + */ + public function signRequestDataProvider() + { + return [ + [ + 'signatureKey' => '3EAFCE5697C1B4B9748385C1FCD29D86F3B9B41C7EED85A3A01DFF65' . + '70C8C29373C2A153355C3313CDF4AF723C0036DBF244A0821713A910024EE85547CEF37F', + 'expectedHash' => '719ED94DF5CF3510CB5531E8115462C8F12CBCC8E917BD809E8D40B4FF06' . + '1E14953554403DD9813CCCE0F31B184EB4DEF558E9C0747505A0C25420372DB00BE1' + ], + [ + 'signatureKey' => '', + 'expectedHash' => '3656211f2c41d1e4c083606f326c0460' + ], + ]; + } +} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/ResponseTest.php b/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/ResponseTest.php index 15c7eecb09a69..ff4aa8b5ee361 100644 --- a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/ResponseTest.php +++ b/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/ResponseTest.php @@ -13,53 +13,16 @@ class ResponseTest extends \PHPUnit\Framework\TestCase /** * @var \Magento\Authorizenet\Model\Directpost\Response */ - protected $responseModel; + private $responseModel; protected function setUp() { $objectManager = new ObjectManager($this); - $this->responseModel = $objectManager->getObject(\Magento\Authorizenet\Model\Directpost\Response::class); - } - - /** - * @param string $merchantMd5 - * @param string $merchantApiLogin - * @param float|null $amount - * @param float|string $amountTestFunc - * @param string $transactionId - * @dataProvider generateHashDataProvider - */ - public function testGenerateHash($merchantMd5, $merchantApiLogin, $amount, $amountTestFunc, $transactionId) - { - $this->assertEquals( - $this->generateHash($merchantMd5, $merchantApiLogin, $amountTestFunc, $transactionId), - $this->responseModel->generateHash($merchantMd5, $merchantApiLogin, $amount, $transactionId) + $this->responseModel = $objectManager->getObject( + \Magento\Authorizenet\Model\Directpost\Response::class ); } - /** - * @return array - */ - public function generateHashDataProvider() - { - return [ - [ - 'merchantMd5' => 'FCD7F001E9274FDEFB14BFF91C799306', - 'merchantApiLogin' => 'Magento', - 'amount' => null, - 'amountTestFunc' => '0.00', - 'transactionId' => '1' - ], - [ - 'merchantMd5' => '8AEF4E508261A287C3E2F544720FCA3A', - 'merchantApiLogin' => 'Magento2', - 'amount' => 100.50, - 'amountTestFunc' => 100.50, - 'transactionId' => '2' - ] - ]; - } - /** * @param $merchantMd5 * @param $merchantApiLogin @@ -73,7 +36,8 @@ protected function generateHash($merchantMd5, $merchantApiLogin, $amount, $trans } /** - * @param string $merchantMd5 + * @param string $storedHash + * @param string $hashKey * @param string $merchantApiLogin * @param float|null $amount * @param string $transactionId @@ -81,12 +45,21 @@ protected function generateHash($merchantMd5, $merchantApiLogin, $amount, $trans * @param bool $expectedValue * @dataProvider isValidHashDataProvider */ - public function testIsValidHash($merchantMd5, $merchantApiLogin, $amount, $transactionId, $hash, $expectedValue) - { + public function testIsValidHash( + string $storedHash, + string $hashKey, + string $merchantApiLogin, + $amount, + string $transactionId, + string $hash, + bool $expectedValue + ) { $this->responseModel->setXAmount($amount); $this->responseModel->setXTransId($transactionId); - $this->responseModel->setData('x_MD5_Hash', $hash); - $this->assertEquals($expectedValue, $this->responseModel->isValidHash($merchantMd5, $merchantApiLogin)); + $this->responseModel->setData($hashKey, $hash); + $result = $this->responseModel->isValidHash($storedHash, $merchantApiLogin); + + $this->assertEquals($expectedValue, $result); } /** @@ -94,9 +67,14 @@ public function testIsValidHash($merchantMd5, $merchantApiLogin, $amount, $trans */ public function isValidHashDataProvider() { + $signatureKey = '3EAFCE5697C1B4B9748385C1FCD29D86F3B9B41C7EED85A3A01DFF6570C8C' . + '29373C2A153355C3313CDF4AF723C0036DBF244A0821713A910024EE85547CEF37F'; + $expectedSha2Hash = '368D48E0CD1274BF41C059138DA69985594021A4AD5B4C5526AE88C8F' . + '7C5769B13C5E1E4358900F3E51076FB69D14B0A797904C22E8A11A52AA49CDE5FBB703C'; return [ [ 'merchantMd5' => 'FCD7F001E9274FDEFB14BFF91C799306', + 'hashKey' => 'x_MD5_Hash', 'merchantApiLogin' => 'Magento', 'amount' => null, 'transactionId' => '1', @@ -105,11 +83,21 @@ public function isValidHashDataProvider() ], [ 'merchantMd5' => '8AEF4E508261A287C3E2F544720FCA3A', + 'hashKey' => 'x_MD5_Hash', 'merchantApiLogin' => 'Magento2', 'amount' => 100.50, 'transactionId' => '2', 'hash' => '1F24A4EC9A169B2B2A072A5F168E16DC', 'expectedValue' => false + ], + [ + 'signatureKey' => $signatureKey, + 'hashKey' => 'x_SHA2_Hash', + 'merchantApiLogin' => 'Magento2', + 'amount' => 100.50, + 'transactionId' => '2', + 'hash' => $expectedSha2Hash, + 'expectedValue' => true ] ]; } diff --git a/app/code/Magento/Authorizenet/etc/adminhtml/system.xml b/app/code/Magento/Authorizenet/etc/adminhtml/system.xml index 28bf6945c8b81..fc86c0d2dc68d 100644 --- a/app/code/Magento/Authorizenet/etc/adminhtml/system.xml +++ b/app/code/Magento/Authorizenet/etc/adminhtml/system.xml @@ -29,6 +29,10 @@ <label>Transaction Key</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> + <field id="signature_key" translate="label" type="obscure" sortOrder="55" showInDefault="1" showInWebsite="1" showInStore="0"> + <label>Signature Key</label> + <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> + </field> <field id="trans_md5" translate="label" type="obscure" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Merchant MD5</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> diff --git a/app/code/Magento/Authorizenet/etc/config.xml b/app/code/Magento/Authorizenet/etc/config.xml index 02dca74023e22..60356460f553f 100644 --- a/app/code/Magento/Authorizenet/etc/config.xml +++ b/app/code/Magento/Authorizenet/etc/config.xml @@ -22,6 +22,7 @@ <title>Credit Card Direct Post (Authorize.Net) + 0 USD 1 From 7471fea4e1176f2e3d49139bbee25feabdedc53a Mon Sep 17 00:00:00 2001 From: Oleksandr Shmyheliuk Date: Tue, 12 Mar 2019 22:54:27 -0500 Subject: [PATCH 242/345] 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 89cb3e4b30345..1c02d24f7e99c 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 66ec6e7418afd..854bf50e0af2f 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 75edc8cb4f6ee..396381960e544 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 aa25ca575a2f379dc69f2b7edc77c15ebf60f118 Mon Sep 17 00:00:00 2001 From: Evgeny Petrov Date: Wed, 13 Mar 2019 10:01:26 +0300 Subject: [PATCH 243/345] 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 4570dfb2af5ec..2e55964560588 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 986ab517ceca1..1584b922abaa9 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 3819dc1dcc2aa19c3811e33b88c8f31f599f8428 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko Date: Wed, 13 Mar 2019 10:58:31 +0200 Subject: [PATCH 244/345] Vitalii Boiko: fixed wrong proxing in the inventory observer --- app/code/Magento/CatalogInventory/etc/di.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogInventory/etc/di.xml b/app/code/Magento/CatalogInventory/etc/di.xml index 8d57fab843f4c..51a5b46b59d22 100644 --- a/app/code/Magento/CatalogInventory/etc/di.xml +++ b/app/code/Magento/CatalogInventory/etc/di.xml @@ -44,7 +44,7 @@ - Magento\CatalogInventory\Model\ResourceModel\Stock\Proxy + Magento\CatalogInventory\Model\ResourceModel\Stock\Item\Proxy From 46bbdd0e3d5796e427899b96c189888423cb55d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= Date: Wed, 13 Mar 2019 11:38:58 +0200 Subject: [PATCH 245/345] 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 db9da71ebf739..4b1a68624e547 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 @@
-
+
+ @@ -67,6 +68,7 @@ + From b9c43adcb19eb5c63fcb95fe355f46fff356c720 Mon Sep 17 00:00:00 2001 From: Alex Calandra Date: Tue, 12 Mar 2019 15:31:06 -0500 Subject: [PATCH 252/345] 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 05b7dfeeb3953..03edc69e6d625 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 @@ - diff --git a/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml index b4bcdaadf9a09..c3c92dc59c288 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 @@ - - @@ -69,7 +67,6 @@ - From 8e87253b92941595a508e6d92f62d3c999e1e1f7 Mon Sep 17 00:00:00 2001 From: Alex Calandra Date: Wed, 13 Mar 2019 08:32:24 -0500 Subject: [PATCH 253/345] 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 c3c92dc59c288..e6ab1c130606b 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 @@ + @@ -67,6 +68,7 @@ + From 4748c2d1a7d504bd6b79e1c1f9c3b2c245ef8580 Mon Sep 17 00:00:00 2001 From: Valerii Naida Date: Wed, 13 Mar 2019 11:11:38 -0500 Subject: [PATCH 254/345] GraphQL-427: Test coverage: SetShippingAddressOnCartTest --- .../GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9e13476c57c32..ae184b8930c07 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 @@ -280,7 +280,7 @@ public function testSetNewShippingAddressAndFromAddressBookAtSameTime() * @magentoApiDataFixture Magento/Customer/_files/customer_address.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @expectedException \Exception - * @expectedExceptionMessage The current user cannot use address with ID "1" + * @expectedExceptionMessage Current customer does not have permission to address with ID "1" */ public function testSetShippingAddressIfCustomerIsNotOwnerOfAddress() { From a32d27b3b5360ba72380da65fac6c7f091f7124f Mon Sep 17 00:00:00 2001 From: nmalevanec Date: Wed, 13 Mar 2019 18:15:54 +0200 Subject: [PATCH 255/345] Fix static and functional tests. --- .../Mftf/Test/AdminSortingByWebsitesTest.xml | 67 +++++++++---------- .../Ui/Component/Listing/Columns/Websites.php | 35 ++++++---- 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml index c4b08bc55d98a..234a7c69913c9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml @@ -15,71 +15,64 @@ + + + + + + + + + + + + + + + + - - + - - - - - - - - - - + + + + + + + + + - - - - + - - - - - - - - - - - - - - - - - - + + + - - - diff --git a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php index 5571f6e70fa74..494b77724e5b7 100644 --- a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php +++ b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Websites.php @@ -1,16 +1,21 @@ -storeManager = $storeManager; - $this->_resourceHelper = $resourceHelper ?: $objectManager->get(Helper::class); + $this->resourceHelper = $resourceHelper ?: $objectManager->get(Helper::class); } /** - * {@inheritdoc} + * @inheritdoc + * * @deprecated 101.0.0 */ public function prepareDataSource(array $dataSource) @@ -86,9 +92,10 @@ public function prepareDataSource(array $dataSource) return $dataSource; } - + /** - * Prepare component configuration + * Prepare component configuration. + * * @return void */ public function prepare() @@ -100,7 +107,7 @@ public function prepare() } /** - * Apply sorting + * Apply sorting. * * @return void */ @@ -131,13 +138,13 @@ protected function applySorting() 'left' ) ->groupByAttribute('entity_id'); - $this->_resourceHelper->addGroupConcatColumn( + $this->resourceHelper->addGroupConcatColumn( $collection->getSelect(), - self::WEBSITE_NAMES, + $this->websiteNames, 'name' ); - $collection->getSelect()->order(self::WEBSITE_NAMES . ' ' . $sorting['direction']); + $collection->getSelect()->order($this->websiteNames . ' ' . $sorting['direction']); } } } From 97a4be98b59c2ce564e057a0fb69f0b21d45a911 Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" Date: Wed, 13 Mar 2019 11:17:04 -0500 Subject: [PATCH 256/345] 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 7efd63fb7a4aa..74e7f40b1f062 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 e24a4c8538492cf1690e292cabf16a818177e39c Mon Sep 17 00:00:00 2001 From: Valerii Naida Date: Wed, 13 Mar 2019 11:22:28 -0500 Subject: [PATCH 257/345] GraphQL-420: Test coverage: GetAvailablePaymentMethodsTest for Guest --- .../Guest/GetAvailablePaymentMethodsTest.php | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) 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 ff2e79b61a3ab..c4dd9af490c99 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 @@ -47,11 +47,11 @@ protected function setUp() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php */ - public function testGetPaymentMethodsFromGuestCart() + public function testGetCartWithPaymentMethods() { $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId('test_order_with_simple_product_without_address'); - $query = $this->getCartAvailablePaymentMethodsQuery($maskedQuoteId); - $response = $this->graphQlQuery($query); + $query = $this->getQuery($maskedQuoteId); + $response = $this->graphQlQuery($query); self::assertArrayHasKey('cart', $response); @@ -73,10 +73,10 @@ public function testGetPaymentMethodsFromGuestCart() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php */ - public function testGetPaymentMethodsFromAnotherCustomerCart() + public function testGetPaymentMethodsFromCustomerCart() { $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId('test_order_1'); - $query = $this->getCartAvailablePaymentMethodsQuery($maskedQuoteId); + $query = $this->getQuery($maskedQuoteId); $this->expectExceptionMessage( "The current user cannot perform operations on cart \"$maskedQuoteId\"" @@ -91,8 +91,8 @@ public function testGetPaymentMethodsFromAnotherCustomerCart() public function testGetPaymentMethodsIfPaymentsAreNotSet() { $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId('test_order_with_simple_product_without_address'); - $query = $this->getCartAvailablePaymentMethodsQuery($maskedQuoteId); - $response = $this->graphQlQuery($query); + $query = $this->getQuery($maskedQuoteId); + $response = $this->graphQlQuery($query); self::assertEquals(0, count($response['cart']['available_payment_methods'])); } @@ -104,7 +104,7 @@ public function testGetPaymentMethodsIfPaymentsAreNotSet() public function testGetPaymentMethodsOfNonExistentCart() { $maskedQuoteId = 'non_existent_masked_id'; - $query = $this->getCartAvailablePaymentMethodsQuery($maskedQuoteId); + $query = $this->getQuery($maskedQuoteId); $this->graphQlQuery($query); } @@ -112,9 +112,9 @@ public function testGetPaymentMethodsOfNonExistentCart() * @param string $maskedQuoteId * @return string */ - private function getCartAvailablePaymentMethodsQuery( + private function getQuery( string $maskedQuoteId - ) : string { + ): string { return << Date: Wed, 13 Mar 2019 11:34:45 -0500 Subject: [PATCH 258/345] GraphQL-408: mutations addProductsToCart dosn't check if the cart is active --- .../QuoteGraphQl/Model/Cart/GetCartForUser.php | 4 ++-- .../GraphQl/Quote/Customer/GetCartTest.php | 4 ++-- .../GraphQl/Quote/Guest/GetCartTest.php | 18 ++++++++++++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php index ba948e2c4fc75..145e9c01980a5 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php @@ -69,9 +69,9 @@ public function execute(string $cartHash, ?int $customerId): Quote ); } - if (!$cart->getIsActive()) { + if (false === (bool)$cart->getIsActive()) { throw new GraphQlNoSuchEntityException( - __('Current customer does not have an active cart.') + __('Current user does not have an active cart.') ); } 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 5912ddf933bc0..4b1509eef354e 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 @@ -118,7 +118,7 @@ public function testGetNonExistentCart() * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @magentoApiDataFixture Magento/Customer/_files/customer.php * @expectedException \Exception - * @expectedExceptionMessage Current customer does not have an active cart. + * @expectedExceptionMessage Current user does not have an active cart. */ public function testGetInactiveCart() { @@ -127,7 +127,7 @@ public function testGetInactiveCart() $quote->setCustomerId(1); $quote->setIsActive(false); $this->quoteResource->save($quote); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$quote->getId()); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$quote->getId()); $query = $this->getCartQuery($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 b1d5e475c793e..916d5951b0ff2 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 @@ -97,6 +97,24 @@ public function testGetNonExistentCart() $this->graphQlQuery($query); } + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.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); + + $this->graphQlQuery($query); + } + /** * @param string $maskedQuoteId * @return string From f14d84dc8045e39de89f47b8015ea31ebc2ba6af Mon Sep 17 00:00:00 2001 From: Iryna Lagno Date: Wed, 13 Mar 2019 10:48:00 -0500 Subject: [PATCH 259/345] MC-15378: Customer visitor model doesn't have all data on first get request --- app/code/Magento/Customer/Model/Visitor.php | 4 ---- .../Magento/Customer/Controller/AccountTest.php | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Customer/Model/Visitor.php b/app/code/Magento/Customer/Model/Visitor.php index 9caa2988c5a94..e334769331e85 100644 --- a/app/code/Magento/Customer/Model/Visitor.php +++ b/app/code/Magento/Customer/Model/Visitor.php @@ -168,10 +168,6 @@ public function initByRequest($observer) $this->setLastVisitAt((new \DateTime())->format(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT)); - // prevent saving Visitor for safe methods, e.g. GET request - if ($this->requestSafety->isSafeMethod()) { - return $this; - } if (!$this->getId()) { $this->setSessionId($this->session->getSessionId()); $this->save(); diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php index ea7a7710acbc3..10b632c002475 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php @@ -751,6 +751,21 @@ public function loginPostRedirectDataProvider() ]; } + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + * @magentoAppArea frontend + */ + public function testCheckVisitorModel() + { + /** @var \Magento\Customer\Model\Visitor $visitor */ + $visitor = $this->_objectManager->get(\Magento\Customer\Model\Visitor::class); + $this->login(1); + $this->assertNull($visitor->getId()); + $this->dispatch('customer/account/index'); + $this->assertNotNull($visitor->getId()); + } + /** * @param string $email * @return void From f59817b06c491d8f83ff240123bca399004f86cf Mon Sep 17 00:00:00 2001 From: nmalevanec Date: Wed, 13 Mar 2019 19:29:17 +0200 Subject: [PATCH 260/345] Fix functional tests. --- .../Mtf/Client/Element/ConditionsElement.php | 15 ++++++++++----- .../Mtf/Client/Element/SelectstateElement.php | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectstateElement.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 6dbf2b1aa6a12..b3e5331af4be6 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php +++ b/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php @@ -265,7 +265,7 @@ protected function addSingleCondition($condition, ElementInterface $context) $this->addCondition($condition['type'], $context); $createdCondition = $context->find($this->created, Locator::SELECTOR_XPATH); $this->waitForCondition($createdCondition); - $this->fillCondition($condition['rules'], $createdCondition); + $this->fillCondition($condition['rules'], $createdCondition, $condition['type']); } /** @@ -306,13 +306,14 @@ protected function addCondition($type, ElementInterface $context) * * @param array $rules * @param ElementInterface $element + * @param string|null $type * @return void * @throws \Exception * * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ - protected function fillCondition(array $rules, ElementInterface $element) + protected function fillCondition(array $rules, ElementInterface $element, $type = null) { $this->resetKeyParam(); foreach ($rules as $rule) { @@ -333,7 +334,7 @@ protected function fillCondition(array $rules, ElementInterface $element) if ($this->fillGrid($rule, $param)) { $isSet = true; - } elseif ($this->fillSelect($rule, $param)) { + } elseif ($this->fillSelect($rule, $param, $type)) { $isSet = true; } elseif ($this->fillText($rule, $param)) { $isSet = true; @@ -390,11 +391,15 @@ protected function fillGrid($rule, ElementInterface $param) * * @param string $rule * @param ElementInterface $param + * @param string|null $type * @return bool */ - protected function fillSelect($rule, ElementInterface $param) + protected function fillSelect($rule, ElementInterface $param, $type = null) { - $value = $param->find('select', Locator::SELECTOR_TAG_NAME, 'select'); + //Avoid confusion between regions like: "Baja California" and "California". + $value = $type === 'Shipping State/Province' + ? $param->find('select', Locator::SELECTOR_TAG_NAME, 'selectstate') + : $param->find('select', Locator::SELECTOR_TAG_NAME, 'select'); if ($value->isVisible()) { $value->setValue($rule); $this->click(); diff --git a/dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectstateElement.php b/dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectstateElement.php new file mode 100644 index 0000000000000..a21353f46c1ca --- /dev/null +++ b/dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectstateElement.php @@ -0,0 +1,19 @@ + Date: Wed, 13 Mar 2019 13:21:45 -0500 Subject: [PATCH 261/345] MC-15378: Customer visitor model doesn't have all data on first get request --- app/code/Magento/Customer/Model/Visitor.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Customer/Model/Visitor.php b/app/code/Magento/Customer/Model/Visitor.php index e334769331e85..4f129f05aa82c 100644 --- a/app/code/Magento/Customer/Model/Visitor.php +++ b/app/code/Magento/Customer/Model/Visitor.php @@ -14,6 +14,7 @@ * * @package Magento\Customer\Model * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class Visitor extends \Magento\Framework\Model\AbstractModel { From 58831beb8e004aae117473a7812ce6f7f992d123 Mon Sep 17 00:00:00 2001 From: Valerii Naida Date: Wed, 13 Mar 2019 13:31:19 -0500 Subject: [PATCH 262/345] GraphQL-424: Test coverage: Set OfflineShipping methods on Cart --- .../SetOfflineShippingMethodsOnCartTest.php | 163 +++++++----------- .../enable_offline_shipping_methods.php} | 8 +- ...ble_offline_shipping_methods_rollback.php} | 4 - 3 files changed, 69 insertions(+), 106 deletions(-) rename dev/tests/integration/testsuite/Magento/{Checkout/_files/enable_all_shipping_methods.php => OfflineShipping/_files/enable_offline_shipping_methods.php} (68%) rename dev/tests/integration/testsuite/Magento/{Checkout/_files/enable_all_shipping_methods_rollback.php => OfflineShipping/_files/enable_offline_shipping_methods_rollback.php} (75%) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php index b053820c8a7a7..b469e93c412f0 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php @@ -53,22 +53,52 @@ protected function setUp() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/OfflineShipping/_files/enable_offline_shipping_methods.php * @magentoApiDataFixture Magento/OfflineShipping/_files/tablerates_weight.php - * @magentoApiDataFixture Magento/Checkout/_files/enable_all_shipping_methods.php - * @dataProvider offlineShippingMethodDataProvider() - * @param string $carrier - * @param string $method + * + * @param string $carrierCode + * @param string $methodCode * @param float $amount * @param string $label + * @dataProvider offlineShippingMethodDataProvider */ - public function testSetOfflineShippingMethod(string $carrier, string $method, float $amount, string $label) + public function testSetOfflineShippingMethod(string $carrierCode, string $methodCode, float $amount, string $label) { - $this->setShippingMethodAndCheckResponse( - $carrier, - $method, - $amount, - $label + $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->getQuery( + $maskedQuoteId, + $shippingAddressId, + $carrierCode, + $methodCode ); + + $response = $this->sendRequestWithToken($query); + + $addressesInformation = $response['setShippingMethodsOnCart']['cart']['shipping_addresses']; + self::assertEquals($addressesInformation[0]['selected_shipping_method']['carrier_code'], $carrierCode); + self::assertEquals($addressesInformation[0]['selected_shipping_method']['method_code'], $methodCode); + self::assertEquals($addressesInformation[0]['selected_shipping_method']['amount'], $amount); + self::assertEquals($addressesInformation[0]['selected_shipping_method']['label'], $label); + } + + /** + * @return array + */ + public function offlineShippingMethodDataProvider() + { + return [ + 'flatrate_flatrate' => ['flatrate', 'flatrate', 10, 'Flat Rate - Fixed'], + 'tablerate_bestway' => ['tablerate', 'bestway', 10, 'Best Way - Table Rate'], + 'freeshipping_freeshipping' => ['freeshipping', 'freeshipping', 0, 'Free Shipping - Free'], + ]; } /** @@ -89,23 +119,21 @@ public function testSetShippingMethodTwiceInOneRequest() $query = <<sendRequestWithToken($query); } - /** - * Data provider for base offline shipping methods - * - * @return array - */ - public function offlineShippingMethodDataProvider() - { - return [ - ['flatrate', 'flatrate', 10, 'Flat Rate - Fixed'], - ['tablerate', 'bestway', 10, 'Best Way - Table Rate'], - ['freeshipping', 'freeshipping', 0, 'Free Shipping - Free'] - ]; - } - - /** - * Send request for setting the requested shipping method and check the output - * - * @param string $shippingCarrierCode - * @param string $shippingMethodCode - * @param float $shippingAmount - * @param string $shippingLabel - * @throws \Magento\Framework\Exception\AuthenticationException - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ - private function setShippingMethodAndCheckResponse( - string $shippingCarrierCode, - string $shippingMethodCode, - float $shippingAmount, - string $shippingLabel - ) { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load( - $quote, - 'test_order_1', - 'reserved_order_id' - ); - $shippingAddress = $quote->getShippingAddress(); - $shippingAddressId = $shippingAddress->getId(); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$quote->getId()); - - $query = $this->getQuery( - $maskedQuoteId, - $shippingMethodCode, - $shippingCarrierCode, - $shippingAddressId - ); - - $response = $this->sendRequestWithToken($query); - - $addressesInformation = $response['setShippingMethodsOnCart']['cart']['shipping_addresses']; - self::assertEquals($addressesInformation[0]['selected_shipping_method']['carrier_code'], $shippingCarrierCode); - self::assertEquals($addressesInformation[0]['selected_shipping_method']['method_code'], $shippingMethodCode); - self::assertEquals($addressesInformation[0]['selected_shipping_method']['amount'], $shippingAmount); - self::assertEquals($addressesInformation[0]['selected_shipping_method']['label'], $shippingLabel); - } - /** * Generates query for setting the specified shipping method on cart * + * @param int $shippingAddressId * @param string $maskedQuoteId - * @param string $shippingMethodCode - * @param string $shippingCarrierCode - * @param string $shippingAddressId + * @param string $carrierCode + * @param string $methodCode * @return string */ private function getQuery( string $maskedQuoteId, - string $shippingMethodCode, - string $shippingCarrierCode, - string $shippingAddressId - ) : string { + int $shippingAddressId, + string $carrierCode, + string $methodCode + ): string { return <<graphQlQuery($query, [], '', $headerMap); } -} \ No newline at end of file +} diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/enable_all_shipping_methods.php b/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/enable_offline_shipping_methods.php similarity index 68% rename from dev/tests/integration/testsuite/Magento/Checkout/_files/enable_all_shipping_methods.php rename to dev/tests/integration/testsuite/Magento/OfflineShipping/_files/enable_offline_shipping_methods.php index dd48975aa2b09..08694bf8c7d0b 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/_files/enable_all_shipping_methods.php +++ b/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/enable_offline_shipping_methods.php @@ -11,16 +11,12 @@ use Magento\Framework\App\Config\ScopeConfigInterface; $objectManager = Bootstrap::getObjectManager(); -/** @var Writer $configWriter */ -$configWriter = $objectManager->create(WriterInterface::class); +/** @var Writer $configWriter */ +$configWriter = $objectManager->get(WriterInterface::class); $configWriter->save('carriers/flatrate/active', 1); $configWriter->save('carriers/tablerate/active', 1); $configWriter->save('carriers/freeshipping/active', 1); -$configWriter->save('carriers/ups/active', 1); -$configWriter->save('carriers/usps/active', 1); -$configWriter->save('carriers/fedex/active', 1); -$configWriter->save('carriers/dhl/active', 1); $scopeConfig = $objectManager->get(ScopeConfigInterface::class); $scopeConfig->clean(); diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/enable_all_shipping_methods_rollback.php b/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/enable_offline_shipping_methods_rollback.php similarity index 75% rename from dev/tests/integration/testsuite/Magento/Checkout/_files/enable_all_shipping_methods_rollback.php rename to dev/tests/integration/testsuite/Magento/OfflineShipping/_files/enable_offline_shipping_methods_rollback.php index 7a3ca79febf6d..31a16f42adfce 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/_files/enable_all_shipping_methods_rollback.php +++ b/dev/tests/integration/testsuite/Magento/OfflineShipping/_files/enable_offline_shipping_methods_rollback.php @@ -16,7 +16,3 @@ $configWriter->delete('carriers/flatrate/active'); $configWriter->delete('carriers/tablerate/active'); $configWriter->delete('carriers/freeshipping/active'); -$configWriter->delete('carriers/ups/active'); -$configWriter->delete('carriers/usps/active'); -$configWriter->delete('carriers/fedex/active'); -$configWriter->delete('carriers/dhl/active'); From 7352a465ee259b6caa2e1f54870374e8a049c53a Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" Date: Wed, 13 Mar 2019 13:34:56 -0500 Subject: [PATCH 263/345] 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 f63e5a681c42b..63182dd5624e6 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 6bf191c20a844..bd38d1451e1b6 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 ccc3c65491422..c2b3ce0498803 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 c1ad5bdcfc068..b2a515b198b11 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 c9a7d0013fe06..53fc5fd7c9275 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 d069499da4aab..07646c2aceda8 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 @@ - MC-13864 Consumer always read config from memory price_scope_website csv_with_advanced_pricing 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 b94f21371496a..40f535cd225a2 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 @@ - >MC-13864 Consumer always read config from memory default catalogProductSimple 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 93240586ec92c..4ab45eac88046 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 @@ - >MC-13864 Consumer always read config from memory default configurableProduct From 30128ecfb7c45f07ab81184ab4746d480afdf36c Mon Sep 17 00:00:00 2001 From: Valerii Naida Date: Wed, 13 Mar 2019 14:01:29 -0500 Subject: [PATCH 264/345] GraphQL-424: Test coverage: Set OfflineShipping methods on Cart --- .../OfflineShipping/SetOfflineShippingMethodsOnCartTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php index b469e93c412f0..7ebaba105a81a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php @@ -17,7 +17,7 @@ /** * Test for setting offline shipping methods on cart */ -class SetOfflineShippingOnCartTest extends GraphQlAbstract +class SetOfflineShippingMethodsOnCartTest extends GraphQlAbstract { /** * @var QuoteFactory From 8bf1909c9d9f7fbc7e155ada38bf453cdb2306ce Mon Sep 17 00:00:00 2001 From: Valerii Naida Date: Wed, 13 Mar 2019 14:11:32 -0500 Subject: [PATCH 265/345] GraphQL-424: Test coverage: Set OfflineShipping methods on Cart --- .../OfflineShipping/SetOfflineShippingMethodsOnCartTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php index 7ebaba105a81a..80acbbdb64230 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflineShipping/SetOfflineShippingMethodsOnCartTest.php @@ -103,7 +103,7 @@ public function offlineShippingMethodDataProvider() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - * @magentoApiDataFixture Magento/Checkout/_files/enable_all_shipping_methods.php + * @magentoApiDataFixture Magento/OfflineShipping/_files/enable_offline_shipping_methods.php */ public function testSetShippingMethodTwiceInOneRequest() { From fde5cebadcf2dd62c74077b2805a716eabf2df34 Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" Date: Wed, 13 Mar 2019 15:09:31 -0500 Subject: [PATCH 266/345] 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 57c067d2cae27..3312fb630ccda 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 ebaa32b95f48b..da408f105ccb6 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" Date: Wed, 13 Mar 2019 15:41:09 -0500 Subject: [PATCH 267/345] 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 3020e69c06399..fefe0d2c126e5 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 e55558482c1f3..b5cd056fb99ad 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 b1fd8a4d05cf9e949778e71f3f14076b3a9909c2 Mon Sep 17 00:00:00 2001 From: Nathan Smith Date: Wed, 13 Mar 2019 16:26:39 -0500 Subject: [PATCH 268/345] MC-15390: Integration tests failing on 2.3.1-release with extensions --- .../Product/FixedBundlePriceCalculatorWithDimensionTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php index b97bd9f822666..e9cb2f2d6c9d4 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php @@ -13,7 +13,6 @@ * @magentoDbIsolation disabled * @magentoIndexerDimensionMode catalog_product_price website_and_customer_group * @group indexer_dimension - * @magentoAppArea frontend */ class FixedBundlePriceCalculatorWithDimensionTest extends BundlePriceAbstract { From 28d5335e04de054a306d13edaec2e8b5817ab1f3 Mon Sep 17 00:00:00 2001 From: Yogesh Suhagiya Date: Thu, 14 Mar 2019 11:26:02 +0530 Subject: [PATCH 269/345] Removed extra whitespaces --- .../Attribute/Edit/Main/AbstractMain.php | 1 - app/code/Magento/Eav/Setup/EavSetup.php | 2 +- .../Entity/Attribute/Source/BooleanTest.php | 18 ++++++------------ 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php b/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php index c5a18a3de99c6..73b45f452d0fe 100644 --- a/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php +++ b/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php @@ -110,7 +110,6 @@ protected function _prepareForm() /** @var \Magento\Framework\Data\Form $form */ $form = $this->_formFactory->create( - ['data' => ['id' => 'edit_form', 'action' => $this->getData('action'), 'method' => 'post']] ); diff --git a/app/code/Magento/Eav/Setup/EavSetup.php b/app/code/Magento/Eav/Setup/EavSetup.php index 6e81ddc36e9c9..04e5957b921a9 100644 --- a/app/code/Magento/Eav/Setup/EavSetup.php +++ b/app/code/Magento/Eav/Setup/EavSetup.php @@ -1063,7 +1063,7 @@ private function _updateAttributeAdditionalData($entityTypeId, $id, $field, $val return $this; } } - + $attributeId = $this->getAttributeId($entityTypeId, $id); if (false === $attributeId) { throw new LocalizedException(__('Attribute with ID: "%1" does not exist', $id)); diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/BooleanTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/BooleanTest.php index ee972c27aa8a2..a524c41c69c34 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/BooleanTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/BooleanTest.php @@ -101,13 +101,11 @@ public function addValueSortToCollectionDataProvider() 'expectedJoinCondition' => [ 0 => [ 'requisites' => ['code_t1' => "table"], - 'condition' => - "e.entity_id=code_t1.entity_id AND code_t1.attribute_id='123' AND code_t1.store_id='0'", + 'condition' => "e.entity_id=code_t1.entity_id AND code_t1.attribute_id='123' AND code_t1.store_id='0'", ], 1 => [ 'requisites' => ['code_t2' => "table"], - 'condition' => - "e.entity_id=code_t2.entity_id AND code_t2.attribute_id='123' AND code_t2.store_id='12'", + 'condition' => "e.entity_id=code_t2.entity_id AND code_t2.attribute_id='123' AND code_t2.store_id='12'", ], ], 'expectedOrder' => 'IF(code_t2.value_id > 0, code_t2.value, code_t1.value) ASC', @@ -118,13 +116,11 @@ public function addValueSortToCollectionDataProvider() 'expectedJoinCondition' => [ 0 => [ 'requisites' => ['code_t1' => "table"], - 'condition' => - "e.entity_id=code_t1.entity_id AND code_t1.attribute_id='123' AND code_t1.store_id='0'", + 'condition' => "e.entity_id=code_t1.entity_id AND code_t1.attribute_id='123' AND code_t1.store_id='0'", ], 1 => [ 'requisites' => ['code_t2' => "table"], - 'condition' => - "e.entity_id=code_t2.entity_id AND code_t2.attribute_id='123' AND code_t2.store_id='12'", + 'condition' => "e.entity_id=code_t2.entity_id AND code_t2.attribute_id='123' AND code_t2.store_id='12'", ], ], 'expectedOrder' => 'IF(code_t2.value_id > 0, code_t2.value, code_t1.value) DESC', @@ -135,8 +131,7 @@ public function addValueSortToCollectionDataProvider() 'expectedJoinCondition' => [ 0 => [ 'requisites' => ['code_t' => "table"], - 'condition' => - "e.entity_id=code_t.entity_id AND code_t.attribute_id='123' AND code_t.store_id='0'", + 'condition' => "e.entity_id=code_t.entity_id AND code_t.attribute_id='123' AND code_t.store_id='0'", ], ], 'expectedOrder' => 'code_t.value DESC', @@ -147,8 +142,7 @@ public function addValueSortToCollectionDataProvider() 'expectedJoinCondition' => [ 0 => [ 'requisites' => ['code_t' => "table"], - 'condition' => - "e.entity_id=code_t.entity_id AND code_t.attribute_id='123' AND code_t.store_id='0'", + 'condition' => "e.entity_id=code_t.entity_id AND code_t.attribute_id='123' AND code_t.store_id='0'", ], ], 'expectedOrder' => 'code_t.value ASC', From 12c2a1de53cd29f92509a973d08e867b3862a052 Mon Sep 17 00:00:00 2001 From: nmalevanec Date: Thu, 14 Mar 2019 12:00:54 +0200 Subject: [PATCH 270/345] Fix functional tests. --- .../lib/Magento/Mtf/Client/Element/ConditionsElement.php | 6 +++--- 1 file changed, 3 insertions(+), 3 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 b3e5331af4be6..82e3e297dc456 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php +++ b/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php @@ -397,9 +397,9 @@ protected function fillGrid($rule, ElementInterface $param) protected function fillSelect($rule, ElementInterface $param, $type = null) { //Avoid confusion between regions like: "Baja California" and "California". - $value = $type === 'Shipping State/Province' - ? $param->find('select', Locator::SELECTOR_TAG_NAME, 'selectstate') - : $param->find('select', Locator::SELECTOR_TAG_NAME, 'select'); + $value = strpos($type, 'State/Province') === false + ? $param->find('select', Locator::SELECTOR_TAG_NAME, 'select') + : $param->find('select', Locator::SELECTOR_TAG_NAME, 'selectstate'); if ($value->isVisible()) { $value->setValue($rule); $this->click(); From fb6713473106625fb0b0816c4479d64b6f477568 Mon Sep 17 00:00:00 2001 From: nmalevanec Date: Thu, 14 Mar 2019 14:49:40 +0200 Subject: [PATCH 271/345] Fix static tests. --- .../Attribute/Edit/Main/AbstractMain.php | 13 ++++++------- app/code/Magento/Eav/Setup/EavSetup.php | 9 ++++++--- .../Entity/Attribute/Source/BooleanTest.php | 18 ++++++++++++------ 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php b/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php index 73b45f452d0fe..be9d2700664c7 100644 --- a/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php +++ b/app/code/Magento/Eav/Block/Adminhtml/Attribute/Edit/Main/AbstractMain.php @@ -4,15 +4,13 @@ * See COPYING.txt for license details. */ -/** - * Product attribute add/edit form main tab - * - * @author Magento Core Team - */ namespace Magento\Eav\Block\Adminhtml\Attribute\Edit\Main; use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +/** + * Product attribute add/edit form main tab + */ abstract class AbstractMain extends \Magento\Backend\Block\Widget\Form\Generic { /** @@ -279,10 +277,11 @@ protected function _initFormValues() } /** - * Processing block html after rendering + * Processing block html after rendering. + * * Adding js block to the end of this block * - * @param string $html + * @param string $html * @return string */ protected function _afterToHtml($html) diff --git a/app/code/Magento/Eav/Setup/EavSetup.php b/app/code/Magento/Eav/Setup/EavSetup.php index 04e5957b921a9..29f9163a6e91d 100644 --- a/app/code/Magento/Eav/Setup/EavSetup.php +++ b/app/code/Magento/Eav/Setup/EavSetup.php @@ -10,12 +10,12 @@ use Magento\Eav\Model\Entity\Setup\PropertyMapperInterface; use Magento\Eav\Model\ResourceModel\Entity\Attribute\Group\CollectionFactory; use Magento\Framework\App\CacheInterface; -use Magento\Framework\App\ObjectManager; -use Magento\Framework\App\ResourceConnection; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Setup\ModuleDataSetupInterface; /** + * Base eav setup class. + * * @api * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -101,7 +101,8 @@ public function __construct( } /** - * Gets setup model + * Gets setup model. + * * @deprecated * @return ModuleDataSetupInterface */ @@ -568,6 +569,8 @@ public function addAttributeGroup($entityTypeId, $setId, $name, $sortOrder = nul } /** + * Convert group name to attribute group code. + * * @param string $groupName * @return string * @since 100.1.0 diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/BooleanTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/BooleanTest.php index a524c41c69c34..8cf5df877a6eb 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/BooleanTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/BooleanTest.php @@ -101,11 +101,13 @@ public function addValueSortToCollectionDataProvider() 'expectedJoinCondition' => [ 0 => [ 'requisites' => ['code_t1' => "table"], - 'condition' => "e.entity_id=code_t1.entity_id AND code_t1.attribute_id='123' AND code_t1.store_id='0'", + 'condition' => "e.entity_id=code_t1.entity_id AND code_t1.attribute_id='123'" + . " AND code_t1.store_id='0'", ], 1 => [ 'requisites' => ['code_t2' => "table"], - 'condition' => "e.entity_id=code_t2.entity_id AND code_t2.attribute_id='123' AND code_t2.store_id='12'", + 'condition' => "e.entity_id=code_t2.entity_id AND code_t2.attribute_id='123'" + . " AND code_t2.store_id='12'", ], ], 'expectedOrder' => 'IF(code_t2.value_id > 0, code_t2.value, code_t1.value) ASC', @@ -116,11 +118,13 @@ public function addValueSortToCollectionDataProvider() 'expectedJoinCondition' => [ 0 => [ 'requisites' => ['code_t1' => "table"], - 'condition' => "e.entity_id=code_t1.entity_id AND code_t1.attribute_id='123' AND code_t1.store_id='0'", + 'condition' => "e.entity_id=code_t1.entity_id AND code_t1.attribute_id='123'" + . " AND code_t1.store_id='0'", ], 1 => [ 'requisites' => ['code_t2' => "table"], - 'condition' => "e.entity_id=code_t2.entity_id AND code_t2.attribute_id='123' AND code_t2.store_id='12'", + 'condition' => "e.entity_id=code_t2.entity_id AND code_t2.attribute_id='123'" + . " AND code_t2.store_id='12'", ], ], 'expectedOrder' => 'IF(code_t2.value_id > 0, code_t2.value, code_t1.value) DESC', @@ -131,7 +135,8 @@ public function addValueSortToCollectionDataProvider() 'expectedJoinCondition' => [ 0 => [ 'requisites' => ['code_t' => "table"], - 'condition' => "e.entity_id=code_t.entity_id AND code_t.attribute_id='123' AND code_t.store_id='0'", + 'condition' => "e.entity_id=code_t.entity_id AND code_t.attribute_id='123'" + . " AND code_t.store_id='0'", ], ], 'expectedOrder' => 'code_t.value DESC', @@ -142,7 +147,8 @@ public function addValueSortToCollectionDataProvider() 'expectedJoinCondition' => [ 0 => [ 'requisites' => ['code_t' => "table"], - 'condition' => "e.entity_id=code_t.entity_id AND code_t.attribute_id='123' AND code_t.store_id='0'", + 'condition' => "e.entity_id=code_t.entity_id AND code_t.attribute_id='123'" + . " AND code_t.store_id='0'", ], ], 'expectedOrder' => 'code_t.value ASC', From 0b49a94f51b7aa52bd267aaf161a68c24f039912 Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" Date: Thu, 14 Mar 2019 07:49:41 -0500 Subject: [PATCH 272/345] 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 c2b3ce0498803..5de93c1ffdbc2 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 53fc5fd7c9275..036135d4de57f 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 5e64d737034c8..73841a2a964cc 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 b8309dbf426dd8e93e095dd429af883fdae1d7bd Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" Date: Thu, 14 Mar 2019 09:13:42 -0500 Subject: [PATCH 273/345] 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 5de93c1ffdbc2..19f104c9f3790 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 036135d4de57f..383b36fd63228 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?= Date: Thu, 14 Mar 2019 16:39:41 +0200 Subject: [PATCH 274/345] 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 c930d2195a01b..1c4a37fedebe3 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 @@ - escapeHtml(__($_data['label'])) ?> - productAttribute($_product, $_data['value'], $_data['code']) ?> + escapeHtml($_data['label']) ?> + productAttribute($_product, $_data['value'], $_data['code']) ?> From 63cffa2f34ee28735cb102fa0466f6a89d646a25 Mon Sep 17 00:00:00 2001 From: "Lopukhov, Stanislav" Date: Thu, 14 Mar 2019 10:00:11 -0500 Subject: [PATCH 275/345] 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 74e7f40b1f062..5c3ee3da8ca81 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?= Date: Thu, 14 Mar 2019 17:15:44 +0100 Subject: [PATCH 276/345] 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 b401da8960f05..3a516fcea5e91 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 c77b2c734460db3ff8147ce89cdd2f9c176e8e76 Mon Sep 17 00:00:00 2001 From: Ansari Date: Fri, 15 Mar 2019 10:12:34 +0530 Subject: [PATCH 277/345] spelling correction --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b86c7b79a0cbd..8fef85cd1e474 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -626,7 +626,7 @@ Tests: * Fixed an issue where filters were not shown on product reviews report grid * Fixed an issue where second customer address was not deleted from customer account * Fixed an issue where custom options pop-up was still displayed after submit - * Fixed an issue where Second Product was not added to Shopping Cart from Wishlist at first atempt + * Fixed an issue where Second Product was not added to Shopping Cart from Wishlist at first attempt * Fixed an issue where customer invalid email message was not displayed * Fixed an issue where All Access Tokens for Customer without Tokens could not be revoked * Fixed an issue where it was impossible to add Product to Shopping Cart from shared Wishlist From 648df72b26eabf438b4570e0db8a5a925f01ecd1 Mon Sep 17 00:00:00 2001 From: Michalk39 Date: Fri, 15 Mar 2019 12:25:26 +0100 Subject: [PATCH 278/345] 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 1fdb15f189ace..af6ff35988e02 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 @@ +
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 0000000000000..36a760d90e125 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/VerifyDisabledCustomerGroupFieldTest.xml @@ -0,0 +1,39 @@ + + + + + + + + + <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 279/345] 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 6dbf2b1aa6a12..f532b12c61492 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 0000000000000..1fe096670135d --- /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 280/345] 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 8f3e6b475b466..2821a46f29416 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 281/345] 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 f711268bc7930..49d9b4aaa34e8 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 89fb54e7e496b..31bdc19f9cf7a 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 282/345] 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 f532b12c61492..9edd087020a72 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 1fe096670135d..15a799eac5188 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 283/345] 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 2c4b8a8dc48d2..09e4e7b5b9bd2 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 87a0e666d2d7b..cf8b8b551c5d4 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 19543375aad58..376da5728b31b 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 c11204bcdd358..e55bb026af6ff 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 3e788c1eba0ee..9abcb68c8dbec 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 0000000000000..799c86c49e8ff --- /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 0000000000000..d728622e372a3 --- /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 61818cbb8c53c..7cfa7274e4e31 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 335006555d2f1..9c779e7c3c651 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 5f7508438a6ed..833b1ced20c06 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 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 284/345] 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 9999c29c1a257..011dfa1019738 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 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 285/345] 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 09e4e7b5b9bd2..36fefcb86217d 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 cf8b8b551c5d4..920cac382fcbf 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 376da5728b31b..ce29bdc526463 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 799c86c49e8ff..39c132503daf8 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 d728622e372a3..0000000000000 --- 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 9c779e7c3c651..00edfc628d936 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 833b1ced20c06..dbc16b808c47e 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 286/345] 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 e73da84d66f46..9e3cf448f99fb 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 287/345] 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 d99d1b69887ed..4b52b2c669edf 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 eb0797d07bf18..67ccb51bf401e 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 288/345] 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 36fefcb86217d..8d197bc6ab045 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 39c132503daf8..8575f208e6c1f 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 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 289/345] 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 0000000000000..83d79a43620ff --- /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 290/345] 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 71dfd12bb4779..4ebd594a28562 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 00bcd2a27005a..8f35fe9f37abf 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 994076badddae..60ccdb88676aa 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 bd06fc9457630d44b33b29d359c1cc9593806384 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Sat, 16 Mar 2019 12:03:56 +0200 Subject: [PATCH 291/345] magento-engcom/magento2ce#2680: Fixed static tests --- .../Order/Create/Form/AbstractForm.php | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php index 5ea77b0f71811..6b87c1fe39d8b 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php @@ -6,6 +6,7 @@ namespace Magento\Sales\Block\Adminhtml\Order\Create\Form; use Magento\Framework\Pricing\PriceCurrencyInterface; +use Magento\Customer\Api\Data\AttributeMetadataInterface; /** * Sales Order Create Form Abstract Block @@ -57,8 +58,7 @@ public function __construct( } /** - * Prepare global layout - * Add renderers to \Magento\Framework\Data\Form + * Prepare global layout. Add renderers to \Magento\Framework\Data\Form * * @return $this */ @@ -152,7 +152,7 @@ protected function _addAdditionalFormElementData(\Magento\Framework\Data\Form\El /** * Add rendering EAV attributes to Form element * - * @param \Magento\Customer\Api\Data\AttributeMetadataInterface[] $attributes + * @param AttributeMetadataInterface[] $attributes * @param \Magento\Framework\Data\Form\AbstractForm $form * @return $this * @SuppressWarnings(PHPMD.CyclomaticComplexity) @@ -231,11 +231,11 @@ public function getFormValues() /** * Retrieve frontend classes according validation rules * - * @param \Magento\Customer\Api\Data\AttributeMetadataInterface $attribute + * @param AttributeMetadataInterface $attribute * * @return string */ - private function getValidationClasses(\Magento\Customer\Api\Data\AttributeMetadataInterface $attribute) : string + private function getValidationClasses(AttributeMetadataInterface $attribute) : string { $out = []; $out[] = $attribute->getFrontendClass(); @@ -252,23 +252,23 @@ private function getValidationClasses(\Magento\Customer\Api\Data\AttributeMetada /** * Retrieve validation classes by min_text_length and max_text_length rules * - * @param \Magento\Customer\Api\Data\AttributeMetadataInterface $attribute + * @param AttributeMetadataInterface $attribute * * @return array */ - private function getTextLengthValidateClasses(\Magento\Customer\Api\Data\AttributeMetadataInterface $attribute) : array + private function getTextLengthValidateClasses(AttributeMetadataInterface $attribute) : array { $classes = []; $validateRules = $attribute->getValidationRules(); - if(!empty($validateRules)) { + if (!empty($validateRules)) { foreach ($validateRules as $rule) { switch ($rule->getName()) { - case 'min_text_length' : + case 'min_text_length': $classes[] = 'minimum-length-' . $rule->getValue(); break; - case 'max_text_length' : + case 'max_text_length': $classes[] = 'maximum-length-' . $rule->getValue(); break; } 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 292/345] #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 69d545f12d075..0a1dcb0b626e6 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 07050c1e5111d..131013bacd808 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 06861277559a4..a6f767259488a 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 293/345] 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 0b3a31194ea36..44635e9b93f9a 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 dcfb12fd4e965..a63dc5be2de30 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 0000000000000..94abf4b26c347 --- /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 294/345] 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 0000000000000..baafbdef47fe8 --- /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 83d79a43620ff..47705262caaf3 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 295/345] 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 baafbdef47fe8..e5f6b84bfc3da 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 296/345] 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 e5f6b84bfc3da..fb0113eb6ae75 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 904a4a8c80b95da48d5095dde7fdec34540952f7 Mon Sep 17 00:00:00 2001 From: Leandry <leandry@atwix.com> Date: Mon, 18 Mar 2019 01:17:48 +0200 Subject: [PATCH 297/345] 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 34a77095d524d..12d35df42bcc5 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 0000000000000..6cd280bb7add3 --- /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 298/345] 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 3a516fcea5e91..83637dac9c475 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 299/345] 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 ca50cdb2440f4..0cdd80208fbcf 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 300/345] 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 1141f451c13a5..72cc08b7c015c 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 301/345] 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 49d9b4aaa34e8..0fa4b919c40fa 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 31bdc19f9cf7a..30f703b5b928f 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 302/345] 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 72cc08b7c015c..1141f451c13a5 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 303/345] 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 eae92e1663fc8..eb760a82916a6 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 304/345] 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 eb760a82916a6..3ab6dc12054be 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 305/345] #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 b53faf74ffa1d..0000000000000 --- 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 5a5dd925a3098..6ed230861e4d0 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 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 306/345] 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 dbc16b808c47e..dba775ea894f4 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 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 307/345] 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 d518af4e04f55..10be4cd5febf6 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 70edb0ce3ea7d..17769c79677f7 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 c9a37ec40e8fa..318ab6555235e 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 d67d5b36109e6..34d85e7b46850 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 95b86ec3a8587..43dae2d70d416 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 981985b3f4ea8..1db9b3e5b79b2 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 e278018330aa6..934a410d58a8a 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 308/345] 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 6e312637c7b5a..ae218561377f2 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 ec15736bef534..46cb2998ede70 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 c22c49d3427c5..7c450a09df320 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 18053f20f010c..8864ab6f9ead1 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 912b0e42ca822..ae236c1b5d740 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 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 309/345] 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 b911a38dbb488..96161e12e9773 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 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 310/345] 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 e55bb026af6ff..c11204bcdd358 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 7cfa7274e4e31..256ad2fdbd3e1 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 00edfc628d936..187bb0ea1446f 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 311/345] 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 f41fc00b55acb..95fe5ae24d23e 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 f18a5ff1d875a..eb6239ea87ef0 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 b48d6d585492a..37031b4fba4bb 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 ac70034a85da5..ac53a14f19b0c 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 312/345] 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 d491678d4d7fc..3b69c8734d9a9 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 7c450a09df320..62521b9de3082 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 ae236c1b5d740..37b5cc38dcbd8 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 313/345] 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 0000000000000..8c5c6f41fffa7 --- /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 70b4e95124da8..0000000000000 --- 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 0000000000000..ee8b761a452d4 --- /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 be6e1af67fc63..a14ed147aae22 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 bdb02835c6276..38c88bf4f80bb 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 451bce9e49a8f..7318f865a0dc1 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 314/345] 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 aea1094b4d629..40a731410a899 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 95582e107da19..d1381bbb1efb0 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 6fe25160655fc..c2daaac84dd42 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 315/345] 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 8d197bc6ab045..c913716fedee7 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 96161e12e9773..4c76087bfea12 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 316/345] 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 e218f5ae74fc0..2de7bf19fd378 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 4447b27360a80..c58479a7b73e5 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 9dc5f15c0aa82dd8329005972cf30886e44e8849 Mon Sep 17 00:00:00 2001 From: Michalk39 <michalk9339@gmail.com> Date: Tue, 19 Mar 2019 09:36:03 +0100 Subject: [PATCH 317/345] 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 6b4f3fc9d6b6e..6d3da9715b905 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 af6ff35988e02..182adeade69fe 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 36a760d90e125..bb17b1b3269b5 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 70a912a3b5ffe..e88e5161e474e 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 318/345] 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 12d35df42bcc5..ee0c32633569a 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 6cd280bb7add3..4a74a62c947fd 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 319/345] 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 63182dd5624e6..342bbc388f872 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 becd6c160155c..dc24a3090481e 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 320/345] 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 37031b4fba4bb..0000000000000 --- 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 e2df3d69ca0aa..3d5b895575597 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 325cfd597956a..db97990ebbad5 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 1234228dad72d..f37f2157c3575 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 ac53a14f19b0c..0000000000000 --- 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 6155526287555..a8e40ea495002 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 ee3d09ec3eaed..283fff8ace7c7 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 3b2cb8718e041..22cfea976a722 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 321/345] 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 1b54dd2c127db..5d3186781e7d7 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 0cdd80208fbcf..adcffe01b910e 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 83637dac9c475..d058bfd41ab1a 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 322/345] 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 95fe5ae24d23e..334d2b22edbfa 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 323/345] 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 8575f208e6c1f..8b500fe7fd3cb 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 187bb0ea1446f..6c4746d8218ea 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 324/345] 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 ae218561377f2..8d0caad5d55e4 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 3b69c8734d9a9..1e7ca069df79b 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 b5f8eee0f2c4f..2718bf6cb3456 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 37b5cc38dcbd8..799409d1560ac 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 f7ca6e0a09b5a..5a150ad3dcd86 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 325/345] 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 cb27439a9d886..8108577145421 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 bbb6aa71b8938..5f69f52987688 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 326/345] 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 07af21505f180..89f1e5e5d53d6 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 327/345] 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 182adeade69fe..4cb7f5e3f628e 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 bb17b1b3269b5..648c30b1ca0bb 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 4a74a62c947fd..bd6f7ba362bf4 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 cbdce59057195..bc529729f1217 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 328/345] 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 5e48fb5ed30f9..e560fd664493b 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 329/345] 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 07af21505f180..89f1e5e5d53d6 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 330/345] 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 4499c314ce6ca..08d16842b849c 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 331/345] 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 e560fd664493b..919b0e4c86ba0 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 332/345] #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 0000000000000..b53faf74ffa1d --- /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 6ed230861e4d0..68fcd17122bd2 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 333/345] 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 44635e9b93f9a..6a712fde28025 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 a63dc5be2de30..dcfb12fd4e965 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 0000000000000..36eb10826e63c --- /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 94abf4b26c347..654a8b231c63c 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 e0ea721a51f1b..43acf9cd740d1 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 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 334/345] 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 3ab6dc12054be..89d468159c6e9 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 335/345] 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 492343acfe306..963efc648d94b 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 336/345] 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 3a4010f417104..1f5ae6b6905bc 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 09d9469cb093d..a587d71ba0e68 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 aaeb0cd38cd99..6f64da4693692 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 597ee2336b21f..993f1c9cd9da2 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 b1e723e5ee1ff..491d20604a08b 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 e3c5cd78397f6..f671b54803e35 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 337/345] 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 c913716fedee7..c63ccae871657 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 8b500fe7fd3cb..216d8e9a0a01b 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 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 338/345] 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 a14ed147aae22..e7a5992ad8943 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 36eb10826e63c..423f4049f6722 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 654a8b231c63c..84080b04c80ee 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 43acf9cd740d1..5caa3ba9b924e 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 339/345] 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 256ad2fdbd3e1..dfe6bbb828352 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 340/345] 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 63248dd53fc1b..b9eea2b114634 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 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 341/345] 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 0000000000000..fd550ac14f22f --- /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 0000000000000..79f41829f1091 --- /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 1e7ca069df79b..1dbedaaaa5e1b 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 46cb2998ede70..20e0f69328c88 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 8864ab6f9ead1..030b126284db4 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 799409d1560ac..b4cc14e25ca19 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 5a150ad3dcd86..1a46bddf5f21a 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 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 342/345] 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 fd550ac14f22f..feb111e436b1d 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 343/345] 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 79f41829f1091..47aed8a6fe3ec 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 1dbedaaaa5e1b..cbba981ae1b51 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 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 344/345] 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 feb111e436b1d..e64b3c505acf1 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 47aed8a6fe3ec..d168e910a4ab7 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 20e0f69328c88..b142085ef6563 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 030b126284db4..ebf2f54f3e093 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 345/345] 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 b4cc14e25ca19..66f41128c46b1 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) {