From ca47981c00d1a4d1803520108d5a152d7b3d584a Mon Sep 17 00:00:00 2001 From: Tommy Quissens Date: Thu, 5 Apr 2018 11:31:02 +0200 Subject: [PATCH 001/211] made attributes nillable so we can reset parent settings --- lib/internal/Magento/Framework/Config/etc/view.xsd | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/internal/Magento/Framework/Config/etc/view.xsd b/lib/internal/Magento/Framework/Config/etc/view.xsd index 20b6a7d4fbfd2..eb0583a58bf29 100644 --- a/lib/internal/Magento/Framework/Config/etc/view.xsd +++ b/lib/internal/Magento/Framework/Config/etc/view.xsd @@ -49,12 +49,12 @@ - - - - - - + + + + + + From 951d5e8bcad09fec5619ad5da582b67025f6e914 Mon Sep 17 00:00:00 2001 From: Tommy Quissens Date: Thu, 5 Apr 2018 12:45:28 +0200 Subject: [PATCH 002/211] parse nil values correctly --- app/code/Magento/Catalog/Model/ImageExtractor.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/ImageExtractor.php b/app/code/Magento/Catalog/Model/ImageExtractor.php index 7888d8de1c2ff..07a4a8fe9f700 100644 --- a/app/code/Magento/Catalog/Model/ImageExtractor.php +++ b/app/code/Magento/Catalog/Model/ImageExtractor.php @@ -35,7 +35,11 @@ public function process(\DOMElement $mediaNode, $mediaParentTag) if ($attributeTagName === 'background') { $nodeValue = $this->processImageBackground($attribute->nodeValue); } elseif ($attributeTagName === 'width' || $attributeTagName === 'height') { - $nodeValue = intval($attribute->nodeValue); + if ((bool)$attribute->getAttribute('xsi:nil') !== true) { + $nodeValue = intval($attribute->nodeValue); + } else { + $nodeValue = null; + } } else { $nodeValue = $attribute->nodeValue; } From 540917e54db5490f411beb6cbbe8715502de5b1a Mon Sep 17 00:00:00 2001 From: Jayanka Date: Sat, 19 May 2018 12:30:16 +0530 Subject: [PATCH 003/211] Fixed issue #13480 - Unable to activate logs after switching from production mode to developer --- app/code/Magento/Deploy/etc/di.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/code/Magento/Deploy/etc/di.xml b/app/code/Magento/Deploy/etc/di.xml index ce7c84c95538a..3b47f308d7bbb 100644 --- a/app/code/Magento/Deploy/etc/di.xml +++ b/app/code/Magento/Deploy/etc/di.xml @@ -82,4 +82,15 @@ + + + + + + + + + + + From c6aabf8f6b88b9b54a5022eb2163718af270b7a8 Mon Sep 17 00:00:00 2001 From: Jayanka Date: Sat, 19 May 2018 14:12:51 +0530 Subject: [PATCH 004/211] Fixed issue #13480 - Combined duplicate Magento\Deploy\App\Mode\ConfigProvider type into one tag --- app/code/Magento/Deploy/etc/di.xml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/code/Magento/Deploy/etc/di.xml b/app/code/Magento/Deploy/etc/di.xml index 3b47f308d7bbb..fd604aa1b397b 100644 --- a/app/code/Magento/Deploy/etc/di.xml +++ b/app/code/Magento/Deploy/etc/di.xml @@ -78,14 +78,6 @@ 0 - - - - - - - - From 03298b3b2f322ca0bebc423f906428718b31809a Mon Sep 17 00:00:00 2001 From: Claudio Zambon Date: Sun, 27 May 2018 11:47:33 +0200 Subject: [PATCH 005/211] Convert to string $option->getValue, in order to be compared with other saved options in previous cart items --- .../ConfigurableProduct/Model/Quote/Item/CartItemProcessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/ConfigurableProduct/Model/Quote/Item/CartItemProcessor.php b/app/code/Magento/ConfigurableProduct/Model/Quote/Item/CartItemProcessor.php index 4450bbd75e574..85493b81bc6d4 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Quote/Item/CartItemProcessor.php +++ b/app/code/Magento/ConfigurableProduct/Model/Quote/Item/CartItemProcessor.php @@ -69,7 +69,7 @@ public function convertToBuyRequest(CartItemInterface $cartItem) if (is_array($options)) { $requestData = []; foreach ($options as $option) { - $requestData['super_attribute'][$option->getOptionId()] = $option->getOptionValue(); + $requestData['super_attribute'][$option->getOptionId()] = (string) $option->getOptionValue(); } return $this->objectFactory->create($requestData); } From a99e97e1ebbb3fd8b09e3a7236813831a76c7cef Mon Sep 17 00:00:00 2001 From: Sam Butler-Thompson Date: Thu, 31 May 2018 06:40:01 +0100 Subject: [PATCH 006/211] Fix #10687 - add types to images array To fix #10687 - adds the 'types' key to the $images array to provide existing image roles (if not set), which are re-attributed after clearMediaAttribute() is called --- .../MediaGalleryProcessor.php | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php b/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php index 4cc31d98fdfc2..56d1cfda8bce5 100644 --- a/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php +++ b/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php @@ -100,8 +100,13 @@ public function processMediaGallery(ProductInterface $product, array $mediaGalle $newEntries = $mediaGalleryEntries; } - $this->processor->clearMediaAttribute($product, array_keys($product->getMediaAttributes())); $images = $product->getMediaGallery('images'); + + if ($images) { + $images = $this->determineImageRoles($product, $images); + } + + $this->processor->clearMediaAttribute($product, array_keys($product->getMediaAttributes())); if ($images) { foreach ($images as $image) { if (!isset($image['removed']) && !empty($image['types'])) { @@ -112,6 +117,32 @@ public function processMediaGallery(ProductInterface $product, array $mediaGalle $this->processEntries($product, $newEntries, $entriesById); } + /** + * Ascertain image roles, if they are not set against the gallery entries + * + * @param ProductInterface $product + * @param array $images + * @return array + */ + private function determineImageRoles(ProductInterface $product, array $images) + { + $imagesWithRoles = []; + foreach ($images as $image) { + if (!isset($image['types'])) { + $image['types'] = []; + if (isset($image['file'])) { + foreach (array_keys($product->getMediaAttributes()) as $attribute) { + if ($image['file'] == $product->getData($attribute)) { + $image['types'][] = $attribute; + } + } + } + } + $imagesWithRoles[] = $image; + } + return $imagesWithRoles; + } + /** * Convert entries into product media gallery data and set to product. * From c55a0411ab366e3837e98e010adffd4ca9b85584 Mon Sep 17 00:00:00 2001 From: Andrea Rivadossi Date: Fri, 1 Jun 2018 08:38:57 +0200 Subject: [PATCH 007/211] Fix #13445 Added the class for fix the issue #13445 --- .../view/frontend/layout/catalogsearch_result_index.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/LayeredNavigation/view/frontend/layout/catalogsearch_result_index.xml b/app/code/Magento/LayeredNavigation/view/frontend/layout/catalogsearch_result_index.xml index ab9aa485aeea2..3bb21ed09a01c 100644 --- a/app/code/Magento/LayeredNavigation/view/frontend/layout/catalogsearch_result_index.xml +++ b/app/code/Magento/LayeredNavigation/view/frontend/layout/catalogsearch_result_index.xml @@ -7,6 +7,7 @@ --> + From a49618ada2a310601cdf1568bca28170d70bcfc5 Mon Sep 17 00:00:00 2001 From: simonjanguapa Date: Sat, 2 Jun 2018 12:51:07 +0200 Subject: [PATCH 008/211] Update Output.php --- app/code/Magento/Catalog/Helper/Output.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Helper/Output.php b/app/code/Magento/Catalog/Helper/Output.php index facd5351f269a..b14274b950db5 100644 --- a/app/code/Magento/Catalog/Helper/Output.php +++ b/app/code/Magento/Catalog/Helper/Output.php @@ -151,7 +151,7 @@ public function productAttribute($product, $attributeHtml, $attributeName) $attributeHtml = nl2br($attributeHtml); } } - if ($attribute->getIsHtmlAllowedOnFront() && $attribute->getIsWysiwygEnabled()) { + if ($attribute->getIsHtmlAllowedOnFront() || $attribute->getIsWysiwygEnabled()) { if ($this->_catalogData->isUrlDirectivesParsingAllowed()) { $attributeHtml = $this->_getTemplateProcessor()->filter($attributeHtml); } From 28159a37d1b63e69a72e7cd21022bde533721cc6 Mon Sep 17 00:00:00 2001 From: simonjanguapa Date: Sat, 2 Jun 2018 12:52:15 +0200 Subject: [PATCH 009/211] Update Table.php --- app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php b/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php index bd77b952a07b8..0f3b8be32b988 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php @@ -167,7 +167,9 @@ public function getOptionText($value) $optionsText = []; foreach ($options as $item) { if (in_array($item['value'], $value)) { - $optionsText[] = $this->escaper->escapeHtml($item['label']); + $optionsText[] = ($this->_attribute->getIsHtmlAllowedOnFront()) + ? $item['label'] + : $this->escaper->escapeHtml($item['label']); } } From edb35169ddf49d5b7cb7f0798021a4c945c537fd Mon Sep 17 00:00:00 2001 From: simonjanguapa Date: Sat, 2 Jun 2018 15:28:06 +0200 Subject: [PATCH 010/211] Update ProductGettersTest.php Updated tests --- .../testsuite/Magento/Catalog/Model/ProductGettersTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductGettersTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductGettersTest.php index 6ea7c19dce91e..1ed0057ca2486 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductGettersTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductGettersTest.php @@ -217,7 +217,7 @@ public function testGetAttributeTextArray() $expected = [ 'Option 2', 'Option 3', - 'Option 4 "!@#$%^&*' + 'Option 4 "!@#$%^&*' ]; self::assertEquals( $expected, From 4c1989ee90e2cde19cb71445f4c07dd19f8f7b3a Mon Sep 17 00:00:00 2001 From: Julian van Drielen Date: Sat, 2 Jun 2018 16:09:07 +0200 Subject: [PATCH 011/211] Forward pull for issue 4803 2.1->2.2 --- app/code/Magento/Eav/Api/Data/AttributeInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Api/Data/AttributeInterface.php b/app/code/Magento/Eav/Api/Data/AttributeInterface.php index 9664cabc79ba0..b7c0f435a8fc2 100644 --- a/app/code/Magento/Eav/Api/Data/AttributeInterface.php +++ b/app/code/Magento/Eav/Api/Data/AttributeInterface.php @@ -11,7 +11,7 @@ * @api * @since 100.0.2 */ -interface AttributeInterface extends \Magento\Framework\Api\CustomAttributesDataInterface +interface AttributeInterface extends \Magento\Framework\Api\CustomAttributesDataInterface, \Magento\Framework\Api\MetadataObjectInterface { const ATTRIBUTE_ID = 'attribute_id'; From 215be5a9d10ec81d243b3a2225c45264632176ef Mon Sep 17 00:00:00 2001 From: Vishal Gelani Date: Sat, 9 Jun 2018 14:58:49 +0530 Subject: [PATCH 012/211] Fixed coding standard error --- app/code/Magento/Eav/Api/Data/AttributeInterface.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Api/Data/AttributeInterface.php b/app/code/Magento/Eav/Api/Data/AttributeInterface.php index b7c0f435a8fc2..b011304580c06 100644 --- a/app/code/Magento/Eav/Api/Data/AttributeInterface.php +++ b/app/code/Magento/Eav/Api/Data/AttributeInterface.php @@ -11,7 +11,8 @@ * @api * @since 100.0.2 */ -interface AttributeInterface extends \Magento\Framework\Api\CustomAttributesDataInterface, \Magento\Framework\Api\MetadataObjectInterface +interface AttributeInterface extends \Magento\Framework\Api\CustomAttributesDataInterface, +\Magento\Framework\Api\MetadataObjectInterface { const ATTRIBUTE_ID = 'attribute_id'; From 58f223abb636973476ef006066a98652b2382b16 Mon Sep 17 00:00:00 2001 From: Valerij Ivashchenko Date: Mon, 11 Jun 2018 12:12:58 +0300 Subject: [PATCH 013/211] Don't disable "Use default" inputs according to comment above --- lib/web/mage/adminhtml/form.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/web/mage/adminhtml/form.js b/lib/web/mage/adminhtml/form.js index f1dc26a6b9144..c2181b6803fce 100644 --- a/lib/web/mage/adminhtml/form.js +++ b/lib/web/mage/adminhtml/form.js @@ -494,7 +494,8 @@ define([ inputs.each(function (item) { // don't touch hidden inputs (and Use Default inputs too), bc they may have custom logic if ((!item.type || item.type != 'hidden') && !($(item.id + '_inherit') && $(item.id + '_inherit').checked) && //eslint-disable-line - !(currentConfig['can_edit_price'] != undefined && !currentConfig['can_edit_price']) //eslint-disable-line + !(currentConfig['can_edit_price'] != undefined && !currentConfig['can_edit_price']) && //eslint-disable-line + !item.id.endsWith('_inherit') ) { item.disabled = false; jQuery(item).removeClass('ignore-validate'); From 96c28a6238559574fa5ba791b080793d794aacef Mon Sep 17 00:00:00 2001 From: Tommy Quissens Date: Tue, 19 Jun 2018 15:23:15 +0200 Subject: [PATCH 014/211] made more attributes nillable --- app/code/Magento/Catalog/Model/ImageExtractor.php | 14 +++++++------- lib/internal/Magento/Framework/Config/etc/view.xsd | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ImageExtractor.php b/app/code/Magento/Catalog/Model/ImageExtractor.php index 07a4a8fe9f700..1c20608670672 100644 --- a/app/code/Magento/Catalog/Model/ImageExtractor.php +++ b/app/code/Magento/Catalog/Model/ImageExtractor.php @@ -32,16 +32,16 @@ public function process(\DOMElement $mediaNode, $mediaParentTag) continue; } $attributeTagName = $attribute->tagName; - if ($attributeTagName === 'background') { - $nodeValue = $this->processImageBackground($attribute->nodeValue); - } elseif ($attributeTagName === 'width' || $attributeTagName === 'height') { - if ((bool)$attribute->getAttribute('xsi:nil') !== true) { - $nodeValue = intval($attribute->nodeValue); + if ((bool)$attribute->getAttribute('xsi:nil') !== true) { + if ($attributeTagName === 'background') { + $nodeValue = $this->processImageBackground($attribute->nodeValue); + } elseif ($attributeTagName === 'width' || $attributeTagName === 'height') { + $nodeValue = intval($attribute->nodeValue); } else { - $nodeValue = null; + $nodeValue = $attribute->nodeValue; } } else { - $nodeValue = $attribute->nodeValue; + $nodeValue = null; } $result[$mediaParentTag][$moduleNameImage][Image::MEDIA_TYPE_CONFIG_NODE][$imageId][$attribute->tagName] = $nodeValue; diff --git a/lib/internal/Magento/Framework/Config/etc/view.xsd b/lib/internal/Magento/Framework/Config/etc/view.xsd index eb0583a58bf29..b908862b02147 100644 --- a/lib/internal/Magento/Framework/Config/etc/view.xsd +++ b/lib/internal/Magento/Framework/Config/etc/view.xsd @@ -55,7 +55,7 @@ - + From 1115b3f6e39f9b4406ada0a6dae7ac60ea333da3 Mon Sep 17 00:00:00 2001 From: Vasilii Burlacu Date: Thu, 21 Jun 2018 22:53:20 +0300 Subject: [PATCH 015/211] #16273: Fix bug in method getUrlInStore() of product model # Method $product->getUrlInStore() returning extremely long URLs, could be a bug (cherry picked from commit 7558ac0a478af8fc008228077f9e5380612d4138) --- .../Magento/Store/Url/Plugin/RouteParamsResolver.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Store/Url/Plugin/RouteParamsResolver.php b/app/code/Magento/Store/Url/Plugin/RouteParamsResolver.php index c4f8a31430963..325e621c8a113 100644 --- a/app/code/Magento/Store/Url/Plugin/RouteParamsResolver.php +++ b/app/code/Magento/Store/Url/Plugin/RouteParamsResolver.php @@ -51,6 +51,8 @@ public function __construct( * @param \Magento\Framework\Url\RouteParamsResolver $subject * @param array $data * @param bool $unsetOldParams + * @throws \Magento\Framework\Exception\NoSuchEntityException + * * @return array */ public function beforeSetRouteParams( @@ -63,13 +65,19 @@ public function beforeSetRouteParams( unset($data['_scope']); } if (isset($data['_scope_to_url']) && (bool)$data['_scope_to_url'] === true) { - $storeCode = $subject->getScope() ?: $this->storeManager->getStore()->getCode(); + /** @var Store $currentScope */ + $currentScope = $subject->getScope(); + $storeCode = $currentScope && $currentScope instanceof Store ? + $currentScope->getCode() : + $this->storeManager->getStore()->getCode(); + $useStoreInUrl = $this->scopeConfig->getValue( Store::XML_PATH_STORE_IN_URL, StoreScopeInterface::SCOPE_STORE, $storeCode ); - if (!$useStoreInUrl && !$this->storeManager->hasSingleStore()) { + + if ($useStoreInUrl && !$this->storeManager->hasSingleStore()) { $this->queryParamsResolver->setQueryParam('___store', $storeCode); } } From 93e4e6d22e7772d74f4d461a8181e60c78b46525 Mon Sep 17 00:00:00 2001 From: Yogesh Suhagiya Date: Fri, 22 Jun 2018 19:04:10 +0530 Subject: [PATCH 016/211] Remove unnecessary translation of HTML tags --- .../Block/Adminhtml/Form/Renderer/Config/YearRange.php | 9 ++++----- app/code/Magento/Catalog/i18n/en_US.csv | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Form/Renderer/Config/YearRange.php b/app/code/Magento/Catalog/Block/Adminhtml/Form/Renderer/Config/YearRange.php index 0026e52e039ef..cd6c5021f0cc9 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Form/Renderer/Config/YearRange.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Form/Renderer/Config/YearRange.php @@ -32,10 +32,9 @@ protected function _getElementHtml(AbstractElement $element) $from = $element->setValue(isset($values[0]) ? $values[0] : null)->getElementHtml(); $to = $element->setValue(isset($values[1]) ? $values[1] : null)->getElementHtml(); - return __( - '' - ) . $from . __( - '' - ) . $to; + return '' + . $from . + '' + . $to; } } diff --git a/app/code/Magento/Catalog/i18n/en_US.csv b/app/code/Magento/Catalog/i18n/en_US.csv index a316bac5d3584..e3205195d32e0 100644 --- a/app/code/Magento/Catalog/i18n/en_US.csv +++ b/app/code/Magento/Catalog/i18n/en_US.csv @@ -13,8 +13,8 @@ Position,Position Day,Day Month,Month Year,Year -"","" -"","" +from,from +to,to [GLOBAL],[GLOBAL] [WEBSITE],[WEBSITE] "[STORE VIEW]","[STORE VIEW]" From 2639d0ec3ae7b0b2952f9fa1fd3f0f2d73f761c1 Mon Sep 17 00:00:00 2001 From: Vishal Gelani Date: Sat, 23 Jun 2018 14:36:14 +0530 Subject: [PATCH 017/211] Update AttributeInterface.php --- app/code/Magento/Eav/Api/Data/AttributeInterface.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Eav/Api/Data/AttributeInterface.php b/app/code/Magento/Eav/Api/Data/AttributeInterface.php index b011304580c06..1e87622244619 100644 --- a/app/code/Magento/Eav/Api/Data/AttributeInterface.php +++ b/app/code/Magento/Eav/Api/Data/AttributeInterface.php @@ -11,8 +11,8 @@ * @api * @since 100.0.2 */ -interface AttributeInterface extends \Magento\Framework\Api\CustomAttributesDataInterface, -\Magento\Framework\Api\MetadataObjectInterface +interface AttributeInterface extends \Magento\Framework\Api\CustomAttributesDataInterface, + \Magento\Framework\Api\MetadataObjectInterface { const ATTRIBUTE_ID = 'attribute_id'; From 4989d8b358bcaccac21ee5aa985e2359d8abb9ca Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Tue, 26 Jun 2018 13:55:38 +0300 Subject: [PATCH 018/211] Remove PDF invoice after generation --- .../Invoice/AbstractInvoice/PrintAction.php | 4 +++- .../App/Response/Http/FileFactory.php | 24 +++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/PrintAction.php b/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/PrintAction.php index 2421267aa753d..5ae021f39bc54 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/PrintAction.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/PrintAction.php @@ -58,9 +58,11 @@ public function execute() $date = $this->_objectManager->get( \Magento\Framework\Stdlib\DateTime\DateTime::class )->date('Y-m-d_H-i-s'); + $fileContent = ['type' => 'string', 'value' => $pdf->render(), 'rm' => true]; + return $this->_fileFactory->create( 'invoice' . $date . '.pdf', - $pdf->render(), + $fileContent, DirectoryList::VAR_DIR, 'application/pdf' ); diff --git a/lib/internal/Magento/Framework/App/Response/Http/FileFactory.php b/lib/internal/Magento/Framework/App/Response/Http/FileFactory.php index 979ea1c429590..fb4e0fc62bf44 100644 --- a/lib/internal/Magento/Framework/App/Response/Http/FileFactory.php +++ b/lib/internal/Magento/Framework/App/Response/Http/FileFactory.php @@ -76,7 +76,11 @@ public function create( ->setHeader('Pragma', 'public', true) ->setHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0', true) ->setHeader('Content-type', $contentType, true) - ->setHeader('Content-Length', $contentLength === null ? strlen($content) : $contentLength, true) + ->setHeader( + 'Content-Length', + $contentLength === null ? strlen($this->getFileContent($content)) : $contentLength, + true + ) ->setHeader('Content-Disposition', 'attachment; filename="' . $fileName . '"', true) ->setHeader('Last-Modified', date('r'), true); @@ -88,7 +92,8 @@ public function create( echo $stream->read(1024); } } else { - $dir->writeFile($fileName, $content); + $dir->writeFile($fileName, $this->getFileContent($content)); + $file = $fileName; $stream = $dir->openFile($fileName, 'r'); while (!$stream->eof()) { echo $stream->read(1024); @@ -102,4 +107,19 @@ public function create( } return $this->_response; } + + /** + * Returns file content for writing. + * + * @param string|array $content + * @return string + */ + private function getFileContent($content) + { + if (isset($content['type']) && $content['type'] == 'string') { + return $content['value']; + } + + return $content; + } } From 7e3c3fc3b1da25b611ae3d80e0408dcd3a0d0282 Mon Sep 17 00:00:00 2001 From: Vasilii Burlacu Date: Sat, 30 Jun 2018 15:04:59 +0300 Subject: [PATCH 019/211] Modified RouteParamsResolverTest according to latest bugfixes on tested class --- .../Url/Plugin/RouteParamsResolverTest.php | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Store/Test/Unit/Url/Plugin/RouteParamsResolverTest.php b/app/code/Magento/Store/Test/Unit/Url/Plugin/RouteParamsResolverTest.php index ed9ac7ebe438a..51e3a9dc32e13 100644 --- a/app/code/Magento/Store/Test/Unit/Url/Plugin/RouteParamsResolverTest.php +++ b/app/code/Magento/Store/Test/Unit/Url/Plugin/RouteParamsResolverTest.php @@ -22,6 +22,11 @@ class RouteParamsResolverTest extends \PHPUnit\Framework\TestCase */ protected $queryParamsResolverMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Store\Model\Store + */ + protected $storeMock; + /** * @var \Magento\Store\Url\Plugin\RouteParamsResolver */ @@ -30,7 +35,19 @@ class RouteParamsResolverTest extends \PHPUnit\Framework\TestCase protected function setUp() { $this->scopeConfigMock = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); + + $this->storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) + ->setMethods(['getCode']) + ->disableOriginalConstructor() + ->getMock(); + $this->storeMock->expects($this->any())->method('getCode')->willReturn('custom_store'); + $this->storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); + $this->storeManagerMock + ->expects($this->once()) + ->method('getStore') + ->willReturn($this->storeMock); + $this->queryParamsResolverMock = $this->createMock(\Magento\Framework\Url\QueryParamsResolverInterface::class); $this->model = new \Magento\Store\Url\Plugin\RouteParamsResolver( $this->scopeConfigMock, @@ -42,6 +59,8 @@ protected function setUp() public function testBeforeSetRouteParamsScopeInParams() { $storeCode = 'custom_store'; + $data = ['_scope' => $storeCode, '_scope_to_url' => true]; + $this->scopeConfigMock ->expects($this->once()) ->method('getValue') @@ -52,7 +71,7 @@ public function testBeforeSetRouteParamsScopeInParams() ) ->will($this->returnValue(false)); $this->storeManagerMock->expects($this->any())->method('hasSingleStore')->willReturn(false); - $data = ['_scope' => $storeCode, '_scope_to_url' => true]; + /** @var \PHPUnit_Framework_MockObject_MockObject $routeParamsResolverMock */ $routeParamsResolverMock = $this->getMockBuilder(\Magento\Framework\Url\RouteParamsResolver::class) ->setMethods(['setScope', 'getScope']) @@ -61,7 +80,8 @@ public function testBeforeSetRouteParamsScopeInParams() $routeParamsResolverMock->expects($this->once())->method('setScope')->with($storeCode); $routeParamsResolverMock->expects($this->once())->method('getScope')->willReturn($storeCode); - $this->queryParamsResolverMock->expects($this->once())->method('setQueryParam')->with('___store', $storeCode); + $this->queryParamsResolverMock->expects($this->never())->method('setQueryParam'); + $this->model->beforeSetRouteParams( $routeParamsResolverMock, @@ -72,6 +92,8 @@ public function testBeforeSetRouteParamsScopeInParams() public function testBeforeSetRouteParamsScopeUseStoreInUrl() { $storeCode = 'custom_store'; + $data = ['_scope' => $storeCode, '_scope_to_url' => true]; + $this->scopeConfigMock ->expects($this->once()) ->method('getValue') @@ -81,8 +103,9 @@ public function testBeforeSetRouteParamsScopeUseStoreInUrl() $storeCode ) ->will($this->returnValue(true)); + $this->storeManagerMock->expects($this->any())->method('hasSingleStore')->willReturn(false); - $data = ['_scope' => $storeCode, '_scope_to_url' => true]; + /** @var \PHPUnit_Framework_MockObject_MockObject $routeParamsResolverMock */ $routeParamsResolverMock = $this->getMockBuilder(\Magento\Framework\Url\RouteParamsResolver::class) ->setMethods(['setScope', 'getScope']) @@ -91,7 +114,7 @@ public function testBeforeSetRouteParamsScopeUseStoreInUrl() $routeParamsResolverMock->expects($this->once())->method('setScope')->with($storeCode); $routeParamsResolverMock->expects($this->once())->method('getScope')->willReturn($storeCode); - $this->queryParamsResolverMock->expects($this->never())->method('setQueryParam'); + $this->queryParamsResolverMock->expects($this->once())->method('setQueryParam')->with('___store', $storeCode); $this->model->beforeSetRouteParams( $routeParamsResolverMock, @@ -102,6 +125,8 @@ public function testBeforeSetRouteParamsScopeUseStoreInUrl() public function testBeforeSetRouteParamsSingleStore() { $storeCode = 'custom_store'; + $data = ['_scope' => $storeCode, '_scope_to_url' => true]; + $this->scopeConfigMock ->expects($this->once()) ->method('getValue') @@ -112,7 +137,7 @@ public function testBeforeSetRouteParamsSingleStore() ) ->will($this->returnValue(false)); $this->storeManagerMock->expects($this->any())->method('hasSingleStore')->willReturn(true); - $data = ['_scope' => $storeCode, '_scope_to_url' => true]; + /** @var \PHPUnit_Framework_MockObject_MockObject $routeParamsResolverMock */ $routeParamsResolverMock = $this->getMockBuilder(\Magento\Framework\Url\RouteParamsResolver::class) ->setMethods(['setScope', 'getScope']) @@ -132,6 +157,8 @@ public function testBeforeSetRouteParamsSingleStore() public function testBeforeSetRouteParamsNoScopeInParams() { $storeCode = 'custom_store'; + $data = ['_scope_to_url' => true]; + $this->scopeConfigMock ->expects($this->once()) ->method('getValue') @@ -140,17 +167,10 @@ public function testBeforeSetRouteParamsNoScopeInParams() \Magento\Store\Model\ScopeInterface::SCOPE_STORE, $storeCode ) - ->will($this->returnValue(false)); + ->will($this->returnValue(true)); + $this->storeManagerMock->expects($this->any())->method('hasSingleStore')->willReturn(false); - /** @var \PHPUnit_Framework_MockObject_MockObject| $routeParamsResolverMock */ - $storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) - ->setMethods(['getCode']) - ->disableOriginalConstructor() - ->getMock(); - $storeMock->expects($this->any())->method('getCode')->willReturn($storeCode); - $this->storeManagerMock->expects($this->any())->method('getStore')->willReturn($storeMock); - $data = ['_scope_to_url' => true]; /** @var \PHPUnit_Framework_MockObject_MockObject $routeParamsResolverMock */ $routeParamsResolverMock = $this->getMockBuilder(\Magento\Framework\Url\RouteParamsResolver::class) ->setMethods(['setScope', 'getScope']) From 66ee8273028fb3e0e118cdaad6e6163bf2477361 Mon Sep 17 00:00:00 2001 From: Vishal Gelani Date: Sun, 1 Jul 2018 11:33:38 +0530 Subject: [PATCH 020/211] Update RouteParamsResolverTest.php --- .../Store/Test/Unit/Url/Plugin/RouteParamsResolverTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Store/Test/Unit/Url/Plugin/RouteParamsResolverTest.php b/app/code/Magento/Store/Test/Unit/Url/Plugin/RouteParamsResolverTest.php index 51e3a9dc32e13..f31acd40a2e69 100644 --- a/app/code/Magento/Store/Test/Unit/Url/Plugin/RouteParamsResolverTest.php +++ b/app/code/Magento/Store/Test/Unit/Url/Plugin/RouteParamsResolverTest.php @@ -82,7 +82,6 @@ public function testBeforeSetRouteParamsScopeInParams() $this->queryParamsResolverMock->expects($this->never())->method('setQueryParam'); - $this->model->beforeSetRouteParams( $routeParamsResolverMock, $data From 9366da978649cd3549f1732521d45f4963f74d5e Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Mon, 2 Jul 2018 12:05:33 +0300 Subject: [PATCH 021/211] File content extracted to a separate var. Type hinting, strict comparison --- .../Framework/App/Response/Http/FileFactory.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/internal/Magento/Framework/App/Response/Http/FileFactory.php b/lib/internal/Magento/Framework/App/Response/Http/FileFactory.php index fb4e0fc62bf44..6697e1f392ba0 100644 --- a/lib/internal/Magento/Framework/App/Response/Http/FileFactory.php +++ b/lib/internal/Magento/Framework/App/Response/Http/FileFactory.php @@ -59,6 +59,7 @@ public function create( $dir = $this->_filesystem->getDirectoryWrite($baseDir); $isFile = false; $file = null; + $fileContent = $this->getFileContent($content); if (is_array($content)) { if (!isset($content['type']) || !isset($content['value'])) { throw new \InvalidArgumentException("Invalid arguments. Keys 'type' and 'value' are required."); @@ -76,11 +77,7 @@ public function create( ->setHeader('Pragma', 'public', true) ->setHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0', true) ->setHeader('Content-type', $contentType, true) - ->setHeader( - 'Content-Length', - $contentLength === null ? strlen($this->getFileContent($content)) : $contentLength, - true - ) + ->setHeader('Content-Length', $contentLength === null ? strlen($fileContent) : $contentLength, true) ->setHeader('Content-Disposition', 'attachment; filename="' . $fileName . '"', true) ->setHeader('Last-Modified', date('r'), true); @@ -92,7 +89,7 @@ public function create( echo $stream->read(1024); } } else { - $dir->writeFile($fileName, $this->getFileContent($content)); + $dir->writeFile($fileName, $fileContent); $file = $fileName; $stream = $dir->openFile($fileName, 'r'); while (!$stream->eof()) { @@ -114,9 +111,9 @@ public function create( * @param string|array $content * @return string */ - private function getFileContent($content) + private function getFileContent($content): string { - if (isset($content['type']) && $content['type'] == 'string') { + if (isset($content['type']) && $content['type'] === 'string') { return $content['value']; } From 70fed741d14a72c11ae7f62c7164b9fd066c532e Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Mon, 2 Jul 2018 13:07:12 +0300 Subject: [PATCH 022/211] Integration test for checking file generation and removing process --- .../App/Filesystem/CreatePdfFileTest.php | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php b/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php new file mode 100644 index 0000000000000..2f8383667d2f6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php @@ -0,0 +1,53 @@ +get(FileFactory::class); + /** @var Filesystem $filesystem */ + $filesystem = $objectManager->get(Filesystem::class); + $filename = 'test.pdf'; + $contentType = 'application/pdf'; + $fileContent = ['type' => 'string', 'value' => '']; + $response = $fileFactory->create($filename, $fileContent, DirectoryList::VAR_DIR, $contentType); + /** @var ContentType $contentTypeHeader */ + $contentTypeHeader = $response->getHeader('Content-type'); + + /* Check the system returns the correct type */ + self::assertEquals("Content-Type: $contentType", $contentTypeHeader->toString()); + + $varDirectory = $filesystem->getDirectoryRead(DirectoryList::VAR_DIR); + $varDirectory->isFile($filename); + + /* Check the file is generated */ + self::assertTrue($varDirectory->isFile($filename)); + + /* Check the file is removed after generation if the corresponding option is set */ + $fileContent = ['type' => 'string', 'value' => '', 'rm' => true]; + $fileFactory->create($filename, $fileContent, DirectoryList::VAR_DIR, $contentType); + + self::assertFalse($varDirectory->isFile($filename)); + } +} From 31df140d545a5a4cecf3b7c410069d0ae169a328 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Mon, 2 Jul 2018 13:26:51 +0300 Subject: [PATCH 023/211] Changed the PDF file request in all other places --- .../Creditmemo/AbstractCreditmemo/Pdfcreditmemos.php | 5 ++++- .../Creditmemo/AbstractCreditmemo/PrintAction.php | 5 ++++- .../Adminhtml/Invoice/AbstractInvoice/Pdfinvoices.php | 5 ++++- .../Adminhtml/Invoice/AbstractInvoice/PrintAction.php | 1 + .../Sales/Controller/Adminhtml/Order/Pdfcreditmemos.php | 6 +++++- .../Magento/Sales/Controller/Adminhtml/Order/Pdfdocs.php | 4 +++- .../Sales/Controller/Adminhtml/Order/Pdfinvoices.php | 6 +++++- .../Sales/Controller/Adminhtml/Order/Pdfshipments.php | 7 ++++++- .../Adminhtml/Shipment/AbstractShipment/Pdfshipments.php | 5 ++++- .../Adminhtml/Shipment/AbstractShipment/PrintAction.php | 5 ++++- 10 files changed, 40 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/Pdfcreditmemos.php b/app/code/Magento/Sales/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/Pdfcreditmemos.php index 83486704984f7..94ca6a0375e7c 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/Pdfcreditmemos.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/Pdfcreditmemos.php @@ -74,9 +74,12 @@ public function __construct( */ public function massAction(AbstractCollection $collection) { + $pdf = $this->pdfCreditmemo->getPdf($collection); + $fileContent = ['type' => 'string', 'value' => $pdf->render(), 'rm' => true]; + return $this->fileFactory->create( sprintf('creditmemo%s.pdf', $this->dateTime->date('Y-m-d_H-i-s')), - $this->pdfCreditmemo->getPdf($collection)->render(), + $fileContent, DirectoryList::VAR_DIR, 'application/pdf' ); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/PrintAction.php b/app/code/Magento/Sales/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/PrintAction.php index d6c94feb57606..c5902aac33355 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/PrintAction.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Creditmemo/AbstractCreditmemo/PrintAction.php @@ -53,6 +53,7 @@ public function __construct( /** * @return ResponseInterface|\Magento\Backend\Model\View\Result\Forward + * @throws \Exception */ public function execute() { @@ -69,9 +70,11 @@ public function execute() $date = $this->_objectManager->get( \Magento\Framework\Stdlib\DateTime\DateTime::class )->date('Y-m-d_H-i-s'); + $fileContent = ['type' => 'string', 'value' => $pdf->render(), 'rm' => true]; + return $this->_fileFactory->create( \creditmemo::class . $date . '.pdf', - $pdf->render(), + $fileContent, DirectoryList::VAR_DIR, 'application/pdf' ); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/Pdfinvoices.php b/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/Pdfinvoices.php index a949ceca53961..18cf397c0e3b9 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/Pdfinvoices.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/Pdfinvoices.php @@ -75,9 +75,12 @@ public function __construct( */ public function massAction(AbstractCollection $collection) { + $pdf = $this->pdfInvoice->getPdf($collection); + $fileContent = ['type' => 'string', 'value' => $pdf->render(), 'rm' => true]; + return $this->fileFactory->create( sprintf('invoice%s.pdf', $this->dateTime->date('Y-m-d_H-i-s')), - $this->pdfInvoice->getPdf($collection)->render(), + $fileContent, DirectoryList::VAR_DIR, 'application/pdf' ); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/PrintAction.php b/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/PrintAction.php index 5ae021f39bc54..2caa8ec2dcb83 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/PrintAction.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/PrintAction.php @@ -45,6 +45,7 @@ public function __construct( /** * @return ResponseInterface|void + * @throws \Exception */ public function execute() { diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfcreditmemos.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfcreditmemos.php index f96e2fd09c2b0..b6e3c4998eade 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfcreditmemos.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfcreditmemos.php @@ -76,6 +76,7 @@ public function __construct( * * @param AbstractCollection $collection * @return ResponseInterface|ResultInterface + * @throws \Exception */ protected function massAction(AbstractCollection $collection) { @@ -85,9 +86,12 @@ protected function massAction(AbstractCollection $collection) $this->messageManager->addError(__('There are no printable documents related to selected orders.')); return $this->resultRedirectFactory->create()->setPath($this->getComponentRefererUrl()); } + $pdf = $this->pdfCreditmemo->getPdf($creditmemoCollection->getItems()); + $fileContent = ['type' => 'string', 'value' => $pdf->render(), 'rm' => true]; + return $this->fileFactory->create( sprintf('creditmemo%s.pdf', $this->dateTime->date('Y-m-d_H-i-s')), - $this->pdfCreditmemo->getPdf($creditmemoCollection->getItems())->render(), + $fileContent, DirectoryList::VAR_DIR, 'application/pdf' ); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfdocs.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfdocs.php index d68cfe696b0ef..90ffa2b75de22 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfdocs.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfdocs.php @@ -113,6 +113,7 @@ public function __construct( * @return ResponseInterface|\Magento\Backend\Model\View\Result\Redirect * * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @throws \Exception */ protected function massAction(AbstractCollection $collection) { @@ -142,10 +143,11 @@ protected function massAction(AbstractCollection $collection) foreach ($documents as $document) { $pdf->pages = array_merge($pdf->pages, $document->pages); } + $fileContent = ['type' => 'string', 'value' => $pdf->render(), 'rm' => true]; return $this->fileFactory->create( sprintf('docs%s.pdf', $this->dateTime->date('Y-m-d_H-i-s')), - $pdf->render(), + $fileContent, DirectoryList::VAR_DIR, 'application/pdf' ); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfinvoices.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfinvoices.php index fee124a91410d..93cb5882ed5de 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfinvoices.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfinvoices.php @@ -75,6 +75,7 @@ public function __construct( * * @param AbstractCollection $collection * @return ResponseInterface|ResultInterface + * @throws \Exception */ protected function massAction(AbstractCollection $collection) { @@ -83,9 +84,12 @@ protected function massAction(AbstractCollection $collection) $this->messageManager->addError(__('There are no printable documents related to selected orders.')); return $this->resultRedirectFactory->create()->setPath($this->getComponentRefererUrl()); } + $pdf = $this->pdfInvoice->getPdf($invoicesCollection->getItems()); + $fileContent = ['type' => 'string', 'value' => $pdf->render(), 'rm' => true]; + return $this->fileFactory->create( sprintf('invoice%s.pdf', $this->dateTime->date('Y-m-d_H-i-s')), - $this->pdfInvoice->getPdf($invoicesCollection->getItems())->render(), + $fileContent, DirectoryList::VAR_DIR, 'application/pdf' ); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfshipments.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfshipments.php index 1aa5bfdb83878..d414ec99c2e6a 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfshipments.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Pdfshipments.php @@ -77,6 +77,7 @@ public function __construct( * * @param AbstractCollection $collection * @return ResponseInterface|\Magento\Backend\Model\View\Result\Redirect + * @throws \Exception */ protected function massAction(AbstractCollection $collection) { @@ -87,9 +88,13 @@ protected function massAction(AbstractCollection $collection) $this->messageManager->addError(__('There are no printable documents related to selected orders.')); return $this->resultRedirectFactory->create()->setPath($this->getComponentRefererUrl()); } + + $pdf = $this->pdfShipment->getPdf($shipmentsCollection->getItems()); + $fileContent = ['type' => 'string', 'value' => $pdf->render(), 'rm' => true]; + return $this->fileFactory->create( sprintf('packingslip%s.pdf', $this->dateTime->date('Y-m-d_H-i-s')), - $this->pdfShipment->getPdf($shipmentsCollection->getItems())->render(), + $fileContent, DirectoryList::VAR_DIR, 'application/pdf' ); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Shipment/AbstractShipment/Pdfshipments.php b/app/code/Magento/Sales/Controller/Adminhtml/Shipment/AbstractShipment/Pdfshipments.php index b10dca908fe6a..4a7c6e0533e33 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Shipment/AbstractShipment/Pdfshipments.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Shipment/AbstractShipment/Pdfshipments.php @@ -70,9 +70,12 @@ public function __construct( */ public function massAction(AbstractCollection $collection) { + $pdf = $this->pdfShipment->getPdf($collection); + $fileContent = ['type' => 'string', 'value' => $pdf->render(), 'rm' => true]; + return $this->fileFactory->create( sprintf('packingslip%s.pdf', $this->dateTime->date('Y-m-d_H-i-s')), - $this->pdfShipment->getPdf($collection)->render(), + $fileContent, DirectoryList::VAR_DIR, 'application/pdf' ); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Shipment/AbstractShipment/PrintAction.php b/app/code/Magento/Sales/Controller/Adminhtml/Shipment/AbstractShipment/PrintAction.php index da8646a0c30b2..1e49fe7eff60a 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Shipment/AbstractShipment/PrintAction.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Shipment/AbstractShipment/PrintAction.php @@ -48,6 +48,7 @@ public function __construct( /** * @return ResponseInterface|\Magento\Backend\Model\View\Result\Forward + * @throws \Exception */ public function execute() { @@ -63,9 +64,11 @@ public function execute() $date = $this->_objectManager->get( \Magento\Framework\Stdlib\DateTime\DateTime::class )->date('Y-m-d_H-i-s'); + $fileContent = ['type' => 'string', 'value' => $pdf->render(), 'rm' => true]; + return $this->_fileFactory->create( 'packingslip' . $date . '.pdf', - $pdf->render(), + $fileContent, DirectoryList::VAR_DIR, 'application/pdf' ); From 4746315f611b080e4338fb1c0df8113f3bb8d03d Mon Sep 17 00:00:00 2001 From: Anshu Mishra Date: Tue, 3 Jul 2018 17:56:39 +0530 Subject: [PATCH 024/211] admin checkout agreement controllers refactor --- .../Controller/Adminhtml/Agreement.php | 27 +++++++++++++++++-- .../Controller/Adminhtml/Agreement/Delete.php | 6 ++--- .../Controller/Adminhtml/Agreement/Edit.php | 4 +-- .../Controller/Adminhtml/Agreement/Save.php | 4 +-- 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement.php index 13130a4491eb2..92829feecace9 100644 --- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement.php +++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement.php @@ -5,6 +5,9 @@ */ namespace Magento\CheckoutAgreements\Controller\Adminhtml; +use Magento\Framework\App\ObjectManager; +use Magento\CheckoutAgreements\Api\CheckoutAgreementsRepositoryInterface; + abstract class Agreement extends \Magento\Backend\App\Action { /** @@ -20,15 +23,35 @@ abstract class Agreement extends \Magento\Backend\App\Action * @var \Magento\Framework\Registry */ protected $_coreRegistry = null; + + /** + * @var CheckoutAgreementsRepositoryInterface + */ + protected $_agreementRepository; + + /** + * @var \Magento\CheckoutAgreements\Model\AgreementFactory + */ + protected $_agreementFactory; /** * @param \Magento\Backend\App\Action\Context $context * @param \Magento\Framework\Registry $coreRegistry + * @param CheckoutAgreementsRepositoryInterface $agreementRepository + * @param \Magento\CheckoutAgreements\Model\AgreementFactory $agreementFactory * @codeCoverageIgnore */ - public function __construct(\Magento\Backend\App\Action\Context $context, \Magento\Framework\Registry $coreRegistry) - { + public function __construct( + \Magento\Backend\App\Action\Context $context, + \Magento\Framework\Registry $coreRegistry, + CheckoutAgreementsRepositoryInterface $agreementRepository = null, + \Magento\CheckoutAgreements\Model\AgreementFactory $agreementFactory = null + ) { $this->_coreRegistry = $coreRegistry; + $this->_agreementRepository = $agreementRepository ?: + ObjectManager::getInstance()->get(CheckoutAgreementsRepositoryInterface::class); + $this->_agreementFactory = $agreementFactory ?: + ObjectManager::getInstance()->get(\Magento\CheckoutAgreements\Model\AgreementFactory::class); parent::__construct($context); } diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php index 65aca6205caa4..169246e359b1e 100644 --- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php +++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php @@ -14,15 +14,15 @@ class Delete extends \Magento\CheckoutAgreements\Controller\Adminhtml\Agreement public function execute() { $id = (int)$this->getRequest()->getParam('id'); - $model = $this->_objectManager->get(\Magento\CheckoutAgreements\Model\Agreement::class)->load($id); - if (!$model->getId()) { + $repository = $this->_agreementRepository->get($id); + if (!$repository->getAgreementId()) { $this->messageManager->addError(__('This condition no longer exists.')); $this->_redirect('checkout/*/'); return; } try { - $model->delete(); + $repository->delete(); $this->messageManager->addSuccess(__('You deleted the condition.')); $this->_redirect('checkout/*/'); return; diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php index 73ac129bc993c..fb160f450f0dd 100644 --- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php +++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php @@ -15,7 +15,7 @@ class Edit extends \Magento\CheckoutAgreements\Controller\Adminhtml\Agreement public function execute() { $id = $this->getRequest()->getParam('id'); - $agreementModel = $this->_objectManager->create(\Magento\CheckoutAgreements\Model\Agreement::class); + $agreementModel = $this->_agreementFactory->create(); if ($id) { $agreementModel->load($id); @@ -26,7 +26,7 @@ public function execute() } } - $data = $this->_objectManager->get(\Magento\Backend\Model\Session::class)->getAgreementData(true); + $data = $this->_session->getAgreementData(true); if (!empty($data)) { $agreementModel->setData($data); } diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php index 25c034203620b..c8c2b7d0a40bf 100644 --- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php +++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php @@ -15,7 +15,7 @@ public function execute() { $postData = $this->getRequest()->getPostValue(); if ($postData) { - $model = $this->_objectManager->get(\Magento\CheckoutAgreements\Model\Agreement::class); + $model = $this->_agreementFactory->create(); $model->setData($postData); try { @@ -36,7 +36,7 @@ public function execute() $this->messageManager->addError(__('Something went wrong while saving this condition.')); } - $this->_objectManager->get(\Magento\Backend\Model\Session::class)->setAgreementData($postData); + $this->_session->setAgreementData($postData); $this->getResponse()->setRedirect($this->_redirect->getRedirectUrl($this->getUrl('*'))); } } From ccb0f7fdd1a1b64af514b9c62480b09342bee27c Mon Sep 17 00:00:00 2001 From: DmitryChukhnov Date: Tue, 3 Jul 2018 20:22:57 +0300 Subject: [PATCH 025/211] Fix the special price expression. Without this fix, catalog_product_price generate incorrect price data. The priority of "OR" is lower then "AND". That's why we have to add brackets around OR-expresstion. --- .../Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php index 849c12238db5a..7ea85cd3f6f10 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php @@ -456,7 +456,7 @@ protected function getSelect($entityIds = null, $type = null) $specialFromExpr = "{$specialFrom} IS NULL OR {$specialFromDate} <= {$currentDate}"; $specialToExpr = "{$specialTo} IS NULL OR {$specialToDate} >= {$currentDate}"; $specialPriceExpr = $connection->getCheckSql( - "{$specialPrice} IS NOT NULL AND {$specialFromExpr} AND {$specialToExpr}", + "{$specialPrice} IS NOT NULL AND ({$specialFromExpr}) AND ({$specialToExpr})", $specialPrice, $maxUnsignedBigint ); From 8d8b39a78730981d23e2325b124b4676b5f4b73d Mon Sep 17 00:00:00 2001 From: DmitryChukhnov Date: Tue, 3 Jul 2018 20:23:07 +0300 Subject: [PATCH 026/211] Fix the special price expression. Without this fix, catalog_product_price generate incorrect price data. The priority of "OR" is lower then "AND". That's why we have to add brackets around OR-expresstion. From 25d49eb011d2aa7cd232b0139f05ad09cc1fb731 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza Date: Thu, 5 Jul 2018 16:15:43 +0300 Subject: [PATCH 027/211] Failing tests fixed --- .../Adminhtml/Order/Creditmemo/PrintActionTest.php | 7 ++++--- .../Magento/Framework/App/Filesystem/CreatePdfFileTest.php | 2 +- .../Magento/Framework/App/Response/Http/FileFactory.php | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/PrintActionTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/PrintActionTest.php index 881af16f5fe69..e12a4195db4c6 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/PrintActionTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/PrintActionTest.php @@ -155,7 +155,8 @@ public function testExecute() $creditmemoId = 2; $date = '2015-01-19_13-03-45'; $fileName = 'creditmemo2015-01-19_13-03-45.pdf'; - $fileContents = 'pdf0123456789'; + $pdfContent = 'pdf0123456789'; + $fileData = ['type' => 'string', 'value' => $pdfContent, 'rm' => true]; $this->prepareTestExecute($creditmemoId); $this->objectManagerMock->expects($this->any()) @@ -184,12 +185,12 @@ public function testExecute() ->willReturn($date); $this->pdfMock->expects($this->once()) ->method('render') - ->willReturn($fileContents); + ->willReturn($pdfContent); $this->fileFactoryMock->expects($this->once()) ->method('create') ->with( $fileName, - $fileContents, + $fileData, \Magento\Framework\App\Filesystem\DirectoryList::VAR_DIR, 'application/pdf' ) diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php b/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php index 2f8383667d2f6..d97a57589bc59 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Filesystem/CreatePdfFileTest.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace Magento\Framework\App\Filesystem\Images; +namespace Magento\Framework\App\Filesystem; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\App\Response\Http\FileFactory; diff --git a/lib/internal/Magento/Framework/App/Response/Http/FileFactory.php b/lib/internal/Magento/Framework/App/Response/Http/FileFactory.php index 6697e1f392ba0..19a89681a2d5f 100644 --- a/lib/internal/Magento/Framework/App/Response/Http/FileFactory.php +++ b/lib/internal/Magento/Framework/App/Response/Http/FileFactory.php @@ -109,9 +109,9 @@ public function create( * Returns file content for writing. * * @param string|array $content - * @return string + * @return string|array */ - private function getFileContent($content): string + private function getFileContent($content) { if (isset($content['type']) && $content['type'] === 'string') { return $content['value']; From 8653ba1fbe29acc37505b85adce1463d70e9cdf1 Mon Sep 17 00:00:00 2001 From: hitesh-wagento Date: Fri, 6 Jul 2018 14:40:23 +0530 Subject: [PATCH 028/211] [Changed front js file locations] --- .../Magento/Authorizenet/view/frontend/requirejs-config.js | 2 +- app/code/Magento/Captcha/view/frontend/requirejs-config.js | 2 +- .../Magento/Captcha/view/frontend/web/{ => js}/captcha.js | 0 .../Magento/Captcha/view/frontend/web/{ => js}/onepage.js | 0 app/code/Magento/Customer/view/frontend/requirejs-config.js | 4 ++-- .../Magento/Customer/view/frontend/web/{ => js}/address.js | 0 .../view/frontend/web/{ => js}/change-email-password.js | 0 .../Magento/Downloadable/view/frontend/requirejs-config.js | 2 +- .../Downloadable/view/frontend/web/{ => js}/downloadable.js | 0 .../Magento/GiftMessage/view/frontend/requirejs-config.js | 4 ++-- .../GiftMessage/view/frontend/web/{ => js}/extra-options.js | 0 .../GiftMessage/view/frontend/web/{ => js}/gift-options.js | 0 app/code/Magento/Payment/view/frontend/requirejs-config.js | 2 +- .../Magento/Payment/view/frontend/web/{ => js}/cc-type.js | 0 .../Magento/Payment/view/frontend/web/{ => js}/transparent.js | 0 app/code/Magento/Paypal/view/base/requirejs-config.js | 2 +- app/code/Magento/Paypal/view/frontend/requirejs-config.js | 2 +- .../Magento/Paypal/view/frontend/web/{ => js}/order-review.js | 0 app/code/Magento/Sales/view/frontend/requirejs-config.js | 4 ++-- .../Magento/Sales/view/frontend/web/{ => js}/gift-message.js | 0 .../Sales/view/frontend/web/{ => js}/orders-returns.js | 0 app/code/Magento/Search/view/frontend/requirejs-config.js | 2 +- .../Magento/Search/view/frontend/web/{ => js}/form-mini.js | 0 .../Magento/SendFriend/view/frontend/templates/send.phtml | 2 +- .../SendFriend/view/frontend/web/{ => js}/back-event.js | 0 .../Magento/Translation/view/frontend/requirejs-config.js | 2 +- .../Translation/view/frontend/web/{ => js}/add-class.js | 0 app/code/Magento/Weee/view/frontend/requirejs-config.js | 2 +- .../Magento/Weee/view/frontend/web/{ => js}/tax-toggle.js | 0 29 files changed, 16 insertions(+), 16 deletions(-) rename app/code/Magento/Captcha/view/frontend/web/{ => js}/captcha.js (100%) rename app/code/Magento/Captcha/view/frontend/web/{ => js}/onepage.js (100%) rename app/code/Magento/Customer/view/frontend/web/{ => js}/address.js (100%) rename app/code/Magento/Customer/view/frontend/web/{ => js}/change-email-password.js (100%) rename app/code/Magento/Downloadable/view/frontend/web/{ => js}/downloadable.js (100%) rename app/code/Magento/GiftMessage/view/frontend/web/{ => js}/extra-options.js (100%) rename app/code/Magento/GiftMessage/view/frontend/web/{ => js}/gift-options.js (100%) rename app/code/Magento/Payment/view/frontend/web/{ => js}/cc-type.js (100%) rename app/code/Magento/Payment/view/frontend/web/{ => js}/transparent.js (100%) rename app/code/Magento/Paypal/view/frontend/web/{ => js}/order-review.js (100%) rename app/code/Magento/Sales/view/frontend/web/{ => js}/gift-message.js (100%) rename app/code/Magento/Sales/view/frontend/web/{ => js}/orders-returns.js (100%) rename app/code/Magento/Search/view/frontend/web/{ => js}/form-mini.js (100%) rename app/code/Magento/SendFriend/view/frontend/web/{ => js}/back-event.js (100%) rename app/code/Magento/Translation/view/frontend/web/{ => js}/add-class.js (100%) rename app/code/Magento/Weee/view/frontend/web/{ => js}/tax-toggle.js (100%) diff --git a/app/code/Magento/Authorizenet/view/frontend/requirejs-config.js b/app/code/Magento/Authorizenet/view/frontend/requirejs-config.js index 8edc38dce6f60..2b57e5cc2fb0d 100644 --- a/app/code/Magento/Authorizenet/view/frontend/requirejs-config.js +++ b/app/code/Magento/Authorizenet/view/frontend/requirejs-config.js @@ -6,7 +6,7 @@ var config = { map: { '*': { - transparent: 'Magento_Payment/transparent' + transparent: 'Magento_Payment/js/transparent' } } }; diff --git a/app/code/Magento/Captcha/view/frontend/requirejs-config.js b/app/code/Magento/Captcha/view/frontend/requirejs-config.js index 3b322711f8b1f..0f3394e41e7c2 100644 --- a/app/code/Magento/Captcha/view/frontend/requirejs-config.js +++ b/app/code/Magento/Captcha/view/frontend/requirejs-config.js @@ -6,7 +6,7 @@ var config = { map: { '*': { - captcha: 'Magento_Captcha/captcha' + captcha: 'Magento_Captcha/js/captcha' } } }; diff --git a/app/code/Magento/Captcha/view/frontend/web/captcha.js b/app/code/Magento/Captcha/view/frontend/web/js/captcha.js similarity index 100% rename from app/code/Magento/Captcha/view/frontend/web/captcha.js rename to app/code/Magento/Captcha/view/frontend/web/js/captcha.js diff --git a/app/code/Magento/Captcha/view/frontend/web/onepage.js b/app/code/Magento/Captcha/view/frontend/web/js/onepage.js similarity index 100% rename from app/code/Magento/Captcha/view/frontend/web/onepage.js rename to app/code/Magento/Captcha/view/frontend/web/js/onepage.js diff --git a/app/code/Magento/Customer/view/frontend/requirejs-config.js b/app/code/Magento/Customer/view/frontend/requirejs-config.js index 20dd53ded11c9..967bbdcc0e663 100644 --- a/app/code/Magento/Customer/view/frontend/requirejs-config.js +++ b/app/code/Magento/Customer/view/frontend/requirejs-config.js @@ -7,8 +7,8 @@ var config = { map: { '*': { checkoutBalance: 'Magento_Customer/js/checkout-balance', - address: 'Magento_Customer/address', - changeEmailPassword: 'Magento_Customer/change-email-password', + address: 'Magento_Customer/js/address', + changeEmailPassword: 'Magento_Customer/js/change-email-password', passwordStrengthIndicator: 'Magento_Customer/js/password-strength-indicator', zxcvbn: 'Magento_Customer/js/zxcvbn', addressValidation: 'Magento_Customer/js/addressValidation' diff --git a/app/code/Magento/Customer/view/frontend/web/address.js b/app/code/Magento/Customer/view/frontend/web/js/address.js similarity index 100% rename from app/code/Magento/Customer/view/frontend/web/address.js rename to app/code/Magento/Customer/view/frontend/web/js/address.js diff --git a/app/code/Magento/Customer/view/frontend/web/change-email-password.js b/app/code/Magento/Customer/view/frontend/web/js/change-email-password.js similarity index 100% rename from app/code/Magento/Customer/view/frontend/web/change-email-password.js rename to app/code/Magento/Customer/view/frontend/web/js/change-email-password.js diff --git a/app/code/Magento/Downloadable/view/frontend/requirejs-config.js b/app/code/Magento/Downloadable/view/frontend/requirejs-config.js index 59d0558decd16..c3d33949eb012 100644 --- a/app/code/Magento/Downloadable/view/frontend/requirejs-config.js +++ b/app/code/Magento/Downloadable/view/frontend/requirejs-config.js @@ -6,7 +6,7 @@ var config = { map: { '*': { - downloadable: 'Magento_Downloadable/downloadable' + downloadable: 'Magento_Downloadable/js/downloadable' } } }; diff --git a/app/code/Magento/Downloadable/view/frontend/web/downloadable.js b/app/code/Magento/Downloadable/view/frontend/web/js/downloadable.js similarity index 100% rename from app/code/Magento/Downloadable/view/frontend/web/downloadable.js rename to app/code/Magento/Downloadable/view/frontend/web/js/downloadable.js diff --git a/app/code/Magento/GiftMessage/view/frontend/requirejs-config.js b/app/code/Magento/GiftMessage/view/frontend/requirejs-config.js index db33efeba1a94..c3f8ecc45da38 100644 --- a/app/code/Magento/GiftMessage/view/frontend/requirejs-config.js +++ b/app/code/Magento/GiftMessage/view/frontend/requirejs-config.js @@ -6,8 +6,8 @@ var config = { map: { '*': { - giftOptions: 'Magento_GiftMessage/gift-options', - extraOptions: 'Magento_GiftMessage/extra-options' + giftOptions: 'Magento_GiftMessage/js/gift-options', + extraOptions: 'Magento_GiftMessage/js/extra-options' } } }; diff --git a/app/code/Magento/GiftMessage/view/frontend/web/extra-options.js b/app/code/Magento/GiftMessage/view/frontend/web/js/extra-options.js similarity index 100% rename from app/code/Magento/GiftMessage/view/frontend/web/extra-options.js rename to app/code/Magento/GiftMessage/view/frontend/web/js/extra-options.js diff --git a/app/code/Magento/GiftMessage/view/frontend/web/gift-options.js b/app/code/Magento/GiftMessage/view/frontend/web/js/gift-options.js similarity index 100% rename from app/code/Magento/GiftMessage/view/frontend/web/gift-options.js rename to app/code/Magento/GiftMessage/view/frontend/web/js/gift-options.js diff --git a/app/code/Magento/Payment/view/frontend/requirejs-config.js b/app/code/Magento/Payment/view/frontend/requirejs-config.js index 70f93854ead4b..efa24d129e8ec 100644 --- a/app/code/Magento/Payment/view/frontend/requirejs-config.js +++ b/app/code/Magento/Payment/view/frontend/requirejs-config.js @@ -6,7 +6,7 @@ var config = { map: { '*': { - creditCardType: 'Magento_Payment/cc-type' + creditCardType: 'Magento_Payment/js/cc-type' } } }; diff --git a/app/code/Magento/Payment/view/frontend/web/cc-type.js b/app/code/Magento/Payment/view/frontend/web/js/cc-type.js similarity index 100% rename from app/code/Magento/Payment/view/frontend/web/cc-type.js rename to app/code/Magento/Payment/view/frontend/web/js/cc-type.js diff --git a/app/code/Magento/Payment/view/frontend/web/transparent.js b/app/code/Magento/Payment/view/frontend/web/js/transparent.js similarity index 100% rename from app/code/Magento/Payment/view/frontend/web/transparent.js rename to app/code/Magento/Payment/view/frontend/web/js/transparent.js diff --git a/app/code/Magento/Paypal/view/base/requirejs-config.js b/app/code/Magento/Paypal/view/base/requirejs-config.js index 8edc38dce6f60..2b57e5cc2fb0d 100644 --- a/app/code/Magento/Paypal/view/base/requirejs-config.js +++ b/app/code/Magento/Paypal/view/base/requirejs-config.js @@ -6,7 +6,7 @@ var config = { map: { '*': { - transparent: 'Magento_Payment/transparent' + transparent: 'Magento_Payment/js/transparent' } } }; diff --git a/app/code/Magento/Paypal/view/frontend/requirejs-config.js b/app/code/Magento/Paypal/view/frontend/requirejs-config.js index d2cf3f9a0ce7d..f2ac876f560c9 100644 --- a/app/code/Magento/Paypal/view/frontend/requirejs-config.js +++ b/app/code/Magento/Paypal/view/frontend/requirejs-config.js @@ -6,7 +6,7 @@ var config = { map: { '*': { - orderReview: 'Magento_Paypal/order-review', + orderReview: 'Magento_Paypal/js/order-review', paypalCheckout: 'Magento_Paypal/js/paypal-checkout' } }, diff --git a/app/code/Magento/Paypal/view/frontend/web/order-review.js b/app/code/Magento/Paypal/view/frontend/web/js/order-review.js similarity index 100% rename from app/code/Magento/Paypal/view/frontend/web/order-review.js rename to app/code/Magento/Paypal/view/frontend/web/js/order-review.js diff --git a/app/code/Magento/Sales/view/frontend/requirejs-config.js b/app/code/Magento/Sales/view/frontend/requirejs-config.js index 04778765f3c97..658960c749f8c 100644 --- a/app/code/Magento/Sales/view/frontend/requirejs-config.js +++ b/app/code/Magento/Sales/view/frontend/requirejs-config.js @@ -6,8 +6,8 @@ var config = { map: { '*': { - giftMessage: 'Magento_Sales/gift-message', - ordersReturns: 'Magento_Sales/orders-returns' + giftMessage: 'Magento_Sales/js/gift-message', + ordersReturns: 'Magento_Sales/js/orders-returns' } } }; diff --git a/app/code/Magento/Sales/view/frontend/web/gift-message.js b/app/code/Magento/Sales/view/frontend/web/js/gift-message.js similarity index 100% rename from app/code/Magento/Sales/view/frontend/web/gift-message.js rename to app/code/Magento/Sales/view/frontend/web/js/gift-message.js diff --git a/app/code/Magento/Sales/view/frontend/web/orders-returns.js b/app/code/Magento/Sales/view/frontend/web/js/orders-returns.js similarity index 100% rename from app/code/Magento/Sales/view/frontend/web/orders-returns.js rename to app/code/Magento/Sales/view/frontend/web/js/orders-returns.js diff --git a/app/code/Magento/Search/view/frontend/requirejs-config.js b/app/code/Magento/Search/view/frontend/requirejs-config.js index c38cba4315eac..cca294dd3689d 100644 --- a/app/code/Magento/Search/view/frontend/requirejs-config.js +++ b/app/code/Magento/Search/view/frontend/requirejs-config.js @@ -6,7 +6,7 @@ var config = { map: { '*': { - quickSearch: 'Magento_Search/form-mini' + quickSearch: 'Magento_Search/js/form-mini' } } }; diff --git a/app/code/Magento/Search/view/frontend/web/form-mini.js b/app/code/Magento/Search/view/frontend/web/js/form-mini.js similarity index 100% rename from app/code/Magento/Search/view/frontend/web/form-mini.js rename to app/code/Magento/Search/view/frontend/web/js/form-mini.js diff --git a/app/code/Magento/SendFriend/view/frontend/templates/send.phtml b/app/code/Magento/SendFriend/view/frontend/templates/send.phtml index 7a972cd5f01c5..1fe4ae381420e 100644 --- a/app/code/Magento/SendFriend/view/frontend/templates/send.phtml +++ b/app/code/Magento/SendFriend/view/frontend/templates/send.phtml @@ -121,7 +121,7 @@ diff --git a/app/code/Magento/SendFriend/view/frontend/web/back-event.js b/app/code/Magento/SendFriend/view/frontend/web/js/back-event.js similarity index 100% rename from app/code/Magento/SendFriend/view/frontend/web/back-event.js rename to app/code/Magento/SendFriend/view/frontend/web/js/back-event.js diff --git a/app/code/Magento/Translation/view/frontend/requirejs-config.js b/app/code/Magento/Translation/view/frontend/requirejs-config.js index 47ccaf5a33e57..4414f0d153ee8 100644 --- a/app/code/Magento/Translation/view/frontend/requirejs-config.js +++ b/app/code/Magento/Translation/view/frontend/requirejs-config.js @@ -7,7 +7,7 @@ var config = { map: { '*': { editTrigger: 'mage/edit-trigger', - addClass: 'Magento_Translation/add-class' + addClass: 'Magento_Translation/js/add-class' } }, deps: [ diff --git a/app/code/Magento/Translation/view/frontend/web/add-class.js b/app/code/Magento/Translation/view/frontend/web/js/add-class.js similarity index 100% rename from app/code/Magento/Translation/view/frontend/web/add-class.js rename to app/code/Magento/Translation/view/frontend/web/js/add-class.js diff --git a/app/code/Magento/Weee/view/frontend/requirejs-config.js b/app/code/Magento/Weee/view/frontend/requirejs-config.js index 49dfb6b9d469b..5b1b3a0f7ec73 100644 --- a/app/code/Magento/Weee/view/frontend/requirejs-config.js +++ b/app/code/Magento/Weee/view/frontend/requirejs-config.js @@ -6,7 +6,7 @@ var config = { map: { '*': { - 'taxToggle': 'Magento_Weee/tax-toggle' + 'taxToggle': 'Magento_Weee/js/tax-toggle' } } }; diff --git a/app/code/Magento/Weee/view/frontend/web/tax-toggle.js b/app/code/Magento/Weee/view/frontend/web/js/tax-toggle.js similarity index 100% rename from app/code/Magento/Weee/view/frontend/web/tax-toggle.js rename to app/code/Magento/Weee/view/frontend/web/js/tax-toggle.js From d866b18757ca50bc97df4038a874a4bddf465a4c Mon Sep 17 00:00:00 2001 From: "al.kravchuk" Date: Fri, 6 Jul 2018 15:07:14 +0300 Subject: [PATCH 029/211] magento/magento2#7372: Product images gets removed from "Images And Videos" after validation alert. - Add check for product in case of not initialized product. --- .../Magento/Catalog/Controller/Adminhtml/Product/Save.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php index 15154b02dbf8a..03fd93080ee77 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php @@ -147,13 +147,13 @@ public function execute() } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); $this->messageManager->addExceptionMessage($e); - $data = $this->persistMediaData($product, $data); + $data = isset($product) ? $this->persistMediaData($product, $data) : $data; $this->getDataPersistor()->set('catalog_product', $data); $redirectBack = $productId ? true : 'new'; } catch (\Exception $e) { $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); $this->messageManager->addErrorMessage($e->getMessage()); - $data = $this->persistMediaData($product, $data); + $data = isset($product) ? $this->persistMediaData($product, $data) : $data; $this->getDataPersistor()->set('catalog_product', $data); $redirectBack = $productId ? true : 'new'; } From a012c45166619554619691b996d4474ed6278bfb Mon Sep 17 00:00:00 2001 From: gwharton Date: Fri, 6 Jul 2018 13:34:43 +0100 Subject: [PATCH 030/211] Fix broken commit in #15040 that accidentally reverted previous changes. --- .../templates/product/view/gallery.phtml | 40 +++++-------------- 1 file changed, 10 insertions(+), 30 deletions(-) diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/gallery.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/gallery.phtml index ffecb2ba4f305..a39701b2c8a17 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/gallery.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/gallery.phtml @@ -47,21 +47,11 @@ "data": getGalleryImagesJson() ?>, "options": { "nav": "getVar("gallery/nav") ?>", - getVar("gallery/loop"))): ?> - "loop": getVar("gallery/loop") ?>, - - getVar("gallery/keyboard"))): ?> - "keyboard": getVar("gallery/keyboard") ?>, - - getVar("gallery/arrows"))): ?> - "arrows": getVar("gallery/arrows") ?>, - - getVar("gallery/allowfullscreen"))): ?> - "allowfullscreen": getVar("gallery/allowfullscreen") ?>, - - getVar("gallery/caption"))): ?> - "showCaption": getVar("gallery/caption") ?>, - + "loop": getVar("gallery/loop") ? 'true' : 'false' ?>, + "keyboard": getVar("gallery/keyboard") ? 'true' : 'false' ?>, + "arrows": getVar("gallery/arrows") ? 'true' : 'false' ?>, + "allowfullscreen": getVar("gallery/allowfullscreen") ? 'true' : 'false' ?>, + "showCaption": getVar("gallery/caption") ? 'true' : 'false' ?>, "width": "getImageAttribute('product_page_image_medium', 'width') ?>", "thumbwidth": "getImageAttribute('product_page_image_small', 'width') ?>", getImageAttribute('product_page_image_small', 'height') || $block->getImageAttribute('product_page_image_small', 'width')): ?> @@ -79,28 +69,18 @@ "transitionduration": getVar("gallery/transition/duration") ?>, "transition": "getVar("gallery/transition/effect") ?>", - getVar("gallery/navarrows"))): ?> - "navarrows": getVar("gallery/navarrows") ?>, - + "navarrows": getVar("gallery/navarrows") ? 'true' : 'false' ?>, "navtype": "getVar("gallery/navtype") ?>", "navdir": "getVar("gallery/navdir") ?>" }, "fullscreen": { "nav": "getVar("gallery/fullscreen/nav") ?>", - getVar("gallery/fullscreen/loop")): ?> - "loop": getVar("gallery/fullscreen/loop") ?>, - + "loop": getVar("gallery/fullscreen/loop") ? 'true' : 'false' ?>, "navdir": "getVar("gallery/fullscreen/navdir") ?>", - getVar("gallery/transition/navarrows")): ?> - "navarrows": getVar("gallery/fullscreen/navarrows") ?>, - + "navarrows": getVar("gallery/fullscreen/navarrows") ? 'true' : 'false' ?>, "navtype": "getVar("gallery/fullscreen/navtype") ?>", - getVar("gallery/fullscreen/arrows")): ?> - "arrows": getVar("gallery/fullscreen/arrows") ?>, - - getVar("gallery/fullscreen/caption")): ?> - "showCaption": getVar("gallery/fullscreen/caption") ?>, - + "arrows": getVar("gallery/fullscreen/arrows") ? 'true' : 'false' ?>, + "showCaption": getVar("gallery/fullscreen/caption") ? 'true' : 'false' ?>, getVar("gallery/fullscreen/transition/duration")): ?> "transitionduration": getVar("gallery/fullscreen/transition/duration") ?>, From bc00d8e059ba168694962249d09cd82f99e9ef46 Mon Sep 17 00:00:00 2001 From: "al.kravchuk" Date: Fri, 6 Jul 2018 16:13:34 +0300 Subject: [PATCH 031/211] magento/magento2#13177: Can't save attributes on a configurable product. - fix error while saving configurable product after validation error. --- .../adminhtml/web/js/variations/variations.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.js index be290e49a43c3..e2e0faec3b805 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.js +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.js @@ -407,15 +407,17 @@ define([ * - associated_product_ids_serialized. */ serializeData: function () { - this.source.data['configurable-matrix-serialized'] = - JSON.stringify(this.source.data['configurable-matrix']); - - delete this.source.data['configurable-matrix']; - - this.source.data['associated_product_ids_serialized'] = - JSON.stringify(this.source.data['associated_product_ids']); + if (this.source.data['configurable-matrix']) { + this.source.data['configurable-matrix-serialized'] = + JSON.stringify(this.source.data['configurable-matrix']); + delete this.source.data['configurable-matrix']; + } - delete this.source.data['associated_product_ids']; + if (this.source.data['associated_product_ids']) { + this.source.data['associated_product_ids_serialized'] = + JSON.stringify(this.source.data['associated_product_ids']); + delete this.source.data['associated_product_ids']; + } }, /** From 19bff300ecd22dca8966da691f01044ba9e5575b Mon Sep 17 00:00:00 2001 From: Danny Verkade Date: Mon, 9 Jul 2018 11:50:47 -0400 Subject: [PATCH 032/211] - Removed the unused Totalbar block from the invoice create and credit memo create layouts. - Deprecated Totalbar class - Deprecated totalbar template file --- app/code/Magento/Sales/Block/Adminhtml/Order/Totalbar.php | 1 + .../Sales/view/adminhtml/layout/sales_order_creditmemo_new.xml | 1 - .../view/adminhtml/layout/sales_order_creditmemo_updateqty.xml | 1 - .../Sales/view/adminhtml/layout/sales_order_invoice_new.xml | 1 - .../view/adminhtml/layout/sales_order_invoice_updateqty.xml | 1 - .../Magento/Sales/view/adminhtml/templates/order/totalbar.phtml | 1 + 6 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Totalbar.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Totalbar.php index 648a281228908..c4ce48d162c2c 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Totalbar.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Totalbar.php @@ -8,6 +8,7 @@ /** * Adminhtml creditmemo bar * + * @deprecated * @api * @author Magento Core Team * @since 100.0.2 diff --git a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_new.xml b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_new.xml index 0c1b395b5116d..71490553aff17 100644 --- a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_new.xml +++ b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_new.xml @@ -22,7 +22,6 @@ - diff --git a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_updateqty.xml b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_updateqty.xml index 29a61308391c6..8375bec965794 100644 --- a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_updateqty.xml +++ b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_updateqty.xml @@ -13,7 +13,6 @@ - diff --git a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_invoice_new.xml b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_invoice_new.xml index def5ebaf546cd..b589ac0ac793d 100644 --- a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_invoice_new.xml +++ b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_invoice_new.xml @@ -25,7 +25,6 @@ - diff --git a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_invoice_updateqty.xml b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_invoice_updateqty.xml index 4df3f057f6a58..38e4cb50f4343 100644 --- a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_invoice_updateqty.xml +++ b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_invoice_updateqty.xml @@ -13,7 +13,6 @@ - diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/totalbar.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/totalbar.phtml index b038c72199c7f..8ff360b9635c3 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/totalbar.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/totalbar.phtml @@ -4,6 +4,7 @@ * See COPYING.txt for license details. */ +// @deprecated // @codingStandardsIgnoreFile ?> From e189fc97873278be4e4358565f022bd15276d64e Mon Sep 17 00:00:00 2001 From: vitaliyboyko Date: Wed, 11 Jul 2018 21:34:48 +0300 Subject: [PATCH 033/211] 16544: fixed behaviour when some of JS validation rules making fields required --- .../view/base/web/js/lib/validation/rules.js | 52 +++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js index 9c7d4b9d4fa81..beaf31241e8ea 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js @@ -72,13 +72,13 @@ define([ ], 'max-words': [ function (value, params) { - return utils.stripHtml(value).match(/\b\w+\b/g).length < params; + return utils.isEmpty(value) || utils.stripHtml(value).match(/\b\w+\b/g).length < params; }, $.mage.__('Please enter {0} words or less.') ], 'min-words': [ function (value, params) { - return utils.stripHtml(value).match(/\b\w+\b/g).length >= params; + return utils.isEmpty(value) || utils.stripHtml(value).match(/\b\w+\b/g).length >= params; }, $.mage.__('Please enter at least {0} words.') ], @@ -86,49 +86,52 @@ define([ function (value, params) { var match = utils.stripHtml(value).match(/\b\w+\b/g) || []; - return match.length >= params[0] && + return utils.isEmpty(value) || match.length >= params[0] && match.length <= params[1]; }, $.mage.__('Please enter between {0} and {1} words.') ], 'letters-with-basic-punc': [ function (value) { - return /^[a-z\-.,()\u0027\u0022\s]+$/i.test(value); + return utils.isEmpty(value) || /^[a-z\-.,()\u0027\u0022\s]+$/i.test(value); }, $.mage.__('Letters or punctuation only please') ], 'alphanumeric': [ function (value) { - return /^\w+$/i.test(value); + return utils.isEmpty(value) || /^\w+$/i.test(value); }, $.mage.__('Letters, numbers, spaces or underscores only please') ], 'letters-only': [ function (value) { - return /^[a-z]+$/i.test(value); + return utils.isEmpty(value) || /^[a-z]+$/i.test(value); }, $.mage.__('Letters only please') ], 'no-whitespace': [ function (value) { - return /^\S+$/i.test(value); + return utils.isEmpty(value) || /^\S+$/i.test(value); }, $.mage.__('No white space please') ], 'zip-range': [ function (value) { - return /^90[2-5]-\d{2}-\d{4}$/.test(value); + return utils.isEmpty(value) || /^90[2-5]-\d{2}-\d{4}$/.test(value); }, $.mage.__('Your ZIP-code must be in the range 902xx-xxxx to 905-xx-xxxx') ], 'integer': [ function (value) { - return /^-?\d+$/.test(value); + return utils.isEmpty(value) || /^-?\d+$/.test(value); }, $.mage.__('A positive or negative non-decimal number please') ], 'vinUS': [ function (value) { + if (utils.isEmpty(value)) { + return true; + } if (value.length !== 17) { return false; } @@ -214,13 +217,13 @@ define([ ], 'time': [ function (value) { - return /^([01]\d|2[0-3])(:[0-5]\d){0,2}$/.test(value); + return utils.isEmpty(value) || /^([01]\d|2[0-3])(:[0-5]\d){0,2}$/.test(value); }, $.mage.__('Please enter a valid time, between 00:00 and 23:59') ], 'time12h': [ function (value) { - return /^((0?[1-9]|1[012])(:[0-5]\d){0,2}(\ [AP]M))$/i.test(value); + return utils.isEmpty(value) || /^((0?[1-9]|1[012])(:[0-5]\d){0,2}(\ [AP]M))$/i.test(value); }, $.mage.__('Please enter a valid time, between 00:00 am and 12:00 pm') ], @@ -228,19 +231,19 @@ define([ function (value) { value = value.replace(/\s+/g, ''); - return value.length > 9 && value.match(/^(1-?)?(\([2-9]\d{2}\)|[2-9]\d{2})-?[2-9]\d{2}-?\d{4}$/); + return utils.isEmpty(value) || value.length > 9 && value.match(/^(1-?)?(\([2-9]\d{2}\)|[2-9]\d{2})-?[2-9]\d{2}-?\d{4}$/); }, $.mage.__('Please specify a valid phone number') ], 'phoneUK': [ function (value) { - return value.length > 9 && value.match(/^(\(?(0|\+44)[1-9]{1}\d{1,4}?\)?\s?\d{3,4}\s?\d{3,4})$/); + return utils.isEmpty(value) || value.length > 9 && value.match(/^(\(?(0|\+44)[1-9]{1}\d{1,4}?\)?\s?\d{3,4}\s?\d{3,4})$/); }, $.mage.__('Please specify a valid phone number') ], 'mobileUK': [ function (value) { - return value.length > 9 && value.match(/^((0|\+44)7\d{3}\s?\d{6})$/); + return utils.isEmpty(value) || value.length > 9 && value.match(/^((0|\+44)7\d{3}\s?\d{6})$/); }, $.mage.__('Please specify a valid mobile number') ], @@ -252,13 +255,13 @@ define([ ], 'email2': [ function (value) { - return /^((([a-z]|\d|[!#\$%&\u0027\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&\u0027\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\u0022)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\u0022)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)*(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(value);//eslint-disable-line max-len + return utils.isEmpty(value) || /^((([a-z]|\d|[!#\$%&\u0027\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&\u0027\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\u0022)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\u0022)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)*(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(value);//eslint-disable-line max-len }, $.validator.messages.email ], 'url2': [ function (value) { - return /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&\u0027\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)*(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&\u0027\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&\u0027\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&\u0027\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&\u0027\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value);//eslint-disable-line max-len + return utils.isEmpty(value) || /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&\u0027\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)*(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&\u0027\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&\u0027\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&\u0027\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&\u0027\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value);//eslint-disable-line max-len }, $.validator.messages.url ], @@ -266,6 +269,9 @@ define([ function (value, param) { var validTypes; + if (utils.isEmpty(value)) { + return true; + } if (/[^0-9-]+/.test(value)) { return false; } @@ -350,19 +356,19 @@ define([ ], 'ipv4': [ function (value) { - return /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/i.test(value);//eslint-disable-line max-len + return utils.isEmpty(value) || /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/i.test(value);//eslint-disable-line max-len }, $.mage.__('Please enter a valid IP v4 address.') ], 'ipv6': [ function (value) { - return /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i.test(value);//eslint-disable-line max-len + return utils.isEmpty(value) || /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i.test(value);//eslint-disable-line max-len }, $.mage.__('Please enter a valid IP v6 address.') ], 'pattern': [ function (value, param) { - return new RegExp(param).test(value); + return utils.isEmpty(value) || new RegExp(param).test(value); }, $.mage.__('Invalid format.') ], @@ -838,7 +844,7 @@ define([ return validateCreditCard(value); } - return false; + return true; }, $.mage.__('Please enter a valid credit card number.') ], @@ -879,10 +885,14 @@ define([ ], 'validate-per-page-value-list': [ function (value) { - var isValid = !utils.isEmpty(value), + var isValid = utils.isEmpty(value), values = value.split(','), i; + if (isValid) { + return true; + } + for (i = 0; i < values.length; i++) { if (!/^[0-9]+$/.test(values[i])) { isValid = false; From 2ed9af37dee0e223b2cae2d22659564a3b472a74 Mon Sep 17 00:00:00 2001 From: vitaliyboyko Date: Thu, 12 Jul 2018 09:35:02 +0300 Subject: [PATCH 034/211] 16544: Static fixes --- .../Magento/Ui/view/base/web/js/lib/validation/rules.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js index beaf31241e8ea..6bb83fd9eed16 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js @@ -231,13 +231,15 @@ define([ function (value) { value = value.replace(/\s+/g, ''); - return utils.isEmpty(value) || value.length > 9 && value.match(/^(1-?)?(\([2-9]\d{2}\)|[2-9]\d{2})-?[2-9]\d{2}-?\d{4}$/); + return utils.isEmpty(value) || value.length > 9 && + value.match(/^(1-?)?(\([2-9]\d{2}\)|[2-9]\d{2})-?[2-9]\d{2}-?\d{4}$/); }, $.mage.__('Please specify a valid phone number') ], 'phoneUK': [ function (value) { - return utils.isEmpty(value) || value.length > 9 && value.match(/^(\(?(0|\+44)[1-9]{1}\d{1,4}?\)?\s?\d{3,4}\s?\d{3,4})$/); + return utils.isEmpty(value) || value.length > 9 && + value.match(/^(\(?(0|\+44)[1-9]{1}\d{1,4}?\)?\s?\d{3,4}\s?\d{3,4})$/); }, $.mage.__('Please specify a valid phone number') ], From e71836be515b7e0eb15fb1806e34b4672dcd7740 Mon Sep 17 00:00:00 2001 From: vitaliyboyko Date: Thu, 12 Jul 2018 12:27:33 +0300 Subject: [PATCH 035/211] 16544: Static fixes, added empty lines to if statements --- app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js index 6bb83fd9eed16..34a06a4c6ac22 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js @@ -132,6 +132,7 @@ define([ if (utils.isEmpty(value)) { return true; } + if (value.length !== 17) { return false; } @@ -274,6 +275,7 @@ define([ if (utils.isEmpty(value)) { return true; } + if (/[^0-9-]+/.test(value)) { return false; } From c6a8fec4eedd1fe4033c56f42010bef81e7e1c6f Mon Sep 17 00:00:00 2001 From: vitaliyboyko Date: Thu, 12 Jul 2018 13:31:08 +0300 Subject: [PATCH 036/211] 16544: Fixed range-words test --- .../app/code/Magento/Ui/base/js/lib/validation/rules.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/validation/rules.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/validation/rules.test.js index 5d90d6088ba90..19b4baaff05ae 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/validation/rules.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/validation/rules.test.js @@ -15,7 +15,7 @@ define([ var value = '', params = [1,3]; - expect(rules['range-words'].handler(value, params)).toBe(false); + expect(rules['range-words'].handler(value, params)).toBe(true); }); it('Check on redundant words', function () { From af1c7362bfeb5a98a886604f7221003ef0fd377d Mon Sep 17 00:00:00 2001 From: Ievgen Sentiabov Date: Thu, 12 Jul 2018 10:30:41 +0300 Subject: [PATCH 037/211] MAGETWO-92831: Currency conversion rate services do not work in admin panel - Removed not working Yahoo Finance and Webservicex currency services --- .../Model/Currency/Import/Webservicex.php | 92 --------- .../Model/Currency/Import/YahooFinance.php | 177 ------------------ .../Currency/Import/YahooFinanceTest.php | 94 ---------- .../Test/Unit/Model/ObserverTest.php | 153 --------------- .../Directory/etc/adminhtml/system.xml | 12 -- app/code/Magento/Directory/etc/config.xml | 6 - app/code/Magento/Directory/etc/di.xml | 8 - app/code/Magento/Directory/i18n/en_US.csv | 2 - .../System/Currency/FetchRatesTest.php | 81 -------- .../Magento/Directory/Model/ObserverTest.php | 90 --------- 10 files changed, 715 deletions(-) delete mode 100644 app/code/Magento/Directory/Model/Currency/Import/Webservicex.php delete mode 100644 app/code/Magento/Directory/Model/Currency/Import/YahooFinance.php delete mode 100644 app/code/Magento/Directory/Test/Unit/Model/Currency/Import/YahooFinanceTest.php delete mode 100644 app/code/Magento/Directory/Test/Unit/Model/ObserverTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Directory/Model/ObserverTest.php diff --git a/app/code/Magento/Directory/Model/Currency/Import/Webservicex.php b/app/code/Magento/Directory/Model/Currency/Import/Webservicex.php deleted file mode 100644 index fa3c3a9018dbc..0000000000000 --- a/app/code/Magento/Directory/Model/Currency/Import/Webservicex.php +++ /dev/null @@ -1,92 +0,0 @@ -scopeConfig = $scopeConfig; - $this->httpClientFactory = $zendClientFactory ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\HTTP\ZendClientFactory::class); - } - - /** - * @param string $currencyFrom - * @param string $currencyTo - * @param int $retry - * @return float|null - */ - protected function _convert($currencyFrom, $currencyTo, $retry = 0) - { - $url = str_replace('{{CURRENCY_FROM}}', $currencyFrom, self::CURRENCY_CONVERTER_URL); - $url = str_replace('{{CURRENCY_TO}}', $currencyTo, $url); - /** @var \Magento\Framework\HTTP\ZendClient $httpClient */ - $httpClient = $this->httpClientFactory->create(); - - try { - $response = $httpClient->setUri( - $url - )->setConfig( - [ - 'timeout' => $this->scopeConfig->getValue( - 'currency/webservicex/timeout', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ), - ] - )->request( - 'GET' - )->getBody(); - - $xml = simplexml_load_string($response, null, LIBXML_NOERROR); - if (!$xml || (isset($xml[0]) && $xml[0] == -1)) { - $this->_messages[] = __('We can\'t retrieve a rate from %1.', $url); - return null; - } - return (double)$xml; - } catch (\Exception $e) { - if ($retry == 0) { - $this->_convert($currencyFrom, $currencyTo, 1); - } else { - $this->_messages[] = __('We can\'t retrieve a rate from %1.', $url); - } - } - } -} diff --git a/app/code/Magento/Directory/Model/Currency/Import/YahooFinance.php b/app/code/Magento/Directory/Model/Currency/Import/YahooFinance.php deleted file mode 100644 index 5f96b8627af65..0000000000000 --- a/app/code/Magento/Directory/Model/Currency/Import/YahooFinance.php +++ /dev/null @@ -1,177 +0,0 @@ -scopeConfig = $scopeConfig; - $this->httpClientFactory = $httpClientFactory; - } - - /** - * {@inheritdoc} - */ - public function fetchRates() - { - $data = []; - $currencies = $this->_getCurrencyCodes(); - $defaultCurrencies = $this->_getDefaultCurrencyCodes(); - - foreach ($defaultCurrencies as $currencyFrom) { - if (!isset($data[$currencyFrom])) { - $data[$currencyFrom] = []; - } - $data = $this->convertBatch($data, $currencyFrom, $currencies); - ksort($data[$currencyFrom]); - } - return $data; - } - - /** - * Return currencies convert rates in batch mode - * - * @param array $data - * @param string $currencyFrom - * @param array $currenciesTo - * @return array - */ - private function convertBatch($data, $currencyFrom, $currenciesTo) - { - $url = $this->buildUrl($currencyFrom, $currenciesTo); - set_time_limit(0); - try { - $response = $this->getServiceResponse($url); - } finally { - ini_restore('max_execution_time'); - } - - foreach ($currenciesTo as $currencyTo) { - if ($currencyFrom == $currencyTo) { - $data[$currencyFrom][$currencyTo] = $this->_numberFormat(1); - } else { - if (empty($response[$currencyFrom . $currencyTo])) { - $this->_messages[] = __('We can\'t retrieve a rate from %1 for %2.', $url, $currencyTo); - $data[$currencyFrom][$currencyTo] = null; - } else { - $data[$currencyFrom][$currencyTo] = $this->_numberFormat( - (double)$response[$currencyFrom . $currencyTo] - ); - } - } - } - return $data; - } - - /** - * Get Fixer.io service response - * - * @param string $url - * @param int $retry - * @return array - */ - private function getServiceResponse($url, $retry = 0) - { - /** @var \Magento\Framework\HTTP\ZendClient $httpClient */ - $httpClient = $this->httpClientFactory->create(); - $response = []; - try { - $jsonResponse = $httpClient->setUri( - $url - )->setConfig( - [ - 'timeout' => $this->scopeConfig->getValue( - $this->timeoutConfigPath, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ), - ] - )->request( - 'GET' - )->getBody(); - - $jsonResponse = json_decode($jsonResponse, true); - if (!empty($jsonResponse['query']['results']['rate'])) { - $response = array_column($jsonResponse['query']['results']['rate'], 'Rate', 'id'); - } - } catch (\Exception $e) { - if ($retry == 0) { - $response = $this->getServiceResponse($url, 1); - } - } - return $response; - } - - /** - * {@inheritdoc} - */ - protected function _convert($currencyFrom, $currencyTo) - { - } - - /** - * Build url for Currency Service - * - * @param string $currencyFrom - * @param string[] $currenciesTo - * @return string - */ - private function buildUrl($currencyFrom, $currenciesTo) - { - $query = urlencode('select ') . '*' . urlencode(' from yahoo.finance.xchange where pair in ('); - $query .= - urlencode( - implode( - ',', - array_map( - function ($currencyTo) use ($currencyFrom) { - return '"' . $currencyFrom . $currencyTo . '"'; - }, - $currenciesTo - ) - ) - ); - $query .= ')'; - return str_replace('{{YQL_STRING}}', $query, $this->currencyConverterUrl); - } -} diff --git a/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/YahooFinanceTest.php b/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/YahooFinanceTest.php deleted file mode 100644 index b7b9015ffdfe6..0000000000000 --- a/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/YahooFinanceTest.php +++ /dev/null @@ -1,94 +0,0 @@ -currencyFactoryMock = $this->getMockBuilder(\Magento\Directory\Model\CurrencyFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $this->httpClientFactoryMock = $this->getMockBuilder(\Magento\Framework\HTTP\ZendClientFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $scopeMock = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) - ->disableOriginalConstructor() - ->setMethods([]) - ->getMock(); - - $this->model = $objectManagerHelper->getObject( - \Magento\Directory\Model\Currency\Import\YahooFinance::class, - [ - 'currencyFactory' => $this->currencyFactoryMock, - 'scopeConfig' => $scopeMock, - 'httpClientFactory' => $this->httpClientFactoryMock - ] - ); - } - - public function testFetchRates() - { - $currencyFromList = ['USD']; - $currencyToList = ['EUR', 'UAH']; - $responseBody = '{"query":{"count":7,"created":"2016-04-05T16:46:55Z","lang":"en-US","results":{"rate":' - . '[{"id":"USDEUR","Name":"USD/EUR","Rate":"0.9022","Date":"4/5/2016"}]}}}'; - $expectedCurrencyRateList = ['USD' => ['EUR' => 0.9022, 'UAH' => null]]; - $message = "We can't retrieve a rate from http://query.yahooapis.com/v1/public/yql?format=json" - . "&q=select+*+from+yahoo.finance.xchange+where+pair+in+%28%22USDEUR%22%2C%22USDUAH%22)" - . "&env=store://datatables.org/alltableswithkeys for UAH."; - - /** @var \Magento\Directory\Model\Currency|\PHPUnit_Framework_MockObject_MockObject $currencyMock */ - $currencyMock = $this->getMockBuilder(\Magento\Directory\Model\Currency::class) - ->disableOriginalConstructor() - ->setMethods([]) - ->getMock(); - /** @var \Magento\Framework\HTTP\ZendClient|\PHPUnit_Framework_MockObject_MockObject $currencyMock */ - $httpClientMock = $this->getMockBuilder(\Magento\Framework\HTTP\ZendClient::class) - ->disableOriginalConstructor() - ->setMethods([]) - ->getMock(); - /** @var \Zend_Http_Response|\PHPUnit_Framework_MockObject_MockObject $currencyMock */ - $httpResponseMock = $this->getMockBuilder('Zend_Http_Response') - ->disableOriginalConstructor() - ->setMethods([]) - ->getMock(); - $this->currencyFactoryMock->expects($this->any())->method('create')->willReturn($currencyMock); - $currencyMock->expects($this->once())->method('getConfigBaseCurrencies')->willReturn($currencyFromList); - $currencyMock->expects($this->once())->method('getConfigAllowCurrencies')->willReturn($currencyToList); - $this->httpClientFactoryMock->expects($this->any())->method('create')->willReturn($httpClientMock); - $httpClientMock->expects($this->atLeastOnce())->method('setUri')->willReturnSelf(); - $httpClientMock->expects($this->atLeastOnce())->method('setConfig')->willReturnSelf(); - $httpClientMock->expects($this->atLeastOnce())->method('request')->willReturn($httpResponseMock); - $httpResponseMock->expects($this->any())->method('getBody')->willReturn($responseBody); - - $this->assertEquals($expectedCurrencyRateList, $this->model->fetchRates()); - $messages = $this->model->getMessages(); - $this->assertNotEmpty($messages); - $this->assertTrue(is_array($messages)); - $this->assertEquals($message, (string)$messages[0]); - } -} diff --git a/app/code/Magento/Directory/Test/Unit/Model/ObserverTest.php b/app/code/Magento/Directory/Test/Unit/Model/ObserverTest.php deleted file mode 100644 index 5fb7271a411ed..0000000000000 --- a/app/code/Magento/Directory/Test/Unit/Model/ObserverTest.php +++ /dev/null @@ -1,153 +0,0 @@ -objectManager = new ObjectManager($this); - - $this->importFactory = $this->getMockBuilder(\Magento\Directory\Model\Currency\Import\Factory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $this->scopeConfig = $this->getMockBuilder(\Magento\Framework\App\MutableScopeConfig::class) - ->disableOriginalConstructor() - ->setMethods(['getValue']) - ->getMock(); - $this->transportBuilder = $this->getMockBuilder(\Magento\Framework\Mail\Template\TransportBuilder::class) - ->disableOriginalConstructor() - ->getMock(); - $this->storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManager::class) - ->disableOriginalConstructor() - ->getMock(); - $this->currencyFactory = $this->getMockBuilder(\Magento\Directory\Model\CurrencyFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $this->inlineTranslation = $this->getMockBuilder(\Magento\Framework\Translate\Inline\StateInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->observer = $this->objectManager->getObject( - \Magento\Directory\Model\Observer::class, - [ - 'importFactory' => $this->importFactory, - 'scopeConfig' => $this->scopeConfig, - 'transportBuilder' => $this->transportBuilder, - 'storeManager' => $this->storeManager, - 'currencyFactory' => $this->currencyFactory, - 'inlineTranslation' => $this->inlineTranslation - ] - ); - } - - public function testScheduledUpdateCurrencyRates() - { - $this->scopeConfig - ->expects($this->at(0)) - ->method('getValue') - ->with(Observer::IMPORT_ENABLE, ScopeInterface::SCOPE_STORE) - ->will($this->returnValue(1)); - $this->scopeConfig - ->expects($this->at(1)) - ->method('getValue') - ->with(Observer::CRON_STRING_PATH, ScopeInterface::SCOPE_STORE) - ->will($this->returnValue('cron-path')); - $this->scopeConfig - ->expects($this->at(2)) - ->method('getValue') - ->with(Observer::IMPORT_SERVICE, ScopeInterface::SCOPE_STORE) - ->will($this->returnValue('import-service')); - $importInterfaceMock = $this->getMockBuilder(\Magento\Directory\Model\Currency\Import\Webservicex::class) - ->disableOriginalConstructor() - ->setMethods(['fetchRates', 'getMessages']) - ->getMock(); - $importInterfaceMock->expects($this->once()) - ->method('fetchRates') - ->will($this->returnValue([])); - $importInterfaceMock->expects($this->once()) - ->method('getMessages') - ->will($this->returnValue([])); - - $this->importFactory - ->expects($this->once()) - ->method('create') - ->with('import-service') - ->will($this->returnValue($importInterfaceMock)); - - $currencyMock = $this->getMockBuilder(\Magento\Directory\Model\Currency::class) - ->disableOriginalConstructor() - ->setMethods(['saveRates', '__wakeup', '__sleep']) - ->getMock(); - $currencyMock->expects($this->once()) - ->method('saveRates') - ->will($this->returnValue(null)); - $this->currencyFactory - ->expects($this->once()) - ->method('create') - ->will($this->returnValue($currencyMock)); - - $this->observer->scheduledUpdateCurrencyRates(null); - } - - /** - * @expectedException \Exception - */ - public function testScheduledUpdateCurrencyRatesThrowsException() - { - $this->scopeConfig->expects($this->exactly(3)) - ->method('getValue') - ->willReturnMap( - [ - [Observer::IMPORT_ENABLE, ScopeInterface::SCOPE_STORE, null, 1], - [Observer::CRON_STRING_PATH, ScopeInterface::SCOPE_STORE, null, 'cron-path'], - [Observer::IMPORT_SERVICE, ScopeInterface::SCOPE_STORE, null, 'import-service'] - ] - ); - - $this->importFactory - ->expects($this->once()) - ->method('create') - ->with('import-service') - ->willThrowException(new \Exception()); - - $this->observer->scheduledUpdateCurrencyRates(null); - } -} diff --git a/app/code/Magento/Directory/etc/adminhtml/system.xml b/app/code/Magento/Directory/etc/adminhtml/system.xml index 15a82e006bffe..17d0a3b1946eb 100644 --- a/app/code/Magento/Directory/etc/adminhtml/system.xml +++ b/app/code/Magento/Directory/etc/adminhtml/system.xml @@ -34,24 +34,12 @@ 1 - - - - - - - - - - - - diff --git a/app/code/Magento/Directory/etc/config.xml b/app/code/Magento/Directory/etc/config.xml index fa4e9d64d10d8..0835099b7bf47 100644 --- a/app/code/Magento/Directory/etc/config.xml +++ b/app/code/Magento/Directory/etc/config.xml @@ -18,15 +18,9 @@ USD USD - - 100 - 100 - - 100 - 0 diff --git a/app/code/Magento/Directory/etc/di.xml b/app/code/Magento/Directory/etc/di.xml index 3bc50e3bc0316..2c345ef92fee1 100644 --- a/app/code/Magento/Directory/etc/di.xml +++ b/app/code/Magento/Directory/etc/di.xml @@ -10,14 +10,6 @@ - - Yahoo Finance Exchange - Magento\Directory\Model\Currency\Import\YahooFinance - - - Webservicex - Magento\Directory\Model\Currency\Import\Webservicex - Fixer.io Magento\Directory\Model\Currency\Import\FixerIo diff --git a/app/code/Magento/Directory/i18n/en_US.csv b/app/code/Magento/Directory/i18n/en_US.csv index 00e32ab8c8501..06b2c22fbfcf7 100644 --- a/app/code/Magento/Directory/i18n/en_US.csv +++ b/app/code/Magento/Directory/i18n/en_US.csv @@ -30,10 +30,8 @@ Continue,Continue " "Default Display Currency","Default Display Currency" "Allowed Currencies","Allowed Currencies" -"Yahoo Finance Exchange","Yahoo Finance Exchange" "Connection Timeout in Seconds","Connection Timeout in Seconds" Fixer.io,Fixer.io -Webservicex,Webservicex "Scheduled Import Settings","Scheduled Import Settings" Enabled,Enabled "Error Email Recipient","Error Email Recipient" diff --git a/dev/tests/integration/testsuite/Magento/CurrencySymbol/Controller/Adminhtml/System/Currency/FetchRatesTest.php b/dev/tests/integration/testsuite/Magento/CurrencySymbol/Controller/Adminhtml/System/Currency/FetchRatesTest.php index 28f744aa4142f..e1a1df2b50a51 100644 --- a/dev/tests/integration/testsuite/Magento/CurrencySymbol/Controller/Adminhtml/System/Currency/FetchRatesTest.php +++ b/dev/tests/integration/testsuite/Magento/CurrencySymbol/Controller/Adminhtml/System/Currency/FetchRatesTest.php @@ -42,85 +42,4 @@ public function testFetchRatesActionWithNonexistentService() \Magento\Framework\Message\MessageInterface::TYPE_ERROR ); } - - /** - * Test save action with nonexistent service - */ - public function testFetchRatesActionWithServiceErrors() - { - $this->runActionWithMockedImportService(['We can\'t retrieve a rate from url']); - - $this->assertSessionMessages( - $this->contains('Click "Save" to apply the rates we found.'), - \Magento\Framework\Message\MessageInterface::TYPE_WARNING - ); - } - - /** - * Test save action with nonexistent service - */ - public function testFetchRatesActionWithoutServiceErrors() - { - $this->runActionWithMockedImportService(); - - $this->assertSessionMessages( - $this->contains('Click "Save" to apply the rates we found.'), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS - ); - } - - /** - * Run fetchRates with mocked external import service - * - * @param array $messages messages from external import service - */ - protected function runActionWithMockedImportService(array $messages = []) - { - $importServiceMock = $this->getMockBuilder(\Magento\Directory\Model\Currency\Import\Webservicex::class) - ->disableOriginalConstructor() - ->getMock(); - - $importServiceMock->method('fetchRates') - ->willReturn(['USD' => ['USD' => 1]]); - - $importServiceMock->method('getMessages') - ->willReturn($messages); - - $backendSessionMock = $this->getMockBuilder(\Magento\Backend\Model\Session::class) - ->disableOriginalConstructor() - ->getMock(); - - $importServiceFactoryMock = $this->getMockBuilder(\Magento\Directory\Model\Currency\Import\Factory::class) - ->disableOriginalConstructor() - ->getMock(); - - $importServiceFactoryMock->method('create') - ->willReturn($importServiceMock); - - $objectManagerMock = $this->getMockBuilder(\Magento\Framework\ObjectManagerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $objectManagerMap = [ - [\Magento\Directory\Model\Currency\Import\Factory::class, $importServiceFactoryMock], - [\Magento\Backend\Model\Session::class, $backendSessionMock] - ]; - - $objectManagerMock->method('get') - ->will($this->returnValueMap($objectManagerMap)); - - $context = $this->_objectManager->create( - \Magento\Backend\App\Action\Context::class, - ["objectManager" => $objectManagerMock] - ); - $registry = $this->_objectManager->get(\Magento\Framework\Registry::class); - - $this->getRequest()->setParam('rate_services', 'webservicex'); - - $action = new \Magento\CurrencySymbol\Controller\Adminhtml\System\Currency\FetchRates( - $context, - $registry - ); - $action->execute(); - } } diff --git a/dev/tests/integration/testsuite/Magento/Directory/Model/ObserverTest.php b/dev/tests/integration/testsuite/Magento/Directory/Model/ObserverTest.php deleted file mode 100644 index ee323ace6d78e..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Directory/Model/ObserverTest.php +++ /dev/null @@ -1,90 +0,0 @@ -objectManager = Bootstrap::getObjectManager(); - - $this->scopeConfig = $this->objectManager->create(\Magento\Framework\App\MutableScopeConfig::class); - $this->scopeConfig->setValue(Observer::IMPORT_ENABLE, 1, ScopeInterface::SCOPE_STORE); - $this->scopeConfig->setValue(Observer::CRON_STRING_PATH, 'cron-string-path', ScopeInterface::SCOPE_STORE); - $this->scopeConfig->setValue(Observer::IMPORT_SERVICE, 'webservicex', ScopeInterface::SCOPE_STORE); - - $this->configResource = $this->objectManager->get(\Magento\Config\Model\ResourceModel\Config::class); - $this->configResource->saveConfig( - $this->baseCurrencyPath, - $this->baseCurrency, - ScopeInterface::SCOPE_STORE, - 0 - ); - - $this->observer = $this->objectManager->create(\Magento\Directory\Model\Observer::class); - } - - public function testScheduledUpdateCurrencyRates() - { - //skipping test if service is unavailable - $url = str_replace('{{CURRENCY_FROM}}', 'USD', - \Magento\Directory\Model\Currency\Import\Webservicex::CURRENCY_CONVERTER_URL - ); - $url = str_replace('{{CURRENCY_TO}}', 'GBP', $url); - try { - file_get_contents($url); - } catch (\PHPUnit\Framework\Exception $e) { - $this->markTestSkipped('http://www.webservicex.net is unavailable '); - } - - $allowedCurrencies = 'USD,GBP,EUR'; - $this->configResource->saveConfig( - $this->allowedCurrenciesPath, - $allowedCurrencies, - ScopeInterface::SCOPE_STORE, - 0 - ); - $this->observer->scheduledUpdateCurrencyRates(null); - /** @var Currency $currencyResource */ - $currencyResource = $this->objectManager - ->create(\Magento\Directory\Model\CurrencyFactory::class) - ->create() - ->getResource(); - $rates = $currencyResource->getCurrencyRates($this->baseCurrency, explode(',', $allowedCurrencies)); - $this->assertNotEmpty($rates); - } -} From c0c5bb6100483622088e3c8b2279dc6671e6fd4a Mon Sep 17 00:00:00 2001 From: Anshu Mishra Date: Fri, 13 Jul 2018 15:01:41 +0530 Subject: [PATCH 038/211] refactor code --- .../Controller/Adminhtml/Agreement.php | 33 +++++------------ .../Controller/Adminhtml/Agreement/Delete.php | 32 +++++++++++++++-- .../Controller/Adminhtml/Agreement/Edit.php | 33 +++++++++++++++-- .../Controller/Adminhtml/Agreement/Save.php | 36 ++++++++++++++++--- 4 files changed, 99 insertions(+), 35 deletions(-) diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement.php index 92829feecace9..aa6f461fc5ee2 100644 --- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement.php +++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement.php @@ -5,10 +5,11 @@ */ namespace Magento\CheckoutAgreements\Controller\Adminhtml; -use Magento\Framework\App\ObjectManager; -use Magento\CheckoutAgreements\Api\CheckoutAgreementsRepositoryInterface; +use Magento\Backend\App\Action; +use Magento\Backend\App\Action\Context; +use Magento\Framework\Registry; -abstract class Agreement extends \Magento\Backend\App\Action +abstract class Agreement extends Action { /** * Authorization level of a basic admin session @@ -23,35 +24,17 @@ abstract class Agreement extends \Magento\Backend\App\Action * @var \Magento\Framework\Registry */ protected $_coreRegistry = null; - - /** - * @var CheckoutAgreementsRepositoryInterface - */ - protected $_agreementRepository; - - /** - * @var \Magento\CheckoutAgreements\Model\AgreementFactory - */ - protected $_agreementFactory; /** - * @param \Magento\Backend\App\Action\Context $context - * @param \Magento\Framework\Registry $coreRegistry - * @param CheckoutAgreementsRepositoryInterface $agreementRepository - * @param \Magento\CheckoutAgreements\Model\AgreementFactory $agreementFactory + * @param Context $context + * @param Registry $coreRegistry * @codeCoverageIgnore */ public function __construct( - \Magento\Backend\App\Action\Context $context, - \Magento\Framework\Registry $coreRegistry, - CheckoutAgreementsRepositoryInterface $agreementRepository = null, - \Magento\CheckoutAgreements\Model\AgreementFactory $agreementFactory = null + Context $context, + Registry $coreRegistry ) { $this->_coreRegistry = $coreRegistry; - $this->_agreementRepository = $agreementRepository ?: - ObjectManager::getInstance()->get(CheckoutAgreementsRepositoryInterface::class); - $this->_agreementFactory = $agreementFactory ?: - ObjectManager::getInstance()->get(\Magento\CheckoutAgreements\Model\AgreementFactory::class); parent::__construct($context); } diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php index 169246e359b1e..144ce2b2f6bc5 100644 --- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php +++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php @@ -6,15 +6,41 @@ */ namespace Magento\CheckoutAgreements\Controller\Adminhtml\Agreement; -class Delete extends \Magento\CheckoutAgreements\Controller\Adminhtml\Agreement +use Magento\CheckoutAgreements\Api\CheckoutAgreementsRepositoryInterface; +use Magento\CheckoutAgreements\Controller\Adminhtml\Agreement; +use Magento\Backend\App\Action\Context; +use Magento\Framework\Registry; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\LocalizedException; + +class Delete extends Agreement { + /** + * @var CheckoutAgreementsRepositoryInterface + */ + private $agreementRepository; + + /** + * @param Context $context + * @param Registry $coreRegistry + * @param CheckoutAgreementsRepositoryInterface $agreementRepository + */ + public function __construct( + Context $context, + Registry $coreRegistry, + CheckoutAgreementsRepositoryInterface $agreementRepository = null + ) { + $this->agreementRepository = $agreementRepository ?: + ObjectManager::getInstance()->get(CheckoutAgreementsRepositoryInterface::class); + parent::__construct($context, $coreRegistry); + } /** * @return void */ public function execute() { $id = (int)$this->getRequest()->getParam('id'); - $repository = $this->_agreementRepository->get($id); + $repository = $this->agreementRepository->get($id); if (!$repository->getAgreementId()) { $this->messageManager->addError(__('This condition no longer exists.')); $this->_redirect('checkout/*/'); @@ -26,7 +52,7 @@ public function execute() $this->messageManager->addSuccess(__('You deleted the condition.')); $this->_redirect('checkout/*/'); return; - } catch (\Magento\Framework\Exception\LocalizedException $e) { + } catch (LocalizedException $e) { $this->messageManager->addError($e->getMessage()); } catch (\Exception $e) { $this->messageManager->addError(__('Something went wrong while deleting this condition.')); diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php index fb160f450f0dd..768ac6ee273f0 100644 --- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php +++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php @@ -6,8 +6,35 @@ */ namespace Magento\CheckoutAgreements\Controller\Adminhtml\Agreement; -class Edit extends \Magento\CheckoutAgreements\Controller\Adminhtml\Agreement +use Magento\CheckoutAgreements\Controller\Adminhtml\Agreement; +use Magento\CheckoutAgreements\Model\AgreementFactory; +use Magento\Backend\App\Action\Context; +use Magento\Framework\Registry; +use Magento\Framework\App\ObjectManager; +use Magento\CheckoutAgreements\Controller\Adminhtml\Agreement; +use Magento\CheckoutAgreements\Block\Adminhtml\Agreement\Edit; + +class Edit extends Agreement { + /** + * @var AgreementFactory + */ + private $agreementFactory; + + /** + * @param Context $context + * @param Registry $coreRegistry + * @param AgreementFactory $agreementFactory + */ + public function __construct( + Context $context, + Registry $coreRegistry, + AgreementFactory $agreementFactory = null + ) { + $this->agreementFactory = $agreementFactory ?: + ObjectManager::getInstance()->get(AgreementFactory::class); + parent::__construct($context, $coreRegistry); + } /** * @return void * @SuppressWarnings(PHPMD.NPathComplexity) @@ -15,7 +42,7 @@ class Edit extends \Magento\CheckoutAgreements\Controller\Adminhtml\Agreement public function execute() { $id = $this->getRequest()->getParam('id'); - $agreementModel = $this->_agreementFactory->create(); + $agreementModel = $this->agreementFactory->create(); if ($id) { $agreementModel->load($id); @@ -38,7 +65,7 @@ public function execute() $id ? __('Edit Condition') : __('New Condition') )->_addContent( $this->_view->getLayout()->createBlock( - \Magento\CheckoutAgreements\Block\Adminhtml\Agreement\Edit::class + Edit::class )->setData( 'action', $this->getUrl('checkout/*/save') diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php index c8c2b7d0a40bf..c0eef133a2a9a 100644 --- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php +++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php @@ -6,8 +6,36 @@ */ namespace Magento\CheckoutAgreements\Controller\Adminhtml\Agreement; -class Save extends \Magento\CheckoutAgreements\Controller\Adminhtml\Agreement +use Magento\CheckoutAgreements\Controller\Adminhtml\Agreement; +use Magento\CheckoutAgreements\Model\AgreementFactory; +use Magento\Backend\App\Action\Context; +use Magento\Framework\Registry; +use Magento\Framework\App\ObjectManager; +use Magento\CheckoutAgreements\Controller\Adminhtml\Agreement; +use Magento\Framework\DataObject; +use Magento\Framework\Exception\LocalizedException; + +class Save extends Agreement { + /** + * @var AgreementFactory + */ + private $agreementFactory; + + /** + * @param Context $context + * @param Registry $coreRegistry + * @param AgreementFactory $agreementFactory + */ + public function __construct( + Context $context, + Registry $coreRegistry, + AgreementFactory $agreementFactory = null + ) { + $this->agreementFactory = $agreementFactory ?: + ObjectManager::getInstance()->get(AgreementFactory::class); + parent::__construct($context, $coreRegistry); + } /** * @return void */ @@ -15,11 +43,11 @@ public function execute() { $postData = $this->getRequest()->getPostValue(); if ($postData) { - $model = $this->_agreementFactory->create(); + $model = $this->agreementFactory->create(); $model->setData($postData); try { - $validationResult = $model->validateData(new \Magento\Framework\DataObject($postData)); + $validationResult = $model->validateData(new DataObject($postData)); if ($validationResult !== true) { foreach ($validationResult as $message) { $this->messageManager->addError($message); @@ -30,7 +58,7 @@ public function execute() $this->_redirect('checkout/*/'); return; } - } catch (\Magento\Framework\Exception\LocalizedException $e) { + } catch (LocalizedException $e) { $this->messageManager->addError($e->getMessage()); } catch (\Exception $e) { $this->messageManager->addError(__('Something went wrong while saving this condition.')); From 29d486ebf9dc89171490f99177d5917a194e7182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Bajsarowicz?= Date: Fri, 13 Jul 2018 17:04:54 +0200 Subject: [PATCH 039/211] Replace sort callback to spaceship --- app/code/Magento/Quote/Model/Quote/Address.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/app/code/Magento/Quote/Model/Quote/Address.php b/app/code/Magento/Quote/Model/Quote/Address.php index 67393e3598568..a738f844c8d8d 100644 --- a/app/code/Magento/Quote/Model/Quote/Address.php +++ b/app/code/Magento/Quote/Model/Quote/Address.php @@ -872,13 +872,7 @@ public function getGroupedAllShippingRates() */ protected function _sortRates($firstItem, $secondItem) { - if ((int)$firstItem[0]->carrier_sort_order < (int)$secondItem[0]->carrier_sort_order) { - return -1; - } elseif ((int)$firstItem[0]->carrier_sort_order > (int)$secondItem[0]->carrier_sort_order) { - return 1; - } else { - return 0; - } + return (int) $firstItem[0]->carrier_sort_order <=> (int) $secondItem[0]->carrier_sort_order; } /** From 9e6d4b5c7f7937c2f23f0ae53da8cc574e44a42d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Bajsarowicz?= Date: Fri, 13 Jul 2018 18:45:49 +0200 Subject: [PATCH 040/211] Additional usort callback replacements with spaceship operator --- .../Magento/Catalog/Model/Product/Type/AbstractType.php | 8 +------- app/code/Magento/Eav/Model/Entity/AbstractEntity.php | 8 +------- app/code/Magento/Sales/Model/Config/Ordered.php | 9 ++------- 3 files changed, 4 insertions(+), 21 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php b/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php index 9ad0db444eed0..5e00ec188addd 100644 --- a/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php +++ b/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php @@ -291,13 +291,7 @@ public function attributesCompare($attributeOne, $attributeTwo) $sortOne = $attributeOne->getGroupSortPath() * 1000 + $attributeOne->getSortPath() * 0.0001; $sortTwo = $attributeTwo->getGroupSortPath() * 1000 + $attributeTwo->getSortPath() * 0.0001; - if ($sortOne > $sortTwo) { - return 1; - } elseif ($sortOne < $sortTwo) { - return -1; - } - - return 0; + return $sortOne <=> $sortTwo; } /** diff --git a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php index feb2c4d7d3f9f..40b884aec529f 100644 --- a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php +++ b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php @@ -602,13 +602,7 @@ public function attributesCompare($firstAttribute, $secondAttribute) $firstSort = $firstAttribute->getSortWeight((int) $this->_sortingSetId); $secondSort = $secondAttribute->getSortWeight((int) $this->_sortingSetId); - if ($firstSort > $secondSort) { - return 1; - } elseif ($firstSort < $secondSort) { - return -1; - } - - return 0; + return $firstSort <=> $secondSort; } /** diff --git a/app/code/Magento/Sales/Model/Config/Ordered.php b/app/code/Magento/Sales/Model/Config/Ordered.php index 8c5ddb8e07df7..bae6223ee7d5e 100644 --- a/app/code/Magento/Sales/Model/Config/Ordered.php +++ b/app/code/Magento/Sales/Model/Config/Ordered.php @@ -167,13 +167,8 @@ function ($a, $b) { if (!isset($a['sort_order']) || !isset($b['sort_order'])) { return 0; } - if ($a['sort_order'] > $b['sort_order']) { - return 1; - } elseif ($a['sort_order'] < $b['sort_order']) { - return -1; - } else { - return 0; - } + + return $a['sort_order'] <=> $b['sort_order']; } ); } From ae92c11c479097b643251ea25cfd5413ba144b5f Mon Sep 17 00:00:00 2001 From: Marcel Hauri Date: Mon, 16 Jul 2018 08:19:59 +0200 Subject: [PATCH 041/211] [task] replace floatval() --- .../Backend/Block/Widget/Grid/Column/Renderer/Currency.php | 6 +++--- .../Backend/Block/Widget/Grid/Column/Renderer/Price.php | 6 +++--- .../Magento/Bundle/Pricing/Price/BundleSelectionFactory.php | 2 +- .../Magento/Catalog/Model/Product/Type/AbstractType.php | 2 +- .../Catalog/Model/ResourceModel/Layer/Filter/Price.php | 2 +- app/code/Magento/Catalog/Pricing/Price/RegularPrice.php | 2 +- app/code/Magento/Catalog/Pricing/Price/TierPrice.php | 2 +- .../Test/Unit/Model/Layer/Filter/DataProvider/PriceTest.php | 2 +- app/code/Magento/CatalogInventory/Model/Stock/Item.php | 2 +- .../Magento/CatalogRule/Pricing/Price/CatalogRulePrice.php | 4 ++-- app/code/Magento/Checkout/Helper/Data.php | 4 ++-- .../Config/Model/Config/Structure/Mapper/Sorting.php | 4 ++-- app/code/Magento/Directory/Model/Currency.php | 2 +- .../GroupedProduct/Pricing/Price/ConfiguredRegularPrice.php | 2 +- .../Block/Adminhtml/Grid/Column/Renderer/Currency.php | 2 +- app/code/Magento/Sales/Model/Order/StateResolver.php | 2 +- .../Sales/Model/ResourceModel/Order/Handler/State.php | 2 +- app/code/Magento/Tax/Helper/Data.php | 4 ++-- app/code/Magento/Ups/Model/Carrier.php | 2 +- 19 files changed, 27 insertions(+), 27 deletions(-) diff --git a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Currency.php b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Currency.php index b3f467ce37c88..03566bce3fc34 100644 --- a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Currency.php +++ b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Currency.php @@ -82,7 +82,7 @@ public function render(\Magento\Framework\DataObject $row) { if ($data = (string)$this->_getValue($row)) { $currency_code = $this->_getCurrencyCode($row); - $data = floatval($data) * $this->_getRate($row); + $data = (float)$data * $this->_getRate($row); $sign = (bool)(int)$this->getColumn()->getShowNumberSign() && $data > 0 ? '+' : ''; $data = sprintf("%f", $data); $data = $this->_localeCurrency->getCurrency($currency_code)->toCurrency($data); @@ -118,10 +118,10 @@ protected function _getCurrencyCode($row) protected function _getRate($row) { if ($rate = $this->getColumn()->getRate()) { - return floatval($rate); + return (float)$rate; } if ($rate = $row->getData($this->getColumn()->getRateField())) { - return floatval($rate); + return (float)$rate; } return $this->_defaultBaseCurrency->getRate($this->_getCurrencyCode($row)); } diff --git a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Price.php b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Price.php index e4300c63485f5..9da23af83f036 100644 --- a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Price.php +++ b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Price.php @@ -60,7 +60,7 @@ public function render(\Magento\Framework\DataObject $row) return $data; } - $data = floatval($data) * $this->_getRate($row); + $data = (float)$data * $this->_getRate($row); $data = sprintf("%f", $data); $data = $this->_localeCurrency->getCurrency($currencyCode)->toCurrency($data); return $data; @@ -94,10 +94,10 @@ protected function _getCurrencyCode($row) protected function _getRate($row) { if ($rate = $this->getColumn()->getRate()) { - return floatval($rate); + return (float)$rate; } if ($rate = $row->getData($this->getColumn()->getRateField())) { - return floatval($rate); + return (float)$rate; } return 1; } diff --git a/app/code/Magento/Bundle/Pricing/Price/BundleSelectionFactory.php b/app/code/Magento/Bundle/Pricing/Price/BundleSelectionFactory.php index 927b8fbff8d85..a28d721cc9a4e 100644 --- a/app/code/Magento/Bundle/Pricing/Price/BundleSelectionFactory.php +++ b/app/code/Magento/Bundle/Pricing/Price/BundleSelectionFactory.php @@ -54,7 +54,7 @@ public function create( ) { $arguments['bundleProduct'] = $bundleProduct; $arguments['saleableItem'] = $selection; - $arguments['quantity'] = $quantity ? floatval($quantity) : 1.; + $arguments['quantity'] = $quantity ? (float)$quantity : 1.; return $this->objectManager->create(self::SELECTION_CLASS_DEFAULT, $arguments); } diff --git a/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php b/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php index 9ad0db444eed0..d2635cb9f551d 100644 --- a/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php +++ b/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php @@ -938,7 +938,7 @@ public function getForceChildItemQtyChanges($product) */ public function prepareQuoteItemQty($qty, $product) { - return floatval($qty); + return (float)$qty; } /** diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Layer/Filter/Price.php b/app/code/Magento/Catalog/Model/ResourceModel/Layer/Filter/Price.php index 7f66e5e253706..ed51ca8b3c84b 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Layer/Filter/Price.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Layer/Filter/Price.php @@ -112,7 +112,7 @@ public function getCount($range) /** * Check and set correct variable values to prevent SQL-injections */ - $range = floatval($range); + $range = (float)$range; if ($range == 0) { $range = 1; } diff --git a/app/code/Magento/Catalog/Pricing/Price/RegularPrice.php b/app/code/Magento/Catalog/Pricing/Price/RegularPrice.php index 609255d852da3..92ecd9da989b0 100644 --- a/app/code/Magento/Catalog/Pricing/Price/RegularPrice.php +++ b/app/code/Magento/Catalog/Pricing/Price/RegularPrice.php @@ -29,7 +29,7 @@ public function getValue() if ($this->value === null) { $price = $this->product->getPrice(); $priceInCurrentCurrency = $this->priceCurrency->convertAndRound($price); - $this->value = $priceInCurrentCurrency ? floatval($priceInCurrentCurrency) : false; + $this->value = $priceInCurrentCurrency ? (float)$priceInCurrentCurrency : false; } return $this->value; } diff --git a/app/code/Magento/Catalog/Pricing/Price/TierPrice.php b/app/code/Magento/Catalog/Pricing/Price/TierPrice.php index 893978a3b6b3b..a62f3ed09ee1d 100644 --- a/app/code/Magento/Catalog/Pricing/Price/TierPrice.php +++ b/app/code/Magento/Catalog/Pricing/Price/TierPrice.php @@ -82,7 +82,7 @@ public function __construct( GroupManagementInterface $groupManagement, CustomerGroupRetrieverInterface $customerGroupRetriever = null ) { - $quantity = floatval($quantity) ? $quantity : 1; + $quantity = (float)$quantity ? $quantity : 1; parent::__construct($saleableItem, $quantity, $calculator, $priceCurrency); $this->customerSession = $customerSession; $this->groupManagement = $groupManagement; diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Layer/Filter/DataProvider/PriceTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Layer/Filter/DataProvider/PriceTest.php index f2c77627e38d0..8ca23df31cdee 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Layer/Filter/DataProvider/PriceTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Layer/Filter/DataProvider/PriceTest.php @@ -152,7 +152,7 @@ public function testGetMaxPrice() $this->productCollection->expects($this->once()) ->method('getMaxPrice') ->will($this->returnValue($maxPrice)); - $this->assertSame(floatval($maxPrice), $this->target->getMaxPrice()); + $this->assertSame((float)$maxPrice, $this->target->getMaxPrice()); } /** diff --git a/app/code/Magento/CatalogInventory/Model/Stock/Item.php b/app/code/Magento/CatalogInventory/Model/Stock/Item.php index efba8cd97d040..53b622b50b71d 100644 --- a/app/code/Magento/CatalogInventory/Model/Stock/Item.php +++ b/app/code/Magento/CatalogInventory/Model/Stock/Item.php @@ -206,7 +206,7 @@ public function getStockStatusChangedAuto() */ public function getQty() { - return null === $this->_getData(static::QTY) ? null : floatval($this->_getData(static::QTY)); + return null === $this->_getData(static::QTY) ? null : (float)$this->_getData(static::QTY); } /** diff --git a/app/code/Magento/CatalogRule/Pricing/Price/CatalogRulePrice.php b/app/code/Magento/CatalogRule/Pricing/Price/CatalogRulePrice.php index a45a548264a4c..c71b51317fd59 100644 --- a/app/code/Magento/CatalogRule/Pricing/Price/CatalogRulePrice.php +++ b/app/code/Magento/CatalogRule/Pricing/Price/CatalogRulePrice.php @@ -89,7 +89,7 @@ public function getValue() { if (null === $this->value) { if ($this->product->hasData(self::PRICE_CODE)) { - $this->value = floatval($this->product->getData(self::PRICE_CODE)) ?: false; + $this->value = (float)$this->product->getData(self::PRICE_CODE) ?: false; } else { $this->value = $this->getRuleResource() ->getRulePrice( @@ -98,7 +98,7 @@ public function getValue() $this->customerSession->getCustomerGroupId(), $this->product->getId() ); - $this->value = $this->value ? floatval($this->value) : false; + $this->value = $this->value ? (float)$this->value : false; } if ($this->value) { $this->value = $this->priceCurrency->convertAndRound($this->value); diff --git a/app/code/Magento/Checkout/Helper/Data.php b/app/code/Magento/Checkout/Helper/Data.php index 23ffc3dca14ab..497137b3f7d0e 100644 --- a/app/code/Magento/Checkout/Helper/Data.php +++ b/app/code/Magento/Checkout/Helper/Data.php @@ -167,7 +167,7 @@ public function getPriceInclTax($item) } $qty = $item->getQty() ? $item->getQty() : ($item->getQtyOrdered() ? $item->getQtyOrdered() : 1); $taxAmount = $item->getTaxAmount() + $item->getDiscountTaxCompensation(); - $price = floatval($qty) ? ($item->getRowTotal() + $taxAmount) / $qty : 0; + $price = (float)$qty ? ($item->getRowTotal() + $taxAmount) / $qty : 0; return $this->priceCurrency->round($price); } @@ -194,7 +194,7 @@ public function getBasePriceInclTax($item) { $qty = $item->getQty() ? $item->getQty() : ($item->getQtyOrdered() ? $item->getQtyOrdered() : 1); $taxAmount = $item->getBaseTaxAmount() + $item->getBaseDiscountTaxCompensation(); - $price = floatval($qty) ? ($item->getBaseRowTotal() + $taxAmount) / $qty : 0; + $price = (float)$qty ? ($item->getBaseRowTotal() + $taxAmount) / $qty : 0; return $this->priceCurrency->round($price); } diff --git a/app/code/Magento/Config/Model/Config/Structure/Mapper/Sorting.php b/app/code/Magento/Config/Model/Config/Structure/Mapper/Sorting.php index f6f3a0be187a3..2733847bab1d0 100644 --- a/app/code/Magento/Config/Model/Config/Structure/Mapper/Sorting.php +++ b/app/code/Magento/Config/Model/Config/Structure/Mapper/Sorting.php @@ -55,11 +55,11 @@ protected function _cmp($elementA, $elementB) { $sortIndexA = 0; if ($this->_hasValue('sortOrder', $elementA)) { - $sortIndexA = floatval($elementA['sortOrder']); + $sortIndexA = (float)$elementA['sortOrder']; } $sortIndexB = 0; if ($this->_hasValue('sortOrder', $elementB)) { - $sortIndexB = floatval($elementB['sortOrder']); + $sortIndexB = (float)$elementB['sortOrder']; } if ($sortIndexA == $sortIndexB) { diff --git a/app/code/Magento/Directory/Model/Currency.php b/app/code/Magento/Directory/Model/Currency.php index f39c33408f90b..6a2ebb4531502 100644 --- a/app/code/Magento/Directory/Model/Currency.php +++ b/app/code/Magento/Directory/Model/Currency.php @@ -225,7 +225,7 @@ public function convert($price, $toCurrency = null) if ($toCurrency === null) { return $price; } elseif ($rate = $this->getRate($toCurrency)) { - return floatval($price) * floatval($rate); + return (float)$price * (float)$rate; } throw new \Exception(__( diff --git a/app/code/Magento/GroupedProduct/Pricing/Price/ConfiguredRegularPrice.php b/app/code/Magento/GroupedProduct/Pricing/Price/ConfiguredRegularPrice.php index a3d269e481552..08d2c1d193779 100644 --- a/app/code/Magento/GroupedProduct/Pricing/Price/ConfiguredRegularPrice.php +++ b/app/code/Magento/GroupedProduct/Pricing/Price/ConfiguredRegularPrice.php @@ -80,7 +80,7 @@ public function getValue() if ($this->value === null) { $price = $this->product->getPrice(); $priceInCurrentCurrency = $this->priceCurrency->convertAndRound($price); - $this->value = $priceInCurrentCurrency ? floatval($priceInCurrentCurrency) : false; + $this->value = $priceInCurrentCurrency ? (float)$priceInCurrentCurrency : false; } return $this->value; } diff --git a/app/code/Magento/Reports/Block/Adminhtml/Grid/Column/Renderer/Currency.php b/app/code/Magento/Reports/Block/Adminhtml/Grid/Column/Renderer/Currency.php index 260d7bb50679d..f22b3e7bb963b 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Grid/Column/Renderer/Currency.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Grid/Column/Renderer/Currency.php @@ -30,7 +30,7 @@ public function render(\Magento\Framework\DataObject $row) return $data; } - $data = floatval($data) * $this->_getRate($row); + $data = (float)$data * $this->_getRate($row); $data = sprintf("%f", $data); $data = $this->_localeCurrency->getCurrency($currencyCode)->toCurrency($data); return $data; diff --git a/app/code/Magento/Sales/Model/Order/StateResolver.php b/app/code/Magento/Sales/Model/Order/StateResolver.php index 6f84c9b48b6d5..f5575e0388af3 100644 --- a/app/code/Magento/Sales/Model/Order/StateResolver.php +++ b/app/code/Magento/Sales/Model/Order/StateResolver.php @@ -39,7 +39,7 @@ private function isOrderClosed(OrderInterface $order, $arguments) { /** @var $order Order|OrderInterface */ $forceCreditmemo = in_array(self::FORCED_CREDITMEMO, $arguments); - if (floatval($order->getTotalRefunded()) || !$order->getTotalRefunded() && $forceCreditmemo) { + if ((float)$order->getTotalRefunded() || !$order->getTotalRefunded() && $forceCreditmemo) { return true; } return false; diff --git a/app/code/Magento/Sales/Model/ResourceModel/Order/Handler/State.php b/app/code/Magento/Sales/Model/ResourceModel/Order/Handler/State.php index c7bac874fa330..3b127abbda732 100644 --- a/app/code/Magento/Sales/Model/ResourceModel/Order/Handler/State.php +++ b/app/code/Magento/Sales/Model/ResourceModel/Order/Handler/State.php @@ -29,7 +29,7 @@ public function check(Order $order) $order->setState(Order::STATE_COMPLETE) ->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_COMPLETE)); } - } elseif (floatval($order->getTotalRefunded()) + } elseif ((float)$order->getTotalRefunded() || !$order->getTotalRefunded() && $order->hasForcedCanCreditmemo() ) { if ($order->getState() !== Order::STATE_CLOSED) { diff --git a/app/code/Magento/Tax/Helper/Data.php b/app/code/Magento/Tax/Helper/Data.php index 9d31368106924..09f9451a53464 100644 --- a/app/code/Magento/Tax/Helper/Data.php +++ b/app/code/Magento/Tax/Helper/Data.php @@ -733,7 +733,7 @@ protected function calculateTaxForItems(EntityInterface $order, EntityInterface $orderItemId = $orderItem->getId(); $orderItemTax = $orderItem->getTaxAmount(); $itemTax = $item->getTaxAmount(); - if (!$itemTax || !floatval($orderItemTax)) { + if (!$itemTax || !(float)$orderItemTax) { continue; } //An invoiced item or credit memo item can have a different qty than its order item qty @@ -761,7 +761,7 @@ protected function calculateTaxForItems(EntityInterface $order, EntityInterface $shippingTaxAmount = $salesItem->getShippingTaxAmount(); $originalShippingTaxAmount = $order->getShippingTaxAmount(); if ($shippingTaxAmount && $originalShippingTaxAmount && - $shippingTaxAmount != 0 && floatval($originalShippingTaxAmount) + $shippingTaxAmount != 0 && (float)$originalShippingTaxAmount ) { //An invoice or credit memo can have a different qty than its order $shippingRatio = $shippingTaxAmount / $originalShippingTaxAmount; diff --git a/app/code/Magento/Ups/Model/Carrier.php b/app/code/Magento/Ups/Model/Carrier.php index 3fed1210e0c8e..018302bd51fc2 100644 --- a/app/code/Magento/Ups/Model/Carrier.php +++ b/app/code/Magento/Ups/Model/Carrier.php @@ -884,7 +884,7 @@ protected function _parseXmlResponse($xmlResponse) if ($successConversion) { $costArr[$code] = $cost; - $priceArr[$code] = $this->getMethodPrice(floatval($cost), $code); + $priceArr[$code] = $this->getMethodPrice((float)$cost, $code); } } } From abb1fc4d7ffc29bd38db1e874a1d2d759ec81527 Mon Sep 17 00:00:00 2001 From: Marcel Hauri Date: Mon, 16 Jul 2018 08:37:54 +0200 Subject: [PATCH 042/211] [task] remove strval() --- .../Authorizenet/Model/Directpost/Request.php | 52 +++++++++---------- .../Backend/Currency/AbstractCurrency.php | 4 +- .../Paypal/Controller/Payflow/ReturnUrl.php | 2 +- app/code/Magento/Quote/Model/Quote.php | 2 +- .../Framework/DB/Select/OrderRenderer.php | 4 +- .../Data/Form/Element/Checkboxes.php | 12 ++--- .../Magento/Framework/Filter/Translit.php | 2 +- .../Framework/Phrase/Renderer/Placeholder.php | 2 +- 8 files changed, 40 insertions(+), 40 deletions(-) diff --git a/app/code/Magento/Authorizenet/Model/Directpost/Request.php b/app/code/Magento/Authorizenet/Model/Directpost/Request.php index d9a403e5c991e..fc78d836b6080 100644 --- a/app/code/Magento/Authorizenet/Model/Directpost/Request.php +++ b/app/code/Magento/Authorizenet/Model/Directpost/Request.php @@ -112,50 +112,50 @@ public function setDataFromOrder( sprintf('%.2F', $order->getBaseShippingAmount()) ); - //need to use strval() because NULL values IE6-8 decodes as "null" in JSON in JavaScript, + //need to use (string) because NULL values IE6-8 decodes as "null" in JSON in JavaScript, //but we need "" for null values. $billing = $order->getBillingAddress(); if (!empty($billing)) { - $this->setXFirstName(strval($billing->getFirstname())) - ->setXLastName(strval($billing->getLastname())) - ->setXCompany(strval($billing->getCompany())) - ->setXAddress(strval($billing->getStreetLine(1))) - ->setXCity(strval($billing->getCity())) - ->setXState(strval($billing->getRegion())) - ->setXZip(strval($billing->getPostcode())) - ->setXCountry(strval($billing->getCountryId())) - ->setXPhone(strval($billing->getTelephone())) - ->setXFax(strval($billing->getFax())) - ->setXCustId(strval($billing->getCustomerId())) - ->setXCustomerIp(strval($order->getRemoteIp())) - ->setXCustomerTaxId(strval($billing->getTaxId())) - ->setXEmail(strval($order->getCustomerEmail())) - ->setXEmailCustomer(strval($paymentMethod->getConfigData('email_customer'))) - ->setXMerchantEmail(strval($paymentMethod->getConfigData('merchant_email'))); + $this->setXFirstName((string)$billing->getFirstname()) + ->setXLastName((string)$billing->getLastname()) + ->setXCompany((string)$billing->getCompany()) + ->setXAddress((string)$billing->getStreetLine(1)) + ->setXCity((string)$billing->getCity()) + ->setXState((string)$billing->getRegion()) + ->setXZip((string)$billing->getPostcode()) + ->setXCountry((string)$billing->getCountryId()) + ->setXPhone((string)$billing->getTelephone()) + ->setXFax((string)$billing->getFax()) + ->setXCustId((string)$billing->getCustomerId()) + ->setXCustomerIp((string)$order->getRemoteIp()) + ->setXCustomerTaxId((string)$billing->getTaxId()) + ->setXEmail((string)$order->getCustomerEmail()) + ->setXEmailCustomer((string)$paymentMethod->getConfigData('email_customer')) + ->setXMerchantEmail((string)$paymentMethod->getConfigData('merchant_email')); } $shipping = $order->getShippingAddress(); if (!empty($shipping)) { $this->setXShipToFirstName( - strval($shipping->getFirstname()) + (string)$shipping->getFirstname() )->setXShipToLastName( - strval($shipping->getLastname()) + (string)$shipping->getLastname() )->setXShipToCompany( - strval($shipping->getCompany()) + (string)$shipping->getCompany() )->setXShipToAddress( - strval($shipping->getStreetLine(1)) + (string)$shipping->getStreetLine(1) )->setXShipToCity( - strval($shipping->getCity()) + (string)$shipping->getCity() )->setXShipToState( - strval($shipping->getRegion()) + (string)$shipping->getRegion() )->setXShipToZip( - strval($shipping->getPostcode()) + (string)$shipping->getPostcode() )->setXShipToCountry( - strval($shipping->getCountryId()) + (string)$shipping->getCountryId() ); } - $this->setXPoNum(strval($payment->getPoNumber())); + $this->setXPoNum((string)$payment->getPoNumber()); return $this; } diff --git a/app/code/Magento/Config/Model/Config/Backend/Currency/AbstractCurrency.php b/app/code/Magento/Config/Model/Config/Backend/Currency/AbstractCurrency.php index b86b86ad3bb8c..4ae66bfd9692b 100644 --- a/app/code/Magento/Config/Model/Config/Backend/Currency/AbstractCurrency.php +++ b/app/code/Magento/Config/Model/Config/Backend/Currency/AbstractCurrency.php @@ -71,7 +71,7 @@ protected function _getCurrencyBase() $this->getScopeId() ); } - return strval($value); + return (string)$value; } /** @@ -88,7 +88,7 @@ protected function _getCurrencyDefault() $this->getScopeId() ); } - return strval($value); + return (string)$value; } /** diff --git a/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php b/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php index a370eeb40eafd..73b4c9f6ee6ea 100644 --- a/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php +++ b/app/code/Magento/Paypal/Controller/Payflow/ReturnUrl.php @@ -50,7 +50,7 @@ public function execute() $redirectBlock->setData('goto_success_page', true); } else { if ($this->checkPaymentMethod($order)) { - $gotoSection = $this->_cancelPayment(strval($this->getRequest()->getParam('RESPMSG'))); + $gotoSection = $this->_cancelPayment((string)$this->getRequest()->getParam('RESPMSG')); $redirectBlock->setData('goto_section', $gotoSection); $redirectBlock->setData('error_msg', __('Your payment has been declined. Please try again.')); } else { diff --git a/app/code/Magento/Quote/Model/Quote.php b/app/code/Magento/Quote/Model/Quote.php index dcadcde292500..b902996317a8e 100644 --- a/app/code/Magento/Quote/Model/Quote.php +++ b/app/code/Magento/Quote/Model/Quote.php @@ -1609,7 +1609,7 @@ public function addProduct( * Error message */ if (is_string($cartCandidates) || $cartCandidates instanceof \Magento\Framework\Phrase) { - return strval($cartCandidates); + return (string)$cartCandidates; } /** diff --git a/lib/internal/Magento/Framework/DB/Select/OrderRenderer.php b/lib/internal/Magento/Framework/DB/Select/OrderRenderer.php index 36a075b9af8c9..dfe9c8949c353 100644 --- a/lib/internal/Magento/Framework/DB/Select/OrderRenderer.php +++ b/lib/internal/Magento/Framework/DB/Select/OrderRenderer.php @@ -40,12 +40,12 @@ public function render(Select $select, $sql = '') $order = []; foreach ($select->getPart(Select::ORDER) as $term) { if (is_array($term)) { - if (is_numeric($term[0]) && strval(intval($term[0])) == $term[0]) { + if (is_numeric($term[0]) && (string)(int)$term[0] == $term[0]) { $order[] = (int)trim($term[0]) . ' ' . $term[1]; } else { $order[] = $this->quote->quoteIdentifier($term[0]) . ' ' . $term[1]; } - } elseif (is_numeric($term) && strval(intval($term)) == $term) { + } elseif (is_numeric($term) && (string)(int)$term == $term) { $order[] = (int)trim($term); } else { $order[] = $this->quote->quoteIdentifier($term); diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Checkboxes.php b/lib/internal/Magento/Framework/Data/Form/Element/Checkboxes.php index 76bc4fce5f95c..52d23fd166f77 100644 --- a/lib/internal/Magento/Framework/Data/Form/Element/Checkboxes.php +++ b/lib/internal/Magento/Framework/Data/Form/Element/Checkboxes.php @@ -121,13 +121,13 @@ public function getChecked($value) return; } if (!is_array($checked)) { - $checked = [strval($checked)]; + $checked = [(string)$checked]; } else { foreach ($checked as $k => $v) { - $checked[$k] = strval($v); + $checked[$k] = (string)$v; } } - if (in_array(strval($value), $checked)) { + if (in_array((string)$value, $checked)) { return 'checked'; } return; @@ -141,13 +141,13 @@ public function getDisabled($value) { if ($disabled = $this->getData('disabled')) { if (!is_array($disabled)) { - $disabled = [strval($disabled)]; + $disabled = [(string)$disabled]; } else { foreach ($disabled as $k => $v) { - $disabled[$k] = strval($v); + $disabled[$k] = (string)$v; } } - if (in_array(strval($value), $disabled)) { + if (in_array((string)$value, $disabled)) { return 'disabled'; } } diff --git a/lib/internal/Magento/Framework/Filter/Translit.php b/lib/internal/Magento/Framework/Filter/Translit.php index 7a84a6e33af18..a6162aa7a7fff 100644 --- a/lib/internal/Magento/Framework/Filter/Translit.php +++ b/lib/internal/Magento/Framework/Filter/Translit.php @@ -409,7 +409,7 @@ public function __construct(\Magento\Framework\App\Config\ScopeConfigInterface $ $convertConfig = $config->getValue('url/convert', 'default'); if ($convertConfig) { foreach ($convertConfig as $configValue) { - $this->convertTable[strval($configValue['from'])] = strval($configValue['to']); + $this->convertTable[(string)$configValue['from']] = (string)$configValue['to']; } } } diff --git a/lib/internal/Magento/Framework/Phrase/Renderer/Placeholder.php b/lib/internal/Magento/Framework/Phrase/Renderer/Placeholder.php index 4ba8c747fa12c..fd1b0c18ead17 100644 --- a/lib/internal/Magento/Framework/Phrase/Renderer/Placeholder.php +++ b/lib/internal/Magento/Framework/Phrase/Renderer/Placeholder.php @@ -40,6 +40,6 @@ public function render(array $source, array $arguments) */ private function keyToPlaceholder($key) { - return '%' . (is_int($key) ? strval($key + 1) : $key); + return '%' . (is_int($key) ? (string)($key + 1) : $key); } } From 085f89c0c894c79fdad430c3cf5b4d86e5c9ab8e Mon Sep 17 00:00:00 2001 From: Marcel Hauri Date: Mon, 16 Jul 2018 08:42:03 +0200 Subject: [PATCH 043/211] [task] add strval to obsolete methods --- .../testsuite/Magento/Test/Legacy/_files/obsolete_methods.php | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php index 105d44d6016c5..9c5108ef7dd9e 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php @@ -2523,5 +2523,6 @@ ['_isAttributeValueEmpty', 'Magento\Catalog\Model\ResourceModel\AbstractResource'], ['var_dump', ''], ['each', ''], + ['strval', ''], ['isOrderIncrementIdUsed', 'Magento\Quote\Model\ResourceModel\Quote', 'Magento\Sales\Model\OrderIncrementIdChecker::isIncrementIdUsed'] ]; From db165d1c27d2fbfaa772cb3434e4b102792963cd Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi Date: Mon, 16 Jul 2018 16:06:17 +0300 Subject: [PATCH 044/211] MAGETWO-93003: Admin user with permissions for 1 website can view the All Store Views scope on a product - Not allowed for current admin role websites are removed from create product form --- .../DataProvider/Product/Form/Modifier/WebsitesTest.php | 9 ++++----- .../Ui/DataProvider/Product/Form/Modifier/Websites.php | 7 +++++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/WebsitesTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/WebsitesTest.php index 997b66861c21b..df1bdffd7d512 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/WebsitesTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/WebsitesTest.php @@ -87,14 +87,11 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); $this->websiteRepositoryMock = $this->getMockBuilder(\Magento\Store\Api\WebsiteRepositoryInterface::class) - ->setMethods(['getList', 'getDefault']) + ->setMethods(['getDefault']) ->getMockForAbstractClass(); $this->websiteRepositoryMock->expects($this->any()) ->method('getDefault') ->willReturn($this->websiteMock); - $this->websiteRepositoryMock->expects($this->any()) - ->method('getList') - ->willReturn([$this->websiteMock, $this->secondWebsiteMock]); $this->groupRepositoryMock = $this->getMockBuilder(\Magento\Store\Api\GroupRepositoryInterface::class) ->setMethods(['getList']) ->getMockForAbstractClass(); @@ -114,8 +111,10 @@ protected function setUp() ->method('getWebsiteIds') ->willReturn($this->assignedWebsites); $this->storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) - ->setMethods(['isSingleStoreMode']) + ->setMethods(['isSingleStoreMode', 'getWebsites']) ->getMockForAbstractClass(); + $this->storeManagerMock->method('getWebsites') + ->willReturn([$this->websiteMock, $this->secondWebsiteMock]); $this->storeManagerMock->expects($this->any()) ->method('isSingleStoreMode') ->willReturn(false); diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Websites.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Websites.php index 298da3d5cd6f2..e11741116aacf 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Websites.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Websites.php @@ -176,9 +176,11 @@ protected function getFieldsForFieldset() $label = __('Websites'); $defaultWebsiteId = $this->websiteRepository->getDefault()->getId(); + $isOnlyOneWebsiteAvailable = count($websitesList) === 1; foreach ($websitesList as $website) { $isChecked = in_array($website['id'], $websiteIds) - || ($defaultWebsiteId == $website['id'] && $isNewProduct); + || ($defaultWebsiteId == $website['id'] && $isNewProduct) + || $isOnlyOneWebsiteAvailable; $children[$website['id']] = [ 'arguments' => [ 'data' => [ @@ -397,8 +399,9 @@ protected function getWebsitesList() $this->websitesList = []; $groupList = $this->groupRepository->getList(); $storesList = $this->storeRepository->getList(); + $websiteList = $this->storeManager->getWebsites(true); - foreach ($this->websiteRepository->getList() as $website) { + foreach ($websiteList as $website) { $websiteId = $website->getId(); if (!$websiteId) { continue; From 54bc4e3e929f10834f0fd37889051015dc147761 Mon Sep 17 00:00:00 2001 From: Anshu Mishra Date: Tue, 17 Jul 2018 17:31:13 +0530 Subject: [PATCH 045/211] fixes as CI issues --- .../Controller/Adminhtml/Agreement/Edit.php | 5 ++--- .../Controller/Adminhtml/Agreement/Save.php | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php index 768ac6ee273f0..8bec3b581cd54 100644 --- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php +++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Edit.php @@ -11,8 +11,7 @@ use Magento\Backend\App\Action\Context; use Magento\Framework\Registry; use Magento\Framework\App\ObjectManager; -use Magento\CheckoutAgreements\Controller\Adminhtml\Agreement; -use Magento\CheckoutAgreements\Block\Adminhtml\Agreement\Edit; +use Magento\CheckoutAgreements\Block\Adminhtml\Agreement\Edit as BlockEdit; class Edit extends Agreement { @@ -65,7 +64,7 @@ public function execute() $id ? __('Edit Condition') : __('New Condition') )->_addContent( $this->_view->getLayout()->createBlock( - Edit::class + BlockEdit::class )->setData( 'action', $this->getUrl('checkout/*/save') diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php index c0eef133a2a9a..05a16d3dd4264 100644 --- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php +++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Save.php @@ -11,7 +11,6 @@ use Magento\Backend\App\Action\Context; use Magento\Framework\Registry; use Magento\Framework\App\ObjectManager; -use Magento\CheckoutAgreements\Controller\Adminhtml\Agreement; use Magento\Framework\DataObject; use Magento\Framework\Exception\LocalizedException; From 846f60542af6606965485ca5a3b37ebd286ff218 Mon Sep 17 00:00:00 2001 From: Vasilii Burlacu Date: Wed, 18 Jul 2018 09:15:18 +0300 Subject: [PATCH 046/211] Change checking of StoreInterface instead of Store in RouteParamsResolver.php --- app/code/Magento/Store/Url/Plugin/RouteParamsResolver.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Store/Url/Plugin/RouteParamsResolver.php b/app/code/Magento/Store/Url/Plugin/RouteParamsResolver.php index 325e621c8a113..468352af78cbc 100644 --- a/app/code/Magento/Store/Url/Plugin/RouteParamsResolver.php +++ b/app/code/Magento/Store/Url/Plugin/RouteParamsResolver.php @@ -6,6 +6,7 @@ namespace Magento\Store\Url\Plugin; use \Magento\Store\Model\Store; +use \Magento\Store\Api\Data\StoreInterface; use \Magento\Store\Model\ScopeInterface as StoreScopeInterface; /** @@ -65,9 +66,9 @@ public function beforeSetRouteParams( unset($data['_scope']); } if (isset($data['_scope_to_url']) && (bool)$data['_scope_to_url'] === true) { - /** @var Store $currentScope */ + /** @var StoreInterface $currentScope */ $currentScope = $subject->getScope(); - $storeCode = $currentScope && $currentScope instanceof Store ? + $storeCode = $currentScope && $currentScope instanceof StoreInterface ? $currentScope->getCode() : $this->storeManager->getStore()->getCode(); From e18663f3604196971b570a5b78088483892bffd8 Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko Date: Tue, 17 Jul 2018 11:00:44 +0300 Subject: [PATCH 047/211] MAGETWO-92786: Wrong email width on iPhone --- .../frontend/Magento/blank/web/css/source/_email-base.less | 2 +- .../static/testsuite/Magento/Test/Less/_files/blacklist/old.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/blank/web/css/source/_email-base.less b/app/design/frontend/Magento/blank/web/css/source/_email-base.less index 29f6fd0dcbcfc..f9b779d761576 100644 --- a/app/design/frontend/Magento/blank/web/css/source/_email-base.less +++ b/app/design/frontend/Magento/blank/web/css/source/_email-base.less @@ -160,8 +160,8 @@ body { .main { margin: 0 auto; + max-width: @email-body__width; text-align: left; // Necessary to prevent all text from centering in Outlook 2003 - width: @email-body__width; } .header { diff --git a/dev/tests/static/testsuite/Magento/Test/Less/_files/blacklist/old.txt b/dev/tests/static/testsuite/Magento/Test/Less/_files/blacklist/old.txt index db5c1cab06655..70764344de69b 100644 --- a/dev/tests/static/testsuite/Magento/Test/Less/_files/blacklist/old.txt +++ b/dev/tests/static/testsuite/Magento/Test/Less/_files/blacklist/old.txt @@ -23,3 +23,4 @@ app/design/adminhtml/Magento/backend/web/mui/clearless/_sprites.less app/design/adminhtml/Magento/backend/web/mui/styles/_abstract.less app/design/adminhtml/Magento/backend/web/mui/styles/_table.less app/design/adminhtml/Magento/backend/web/mui/styles/_vars.less +app/design/frontend/Magento/blank/web/css/source/_email-base.less From c3ebf6fede3f001cafef9c3808ddb659eb567d35 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi Date: Tue, 17 Jul 2018 20:07:34 +0300 Subject: [PATCH 048/211] MAGETWO-93209: Wildcard values for coupon codes --- .../Model/ResourceModel/Rule/Collection.php | 7 ++- .../Rule/Action/Discount/CartFixedTest.php | 30 ++++++++++++ .../_files/coupon_code_with_wildcard.php | 45 ++++++++++++++++++ .../coupon_code_with_wildcard_rollback.php | 46 +++++++++++++++++++ 4 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/SalesRule/_files/coupon_code_with_wildcard.php create mode 100644 dev/tests/integration/testsuite/Magento/SalesRule/_files/coupon_code_with_wildcard_rollback.php diff --git a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php index 54b50dbdf38db..59f24fa8b6e03 100644 --- a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php +++ b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php @@ -6,6 +6,7 @@ namespace Magento\SalesRule\Model\ResourceModel\Rule; +use Magento\Framework\DB\Select; use Magento\Framework\Serialize\Serializer\Json; use Magento\Quote\Model\Quote\Address; @@ -209,7 +210,9 @@ public function setValidationFilter( $andWhereCondition = implode(' AND ', $andWhereConditions); $select->where( - $noCouponWhereCondition . ' OR ((' . $orWhereCondition . ') AND ' . $andWhereCondition . ')' + $noCouponWhereCondition . ' OR ((' . $orWhereCondition . ') AND ' . $andWhereCondition . ')', + null, + Select::TYPE_CONDITION ); } else { $this->addFieldToFilter( @@ -320,7 +323,7 @@ public function addAttributeInConditionFilter($attributeCode) $this->getSelect()->where( sprintf('(%s OR %s)', $cCond, $aCond), null, - \Magento\Framework\DB\Select::TYPE_CONDITION + Select::TYPE_CONDITION ); return $this; diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Action/Discount/CartFixedTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Action/Discount/CartFixedTest.php index 6c27c334d6ef4..6d75cd8b07e06 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Action/Discount/CartFixedTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Action/Discount/CartFixedTest.php @@ -90,6 +90,36 @@ public function applyFixedDiscountDataProvider() ]; } + /** + * Tests that coupon with wildcard symbols in code can be successfully applied. + * + * @magentoDataFixture Magento/SalesRule/_files/coupon_code_with_wildcard.php + */ + public function testCouponCodeWithWildcard() + { + $expectedDiscount = '-5.00'; + $couponCode = '2?ds5!2d'; + $cartId = $this->cartManagement->createEmptyCart(); + $productPrice = 10; + + $product = $this->createProduct($productPrice); + + /** @var CartItemInterface $quoteItem */ + $quoteItem = Bootstrap::getObjectManager()->create(CartItemInterface::class); + $quoteItem->setQuoteId($cartId); + $quoteItem->setProduct($product); + $quoteItem->setQty(1); + $this->cartItemRepository->save($quoteItem); + + $this->couponManagement->set($cartId, $couponCode); + + /** @var GuestCartTotalRepositoryInterface $cartTotalRepository */ + $cartTotalRepository = Bootstrap::getObjectManager()->get(GuestCartTotalRepositoryInterface::class); + $total = $cartTotalRepository->get($cartId); + + $this->assertEquals($expectedDiscount, $total->getBaseDiscountAmount()); + } + /** * Returns simple product with given price. * diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/coupon_code_with_wildcard.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/coupon_code_with_wildcard.php new file mode 100644 index 0000000000000..9005284f984cf --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/coupon_code_with_wildcard.php @@ -0,0 +1,45 @@ +create(Rule::class); +$salesRule->setData( + [ + 'name' => '5$ fixed discount on whole cart', + 'is_active' => 1, + 'customer_group_ids' => [GroupManagement::NOT_LOGGED_IN_ID], + 'coupon_type' => Rule::COUPON_TYPE_SPECIFIC, + 'conditions' => [], + 'simple_action' => Rule::CART_FIXED_ACTION, + 'discount_amount' => 5, + 'discount_step' => 0, + 'stop_rules_processing' => 1, + 'website_ids' => [ + $objectManager->get(StoreManagerInterface::class)->getWebsite()->getId(), + ], + ] +); +$objectManager->get(\Magento\SalesRule\Model\ResourceModel\Rule::class)->save($salesRule); + +// Create coupon and assign "15$ fixed discount" rule to this coupon. +$coupon = $objectManager->create(Coupon::class); +$coupon->setRuleId($salesRule->getId()) + ->setCode('2?ds5!2d') + ->setType(0); + +/** @var CouponRepositoryInterface $couponRepository */ +$couponRepository = $objectManager->get(CouponRepositoryInterface::class); +$couponRepository->save($coupon); diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/coupon_code_with_wildcard_rollback.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/coupon_code_with_wildcard_rollback.php new file mode 100644 index 0000000000000..776c302210351 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/coupon_code_with_wildcard_rollback.php @@ -0,0 +1,46 @@ +get(RuleRepositoryInterface::class); + $ruleRepository->deleteById($salesRule->getRuleId()); +} + +$coupon = $objectManager->create(Coupon::class); +$coupon->loadByCode('2?ds5!2d'); +if ($coupon->getCouponId()) { + /** @var CouponRepositoryInterface $couponRepository */ + $couponRepository = $objectManager->get(CouponRepositoryInterface::class); + $couponRepository->deleteById($coupon->getCouponId()); +} + +function getSalesRule(string $name) +{ + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = Bootstrap::getObjectManager()->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('name', $name) + ->create(); + + /** @var RuleRepositoryInterface $ruleRepository */ + $ruleRepository = Bootstrap::getObjectManager()->get(RuleRepositoryInterface::class); + $items = $ruleRepository->getList($searchCriteria) + ->getItems(); + + return array_pop($items); +} From 5139cb12fd12596187bcecf7ad09cf9bc7130de8 Mon Sep 17 00:00:00 2001 From: Ievgen Sentiabov Date: Wed, 18 Jul 2018 15:31:37 +0300 Subject: [PATCH 049/211] MAGETWO-93037: User can place order when product changes status to Out of stock during checkout - Added checking quote errors before submitting --- .../CatalogInventory/Model/Stock/Status.php | 4 +- .../Magento/Quote/Model/QuoteValidator.php | 103 +++++++++---- .../Quote/Model/QuoteManagementTest.php | 144 ++++++++++++------ 3 files changed, 168 insertions(+), 83 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Model/Stock/Status.php b/app/code/Magento/CatalogInventory/Model/Stock/Status.php index 899056d8f0835..8a24d3c46abcb 100644 --- a/app/code/Magento/CatalogInventory/Model/Stock/Status.php +++ b/app/code/Magento/CatalogInventory/Model/Stock/Status.php @@ -106,9 +106,9 @@ public function getQty() /** * @return int */ - public function getStockStatus() + public function getStockStatus(): int { - return $this->getData(self::KEY_STOCK_STATUS); + return (int)$this->getData(self::KEY_STOCK_STATUS); } //@codeCoverageIgnoreEnd diff --git a/app/code/Magento/Quote/Model/QuoteValidator.php b/app/code/Magento/Quote/Model/QuoteValidator.php index d3992589ef0b9..ac3820c842967 100644 --- a/app/code/Magento/Quote/Model/QuoteValidator.php +++ b/app/code/Magento/Quote/Model/QuoteValidator.php @@ -6,12 +6,13 @@ namespace Magento\Quote\Model; -use Magento\Framework\Exception\LocalizedException; -use Magento\Quote\Model\Quote as QuoteEntity; use Magento\Directory\Model\AllowedCountries; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Message\Error; +use Magento\Quote\Model\Quote as QuoteEntity; use Magento\Quote\Model\Quote\Validator\MinimumOrderAmount\ValidationMessage as OrderAmountValidationMessage; -use \Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\ScopeInterface; /** * @api @@ -63,50 +64,32 @@ public function validateQuoteAmount(QuoteEntity $quote, $amount) $quote->setHasError(true); $quote->addMessage(__('This item price or quantity is not valid for checkout.')); } + return $this; } /** - * Validate quote before submit + * Validates quote before submit. * * @param Quote $quote * @return $this - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException */ public function validateBeforeSubmit(QuoteEntity $quote) { + if ($quote->getHasError()) { + $errors = $this->getQuoteErrors($quote); + throw new LocalizedException(__($errors ?: 'Something went wrong. Please try to place the order again.')); + } + if (!$quote->isVirtual()) { - $address = $quote->getShippingAddress(); - $address->setStoreId($quote->getStoreId()); - if ($address->validate() !== true) { - throw new \Magento\Framework\Exception\LocalizedException( - __( - 'Please check the shipping address information. %1', - implode(' ', $address->validate()) - ) - ); - } - - // Checks if country id present in the allowed countries list. - if (!in_array( - $address->getCountryId(), - $this->allowedCountryReader->getAllowedCountries(ScopeInterface::SCOPE_STORE, $quote->getStoreId()) - )) { - throw new \Magento\Framework\Exception\LocalizedException( - __('Some addresses cannot be used due to country-specific configurations.') - ); - } - - $method = $address->getShippingMethod(); - $rate = $address->getShippingRateByCode($method); - if (!$method || !$rate) { - throw new \Magento\Framework\Exception\LocalizedException(__('Please specify a shipping method.')); - } + $this->validateShippingAddress($quote); } + $billingAddress = $quote->getBillingAddress(); $billingAddress->setStoreId($quote->getStoreId()); if ($billingAddress->validate() !== true) { - throw new \Magento\Framework\Exception\LocalizedException( + throw new LocalizedException( __( 'Please check the billing address information. %1', implode(' ', $quote->getBillingAddress()->validate()) @@ -114,7 +97,7 @@ public function validateBeforeSubmit(QuoteEntity $quote) ); } if (!$quote->getPayment()->getMethod()) { - throw new \Magento\Framework\Exception\LocalizedException(__('Please select a valid payment method.')); + throw new LocalizedException(__('Please select a valid payment method.')); } if (!$quote->validateMinimumAmount($quote->getIsMultiShipping())) { throw new LocalizedException($this->minimumAmountMessage->getMessage()); @@ -122,4 +105,58 @@ public function validateBeforeSubmit(QuoteEntity $quote) return $this; } + + /** + * Validates shipping address. + * + * @param Quote $quote + * @throws LocalizedException + */ + private function validateShippingAddress(QuoteEntity $quote) + { + $address = $quote->getShippingAddress(); + $address->setStoreId($quote->getStoreId()); + if ($address->validate() !== true) { + throw new LocalizedException( + __( + 'Please check the shipping address information. %1', + implode(' ', $address->validate()) + ) + ); + } + + // Checks if country id present in the allowed countries list. + if (!in_array( + $address->getCountryId(), + $this->allowedCountryReader->getAllowedCountries(ScopeInterface::SCOPE_STORE, $quote->getStoreId()) + )) { + throw new LocalizedException( + __('Some addresses cannot be used due to country-specific configurations.') + ); + } + + $method = $address->getShippingMethod(); + $rate = $address->getShippingRateByCode($method); + if (!$method || !$rate) { + throw new LocalizedException(__('Please specify a shipping method.')); + } + } + + /** + * Parses quote error messages and concatenates them into single string. + * + * @param Quote $quote + * @return string + */ + private function getQuoteErrors(QuoteEntity $quote): string + { + $errors = array_map( + function (Error $error) { + return $error->getText(); + }, + $quote->getErrors() + ); + + return implode(PHP_EOL, $errors); + } } diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php index 0b0071beb5133..b37f86ed1f892 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php @@ -3,10 +3,18 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Quote\Model; +use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Product\Type; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Quote\Api\CartManagementInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Sales\Api\OrderRepositoryInterface; use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; /** * Class for testing QuoteManagement model @@ -14,79 +22,119 @@ class QuoteManagementTest extends \PHPUnit\Framework\TestCase { /** - * Create order with product that has child items + * @var ObjectManager + */ + private $objectManager; + + /** + * @var CartManagementInterface + */ + private $cartManagement; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + + $this->cartManagement = $this->objectManager->create(CartManagementInterface::class); + } + + /** + * Creates order with product that has child items. * * @magentoAppIsolation enabled * @magentoDataFixture Magento/Sales/_files/quote_with_bundle.php */ public function testSubmit() { - /** - * Preconditions: - * Load quote with Bundle product that has at least two child products - */ - $objectManager = Bootstrap::getObjectManager(); - /** @var \Magento\Quote\Model\Quote $quote */ - $quote = $objectManager->create(\Magento\Quote\Model\Quote::class); - $quote->load('test01', 'reserved_order_id'); - - /** Execute SUT */ - /** @var \Magento\Quote\Api\CartManagementInterface $model */ - $cartManagement = $objectManager->create(\Magento\Quote\Api\CartManagementInterface::class); - /** @var \Magento\Sales\Api\OrderRepositoryInterface $orderRepository */ - $orderRepository = $objectManager->create(\Magento\Sales\Api\OrderRepositoryInterface::class); - $orderId = $cartManagement->placeOrder($quote->getId()); + $quote = $this->getQuote('test01'); + $orderId = $this->cartManagement->placeOrder($quote->getId()); + + /** @var OrderRepositoryInterface $orderRepository */ + $orderRepository = $this->objectManager->create(OrderRepositoryInterface::class); $order = $orderRepository->get($orderId); - /** Check if SUT caused expected effects */ $orderItems = $order->getItems(); - $this->assertCount(3, $orderItems); + self::assertCount(3, $orderItems); foreach ($orderItems as $orderItem) { if ($orderItem->getProductType() == Type::TYPE_SIMPLE) { - $this->assertNotEmpty($orderItem->getParentItem(), 'Parent is not set for child product'); - $this->assertNotEmpty($orderItem->getParentItemId(), 'Parent is not set for child product'); + self::assertNotEmpty($orderItem->getParentItem(), 'Parent is not set for child product'); + self::assertNotEmpty($orderItem->getParentItemId(), 'Parent is not set for child product'); } } } /** - * Create order with product that has child items and one of them was deleted + * Tries to create order with product that has child items and one of them was deleted. * * @magentoAppArea adminhtml * @magentoAppIsolation enabled * @magentoDataFixture Magento/Sales/_files/quote_with_bundle.php + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Some of the products below do not have all the required options. */ public function testSubmitWithDeletedItem() { - /** - * Preconditions: - * Load quote with Bundle product that have at least to child products - */ - $objectManager = Bootstrap::getObjectManager(); - /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ - $productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); $product = $productRepository->get('simple-2'); $productRepository->delete($product); - /** @var \Magento\Quote\Model\Quote $quote */ - $quote = $objectManager->create(\Magento\Quote\Model\Quote::class); - $quote->load('test01', 'reserved_order_id'); - - /** Execute SUT */ - /** @var \Magento\Quote\Api\CartManagementInterface $model */ - $cartManagement = $objectManager->create(\Magento\Quote\Api\CartManagementInterface::class); - /** @var \Magento\Sales\Api\OrderRepositoryInterface $orderRepository */ - $orderRepository = $objectManager->create(\Magento\Sales\Api\OrderRepositoryInterface::class); - $orderId = $cartManagement->placeOrder($quote->getId()); - $order = $orderRepository->get($orderId); + $quote = $this->getQuote('test01'); - /** Check if SUT caused expected effects */ - $orderItems = $order->getItems(); - $this->assertCount(2, $orderItems); - foreach ($orderItems as $orderItem) { - if ($orderItem->getProductType() == Type::TYPE_SIMPLE) { - $this->assertNotEmpty($orderItem->getParentItem(), 'Parent is not set for child product'); - $this->assertNotEmpty($orderItem->getParentItemId(), 'Parent is not set for child product'); - } - } + $this->cartManagement->placeOrder($quote->getId()); + } + + /** + * Tries to create order with item of stock during checkout. + * + * @magentoDataFixture Magento/Sales/_files/quote.php + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Some of the products are out of stock. + */ + public function testSubmitWithItemOutOfStock() + { + $this->makeProductOutOfStock('simple'); + $quote = $this->getQuote('test01'); + $this->cartManagement->placeOrder($quote->getId()); + } + + /** + * Gets quote by reserved order ID. + * + * @param string $reservedOrderId + * @return Quote + */ + private function getQuote(string $reservedOrderId): Quote + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId) + ->create(); + + /** @var CartRepositoryInterface $quoteRepository */ + $quoteRepository = $this->objectManager->get(CartRepositoryInterface::class); + $items = $quoteRepository->getList($searchCriteria) + ->getItems(); + + return array_pop($items); + } + + /** + * Makes provided product as out of stock. + * + * @param string $sku + * @return void + */ + private function makeProductOutOfStock(string $sku) + { + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $product = $productRepository->get($sku); + $extensionAttributes = $product->getExtensionAttributes(); + $stockItem = $extensionAttributes->getStockItem(); + $stockItem->setIsInStock(false); + $productRepository->save($product); } } From b243df1c5c8d9a365ee05b11cc92b72f690c346d Mon Sep 17 00:00:00 2001 From: Ievgen Sentiabov Date: Fri, 13 Jul 2018 10:01:37 +0300 Subject: [PATCH 050/211] MAGETWO-92831: Currency conversion rate services do not work in admin panel - Added API Key usage for Fixer.io - Added response validation --- .../Model/Currency/Import/FixerIo.php | 89 ++++++++++++--- .../Model/Currency/Import/FixerIoTest.php | 105 +++++++++++------- .../Directory/etc/adminhtml/system.xml | 7 +- app/code/Magento/Directory/etc/config.xml | 1 + app/code/Magento/Directory/i18n/en_US.csv | 5 + 5 files changed, 147 insertions(+), 60 deletions(-) diff --git a/app/code/Magento/Directory/Model/Currency/Import/FixerIo.php b/app/code/Magento/Directory/Model/Currency/Import/FixerIo.php index 3e0bd5368509e..f1c63ede74151 100644 --- a/app/code/Magento/Directory/Model/Currency/Import/FixerIo.php +++ b/app/code/Magento/Directory/Model/Currency/Import/FixerIo.php @@ -5,15 +5,18 @@ */ namespace Magento\Directory\Model\Currency\Import; +use Magento\Store\Model\ScopeInterface; + /** * Currency rate import model (From http://fixer.io/) */ -class FixerIo extends \Magento\Directory\Model\Currency\Import\AbstractImport +class FixerIo extends AbstractImport { /** * @var string */ - const CURRENCY_CONVERTER_URL = 'http://api.fixer.io/latest?base={{CURRENCY_FROM}}&symbols={{CURRENCY_TO}}'; + const CURRENCY_CONVERTER_URL = 'http://data.fixer.io/api/latest?access_key={{ACCESS_KEY}}' + . '&base={{CURRENCY_FROM}}&symbols={{CURRENCY_TO}}'; /** * Http Client Factory @@ -65,6 +68,13 @@ public function fetchRates() return $data; } + /** + * {@inheritdoc} + */ + protected function _convert($currencyFrom, $currencyTo) + { + } + /** * Return currencies convert rates in batch mode * @@ -75,9 +85,19 @@ public function fetchRates() */ private function convertBatch($data, $currencyFrom, $currenciesTo) { + $accessKey = $this->scopeConfig->getValue('currency/fixerio/api_key', ScopeInterface::SCOPE_STORE); + if (empty($accessKey)) { + $this->_messages[] = __('No API Key was specified or an invalid API Key was specified.'); + $data[$currencyFrom] = $this->makeEmptyResponse($currenciesTo); + return $data; + } + $currenciesStr = implode(',', $currenciesTo); - $url = str_replace('{{CURRENCY_FROM}}', $currencyFrom, self::CURRENCY_CONVERTER_URL); - $url = str_replace('{{CURRENCY_TO}}', $currenciesStr, $url); + $url = str_replace( + ['{{ACCESS_KEY}}', '{{CURRENCY_FROM}}', '{{CURRENCY_TO}}'], + [$accessKey, $currencyFrom, $currenciesStr], + self::CURRENCY_CONVERTER_URL + ); set_time_limit(0); try { @@ -86,6 +106,11 @@ private function convertBatch($data, $currencyFrom, $currenciesTo) ini_restore('max_execution_time'); } + if (!$this->validateResponse($response, $currencyFrom)) { + $data[$currencyFrom] = $this->makeEmptyResponse($currenciesTo); + return $data; + } + foreach ($currenciesTo as $currencyTo) { if ($currencyFrom == $currencyTo) { $data[$currencyFrom][$currencyTo] = $this->_numberFormat(1); @@ -117,18 +142,17 @@ private function getServiceResponse($url, $retry = 0) $response = []; try { - $jsonResponse = $httpClient->setUri( - $url - )->setConfig( - [ - 'timeout' => $this->scopeConfig->getValue( - 'currency/fixerio/timeout', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ), - ] - )->request( - 'GET' - )->getBody(); + $jsonResponse = $httpClient->setUri($url) + ->setConfig( + [ + 'timeout' => $this->scopeConfig->getValue( + 'currency/fixerio/timeout', + ScopeInterface::SCOPE_STORE + ), + ] + ) + ->request('GET') + ->getBody(); $response = json_decode($jsonResponse, true); } catch (\Exception $e) { @@ -140,9 +164,38 @@ private function getServiceResponse($url, $retry = 0) } /** - * {@inheritdoc} + * Validates rates response. + * + * @param array $response + * @param string $baseCurrency + * @return bool */ - protected function _convert($currencyFrom, $currencyTo) + private function validateResponse(array $response, string $baseCurrency): bool + { + if ($response['success']) { + return true; + } + + $errorCodes = [ + 101 => __('No API Key was specified or an invalid API Key was specified.'), + 102 => __('The account this API request is coming from is inactive.'), + 105 => __('The "%1" is not allowed as base currency for your subscription plan.', $baseCurrency), + 201 => __('An invalid base currency has been entered.'), + ]; + + $this->_messages[] = $errorCodes[$response['error']['code']] ?? __('Currency rates can\'t be retrieved.'); + + return false; + } + + /** + * Creates array for provided currencies with empty rates. + * + * @param array $currenciesTo + * @return array + */ + private function makeEmptyResponse(array $currenciesTo): array { + return array_fill_keys($currenciesTo, null); } } diff --git a/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/FixerIoTest.php b/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/FixerIoTest.php index c1c3f2fbacb03..7cbab6c46743c 100644 --- a/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/FixerIoTest.php +++ b/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/FixerIoTest.php @@ -3,89 +3,112 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Directory\Test\Unit\Model\Currency\Import; +use Magento\Directory\Model\Currency; +use Magento\Directory\Model\Currency\Import\FixerIo; +use Magento\Directory\Model\CurrencyFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\DataObject; +use Magento\Framework\HTTP\ZendClient; +use Magento\Framework\HTTP\ZendClientFactory; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + class FixerIoTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Directory\Model\Currency\Import\FixerIo + * @var FixerIo */ private $model; /** - * @var \Magento\Directory\Model\CurrencyFactory|\PHPUnit_Framework_MockObject_MockObject + * @var CurrencyFactory|MockObject + */ + private $currencyFactory; + + /** + * @var ZendClientFactory|MockObject */ - private $currencyFactoryMock; + private $httpClientFactory; /** - * @var \Magento\Framework\HTTP\ZendClientFactory|\PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ - private $httpClientFactoryMock; + private $scopeConfig; protected function setUp() { - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - - $this->currencyFactoryMock = $this->getMockBuilder(\Magento\Directory\Model\CurrencyFactory::class) + $this->currencyFactory = $this->getMockBuilder(CurrencyFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $this->httpClientFactoryMock = $this->getMockBuilder(\Magento\Framework\HTTP\ZendClientFactory::class) + $this->httpClientFactory = $this->getMockBuilder(ZendClientFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $scopeMock = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) + $this->scopeConfig = $this->getMockBuilder(ScopeConfigInterface::class) ->disableOriginalConstructor() ->setMethods([]) ->getMock(); - $this->model = $objectManagerHelper->getObject( - \Magento\Directory\Model\Currency\Import\FixerIo::class, - [ - 'currencyFactory' => $this->currencyFactoryMock, - 'scopeConfig' => $scopeMock, - 'httpClientFactory' => $this->httpClientFactoryMock - ] - ); + $this->model = new FixerIo($this->currencyFactory, $this->scopeConfig, $this->httpClientFactory); } public function testFetchRates() { $currencyFromList = ['USD']; $currencyToList = ['EUR', 'UAH']; - $responseBody = '{"base":"USD","date":"2015-10-07","rates":{"EUR":0.9022}}'; + $responseBody = '{"success":"true","base":"USD","date":"2015-10-07","rates":{"EUR":0.9022}}'; $expectedCurrencyRateList = ['USD' => ['EUR' => 0.9022, 'UAH' => null]]; - $message = "We can't retrieve a rate from http://api.fixer.io/latest?base=USD&symbols=EUR,UAH for UAH."; + $message = "We can't retrieve a rate from " + . "http://data.fixer.io/api/latest?access_key=api_key&base=USD&symbols=EUR,UAH for UAH."; + + $this->scopeConfig->method('getValue') + ->withConsecutive( + ['currency/fixerio/api_key', 'store'], + ['currency/fixerio/timeout', 'store'] + ) + ->willReturnOnConsecutiveCalls('api_key', 100); - /** @var \Magento\Directory\Model\Currency|\PHPUnit_Framework_MockObject_MockObject $currencyMock */ - $currencyMock = $this->getMockBuilder(\Magento\Directory\Model\Currency::class) + /** @var Currency|MockObject $currency */ + $currency = $this->getMockBuilder(Currency::class) ->disableOriginalConstructor() - ->setMethods([]) ->getMock(); - /** @var \Magento\Framework\HTTP\ZendClient|\PHPUnit_Framework_MockObject_MockObject $currencyMock */ - $httpClientMock = $this->getMockBuilder(\Magento\Framework\HTTP\ZendClient::class) + /** @var ZendClient|MockObject $httpClient */ + $httpClient = $this->getMockBuilder(ZendClient::class) ->disableOriginalConstructor() - ->setMethods([]) ->getMock(); - /** @var \Zend_Http_Response|\PHPUnit_Framework_MockObject_MockObject $currencyMock */ - $httpResponseMock = $this->getMockBuilder(\Zend_Http_Response::class) + /** @var DataObject|MockObject $currencyMock */ + $httpResponse = $this->getMockBuilder(DataObject::class) ->disableOriginalConstructor() - ->setMethods([]) + ->setMethods(['getBody']) ->getMock(); - $this->currencyFactoryMock->expects($this->any())->method('create')->willReturn($currencyMock); - $currencyMock->expects($this->once())->method('getConfigBaseCurrencies')->willReturn($currencyFromList); - $currencyMock->expects($this->once())->method('getConfigAllowCurrencies')->willReturn($currencyToList); - $this->httpClientFactoryMock->expects($this->any())->method('create')->willReturn($httpClientMock); - $httpClientMock->expects($this->atLeastOnce())->method('setUri')->willReturnSelf(); - $httpClientMock->expects($this->atLeastOnce())->method('setConfig')->willReturnSelf(); - $httpClientMock->expects($this->atLeastOnce())->method('request')->willReturn($httpResponseMock); - $httpResponseMock->expects($this->any())->method('getBody')->willReturn($responseBody); - $this->assertEquals($expectedCurrencyRateList, $this->model->fetchRates()); + $this->currencyFactory->method('create') + ->willReturn($currency); + $currency->method('getConfigBaseCurrencies') + ->willReturn($currencyFromList); + $currency->method('getConfigAllowCurrencies') + ->willReturn($currencyToList); + + $this->httpClientFactory->method('create') + ->willReturn($httpClient); + $httpClient->method('setUri') + ->willReturnSelf(); + $httpClient->method('setConfig') + ->willReturnSelf(); + $httpClient->method('request') + ->willReturn($httpResponse); + $httpResponse->method('getBody') + ->willReturn($responseBody); + + self::assertEquals($expectedCurrencyRateList, $this->model->fetchRates()); + $messages = $this->model->getMessages(); - $this->assertNotEmpty($messages); - $this->assertTrue(is_array($messages)); - $this->assertEquals($message, (string)$messages[0]); + self::assertNotEmpty($messages); + self::assertTrue(is_array($messages)); + self::assertEquals($message, (string)$messages[0]); } } diff --git a/app/code/Magento/Directory/etc/adminhtml/system.xml b/app/code/Magento/Directory/etc/adminhtml/system.xml index 17d0a3b1946eb..1a11accb0a518 100644 --- a/app/code/Magento/Directory/etc/adminhtml/system.xml +++ b/app/code/Magento/Directory/etc/adminhtml/system.xml @@ -36,7 +36,12 @@ - + + + currency/fixerio/api_key + Magento\Config\Model\Config\Backend\Encrypted + + diff --git a/app/code/Magento/Directory/etc/config.xml b/app/code/Magento/Directory/etc/config.xml index 0835099b7bf47..585d53bac7c19 100644 --- a/app/code/Magento/Directory/etc/config.xml +++ b/app/code/Magento/Directory/etc/config.xml @@ -20,6 +20,7 @@ 100 + 0 diff --git a/app/code/Magento/Directory/i18n/en_US.csv b/app/code/Magento/Directory/i18n/en_US.csv index 06b2c22fbfcf7..53ce272ea88bc 100644 --- a/app/code/Magento/Directory/i18n/en_US.csv +++ b/app/code/Magento/Directory/i18n/en_US.csv @@ -47,3 +47,8 @@ Service,Service "State is Required for","State is Required for" "Allow to Choose State if It is Optional for Country","Allow to Choose State if It is Optional for Country" "Weight Unit","Weight Unit" +"No API Key was specified or an invalid API Key was specified.","No API Key was specified or an invalid API Key was specified." +"The account this API request is coming from is inactive.","The account this API request is coming from is inactive." +"The """%1"" is not allowed as base currency for your subscription plan.","The """%1"" is not allowed as base currency for your subscription plan." +"An invalid base currency has been entered.","An invalid base currency has been entered." +"Currency rates can't be retrieved.","Currency rates can't be retrieved." \ No newline at end of file From e0b09fdc6db266015b3296fe8a272db7c3ba7d3e Mon Sep 17 00:00:00 2001 From: hitesh-wagento Date: Thu, 19 Jul 2018 12:29:32 +0530 Subject: [PATCH 051/211] [changes as per suggession] --- .../Magento/Authorizenet/view/frontend/requirejs-config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Authorizenet/view/frontend/requirejs-config.js b/app/code/Magento/Authorizenet/view/frontend/requirejs-config.js index 2b57e5cc2fb0d..8c4c90bf111de 100644 --- a/app/code/Magento/Authorizenet/view/frontend/requirejs-config.js +++ b/app/code/Magento/Authorizenet/view/frontend/requirejs-config.js @@ -6,7 +6,8 @@ var config = { map: { '*': { - transparent: 'Magento_Payment/js/transparent' + transparent: 'Magento_Payment/js/transparent', + 'Magento_Payment/transparent': 'Magento_Payment/js/transparent' } } }; From c74f9e210a554f9462d8bfaa9e34b46e5542b337 Mon Sep 17 00:00:00 2001 From: hitesh-wagento Date: Thu, 19 Jul 2018 15:07:33 +0530 Subject: [PATCH 052/211] [changes as per suggession] --- app/code/Magento/Captcha/view/frontend/requirejs-config.js | 3 ++- app/code/Magento/Customer/view/frontend/requirejs-config.js | 4 +++- .../Magento/Downloadable/view/frontend/requirejs-config.js | 3 ++- .../Magento/GiftMessage/view/frontend/requirejs-config.js | 4 +++- app/code/Magento/Payment/view/frontend/requirejs-config.js | 3 ++- app/code/Magento/Paypal/view/base/requirejs-config.js | 3 ++- app/code/Magento/Paypal/view/frontend/requirejs-config.js | 1 + app/code/Magento/Sales/view/frontend/requirejs-config.js | 4 +++- app/code/Magento/Search/view/frontend/requirejs-config.js | 3 ++- .../Magento/Translation/view/frontend/requirejs-config.js | 3 ++- app/code/Magento/Weee/view/frontend/requirejs-config.js | 3 ++- 11 files changed, 24 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Captcha/view/frontend/requirejs-config.js b/app/code/Magento/Captcha/view/frontend/requirejs-config.js index 0f3394e41e7c2..42c80632d3e92 100644 --- a/app/code/Magento/Captcha/view/frontend/requirejs-config.js +++ b/app/code/Magento/Captcha/view/frontend/requirejs-config.js @@ -6,7 +6,8 @@ var config = { map: { '*': { - captcha: 'Magento_Captcha/js/captcha' + captcha: 'Magento_Captcha/js/captcha', + 'Magento_Captcha/captcha': 'Magento_Captcha/js/captcha' } } }; diff --git a/app/code/Magento/Customer/view/frontend/requirejs-config.js b/app/code/Magento/Customer/view/frontend/requirejs-config.js index 967bbdcc0e663..52a1458fa5778 100644 --- a/app/code/Magento/Customer/view/frontend/requirejs-config.js +++ b/app/code/Magento/Customer/view/frontend/requirejs-config.js @@ -11,7 +11,9 @@ var config = { changeEmailPassword: 'Magento_Customer/js/change-email-password', passwordStrengthIndicator: 'Magento_Customer/js/password-strength-indicator', zxcvbn: 'Magento_Customer/js/zxcvbn', - addressValidation: 'Magento_Customer/js/addressValidation' + addressValidation: 'Magento_Customer/js/addressValidation', + 'Magento_Customer/address': 'Magento_Customer/js/address', + 'Magento_Customer/changeEmailPassword': 'Magento_Customer/js/change-email-password', } } }; diff --git a/app/code/Magento/Downloadable/view/frontend/requirejs-config.js b/app/code/Magento/Downloadable/view/frontend/requirejs-config.js index c3d33949eb012..f615966d801f7 100644 --- a/app/code/Magento/Downloadable/view/frontend/requirejs-config.js +++ b/app/code/Magento/Downloadable/view/frontend/requirejs-config.js @@ -6,7 +6,8 @@ var config = { map: { '*': { - downloadable: 'Magento_Downloadable/js/downloadable' + downloadable: 'Magento_Downloadable/js/downloadable', + 'Magento_Downloadable/downloadable': 'Magento_Downloadable/js/downloadable' } } }; diff --git a/app/code/Magento/GiftMessage/view/frontend/requirejs-config.js b/app/code/Magento/GiftMessage/view/frontend/requirejs-config.js index c3f8ecc45da38..12359f832e2d1 100644 --- a/app/code/Magento/GiftMessage/view/frontend/requirejs-config.js +++ b/app/code/Magento/GiftMessage/view/frontend/requirejs-config.js @@ -7,7 +7,9 @@ var config = { map: { '*': { giftOptions: 'Magento_GiftMessage/js/gift-options', - extraOptions: 'Magento_GiftMessage/js/extra-options' + extraOptions: 'Magento_GiftMessage/js/extra-options', + 'Magento_GiftMessage/giftOptions': 'Magento_GiftMessage/js/gift-options', + 'Magento_GiftMessage/extraOptions': 'Magento_GiftMessage/js/extra-options' } } }; diff --git a/app/code/Magento/Payment/view/frontend/requirejs-config.js b/app/code/Magento/Payment/view/frontend/requirejs-config.js index efa24d129e8ec..32ed12274b14c 100644 --- a/app/code/Magento/Payment/view/frontend/requirejs-config.js +++ b/app/code/Magento/Payment/view/frontend/requirejs-config.js @@ -6,7 +6,8 @@ var config = { map: { '*': { - creditCardType: 'Magento_Payment/js/cc-type' + creditCardType: 'Magento_Payment/js/cc-type', + 'Magento_Payment/creditCardType': 'Magento_Payment/js/cc-type' } } }; diff --git a/app/code/Magento/Paypal/view/base/requirejs-config.js b/app/code/Magento/Paypal/view/base/requirejs-config.js index 2b57e5cc2fb0d..8c4c90bf111de 100644 --- a/app/code/Magento/Paypal/view/base/requirejs-config.js +++ b/app/code/Magento/Paypal/view/base/requirejs-config.js @@ -6,7 +6,8 @@ var config = { map: { '*': { - transparent: 'Magento_Payment/js/transparent' + transparent: 'Magento_Payment/js/transparent', + 'Magento_Payment/transparent': 'Magento_Payment/js/transparent' } } }; diff --git a/app/code/Magento/Paypal/view/frontend/requirejs-config.js b/app/code/Magento/Paypal/view/frontend/requirejs-config.js index f2ac876f560c9..9be06b7c39261 100644 --- a/app/code/Magento/Paypal/view/frontend/requirejs-config.js +++ b/app/code/Magento/Paypal/view/frontend/requirejs-config.js @@ -7,6 +7,7 @@ var config = { map: { '*': { orderReview: 'Magento_Paypal/js/order-review', + 'Magento_Paypal/orderReview': 'Magento_Paypal/js/order-review', paypalCheckout: 'Magento_Paypal/js/paypal-checkout' } }, diff --git a/app/code/Magento/Sales/view/frontend/requirejs-config.js b/app/code/Magento/Sales/view/frontend/requirejs-config.js index 658960c749f8c..4995dc22a80bd 100644 --- a/app/code/Magento/Sales/view/frontend/requirejs-config.js +++ b/app/code/Magento/Sales/view/frontend/requirejs-config.js @@ -7,7 +7,9 @@ var config = { map: { '*': { giftMessage: 'Magento_Sales/js/gift-message', - ordersReturns: 'Magento_Sales/js/orders-returns' + ordersReturns: 'Magento_Sales/js/orders-returns', + 'Magento_Sales/giftMessage': 'Magento_Sales/js/gift-message', + 'Magento_Sales/ordersReturns': 'Magento_Sales/js/orders-returns' } } }; diff --git a/app/code/Magento/Search/view/frontend/requirejs-config.js b/app/code/Magento/Search/view/frontend/requirejs-config.js index cca294dd3689d..1296608b7143c 100644 --- a/app/code/Magento/Search/view/frontend/requirejs-config.js +++ b/app/code/Magento/Search/view/frontend/requirejs-config.js @@ -6,7 +6,8 @@ var config = { map: { '*': { - quickSearch: 'Magento_Search/js/form-mini' + quickSearch: 'Magento_Search/js/form-mini', + 'Magento_Search/quickSearch': 'Magento_Search/js/form-mini' } } }; diff --git a/app/code/Magento/Translation/view/frontend/requirejs-config.js b/app/code/Magento/Translation/view/frontend/requirejs-config.js index 4414f0d153ee8..59259fd6472d4 100644 --- a/app/code/Magento/Translation/view/frontend/requirejs-config.js +++ b/app/code/Magento/Translation/view/frontend/requirejs-config.js @@ -7,7 +7,8 @@ var config = { map: { '*': { editTrigger: 'mage/edit-trigger', - addClass: 'Magento_Translation/js/add-class' + addClass: 'Magento_Translation/js/add-class', + 'Magento_Translation/addClass': 'Magento_Translation/js/add-class' } }, deps: [ diff --git a/app/code/Magento/Weee/view/frontend/requirejs-config.js b/app/code/Magento/Weee/view/frontend/requirejs-config.js index 5b1b3a0f7ec73..573c04d3d7393 100644 --- a/app/code/Magento/Weee/view/frontend/requirejs-config.js +++ b/app/code/Magento/Weee/view/frontend/requirejs-config.js @@ -6,7 +6,8 @@ var config = { map: { '*': { - 'taxToggle': 'Magento_Weee/js/tax-toggle' + 'taxToggle': 'Magento_Weee/js/tax-toggle', + 'Magento_Weee/taxToggle': 'Magento_Weee/js/tax-toggle' } } }; From 6ad9ea419bd09ffc5b17bd4342f432958ba395ff Mon Sep 17 00:00:00 2001 From: hitesh-wagento Date: Thu, 19 Jul 2018 15:51:49 +0530 Subject: [PATCH 053/211] [changes as per suggession] --- app/code/Magento/Customer/view/frontend/requirejs-config.js | 2 +- .../Magento/GiftMessage/view/frontend/requirejs-config.js | 4 ++-- app/code/Magento/Payment/view/frontend/requirejs-config.js | 2 +- app/code/Magento/Paypal/view/frontend/requirejs-config.js | 2 +- app/code/Magento/Sales/view/frontend/requirejs-config.js | 4 ++-- app/code/Magento/Search/view/frontend/requirejs-config.js | 2 +- .../Magento/Translation/view/frontend/requirejs-config.js | 2 +- app/code/Magento/Weee/view/frontend/requirejs-config.js | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Customer/view/frontend/requirejs-config.js b/app/code/Magento/Customer/view/frontend/requirejs-config.js index 52a1458fa5778..e07a17efe3ca7 100644 --- a/app/code/Magento/Customer/view/frontend/requirejs-config.js +++ b/app/code/Magento/Customer/view/frontend/requirejs-config.js @@ -13,7 +13,7 @@ var config = { zxcvbn: 'Magento_Customer/js/zxcvbn', addressValidation: 'Magento_Customer/js/addressValidation', 'Magento_Customer/address': 'Magento_Customer/js/address', - 'Magento_Customer/changeEmailPassword': 'Magento_Customer/js/change-email-password', + 'Magento_Customer/change-email-password': 'Magento_Customer/js/change-email-password', } } }; diff --git a/app/code/Magento/GiftMessage/view/frontend/requirejs-config.js b/app/code/Magento/GiftMessage/view/frontend/requirejs-config.js index 12359f832e2d1..f8a463bec37f9 100644 --- a/app/code/Magento/GiftMessage/view/frontend/requirejs-config.js +++ b/app/code/Magento/GiftMessage/view/frontend/requirejs-config.js @@ -8,8 +8,8 @@ var config = { '*': { giftOptions: 'Magento_GiftMessage/js/gift-options', extraOptions: 'Magento_GiftMessage/js/extra-options', - 'Magento_GiftMessage/giftOptions': 'Magento_GiftMessage/js/gift-options', - 'Magento_GiftMessage/extraOptions': 'Magento_GiftMessage/js/extra-options' + 'Magento_GiftMessage/gift-options': 'Magento_GiftMessage/js/gift-options', + 'Magento_GiftMessage/extra-options': 'Magento_GiftMessage/js/extra-options' } } }; diff --git a/app/code/Magento/Payment/view/frontend/requirejs-config.js b/app/code/Magento/Payment/view/frontend/requirejs-config.js index 32ed12274b14c..949b1554a6f7d 100644 --- a/app/code/Magento/Payment/view/frontend/requirejs-config.js +++ b/app/code/Magento/Payment/view/frontend/requirejs-config.js @@ -7,7 +7,7 @@ var config = { map: { '*': { creditCardType: 'Magento_Payment/js/cc-type', - 'Magento_Payment/creditCardType': 'Magento_Payment/js/cc-type' + 'Magento_Payment/cc-type': 'Magento_Payment/js/cc-type' } } }; diff --git a/app/code/Magento/Paypal/view/frontend/requirejs-config.js b/app/code/Magento/Paypal/view/frontend/requirejs-config.js index 9be06b7c39261..223ade43d86e5 100644 --- a/app/code/Magento/Paypal/view/frontend/requirejs-config.js +++ b/app/code/Magento/Paypal/view/frontend/requirejs-config.js @@ -7,7 +7,7 @@ var config = { map: { '*': { orderReview: 'Magento_Paypal/js/order-review', - 'Magento_Paypal/orderReview': 'Magento_Paypal/js/order-review', + 'Magento_Paypal/order-review': 'Magento_Paypal/js/order-review', paypalCheckout: 'Magento_Paypal/js/paypal-checkout' } }, diff --git a/app/code/Magento/Sales/view/frontend/requirejs-config.js b/app/code/Magento/Sales/view/frontend/requirejs-config.js index 4995dc22a80bd..4d323684afff6 100644 --- a/app/code/Magento/Sales/view/frontend/requirejs-config.js +++ b/app/code/Magento/Sales/view/frontend/requirejs-config.js @@ -8,8 +8,8 @@ var config = { '*': { giftMessage: 'Magento_Sales/js/gift-message', ordersReturns: 'Magento_Sales/js/orders-returns', - 'Magento_Sales/giftMessage': 'Magento_Sales/js/gift-message', - 'Magento_Sales/ordersReturns': 'Magento_Sales/js/orders-returns' + 'Magento_Sales/gift-message': 'Magento_Sales/js/gift-message', + 'Magento_Sales/orders-returns': 'Magento_Sales/js/orders-returns' } } }; diff --git a/app/code/Magento/Search/view/frontend/requirejs-config.js b/app/code/Magento/Search/view/frontend/requirejs-config.js index 1296608b7143c..d945944daa1b0 100644 --- a/app/code/Magento/Search/view/frontend/requirejs-config.js +++ b/app/code/Magento/Search/view/frontend/requirejs-config.js @@ -7,7 +7,7 @@ var config = { map: { '*': { quickSearch: 'Magento_Search/js/form-mini', - 'Magento_Search/quickSearch': 'Magento_Search/js/form-mini' + 'Magento_Search/form-mini': 'Magento_Search/js/form-mini' } } }; diff --git a/app/code/Magento/Translation/view/frontend/requirejs-config.js b/app/code/Magento/Translation/view/frontend/requirejs-config.js index 59259fd6472d4..b4b3ce0f8c554 100644 --- a/app/code/Magento/Translation/view/frontend/requirejs-config.js +++ b/app/code/Magento/Translation/view/frontend/requirejs-config.js @@ -8,7 +8,7 @@ var config = { '*': { editTrigger: 'mage/edit-trigger', addClass: 'Magento_Translation/js/add-class', - 'Magento_Translation/addClass': 'Magento_Translation/js/add-class' + 'Magento_Translation/add-class': 'Magento_Translation/js/add-class' } }, deps: [ diff --git a/app/code/Magento/Weee/view/frontend/requirejs-config.js b/app/code/Magento/Weee/view/frontend/requirejs-config.js index 573c04d3d7393..94c59da65216d 100644 --- a/app/code/Magento/Weee/view/frontend/requirejs-config.js +++ b/app/code/Magento/Weee/view/frontend/requirejs-config.js @@ -7,7 +7,7 @@ var config = { map: { '*': { 'taxToggle': 'Magento_Weee/js/tax-toggle', - 'Magento_Weee/taxToggle': 'Magento_Weee/js/tax-toggle' + 'Magento_Weee/tax-toggle': 'Magento_Weee/js/tax-toggle' } } }; From abf5d89f1019febc06ac93e5cf44afeedd3e26bb Mon Sep 17 00:00:00 2001 From: Sergey Shvets Date: Thu, 19 Jul 2018 13:39:29 +0300 Subject: [PATCH 054/211] MAGETWO-92036: The error icon does not appear on sections with required attributes that are empty when click on 'save' button --- .../base/web/js/form/components/fieldset.js | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js index b729dd3127d90..433ee78e61cdf 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js @@ -22,7 +22,7 @@ define([ opened: false, level: 0, visible: true, - initializeFieldsetDataByDefault: false, /* Data in some fieldsets should be initialized before open */ + initializeFieldsetDataByDefault: false, /* Data in some fieldsets should be initialized before open */ disabled: false, listens: { 'opened': 'onVisibilityChange' @@ -77,9 +77,9 @@ define([ elem.initContainer(this); elem.on({ - 'update': this.onChildrenUpdate, - 'loading': this.onContentLoading, - 'error': this.onChildrenError + 'update': this.onChildrenUpdate, + 'loading': this.onContentLoading, + 'error': this.onChildrenError }); if (this.disabled) { @@ -155,7 +155,23 @@ define([ * @param {String} message - error message. */ onChildrenError: function (message) { - var hasErrors = this.elems.some('error'); + var hasErrors = false, + isChildrenErrors = function (hasErrors, container) { + if (hasErrors === false && container.hasOwnProperty('elems')) { + hasErrors = container.elems.some('error'); + if (hasErrors === false && container.hasOwnProperty('_elems')) { + container._elems.each(function (child) { + if (hasErrors === false) { + hasErrors = isChildrenErrors(hasErrors, child); + } + }); + } + } + return hasErrors; + }; + if (!message) { + hasErrors = isChildrenErrors(hasErrors, this); + } this.error(hasErrors || message); }, From e2558dc6c0729c6f99cb0688048048a1a3483baf Mon Sep 17 00:00:00 2001 From: hitesh-wagento Date: Thu, 19 Jul 2018 16:10:23 +0530 Subject: [PATCH 055/211] Created new required js --- .../SendFriend/view/frontend/requirejs-config.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 app/code/Magento/SendFriend/view/frontend/requirejs-config.js diff --git a/app/code/Magento/SendFriend/view/frontend/requirejs-config.js b/app/code/Magento/SendFriend/view/frontend/requirejs-config.js new file mode 100644 index 0000000000000..f356cebe57946 --- /dev/null +++ b/app/code/Magento/SendFriend/view/frontend/requirejs-config.js @@ -0,0 +1,12 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +var config = { + map: { + '*': { + 'Magento_SendFriend/back-event': 'Magento_SendFriend/js/back-event' + } + } +}; From f3c87c6f36d35ac2a92bdb0501fdcfade71a41fa Mon Sep 17 00:00:00 2001 From: Sergey Shvets Date: Thu, 19 Jul 2018 14:32:14 +0300 Subject: [PATCH 056/211] MAGETWO-92036: The error icon does not appear on sections with required attributes that are empty when click on 'save' button fix static tests --- .../base/web/js/form/components/fieldset.js | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js index 433ee78e61cdf..ee84ac379eae7 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js @@ -155,27 +155,29 @@ define([ * @param {String} message - error message. */ onChildrenError: function (message) { - var hasErrors = false, - isChildrenErrors = function (hasErrors, container) { - if (hasErrors === false && container.hasOwnProperty('elems')) { - hasErrors = container.elems.some('error'); - if (hasErrors === false && container.hasOwnProperty('_elems')) { - container._elems.each(function (child) { - if (hasErrors === false) { - hasErrors = isChildrenErrors(hasErrors, child); - } - }); - } - } - return hasErrors; - }; + var hasErrors = false; + if (!message) { - hasErrors = isChildrenErrors(hasErrors, this); + hasErrors = this.isChildrenHasErrors(hasErrors, this); } this.error(hasErrors || message); }, + isChildrenHasErrors: function (hasErrors, container) { + if (hasErrors === false && container.hasOwnProperty('elems')) { + hasErrors = container.elems.some('error'); + if (hasErrors === false && container.hasOwnProperty('_elems')) { + container._elems.each(function (child) { + if (hasErrors === false) { + hasErrors = this.isChildrenHasErrors(hasErrors, child); + } + }); + } + } + return hasErrors; + }, + /** * Callback that sets loading property to true. */ From 0aa2694104ce83508636cbe4422d05890f133cfe Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Thu, 19 Jul 2018 14:55:34 +0300 Subject: [PATCH 057/211] MAGETWO-92682: Triggers conditions refactoring --- .../Framework/Mview/View/Subscription.php | 54 +++++++++++-------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/lib/internal/Magento/Framework/Mview/View/Subscription.php b/lib/internal/Magento/Framework/Mview/View/Subscription.php index 4f5c65c799be8..c0f5833c4c0a1 100644 --- a/lib/internal/Magento/Framework/Mview/View/Subscription.php +++ b/lib/internal/Magento/Framework/Mview/View/Subscription.php @@ -203,27 +203,7 @@ protected function buildStatement($event, $changelog) case Trigger::EVENT_UPDATE: $trigger = "INSERT IGNORE INTO %s (%s) VALUES (NEW.%s);"; - - if ($this->connection->isTableExists($this->getTableName()) - && $describe = $this->connection->describeTable($this->getTableName()) - ) { - $columnNames = array_column($describe, 'COLUMN_NAME'); - $columnNames = array_diff($columnNames, $this->ignoredUpdateColumns); - if ($columnNames) { - $columns = []; - foreach ($columnNames as $columnName) { - $columns[] = sprintf( - 'NEW.%1$s != OLD.%1$s', - $this->connection->quoteIdentifier($columnName) - ); - } - $trigger = sprintf( - "IF (%s) THEN %s END IF;", - implode(' OR ', $columns), - $trigger - ); - } - } + $trigger = $this->getTriggerWithCondition($trigger); break; case Trigger::EVENT_DELETE: @@ -242,6 +222,38 @@ protected function buildStatement($event, $changelog) ); } + /** + * Add condition for data change validation to a trigger + * + * @param string $trigger + * @return string + */ + protected function getTriggerWithCondition($trigger) + { + if ($this->connection->isTableExists($this->getTableName()) + && $describe = $this->connection->describeTable($this->getTableName()) + ) { + $columnNames = array_column($describe, 'COLUMN_NAME'); + $columnNames = array_diff($columnNames, $this->ignoredUpdateColumns); + if ($columnNames) { + $columns = []; + foreach ($columnNames as $columnName) { + $columns[] = sprintf( + 'NEW.%1$s <=> OLD.%1$s', + $this->connection->quoteIdentifier($columnName) + ); + } + $trigger = sprintf( + "IF (%s) THEN %s END IF;", + implode(' OR ', $columns), + $trigger + ); + } + } + + return $trigger; + } + /** * Build an "after" event for the given table and event * From 230145092ca1b385148fee7867ef927780674c50 Mon Sep 17 00:00:00 2001 From: hitesh-wagento Date: Thu, 19 Jul 2018 17:55:57 +0530 Subject: [PATCH 058/211] Resolved Travis error --- app/code/Magento/Customer/view/frontend/requirejs-config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/view/frontend/requirejs-config.js b/app/code/Magento/Customer/view/frontend/requirejs-config.js index e07a17efe3ca7..f1bf5c1d1b67f 100644 --- a/app/code/Magento/Customer/view/frontend/requirejs-config.js +++ b/app/code/Magento/Customer/view/frontend/requirejs-config.js @@ -13,7 +13,7 @@ var config = { zxcvbn: 'Magento_Customer/js/zxcvbn', addressValidation: 'Magento_Customer/js/addressValidation', 'Magento_Customer/address': 'Magento_Customer/js/address', - 'Magento_Customer/change-email-password': 'Magento_Customer/js/change-email-password', + 'Magento_Customer/change-email-password': 'Magento_Customer/js/change-email-password' } } }; From 87f5af73db9bee00f3d06997887b81976c36fe38 Mon Sep 17 00:00:00 2001 From: Sergey Shvets Date: Thu, 19 Jul 2018 15:48:43 +0300 Subject: [PATCH 059/211] MAGETWO-92036: The error icon does not appear on sections with required attributes that are empty when click on 'save' button --- .../view/base/web/js/form/components/fieldset.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js index ee84ac379eae7..7b121128a82aa 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js @@ -158,19 +158,27 @@ define([ var hasErrors = false; if (!message) { - hasErrors = this.isChildrenHasErrors(hasErrors, this); + hasErrors = this._isChildrenHasErrors(hasErrors, this); } this.error(hasErrors || message); }, - isChildrenHasErrors: function (hasErrors, container) { + /** + * Returns errors of children if exist + * + * @param {boolean} hasErrors + * @param {*} container + * @return {boolean} + * @private + */ + _isChildrenHasErrors: function (hasErrors, container) { if (hasErrors === false && container.hasOwnProperty('elems')) { hasErrors = container.elems.some('error'); if (hasErrors === false && container.hasOwnProperty('_elems')) { container._elems.each(function (child) { if (hasErrors === false) { - hasErrors = this.isChildrenHasErrors(hasErrors, child); + hasErrors = this._isChildrenHasErrors(hasErrors, child); } }); } From 0255ce3d64865b25417606713b438b5f78543b97 Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko Date: Thu, 19 Jul 2018 13:44:44 +0300 Subject: [PATCH 060/211] MAGETWO-93198: Extra title is displayed under a product image on product pages on Storefront --- .../templates/product/view/gallery.phtml | 118 ++++++++++-------- .../Block/Product/View/GalleryTest.php | 50 ++++++++ 2 files changed, 114 insertions(+), 54 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/gallery.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/gallery.phtml index ffecb2ba4f305..b2fa8e9aaf80f 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/gallery.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/gallery.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** * Product media data template * @@ -15,19 +13,19 @@ diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/catalog-add-to-cart.js b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/catalog-add-to-cart.js new file mode 100644 index 0000000000000..3e6a611c268af --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/catalog-add-to-cart.js @@ -0,0 +1,20 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +require([ + 'jquery' +], function ($) { + 'use strict'; + + /** + * Add selected configurable attributes to redirect url + * + * @see Magento_Catalog/js/catalog-add-to-cart + */ + $('body').on('catalogCategoryAddToCartRedirect', function (event, data) { + $(data.form).find('select[name*="super"]').each(function (index, item) { + data.redirectParameters.push(item.config.id + '=' + $(item).val()); + }); + }); +}); diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddSwatchToProductActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddSwatchToProductActionGroup.xml new file mode 100644 index 0000000000000..5f4eb291a39a0 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddSwatchToProductActionGroup.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml new file mode 100644 index 0000000000000..092a3cf26c3ae --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml @@ -0,0 +1,15 @@ + + + + + + VisualSwatchAttr + Visual Swatch + + diff --git a/app/code/Magento/Swatches/Test/Mftf/Data/SwatchOptionData.xml b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchOptionData.xml new file mode 100644 index 0000000000000..84ffde9ec14f5 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchOptionData.xml @@ -0,0 +1,20 @@ + + + + + + VisualOpt1 + VisualOpt1 + + + + VisualOpt2 + VisualOpt2 + + diff --git a/app/code/Magento/Swatches/Test/Mftf/Section/AdminNewAttributePanelSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/AdminNewAttributePanelSection.xml new file mode 100644 index 0000000000000..81fc8a1ae5922 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Section/AdminNewAttributePanelSection.xml @@ -0,0 +1,16 @@ + + + + +
+ + + +
+
diff --git a/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml new file mode 100644 index 0000000000000..d547d8c95d4b0 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -0,0 +1,15 @@ + + + + +
+ + +
+
diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml new file mode 100644 index 0000000000000..da7b9901c3f16 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml @@ -0,0 +1,93 @@ + + + + + + + + + + <description value="Configurable product with swatch option and file custom option. When adding to cart with an invalid filetype, the correct error message is shown, and options remain selected."/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-93331"/> + <group value="ConfigurableProduct"/> + <group value="Swatches"/> + </annotations> + + <before> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + </before> + + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="DeleteAllProductsOnProductsGridPageFilteredByName" stepKey="deleteAllCreatedProducts"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <actionGroup ref="DeleteProductAttribute" stepKey="deleteCreatedProductAttribute"> + <argument name="productAttribute" value="visualSwatchAttribute"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logoutAdminUserAfterTest"/> + </after> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Create a configurable swatch product via the UI --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> + <waitForPageLoad stepKey="waitForProductPage"/> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="searchAndSelectCategory"/> + <!--Add swatch attribute to configurable product--> + <actionGroup ref="AddVisualSwatchToProductActionGroup" stepKey="addSwatchToProduct"/> + <!--Add custom option to configurable product--> + <actionGroup ref="AddProductCustomOptionFile" stepKey="addCustomOptionToProduct"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> + + <!--Go to storefront--> + <amOnPage url="" stepKey="goToHomePage"/> + <waitForPageLoad stepKey="waitForHomePageLoad"/> + <click selector="{{StorefrontNavigationSection.topCategory($$createCategory.name$$)}}" stepKey="goToCategoryStorefront"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad"/> + <see selector="{{StorefrontCategoryMainSection.categoryTitle}}" userInput="$$createCategory.name$$" stepKey="seeOnCategoryPage"/> + <!--Add configurable product to cart--> + <moveMouseOver selector="{{StorefrontCategoryProductSection.productTitleByName(BaseConfigurableProduct.name)}}" stepKey="hoverProductInGrid"/> + <click selector="{{StorefrontCategoryProductSection.productAddToCartByName(BaseConfigurableProduct.name)}}" stepKey="tryAddToCartFromCategoryPage"/> + <waitForPageLoad stepKey="waitForRedirectToProductPage"/> + <seeInCurrentUrl url="{{StorefrontProductPage.url(BaseConfigurableProduct.name)}}" stepKey="seeOnProductPage"/> + <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel(visualSwatchOption2.default_label)}}" stepKey="clickSwatchOption"/> + <see selector="{{StorefrontProductInfoMainSection.selectedSwatchValue(visualSwatchAttribute.default_label)}}" userInput="{{visualSwatchOption2.default_label}}" stepKey="seeSwatchIsSelected"/> + + <!--Try invalid file--> + <attachFile selector="{{StorefrontProductInfoMainSection.addLinkFileUploadFile(ProductOptionFile.title)}}" userInput="lorem_ipsum.docx" stepKey="attachInvalidFile"/> + <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="addToCartInvalidFile"/> + <waitForElementVisible selector="{{StorefrontProductPageSection.alertMessage}}" stepKey="waitForErrorMessageInvalidFile"/> + <see selector="{{StorefrontProductPageSection.messagesBlock}}" userInput="The file 'lorem_ipsum.docx' for '{{ProductOptionFile.title}}' has an invalid extension." stepKey="seeMessageInvalidFile"/> + <!--Swatch remains selected--> + <see selector="{{StorefrontProductInfoMainSection.selectedSwatchValue(visualSwatchAttribute.default_label)}}" userInput="{{visualSwatchOption2.default_label}}" stepKey="seeSwatchRemainsSelected"/> + <!--Try valid file--> + <attachFile selector="{{StorefrontProductInfoMainSection.addLinkFileUploadFile(ProductOptionFile.title)}}" userInput="{{MagentoLogo.file}}" stepKey="attachValidFile"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$132.99" stepKey="seePriceUpdated"/> + <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="addToCartValidFile"/> + <waitForElementVisible selector="{{StorefrontProductPageSection.successMsg}}" stepKey="waitForSuccessMessage"/> + <see selector="{{StorefrontProductPageSection.messagesBlock}}" userInput="You added {{BaseConfigurableProduct.name}} to your shopping cart." stepKey="seeSuccessMessage"/> + + <!--Check item in cart--> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCart"/> + <waitForPageLoad stepKey="waitForCartPageLoad"/> + <seeElement selector="{{CheckoutCartProductSection.productLinkByName(BaseConfigurableProduct.name)}}" stepKey="seeProductInCart"/> + <see selector="{{CheckoutCartProductSection.productOptionByNameAndAttribute(BaseConfigurableProduct.name, visualSwatchAttribute.default_label)}}" userInput="{{visualSwatchOption2.default_label}}" stepKey="seeSelectedSwatch"/> + <see selector="{{CheckoutCartProductSection.productOptionByNameAndAttribute(BaseConfigurableProduct.name, ProductOptionFile.title)}}" userInput="{{MagentoLogo.file}}" stepKey="seeCorrectOptionFile"/> + <!--Delete cart item--> + <click selector="{{CheckoutCartProductSection.removeItem}}" stepKey="deleteCartItem"/> + </test> +</tests> diff --git a/app/code/Magento/Swatches/view/frontend/templates/product/view/renderer.phtml b/app/code/Magento/Swatches/view/frontend/templates/product/view/renderer.phtml index ea166b9080f5c..fbd6ff15c006e 100644 --- a/app/code/Magento/Swatches/view/frontend/templates/product/view/renderer.phtml +++ b/app/code/Magento/Swatches/view/frontend/templates/product/view/renderer.phtml @@ -22,6 +22,9 @@ "Magento_Swatches/js/configurable-customer-data": { "swatchOptions": <?php /* @noEscape */ echo $swatchOptions ?> } + }, + "*" : { + "Magento_Swatches/js/catalog-add-to-cart": {} } } </script> diff --git a/app/code/Magento/Swatches/view/frontend/web/js/catalog-add-to-cart.js b/app/code/Magento/Swatches/view/frontend/web/js/catalog-add-to-cart.js index d699faae3a85f..ce4a364e97144 100644 --- a/app/code/Magento/Swatches/view/frontend/web/js/catalog-add-to-cart.js +++ b/app/code/Magento/Swatches/view/frontend/web/js/catalog-add-to-cart.js @@ -7,11 +7,22 @@ require([ ], function ($) { 'use strict'; + /** + * Add selected swatch attributes to redirect url + * + * @see Magento_Catalog/js/catalog-add-to-cart + */ $('body').on('catalogCategoryAddToCartRedirect', function (event, data) { $(data.form).find('[name*="super"]').each(function (index, item) { - var $item = $(item); + var $item = $(item), + attr; - data.redirectParameters.push($item.attr('data-attr-name') + '=' + $item.val()); + if ($item.attr('data-attr-name')) { + attr = $item.attr('data-attr-name'); + } else { + attr = $item.parent().attr('attribute-code'); + } + data.redirectParameters.push(attr + '=' + $item.val()); }); }); }); 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 50a3af34aa684..3060dfd316929 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 @@ -1240,8 +1240,20 @@ define([ */ _EmulateSelected: function (selectedAttributes) { $.each(selectedAttributes, $.proxy(function (attributeCode, optionId) { - this.element.find('.' + this.options.classes.attributeClass + - '[attribute-code="' + attributeCode + '"] [option-id="' + optionId + '"]').trigger('click'); + var elem = this.element.find('.' + this.options.classes.attributeClass + + '[attribute-code="' + attributeCode + '"] [option-id="' + optionId + '"]'), + parentInput = elem.parent(); + + if (elem.hasClass('selected')) { + return; + } + + if (parentInput.hasClass(this.options.classes.selectClass)) { + parentInput.val(optionId); + parentInput.trigger('change'); + } else { + elem.trigger('click'); + } }, this)); }, diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml index 2164af26e5ac4..ea0f7e64a8448 100644 --- a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml @@ -12,6 +12,7 @@ <element name="firstRow" type="button" selector="tr.data-row:nth-of-type(1)"/> <element name="columnHeader" type="button" selector="//div[@data-role='grid-wrapper']//table[contains(@class, 'data-grid')]/thead/tr/th[contains(@class, 'data-grid-th')]/span[text() = '{{label}}']" parameterized="true" timeout="30"/> <element name="column" type="text" selector="//tr//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., '{{col}}')]/preceding-sibling::th) +1 ]" parameterized="true"/> + <element name="rowCheckbox" type="checkbox" selector="table.data-grid tbody > tr:nth-of-type({{row}}) td.data-grid-checkbox-cell input" parameterized="true"/> <element name="row" type="text" selector="table.data-grid tbody > tr:nth-of-type({{row}})" parameterized="true"/> <element name="rows" type="text" selector="table.data-grid tbody > tr.data-row"/> <!--Specific cell e.g. {{Section.gridCell('1', 'Name')}}--> diff --git a/dev/tests/acceptance/tests/_data/lorem_ipsum.docx b/dev/tests/acceptance/tests/_data/lorem_ipsum.docx new file mode 100644 index 0000000000000000000000000000000000000000..488f64e86b6ff42d2eaa492cc1cd79220be90350 GIT binary patch literal 13589 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 literal 0 HcmV?d00001 diff --git a/dev/tests/acceptance/tests/_data/lorem_ipsum.txt b/dev/tests/acceptance/tests/_data/lorem_ipsum.txt new file mode 100644 index 0000000000000..64cb8d023361a --- /dev/null +++ b/dev/tests/acceptance/tests/_data/lorem_ipsum.txt @@ -0,0 +1,9 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc venenatis cursus eros, eu congue risus eleifend ut. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean aliquet mi diam, at consequat ex imperdiet eu. Nullam vulputate sollicitudin libero, tristique ullamcorper ante pretium eget. Nunc vehicula, risus ut hendrerit ornare, tortor est mattis urna, vitae egestas arcu tellus quis est. Sed mollis est sem. Aenean rhoncus ultricies sapien, id tempus elit lobortis ac. Pellentesque condimentum gravida purus a pretium. Nulla sed sapien mattis, auctor lacus quis, volutpat metus. Nunc mattis diam elit, viverra tincidunt nisl faucibus eu. Duis ac nisl tellus. Aenean eros lectus, malesuada in ex non, pharetra aliquam odio. Aenean ultricies pharetra mauris, ac rutrum quam posuere at. Phasellus et tempor turpis, ut sodales turpis. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. + +Sed fringilla orci at elit gravida, posuere varius elit eleifend. Nam libero dui, rutrum ac massa et, dictum egestas massa. Fusce rutrum, neque vitae vestibulum mattis, magna orci dictum turpis, ut laoreet eros urna lacinia ipsum. Donec eget ultrices eros. Duis sollicitudin ante est. Maecenas semper pellentesque scelerisque. Vestibulum eu venenatis tellus. Etiam nec massa sem. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Etiam blandit molestie justo, non euismod dolor aliquet ac. Duis consectetur enim in arcu suscipit, in tempus nisi commodo. + +Donec sed venenatis nunc. Proin velit leo, porta eget erat elementum, dapibus posuere odio. Nam varius lectus eu cursus tristique. Ut tempus libero vehicula, iaculis augue sit amet, vestibulum justo. Vivamus porta diam vitae malesuada vestibulum. Donec mi dolor, semper at rutrum eget, vehicula non orci. Nunc dolor urna, laoreet et egestas vitae, sodales quis ipsum. Vivamus aliquet viverra enim cursus tincidunt. Pellentesque vel laoreet mi. Aenean ut rhoncus orci. Donec a purus venenatis, tempor dolor in, facilisis ex. Sed nec metus convallis, viverra nisl nec, luctus arcu. Integer blandit arcu a est posuere pharetra. + +Aliquam ultricies lectus ac mauris luctus, a viverra neque rhoncus. Vestibulum id velit eu nisl efficitur lobortis. Sed id metus at ipsum imperdiet porta. Quisque in quam in turpis fermentum condimentum. Phasellus sagittis risus eu tempus scelerisque. Vivamus dapibus sem odio, vitae fermentum sem viverra et. Quisque sit amet cursus neque, vel hendrerit risus. Integer ut diam porta, volutpat risus in, iaculis diam. Suspendisse vulputate non quam et finibus. + +Donec blandit, sem ut posuere dignissim, dolor lorem egestas magna, vel faucibus dui metus eget orci. Nullam ipsum lacus, imperdiet at nisl sed, condimentum dignissim lectus. Nunc orci libero, tincidunt a molestie vel, dapibus at mi. Quisque scelerisque sem quis massa suscipit, sit amet suscipit arcu volutpat. In tincidunt lacus in porttitor mattis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Morbi rutrum gravida orci quis porta. \ No newline at end of file From e1b23293dfceeaa61bcd899b29364a17e1ed5847 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <dhorytskyi@magento.com> Date: Fri, 20 Jul 2018 13:33:52 +0300 Subject: [PATCH 066/211] MAGETWO-86609: Disabled wishlist product still appears in wishlist --- app/code/Magento/Wishlist/Helper/Data.php | 2 +- .../Magento/Wishlist/Model/ResourceModel/Item/Collection.php | 4 ++-- app/code/Magento/Wishlist/Model/Wishlist.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Wishlist/Helper/Data.php b/app/code/Magento/Wishlist/Helper/Data.php index 968a7f1db8c39..7eef535382b7f 100644 --- a/app/code/Magento/Wishlist/Helper/Data.php +++ b/app/code/Magento/Wishlist/Helper/Data.php @@ -570,7 +570,7 @@ public function calculate() ) { $count = $collection->getItemsQty(); } else { - $count = $collection->getSize(); + $count = $collection->count(); } $this->_customerSession->setWishlistDisplayType( $this->scopeConfig->getValue( diff --git a/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php b/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php index 225026f31a994..bd921f3785c96 100644 --- a/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php +++ b/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php @@ -307,7 +307,7 @@ protected function _assignProducts() $checkInStock = $this->_productInStock && !$this->stockConfiguration->isShowOutOfStock(); - foreach ($this as $item) { + foreach ($this as $itemKey => $item) { $product = $productCollection->getItemById($item->getProductId()); if ($product) { if ($checkInStock && !$product->isInStock()) { @@ -320,7 +320,7 @@ protected function _assignProducts() $item->setPrice($product->getPrice()); } } else { - $item->isDeleted(true); + $this->removeItemByKey($itemKey); } } diff --git a/app/code/Magento/Wishlist/Model/Wishlist.php b/app/code/Magento/Wishlist/Model/Wishlist.php index 12da947ee3e25..6a5a7c45383f5 100644 --- a/app/code/Magento/Wishlist/Model/Wishlist.php +++ b/app/code/Magento/Wishlist/Model/Wishlist.php @@ -582,7 +582,7 @@ public function setStore($store) */ public function getItemsCount() { - return $this->getItemCollection()->getSize(); + return $this->getItemCollection()->count(); } /** From d216e13ffb26904eeea187fee3e3b4622e509548 Mon Sep 17 00:00:00 2001 From: Sergey Shvets <sshvets@magento.com> Date: Fri, 20 Jul 2018 16:49:52 +0300 Subject: [PATCH 067/211] MAGETWO-92036: The error icon does not appear on sections with required attributes that are empty when click on 'save' button --- .../Magento/Ui/view/base/web/js/form/components/fieldset.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js index 7b121128a82aa..895858f0dfdfc 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js @@ -173,12 +173,13 @@ define([ * @private */ _isChildrenHasErrors: function (hasErrors, container) { + var self = this; if (hasErrors === false && container.hasOwnProperty('elems')) { hasErrors = container.elems.some('error'); if (hasErrors === false && container.hasOwnProperty('_elems')) { container._elems.each(function (child) { if (hasErrors === false) { - hasErrors = this._isChildrenHasErrors(hasErrors, child); + hasErrors = self._isChildrenHasErrors(hasErrors, child); } }); } From ece897221a8df2d021f5ef279cc348c897e53f12 Mon Sep 17 00:00:00 2001 From: Sergey Shvets <sshvets@magento.com> Date: Fri, 20 Jul 2018 16:54:59 +0300 Subject: [PATCH 068/211] MAGETWO-92036: The error icon does not appear on sections with required attributes that are empty when click on 'save' button fix static test --- .../Magento/Ui/view/base/web/js/form/components/fieldset.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js index 895858f0dfdfc..9f5818a8d1c2f 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js @@ -167,9 +167,9 @@ define([ /** * Returns errors of children if exist * - * @param {boolean} hasErrors + * @param {Boolean} hasErrors * @param {*} container - * @return {boolean} + * @return {Boolean} * @private */ _isChildrenHasErrors: function (hasErrors, container) { From 48aa2f1ddfedbf8d8138b8ddea5500a736c1ee64 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 20 Jul 2018 18:49:46 +0300 Subject: [PATCH 069/211] MAGETWO-73359: CMS Page does not save when same url key with hierarchy for Multi-store MFTF tests --- ...ductWithCustomOptionsSecondWebsiteTest.xml | 17 ++++++++-- .../CreateNewPageWithAllValuesActionGroup.xml | 31 +++++++++++++++++++ .../DeletePageByUrlKeyActionGroup.xml | 23 ++++++++++++++ .../Section/CmsNewPageHierarchySection.xml | 15 +++++++++ .../Section/CmsNewPagePageActionsSection.xml | 1 + .../Mftf/Section/CmsNewPagePiwSection.xml | 15 +++++++++ .../Section/CmsPagesPageActionsSection.xml | 2 ++ .../AdminCreateNewStoreGroupActionGroup.xml | 11 +++++-- .../AdminCreateStoreViewActionGroup.xml | 4 +-- .../AdminCreateWebsiteActionGroup.xml | 8 +++-- .../AdminDeleteWebsiteActionGroup.xml | 7 +++-- .../Store/Test/Mftf/Data/StoreData.xml | 9 ++++++ .../Store/Test/Mftf/Data/StoreGroupData.xml | 6 ++++ .../AdminNewStoreViewActionsSection.xml | 2 +- .../Section/AdminNewWebsiteActionsSection.xml | 2 +- .../Section/AdminStoreGroupActionsSection.xml | 2 +- 16 files changed, 140 insertions(+), 15 deletions(-) create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/Section/CmsNewPageHierarchySection.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePiwSection.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml index 7925d2f3174a9..ccf29d0089181 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml @@ -21,14 +21,25 @@ <after> <actionGroup ref="ResetWebUrlOptions" stepKey="resetUrlOption"/> - <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"/> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteTestWebsite"> + <argument name="websiteName" value="Second Website"/> + </actionGroup> <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> </after> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <actionGroup ref="EnableWebUrlOptions" stepKey="addStoreCodeToUrls"/> - <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="addnewWebsite"/> - <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="addNewStoreGroup"/> + <!--Create new website --> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createAdditionalWebsite"> + <argument name="newWebsiteName" value="Second Website"/> + <argument name="websiteCode" value="second_website"/> + </actionGroup> + <!--Create new Store Group --> + <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"/> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml new file mode 100644 index 0000000000000..762b701b261ab --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml @@ -0,0 +1,31 @@ +<?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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateNewPageWithAllValues"> + <arguments> + <argument name="PageTitle" type="string"/> + <argument name="ContentHeading" type="string"/> + <argument name="URLKey" type="string"/> + <argument name="selectStoreViewOpt" type="string"/> + <argument name="selectHierarchyOpt" type="string"/> + </arguments> + <amOnPage url="{{CmsNewPagePage.url}}" stepKey="amOnCMSNewPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <fillField selector="{{CmsNewPagePageBasicFieldsSection.pageTitle}}" userInput="{{PageTitle}}" stepKey="fillFieldTitle"/> + <click selector="{{CmsNewPagePageContentSection.header}}" stepKey="clickExpandContent"/> + <fillField selector="{{CmsNewPagePageContentSection.contentHeading}}" userInput="{{ContentHeading}}" stepKey="fillFieldContentHeading"/> + <click selector="{{CmsNewPagePageSeoSection.header}}" stepKey="clickExpandSearchEngineOptimization"/> + <fillField selector="{{CmsNewPagePageSeoSection.urlKey}}" userInput="{{URLKey}}" stepKey="fillFieldURLKey"/> + <click selector="{{CmsNewPagePiwSection.header}}" stepKey="clickPageInWebsites"/> + <waitForElementVisible selector="{{CmsNewPagePiwSection.selectStoreView(selectStoreViewOpt)}}" stepKey="waitForStoreGridReload"/> + <clickWithLeftButton selector="{{CmsNewPagePiwSection.selectStoreView(selectStoreViewOpt)}}" stepKey="clickStoreView2"/> + <click selector="{{CmsNewPageHierarchySection.header}}" stepKey="clickHierarchy"/> + <click selector="{{CmsNewPageHierarchySection.selectHierarchy(selectHierarchyOpt)}}" stepKey="clickPageCheckBoxes"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml new file mode 100644 index 0000000000000..eef6c6f0ca6ed --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeletePageByUrlKeyActionGroup"> + <arguments> + <argument name="UrlKey" type="string"/> + </arguments> + <amOnPage url="{{CmsPagesPage.url}}" stepKey="amOnCMSNewPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <click selector="{{CmsPagesPageActionsSection.select(UrlKey)}}" stepKey="clickSelect"/> + <click selector="{{CmsPagesPageActionsSection.delete(UrlKey)}}" stepKey="clickDelete"/> + <waitForElementVisible selector="{{CmsPagesPageActionsSection.deleteConfirm}}" stepKey="waitForOkButtonToBeVisible"/> + <click selector="{{CmsPagesPageActionsSection.deleteConfirm}}" stepKey="clickOkButton"/> + <waitForPageLoad stepKey="waitForPageLoad3"/> + <see userInput="The page has been deleted." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPageHierarchySection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPageHierarchySection.xml new file mode 100644 index 0000000000000..96ebd34360d33 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPageHierarchySection.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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="CmsNewPageHierarchySection"> + <element name="header" type="button" selector="div[data-index=hierarchy]" timeout="30"/> + <element name="selectHierarchy" type="button" selector="//a/span[contains(text(),'{{var1}}')]" parameterized="true" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageActionsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageActionsSection.xml index 3b9d2ddd9fcf2..f60fced7d05d4 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageActionsSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageActionsSection.xml @@ -10,5 +10,6 @@ xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="CmsNewPagePageActionsSection"> <element name="savePage" type="button" selector="#save" timeout="30"/> + <element name="saveAndContinueEdit" type="button" selector="#save_and_continue" timeout="10"/> </section> </sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePiwSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePiwSection.xml new file mode 100644 index 0000000000000..35d7b9942de67 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePiwSection.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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="CmsNewPagePiwSection"> + <element name="header" type="button" selector="div[data-index=websites]" timeout="30"/> + <element name="selectStoreView" type="select" selector="//option[contains(text(),'{{var1}}')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsPagesPageActionsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsPagesPageActionsSection.xml index b3946b74439a1..63069c2ccf264 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsPagesPageActionsSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsPagesPageActionsSection.xml @@ -13,5 +13,7 @@ <element name="select" type="button" selector="//div[text()='{{var1}}']/parent::td//following-sibling::td[@class='data-grid-actions-cell']//button[text()='Select']" parameterized="true"/> <element name="edit" type="button" selector="//div[text()='{{var1}}']/parent::td//following-sibling::td[@class='data-grid-actions-cell']//a[text()='Edit']" parameterized="true"/> <element name="preview" type="button" selector="//div[text()='{{var1}}']/parent::td//following-sibling::td[@class='data-grid-actions-cell']//a[text()='Preview']" parameterized="true"/> + <element name="delete" type="button" selector="//div[text()='{{var1}}']/parent::td//following-sibling::td[@class='data-grid-actions-cell']//a[text()='Delete']" parameterized="true"/> + <element name="deleteConfirm" type="button" selector=".action-primary.action-accept" timeout="60"/> </section> </sections> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateNewStoreGroupActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateNewStoreGroupActionGroup.xml index b57a61a92a0d3..f241531f61ffd 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateNewStoreGroupActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateNewStoreGroupActionGroup.xml @@ -9,12 +9,17 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminCreateNewStoreGroupActionGroup"> + <arguments> + <argument name="website" type="string"/> + <argument name="storeGroupName" type="string"/> + <argument name="storeGroupCode" type="string"/> + </arguments> <amOnPage url="{{AdminSystemStoreGroupPage.url}}" stepKey="navigateToNewStoreView"/> <waitForPageLoad stepKey="waitForPageLoad1" /> <!--Create Store group --> - <selectOption selector="{{AdminNewStoreGroupSection.storeGrpWebsiteDropdown}}" userInput="Second Website" stepKey="selectWebsite" /> - <fillField selector="{{AdminNewStoreGroupSection.storeGrpNameTextField}}" userInput="Second Store" stepKey="enterStoreGroupName" /> - <fillField selector="{{AdminNewStoreGroupSection.storeGrpCodeTextField}}" userInput="second_store" stepKey="enterStoreGroupCode" /> + <selectOption selector="{{AdminNewStoreGroupSection.storeGrpWebsiteDropdown}}" userInput="{{website}}" stepKey="selectWebsite" /> + <fillField selector="{{AdminNewStoreGroupSection.storeGrpNameTextField}}" userInput="{{storeGroupName}}" stepKey="enterStoreGroupName" /> + <fillField selector="{{AdminNewStoreGroupSection.storeGrpCodeTextField}}" userInput="{{storeGroupCode}}" stepKey="enterStoreGroupCode" /> <selectOption selector="{{AdminNewStoreGroupSection.storeRootCategoryDropdown}}" userInput="Default Category" stepKey="chooseRootCategory" /> <click selector="{{AdminStoreGroupActionsSection.saveButton}}" stepKey="clickSaveStoreGroup" /> <waitForElementVisible selector="{{AdminStoresGridSection.storeGrpFilterTextField}}" stepKey="waitForStoreGridReload"/> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml index 6fdfb2566fee9..b35b36bc667a7 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml @@ -10,13 +10,13 @@ xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminCreateStoreViewActionGroup"> <arguments> - <argument name="storeGroup" defaultValue="_defaultStoreGroup"/> + <argument name="StoreGroup" defaultValue="_defaultStoreGroup"/> <argument name="customStore" defaultValue="customStore"/> </arguments> <amOnPage url="{{AdminSystemStoreViewPage.url}}" stepKey="navigateToNewStoreView"/> <waitForPageLoad stepKey="waitForPageLoad1" /> <!--Create Store View--> - <selectOption selector="{{AdminNewStoreSection.storeGrpDropdown}}" userInput="{{storeGroup.name}}" stepKey="selectStore" /> + <selectOption selector="{{AdminNewStoreSection.storeGrpDropdown}}" userInput="{{StoreGroup.name}}" stepKey="selectStore" /> <fillField selector="{{AdminNewStoreSection.storeNameTextField}}" userInput="{{customStore.name}}" stepKey="enterStoreViewName" /> <fillField selector="{{AdminNewStoreSection.storeCodeTextField}}" userInput="{{customStore.code}}" stepKey="enterStoreViewCode" /> <selectOption selector="{{AdminNewStoreSection.statusDropdown}}" userInput="Enabled" stepKey="setStatus" /> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml index 76c99fedfa990..24c1a79a6959c 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml @@ -9,11 +9,15 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminCreateWebsiteActionGroup"> + <arguments> + <argument name="newWebsiteName" type="string"/> + <argument name="websiteCode" type="string"/> + </arguments> <amOnPage url="{{AdminSystemStoreWebsitePage.url}}" stepKey="navigateToNewWebsitePage"/> <waitForPageLoad stepKey="waitForStoresPageLoad"/> <!--Create Website--> - <fillField selector="{{AdminNewWebsiteSection.name}}" userInput="Second Website" stepKey="enterWebsiteName" /> - <fillField selector="{{AdminNewWebsiteSection.code}}" userInput="second_website" stepKey="enterWebsiteCode" /> + <fillField selector="{{AdminNewWebsiteSection.name}}" userInput="{{newWebsiteName}}" stepKey="enterWebsiteName" /> + <fillField selector="{{AdminNewWebsiteSection.code}}" userInput="{{websiteCode}}" stepKey="enterWebsiteCode" /> <click selector="{{AdminNewWebsiteActionsSection.saveWebsite}}" stepKey="clickSaveWebsite" /> <waitForElementVisible selector="{{AdminStoresGridSection.websiteFilterTextField}}" stepKey="waitForStoreGridToReload"/> <see userInput="You saved the website." stepKey="seeSavedMessage" /> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml index b199d66523115..558205bbc8071 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml @@ -8,11 +8,14 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminDeleteWebsiteActionGroup"> + <arguments> + <argument name="websiteName" type="string"/> + </arguments> <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> - <fillField userInput="Second Website" selector="{{AdminStoresGridSection.websiteFilterTextField}}" stepKey="fillSearchWebsiteField"/> + <fillField userInput="{{websiteName}}" selector="{{AdminStoresGridSection.websiteFilterTextField}}" stepKey="fillSearchWebsiteField"/> <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton"/> - <see userInput="Second Website" selector="{{AdminStoresGridSection.websiteNameInFirstRow}}" stepKey="verifyThatCorrectWebsiteFound"/> + <see userInput="{{websiteName}}" selector="{{AdminStoresGridSection.websiteNameInFirstRow}}" stepKey="verifyThatCorrectWebsiteFound"/> <click selector="{{AdminStoresGridSection.websiteNameInFirstRow}}" stepKey="clickEditExistingStoreRow"/> <waitForPageLoad stepKey="waitForStoreToLoad"/> <click selector="{{AdminStoresMainActionsSection.deleteButton}}" stepKey="clickDeleteWebsiteButtonOnEditWebsitePage"/> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml index 1c67147740b7c..56da78f44a273 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml @@ -39,4 +39,13 @@ <data key="store_type">store</data> <requiredEntity type="storeGroup">customStoreGroup</requiredEntity> </entity> + <entity name="staticStore" type="store"> + <data key="name">Second Store View</data> + <data key="code">store123</data> + <data key="is_active">1</data> + <data key="store_id">null</data> + <data key="store_action">add</data> + <data key="store_type">group</data> + <requiredEntity type="storeGroup">customStoreGroup</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml index 6ec74c540aad4..0be59627c6e3c 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml @@ -21,4 +21,10 @@ <data key="store_action">add</data> <data key="store_type">group</data> </entity> + <entity name="staticStoreGroup" type="group"> + <data key="name">NewStore</data> + <data key="code">Base12</data> + <data key="root_category_id">2</data> + <data key="website_id">1</data> + </entity> </entities> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreViewActionsSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreViewActionsSection.xml index 5f8e27f3ec101..a3b5d1e616319 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreViewActionsSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreViewActionsSection.xml @@ -10,6 +10,6 @@ <element name="backButton" type="button" selector="#back" timeout="30"/> <element name="delete" type="button" selector="#delete" timeout="30"/> <element name="resetButton" type="button" selector="#reset" timeout="30"/> - <element name="saveButton" type="button" selector="#save" timeout="30"/> + <element name="saveButton" type="button" selector="#save" timeout="60"/> </section> </sections> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminNewWebsiteActionsSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminNewWebsiteActionsSection.xml index 70a33d3a2a060..1f02de731f9a0 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminNewWebsiteActionsSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminNewWebsiteActionsSection.xml @@ -7,6 +7,6 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="AdminNewWebsiteActionsSection"> - <element name="saveWebsite" type="button" selector="#save" timeout="30"/> + <element name="saveWebsite" type="button" selector="#save" timeout="60"/> </section> </sections> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminStoreGroupActionsSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminStoreGroupActionsSection.xml index ae0283865b774..6dc766c0c02da 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminStoreGroupActionsSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminStoreGroupActionsSection.xml @@ -7,6 +7,6 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="AdminStoreGroupActionsSection"> - <element name="saveButton" type="button" selector="#save" timeout="30" /> + <element name="saveButton" type="button" selector="#save" timeout="60" /> </section> </sections> From f50853d1ffd0a20fc268fefebc5e685e30593d0e Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <dhorytskyi@magento.com> Date: Fri, 20 Jul 2018 19:02:38 +0300 Subject: [PATCH 070/211] MAGETWO-86609: Disabled wishlist product still appears in wishlist --- app/code/Magento/Wishlist/Model/Wishlist.php | 8 ++--- .../Magento/Wishlist/Controller/IndexTest.php | 1 + .../Magento/Wishlist/Model/WishlistTest.php | 36 +++++++++++++++++++ .../Wishlist/_files/wishlist_rollback.php | 24 +++++++++++++ ...t_with_product_qty_increments_rollback.php | 24 +++++++++++++ 5 files changed, 88 insertions(+), 5 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_product_qty_increments_rollback.php diff --git a/app/code/Magento/Wishlist/Model/Wishlist.php b/app/code/Magento/Wishlist/Model/Wishlist.php index 6a5a7c45383f5..ad66ec18b4557 100644 --- a/app/code/Magento/Wishlist/Model/Wishlist.php +++ b/app/code/Magento/Wishlist/Model/Wishlist.php @@ -339,11 +339,9 @@ protected function _addCatalogProduct(\Magento\Catalog\Model\Product $product, $ public function getItemCollection() { if ($this->_itemCollection === null) { - $this->_itemCollection = $this->_wishlistCollectionFactory->create()->addWishlistFilter( - $this - )->addStoreFilter( - $this->getSharedStoreIds() - )->setVisibilityFilter(); + $this->_itemCollection = $this->_wishlistCollectionFactory->create() + ->addWishlistFilter($this) + ->addStoreFilter($this->getSharedStoreIds()); } return $this->_itemCollection; diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/IndexTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/IndexTest.php index 48cc6cea49156..4999e379e1d0a 100644 --- a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/IndexTest.php @@ -122,6 +122,7 @@ public function testAddActionProductNameXss() } /** + * @magentoDbIsolation disabled * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_product_qty_increments.php */ public function testAllcartAction() diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Model/WishlistTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Model/WishlistTest.php index 99f9aa4991b5e..b684da05dd254 100644 --- a/dev/tests/integration/testsuite/Magento/Wishlist/Model/WishlistTest.php +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Model/WishlistTest.php @@ -6,6 +6,7 @@ namespace Magento\Wishlist\Model; use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status as ProductStatus; use Magento\Framework\App\ObjectManager; use Magento\Framework\DataObject; @@ -85,4 +86,39 @@ public function testAddNewItemInvalidWishlistItemConfiguration() ); $this->wishlist->addNewItem($product); } + + /** + * @magentoDbIsolation disabled + * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + */ + public function testGetItemCollection() + { + $productSku = 'simple'; + $customerId = 1; + + $this->wishlist->loadByCustomerId($customerId, true); + $itemCollection = $this->wishlist->getItemCollection(); + /** @var \Magento\Wishlist\Model\Item $item */ + $item = $itemCollection->getFirstItem(); + $this->assertEquals($productSku, $item->getProduct()->getSku()); + } + + /** + * @magentoDbIsolation disabled + * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + */ + public function testGetItemCollectionWithDisabledProduct() + { + $productSku = 'simple'; + $customerId = 1; + + $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $product = $productRepository->get($productSku); + $product->setStatus(ProductStatus::STATUS_DISABLED); + $productRepository->save($product); + + $this->wishlist->loadByCustomerId($customerId, true); + $itemCollection = $this->wishlist->getItemCollection(); + $this->assertEmpty($itemCollection->getItems()); + } } diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_rollback.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_rollback.php new file mode 100644 index 0000000000000..6b1afb6ffa509 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_rollback.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** @var \Magento\Framework\ObjectManagerInterface $objectManager */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = $objectManager->get(\Magento\Framework\Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Wishlist\Model\Wishlist $wishlist */ +$wishlist = $objectManager->create(\Magento\Wishlist\Model\Wishlist::class); +$wishlist->loadByCustomerId(1); +$wishlist->delete(); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_rollback.php'; +require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_product_qty_increments_rollback.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_product_qty_increments_rollback.php new file mode 100644 index 0000000000000..15e23f0e31b56 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_product_qty_increments_rollback.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** @var \Magento\Framework\ObjectManagerInterface $objectManager */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = $objectManager->get(\Magento\Framework\Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Wishlist\Model\Wishlist $wishlist */ +$wishlist = $objectManager->create(\Magento\Wishlist\Model\Wishlist::class); +$wishlist->loadByCustomerId(1); +$wishlist->delete(); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/../../../Magento/Catalog/_files/product_special_price_rollback.php'; +require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; From 3d072935fe5192436ac73d5c52d808c601ae6519 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Mon, 23 Jul 2018 09:12:42 +0300 Subject: [PATCH 071/211] MAGETWO-77742: [2.2.x] - Error message when uploading unsupported file format --- .../Test/Mftf/Section/StorefrontProductPageSection.xml | 2 +- ...torefrontConfigurableProductWithFileCustomOptionTest.xml | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml index bd6522f5abbcd..165e7eb8a8526 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="StorefrontProductPageSection"> <element name="QtyInput" type="button" selector="input.input-text.qty"/> - <element name="addToCartBtn" type="button" selector="button.action.tocart.primary" timeout="30"/> + <element name="addToCartBtn" type="button" selector="button.action.tocart.primary"/> <element name="successMsg" type="button" selector="div.message-success"/> <element name="AddToWishlist" type="button" selector="//a[@class='action towishlist']" timeout="30"/> <element name="addToCartButtonTitleIsAdding" type="text" selector="//button/span[text()='Adding...']"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml index 6f37c7511dce2..d3b654542eccb 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml @@ -60,6 +60,7 @@ <!--Try invalid file--> <attachFile selector="{{StorefrontProductInfoMainSection.addLinkFileUploadFile(ProductOptionFile.title)}}" userInput="lorem_ipsum.docx" stepKey="attachInvalidFile"/> <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="addToCartInvalidFile"/> + <waitForPageLoad time="30" stepKey="waitForAddToCartWithError"/> <waitForElementVisible selector="{{StorefrontProductPageSection.alertMessage}}" stepKey="waitForErrorMessageInvalidFile"/> <see selector="{{StorefrontProductPageSection.messagesBlock}}" userInput="The file 'lorem_ipsum.docx' for '{{ProductOptionFile.title}}' has an invalid extension." stepKey="seeMessageInvalidFile"/> <!--Option remains selected--> @@ -67,8 +68,9 @@ <!--Try valid file--> <attachFile selector="{{StorefrontProductInfoMainSection.addLinkFileUploadFile(ProductOptionFile.title)}}" userInput="{{MagentoLogo.file}}" stepKey="attachValidFile"/> <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$11.99" stepKey="seePriceUpdated"/> - <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="addToCartValidFile"/> - <waitForElementVisible selector="{{StorefrontProductPageSection.successMsg}}" stepKey="waitForSuccessMessage"/> + <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> + <argument name="productName" value="BaseConfigurableProduct.name"/> + </actionGroup> <see selector="{{StorefrontProductPageSection.messagesBlock}}" userInput="You added {{BaseConfigurableProduct.name}} to your shopping cart." stepKey="seeSuccessMessage"/> <!--Check item in cart--> From 2c8234649e23d8fc545c6aa9ff498bf06a62021d Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Mon, 23 Jul 2018 10:36:52 +0300 Subject: [PATCH 072/211] MAGETWO-93105: "Thank you" text is broken on storefront with enabled translate-inline --- .../Theme/Controller/Result/MessagePlugin.php | 36 ++++++- .../Controller/Result/MessagePluginTest.php | 97 ++++++++++++++++++- 2 files changed, 130 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Theme/Controller/Result/MessagePlugin.php b/app/code/Magento/Theme/Controller/Result/MessagePlugin.php index 145493b8e44d8..0b1a1b73d5826 100644 --- a/app/code/Magento/Theme/Controller/Result/MessagePlugin.php +++ b/app/code/Magento/Theme/Controller/Result/MessagePlugin.php @@ -5,9 +5,12 @@ */ namespace Magento\Theme\Controller\Result; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Controller\Result\Json; use Magento\Framework\Controller\ResultInterface; use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Translate\Inline\ParserInterface; +use Magento\Framework\Translate\InlineInterface; /** * Plugin for putting messages to cookies @@ -44,26 +47,34 @@ class MessagePlugin */ private $serializer; + /** + * @var InlineInterface + */ + private $inlineTranslate; + /** * @param \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager * @param \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory * @param \Magento\Framework\Message\ManagerInterface $messageManager * @param \Magento\Framework\View\Element\Message\InterpretationStrategyInterface $interpretationStrategy * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer + * @param InlineInterface $inlineTranslate */ public function __construct( \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager, \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory, \Magento\Framework\Message\ManagerInterface $messageManager, \Magento\Framework\View\Element\Message\InterpretationStrategyInterface $interpretationStrategy, - \Magento\Framework\Serialize\Serializer\Json $serializer = null + \Magento\Framework\Serialize\Serializer\Json $serializer = null, + InlineInterface $inlineTranslate = null ) { $this->cookieManager = $cookieManager; $this->cookieMetadataFactory = $cookieMetadataFactory; $this->messageManager = $messageManager; - $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() + $this->serializer = $serializer ?: ObjectManager::getInstance() ->get(\Magento\Framework\Serialize\Serializer\Json::class); $this->interpretationStrategy = $interpretationStrategy; + $this->inlineTranslate = $inlineTranslate ?: ObjectManager::getInstance()->get(InlineInterface::class); } /** @@ -112,6 +123,12 @@ public function afterRenderResult( private function setCookie(array $messages) { if (!empty($messages)) { + if ($this->inlineTranslate->isAllowed()) { + foreach ($messages as &$message) { + $message['text'] = $this->convertMessageText($message['text']); + } + } + $publicCookieMetadata = $this->cookieMetadataFactory->createPublicCookieMetadata(); $publicCookieMetadata->setDurationOneYear(); $publicCookieMetadata->setPath('/'); @@ -125,6 +142,21 @@ private function setCookie(array $messages) } } + /** + * Replace wrapping translation with html body. + * + * @param string $text + * @return string + */ + private function convertMessageText(string $text): string + { + if (preg_match('#' . ParserInterface::REGEXP_TOKEN . '#', $text, $matches)) { + $text = $matches[1]; + } + + return $text; + } + /** * Return messages array and clean message manager messages * diff --git a/app/code/Magento/Theme/Test/Unit/Controller/Result/MessagePluginTest.php b/app/code/Magento/Theme/Test/Unit/Controller/Result/MessagePluginTest.php index 60457fc1436c0..389e404ed129f 100644 --- a/app/code/Magento/Theme/Test/Unit/Controller/Result/MessagePluginTest.php +++ b/app/code/Magento/Theme/Test/Unit/Controller/Result/MessagePluginTest.php @@ -14,6 +14,7 @@ use Magento\Framework\Stdlib\Cookie\CookieMetadataFactory; use Magento\Framework\Stdlib\Cookie\PublicCookieMetadata; use Magento\Framework\Stdlib\CookieManagerInterface; +use Magento\Framework\Translate\InlineInterface; use Magento\Framework\View\Element\Message\InterpretationStrategyInterface; use Magento\Theme\Controller\Result\MessagePlugin; @@ -40,6 +41,9 @@ class MessagePluginTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Framework\Serialize\Serializer\Json|\PHPUnit_Framework_MockObject_MockObject */ private $serializerMock; + /** @var InlineInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $inlineTranslateMock; + protected function setUp() { $this->cookieManagerMock = $this->getMockBuilder(CookieManagerInterface::class) @@ -53,13 +57,15 @@ protected function setUp() ->getMockForAbstractClass(); $this->serializerMock = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Json::class) ->getMock(); + $this->inlineTranslateMock = $this->getMockBuilder(InlineInterface::class)->getMockForAbstractClass(); $this->model = new MessagePlugin( $this->cookieManagerMock, $this->cookieMetadataFactoryMock, $this->managerMock, $this->interpretationStrategyMock, - $this->serializerMock + $this->serializerMock, + $this->inlineTranslateMock ); } @@ -450,4 +456,93 @@ function ($data) { $this->assertEquals($resultMock, $this->model->afterRenderResult($resultMock, $resultMock)); } + + /** + * @return void + */ + public function testAfterRenderResultWithAllowedInlineTranslate() + { + $messageType = 'message1type'; + $messageText = '{{{message1text}}{{message1text}}{{message1text}}{{theme/luma}}}'; + $expectedMessages = [ + [ + 'type' => $messageType, + 'text' => 'message1text', + ], + ]; + + /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $resultMock */ + $resultMock = $this->getMockBuilder(Redirect::class) + ->disableOriginalConstructor() + ->getMock(); + + /** @var PublicCookieMetadata|\PHPUnit_Framework_MockObject_MockObject $cookieMetadataMock */ + $cookieMetadataMock = $this->getMockBuilder(PublicCookieMetadata::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->cookieMetadataFactoryMock->expects($this->once()) + ->method('createPublicCookieMetadata') + ->willReturn($cookieMetadataMock); + + $this->cookieManagerMock->expects($this->once()) + ->method('setPublicCookie') + ->with( + MessagePlugin::MESSAGES_COOKIES_NAME, + json_encode($expectedMessages), + $cookieMetadataMock + ); + $this->cookieManagerMock->expects($this->once()) + ->method('getCookie') + ->with( + MessagePlugin::MESSAGES_COOKIES_NAME + ) + ->willReturn(json_encode([])); + + $this->serializerMock->expects($this->once()) + ->method('unserialize') + ->willReturnCallback( + function ($data) { + return json_decode($data, true); + } + ); + $this->serializerMock->expects($this->once()) + ->method('serialize') + ->willReturnCallback( + function ($data) { + return json_encode($data); + } + ); + + /** @var MessageInterface|\PHPUnit_Framework_MockObject_MockObject $messageMock */ + $messageMock = $this->getMockBuilder(MessageInterface::class) + ->getMock(); + $messageMock->expects($this->once()) + ->method('getType') + ->willReturn($messageType); + + $this->interpretationStrategyMock->expects($this->once()) + ->method('interpret') + ->with($messageMock) + ->willReturn($messageText); + + $this->inlineTranslateMock->expects($this->once()) + ->method('isAllowed') + ->willReturn(true); + + /** @var Collection|\PHPUnit_Framework_MockObject_MockObject $collectionMock */ + $collectionMock = $this->getMockBuilder(Collection::class) + ->disableOriginalConstructor() + ->getMock(); + $collectionMock->expects($this->once()) + ->method('getItems') + ->willReturn([$messageMock]); + + $this->managerMock->expects($this->once()) + ->method('getMessages') + ->with(true, null) + ->willReturn($collectionMock); + + $this->assertEquals($resultMock, $this->model->afterRenderResult($resultMock, $resultMock)); + } } From c199ed0076b5ab65f6c7b52dcc58aba83790ac54 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Mon, 23 Jul 2018 10:57:37 +0300 Subject: [PATCH 073/211] MAGETWO-86482: Customizable options truncated when displaying ordered product in admin --- .../Catalog/Test/Mftf/Data/ProductData.xml | 4 + .../Test/Mftf/Data/ProductOptionData.xml | 11 +- .../Test/Mftf/Data/ProductOptionValueData.xml | 14 ++- ...ctWithCustomOptionsWithLongValuesTitle.xml | 82 ++++++++++++++ .../Block/Adminhtml/Items/Column/Name.php | 33 ++++-- .../Mftf/Section/AdminOrdersGridSection.xml | 16 +++ .../Mftf/Section/CheckoutPaymentSection.xml | 7 ++ .../Framework/Filter/TruncateFilterTest.php | 65 +++++++++++ .../Block/Adminhtml/Items/Column/NameTest.php | 56 +++++++++ .../Magento/Framework/Filter/Factory.php | 1 + .../Framework/Filter/FilterManager.php | 1 + .../Magento/Framework/Filter/Truncate.php | 3 + .../Framework/Filter/TruncateFilter.php | 107 ++++++++++++++++++ .../Filter/TruncateFilter/Result.php | 74 ++++++++++++ 14 files changed, 465 insertions(+), 9 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Filter/TruncateFilterTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Items/Column/NameTest.php create mode 100644 lib/internal/Magento/Framework/Filter/TruncateFilter.php create mode 100644 lib/internal/Magento/Framework/Filter/TruncateFilter/Result.php diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 1ac02a6f69294..fe7cfa25d457a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -177,4 +177,8 @@ <requiredEntity type="custom_attribute_array">ApiProductDescription</requiredEntity> <requiredEntity type="custom_attribute_array">ApiProductShortDescription</requiredEntity> </entity> + <entity name="productWithOptions2" type="product"> + <var key="sku" entityType="product" entityKey="sku" /> + <requiredEntity type="product_option">ProductOptionDropDownWithLongValuesTitle</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionData.xml index 1028988146c86..ae8bcf0893ed0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionData.xml @@ -48,6 +48,15 @@ <requiredEntity type="product_option_value">ProductOptionValueDropdown1</requiredEntity> <requiredEntity type="product_option_value">ProductOptionValueDropdown2</requiredEntity> </entity> + <entity name="ProductOptionDropDownWithLongValuesTitle" type="product_option"> + <var key="product_sku" entityType="product" entityKey="sku" /> + <data key="title">OptionDropDownWithLongTitles</data> + <data key="type">drop_down</data> + <data key="sort_order">4</data> + <data key="is_require">true</data> + <requiredEntity type="product_option_value">ProductOptionValueDropdownLongTitle1</requiredEntity> + <requiredEntity type="product_option_value">ProductOptionValueDropdownLongTitle2</requiredEntity> + </entity> <entity name="ProductOptionRadiobutton" type="product_option"> <var key="product_sku" entityType="product" entityKey="sku" /> <data key="title">OptionRadioButtons</data> @@ -101,4 +110,4 @@ <data key="price">0.00</data> <data key="price_type">percent</data> </entity> -</entities> \ No newline at end of file +</entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionValueData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionValueData.xml index 7ad5428794fb5..82063a771d2a3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionValueData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionValueData.xml @@ -49,4 +49,16 @@ <data key="price">2</data> <data key="price_type">fixed</data> </entity> -</entities> \ No newline at end of file + <entity name="ProductOptionValueDropdownLongTitle1" type="product_option_value"> + <data key="title">Optisfvdklvfnkljvnfdklpvnfdjklfdvnjkvfdkjnvfdjkfvndj11111Optisfvdklvfnkljvnfdklpvnfdjklfdvnjkvfdkjnvfdjkfvndj11111</data> + <data key="sort_order">1</data> + <data key="price">10</data> + <data key="price_type">fixed</data> + </entity> + <entity name="ProductOptionValueDropdownLongTitle2" type="product_option_value"> + <data key="title">Optisfvdklvfnkljvnfdklpvnfdjklfdvnjkvfdkjnvfdjkfvndj22222Optisfvdklvfnkljvnfdklpvnfdjklfdvnjkvfdkjnvfdjkfvndj22222</data> + <data key="sort_order">2</data> + <data key="price">20</data> + <data key="price_type">percent</data> + </entity> +</entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml new file mode 100644 index 0000000000000..52e78378d97e1 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml @@ -0,0 +1,82 @@ +<?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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle"> + <annotations> + <group value="Catalog"/> + <title value="Admin should be able to see the full title of the selected custom option value in the order"/> + <description value="Admin should be able to see the full title of the selected custom option value in the order"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-93340"/> + <group value="product"/> + </annotations> + <before> + <!--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="productWithOptions2" stepKey="updateProductWithOptions"/> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + </before> + <after> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + <!-- Login Customer Storefront --> + <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"/> + <!-- Checking the correctness of displayed prices for user parameters --> + <amOnPage url="{{StorefrontHomePage.url}}$$createProduct.custom_attributes[url_key]$$.html" stepKey="amOnProductPage"/> + <seeElement selector="{{StorefrontProductInfoMainSection.productAttributeOptionsDropDown(ProductOptionDropDownWithLongValuesTitle.title, ProductOptionValueDropdownLongTitle1.price)}}" stepKey="checkDropDownProductOption"/> + <!-- Adding items to the checkout --> + <selectOption userInput="{{ProductOptionValueDropdownLongTitle1.price}}" selector="{{StorefrontProductInfoMainSection.productOptionSelect(ProductOptionDropDownWithLongValuesTitle.title)}}" stepKey="seeProductOptionDropDown"/> + <grabTextFrom selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="finalProductPrice"/> + <actionGroup ref="StorefrontAddToCartCustomOptionsProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> + <argument name="productName" value="$$createProduct.name$$"/> + </actionGroup> + <!-- Checking the correctness of displayed custom options for user parameters on checkout --> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart" /> + <conditionalClick selector="{{CheckoutPaymentSection.cartItemsArea}}" dependentSelector="{{CheckoutPaymentSection.cartItemsArea}}" visible="true" stepKey="exposeMiniCart"/> + <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="{{ProductOptionValueDropdownLongTitle1.title}}" stepKey="seeProductOptionValueDropdown1Input1"/> + <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> + <!-- Place Order --> + <waitForElement selector="{{CheckoutPaymentSection.checkMoneyOrderPayment}}" time="30" stepKey="waitCheckMoneyOrderPayment"/> + <click selector="{{CheckoutPaymentSection.checkMoneyOrderPayment}}" stepKey="clickCheckMoneyOrderPayment"/> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> + <!-- Login to Admin and open Order --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnOrdersPage"/> + <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="fillOrderNum"/> + <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchOrderNum"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnSearch"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> + <!-- Checking the correctness of displayed custom options for user parameters on Order --> + <dontSee selector="{{AdminOrderItemsOrderedSection.productNameOptions}}" userInput="{{ProductOptionValueDropdownLongTitle1.title}}" stepKey="dontSeeAdminOrderProductOptionValueDropdown1"/> + <grabTextFrom selector="{{AdminOrderItemsOrderedSection.productNameOptions}} dd" stepKey="productOptionValueText"/> + <assertEquals stepKey="checkProductOptionValue"> + <actualResult type="variable">productOptionValueText</actualResult> + <expectedResult type="string">Optisfvdklvfnkljvnfdklpvnfdjklfdvnjkvfdkjnvfdjkfvndj111 ...</expectedResult> + </assertEquals> + <moveMouseOver selector="{{AdminOrderItemsOrderedSection.productNameOptions}} dd" stepKey="hoverProduct"/> + <waitForElementVisible selector="{{AdminOrderItemsOrderedSection.productNameOptions}} dd:nth-child(2)" stepKey="waitForCustomOptionValueFullName"/> + <see selector="{{AdminOrderItemsOrderedSection.productNameOptions}}" userInput="{{ProductOptionValueDropdownLongTitle1.title}}" stepKey="seeAdminOrderProductOptionValueDropdown1"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Block/Adminhtml/Items/Column/Name.php b/app/code/Magento/Sales/Block/Adminhtml/Items/Column/Name.php index 1ed9d59d29a72..2175d83c67d07 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Items/Column/Name.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Items/Column/Name.php @@ -3,8 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sales\Block\Adminhtml\Items\Column; +use Magento\Framework\Filter\TruncateFilter\Result; + /** * Sales Order items name column renderer * @@ -13,6 +17,11 @@ */ class Name extends \Magento\Sales\Block\Adminhtml\Items\Column\DefaultColumn { + /** + * @var Result + */ + private $truncateResult = null; + /** * Truncate string * @@ -22,13 +31,20 @@ class Name extends \Magento\Sales\Block\Adminhtml\Items\Column\DefaultColumn * @param string &$remainder * @param bool $breakWords * @return string + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function truncateString($value, $length = 80, $etc = '...', &$remainder = '', $breakWords = true) - { - return $this->filterManager->truncate( + public function truncateString( + string $value, + int $length = 80, + string $etc = '...', + string &$remainder = '', + bool $breakWords = true + ): string { + $this->truncateResult = $this->filterManager->truncateFilter( $value, - ['length' => $length, 'etc' => $etc, 'remainder' => $remainder, 'breakWords' => $breakWords] + ['length' => $length, 'etc' => $etc, 'breakWords' => $breakWords] ); + return $this->truncateResult->getValue(); } /** @@ -37,11 +53,14 @@ public function truncateString($value, $length = 80, $etc = '...', &$remainder = * @param string $value * @return array */ - public function getFormattedOption($value) + public function getFormattedOption(string $value): array { $remainder = ''; - $value = $this->truncateString($value, 55, '', $remainder); - $result = ['value' => nl2br($value), 'remainder' => nl2br($remainder)]; + $this->truncateString($value, 55, '', $remainder); + $result = [ + 'value' => nl2br($this->truncateResult->getValue()), + 'remainder' => nl2br($this->truncateResult->getRemainder()) + ]; return $result; } diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml new file mode 100644 index 0000000000000..082eba016f898 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminOrdersGridSection"> + <element name="search" type="input" selector="#fulltext"/> + <element name="submitSearch" type="button" selector=".//*[@id='container']/div/div[2]/div[1]/div[2]/button"/> + <element name="firstRow" type="button" selector="tr.data-row:nth-of-type(1)"/> + </section> +</sections> diff --git a/app/code/Magento/Tax/Test/Mftf/Section/CheckoutPaymentSection.xml b/app/code/Magento/Tax/Test/Mftf/Section/CheckoutPaymentSection.xml index 8250fd5e8e39d..a92ea14d604c5 100644 --- a/app/code/Magento/Tax/Test/Mftf/Section/CheckoutPaymentSection.xml +++ b/app/code/Magento/Tax/Test/Mftf/Section/CheckoutPaymentSection.xml @@ -14,5 +14,12 @@ <element name="orderTotalInclTax" type="text" selector="#opc-sidebar>div.opc-block-summary>table>tbody>tr.grand.totals.incl>td>strong>span"/> <element name="taxRateTab" type="select" selector="table.data.table.table-totals tbody tr.totals-tax-summary" /> <element name="taxRate" type="text" selector="#opc-sidebar>div.opc-block-summary>table>tbody>tr.totals-tax-details.shown>th"/> + <element name="cartItems" type="text" selector="ol.minicart-items"/> + <element name="cartItemsArea" type="button" selector="div.block.items-in-cart"/> + <element name="cartItemsAreaActive" type="textarea" selector="div.block.items-in-cart.active"/> + <element name="ProductOptionsByProductItemName" type="text" selector="//div[@class='product-item-details']//strong[@class='product-item-name'][text()='{{var1}}']//ancestor::div[@class='product-item-details']//div[@class='product options']" parameterized="true" /> + <element name="ProductOptionsActiveByProductItemName" type="text" selector="//div[@class='product-item-details']//strong[@class='product-item-name'][text()='{{var1}}']//ancestor::div[@class='product-item-details']//div[@class='product options active']" parameterized="true" /> + <element name="placeOrder" type="button" selector="button.action.primary.checkout" timeout="30"/> + <element name="checkMoneyOrderPayment" type="radio" selector="input#checkmo.radio" timeout="30"/> </section> </sections> diff --git a/dev/tests/integration/testsuite/Magento/Framework/Filter/TruncateFilterTest.php b/dev/tests/integration/testsuite/Magento/Framework/Filter/TruncateFilterTest.php new file mode 100644 index 0000000000000..f8bbd66aac123 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Filter/TruncateFilterTest.php @@ -0,0 +1,65 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Filter; + +class TruncateFilterTest extends \PHPUnit\Framework\TestCase +{ + /** + * @dataProvider truncateDataProvider + * @param string $expectedValue + * @param string $expectedRemainder + * @param string $string + * @param int $length + * @param string $etc + * @param bool $breakWords + * @return void + */ + public function testFilter( + string $expectedValue, + string $expectedRemainder, + string $string, + int $length = 5, + string $etc = '...', + bool $breakWords = true + ) { + /** @var TruncateFilter $truncateFilter */ + $truncateFilter = \Magento\TestFramework\ObjectManager::getInstance()->create( + TruncateFilter::class, + [ + 'length' => $length, + 'etc' => $etc, + 'breakWords' => $breakWords, + ] + ); + $result = $truncateFilter->filter($string); + $this->assertEquals($expectedValue, $result->getValue()); + $this->assertEquals($expectedRemainder, $result->getRemainder()); + } + + /** + * @return array + */ + public function truncateDataProvider() : array + { + return [ + '1' => [ + '12...', + '34567890', + '1234567890', + ], + '2' => [ + '123..', + ' 456 789', + '123 456 789', + 8, + '..', + false + ] + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Items/Column/NameTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Items/Column/NameTest.php new file mode 100644 index 0000000000000..5be1e7bbce0d7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Items/Column/NameTest.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Block\Adminhtml\Items\Column; + +/** + * @magentoAppArea adminhtml + */ +class NameTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var Name + */ + private $block; + + protected function setUp() + { + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + /** @var $layout \Magento\Framework\View\Layout */ + $layout = $objectManager->create(\Magento\Framework\View\LayoutInterface::class); + /** @var $block \Magento\Sales\Block\Adminhtml\Items\AbstractItems */ + $this->block = $layout->createBlock(Name::class, 'block'); + } + + /** + * @return void + */ + public function testTruncateString() + { + $remainder = ''; + $this->assertEquals( + '12345', + $this->block->truncateString('1234567890', 5, '', $remainder) + ); + } + + /** + * @return void + */ + public function testGetFormattedOption() + { + $this->assertEquals( + [ + 'value' => '1234567890123456789012345678901234567890123456789012345', + 'remainder' => '67890', + ], + $this->block->getFormattedOption( + '123456789012345678901234567890123456789012345678901234567890' + ) + ); + } +} diff --git a/lib/internal/Magento/Framework/Filter/Factory.php b/lib/internal/Magento/Framework/Filter/Factory.php index a5e5978a83f06..dbe037ecbbbd1 100644 --- a/lib/internal/Magento/Framework/Filter/Factory.php +++ b/lib/internal/Magento/Framework/Filter/Factory.php @@ -32,6 +32,7 @@ class Factory extends AbstractFactory 'decrypt' => \Magento\Framework\Filter\Decrypt::class, 'translit' => \Magento\Framework\Filter\Translit::class, 'translitUrl' => \Magento\Framework\Filter\TranslitUrl::class, + 'truncateFilter' => \Magento\Framework\Filter\TruncateFilter::class, ]; /** diff --git a/lib/internal/Magento/Framework/Filter/FilterManager.php b/lib/internal/Magento/Framework/Filter/FilterManager.php index 52c9d017ad11c..ca5d998af833f 100644 --- a/lib/internal/Magento/Framework/Filter/FilterManager.php +++ b/lib/internal/Magento/Framework/Filter/FilterManager.php @@ -21,6 +21,7 @@ * @method string removeTags(string $value, $params = array()) * @method string stripTags(string $value, $params = array()) * @method string truncate(string $value, $params = array()) + * @method string truncateFilter(string $value, $params = array()) * @method string encrypt(string $value, $params = array()) * @method string decrypt(string $value, $params = array()) * @method string translit(string $value) diff --git a/lib/internal/Magento/Framework/Filter/Truncate.php b/lib/internal/Magento/Framework/Filter/Truncate.php index fd4fbe9910427..a4dd35b302705 100644 --- a/lib/internal/Magento/Framework/Filter/Truncate.php +++ b/lib/internal/Magento/Framework/Filter/Truncate.php @@ -10,6 +10,9 @@ * * Truncate a string to a certain length if necessary, appending the $etc string. * $remainder will contain the string that has been replaced with $etc. + * + * @deprecated + * @see \Magento\Framework\Filter\TruncateFilter */ class Truncate implements \Zend_Filter_Interface { diff --git a/lib/internal/Magento/Framework/Filter/TruncateFilter.php b/lib/internal/Magento/Framework/Filter/TruncateFilter.php new file mode 100644 index 0000000000000..8149b4b7c7a4a --- /dev/null +++ b/lib/internal/Magento/Framework/Filter/TruncateFilter.php @@ -0,0 +1,107 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Filter; + +use Magento\Framework\Filter\TruncateFilter\Result; +use Magento\Framework\Filter\TruncateFilter\ResultFactory; + +/** + * Truncate filter + * + * Truncate a string to a certain length if necessary, appending the $etc string. + * $remainder will contain the string that has been replaced with $etc. + */ +class TruncateFilter implements \Zend_Filter_Interface +{ + /** + * @var int + */ + private $length; + + /** + * @var string + */ + private $etc; + + /** + * @var bool + */ + private $breakWords; + + /** + * @var \Magento\Framework\Stdlib\StringUtils + */ + private $stringUtils; + + /** + * @var ResultFactory + */ + private $resultFactory; + + /** + * @param \Magento\Framework\Stdlib\StringUtils $stringUtils + * @param ResultFactory $resultFactory + * @param int $length + * @param string $etc + * @param bool $breakWords + */ + public function __construct( + \Magento\Framework\Stdlib\StringUtils $stringUtils, + ResultFactory $resultFactory, + int $length = 80, + string $etc = '...', + bool $breakWords = true + ) { + $this->stringUtils = $stringUtils; + $this->resultFactory = $resultFactory; + $this->length = $length; + $this->etc = $etc; + $this->breakWords = $breakWords; + } + + /** + * Filter value + * + * @param string $string + * @return Result + */ + public function filter($string) : Result + { + /** @var Result $result */ + $result = $this->resultFactory->create(['value' => $string, 'remainder' => '']); + $length = $this->length; + if (0 == $length) { + $result->setValue(''); + return $result; + } + + $originalLength = $this->stringUtils->strlen($string); + if ($originalLength > $length) { + $length -= $this->stringUtils->strlen($this->etc); + if ($length <= 0) { + $result->setValue(''); + return $result; + } + $preparedString = $string; + $preparedLength = $length; + if (!$this->breakWords) { + $preparedString = preg_replace( + '/\s+?(\S+)?$/u', + '', + $this->stringUtils->substr($string, 0, $length + 1) + ); + $preparedLength = $this->stringUtils->strlen($preparedString); + } + $result->setRemainder($this->stringUtils->substr($string, $preparedLength, $originalLength)); + $result->setValue($this->stringUtils->substr($preparedString, 0, $length) . $this->etc); + return $result; + } + + return $result; + } +} diff --git a/lib/internal/Magento/Framework/Filter/TruncateFilter/Result.php b/lib/internal/Magento/Framework/Filter/TruncateFilter/Result.php new file mode 100644 index 0000000000000..de31c83110f8a --- /dev/null +++ b/lib/internal/Magento/Framework/Filter/TruncateFilter/Result.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Filter\TruncateFilter; + +class Result +{ + /** + * @var string + */ + private $value; + + /** + * @var string + */ + private $remainder; + + /** + * Result constructor. + * @param string $value + * @param string $remainder + */ + public function __construct(string $value, string $remainder) + { + $this->value = $value; + $this->remainder = $remainder; + } + + /** + * Set result value + * + * @param string $value + * @return void + */ + public function setValue(string $value) + { + $this->value = $value; + } + + /** + * Get value + * + * @return string + */ + public function getValue() : string + { + return $this->value; + } + + /** + * Set remainder + * + * @param string $remainder + * @return void + */ + public function setRemainder(string $remainder) + { + $this->remainder = $remainder; + } + + /** + * Get remainder + * + * @return string + */ + public function getRemainder() : string + { + return $this->remainder; + } +} From 3a463891e94ab6002853186ee3ad4e073f7eedcf Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Mon, 23 Jul 2018 11:12:32 +0300 Subject: [PATCH 074/211] MAGETWO-93105: "Thank you" text is broken on storefront with enabled translate-inline --- app/code/Magento/Theme/Controller/Result/MessagePlugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Theme/Controller/Result/MessagePlugin.php b/app/code/Magento/Theme/Controller/Result/MessagePlugin.php index 0b1a1b73d5826..83172df748a47 100644 --- a/app/code/Magento/Theme/Controller/Result/MessagePlugin.php +++ b/app/code/Magento/Theme/Controller/Result/MessagePlugin.php @@ -58,7 +58,7 @@ class MessagePlugin * @param \Magento\Framework\Message\ManagerInterface $messageManager * @param \Magento\Framework\View\Element\Message\InterpretationStrategyInterface $interpretationStrategy * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer - * @param InlineInterface $inlineTranslate + * @param InlineInterface|null $inlineTranslate */ public function __construct( \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager, From 8def03deb0249d7f86a64e8d9fdaa0872300cf04 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <dhorytskyi@magento.com> Date: Mon, 23 Jul 2018 12:54:05 +0300 Subject: [PATCH 075/211] MAGETWO-86609: Disabled wishlist product still appears in wishlist --- app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php b/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php index ff8a3a3b87cec..ac9ff2ae63d8d 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php @@ -272,9 +272,6 @@ public function testUpdateItem($itemId, $buyRequest, $param) $items->expects($this->once()) ->method('addStoreFilter') ->will($this->returnSelf()); - $items->expects($this->once()) - ->method('setVisibilityFilter') - ->will($this->returnSelf()); $items->expects($this->once()) ->method('getItemById') ->will($this->returnValue($item)); From ccf3cc9b9bab69017a5153b24c9a2edfd7162e87 Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko <iivashchenko@magento.com> Date: Mon, 23 Jul 2018 12:56:23 +0300 Subject: [PATCH 076/211] MAGETWO-93239: Gift Message lost at Checkout after merging quotes --- .../Model/Plugin/MergeQuoteItems.php | 36 +++++++++ .../Observer/SalesEventQuoteMerge.php | 5 +- .../Magento/GiftMessage/etc/frontend/di.xml | 3 + app/code/Magento/Quote/Model/Quote.php | 1 + .../Quote/Model/Quote/Item/Processor.php | 14 ++++ .../Magento/Quote/Model/QuoteTest.php | 76 ++++++++++++++++--- 6 files changed, 125 insertions(+), 10 deletions(-) create mode 100644 app/code/Magento/GiftMessage/Model/Plugin/MergeQuoteItems.php diff --git a/app/code/Magento/GiftMessage/Model/Plugin/MergeQuoteItems.php b/app/code/Magento/GiftMessage/Model/Plugin/MergeQuoteItems.php new file mode 100644 index 0000000000000..929cd58f260b6 --- /dev/null +++ b/app/code/Magento/GiftMessage/Model/Plugin/MergeQuoteItems.php @@ -0,0 +1,36 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GiftMessage\Model\Plugin; + +use Magento\Quote\Model\Quote\Item; +use Magento\Quote\Model\Quote\Item\Processor; + +class MergeQuoteItems +{ + /** + * Resolves gift message to be + * applied to merged quote items. + * + * @param Processor $subject + * @param Item $result + * @param Item $source + * @return Item + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterMerge(Processor $subject, Item $result, Item $source) + { + $giftMessageId = $source->getGiftMessageId(); + + if ($giftMessageId) { + $result->setGiftMessageId($giftMessageId); + } + + return $result; + } +} diff --git a/app/code/Magento/GiftMessage/Observer/SalesEventQuoteMerge.php b/app/code/Magento/GiftMessage/Observer/SalesEventQuoteMerge.php index 0d2280d29fed4..044a0bf91c982 100644 --- a/app/code/Magento/GiftMessage/Observer/SalesEventQuoteMerge.php +++ b/app/code/Magento/GiftMessage/Observer/SalesEventQuoteMerge.php @@ -28,7 +28,10 @@ public function execute(\Magento\Framework\Event\Observer $observer) /** @var Quote $sourceQuote */ $sourceQuote = $observer->getData('source'); - $targetQuote->setGiftMessageId($sourceQuote->getGiftMessageId()); + $giftMessageId = $sourceQuote->getGiftMessageId(); + if ($giftMessageId) { + $targetQuote->setGiftMessageId($giftMessageId); + } return $this; } diff --git a/app/code/Magento/GiftMessage/etc/frontend/di.xml b/app/code/Magento/GiftMessage/etc/frontend/di.xml index 1566c51ee9df3..a4837e0180c0b 100644 --- a/app/code/Magento/GiftMessage/etc/frontend/di.xml +++ b/app/code/Magento/GiftMessage/etc/frontend/di.xml @@ -43,4 +43,7 @@ </argument> </arguments> </type> + <type name="Magento\Quote\Model\Quote\Item\Processor"> + <plugin name="mergeQuoteItems" type="Magento\GiftMessage\Model\Plugin\MergeQuoteItems"/> + </type> </config> diff --git a/app/code/Magento/Quote/Model/Quote.php b/app/code/Magento/Quote/Model/Quote.php index dcadcde292500..8ef052bb57e74 100644 --- a/app/code/Magento/Quote/Model/Quote.php +++ b/app/code/Magento/Quote/Model/Quote.php @@ -2342,6 +2342,7 @@ public function merge(Quote $quote) foreach ($this->getAllItems() as $quoteItem) { if ($quoteItem->compare($item)) { $quoteItem->setQty($quoteItem->getQty() + $item->getQty()); + $this->itemProcessor->merge($item, $quoteItem); $found = true; break; } diff --git a/app/code/Magento/Quote/Model/Quote/Item/Processor.php b/app/code/Magento/Quote/Model/Quote/Item/Processor.php index f34591cfad143..f9dac1779ad27 100644 --- a/app/code/Magento/Quote/Model/Quote/Item/Processor.php +++ b/app/code/Magento/Quote/Model/Quote/Item/Processor.php @@ -103,6 +103,20 @@ public function prepare(Item $item, DataObject $request, Product $candidate) } } + /** + * Merge two quote items. + * + * @param Item $source + * @param Item $target + * @return Item + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function merge(Item $source, Item $target): Item + { + return $target; + } + /** * Set store_id value to quote item * diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php index bcd7fef9c3c93..802358c2c83c7 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php @@ -3,12 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Quote\Model; +use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\ProductRepository; use Magento\Customer\Api\Data\CustomerInterfaceFactory; use Magento\Framework\Exception\LocalizedException; -use Magento\Quote\Api\Data\CartInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\ObjectManager; use Magento\Framework\Api\SearchCriteriaBuilder; @@ -49,7 +51,7 @@ public function testCollectTotalsWithVirtual() $quote->load('test01', 'reserved_order_id'); $productRepository = $this->objectManager->create( - \Magento\Catalog\Api\ProductRepositoryInterface::class + ProductRepositoryInterface::class ); $product = $productRepository->get('virtual-product', false, null, true); $quote->addProduct($product); @@ -318,7 +320,7 @@ public function testAddProductUpdateItem() $productStockQty = 100; $productRepository = $this->objectManager->create( - \Magento\Catalog\Api\ProductRepositoryInterface::class + ProductRepositoryInterface::class ); $product = $productRepository->get('simple-1', false, null, true); @@ -474,7 +476,7 @@ public function testGetItemById() $quoteItem = $this->objectManager->create(\Magento\Quote\Model\Quote\Item::class); - $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); $product = $productRepository->get('simple'); $quoteItem->setProduct($product); @@ -488,21 +490,77 @@ public function testGetItemById() /** * Tests of quotes merging. * + * @param int|null $guestItemGiftMessageId + * @param int|null $customerItemGiftMessageId + * @param int|null $guestOrderGiftMessageId + * @param int|null $customerOrderGiftMessageId + * @param int|null $expectedItemGiftMessageId + * @param int|null $expectedOrderGiftMessageId + * * @magentoDataFixture Magento/Sales/_files/quote.php + * @dataProvider giftMessageDataProvider + * @throws LocalizedException */ - public function testMerge() - { - $giftMessageId = 1; + public function testMerge( + $guestItemGiftMessageId, + $customerItemGiftMessageId, + $guestOrderGiftMessageId, + $customerOrderGiftMessageId, + $expectedItemGiftMessageId, + $expectedOrderGiftMessageId + ) { + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $product = $productRepository->get('simple', false, null, true); /** @var Quote $quote */ $guestQuote = $this->getQuote('test01'); - $guestQuote->setGiftMessageId($giftMessageId); + $guestQuote->setGiftMessageId($guestOrderGiftMessageId); /** @var Quote $customerQuote */ $customerQuote = $this->objectManager->create(Quote::class); + $customerQuote->setReservedOrderId('test02') + ->setStoreId($guestQuote->getStoreId()) + ->addProduct($product); + $customerQuote->setGiftMessageId($customerOrderGiftMessageId); + + $guestItem = $guestQuote->getItemByProduct($product); + $guestItem->setGiftMessageId($guestItemGiftMessageId); + + $customerItem = $customerQuote->getItemByProduct($product); + $customerItem->setGiftMessageId($customerItemGiftMessageId); + $customerQuote->merge($guestQuote); + $mergedItemItem = $customerQuote->getItemByProduct($product); + + self::assertEquals($expectedOrderGiftMessageId, $customerQuote->getGiftMessageId()); + self::assertEquals($expectedItemGiftMessageId, $mergedItemItem->getGiftMessageId()); + } - self::assertEquals($giftMessageId, $customerQuote->getGiftMessageId()); + /** + * Provides order- and item-level gift message Id. + * + * @return array + */ + public function giftMessageDataProvider(): array + { + return [ + [ + 'guestItemId' => null, + 'customerItemId' => 1, + 'guestOrderId' => null, + 'customerOrderId' => 11, + 'expectedItemId' => 1, + 'expectedOrderId' => 11 + ], + [ + 'guestItemId' => 1, + 'customerItemId' => 2, + 'guestOrderId' => 11, + 'customerOrderId' => 22, + 'expectedItemId' => 1, + 'expectedOrderId' => 11 + ] + ]; } /** From f805efdc38cfb298b4dabb244e0784dc1f6ab259 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Mon, 23 Jul 2018 14:25:03 +0300 Subject: [PATCH 077/211] MAGETWO-86482: Customizable options truncated when displaying ordered product in admin --- ...rontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml | 3 +-- .../Magento/Tax/Test/Mftf/Section/CheckoutPaymentSection.xml | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml index 52e78378d97e1..9b2a14c89f616 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml @@ -56,8 +56,7 @@ <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="{{ProductOptionValueDropdownLongTitle1.title}}" stepKey="seeProductOptionValueDropdown1Input1"/> <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> <!-- Place Order --> - <waitForElement selector="{{CheckoutPaymentSection.checkMoneyOrderPayment}}" time="30" stepKey="waitCheckMoneyOrderPayment"/> - <click selector="{{CheckoutPaymentSection.checkMoneyOrderPayment}}" stepKey="clickCheckMoneyOrderPayment"/> + <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/> <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> <!-- Login to Admin and open Order --> diff --git a/app/code/Magento/Tax/Test/Mftf/Section/CheckoutPaymentSection.xml b/app/code/Magento/Tax/Test/Mftf/Section/CheckoutPaymentSection.xml index a92ea14d604c5..599fdc2540b2c 100644 --- a/app/code/Magento/Tax/Test/Mftf/Section/CheckoutPaymentSection.xml +++ b/app/code/Magento/Tax/Test/Mftf/Section/CheckoutPaymentSection.xml @@ -20,6 +20,5 @@ <element name="ProductOptionsByProductItemName" type="text" selector="//div[@class='product-item-details']//strong[@class='product-item-name'][text()='{{var1}}']//ancestor::div[@class='product-item-details']//div[@class='product options']" parameterized="true" /> <element name="ProductOptionsActiveByProductItemName" type="text" selector="//div[@class='product-item-details']//strong[@class='product-item-name'][text()='{{var1}}']//ancestor::div[@class='product-item-details']//div[@class='product options active']" parameterized="true" /> <element name="placeOrder" type="button" selector="button.action.primary.checkout" timeout="30"/> - <element name="checkMoneyOrderPayment" type="radio" selector="input#checkmo.radio" timeout="30"/> </section> </sections> From ced3983be9e1e4c008d48a75902584775b5970b5 Mon Sep 17 00:00:00 2001 From: Sergey Shvets <sshvets@magento.com> Date: Mon, 23 Jul 2018 15:36:02 +0300 Subject: [PATCH 078/211] MAGETWO-92036: The error icon does not appear on sections with required attributes that are empty when click on 'save' button --- app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js index 9f5818a8d1c2f..aa58549c1def3 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js @@ -174,6 +174,7 @@ define([ */ _isChildrenHasErrors: function (hasErrors, container) { var self = this; + if (hasErrors === false && container.hasOwnProperty('elems')) { hasErrors = container.elems.some('error'); if (hasErrors === false && container.hasOwnProperty('_elems')) { From dbfad00dee4159d62a1d10504289871c7b5cbbcb Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Mon, 23 Jul 2018 16:21:28 +0300 Subject: [PATCH 079/211] MAGETWO-73359: CMS Page does not save when same url key with hierarchy for Multi-store --- .../CreateNewPageWithAllValuesActionGroup.xml | 10 ++++------ ...agePiwSection.xml => AdminCmsNewPagePiwSection.xml} | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) rename app/code/Magento/Cms/Test/Mftf/Section/{CmsNewPagePiwSection.xml => AdminCmsNewPagePiwSection.xml} (93%) diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml index 762b701b261ab..2789d16d3a5d6 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml @@ -13,19 +13,17 @@ <argument name="ContentHeading" type="string"/> <argument name="URLKey" type="string"/> <argument name="selectStoreViewOpt" type="string"/> - <argument name="selectHierarchyOpt" type="string"/> </arguments> <amOnPage url="{{CmsNewPagePage.url}}" stepKey="amOnCMSNewPage"/> <waitForPageLoad stepKey="waitForPageLoad1"/> + <waitForElementVisible selector="{{CmsNewPagePageBasicFieldsSection.pageTitle}}" stepKey="waitUntilTitleAppears"/> <fillField selector="{{CmsNewPagePageBasicFieldsSection.pageTitle}}" userInput="{{PageTitle}}" stepKey="fillFieldTitle"/> <click selector="{{CmsNewPagePageContentSection.header}}" stepKey="clickExpandContent"/> <fillField selector="{{CmsNewPagePageContentSection.contentHeading}}" userInput="{{ContentHeading}}" stepKey="fillFieldContentHeading"/> <click selector="{{CmsNewPagePageSeoSection.header}}" stepKey="clickExpandSearchEngineOptimization"/> <fillField selector="{{CmsNewPagePageSeoSection.urlKey}}" userInput="{{URLKey}}" stepKey="fillFieldURLKey"/> - <click selector="{{CmsNewPagePiwSection.header}}" stepKey="clickPageInWebsites"/> - <waitForElementVisible selector="{{CmsNewPagePiwSection.selectStoreView(selectStoreViewOpt)}}" stepKey="waitForStoreGridReload"/> - <clickWithLeftButton selector="{{CmsNewPagePiwSection.selectStoreView(selectStoreViewOpt)}}" stepKey="clickStoreView2"/> - <click selector="{{CmsNewPageHierarchySection.header}}" stepKey="clickHierarchy"/> - <click selector="{{CmsNewPageHierarchySection.selectHierarchy(selectHierarchyOpt)}}" stepKey="clickPageCheckBoxes"/> + <click selector="{{AdminCmsNewPagePiwSection.header}}" stepKey="clickPageInWebsites"/> + <waitForElementVisible selector="{{AdminCmsNewPagePiwSection.selectStoreView(selectStoreViewOpt)}}" stepKey="waitForStoreGridReload"/> + <clickWithLeftButton selector="{{AdminCmsNewPagePiwSection.selectStoreView(selectStoreViewOpt)}}" stepKey="clickStoreView2"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePiwSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/AdminCmsNewPagePiwSection.xml similarity index 93% rename from app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePiwSection.xml rename to app/code/Magento/Cms/Test/Mftf/Section/AdminCmsNewPagePiwSection.xml index 35d7b9942de67..ce22bae4e2b93 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePiwSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/AdminCmsNewPagePiwSection.xml @@ -8,7 +8,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> - <section name="CmsNewPagePiwSection"> + <section name="AdminCmsNewPagePiwSection"> <element name="header" type="button" selector="div[data-index=websites]" timeout="30"/> <element name="selectStoreView" type="select" selector="//option[contains(text(),'{{var1}}')]" parameterized="true"/> </section> From fc4d1fd4434e7982d4c2d8392afc2dc2d6bc98ae Mon Sep 17 00:00:00 2001 From: Sergey Shvets <sshvets@magento.com> Date: Mon, 23 Jul 2018 17:08:04 +0300 Subject: [PATCH 080/211] MAGETWO-92036: The error icon does not appear on sections with required attributes that are empty when click on 'save' button --- .../Magento/Ui/view/base/web/js/form/components/fieldset.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js index aa58549c1def3..69137cd78f4a5 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js @@ -177,8 +177,10 @@ define([ if (hasErrors === false && container.hasOwnProperty('elems')) { hasErrors = container.elems.some('error'); + if (hasErrors === false && container.hasOwnProperty('_elems')) { container._elems.each(function (child) { + if (hasErrors === false) { hasErrors = self._isChildrenHasErrors(hasErrors, child); } From cc011c7cfc52a58d862090840ddb435e0bad7065 Mon Sep 17 00:00:00 2001 From: Sergey Shvets <sshvets@magento.com> Date: Mon, 23 Jul 2018 17:49:33 +0300 Subject: [PATCH 081/211] MAGETWO-92036: The error icon does not appear on sections with required attributes that are empty when click on 'save' button --- app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js index 69137cd78f4a5..1f29cb61b8eb9 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js @@ -187,6 +187,7 @@ define([ }); } } + return hasErrors; }, From 02a0cb791f4a99f6e0f2c4288b4fd5591663f6cc Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Mon, 23 Jul 2018 12:23:46 -0300 Subject: [PATCH 082/211] Replaced deprecated methods. --- .../Controller/Adminhtml/Notification/MarkAsRead.php | 6 +++--- .../Controller/Adminhtml/Notification/MassMarkAsRead.php | 8 ++++---- .../Controller/Adminhtml/Notification/MassRemove.php | 8 ++++---- .../Controller/Adminhtml/Notification/Remove.php | 6 +++--- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MarkAsRead.php b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MarkAsRead.php index 79f69ab5da88d..6b5e0681139cf 100644 --- a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MarkAsRead.php +++ b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MarkAsRead.php @@ -28,11 +28,11 @@ public function execute() )->markAsRead( $notificationId ); - $this->messageManager->addSuccess(__('The message has been marked as Read.')); + $this->messageManager->addSuccessMessage(__('The message has been marked as Read.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException( + $this->messageManager->addExceptionMessage( $e, __("We couldn't mark the notification as Read because of an error.") ); diff --git a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassMarkAsRead.php b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassMarkAsRead.php index 9e61b8ff4b83c..9ae4a7cdac0b9 100644 --- a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassMarkAsRead.php +++ b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassMarkAsRead.php @@ -23,7 +23,7 @@ public function execute() { $ids = $this->getRequest()->getParam('notification'); if (!is_array($ids)) { - $this->messageManager->addError(__('Please select messages.')); + $this->messageManager->addErrorMessage(__('Please select messages.')); } else { try { foreach ($ids as $id) { @@ -32,13 +32,13 @@ public function execute() $model->setIsRead(1)->save(); } } - $this->messageManager->addSuccess( + $this->messageManager->addSuccessMessage( __('A total of %1 record(s) have been marked as Read.', count($ids)) ); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException( + $this->messageManager->addExceptionMessage( $e, __("We couldn't mark the notification as Read because of an error.") ); diff --git a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassRemove.php b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassRemove.php index 94c7d955f592b..89a9e4608585f 100644 --- a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassRemove.php +++ b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassRemove.php @@ -23,7 +23,7 @@ public function execute() { $ids = $this->getRequest()->getParam('notification'); if (!is_array($ids)) { - $this->messageManager->addError(__('Please select messages.')); + $this->messageManager->addErrorMessage(__('Please select messages.')); } else { try { foreach ($ids as $id) { @@ -32,11 +32,11 @@ public function execute() $model->setIsRemove(1)->save(); } } - $this->messageManager->addSuccess(__('Total of %1 record(s) have been removed.', count($ids))); + $this->messageManager->addSuccessMessage(__('Total of %1 record(s) have been removed.', count($ids))); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __("We couldn't remove the messages because of an error.")); + $this->messageManager->addExceptionMessage($e, __("We couldn't remove the messages because of an error.")); } } $this->_redirect('adminhtml/*/'); diff --git a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/Remove.php b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/Remove.php index 17f911339cb61..5459ac82b55e1 100644 --- a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/Remove.php +++ b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/Remove.php @@ -31,11 +31,11 @@ public function execute() try { $model->setIsRemove(1)->save(); - $this->messageManager->addSuccess(__('The message has been removed.')); + $this->messageManager->addSuccessMessage(__('The message has been removed.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __("We couldn't remove the messages because of an error.")); + $this->messageManager->addExceptionMessage($e, __("We couldn't remove the messages because of an error.")); } $this->_redirect('adminhtml/*/'); From fde3d7ada7467724875e1b2cbe1e8f2e723ec598 Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Mon, 23 Jul 2018 12:30:50 -0300 Subject: [PATCH 083/211] Replaced deprecated methods. --- .../Controller/Adminhtml/Export/GetFilter.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/AdvancedPricingImportExport/Controller/Adminhtml/Export/GetFilter.php b/app/code/Magento/AdvancedPricingImportExport/Controller/Adminhtml/Export/GetFilter.php index 02413a1899cd7..818bcda1da65f 100644 --- a/app/code/Magento/AdvancedPricingImportExport/Controller/Adminhtml/Export/GetFilter.php +++ b/app/code/Magento/AdvancedPricingImportExport/Controller/Adminhtml/Export/GetFilter.php @@ -37,10 +37,10 @@ public function execute() ); return $resultLayout; } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } } else { - $this->messageManager->addError(__('Please correct the data sent.')); + $this->messageManager->addErrorMessage(__('Please correct the data sent.')); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); From 9c45a6451209868220c5e78d79ebcbb52a78ec3c Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Mon, 23 Jul 2018 12:43:16 -0300 Subject: [PATCH 084/211] Replacing deprecated methods to Magento_Backend. --- .../Backend/App/Action/Plugin/Authentication.php | 4 ++-- .../Backend/Controller/Adminhtml/Auth/Logout.php | 2 +- .../Backend/Controller/Adminhtml/Cache/CleanImages.php | 6 +++--- .../Backend/Controller/Adminhtml/Cache/CleanMedia.php | 6 +++--- .../Controller/Adminhtml/Cache/CleanStaticFiles.php | 2 +- .../Backend/Controller/Adminhtml/Cache/FlushAll.php | 2 +- .../Backend/Controller/Adminhtml/Cache/FlushSystem.php | 2 +- .../Backend/Controller/Adminhtml/Cache/MassDisable.php | 6 +++--- .../Backend/Controller/Adminhtml/Cache/MassEnable.php | 6 +++--- .../Backend/Controller/Adminhtml/Cache/MassRefresh.php | 6 +++--- .../Adminhtml/Dashboard/RefreshStatistics.php | 4 ++-- .../Controller/Adminhtml/System/Account/Save.php | 10 +++++----- .../Controller/Adminhtml/System/Design/Delete.php | 6 +++--- .../Controller/Adminhtml/System/Design/Save.php | 4 ++-- .../Backend/Controller/Adminhtml/System/Store.php | 8 ++++---- .../Controller/Adminhtml/System/Store/DeleteGroup.php | 4 ++-- .../Adminhtml/System/Store/DeleteGroupPost.php | 10 +++++----- .../Controller/Adminhtml/System/Store/DeleteStore.php | 4 ++-- .../Adminhtml/System/Store/DeleteStorePost.php | 10 +++++----- .../Adminhtml/System/Store/DeleteWebsite.php | 4 ++-- .../Adminhtml/System/Store/DeleteWebsitePost.php | 10 +++++----- .../Controller/Adminhtml/System/Store/EditStore.php | 4 ++-- .../Backend/Controller/Adminhtml/System/Store/Save.php | 10 +++++----- .../Unit/Controller/Adminhtml/Cache/CleanMediaTest.php | 4 ++-- .../Adminhtml/Cache/CleanStaticFilesTest.php | 2 +- .../Controller/Adminhtml/Cache/MassDisableTest.php | 6 +++--- .../Unit/Controller/Adminhtml/Cache/MassEnableTest.php | 6 +++--- .../Adminhtml/Dashboard/RefreshStatisticsTest.php | 2 +- .../Controller/Adminhtml/System/Account/SaveTest.php | 4 ++-- 29 files changed, 77 insertions(+), 77 deletions(-) diff --git a/app/code/Magento/Backend/App/Action/Plugin/Authentication.php b/app/code/Magento/Backend/App/Action/Plugin/Authentication.php index 68506a521c1cf..4b25e9921e404 100644 --- a/app/code/Magento/Backend/App/Action/Plugin/Authentication.php +++ b/app/code/Magento/Backend/App/Action/Plugin/Authentication.php @@ -160,7 +160,7 @@ protected function _processNotLoggedInUser(\Magento\Framework\App\RequestInterfa } else { $this->_actionFlag->set('', \Magento\Framework\App\ActionInterface::FLAG_NO_DISPATCH, true); $this->_response->setRedirect($this->_url->getCurrentUrl()); - $this->messageManager->addError(__('Invalid Form Key. Please refresh the page.')); + $this->messageManager->addErrorMessage(__('Invalid Form Key. Please refresh the page.')); $isRedirectNeeded = true; } } @@ -205,7 +205,7 @@ protected function _performLogin(\Magento\Framework\App\RequestInterface $reques $this->_auth->login($username, $password); } catch (AuthenticationException $e) { if (!$request->getParam('messageSent')) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $request->setParam('messageSent', true); $outputValue = false; } diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Auth/Logout.php b/app/code/Magento/Backend/Controller/Adminhtml/Auth/Logout.php index 41e32c929287a..e55c449a0e5bb 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Auth/Logout.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Auth/Logout.php @@ -16,7 +16,7 @@ class Logout extends \Magento\Backend\Controller\Adminhtml\Auth public function execute() { $this->_auth->logout(); - $this->messageManager->addSuccess(__('You have logged out.')); + $this->messageManager->addSuccessMessage(__('You have logged out.')); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanImages.php b/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanImages.php index 7a926b1c09c3e..888ce11313b8a 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanImages.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanImages.php @@ -28,11 +28,11 @@ public function execute() try { $this->_objectManager->create(\Magento\Catalog\Model\Product\Image::class)->clearCache(); $this->_eventManager->dispatch('clean_catalog_images_cache_after'); - $this->messageManager->addSuccess(__('The image cache was cleaned.')); + $this->messageManager->addSuccessMessage(__('The image cache was cleaned.')); } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('An error occurred while clearing the image cache.')); + $this->messageManager->addExceptionMessage($e, __('An error occurred while clearing the image cache.')); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanMedia.php b/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanMedia.php index 72f23ab65cf8a..30aeb038ff9c2 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanMedia.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanMedia.php @@ -28,11 +28,11 @@ public function execute() try { $this->_objectManager->get(\Magento\Framework\View\Asset\MergeService::class)->cleanMergedJsCss(); $this->_eventManager->dispatch('clean_media_cache_after'); - $this->messageManager->addSuccess(__('The JavaScript/CSS cache has been cleaned.')); + $this->messageManager->addSuccessMessage(__('The JavaScript/CSS cache has been cleaned.')); } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('An error occurred while clearing the JavaScript/CSS cache.')); + $this->messageManager->addExceptionMessage($e, __('An error occurred while clearing the JavaScript/CSS cache.')); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanStaticFiles.php b/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanStaticFiles.php index 27ae2fc31e150..489eb5799a5e7 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanStaticFiles.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanStaticFiles.php @@ -26,7 +26,7 @@ public function execute() { $this->_objectManager->get(\Magento\Framework\App\State\CleanupFiles::class)->clearMaterializedViewFiles(); $this->_eventManager->dispatch('clean_static_files_cache_after'); - $this->messageManager->addSuccess(__('The static files cache has been cleaned.')); + $this->messageManager->addSuccessMessage(__('The static files cache has been cleaned.')); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Cache/FlushAll.php b/app/code/Magento/Backend/Controller/Adminhtml/Cache/FlushAll.php index ca89ea58fa6f3..a2f18b4baf53d 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Cache/FlushAll.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Cache/FlushAll.php @@ -27,7 +27,7 @@ public function execute() foreach ($this->_cacheFrontendPool as $cacheFrontend) { $cacheFrontend->getBackend()->clean(); } - $this->messageManager->addSuccess(__("You flushed the cache storage.")); + $this->messageManager->addSuccessMessage(__("You flushed the cache storage.")); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); return $resultRedirect->setPath('adminhtml/*'); diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Cache/FlushSystem.php b/app/code/Magento/Backend/Controller/Adminhtml/Cache/FlushSystem.php index f0fed159e0f22..90ed3432fa87b 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Cache/FlushSystem.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Cache/FlushSystem.php @@ -27,7 +27,7 @@ public function execute() $cacheFrontend->clean(); } $this->_eventManager->dispatch('adminhtml_cache_flush_system'); - $this->messageManager->addSuccess(__("The Magento cache storage has been flushed.")); + $this->messageManager->addSuccessMessage(__("The Magento cache storage has been flushed.")); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); return $resultRedirect->setPath('adminhtml/*'); diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassDisable.php b/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassDisable.php index 2bfa937b06b77..03b88ca1d3f47 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassDisable.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassDisable.php @@ -67,12 +67,12 @@ private function disableCache() } if ($updatedTypes > 0) { $this->_cacheState->persist(); - $this->messageManager->addSuccess(__("%1 cache type(s) disabled.", $updatedTypes)); + $this->messageManager->addSuccessMessage(__("%1 cache type(s) disabled.", $updatedTypes)); } } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('An error occurred while disabling cache.')); + $this->messageManager->addExceptionMessage($e, __('An error occurred while disabling cache.')); } } diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassEnable.php b/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassEnable.php index 113e0f2d8961b..1b98a00d4bf35 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassEnable.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassEnable.php @@ -66,12 +66,12 @@ private function enableCache() } if ($updatedTypes > 0) { $this->_cacheState->persist(); - $this->messageManager->addSuccess(__("%1 cache type(s) enabled.", $updatedTypes)); + $this->messageManager->addSuccessMessage(__("%1 cache type(s) enabled.", $updatedTypes)); } } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('An error occurred while enabling cache.')); + $this->messageManager->addExceptionMessage($e, __('An error occurred while enabling cache.')); } } diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassRefresh.php b/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassRefresh.php index 3843b030afb3d..bde211debcf72 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassRefresh.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Cache/MassRefresh.php @@ -37,12 +37,12 @@ public function execute() $updatedTypes++; } if ($updatedTypes > 0) { - $this->messageManager->addSuccess(__("%1 cache type(s) refreshed.", $updatedTypes)); + $this->messageManager->addSuccessMessage(__("%1 cache type(s) refreshed.", $updatedTypes)); } } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('An error occurred while refreshing cache.')); + $this->messageManager->addExceptionMessage($e, __('An error occurred while refreshing cache.')); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/RefreshStatistics.php b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/RefreshStatistics.php index f831fa67f4bb0..c10d1a77997b7 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/RefreshStatistics.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/RefreshStatistics.php @@ -34,9 +34,9 @@ public function execute() foreach ($collectionsNames as $collectionName) { $this->_objectManager->create($collectionName)->aggregate(); } - $this->messageManager->addSuccess(__('We updated lifetime statistic.')); + $this->messageManager->addSuccessMessage(__('We updated lifetime statistic.')); } catch (\Exception $e) { - $this->messageManager->addError(__('We can\'t refresh lifetime statistics.')); + $this->messageManager->addErrorMessage(__('We can\'t refresh lifetime statistics.')); $this->logger->critical($e); } diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Account/Save.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Account/Save.php index 1b10c151a9d21..d95b0541c2c76 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Account/Save.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Account/Save.php @@ -76,12 +76,12 @@ public function execute() $errors = $user->validate(); if ($errors !== true && !empty($errors)) { foreach ($errors as $error) { - $this->messageManager->addError($error); + $this->messageManager->addErrorMessage($error); } } else { $user->save(); $user->sendNotificationEmailsIfRequired(); - $this->messageManager->addSuccess(__('You saved the account.')); + $this->messageManager->addSuccessMessage(__('You saved the account.')); } } catch (UserLockedException $e) { $this->_auth->logout(); @@ -91,12 +91,12 @@ public function execute() } catch (ValidatorException $e) { $this->messageManager->addMessages($e->getMessages()); if ($e->getMessage()) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError(__('An error occurred while saving account.')); + $this->messageManager->addErrorMessage(__('An error occurred while saving account.')); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Design/Delete.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Design/Delete.php index 76402169f269e..21f28188cf874 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Design/Delete.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Design/Delete.php @@ -19,11 +19,11 @@ public function execute() try { $design->delete(); - $this->messageManager->addSuccess(__('You deleted the design change.')); + $this->messageManager->addSuccessMessage(__('You deleted the design change.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __("You can't delete the design change.")); + $this->messageManager->addExceptionMessage($e, __("You can't delete the design change.")); } } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Design/Save.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Design/Save.php index 1f478604ced7d..0228b48f7f11e 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Design/Save.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Design/Save.php @@ -50,9 +50,9 @@ public function execute() try { $design->save(); $this->_eventManager->dispatch('theme_save_after'); - $this->messageManager->addSuccess(__('You saved the design change.')); + $this->messageManager->addSuccessMessage(__('You saved the design change.')); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $this->_objectManager->get(\Magento\Backend\Model\Session::class)->setDesignData($data); return $resultRedirect->setPath('adminhtml/*/', ['id' => $design->getId()]); } diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Store.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Store.php index 4fbae6abb423a..0beeb5168b6d1 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Store.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Store.php @@ -103,12 +103,12 @@ protected function _backupDatabase() ->setType('db') ->setPath($filesystem->getDirectoryRead(DirectoryList::VAR_DIR)->getAbsolutePath('backups')); $backupDb->createBackup($backup); - $this->messageManager->addSuccess(__('The database was backed up.')); + $this->messageManager->addSuccessMessage(__('The database was backed up.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); return false; } catch (\Exception $e) { - $this->messageManager->addException( + $this->messageManager->addExceptionMessage( $e, __('We can\'t create a backup right now. Please try again later.') ); @@ -125,7 +125,7 @@ protected function _backupDatabase() */ protected function _addDeletionNotice($typeTitle) { - $this->messageManager->addNotice( + $this->messageManager->addNoticeMessage( __( 'Deleting a %1 will not delete the information associated with the %1 (e.g. categories, products, etc.)' . ', but the %1 will not be able to be restored. It is suggested that you create a database backup ' diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteGroup.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteGroup.php index 925ae4c69ee8e..4e323be709ae1 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteGroup.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteGroup.php @@ -15,13 +15,13 @@ public function execute() { $itemId = $this->getRequest()->getParam('item_id', null); if (!($model = $this->_objectManager->create(\Magento\Store\Model\Group::class)->load($itemId))) { - $this->messageManager->addError(__('Something went wrong. Please try again.')); + $this->messageManager->addErrorMessage(__('Something went wrong. Please try again.')); /** @var \Magento\Backend\Model\View\Result\Redirect $redirectResult */ $redirectResult = $this->resultRedirectFactory->create(); return $redirectResult->setPath('adminhtml/*/'); } if (!$model->isCanDelete()) { - $this->messageManager->addError(__('This store cannot be deleted.')); + $this->messageManager->addErrorMessage(__('This store cannot be deleted.')); /** @var \Magento\Backend\Model\View\Result\Redirect $redirectResult */ $redirectResult = $this->resultRedirectFactory->create(); return $redirectResult->setPath('adminhtml/*/editGroup', ['group_id' => $itemId]); diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteGroupPost.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteGroupPost.php index b6fbd88c7669c..23364aac1f0ab 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteGroupPost.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteGroupPost.php @@ -21,11 +21,11 @@ public function execute() $redirectResult = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); if (!($model = $this->_objectManager->create(\Magento\Store\Model\Group::class)->load($itemId))) { - $this->messageManager->addError(__('Something went wrong. Please try again.')); + $this->messageManager->addErrorMessage(__('Something went wrong. Please try again.')); return $redirectResult->setPath('adminhtml/*/'); } if (!$model->isCanDelete()) { - $this->messageManager->addError(__('This store cannot be deleted.')); + $this->messageManager->addErrorMessage(__('This store cannot be deleted.')); return $redirectResult->setPath('adminhtml/*/editGroup', ['group_id' => $model->getId()]); } @@ -35,12 +35,12 @@ public function execute() try { $model->delete(); - $this->messageManager->addSuccess(__('You deleted the store.')); + $this->messageManager->addSuccessMessage(__('You deleted the store.')); return $redirectResult->setPath('adminhtml/*/'); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('Unable to delete the store. Please try again later.')); + $this->messageManager->addExceptionMessage($e, __('Unable to delete the store. Please try again later.')); } return $redirectResult->setPath('adminhtml/*/editGroup', ['group_id' => $itemId]); } diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteStore.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteStore.php index b31de6cacc5ff..c340b1ec53aa5 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteStore.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteStore.php @@ -15,13 +15,13 @@ public function execute() { $itemId = $this->getRequest()->getParam('item_id', null); if (!($model = $this->_objectManager->create(\Magento\Store\Model\Store::class)->load($itemId))) { - $this->messageManager->addError(__('Something went wrong. Please try again.')); + $this->messageManager->addErrorMessage(__('Something went wrong. Please try again.')); /** @var \Magento\Backend\Model\View\Result\Redirect $redirectResult */ $redirectResult = $this->resultRedirectFactory->create(); return $redirectResult->setPath('adminhtml/*/'); } if (!$model->isCanDelete()) { - $this->messageManager->addError(__('This store view cannot be deleted.')); + $this->messageManager->addErrorMessage(__('This store view cannot be deleted.')); /** @var \Magento\Backend\Model\View\Result\Redirect $redirectResult */ $redirectResult = $this->resultRedirectFactory->create(); return $redirectResult->setPath('adminhtml/*/editStore', ['store_id' => $itemId]); diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteStorePost.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteStorePost.php index 13b104c5ec4c0..518e5e1d549a3 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteStorePost.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteStorePost.php @@ -22,11 +22,11 @@ public function execute() /** @var \Magento\Backend\Model\View\Result\Redirect $redirectResult */ $redirectResult = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); if (!($model = $this->_objectManager->create(\Magento\Store\Model\Store::class)->load($itemId))) { - $this->messageManager->addError(__('Something went wrong. Please try again.')); + $this->messageManager->addErrorMessage(__('Something went wrong. Please try again.')); return $redirectResult->setPath('adminhtml/*/'); } if (!$model->isCanDelete()) { - $this->messageManager->addError(__('This store view cannot be deleted.')); + $this->messageManager->addErrorMessage(__('This store view cannot be deleted.')); return $redirectResult->setPath('adminhtml/*/editStore', ['store_id' => $model->getId()]); } @@ -37,12 +37,12 @@ public function execute() try { $model->delete(); - $this->messageManager->addSuccess(__('You deleted the store view.')); + $this->messageManager->addSuccessMessage(__('You deleted the store view.')); return $redirectResult->setPath('adminhtml/*/'); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('Unable to delete the store view. Please try again later.')); + $this->messageManager->addExceptionMessage($e, __('Unable to delete the store view. Please try again later.')); } return $redirectResult->setPath('adminhtml/*/editStore', ['store_id' => $itemId]); } diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteWebsite.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteWebsite.php index 1f2ec4b2ba4b1..d86f57daa396c 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteWebsite.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteWebsite.php @@ -15,13 +15,13 @@ public function execute() { $itemId = $this->getRequest()->getParam('item_id', null); if (!($model = $this->_objectManager->create(\Magento\Store\Model\Website::class)->load($itemId))) { - $this->messageManager->addError(__('Something went wrong. Please try again.')); + $this->messageManager->addErrorMessage(__('Something went wrong. Please try again.')); /** @var \Magento\Backend\Model\View\Result\Redirect $redirectResult */ $redirectResult = $this->resultRedirectFactory->create(); return $redirectResult->setPath('adminhtml/*/'); } if (!$model->isCanDelete()) { - $this->messageManager->addError(__('This website cannot be deleted.')); + $this->messageManager->addErrorMessage(__('This website cannot be deleted.')); /** @var \Magento\Backend\Model\View\Result\Redirect $redirectResult */ $redirectResult = $this->resultRedirectFactory->create(); return $redirectResult->setPath('adminhtml/*/editWebsite', ['website_id' => $itemId]); diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteWebsitePost.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteWebsitePost.php index c2d24b8c41a8c..1fca5a896e050 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteWebsitePost.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteWebsitePost.php @@ -23,11 +23,11 @@ public function execute() $redirectResult = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); if (!$model) { - $this->messageManager->addError(__('Something went wrong. Please try again.')); + $this->messageManager->addErrorMessage(__('Something went wrong. Please try again.')); return $redirectResult->setPath('adminhtml/*/'); } if (!$model->isCanDelete()) { - $this->messageManager->addError(__('This website cannot be deleted.')); + $this->messageManager->addErrorMessage(__('This website cannot be deleted.')); return $redirectResult->setPath('adminhtml/*/editWebsite', ['website_id' => $model->getId()]); } @@ -37,12 +37,12 @@ public function execute() try { $model->delete(); - $this->messageManager->addSuccess(__('You deleted the website.')); + $this->messageManager->addSuccessMessage(__('You deleted the website.')); return $redirectResult->setPath('adminhtml/*/'); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('Unable to delete the website. Please try again later.')); + $this->messageManager->addExceptionMessage($e, __('Unable to delete the website. Please try again later.')); } return $redirectResult->setPath('*/*/editWebsite', ['website_id' => $itemId]); } diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/EditStore.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/EditStore.php index cbc068a480865..a8651984cfa63 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/EditStore.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/EditStore.php @@ -57,7 +57,7 @@ public function execute() if ($model->getId() || $this->_coreRegistry->registry('store_action') == 'add') { $this->_coreRegistry->register('store_data', $model); if ($this->_coreRegistry->registry('store_action') == 'edit' && $codeBase && !$model->isReadOnly()) { - $this->messageManager->addNotice($codeBase); + $this->messageManager->addNoticeMessage($codeBase); } $resultPage = $this->createPage(); if ($this->_coreRegistry->registry('store_action') == 'add') { @@ -71,7 +71,7 @@ public function execute() )); return $resultPage; } else { - $this->messageManager->addError($notExists); + $this->messageManager->addErrorMessage($notExists); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); return $resultRedirect->setPath('adminhtml/*/'); diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/Save.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/Save.php index 8ca783f887ec4..910511c2b275e 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/Save.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/Save.php @@ -32,7 +32,7 @@ private function processWebsiteSave($postData) } $websiteModel->save(); - $this->messageManager->addSuccess(__('You saved the website.')); + $this->messageManager->addSuccessMessage(__('You saved the website.')); return $postData; } @@ -68,7 +68,7 @@ private function processStoreSave($postData) ); } $storeModel->save(); - $this->messageManager->addSuccess(__('You saved the store view.')); + $this->messageManager->addSuccessMessage(__('You saved the store view.')); return $postData; } @@ -98,7 +98,7 @@ private function processGroupSave($postData) ); } $groupModel->save(); - $this->messageManager->addSuccess(__('You saved the store.')); + $this->messageManager->addSuccessMessage(__('You saved the store.')); return $postData; } @@ -134,10 +134,10 @@ public function execute() $redirectResult->setPath('adminhtml/*/'); return $redirectResult; } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $this->_getSession()->setPostData($postData); } catch (\Exception $e) { - $this->messageManager->addException( + $this->messageManager->addExceptionMessage( $e, __('Something went wrong while saving. Please review the error log.') ); diff --git a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/CleanMediaTest.php b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/CleanMediaTest.php index b1911da024227..ac0f4a2f467c8 100644 --- a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/CleanMediaTest.php +++ b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/CleanMediaTest.php @@ -38,7 +38,7 @@ public function testExecute() $messageManagerParams = $helper->getConstructArguments(\Magento\Framework\Message\Manager::class); $messageManagerParams['exceptionMessageFactory'] = $exceptionMessageFactory; $messageManager = $this->getMockBuilder(\Magento\Framework\Message\Manager::class) - ->setMethods(['addSuccess']) + ->setMethods(['addSuccessMessage']) ->setConstructorArgs($messageManagerParams) ->getMock(); @@ -86,7 +86,7 @@ public function testExecute() $mergeService->expects($this->once())->method('cleanMergedJsCss'); $messageManager->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with('The JavaScript/CSS cache has been cleaned.'); $valueMap = [ diff --git a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/CleanStaticFilesTest.php b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/CleanStaticFilesTest.php index 40d9ca1aa8996..fc457cd9681e6 100644 --- a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/CleanStaticFilesTest.php +++ b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/CleanStaticFilesTest.php @@ -76,7 +76,7 @@ public function testExecute() ->with('clean_static_files_cache_after'); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with('The static files cache has been cleaned.'); $resultRedirect = $this->getMockBuilder(\Magento\Backend\Model\View\Result\Redirect::class) diff --git a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassDisableTest.php b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassDisableTest.php index 556db311748bd..9a42d0acdba89 100644 --- a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassDisableTest.php +++ b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassDisableTest.php @@ -155,7 +155,7 @@ public function testExecuteInvalidTypeCache() ->willReturn(['someCache']); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('Specified cache type(s) don\'t exist: someCache') ->willReturnSelf(); @@ -175,7 +175,7 @@ public function testExecuteWithException() ->willThrowException($exception); $this->messageManagerMock->expects($this->once()) - ->method('addException') + ->method('addExceptionMessage') ->with($exception, 'An error occurred while disabling cache.') ->willReturnSelf(); @@ -215,7 +215,7 @@ public function testExecuteSuccess() ->method('persist'); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with('1 cache type(s) disabled.') ->willReturnSelf(); diff --git a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassEnableTest.php b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassEnableTest.php index ad622ca69757a..23cf6a9a0a70a 100644 --- a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassEnableTest.php +++ b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Cache/MassEnableTest.php @@ -155,7 +155,7 @@ public function testExecuteInvalidTypeCache() ->willReturn(['someCache']); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('Specified cache type(s) don\'t exist: someCache') ->willReturnSelf(); @@ -175,7 +175,7 @@ public function testExecuteWithException() ->willThrowException($exception); $this->messageManagerMock->expects($this->once()) - ->method('addException') + ->method('addExceptionMessage') ->with($exception, 'An error occurred while enabling cache.') ->willReturnSelf(); @@ -215,7 +215,7 @@ public function testExecuteSuccess() ->method('persist'); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with('1 cache type(s) enabled.') ->willReturnSelf(); diff --git a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Dashboard/RefreshStatisticsTest.php b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Dashboard/RefreshStatisticsTest.php index e8dcc00345fc6..a985681919f0b 100644 --- a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Dashboard/RefreshStatisticsTest.php +++ b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/Dashboard/RefreshStatisticsTest.php @@ -107,7 +107,7 @@ public function testExecute() $this->resultRedirectFactory->expects($this->any())->method('create')->willReturn($this->resultRedirect); $this->messageManager->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('We updated lifetime statistic.')); $this->objectManager->expects($this->any()) diff --git a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/System/Account/SaveTest.php b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/System/Account/SaveTest.php index 844a821df1c20..a8490d6ba2e58 100644 --- a/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/System/Account/SaveTest.php +++ b/app/code/Magento/Backend/Test/Unit/Controller/Adminhtml/System/Account/SaveTest.php @@ -71,7 +71,7 @@ protected function setUp() ->getMock(); $this->_messagesMock = $this->getMockBuilder(\Magento\Framework\Message\Manager::class) ->disableOriginalConstructor() - ->setMethods(['addSuccess']) + ->setMethods(['addSuccessMessage']) ->getMockForAbstractClass(); $this->_authSessionMock = $this->getMockBuilder(\Magento\Backend\Model\Auth\Session::class) @@ -221,7 +221,7 @@ public function testSaveAction() $this->_requestMock->setParams($requestParams); - $this->_messagesMock->expects($this->once())->method('addSuccess')->with($this->equalTo($testedMessage)); + $this->_messagesMock->expects($this->once())->method('addSuccessMessage')->with($this->equalTo($testedMessage)); $this->_controller->execute(); } From 36fcbfc8580163c08128d261bdc0a825aa03ea70 Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Mon, 23 Jul 2018 15:26:17 -0300 Subject: [PATCH 085/211] Replacing deprecated methods in Magento_Backup module. --- app/code/Magento/Backup/Controller/Adminhtml/Index/Create.php | 2 +- .../Magento/Backup/Controller/Adminhtml/Index/MassDelete.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Backup/Controller/Adminhtml/Index/Create.php b/app/code/Magento/Backup/Controller/Adminhtml/Index/Create.php index 0bd7ef3d82ade..65ea398b74c42 100644 --- a/app/code/Magento/Backup/Controller/Adminhtml/Index/Create.php +++ b/app/code/Magento/Backup/Controller/Adminhtml/Index/Create.php @@ -82,7 +82,7 @@ public function execute() $backupManager->create(); - $this->messageManager->addSuccess($successMessage); + $this->messageManager->addSuccessMessage($successMessage); $response->setRedirectUrl($this->getUrl('*/*/index')); } catch (\Magento\Framework\Backup\Exception\NotEnoughFreeSpace $e) { diff --git a/app/code/Magento/Backup/Controller/Adminhtml/Index/MassDelete.php b/app/code/Magento/Backup/Controller/Adminhtml/Index/MassDelete.php index 04292d2759093..90657fc2490ba 100644 --- a/app/code/Magento/Backup/Controller/Adminhtml/Index/MassDelete.php +++ b/app/code/Magento/Backup/Controller/Adminhtml/Index/MassDelete.php @@ -49,13 +49,13 @@ public function execute() $resultData->setIsSuccess(true); if ($allBackupsDeleted) { - $this->messageManager->addSuccess(__('You deleted the selected backup(s).')); + $this->messageManager->addSuccessMessage(__('You deleted the selected backup(s).')); } else { throw new \Exception($deleteFailMessage); } } catch (\Exception $e) { $resultData->setIsSuccess(false); - $this->messageManager->addError($deleteFailMessage); + $this->messageManager->addErrorMessage($deleteFailMessage); } return $this->_redirect('backup/*/index'); From 17596e8e9cf066d90d9c866045e3f7b766304412 Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Mon, 23 Jul 2018 17:51:32 -0300 Subject: [PATCH 086/211] Replacing deprecated methods in Magento_CatalogRule module. --- .../Controller/Adminhtml/Promo/Catalog/ApplyRules.php | 6 +++--- .../Controller/Adminhtml/Promo/Catalog/Delete.php | 8 ++++---- .../Controller/Adminhtml/Promo/Catalog/Edit.php | 2 +- .../Controller/Adminhtml/Promo/Catalog/Save.php | 8 ++++---- .../Magento/CatalogRule/Observer/AddDirtyRulesNotice.php | 2 +- .../CatalogRule/Plugin/Indexer/Product/Attribute.php | 2 +- .../Test/Unit/Observer/AddDirtyRulesNoticeTest.php | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/ApplyRules.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/ApplyRules.php index 85ad74f7bbfe2..4badfa1219e10 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/ApplyRules.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/ApplyRules.php @@ -25,14 +25,14 @@ public function execute() $ruleJob->applyAll(); if ($ruleJob->hasSuccess()) { - $this->messageManager->addSuccess($ruleJob->getSuccess()); + $this->messageManager->addSuccessMessage($ruleJob->getSuccess()); $this->_objectManager->create(\Magento\CatalogRule\Model\Flag::class)->loadSelf()->setState(0)->save(); } elseif ($ruleJob->hasError()) { - $this->messageManager->addError($errorMessage . ' ' . $ruleJob->getError()); + $this->messageManager->addErrorMessage($errorMessage . ' ' . $ruleJob->getError()); } } catch (\Exception $e) { $this->_objectManager->create(\Psr\Log\LoggerInterface::class)->critical($e); - $this->messageManager->addError($errorMessage); + $this->messageManager->addErrorMessage($errorMessage); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Delete.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Delete.php index 8b007031f3305..3500506d8d6c5 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Delete.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Delete.php @@ -25,13 +25,13 @@ public function execute() $ruleRepository->deleteById($id); $this->_objectManager->create(\Magento\CatalogRule\Model\Flag::class)->loadSelf()->setState(1)->save(); - $this->messageManager->addSuccess(__('You deleted the rule.')); + $this->messageManager->addSuccessMessage(__('You deleted the rule.')); $this->_redirect('catalog_rule/*/'); return; } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError( + $this->messageManager->addErrorMessage( __('We can\'t delete this rule right now. Please review the log and try again.') ); $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); @@ -39,7 +39,7 @@ public function execute() return; } } - $this->messageManager->addError(__('We can\'t find a rule to delete.')); + $this->messageManager->addErrorMessage(__('We can\'t find a rule to delete.')); $this->_redirect('catalog_rule/*/'); } } diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Edit.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Edit.php index 97a5693b18117..945c28b2088f2 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Edit.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Edit.php @@ -24,7 +24,7 @@ public function execute() try { $model = $ruleRepository->get($id); } catch (\Magento\Framework\Exception\NoSuchEntityException $exception) { - $this->messageManager->addError(__('This rule no longer exists.')); + $this->messageManager->addErrorMessage(__('This rule no longer exists.')); $this->_redirect('catalog_rule/*'); return; } diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php index 0170fc76b6aab..f3046c58a389b 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php @@ -68,7 +68,7 @@ public function execute() $validateResult = $model->validateData(new \Magento\Framework\DataObject($data)); if ($validateResult !== true) { foreach ($validateResult as $errorMessage) { - $this->messageManager->addError($errorMessage); + $this->messageManager->addErrorMessage($errorMessage); } $this->_getSession()->setPageData($data); $this->dataPersistor->set('catalog_rule', $data); @@ -88,7 +88,7 @@ public function execute() $ruleRepository->save($model); - $this->messageManager->addSuccess(__('You saved the rule.')); + $this->messageManager->addSuccessMessage(__('You saved the rule.')); $this->_objectManager->get(\Magento\Backend\Model\Session::class)->setPageData(false); $this->dataPersistor->clear('catalog_rule'); @@ -111,9 +111,9 @@ public function execute() } return; } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError( + $this->messageManager->addErrorMessage( __('Something went wrong while saving the rule data. Please review the error log.') ); $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); diff --git a/app/code/Magento/CatalogRule/Observer/AddDirtyRulesNotice.php b/app/code/Magento/CatalogRule/Observer/AddDirtyRulesNotice.php index 08c3d97b216ed..749ac3cf51249 100644 --- a/app/code/Magento/CatalogRule/Observer/AddDirtyRulesNotice.php +++ b/app/code/Magento/CatalogRule/Observer/AddDirtyRulesNotice.php @@ -37,7 +37,7 @@ public function execute(\Magento\Framework\Event\Observer $observer) $dirtyRules = $observer->getData('dirty_rules'); if (!empty($dirtyRules)) { if ($dirtyRules->getState()) { - $this->messageManager->addNotice($observer->getData('message')); + $this->messageManager->addNoticeMessage($observer->getData('message')); } } } diff --git a/app/code/Magento/CatalogRule/Plugin/Indexer/Product/Attribute.php b/app/code/Magento/CatalogRule/Plugin/Indexer/Product/Attribute.php index cc808a38db698..7fdffe933db8c 100644 --- a/app/code/Magento/CatalogRule/Plugin/Indexer/Product/Attribute.php +++ b/app/code/Magento/CatalogRule/Plugin/Indexer/Product/Attribute.php @@ -103,7 +103,7 @@ protected function checkCatalogRulesAvailability($attributeCode) if ($disabledRulesCount) { $this->ruleProductProcessor->markIndexerAsInvalid(); - $this->messageManager->addWarning( + $this->messageManager->addWarningMessage( __( 'You disabled %1 Catalog Price Rules based on "%2" attribute.', $disabledRulesCount, diff --git a/app/code/Magento/CatalogRule/Test/Unit/Observer/AddDirtyRulesNoticeTest.php b/app/code/Magento/CatalogRule/Test/Unit/Observer/AddDirtyRulesNoticeTest.php index b052ccddbf6b4..25bae43a930bb 100644 --- a/app/code/Magento/CatalogRule/Test/Unit/Observer/AddDirtyRulesNoticeTest.php +++ b/app/code/Magento/CatalogRule/Test/Unit/Observer/AddDirtyRulesNoticeTest.php @@ -49,7 +49,7 @@ public function testExecute() $eventObserverMock->expects($this->at(0))->method('getData')->with('dirty_rules')->willReturn($flagMock); $flagMock->expects($this->once())->method('getState')->willReturn(1); $eventObserverMock->expects($this->at(1))->method('getData')->with('message')->willReturn($message); - $this->messageManagerMock->expects($this->once())->method('addNotice')->with($message); + $this->messageManagerMock->expects($this->once())->method('addNoticeMessage')->with($message); $this->observer->execute($eventObserverMock); } } From 34acb942ab6f8455681b63b872e32718e27ef66e Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Tue, 24 Jul 2018 11:36:17 +0300 Subject: [PATCH 087/211] MAGETWO-73245: Add product to website will reset has_options and required_options --- app/code/Magento/Catalog/Model/Product.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php index 850fb21777585..84ce132f3ab50 100644 --- a/app/code/Magento/Catalog/Model/Product.php +++ b/app/code/Magento/Catalog/Model/Product.php @@ -871,7 +871,7 @@ public function beforeSave() * or in type instance as well */ $this->canAffectOptions($this->_canAffectOptions && $this->getCanSaveCustomOptions()); - if ($this->getCanSaveCustomOptions()) { + if ($this->getCanSaveCustomOptions() || $this->getCopyFromView()) { $options = $this->getOptions(); if (is_array($options)) { $this->setIsCustomOptionChanged(true); From dd602f734327f7cc027cc07ffd5b7a1118eb9c99 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Tue, 24 Jul 2018 12:43:34 +0300 Subject: [PATCH 088/211] MAGETWO-75086: Child product image should be shown in Wishist if options are selected for configurable product #8168 --- .../Catalog/Block/Product/ImageBuilder.php | 8 - .../Item/ItemResolverComposite.php | 68 ++++++++ .../Item/ItemResolverInterface.php | 26 +++ ...minCategoryProductAttributeActionGroup.xml | 28 +++ .../ActionGroup/AdminProductActionGroup.xml | 13 ++ .../AdminProductGridActionGroup.xml | 12 ++ .../ProductsOnAdminActionGroup.xml | 18 ++ .../Catalog/Test/Mftf/Data/ProductData.xml | 18 ++ .../AdminCategoryProductAttributeEditPage.xml | 15 ++ .../AdminProductAttributeEditSection.xml | 14 ++ .../Section/AdminProductGridFilterSection.xml | 1 + .../Mftf/Section/AdminProductGridSection.xml | 1 + .../Section/StorefrontProductPageSection.xml | 2 +- .../Unit/Block/Product/ImageBuilderTest.php | 6 - app/code/Magento/Catalog/etc/di.xml | 1 + .../Checkout/Block/Cart/Item/Renderer.php | 14 +- .../Checkout/CustomerData/DefaultItem.php | 15 +- .../Checkout/Model/Cart/ImageProvider.php | 20 ++- .../Unit/Block/Cart/Item/RendererTest.php | 78 +++++---- .../Unit/CustomerData/DefaultItemTest.php | 20 ++- .../Unit/Model/Cart/ImageProviderTest.php | 19 ++- app/code/Magento/Checkout/etc/frontend/di.xml | 2 +- .../Block/Cart/Item/Renderer/Configurable.php | 28 +-- .../CustomerData/ConfigurableItem.php | 3 + .../Item/ItemProductResolver.php | 85 ++++++++++ .../AdminConfigurableProductActionGroup.xml | 78 +++++++++ .../Test/Mftf/Page/AdminProductCreatePage.xml | 3 + .../Page/StorefrontCustomerWishlistPage.xml | 15 ++ ...AdminChooseAffectedAttributeSetSection.xml | 14 ++ ...reateProductConfigurationsPanelSection.xml | 31 ++++ .../AdminProductFormConfigurationsSection.xml | 26 +++ ...orefrontCustomerWishlistProductSection.xml | 14 ++ .../StorefrontCustomerWishlistSection.xml | 14 ++ ...orefrontCustomerWishlistSidebarSection.xml | 14 ++ .../StorefrontProductInfoMainSection.xml | 14 ++ .../Cart/Item/Renderer/ConfigurableTest.php | 160 ++++-------------- .../Item/ItemProductResolverTest.php | 99 +++++++++++ .../Magento/ConfigurableProduct/etc/di.xml | 7 + .../ConfigurableProduct/etc/frontend/di.xml | 7 - .../Block/Cart/Item/Renderer/Grouped.php | 24 +-- .../CustomerData/GroupedItem.php | 4 + .../Item/ItemProductResolver.php | 86 ++++++++++ .../Block/Cart/Item/Renderer/GroupedTest.php | 127 ++------------ .../Item/ItemProductResolverTest.php | 99 +++++++++++ app/code/Magento/GroupedProduct/etc/di.xml | 7 + .../GroupedProduct/etc/frontend/di.xml | 17 -- .../Customer/Wishlist/Item/Column/Image.php | 45 ++++- .../Wishlist/CustomerData/Wishlist.php | 22 +-- ...tChildImageShouldBeShownOnWishListTest.xml | 81 +++++++++ ...AddMultipleStoreProductsToWishlistTest.xml | 4 +- .../Test/Unit/CustomerData/WishlistTest.php | 53 ++++-- .../templates/item/column/image.phtml | 2 +- .../acceptance/tests/_data/magento-again.jpg | Bin 0 -> 55303 bytes 53 files changed, 1172 insertions(+), 410 deletions(-) create mode 100644 app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php create mode 100644 app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverInterface.php create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryProductAttributeActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryProductAttributeEditPage.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeEditSection.xml create mode 100644 app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Page/StorefrontCustomerWishlistPage.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminChooseAffectedAttributeSetSection.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontCustomerWishlistSection.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontCustomerWishlistSidebarSection.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontProductInfoMainSection.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php create mode 100644 app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php create mode 100644 app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php delete mode 100644 app/code/Magento/GroupedProduct/etc/frontend/di.xml create mode 100644 app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml create mode 100644 dev/tests/acceptance/tests/_data/magento-again.jpg diff --git a/app/code/Magento/Catalog/Block/Product/ImageBuilder.php b/app/code/Magento/Catalog/Block/Product/ImageBuilder.php index 375b7bff050fe..f1149f15c41d3 100644 --- a/app/code/Magento/Catalog/Block/Product/ImageBuilder.php +++ b/app/code/Magento/Catalog/Block/Product/ImageBuilder.php @@ -121,14 +121,6 @@ protected function getRatio(\Magento\Catalog\Helper\Image $helper) */ public function create() { - /** @var \Magento\Catalog\Model\Product\Configuration\Item\Option\OptionInterface $simpleOption */ - $simpleOption = $this->product->getOptionById('simple_product'); - - if ($simpleOption !== null) { - $optionProduct = $simpleOption->getProduct(); - $this->setProduct($optionProduct); - } - /** @var \Magento\Catalog\Helper\Image $helper */ $helper = $this->helperFactory->create() ->init($this->product, $this->imageId); diff --git a/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php b/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php new file mode 100644 index 0000000000000..ec500ac2f2030 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverComposite.php @@ -0,0 +1,68 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\Configuration\Item; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Framework\App\ObjectManager; + +/** + * {@inheritdoc} + */ +class ItemResolverComposite implements ItemResolverInterface +{ + /** + * @var string[] + */ + private $itemResolvers = []; + + /** + * @var ItemResolverInterface[] + */ + private $itemResolversInstances = []; + + /** + * @param string[] $itemResolvers + */ + public function __construct(array $itemResolvers) + { + $this->itemResolvers = $itemResolvers; + } + + /** + * {@inheritdoc} + */ + public function getFinalProduct(ItemInterface $item): ProductInterface + { + $finalProduct = $item->getProduct(); + + foreach ($this->itemResolvers as $resolver) { + $resolvedProduct = $this->getItemResolverInstance($resolver)->getFinalProduct($item); + if ($resolvedProduct !== $finalProduct) { + $finalProduct = $resolvedProduct; + break; + } + } + + return $finalProduct; + } + + /** + * Get the instance of the item resolver by class name. + * + * @param string $className + * @return ItemResolverInterface + */ + private function getItemResolverInstance(string $className): ItemResolverInterface + { + if (!isset($this->itemResolversInstances[$className])) { + $this->itemResolversInstances[$className] = ObjectManager::getInstance()->get($className); + } + + return $this->itemResolversInstances[$className]; + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverInterface.php b/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverInterface.php new file mode 100644 index 0000000000000..9e6de01bb5112 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Configuration/Item/ItemResolverInterface.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\Configuration\Item; + +use Magento\Catalog\Api\Data\ProductInterface; + +/** + * Resolves the product from a configured item. + * + * @api + */ +interface ItemResolverInterface +{ + /** + * Get the final product from a configured item by product type and selection. + * + * @param ItemInterface $item + * @return ProductInterface + */ + public function getFinalProduct(ItemInterface $item): ProductInterface; +} diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryProductAttributeActionGroup.xml new file mode 100644 index 0000000000000..c9c91ebec1523 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryProductAttributeActionGroup.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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <!--Actions to delete category--> + <actionGroup name="DeleteProductAttribute"> + <arguments> + <argument name="productAttribute"/> + </arguments> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributesGridPage"/> + <waitForPageLoad time="30" stepKey="waitForProductAttributesGridPageLoad"/> + <click selector="{{AdminProductAttributeGridSection.resetFilter}}" stepKey="resetFilter"/> + <fillField selector="{{AdminProductAttributeGridSection.gridFilterFrontEndLabel}}" + userInput="{{productAttribute.default_label}}" stepKey="fillAttributeDefaultLabelInput"/> + <click selector="{{AdminProductAttributeGridSection.search}}" stepKey="searchForAttribute"/> + <click selector="{{AdminProductAttributeGridSection.firstRow}}" stepKey="clickFirstRow"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad"/> + <click selector="{{AdminProductAttributeEditSection.deleteAttribute}}" stepKey="deleteProductAttribute"/> + <waitForElementVisible selector="{{AdminConfirmationModalSection.message}}" stepKey="waitingForWarningModal"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmStoreDelete"/> + </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 32b16eed86fb0..d7b8d84b4a9f3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -48,4 +48,17 @@ <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="You saved the product." stepKey="seeSaveConfirmation"/> </actionGroup> + + <!--Upload image for product--> + <actionGroup name="addProductImage"> + <arguments> + <argument name="image" defaultValue="ProductImage"/> + </arguments> + <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageUploadButton}}" visible="false" stepKey="openProductImagesSection"/> + <waitForPageLoad time="30" stepKey="waitForPageRefresh"/> + <waitForElementVisible selector="{{AdminProductImagesSection.imageUploadButton}}" stepKey="seeImageSectionIsReady"/> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="{{image.file}}" stepKey="uploadFile"/> + <waitForElementNotVisible selector="{{AdminProductImagesSection.uploadProgressBar}}" stepKey="waitForUpload"/> + <waitForElementVisible selector="{{AdminProductImagesSection.imageFile(image.fileName)}}" stepKey="waitForThumbnail"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml index 57133f39217ca..ff5f0a0c52523 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml @@ -29,6 +29,18 @@ <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad" time="30"/> </actionGroup> + <!--Filter the product grid by the SKU string --> + <actionGroup name="filterProductGridBySku2"> + <arguments> + <argument name="sku" type="string"/> + </arguments> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.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"/> + <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad" time="30"/> + </actionGroup> + <!--Delete a product by filtering grid and using delete action--> <actionGroup name="deleteProductUsingProductGrid"> <arguments> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductsOnAdminActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductsOnAdminActionGroup.xml index 113a70cbef701..6a61832edd8b0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductsOnAdminActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductsOnAdminActionGroup.xml @@ -25,4 +25,22 @@ <waitForElementVisible selector="{{AdminProductGridConfirmActionSection.title}}" stepKey="waitForConfirmModal"/> <click selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="clickOkInConfirmation"/> </actionGroup> + + <actionGroup name="DeleteAllProductsOnProductsGridPageFilteredByName"> + <arguments> + <argument name="product"/> + </arguments> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="openProductsGridPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="clickOnFiltersButton"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clearFilters"/> + <fillField selector="{{AdminProductGridFilterSection.name}}" userInput="{{product.name}}" stepKey="fillNameFieldInFilter"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="applyFilters"/> + <click selector="{{AdminProductGridSection.multicheckDropdown}}" stepKey="clickMulticheckDropDown"/> + <click selector="{{AdminProductGridSection.multicheckOption('Select All')}}" stepKey="selectAllFilteredProducts"/> + <click selector="{{AdminProductGridActionSection.actionsSelectBox}}" stepKey="openActionsSelectBox"/> + <click selector="{{AdminProductGridActionSection.deleteOptionInActionsSelectBox}}" stepKey="clickDeleteAction"/> + <waitForElementVisible selector="{{AdminProductGridConfirmActionSection.title}}" stepKey="waitForConfirmModal"/> + <click selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="clickOkInConfirmation"/> + </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 1ac02a6f69294..f4c381cb6e7b6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -94,6 +94,24 @@ <data key="file">magento-logo.png</data> <data key="fileName">magento-logo</data> </entity> + <entity name="MagentoLogo" type="image"> + <data key="title" unique="suffix">MagentoLogo</data> + <data key="price">1.00</data> + <data key="file_type">Upload File</data> + <data key="shareable">Yes</data> + <data key="file">magento-logo.png</data> + <data key="filename">magento-logo</data> + <data key="file_extension">png</data> + </entity> + <entity name="TestImageNew" type="image"> + <data key="title" unique="suffix">magento-again</data> + <data key="price">1.00</data> + <data key="file_type">Upload File</data> + <data key="shareable">Yes</data> + <data key="file">magento-again.jpg</data> + <data key="filename">magento-again</data> + <data key="file_extension">jpg</data> + </entity> <entity name="productWithDescription" type="product"> <data key="sku" unique="suffix">testProductWithDescriptionSku</data> <data key="type_id">simple</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryProductAttributeEditPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryProductAttributeEditPage.xml new file mode 100644 index 0000000000000..6460f520bea3f --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryProductAttributeEditPage.xml @@ -0,0 +1,15 @@ +<?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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + <page name="AdminCategoryProductAttributeEditPage" url="catalog/product_attribute/edit/" area="admin" module="Magento_Catalog"> + <section name="AdminProductAttributeEditSection"/> + <section name="AdminConfirmationModalSection"/> + </page> +</pages> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeEditSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeEditSection.xml new file mode 100644 index 0000000000000..798832f707bf0 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeEditSection.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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminProductAttributeEditSection"> + <element name="deleteAttribute" type="button" selector="#delete" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridFilterSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridFilterSection.xml index cbd59545ff944..7ff653b9c32b3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridFilterSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridFilterSection.xml @@ -17,6 +17,7 @@ <element name="applyFilters" type="button" selector="button[data-action='grid-filter-apply']" timeout="30"/> <element name="newFromDateFilter" type="input" selector="input.admin__control-text[name='news_from_date[from]']"/> <element name="skuFilter" type="input" selector="input.admin__control-text[name='sku']"/> + <element name="name" type="input" selector="input.admin__control-text[name='name']"/> <element name="viewBookmark" type="button" selector="//div[contains(@class, 'admin__data-grid-action-bookmarks')]/ul/li/div/a[text() = '{{label}}']" parameterized="true" timeout="30"/> <element name="viewDropdown" type="button" selector=".admin__data-grid-action-bookmarks button.admin__action-dropdown"/> </section> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml index 9488fb25dd63d..3110123e02133 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml @@ -25,5 +25,6 @@ <element name="bulkActionOption" type="button" selector="//div[contains(@class,'admin__data-grid-header-row') and contains(@class, 'row')]//div[contains(@class, 'action-select-wrap')]//ul/li/span[text() = '{{label}}']" parameterized="true"/> <element name="productGridNameProduct" type="input" selector="//tbody//tr//td//div[contains(., '{{var1}}')]" parameterized="true" timeout="30"/> <element name="adminImgGridThumbnail" type="text" selector="img.admin__control-thumbnail[src*='/{{var1}}']" parameterized="true"/> + <element name="selectRowBasedOnName" type="input" selector="//td/div[text()='{{var1}}']" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml index d198961befb45..c1d04f2a1b033 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml @@ -12,7 +12,7 @@ <element name="QtyInput" type="button" selector="input.input-text.qty"/> <element name="AddToCartBtn" type="button" selector="button.action.tocart.primary"/> <element name="SuccessMsg" type="button" selector="div.message-success"/> - <element name="AddToWishlist" type="button" selector="//a[@class='action towishlist']" timeout="30"/> + <element name="addToWishlist" type="button" selector="//a[@class='action towishlist']" timeout="30"/> <element name="addToCartButtonTitleIsAdding" type="text" selector="//button/span[text()='Adding...']"/> <element name="addToCartButtonTitleIsAdded" type="text" selector="//button/span[text()='Added']"/> <element name="addToCartButtonTitleIsAddToCart" type="text" selector="//button/span[text()='Add to Cart']"/> diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/ImageBuilderTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/ImageBuilderTest.php index 39ddd8768fedc..8612858960c8c 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Product/ImageBuilderTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/ImageBuilderTest.php @@ -303,14 +303,8 @@ public function testCreateWithSimpleProduct($data, $expected) $imageId = 'test_image_id'; $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $simpleOptionMock = $this->createMock(\Magento\Wishlist\Model\Item\Option::class); $simpleProductMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $productMock->expects($this->once())->method('getOptionById') - ->with('simple_product')->willReturn($simpleOptionMock); - - $simpleOptionMock->expects($this->once())->method('getProduct')->willReturn($simpleProductMock); - $helperMock = $this->createMock(\Magento\Catalog\Helper\Image::class); $helperMock->expects($this->once()) ->method('init') diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index 3eecfb3c7453a..f1c1ab73b79ff 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -70,6 +70,7 @@ <preference for="Magento\Catalog\Api\Data\ProductRender\FormattedPriceInfoInterface" type="Magento\Catalog\Model\ProductRender\FormattedPriceInfo" /> <preference for="Magento\Framework\Indexer\BatchProviderInterface" type="Magento\Framework\Indexer\BatchProvider" /> <preference for="Magento\Catalog\Model\Indexer\Product\Price\UpdateIndexInterface" type="Magento\Catalog\Model\Indexer\Product\Price\InvalidateIndex" /> + <preference for="Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface" type="Magento\Catalog\Model\Product\Configuration\Item\ItemResolverComposite" /> <type name="Magento\Customer\Model\ResourceModel\Visitor"> <plugin name="catalogLog" type="Magento\Catalog\Model\Plugin\Log" /> </type> diff --git a/app/code/Magento/Checkout/Block/Cart/Item/Renderer.php b/app/code/Magento/Checkout/Block/Cart/Item/Renderer.php index 57ca4b7b2e606..95d63bb0eda95 100644 --- a/app/code/Magento/Checkout/Block/Cart/Item/Renderer.php +++ b/app/code/Magento/Checkout/Block/Cart/Item/Renderer.php @@ -11,6 +11,8 @@ use Magento\Framework\View\Element\AbstractBlock; use Magento\Framework\View\Element\Message\InterpretationStrategyInterface; use Magento\Quote\Model\Quote\Item\AbstractItem; +use Magento\Framework\App\ObjectManager; +use Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface; /** * Shopping cart item render block @@ -91,6 +93,11 @@ class Renderer extends \Magento\Framework\View\Element\Template implements */ private $messageInterpretationStrategy; + /** + * @var ItemResolverInterface + */ + private $itemResolver; + /** * @param \Magento\Framework\View\Element\Template\Context $context * @param \Magento\Catalog\Helper\Product\Configuration $productConfig @@ -102,6 +109,7 @@ class Renderer extends \Magento\Framework\View\Element\Template implements * @param \Magento\Framework\Module\Manager $moduleManager * @param InterpretationStrategyInterface $messageInterpretationStrategy * @param array $data + * @param ItemResolverInterface|null $itemResolver * @SuppressWarnings(PHPMD.ExcessiveParameterList) * @codeCoverageIgnore */ @@ -115,7 +123,8 @@ public function __construct( PriceCurrencyInterface $priceCurrency, \Magento\Framework\Module\Manager $moduleManager, InterpretationStrategyInterface $messageInterpretationStrategy, - array $data = [] + array $data = [], + ItemResolverInterface $itemResolver = null ) { $this->priceCurrency = $priceCurrency; $this->imageBuilder = $imageBuilder; @@ -127,6 +136,7 @@ public function __construct( $this->_isScopePrivate = true; $this->moduleManager = $moduleManager; $this->messageInterpretationStrategy = $messageInterpretationStrategy; + $this->itemResolver = $itemResolver ?: ObjectManager::getInstance()->get(ItemResolverInterface::class); } /** @@ -172,7 +182,7 @@ public function getProduct() */ public function getProductForThumbnail() { - return $this->getProduct(); + return $this->itemResolver->getFinalProduct($this->getItem()); } /** diff --git a/app/code/Magento/Checkout/CustomerData/DefaultItem.php b/app/code/Magento/Checkout/CustomerData/DefaultItem.php index 9351685405a60..21580d1275d0c 100644 --- a/app/code/Magento/Checkout/CustomerData/DefaultItem.php +++ b/app/code/Magento/Checkout/CustomerData/DefaultItem.php @@ -7,6 +7,7 @@ namespace Magento\Checkout\CustomerData; use Magento\Framework\App\ObjectManager; +use Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface; /** * Default item @@ -39,12 +40,15 @@ class DefaultItem extends AbstractItem protected $checkoutHelper; /** - * Escaper - * * @var \Magento\Framework\Escaper */ private $escaper; + /** + * @var ItemResolverInterface + */ + private $itemResolver; + /** * @param \Magento\Catalog\Helper\Image $imageHelper * @param \Magento\Msrp\Helper\Data $msrpHelper @@ -52,6 +56,7 @@ class DefaultItem extends AbstractItem * @param \Magento\Catalog\Helper\Product\ConfigurationPool $configurationPool * @param \Magento\Checkout\Helper\Data $checkoutHelper * @param \Magento\Framework\Escaper|null $escaper + * @param ItemResolverInterface|null $itemResolver * @codeCoverageIgnore */ public function __construct( @@ -60,7 +65,8 @@ public function __construct( \Magento\Framework\UrlInterface $urlBuilder, \Magento\Catalog\Helper\Product\ConfigurationPool $configurationPool, \Magento\Checkout\Helper\Data $checkoutHelper, - \Magento\Framework\Escaper $escaper = null + \Magento\Framework\Escaper $escaper = null, + ItemResolverInterface $itemResolver = null ) { $this->configurationPool = $configurationPool; $this->imageHelper = $imageHelper; @@ -68,6 +74,7 @@ public function __construct( $this->urlBuilder = $urlBuilder; $this->checkoutHelper = $checkoutHelper; $this->escaper = $escaper ?: ObjectManager::getInstance()->get(\Magento\Framework\Escaper::class); + $this->itemResolver = $itemResolver ?: ObjectManager::getInstance()->get(ItemResolverInterface::class); } /** @@ -119,7 +126,7 @@ protected function getOptionList() */ protected function getProductForThumbnail() { - return $this->getProduct(); + return $this->itemResolver->getFinalProduct($this->item); } /** diff --git a/app/code/Magento/Checkout/Model/Cart/ImageProvider.php b/app/code/Magento/Checkout/Model/Cart/ImageProvider.php index d8d0003d8ca7e..598d8efe85eda 100644 --- a/app/code/Magento/Checkout/Model/Cart/ImageProvider.php +++ b/app/code/Magento/Checkout/Model/Cart/ImageProvider.php @@ -5,8 +5,10 @@ */ namespace Magento\Checkout\Model\Cart; +use Magento\Checkout\CustomerData\DefaultItem; +use Magento\Framework\App\ObjectManager; + /** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @api */ class ImageProvider @@ -18,20 +20,29 @@ class ImageProvider /** * @var \Magento\Checkout\CustomerData\ItemPoolInterface + * @deprecated No need for the pool as images are resolved in the default item implementation + * @see \Magento\Checkout\CustomerData\DefaultItem::getProductForThumbnail */ protected $itemPool; + /** + * @var \Magento\Checkout\CustomerData\DefaultItem + */ + private $customerDataItem; + /** * @param \Magento\Quote\Api\CartItemRepositoryInterface $itemRepository * @param \Magento\Checkout\CustomerData\ItemPoolInterface $itemPool - * @codeCoverageIgnore + * @param DefaultItem|null $customerDataItem */ public function __construct( \Magento\Quote\Api\CartItemRepositoryInterface $itemRepository, - \Magento\Checkout\CustomerData\ItemPoolInterface $itemPool + \Magento\Checkout\CustomerData\ItemPoolInterface $itemPool, + \Magento\Checkout\CustomerData\DefaultItem $customerDataItem = null ) { $this->itemRepository = $itemRepository; $this->itemPool = $itemPool; + $this->customerDataItem = $customerDataItem ?: ObjectManager::getInstance()->get(DefaultItem::class); } /** @@ -45,9 +56,10 @@ public function getImages($cartId) $items = $this->itemRepository->getList($cartId); /** @var \Magento\Quote\Model\Quote\Item $cartItem */ foreach ($items as $cartItem) { - $allData = $this->itemPool->getItemData($cartItem); + $allData = $this->customerDataItem->getItemData($cartItem); $itemData[$cartItem->getItemId()] = $allData['product_image']; } + return $itemData; } } diff --git a/app/code/Magento/Checkout/Test/Unit/Block/Cart/Item/RendererTest.php b/app/code/Magento/Checkout/Test/Unit/Block/Cart/Item/RendererTest.php index d963fa2d76e6b..38c1288069d38 100644 --- a/app/code/Magento/Checkout/Test/Unit/Block/Cart/Item/RendererTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Block/Cart/Item/RendererTest.php @@ -5,8 +5,11 @@ */ namespace Magento\Checkout\Test\Unit\Block\Cart\Item; +use Magento\Catalog\Block\Product\Image; +use Magento\Catalog\Model\Product; use Magento\Checkout\Block\Cart\Item\Renderer; use Magento\Quote\Model\Quote\Item; +use Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -16,17 +19,22 @@ class RendererTest extends \PHPUnit\Framework\TestCase /** * @var Renderer */ - protected $_renderer; + private $renderer; /** * @var \PHPUnit_Framework_MockObject_MockObject */ - protected $layout; + private $layout; /** * @var \Magento\Catalog\Block\Product\ImageBuilder|\PHPUnit_Framework_MockObject_MockObject */ - protected $imageBuilder; + private $imageBuilder; + + /** + * @var ItemResolverInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $itemResolver; protected function setUp() { @@ -39,17 +47,20 @@ protected function setUp() ->getMock(); $context->expects($this->once()) ->method('getLayout') - ->will($this->returnValue($this->layout)); + ->willReturn($this->layout); $this->imageBuilder = $this->getMockBuilder(\Magento\Catalog\Block\Product\ImageBuilder::class) ->disableOriginalConstructor() ->getMock(); - $this->_renderer = $objectManagerHelper->getObject( + $this->itemResolver = $this->createMock(ItemResolverInterface::class); + + $this->renderer = $objectManagerHelper->getObject( \Magento\Checkout\Block\Cart\Item\Renderer::class, [ 'context' => $context, 'imageBuilder' => $this->imageBuilder, + 'itemResolver' => $this->itemResolver, ] ); } @@ -57,29 +68,34 @@ protected function setUp() public function testGetProductForThumbnail() { $product = $this->_initProduct(); - $productForThumbnail = $this->_renderer->getProductForThumbnail(); + $productForThumbnail = $this->renderer->getProductForThumbnail(); $this->assertEquals($product->getName(), $productForThumbnail->getName(), 'Invalid product was returned.'); } /** * Initialize product. * - * @return \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject + * @return Product|\PHPUnit_Framework_MockObject_MockObject */ protected function _initProduct() { - /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $product */ + /** @var Product|\PHPUnit_Framework_MockObject_MockObject $product */ $product = $this->createPartialMock( - \Magento\Catalog\Model\Product::class, + Product::class, ['getName', '__wakeup', 'getIdentities'] ); - $product->expects($this->any())->method('getName')->will($this->returnValue('Parent Product')); + $product->expects($this->any())->method('getName')->willReturn('Parent Product'); /** @var Item|\PHPUnit_Framework_MockObject_MockObject $item */ $item = $this->createMock(\Magento\Quote\Model\Quote\Item::class); - $item->expects($this->any())->method('getProduct')->will($this->returnValue($product)); + $item->expects($this->any())->method('getProduct')->willReturn($product); + + $this->itemResolver->expects($this->any()) + ->method('getFinalProduct') + ->with($item) + ->willReturn($product); - $this->_renderer->setItem($item); + $this->renderer->setItem($item); return $product; } @@ -89,14 +105,14 @@ public function testGetIdentities() $identities = [1 => 1, 2 => 2, 3 => 3]; $product->expects($this->exactly(2)) ->method('getIdentities') - ->will($this->returnValue($identities)); + ->willReturn($identities); - $this->assertEquals($product->getIdentities(), $this->_renderer->getIdentities()); + $this->assertEquals($product->getIdentities(), $this->renderer->getIdentities()); } public function testGetIdentitiesFromEmptyItem() { - $this->assertEmpty($this->_renderer->getIdentities()); + $this->assertEmpty($this->renderer->getIdentities()); } /** @@ -106,7 +122,7 @@ public function testGetIdentitiesFromEmptyItem() public function testGetProductPriceHtml() { $priceHtml = 'some price html'; - $product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + $product = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() ->getMock(); @@ -117,7 +133,7 @@ public function testGetProductPriceHtml() $this->layout->expects($this->atLeastOnce()) ->method('getBlock') ->with('product.price.render.default') - ->will($this->returnValue($priceRender)); + ->willReturn($priceRender); $priceRender->expects($this->once()) ->method('render') @@ -129,9 +145,9 @@ public function testGetProductPriceHtml() 'display_minimal_price' => true, 'zone' => \Magento\Framework\Pricing\Render::ZONE_ITEM_LIST ] - )->will($this->returnValue($priceHtml)); + )->willReturn($priceHtml); - $this->assertEquals($priceHtml, $this->_renderer->getProductPriceHtml($product)); + $this->assertEquals($priceHtml, $this->renderer->getProductPriceHtml($product)); } public function testGetActions() @@ -148,7 +164,7 @@ public function testGetActions() $this->layout->expects($this->once()) ->method('getChildName') - ->with($this->_renderer->getNameInLayout(), 'actions') + ->with($this->renderer->getNameInLayout(), 'actions') ->willReturn($blockNameInLayout); $this->layout->expects($this->once()) ->method('getBlock') @@ -169,14 +185,14 @@ public function testGetActions() ->method('toHtml') ->willReturn($blockHtml); - $this->assertEquals($blockHtml, $this->_renderer->getActions($itemMock)); + $this->assertEquals($blockHtml, $this->renderer->getActions($itemMock)); } public function testGetActionsWithNoBlock() { $this->layout->expects($this->once()) ->method('getChildName') - ->with($this->_renderer->getNameInLayout(), 'actions') + ->with($this->renderer->getNameInLayout(), 'actions') ->willReturn(false); /** @@ -186,25 +202,19 @@ public function testGetActionsWithNoBlock() ->disableOriginalConstructor() ->getMock(); - $this->assertEquals('', $this->_renderer->getActions($itemMock)); + $this->assertEquals('', $this->renderer->getActions($itemMock)); } public function testGetImage() { $imageId = 'test_image_id'; $attributes = []; - - $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) - ->disableOriginalConstructor() - ->getMock(); - - $imageMock = $this->getMockBuilder(\Magento\Catalog\Block\Product\Image::class) - ->disableOriginalConstructor() - ->getMock(); + $product = $this->createMock(Product::class); + $imageMock = $this->createMock(Image::class); $this->imageBuilder->expects($this->once()) ->method('setProduct') - ->with($productMock) + ->with($product) ->willReturnSelf(); $this->imageBuilder->expects($this->once()) ->method('setImageId') @@ -219,8 +229,8 @@ public function testGetImage() ->willReturn($imageMock); $this->assertInstanceOf( - \Magento\Catalog\Block\Product\Image::class, - $this->_renderer->getImage($productMock, $imageId, $attributes) + Image::class, + $this->renderer->getImage($product, $imageId, $attributes) ); } } diff --git a/app/code/Magento/Checkout/Test/Unit/CustomerData/DefaultItemTest.php b/app/code/Magento/Checkout/Test/Unit/CustomerData/DefaultItemTest.php index 8a7c2e951dd72..86825f6ca1da0 100644 --- a/app/code/Magento/Checkout/Test/Unit/CustomerData/DefaultItemTest.php +++ b/app/code/Magento/Checkout/Test/Unit/CustomerData/DefaultItemTest.php @@ -5,12 +5,14 @@ */ namespace Magento\Checkout\Test\Unit\CustomerData; +use Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface; + class DefaultItemTest extends \PHPUnit\Framework\TestCase { /** * @var \Magento\Checkout\CustomerData\DefaultItem */ - protected $model; + private $model; /** * @var \Magento\Catalog\Helper\Image @@ -22,6 +24,14 @@ class DefaultItemTest extends \PHPUnit\Framework\TestCase */ private $configurationPool; + /** + * @var ItemResolverInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $itemResolver; + + /** + * @inheritdoc + */ protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -35,12 +45,14 @@ protected function setUp() $checkoutHelper = $this->getMockBuilder(\Magento\Checkout\Helper\Data::class) ->setMethods(['formatPrice'])->disableOriginalConstructor()->getMock(); $checkoutHelper->expects($this->any())->method('formatPrice')->willReturn(5); + $this->itemResolver = $this->createMock(ItemResolverInterface::class); $this->model = $objectManager->getObject( \Magento\Checkout\CustomerData\DefaultItem::class, [ 'imageHelper' => $this->imageHelper, 'configurationPool' => $this->configurationPool, - 'checkoutHelper' => $checkoutHelper + 'checkoutHelper' => $checkoutHelper, + 'itemResolver' => $this->itemResolver, ] ); } @@ -72,6 +84,10 @@ public function testGetItemData() $this->imageHelper->expects($this->any())->method('getWidth')->willReturn(100); $this->imageHelper->expects($this->any())->method('getHeight')->willReturn(100); $this->configurationPool->expects($this->any())->method('getByProductType')->willReturn($product); + $this->itemResolver->expects($this->any()) + ->method('getFinalProduct') + ->with($item) + ->willReturn($product); $itemData = $this->model->getItemData($item); $this->assertArrayHasKey('options', $itemData); diff --git a/app/code/Magento/Checkout/Test/Unit/Model/Cart/ImageProviderTest.php b/app/code/Magento/Checkout/Test/Unit/Model/Cart/ImageProviderTest.php index 5330d93b46f6a..993a01d922c1c 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/Cart/ImageProviderTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/Cart/ImageProviderTest.php @@ -11,25 +11,34 @@ class ImageProviderTest extends \PHPUnit\Framework\TestCase /** * @var \Magento\Checkout\Model\Cart\ImageProvider */ - public $model; + private $model; /** * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Quote\Api\CartItemRepositoryInterface */ - protected $itemRepositoryMock; + private $itemRepositoryMock; /** * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Checkout\CustomerData\ItemPoolInterface */ - protected $itemPoolMock; + private $itemPoolMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Checkout\CustomerData\DefaultItem + */ + private $customerItem; protected function setUp() { $this->itemRepositoryMock = $this->createMock(\Magento\Quote\Api\CartItemRepositoryInterface::class); $this->itemPoolMock = $this->createMock(\Magento\Checkout\CustomerData\ItemPoolInterface::class); + $this->customerItem = $this->getMockBuilder(\Magento\Checkout\CustomerData\DefaultItem::class) + ->disableOriginalConstructor() + ->getMock(); $this->model = new \Magento\Checkout\Model\Cart\ImageProvider( $this->itemRepositoryMock, - $this->itemPoolMock + $this->itemPoolMock, + $this->customerItem ); } @@ -44,7 +53,7 @@ public function testGetImages() $expectedResult = [$itemId => $itemData['product_image']]; $this->itemRepositoryMock->expects($this->once())->method('getList')->with($cartId)->willReturn([$itemMock]); - $this->itemPoolMock->expects($this->once())->method('getItemData')->with($itemMock)->willReturn($itemData); + $this->customerItem->expects($this->once())->method('getItemData')->with($itemMock)->willReturn($itemData); $this->assertEquals($expectedResult, $this->model->getImages($cartId)); } diff --git a/app/code/Magento/Checkout/etc/frontend/di.xml b/app/code/Magento/Checkout/etc/frontend/di.xml index 940b60e796c9d..d80f88786c87b 100644 --- a/app/code/Magento/Checkout/etc/frontend/di.xml +++ b/app/code/Magento/Checkout/etc/frontend/di.xml @@ -39,7 +39,7 @@ </type> <preference for="Magento\Checkout\CustomerData\ItemPoolInterface" type="Magento\Checkout\CustomerData\ItemPool"/> - <type name="Magento\Checkout\CustomerData\ItemPoolInterface"> + <type name="Magento\Checkout\CustomerData\ItemPool"> <arguments> <argument name="defaultItemId" xsi:type="string">Magento\Checkout\CustomerData\DefaultItem</argument> </arguments> diff --git a/app/code/Magento/ConfigurableProduct/Block/Cart/Item/Renderer/Configurable.php b/app/code/Magento/ConfigurableProduct/Block/Cart/Item/Renderer/Configurable.php index 63eec3bb698bb..4c7c5df736112 100644 --- a/app/code/Magento/ConfigurableProduct/Block/Cart/Item/Renderer/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Block/Cart/Item/Renderer/Configurable.php @@ -19,6 +19,8 @@ class Configurable extends Renderer implements IdentityInterface { /** * Path in config to the setting which defines if parent or child product should be used to generate a thumbnail. + * @deprecated moved to model because of class refactoring + * @see \Magento\ConfigurableProduct\Model\Product\Configuration\Item\ItemProductResolver::CONFIG_THUMBNAIL_SOURCE */ const CONFIG_THUMBNAIL_SOURCE = 'checkout/cart/configurable_product_image'; @@ -55,32 +57,6 @@ public function getOptionList() return $this->_productConfig->getOptions($this->getItem()); } - /** - * {@inheritdoc} - */ - public function getProductForThumbnail() - { - /** - * Show parent product thumbnail if it must be always shown according to the related setting in system config - * or if child thumbnail is not available - */ - if ($this->_scopeConfig->getValue( - self::CONFIG_THUMBNAIL_SOURCE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) == ThumbnailSource::OPTION_USE_PARENT_IMAGE || - !( - $this->getChildProduct() && - $this->getChildProduct()->getThumbnail() && - $this->getChildProduct()->getThumbnail() != 'no_selection' - ) - ) { - $product = $this->getProduct(); - } else { - $product = $this->getChildProduct(); - } - return $product; - } - /** * Return identifiers for produced content * diff --git a/app/code/Magento/ConfigurableProduct/CustomerData/ConfigurableItem.php b/app/code/Magento/ConfigurableProduct/CustomerData/ConfigurableItem.php index 67e51b3797540..e8169fd926ae5 100644 --- a/app/code/Magento/ConfigurableProduct/CustomerData/ConfigurableItem.php +++ b/app/code/Magento/ConfigurableProduct/CustomerData/ConfigurableItem.php @@ -11,6 +11,9 @@ /** * Configurable item + * + * @deprecated moved to model because of class refactoring + * @see \Magento\ConfigurableProduct\Model\Product\Configuration\Item\ItemProductResolver */ class ConfigurableItem extends DefaultItem { diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php b/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php new file mode 100644 index 0000000000000..d63ff06b7a483 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Configuration/Item/ItemProductResolver.php @@ -0,0 +1,85 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Model\Product\Configuration\Item; + +use Magento\Catalog\Model\Config\Source\Product\Thumbnail; +use Magento\Catalog\Model\Product\Configuration\Item\ItemInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Catalog\Model\Product; + +/** + * {@inheritdoc} + */ +class ItemProductResolver implements ItemResolverInterface +{ + /** + * Path in config to the setting which defines if parent or child product should be used to generate a thumbnail. + */ + const CONFIG_THUMBNAIL_SOURCE = 'checkout/cart/configurable_product_image'; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @param ScopeConfigInterface $scopeConfig + */ + public function __construct(ScopeConfigInterface $scopeConfig) + { + $this->scopeConfig = $scopeConfig; + } + + /** + * {@inheritdoc} + */ + public function getFinalProduct(ItemInterface $item): ProductInterface + { + /** + * Show parent product thumbnail if it must be always shown according to the related setting in system config + * or if child thumbnail is not available. + */ + $parentProduct = $item->getProduct(); + $finalProduct = $parentProduct; + $childProduct = $this->getChildProduct($item); + + if ($childProduct !== $parentProduct) { + $configValue = $this->scopeConfig->getValue( + self::CONFIG_THUMBNAIL_SOURCE, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + $childThumb = $childProduct->getData('thumbnail'); + $finalProduct = + ($configValue == Thumbnail::OPTION_USE_PARENT_IMAGE) || (!$childThumb || $childThumb == 'no_selection') + ? $parentProduct + : $childProduct; + } + + return $finalProduct; + } + + /** + * Get item configurable child product. + * + * @param ItemInterface $item + * @return Product + */ + private function getChildProduct(ItemInterface $item): Product + { + $option = $item->getOptionByCode('simple_product'); + $product = $item->getProduct(); + + if ($option) { + $product = $option->getProduct(); + } + + return $product; + } +} diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml new file mode 100644 index 0000000000000..b9da545eb8bde --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml @@ -0,0 +1,78 @@ +<?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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <!-- + + Create a configurable product with three options for color: red, white, and blue + + Expected start state = logged in as an admin + End state = on the product edit page in the admin + + --> + <actionGroup name="createConfigurableProduct"> + <arguments> + <argument name="product" defaultValue="_defaultProduct"/> + <argument name="category" defaultValue="_defaultCategory"/> + </arguments> + + <!-- fill in basic configurable product values --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> + <waitForPageLoad time="30" stepKey="wait1"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickOnAddProductToggle"/> + <click selector="{{AdminProductGridActionSection.addConfigurableProduct}}" stepKey="clickOnAddConfigurableProduct"/> + <fillField userInput="{{product.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> + <fillField userInput="{{product.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillSKU"/> + <fillField userInput="{{product.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> + <fillField userInput="{{product.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{category.name}}]" stepKey="fillCategory"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> + <fillField userInput="{{product.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> + + <!-- create configurations for colors the product is available in --> + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickOnCreateConfigurations"/> + <click selector="{{AdminCreateProductConfigurationsPanelSection.createNewAttribute}}" stepKey="clickOnNewAttribute"/> + <waitForPageLoad stepKey="waitForIFrame"/> + <switchToIFrame selector="{{AdminNewAttributePanel.newAttributeIFrame}}" stepKey="switchToNewAttributeIFrame"/> + <fillField selector="{{AdminNewAttributePanel.defaultLabel}}" userInput="{{colorProductAttribute.default_label}}" stepKey="fillDefaultLabel"/> + <click selector="{{AdminNewAttributePanel.saveAttribute}}" stepKey="clickOnNewAttributePanel"/> + <waitForPageLoad stepKey="waitForSaveAttribute"/> + <switchToIFrame stepKey="switchOutOfIFrame"/> + <waitForPageLoad stepKey="waitForFilters"/> + <click selector="{{AdminCreateProductConfigurationsPanelSection.filters}}" stepKey="clickOnFilters"/> + <fillField userInput="{{colorProductAttribute.default_label}}" selector="{{AdminCreateProductConfigurationsPanelSection.attributeCode}}" stepKey="fillFilterAttributeCodeField"/> + <click selector="{{AdminCreateProductConfigurationsPanelSection.applyFilters}}" stepKey="clickApplyFiltersButton"/> + <click selector="{{AdminCreateProductConfigurationsPanelSection.firstCheckbox}}" stepKey="clickOnFirstCheckbox"/> + <click selector="{{AdminCreateProductConfigurationsPanelSection.next}}" stepKey="clickOnNextButton1"/> + <click selector="{{AdminCreateProductConfigurationsPanelSection.createNewValue}}" stepKey="clickOnCreateNewValue1"/> + <fillField userInput="{{colorProductAttribute1.name}}" selector="{{AdminCreateProductConfigurationsPanelSection.attributeName}}" stepKey="fillFieldForNewAttribute1"/> + <click selector="{{AdminCreateProductConfigurationsPanelSection.saveAttribute}}" stepKey="clickOnSaveNewAttribute1"/> + <click selector="{{AdminCreateProductConfigurationsPanelSection.createNewValue}}" stepKey="clickOnCreateNewValue2"/> + <fillField userInput="{{colorProductAttribute2.name}}" selector="{{AdminCreateProductConfigurationsPanelSection.attributeName}}" stepKey="fillFieldForNewAttribute2"/> + <click selector="{{AdminCreateProductConfigurationsPanelSection.saveAttribute}}" stepKey="clickOnSaveNewAttribute2"/> + <click selector="{{AdminCreateProductConfigurationsPanelSection.createNewValue}}" stepKey="clickOnCreateNewValue3"/> + <fillField userInput="{{colorProductAttribute3.name}}" selector="{{AdminCreateProductConfigurationsPanelSection.attributeName}}" stepKey="fillFieldForNewAttribute3"/> + <click selector="{{AdminCreateProductConfigurationsPanelSection.saveAttribute}}" stepKey="clickOnSaveNewAttribute3"/> + <click selector="{{AdminCreateProductConfigurationsPanelSection.selectAll}}" stepKey="clickOnSelectAll"/> + <click selector="{{AdminCreateProductConfigurationsPanelSection.next}}" stepKey="clickOnNextButton2"/> + <click selector="{{AdminCreateProductConfigurationsPanelSection.applyUniquePricesByAttributeToEachSku}}" stepKey="clickOnApplyUniquePricesByAttributeToEachSku"/> + <selectOption selector="{{AdminCreateProductConfigurationsPanelSection.selectAttribute}}" userInput="{{colorProductAttribute.default_label}}" stepKey="selectAttributes"/> + <fillField selector="{{AdminCreateProductConfigurationsPanelSection.attribute1}}" userInput="{{colorProductAttribute1.price}}" stepKey="fillAttributePrice1"/> + <fillField selector="{{AdminCreateProductConfigurationsPanelSection.attribute2}}" userInput="{{colorProductAttribute2.price}}" stepKey="fillAttributePrice2"/> + <fillField selector="{{AdminCreateProductConfigurationsPanelSection.attribute3}}" userInput="{{colorProductAttribute3.price}}" stepKey="fillAttributePrice3"/> + <click selector="{{AdminCreateProductConfigurationsPanelSection.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> + <fillField selector="{{AdminCreateProductConfigurationsPanelSection.quantity}}" userInput="1" stepKey="enterAttributeQuantity"/> + <click selector="{{AdminCreateProductConfigurationsPanelSection.next}}" stepKey="clickOnNextButton3"/> + <click selector="{{AdminCreateProductConfigurationsPanelSection.next}}" stepKey="clickOnNextButton4"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton2"/> + <click selector="{{AdminChooseAffectedAttributeSetSection.confirm}}" stepKey="clickOnConfirmInPopup"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessage"/> + <seeInTitle userInput="{{product.name}}" stepKey="seeProductNameInTitle"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Page/AdminProductCreatePage.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Page/AdminProductCreatePage.xml index a9973c5fabbb9..15392480108a5 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Page/AdminProductCreatePage.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Page/AdminProductCreatePage.xml @@ -10,5 +10,8 @@ xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> <page name="AdminProductCreatePage" url="catalog/product/new/set/{{set}}/type/{{type}}/" area="admin" module="Magento_Catalog" parameterized="true"> <section name="AdminNewAttributePanel"/> + <section name="AdminProductFormConfigurationsSection"/> + <section name="AdminCreateProductConfigurationsPanelSection"/> + <section name="AdminChooseAffectedAttributeSetSection"/> </page> </pages> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Page/StorefrontCustomerWishlistPage.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Page/StorefrontCustomerWishlistPage.xml new file mode 100644 index 0000000000000..7b7b5e8b39aa6 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Page/StorefrontCustomerWishlistPage.xml @@ -0,0 +1,15 @@ +<?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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + <page name="StorefrontCustomerWishlistPage" url="/wishlist/" area="storefront" module="Magento_Wishlist"> + <section name="StorefrontCustomerWishlistSection" /> + <section name="StorefrontCustomerWishlistProductSection" /> + </page> +</pages> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminChooseAffectedAttributeSetSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminChooseAffectedAttributeSetSection.xml new file mode 100644 index 0000000000000..ca3e2e9cee1b1 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminChooseAffectedAttributeSetSection.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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminChooseAffectedAttributeSetSection"> + <element name="confirm" type="button" selector="button[data-index='confirm_button']" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml new file mode 100644 index 0000000000000..2c20d69189257 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml @@ -0,0 +1,31 @@ +<?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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminCreateProductConfigurationsPanelSection"> + <element name="next" type="button" selector=".steps-wizard-navigation .action-next-step" timeout="30"/> + <element name="createNewAttribute" type="button" selector=".select-attributes-actions button[title='Create New Attribute']" timeout="30"/> + <element name="filters" type="button" selector="button[data-action='grid-filter-expand']"/> + <element name="attributeCode" type="input" selector=".admin__control-text[name='attribute_code']"/> + <element name="applyFilters" type="button" selector="button[data-action='grid-filter-apply']" timeout="30"/> + <element name="firstCheckbox" type="input" selector="tr[data-repeat-index='0'] .admin__control-checkbox"/> + + <element name="selectAll" type="button" selector=".action-select-all"/> + <element name="createNewValue" type="input" selector=".action-create-new" timeout="30"/> + <element name="attributeName" type="input" selector="li[data-attribute-option-title=''] .admin__field-create-new .admin__control-text"/> + <element name="saveAttribute" type="button" selector="li[data-attribute-option-title=''] .action-save" timeout="30"/> + <element name="applyUniquePricesByAttributeToEachSku" type="radio" selector=".admin__field-label[for='apply-unique-prices-radio']"/> + <element name="selectAttribute" type="select" selector="#select-each-price" timeout="30"/> + <element name="attribute1" type="input" selector="#apply-single-price-input-0"/> + <element name="attribute2" type="input" selector="#apply-single-price-input-1"/> + <element name="attribute3" type="input" selector="#apply-single-price-input-2"/> + <element name="applySingleQuantityToEachSkus" type="radio" selector=".admin__field-label[for='apply-single-inventory-radio']" timeout="30"/> + <element name="quantity" type="input" selector="#apply-single-inventory-input"/> + </section> +</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml new file mode 100644 index 0000000000000..5ae70488d164d --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml @@ -0,0 +1,26 @@ +<?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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminProductFormConfigurationsSection"> + <element name="sectionHeader" type="text" selector=".admin__collapsible-block-wrapper[data-index='configurable']"/> + <element name="createConfigurations" type="button" selector="button[data-index='create_configurable_products_button']" timeout="30"/> + <element name="currentVariationsRows" type="button" selector=".data-row"/> + <element name="currentVariationsNameCells" type="textarea" selector=".admin__control-fields[data-index='name_container']"/> + <element name="currentVariationsSkuCells" type="textarea" selector=".admin__control-fields[data-index='sku_container']"/> + <element name="currentVariationsPriceCells" type="textarea" selector=".admin__control-fields[data-index='price_container']"/> + <element name="currentVariationsQuantityCells" type="textarea" selector=".admin__control-fields[data-index='quantity_container']"/> + <element name="currentVariationsAttributesCells" type="textarea" selector=".admin__control-fields[data-index='attributes']"/> + <element name="currentVariationsStatusCells" type="textarea" selector="._no-header[data-index='status']"/> + <element name="actionsBtn" type="button" selector="(//button[@class='action-select']/span[contains(text(), 'Select')])[{{var1}}]" parameterized="true"/> + <element name="removeProductBtn" type="button" selector="//a[text()='Remove Product']"/> + <element name="disableProductBtn" type="button" selector="//a[text()='Disable Product']"/> + <element name="enableProductBtn" type="button" selector="//a[text()='Enable Product']"/> + </section> +</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml new file mode 100644 index 0000000000000..da738d95d038e --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="StorefrontCustomerWishlistProductSection"> + <element name="productImageByImageName" type="text" selector="//main//li//a//img[contains(@src, '{{var1}}')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontCustomerWishlistSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontCustomerWishlistSection.xml new file mode 100644 index 0000000000000..c7d270ab306df --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontCustomerWishlistSection.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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="StorefrontCustomerWishlistSection"> + <element name="successMsg" type="text" selector="div.message-success.success.message"/> + </section> +</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontCustomerWishlistSidebarSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontCustomerWishlistSidebarSection.xml new file mode 100644 index 0000000000000..1fb2dc07393a7 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontCustomerWishlistSidebarSection.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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="StorefrontCustomerWishlistSidebarSection"> + <element name="productImageByImageName" type="text" selector="//main//ol[@id='wishlist-sidebar']//a//img[contains(@src, '{{var1}}')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontProductInfoMainSection.xml new file mode 100644 index 0000000000000..0db3502eba779 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontProductInfoMainSection.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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="StorefrontProductInfoMainSection"> + <element name="productAttributeOptionsSelectButton" type="select" selector="#product-options-wrapper .super-attribute-select"/> + </section> +</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Cart/Item/Renderer/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Cart/Item/Renderer/ConfigurableTest.php index e199841cbcdc4..f324494f5918a 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Cart/Item/Renderer/ConfigurableTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Cart/Item/Renderer/ConfigurableTest.php @@ -10,155 +10,59 @@ class ConfigurableTest extends \PHPUnit\Framework\TestCase { - /** @var \Magento\Framework\View\ConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $_configManager; + /** + * @var \Magento\Framework\View\ConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $configManager; - /** @var \Magento\Catalog\Helper\Image|\PHPUnit_Framework_MockObject_MockObject */ - protected $_imageHelper; + /** + * @var \Magento\Catalog\Helper\Image|\PHPUnit_Framework_MockObject_MockObject + */ + private $imageHelper; - /** @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $_scopeConfig; + /** + * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $scopeConfig; - /** @var \PHPUnit_Framework_MockObject_MockObject */ - protected $productConfigMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $productConfigMock; - /** @var Renderer */ - protected $_renderer; + /** + * @var Renderer + */ + private $renderer; protected function setUp() { parent::setUp(); $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->_configManager = $this->createMock(\Magento\Framework\View\ConfigInterface::class); - $this->_imageHelper = $this->createPartialMock( + $this->configManager = $this->createMock(\Magento\Framework\View\ConfigInterface::class); + $this->imageHelper = $this->createPartialMock( \Magento\Catalog\Helper\Image::class, ['init', 'resize', '__toString'] ); - $this->_scopeConfig = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); + $this->scopeConfig = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); $this->productConfigMock = $this->createMock(\Magento\Catalog\Helper\Product\Configuration::class); - $this->_renderer = $objectManagerHelper->getObject( + $this->renderer = $objectManagerHelper->getObject( \Magento\ConfigurableProduct\Block\Cart\Item\Renderer\Configurable::class, [ - 'viewConfig' => $this->_configManager, - 'imageHelper' => $this->_imageHelper, - 'scopeConfig' => $this->_scopeConfig, - 'productConfig' => $this->productConfigMock + 'viewConfig' => $this->configManager, + 'imageHelper' => $this->imageHelper, + 'scopeConfig' => $this->scopeConfig, + 'productConfig' => $this->productConfigMock, ] ); } - /** - * Child thumbnail is available and config option is not set to use parent thumbnail. - */ - public function testGetProductForThumbnail() - { - $childHasThumbnail = true; - $useParentThumbnail = false; - $products = $this->_initProducts($childHasThumbnail, $useParentThumbnail); - - $productForThumbnail = $this->_renderer->getProductForThumbnail(); - $this->assertSame( - $products['childProduct'], - $productForThumbnail, - 'Child product was expected to be returned.' - ); - } - - /** - * Child thumbnail is not available and config option is not set to use parent thumbnail. - */ - public function testGetProductForThumbnailChildThumbnailNotAvailable() - { - $childHasThumbnail = false; - $useParentThumbnail = false; - $products = $this->_initProducts($childHasThumbnail, $useParentThumbnail); - - $productForThumbnail = $this->_renderer->getProductForThumbnail(); - $this->assertSame( - $products['parentProduct'], - $productForThumbnail, - 'Parent product was expected to be returned.' - ); - } - - /** - * Child thumbnail is available and config option is set to use parent thumbnail. - */ - public function testGetProductForThumbnailConfigUseParent() - { - $childHasThumbnail = true; - $useParentThumbnail = true; - $products = $this->_initProducts($childHasThumbnail, $useParentThumbnail); - - $productForThumbnail = $this->_renderer->getProductForThumbnail(); - $this->assertSame( - $products['parentProduct'], - $productForThumbnail, - 'Parent product was expected to be returned ' . - 'if "checkout/cart/configurable_product_image option" is set to "parent" in system config.' - ); - } - - /** - * Initialize parent configurable product and child product. - * - * @param bool $childHasThumbnail - * @param bool $useParentThumbnail - * @return \Magento\Catalog\Model\Product[]|\PHPUnit_Framework_MockObject_MockObject[] - */ - protected function _initProducts($childHasThumbnail = true, $useParentThumbnail = false) - { - /** Set option which can force usage of parent product thumbnail when configurable product is displayed */ - $thumbnailToBeUsed = $useParentThumbnail - ? ThumbnailSource::OPTION_USE_PARENT_IMAGE - : ThumbnailSource::OPTION_USE_OWN_IMAGE; - $this->_scopeConfig->expects( - $this->any() - )->method( - 'getValue' - )->with( - Renderer::CONFIG_THUMBNAIL_SOURCE - )->will( - $this->returnValue($thumbnailToBeUsed) - ); - - /** Initialized parent product */ - /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $parentProduct */ - $parentProduct = $this->createMock(\Magento\Catalog\Model\Product::class); - - /** Initialize child product */ - /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $childProduct */ - $childProduct = $this->createPartialMock(\Magento\Catalog\Model\Product::class, ['getThumbnail', '__wakeup']); - $childThumbnail = $childHasThumbnail ? 'thumbnail.jpg' : 'no_selection'; - $childProduct->expects($this->any())->method('getThumbnail')->will($this->returnValue($childThumbnail)); - - /** Mock methods which return parent and child products */ - /** @var \Magento\Quote\Model\Quote\Item\Option|\PHPUnit_Framework_MockObject_MockObject $itemOption */ - $itemOption = $this->createMock(\Magento\Quote\Model\Quote\Item\Option::class); - $itemOption->expects($this->any())->method('getProduct')->will($this->returnValue($childProduct)); - /** @var \Magento\Quote\Model\Quote\Item|\PHPUnit_Framework_MockObject_MockObject $item */ - $item = $this->createMock(\Magento\Quote\Model\Quote\Item::class); - $item->expects($this->any())->method('getProduct')->will($this->returnValue($parentProduct)); - $item->expects( - $this->any() - )->method( - 'getOptionByCode' - )->with( - 'simple_product' - )->will( - $this->returnValue($itemOption) - ); - $this->_renderer->setItem($item); - - return ['parentProduct' => $parentProduct, 'childProduct' => $childProduct]; - } - public function testGetOptionList() { $itemMock = $this->createMock(\Magento\Quote\Model\Quote\Item::class); - $this->_renderer->setItem($itemMock); + $this->renderer->setItem($itemMock); $this->productConfigMock->expects($this->once())->method('getOptions')->with($itemMock); - $this->_renderer->getOptionList(); + $this->renderer->getOptionList(); } public function testGetIdentities() @@ -168,7 +72,7 @@ public function testGetIdentities() $product->expects($this->exactly(2))->method('getIdentities')->will($this->returnValue($productTags)); $item = $this->createMock(\Magento\Quote\Model\Quote\Item::class); $item->expects($this->exactly(2))->method('getProduct')->will($this->returnValue($product)); - $this->_renderer->setItem($item); - $this->assertEquals(array_merge($productTags, $productTags), $this->_renderer->getIdentities()); + $this->renderer->setItem($item); + $this->assertEquals(array_merge($productTags, $productTags), $this->renderer->getIdentities()); } } diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php new file mode 100644 index 0000000000000..b940ef0712ad3 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php @@ -0,0 +1,99 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Test\Unit\Model\Product\Configuration; + +use Magento\Catalog\Model\Config\Source\Product\Thumbnail; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Configuration\Item\ItemInterface; +use Magento\Catalog\Model\Product\Configuration\Item\Option\OptionInterface; +use Magento\ConfigurableProduct\Model\Product\Configuration\Item\ItemProductResolver; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Store\Model\ScopeInterface; + +/** + * Tests \Magento\ConfigurableProduct\Model\Product\Configuration\Item\ItemProductResolver + */ +class ItemProductResolverTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ItemProductResolver + */ + private $resolver; + + /** + * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $scopeConfig; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManagerHelper = new ObjectManager($this); + $this->scopeConfig = $this->createPartialMock(ScopeConfigInterface::class, ['getValue', 'isSetFlag']); + $this->resolver = $objectManagerHelper->getObject( + ItemProductResolver::class, + ['scopeConfig' => $this->scopeConfig] + ); + } + + /** + * @param bool $existOption + * @param string $configImageSource + * @return void + * @dataProvider getFinalProductDataProvider + */ + public function testGetFinalProduct(bool $existOption, string $configImageSource) + { + $option = null; + $parentProduct = $this->createMock(Product::class); + $finalProduct = $parentProduct; + + if ($existOption) { + $childProduct = $this->createPartialMock(Product::class, ['getData']); + $childProduct->expects($this->once())->method('getData')->with('thumbnail')->willReturn('someImage'); + + $option = $this->createPartialMock( + OptionInterface::class, + ['getProduct', 'getValue'] + ); + $option->expects($this->once())->method('getProduct')->willReturn($childProduct); + + $this->scopeConfig->expects($this->once()) + ->method('getValue') + ->with(ItemProductResolver::CONFIG_THUMBNAIL_SOURCE, ScopeInterface::SCOPE_STORE) + ->willReturn($configImageSource); + + $finalProduct = ($configImageSource == Thumbnail::OPTION_USE_PARENT_IMAGE)? $parentProduct: $childProduct; + } + + $item = $this->createPartialMock( + ItemInterface::class, + ['getProduct', 'getOptionByCode', 'getFileDownloadParams'] + ); + $item->expects($this->exactly(2))->method('getProduct')->willReturn($parentProduct); + $item->expects($this->once())->method('getOptionByCode')->with('simple_product')->willReturn($option); + + $this->assertEquals($finalProduct, $this->resolver->getFinalProduct($item)); + } + + /** + * @return array + */ + public function getFinalProductDataProvider(): array + { + return [ + [false, Thumbnail::OPTION_USE_PARENT_IMAGE], + [true, Thumbnail::OPTION_USE_PARENT_IMAGE], + [true, Thumbnail::OPTION_USE_OWN_IMAGE], + ]; + } +} diff --git a/app/code/Magento/ConfigurableProduct/etc/di.xml b/app/code/Magento/ConfigurableProduct/etc/di.xml index 43e70f3766dca..dfbad0dd5a764 100644 --- a/app/code/Magento/ConfigurableProduct/etc/di.xml +++ b/app/code/Magento/ConfigurableProduct/etc/di.xml @@ -221,4 +221,11 @@ <type name="Magento\Catalog\Model\Product"> <plugin name="product_identities_extender" type="Magento\ConfigurableProduct\Model\Plugin\ProductIdentitiesExtender" /> </type> + <type name="Magento\Catalog\Model\Product\Configuration\Item\ItemResolverComposite"> + <arguments> + <argument name="itemResolvers" xsi:type="array"> + <item name="configurable" xsi:type="string">Magento\ConfigurableProduct\Model\Product\Configuration\Item\ItemProductResolver</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/ConfigurableProduct/etc/frontend/di.xml b/app/code/Magento/ConfigurableProduct/etc/frontend/di.xml index 592b0292c98ab..bb830c36b929d 100644 --- a/app/code/Magento/ConfigurableProduct/etc/frontend/di.xml +++ b/app/code/Magento/ConfigurableProduct/etc/frontend/di.xml @@ -7,13 +7,6 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> - <type name="Magento\Checkout\CustomerData\ItemPoolInterface"> - <arguments> - <argument name="itemMap" xsi:type="array"> - <item name="configurable" xsi:type="string">Magento\ConfigurableProduct\CustomerData\ConfigurableItem</item> - </argument> - </arguments> - </type> <type name="Magento\ConfigurableProduct\Model\ResourceModel\Attribute\OptionSelectBuilderInterface"> <plugin name="Magento_ConfigurableProduct_Plugin_Model_ResourceModel_Attribute_InStockOptionSelectBuilder" type="Magento\ConfigurableProduct\Plugin\Model\ResourceModel\Attribute\InStockOptionSelectBuilder"/> </type> diff --git a/app/code/Magento/GroupedProduct/Block/Cart/Item/Renderer/Grouped.php b/app/code/Magento/GroupedProduct/Block/Cart/Item/Renderer/Grouped.php index a97b1c246741e..e85d4eeca730a 100644 --- a/app/code/Magento/GroupedProduct/Block/Cart/Item/Renderer/Grouped.php +++ b/app/code/Magento/GroupedProduct/Block/Cart/Item/Renderer/Grouped.php @@ -19,6 +19,8 @@ class Grouped extends Renderer implements IdentityInterface { /** * Path in config to the setting which defines if parent or child product should be used to generate a thumbnail. + * @deprecated moved to model because of class refactoring + * @see \Magento\GroupedProduct\Model\Product\Configuration\Item\ItemProductResolver::CONFIG_THUMBNAIL_SOURCE */ const CONFIG_THUMBNAIL_SOURCE = 'checkout/cart/grouped_product_image'; @@ -36,28 +38,6 @@ public function getGroupedProduct() return $this->getProduct(); } - /** - * {@inheritdoc} - */ - public function getProductForThumbnail() - { - /** - * Show grouped product thumbnail if it must be always shown according to the related setting in system config - * or if child product thumbnail is not available - */ - if ($this->_scopeConfig->getValue( - self::CONFIG_THUMBNAIL_SOURCE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) == ThumbnailSource::OPTION_USE_PARENT_IMAGE || - !($this->getProduct()->getThumbnail() && $this->getProduct()->getThumbnail() != 'no_selection') - ) { - $product = $this->getGroupedProduct(); - } else { - $product = $this->getProduct(); - } - return $product; - } - /** * Return identifiers for produced content * diff --git a/app/code/Magento/GroupedProduct/CustomerData/GroupedItem.php b/app/code/Magento/GroupedProduct/CustomerData/GroupedItem.php index a38ce3ac65296..a33c33ce85ee9 100644 --- a/app/code/Magento/GroupedProduct/CustomerData/GroupedItem.php +++ b/app/code/Magento/GroupedProduct/CustomerData/GroupedItem.php @@ -8,6 +8,10 @@ use Magento\Catalog\Model\Config\Source\Product\Thumbnail as ThumbnailSource; use Magento\Checkout\CustomerData\DefaultItem; +/** + * @deprecated moved to model because of class refactoring + * @see \Magento\GroupedProduct\Model\Product\Configuration\Item\ItemProductResolver + */ class GroupedItem extends DefaultItem { /** diff --git a/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php b/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php new file mode 100644 index 0000000000000..832823cd3b54c --- /dev/null +++ b/app/code/Magento/GroupedProduct/Model/Product/Configuration/Item/ItemProductResolver.php @@ -0,0 +1,86 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GroupedProduct\Model\Product\Configuration\Item; + +use Magento\Catalog\Model\Config\Source\Product\Thumbnail; +use Magento\Catalog\Model\Product\Configuration\Item\ItemInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface; + +/** + * {@inheritdoc} + */ +class ItemProductResolver implements ItemResolverInterface +{ + /** + * Path in config to the setting which defines if parent or child product should be used to generate a thumbnail. + */ + const CONFIG_THUMBNAIL_SOURCE = 'checkout/cart/grouped_product_image'; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @param ScopeConfigInterface $scopeConfig + */ + public function __construct(ScopeConfigInterface $scopeConfig) + { + $this->scopeConfig = $scopeConfig; + } + + /** + * {@inheritdoc} + */ + public function getFinalProduct(ItemInterface $item): ProductInterface + { + /** + * Show grouped product thumbnail if it must be always shown according to the related setting in system config + * or if child product thumbnail is not available. + */ + $childProduct = $item->getProduct(); + $finalProduct = $childProduct; + $parentProduct = $this->getParentProduct($item); + + if ($childProduct !== $parentProduct) { + $configValue = $this->scopeConfig->getValue( + self::CONFIG_THUMBNAIL_SOURCE, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + $childThumb = $childProduct->getData('thumbnail'); + + $finalProduct = + ($configValue == Thumbnail::OPTION_USE_PARENT_IMAGE) || (!$childThumb || $childThumb == 'no_selection') + ? $parentProduct + : $childProduct; + } + + return $finalProduct; + } + + /** + * Get grouped product. + * + * @param ItemInterface $item + * @return Product + */ + private function getParentProduct(ItemInterface $item) : Product + { + $option = $item->getOptionByCode('product_type'); + $product = $item->getProduct(); + + if ($option) { + $product = $option->getProduct(); + } + + return $product; + } +} diff --git a/app/code/Magento/GroupedProduct/Test/Unit/Block/Cart/Item/Renderer/GroupedTest.php b/app/code/Magento/GroupedProduct/Test/Unit/Block/Cart/Item/Renderer/GroupedTest.php index 5dc12fe39c46a..7328f710f9231 100644 --- a/app/code/Magento/GroupedProduct/Test/Unit/Block/Cart/Item/Renderer/GroupedTest.php +++ b/app/code/Magento/GroupedProduct/Test/Unit/Block/Cart/Item/Renderer/GroupedTest.php @@ -10,127 +10,28 @@ class GroupedTest extends \PHPUnit\Framework\TestCase { - /** @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $_scopeConfig; - - /** @var Renderer */ - protected $_renderer; - - protected function setUp() - { - parent::setUp(); - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->_scopeConfig = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); - $this->_renderer = $objectManagerHelper->getObject( - \Magento\GroupedProduct\Block\Cart\Item\Renderer\Grouped::class, - ['scopeConfig' => $this->_scopeConfig] - ); - } - /** - * Child thumbnail is available and config option is not set to use parent thumbnail. + * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ - public function testGetProductForThumbnail() - { - $childHasThumbnail = true; - $useParentThumbnail = false; - $products = $this->_initProducts($childHasThumbnail, $useParentThumbnail); - - $productForThumbnail = $this->_renderer->getProductForThumbnail(); - $this->assertSame( - $products['childProduct'], - $productForThumbnail, - 'Child product was expected to be returned.' - ); - } + private $scopeConfig; /** - * Child thumbnail is not available and config option is not set to use parent thumbnail. + * @var Renderer */ - public function testGetProductForThumbnailChildThumbnailNotAvailable() - { - $childHasThumbnail = false; - $useParentThumbnail = false; - $products = $this->_initProducts($childHasThumbnail, $useParentThumbnail); - - $productForThumbnail = $this->_renderer->getProductForThumbnail(); - $this->assertSame( - $products['parentProduct'], - $productForThumbnail, - 'Parent product was expected to be returned.' - ); - } + private $renderer; /** - * Child thumbnail is available and config option is set to use parent thumbnail. + * @inheritdoc */ - public function testGetProductForThumbnailConfigUseParent() - { - $childHasThumbnail = true; - $useParentThumbnail = true; - $products = $this->_initProducts($childHasThumbnail, $useParentThumbnail); - - $productForThumbnail = $this->_renderer->getProductForThumbnail(); - $this->assertSame( - $products['parentProduct'], - $productForThumbnail, - 'Parent product was expected to be returned ' . - 'if "checkout/cart/grouped_product_image" is set to "parent" in system config.' - ); - } - - /** - * Initialize parent grouped product and child product. - * - * @param bool $childHasThumbnail - * @param bool $useParentThumbnail - * @return \Magento\Catalog\Model\Product[]|\PHPUnit_Framework_MockObject_MockObject[] - */ - protected function _initProducts($childHasThumbnail = true, $useParentThumbnail = false) + protected function setUp() { - /** Set option which can force usage of parent product thumbnail when grouped product is displayed */ - $thumbnailToBeUsed = $useParentThumbnail - ? ThumbnailSource::OPTION_USE_PARENT_IMAGE - : ThumbnailSource::OPTION_USE_OWN_IMAGE; - $this->_scopeConfig->expects( - $this->any() - )->method( - 'getValue' - )->with( - Renderer::CONFIG_THUMBNAIL_SOURCE - )->will( - $this->returnValue($thumbnailToBeUsed) - ); - - /** Initialized parent product */ - /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $parentProduct */ - $parentProduct = $this->createMock(\Magento\Catalog\Model\Product::class); - - /** Initialize child product */ - /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $childProduct */ - $childProduct = $this->createPartialMock(\Magento\Catalog\Model\Product::class, ['getThumbnail', '__wakeup']); - $childThumbnail = $childHasThumbnail ? 'thumbnail.jpg' : 'no_selection'; - $childProduct->expects($this->any())->method('getThumbnail')->will($this->returnValue($childThumbnail)); - - /** Mock methods which return parent and child products */ - /** @var \Magento\Quote\Model\Quote\Item\Option|\PHPUnit_Framework_MockObject_MockObject $itemOption */ - $itemOption = $this->createMock(\Magento\Quote\Model\Quote\Item\Option::class); - $itemOption->expects($this->any())->method('getProduct')->will($this->returnValue($parentProduct)); - /** @var \Magento\Quote\Model\Quote\Item|\PHPUnit_Framework_MockObject_MockObject $item */ - $item = $this->createMock(\Magento\Quote\Model\Quote\Item::class); - $item->expects($this->any())->method('getProduct')->will($this->returnValue($childProduct)); - $item->expects( - $this->any() - )->method( - 'getOptionByCode' - )->with( - 'product_type' - )->will( - $this->returnValue($itemOption) + parent::setUp(); + $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->scopeConfig = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); + $this->renderer = $objectManagerHelper->getObject( + \Magento\GroupedProduct\Block\Cart\Item\Renderer\Grouped::class, + ['scopeConfig' => $this->scopeConfig] ); - $this->_renderer->setItem($item); - - return ['parentProduct' => $parentProduct, 'childProduct' => $childProduct]; } public function testGetIdentities() @@ -140,7 +41,7 @@ public function testGetIdentities() $product->expects($this->exactly(2))->method('getIdentities')->will($this->returnValue($productTags)); $item = $this->createMock(\Magento\Quote\Model\Quote\Item::class); $item->expects($this->exactly(2))->method('getProduct')->will($this->returnValue($product)); - $this->_renderer->setItem($item); - $this->assertEquals(array_merge($productTags, $productTags), $this->_renderer->getIdentities()); + $this->renderer->setItem($item); + $this->assertEquals(array_merge($productTags, $productTags), $this->renderer->getIdentities()); } } diff --git a/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php b/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php new file mode 100644 index 0000000000000..1434788be3814 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php @@ -0,0 +1,99 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\GroupedProduct\Test\Unit\Model\Product\Configuration; + +use Magento\Catalog\Model\Config\Source\Product\Thumbnail; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Configuration\Item\ItemInterface; +use Magento\Catalog\Model\Product\Configuration\Item\Option\OptionInterface; +use Magento\GroupedProduct\Model\Product\Configuration\Item\ItemProductResolver; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Store\Model\ScopeInterface; + +/** + * Tests \Magento\GroupedProduct\Model\Product\Configuration\Item\ItemProductResolver + */ +class ItemProductResolverTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ItemProductResolver + */ + private $resolver; + + /** + * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $scopeConfig; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManagerHelper = new ObjectManager($this); + $this->scopeConfig = $this->createPartialMock(ScopeConfigInterface::class, ['getValue', 'isSetFlag']); + $this->resolver = $objectManagerHelper->getObject( + ItemProductResolver::class, + ['scopeConfig' => $this->scopeConfig] + ); + } + + /** + * @param bool $existOption + * @param string $configImageSource + * @return void + * @dataProvider getFinalProductDataProvider + */ + public function testGetFinalProduct(bool $existOption, string $configImageSource) + { + $option = null; + $childProduct = $this->createPartialMock(Product::class, ['getData']); + $finalProduct = $childProduct; + + if ($existOption) { + $parentProduct = $this->createMock(Product::class); + $childProduct->expects($this->once())->method('getData')->with('thumbnail')->willReturn('someImage'); + + $option = $this->createPartialMock( + OptionInterface::class, + ['getProduct', 'getValue'] + ); + $option->expects($this->once())->method('getProduct')->willReturn($parentProduct); + + $this->scopeConfig->expects($this->once()) + ->method('getValue') + ->with(ItemProductResolver::CONFIG_THUMBNAIL_SOURCE, ScopeInterface::SCOPE_STORE) + ->willReturn($configImageSource); + + $finalProduct = ($configImageSource == Thumbnail::OPTION_USE_PARENT_IMAGE)? $parentProduct: $childProduct; + } + + $item = $this->createPartialMock( + ItemInterface::class, + ['getProduct', 'getOptionByCode', 'getFileDownloadParams'] + ); + $item->expects($this->exactly(2))->method('getProduct')->willReturn($childProduct); + $item->expects($this->once())->method('getOptionByCode')->with('product_type')->willReturn($option); + + $this->assertEquals($finalProduct, $this->resolver->getFinalProduct($item)); + } + + /** + * @return array + */ + public function getFinalProductDataProvider(): array + { + return [ + [false, Thumbnail::OPTION_USE_PARENT_IMAGE], + [true, Thumbnail::OPTION_USE_PARENT_IMAGE], + [true, Thumbnail::OPTION_USE_OWN_IMAGE], + ]; + } +} diff --git a/app/code/Magento/GroupedProduct/etc/di.xml b/app/code/Magento/GroupedProduct/etc/di.xml index 44d793e74d728..43678d0ad7a82 100644 --- a/app/code/Magento/GroupedProduct/etc/di.xml +++ b/app/code/Magento/GroupedProduct/etc/di.xml @@ -98,4 +98,11 @@ </argument> </arguments> </type> + <type name="Magento\Catalog\Model\Product\Configuration\Item\ItemResolverComposite"> + <arguments> + <argument name="itemResolvers" xsi:type="array"> + <item name="grouped" xsi:type="string">Magento\GroupedProduct\Model\Product\Configuration\Item\ItemProductResolver</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/GroupedProduct/etc/frontend/di.xml b/app/code/Magento/GroupedProduct/etc/frontend/di.xml deleted file mode 100644 index 755d53f46bfdd..0000000000000 --- a/app/code/Magento/GroupedProduct/etc/frontend/di.xml +++ /dev/null @@ -1,17 +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\Checkout\CustomerData\ItemPoolInterface"> - <arguments> - <argument name="itemMap" xsi:type="array"> - <item name="grouped" xsi:type="string">Magento\GroupedProduct\CustomerData\GroupedItem</item> - </argument> - </arguments> - </type> -</config> diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php index 02a897d44b3c6..64ef62a423eb3 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php @@ -3,18 +3,53 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); -/** - * Wishlist block customer item cart column - * - * @author Magento Core Team <core@magentocommerce.com> - */ namespace Magento\Wishlist\Block\Customer\Wishlist\Item\Column; +use Magento\Framework\App\ObjectManager; +use Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface; + /** + * Wishlist block customer item cart column + * * @api * @since 100.0.2 */ class Image extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column { + /** + * @var ItemResolverInterface + */ + private $itemResolver; + + /** + * @param \Magento\Catalog\Block\Product\Context $context + * @param \Magento\Framework\App\Http\Context $httpContext + * @param array|null $data + * @param ItemResolverInterface|null $itemResolver + */ + public function __construct( + \Magento\Catalog\Block\Product\Context $context, + \Magento\Framework\App\Http\Context $httpContext, + array $data = [], + ItemResolverInterface $itemResolver = null + ) { + $this->itemResolver = $itemResolver ?: ObjectManager::getInstance()->get(ItemResolverInterface::class); + parent::__construct( + $context, + $httpContext, + $data + ); + } + + /** + * Identify the product from which thumbnail should be taken. + * + * @return \Magento\Catalog\Model\Product + */ + public function getProductForThumbnail(\Magento\Wishlist\Model\Item $item): \Magento\Catalog\Model\Product + { + return $this->itemResolver->getFinalProduct($item); + } } diff --git a/app/code/Magento/Wishlist/CustomerData/Wishlist.php b/app/code/Magento/Wishlist/CustomerData/Wishlist.php index d7dd27874d365..84e4857ffeb29 100644 --- a/app/code/Magento/Wishlist/CustomerData/Wishlist.php +++ b/app/code/Magento/Wishlist/CustomerData/Wishlist.php @@ -5,8 +5,10 @@ */ namespace Magento\Wishlist\CustomerData; +use Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface; use Magento\Catalog\Model\Product\Image\NotLoadInfoImageException; use Magento\Customer\CustomerData\SectionSourceInterface; +use Magento\Framework\App\ObjectManager; /** * Wishlist section @@ -38,22 +40,30 @@ class Wishlist implements SectionSourceInterface */ protected $block; + /** + * @var ItemResolverInterface + */ + private $itemResolver; + /** * @param \Magento\Wishlist\Helper\Data $wishlistHelper * @param \Magento\Wishlist\Block\Customer\Sidebar $block * @param \Magento\Catalog\Helper\ImageFactory $imageHelperFactory * @param \Magento\Framework\App\ViewInterface $view + * @param ItemResolverInterface|null $itemResolver */ public function __construct( \Magento\Wishlist\Helper\Data $wishlistHelper, \Magento\Wishlist\Block\Customer\Sidebar $block, \Magento\Catalog\Helper\ImageFactory $imageHelperFactory, - \Magento\Framework\App\ViewInterface $view + \Magento\Framework\App\ViewInterface $view, + ItemResolverInterface $itemResolver = null ) { $this->wishlistHelper = $wishlistHelper; $this->imageHelperFactory = $imageHelperFactory; $this->block = $block; $this->view = $view; + $this->itemResolver = $itemResolver ?: ObjectManager::getInstance()->get(ItemResolverInterface::class); } /** @@ -122,7 +132,7 @@ protected function getItemData(\Magento\Wishlist\Model\Item $wishlistItem) { $product = $wishlistItem->getProduct(); return [ - 'image' => $this->getImageData($product), + 'image' => $this->getImageData($this->itemResolver->getFinalProduct($wishlistItem)), 'product_url' => $this->wishlistHelper->getProductUrl($wishlistItem), 'product_name' => $product->getName(), 'product_price' => $this->block->getProductPriceHtml( @@ -147,14 +157,6 @@ protected function getItemData(\Magento\Wishlist\Model\Item $wishlistItem) */ protected function getImageData($product) { - /*Set variant product if it is configurable product. - It will show variant product image in sidebar instead of configurable product image.*/ - $simpleOption = $product->getCustomOption('simple_product'); - if ($simpleOption !== null) { - $optionProduct = $simpleOption->getProduct(); - $product = $optionProduct; - } - /** @var \Magento\Catalog\Helper\Image $helper */ $helper = $this->imageHelperFactory->create() ->init($product, 'wishlist_sidebar_block'); diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml new file mode 100644 index 0000000000000..4bd0d34382105 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml @@ -0,0 +1,81 @@ +<?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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> + <test name="ConfigurableProductChildImageShouldBeShownOnWishListTest"> + <annotations> + <features value="Wishlist"/> + <group value="wishlist"/> + <title value="When user add Configurable child product to WIshlist then child product image should be shown in Wishlist"/> + <description value="When user add Configurable child product to WIshlist then child product image should be shown in Wishlist"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-93730"/> + </annotations> + <before> + <magentoCLI command="config:set checkout/cart/configurable_product_image itself" stepKey="setProductImageSettingUnderCofigurationSalesCheckout"/> + <createData entity="ApiCategory" stepKey="createCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + + <actionGroup ref="createConfigurableProduct" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + <createData entity="Simple_US_Customer" stepKey="customer"/> + </before> + <after> + <actionGroup ref="DeleteAllProductsOnProductsGridPageFilteredByName" stepKey="deleteAllCreatedProducts"> + <argument name="product" value="_defaultProduct"/> + </actionGroup> + <actionGroup ref="DeleteProductAttribute" stepKey="deleteCreatedProductAttribute"> + <argument name="productAttribute" value="colorProductAttribute"/> + </actionGroup> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="customer" stepKey="deleteCustomer"/> + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + </after> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad2"/> + <click selector="{{AdminProductGridSection.selectRowBasedOnName(_defaultProduct.name)}}" stepKey="selectProductToAddImage"/> + <waitForPageLoad stepKey="waitForProductEditPageLoad"/> + <actionGroup ref="addProductImage" stepKey="addImageForParentProduct"> + <argument name="image" value="MagentoLogo"/> + </actionGroup> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex1"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad1"/> + <click selector="{{AdminProductGridSection.selectRowBasedOnName(colorProductAttribute1.name)}}" stepKey="selectProductToAddImage1"/> + <waitForPageLoad stepKey="waitForProductEditPageLoad1"/> + <actionGroup ref="addProductImage" stepKey="addImageForChildProduct"> + <argument name="image" value="TestImageNew"/> + </actionGroup> + <actionGroup ref="saveProductForm" stepKey="saveSimpleProduct"/> + + <!--Sign in as customer --> + <amOnPage url="{{StorefrontCustomerSignInPage.url}}" stepKey="amOnSignInPage"/> + <fillField userInput="$$customer.email$$" selector="{{StorefrontCustomerSignInFormSection.emailField}}" stepKey="fillEmail"/> + <fillField userInput="$$customer.password$$" selector="{{StorefrontCustomerSignInFormSection.passwordField}}" stepKey="fillPassword"/> + <waitForElementVisible selector="{{StorefrontCustomerSignInFormSection.signInAccountButton}}" stepKey="waitForButton"/> + <click selector="{{StorefrontCustomerSignInFormSection.signInAccountButton}}" stepKey="clickSignInAccountButton"/> + <see userInput="$$customer.firstname$$" selector="{{StorefrontCustomerDashboardAccountInformationSection.ContactInformation}}" stepKey="seeFirstName"/> + <see userInput="$$customer.lastname$$" selector="{{StorefrontCustomerDashboardAccountInformationSection.ContactInformation}}" stepKey="seeLastName"/> + <see userInput="$$customer.email$$" selector="{{StorefrontCustomerDashboardAccountInformationSection.ContactInformation}}" stepKey="seeEmail"/> + <waitForPageLoad stepKey="waitForLogin"/> + <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnConfigurableProductPage"/> + <waitForPageLoad stepKey="wait3"/> + <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeProductName"/> + <selectOption userInput="{{colorProductAttribute1.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption1"/> + <waitForElementVisible selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="waitForAddToCartVisible"/> + <click selector="{{StorefrontProductPageSection.addToWishlist}}" stepKey="addFirstProductToWishlist"/> + <waitForElement selector="{{StorefrontCustomerWishlistSection.successMsg}}" time="30" stepKey="addProductToWishlistWaitForSuccessMessage"/> + <waitForPageLoad stepKey="waitForPageToLoad2"/> + <seeElement selector="{{StorefrontCustomerWishlistProductSection.productImageByImageName(TestImageNew.filename)}}" stepKey="AssertWishlistProductImage" /> + <seeElement selector="{{StorefrontCustomerWishlistSidebarSection.productImageByImageName(TestImageNew.filename)}}" stepKey="AssertWishlistSidebarProductImage" /> + </test> +</tests> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml index d146834bb6618..196721f4166d1 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml @@ -75,7 +75,7 @@ <!-- Add product visible on default store to wishlist --> <amOnPage url="$$product.name$$.html" stepKey="navigateToProductPageOnDefaultStore"/> <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$product.name$$" stepKey="assertFirstProductNameTitle"/> - <click stepKey="addFirstProductToWishlist" selector="{{StorefrontProductPageSection.AddToWishlist}}"/> + <click stepKey="addFirstProductToWishlist" selector="{{StorefrontProductPageSection.addToWishlist}}"/> <!-- Switch to second store and add second product (visible on second store) to wishlist --> <click stepKey="ClickSwitchStoreButtonOnDefaultStore" selector="{{StorefrontFooterSection.SwitchStoreButton}}"/> <click stepKey="SelectSecondStoreToSwitchOn" selector="{{StorefrontFooterSection.StoreLink($$storeGroup.group[name]$$)}}"/> @@ -83,7 +83,7 @@ <see stepKey="seeProduct1InWishlist" userInput="$$product.name$$" selector="{{StorefrontCustomerWishlistSection.productItemNameText}}"/> <amOnPage url="$$secondProduct.name$$.html" stepKey="navigateToProductPageOnSecondStore"/> <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$secondProduct.name$$" stepKey="assertSecondProductNameTitle"/> - <click stepKey="addSecondProductToWishlist" selector="{{StorefrontProductPageSection.AddToWishlist}}"/> + <click stepKey="addSecondProductToWishlist" selector="{{StorefrontProductPageSection.addToWishlist}}"/> <see stepKey="seeProduct1InWishlistOnSecondStore" userInput="$$product.name$$" selector="{{StorefrontCustomerWishlistSection.productItemNameText}}"/> <see stepKey="seeProduct2InWishlistOnSecondStore" userInput="$$secondProduct.name$$" selector="{{StorefrontCustomerWishlistSection.productItemNameText}}"/> <click stepKey="ClickSwitchStoreButtonOnSecondStore" selector="{{StorefrontFooterSection.SwitchStoreButton}}"/> diff --git a/app/code/Magento/Wishlist/Test/Unit/CustomerData/WishlistTest.php b/app/code/Magento/Wishlist/Test/Unit/CustomerData/WishlistTest.php index 9ecd58b44f0e5..7a7c982ce0497 100644 --- a/app/code/Magento/Wishlist/Test/Unit/CustomerData/WishlistTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/CustomerData/WishlistTest.php @@ -8,7 +8,7 @@ use Magento\Catalog\Helper\Image; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Type\AbstractType; -use Magento\Catalog\Pricing\Price\ConfiguredPriceInterface; +use Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface; use Magento\Framework\App\ViewInterface; use Magento\Framework\Pricing\Render; use Magento\Wishlist\Block\Customer\Sidebar; @@ -23,21 +23,39 @@ */ class WishlistTest extends \PHPUnit\Framework\TestCase { - /** @var Wishlist */ - protected $model; + /** + * @var Wishlist + */ + private $model; + + /** + * @var Data|\PHPUnit_Framework_MockObject_MockObject + */ + private $wishlistHelperMock; - /** @var Data|\PHPUnit_Framework_MockObject_MockObject */ - protected $wishlistHelperMock; + /** + * @var Sidebar|\PHPUnit_Framework_MockObject_MockObject + */ + private $sidebarMock; - /** @var Sidebar|\PHPUnit_Framework_MockObject_MockObject */ - protected $sidebarMock; + /** + * @var Image|\PHPUnit_Framework_MockObject_MockObject + */ + private $catalogImageHelperMock; - /** @var Image|\PHPUnit_Framework_MockObject_MockObject */ - protected $catalogImageHelperMock; + /** + * @var ViewInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $viewMock; - /** @var ViewInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $viewMock; + /** + * @var \Magento\Catalog\Block\Product\ImageBuilder|\PHPUnit_Framework_MockObject_MockObject + */ + private $itemResolver; + /** + * @inheritdoc + */ protected function setUp() { $this->wishlistHelperMock = $this->getMockBuilder(\Magento\Wishlist\Helper\Data::class) @@ -60,11 +78,14 @@ protected function setUp() ->method('create') ->willReturn($this->catalogImageHelperMock); + $this->itemResolver = $this->createMock(ItemResolverInterface::class); + $this->model = new Wishlist( $this->wishlistHelperMock, $this->sidebarMock, $imageHelperFactory, - $this->viewMock + $this->viewMock, + $this->itemResolver ); } @@ -158,6 +179,10 @@ public function testGetSectionData() ->method('getProduct') ->willReturn($productMock); + $this->itemResolver->expects($this->once()) + ->method('getFinalProduct') + ->willReturn($productMock); + $this->catalogImageHelperMock->expects($this->once()) ->method('init') ->with($productMock, 'wishlist_sidebar_block', []) @@ -343,6 +368,10 @@ public function testGetSectionDataWithTwoItems() ->method('getProduct') ->willReturn($productMock); + $this->itemResolver->expects($this->exactly(2)) + ->method('getFinalProduct') + ->willReturn($productMock); + $this->catalogImageHelperMock->expects($this->exactly(2)) ->method('init') ->with($productMock, 'wishlist_sidebar_block', []) diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/image.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/image.phtml index 1008f5f377df5..2eb4664991e99 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/image.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/image.phtml @@ -13,5 +13,5 @@ $item = $block->getItem(); $product = $item->getProduct(); ?> <a class="product-item-photo" tabindex="-1" href="<?= $block->escapeUrl($block->getProductUrl($item)) ?>" title="<?= $block->escapeHtmlAttr($product->getName()) ?>"> - <?= $block->getImage($product, 'wishlist_thumbnail')->toHtml() ?> + <?= $block->getImage($block->getProductForThumbnail($item), 'wishlist_thumbnail')->toHtml() ?> </a> diff --git a/dev/tests/acceptance/tests/_data/magento-again.jpg b/dev/tests/acceptance/tests/_data/magento-again.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c377daf8fb0b390d89aa4f6f111715fc9118ca1b GIT binary patch literal 55303 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 Hh5vs5{BvNq literal 0 HcmV?d00001 From a12d39a2de2e2fc630ea48c719b47fb1faf491a9 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 24 Jul 2018 12:56:28 +0300 Subject: [PATCH 089/211] MAGETWO-86482: Customizable options truncated when displaying ordered product in admin --- ...seProductWithCustomOptionsWithLongValuesTitle.xml | 12 ++++++++---- .../Tax/Test/Mftf/Section/CheckoutPaymentSection.xml | 6 ------ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml index 9b2a14c89f616..e1ebb613e7d06 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml @@ -52,8 +52,8 @@ <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="{{ProductOptionValueDropdownLongTitle1.title}}" stepKey="seeProductOptionValueDropdown1Input1"/> + <conditionalClick selector="{{CheckoutPaymentSection.productOptionsByProductItemName($$createProduct.name$$)}}" dependentSelector="{{CheckoutPaymentSection.productOptionsActiveByProductItemName($$createProduct.name$$)}}" visible="false" stepKey="exposeProductOptions"/> + <see selector="{{CheckoutPaymentSection.productOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="{{ProductOptionValueDropdownLongTitle1.title}}" stepKey="seeProductOptionValueDropdown1Input1"/> <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> <!-- Place Order --> <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/> @@ -75,7 +75,11 @@ <expectedResult type="string">Optisfvdklvfnkljvnfdklpvnfdjklfdvnjkvfdkjnvfdjkfvndj111 ...</expectedResult> </assertEquals> <moveMouseOver selector="{{AdminOrderItemsOrderedSection.productNameOptions}} dd" stepKey="hoverProduct"/> - <waitForElementVisible selector="{{AdminOrderItemsOrderedSection.productNameOptions}} dd:nth-child(2)" stepKey="waitForCustomOptionValueFullName"/> - <see selector="{{AdminOrderItemsOrderedSection.productNameOptions}}" userInput="{{ProductOptionValueDropdownLongTitle1.title}}" stepKey="seeAdminOrderProductOptionValueDropdown1"/> + <grabTextFrom selector="{{AdminOrderItemsOrderedSection.productNameOptions}} span:nth-child(2)" stepKey="productOptionValueTruncatedText"/> + <assertEquals stepKey="checkProductOptionTruncatedValue"> + <actualResult type="variable">productOptionValueTruncatedText</actualResult> + <expectedResult type="string">11Optisfvdklvfnkljvnfdklpvnfdjklfdvnjkvfdkjnvfdjkfvndj11111</expectedResult> + </assertEquals> + <seeElement selector="{{AdminOrderItemsOrderedSection.productNameOptions}} span:nth-child(2)" stepKey="seeAdminOrderProductOptionValueDropdown1"/> </test> </tests> diff --git a/app/code/Magento/Tax/Test/Mftf/Section/CheckoutPaymentSection.xml b/app/code/Magento/Tax/Test/Mftf/Section/CheckoutPaymentSection.xml index 599fdc2540b2c..8250fd5e8e39d 100644 --- a/app/code/Magento/Tax/Test/Mftf/Section/CheckoutPaymentSection.xml +++ b/app/code/Magento/Tax/Test/Mftf/Section/CheckoutPaymentSection.xml @@ -14,11 +14,5 @@ <element name="orderTotalInclTax" type="text" selector="#opc-sidebar>div.opc-block-summary>table>tbody>tr.grand.totals.incl>td>strong>span"/> <element name="taxRateTab" type="select" selector="table.data.table.table-totals tbody tr.totals-tax-summary" /> <element name="taxRate" type="text" selector="#opc-sidebar>div.opc-block-summary>table>tbody>tr.totals-tax-details.shown>th"/> - <element name="cartItems" type="text" selector="ol.minicart-items"/> - <element name="cartItemsArea" type="button" selector="div.block.items-in-cart"/> - <element name="cartItemsAreaActive" type="textarea" selector="div.block.items-in-cart.active"/> - <element name="ProductOptionsByProductItemName" type="text" selector="//div[@class='product-item-details']//strong[@class='product-item-name'][text()='{{var1}}']//ancestor::div[@class='product-item-details']//div[@class='product options']" parameterized="true" /> - <element name="ProductOptionsActiveByProductItemName" type="text" selector="//div[@class='product-item-details']//strong[@class='product-item-name'][text()='{{var1}}']//ancestor::div[@class='product-item-details']//div[@class='product options active']" parameterized="true" /> - <element name="placeOrder" type="button" selector="button.action.primary.checkout" timeout="30"/> </section> </sections> From 8c8a71bd6a8e18220a33db4e3bfecf81f458b21a Mon Sep 17 00:00:00 2001 From: Ievgen Sentiabov <isentiabov@magento.com> Date: Mon, 23 Jul 2018 19:36:09 +0300 Subject: [PATCH 090/211] MAGETWO-84929: Default merchant account ID is used on subsequent partial invoices - Added `Merchant Account ID` to params for transaction settlement --- .../Request/MerchantAccountDataBuilder.php | 64 ++++++++ .../Gateway/Request/PaymentDataBuilder.php | 21 +-- .../Request/PaymentDataBuilderTest.php | 24 +-- .../Magento/Braintree/etc/adminhtml/di.xml | 2 + app/code/Magento/Braintree/etc/di.xml | 5 + .../frontend/web/template/payment/paypal.html | 2 +- .../Order/Payment/Transaction/Repository.php | 5 +- .../Adminhtml/Invoice/CreateTest.php | 149 ++++++++++++++++++ .../Magento/Braintree/Fixtures/order.php | 57 +++++++ .../Braintree/Fixtures/order_rollback.php | 28 ++++ .../Braintree/Fixtures/partial_invoice.php | 47 ++++++ .../Fixtures/partial_invoice_rollback.php | 28 ++++ .../Magento/Braintree/Fixtures/payment.php | 29 ++++ .../Adminhtml/Order/AddCommentTest.php | 16 -- .../Adminhtml/Order/AddressSaveTest.php | 16 -- .../Adminhtml/Order/AddressTest.php | 16 -- .../Controller/Adminhtml/Order/CancelTest.php | 16 -- .../Controller/Adminhtml/Order/EmailTest.php | 16 -- .../Controller/Adminhtml/Order/HoldTest.php | 16 -- .../Adminhtml/Order/ReviewPaymentTest.php | 16 -- .../Controller/Adminhtml/Order/UnholdTest.php | 16 -- .../Controller/Adminhtml/Order/ViewTest.php | 16 -- .../Adminhtml/Transactions/FetchTest.php | 18 --- .../testsuite/Magento/Vault/_files/token.php | 2 +- 24 files changed, 424 insertions(+), 201 deletions(-) create mode 100644 app/code/Magento/Braintree/Gateway/Request/MerchantAccountDataBuilder.php create mode 100644 dev/tests/integration/testsuite/Magento/Braintree/Controller/Adminhtml/Invoice/CreateTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Braintree/Fixtures/order.php create mode 100644 dev/tests/integration/testsuite/Magento/Braintree/Fixtures/order_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Braintree/Fixtures/partial_invoice.php create mode 100644 dev/tests/integration/testsuite/Magento/Braintree/Fixtures/partial_invoice_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment.php delete mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddCommentTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddressSaveTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddressTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CancelTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/EmailTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/HoldTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/ReviewPaymentTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/UnholdTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/ViewTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Transactions/FetchTest.php diff --git a/app/code/Magento/Braintree/Gateway/Request/MerchantAccountDataBuilder.php b/app/code/Magento/Braintree/Gateway/Request/MerchantAccountDataBuilder.php new file mode 100644 index 0000000000000..6dc40e76322df --- /dev/null +++ b/app/code/Magento/Braintree/Gateway/Request/MerchantAccountDataBuilder.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Braintree\Gateway\Request; + +use Magento\Braintree\Gateway\Config\Config; +use Magento\Braintree\Gateway\SubjectReader; +use Magento\Payment\Gateway\Request\BuilderInterface; + +/** + * Adds Merchant Account ID to the request if it was specified in the configuration. + */ +class MerchantAccountDataBuilder implements BuilderInterface +{ + /** + * The merchant account ID used to create a transaction. + * Currency is also determined by merchant account ID. + * If no merchant account ID is specified, Braintree will use your default merchant account. + */ + private static $merchantAccountId = 'merchantAccountId'; + + /** + * @var Config + */ + private $config; + + /** + * @var SubjectReader + */ + private $subjectReader; + + /** + * Constructor + * + * @param Config $config + * @param SubjectReader $subjectReader + */ + public function __construct(Config $config, SubjectReader $subjectReader) + { + $this->config = $config; + $this->subjectReader = $subjectReader; + } + + /** + * @inheritdoc + */ + public function build(array $buildSubject): array + { + $paymentDO = $this->subjectReader->readPayment($buildSubject); + $order = $paymentDO->getOrder(); + + $result = []; + $merchantAccountId = $this->config->getMerchantAccountId($order->getStoreId()); + if (!empty($merchantAccountId)) { + $result[self::$merchantAccountId] = $merchantAccountId; + } + + return $result; + } +} diff --git a/app/code/Magento/Braintree/Gateway/Request/PaymentDataBuilder.php b/app/code/Magento/Braintree/Gateway/Request/PaymentDataBuilder.php index 85a0c64451398..fe75ce86cca2f 100644 --- a/app/code/Magento/Braintree/Gateway/Request/PaymentDataBuilder.php +++ b/app/code/Magento/Braintree/Gateway/Request/PaymentDataBuilder.php @@ -6,8 +6,8 @@ namespace Magento\Braintree\Gateway\Request; use Magento\Braintree\Gateway\Config\Config; -use Magento\Braintree\Observer\DataAssignObserver; use Magento\Braintree\Gateway\SubjectReader; +use Magento\Braintree\Observer\DataAssignObserver; use Magento\Payment\Gateway\Request\BuilderInterface; use Magento\Payment\Helper\Formatter; @@ -36,9 +36,8 @@ class PaymentDataBuilder implements BuilderInterface const PAYMENT_METHOD_NONCE = 'paymentMethodNonce'; /** - * The merchant account ID used to create a transaction. - * Currency is also determined by merchant account ID. - * If no merchant account ID is specified, Braintree will use your default merchant account. + * @deprecated + * @see \Magento\Braintree\Gateway\Request\MerchantAccountDataBuilder */ const MERCHANT_ACCOUNT_ID = 'merchantAccountId'; @@ -47,25 +46,18 @@ class PaymentDataBuilder implements BuilderInterface */ const ORDER_ID = 'orderId'; - /** - * @var Config - */ - private $config; - /** * @var SubjectReader */ private $subjectReader; /** - * Constructor - * * @param Config $config * @param SubjectReader $subjectReader + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct(Config $config, SubjectReader $subjectReader) { - $this->config = $config; $this->subjectReader = $subjectReader; } @@ -87,11 +79,6 @@ public function build(array $buildSubject) self::ORDER_ID => $order->getOrderIncrementId() ]; - $merchantAccountId = $this->config->getMerchantAccountId($order->getStoreId()); - if (!empty($merchantAccountId)) { - $result[self::MERCHANT_ACCOUNT_ID] = $merchantAccountId; - } - return $result; } } diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Request/PaymentDataBuilderTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Request/PaymentDataBuilderTest.php index 16f11559f73cc..62ef402e9ca9f 100644 --- a/app/code/Magento/Braintree/Test/Unit/Gateway/Request/PaymentDataBuilderTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Request/PaymentDataBuilderTest.php @@ -5,14 +5,14 @@ */ namespace Magento\Braintree\Test\Unit\Gateway\Request; -use Magento\Braintree\Gateway\Config\Config; -use Magento\Braintree\Gateway\SubjectReader; use Magento\Braintree\Gateway\Request\PaymentDataBuilder; +use Magento\Braintree\Gateway\SubjectReader; use Magento\Braintree\Observer\DataAssignObserver; use Magento\Payment\Gateway\Data\OrderAdapterInterface; use Magento\Payment\Gateway\Data\PaymentDataObjectInterface; use Magento\Sales\Model\Order\Payment; use PHPUnit_Framework_MockObject_MockObject as MockObject; +use Magento\Braintree\Gateway\Config\Config; /** * Class PaymentDataBuilderTest @@ -29,11 +29,6 @@ class PaymentDataBuilderTest extends \PHPUnit\Framework\TestCase */ private $builder; - /** - * @var Config|MockObject - */ - private $config; - /** * @var Payment|MockObject */ @@ -52,15 +47,16 @@ class PaymentDataBuilderTest extends \PHPUnit\Framework\TestCase protected function setUp() { $this->paymentDO = $this->createMock(PaymentDataObjectInterface::class); - $this->config = $this->getMockBuilder(Config::class) - ->disableOriginalConstructor() - ->getMock(); $this->payment = $this->getMockBuilder(Payment::class) ->disableOriginalConstructor() ->getMock(); $this->order = $this->createMock(OrderAdapterInterface::class); - $this->builder = new PaymentDataBuilder($this->config, new SubjectReader()); + $config = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->builder = new PaymentDataBuilder($config, new SubjectReader()); } /** @@ -98,8 +94,7 @@ public function testBuild() $expectedResult = [ PaymentDataBuilder::AMOUNT => 10.00, PaymentDataBuilder::PAYMENT_METHOD_NONCE => self::PAYMENT_METHOD_NONCE, - PaymentDataBuilder::ORDER_ID => '000000101', - PaymentDataBuilder::MERCHANT_ACCOUNT_ID => self::MERCHANT_ACCOUNT_ID, + PaymentDataBuilder::ORDER_ID => '000000101' ]; $buildSubject = [ @@ -111,9 +106,6 @@ public function testBuild() ->method('getAdditionalInformation') ->willReturnMap($additionalData); - $this->config->method('getMerchantAccountId') - ->willReturn(self::MERCHANT_ACCOUNT_ID); - $this->paymentDO->method('getPayment') ->willReturn($this->payment); diff --git a/app/code/Magento/Braintree/etc/adminhtml/di.xml b/app/code/Magento/Braintree/etc/adminhtml/di.xml index bb5763469cad3..cd449c8bce76d 100644 --- a/app/code/Magento/Braintree/etc/adminhtml/di.xml +++ b/app/code/Magento/Braintree/etc/adminhtml/di.xml @@ -29,6 +29,7 @@ <item name="vault" xsi:type="string">Magento\Braintree\Gateway\Request\VaultDataBuilder</item> <item name="dynamic_descriptor" xsi:type="string">Magento\Braintree\Gateway\Request\DescriptorDataBuilder</item> <item name="store" xsi:type="string">Magento\Braintree\Gateway\Request\StoreConfigBuilder</item> + <item name="merchant_account" xsi:type="string">Magento\Braintree\Gateway\Request\MerchantAccountDataBuilder</item> </argument> </arguments> </virtualType> @@ -41,6 +42,7 @@ <item name="address" xsi:type="string">Magento\Braintree\Gateway\Request\AddressDataBuilder</item> <item name="dynamic_descriptor" xsi:type="string">Magento\Braintree\Gateway\Request\DescriptorDataBuilder</item> <item name="store" xsi:type="string">Magento\Braintree\Gateway\Request\StoreConfigBuilder</item> + <item name="merchant_account" xsi:type="string">Magento\Braintree\Gateway\Request\MerchantAccountDataBuilder</item> </argument> </arguments> </virtualType> diff --git a/app/code/Magento/Braintree/etc/di.xml b/app/code/Magento/Braintree/etc/di.xml index f4e6e17bfca76..c5440db1889f4 100644 --- a/app/code/Magento/Braintree/etc/di.xml +++ b/app/code/Magento/Braintree/etc/di.xml @@ -233,6 +233,7 @@ <item name="device_data" xsi:type="string">Magento\Braintree\Gateway\Request\KountPaymentDataBuilder</item> <item name="dynamic_descriptor" xsi:type="string">Magento\Braintree\Gateway\Request\DescriptorDataBuilder</item> <item name="store" xsi:type="string">Magento\Braintree\Gateway\Request\StoreConfigBuilder</item> + <item name="merchant_account" xsi:type="string">Magento\Braintree\Gateway\Request\MerchantAccountDataBuilder</item> </argument> </arguments> </virtualType> @@ -291,6 +292,7 @@ <item name="device_data" xsi:type="string">Magento\Braintree\Gateway\Request\KountPaymentDataBuilder</item> <item name="dynamic_descriptor" xsi:type="string">Magento\Braintree\Gateway\Request\DescriptorDataBuilder</item> <item name="store" xsi:type="string">Magento\Braintree\Gateway\Request\StoreConfigBuilder</item> + <item name="merchant_account" xsi:type="string">Magento\Braintree\Gateway\Request\MerchantAccountDataBuilder</item> </argument> </arguments> </virtualType> @@ -325,6 +327,7 @@ <item name="vault_capture" xsi:type="string">Magento\Braintree\Gateway\Request\VaultCaptureDataBuilder</item> <item name="settlement" xsi:type="string">Magento\Braintree\Gateway\Request\SettlementDataBuilder</item> <item name="store" xsi:type="string">Magento\Braintree\Gateway\Request\StoreConfigBuilder</item> + <item name="merchant_account" xsi:type="string">Magento\Braintree\Gateway\Request\MerchantAccountDataBuilder</item> </argument> </arguments> </virtualType> @@ -347,6 +350,7 @@ <item name="device_data" xsi:type="string">Magento\Braintree\Gateway\Request\PayPal\DeviceDataBuilder</item> <item name="dynamic_descriptor" xsi:type="string">Magento\Braintree\Gateway\Request\DescriptorDataBuilder</item> <item name="store" xsi:type="string">Magento\Braintree\Gateway\Request\StoreConfigBuilder</item> + <item name="merchant_account" xsi:type="string">Magento\Braintree\Gateway\Request\MerchantAccountDataBuilder</item> </argument> </arguments> </virtualType> @@ -380,6 +384,7 @@ <item name="address" xsi:type="string">Magento\Braintree\Gateway\Request\AddressDataBuilder</item> <item name="dynamic_descriptor" xsi:type="string">Magento\Braintree\Gateway\Request\DescriptorDataBuilder</item> <item name="store" xsi:type="string">Magento\Braintree\Gateway\Request\StoreConfigBuilder</item> + <item name="merchant_account" xsi:type="string">Magento\Braintree\Gateway\Request\MerchantAccountDataBuilder</item> </argument> </arguments> </virtualType> diff --git a/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html b/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html index e47024b7d2330..d39ea62012b14 100644 --- a/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html +++ b/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html @@ -12,7 +12,7 @@ data-bind="attr: {'id': getCode()}, value: getCode(), checked: isChecked, click: selectPaymentMethod, visible: isRadioButtonVisible()" /> <label class="label" data-bind="attr: {'for': getCode()}"> <!-- PayPal Logo --> - <img data-bind="attr: {src: getPaymentAcceptanceMarkSrc(), alt: $t('Acceptance Mark')}, title: $t('Acceptance Mark')}" + <img data-bind="attr: {src: getPaymentAcceptanceMarkSrc(), alt: $t('Acceptance Mark'), title: $t('Acceptance Mark')}" class="payment-icon"/> <!-- PayPal Logo --> <span text="getTitle()"></span> diff --git a/app/code/Magento/Sales/Model/Order/Payment/Transaction/Repository.php b/app/code/Magento/Sales/Model/Order/Payment/Transaction/Repository.php index a2f1d6bcdfbcc..2bcc1fd50dbfa 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/Transaction/Repository.php +++ b/app/code/Magento/Sales/Model/Order/Payment/Transaction/Repository.php @@ -13,14 +13,11 @@ use Magento\Framework\Data\Collection; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Sales\Api\Data\TransactionInterface; -use Magento\Sales\Api\OrderPaymentRepositoryInterface; -use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Api\Data\TransactionSearchResultInterfaceFactory as SearchResultFactory; use Magento\Sales\Api\TransactionRepositoryInterface; use Magento\Sales\Model\EntityStorage; use Magento\Sales\Model\EntityStorageFactory; -use Magento\Sales\Model\Order\Payment; use Magento\Sales\Model\ResourceModel\Metadata; -use Magento\Sales\Api\Data\TransactionSearchResultInterfaceFactory as SearchResultFactory; use Magento\Sales\Model\ResourceModel\Order\Payment\Transaction as TransactionResource; /** diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Controller/Adminhtml/Invoice/CreateTest.php b/dev/tests/integration/testsuite/Magento/Braintree/Controller/Adminhtml/Invoice/CreateTest.php new file mode 100644 index 0000000000000..8476542c8f054 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Controller/Adminhtml/Invoice/CreateTest.php @@ -0,0 +1,149 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Braintree\Controller\Adminhtml\Invoice; + +use Braintree\Result\Successful; +use Braintree\Transaction; +use Magento\Braintree\Model\Adapter\BraintreeAdapter; +use Magento\Braintree\Model\Adapter\BraintreeAdapterFactory; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Data\Form\FormKey; +use Magento\Framework\Message\MessageInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\TestFramework\TestCase\AbstractBackendController; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * @magentoAppArea adminhtml + */ +class CreateTest extends AbstractBackendController +{ + /** + * @var BraintreeAdapter|MockObject + */ + private $adapter; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $adapterFactory = $this->getMockBuilder(BraintreeAdapterFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->adapter = $this->getMockBuilder(BraintreeAdapter::class) + ->disableOriginalConstructor() + ->getMock(); + $adapterFactory->method('create') + ->willReturn($this->adapter); + + $this->_objectManager->addSharedInstance($adapterFactory, BraintreeAdapterFactory::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->_objectManager->removeSharedInstance(BraintreeAdapterFactory::class); + parent::tearDown(); + } + + /** + * Checks a case when non default Merchant Account ID should be send to Braintree + * during creation second partial invoice. + * + * @magentoConfigFixture default_store payment/braintree/merchant_account_id Magneto + * @magentoConfigFixture current_store payment/braintree/merchant_account_id USA_Merchant + * @magentoDataFixture Magento/Braintree/Fixtures/partial_invoice.php + */ + public function testCreatePartialInvoiceWithNonDefaultMerchantAccount() + { + $order = $this->getOrder('100000002'); + + $this->adapter->method('sale') + ->with(self::callback(function ($request) { + self::assertEquals('USA_Merchant', $request['merchantAccountId']); + return true; + })) + ->willReturn($this->getTransactionStub()); + + $uri = 'backend/sales/order_invoice/save/order_id/' . $order->getEntityId(); + $this->prepareRequest($uri); + $this->dispatch($uri); + + self::assertSessionMessages( + self::equalTo(['The invoice has been created.']), + MessageInterface::TYPE_SUCCESS + ); + } + + /** + * Creates stub for Braintree capture Transaction. + * + * @return Successful + */ + private function getTransactionStub(): Successful + { + $transaction = $this->getMockBuilder(Transaction::class) + ->disableOriginalConstructor() + ->getMock(); + $transaction->status = 'submitted_for_settlement'; + $response = new Successful(); + $response->success = true; + $response->transaction = $transaction; + + return $response; + } + + /** + * Gets order by increment ID. + * + * @param string $incrementId + * @return OrderInterface + */ + private function getOrder(string $incrementId): OrderInterface + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->_objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('increment_id', $incrementId) + ->create(); + + /** @var OrderRepositoryInterface $repository */ + $repository = $this->_objectManager->get(OrderRepositoryInterface::class); + $items = $repository->getList($searchCriteria) + ->getItems(); + + return array_pop($items); + } + + /** + * Prepares POST request for invoice creation. + * + * @param string $uri + */ + private function prepareRequest(string $uri) + { + /** @var FormKey $formKey */ + $formKey = $this->_objectManager->get(FormKey::class); + $request = $this->getRequest(); + $request->setMethod('POST'); + $request->setParam('form_key', $formKey->getFormKey()); + $request->setRequestUri($uri); + $request->setPostValue( + [ + 'invoice' => [ + 'capture_case' => 'online' + ] + ] + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/order.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/order.php new file mode 100644 index 0000000000000..ceb90710ed5e7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/order.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Address; +use Magento\Sales\Model\Order\Item as OrderItem; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; + +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +$addressData = include __DIR__ . '/../../Sales/_files/address_data.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; + +$billingAddress = $objectManager->create(Address::class, ['data' => $addressData]); +$billingAddress->setAddressType('billing'); + +$shippingAddress = clone $billingAddress; +$shippingAddress->setId(null) + ->setAddressType('shipping'); + +/** @var OrderItem $orderItem */ +$orderItem = $objectManager->create(OrderItem::class); +$orderItem->setProductId($product->getId()) + ->setQtyOrdered(2) + ->setBasePrice($product->getPrice()) + ->setPrice($product->getPrice()) + ->setRowTotal($product->getPrice()) + ->setProductType('simple'); + +require __DIR__ . '/payment.php'; + +$order = $objectManager->create(Order::class); +$order->setIncrementId('100000002') + ->setSubtotal($product->getPrice() * 2) + ->setBaseSubtotal($product->getPrice() * 2) + ->setCustomerEmail('admin@example.com') + ->setCustomerIsGuest(true) + ->setBillingAddress($billingAddress) + ->setShippingAddress($shippingAddress) + ->setStoreId( + $objectManager->get(StoreManagerInterface::class)->getStore() + ->getId() + ) + ->addItem($orderItem) + ->setPayment($payment); + +/** @var OrderRepositoryInterface $orderRepository */ +$orderRepository = $objectManager->get(OrderRepositoryInterface::class); +$orderRepository->save($order); diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/order_rollback.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/order_rollback.php new file mode 100644 index 0000000000000..a2da0b639e98d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/order_rollback.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\TestFramework\ObjectManager; + +$objectManager = ObjectManager::getInstance(); + +/** @var SearchCriteriaBuilder $searchCriteriaBuilder */ +$searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class); +$searchCriteria = $searchCriteriaBuilder->addFilter('increment_id', '100000002') + ->create(); + +/** @var OrderRepositoryInterface $orderRepository */ +$orderRepository = $objectManager->get(OrderRepositoryInterface::class); +$items = $orderRepository->getList($searchCriteria) + ->getItems(); + +foreach ($items as $item) { + $orderRepository->delete($item); +} + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/partial_invoice.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/partial_invoice.php new file mode 100644 index 0000000000000..22b954515f3b5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/partial_invoice.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Sales\Api\InvoiceRepositoryInterface; +use Magento\Sales\Api\TransactionRepositoryInterface; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Service\InvoiceService; +use Magento\TestFramework\ObjectManager; + +/** @var Order $order */ + +require __DIR__ . '/order.php'; + +$objectManager = ObjectManager::getInstance(); + +/** @var InvoiceService $invoiceService */ +$invoiceService = $objectManager->get(InvoiceService::class); +$invoice = $invoiceService->prepareInvoice($order); +$invoice->setIncrementId('100000002'); +$invoice->register(); + +$items = $invoice->getAllItems(); +$item = array_pop($items); +$item->setQty(1); +$invoice->setTotalQty(1); + +$items = $order->getAllItems(); +/** @var \Magento\Sales\Api\Data\OrderItemInterface $item */ +$item = array_pop($items); +$item->setQtyInvoiced(1); +$invoice->collectTotals(); + +/** @var InvoiceRepositoryInterface $invoiceRepository */ +$invoiceRepository = $objectManager->get(InvoiceRepositoryInterface::class); +$invoice = $invoiceRepository->save($invoice); + +/** @var TransactionRepositoryInterface $transactionRepository */ +$transactionRepository = $objectManager->get(TransactionRepositoryInterface::class); +$transaction = $transactionRepository->create(); +$transaction->setTxnType('capture'); +$transaction->setPaymentId($order->getPayment()->getEntityId()); +$transaction->setOrderId($order->getEntityId()); +$transactionRepository->save($transaction); diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/partial_invoice_rollback.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/partial_invoice_rollback.php new file mode 100644 index 0000000000000..1ed4438f87db2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/partial_invoice_rollback.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Sales\Api\InvoiceRepositoryInterface; +use Magento\TestFramework\ObjectManager; + +$objectManager = ObjectManager::getInstance(); + +/** @var SearchCriteriaBuilder $searchCriteriaBuilder */ +$searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class); +$searchCriteria = $searchCriteriaBuilder->addFilter('increment_id', '%10000000%', 'like') + ->create(); + +/** @var InvoiceRepositoryInterface $invoiceRepository */ +$invoiceRepository = $objectManager->get(InvoiceRepositoryInterface::class); +$items = $invoiceRepository->getList($searchCriteria) + ->getItems(); + +foreach ($items as $item) { + $invoiceRepository->delete($item); +} + +require __DIR__ . '/order_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment.php new file mode 100644 index 0000000000000..a4285b963bffa --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Braintree\Model\Ui\ConfigProvider; +use Magento\Sales\Api\Data\OrderPaymentExtensionInterfaceFactory; +use Magento\Sales\Model\Order\Payment; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; + +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +require __DIR__ . '/../../Vault/_files/token.php'; + +$token->setPaymentMethodCode(ConfigProvider::CODE); +/** @var OrderPaymentExtensionInterfaceFactory $paymentExtensionFactory */ +$paymentExtensionFactory = $objectManager->get(OrderPaymentExtensionInterfaceFactory::class); +$extensionAttributes = $paymentExtensionFactory->create(); +$extensionAttributes->setVaultPaymentToken($token); + +/** @var Payment $payment */ +$payment = $objectManager->create(Payment::class); +$payment->setMethod(ConfigProvider::CODE); +$payment->setExtensionAttributes($extensionAttributes); +$payment->setAuthorizationTransaction(true); diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddCommentTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddCommentTest.php deleted file mode 100644 index 020c9f51bb10f..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddCommentTest.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php -/*** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Sales\Controller\Adminhtml\Order; - -class AddCommentTest extends \Magento\TestFramework\TestCase\AbstractBackendController -{ - public function setUp() - { - $this->resource = 'Magento_Sales::comment'; - $this->uri = 'backend/sales/order/addcomment'; - parent::setUp(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddressSaveTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddressSaveTest.php deleted file mode 100644 index 9cdd84a5971f3..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddressSaveTest.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php -/*** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Sales\Controller\Adminhtml\Order; - -class AddressSaveTest extends \Magento\TestFramework\TestCase\AbstractBackendController -{ - public function setUp() - { - $this->resource = 'Magento_Sales::actions_edit'; - $this->uri = 'backend/sales/order/addresssave'; - parent::setUp(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddressTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddressTest.php deleted file mode 100644 index 73e46b513287f..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddressTest.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php -/*** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Sales\Controller\Adminhtml\Order; - -class AddressTest extends \Magento\TestFramework\TestCase\AbstractBackendController -{ - public function setUp() - { - $this->resource = 'Magento_Sales::actions_edit'; - $this->uri = 'backend/sales/order/address'; - parent::setUp(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CancelTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CancelTest.php deleted file mode 100644 index c24191fe5e45e..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CancelTest.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php -/*** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Sales\Controller\Adminhtml\Order; - -class CancelTest extends \Magento\TestFramework\TestCase\AbstractBackendController -{ - public function setUp() - { - $this->resource = 'Magento_Sales::cancel'; - $this->uri = 'backend/sales/order/cancel'; - parent::setUp(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/EmailTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/EmailTest.php deleted file mode 100644 index 70fbb2ee9a5bd..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/EmailTest.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php -/*** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Sales\Controller\Adminhtml\Order; - -class EmailTest extends \Magento\TestFramework\TestCase\AbstractBackendController -{ - public function setUp() - { - $this->resource = 'Magento_Sales::email'; - $this->uri = 'backend/sales/order/email'; - parent::setUp(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/HoldTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/HoldTest.php deleted file mode 100644 index 4c90939d75b2d..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/HoldTest.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php -/*** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Sales\Controller\Adminhtml\Order; - -class HoldTest extends \Magento\TestFramework\TestCase\AbstractBackendController -{ - public function setUp() - { - $this->resource = 'Magento_Sales::hold'; - $this->uri = 'backend/sales/order/hold'; - parent::setUp(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/ReviewPaymentTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/ReviewPaymentTest.php deleted file mode 100644 index 96d197628584a..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/ReviewPaymentTest.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php -/*** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Sales\Controller\Adminhtml\Order; - -class ReviewPaymentTest extends \Magento\TestFramework\TestCase\AbstractBackendController -{ - public function setUp() - { - $this->resource = 'Magento_Sales::review_payment'; - $this->uri = 'backend/sales/order/reviewpayment'; - parent::setUp(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/UnholdTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/UnholdTest.php deleted file mode 100644 index 351801d8a0558..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/UnholdTest.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php -/*** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Sales\Controller\Adminhtml\Order; - -class UnholdTest extends \Magento\TestFramework\TestCase\AbstractBackendController -{ - public function setUp() - { - $this->resource = 'Magento_Sales::unhold'; - $this->uri = 'backend/sales/order/unhold'; - parent::setUp(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/ViewTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/ViewTest.php deleted file mode 100644 index 0374edfaad9d9..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/ViewTest.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php -/*** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Sales\Controller\Adminhtml\Order; - -class ViewTest extends \Magento\TestFramework\TestCase\AbstractBackendController -{ - public function setUp() - { - $this->resource = 'Magento_Sales::actions_view'; - $this->uri = 'backend/sales/order/view'; - parent::setUp(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Transactions/FetchTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Transactions/FetchTest.php deleted file mode 100644 index 7b9481db7997a..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Transactions/FetchTest.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php -/*** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Sales\Controller\Adminhtml\Transactions; - -use Magento\TestFramework\TestCase\AbstractBackendController; - -class FetchTest extends \Magento\TestFramework\TestCase\AbstractBackendController -{ - public function setUp() - { - $this->resource = 'Magento_Sales::transactions_fetch'; - $this->uri = 'backend/sales/transactions/fetch'; - parent::setUp(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Vault/_files/token.php b/dev/tests/integration/testsuite/Magento/Vault/_files/token.php index 396b2a5c20536..e1e0a31da4f5f 100644 --- a/dev/tests/integration/testsuite/Magento/Vault/_files/token.php +++ b/dev/tests/integration/testsuite/Magento/Vault/_files/token.php @@ -23,4 +23,4 @@ /** @var PaymentTokenRepository $tokenRepository */ $tokenRepository = $objectManager->create(PaymentTokenRepository::class); -$tokenRepository->save($token); +$token = $tokenRepository->save($token); From 25573a4b7e4742ea05b5900b3f098c685d1b48d5 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 24 Jul 2018 14:14:18 +0300 Subject: [PATCH 091/211] MAGETWO-73359: CMS Page does not save when same url key with hierarchy for Multi-store AdminMultipleWebsitesUseDefaultValuesTest test fix --- .../Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml index e191cebbf9032..5cdfbffa97d28 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml @@ -43,6 +43,8 @@ <waitForPageLoad time="30" stepKey="waitForProductsGridPageLoad"/> <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickAddSimpleProduct"/> + <!-- Need to wait because fields are loaded a little bit later then it starts to fill them --> + <waitForElementVisible selector="{{AdminProductFormSection.productName}}" stepKey="waitForNameFieldVisible"/> <fillField userInput="{{_defaultProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillProductName"/> <fillField userInput="{{_defaultProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillProductSKU"/> <fillField userInput="{{_defaultProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillProductPrice"/> @@ -56,6 +58,7 @@ <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessage"/> <!-- switch to the second store view --> + <scrollToTopOfPage stepKey="scrollToPageTopToSeeStoreSwitcher"/> <click selector="{{AdminProductFormActionSection.changeStoreButton}}" stepKey="clickStoreviewSwitcher"/> <click selector="{{AdminProductFormActionSection.selectStoreView('Second Store View')}}" stepKey="chooseStoreView"/> <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="acceptStoreSwitchingMessage"/> From 23afd2f3e21b0c01c18733e5ace8de6533ace9cf Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Tue, 24 Jul 2018 14:54:41 +0300 Subject: [PATCH 092/211] MAGETWO-77742: [2.2.x] - Error message when uploading unsupported file format --- .../Mftf/ActionGroup/CustomOptionsActionGroup.xml | 1 - .../ActionGroup/ProductsOnAdminActionGroup.xml | 2 +- ...atalogPage.xml => AdminCatalogProductPage.xml} | 2 +- ...Page.xml => AdminProductAttributeEditPage.xml} | 2 +- .../Section/AdminProductAttributeGridSection.xml | 1 + .../AdminProductCustomizableOptionsSection.xml | 3 ++- .../Section/AdminProductGridFilterSection.xml | 2 +- .../view/frontend/web/js/catalog-add-to-cart.js | 1 + .../AdminConfigurableProductActionGroup.xml | 6 +++--- ...minCreateProductConfigurationsPanelSection.xml | 4 +--- .../Mftf/Section/AdminDataGridHeaderSection.xml | 14 -------------- ...onfigurableProductWithFileCustomOptionTest.xml | 2 +- ...CreateConfigurationsWithSwatchActionGroup.xml} | 12 ++++++------ .../Test/Mftf/Data/SwatchAttributeData.xml | 2 +- .../Swatches/Test/Mftf/Data/SwatchOptionData.xml | 4 ++-- .../Test/Mftf/Page/AdminProductCreatePage.xml | 14 ++++++++++++++ .../Section/AdminNewAttributePanelSection.xml | 2 +- ...frontSwatchProductWithFileCustomOptionTest.xml | 15 ++++++++------- 18 files changed, 45 insertions(+), 44 deletions(-) rename app/code/Magento/Catalog/Test/Mftf/Page/{ProductCatalogPage.xml => AdminCatalogProductPage.xml} (80%) rename app/code/Magento/Catalog/Test/Mftf/Page/{AdminCategoryProductAttributeEditPage.xml => AdminProductAttributeEditPage.xml} (80%) delete mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminDataGridHeaderSection.xml rename app/code/Magento/Swatches/Test/Mftf/ActionGroup/{AddSwatchToProductActionGroup.xml => CreateConfigurationsWithSwatchActionGroup.xml} (90%) create mode 100644 app/code/Magento/Swatches/Test/Mftf/Page/AdminProductCreatePage.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml index 6ee0fe5324e9b..c095faa73d9b1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml @@ -24,5 +24,4 @@ <selectOption selector="{{AdminProductCustomizableOptionsSection.optionPriceType}}" userInput="{{option.price_type}}" stepKey="selectPriceType"/> <fillField selector="{{AdminProductCustomizableOptionsSection.optionFileExtensions}}" userInput="{{option.file_extension}}" stepKey="fillCompatibleExtensions"/> </actionGroup> - </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductsOnAdminActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductsOnAdminActionGroup.xml index 6a61832edd8b0..067a3cff587bb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductsOnAdminActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductsOnAdminActionGroup.xml @@ -34,7 +34,7 @@ <waitForPageLoad stepKey="waitForPageLoad"/> <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="clickOnFiltersButton"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clearFilters"/> - <fillField selector="{{AdminProductGridFilterSection.name}}" userInput="{{product.name}}" stepKey="fillNameFieldInFilter"/> + <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{product.name}}" stepKey="fillNameFieldInFilter"/> <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="applyFilters"/> <click selector="{{AdminProductGridSection.multicheckDropdown}}" stepKey="clickMulticheckDropDown"/> <click selector="{{AdminProductGridSection.multicheckOption('Select All')}}" stepKey="selectAllFilteredProducts"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/ProductCatalogPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminCatalogProductPage.xml similarity index 80% rename from app/code/Magento/Catalog/Test/Mftf/Page/ProductCatalogPage.xml rename to app/code/Magento/Catalog/Test/Mftf/Page/AdminCatalogProductPage.xml index 412eed01e4a2e..e5d3704bafcd1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/ProductCatalogPage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminCatalogProductPage.xml @@ -8,6 +8,6 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> - <page name="ProductCatalogPage" url="/catalog/product/" area="admin" module="Magento_Catalog"> + <page name="AdminCatalogProductPage" url="/catalog/product/" area="admin" module="Magento_Catalog"> </page> </pages> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryProductAttributeEditPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeEditPage.xml similarity index 80% rename from app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryProductAttributeEditPage.xml rename to app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeEditPage.xml index b22239ac51673..d987a58a75963 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryProductAttributeEditPage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeEditPage.xml @@ -8,7 +8,7 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> - <page name="AdminCategoryProductAttributeEditPage" url="catalog/product_attribute/edit/" area="admin" module="Magento_Catalog"> + <page name="AdminProductAttributeEditPage" url="catalog/product_attribute/edit/" area="admin" module="Magento_Catalog"> <section name="AdminProductAttributeEditSection"/> <section name="AdminConfirmationModalSection"/> </page> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml index 96d03815df286..bb944270a10a9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml @@ -15,5 +15,6 @@ <element name="search" type="button" selector="button[data-action=grid-filter-apply]" timeout="30"/> <element name="resetFilter" type="button" selector="button[data-action='grid-filter-reset']" timeout="30"/> <element name="firstRow" type="button" selector="//*[@id='attributeGrid_table']/tbody/tr[1]"/> + <element name="attributeCodeFilterInput" type="input" selector=".admin__data-grid-filters input[name='attribute_code']"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml index 43837d7bace9c..98354601db7e5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml @@ -28,5 +28,6 @@ <element name="optionType" type="block" selector="//*[@data-index='custom_options']//label[text()='{{var1}}'][ancestor::*[contains(@class, '_active')]]" parameterized="true" /> <element name="optionPrice" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[@name='product[options][0][price]']"/> <element name="optionPriceType" type="select" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[@name='product[options][0][price_type]']"/> - <element name="optionFileExtensions" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[@name='product[options][0][file_extension]']"/> </section> + <element name="optionFileExtensions" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[@name='product[options][0][file_extension]']"/> + </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridFilterSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridFilterSection.xml index 7ff653b9c32b3..ba636eb42633d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridFilterSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridFilterSection.xml @@ -17,7 +17,7 @@ <element name="applyFilters" type="button" selector="button[data-action='grid-filter-apply']" timeout="30"/> <element name="newFromDateFilter" type="input" selector="input.admin__control-text[name='news_from_date[from]']"/> <element name="skuFilter" type="input" selector="input.admin__control-text[name='sku']"/> - <element name="name" type="input" selector="input.admin__control-text[name='name']"/> + <element name="nameFilter" type="input" selector="input.admin__control-text[name='name']"/> <element name="viewBookmark" type="button" selector="//div[contains(@class, 'admin__data-grid-action-bookmarks')]/ul/li/div/a[text() = '{{label}}']" parameterized="true" timeout="30"/> <element name="viewDropdown" type="button" selector=".admin__data-grid-action-bookmarks button.admin__action-dropdown"/> </section> diff --git a/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js b/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js index 9f46b4a689459..9375d207e6ced 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js @@ -51,6 +51,7 @@ define([ /** * @private + * @param {String} url */ _redirect: function (url) { var urlParts = url.split('#'), diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml index cab584998e52f..5105a9c71b8f2 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml @@ -61,9 +61,9 @@ <click selector="{{AdminCreateProductConfigurationsPanelSection.next}}" stepKey="clickOnNextButton2"/> <click selector="{{AdminCreateProductConfigurationsPanelSection.applyUniquePricesByAttributeToEachSku}}" stepKey="clickOnApplyUniquePricesByAttributeToEachSku"/> <selectOption selector="{{AdminCreateProductConfigurationsPanelSection.selectAttribute}}" userInput="{{colorProductAttribute.default_label}}" stepKey="selectAttributes"/> - <fillField selector="{{AdminCreateProductConfigurationsPanelSection.attribute1}}" userInput="{{colorProductAttribute1.price}}" stepKey="fillAttributePrice1"/> - <fillField selector="{{AdminCreateProductConfigurationsPanelSection.attribute2}}" userInput="{{colorProductAttribute2.price}}" stepKey="fillAttributePrice2"/> - <fillField selector="{{AdminCreateProductConfigurationsPanelSection.attribute3}}" userInput="{{colorProductAttribute3.price}}" stepKey="fillAttributePrice3"/> + <fillField selector="{{AdminCreateProductConfigurationsPanelSection.attribute('0')}}" userInput="{{colorProductAttribute1.price}}" stepKey="fillAttributePrice1"/> + <fillField selector="{{AdminCreateProductConfigurationsPanelSection.attribute('1')}}" userInput="{{colorProductAttribute2.price}}" stepKey="fillAttributePrice2"/> + <fillField selector="{{AdminCreateProductConfigurationsPanelSection.attribute('2')}}" userInput="{{colorProductAttribute3.price}}" stepKey="fillAttributePrice3"/> <click selector="{{AdminCreateProductConfigurationsPanelSection.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> <fillField selector="{{AdminCreateProductConfigurationsPanelSection.quantity}}" userInput="1" stepKey="enterAttributeQuantity"/> <click selector="{{AdminCreateProductConfigurationsPanelSection.next}}" stepKey="clickOnNextButton3"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml index 031d8913b0dbd..b1caaa5782c8f 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml @@ -22,9 +22,7 @@ <element name="saveAttribute" type="button" selector="li[data-attribute-option-title=''] .action-save" timeout="30"/> <element name="applyUniquePricesByAttributeToEachSku" type="radio" selector=".admin__field-label[for='apply-unique-prices-radio']"/> <element name="selectAttribute" type="select" selector="#select-each-price" timeout="30"/> - <element name="attribute1" type="input" selector="#apply-single-price-input-0"/> - <element name="attribute2" type="input" selector="#apply-single-price-input-1"/> - <element name="attribute3" type="input" selector="#apply-single-price-input-2"/> + <element name="attribute" type="input" selector="#apply-single-price-input-{{var1}}" parameterized="true"/> <element name="applySingleQuantityToEachSkus" type="radio" selector=".admin__field-label[for='apply-single-inventory-radio']" timeout="30"/> <element name="quantity" type="input" selector="#apply-single-inventory-input"/> </section> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminDataGridHeaderSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminDataGridHeaderSection.xml deleted file mode 100644 index 8fd542131177a..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminDataGridHeaderSection.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> - <section name="AdminDataGridHeaderSection"> - <element name="attributeCodeFilterInput" type="input" selector=".admin__data-grid-filters input[name='attribute_code']"/> - </section> -</sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml index d3b654542eccb..c4f63cef6657d 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml @@ -16,7 +16,7 @@ <description value="Configurable product has file custom option. When adding to cart with an invalid filetype, the correct error message is shown, and options remain selected."/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-93318"/> - <group value="ConfigurableProduct"/> + <group value="configurableProduct"/> </annotations> <before> diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddSwatchToProductActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/CreateConfigurationsWithSwatchActionGroup.xml similarity index 90% rename from app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddSwatchToProductActionGroup.xml rename to app/code/Magento/Swatches/Test/Mftf/ActionGroup/CreateConfigurationsWithSwatchActionGroup.xml index 5f4eb291a39a0..4eb4ca0b1327b 100644 --- a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddSwatchToProductActionGroup.xml +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/CreateConfigurationsWithSwatchActionGroup.xml @@ -8,13 +8,13 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AddVisualSwatchToProductActionGroup"> + <actionGroup name="CreateConfigurationsWithVisualSwatch"> <arguments> - <argument name="attribute" defaultValue="visualSwatchAttribute"/> - <argument name="option1" defaultValue="visualSwatchOption1"/> - <argument name="option2" defaultValue="visualSwatchOption2"/> + <argument name="attribute" defaultValue="VisualSwatchAttribute"/> + <argument name="option1" defaultValue="VisualSwatchOption1"/> + <argument name="option2" defaultValue="VisualSwatchOption2"/> </arguments> - <seeInCurrentUrl url="{{ProductCatalogPage.url}}" stepKey="seeOnProductEditPage"/> + <seeInCurrentUrl url="{{AdminCatalogProductPage.url}}" stepKey="seeOnProductEditPage"/> <conditionalClick selector="{{AdminProductFormConfigurationsSection.sectionHeader}}" dependentSelector="{{AdminProductFormConfigurationsSection.createConfigurations}}" visible="false" stepKey="openConfigurationSection"/> <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="openConfigurationPanel"/> <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanelSection.createNewAttribute}}" stepKey="waitForSlideOut"/> @@ -42,7 +42,7 @@ <!--Find attribute in grid and select--> <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFilters"/> <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="clickOnFilters"/> - <fillField selector="{{AdminDataGridHeaderSection.attributeCodeFilterInput}}" userInput="{{attribute.default_label}}" stepKey="fillFilterAttributeCodeField"/> + <fillField selector="{{AdminProductAttributeGridSection.attributeCodeFilterInput}}" userInput="{{attribute.default_label}}" stepKey="fillFilterAttributeCodeField"/> <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFiltersButton"/> <click selector="{{AdminDataGridTableSection.rowCheckbox('1')}}" stepKey="clickOnFirstCheckbox"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml index 092a3cf26c3ae..7822120337e1d 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchAttributeData.xml @@ -8,7 +8,7 @@ <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> - <entity name="visualSwatchAttribute" type="SwatchAttribute"> + <entity name="VisualSwatchAttribute" type="SwatchAttribute"> <data key="default_label" unique="suffix">VisualSwatchAttr</data> <data key="input_type">Visual Swatch</data> </entity> diff --git a/app/code/Magento/Swatches/Test/Mftf/Data/SwatchOptionData.xml b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchOptionData.xml index 84ffde9ec14f5..d6228512c807c 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Data/SwatchOptionData.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Data/SwatchOptionData.xml @@ -8,12 +8,12 @@ <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> - <entity name="visualSwatchOption1" type="SwatchOption"> + <entity name="VisualSwatchOption1" type="SwatchOption"> <data key="admin_label" unique="suffix">VisualOpt1</data> <data key="default_label" unique="suffix">VisualOpt1</data> </entity> - <entity name="visualSwatchOption2" type="SwatchOption"> + <entity name="VisualSwatchOption2" type="SwatchOption"> <data key="admin_label" unique="suffix">VisualOpt2</data> <data key="default_label" unique="suffix">VisualOpt2</data> </entity> diff --git a/app/code/Magento/Swatches/Test/Mftf/Page/AdminProductCreatePage.xml b/app/code/Magento/Swatches/Test/Mftf/Page/AdminProductCreatePage.xml new file mode 100644 index 0000000000000..bce9c170899a3 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Page/AdminProductCreatePage.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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + <page name="AdminProductCreatePage" url="catalog/product/new/set/{{set}}/type/{{type}}/" area="admin" module="Magento_Catalog" parameterized="true"> + <section name="AdminNewAttributePanelSection"/> + </page> +</pages> diff --git a/app/code/Magento/Swatches/Test/Mftf/Section/AdminNewAttributePanelSection.xml b/app/code/Magento/Swatches/Test/Mftf/Section/AdminNewAttributePanelSection.xml index 81fc8a1ae5922..664d318c6e831 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Section/AdminNewAttributePanelSection.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Section/AdminNewAttributePanelSection.xml @@ -9,7 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> <section name="AdminNewAttributePanelSection"> - <element name="addVisualSwatchOption" type="button" selector="button#add_new_swatch_visual_option_button"/> + <element name="addVisualSwatchOption" type="button" selector="#add_new_swatch_visual_option_button"/> <element name="visualSwatchOptionAdminValue" type="input" selector="[data-role='swatch-visual-options-container'] input[name='optionvisual[value][option_{{row}}][0]']" parameterized="true"/> <element name="visualSwatchOptionDefaultStoreValue" type="input" selector="[data-role='swatch-visual-options-container'] input[name='optionvisual[value][option_{{row}}][1]']" parameterized="true"/> </section> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml index da7b9901c3f16..bd05a326fd4e9 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml @@ -17,7 +17,7 @@ <severity value="CRITICAL"/> <testCaseId value="MAGETWO-93331"/> <group value="ConfigurableProduct"/> - <group value="Swatches"/> + <group value="swatches"/> </annotations> <before> @@ -30,7 +30,7 @@ <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> <actionGroup ref="DeleteProductAttribute" stepKey="deleteCreatedProductAttribute"> - <argument name="productAttribute" value="visualSwatchAttribute"/> + <argument name="productAttribute" value="VisualSwatchAttribute"/> </actionGroup> <actionGroup ref="logout" stepKey="logoutAdminUserAfterTest"/> </after> @@ -48,7 +48,7 @@ </actionGroup> <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="searchAndSelectCategory"/> <!--Add swatch attribute to configurable product--> - <actionGroup ref="AddVisualSwatchToProductActionGroup" stepKey="addSwatchToProduct"/> + <actionGroup ref="CreateConfigurationsWithVisualSwatch" stepKey="createConfigurationsWithVisualSwatch"/> <!--Add custom option to configurable product--> <actionGroup ref="AddProductCustomOptionFile" stepKey="addCustomOptionToProduct"/> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> @@ -64,16 +64,17 @@ <click selector="{{StorefrontCategoryProductSection.productAddToCartByName(BaseConfigurableProduct.name)}}" stepKey="tryAddToCartFromCategoryPage"/> <waitForPageLoad stepKey="waitForRedirectToProductPage"/> <seeInCurrentUrl url="{{StorefrontProductPage.url(BaseConfigurableProduct.name)}}" stepKey="seeOnProductPage"/> - <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel(visualSwatchOption2.default_label)}}" stepKey="clickSwatchOption"/> - <see selector="{{StorefrontProductInfoMainSection.selectedSwatchValue(visualSwatchAttribute.default_label)}}" userInput="{{visualSwatchOption2.default_label}}" stepKey="seeSwatchIsSelected"/> + <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel(VisualSwatchOption2.default_label)}}" stepKey="clickSwatchOption"/> + <see selector="{{StorefrontProductInfoMainSection.selectedSwatchValue(VisualSwatchAttribute.default_label)}}" userInput="{{VisualSwatchOption2.default_label}}" stepKey="seeSwatchIsSelected"/> <!--Try invalid file--> <attachFile selector="{{StorefrontProductInfoMainSection.addLinkFileUploadFile(ProductOptionFile.title)}}" userInput="lorem_ipsum.docx" stepKey="attachInvalidFile"/> <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="addToCartInvalidFile"/> + <waitForPageLoad time="30" stepKey="waitForAddToCartWithError"/> <waitForElementVisible selector="{{StorefrontProductPageSection.alertMessage}}" stepKey="waitForErrorMessageInvalidFile"/> <see selector="{{StorefrontProductPageSection.messagesBlock}}" userInput="The file 'lorem_ipsum.docx' for '{{ProductOptionFile.title}}' has an invalid extension." stepKey="seeMessageInvalidFile"/> <!--Swatch remains selected--> - <see selector="{{StorefrontProductInfoMainSection.selectedSwatchValue(visualSwatchAttribute.default_label)}}" userInput="{{visualSwatchOption2.default_label}}" stepKey="seeSwatchRemainsSelected"/> + <see selector="{{StorefrontProductInfoMainSection.selectedSwatchValue(VisualSwatchAttribute.default_label)}}" userInput="{{VisualSwatchOption2.default_label}}" stepKey="seeSwatchRemainsSelected"/> <!--Try valid file--> <attachFile selector="{{StorefrontProductInfoMainSection.addLinkFileUploadFile(ProductOptionFile.title)}}" userInput="{{MagentoLogo.file}}" stepKey="attachValidFile"/> <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$132.99" stepKey="seePriceUpdated"/> @@ -85,7 +86,7 @@ <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCart"/> <waitForPageLoad stepKey="waitForCartPageLoad"/> <seeElement selector="{{CheckoutCartProductSection.productLinkByName(BaseConfigurableProduct.name)}}" stepKey="seeProductInCart"/> - <see selector="{{CheckoutCartProductSection.productOptionByNameAndAttribute(BaseConfigurableProduct.name, visualSwatchAttribute.default_label)}}" userInput="{{visualSwatchOption2.default_label}}" stepKey="seeSelectedSwatch"/> + <see selector="{{CheckoutCartProductSection.productOptionByNameAndAttribute(BaseConfigurableProduct.name, VisualSwatchAttribute.default_label)}}" userInput="{{VisualSwatchOption2.default_label}}" stepKey="seeSelectedSwatch"/> <see selector="{{CheckoutCartProductSection.productOptionByNameAndAttribute(BaseConfigurableProduct.name, ProductOptionFile.title)}}" userInput="{{MagentoLogo.file}}" stepKey="seeCorrectOptionFile"/> <!--Delete cart item--> <click selector="{{CheckoutCartProductSection.removeItem}}" stepKey="deleteCartItem"/> From c19bd50544dd009408bd6b4db316ba84f9f444fd Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Tue, 24 Jul 2018 15:33:25 +0300 Subject: [PATCH 093/211] MAGETWO-75086: Child product image should be shown in Wishist if options are selected for configurable product #8168 --- .../Test/Mftf/ActionGroup/ProductsOnAdminActionGroup.xml | 2 +- ...ributeEditPage.xml => AdminProductAttributeEditPage.xml} | 2 +- .../Test/Mftf/Section/AdminProductGridFilterSection.xml | 2 +- app/code/Magento/Checkout/Model/Cart/ImageProvider.php | 4 ++-- .../ActionGroup/AdminConfigurableProductActionGroup.xml | 6 +++--- .../AdminCreateProductConfigurationsPanelSection.xml | 4 +--- .../Product/Configuration/Item/ItemProductResolverTest.php | 2 +- .../Product/Configuration/Item/ItemProductResolverTest.php | 2 +- 8 files changed, 11 insertions(+), 13 deletions(-) rename app/code/Magento/Catalog/Test/Mftf/Page/{AdminCategoryProductAttributeEditPage.xml => AdminProductAttributeEditPage.xml} (80%) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductsOnAdminActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductsOnAdminActionGroup.xml index 6a61832edd8b0..067a3cff587bb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductsOnAdminActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductsOnAdminActionGroup.xml @@ -34,7 +34,7 @@ <waitForPageLoad stepKey="waitForPageLoad"/> <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="clickOnFiltersButton"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clearFilters"/> - <fillField selector="{{AdminProductGridFilterSection.name}}" userInput="{{product.name}}" stepKey="fillNameFieldInFilter"/> + <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{product.name}}" stepKey="fillNameFieldInFilter"/> <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="applyFilters"/> <click selector="{{AdminProductGridSection.multicheckDropdown}}" stepKey="clickMulticheckDropDown"/> <click selector="{{AdminProductGridSection.multicheckOption('Select All')}}" stepKey="selectAllFilteredProducts"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryProductAttributeEditPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeEditPage.xml similarity index 80% rename from app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryProductAttributeEditPage.xml rename to app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeEditPage.xml index 6460f520bea3f..3d7e79343155b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryProductAttributeEditPage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeEditPage.xml @@ -8,7 +8,7 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> - <page name="AdminCategoryProductAttributeEditPage" url="catalog/product_attribute/edit/" area="admin" module="Magento_Catalog"> + <page name="AdminProductAttributeEditPage" url="catalog/product_attribute/edit/" area="admin" module="Magento_Catalog"> <section name="AdminProductAttributeEditSection"/> <section name="AdminConfirmationModalSection"/> </page> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridFilterSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridFilterSection.xml index 7ff653b9c32b3..ba636eb42633d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridFilterSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridFilterSection.xml @@ -17,7 +17,7 @@ <element name="applyFilters" type="button" selector="button[data-action='grid-filter-apply']" timeout="30"/> <element name="newFromDateFilter" type="input" selector="input.admin__control-text[name='news_from_date[from]']"/> <element name="skuFilter" type="input" selector="input.admin__control-text[name='sku']"/> - <element name="name" type="input" selector="input.admin__control-text[name='name']"/> + <element name="nameFilter" type="input" selector="input.admin__control-text[name='name']"/> <element name="viewBookmark" type="button" selector="//div[contains(@class, 'admin__data-grid-action-bookmarks')]/ul/li/div/a[text() = '{{label}}']" parameterized="true" timeout="30"/> <element name="viewDropdown" type="button" selector=".admin__data-grid-action-bookmarks button.admin__action-dropdown"/> </section> diff --git a/app/code/Magento/Checkout/Model/Cart/ImageProvider.php b/app/code/Magento/Checkout/Model/Cart/ImageProvider.php index 598d8efe85eda..673e8a3b5937b 100644 --- a/app/code/Magento/Checkout/Model/Cart/ImageProvider.php +++ b/app/code/Magento/Checkout/Model/Cart/ImageProvider.php @@ -25,8 +25,8 @@ class ImageProvider */ protected $itemPool; - /** - * @var \Magento\Checkout\CustomerData\DefaultItem + /** + * @var \Magento\Checkout\CustomerData\DefaultItem */ private $customerDataItem; diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml index b9da545eb8bde..462fd64031849 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml @@ -63,9 +63,9 @@ <click selector="{{AdminCreateProductConfigurationsPanelSection.next}}" stepKey="clickOnNextButton2"/> <click selector="{{AdminCreateProductConfigurationsPanelSection.applyUniquePricesByAttributeToEachSku}}" stepKey="clickOnApplyUniquePricesByAttributeToEachSku"/> <selectOption selector="{{AdminCreateProductConfigurationsPanelSection.selectAttribute}}" userInput="{{colorProductAttribute.default_label}}" stepKey="selectAttributes"/> - <fillField selector="{{AdminCreateProductConfigurationsPanelSection.attribute1}}" userInput="{{colorProductAttribute1.price}}" stepKey="fillAttributePrice1"/> - <fillField selector="{{AdminCreateProductConfigurationsPanelSection.attribute2}}" userInput="{{colorProductAttribute2.price}}" stepKey="fillAttributePrice2"/> - <fillField selector="{{AdminCreateProductConfigurationsPanelSection.attribute3}}" userInput="{{colorProductAttribute3.price}}" stepKey="fillAttributePrice3"/> + <fillField selector="{{AdminCreateProductConfigurationsPanelSection.attribute('0')}}" userInput="{{colorProductAttribute1.price}}" stepKey="fillAttributePrice1"/> + <fillField selector="{{AdminCreateProductConfigurationsPanelSection.attribute('1')}}" userInput="{{colorProductAttribute2.price}}" stepKey="fillAttributePrice2"/> + <fillField selector="{{AdminCreateProductConfigurationsPanelSection.attribute('2')}}" userInput="{{colorProductAttribute3.price}}" stepKey="fillAttributePrice3"/> <click selector="{{AdminCreateProductConfigurationsPanelSection.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> <fillField selector="{{AdminCreateProductConfigurationsPanelSection.quantity}}" userInput="1" stepKey="enterAttributeQuantity"/> <click selector="{{AdminCreateProductConfigurationsPanelSection.next}}" stepKey="clickOnNextButton3"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml index 2c20d69189257..ffb15f96f1f58 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml @@ -22,9 +22,7 @@ <element name="saveAttribute" type="button" selector="li[data-attribute-option-title=''] .action-save" timeout="30"/> <element name="applyUniquePricesByAttributeToEachSku" type="radio" selector=".admin__field-label[for='apply-unique-prices-radio']"/> <element name="selectAttribute" type="select" selector="#select-each-price" timeout="30"/> - <element name="attribute1" type="input" selector="#apply-single-price-input-0"/> - <element name="attribute2" type="input" selector="#apply-single-price-input-1"/> - <element name="attribute3" type="input" selector="#apply-single-price-input-2"/> + <element name="attribute" type="input" selector="#apply-single-price-input-{{var1}}" parameterized="true"/> <element name="applySingleQuantityToEachSkus" type="radio" selector=".admin__field-label[for='apply-single-inventory-radio']" timeout="30"/> <element name="quantity" type="input" selector="#apply-single-inventory-input"/> </section> diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php index b940ef0712ad3..327a231d05272 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php @@ -6,7 +6,7 @@ declare(strict_types=1); -namespace Magento\ConfigurableProduct\Test\Unit\Model\Product\Configuration; +namespace Magento\ConfigurableProduct\Test\Unit\Model\Product\Configuration\Item; use Magento\Catalog\Model\Config\Source\Product\Thumbnail; use Magento\Catalog\Model\Product; diff --git a/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php b/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php index 1434788be3814..5145800c6d911 100644 --- a/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php +++ b/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php @@ -6,7 +6,7 @@ declare(strict_types=1); -namespace Magento\GroupedProduct\Test\Unit\Model\Product\Configuration; +namespace Magento\GroupedProduct\Test\Unit\Model\Product\Configuration\Item; use Magento\Catalog\Model\Config\Source\Product\Thumbnail; use Magento\Catalog\Model\Product; From 079b346582db80088d4c47e868e3e683e4262b06 Mon Sep 17 00:00:00 2001 From: Sergey Shvets <sshvets@magento.com> Date: Tue, 24 Jul 2018 15:37:26 +0300 Subject: [PATCH 094/211] MAGETWO-93729: Fix scope selector for repors --- .../Block/Adminhtml/Grid/AbstractGrid.php | 38 +++++++++-- .../Adminhtml/Report/AbstractReport.php | 64 +++++++++++++------ 2 files changed, 75 insertions(+), 27 deletions(-) diff --git a/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php b/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php index 158455db26455..f58b4393a73d1 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Reports\Block\Adminhtml\Grid; class AbstractGrid extends \Magento\Backend\Block\Widget\Grid\Extended @@ -170,12 +171,7 @@ public function addColumn($columnId, $column) */ protected function _getStoreIds() { - $filterData = $this->getFilterData(); - if ($filterData) { - $storeIds = explode(',', $filterData->getData('store_ids')); - } else { - $storeIds = []; - } + $storeIds = $this->getFilteredStores(); // By default storeIds array contains only allowed stores $allowedStoreIds = array_keys($this->_storeManager->getStores()); // And then array_intersect with post data for prevent unauthorized stores reports @@ -409,4 +405,34 @@ protected function _addCustomFilter($collection, $filterData) { return $this; } + + /** + * + * @return array + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function getFilteredStores(): array + { + $storeIds = []; + + $filterData = $this->getFilterData(); + if ($filterData) { + if ($filterData->getWebsite()) { + $storeIds = array_keys( + $this->_storeManager->getWebsite($filterData->getWebsite())->getStores() + ); + } + + if ($filterData->getGroup()) { + $storeIds = array_keys( + $this->_storeManager->getGroup($filterData->getGroup())->getStores() + ); + } + + if ($filterData->getData('store_ids')) { + $storeIds = explode(',', $filterData->getData('store_ids')); + } + } + return is_array($storeIds) ? $storeIds : []; + } } diff --git a/app/code/Magento/Reports/Controller/Adminhtml/Report/AbstractReport.php b/app/code/Magento/Reports/Controller/Adminhtml/Report/AbstractReport.php index 3dbced45e0a69..68f2722ca6dfb 100644 --- a/app/code/Magento/Reports/Controller/Adminhtml/Report/AbstractReport.php +++ b/app/code/Magento/Reports/Controller/Adminhtml/Report/AbstractReport.php @@ -9,8 +9,10 @@ * * @author Magento Core Team <core@magentocommerce.com> */ + namespace Magento\Reports\Controller\Adminhtml\Report; +use Magento\Backend\Helper\Data as BackendHelper; use Magento\Framework\Stdlib\DateTime\TimezoneInterface; /** @@ -41,22 +43,30 @@ abstract class AbstractReport extends \Magento\Backend\App\Action */ protected $timezone; + /** + * @var BackendHelper + */ + private $backendHelper; + /** * @param \Magento\Backend\App\Action\Context $context * @param \Magento\Framework\App\Response\Http\FileFactory $fileFactory * @param \Magento\Framework\Stdlib\DateTime\Filter\Date $dateFilter * @param TimezoneInterface $timezone + * @param BackendHelper|null $backendHelperData */ public function __construct( \Magento\Backend\App\Action\Context $context, \Magento\Framework\App\Response\Http\FileFactory $fileFactory, \Magento\Framework\Stdlib\DateTime\Filter\Date $dateFilter, - TimezoneInterface $timezone + TimezoneInterface $timezone, + BackendHelper $backendHelperData = null ) { parent::__construct($context); $this->_fileFactory = $fileFactory; $this->_dateFilter = $dateFilter; $this->timezone = $timezone; + $this->backendHelper = $backendHelperData ?: $this->_objectManager->get(BackendHelper::class); } /** @@ -103,25 +113,7 @@ public function _initReportAction($blocks) $blocks = [$blocks]; } - $requestData = $this->_objectManager->get( - \Magento\Backend\Helper\Data::class - )->prepareFilterString( - $this->getRequest()->getParam('filter') - ); - $inputFilter = new \Zend_Filter_Input( - ['from' => $this->_dateFilter, 'to' => $this->_dateFilter], - [], - $requestData - ); - $requestData = $inputFilter->getUnescaped(); - $requestData['store_ids'] = $this->getRequest()->getParam('store_ids'); - $params = new \Magento\Framework\DataObject(); - - foreach ($requestData as $key => $value) { - if (!empty($value)) { - $params->setData($key, $value); - } - } + $params = $this->initFilterData(); foreach ($blocks as $block) { if ($block) { @@ -147,7 +139,7 @@ protected function _showLastExecutionTime($flagCode, $refreshCode) ->loadSelf(); $updatedAt = 'undefined'; if ($flag->hasData()) { - $updatedAt = $this->timezone->formatDate( + $updatedAt = $this->timezone->formatDate( $flag->getLastUpdate(), \IntlDateFormatter::MEDIUM, true @@ -168,4 +160,34 @@ protected function _showLastExecutionTime($flagCode, $refreshCode) ); return $this; } + + /** + * Init filter data + * + * @return \Magento\Framework\DataObject + */ + private function initFilterData(): \Magento\Framework\DataObject + { + $requestData = $this->backendHelper + ->prepareFilterString( + $this->getRequest()->getParam('filter') + ); + + $filterRules = ['from' => $this->_dateFilter, 'to' => $this->_dateFilter]; + $inputFilter = new \Zend_Filter_Input($filterRules, [], $requestData); + + $requestData = $inputFilter->getUnescaped(); + $requestData['store_ids'] = $this->getRequest()->getParam('store_ids'); + $requestData['group'] = $this->getRequest()->getParam('group'); + $requestData['website'] = $this->getRequest()->getParam('website'); + + $params = new \Magento\Framework\DataObject(); + + foreach ($requestData as $key => $value) { + if (!empty($value)) { + $params->setData($key, $value); + } + } + return $params; + } } From df7c2ea0264097a62be1e9467c0f313b56c4b4e7 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryvinskyi <volodymyr@hryvinskyi.com> Date: Tue, 24 Jul 2018 15:38:06 +0300 Subject: [PATCH 095/211] Update CMS Page Index --- .../Magento/Cms/Controller/Index/Index.php | 48 ++++++++++++++----- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/Cms/Controller/Index/Index.php b/app/code/Magento/Cms/Controller/Index/Index.php index 8e20feb0f058f..3326749727c0f 100644 --- a/app/code/Magento/Cms/Controller/Index/Index.php +++ b/app/code/Magento/Cms/Controller/Index/Index.php @@ -1,11 +1,20 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Cms\Controller\Index; +use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\Controller\Result\Forward; +use Magento\Framework\Controller\Result\ForwardFactory; +use Magento\Framework\View\Result\Page as ResultPage; +use Magento\Cms\Helper\Page; +use Magento\Store\Model\ScopeInterface; + class Index extends \Magento\Framework\App\Action\Action { /** @@ -14,14 +23,30 @@ class Index extends \Magento\Framework\App\Action\Action protected $resultForwardFactory; /** - * @param \Magento\Framework\App\Action\Context $context - * @param \Magento\Framework\Controller\Result\ForwardFactory $resultForwardFactory + * @var \Magento\Framework\App\Config\ScopeConfigInterface + */ + protected $scopeConfig; + + /** + * @var Page + */ + protected $page; + + /** + * @param Context $context + * @param ScopeConfigInterface $scopeConfig + * @param ForwardFactory $resultForwardFactory + * @param Page $page */ public function __construct( - \Magento\Framework\App\Action\Context $context, - \Magento\Framework\Controller\Result\ForwardFactory $resultForwardFactory + Context $context, + ScopeConfigInterface $scopeConfig, + ForwardFactory $resultForwardFactory, + Page $page ) { + $this->scopeConfig = $scopeConfig; $this->resultForwardFactory = $resultForwardFactory; + $this->page = $page; parent::__construct($context); } @@ -29,18 +54,15 @@ public function __construct( * Renders CMS Home page * * @param string|null $coreRoute - * @return \Magento\Framework\Controller\Result\Forward + * + * @return bool|ResponseInterface|Forward|ResultInterface|ResultPage + * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function execute($coreRoute = null) { - $pageId = $this->_objectManager->get( - \Magento\Framework\App\Config\ScopeConfigInterface::class - )->getValue( - \Magento\Cms\Helper\Page::XML_PATH_HOME_PAGE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ); - $resultPage = $this->_objectManager->get(\Magento\Cms\Helper\Page::class)->prepareResultPage($this, $pageId); + $pageId = $this->scopeConfig->getValue(Page::XML_PATH_HOME_PAGE, ScopeInterface::SCOPE_STORE); + $resultPage = $this->page->prepareResultPage($this, $pageId); if (!$resultPage) { /** @var \Magento\Framework\Controller\Result\Forward $resultForward */ $resultForward = $this->resultForwardFactory->create(); From 6bf457c6e146bec98244fd5fe162fb4bd045ea01 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <dhorytskyi@magento.com> Date: Tue, 24 Jul 2018 16:28:37 +0300 Subject: [PATCH 096/211] MAGETWO-93668: Unable to use an attribute with a source model to generate simple products for a configurable --- .../Model/Product/Type/Configurable.php | 2 +- .../Attribute/OptionSelectBuilder.php | 2 +- .../Attribute/OptionSelectBuilderTest.php | 8 +- .../Model/Product/Type/ConfigurableTest.php | 13 +++ ...nfigurable_attribute_with_source_model.php | 37 ++++++++ ...e_attribute_with_source_model_rollback.php | 21 +++++ .../_files/product_configurable_custom.php | 91 +++++++++++++++++++ .../product_configurable_custom_rollback.php | 34 +++++++ 8 files changed, 202 insertions(+), 6 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_with_source_model.php create mode 100644 dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_with_source_model_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_custom.php create mode 100644 dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_custom_rollback.php diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php index 2bfee9fc6e794..254a16e7db8ad 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php @@ -444,7 +444,7 @@ public function getUsedProductAttributes($product) * Retrieve configurable attributes data * * @param \Magento\Catalog\Model\Product $product - * @return \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute[] + * @return \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection */ public function getConfigurableAttributes($product) { diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Attribute/OptionSelectBuilder.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Attribute/OptionSelectBuilder.php index 5d9eed0a188fc..ef5005d7bf5e0 100644 --- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Attribute/OptionSelectBuilder.php +++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Attribute/OptionSelectBuilder.php @@ -91,7 +91,7 @@ public function getSelect(AbstractAttribute $superAttribute, int $productId, Sco ] ), [] - )->joinInner( + )->joinLeft( ['attribute_option' => $this->attributeResource->getTable('eav_attribute_option')], 'attribute_option.option_id = entity_value.value', [] diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Attribute/OptionSelectBuilderTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Attribute/OptionSelectBuilderTest.php index 9802c97372bbb..537288618b648 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Attribute/OptionSelectBuilderTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Attribute/OptionSelectBuilderTest.php @@ -112,8 +112,8 @@ public function testGetSelect() { $this->select->expects($this->exactly(1))->method('from')->willReturnSelf(); $this->select->expects($this->exactly(1))->method('columns')->willReturnSelf(); - $this->select->expects($this->exactly(6))->method('joinInner')->willReturnSelf(); - $this->select->expects($this->exactly(3))->method('joinLeft')->willReturnSelf(); + $this->select->expects($this->exactly(5))->method('joinInner')->willReturnSelf(); + $this->select->expects($this->exactly(4))->method('joinLeft')->willReturnSelf(); $this->select->expects($this->exactly(1))->method('order')->willReturnSelf(); $this->select->expects($this->exactly(2))->method('where')->willReturnSelf(); @@ -156,8 +156,8 @@ public function testGetSelectWithBackendModel() { $this->select->expects($this->exactly(1))->method('from')->willReturnSelf(); $this->select->expects($this->exactly(0))->method('columns')->willReturnSelf(); - $this->select->expects($this->exactly(6))->method('joinInner')->willReturnSelf(); - $this->select->expects($this->exactly(1))->method('joinLeft')->willReturnSelf(); + $this->select->expects($this->exactly(5))->method('joinInner')->willReturnSelf(); + $this->select->expects($this->exactly(2))->method('joinLeft')->willReturnSelf(); $this->select->expects($this->exactly(1))->method('order')->willReturnSelf(); $this->select->expects($this->exactly(2))->method('where')->willReturnSelf(); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php index d8f905f7d4284..78fa4733a2562 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php @@ -157,6 +157,19 @@ public function testGetConfigurableAttributes() } } + /** + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable_custom.php + */ + public function testGetConfigurableAttributesWithSourceModel() + { + $collection = $this->model->getConfigurableAttributes($this->product); + /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute $configurableAttribute */ + $configurableAttribute = $collection->getFirstItem(); + $attribute = $this->_getAttributeByCode('test_configurable_with_sm'); + $this->assertSameSize($attribute->getSource()->getAllOptions(), $configurableAttribute->getOptions()); + } + /** * @magentoAppIsolation enabled * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_with_source_model.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_with_source_model.php new file mode 100644 index 0000000000000..2c4713ac6b16e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_with_source_model.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +$eavSetup = $objectManager->create(\Magento\Eav\Setup\EavSetup::class); +$eavSetup->addAttribute( + \Magento\Catalog\Model\Product::ENTITY, + 'test_configurable_with_sm', + [ + 'group' => 'General', + 'type' => 'varchar', + 'backend' => '', + 'frontend' => '', + 'label' => 'Test configurable with source model', + 'input' => 'select', + 'source' => \Magento\Catalog\Model\Category\Attribute\Source\Mode::class, + 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL, + 'visible' => true, + 'required' => false, + 'user_defined' => true, + 'default' => '', + 'searchable' => false, + 'filterable' => false, + 'comparable' => false, + 'visible_on_front' => false, + 'used_in_product_listing' => true, + 'unique' => false, + 'apply_to' => '' + ] +); + +$eavConfig = $objectManager->get(\Magento\Eav\Model\Config::class); +$eavConfig->clear(); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_with_source_model_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_with_source_model_rollback.php new file mode 100644 index 0000000000000..8930f4da7f686 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_with_source_model_rollback.php @@ -0,0 +1,21 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +$registry = $objectManager->get(\Magento\Framework\Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +$eavConfig = $objectManager->get(\Magento\Eav\Model\Config::class); +$attribute = $eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, 'test_configurable_with_sm'); +if ($attribute instanceof \Magento\Eav\Model\Entity\Attribute\AbstractAttribute && $attribute->getId()) { + $attribute->delete(); +} +$eavConfig->clear(); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_custom.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_custom.php new file mode 100644 index 0000000000000..56306c9f94c26 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_custom.php @@ -0,0 +1,91 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require __DIR__ . '/configurable_attribute_with_source_model.php'; + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +$installer = $objectManager->create(\Magento\Catalog\Setup\CategorySetup::class); + +$eavConfig = $objectManager->get(\Magento\Eav\Model\Config::class); +$attribute = $eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, 'test_configurable_with_sm'); +$options = $attribute->getOptions(); + +$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); +$attributeValues = []; +$associatedProductIds = []; +$attributeSetId = $installer->getAttributeSetId(\Magento\Catalog\Model\Product::ENTITY, 'Default'); + +foreach ($options as $key => $option) { + $productId = $key + 10; + + $product = $objectManager->create(\Magento\Catalog\Model\Product::class); + $product->setTypeId(Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setId($productId) + ->setAttributeSetId($attributeSetId) + ->setWebsiteIds([1]) + ->setName('Configurable Option ' . $option->getLabel()) + ->setSku('simple_' . $productId) + ->setPrice($productId) + ->setTestConfigurableWithSm($option->getValue()) + ->setVisibility(Magento\Catalog\Model\Product\Visibility::VISIBILITY_NOT_VISIBLE) + ->setStatus(Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); + $product = $productRepository->save($product); + + $stockItem = $objectManager->create(\Magento\CatalogInventory\Model\Stock\Item::class); + $stockItem->load($productId, 'product_id'); + if (!$stockItem->getProductId()) { + $stockItem->setProductId($productId); + } + $stockItem->setUseConfigManageStock(1); + $stockItem->setQty(1000); + $stockItem->setIsQtyDecimal(0); + $stockItem->setIsInStock(1); + $stockItem->save(); + + $attributeValues[] = [ + 'label' => 'test', + 'attribute_id' => $attribute->getId(), + 'value_index' => $option->getValue(), + ]; + $associatedProductIds[] = $product->getId(); +} + +$optionsFactory = $objectManager->create(\Magento\ConfigurableProduct\Helper\Product\Options\Factory::class); +$configurableAttributesData = [ + [ + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getStoreLabel(), + 'position' => '0', + 'values' => $attributeValues, + ], +]; +$configurableOptions = $optionsFactory->create($configurableAttributesData); + +$product = $objectManager->create(\Magento\Catalog\Model\Product::class); +$extensionConfigurableAttributes = $product->getExtensionAttributes(); +$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions); +$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds); +$product->setExtensionAttributes($extensionConfigurableAttributes); + +$product->setTypeId(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE) + ->setId(1) + ->setAttributeSetId($attributeSetId) + ->setWebsiteIds([1]) + ->setName('Configurable Product') + ->setSku('configurable') + ->setVisibility(Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]); +$productRepository->save($product); + +$categoryLinkManagement = $objectManager->create(\Magento\Catalog\Api\CategoryLinkManagementInterface::class); +$categoryLinkManagement->assignProductToCategories( + $product->getSku(), + [2] +); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_custom_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_custom_rollback.php new file mode 100644 index 0000000000000..1b7c2b76d0479 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_custom_rollback.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = $objectManager->get(\Magento\Framework\Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +$productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); +foreach (['simple_10', 'simple_11', 'simple_12', 'configurable'] as $sku) { + try { + $product = $productRepository->get($sku, true); + + $stockStatus = $objectManager->create(\Magento\CatalogInventory\Model\Stock\Status::class); + $stockStatus->load($product->getEntityId(), 'product_id'); + $stockStatus->delete(); + + if ($product->getId()) { + $productRepository->delete($product); + } + } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + //Product already removed + } +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/configurable_attribute_with_source_model_rollback.php'; From 846bf62c6603e18dca60bbc88ece7875c239f299 Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko <iivashchenko@magento.com> Date: Tue, 24 Jul 2018 16:35:00 +0300 Subject: [PATCH 097/211] MAGETWO-93347: Double click (not too fast) on proceed to check out after view edit mini cart returns empty shopping cart --- app/code/Magento/Checkout/view/frontend/web/js/sidebar.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js b/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js index debb1acf90d4f..3b5168453e11a 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js @@ -61,13 +61,15 @@ define([ }; events['click ' + this.options.button.checkout] = $.proxy(function () { var cart = customerData.get('cart'), - customer = customerData.get('customer'); + customer = customerData.get('customer'), + element = $(this.options.button.checkout); if (!customer().firstname && cart().isGuestCheckoutAllowed === false) { // set URL for redirect on successful login/registration. It's postprocessed on backend. $.cookie('login_redirect', this.options.url.checkout); if (this.options.url.isRedirectRequired) { + element.prop('disabled', true); location.href = this.options.url.loginUrl; } else { authenticationPopup.showModal(); @@ -75,6 +77,7 @@ define([ return false; } + element.prop('disabled', true); location.href = this.options.url.checkout; }, this); From 45662bb2a3615f3474dce1d80299655e23f5b496 Mon Sep 17 00:00:00 2001 From: "Deep A. Joshi" <deep.ajoshi@EIC.EINFOCHIPS.COM> Date: Tue, 24 Jul 2018 19:29:44 +0530 Subject: [PATCH 098/211] Resolved special character issue for sidebar --- app/code/Magento/Wishlist/view/frontend/templates/sidebar.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Wishlist/view/frontend/templates/sidebar.phtml b/app/code/Magento/Wishlist/view/frontend/templates/sidebar.phtml index 00fb87b411fe0..84b607adb6362 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/sidebar.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/sidebar.phtml @@ -31,7 +31,7 @@ $wishlistHelper = $this->helper('Magento\Wishlist\Helper\Data'); <div class="product-item-details"> <strong class="product-item-name"> <a data-bind="attr: { href: product_url }" class="product-item-link"> - <span data-bind="text: product_name"></span> + <span data-bind="html: product_name"></span> </a> </strong> <div data-bind="html: product_price"></div> From a22e87be610490eafbd05fcabd2dc2c94f583d1f Mon Sep 17 00:00:00 2001 From: "Deep A. Joshi" <deep.ajoshi@EIC.EINFOCHIPS.COM> Date: Tue, 24 Jul 2018 19:55:47 +0530 Subject: [PATCH 099/211] Resolved special character issue order summary --- .../view/frontend/web/template/summary/item/details.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/template/summary/item/details.html b/app/code/Magento/Checkout/view/frontend/web/template/summary/item/details.html index daa37cbe2dc89..dd59bd78416c6 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/summary/item/details.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/summary/item/details.html @@ -12,7 +12,7 @@ <div class="product-item-inner"> <div class="product-item-name-block"> - <strong class="product-item-name" data-bind="text: $parent.name"></strong> + <strong class="product-item-name" data-bind="html: $parent.name"></strong> <div class="details-qty"> <span class="label"><!-- ko i18n: 'Qty' --><!-- /ko --></span> <span class="value" data-bind="text: $parent.qty"></span> From f496bbb187f5c5e858ebe092d6b7acb45ae5a6a9 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Tue, 24 Jul 2018 17:36:02 +0300 Subject: [PATCH 100/211] MAGETWO-73245: Add product to website will reset has_options and required_options --- .../Magento/Catalog/Controller/Adminhtml/Product/Save.php | 4 ++++ app/code/Magento/Catalog/Model/Product.php | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php index 940062cd6cb4c..fcbb72a98df59 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php @@ -234,10 +234,14 @@ protected function copyToStores($data, $productId) $copyFrom = $store['copy_from']; $copyTo = (isset($store['copy_to'])) ? $store['copy_to'] : 0; if ($copyTo) { + $canSaveCustomOptions = (!empty($data['product']['affect_product_custom_options'])) + ? (bool)$data['product']['affect_product_custom_options'] + : false; $this->_objectManager->create(\Magento\Catalog\Model\Product::class) ->setStoreId($copyFrom) ->load($productId) ->setStoreId($copyTo) + ->setCanSaveCustomOptions($canSaveCustomOptions) ->setCopyFromView(true) ->save(); } diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php index 84ce132f3ab50..850fb21777585 100644 --- a/app/code/Magento/Catalog/Model/Product.php +++ b/app/code/Magento/Catalog/Model/Product.php @@ -871,7 +871,7 @@ public function beforeSave() * or in type instance as well */ $this->canAffectOptions($this->_canAffectOptions && $this->getCanSaveCustomOptions()); - if ($this->getCanSaveCustomOptions() || $this->getCopyFromView()) { + if ($this->getCanSaveCustomOptions()) { $options = $this->getOptions(); if (is_array($options)) { $this->setIsCustomOptionChanged(true); From 1eac6e823bcbb1abefe0dd51fea568203bef3e5c Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <dhorytskyi@magento.com> Date: Tue, 24 Jul 2018 17:55:46 +0300 Subject: [PATCH 101/211] MAGETWO-93668: Unable to use an attribute with a source model to generate simple products for a configurable --- .../ConfigurableProduct/Model/Product/Type/Configurable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php index 254a16e7db8ad..2bfee9fc6e794 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php @@ -444,7 +444,7 @@ public function getUsedProductAttributes($product) * Retrieve configurable attributes data * * @param \Magento\Catalog\Model\Product $product - * @return \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection + * @return \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute[] */ public function getConfigurableAttributes($product) { From ffff49d37ec1329f69a00efc914117cf5a09e7c2 Mon Sep 17 00:00:00 2001 From: olysenko <olysenko@magento.com> Date: Tue, 24 Jul 2018 18:20:49 +0300 Subject: [PATCH 102/211] MAGETWO-86650: Abandoned Cart report exports only current page --- .../Adminhtml/Shopcart/Abandoned/Grid.php | 3 +++ .../Adminhtml/Shopcart/Abandoned/GridTest.php | 18 ++++++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Reports/Block/Adminhtml/Shopcart/Abandoned/Grid.php b/app/code/Magento/Reports/Block/Adminhtml/Shopcart/Abandoned/Grid.php index f81176b7a1124..ff76702592196 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Shopcart/Abandoned/Grid.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Shopcart/Abandoned/Grid.php @@ -67,6 +67,9 @@ protected function _prepareCollection() $this->setCollection($collection); parent::_prepareCollection(); + if ($this->_isExport) { + $collection->setPageSize(null); + } $this->getCollection()->resolveCustomerNames(); return $this; } diff --git a/dev/tests/integration/testsuite/Magento/Reports/Block/Adminhtml/Shopcart/Abandoned/GridTest.php b/dev/tests/integration/testsuite/Magento/Reports/Block/Adminhtml/Shopcart/Abandoned/GridTest.php index b8446839c8c99..8402249a50a5f 100644 --- a/dev/tests/integration/testsuite/Magento/Reports/Block/Adminhtml/Shopcart/Abandoned/GridTest.php +++ b/dev/tests/integration/testsuite/Magento/Reports/Block/Adminhtml/Shopcart/Abandoned/GridTest.php @@ -7,6 +7,7 @@ use Magento\Quote\Model\Quote; use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\View\LayoutInterface; /** * Test class for \Magento\Reports\Block\Adminhtml\Shopcart\Abandoned\Grid @@ -23,9 +24,9 @@ class GridTest extends \Magento\Reports\Block\Adminhtml\Shopcart\GridTestAbstrac public function testGridContent() { /** @var \Magento\Framework\View\LayoutInterface $layout */ - $layout = Bootstrap::getObjectManager()->get(\Magento\Framework\View\LayoutInterface::class); + $layout = Bootstrap::getObjectManager()->get(LayoutInterface::class); /** @var Grid $grid */ - $grid = $layout->createBlock(\Magento\Reports\Block\Adminhtml\Shopcart\Abandoned\Grid::class); + $grid = $layout->createBlock(Grid::class); $grid->getRequest()->setParams(['filter' => base64_encode(urlencode('email=customer@example.com'))]); $result = $grid->getPreparedCollection(); @@ -35,4 +36,17 @@ public function testGridContent() $this->assertEquals('customer@example.com', $quote->getCustomerEmail()); $this->assertEquals(10.00, $quote->getSubtotal()); } + + /** + * @return void + */ + public function testPageSizeIsSetToNullWhenExportCsvFile() + { + /** @var \Magento\Framework\View\LayoutInterface $layout */ + $layout = Bootstrap::getObjectManager()->get(LayoutInterface::class); + /** @var Grid $grid */ + $grid = $layout->createBlock(Grid::class); + $grid->getCsvFile(); + $this->assertNull($grid->getCollection()->getPageSize()); + } } From f439513b8ecdcb41ad5d0b42fd7e0c29d5b3a84c Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 24 Jul 2018 18:23:24 +0300 Subject: [PATCH 103/211] MAGETWO-73359: CMS Page does not save when same url key with hierarchy for Multi-store AdminMultipleWebsitesUseDefaultValuesTest test fix --- .../Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml index 5cdfbffa97d28..1deac4262ff7b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml @@ -62,6 +62,8 @@ <click selector="{{AdminProductFormActionSection.changeStoreButton}}" stepKey="clickStoreviewSwitcher"/> <click selector="{{AdminProductFormActionSection.selectStoreView('Second Store View')}}" stepKey="chooseStoreView"/> <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="acceptStoreSwitchingMessage"/> + <scrollToTopOfPage stepKey="scrollToPageTopToSeeStoreSwitcher2"/> + <waitForElementVisible selector="{{AdminMainActionsSection.storeSwitcher}}" stepKey="waitForStoreSwitcherToBeVisible"/> <see userInput="Second Store View" selector="{{AdminMainActionsSection.storeSwitcher}}" stepKey="seeNewStoreViewName"/> <!-- Check if Use Default Value checkboxes are checked --> From e8e3c0670ee3be732e1818481203c0459f57f309 Mon Sep 17 00:00:00 2001 From: olysenko <olysenko@magento.com> Date: Tue, 24 Jul 2018 18:24:34 +0300 Subject: [PATCH 104/211] MAGETWO-86650: Abandoned Cart report exports only current page --- .../Reports/Block/Adminhtml/Shopcart/Abandoned/GridTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Reports/Block/Adminhtml/Shopcart/Abandoned/GridTest.php b/dev/tests/integration/testsuite/Magento/Reports/Block/Adminhtml/Shopcart/Abandoned/GridTest.php index 8402249a50a5f..895f7b846f4d2 100644 --- a/dev/tests/integration/testsuite/Magento/Reports/Block/Adminhtml/Shopcart/Abandoned/GridTest.php +++ b/dev/tests/integration/testsuite/Magento/Reports/Block/Adminhtml/Shopcart/Abandoned/GridTest.php @@ -23,7 +23,7 @@ class GridTest extends \Magento\Reports\Block\Adminhtml\Shopcart\GridTestAbstrac */ public function testGridContent() { - /** @var \Magento\Framework\View\LayoutInterface $layout */ + /** @var LayoutInterface $layout */ $layout = Bootstrap::getObjectManager()->get(LayoutInterface::class); /** @var Grid $grid */ $grid = $layout->createBlock(Grid::class); @@ -42,7 +42,7 @@ public function testGridContent() */ public function testPageSizeIsSetToNullWhenExportCsvFile() { - /** @var \Magento\Framework\View\LayoutInterface $layout */ + /** @var LayoutInterface $layout */ $layout = Bootstrap::getObjectManager()->get(LayoutInterface::class); /** @var Grid $grid */ $grid = $layout->createBlock(Grid::class); From 8a0a4d55afa90828b4e6efe9969b3c27b3fecc4f Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Tue, 24 Jul 2018 12:44:56 -0300 Subject: [PATCH 105/211] Fixing the size of columns that exceeds 120 characters. --- .../Controller/Adminhtml/Notification/MassRemove.php | 3 ++- .../Controller/Adminhtml/Notification/Remove.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassRemove.php b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassRemove.php index 89a9e4608585f..f4cafa09c7e45 100644 --- a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassRemove.php +++ b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/MassRemove.php @@ -36,7 +36,8 @@ public function execute() } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addExceptionMessage($e, __("We couldn't remove the messages because of an error.")); + $this->messageManager + ->addExceptionMessage($e, __("We couldn't remove the messages because of an error.")); } } $this->_redirect('adminhtml/*/'); diff --git a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/Remove.php b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/Remove.php index 5459ac82b55e1..bec101fc27d48 100644 --- a/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/Remove.php +++ b/app/code/Magento/AdminNotification/Controller/Adminhtml/Notification/Remove.php @@ -35,7 +35,8 @@ public function execute() } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addExceptionMessage($e, __("We couldn't remove the messages because of an error.")); + $this->messageManager + ->addExceptionMessage($e, __("We couldn't remove the messages because of an error.")); } $this->_redirect('adminhtml/*/'); From 1ac94a2159c0f1bdb2cb9391c911a3d2c448a929 Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Tue, 24 Jul 2018 12:49:05 -0300 Subject: [PATCH 106/211] Fixing the size of columns that exceeds 120 characters. --- .../Magento/Backend/Controller/Adminhtml/Cache/CleanMedia.php | 3 ++- .../Controller/Adminhtml/System/Store/DeleteStorePost.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanMedia.php b/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanMedia.php index 30aeb038ff9c2..5df0a7779c4c1 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanMedia.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Cache/CleanMedia.php @@ -32,7 +32,8 @@ public function execute() } catch (LocalizedException $e) { $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addExceptionMessage($e, __('An error occurred while clearing the JavaScript/CSS cache.')); + $this->messageManager + ->addExceptionMessage($e, __('An error occurred while clearing the JavaScript/CSS cache.')); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ diff --git a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteStorePost.php b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteStorePost.php index 518e5e1d549a3..8146bfdd41e40 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteStorePost.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Store/DeleteStorePost.php @@ -42,7 +42,8 @@ public function execute() } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addExceptionMessage($e, __('Unable to delete the store view. Please try again later.')); + $this->messageManager + ->addExceptionMessage($e, __('Unable to delete the store view. Please try again later.')); } return $redirectResult->setPath('adminhtml/*/editStore', ['store_id' => $itemId]); } From 36925be30a0c31cd103641aeb28b454cc8277f4b Mon Sep 17 00:00:00 2001 From: mage2pratik <magepratik@gmail.com> Date: Wed, 25 Jul 2018 00:37:24 +0530 Subject: [PATCH 107/211] Code cleanup of lib files --- .../Framework/Api/AbstractExtensibleObject.php | 4 ++-- .../Magento/Framework/Api/AbstractSimpleObject.php | 2 +- .../Magento/Framework/Api/ImageProcessor.php | 2 +- .../Magento/Framework/Api/Search/Document.php | 4 +--- .../CollectionProcessor/FilterProcessor.php | 2 +- .../CollectionProcessor/JoinProcessor.php | 2 +- .../CollectionProcessor/SortingProcessor.php | 2 +- lib/internal/Magento/Framework/App/ActionFlag.php | 4 +--- lib/internal/Magento/Framework/App/AreaList.php | 4 ++-- .../Magento/Framework/App/Config/Initial.php | 4 ++-- .../Framework/App/DefaultPath/DefaultPath.php | 2 +- .../Magento/Framework/App/DeploymentConfig.php | 2 +- .../Magento/Framework/App/Http/Context.php | 4 +--- .../Framework/Component/ComponentRegistrar.php | 2 +- lib/internal/Magento/Framework/Config/View.php | 14 ++++++-------- .../Magento/Framework/DB/Adapter/Pdo/Mysql.php | 2 +- .../Magento/Framework/Data/AbstractCriteria.php | 2 +- .../Magento/Framework/Data/AbstractDataObject.php | 2 +- lib/internal/Magento/Framework/Data/Collection.php | 2 +- lib/internal/Magento/Framework/Data/Structure.php | 8 +++----- lib/internal/Magento/Framework/Event.php | 2 +- .../Magento/Framework/Filesystem/Io/File.php | 2 +- .../Magento/Framework/Filter/AbstractFactory.php | 2 +- lib/internal/Magento/Framework/Filter/Input.php | 2 +- .../Interception/PluginList/PluginList.php | 2 +- .../Magento/Framework/Message/Collection.php | 2 +- .../Framework/Model/AbstractExtensibleModel.php | 4 +--- .../Magento/Framework/Module/FullModuleList.php | 2 +- .../Magento/Framework/Module/ModuleList.php | 2 +- .../Magento/Framework/Module/ModuleResource.php | 4 ++-- .../Magento/Framework/Module/PackageInfo.php | 2 +- .../Magento/Framework/Notification/MessageList.php | 2 +- .../Test/Unit/Factory/Fixture/Polymorphous.php | 2 +- .../Magento/Framework/Pricing/Amount/Base.php | 4 +--- .../Magento/Framework/Pricing/Price/Pool.php | 2 +- .../Framework/Search/Response/Aggregation.php | 2 +- .../Magento/Framework/Session/SessionManager.php | 2 +- lib/internal/Magento/Framework/Url.php | 2 +- .../Magento/Framework/Validator/Exception.php | 2 +- .../Magento/Framework/View/Asset/PropertyGroup.php | 2 +- lib/internal/Magento/Framework/View/BlockPool.php | 2 +- .../Magento/Framework/View/DataSourcePool.php | 4 ++-- .../Framework/View/Design/Theme/Validator.php | 2 +- .../Framework/View/Element/UiComponent/Context.php | 2 +- .../UiComponent/DataProvider/DataProvider.php | 10 ++++------ .../Magento/Framework/View/Layout/Generic.php | 2 +- .../Framework/View/Layout/ScheduledStructure.php | 8 ++++---- .../Magento/Framework/View/Page/Config.php | 4 ++-- lib/internal/Magento/Framework/Xml/Generator.php | 4 +--- 49 files changed, 68 insertions(+), 86 deletions(-) diff --git a/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php b/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php index 3ea43cbccd24f..99dd972dc26ea 100644 --- a/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php +++ b/lib/internal/Magento/Framework/Api/AbstractExtensibleObject.php @@ -74,7 +74,7 @@ public function getCustomAttribute($attributeCode) */ public function getCustomAttributes() { - return isset($this->_data[self::CUSTOM_ATTRIBUTES]) ? $this->_data[self::CUSTOM_ATTRIBUTES] : []; + return $this->_data[self::CUSTOM_ATTRIBUTES] ?? []; } /** @@ -129,7 +129,7 @@ public function setCustomAttribute($attributeCode, $attributeValue) */ protected function getCustomAttributesCodes() { - return isset($this->customAttributesCodes) ? $this->customAttributesCodes : []; + return $this->customAttributesCodes ?? []; } /** diff --git a/lib/internal/Magento/Framework/Api/AbstractSimpleObject.php b/lib/internal/Magento/Framework/Api/AbstractSimpleObject.php index b907246139d42..9cc0ddab3bfcb 100644 --- a/lib/internal/Magento/Framework/Api/AbstractSimpleObject.php +++ b/lib/internal/Magento/Framework/Api/AbstractSimpleObject.php @@ -34,7 +34,7 @@ public function __construct(array $data = []) */ protected function _get($key) { - return isset($this->_data[$key]) ? $this->_data[$key] : null; + return $this->_data[$key] ?? null; } /** diff --git a/lib/internal/Magento/Framework/Api/ImageProcessor.php b/lib/internal/Magento/Framework/Api/ImageProcessor.php index e2c5f8c4083b9..4642a742576df 100644 --- a/lib/internal/Magento/Framework/Api/ImageProcessor.php +++ b/lib/internal/Magento/Framework/Api/ImageProcessor.php @@ -172,7 +172,7 @@ public function processImageContent($entityType, $imageContent) */ protected function getMimeTypeExtension($mimeType) { - return isset($this->mimeTypeExtensionMap[$mimeType]) ? $this->mimeTypeExtensionMap[$mimeType] : ''; + return $this->mimeTypeExtensionMap[$mimeType] ?? ''; } /** diff --git a/lib/internal/Magento/Framework/Api/Search/Document.php b/lib/internal/Magento/Framework/Api/Search/Document.php index d60458a6e5585..7454fa7974ece 100644 --- a/lib/internal/Magento/Framework/Api/Search/Document.php +++ b/lib/internal/Magento/Framework/Api/Search/Document.php @@ -33,9 +33,7 @@ public function setId($id) */ public function getCustomAttribute($attributeCode) { - return isset($this->_data[self::CUSTOM_ATTRIBUTES][$attributeCode]) - ? $this->_data[self::CUSTOM_ATTRIBUTES][$attributeCode] - : null; + return $this->_data[self::CUSTOM_ATTRIBUTES][$attributeCode] ?? null; } /** diff --git a/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php b/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php index b663a3a2f733c..e127dfd4b1624 100644 --- a/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php +++ b/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php @@ -114,6 +114,6 @@ private function getCustomFilterForField($field) */ private function getFieldMapping($field) { - return isset($this->fieldMapping[$field]) ? $this->fieldMapping[$field] : $field; + return $this->fieldMapping[$field] ?? $field; } } diff --git a/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/JoinProcessor.php b/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/JoinProcessor.php index b8e52334bee1f..207325042c737 100644 --- a/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/JoinProcessor.php +++ b/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/JoinProcessor.php @@ -121,6 +121,6 @@ private function getCustomJoin($field) */ private function getFieldMapping($field) { - return isset($this->fieldMapping[$field]) ? $this->fieldMapping[$field] : $field; + return $this->fieldMapping[$field] ?? $field; } } diff --git a/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/SortingProcessor.php b/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/SortingProcessor.php index 7598c4eb4abdc..9c18b8c1a9ae5 100644 --- a/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/SortingProcessor.php +++ b/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/SortingProcessor.php @@ -59,7 +59,7 @@ public function process(SearchCriteriaInterface $searchCriteria, AbstractDb $col */ private function getFieldMapping($field) { - return isset($this->fieldMapping[$field]) ? $this->fieldMapping[$field] : $field; + return $this->fieldMapping[$field] ?? $field; } /** diff --git a/lib/internal/Magento/Framework/App/ActionFlag.php b/lib/internal/Magento/Framework/App/ActionFlag.php index 657c2ede9262d..55201504c968f 100644 --- a/lib/internal/Magento/Framework/App/ActionFlag.php +++ b/lib/internal/Magento/Framework/App/ActionFlag.php @@ -65,9 +65,7 @@ public function get($action, $flag = '') $action = $this->_request->getActionName(); } if ('' === $flag) { - return isset( - $this->_flags[$this->_getControllerKey()] - ) ? $this->_flags[$this->_getControllerKey()] : []; + return $this->_flags[$this->_getControllerKey()] ?? []; } elseif (isset($this->_flags[$this->_getControllerKey()][$action][$flag])) { return $this->_flags[$this->_getControllerKey()][$action][$flag]; } else { diff --git a/lib/internal/Magento/Framework/App/AreaList.php b/lib/internal/Magento/Framework/App/AreaList.php index 7c123f7ff9d60..fb28d09d5fe09 100644 --- a/lib/internal/Magento/Framework/App/AreaList.php +++ b/lib/internal/Magento/Framework/App/AreaList.php @@ -88,7 +88,7 @@ public function getCodeByFrontName($frontName) */ public function getFrontName($areaCode) { - return isset($this->_areas[$areaCode]['frontName']) ? $this->_areas[$areaCode]['frontName'] : null; + return $this->_areas[$areaCode]['frontName'] ?? null; } /** @@ -111,7 +111,7 @@ public function getCodes() */ public function getDefaultRouter($areaCode) { - return isset($this->_areas[$areaCode]['router']) ? $this->_areas[$areaCode]['router'] : null; + return $this->_areas[$areaCode]['router'] ?? null; } /** diff --git a/lib/internal/Magento/Framework/App/Config/Initial.php b/lib/internal/Magento/Framework/App/Config/Initial.php index 1933682346ad3..b65c9a2f53489 100644 --- a/lib/internal/Magento/Framework/App/Config/Initial.php +++ b/lib/internal/Magento/Framework/App/Config/Initial.php @@ -72,9 +72,9 @@ public function getData($scope) list($scopeType, $scopeCode) = array_pad(explode('|', $scope), 2, null); if (ScopeConfigInterface::SCOPE_TYPE_DEFAULT == $scopeType) { - return isset($this->_data[$scopeType]) ? $this->_data[$scopeType] : []; + return $this->_data[$scopeType] ?? []; } elseif ($scopeCode) { - return isset($this->_data[$scopeType][$scopeCode]) ? $this->_data[$scopeType][$scopeCode] : []; + return $this->_data[$scopeType][$scopeCode] ?? []; } return []; } diff --git a/lib/internal/Magento/Framework/App/DefaultPath/DefaultPath.php b/lib/internal/Magento/Framework/App/DefaultPath/DefaultPath.php index 61d0aa138f9a4..8a4188aed9605 100644 --- a/lib/internal/Magento/Framework/App/DefaultPath/DefaultPath.php +++ b/lib/internal/Magento/Framework/App/DefaultPath/DefaultPath.php @@ -32,6 +32,6 @@ public function __construct(array $parts) */ public function getPart($code) { - return isset($this->_parts[$code]) ? $this->_parts[$code] : null; + return $this->_parts[$code] ?? null; } } diff --git a/lib/internal/Magento/Framework/App/DeploymentConfig.php b/lib/internal/Magento/Framework/App/DeploymentConfig.php index 92bb3fdbbc54f..615c295675adc 100644 --- a/lib/internal/Magento/Framework/App/DeploymentConfig.php +++ b/lib/internal/Magento/Framework/App/DeploymentConfig.php @@ -70,7 +70,7 @@ public function get($key = null, $defaultValue = null) if ($key === null) { return $this->flatData; } - return isset($this->flatData[$key]) ? $this->flatData[$key] : $defaultValue; + return $this->flatData[$key] ?? $defaultValue; } /** diff --git a/lib/internal/Magento/Framework/App/Http/Context.php b/lib/internal/Magento/Framework/App/Http/Context.php index a5eba2753b510..79a15110234cd 100644 --- a/lib/internal/Magento/Framework/App/Http/Context.php +++ b/lib/internal/Magento/Framework/App/Http/Context.php @@ -84,9 +84,7 @@ public function unsValue($name) */ public function getValue($name) { - return isset($this->data[$name]) - ? $this->data[$name] - : (isset($this->default[$name]) ? $this->default[$name] : null); + return $this->data[$name] ?? ($this->default[$name] ?? null); } /** diff --git a/lib/internal/Magento/Framework/Component/ComponentRegistrar.php b/lib/internal/Magento/Framework/Component/ComponentRegistrar.php index 9c746177d6f7d..88b2995179d4d 100644 --- a/lib/internal/Magento/Framework/Component/ComponentRegistrar.php +++ b/lib/internal/Magento/Framework/Component/ComponentRegistrar.php @@ -68,7 +68,7 @@ public function getPaths($type) public function getPath($type, $componentName) { self::validateType($type); - return isset(self::$paths[$type][$componentName]) ? self::$paths[$type][$componentName] : null; + return self::$paths[$type][$componentName] ?? null; } /** diff --git a/lib/internal/Magento/Framework/Config/View.php b/lib/internal/Magento/Framework/Config/View.php index ef9c39e221e86..05863caeec2b6 100644 --- a/lib/internal/Magento/Framework/Config/View.php +++ b/lib/internal/Magento/Framework/Config/View.php @@ -71,7 +71,7 @@ public function __construct( public function getVars($module) { $this->initData(); - return isset($this->data['vars'][$module]) ? $this->data['vars'][$module] : []; + return $this->data['vars'][$module] ?? []; } /** @@ -110,7 +110,7 @@ public function getVarValue($module, $var) public function getMediaEntities($module, $mediaType) { $this->initData(); - return isset($this->data['media'][$module][$mediaType]) ? $this->data['media'][$module][$mediaType] : []; + return $this->data['media'][$module][$mediaType] ?? []; } /** @@ -124,9 +124,7 @@ public function getMediaEntities($module, $mediaType) public function getMediaAttributes($module, $mediaType, $mediaId) { $this->initData(); - return isset($this->data['media'][$module][$mediaType][$mediaId]) - ? $this->data['media'][$module][$mediaType][$mediaId] - : []; + return $this->data['media'][$module][$mediaType][$mediaId] ?? []; } /** @@ -163,7 +161,7 @@ protected function getIdAttributes() public function getExcludedFiles() { $items = $this->getItems(); - return isset($items['file']) ? $items['file'] : []; + return $items['file'] ?? []; } /** @@ -174,7 +172,7 @@ public function getExcludedFiles() public function getExcludedDir() { $items = $this->getItems(); - return isset($items['directory']) ? $items['directory'] : []; + return $items['directory'] ?? []; } /** @@ -185,7 +183,7 @@ public function getExcludedDir() protected function getItems() { $this->initData(); - return isset($this->data['exclude']) ? $this->data['exclude'] : []; + return $this->data['exclude'] ?? []; } /** diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php index 2274fde82b818..1449d6d2a6456 100644 --- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php +++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php @@ -483,7 +483,7 @@ public function rawFetchRow($sql, $field = null) if (empty($field)) { return $row; } else { - return isset($row[$field]) ? $row[$field] : false; + return $row[$field] ?? false; } } diff --git a/lib/internal/Magento/Framework/Data/AbstractCriteria.php b/lib/internal/Magento/Framework/Data/AbstractCriteria.php index c90ed2b03bf3d..d4811b79980a9 100644 --- a/lib/internal/Magento/Framework/Data/AbstractCriteria.php +++ b/lib/internal/Magento/Framework/Data/AbstractCriteria.php @@ -274,7 +274,7 @@ public function getLimit() */ public function getPart($name, $default = null) { - return isset($this->data[$name]) ? $this->data[$name] : $default; + return $this->data[$name] ?? $default; } /** diff --git a/lib/internal/Magento/Framework/Data/AbstractDataObject.php b/lib/internal/Magento/Framework/Data/AbstractDataObject.php index 5916100ffbbfa..da04fecc447cc 100644 --- a/lib/internal/Magento/Framework/Data/AbstractDataObject.php +++ b/lib/internal/Magento/Framework/Data/AbstractDataObject.php @@ -50,6 +50,6 @@ public function toArray() */ protected function get($key) { - return isset($this->data[$key]) ? $this->data[$key] : null; + return $this->data[$key] ?? null; } } diff --git a/lib/internal/Magento/Framework/Data/Collection.php b/lib/internal/Magento/Framework/Data/Collection.php index f8b82d3122234..71ec8c1aa8379 100644 --- a/lib/internal/Magento/Framework/Data/Collection.php +++ b/lib/internal/Magento/Framework/Data/Collection.php @@ -851,7 +851,7 @@ public function count() */ public function getFlag($flag) { - return isset($this->_flags[$flag]) ? $this->_flags[$flag] : null; + return $this->_flags[$flag] ?? null; } /** diff --git a/lib/internal/Magento/Framework/Data/Structure.php b/lib/internal/Magento/Framework/Data/Structure.php index 9d540e8a49f08..a977d3448ec26 100644 --- a/lib/internal/Magento/Framework/Data/Structure.php +++ b/lib/internal/Magento/Framework/Data/Structure.php @@ -166,7 +166,7 @@ public function createElement($elementId, array $data) */ public function getElement($elementId) { - return isset($this->_elements[$elementId]) ? $this->_elements[$elementId] : false; + return $this->_elements[$elementId] ?? false; } /** @@ -456,9 +456,7 @@ public function getChildId($parentId, $alias) */ public function getChildren($parentId) { - return isset( - $this->_elements[$parentId][self::CHILDREN] - ) ? $this->_elements[$parentId][self::CHILDREN] : []; + return $this->_elements[$parentId][self::CHILDREN] ?? []; } /** @@ -469,7 +467,7 @@ public function getChildren($parentId) */ public function getParentId($childId) { - return isset($this->_elements[$childId][self::PARENT]) ? $this->_elements[$childId][self::PARENT] : false; + return $this->_elements[$childId][self::PARENT] ?? false; } /** diff --git a/lib/internal/Magento/Framework/Event.php b/lib/internal/Magento/Framework/Event.php index 4c116d0a33629..c7b15a8eb0722 100644 --- a/lib/internal/Magento/Framework/Event.php +++ b/lib/internal/Magento/Framework/Event.php @@ -88,7 +88,7 @@ public function dispatch() */ public function getName() { - return isset($this->_data['name']) ? $this->_data['name'] : null; + return $this->_data['name'] ?? null; } /** diff --git a/lib/internal/Magento/Framework/Filesystem/Io/File.php b/lib/internal/Magento/Framework/Filesystem/Io/File.php index 60a94d5b430e9..2e724260ff543 100644 --- a/lib/internal/Magento/Framework/Filesystem/Io/File.php +++ b/lib/internal/Magento/Framework/Filesystem/Io/File.php @@ -228,7 +228,7 @@ public function streamStat($part = null, $default = null) } $stat = @fstat($this->_streamHandler); if ($part !== null) { - return isset($stat[$part]) ? $stat[$part] : $default; + return $stat[$part] ?? $default; } return $stat; } diff --git a/lib/internal/Magento/Framework/Filter/AbstractFactory.php b/lib/internal/Magento/Framework/Filter/AbstractFactory.php index 2a0ae0ba15e42..c30b07aa09061 100644 --- a/lib/internal/Magento/Framework/Filter/AbstractFactory.php +++ b/lib/internal/Magento/Framework/Filter/AbstractFactory.php @@ -68,7 +68,7 @@ public function canCreateFilter($alias) */ public function isShared($class) { - return isset($this->shared[$class]) ? $this->shared[$class] : $this->shareByDefault; + return $this->shared[$class] ?? $this->shareByDefault; } /** diff --git a/lib/internal/Magento/Framework/Filter/Input.php b/lib/internal/Magento/Framework/Filter/Input.php index 39c7a54786edb..6da748fcbeb9f 100644 --- a/lib/internal/Magento/Framework/Filter/Input.php +++ b/lib/internal/Magento/Framework/Filter/Input.php @@ -183,7 +183,7 @@ public function getFilters($name = null) if (null === $name) { return $this->_filters; } else { - return isset($this->_filters[$name]) ? $this->_filters[$name] : null; + return $this->_filters[$name] ?? null; } } diff --git a/lib/internal/Magento/Framework/Interception/PluginList/PluginList.php b/lib/internal/Magento/Framework/Interception/PluginList/PluginList.php index 489755a2846fc..718b08190d8be 100644 --- a/lib/internal/Magento/Framework/Interception/PluginList/PluginList.php +++ b/lib/internal/Magento/Framework/Interception/PluginList/PluginList.php @@ -268,7 +268,7 @@ public function getNext($type, $method, $code = '__self') $this->_inheritPlugins($type); } $key = $type . '_' . lcfirst($method) . '_' . $code; - return isset($this->_processed[$key]) ? $this->_processed[$key] : null; + return $this->_processed[$key] ?? null; } /** diff --git a/lib/internal/Magento/Framework/Message/Collection.php b/lib/internal/Magento/Framework/Message/Collection.php index fb8351c5a04ba..b1e3088a703f7 100644 --- a/lib/internal/Magento/Framework/Message/Collection.php +++ b/lib/internal/Magento/Framework/Message/Collection.php @@ -142,7 +142,7 @@ public function getItems() */ public function getItemsByType($type) { - return isset($this->messages[$type]) ? $this->messages[$type] : []; + return $this->messages[$type] ?? []; } /** diff --git a/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php b/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php index db919c80e251c..5963f5f942374 100644 --- a/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php +++ b/lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php @@ -151,9 +151,7 @@ public function getCustomAttributes() public function getCustomAttribute($attributeCode) { $this->initializeCustomAttributes(); - return isset($this->_data[self::CUSTOM_ATTRIBUTES][$attributeCode]) - ? $this->_data[self::CUSTOM_ATTRIBUTES][$attributeCode] - : null; + return $this->_data[self::CUSTOM_ATTRIBUTES][$attributeCode] ?? null; } /** diff --git a/lib/internal/Magento/Framework/Module/FullModuleList.php b/lib/internal/Magento/Framework/Module/FullModuleList.php index 5ad5b05a413ef..c6e403dee0898 100644 --- a/lib/internal/Magento/Framework/Module/FullModuleList.php +++ b/lib/internal/Magento/Framework/Module/FullModuleList.php @@ -55,7 +55,7 @@ public function getAll() public function getOne($name) { $data = $this->getAll(); - return isset($data[$name]) ? $data[$name] : null; + return $data[$name] ?? null; } /** diff --git a/lib/internal/Magento/Framework/Module/ModuleList.php b/lib/internal/Magento/Framework/Module/ModuleList.php index 406aa9efd31a0..6ee061cffb3d0 100644 --- a/lib/internal/Magento/Framework/Module/ModuleList.php +++ b/lib/internal/Magento/Framework/Module/ModuleList.php @@ -90,7 +90,7 @@ public function getAll() public function getOne($name) { $enabled = $this->getAll(); - return isset($enabled[$name]) ? $enabled[$name] : null; + return $enabled[$name] ?? null; } /** diff --git a/lib/internal/Magento/Framework/Module/ModuleResource.php b/lib/internal/Magento/Framework/Module/ModuleResource.php index ed740d459060e..9f7ac2a1b393c 100644 --- a/lib/internal/Magento/Framework/Module/ModuleResource.php +++ b/lib/internal/Magento/Framework/Module/ModuleResource.php @@ -84,7 +84,7 @@ public function getDbVersion($moduleName) return false; } $this->_loadVersion('db'); - return isset(self::$schemaVersions[$moduleName]) ? self::$schemaVersions[$moduleName] : false; + return self::$schemaVersions[$moduleName] ?? false; } /** @@ -116,7 +116,7 @@ public function getDataVersion($moduleName) return false; } $this->_loadVersion('data'); - return isset(self::$dataVersions[$moduleName]) ? self::$dataVersions[$moduleName] : false; + return self::$dataVersions[$moduleName] ?? false; } /** diff --git a/lib/internal/Magento/Framework/Module/PackageInfo.php b/lib/internal/Magento/Framework/Module/PackageInfo.php index 7edb0c04ebf98..0dce507ba26f4 100644 --- a/lib/internal/Magento/Framework/Module/PackageInfo.php +++ b/lib/internal/Magento/Framework/Module/PackageInfo.php @@ -283,6 +283,6 @@ public function getConflict($moduleName) public function getVersion($moduleName) { $this->load(); - return isset($this->modulePackageVersionMap[$moduleName]) ? $this->modulePackageVersionMap[$moduleName] : ''; + return $this->modulePackageVersionMap[$moduleName] ?? ''; } } diff --git a/lib/internal/Magento/Framework/Notification/MessageList.php b/lib/internal/Magento/Framework/Notification/MessageList.php index 8fb91890b2ff0..ac753b48c8944 100644 --- a/lib/internal/Magento/Framework/Notification/MessageList.php +++ b/lib/internal/Magento/Framework/Notification/MessageList.php @@ -72,7 +72,7 @@ protected function _loadMessages() public function getMessageByIdentity($identity) { $this->_loadMessages(); - return isset($this->_messages[$identity]) ? $this->_messages[$identity] : null; + return $this->_messages[$identity] ?? null; } /** diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/Fixture/Polymorphous.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/Fixture/Polymorphous.php index ebb7d76dcb63c..0c1a2128560f8 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/Fixture/Polymorphous.php +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/Fixture/Polymorphous.php @@ -26,6 +26,6 @@ public function __construct() */ public function getArg($key) { - return isset($this->args[$key]) ? $this->args[$key] : null; + return $this->args[$key] ?? null; } } diff --git a/lib/internal/Magento/Framework/Pricing/Amount/Base.php b/lib/internal/Magento/Framework/Pricing/Amount/Base.php index 2664ccc54944c..d055819a4a126 100644 --- a/lib/internal/Magento/Framework/Pricing/Amount/Base.php +++ b/lib/internal/Magento/Framework/Pricing/Amount/Base.php @@ -105,9 +105,7 @@ public function getBaseAmount() */ public function getAdjustmentAmount($adjustmentCode) { - return isset($this->adjustmentAmounts[$adjustmentCode]) - ? $this->adjustmentAmounts[$adjustmentCode] - : false; + return $this->adjustmentAmounts[$adjustmentCode] ?? false; } /** diff --git a/lib/internal/Magento/Framework/Pricing/Price/Pool.php b/lib/internal/Magento/Framework/Pricing/Price/Pool.php index b460113fc32c8..dfdd0c52681e1 100644 --- a/lib/internal/Magento/Framework/Pricing/Price/Pool.php +++ b/lib/internal/Magento/Framework/Pricing/Price/Pool.php @@ -141,6 +141,6 @@ public function offsetUnset($offset) */ public function offsetGet($offset) { - return isset($this->prices[$offset]) ? $this->prices[$offset] : null; + return $this->prices[$offset] ?? null; } } diff --git a/lib/internal/Magento/Framework/Search/Response/Aggregation.php b/lib/internal/Magento/Framework/Search/Response/Aggregation.php index 9cb7a364ff21c..ea72597c53034 100644 --- a/lib/internal/Magento/Framework/Search/Response/Aggregation.php +++ b/lib/internal/Magento/Framework/Search/Response/Aggregation.php @@ -47,7 +47,7 @@ public function getIterator() */ public function getBucket($bucketName) { - return isset($this->buckets[$bucketName]) ? $this->buckets[$bucketName] : null; + return $this->buckets[$bucketName] ?? null; } /** diff --git a/lib/internal/Magento/Framework/Session/SessionManager.php b/lib/internal/Magento/Framework/Session/SessionManager.php index ecf169cb0bc88..b855b05a38354 100644 --- a/lib/internal/Magento/Framework/Session/SessionManager.php +++ b/lib/internal/Magento/Framework/Session/SessionManager.php @@ -476,7 +476,7 @@ protected function _addHost() */ protected function _getHosts() { - return isset($_SESSION[self::HOST_KEY]) ? $_SESSION[self::HOST_KEY] : []; + return $_SESSION[self::HOST_KEY] ?? []; } /** diff --git a/lib/internal/Magento/Framework/Url.php b/lib/internal/Magento/Framework/Url.php index 2982edaa4766f..5a02beb798ed0 100644 --- a/lib/internal/Magento/Framework/Url.php +++ b/lib/internal/Magento/Framework/Url.php @@ -1074,7 +1074,7 @@ function ($match) { if ($match[1] == '?') { return isset($match[3]) ? '?' : ''; } elseif ($match[1] == '&' || $match[1] == '&') { - return isset($match[3]) ? $match[3] : ''; + return $match[3] ?? ''; } } }, diff --git a/lib/internal/Magento/Framework/Validator/Exception.php b/lib/internal/Magento/Framework/Validator/Exception.php index c70ecfabb52af..370f66c424b01 100644 --- a/lib/internal/Magento/Framework/Validator/Exception.php +++ b/lib/internal/Magento/Framework/Validator/Exception.php @@ -84,6 +84,6 @@ public function getMessages($type = '') } return $allMessages; } - return isset($this->messages[$type]) ? $this->messages[$type] : []; + return $this->messages[$type] ?? []; } } diff --git a/lib/internal/Magento/Framework/View/Asset/PropertyGroup.php b/lib/internal/Magento/Framework/View/Asset/PropertyGroup.php index ad86dfca47a25..4fe8f48d6b723 100644 --- a/lib/internal/Magento/Framework/View/Asset/PropertyGroup.php +++ b/lib/internal/Magento/Framework/View/Asset/PropertyGroup.php @@ -45,6 +45,6 @@ public function getProperties() */ public function getProperty($name) { - return isset($this->properties[$name]) ? $this->properties[$name] : null; + return $this->properties[$name] ?? null; } } diff --git a/lib/internal/Magento/Framework/View/BlockPool.php b/lib/internal/Magento/Framework/View/BlockPool.php index c50af9d5bbc5f..dff280057fdaf 100644 --- a/lib/internal/Magento/Framework/View/BlockPool.php +++ b/lib/internal/Magento/Framework/View/BlockPool.php @@ -72,6 +72,6 @@ public function get($name = null) return $this->blocks; } - return isset($this->blocks[$name]) ? $this->blocks[$name] : null; + return $this->blocks[$name] ?? null; } } diff --git a/lib/internal/Magento/Framework/View/DataSourcePool.php b/lib/internal/Magento/Framework/View/DataSourcePool.php index 24bdb6639981b..94f4f1dceae97 100644 --- a/lib/internal/Magento/Framework/View/DataSourcePool.php +++ b/lib/internal/Magento/Framework/View/DataSourcePool.php @@ -80,7 +80,7 @@ public function get($name = null) return $this->dataSources; } - return isset($this->dataSources[$name]) ? $this->dataSources[$name] : null; + return $this->dataSources[$name] ?? null; } /** @@ -107,6 +107,6 @@ public function assign($dataName, $namespace, $alias) */ public function getNamespaceData($namespace) { - return isset($this->assignments[$namespace]) ? $this->assignments[$namespace] : []; + return $this->assignments[$namespace] ?? []; } } diff --git a/lib/internal/Magento/Framework/View/Design/Theme/Validator.php b/lib/internal/Magento/Framework/View/Design/Theme/Validator.php index 06c78e7125040..04e775d730b56 100644 --- a/lib/internal/Magento/Framework/View/Design/Theme/Validator.php +++ b/lib/internal/Magento/Framework/View/Design/Theme/Validator.php @@ -118,7 +118,7 @@ public function addDataValidators($dataKey, $validators) public function getErrorMessages($dataKey = null) { if ($dataKey) { - return isset($this->_errorMessages[$dataKey]) ? $this->_errorMessages[$dataKey] : []; + return $this->_errorMessages[$dataKey] ?? []; } return $this->_errorMessages; } diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php b/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php index 4430d18db1e70..7312f7546df9b 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php @@ -208,7 +208,7 @@ public function getFiltersParams() public function getFilterParam($key, $defaultValue = null) { $filter = $this->getFiltersParams(); - return isset($filter[$key]) ? $filter[$key] : $defaultValue; + return $filter[$key] ?? $defaultValue; } /** diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/DataProvider/DataProvider.php b/lib/internal/Magento/Framework/View/Element/UiComponent/DataProvider/DataProvider.php index b2288a47f8f83..baa4e94eed978 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/DataProvider/DataProvider.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/DataProvider/DataProvider.php @@ -181,7 +181,7 @@ public function getMeta() */ public function getFieldSetMetaInfo($fieldSetName) { - return isset($this->meta[$fieldSetName]) ? $this->meta[$fieldSetName] : []; + return $this->meta[$fieldSetName] ?? []; } /** @@ -190,7 +190,7 @@ public function getFieldSetMetaInfo($fieldSetName) */ public function getFieldsMetaInfo($fieldSetName) { - return isset($this->meta[$fieldSetName]['children']) ? $this->meta[$fieldSetName]['children'] : []; + return $this->meta[$fieldSetName]['children'] ?? []; } /** @@ -200,9 +200,7 @@ public function getFieldsMetaInfo($fieldSetName) */ public function getFieldMetaInfo($fieldSetName, $fieldName) { - return isset($this->meta[$fieldSetName]['children'][$fieldName]) - ? $this->meta[$fieldSetName]['children'][$fieldName] - : []; + return $this->meta[$fieldSetName]['children'][$fieldName] ?? []; } /** @@ -291,7 +289,7 @@ public function getData() */ public function getConfigData() { - return isset($this->data['config']) ? $this->data['config'] : []; + return $this->data['config'] ?? []; } /** diff --git a/lib/internal/Magento/Framework/View/Layout/Generic.php b/lib/internal/Magento/Framework/View/Layout/Generic.php index b83545ff3c439..b527d1a817a96 100644 --- a/lib/internal/Magento/Framework/View/Layout/Generic.php +++ b/lib/internal/Magento/Framework/View/Layout/Generic.php @@ -200,6 +200,6 @@ protected function createChildFormComponent(UiComponentInterface $childComponent */ protected function getConfig($name) { - return isset($this->data['config'][$name]) ? $this->data['config'][$name] : null; + return $this->data['config'][$name] ?? null; } } diff --git a/lib/internal/Magento/Framework/View/Layout/ScheduledStructure.php b/lib/internal/Magento/Framework/View/Layout/ScheduledStructure.php index 25a04845a0728..3193e10282fd4 100644 --- a/lib/internal/Magento/Framework/View/Layout/ScheduledStructure.php +++ b/lib/internal/Magento/Framework/View/Layout/ScheduledStructure.php @@ -146,7 +146,7 @@ public function unsetElementToSort($elementName) */ public function getElementToSort($elementName, array $default = []) { - return isset($this->elementsToSort[$elementName]) ? $this->elementsToSort[$elementName] : $default; + return $this->elementsToSort[$elementName] ?? $default; } /** @@ -257,7 +257,7 @@ public function unsetElement($elementName) */ public function getElementToMove($elementName, $default = null) { - return isset($this->scheduledMoves[$elementName]) ? $this->scheduledMoves[$elementName] : $default; + return $this->scheduledMoves[$elementName] ?? $default; } /** @@ -370,7 +370,7 @@ public function unsetStructureElement($elementName) */ public function getStructureElementData($elementName, $default = null) { - return isset($this->scheduledData[$elementName]) ? $this->scheduledData[$elementName] : $default; + return $this->scheduledData[$elementName] ?? $default; } /** @@ -524,6 +524,6 @@ public function populateWithArray(array $data) */ private function getArrayValueByKey($key, array $array) { - return isset($array[$key]) ? $array[$key] : []; + return $array[$key] ?? []; } } diff --git a/lib/internal/Magento/Framework/View/Page/Config.php b/lib/internal/Magento/Framework/View/Page/Config.php index 058cad00320cd..226abc538112b 100644 --- a/lib/internal/Magento/Framework/View/Page/Config.php +++ b/lib/internal/Magento/Framework/View/Page/Config.php @@ -542,7 +542,7 @@ public function setElementAttribute($elementType, $attribute, $value) public function getElementAttribute($elementType, $attribute) { $this->build(); - return isset($this->elements[$elementType][$attribute]) ? $this->elements[$elementType][$attribute] : null; + return $this->elements[$elementType][$attribute] ?? null; } /** @@ -552,7 +552,7 @@ public function getElementAttribute($elementType, $attribute) public function getElementAttributes($elementType) { $this->build(); - return isset($this->elements[$elementType]) ? $this->elements[$elementType] : []; + return $this->elements[$elementType] ?? []; } /** diff --git a/lib/internal/Magento/Framework/Xml/Generator.php b/lib/internal/Magento/Framework/Xml/Generator.php index 975073e443d0e..f165793c2e2a4 100644 --- a/lib/internal/Magento/Framework/Xml/Generator.php +++ b/lib/internal/Magento/Framework/Xml/Generator.php @@ -146,8 +146,6 @@ public function setIndexedArrayItemName($name) */ protected function _getIndexedArrayItemName() { - return isset($this->_defaultIndexedArrayItemName) - ? $this->_defaultIndexedArrayItemName - : self::DEFAULT_ENTITY_ITEM_NAME; + return $this->_defaultIndexedArrayItemName ?? self::DEFAULT_ENTITY_ITEM_NAME; } } From eae30acf6ac15fbf4d843998662bce481caa1c82 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 25 Jul 2018 09:18:14 +0300 Subject: [PATCH 108/211] MAGETWO-73245: Add product to website will reset has_options and required_options --- .../Catalog/Controller/Adminhtml/Product/Save.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php index fcbb72a98df59..e7d566e7d0159 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php @@ -234,13 +234,13 @@ protected function copyToStores($data, $productId) $copyFrom = $store['copy_from']; $copyTo = (isset($store['copy_to'])) ? $store['copy_to'] : 0; if ($copyTo) { - $canSaveCustomOptions = (!empty($data['product']['affect_product_custom_options'])) - ? (bool)$data['product']['affect_product_custom_options'] - : false; - $this->_objectManager->create(\Magento\Catalog\Model\Product::class) + $canSaveCustomOptions = !empty($data['product']['affect_product_custom_options']); + + $product = $this->_objectManager->create(\Magento\Catalog\Model\Product::class) ->setStoreId($copyFrom) - ->load($productId) - ->setStoreId($copyTo) + ->load($productId); + + $product->setStoreId($copyTo) ->setCanSaveCustomOptions($canSaveCustomOptions) ->setCopyFromView(true) ->save(); From 1764ef17d4d706dad516f1557f0af05a2b26ee6e Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Wed, 25 Jul 2018 09:44:35 +0300 Subject: [PATCH 109/211] MAGETWO-77742: [2.2.x] - Error message when uploading unsupported file format --- .../Magento/Catalog/Test/Mftf/Data/ProductAttributeSetData.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeSetData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeSetData.xml index 68c0a54ff88fc..db8834f547457 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeSetData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeSetData.xml @@ -12,6 +12,6 @@ <var key="attributeCode" entityKey="attribute_code" entityType="ProductAttribute"/> <data key="attributeSetId">4</data> <data key="attributeGroupId">7</data> - <data key="sortOrder">0</data> + <data key="sortOrder">25</data> </entity> </entities> From b539fdbd2cf5d47a84fc4963fd3ea7bb0c00f5be Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Wed, 25 Jul 2018 09:51:20 +0300 Subject: [PATCH 110/211] MAGETWO-77742: [2.2.x] - Error message when uploading unsupported file format --- .../Magento/Catalog/Test/Mftf/Data/ProductAttributeSetData.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeSetData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeSetData.xml index db8834f547457..68c0a54ff88fc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeSetData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeSetData.xml @@ -12,6 +12,6 @@ <var key="attributeCode" entityKey="attribute_code" entityType="ProductAttribute"/> <data key="attributeSetId">4</data> <data key="attributeGroupId">7</data> - <data key="sortOrder">25</data> + <data key="sortOrder">0</data> </entity> </entities> From 31f36bcc5f3b993437d1c07b08abbd6a63b56162 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 25 Jul 2018 09:53:48 +0300 Subject: [PATCH 111/211] MAGETWO-73245: Add product to website will reset has_options and required_options --- .../Controller/Adminhtml/Product/Save.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php index e7d566e7d0159..9ea7031621c32 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php @@ -108,6 +108,8 @@ public function execute() } $originalSku = $product->getSku(); + $canSaveCustomOptions = $product->getCanSaveCustomOptions(); + $product->save(); $this->handleImageRemoveError($data, $product->getId()); $this->getCategoryLinkManagement()->assignProductToCategories( @@ -118,7 +120,9 @@ public function execute() $productAttributeSetId = $product->getAttributeSetId(); $productTypeId = $product->getTypeId(); - $this->copyToStores($data, $productId); + $extendedData = $data; + $extendedData['can_save_custom_options'] = $canSaveCustomOptions; + $this->copyToStores($extendedData, $productId); $this->messageManager->addSuccessMessage(__('You saved the product.')); $this->getDataPersistor()->clear('catalog_product'); @@ -234,14 +238,10 @@ protected function copyToStores($data, $productId) $copyFrom = $store['copy_from']; $copyTo = (isset($store['copy_to'])) ? $store['copy_to'] : 0; if ($copyTo) { - $canSaveCustomOptions = !empty($data['product']['affect_product_custom_options']); - - $product = $this->_objectManager->create(\Magento\Catalog\Model\Product::class) + $this->_objectManager->create(\Magento\Catalog\Model\Product::class) ->setStoreId($copyFrom) - ->load($productId); - - $product->setStoreId($copyTo) - ->setCanSaveCustomOptions($canSaveCustomOptions) + ->load($productId) + ->setCanSaveCustomOptions($data['can_save_custom_options']) ->setCopyFromView(true) ->save(); } From 4afd5882e9be7a2c088f9078df504a9ec92e7c3e Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 25 Jul 2018 10:20:19 +0300 Subject: [PATCH 112/211] MAGETWO-73245: Add product to website will reset has_options and required_options --- app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php index 9ea7031621c32..695038cbf6037 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php @@ -109,7 +109,6 @@ public function execute() $originalSku = $product->getSku(); $canSaveCustomOptions = $product->getCanSaveCustomOptions(); - $product->save(); $this->handleImageRemoveError($data, $product->getId()); $this->getCategoryLinkManagement()->assignProductToCategories( @@ -119,7 +118,6 @@ public function execute() $productId = $product->getEntityId(); $productAttributeSetId = $product->getAttributeSetId(); $productTypeId = $product->getTypeId(); - $extendedData = $data; $extendedData['can_save_custom_options'] = $canSaveCustomOptions; $this->copyToStores($extendedData, $productId); From 6bc40ea7d95190fce41fed893fc59c12a56eaced Mon Sep 17 00:00:00 2001 From: Guillaume GIORDANA <guillaume.giordana@gmail.com> Date: Tue, 24 Jul 2018 17:56:52 +0200 Subject: [PATCH 113/211] MAGETWO-84608: Cannot perform setup:install if Redis needs a password and Redis page cache is enabled in env.php --- .../Setup/Model/ConfigOptionsList/Cache.php | 19 ++++++++++++++++- .../Model/ConfigOptionsList/PageCache.php | 21 +++++++++++++++++-- .../Model/ConfigOptionsList/CacheTest.php | 10 +++++++-- .../Model/ConfigOptionsList/PageCacheTest.php | 10 +++++++-- 4 files changed, 53 insertions(+), 7 deletions(-) diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php index 1ec9d486a5a22..04ec83a3d0ca2 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php @@ -26,11 +26,13 @@ class Cache implements ConfigOptionsListInterface const INPUT_KEY_CACHE_BACKEND_REDIS_SERVER = 'cache-backend-redis-server'; const INPUT_KEY_CACHE_BACKEND_REDIS_DATABASE = 'cache-backend-redis-db'; const INPUT_KEY_CACHE_BACKEND_REDIS_PORT = 'cache-backend-redis-port'; + const INPUT_KEY_CACHE_BACKEND_REDIS_PASSWORD = 'cache-backend-redis-password'; const CONFIG_PATH_CACHE_BACKEND = 'cache/frontend/default/backend'; const CONFIG_PATH_CACHE_BACKEND_SERVER = 'cache/frontend/default/backend_options/server'; const CONFIG_PATH_CACHE_BACKEND_DATABASE = 'cache/frontend/default/backend_options/database'; const CONFIG_PATH_CACHE_BACKEND_PORT = 'cache/frontend/default/backend_options/port'; + const CONFIG_PATH_CACHE_BACKEND_PASSWORD = 'cache/frontend/default/backend_options/password'; /** * @var array @@ -38,7 +40,8 @@ class Cache implements ConfigOptionsListInterface private $defaultConfigValues = [ self::INPUT_KEY_CACHE_BACKEND_REDIS_SERVER => '127.0.0.1', self::INPUT_KEY_CACHE_BACKEND_REDIS_DATABASE => '0', - self::INPUT_KEY_CACHE_BACKEND_REDIS_PORT => '6379' + self::INPUT_KEY_CACHE_BACKEND_REDIS_PORT => '6379', + self::INPUT_KEY_CACHE_BACKEND_REDIS_PASSWORD => '' ]; /** @@ -55,6 +58,7 @@ class Cache implements ConfigOptionsListInterface self::INPUT_KEY_CACHE_BACKEND_REDIS_SERVER => self::CONFIG_PATH_CACHE_BACKEND_SERVER, self::INPUT_KEY_CACHE_BACKEND_REDIS_DATABASE => self::CONFIG_PATH_CACHE_BACKEND_DATABASE, self::INPUT_KEY_CACHE_BACKEND_REDIS_PORT => self::CONFIG_PATH_CACHE_BACKEND_PORT, + self::INPUT_KEY_CACHE_BACKEND_REDIS_PASSWORD => self::CONFIG_PATH_CACHE_BACKEND_PASSWORD ]; /** @@ -102,6 +106,12 @@ public function getOptions() TextConfigOption::FRONTEND_WIZARD_TEXT, self::CONFIG_PATH_CACHE_BACKEND_PORT, 'Redis server listen port' + ), + new TextConfigOption( + self::INPUT_KEY_CACHE_BACKEND_REDIS_PASSWORD, + TextConfigOption::FRONTEND_WIZARD_TEXT, + self::CONFIG_PATH_CACHE_BACKEND_PASSWORD, + 'Redis server password' ) ]; } @@ -190,6 +200,13 @@ private function validateRedisConfig(array $options, DeploymentConfig $deploymen self::CONFIG_PATH_CACHE_BACKEND_DATABASE, $this->getDefaultConfigValue(self::INPUT_KEY_CACHE_BACKEND_REDIS_DATABASE) ); + + $config['password'] = isset($options[self::INPUT_KEY_CACHE_BACKEND_REDIS_PASSWORD]) + ? $options[self::INPUT_KEY_CACHE_BACKEND_REDIS_PASSWORD] + : $deploymentConfig->get( + self::CONFIG_PATH_CACHE_BACKEND_PASSWORD, + $this->getDefaultConfigValue(self::INPUT_KEY_CACHE_BACKEND_REDIS_PASSWORD) + ); return $this->redisValidator->isValidConnection($config); } diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php index be1cc5b010185..944c543495751 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php @@ -27,12 +27,14 @@ class PageCache implements ConfigOptionsListInterface const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_DATABASE = 'page-cache-redis-db'; const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PORT = 'page-cache-redis-port'; const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_COMPRESS_DATA = 'page-cache-redis-compress-data'; + const INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PASSWORD = 'page-cache-redis-password'; const CONFIG_PATH_PAGE_CACHE_BACKEND = 'cache/frontend/page_cache/backend'; const CONFIG_PATH_PAGE_CACHE_BACKEND_SERVER = 'cache/frontend/page_cache/backend_options/server'; const CONFIG_PATH_PAGE_CACHE_BACKEND_DATABASE = 'cache/frontend/page_cache/backend_options/database'; const CONFIG_PATH_PAGE_CACHE_BACKEND_PORT = 'cache/frontend/page_cache/backend_options/port'; const CONFIG_PATH_PAGE_CACHE_BACKEND_COMPRESS_DATA = 'cache/frontend/page_cache/backend_options/compress_data'; + const CONFIG_PATH_PAGE_CACHE_BACKEND_PASSWORD = 'cache/frontend/page_cache/backend_options/password'; /** * @var array @@ -41,7 +43,8 @@ class PageCache implements ConfigOptionsListInterface self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_SERVER => '127.0.0.1', self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_DATABASE => '1', self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PORT => '6379', - self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_COMPRESS_DATA => '0' + self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_COMPRESS_DATA => '0', + self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PASSWORD => '' ]; /** @@ -58,7 +61,8 @@ class PageCache implements ConfigOptionsListInterface self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_SERVER => self::CONFIG_PATH_PAGE_CACHE_BACKEND_SERVER, self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_DATABASE => self::CONFIG_PATH_PAGE_CACHE_BACKEND_DATABASE, self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PORT => self::CONFIG_PATH_PAGE_CACHE_BACKEND_PORT, - self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_COMPRESS_DATA => self::CONFIG_PATH_PAGE_CACHE_BACKEND_COMPRESS_DATA + self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_COMPRESS_DATA => self::CONFIG_PATH_PAGE_CACHE_BACKEND_COMPRESS_DATA, + self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PASSWORD => self::CONFIG_PATH_PAGE_CACHE_BACKEND_PASSWORD ]; /** @@ -112,6 +116,12 @@ public function getOptions() TextConfigOption::FRONTEND_WIZARD_TEXT, self::CONFIG_PATH_PAGE_CACHE_BACKEND_COMPRESS_DATA, 'Set to 1 to compress the full page cache (use 0 to disable)' + ), + new TextConfigOption( + self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PASSWORD, + TextConfigOption::FRONTEND_WIZARD_TEXT, + self::CONFIG_PATH_PAGE_CACHE_BACKEND_PASSWORD, + 'Redis server password' ) ]; } @@ -201,6 +211,13 @@ private function validateRedisConfig(array $options, DeploymentConfig $deploymen self::CONFIG_PATH_PAGE_CACHE_BACKEND_DATABASE, $this->getDefaultConfigValue(self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_DATABASE) ); + + $config['password'] = isset($options[self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PASSWORD]) + ? $options[self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PASSWORD] + : $deploymentConfig->get( + self::CONFIG_PATH_PAGE_CACHE_BACKEND_PASSWORD, + $this->getDefaultConfigValue(self::INPUT_KEY_PAGE_CACHE_BACKEND_REDIS_PASSWORD) + ); return $this->redisValidator->isValidConnection($config); } diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php index ef0ea3e988364..abc2a6fdc5ae2 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php @@ -56,6 +56,10 @@ public function testGetOptions() $this->assertArrayHasKey(3, $options); $this->assertInstanceOf(TextConfigOption::class, $options[3]); $this->assertEquals('cache-backend-redis-port', $options[3]->getName()); + + $this->assertArrayHasKey(4, $options); + $this->assertInstanceOf(TextConfigOption::class, $options[4]); + $this->assertEquals('cache-backend-redis-password', $options[4]->getName()); } public function testCreateConfigCacheRedis() @@ -70,7 +74,8 @@ public function testCreateConfigCacheRedis() 'backend_options' => [ 'server' => '', 'port' => '', - 'database' => '' + 'database' => '', + 'password' => '' ] ] ] @@ -92,7 +97,8 @@ public function testCreateConfigWithRedisConfig() 'backend_options' => [ 'server' => 'localhost', 'port' => '1234', - 'database' => '5' + 'database' => '5', + 'password' => '' ] ] ] diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php index e654bea9ac1c5..b9cd137530aa9 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php @@ -60,6 +60,10 @@ public function testGetOptions() $this->assertArrayHasKey(4, $options); $this->assertInstanceOf(TextConfigOption::class, $options[4]); $this->assertEquals('page-cache-redis-compress-data', $options[4]->getName()); + + $this->assertArrayHasKey(5, $options); + $this->assertInstanceOf(TextConfigOption::class, $options[5]); + $this->assertEquals('page-cache-redis-password', $options[5]->getName()); } public function testCreateConfigWithRedis() @@ -75,7 +79,8 @@ public function testCreateConfigWithRedis() 'server'=> '', 'port' => '', 'database' => '', - 'compress_data' => '' + 'compress_data' => '', + 'password' => '' ] ] ] @@ -98,7 +103,8 @@ public function testCreateConfigWithRedisConfiguration() 'server' => 'foo.bar', 'port' => '9000', 'database' => '6', - 'compress_data' => '1' + 'compress_data' => '1', + 'password' => '' ] ] ] From 7dca0a7689ebb26d0a7ff083ff2da395ccea587e Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 25 Jul 2018 11:09:19 +0300 Subject: [PATCH 114/211] MAGETWO-92682: Triggers optimization --- lib/internal/Magento/Framework/Mview/View/Subscription.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Mview/View/Subscription.php b/lib/internal/Magento/Framework/Mview/View/Subscription.php index f6dc9fd673081..fd8fb8bf9705d 100644 --- a/lib/internal/Magento/Framework/Mview/View/Subscription.php +++ b/lib/internal/Magento/Framework/Mview/View/Subscription.php @@ -203,8 +203,8 @@ protected function buildStatement($event, $changelog) case Trigger::EVENT_UPDATE: $trigger = "INSERT IGNORE INTO %s (%s) VALUES (NEW.%s);"; - if ($this->connection->isTableExists($this->getTableName()) - && $describe = $this->connection->describeTable($this->getTableName()) + if ($this->connection->isTableExists($this->resource->getTableName($this->getTableName())) && + $describe = $this->connection->describeTable($this->resource->getTableName($this->getTableName())) ) { $columnNames = array_column($describe, 'COLUMN_NAME'); $columnNames = array_diff($columnNames, $this->ignoredUpdateColumns); From b18a8dab6325970ce0da973928a26e833065c0f5 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <dhorytskyi@magento.com> Date: Wed, 25 Jul 2018 11:10:24 +0300 Subject: [PATCH 115/211] MAGETWO-86609: Disabled wishlist product still appears in wishlist --- app/code/Magento/Wishlist/Model/Wishlist.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Wishlist/Model/Wishlist.php b/app/code/Magento/Wishlist/Model/Wishlist.php index ad66ec18b4557..6a5a7c45383f5 100644 --- a/app/code/Magento/Wishlist/Model/Wishlist.php +++ b/app/code/Magento/Wishlist/Model/Wishlist.php @@ -339,9 +339,11 @@ protected function _addCatalogProduct(\Magento\Catalog\Model\Product $product, $ public function getItemCollection() { if ($this->_itemCollection === null) { - $this->_itemCollection = $this->_wishlistCollectionFactory->create() - ->addWishlistFilter($this) - ->addStoreFilter($this->getSharedStoreIds()); + $this->_itemCollection = $this->_wishlistCollectionFactory->create()->addWishlistFilter( + $this + )->addStoreFilter( + $this->getSharedStoreIds() + )->setVisibilityFilter(); } return $this->_itemCollection; From 93cc9e3dc26c5efdd7d90211e81dc6afae2b9b01 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 25 Jul 2018 11:43:04 +0300 Subject: [PATCH 116/211] MAGETWO-73245: Add product to website will reset has_options and required_options --- app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php index 695038cbf6037..15fad3d45daff 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php @@ -239,6 +239,7 @@ protected function copyToStores($data, $productId) $this->_objectManager->create(\Magento\Catalog\Model\Product::class) ->setStoreId($copyFrom) ->load($productId) + ->setStoreId($copyTo) ->setCanSaveCustomOptions($data['can_save_custom_options']) ->setCopyFromView(true) ->save(); From 23e8dfc7289f7672972fda86d21abc54e858deec Mon Sep 17 00:00:00 2001 From: Volodymyr Hryvinskyi <volodymyr@hryvinskyi.com> Date: Wed, 25 Jul 2018 12:03:39 +0300 Subject: [PATCH 117/211] Update Backward Compatible and change protected to private properties --- .../Magento/Cms/Controller/Index/Index.php | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Cms/Controller/Index/Index.php b/app/code/Magento/Cms/Controller/Index/Index.php index 3326749727c0f..236023373a735 100644 --- a/app/code/Magento/Cms/Controller/Index/Index.php +++ b/app/code/Magento/Cms/Controller/Index/Index.php @@ -18,35 +18,37 @@ class Index extends \Magento\Framework\App\Action\Action { /** - * @var \Magento\Framework\Controller\Result\ForwardFactory + * @var ForwardFactory */ - protected $resultForwardFactory; + private $resultForwardFactory; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ScopeConfigInterface */ - protected $scopeConfig; + private $scopeConfig; /** * @var Page */ - protected $page; + private $page; /** + * Index constructor. + * * @param Context $context - * @param ScopeConfigInterface $scopeConfig * @param ForwardFactory $resultForwardFactory - * @param Page $page + * @param ScopeConfigInterface|null $scopeConfig + * @param Page|null $page */ public function __construct( Context $context, - ScopeConfigInterface $scopeConfig, ForwardFactory $resultForwardFactory, - Page $page + ScopeConfigInterface $scopeConfig = null, + Page $page = null ) { - $this->scopeConfig = $scopeConfig; $this->resultForwardFactory = $resultForwardFactory; - $this->page = $page; + $this->scopeConfig = $scopeConfig ? : $this->_objectManager->get(ScopeConfigInterface::class); + $this->page = $page ? : $this->_objectManager->get(Page::class); parent::__construct($context); } @@ -64,7 +66,7 @@ public function execute($coreRoute = null) $pageId = $this->scopeConfig->getValue(Page::XML_PATH_HOME_PAGE, ScopeInterface::SCOPE_STORE); $resultPage = $this->page->prepareResultPage($this, $pageId); if (!$resultPage) { - /** @var \Magento\Framework\Controller\Result\Forward $resultForward */ + /** @var Forward $resultForward */ $resultForward = $this->resultForwardFactory->create(); $resultForward->forward('defaultIndex'); return $resultForward; From 0134ab0344785ea36e9a0c170cb9f8b6a7deb1fe Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 25 Jul 2018 12:40:18 +0300 Subject: [PATCH 118/211] MAGETWO-73245: Add product to website will reset has_options and required_options --- app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php index 15fad3d45daff..29c90eac1272b 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php @@ -102,7 +102,6 @@ public function execute() $this->productBuilder->build($this->getRequest()) ); $this->productTypeManager->processProduct($product); - if (isset($data['product'][$product->getIdFieldName()])) { throw new \Magento\Framework\Exception\LocalizedException(__('Unable to save product')); } From 922971ed1adf584d9e71037cecb88040eb192dcc Mon Sep 17 00:00:00 2001 From: Sergey Shvets <sshvets@magento.com> Date: Wed, 25 Jul 2018 13:01:32 +0300 Subject: [PATCH 119/211] MAGETWO-93729: Fix scope selector for reports --- .../Block/Adminhtml/Sales/Sales/Grid.php | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Reports/Block/Adminhtml/Sales/Sales/Grid.php b/app/code/Magento/Reports/Block/Adminhtml/Sales/Sales/Grid.php index 21836f1a8c276..1f90309721c23 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Sales/Sales/Grid.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Sales/Sales/Grid.php @@ -6,6 +6,8 @@ namespace Magento\Reports\Block\Adminhtml\Sales\Sales; +use Magento\Reports\Block\Adminhtml\Grid\Column\Renderer\Currency; + /** * Adminhtml sales report grid block * @@ -36,7 +38,7 @@ protected function _construct() */ public function getResourceCollectionName() { - return $this->getFilterData()->getData('report_type') == 'updated_at_order' + return $this->getFilterData()->getData('report_type') === 'updated_at_order' ? \Magento\Sales\Model\ResourceModel\Report\Order\Updatedat\Collection::class : \Magento\Sales\Model\ResourceModel\Report\Order\Collection::class; } @@ -103,9 +105,7 @@ protected function _prepareColumns() ] ); - if ($this->getFilterData()->getStoreIds()) { - $this->setStoreIds(explode(',', $this->getFilterData()->getStoreIds())); - } + $this->setStoreIds($this->_getStoreIds()); $currencyCode = $this->getCurrentCurrencyCode(); $rate = $this->getRate($currencyCode); @@ -118,6 +118,7 @@ protected function _prepareColumns() 'index' => 'total_income_amount', 'total' => 'sum', 'sortable' => false, + 'renderer' => Currency::class, 'rate' => $rate, 'header_css_class' => 'col-sales-total', 'column_css_class' => 'col-sales-total' @@ -133,6 +134,7 @@ protected function _prepareColumns() 'index' => 'total_revenue_amount', 'total' => 'sum', 'sortable' => false, + 'renderer' => Currency::class, 'visibility_filter' => ['show_actual_columns'], 'rate' => $rate, 'header_css_class' => 'col-revenue', @@ -149,6 +151,7 @@ protected function _prepareColumns() 'index' => 'total_profit_amount', 'total' => 'sum', 'sortable' => false, + 'renderer' => Currency::class, 'visibility_filter' => ['show_actual_columns'], 'rate' => $rate, 'header_css_class' => 'col-profit', @@ -165,6 +168,7 @@ protected function _prepareColumns() 'index' => 'total_invoiced_amount', 'total' => 'sum', 'sortable' => false, + 'renderer' => Currency::class, 'rate' => $rate, 'header_css_class' => 'col-invoiced', 'column_css_class' => 'col-invoiced' @@ -180,6 +184,7 @@ protected function _prepareColumns() 'index' => 'total_paid_amount', 'total' => 'sum', 'sortable' => false, + 'renderer' => Currency::class, 'visibility_filter' => ['show_actual_columns'], 'rate' => $rate, 'header_css_class' => 'col-paid', @@ -196,6 +201,7 @@ protected function _prepareColumns() 'index' => 'total_refunded_amount', 'total' => 'sum', 'sortable' => false, + 'renderer' => Currency::class, 'rate' => $rate, 'header_css_class' => 'col-refunded', 'column_css_class' => 'col-refunded' @@ -211,6 +217,7 @@ protected function _prepareColumns() 'index' => 'total_tax_amount', 'total' => 'sum', 'sortable' => false, + 'renderer' => Currency::class, 'rate' => $rate, 'header_css_class' => 'col-sales-tax', 'column_css_class' => 'col-sales-tax' @@ -226,6 +233,7 @@ protected function _prepareColumns() 'index' => 'total_tax_amount_actual', 'total' => 'sum', 'sortable' => false, + 'renderer' => Currency::class, 'visibility_filter' => ['show_actual_columns'], 'rate' => $rate, 'header_css_class' => 'col-tax', @@ -242,6 +250,7 @@ protected function _prepareColumns() 'index' => 'total_shipping_amount', 'total' => 'sum', 'sortable' => false, + 'renderer' => Currency::class, 'rate' => $rate, 'header_css_class' => 'col-sales-shipping', 'column_css_class' => 'col-sales-shipping' @@ -257,6 +266,7 @@ protected function _prepareColumns() 'index' => 'total_shipping_amount_actual', 'total' => 'sum', 'sortable' => false, + 'renderer' => Currency::class, 'visibility_filter' => ['show_actual_columns'], 'rate' => $rate, 'header_css_class' => 'col-shipping', @@ -273,6 +283,7 @@ protected function _prepareColumns() 'index' => 'total_discount_amount', 'total' => 'sum', 'sortable' => false, + 'renderer' => Currency::class, 'rate' => $rate, 'header_css_class' => 'col-sales-discount', 'column_css_class' => 'col-sales-discount' @@ -288,6 +299,7 @@ protected function _prepareColumns() 'index' => 'total_discount_amount_actual', 'total' => 'sum', 'sortable' => false, + 'renderer' => Currency::class, 'visibility_filter' => ['show_actual_columns'], 'rate' => $rate, 'header_css_class' => 'col-discount', @@ -304,6 +316,7 @@ protected function _prepareColumns() 'index' => 'total_canceled_amount', 'total' => 'sum', 'sortable' => false, + 'renderer' => Currency::class, 'rate' => $rate, 'header_css_class' => 'col-canceled', 'column_css_class' => 'col-canceled' From 613a75995e498d4e93ece468388a97a28f979757 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Wed, 25 Jul 2018 13:33:46 +0300 Subject: [PATCH 120/211] MAGETWO-77742: [2.2.x] - Error message when uploading unsupported file format --- ...up.xml => CreateConfigurationsWithVisualSwatchActionGroup.xml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/code/Magento/Swatches/Test/Mftf/ActionGroup/{CreateConfigurationsWithSwatchActionGroup.xml => CreateConfigurationsWithVisualSwatchActionGroup.xml} (100%) diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/CreateConfigurationsWithSwatchActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/CreateConfigurationsWithVisualSwatchActionGroup.xml similarity index 100% rename from app/code/Magento/Swatches/Test/Mftf/ActionGroup/CreateConfigurationsWithSwatchActionGroup.xml rename to app/code/Magento/Swatches/Test/Mftf/ActionGroup/CreateConfigurationsWithVisualSwatchActionGroup.xml From ffc050a8f4307077de6cdd5a111797dda98da7e7 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryvinskyi <volodymyr@hryvinskyi.com> Date: Wed, 25 Jul 2018 13:42:46 +0300 Subject: [PATCH 121/211] Fix Object Manager --- app/code/Magento/Cms/Controller/Index/Index.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Cms/Controller/Index/Index.php b/app/code/Magento/Cms/Controller/Index/Index.php index 236023373a735..3197bac650e96 100644 --- a/app/code/Magento/Cms/Controller/Index/Index.php +++ b/app/code/Magento/Cms/Controller/Index/Index.php @@ -7,6 +7,7 @@ use Magento\Framework\App\Action\Context; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\ResponseInterface; use Magento\Framework\Controller\ResultInterface; use Magento\Framework\Controller\Result\Forward; @@ -47,8 +48,8 @@ public function __construct( Page $page = null ) { $this->resultForwardFactory = $resultForwardFactory; - $this->scopeConfig = $scopeConfig ? : $this->_objectManager->get(ScopeConfigInterface::class); - $this->page = $page ? : $this->_objectManager->get(Page::class); + $this->scopeConfig = $scopeConfig ? : ObjectManager::getInstance()->get(ScopeConfigInterface::class); + $this->page = $page ? : ObjectManager::getInstance()->get(Page::class); parent::__construct($context); } From 4a85afda01d17e7972a9744acad8f11c9636ea3c Mon Sep 17 00:00:00 2001 From: Wouter Samaey <wouter.samaey@storefront.be> Date: Wed, 25 Jul 2018 14:34:38 +0200 Subject: [PATCH 122/211] Using interface instead of model directly --- app/code/Magento/Theme/etc/di.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Theme/etc/di.xml b/app/code/Magento/Theme/etc/di.xml index c20184fec6bc4..21e1736d01bf1 100644 --- a/app/code/Magento/Theme/etc/di.xml +++ b/app/code/Magento/Theme/etc/di.xml @@ -69,7 +69,7 @@ </type> <type name="Magento\Framework\App\Area"> <arguments> - <argument name="translator" xsi:type="object">Magento\Framework\Translate</argument> + <argument name="translator" xsi:type="object">Magento\Framework\TranslateInterface</argument> <argument name="design" xsi:type="object">Magento\Theme\Model\Design\Proxy</argument> </arguments> </type> From 0df3a1fa3d8132f6234a97fdb0d0a94164d8a9b2 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Wed, 25 Jul 2018 16:14:40 +0300 Subject: [PATCH 123/211] MAGETWO-73359: CMS Page does not save when same url key with hierarchy for Multi-store --- .../CreateNewPageWithAllValuesActionGroup.xml | 12 ++++++------ .../ActionGroup/DeletePageByUrlKeyActionGroup.xml | 8 ++++---- .../Magento/Cms/Test/Mftf/Page/CmsNewPagePage.xml | 1 + .../Mftf/Section/CmsNewPageHierarchySection.xml | 15 --------------- 4 files changed, 11 insertions(+), 25 deletions(-) delete mode 100644 app/code/Magento/Cms/Test/Mftf/Section/CmsNewPageHierarchySection.xml diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml index 2789d16d3a5d6..48b10d0ee48be 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml @@ -9,19 +9,19 @@ xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> <actionGroup name="CreateNewPageWithAllValues"> <arguments> - <argument name="PageTitle" type="string"/> - <argument name="ContentHeading" type="string"/> - <argument name="URLKey" type="string"/> + <argument name="pageTitle" type="string"/> + <argument name="contentHeading" type="string"/> + <argument name="urlKey" type="string"/> <argument name="selectStoreViewOpt" type="string"/> </arguments> <amOnPage url="{{CmsNewPagePage.url}}" stepKey="amOnCMSNewPage"/> <waitForPageLoad stepKey="waitForPageLoad1"/> <waitForElementVisible selector="{{CmsNewPagePageBasicFieldsSection.pageTitle}}" stepKey="waitUntilTitleAppears"/> - <fillField selector="{{CmsNewPagePageBasicFieldsSection.pageTitle}}" userInput="{{PageTitle}}" stepKey="fillFieldTitle"/> + <fillField selector="{{CmsNewPagePageBasicFieldsSection.pageTitle}}" userInput="{{pageTitle}}" stepKey="fillFieldTitle"/> <click selector="{{CmsNewPagePageContentSection.header}}" stepKey="clickExpandContent"/> - <fillField selector="{{CmsNewPagePageContentSection.contentHeading}}" userInput="{{ContentHeading}}" stepKey="fillFieldContentHeading"/> + <fillField selector="{{CmsNewPagePageContentSection.contentHeading}}" userInput="{{contentHeading}}" stepKey="fillFieldContentHeading"/> <click selector="{{CmsNewPagePageSeoSection.header}}" stepKey="clickExpandSearchEngineOptimization"/> - <fillField selector="{{CmsNewPagePageSeoSection.urlKey}}" userInput="{{URLKey}}" stepKey="fillFieldURLKey"/> + <fillField selector="{{CmsNewPagePageSeoSection.urlKey}}" userInput="{{urlKey}}" stepKey="fillFieldUrlKey"/> <click selector="{{AdminCmsNewPagePiwSection.header}}" stepKey="clickPageInWebsites"/> <waitForElementVisible selector="{{AdminCmsNewPagePiwSection.selectStoreView(selectStoreViewOpt)}}" stepKey="waitForStoreGridReload"/> <clickWithLeftButton selector="{{AdminCmsNewPagePiwSection.selectStoreView(selectStoreViewOpt)}}" stepKey="clickStoreView2"/> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml index eef6c6f0ca6ed..05e61ac86e166 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml @@ -9,12 +9,12 @@ xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> <actionGroup name="DeletePageByUrlKeyActionGroup"> <arguments> - <argument name="UrlKey" type="string"/> + <argument name="urlKey" type="string"/> </arguments> - <amOnPage url="{{CmsPagesPage.url}}" stepKey="amOnCMSNewPage"/> + <amOnPage url="{{CmsPagesPage.url}}" stepKey="amOnCMSPagesIndexPage"/> <waitForPageLoad stepKey="waitForPageLoad1"/> - <click selector="{{CmsPagesPageActionsSection.select(UrlKey)}}" stepKey="clickSelect"/> - <click selector="{{CmsPagesPageActionsSection.delete(UrlKey)}}" stepKey="clickDelete"/> + <click selector="{{CmsPagesPageActionsSection.select(urlKey)}}" stepKey="clickSelect"/> + <click selector="{{CmsPagesPageActionsSection.delete(urlKey)}}" stepKey="clickDelete"/> <waitForElementVisible selector="{{CmsPagesPageActionsSection.deleteConfirm}}" stepKey="waitForOkButtonToBeVisible"/> <click selector="{{CmsPagesPageActionsSection.deleteConfirm}}" stepKey="clickOkButton"/> <waitForPageLoad stepKey="waitForPageLoad3"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Page/CmsNewPagePage.xml b/app/code/Magento/Cms/Test/Mftf/Page/CmsNewPagePage.xml index b165d6c044c2b..bb8dc9cf9159c 100644 --- a/app/code/Magento/Cms/Test/Mftf/Page/CmsNewPagePage.xml +++ b/app/code/Magento/Cms/Test/Mftf/Page/CmsNewPagePage.xml @@ -13,5 +13,6 @@ <section name="CmsNewPagePageBasicFieldsSection"/> <section name="CmsNewPagePageContentSection"/> <section name="CmsNewPagePageSeoSection"/> + <section name="AdminCmsNewPagePiwSection"/> </page> </pages> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPageHierarchySection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPageHierarchySection.xml deleted file mode 100644 index 96ebd34360d33..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPageHierarchySection.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?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="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> - <section name="CmsNewPageHierarchySection"> - <element name="header" type="button" selector="div[data-index=hierarchy]" timeout="30"/> - <element name="selectHierarchy" type="button" selector="//a/span[contains(text(),'{{var1}}')]" parameterized="true" timeout="30"/> - </section> -</sections> From cfe7afebff59858ede5d282a6158e0a54bbd3509 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Wed, 25 Jul 2018 16:16:05 +0300 Subject: [PATCH 124/211] MAGETWO-77742: [2.2.x] - Error message when uploading unsupported file format --- .../StorefrontConfigurableProductWithFileCustomOptionTest.xml | 2 +- .../Test/StorefrontSwatchProductWithFileCustomOptionTest.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml index c4f63cef6657d..5dcbfbaf44037 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml @@ -46,7 +46,7 @@ <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> <!--Go to storefront--> - <amOnPage url="" stepKey="goToHomePage"/> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePage"/> <waitForPageLoad stepKey="waitForHomePageLoad"/> <click selector="{{StorefrontNavigationSection.topCategory($$createCategory.name$$)}}" stepKey="goToCategoryStorefront"/> <waitForPageLoad stepKey="waitForCategoryPageLoad"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml index bd05a326fd4e9..1786b9bad236d 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml @@ -54,7 +54,7 @@ <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> <!--Go to storefront--> - <amOnPage url="" stepKey="goToHomePage"/> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePage"/> <waitForPageLoad stepKey="waitForHomePageLoad"/> <click selector="{{StorefrontNavigationSection.topCategory($$createCategory.name$$)}}" stepKey="goToCategoryStorefront"/> <waitForPageLoad stepKey="waitForCategoryPageLoad"/> From 1ab9bfd8dc8ac86e9246a905e18cda443428fcec Mon Sep 17 00:00:00 2001 From: Navarr Barnier <navarr@mediotype.com> Date: Wed, 25 Jul 2018 09:55:41 -0400 Subject: [PATCH 125/211] Allow 3rd party modules to perform actions after totals calculation by returning the storage.post promise, third party modules can perform additional actions by adding .done/.fail or .always tasks to the request promise by creating a Javascript mixin for the totals processor. --- .../frontend/web/js/model/cart/totals-processor/default.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/cart/totals-processor/default.js b/app/code/Magento/Checkout/view/frontend/web/js/model/cart/totals-processor/default.js index e269462047748..0e94232786c65 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/cart/totals-processor/default.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/cart/totals-processor/default.js @@ -38,7 +38,7 @@ define([ payload.addressInformation['shipping_carrier_code'] = quote.shippingMethod()['carrier_code']; } - storage.post( + return storage.post( serviceUrl, JSON.stringify(payload), false ).done(function (result) { var data = { @@ -96,7 +96,7 @@ define([ ) { quote.setTotals(cartCache.get('totals')); } else { - loadFromServer(address); + return loadFromServer(address); } } }; From 640309b09b7b735b961b30e4eb78702952516036 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <dhorytskyi@magento.com> Date: Wed, 25 Jul 2018 17:18:14 +0300 Subject: [PATCH 126/211] MAGETWO-86609: Disabled wishlist product still appears in wishlist --- .../Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml index d146834bb6618..f07e509c610ad 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml @@ -80,15 +80,12 @@ <click stepKey="ClickSwitchStoreButtonOnDefaultStore" selector="{{StorefrontFooterSection.SwitchStoreButton}}"/> <click stepKey="SelectSecondStoreToSwitchOn" selector="{{StorefrontFooterSection.StoreLink($$storeGroup.group[name]$$)}}"/> <!-- Verify that both products are visible in wishlist on both stores --> - <see stepKey="seeProduct1InWishlist" userInput="$$product.name$$" selector="{{StorefrontCustomerWishlistSection.productItemNameText}}"/> <amOnPage url="$$secondProduct.name$$.html" stepKey="navigateToProductPageOnSecondStore"/> <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$secondProduct.name$$" stepKey="assertSecondProductNameTitle"/> <click stepKey="addSecondProductToWishlist" selector="{{StorefrontProductPageSection.AddToWishlist}}"/> - <see stepKey="seeProduct1InWishlistOnSecondStore" userInput="$$product.name$$" selector="{{StorefrontCustomerWishlistSection.productItemNameText}}"/> <see stepKey="seeProduct2InWishlistOnSecondStore" userInput="$$secondProduct.name$$" selector="{{StorefrontCustomerWishlistSection.productItemNameText}}"/> <click stepKey="ClickSwitchStoreButtonOnSecondStore" selector="{{StorefrontFooterSection.SwitchStoreButton}}"/> <click stepKey="SelectDefaultStoreToSwitchOn" selector="{{StorefrontFooterSection.StoreLink('Main Website Store')}}"/> <see stepKey="seeProduct1InWishlistOnDefaultStore" userInput="$$product.name$$" selector="{{StorefrontCustomerWishlistSection.productItemNameText}}"/> - <see stepKey="seeProduct2InWishlistOnDefaultStore" userInput="$$secondProduct.name$$" selector="{{StorefrontCustomerWishlistSection.productItemNameText}}"/> </test> </tests> From 50cd369d2f49a458711801a0c49ccca6acb173b6 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <dhorytskyi@magento.com> Date: Wed, 25 Jul 2018 18:16:59 +0300 Subject: [PATCH 127/211] MAGETWO-86609: Disabled wishlist product still appears in wishlist --- .../Magento/Wishlist/Model/ResourceModel/Item/Collection.php | 5 +++-- app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php b/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php index bd921f3785c96..d3b710f229527 100644 --- a/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php +++ b/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php @@ -307,7 +307,8 @@ protected function _assignProducts() $checkInStock = $this->_productInStock && !$this->stockConfiguration->isShowOutOfStock(); - foreach ($this as $itemKey => $item) { + /** @var \Magento\Wishlist\Model\Item $item */ + foreach ($this as $item) { $product = $productCollection->getItemById($item->getProductId()); if ($product) { if ($checkInStock && !$product->isInStock()) { @@ -320,7 +321,7 @@ protected function _assignProducts() $item->setPrice($product->getPrice()); } } else { - $this->removeItemByKey($itemKey); + $this->removeItemByKey($item->getId()); } } diff --git a/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php b/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php index ac9ff2ae63d8d..ff8a3a3b87cec 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php @@ -272,6 +272,9 @@ public function testUpdateItem($itemId, $buyRequest, $param) $items->expects($this->once()) ->method('addStoreFilter') ->will($this->returnSelf()); + $items->expects($this->once()) + ->method('setVisibilityFilter') + ->will($this->returnSelf()); $items->expects($this->once()) ->method('getItemById') ->will($this->returnValue($item)); From 7eb00ddd9f029341c6c885ed035344ae1da7630f Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Wed, 25 Jul 2018 19:09:14 +0300 Subject: [PATCH 128/211] MAGETWO-73359: CMS Page does not save when same url key with hierarchy for Multi-store StorefrontAddMultipleStoreProductsToWishlistTest test fix --- .../Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml index d146834bb6618..3f4c8eb764c1d 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml @@ -46,7 +46,8 @@ <scrollToTopOfPage stepKey="scrollToTop1"/> <click stepKey="clickSwitchStoreMenuForProduct1" selector="{{AdminProductFormActionSection.changeStoreButton}}"/> <click stepKey="clickOnStoreNameItemForProduct1" selector="{{AdminProductFormChangeStoreSection.storeSelector(customStore.name)}}"/> - <click stepKey="acceptStoreSwitchingForProduct1" selector="{{AdminProductFormChangeStoreSection.acceptButton}}"/> + <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitingForConfirmationModal"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="acceptModal"/> <click stepKey="uncheckVisibilityUseDefaultValueForProduct1" selector="{{AdminProductFormSection.visibilityUseDefault}}"/> <selectOption stepKey="makeProductNotVisibleOnSecondaryStoreView" selector="{{AdminProductFormSection.visibility}}" userInput="Not Visible Individually"/> <click stepKey="saveEditedProductForProduct1" selector="{{AdminProductFormActionSection.saveButton}}"/> From e98ffc7639ac82103790c5858c96c1108d07f445 Mon Sep 17 00:00:00 2001 From: Tomash Khamlai <tomash.khamlai@gmail.com> Date: Thu, 26 Jul 2018 01:38:34 +0300 Subject: [PATCH 129/211] Set proper text-aligh for the <th> element of the Subtotal column --- .../Magento/blank/Magento_Sales/web/css/source/_email.less | 3 ++- .../Magento/luma/Magento_Sales/web/css/source/_email.less | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_Sales/web/css/source/_email.less b/app/design/frontend/Magento/blank/Magento_Sales/web/css/source/_email.less index 84adec39c8892..215d7d8b322b4 100644 --- a/app/design/frontend/Magento/blank/Magento_Sales/web/css/source/_email.less +++ b/app/design/frontend/Magento/blank/Magento_Sales/web/css/source/_email.less @@ -203,7 +203,8 @@ text-align: center; } - .item-price { + .item-price, + .item-subtotal { text-align: right; } diff --git a/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_email.less b/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_email.less index 9a3f433618c36..3f19d1020bab9 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_email.less +++ b/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_email.less @@ -207,7 +207,8 @@ text-align: center; } - .item-price { + .item-price, + .item-subtotal { text-align: right; } From d68802927ae088846a6b9001b71d99acbd836ea4 Mon Sep 17 00:00:00 2001 From: Wouter Samaey <wouter.samaey@storefront.be> Date: Wed, 25 Jul 2018 14:28:47 +0200 Subject: [PATCH 130/211] Added missing exception cause for better error handling Added missing exception cause for better error handling --- app/code/Magento/Translation/Model/Js/DataProvider.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Translation/Model/Js/DataProvider.php b/app/code/Magento/Translation/Model/Js/DataProvider.php index 9037574e28a5b..f782d1fdd35b7 100644 --- a/app/code/Magento/Translation/Model/Js/DataProvider.php +++ b/app/code/Magento/Translation/Model/Js/DataProvider.php @@ -114,7 +114,8 @@ public function getData($themePath) } } catch (\Exception $e) { throw new LocalizedException( - __('Error while translating phrase "%s" in file %s.', $phrase, $filePath[0]) + __('Error while translating phrase "%s" in file %s.', $phrase, $filePath[0]), + $e ); } } From 1c72191e25444a0213723f6825d236ee4df65e0e Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Thu, 26 Jul 2018 09:57:58 +0300 Subject: [PATCH 131/211] MAGETWO-75086: Child product image should be shown in Wishist if options are selected for configurable product #8168 --- .../Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml | 2 +- .../Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml | 2 +- .../Product/Configuration/Item/ItemProductResolverTest.php | 2 +- .../Product/Configuration/Item/ItemProductResolverTest.php | 2 +- .../Wishlist/Block/Customer/Wishlist/Item/Column/Image.php | 1 + ...onfigurableProductChildImageShouldBeShownOnWishlistTest.xml} | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) rename app/code/Magento/Wishlist/Test/Mftf/Test/{ConfigurableProductChildImageShouldBeShownOnWishListTest.xml => StorefrontConfigurableProductChildImageShouldBeShownOnWishlistTest.xml} (98%) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml index ff5f0a0c52523..388727202230c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml @@ -38,7 +38,7 @@ <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{sku}}" stepKey="fillProductSkuFilter"/> <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> - <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad" time="30"/> + <waitForLoadingMaskToDisappear stepKey="waitForFilteredGridLoad"/> </actionGroup> <!--Delete a product by filtering grid and using delete action--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml index c1d04f2a1b033..973b79adb54a5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml @@ -12,7 +12,7 @@ <element name="QtyInput" type="button" selector="input.input-text.qty"/> <element name="AddToCartBtn" type="button" selector="button.action.tocart.primary"/> <element name="SuccessMsg" type="button" selector="div.message-success"/> - <element name="addToWishlist" type="button" selector="//a[@class='action towishlist']" timeout="30"/> + <element name="addToWishlist" type="button" selector="a.action.towishlist" timeout="30"/> <element name="addToCartButtonTitleIsAdding" type="text" selector="//button/span[text()='Adding...']"/> <element name="addToCartButtonTitleIsAdded" type="text" selector="//button/span[text()='Added']"/> <element name="addToCartButtonTitleIsAddToCart" type="text" selector="//button/span[text()='Add to Cart']"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php index 327a231d05272..28e3e5e4cb5c6 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php @@ -72,7 +72,7 @@ public function testGetFinalProduct(bool $existOption, string $configImageSource ->with(ItemProductResolver::CONFIG_THUMBNAIL_SOURCE, ScopeInterface::SCOPE_STORE) ->willReturn($configImageSource); - $finalProduct = ($configImageSource == Thumbnail::OPTION_USE_PARENT_IMAGE)? $parentProduct: $childProduct; + $finalProduct = ($configImageSource == Thumbnail::OPTION_USE_PARENT_IMAGE) ? $parentProduct : $childProduct; } $item = $this->createPartialMock( diff --git a/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php b/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php index 5145800c6d911..bd4fb9231c0b2 100644 --- a/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php +++ b/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Configuration/Item/ItemProductResolverTest.php @@ -72,7 +72,7 @@ public function testGetFinalProduct(bool $existOption, string $configImageSource ->with(ItemProductResolver::CONFIG_THUMBNAIL_SOURCE, ScopeInterface::SCOPE_STORE) ->willReturn($configImageSource); - $finalProduct = ($configImageSource == Thumbnail::OPTION_USE_PARENT_IMAGE)? $parentProduct: $childProduct; + $finalProduct = ($configImageSource == Thumbnail::OPTION_USE_PARENT_IMAGE) ? $parentProduct : $childProduct; } $item = $this->createPartialMock( diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php index 64ef62a423eb3..26fb8cd283ec1 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Image.php @@ -46,6 +46,7 @@ public function __construct( /** * Identify the product from which thumbnail should be taken. * + * @param \Magento\Wishlist\Model\Item $item * @return \Magento\Catalog\Model\Product */ public function getProductForThumbnail(\Magento\Wishlist\Model\Item $item): \Magento\Catalog\Model\Product diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontConfigurableProductChildImageShouldBeShownOnWishlistTest.xml similarity index 98% rename from app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml rename to app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontConfigurableProductChildImageShouldBeShownOnWishlistTest.xml index 4bd0d34382105..ad52766c617b5 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontConfigurableProductChildImageShouldBeShownOnWishlistTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/testSchema.xsd"> - <test name="ConfigurableProductChildImageShouldBeShownOnWishListTest"> + <test name="StorefrontConfigurableProductChildImageShouldBeShownOnWishlistTest"> <annotations> <features value="Wishlist"/> <group value="wishlist"/> From f9839bbfeb779c5fb4f3fa6f2ee323ab33808a83 Mon Sep 17 00:00:00 2001 From: Colin Tickle <colin@twojay.co> Date: Thu, 26 Jul 2018 10:25:23 +0100 Subject: [PATCH 132/211] Add meta NOINDEX,NOFOLLOW to admin scope to avoid accidental bot crawling. --- .../adminhtml/Magento/backend/Magento_Backend/layout/default.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/layout/default.xml b/app/design/adminhtml/Magento/backend/Magento_Backend/layout/default.xml index da16bde107673..337d63369b160 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/layout/default.xml +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/layout/default.xml @@ -7,6 +7,7 @@ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <head> + <meta name="robots" content="NOINDEX,NOFOLLOW"/> <css src="jquery/jstree/themes/default/style.css"/> <css src="css/styles-old.css"/> <css src="css/styles.css"/> From fcb9328962706d8629d9b449f3f2f72669a3d6c8 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> Date: Thu, 26 Jul 2018 13:00:16 +0300 Subject: [PATCH 133/211] magento/magento2#17035 Replaced deprecated methods Fix integration test --- .../Magento/Backend/Controller/Adminhtml/CacheTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/CacheTest.php b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/CacheTest.php index 0525c68b04d14..1b97fb2ad510c 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/CacheTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/CacheTest.php @@ -72,7 +72,7 @@ public function testMassActionsInvalidTypes($action) $this->getRequest()->setParams(['types' => ['invalid_type_1', 'invalid_type_2', 'config']]); $this->dispatch('backend/admin/cache/' . $action); $this->assertSessionMessages( - $this->contains("Specified cache type(s) don't exist: invalid_type_1, invalid_type_2"), + $this->contains("Specified cache type(s) don't exist: invalid_type_1, invalid_type_2"), \Magento\Framework\Message\MessageInterface::TYPE_ERROR ); } From 93bb9d67f03902c578880102852995b4b7f7fa1d Mon Sep 17 00:00:00 2001 From: Ievgen Sentiabov <isentiabov@magento.com> Date: Thu, 26 Jul 2018 13:29:10 +0300 Subject: [PATCH 134/211] MAGETWO-93108: Product URL rewrites get deleted in multi store views - Added products URL rewrites regeneration with custom URL Keys if category was changed in Global scope --- .../Observer/UrlRewriteHandler.php | 240 +++++++++++------- .../Fixtures/product_custom_url_key.php | 30 +++ .../product_custom_url_key_rollback.php | 21 ++ .../Observer/UrlRewriteHandlerTest.php | 111 ++++++++ .../_files/product_with_category.php | 2 +- 5 files changed, 314 insertions(+), 90 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Fixtures/product_custom_url_key.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Fixtures/product_custom_url_key_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandlerTest.php diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php b/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php index 9892465d1538a..18360dedf0693 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php @@ -3,27 +3,48 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\CatalogUrlRewrite\Observer; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; +use Magento\CatalogUrlRewrite\Model\Category\ChildrenCategoriesProvider; +use Magento\CatalogUrlRewrite\Model\CategoryProductUrlPathGenerator; +use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator; +use Magento\CatalogUrlRewrite\Model\ProductScopeRewriteGenerator; +use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\UrlRewrite\Model\MergeDataProvider; +use Magento\UrlRewrite\Model\MergeDataProviderFactory; +use Magento\UrlRewrite\Model\UrlPersistInterface; +use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class UrlRewriteHandler { /** - * @var \Magento\CatalogUrlRewrite\Model\Category\ChildrenCategoriesProvider + * @var ChildrenCategoriesProvider */ protected $childrenCategoriesProvider; /** - * @var \Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator + * @var CategoryUrlRewriteGenerator */ protected $categoryUrlRewriteGenerator; /** - * @var \Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator + * @var ProductUrlRewriteGenerator */ protected $productUrlRewriteGenerator; /** - * @var \Magento\UrlRewrite\Model\UrlPersistInterface + * @var UrlPersistInterface */ protected $urlPersist; @@ -33,44 +54,51 @@ class UrlRewriteHandler protected $isSkippedProduct; /** - * @var \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory + * @var CollectionFactory */ protected $productCollectionFactory; /** - * @var \Magento\CatalogUrlRewrite\Model\CategoryProductUrlPathGenerator + * @var CategoryProductUrlPathGenerator */ private $categoryBasedProductRewriteGenerator; /** - * @var \Magento\UrlRewrite\Model\MergeDataProvider + * @var MergeDataProvider */ private $mergeDataProviderPrototype; /** - * @var \Magento\Framework\Serialize\Serializer\Json + * @var Json */ private $serializer; /** - * @param \Magento\CatalogUrlRewrite\Model\Category\ChildrenCategoriesProvider $childrenCategoriesProvider - * @param \Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator - * @param \Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator $productUrlRewriteGenerator - * @param \Magento\UrlRewrite\Model\UrlPersistInterface $urlPersist - * @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory - * @param \Magento\CatalogUrlRewrite\Model\CategoryProductUrlPathGenerator $categoryBasedProductRewriteGenerator - * @param \Magento\UrlRewrite\Model\MergeDataProviderFactory|null $mergeDataProviderFactory - * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer + * @var ProductScopeRewriteGenerator + */ + private $productScopeRewriteGenerator; + + /** + * @param ChildrenCategoriesProvider $childrenCategoriesProvider + * @param CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator + * @param ProductUrlRewriteGenerator $productUrlRewriteGenerator + * @param UrlPersistInterface $urlPersist + * @param CollectionFactory $productCollectionFactory + * @param CategoryProductUrlPathGenerator $categoryBasedProductRewriteGenerator + * @param MergeDataProviderFactory|null $mergeDataProviderFactory + * @param Json|null $serializer + * @param ProductScopeRewriteGenerator|null $productScopeRewriteGenerator */ public function __construct( - \Magento\CatalogUrlRewrite\Model\Category\ChildrenCategoriesProvider $childrenCategoriesProvider, - \Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator, - \Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator $productUrlRewriteGenerator, - \Magento\UrlRewrite\Model\UrlPersistInterface $urlPersist, - \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory, - \Magento\CatalogUrlRewrite\Model\CategoryProductUrlPathGenerator $categoryBasedProductRewriteGenerator, - \Magento\UrlRewrite\Model\MergeDataProviderFactory $mergeDataProviderFactory = null, - \Magento\Framework\Serialize\Serializer\Json $serializer = null + ChildrenCategoriesProvider $childrenCategoriesProvider, + CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator, + ProductUrlRewriteGenerator $productUrlRewriteGenerator, + UrlPersistInterface $urlPersist, + CollectionFactory $productCollectionFactory, + CategoryProductUrlPathGenerator $categoryBasedProductRewriteGenerator, + MergeDataProviderFactory $mergeDataProviderFactory = null, + Json $serializer = null, + ProductScopeRewriteGenerator $productScopeRewriteGenerator = null ) { $this->childrenCategoriesProvider = $childrenCategoriesProvider; $this->categoryUrlRewriteGenerator = $categoryUrlRewriteGenerator; @@ -79,58 +107,29 @@ public function __construct( $this->productCollectionFactory = $productCollectionFactory; $this->categoryBasedProductRewriteGenerator = $categoryBasedProductRewriteGenerator; - if (!isset($mergeDataProviderFactory)) { - $mergeDataProviderFactory = \Magento\Framework\App\ObjectManager::getInstance()->get( - \Magento\UrlRewrite\Model\MergeDataProviderFactory::class - ); - } - + $objectManager = ObjectManager::getInstance(); + $mergeDataProviderFactory = $mergeDataProviderFactory ?: $objectManager->get(MergeDataProviderFactory::class); $this->mergeDataProviderPrototype = $mergeDataProviderFactory->create(); - - $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance()->get( - \Magento\Framework\Serialize\Serializer\Json::class - ); + $this->serializer = $serializer ?: $objectManager->get(Json::class); + $this->productScopeRewriteGenerator = $productScopeRewriteGenerator + ?: $objectManager->get(ProductScopeRewriteGenerator::class); } /** - * Generate url rewrites for products assigned to category + * Generates URL rewrites for products assigned to category. * - * @param \Magento\Catalog\Model\Category $category + * @param Category $category * @return array */ - public function generateProductUrlRewrites(\Magento\Catalog\Model\Category $category) + public function generateProductUrlRewrites(Category $category): array { $mergeDataProvider = clone $this->mergeDataProviderPrototype; $this->isSkippedProduct[$category->getEntityId()] = []; $saveRewriteHistory = $category->getData('save_rewrites_history'); - $storeId = $category->getStoreId(); + $storeId = (int)$category->getStoreId(); if ($category->getChangedProductIds()) { - $this->isSkippedProduct[$category->getEntityId()] = $category->getAffectedProductIds(); - /* @var \Magento\Catalog\Model\ResourceModel\Product\Collection $collection */ - $collection = $this->productCollectionFactory->create() - ->setStoreId($storeId) - ->addIdFilter($category->getAffectedProductIds()) - ->addAttributeToSelect('visibility') - ->addAttributeToSelect('name') - ->addAttributeToSelect('url_key') - ->addAttributeToSelect('url_path'); - - $collection->setPageSize(1000); - $pageCount = $collection->getLastPageNumber(); - $currentPage = 1; - while ($currentPage <= $pageCount) { - $collection->setCurPage($currentPage); - foreach ($collection as $product) { - $product->setStoreId($storeId); - $product->setData('save_rewrites_history', $saveRewriteHistory); - $mergeDataProvider->merge( - $this->productUrlRewriteGenerator->generate($product, $category->getEntityId()) - ); - } - $collection->clear(); - $currentPage++; - } + $this->generateChangedProductUrls($mergeDataProvider, $category, $storeId, $saveRewriteHistory); } else { $mergeDataProvider->merge( $this->getCategoryProductsUrlRewrites( @@ -157,21 +156,49 @@ public function generateProductUrlRewrites(\Magento\Catalog\Model\Category $cate } /** - * @param \Magento\Catalog\Model\Category $category + * @param Category $category + * @return void + */ + public function deleteCategoryRewritesForChildren(Category $category) + { + $categoryIds = $this->childrenCategoriesProvider->getChildrenIds($category, true); + $categoryIds[] = $category->getId(); + foreach ($categoryIds as $categoryId) { + $this->urlPersist->deleteByData( + [ + UrlRewrite::ENTITY_ID => + $categoryId, + UrlRewrite::ENTITY_TYPE => + CategoryUrlRewriteGenerator::ENTITY_TYPE, + ] + ); + $this->urlPersist->deleteByData( + [ + UrlRewrite::METADATA => + $this->serializer->serialize(['category_id' => $categoryId]), + UrlRewrite::ENTITY_TYPE => + ProductUrlRewriteGenerator::ENTITY_TYPE, + ] + ); + } + } + + /** + * @param Category $category * @param int $storeId * @param bool $saveRewriteHistory * @param int|null $rootCategoryId * @return array */ private function getCategoryProductsUrlRewrites( - \Magento\Catalog\Model\Category $category, + Category $category, $storeId, $saveRewriteHistory, $rootCategoryId = null ) { $mergeDataProvider = clone $this->mergeDataProviderPrototype; - /** @var \Magento\Catalog\Model\ResourceModel\Product\Collection $productCollection */ + /** @var Collection $productCollection */ $productCollection = $this->productCollectionFactory->create(); $productCollection->addCategoriesFilter(['eq' => [$category->getEntityId()]]) @@ -199,30 +226,65 @@ private function getCategoryProductsUrlRewrites( } /** - * @param \Magento\Catalog\Model\Category $category - * @return void + * Generates product URL rewrites. + * + * @param MergeDataProvider $mergeDataProvider + * @param Category $category + * @param Product $product + * @param int $storeId + * @param $saveRewriteHistory */ - public function deleteCategoryRewritesForChildren(\Magento\Catalog\Model\Category $category) - { - $categoryIds = $this->childrenCategoriesProvider->getChildrenIds($category, true); - $categoryIds[] = $category->getId(); - foreach ($categoryIds as $categoryId) { - $this->urlPersist->deleteByData( - [ - \Magento\UrlRewrite\Service\V1\Data\UrlRewrite::ENTITY_ID => - $categoryId, - \Magento\UrlRewrite\Service\V1\Data\UrlRewrite::ENTITY_TYPE => - \Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator::ENTITY_TYPE, - ] - ); - $this->urlPersist->deleteByData( - [ - \Magento\UrlRewrite\Service\V1\Data\UrlRewrite::METADATA => - $this->serializer->serialize(['category_id' => $categoryId]), - \Magento\UrlRewrite\Service\V1\Data\UrlRewrite::ENTITY_TYPE => - \Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator::ENTITY_TYPE, - ] - ); + private function generateChangedProductUrls( + MergeDataProvider $mergeDataProvider, + Category $category, + int $storeId, + $saveRewriteHistory + ) { + $this->isSkippedProduct[$category->getEntityId()] = $category->getAffectedProductIds(); + + $categoryStoreIds = [$storeId]; + // If category is changed in the Global scope when need to regenerate product URL rewrites for all other scopes. + if ($this->productScopeRewriteGenerator->isGlobalScope($storeId)) { + $categoryStoreIds = $this->getCategoryStoreIds($category); + } + + foreach ($categoryStoreIds as $categoryStoreId) { + /* @var Collection $collection */ + $collection = $this->productCollectionFactory->create() + ->setStoreId($categoryStoreId) + ->addIdFilter($category->getAffectedProductIds()) + ->addAttributeToSelect('visibility') + ->addAttributeToSelect('name') + ->addAttributeToSelect('url_key') + ->addAttributeToSelect('url_path'); + + $collection->setPageSize(1000); + $pageCount = $collection->getLastPageNumber(); + $currentPage = 1; + while ($currentPage <= $pageCount) { + $collection->setCurPage($currentPage); + foreach ($collection as $product) { + $product->setData('save_rewrites_history', $saveRewriteHistory); + $product->setStoreId($categoryStoreId); + $mergeDataProvider->merge( + $this->productUrlRewriteGenerator->generate($product, $category->getEntityId()) + ); + } + $collection->clear(); + $currentPage++; + } } } + + /** + * Gets category store IDs without Global Store. + * + * @param Category $category + * @return array + */ + private function getCategoryStoreIds(Category $category): array + { + $ids = $category->getStoreIds(); + return array_filter($ids); + } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Fixtures/product_custom_url_key.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Fixtures/product_custom_url_key.php new file mode 100644 index 0000000000000..cae23dcc9c674 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Fixtures/product_custom_url_key.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Category; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +require __DIR__ . '/../_files/product_with_category.php'; + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$product->setStoreId(1); +$product->setUrlKey('store-1-key'); +$product = $productRepository->save($product); +$linkManagement->assignProductToCategories($product->getSku(), [Category::TREE_ROOT_ID, $category->getEntityId()]); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Fixtures/product_custom_url_key_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Fixtures/product_custom_url_key_rollback.php new file mode 100644 index 0000000000000..517e9121a6d1e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Fixtures/product_custom_url_key_rollback.php @@ -0,0 +1,21 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +require __DIR__ . '/../_files/product_with_category_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandlerTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandlerTest.php new file mode 100644 index 0000000000000..402db06a0bbc9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandlerTest.php @@ -0,0 +1,111 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogUrlRewrite\Observer; + +use Magento\Catalog\Api\CategoryListInterface; +use Magento\Catalog\Api\Data\CategoryInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; +use PHPUnit\Framework\TestCase; + +/** + * @magentoAppArea adminhtml + */ +class UrlRewriteHandlerTest extends TestCase +{ + /** + * @var UrlRewriteHandler + */ + private $handler; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->handler = $this->objectManager->get(UrlRewriteHandler::class); + } + + /** + * Checks category URLs rewrites generation with enabled `Use Categories Path for Product URLs` option and + * store's specific product URL key. + * + * @magentoDbIsolation disabled + * @magentoDataFixture Magento/CatalogUrlRewrite/Fixtures/product_custom_url_key.php + * @magentoConfigFixture admin_store catalog/seo/product_use_categories 1 + */ + public function testGenerateProductUrlRewrites() + { + $product = $this->getProduct('p002'); + $category = $this->getCategory('category 1'); + // change the category scope to the global + $category->setStoreId(0) + ->setChangedProductIds([$product->getId()]) + ->setAffectedProductIds([$product->getId()]) + ->setAnchorsAbove(false); + + $generatedUrls = $this->handler->generateProductUrlRewrites($category); + $actual = array_values(array_map(function (UrlRewrite $urlRewrite) { + return $urlRewrite->getRequestPath(); + }, $generatedUrls)); + + $expected = [ + 'store-1-key.html', // the Default store + 'cat-1/store-1-key.html', // the Default store with Category URL key + '/store-1-key.html', // an anchor URL the Default store + 'p002.html', // the Secondary store + 'cat-1-2/p002.html', // the Secondary store with Category URL key + '/p002.html', // an anchor URL the Secondary store + ]; + self::assertEquals($expected, $actual, 'Generated URLs rewrites do not match.'); + } + + /** + * Gets category by name. + * + * @param string $name + * @return CategoryInterface + */ + private function getCategory(string $name): CategoryInterface + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('name', $name) + ->create(); + /** @var CategoryListInterface $repository */ + $repository = $this->objectManager->get(CategoryListInterface::class); + $items = $repository->getList($searchCriteria) + ->getItems(); + + return array_pop($items); + } + + /** + * Gets product by SKU. + * + * @param string $sku + * @return ProductInterface + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function getProduct(string $sku): ProductInterface + { + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + return $productRepository->get($sku); + } +} diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_category.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_category.php index 236a80a3e66f9..4c55bf47484b6 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_category.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_category.php @@ -65,7 +65,7 @@ /** @var ProductRepositoryInterface $productRepository */ $productRepository = $objectManager->get(ProductRepositoryInterface::class); -$productRepository->save($product); +$product = $productRepository->save($product); /** @var CategoryLinkManagementInterface $linkManagement */ $linkManagement = $objectManager->get(CategoryLinkManagementInterface::class); From 9144a57c77a9e4edebec773d092ce81b3eb32fbc Mon Sep 17 00:00:00 2001 From: Sergey Shvets <sshvets@magento.com> Date: Thu, 26 Jul 2018 14:11:58 +0300 Subject: [PATCH 135/211] MAGETWO-92036: The error icon does not appear on sections with required attributes that are empty when click on 'save' button --- .../Magento/Ui/view/base/web/js/form/components/fieldset.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js index 1f29cb61b8eb9..6d33386fa1f1c 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/fieldset.js @@ -179,7 +179,7 @@ define([ hasErrors = container.elems.some('error'); if (hasErrors === false && container.hasOwnProperty('_elems')) { - container._elems.each(function (child) { + container._elems.forEach(function (child) { if (hasErrors === false) { hasErrors = self._isChildrenHasErrors(hasErrors, child); From 8e5d9e3eba61db7b61d0c07a7ebfc55cb1f74ab4 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryvinskyi <volodymyr@hryvinskyi.com> Date: Thu, 26 Jul 2018 14:22:47 +0300 Subject: [PATCH 136/211] Update CMS IndexTest --- app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php index 8ff206e8a80fc..06c7a03d57d1d 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php @@ -84,7 +84,9 @@ protected function setUp() 'response' => $responseMock, 'objectManager' => $objectManagerMock, 'request' => $this->requestMock, - 'resultForwardFactory' => $this->forwardFactoryMock + 'resultForwardFactory' => $this->forwardFactoryMock, + 'scopeConfig' => $scopeConfigMock, + 'page' => $this->cmsHelperMock ] ); } From 4929a5bb300ba50b0b7e23a7cc6e7912b169681c Mon Sep 17 00:00:00 2001 From: Volodymyr Hryvinskyi <volodymyr@hryvinskyi.com> Date: Thu, 26 Jul 2018 14:23:50 +0300 Subject: [PATCH 137/211] Update tab to space --- app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php index 06c7a03d57d1d..d8453bc6ecbce 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Index/IndexTest.php @@ -85,8 +85,8 @@ protected function setUp() 'objectManager' => $objectManagerMock, 'request' => $this->requestMock, 'resultForwardFactory' => $this->forwardFactoryMock, - 'scopeConfig' => $scopeConfigMock, - 'page' => $this->cmsHelperMock + 'scopeConfig' => $scopeConfigMock, + 'page' => $this->cmsHelperMock ] ); } From 7b7e731f92119160cfff53e09af046c81b30132f Mon Sep 17 00:00:00 2001 From: Navarr Barnier <navarr@mediotype.com> Date: Thu, 26 Jul 2018 08:15:07 -0400 Subject: [PATCH 138/211] Update test to expect a deferral instead of undefined --- .../js/model/cart/totals-processor/default.test.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js index 44f06279dcbef..100d91566f9dc 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js @@ -112,14 +112,15 @@ define([ 'data_id': 1 }) ); + var deferral = new $.Deferred(); spyOn(mocks['Magento_Checkout/js/model/cart/cache'], 'get'); spyOn(mocks['mage/storage'], 'post').and.callFake(function () { data.shippingMethodCode = mocks['Magento_Checkout/js/model/quote'].shippingMethod()['method_code']; data.shippingCarrierCode = mocks['Magento_Checkout/js/model/quote'].shippingMethod()['carrier_code']; - return new $.Deferred().resolve(result); + return deferral.resolve(result); }); - expect(defaultProcessor.estimateTotals(address)).toBeUndefined(); + expect(defaultProcessor.estimateTotals(address)).toBe(deferral); expect(mocks['Magento_Checkout/js/model/quote'].setTotals).toHaveBeenCalledWith(totals); expect(mocks['Magento_Checkout/js/model/totals'].isLoading.calls.argsFor(0)[0]).toBe(true); expect(mocks['Magento_Checkout/js/model/totals'].isLoading.calls.argsFor(1)[0]).toBe(false); @@ -136,10 +137,11 @@ define([ }) ); spyOn(mocks['Magento_Checkout/js/model/cart/cache'], 'get'); + var deferral = new $.Deferred(); spyOn(mocks['mage/storage'], 'post').and.callFake(function () { - return new $.Deferred().reject('Error Message'); + return deferral.reject('Error Message'); }); - expect(defaultProcessor.estimateTotals(address)).toBeUndefined(); + expect(defaultProcessor.estimateTotals(address)).toBe(deferral); expect(mocks['Magento_Checkout/js/model/totals'].isLoading.calls.argsFor(0)[0]).toBe(true); expect(mocks['Magento_Checkout/js/model/totals'].isLoading.calls.argsFor(1)[0]).toBe(false); expect(mocks['mage/storage'].post).toHaveBeenCalled(); From fdd90fdf69ed4712153ff467c0445e9d1b133587 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Thu, 26 Jul 2018 16:10:56 +0300 Subject: [PATCH 139/211] MAGETWO-75086: Child product image should be shown in Wishist if options are selected for configurable product #8168 --- ...leProductChildImageShouldBeShownOnWishlistTest.xml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontConfigurableProductChildImageShouldBeShownOnWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontConfigurableProductChildImageShouldBeShownOnWishlistTest.xml index ad52766c617b5..a9804e56a56d7 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontConfigurableProductChildImageShouldBeShownOnWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontConfigurableProductChildImageShouldBeShownOnWishlistTest.xml @@ -58,16 +58,15 @@ <actionGroup ref="saveProductForm" stepKey="saveSimpleProduct"/> <!--Sign in as customer --> - <amOnPage url="{{StorefrontCustomerSignInPage.url}}" stepKey="amOnSignInPage"/> - <fillField userInput="$$customer.email$$" selector="{{StorefrontCustomerSignInFormSection.emailField}}" stepKey="fillEmail"/> - <fillField userInput="$$customer.password$$" selector="{{StorefrontCustomerSignInFormSection.passwordField}}" stepKey="fillPassword"/> - <waitForElementVisible selector="{{StorefrontCustomerSignInFormSection.signInAccountButton}}" stepKey="waitForButton"/> - <click selector="{{StorefrontCustomerSignInFormSection.signInAccountButton}}" stepKey="clickSignInAccountButton"/> + <actionGroup ref="CustomerLoginOnStorefront" stepKey="customerLogin"> + <argument name="customer" value="$$customer$$" /> + </actionGroup> <see userInput="$$customer.firstname$$" selector="{{StorefrontCustomerDashboardAccountInformationSection.ContactInformation}}" stepKey="seeFirstName"/> <see userInput="$$customer.lastname$$" selector="{{StorefrontCustomerDashboardAccountInformationSection.ContactInformation}}" stepKey="seeLastName"/> <see userInput="$$customer.email$$" selector="{{StorefrontCustomerDashboardAccountInformationSection.ContactInformation}}" stepKey="seeEmail"/> <waitForPageLoad stepKey="waitForLogin"/> - <amOnPage url="{{_defaultProduct.urlKey}}.html" stepKey="amOnConfigurableProductPage"/> + + <amOnPage url="{{StorefrontProductPage.url(_defaultProduct.urlKey)}}" stepKey="amOnConfigurableProductPage"/> <waitForPageLoad stepKey="wait3"/> <see userInput="{{_defaultProduct.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeProductName"/> <selectOption userInput="{{colorProductAttribute1.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption1"/> From f137a7790b97c9fce5b9874180579b1711247231 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 26 Jul 2018 16:20:32 +0300 Subject: [PATCH 140/211] MAGETWO-73359: CMS Page does not save when same url key with hierarchy for Multi-store --- .../Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml index b35b36bc667a7..06a87a8ee8d23 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminCreateStoreViewActionGroup"> <arguments> - <argument name="StoreGroup" defaultValue="_defaultStoreGroup"/> + <argument name="storeGroup" defaultValue="_defaultStoreGroup"/> <argument name="customStore" defaultValue="customStore"/> </arguments> <amOnPage url="{{AdminSystemStoreViewPage.url}}" stepKey="navigateToNewStoreView"/> From b1af6ad59033e17349d05313e1d0753a7d73154e Mon Sep 17 00:00:00 2001 From: Colin Tickle <colin.tickle@gmail.com> Date: Thu, 26 Jul 2018 10:25:23 +0100 Subject: [PATCH 141/211] Add meta NOINDEX,NOFOLLOW to admin scope to avoid accidental bot crawling. --- .../adminhtml/Magento/backend/Magento_Backend/layout/default.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/layout/default.xml b/app/design/adminhtml/Magento/backend/Magento_Backend/layout/default.xml index da16bde107673..337d63369b160 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/layout/default.xml +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/layout/default.xml @@ -7,6 +7,7 @@ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <head> + <meta name="robots" content="NOINDEX,NOFOLLOW"/> <css src="jquery/jstree/themes/default/style.css"/> <css src="css/styles-old.css"/> <css src="css/styles.css"/> From 8a7ee22caf70817f8b0f34e702792f2406d180dd Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> Date: Thu, 26 Jul 2018 17:43:39 +0300 Subject: [PATCH 142/211] magento/magento2#17122 Added missing exception cause for better error handling Replace tabs with spaces --- app/code/Magento/Translation/Model/Js/DataProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Translation/Model/Js/DataProvider.php b/app/code/Magento/Translation/Model/Js/DataProvider.php index f782d1fdd35b7..a1cfab00b6c8d 100644 --- a/app/code/Magento/Translation/Model/Js/DataProvider.php +++ b/app/code/Magento/Translation/Model/Js/DataProvider.php @@ -115,7 +115,7 @@ public function getData($themePath) } catch (\Exception $e) { throw new LocalizedException( __('Error while translating phrase "%s" in file %s.', $phrase, $filePath[0]), - $e + $e ); } } From 444ad9182a30e9cbe7c58bb8f4ffbecc30afbf65 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 26 Jul 2018 17:53:59 +0300 Subject: [PATCH 143/211] MAGETWO-73359: CMS Page does not save when same url key with hierarchy for Multi-store --- .../Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml index 06a87a8ee8d23..6fdfb2566fee9 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml @@ -16,7 +16,7 @@ <amOnPage url="{{AdminSystemStoreViewPage.url}}" stepKey="navigateToNewStoreView"/> <waitForPageLoad stepKey="waitForPageLoad1" /> <!--Create Store View--> - <selectOption selector="{{AdminNewStoreSection.storeGrpDropdown}}" userInput="{{StoreGroup.name}}" stepKey="selectStore" /> + <selectOption selector="{{AdminNewStoreSection.storeGrpDropdown}}" userInput="{{storeGroup.name}}" stepKey="selectStore" /> <fillField selector="{{AdminNewStoreSection.storeNameTextField}}" userInput="{{customStore.name}}" stepKey="enterStoreViewName" /> <fillField selector="{{AdminNewStoreSection.storeCodeTextField}}" userInput="{{customStore.code}}" stepKey="enterStoreViewCode" /> <selectOption selector="{{AdminNewStoreSection.statusDropdown}}" userInput="Enabled" stepKey="setStatus" /> From fbb8e282fe98f66a845f99f140ae0ffb06449c2f Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <dhorytskyi@magento.com> Date: Thu, 26 Jul 2018 19:09:24 +0300 Subject: [PATCH 144/211] MAGETWO-93345: Wrong totals shown in exported Coupon Report --- .../Block/Adminhtml/Grid/AbstractGrid.php | 18 +- .../Adminhtml/Sales/Coupons/GridTest.php | 163 ++++++++++++++++++ 2 files changed, 171 insertions(+), 10 deletions(-) create mode 100644 app/code/Magento/Reports/Test/Unit/Block/Adminhtml/Sales/Coupons/GridTest.php diff --git a/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php b/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php index 158455db26455..83d8dfa58bf7c 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php @@ -248,7 +248,7 @@ protected function _prepareCollection() $this->getSubTotals(); } - if ($this->getCountTotals()) { + if ($this->getCountTotals() && !$this->getTotals()) { $totalsCollection = $this->_resourceFactory->create( $this->getResourceCollectionName() )->setPeriod( @@ -267,9 +267,8 @@ protected function _prepareCollection() $this->_addOrderStatusFilter($totalsCollection, $filterData); $this->_addCustomFilter($totalsCollection, $filterData); - foreach ($totalsCollection as $item) { - $this->setTotals($item); - break; + if ($totalsCollection->count()) { + $this->setTotals($totalsCollection->getFirstItem()); } } @@ -280,7 +279,7 @@ protected function _prepareCollection() } /** - * @return array + * @return bool */ public function getCountTotals() { @@ -302,17 +301,16 @@ public function getCountTotals() ); $this->_addOrderStatusFilter($totalsCollection, $filterData); + $this->_addCustomFilter($totalsCollection, $filterData); - if ($totalsCollection->load()->getSize() < 1 || !$filterData->getData('from')) { + if (!$totalsCollection->count() || !$filterData->getData('from')) { $this->setTotals(new \Magento\Framework\DataObject()); $this->setCountTotals(false); } else { - foreach ($totalsCollection->getItems() as $item) { - $this->setTotals($item); - break; - } + $this->setTotals($totalsCollection->getFirstItem()); } } + return parent::getCountTotals(); } diff --git a/app/code/Magento/Reports/Test/Unit/Block/Adminhtml/Sales/Coupons/GridTest.php b/app/code/Magento/Reports/Test/Unit/Block/Adminhtml/Sales/Coupons/GridTest.php new file mode 100644 index 0000000000000..293e7c8cf3773 --- /dev/null +++ b/app/code/Magento/Reports/Test/Unit/Block/Adminhtml/Sales/Coupons/GridTest.php @@ -0,0 +1,163 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Reports\Test\Unit\Block\Adminhtml\Sales\Coupons; + +/** + * Test for class \Magento\Reports\Block\Adminhtml\Sales\Coupons\Grid + */ +class GridTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Reports\Block\Adminhtml\Sales\Coupons\Grid + */ + private $model; + + /** + * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $storeManagerMock; + + /** + * @var \Magento\Reports\Model\ResourceModel\Report\Collection\Factory|\PHPUnit_Framework_MockObject_MockObject + */ + private $resourceFactoryMock; + + protected function setUp() + { + $this->storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) + ->getMock(); + $this->resourceFactoryMock = $this + ->getMockBuilder(\Magento\Reports\Model\ResourceModel\Report\Collection\Factory::class) + ->disableOriginalConstructor() + ->getMock(); + $aggregatedColumns = [1 => 'SUM(value)']; + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->model = $objectManager->getObject( + \Magento\Reports\Block\Adminhtml\Sales\Coupons\Grid::class, + [ + '_storeManager' => $this->storeManagerMock, + '_aggregatedColumns' => $aggregatedColumns, + 'resourceFactory' => $this->resourceFactoryMock, + ] + ); + } + + /** + * @dataProvider getCountTotalsDataProvider + * + * @param \Magento\Framework\DataObject $filterData + * @param \PHPUnit_Framework_MockObject_MockObject $collection + * @param bool $expectedCountTotals + */ + public function testGetCountTotals( + \Magento\Framework\DataObject $filterData, + \PHPUnit_Framework_MockObject_MockObject $collection, + bool $expectedCountTotals + ) { + $this->model->setFilterData($filterData); + + $store = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) + ->getMock(); + $this->storeManagerMock->method('getStores') + ->willReturn([1 => $store]); + $this->resourceFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($collection); + + $this->assertEquals($expectedCountTotals, $this->model->getCountTotals()); + } + + /** + * @return array + */ + public function getCountTotalsDataProvider(): array + { + $filterData = new \Magento\Framework\DataObject(); + $filterData->setData('period_type', 'day'); + $filterData->setData('from', '2000-01-01'); + $filterData->setData('to', '2000-01-30'); + $filterData->setData('store_ids', '1'); + $filterData->setData('price_rule_type', 1); + $filterData->setData('rules_list', ['0,1']); + $filterData->setData('order_statuses', 'statuses'); + + $emptyCollectionMock = $this->buildBaseCollectionMock($filterData); + $emptyCollectionMock->expects($this->atLeastOnce()) + ->method('count') + ->willReturn(0); + + $collectionMock = $this->buildBaseCollectionMock($filterData); + $collectionMock->expects($this->atLeastOnce()) + ->method('count') + ->willReturn(1); + $itemMock = $this->getMockBuilder(\Magento\Reports\Model\Item::class) + ->disableOriginalConstructor() + ->getMock(); + $collectionMock->expects($this->atLeastOnce()) + ->method('getFirstItem') + ->willReturn($itemMock); + + return [ + [$filterData, $emptyCollectionMock, false], + [$filterData, $collectionMock, true], + ]; + } + + /** + * @param \Magento\Framework\DataObject $filterData + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function buildBaseCollectionMock( + \Magento\Framework\DataObject $filterData + ): \PHPUnit_Framework_MockObject_MockObject { + $collectionMethods = [ + 'setPeriod', + 'setDateRange', + 'addStoreFilter', + 'setAggregatedColumns', + 'isTotals', + 'addRuleFilter', + 'addOrderStatusFilter', + 'count', + 'getFirstItem', + ]; + $collectionMock = $this + ->getMockBuilder(\Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection::class) + ->disableOriginalConstructor() + ->setMethods($collectionMethods) + ->getMock(); + $collectionMock->expects($this->once()) + ->method('setPeriod') + ->with($filterData->getData('period_type')) + ->willReturnSelf(); + $collectionMock->expects($this->once()) + ->method('setDateRange') + ->with($filterData->getData('from'), $filterData->getData('to')) + ->willReturnSelf(); + $collectionMock->expects($this->once()) + ->method('addStoreFilter') + ->with(\explode(',', $filterData->getData('store_ids'))) + ->willReturnSelf(); + $collectionMock->expects($this->once()) + ->method('setAggregatedColumns') + ->willReturnSelf(); + $collectionMock->expects($this->once()) + ->method('isTotals') + ->with(true) + ->willReturnSelf(); + $collectionMock->expects($this->once()) + ->method('addOrderStatusFilter') + ->with($filterData->getData('order_statuses')) + ->willReturnSelf(); + $collectionMock->expects($this->once()) + ->method('addRuleFilter') + ->with(\explode(',', $filterData->getData('rules_list')[0])) + ->willReturnSelf(); + + return $collectionMock; + } +} From 12afd21cc3077d3e7715ad073bcd5f6dc2a2988c Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <dhorytskyi@magento.com> Date: Thu, 26 Jul 2018 19:29:29 +0300 Subject: [PATCH 145/211] MAGETWO-93345: Wrong totals shown in exported Coupon Report --- app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php b/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php index 83d8dfa58bf7c..6c5f1e51feb5b 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php @@ -279,7 +279,7 @@ protected function _prepareCollection() } /** - * @return bool + * @return array */ public function getCountTotals() { From ce5ac6e0c578900a2e9f92997a635d72c9643919 Mon Sep 17 00:00:00 2001 From: vagrant <eino.keskitalo@vaimo.com> Date: Tue, 19 Jun 2018 21:51:55 +0200 Subject: [PATCH 146/211] Filter test result collection with the cron job code defined in the config fixture. Set the schedule for every minute so that the job is scheduled regardless of the time when you run the test. --- .../Magento/Cron/Observer/ProcessCronQueueObserverTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Cron/Observer/ProcessCronQueueObserverTest.php b/dev/tests/integration/testsuite/Magento/Cron/Observer/ProcessCronQueueObserverTest.php index cdeb67afca471..99be1bcbae379 100644 --- a/dev/tests/integration/testsuite/Magento/Cron/Observer/ProcessCronQueueObserverTest.php +++ b/dev/tests/integration/testsuite/Magento/Cron/Observer/ProcessCronQueueObserverTest.php @@ -27,7 +27,7 @@ protected function setUp() } /** - * @magentoConfigFixture current_store crontab/default/jobs/catalog_product_alert/schedule/cron_expr 8 * * * * + * @magentoConfigFixture current_store crontab/default/jobs/catalog_product_alert/schedule/cron_expr * * * * * */ public function testDispatchScheduled() { @@ -35,6 +35,7 @@ public function testDispatchScheduled() \Magento\Cron\Model\ResourceModel\Schedule\Collection::class ); $collection->addFieldToFilter('status', \Magento\Cron\Model\Schedule::STATUS_PENDING); + $collection->addFieldToFilter('job_code', 'catalog_product_alert'); $this->assertGreaterThan(0, $collection->count(), 'Cron has failed to schedule tasks for itself for future.'); } From 4436ed7dcb30891fd8cb867ab37ed4700f21599b Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Fri, 27 Jul 2018 10:25:40 +0300 Subject: [PATCH 147/211] MAGETWO-92682: Triggers modification --- .../Magento/Framework/Mview/View/Subscription.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/internal/Magento/Framework/Mview/View/Subscription.php b/lib/internal/Magento/Framework/Mview/View/Subscription.php index fd8fb8bf9705d..a9d961c52c675 100644 --- a/lib/internal/Magento/Framework/Mview/View/Subscription.php +++ b/lib/internal/Magento/Framework/Mview/View/Subscription.php @@ -196,6 +196,8 @@ protected function getLinkedViews() */ protected function buildStatement($event, $changelog) { + $tableName = $this->resource->getTableName($this->getTableName()); + switch ($event) { case Trigger::EVENT_INSERT: $trigger = "INSERT IGNORE INTO %s (%s) VALUES (NEW.%s);"; @@ -203,8 +205,8 @@ protected function buildStatement($event, $changelog) case Trigger::EVENT_UPDATE: $trigger = "INSERT IGNORE INTO %s (%s) VALUES (NEW.%s);"; - if ($this->connection->isTableExists($this->resource->getTableName($this->getTableName())) && - $describe = $this->connection->describeTable($this->resource->getTableName($this->getTableName())) + if ($this->connection->isTableExists($tableName) && + $describe = $this->connection->describeTable($tableName) ) { $columnNames = array_column($describe, 'COLUMN_NAME'); $columnNames = array_diff($columnNames, $this->ignoredUpdateColumns); @@ -235,7 +237,7 @@ protected function buildStatement($event, $changelog) return sprintf( $trigger, - $this->connection->quoteIdentifier($this->resource->getTableName($changelog->getName())), + $this->connection->quoteIdentifier($tableName), $this->connection->quoteIdentifier($changelog->getColumnName()), $this->connection->quoteIdentifier($this->getColumnName()) ); From 088d8962268c92e036d7efbde5cccaf25c496a29 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Fri, 27 Jul 2018 11:07:09 +0300 Subject: [PATCH 148/211] MAGETWO-75086: Child product image should be shown in Wishist if options are selected for configurable product #8168 --- .../Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml index e6480fa37870c..84e56e82410ff 100644 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml @@ -25,14 +25,14 @@ <!-- Select First Simple --> <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="clickOnFiltersButton"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clearFilters"/> - <fillField selector="{{AdminProductGridFilterSection.name}}" userInput="{{simpleProductFirst.name}}" stepKey="fillNameFilter"/> + <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{simpleProductFirst.name}}" stepKey="fillNameFilter"/> <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="applyFilters"/> <click selector="{{AdminProductFormBundleSection.firstRowCheckbox}}" stepKey="selectFirstSimple"/> <!-- Select Second Simple --> <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="clickOnFiltersButton2"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clearFilters2"/> - <fillField selector="{{AdminProductGridFilterSection.name}}" userInput="{{simpleProductSecond.name}}" stepKey="fillNameFilter2"/> + <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{simpleProductSecond.name}}" stepKey="fillNameFilter2"/> <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="applyFilters2"/> <click selector="{{AdminProductFormBundleSection.firstRowCheckbox}}" stepKey="selectSecondSimple"/> From 07e1297083c7b989242668e72610c47e03d0b51a Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Fri, 27 Jul 2018 11:42:29 +0300 Subject: [PATCH 149/211] MAGETWO-91166: [2.2.x][L2] - CE-DEV-TPFX tests fails on 2.2-develop --- .../Magento/Sales/Model/Order/AddressRepositoryTest.php | 2 +- .../Magento/UrlRewrite/Block/Catalog/Category/TreeTest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/AddressRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/AddressRepositoryTest.php index 872661a24b7c4..a813ac044ac71 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/AddressRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/AddressRepositoryTest.php @@ -49,7 +49,7 @@ protected function setUp() */ public function testGetListWithMultipleFiltersAndSorting() { - $this->markTestSkipped('To be fixed in MAGETWO-91166'); +// $this->markTestSkipped('To be fixed in MAGETWO-91166'); $filter1 = $this->filterBuilder ->setField('postcode') diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Catalog/Category/TreeTest.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Catalog/Category/TreeTest.php index ba7ed7b8c1c14..f8a4a73b15f05 100644 --- a/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Catalog/Category/TreeTest.php +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Catalog/Category/TreeTest.php @@ -54,7 +54,7 @@ public function testGetTreeArray() */ public function testGetTreeArrayApostropheReplaced() { - $this->markTestSkipped('To be fixed in MAGETWO-91166'); +// $this->markTestSkipped('To be fixed in MAGETWO-91166'); $tree = $this->_treeBlock->getTreeArray(); $this->assertNotContains('\'', $tree['children'][0]['children'][0]['children'][0]['name']); @@ -72,7 +72,7 @@ public function testGetTreeArrayApostropheReplaced() */ public function testGetTreeArrayDoubleQuotesReplaced() { - $this->markTestSkipped('To be fixed in MAGETWO-91166'); +// $this->markTestSkipped('To be fixed in MAGETWO-91166'); $tree = $this->_treeBlock->getTreeArray(); $this->assertNotContains('\"', $tree['children'][0]['children'][0]['children'][0]['name']); From f12dc317a2e754b8659f620fb6dd62fcdf5954ae Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 27 Jul 2018 11:48:53 +0300 Subject: [PATCH 150/211] MAGETWO-86482: Customizable options truncated when displaying ordered product in admin --- .../Catalog/Test/Mftf/Data/ProductData.xml | 2 +- ...roductWithCustomOptionsWithLongValuesTitle.xml | 15 ++++++++------- .../Framework/Filter/TruncateFilterTest.php | 4 ++-- .../Magento/Framework/Filter/TruncateFilter.php | 12 ++++++++---- .../Framework/Filter/TruncateFilter/Result.php | 4 +++- 5 files changed, 22 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index ba543f598e654..405dc48e2f003 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -188,7 +188,7 @@ <requiredEntity type="custom_attribute_array">ApiProductDescription</requiredEntity> <requiredEntity type="custom_attribute_array">ApiProductShortDescription</requiredEntity> </entity> - <entity name="productWithOptions2" type="product"> + <entity name="ProductWithOptions2" type="product"> <var key="sku" entityType="product" entityKey="sku" /> <requiredEntity type="product_option">ProductOptionDropDownWithLongValuesTitle</requiredEntity> </entity> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml index e1ebb613e7d06..d5dd0d8a23806 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml @@ -24,7 +24,7 @@ <requiredEntity createDataKey="createCategory"/> <field key="price">17</field> </createData> - <updateData createDataKey="createProduct" entity="productWithOptions2" stepKey="updateProductWithOptions"/> + <updateData createDataKey="createProduct" entity="ProductWithOptions2" stepKey="updateProductWithOptions"/> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> </before> <after> @@ -33,12 +33,12 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> </after> <!-- Login Customer Storefront --> - <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="CustomerLoginOnStorefront" stepKey="customerLogin"> + <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="amOnProductPage"/> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="amOnProductPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> <seeElement selector="{{StorefrontProductInfoMainSection.productAttributeOptionsDropDown(ProductOptionDropDownWithLongValuesTitle.title, ProductOptionValueDropdownLongTitle1.price)}}" stepKey="checkDropDownProductOption"/> <!-- Adding items to the checkout --> <selectOption userInput="{{ProductOptionValueDropdownLongTitle1.price}}" selector="{{StorefrontProductInfoMainSection.productOptionSelect(ProductOptionDropDownWithLongValuesTitle.title)}}" stepKey="seeProductOptionDropDown"/> @@ -56,13 +56,14 @@ <see selector="{{CheckoutPaymentSection.productOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="{{ProductOptionValueDropdownLongTitle1.title}}" stepKey="seeProductOptionValueDropdown1Input1"/> <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> <!-- Place Order --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectPayment"/> <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/> <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> <!-- Login to Admin and open Order --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnOrdersPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="fillOrderNum"/> <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchOrderNum"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnSearch"/> diff --git a/dev/tests/integration/testsuite/Magento/Framework/Filter/TruncateFilterTest.php b/dev/tests/integration/testsuite/Magento/Framework/Filter/TruncateFilterTest.php index f8bbd66aac123..01fd999d4ab30 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Filter/TruncateFilterTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Filter/TruncateFilterTest.php @@ -58,8 +58,8 @@ public function truncateDataProvider() : array '123 456 789', 8, '..', - false - ] + false, + ], ]; } } diff --git a/lib/internal/Magento/Framework/Filter/TruncateFilter.php b/lib/internal/Magento/Framework/Filter/TruncateFilter.php index 8149b4b7c7a4a..c8667a7d49f26 100644 --- a/lib/internal/Magento/Framework/Filter/TruncateFilter.php +++ b/lib/internal/Magento/Framework/Filter/TruncateFilter.php @@ -9,6 +9,7 @@ use Magento\Framework\Filter\TruncateFilter\Result; use Magento\Framework\Filter\TruncateFilter\ResultFactory; +use Magento\Framework\Stdlib\StringUtils; /** * Truncate filter @@ -34,7 +35,7 @@ class TruncateFilter implements \Zend_Filter_Interface private $breakWords; /** - * @var \Magento\Framework\Stdlib\StringUtils + * @var StringUtils */ private $stringUtils; @@ -44,14 +45,14 @@ class TruncateFilter implements \Zend_Filter_Interface private $resultFactory; /** - * @param \Magento\Framework\Stdlib\StringUtils $stringUtils + * @param StringUtils $stringUtils * @param ResultFactory $resultFactory * @param int $length * @param string $etc * @param bool $breakWords */ public function __construct( - \Magento\Framework\Stdlib\StringUtils $stringUtils, + StringUtils $stringUtils, ResultFactory $resultFactory, int $length = 80, string $etc = '...', @@ -67,7 +68,7 @@ public function __construct( /** * Filter value * - * @param string $string + * @param mixed $string * @return Result */ public function filter($string) : Result @@ -77,6 +78,7 @@ public function filter($string) : Result $length = $this->length; if (0 == $length) { $result->setValue(''); + return $result; } @@ -85,6 +87,7 @@ public function filter($string) : Result $length -= $this->stringUtils->strlen($this->etc); if ($length <= 0) { $result->setValue(''); + return $result; } $preparedString = $string; @@ -99,6 +102,7 @@ public function filter($string) : Result } $result->setRemainder($this->stringUtils->substr($string, $preparedLength, $originalLength)); $result->setValue($this->stringUtils->substr($preparedString, 0, $length) . $this->etc); + return $result; } diff --git a/lib/internal/Magento/Framework/Filter/TruncateFilter/Result.php b/lib/internal/Magento/Framework/Filter/TruncateFilter/Result.php index de31c83110f8a..801ce355f5eb9 100644 --- a/lib/internal/Magento/Framework/Filter/TruncateFilter/Result.php +++ b/lib/internal/Magento/Framework/Filter/TruncateFilter/Result.php @@ -7,6 +7,9 @@ namespace Magento\Framework\Filter\TruncateFilter; +/** + * Resulting class for truncate filter + */ class Result { /** @@ -20,7 +23,6 @@ class Result private $remainder; /** - * Result constructor. * @param string $value * @param string $remainder */ From 166027a9cc505d2467815bcf4c06950b475e3f71 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <dhorytskyi@magento.com> Date: Fri, 27 Jul 2018 11:26:28 +0300 Subject: [PATCH 151/211] MAGETWO-93345: Wrong totals shown in exported Coupon Report --- .../Block/Adminhtml/Grid/AbstractGrid.php | 14 ++- .../Adminhtml/Sales/Coupons/GridTest.php | 102 +++++++++--------- 2 files changed, 62 insertions(+), 54 deletions(-) diff --git a/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php b/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php index 6c5f1e51feb5b..be7dfa70efbb4 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php @@ -248,7 +248,7 @@ protected function _prepareCollection() $this->getSubTotals(); } - if ($this->getCountTotals() && !$this->getTotals()) { + if ($this->getCountTotals()) { $totalsCollection = $this->_resourceFactory->create( $this->getResourceCollectionName() )->setPeriod( @@ -267,8 +267,9 @@ protected function _prepareCollection() $this->_addOrderStatusFilter($totalsCollection, $filterData); $this->_addCustomFilter($totalsCollection, $filterData); - if ($totalsCollection->count()) { - $this->setTotals($totalsCollection->getFirstItem()); + foreach ($totalsCollection as $item) { + $this->setTotals($item); + break; } } @@ -303,11 +304,14 @@ public function getCountTotals() $this->_addOrderStatusFilter($totalsCollection, $filterData); $this->_addCustomFilter($totalsCollection, $filterData); - if (!$totalsCollection->count() || !$filterData->getData('from')) { + if ($totalsCollection->load()->getSize() < 1 || !$filterData->getData('from')) { $this->setTotals(new \Magento\Framework\DataObject()); $this->setCountTotals(false); } else { - $this->setTotals($totalsCollection->getFirstItem()); + foreach ($totalsCollection->getItems() as $item) { + $this->setTotals($item); + break; + } } } diff --git a/app/code/Magento/Reports/Test/Unit/Block/Adminhtml/Sales/Coupons/GridTest.php b/app/code/Magento/Reports/Test/Unit/Block/Adminhtml/Sales/Coupons/GridTest.php index 293e7c8cf3773..9e97af428de90 100644 --- a/app/code/Magento/Reports/Test/Unit/Block/Adminhtml/Sales/Coupons/GridTest.php +++ b/app/code/Magento/Reports/Test/Unit/Block/Adminhtml/Sales/Coupons/GridTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Reports\Test\Unit\Block\Adminhtml\Sales\Coupons; /** @@ -49,24 +51,40 @@ protected function setUp() /** * @dataProvider getCountTotalsDataProvider * - * @param \Magento\Framework\DataObject $filterData - * @param \PHPUnit_Framework_MockObject_MockObject $collection + * @param string $reportType + * @param int $priceRuleType + * @param int $collectionSize * @param bool $expectedCountTotals */ public function testGetCountTotals( - \Magento\Framework\DataObject $filterData, - \PHPUnit_Framework_MockObject_MockObject $collection, + string $reportType, + int $priceRuleType, + int $collectionSize, bool $expectedCountTotals ) { + $filterData = new \Magento\Framework\DataObject(); + $filterData->setData('report_type', $reportType); + $filterData->setData('period_type', 'day'); + $filterData->setData('from', '2000-01-01'); + $filterData->setData('to', '2000-01-30'); + $filterData->setData('store_ids', '1'); + $filterData->setData('price_rule_type', $priceRuleType); + if ($priceRuleType) { + $filterData->setData('rules_list', ['0,1']); + } + $filterData->setData('order_statuses', 'statuses'); $this->model->setFilterData($filterData); + $resourceCollectionName = $this->model->getResourceCollectionName(); + $collectionMock = $this->buildBaseCollectionMock($filterData, $resourceCollectionName, $collectionSize); + $store = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) ->getMock(); $this->storeManagerMock->method('getStores') ->willReturn([1 => $store]); $this->resourceFactoryMock->expects($this->once()) ->method('create') - ->willReturn($collection); + ->willReturn($collectionMock); $this->assertEquals($expectedCountTotals, $this->model->getCountTotals()); } @@ -76,59 +94,27 @@ public function testGetCountTotals( */ public function getCountTotalsDataProvider(): array { - $filterData = new \Magento\Framework\DataObject(); - $filterData->setData('period_type', 'day'); - $filterData->setData('from', '2000-01-01'); - $filterData->setData('to', '2000-01-30'); - $filterData->setData('store_ids', '1'); - $filterData->setData('price_rule_type', 1); - $filterData->setData('rules_list', ['0,1']); - $filterData->setData('order_statuses', 'statuses'); - - $emptyCollectionMock = $this->buildBaseCollectionMock($filterData); - $emptyCollectionMock->expects($this->atLeastOnce()) - ->method('count') - ->willReturn(0); - - $collectionMock = $this->buildBaseCollectionMock($filterData); - $collectionMock->expects($this->atLeastOnce()) - ->method('count') - ->willReturn(1); - $itemMock = $this->getMockBuilder(\Magento\Reports\Model\Item::class) - ->disableOriginalConstructor() - ->getMock(); - $collectionMock->expects($this->atLeastOnce()) - ->method('getFirstItem') - ->willReturn($itemMock); - return [ - [$filterData, $emptyCollectionMock, false], - [$filterData, $collectionMock, true], + ['created_at_shipment', 0, 0, false], + ['created_at_shipment', 0, 1, true], + ['updated_at_order', 0, 1, true], + ['updated_at_order', 1, 1, true], ]; } /** * @param \Magento\Framework\DataObject $filterData + * @param string $resourceCollectionName + * @param int $collectionSize * @return \PHPUnit_Framework_MockObject_MockObject */ private function buildBaseCollectionMock( - \Magento\Framework\DataObject $filterData + \Magento\Framework\DataObject $filterData, + string $resourceCollectionName, + int $collectionSize ): \PHPUnit_Framework_MockObject_MockObject { - $collectionMethods = [ - 'setPeriod', - 'setDateRange', - 'addStoreFilter', - 'setAggregatedColumns', - 'isTotals', - 'addRuleFilter', - 'addOrderStatusFilter', - 'count', - 'getFirstItem', - ]; - $collectionMock = $this - ->getMockBuilder(\Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection::class) + $collectionMock = $this->getMockBuilder($resourceCollectionName) ->disableOriginalConstructor() - ->setMethods($collectionMethods) ->getMock(); $collectionMock->expects($this->once()) ->method('setPeriod') @@ -153,10 +139,28 @@ private function buildBaseCollectionMock( ->method('addOrderStatusFilter') ->with($filterData->getData('order_statuses')) ->willReturnSelf(); + + if ($filterData->getData('price_rule_type')) { + $collectionMock->expects($this->once()) + ->method('addRuleFilter') + ->with(\explode(',', $filterData->getData('rules_list')[0])) + ->willReturnSelf(); + } + $collectionMock->expects($this->once()) - ->method('addRuleFilter') - ->with(\explode(',', $filterData->getData('rules_list')[0])) + ->method('load') ->willReturnSelf(); + $collectionMock->expects($this->once()) + ->method('getSize') + ->willReturn($collectionSize); + if ($collectionSize) { + $itemMock = $this->getMockBuilder(\Magento\Reports\Model\Item::class) + ->disableOriginalConstructor() + ->getMock(); + $collectionMock->expects($this->once()) + ->method('getItems') + ->willReturn([$itemMock]); + } return $collectionMock; } From 8a1fa550d786f7037fd65f37b2b8de5ba0abd587 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Fri, 27 Jul 2018 12:58:49 +0300 Subject: [PATCH 152/211] MAGETWO-91166: [2.2.x][L2] - CE-DEV-TPFX tests fails on 2.2-develop --- .../Magento/Sales/Model/Order/AddressRepositoryTest.php | 2 -- .../Magento/UrlRewrite/Block/Catalog/Category/TreeTest.php | 2 -- 2 files changed, 4 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/AddressRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/AddressRepositoryTest.php index a813ac044ac71..7a38c14685073 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/AddressRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/AddressRepositoryTest.php @@ -49,8 +49,6 @@ protected function setUp() */ public function testGetListWithMultipleFiltersAndSorting() { -// $this->markTestSkipped('To be fixed in MAGETWO-91166'); - $filter1 = $this->filterBuilder ->setField('postcode') ->setConditionType('neq') diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Catalog/Category/TreeTest.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Catalog/Category/TreeTest.php index f8a4a73b15f05..751a91b932a16 100644 --- a/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Catalog/Category/TreeTest.php +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Catalog/Category/TreeTest.php @@ -54,7 +54,6 @@ public function testGetTreeArray() */ public function testGetTreeArrayApostropheReplaced() { -// $this->markTestSkipped('To be fixed in MAGETWO-91166'); $tree = $this->_treeBlock->getTreeArray(); $this->assertNotContains('\'', $tree['children'][0]['children'][0]['children'][0]['name']); @@ -72,7 +71,6 @@ public function testGetTreeArrayApostropheReplaced() */ public function testGetTreeArrayDoubleQuotesReplaced() { -// $this->markTestSkipped('To be fixed in MAGETWO-91166'); $tree = $this->_treeBlock->getTreeArray(); $this->assertNotContains('\"', $tree['children'][0]['children'][0]['children'][0]['name']); From 60cebfa6edcb66fc4f4e5e90f53728e96dcc8b80 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Fri, 27 Jul 2018 13:59:02 +0300 Subject: [PATCH 153/211] MAGETWO-77742: [2.2.x] - Error message when uploading unsupported file format --- .../Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml index e6480fa37870c..84e56e82410ff 100644 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml @@ -25,14 +25,14 @@ <!-- Select First Simple --> <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="clickOnFiltersButton"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clearFilters"/> - <fillField selector="{{AdminProductGridFilterSection.name}}" userInput="{{simpleProductFirst.name}}" stepKey="fillNameFilter"/> + <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{simpleProductFirst.name}}" stepKey="fillNameFilter"/> <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="applyFilters"/> <click selector="{{AdminProductFormBundleSection.firstRowCheckbox}}" stepKey="selectFirstSimple"/> <!-- Select Second Simple --> <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="clickOnFiltersButton2"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clearFilters2"/> - <fillField selector="{{AdminProductGridFilterSection.name}}" userInput="{{simpleProductSecond.name}}" stepKey="fillNameFilter2"/> + <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{simpleProductSecond.name}}" stepKey="fillNameFilter2"/> <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="applyFilters2"/> <click selector="{{AdminProductFormBundleSection.firstRowCheckbox}}" stepKey="selectSecondSimple"/> From 8e11bcedddb9d1137d6a35e632c49a2ca404b170 Mon Sep 17 00:00:00 2001 From: Ievgen Sentiabov <isentiabov@magento.com> Date: Fri, 27 Jul 2018 13:38:42 +0300 Subject: [PATCH 154/211] MAGETWO-84929: Default merchant account ID is used on subsequent partial invoices - Added application isolation level for tests --- .../Customer/Model/CustomerMetadataTest.php | 3 + .../Customer/Model/CustomerRegistryTest.php | 1 + .../Customer/Model/GroupRegistryTest.php | 1 + .../ResourceModel/AddressRepositoryTest.php | 1 + .../ResourceModel/CustomerRepositoryTest.php | 1 + .../ResourceModel/Grid/CollectionTest.php | 1 + .../ResourceModel/GroupRepositoryTest.php | 1 + .../Magento/Customer/Model/VisitorTest.php | 3 + .../Model/Validator/Attribute/BackendTest.php | 3 + .../ExtensionAttribute/JoinProcessorTest.php | 79 ++++++++++--------- 10 files changed, 55 insertions(+), 39 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerMetadataTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerMetadataTest.php index 4f9fa437dcd52..5425af856bd9f 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerMetadataTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerMetadataTest.php @@ -11,6 +11,9 @@ use Magento\Framework\Exception\NoSuchEntityException; use Magento\TestFramework\Helper\CacheCleaner; +/** + * @magentoAppIsolation enabled + */ class CustomerMetadataTest extends \PHPUnit\Framework\TestCase { /** @var CustomerRepositoryInterface */ diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerRegistryTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerRegistryTest.php index 3d469dc653128..167f654162ad8 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerRegistryTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerRegistryTest.php @@ -13,6 +13,7 @@ /** * Test for \Magento\Customer\Model\CustomerRegistry + * @magentoAppIsolation enabled */ class CustomerRegistryTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/GroupRegistryTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/GroupRegistryTest.php index cf5939d0a331d..a9842a2b7226a 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/GroupRegistryTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/GroupRegistryTest.php @@ -8,6 +8,7 @@ /** * Test for \Magento\Customer\Model\GroupRegistry + * @magentoAppIsolation enabled */ class GroupRegistryTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/AddressRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/AddressRepositoryTest.php index 9b66d3e3f452b..2b74a58288600 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/AddressRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/AddressRepositoryTest.php @@ -20,6 +20,7 @@ * @SuppressWarnings(PHPMD.TooManyMethods) * @SuppressWarnings(PHPMD.ExcessivePublicCount) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @magentoAppIsolation enabled */ class AddressRepositoryTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/CustomerRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/CustomerRepositoryTest.php index fe9037e4ffd2b..e777313f13fe8 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/CustomerRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/CustomerRepositoryTest.php @@ -15,6 +15,7 @@ * Checks Customer insert, update, search with repository * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @magentoAppIsolation enabled */ class CustomerRepositoryTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/Grid/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/Grid/CollectionTest.php index 016efd60ce786..b1e394db11674 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/Grid/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/Grid/CollectionTest.php @@ -12,6 +12,7 @@ /** * Customer grid collection tests. + * @magentoAppIsolation enabled */ class CollectionTest extends \Magento\TestFramework\Indexer\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/GroupRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/GroupRepositoryTest.php index 9d86b72ec28be..ae2cc2c9d078c 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/GroupRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/GroupRepositoryTest.php @@ -11,6 +11,7 @@ /** * Integration test for \Magento\Customer\Model\ResourceModel\GroupRepository + * @magentoAppIsolation enabled */ class GroupRepositoryTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/VisitorTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/VisitorTest.php index 0d5a5f262d1a6..55ed0c6321b1b 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/VisitorTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/VisitorTest.php @@ -7,6 +7,9 @@ use Magento\TestFramework\Helper\Bootstrap; +/** + * @magentoAppIsolation enabled + */ class VisitorTest extends \PHPUnit\Framework\TestCase { /** diff --git a/dev/tests/integration/testsuite/Magento/Eav/Model/Validator/Attribute/BackendTest.php b/dev/tests/integration/testsuite/Magento/Eav/Model/Validator/Attribute/BackendTest.php index b3c4323ed67dd..3784c8aae7e98 100644 --- a/dev/tests/integration/testsuite/Magento/Eav/Model/Validator/Attribute/BackendTest.php +++ b/dev/tests/integration/testsuite/Magento/Eav/Model/Validator/Attribute/BackendTest.php @@ -9,6 +9,9 @@ */ namespace Magento\Eav\Model\Validator\Attribute; +/** + * @magentoAppIsolation enabled + */ class BackendTest extends \PHPUnit\Framework\TestCase { /** diff --git a/dev/tests/integration/testsuite/Magento/Framework/Api/ExtensionAttribute/JoinProcessorTest.php b/dev/tests/integration/testsuite/Magento/Framework/Api/ExtensionAttribute/JoinProcessorTest.php index 4b2c59b433afa..6806c2cfd060f 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Api/ExtensionAttribute/JoinProcessorTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Api/ExtensionAttribute/JoinProcessorTest.php @@ -17,6 +17,7 @@ * Class to test the JoinProcessor functionality * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @magentoAppIsolation enabled */ class JoinProcessorTest extends \PHPUnit\Framework\TestCase { @@ -144,55 +145,55 @@ public function testProcess() private function getConfig() { return [\Magento\Catalog\Api\Data\ProductInterface::class => [ - 'review_id' => [ - Converter::DATA_TYPE => 'string', - Converter::RESOURCE_PERMISSIONS => [], - Converter::JOIN_DIRECTIVE => [ - Converter::JOIN_REFERENCE_TABLE => "reviews", - Converter::JOIN_REFERENCE_FIELD => "product_id", - Converter::JOIN_FIELDS => [ - [ - Converter::JOIN_FIELD => "review_id", - Converter::JOIN_FIELD_COLUMN => "db_review_id", - ], + 'review_id' => [ + Converter::DATA_TYPE => 'string', + Converter::RESOURCE_PERMISSIONS => [], + Converter::JOIN_DIRECTIVE => [ + Converter::JOIN_REFERENCE_TABLE => "reviews", + Converter::JOIN_REFERENCE_FIELD => "product_id", + Converter::JOIN_FIELDS => [ + [ + Converter::JOIN_FIELD => "review_id", + Converter::JOIN_FIELD_COLUMN => "db_review_id", ], - Converter::JOIN_ON_FIELD => "id", ], + Converter::JOIN_ON_FIELD => "id", ], - ], \Magento\Customer\Api\Data\CustomerInterface::class => [ - 'library_card_id' => [ - Converter::DATA_TYPE => 'string', - Converter::RESOURCE_PERMISSIONS => [], - Converter::JOIN_DIRECTIVE => [ - Converter::JOIN_REFERENCE_TABLE => "library_account", - Converter::JOIN_FIELDS => [ - [ - Converter::JOIN_FIELD => "library_card_id", - Converter::JOIN_FIELD_COLUMN => "", - ], + ], + ], \Magento\Customer\Api\Data\CustomerInterface::class => [ + 'library_card_id' => [ + Converter::DATA_TYPE => 'string', + Converter::RESOURCE_PERMISSIONS => [], + Converter::JOIN_DIRECTIVE => [ + Converter::JOIN_REFERENCE_TABLE => "library_account", + Converter::JOIN_FIELDS => [ + [ + Converter::JOIN_FIELD => "library_card_id", + Converter::JOIN_FIELD_COLUMN => "", ], - Converter::JOIN_ON_FIELD => "customer_id", ], + Converter::JOIN_ON_FIELD => "customer_id", ], - 'reviews' => [ - Converter::DATA_TYPE => 'Magento\Reviews\Api\Data\Reviews[]', - Converter::RESOURCE_PERMISSIONS => [], - Converter::JOIN_DIRECTIVE => [ - Converter::JOIN_REFERENCE_TABLE => "reviews", - Converter::JOIN_FIELDS => [ - [ - Converter::JOIN_FIELD => "comment", - Converter::JOIN_FIELD_COLUMN => "", - ], - [ - Converter::JOIN_FIELD => "rating", - Converter::JOIN_FIELD_COLUMN => "", - ], + ], + 'reviews' => [ + Converter::DATA_TYPE => 'Magento\Reviews\Api\Data\Reviews[]', + Converter::RESOURCE_PERMISSIONS => [], + Converter::JOIN_DIRECTIVE => [ + Converter::JOIN_REFERENCE_TABLE => "reviews", + Converter::JOIN_FIELDS => [ + [ + Converter::JOIN_FIELD => "comment", + Converter::JOIN_FIELD_COLUMN => "", + ], + [ + Converter::JOIN_FIELD => "rating", + Converter::JOIN_FIELD_COLUMN => "", ], - Converter::JOIN_ON_FIELD => "customer_id", ], + Converter::JOIN_ON_FIELD => "customer_id", ], ], + ], ]; } From 0077903f7dd9a42765ec6cf494608173612de39d Mon Sep 17 00:00:00 2001 From: Navarr Barnier <navarr@mediotype.com> Date: Fri, 27 Jul 2018 08:04:08 -0400 Subject: [PATCH 155/211] Move new variables to top of scope to fall in line with convention --- .../frontend/js/model/cart/totals-processor/default.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js index 100d91566f9dc..039bf91217f35 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js @@ -106,13 +106,13 @@ define([ }); it('estimateTotals if data wasn\'t cached and request was successfully sent', function () { + var deferral = new $.Deferred(); spyOn(mocks['Magento_Checkout/js/model/cart/cache'], 'isChanged').and.returnValue(true); spyOn(mocks['Magento_Customer/js/customer-data'], 'get').and.returnValue( ko.observable({ 'data_id': 1 }) ); - var deferral = new $.Deferred(); spyOn(mocks['Magento_Checkout/js/model/cart/cache'], 'get'); spyOn(mocks['mage/storage'], 'post').and.callFake(function () { data.shippingMethodCode = mocks['Magento_Checkout/js/model/quote'].shippingMethod()['method_code']; @@ -130,6 +130,7 @@ define([ }); it('estimateTotals if data wasn\'t cached and request returns error', function () { + var deferral = new $.Deferred(); spyOn(mocks['Magento_Checkout/js/model/cart/cache'], 'isChanged').and.returnValue(true); spyOn(mocks['Magento_Customer/js/customer-data'], 'get').and.returnValue( ko.observable({ @@ -137,7 +138,6 @@ define([ }) ); spyOn(mocks['Magento_Checkout/js/model/cart/cache'], 'get'); - var deferral = new $.Deferred(); spyOn(mocks['mage/storage'], 'post').and.callFake(function () { return deferral.reject('Error Message'); }); From 667a737718c55ade84191f7613d7f49d5267608b Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Fri, 27 Jul 2018 16:06:19 +0300 Subject: [PATCH 156/211] MAGETWO-92682: Triggers optimization --- lib/internal/Magento/Framework/Mview/View/Subscription.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/internal/Magento/Framework/Mview/View/Subscription.php b/lib/internal/Magento/Framework/Mview/View/Subscription.php index a9d961c52c675..653d68502e384 100644 --- a/lib/internal/Magento/Framework/Mview/View/Subscription.php +++ b/lib/internal/Magento/Framework/Mview/View/Subscription.php @@ -196,14 +196,13 @@ protected function getLinkedViews() */ protected function buildStatement($event, $changelog) { - $tableName = $this->resource->getTableName($this->getTableName()); - switch ($event) { case Trigger::EVENT_INSERT: $trigger = "INSERT IGNORE INTO %s (%s) VALUES (NEW.%s);"; break; case Trigger::EVENT_UPDATE: + $tableName = $this->resource->getTableName($this->getTableName()); $trigger = "INSERT IGNORE INTO %s (%s) VALUES (NEW.%s);"; if ($this->connection->isTableExists($tableName) && $describe = $this->connection->describeTable($tableName) @@ -237,7 +236,7 @@ protected function buildStatement($event, $changelog) return sprintf( $trigger, - $this->connection->quoteIdentifier($tableName), + $this->connection->quoteIdentifier($this->resource->getTableName($changelog->getName())), $this->connection->quoteIdentifier($changelog->getColumnName()), $this->connection->quoteIdentifier($this->getColumnName()) ); From a97acac23f7d6051a4ec1ac7a6b33ecd53cffa14 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Fri, 27 Jul 2018 16:00:55 +0300 Subject: [PATCH 157/211] MAGETWO-88759: Incorrect amount is sent to PayPal when discount is applied to an order - Fixed incorrect shipping discount calculation when taxes are included in shipping price - Fixed incorrect BaseShippingDiscountTaxCompensationAmount calculation for PayPal - Total amounts sent to PayPal were rounded for 2 signs --- .../Magento/Paypal/Model/Api/AbstractApi.php | 1 + app/code/Magento/Paypal/Model/Cart.php | 2 +- .../Magento/Paypal/Model/Express/Checkout.php | 3 +- .../Paypal/Test/Unit/Model/CartTest.php | 12 +- .../Tax/Model/Sales/Total/Quote/Shipping.php | 13 +- .../Tax/Model/Sales/Total/Quote/SetupUtil.php | 1 + ...including_tax_apply_tax_after_discount.php | 131 ++++++++++++++++++ .../tax_calculation_data_aggregated.php | 1 + 8 files changed, 154 insertions(+), 10 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_apply_tax_after_discount.php diff --git a/app/code/Magento/Paypal/Model/Api/AbstractApi.php b/app/code/Magento/Paypal/Model/Api/AbstractApi.php index 0d1cd44639e93..89f83790263b8 100644 --- a/app/code/Magento/Paypal/Model/Api/AbstractApi.php +++ b/app/code/Magento/Paypal/Model/Api/AbstractApi.php @@ -427,6 +427,7 @@ protected function _exportLineItems(array &$request, $i = 0) if (isset($this->_lineItemTotalExportMap[$key])) { // !empty($total) $privateKey = $this->_lineItemTotalExportMap[$key]; + $total = round($total, 2); $request[$privateKey] = $this->formatPrice($total); } } diff --git a/app/code/Magento/Paypal/Model/Cart.php b/app/code/Magento/Paypal/Model/Cart.php index c8e4a6acf4649..b65fcd340d535 100644 --- a/app/code/Magento/Paypal/Model/Cart.php +++ b/app/code/Magento/Paypal/Model/Cart.php @@ -177,7 +177,7 @@ protected function _applyDiscountTaxCompensationWorkaround( ) { $dataContainer = $salesEntity->getTaxContainer(); $this->addTax((double)$dataContainer->getBaseDiscountTaxCompensationAmount()); - $this->addTax((double)$dataContainer->getBaseShippingDiscountTaxCompensationAmnt()); + $this->addTax((double)$dataContainer->getBaseShippingDiscountTaxCompensationAmount()); } /** diff --git a/app/code/Magento/Paypal/Model/Express/Checkout.php b/app/code/Magento/Paypal/Model/Express/Checkout.php index 9c9b4dc3e87a7..3d1f5159933f8 100644 --- a/app/code/Magento/Paypal/Model/Express/Checkout.php +++ b/app/code/Magento/Paypal/Model/Express/Checkout.php @@ -498,7 +498,8 @@ public function start($returnUrl, $cancelUrl, $button = null) $solutionType = $this->_config->getMerchantCountry() == 'DE' ? \Magento\Paypal\Model\Config::EC_SOLUTION_TYPE_MARK : $this->_config->getValue('solutionType'); - $this->_getApi()->setAmount($this->_quote->getBaseGrandTotal()) + $totalAmount = round($this->_quote->getBaseGrandTotal(), 2); + $this->_getApi()->setAmount($totalAmount) ->setCurrencyCode($this->_quote->getBaseCurrencyCode()) ->setInvNum($this->_quote->getReservedOrderId()) ->setReturnUrl($returnUrl) diff --git a/app/code/Magento/Paypal/Test/Unit/Model/CartTest.php b/app/code/Magento/Paypal/Test/Unit/Model/CartTest.php index 0f787f0f513a1..28837727533d2 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/CartTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/CartTest.php @@ -70,7 +70,7 @@ protected function setUp() public function testInvalidGetAllItems($items) { $taxContainer = new \Magento\Framework\DataObject( - ['base_discount_tax_compensation_amount' => 0.2, 'base_shipping_discount_tax_compensation_amnt' => 0.1] + ['base_discount_tax_compensation_amount' => 0.2, 'base_shipping_discount_tax_compensation_amount' => 0.1] ); $this->_salesModel->expects($this->once())->method('getTaxContainer')->will($this->returnValue($taxContainer)); $this->_salesModel->expects($this->once())->method('getAllItems')->will($this->returnValue($items)); @@ -146,7 +146,7 @@ public function testInvalidTotalsGetAllItems($values, $transferDiscount) $this->assertEquals( $values['base_tax_amount'] + $values['base_discount_tax_compensation_amount'] + - $values['base_shipping_discount_tax_compensation_amnt'], + $values['base_shipping_discount_tax_compensation_amount'], $this->_model->getTax() ); $this->assertEquals($values['base_shipping_amount'], $this->_model->getShipping()); @@ -162,7 +162,7 @@ public function invalidTotalsGetAllItemsDataProvider() [ [ 'base_discount_tax_compensation_amount' => 0, - 'base_shipping_discount_tax_compensation_amnt' => 0, + 'base_shipping_discount_tax_compensation_amount' => 0, 'base_subtotal' => 0, 'base_tax_amount' => 0, 'base_shipping_amount' => 0, @@ -174,7 +174,7 @@ public function invalidTotalsGetAllItemsDataProvider() [ [ 'base_discount_tax_compensation_amount' => 1, - 'base_shipping_discount_tax_compensation_amnt' => 2, + 'base_shipping_discount_tax_compensation_amount' => 2, 'base_subtotal' => 3, 'base_tax_amount' => 4, 'base_shipping_amount' => 5, @@ -255,8 +255,8 @@ protected function _prepareInvalidModelData($values, $transferDiscount) [ 'base_discount_tax_compensation_amount' => $values['base_discount_tax_compensation_amount'], - 'base_shipping_discount_tax_compensation_amnt' => - $values['base_shipping_discount_tax_compensation_amnt'], + 'base_shipping_discount_tax_compensation_amount' => + $values['base_shipping_discount_tax_compensation_amount'], ] ); $expectedSubtotal = $values['base_subtotal']; diff --git a/app/code/Magento/Tax/Model/Sales/Total/Quote/Shipping.php b/app/code/Magento/Tax/Model/Sales/Total/Quote/Shipping.php index 16a55dbfac3e2..6e38e29decae5 100644 --- a/app/code/Magento/Tax/Model/Sales/Total/Quote/Shipping.php +++ b/app/code/Magento/Tax/Model/Sales/Total/Quote/Shipping.php @@ -39,16 +39,23 @@ public function collect( $quoteDetails = $this->prepareQuoteDetails($shippingAssignment, [$shippingDataObject]); $taxDetails = $this->taxCalculationService ->calculateTax($quoteDetails, $storeId); + $taxDetailsItems = $taxDetails->getItems()[self::ITEM_CODE_SHIPPING]; $baseQuoteDetails = $this->prepareQuoteDetails($shippingAssignment, [$baseShippingDataObject]); $baseTaxDetails = $this->taxCalculationService ->calculateTax($baseQuoteDetails, $storeId); + $baseTaxDetailsItems = $baseTaxDetails->getItems()[self::ITEM_CODE_SHIPPING]; + + $quote->getShippingAddress() + ->setShippingAmount($taxDetailsItems->getRowTotal()); + $quote->getShippingAddress() + ->setBaseShippingAmount($baseTaxDetailsItems->getRowTotal()); $this->processShippingTaxInfo( $shippingAssignment, $total, - $taxDetails->getItems()[self::ITEM_CODE_SHIPPING], - $baseTaxDetails->getItems()[self::ITEM_CODE_SHIPPING] + $taxDetailsItems, + $baseTaxDetailsItems ); return $this; @@ -58,6 +65,8 @@ public function collect( * @param \Magento\Quote\Model\Quote $quote * @param Address\Total $total * @return array|null + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function fetch(\Magento\Quote\Model\Quote $quote, \Magento\Quote\Model\Quote\Address\Total $total) { diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SetupUtil.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SetupUtil.php index 54734e5a01c1b..bd6c900cf203c 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SetupUtil.php +++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SetupUtil.php @@ -143,6 +143,7 @@ class SetupUtil 'discount_amount' => 40, 'discount_step' => 0, 'stop_rules_processing' => 1, + 'apply_to_shipping' => 0, 'website_ids' => [1], ]; diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_apply_tax_after_discount.php b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_apply_tax_after_discount.php new file mode 100644 index 0000000000000..a341344ddbb95 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_apply_tax_after_discount.php @@ -0,0 +1,131 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Tax\Model\Calculation; +use Magento\Tax\Model\Config; +use Magento\Tax\Model\Sales\Total\Quote\SetupUtil; + +$taxCalculationData['including_tax_apply_tax_after_discount'] = [ + 'config_data' => [ + SetupUtil::CONFIG_OVERRIDES => [ + Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 1, + Config::CONFIG_XML_PATH_PRICE_INCLUDES_TAX => 1, + Config::CONFIG_XML_PATH_SHIPPING_INCLUDES_TAX => 1, + Config::CONFIG_XML_PATH_SHIPPING_TAX_CLASS => SetupUtil::SHIPPING_TAX_CLASS, + Config::XML_PATH_ALGORITHM => Calculation::CALC_ROW_BASE, + ], + SetupUtil::TAX_RATE_OVERRIDES => [ + SetupUtil::TAX_RATE_TX => 10, + SetupUtil::TAX_STORE_RATE => 10, + SetupUtil::TAX_RATE_SHIPPING => 10 + ], + SetupUtil::TAX_RULE_OVERRIDES => [ + [ + //tax rule for product + 'code' => 'Product Tax Rule', + 'product_tax_class_ids' => [SetupUtil::PRODUCT_TAX_CLASS_1], + ], + [ + //tax rule for shipping + 'code' => 'Shipping Tax Rule', + 'product_tax_class_ids' => [SetupUtil::SHIPPING_TAX_CLASS], + 'tax_rate_ids' => [SetupUtil::TAX_RATE_SHIPPING], + ], + ], + ], + 'quote_data' => [ + 'billing_address' => [ + 'region_id' => SetupUtil::REGION_TX, + ], + 'shipping_address' => [ + 'region_id' => SetupUtil::REGION_TX, + ], + 'items' => [ + [ + 'sku' => 'simple1', + 'price' => 29, + 'qty' => 1, + ], + ], + 'shipping_method' => 'flatrate_flatrate', + 'shopping_cart_rules' => [ + [ + 'discount_amount' => 50, + 'apply_to_shipping' => 1, + ], + ], + ], + 'expected_results' => [ + 'address_data' => [ + 'subtotal' => 26.36, + 'base_subtotal' => 26.36, + 'subtotal_incl_tax' => 29, + 'base_subtotal_incl_tax' => 29, + 'tax_amount' => 1.69, + 'base_tax_amount' => 1.69, + 'shipping_amount' => 4.55, + 'base_shipping_amount' => 4.55, + 'shipping_incl_tax' => 5, + 'base_shipping_incl_tax' => 5, + 'shipping_tax_amount' => 0.25, + 'base_shipping_tax_amount' => 0.25, + 'discount_amount' => -15.455, + 'base_discount_amount' => -15.455, + 'discount_tax_compensation_amount' => 1.2, + 'base_discount_tax_compensation_amount' => 1.2, + 'shipping_discount_tax_compensation_amount' => 0.2, + 'base_shipping_discount_tax_compensation_amount' => 0.2, + 'grand_total' => 18.545, + 'base_grand_total' => 18.545, + 'applied_taxes' => [ + SetupUtil::TAX_RATE_TX => [ + 'percent' => 10, + 'amount' => 1.44, + 'base_amount' => 1.44, + 'rates' => [ + [ + 'code' => SetupUtil::TAX_RATE_TX, + 'title' => SetupUtil::TAX_RATE_TX, + 'percent' => 10, + ], + ], + ], + SetupUtil::TAX_RATE_SHIPPING => [ + 'percent' => 10, + 'amount' => 0.25, + 'base_amount' => 0.25, + 'rates' => [ + [ + 'code' => SetupUtil::TAX_RATE_SHIPPING, + 'title' => SetupUtil::TAX_RATE_SHIPPING, + 'percent' => 10, + ], + ], + ], + ], + ], + 'items_data' => [ + 'simple1' => [ + 'row_total' => 26.36, + 'base_row_total' => 26.36, + 'tax_percent' => 10, + 'price' => 26.36, + 'base_price' => 26.36, + 'price_incl_tax' => 29, + 'base_price_incl_tax' => 29, + 'row_total_incl_tax' => 29, + 'base_row_total_incl_tax' => 29, + 'tax_amount' => 1.44, + 'base_tax_amount' => 1.44, + 'discount_amount' => 13.18, + 'base_discount_amount' => 13.18, + 'discount_percent' => 50, + 'discount_tax_compensation_amount' => 1.2, + 'base_discount_tax_compensation_amount' => 1.2, + ], + ], + ], +]; diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/tax_calculation_data_aggregated.php b/dev/tests/integration/testsuite/Magento/Tax/_files/tax_calculation_data_aggregated.php index c47d348cedda5..f22b48a259685 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/_files/tax_calculation_data_aggregated.php +++ b/dev/tests/integration/testsuite/Magento/Tax/_files/tax_calculation_data_aggregated.php @@ -30,3 +30,4 @@ require_once __DIR__ . '/scenarios/multi_tax_rule_total_calculate_subtotal_yes.php'; require_once __DIR__ . '/scenarios/multi_tax_rule_two_row_calculate_subtotal_yes_row.php'; require_once __DIR__ . '/scenarios/multi_tax_rule_two_row_calculate_subtotal_yes_total.php'; +require_once __DIR__ . '/scenarios/including_tax_apply_tax_after_discount.php'; From a117cd46332cfce75e2eace6c1f7a3f2bfc95783 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Fri, 27 Jul 2018 16:42:10 +0300 Subject: [PATCH 158/211] MAGETWO-75086: Child product image should be shown in Wishist if options are selected for configurable product #8168 --- .../Mftf/ActionGroup/AdminProductGridActionGroup.xml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml index 388727202230c..57133f39217ca 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml @@ -29,18 +29,6 @@ <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad" time="30"/> </actionGroup> - <!--Filter the product grid by the SKU string --> - <actionGroup name="filterProductGridBySku2"> - <arguments> - <argument name="sku" type="string"/> - </arguments> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.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"/> - <waitForLoadingMaskToDisappear stepKey="waitForFilteredGridLoad"/> - </actionGroup> - <!--Delete a product by filtering grid and using delete action--> <actionGroup name="deleteProductUsingProductGrid"> <arguments> From 7d4721dd38d832b072c2f77d74da3a23a53fa675 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Fri, 27 Jul 2018 16:43:51 +0300 Subject: [PATCH 159/211] MAGETWO-73342: Clicking on area around the label of a toggle element results in the element's state being changed --- .../AdminProductCustomizableOptionsSection.xml | 2 +- .../Test/Mftf/Section/AdminProductFormSection.xml | 4 +++- .../Ui/view/base/web/templates/form/field.html | 6 +++--- .../Magento/backend/web/css/source/forms/_fields.less | 11 +++++++---- .../Test/Block/Adminhtml/Product/ProductForm.php | 2 +- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml index 6c179f499b910..287e85310ce7d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml @@ -14,7 +14,7 @@ <element name="useDefaultOptionTitle" type="text" selector="[data-index='options'] tr.data-row [data-index='title'] [name^='options_use_default']"/> <element name="useDefaultOptionValueTitleByIndex" type="text" selector="[data-index='options'] [data-index='values'] tr[data-repeat-index='{{var1}}'] [name^='options_use_default']" parameterized="true"/> <element name="addOptionBtn" type="button" selector="button[data-index='button_add']"/> - <element name="fillOptionTitle" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//span[text()='Option Title']/parent::label/parent::div//input[@class='admin__control-text']" parameterized="true"/> + <element name="fillOptionTitle" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//label[text()='Option Title']/parent::span/parent::div//input[@class='admin__control-text']" parameterized="true"/> <element name="checkSelect" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//span[text()='Option Type']/parent::label/parent::div//div[@data-role='selected-option']" parameterized="true"/> <element name="checkDropDown" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//parent::label/parent::div//li[@class='admin__action-multiselect-menu-inner-item']//label[text()='Drop-down']" parameterized="true"/> <element name="clickAddValue" type="button" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tfoot//button" parameterized="true"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml index 61a57914bc195..4f9ff872d6eeb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml @@ -15,6 +15,8 @@ <element name="productName" type="input" selector=".admin__field[data-index=name] input"/> <element name="productNameUseDefault" type="checkbox" selector="input[name='use_default[name]']"/> <element name="productSku" type="input" selector=".admin__field[data-index=sku] input"/> + <element name="enableProductAttributeLabel" type="text" selector="//label[text()='Enable Product']"/> + <element name="enableProductAttributeLabelWrapper" type="text" selector="//label[text()='Enable Product']/parent::span"/> <element name="productStatus" type="checkbox" selector="input[name='product[status]']"/> <element name="productStatusUseDefault" type="checkbox" selector="input[name='use_default[status]']"/> <element name="productPrice" type="input" selector=".admin__field[data-index=price] input"/> @@ -31,7 +33,7 @@ <element name="visibilityUseDefault" type="checkbox" selector="//input[@name='use_default[visibility]']"/> <element name="divByDataIndex" type="input" selector="div[data-index='{{var}}']" parameterized="true"/> <element name="attributeSetSearchCount" type="text" selector="div[data-index='attribute_set_id'] .admin__action-multiselect-search-count"/> - <element name="attributeLabelByText" type="text" selector="//*[@class='admin__field']//span[text()='{{attributeLabel}}']" parameterized="true"/> + <element name="attributeLabelByText" type="text" selector="//*[@class='admin__field']//label[text()='{{attributeLabel}}']" parameterized="true"/> <element name="addAttributeBtn" type="button" selector="#addAttribute"/> </section> <section name="ProductInWebsitesSection"> diff --git a/app/code/Magento/Ui/view/base/web/templates/form/field.html b/app/code/Magento/Ui/view/base/web/templates/form/field.html index 888bc911d747f..144825967401e 100644 --- a/app/code/Magento/Ui/view/base/web/templates/form/field.html +++ b/app/code/Magento/Ui/view/base/web/templates/form/field.html @@ -8,9 +8,9 @@ visible="visible" css="$data.additionalClasses" attr="'data-index': index"> - <label class="admin__field-label" if="$data.label" visible="$data.labelVisible" attr="for: uid"> - <span translate="label" attr="'data-config-scope': $data.scopeLabel"/> - </label> + <span class="admin__field-label" if="$data.label" visible="$data.labelVisible"> + <label translate="label" attr="'data-config-scope': $data.scopeLabel, for: uid"/> + </span> <div class="admin__field-control" css="'_with-tooltip': $data.tooltip, '_with-reset': $data.showFallbackReset && $data.isDifferedFromDefault"> <render args="elementTmpl" ifnot="hasAddons()"/> diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less index 59675de698787..8f0d4c33a8ee7 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less @@ -183,10 +183,13 @@ .admin__field-label { color: @field-label__color; - cursor: pointer; margin: 0; text-align: right; + label { + cursor: pointer; + } + + br { display: none; } @@ -207,7 +210,7 @@ overflow: hidden; } - span { + label { display: inline-block; line-height: @field-label__line-height; vertical-align: middle; @@ -512,7 +515,7 @@ position: absolute; top: 0; - span { + label { &:before { display: block; } @@ -527,7 +530,7 @@ } & > .admin__field-label { - span { + label { &:before { display: none; } diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/ProductForm.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/ProductForm.php index 9331c2c987549..61142adc8f9b3 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/ProductForm.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/ProductForm.php @@ -28,7 +28,7 @@ class ProductForm extends FormSections * * @var string */ - protected $attribute = './/*[contains(@class,"label")]/span[text()="%s"]'; + protected $attribute = './/*[contains(@class,"label")]/label[text()="%s"]'; /** * Product new from date field on the product form From e54a63ee09045c4b1fe2540b6b4092eaaeb82a07 Mon Sep 17 00:00:00 2001 From: Jose Ortega <joc.hhop@gmail.com> Date: Wed, 25 Jul 2018 14:27:50 +0200 Subject: [PATCH 160/211] Refactored multiples conditions which could be grouped in a single one in captcha observers --- .../Observer/CheckGuestCheckoutObserver.php | 17 +++++++------- .../CheckRegisterCheckoutObserver.php | 17 +++++++------- ...CheckUserForgotPasswordBackendObserver.php | 23 +++++++++---------- .../CheckUserLoginBackendObserver.php | 10 ++++---- 4 files changed, 32 insertions(+), 35 deletions(-) diff --git a/app/code/Magento/Captcha/Observer/CheckGuestCheckoutObserver.php b/app/code/Magento/Captcha/Observer/CheckGuestCheckoutObserver.php index 40c215ec218a1..7ccaa76b6c7c8 100644 --- a/app/code/Magento/Captcha/Observer/CheckGuestCheckoutObserver.php +++ b/app/code/Magento/Captcha/Observer/CheckGuestCheckoutObserver.php @@ -66,15 +66,14 @@ public function execute(\Magento\Framework\Event\Observer $observer) $formId = 'guest_checkout'; $captchaModel = $this->_helper->getCaptcha($formId); $checkoutMethod = $this->_typeOnepage->getQuote()->getCheckoutMethod(); - if ($checkoutMethod == \Magento\Checkout\Model\Type\Onepage::METHOD_GUEST) { - if ($captchaModel->isRequired()) { - $controller = $observer->getControllerAction(); - if (!$captchaModel->isCorrect($this->captchaStringResolver->resolve($controller->getRequest(), $formId)) - ) { - $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); - $result = ['error' => 1, 'message' => __('Incorrect CAPTCHA')]; - $controller->getResponse()->representJson($this->jsonHelper->jsonEncode($result)); - } + if ($checkoutMethod == \Magento\Checkout\Model\Type\Onepage::METHOD_GUEST + && $captchaModel->isRequired() + ) { + $controller = $observer->getControllerAction(); + if (!$captchaModel->isCorrect($this->captchaStringResolver->resolve($controller->getRequest(), $formId))) { + $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); + $result = ['error' => 1, 'message' => __('Incorrect CAPTCHA')]; + $controller->getResponse()->representJson($this->jsonHelper->jsonEncode($result)); } } diff --git a/app/code/Magento/Captcha/Observer/CheckRegisterCheckoutObserver.php b/app/code/Magento/Captcha/Observer/CheckRegisterCheckoutObserver.php index 3bf2ac38debee..8e110a9f4653d 100644 --- a/app/code/Magento/Captcha/Observer/CheckRegisterCheckoutObserver.php +++ b/app/code/Magento/Captcha/Observer/CheckRegisterCheckoutObserver.php @@ -66,15 +66,14 @@ public function execute(\Magento\Framework\Event\Observer $observer) $formId = 'register_during_checkout'; $captchaModel = $this->_helper->getCaptcha($formId); $checkoutMethod = $this->_typeOnepage->getQuote()->getCheckoutMethod(); - if ($checkoutMethod == \Magento\Checkout\Model\Type\Onepage::METHOD_REGISTER) { - if ($captchaModel->isRequired()) { - $controller = $observer->getControllerAction(); - if (!$captchaModel->isCorrect($this->captchaStringResolver->resolve($controller->getRequest(), $formId)) - ) { - $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); - $result = ['error' => 1, 'message' => __('Incorrect CAPTCHA')]; - $controller->getResponse()->representJson($this->jsonHelper->jsonEncode($result)); - } + if ($checkoutMethod == \Magento\Checkout\Model\Type\Onepage::METHOD_REGISTER + && $captchaModel->isRequired() + ) { + $controller = $observer->getControllerAction(); + if (!$captchaModel->isCorrect($this->captchaStringResolver->resolve($controller->getRequest(), $formId))) { + $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); + $result = ['error' => 1, 'message' => __('Incorrect CAPTCHA')]; + $controller->getResponse()->representJson($this->jsonHelper->jsonEncode($result)); } } diff --git a/app/code/Magento/Captcha/Observer/CheckUserForgotPasswordBackendObserver.php b/app/code/Magento/Captcha/Observer/CheckUserForgotPasswordBackendObserver.php index 402fc028c5ad0..2de93dcf6b59b 100644 --- a/app/code/Magento/Captcha/Observer/CheckUserForgotPasswordBackendObserver.php +++ b/app/code/Magento/Captcha/Observer/CheckUserForgotPasswordBackendObserver.php @@ -69,18 +69,17 @@ public function execute(\Magento\Framework\Event\Observer $observer) $controller = $observer->getControllerAction(); $email = (string)$observer->getControllerAction()->getRequest()->getParam('email'); $params = $observer->getControllerAction()->getRequest()->getParams(); - if (!empty($email) && !empty($params)) { - if ($captchaModel->isRequired()) { - if (!$captchaModel->isCorrect($this->captchaStringResolver->resolve($controller->getRequest(), $formId)) - ) { - $this->_session->setEmail((string)$controller->getRequest()->getPost('email')); - $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); - $this->messageManager->addError(__('Incorrect CAPTCHA')); - $controller->getResponse()->setRedirect( - $controller->getUrl('*/*/forgotpassword', ['_nosecret' => true]) - ); - } - } + if (!empty($email) + && !empty($params) + && $captchaModel->isRequired() + && !$captchaModel->isCorrect($this->captchaStringResolver->resolve($controller->getRequest(), $formId)) + ) { + $this->_session->setEmail((string)$controller->getRequest()->getPost('email')); + $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); + $this->messageManager->addError(__('Incorrect CAPTCHA')); + $controller->getResponse()->setRedirect( + $controller->getUrl('*/*/forgotpassword', ['_nosecret' => true]) + ); } return $this; diff --git a/app/code/Magento/Captcha/Observer/CheckUserLoginBackendObserver.php b/app/code/Magento/Captcha/Observer/CheckUserLoginBackendObserver.php index 8cc907d7bd12b..924514cd48c5d 100644 --- a/app/code/Magento/Captcha/Observer/CheckUserLoginBackendObserver.php +++ b/app/code/Magento/Captcha/Observer/CheckUserLoginBackendObserver.php @@ -52,11 +52,11 @@ public function execute(\Magento\Framework\Event\Observer $observer) $formId = 'backend_login'; $captchaModel = $this->_helper->getCaptcha($formId); $login = $observer->getEvent()->getUsername(); - if ($captchaModel->isRequired($login)) { - if (!$captchaModel->isCorrect($this->captchaStringResolver->resolve($this->_request, $formId))) { - $captchaModel->logAttempt($login); - throw new PluginAuthenticationException(__('Incorrect CAPTCHA.')); - } + if ($captchaModel->isRequired($login) + && !$captchaModel->isCorrect($this->captchaStringResolver->resolve($this->_request, $formId)) + ) { + $captchaModel->logAttempt($login); + throw new PluginAuthenticationException(__('Incorrect CAPTCHA.')); } $captchaModel->logAttempt($login); From bae58927d9e5ff2ef0bcd2f07aa838401409e6c7 Mon Sep 17 00:00:00 2001 From: Rodrigo Biassi <rodrigobiassi.net@gmail.com> Date: Fri, 20 Jul 2018 19:41:35 -0300 Subject: [PATCH 161/211] Broken Responsive Layout on Top --- .../luma/Magento_Theme/web/css/source/_module.less | 11 +++++++++++ 1 file changed, 11 insertions(+) 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 edb096360b5e0..dee6f8e09fc76 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 @@ -435,6 +435,17 @@ } } +// +// Mobile +// _____________________________________________ + +.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { + .cms-page-view .page-main { + padding-top: 41px; + position: relative; + } +} + // // Desktop // _____________________________________________ From bbd33fcfb5dacf076d118d8f285919aa2cf63e34 Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Sat, 28 Jul 2018 10:47:01 -0300 Subject: [PATCH 162/211] Replaced deprecated methods. --- .../Controller/Adminhtml/Promo/Catalog/ApplyRules.php | 6 +++--- .../Controller/Adminhtml/Promo/Catalog/Delete.php | 8 ++++---- .../Controller/Adminhtml/Promo/Catalog/Edit.php | 2 +- .../Controller/Adminhtml/Promo/Catalog/Save.php | 8 ++++---- .../CatalogRule/Plugin/Indexer/Product/Attribute.php | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/ApplyRules.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/ApplyRules.php index 85ad74f7bbfe2..4badfa1219e10 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/ApplyRules.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/ApplyRules.php @@ -25,14 +25,14 @@ public function execute() $ruleJob->applyAll(); if ($ruleJob->hasSuccess()) { - $this->messageManager->addSuccess($ruleJob->getSuccess()); + $this->messageManager->addSuccessMessage($ruleJob->getSuccess()); $this->_objectManager->create(\Magento\CatalogRule\Model\Flag::class)->loadSelf()->setState(0)->save(); } elseif ($ruleJob->hasError()) { - $this->messageManager->addError($errorMessage . ' ' . $ruleJob->getError()); + $this->messageManager->addErrorMessage($errorMessage . ' ' . $ruleJob->getError()); } } catch (\Exception $e) { $this->_objectManager->create(\Psr\Log\LoggerInterface::class)->critical($e); - $this->messageManager->addError($errorMessage); + $this->messageManager->addErrorMessage($errorMessage); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Delete.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Delete.php index 8b007031f3305..3500506d8d6c5 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Delete.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Delete.php @@ -25,13 +25,13 @@ public function execute() $ruleRepository->deleteById($id); $this->_objectManager->create(\Magento\CatalogRule\Model\Flag::class)->loadSelf()->setState(1)->save(); - $this->messageManager->addSuccess(__('You deleted the rule.')); + $this->messageManager->addSuccessMessage(__('You deleted the rule.')); $this->_redirect('catalog_rule/*/'); return; } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError( + $this->messageManager->addErrorMessage( __('We can\'t delete this rule right now. Please review the log and try again.') ); $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); @@ -39,7 +39,7 @@ public function execute() return; } } - $this->messageManager->addError(__('We can\'t find a rule to delete.')); + $this->messageManager->addErrorMessage(__('We can\'t find a rule to delete.')); $this->_redirect('catalog_rule/*/'); } } diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Edit.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Edit.php index 97a5693b18117..945c28b2088f2 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Edit.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Edit.php @@ -24,7 +24,7 @@ public function execute() try { $model = $ruleRepository->get($id); } catch (\Magento\Framework\Exception\NoSuchEntityException $exception) { - $this->messageManager->addError(__('This rule no longer exists.')); + $this->messageManager->addErrorMessage(__('This rule no longer exists.')); $this->_redirect('catalog_rule/*'); return; } diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php index 0170fc76b6aab..f3046c58a389b 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php @@ -68,7 +68,7 @@ public function execute() $validateResult = $model->validateData(new \Magento\Framework\DataObject($data)); if ($validateResult !== true) { foreach ($validateResult as $errorMessage) { - $this->messageManager->addError($errorMessage); + $this->messageManager->addErrorMessage($errorMessage); } $this->_getSession()->setPageData($data); $this->dataPersistor->set('catalog_rule', $data); @@ -88,7 +88,7 @@ public function execute() $ruleRepository->save($model); - $this->messageManager->addSuccess(__('You saved the rule.')); + $this->messageManager->addSuccessMessage(__('You saved the rule.')); $this->_objectManager->get(\Magento\Backend\Model\Session::class)->setPageData(false); $this->dataPersistor->clear('catalog_rule'); @@ -111,9 +111,9 @@ public function execute() } return; } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError( + $this->messageManager->addErrorMessage( __('Something went wrong while saving the rule data. Please review the error log.') ); $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); diff --git a/app/code/Magento/CatalogRule/Plugin/Indexer/Product/Attribute.php b/app/code/Magento/CatalogRule/Plugin/Indexer/Product/Attribute.php index cc808a38db698..7fdffe933db8c 100644 --- a/app/code/Magento/CatalogRule/Plugin/Indexer/Product/Attribute.php +++ b/app/code/Magento/CatalogRule/Plugin/Indexer/Product/Attribute.php @@ -103,7 +103,7 @@ protected function checkCatalogRulesAvailability($attributeCode) if ($disabledRulesCount) { $this->ruleProductProcessor->markIndexerAsInvalid(); - $this->messageManager->addWarning( + $this->messageManager->addWarningMessage( __( 'You disabled %1 Catalog Price Rules based on "%2" attribute.', $disabledRulesCount, From 98b5a1eb76b3e4b97fd6c57cccebc9dcfbfaf672 Mon Sep 17 00:00:00 2001 From: Ievgen Sentiabov <isentiabov@magento.com> Date: Tue, 24 Jul 2018 15:59:40 +0300 Subject: [PATCH 163/211] Fixed invalid knockout data binding --- .../Braintree/view/frontend/web/template/payment/paypal.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html b/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html index e050752dfd2c0..e1f6a1b4c25ce 100644 --- a/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html +++ b/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html @@ -12,7 +12,7 @@ data-bind="attr: {'id': getCode()}, value: getCode(), checked: isChecked, click: selectPaymentMethod, visible: isRadioButtonVisible()" /> <label class="label" data-bind="attr: {'for': getCode()}"> <!-- PayPal Logo --> - <img data-bind="attr: {src: getPaymentAcceptanceMarkSrc(), alt: $t('Acceptance Mark')}, title: $t('Acceptance Mark')}" + <img data-bind="attr: {src: getPaymentAcceptanceMarkSrc(), alt: $t('Acceptance Mark'), title: $t('Acceptance Mark')}" class="payment-icon"/> <!-- PayPal Logo --> <span text="getTitle()"></span> From 32f8eadc071ae7334d24d89be949c84041ae2b09 Mon Sep 17 00:00:00 2001 From: Arnoud Beekman <arnoud.beekman@mediact.nl> Date: Fri, 27 Jul 2018 13:58:09 +0200 Subject: [PATCH 164/211] Don't add empty method to the cart summary In some cases the method html is empty, this will result in an empty list item, which in the end results in an extra margin of 20px because of default styling. --- .../Checkout/view/frontend/templates/cart/methods.phtml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/methods.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/methods.phtml index 5530f7661bb1b..d329e2e8c1770 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/methods.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/methods.phtml @@ -14,7 +14,8 @@ <?php $methods = $block->getMethods('methods') ?: $block->getMethods('top_methods') ?> <ul class="checkout methods items checkout-methods-items"> <?php foreach ($methods as $method): ?> - <?php if ($methodHtml = $block->getMethodHtml($method)): ?> + <?php $methodHtml = $block->getMethodHtml($method); ?> + <?php if (trim($methodHtml) !== ''): ?> <li class="item"><?= /* @escapeNotVerified */ $methodHtml ?></li> <?php endif; ?> <?php endforeach; ?> From 7866d004c2755fb18d87d99c4367850b221688a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20P=C3=A9rez?= <javier.perez.m@gmail.com> Date: Tue, 17 Jul 2018 12:36:34 +0200 Subject: [PATCH 165/211] FIXED: FTP user and password strings urldecoded --- lib/internal/Magento/Framework/System/Ftp.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/internal/Magento/Framework/System/Ftp.php b/lib/internal/Magento/Framework/System/Ftp.php index 4b6432a863ebe..3a062651e0db1 100644 --- a/lib/internal/Magento/Framework/System/Ftp.php +++ b/lib/internal/Magento/Framework/System/Ftp.php @@ -105,6 +105,11 @@ public function validateConnectionString($string) if ($data['scheme'] != 'ftp') { throw new \Exception("Support for scheme unsupported: '{$data['scheme']}'"); } + + // Decode user & password strings from URL + if ( array_key_exists('user', $data) ) $data['user'] = urldecode($data['user']); + if ( array_key_exists('pass', $data) ) $data['pass'] = urldecode($data['pass']); + return $data; } From a1ebbeeb00566865345f32a046d0ca48f63f919a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20P=C3=A9rez?= <javier.perez.m@gmail.com> Date: Wed, 18 Jul 2018 14:10:33 +0200 Subject: [PATCH 166/211] Fixed: urldecode user & pass from FTP connection string recoding to pass travis coding standards --- lib/internal/Magento/Framework/System/Ftp.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/System/Ftp.php b/lib/internal/Magento/Framework/System/Ftp.php index 3a062651e0db1..8bf898965cbc3 100644 --- a/lib/internal/Magento/Framework/System/Ftp.php +++ b/lib/internal/Magento/Framework/System/Ftp.php @@ -107,8 +107,9 @@ public function validateConnectionString($string) } // Decode user & password strings from URL - if ( array_key_exists('user', $data) ) $data['user'] = urldecode($data['user']); - if ( array_key_exists('pass', $data) ) $data['pass'] = urldecode($data['pass']); + foreach (array_intersect(array_keys($data), ['user','pass']) as $key) { + $data[$key] = urldecode($data[$key]); + } return $data; } From 9d56ebe0dbabac6aed57862943abdd28afc5db57 Mon Sep 17 00:00:00 2001 From: "al.kravchuk" <al.kravchuk@ism-ukraine.com> Date: Sat, 28 Jul 2018 23:23:12 +0300 Subject: [PATCH 167/211] Maintenance. - add missed property; - add variable annotation; --- app/code/Magento/Catalog/CustomerData/CompareProducts.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/code/Magento/Catalog/CustomerData/CompareProducts.php b/app/code/Magento/Catalog/CustomerData/CompareProducts.php index 0e688042c615a..afbeab8c9070e 100644 --- a/app/code/Magento/Catalog/CustomerData/CompareProducts.php +++ b/app/code/Magento/Catalog/CustomerData/CompareProducts.php @@ -19,6 +19,11 @@ class CompareProducts implements SectionSourceInterface */ protected $productUrl; + /** + * @var \Magento\Catalog\Helper\Output + */ + private $outputHelper; + /** * @param \Magento\Catalog\Helper\Product\Compare $helper * @param \Magento\Catalog\Model\Product\Url $productUrl @@ -54,6 +59,7 @@ public function getSectionData() protected function getItems() { $items = []; + /** @var \Magento\Catalog\Model\Product $item */ foreach ($this->helper->getItemCollection() as $item) { $items[] = [ 'id' => $item->getId(), From 8019ccb847d32b0de4976220975200ab3d4d9c6e Mon Sep 17 00:00:00 2001 From: "al.kravchuk" <al.kravchuk@ism-ukraine.com> Date: Sat, 28 Jul 2018 23:24:53 +0300 Subject: [PATCH 168/211] Maintenance. - add unit test coverage. --- .../Unit/CustomerData/CompareProductsTest.php | 286 ++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Unit/CustomerData/CompareProductsTest.php diff --git a/app/code/Magento/Catalog/Test/Unit/CustomerData/CompareProductsTest.php b/app/code/Magento/Catalog/Test/Unit/CustomerData/CompareProductsTest.php new file mode 100644 index 0000000000000..e30ddda0b70b9 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/CustomerData/CompareProductsTest.php @@ -0,0 +1,286 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Test\Unit\CustomerData; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\CustomerData\CompareProducts; +use Magento\Catalog\Helper\Output; +use Magento\Catalog\Helper\Product\Compare; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Url; +use Magento\Catalog\Model\ResourceModel\Product\Compare\Item\Collection; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +class CompareProductsTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var CompareProducts + */ + private $model; + + /** + * @var Compare|\PHPUnit_Framework_MockObject_MockObject + */ + private $helperMock; + + /** + * @var Url|\PHPUnit_Framework_MockObject_MockObject + */ + private $productUrlMock; + + /** + * @var Output|\PHPUnit_Framework_MockObject_MockObject + */ + private $outputHelperMock; + + /** + * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + */ + private $objectManagerHelper; + + /** + * @var array + */ + private $productValueMap = [ + 'id' => 'getId', + ProductInterface::NAME => 'getName' + ]; + + protected function setUp() + { + parent::setUp(); + + $this->helperMock = $this->getMockBuilder(Compare::class) + ->disableOriginalConstructor() + ->getMock(); + $this->productUrlMock = $this->getMockBuilder(Url::class) + ->disableOriginalConstructor() + ->getMock(); + $this->outputHelperMock = $this->getMockBuilder(Output::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->model = $this->objectManagerHelper->getObject( + CompareProducts::class, + [ + 'helper' => $this->helperMock, + 'productUrl' => $this->productUrlMock, + 'outputHelper' => $this->outputHelperMock + ] + ); + } + + /** + * Prepare compare items collection. + * + * @param array $items + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getItemCollectionMock(array $items) : \PHPUnit_Framework_MockObject_MockObject + { + $itemCollectionMock = $this->getMockBuilder(Collection::class) + ->disableOriginalConstructor() + ->getMock(); + + $itemCollectionMock->expects($this->any()) + ->method('getIterator') + ->willReturn(new \ArrayIterator($items)); + + return $itemCollectionMock; + } + + /** + * Prepare product mocks objects and add corresponding method mocks for helpers. + * + * @param array $dataSet + * @return array + */ + private function prepareProductsWithCorrespondingMocks(array $dataSet) : array + { + $items = []; + $urlMap = []; + $outputMap = []; + $helperMap = []; + + $count = count($dataSet); + + foreach ($dataSet as $data) { + $item = $this->getProductMock($data); + $items[] = $item; + + $outputMap[] = [$item, $data['name'], 'name', 'productName#' . $data['id']]; + $helperMap[] = [$item, 'http://remove.url/' . $data['id']]; + $urlMap[] = [$item, [], 'http://product.url/' . $data['id']]; + } + + $this->productUrlMock->expects($this->exactly($count)) + ->method('getUrl') + ->will($this->returnValueMap($urlMap)); + + $this->outputHelperMock->expects($this->exactly($count)) + ->method('productAttribute') + ->will($this->returnValueMap($outputMap)); + + $this->helperMock->expects($this->exactly($count)) + ->method('getPostDataRemove') + ->will($this->returnValueMap($helperMap)); + + return $items; + } + + /** + * Prepare mock of product object. + * + * @param array $data + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getProductMock(array $data) : \PHPUnit_Framework_MockObject_MockObject + { + $product = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + + foreach ($data as $index => $value) { + $product->expects($this->once()) + ->method($this->productValueMap[$index]) + ->willReturn($value); + } + + return $product; + } + + public function testGetSectionData() + { + $dataSet = [ + ['id' => 1, 'name' => 'product#1'], + ['id' => 2, 'name' => 'product#2'], + ['id' => 3, 'name' => 'product#3'] + ]; + + $count = count($dataSet); + + $this->helperMock->expects($this->once()) + ->method('getItemCount') + ->willReturn($count); + + $items = $this->prepareProductsWithCorrespondingMocks($dataSet); + + $itemCollectionMock = $this->getItemCollectionMock($items); + + $this->helperMock->expects($this->once()) + ->method('getItemCollection') + ->willReturn($itemCollectionMock); + + $this->helperMock->expects($this->once()) + ->method('getListUrl') + ->willReturn('http://list.url'); + + $this->assertEquals( + [ + 'count' => $count, + 'countCaption' => __('%1 items', $count), + 'listUrl' => 'http://list.url', + 'items' => [ + [ + 'id' => 1, + 'product_url' => 'http://product.url/1', + 'name' => 'productName#1', + 'remove_url' => 'http://remove.url/1' + ], + [ + 'id' => 2, + 'product_url' => 'http://product.url/2', + 'name' => 'productName#2', + 'remove_url' => 'http://remove.url/2' + ], + [ + 'id' => 3, + 'product_url' => 'http://product.url/3', + 'name' => 'productName#3', + 'remove_url' => 'http://remove.url/3' + ] + ] + ], + $this->model->getSectionData() + ); + } + + public function testGetSectionDataNoItems() + { + $count = 0; + + $this->helperMock->expects($this->once()) + ->method('getItemCount') + ->willReturn($count); + + $this->helperMock->expects($this->never()) + ->method('getItemCollection'); + + $this->helperMock->expects($this->once()) + ->method('getListUrl') + ->willReturn('http://list.url'); + + $this->assertEquals( + [ + 'count' => $count, + 'countCaption' => __('%1 items', $count), + 'listUrl' => 'http://list.url', + 'items' => [] + ], + $this->model->getSectionData() + ); + } + + public function testGetSectionDataSingleItem() + { + $count = 1; + + $this->helperMock->expects($this->once()) + ->method('getItemCount') + ->willReturn($count); + + $items = $this->prepareProductsWithCorrespondingMocks( + [ + [ + 'id' => 12345, + 'name' => 'SingleProduct' + ] + ] + ); + + $itemCollectionMock = $this->getItemCollectionMock($items); + + $this->helperMock->expects($this->once()) + ->method('getItemCollection') + ->willReturn($itemCollectionMock); + + $this->helperMock->expects($this->once()) + ->method('getListUrl') + ->willReturn('http://list.url'); + + $this->assertEquals( + [ + 'count' => 1, + 'countCaption' => __('1 item'), + 'listUrl' => 'http://list.url', + 'items' => [ + [ + 'id' => 12345, + 'product_url' => 'http://product.url/12345', + 'name' => 'productName#12345', + 'remove_url' => 'http://remove.url/12345' + ] + ] + ], + $this->model->getSectionData() + ); + } +} From cb06c618f651c9d2d9e28b71ba61ff39c7895712 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Mon, 30 Jul 2018 10:52:04 +0300 Subject: [PATCH 169/211] MAGETWO-91166: [2.2.x][L2] - CE-DEV-TPFX tests fails on 2.2-develop --- .../catalog_category_with_apostrophe.php | 21 +++++++++-------- .../catalog_category_with_doublequotes.php | 21 +++++++++-------- .../Block/Catalog/Category/TreeTest.php | 23 ++++++++++--------- 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_apostrophe.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_apostrophe.php index 4da1519c0366a..adfc604adf90a 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_apostrophe.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_apostrophe.php @@ -4,23 +4,24 @@ * See COPYING.txt for license details. */ +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; + /** @var $category \Magento\Catalog\Model\Category */ $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); -$categoryFirst = $objectManager->create(\Magento\Catalog\Model\Category::class); -$categoryFirst->setName('Category 1') - ->setPath('1/2') - ->setLevel(2) - ->setAvailableSortBy('name') - ->setDefaultSortBy('name') - ->setIsActive(true) - ->setPosition(1) - ->save(); +/** @var CategoryRepositoryInterface $categoryRepository */ +$categoryRepository = $objectManager->get(CategoryRepositoryInterface::class); +try { + $category = $categoryRepository->get(333); +} catch (NoSuchEntityException $e) { + require_once __DIR__ . '/category.php'; +} // products from this fixture were moved to indexer_catalog_products.php $categorySecond = $objectManager->create(\Magento\Catalog\Model\Category::class); $categorySecond->setName('\'Category 6\'') - ->setPath($categoryFirst->getPath()) + ->setPath($category->getPath()) ->setLevel(3) ->setAvailableSortBy('name') ->setDefaultSortBy('name') diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_doublequotes.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_doublequotes.php index ccf91282ab1f6..62251e37b521e 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_doublequotes.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_doublequotes.php @@ -4,23 +4,24 @@ * See COPYING.txt for license details. */ +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; + /** @var $category \Magento\Catalog\Model\Category */ $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); -$categoryFirst = $objectManager->create(\Magento\Catalog\Model\Category::class); -$categoryFirst->setName('Category 1') - ->setPath('1/2') - ->setLevel(2) - ->setAvailableSortBy('name') - ->setDefaultSortBy('name') - ->setIsActive(true) - ->setPosition(1) - ->save(); +/** @var CategoryRepositoryInterface $categoryRepository */ +$categoryRepository = $objectManager->get(CategoryRepositoryInterface::class); +try { + $category = $categoryRepository->get(333); +} catch (NoSuchEntityException $e) { + require_once __DIR__ . '/category.php'; +} // products from this fixture were moved to indexer_catalog_products.php $categorySecond = $objectManager->create(\Magento\Catalog\Model\Category::class); $categorySecond->setName('"Category 6"') - ->setPath($categoryFirst->getPath()) + ->setPath($category->getPath()) ->setLevel(3) ->setAvailableSortBy('name') ->setDefaultSortBy('name') diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Catalog/Category/TreeTest.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Catalog/Category/TreeTest.php index 751a91b932a16..d82215bcf4f2e 100644 --- a/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Catalog/Category/TreeTest.php +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Catalog/Category/TreeTest.php @@ -5,6 +5,7 @@ */ namespace Magento\UrlRewrite\Block\Catalog\Category; +use Magento\Catalog\Api\CategoryRepositoryInterface; /** * Test for \Magento\UrlRewrite\Block\Catalog\Category\Tree @@ -16,7 +17,7 @@ class TreeTest extends \PHPUnit\Framework\TestCase /** * @var \Magento\UrlRewrite\Block\Catalog\Category\Tree */ - private $_treeBlock; + private $treeBlock; /** * Set up @@ -24,7 +25,7 @@ class TreeTest extends \PHPUnit\Framework\TestCase protected function setUp() { parent::setUp(); - $this->_treeBlock = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + $this->treeBlock = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( \Magento\Framework\View\LayoutInterface::class )->createBlock( \Magento\UrlRewrite\Block\Catalog\Category\Tree::class @@ -39,7 +40,7 @@ protected function setUp() */ public function testGetTreeArray() { - $tree = $this->_treeBlock->getTreeArray(); + $tree = $this->treeBlock->getTreeArray(); $this->assertEquals(false, $tree['is_active']); $this->assertEquals('Root', (string)$tree['name']); $this->assertEquals(true, $tree['expanded']); @@ -54,12 +55,12 @@ public function testGetTreeArray() */ public function testGetTreeArrayApostropheReplaced() { - $tree = $this->_treeBlock->getTreeArray(); + $tree = $this->treeBlock->getTreeArray(333); - $this->assertNotContains('\'', $tree['children'][0]['children'][0]['children'][0]['name']); + $this->assertNotContains('\'', $tree[0]['name']); $this->assertEquals( ''Category 6'', - $tree['children'][0]['children'][0]['children'][0]['name'] + $tree[0]['name'] ); } @@ -71,12 +72,12 @@ public function testGetTreeArrayApostropheReplaced() */ public function testGetTreeArrayDoubleQuotesReplaced() { - $tree = $this->_treeBlock->getTreeArray(); + $tree = $this->treeBlock->getTreeArray(333); - $this->assertNotContains('\"', $tree['children'][0]['children'][0]['children'][0]['name']); + $this->assertNotContains('\"', $tree[0]['name']); $this->assertEquals( '"Category 6"', - $tree['children'][0]['children'][0]['children'][0]['name'] + $tree[0]['name'] ); } @@ -88,7 +89,7 @@ public function testGetLoadTreeUrl() $row = new \Magento\Framework\DataObject(['id' => 1]); $this->assertStringStartsWith( 'http://localhost/index.php', - $this->_treeBlock->getLoadTreeUrl($row), + $this->treeBlock->getLoadTreeUrl($row), 'Tree load URL is invalid' ); } @@ -98,7 +99,7 @@ public function testGetLoadTreeUrl() */ public function testGetCategoryCollection() { - $collection = $this->_treeBlock->getCategoryCollection(); + $collection = $this->treeBlock->getCategoryCollection(); $this->assertInstanceOf(\Magento\Catalog\Model\ResourceModel\Category\Collection::class, $collection); } } From 6394da1eb3702ad49ed5b89ade35f49f695fa9d5 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Mon, 30 Jul 2018 11:37:41 +0300 Subject: [PATCH 170/211] MAGETWO-91166: [2.2.x][L2] - CE-DEV-TPFX tests fails on 2.2-develop --- .../catalog_category_with_apostrophe.php | 24 +++++++++-------- .../catalog_category_with_doublequotes.php | 24 +++++++++-------- ...og_category_with_doublequotes_rollback.php | 26 +++++++++++++++++++ .../Block/Catalog/Category/TreeTest.php | 5 ++-- 4 files changed, 54 insertions(+), 25 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_doublequotes_rollback.php diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_apostrophe.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_apostrophe.php index adfc604adf90a..dda5c80137f61 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_apostrophe.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_apostrophe.php @@ -4,24 +4,26 @@ * See COPYING.txt for license details. */ -use Magento\Catalog\Api\CategoryRepositoryInterface; -use Magento\Framework\Exception\NoSuchEntityException; - /** @var $category \Magento\Catalog\Model\Category */ $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); -/** @var CategoryRepositoryInterface $categoryRepository */ -$categoryRepository = $objectManager->get(CategoryRepositoryInterface::class); -try { - $category = $categoryRepository->get(333); -} catch (NoSuchEntityException $e) { - require_once __DIR__ . '/category.php'; -} +$categoryFirst = $objectManager->create(\Magento\Catalog\Model\Category::class); +$categoryFirst->isObjectNew(true); +$categoryFirst->setId(523) + ->setName('Parent Apostrophe Category') + ->setParentId(2) + ->setPath('1/2/523') + ->setLevel(2) + ->setAvailableSortBy('name') + ->setDefaultSortBy('name') + ->setIsActive(true) + ->setPosition(1) + ->save(); // products from this fixture were moved to indexer_catalog_products.php $categorySecond = $objectManager->create(\Magento\Catalog\Model\Category::class); $categorySecond->setName('\'Category 6\'') - ->setPath($category->getPath()) + ->setPath($categoryFirst->getPath()) ->setLevel(3) ->setAvailableSortBy('name') ->setDefaultSortBy('name') diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_doublequotes.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_doublequotes.php index 62251e37b521e..8f4add8144859 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_doublequotes.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_doublequotes.php @@ -4,24 +4,26 @@ * See COPYING.txt for license details. */ -use Magento\Catalog\Api\CategoryRepositoryInterface; -use Magento\Framework\Exception\NoSuchEntityException; - /** @var $category \Magento\Catalog\Model\Category */ $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); -/** @var CategoryRepositoryInterface $categoryRepository */ -$categoryRepository = $objectManager->get(CategoryRepositoryInterface::class); -try { - $category = $categoryRepository->get(333); -} catch (NoSuchEntityException $e) { - require_once __DIR__ . '/category.php'; -} +$categoryFirst = $objectManager->create(\Magento\Catalog\Model\Category::class); +$categoryFirst->isObjectNew(true); +$categoryFirst->setId(523) + ->setName('Parent Doublequotes Category') + ->setParentId(2) + ->setPath('1/2/523') + ->setLevel(2) + ->setAvailableSortBy('name') + ->setDefaultSortBy('name') + ->setIsActive(true) + ->setPosition(1) + ->save(); // products from this fixture were moved to indexer_catalog_products.php $categorySecond = $objectManager->create(\Magento\Catalog\Model\Category::class); $categorySecond->setName('"Category 6"') - ->setPath($category->getPath()) + ->setPath($categoryFirst->getPath()) ->setLevel(3) ->setAvailableSortBy('name') ->setDefaultSortBy('name') diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_doublequotes_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_doublequotes_rollback.php new file mode 100644 index 0000000000000..134f3d844e00b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_with_doublequotes_rollback.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +/** @var \Magento\Framework\ObjectManagerInterface $objectManager */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = $objectManager->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Model\ResourceModel\Product\Collection $collection */ +$collection = $objectManager->create(\Magento\Catalog\Model\ResourceModel\Category\Collection::class); +$collection + ->addAttributeToFilter('level', 2) + ->load() + ->delete(); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Catalog/Category/TreeTest.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Catalog/Category/TreeTest.php index d82215bcf4f2e..a9631272cdf39 100644 --- a/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Catalog/Category/TreeTest.php +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Catalog/Category/TreeTest.php @@ -5,7 +5,6 @@ */ namespace Magento\UrlRewrite\Block\Catalog\Category; -use Magento\Catalog\Api\CategoryRepositoryInterface; /** * Test for \Magento\UrlRewrite\Block\Catalog\Category\Tree @@ -55,7 +54,7 @@ public function testGetTreeArray() */ public function testGetTreeArrayApostropheReplaced() { - $tree = $this->treeBlock->getTreeArray(333); + $tree = $this->treeBlock->getTreeArray(523); $this->assertNotContains('\'', $tree[0]['name']); $this->assertEquals( @@ -72,7 +71,7 @@ public function testGetTreeArrayApostropheReplaced() */ public function testGetTreeArrayDoubleQuotesReplaced() { - $tree = $this->treeBlock->getTreeArray(333); + $tree = $this->treeBlock->getTreeArray(523); $this->assertNotContains('\"', $tree[0]['name']); $this->assertEquals( From f36d67a6c807872e463133d818fa2499ace30d62 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Mon, 30 Jul 2018 11:42:56 +0300 Subject: [PATCH 171/211] Fixed code style issues --- .../frontend/js/model/cart/totals-processor/default.test.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js index 039bf91217f35..f3d51cbb03790 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js @@ -107,6 +107,7 @@ define([ it('estimateTotals if data wasn\'t cached and request was successfully sent', function () { var deferral = new $.Deferred(); + spyOn(mocks['Magento_Checkout/js/model/cart/cache'], 'isChanged').and.returnValue(true); spyOn(mocks['Magento_Customer/js/customer-data'], 'get').and.returnValue( ko.observable({ @@ -131,6 +132,7 @@ define([ it('estimateTotals if data wasn\'t cached and request returns error', function () { var deferral = new $.Deferred(); + spyOn(mocks['Magento_Checkout/js/model/cart/cache'], 'isChanged').and.returnValue(true); spyOn(mocks['Magento_Customer/js/customer-data'], 'get').and.returnValue( ko.observable({ From 26104179d2d8cc225181d2a4e680f733471196a7 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Mon, 30 Jul 2018 14:37:08 +0300 Subject: [PATCH 172/211] MAGETWO-73357: Advanced Pricing import validation results show 0 value for checked entities --- .../ImportExport/Model/Import/Entity/AbstractEntity.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php b/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php index f5dca6a19651d..e270d2954ea75 100644 --- a/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php +++ b/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php @@ -391,6 +391,7 @@ protected function _saveValidatedBunches() $nextRowBackup = []; $maxDataSize = $this->_resourceHelper->getMaxDataSize(); $bunchSize = $this->_importExportData->getBunchSize(); + $entitiesUniq = []; $source->rewind(); $this->_dataSourceModel->cleanBunches(); @@ -407,6 +408,7 @@ protected function _saveValidatedBunches() if ($source->valid()) { try { $rowData = $source->current(); + $entitiesUniq[$rowData['sku']] = 1; } catch (\InvalidArgumentException $e) { $this->addRowError($e->getMessage(), $this->_processedRowsCount); $this->_processedRowsCount++; @@ -434,6 +436,8 @@ protected function _saveValidatedBunches() $source->next(); } } + $this->_processedEntitiesCount = count($entitiesUniq); + return $this; } From e5cf5abed1a1e8642db8e8f9115b8d7b596a7787 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Mon, 30 Jul 2018 14:38:05 +0300 Subject: [PATCH 173/211] MAGETWO-73342: Clicking on area around the label of a toggle element results in the element's state being changed --- .../Mftf/Section/AdminProductCustomizableOptionsSection.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml index 287e85310ce7d..c8df6c0e71e8e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml @@ -15,7 +15,7 @@ <element name="useDefaultOptionValueTitleByIndex" type="text" selector="[data-index='options'] [data-index='values'] tr[data-repeat-index='{{var1}}'] [name^='options_use_default']" parameterized="true"/> <element name="addOptionBtn" type="button" selector="button[data-index='button_add']"/> <element name="fillOptionTitle" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//label[text()='Option Title']/parent::span/parent::div//input[@class='admin__control-text']" parameterized="true"/> - <element name="checkSelect" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//span[text()='Option Type']/parent::label/parent::div//div[@data-role='selected-option']" parameterized="true"/> + <element name="checkSelect" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//label[text()='Option Type']/parent::label/parent::div//div[@data-role='selected-option']" parameterized="true"/> <element name="checkDropDown" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//parent::label/parent::div//li[@class='admin__action-multiselect-menu-inner-item']//label[text()='Drop-down']" parameterized="true"/> <element name="clickAddValue" type="button" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tfoot//button" parameterized="true"/> <element name="fillOptionValueTitle" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody/tr[@data-repeat-index='{{var2}}']//span[text()='Title']/parent::label/parent::div//div[@class='admin__field-control']/input" parameterized="true"/> From c727e90987578c1990dbb71ce2c383dd659234b6 Mon Sep 17 00:00:00 2001 From: Navarr Barnier <me@navarr.me> Date: Mon, 30 Jul 2018 08:28:00 -0400 Subject: [PATCH 174/211] Fix styling issues --- .../frontend/js/model/cart/totals-processor/default.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js index f3d51cbb03790..91cb4480039d1 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/model/cart/totals-processor/default.test.js @@ -107,7 +107,7 @@ define([ it('estimateTotals if data wasn\'t cached and request was successfully sent', function () { var deferral = new $.Deferred(); - + spyOn(mocks['Magento_Checkout/js/model/cart/cache'], 'isChanged').and.returnValue(true); spyOn(mocks['Magento_Customer/js/customer-data'], 'get').and.returnValue( ko.observable({ @@ -132,7 +132,7 @@ define([ it('estimateTotals if data wasn\'t cached and request returns error', function () { var deferral = new $.Deferred(); - + spyOn(mocks['Magento_Checkout/js/model/cart/cache'], 'isChanged').and.returnValue(true); spyOn(mocks['Magento_Customer/js/customer-data'], 'get').and.returnValue( ko.observable({ From b8a80a45e7096d355a7666ee53916ce6724610dd Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Mon, 30 Jul 2018 17:02:06 +0300 Subject: [PATCH 175/211] MAGETWO-73342: Clicking on area around the label of a toggle element results in the element's state being changed --- .../Mftf/Section/AdminProductCustomizableOptionsSection.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml index c8df6c0e71e8e..36fae30791419 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml @@ -15,11 +15,11 @@ <element name="useDefaultOptionValueTitleByIndex" type="text" selector="[data-index='options'] [data-index='values'] tr[data-repeat-index='{{var1}}'] [name^='options_use_default']" parameterized="true"/> <element name="addOptionBtn" type="button" selector="button[data-index='button_add']"/> <element name="fillOptionTitle" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//label[text()='Option Title']/parent::span/parent::div//input[@class='admin__control-text']" parameterized="true"/> - <element name="checkSelect" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//label[text()='Option Type']/parent::label/parent::div//div[@data-role='selected-option']" parameterized="true"/> + <element name="checkSelect" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//label[text()='Option Type']/parent::span/parent::div//div[@data-role='selected-option']" parameterized="true"/> <element name="checkDropDown" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//parent::label/parent::div//li[@class='admin__action-multiselect-menu-inner-item']//label[text()='Drop-down']" parameterized="true"/> <element name="clickAddValue" type="button" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tfoot//button" parameterized="true"/> - <element name="fillOptionValueTitle" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody/tr[@data-repeat-index='{{var2}}']//span[text()='Title']/parent::label/parent::div//div[@class='admin__field-control']/input" parameterized="true"/> + <element name="fillOptionValueTitle" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody/tr[@data-repeat-index='{{var2}}']//label[text()='Title']/parent::span/parent::div//div[@class='admin__field-control']/input" parameterized="true"/> <element name="fillOptionValuePrice" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody/tr[@data-repeat-index='{{var2}}']//span[text()='Price']/parent::label/parent::div//div[@class='admin__control-addon']/input" parameterized="true"/> - <element name="clickSelectPriceType" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody//tr[@data-repeat-index='{{var2}}']//span[text()='Price Type']/parent::label/parent::div//select" parameterized="true"/> + <element name="clickSelectPriceType" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody//tr[@data-repeat-index='{{var2}}']//label[text()='Price Type']/parent::span/parent::div//select" parameterized="true"/> </section> </sections> From 5cfde8a4231203033d78452e3df64a26f0b8af19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Mon, 30 Jul 2018 17:14:14 +0300 Subject: [PATCH 176/211] fixed translation issue --- app/code/Magento/Ui/view/base/web/js/modal/confirm.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/modal/confirm.js b/app/code/Magento/Ui/view/base/web/js/modal/confirm.js index 9cb14f7b2ef11..eceab940d1141 100644 --- a/app/code/Magento/Ui/view/base/web/js/modal/confirm.js +++ b/app/code/Magento/Ui/view/base/web/js/modal/confirm.js @@ -9,10 +9,10 @@ define([ 'jquery', 'underscore', + 'mage/translate', 'jquery/ui', - 'Magento_Ui/js/modal/modal', - 'mage/translate' -], function ($, _) { + 'Magento_Ui/js/modal/modal' +], function ($, _, $t) { 'use strict'; $.widget('mage.confirm', $.mage.modal, { @@ -38,7 +38,7 @@ define([ cancel: function () {} }, buttons: [{ - text: $.mage.__('Cancel'), + text: $t('Cancel'), class: 'action-secondary action-dismiss', /** @@ -48,7 +48,7 @@ define([ this.closeModal(event); } }, { - text: $.mage.__('OK'), + text: $t('OK'), class: 'action-primary action-accept', /** From b871f160d2ce428c4ba1ddf391069bbd81effa65 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Tue, 31 Jul 2018 09:41:47 +0300 Subject: [PATCH 177/211] Fixed variable name & repository usage --- .../Controller/Adminhtml/Agreement/Delete.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php index 144ce2b2f6bc5..f7b178df99624 100644 --- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php +++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php @@ -40,15 +40,15 @@ public function __construct( public function execute() { $id = (int)$this->getRequest()->getParam('id'); - $repository = $this->agreementRepository->get($id); - if (!$repository->getAgreementId()) { + $agreement = $this->agreementRepository->get($id); + if (!$agreement->getAgreementId()) { $this->messageManager->addError(__('This condition no longer exists.')); $this->_redirect('checkout/*/'); return; } try { - $repository->delete(); + $this->agreementRepository->delete($agreement); $this->messageManager->addSuccess(__('You deleted the condition.')); $this->_redirect('checkout/*/'); return; From 87c260963b81728e72d2f4ed336ff9c9aead56e1 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Tue, 31 Jul 2018 12:50:18 +0300 Subject: [PATCH 178/211] MAGETWO-91455: [FT] Magento\Checkout\Test\TestCase\OnePageCheckoutTest::OnePageCheckoutBraintreeTestVariation5 failed on Bamboo --- .../Constraint/AssertDeviceDataIsPresentInBraintreeRequest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/Constraint/AssertDeviceDataIsPresentInBraintreeRequest.php b/dev/tests/functional/tests/app/Magento/Braintree/Test/Constraint/AssertDeviceDataIsPresentInBraintreeRequest.php index 7eb53d728b079..40ae920a6bbde 100644 --- a/dev/tests/functional/tests/app/Magento/Braintree/Test/Constraint/AssertDeviceDataIsPresentInBraintreeRequest.php +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/Constraint/AssertDeviceDataIsPresentInBraintreeRequest.php @@ -17,7 +17,7 @@ class AssertDeviceDataIsPresentInBraintreeRequest extends AbstractConstraint /** * Log file name. */ - const FILE_NAME = 'debug.log'; + const FILE_NAME = 'payment.log'; /** * Device data pattern for regular expression. From 56fa04d98c4a6c1606a5bbf5468f5396f8f16d70 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Tue, 31 Jul 2018 13:23:08 +0300 Subject: [PATCH 179/211] MAGETWO-73342: Clicking on area around the label of a toggle element results in the element's state being changed --- .../Catalog/Test/Mftf/Section/AdminProductFormSection.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml index 4f9ff872d6eeb..5f33eb6f52edf 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml @@ -15,8 +15,8 @@ <element name="productName" type="input" selector=".admin__field[data-index=name] input"/> <element name="productNameUseDefault" type="checkbox" selector="input[name='use_default[name]']"/> <element name="productSku" type="input" selector=".admin__field[data-index=sku] input"/> - <element name="enableProductAttributeLabel" type="text" selector="//label[text()='Enable Product']"/> - <element name="enableProductAttributeLabelWrapper" type="text" selector="//label[text()='Enable Product']/parent::span"/> + <element name="enableProductAttributeLabel" type="text" selector="[data-index='status'] .admin__field-label label"/> + <element name="enableProductAttributeLabelWrapper" type="text" selector="[data-index='status'] .admin__field-label"/> <element name="productStatus" type="checkbox" selector="input[name='product[status]']"/> <element name="productStatusUseDefault" type="checkbox" selector="input[name='use_default[status]']"/> <element name="productPrice" type="input" selector=".admin__field[data-index=price] input"/> From f0f32c19904e95aa1dcba9b7fc64f9a18102ce45 Mon Sep 17 00:00:00 2001 From: Victor Rad <vrad@magento.com> Date: Tue, 31 Jul 2018 14:01:24 +0300 Subject: [PATCH 180/211] MAGETWO-93145: Bundle summary is not sorted by Bundle Item Position but by `option_id` --- .../Bundle/Block/Catalog/Product/View/Type/Bundle.php | 7 +++++++ .../Unit/Block/Catalog/Product/View/Type/BundleTest.php | 1 + .../Magento/Bundle/view/frontend/web/js/product-summary.js | 5 +++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php index 3f19e20de8918..fd2b8efa8eebd 100644 --- a/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php +++ b/app/code/Magento/Bundle/Block/Catalog/Product/View/Type/Bundle.php @@ -56,6 +56,11 @@ class Bundle extends \Magento\Catalog\Block\Product\View\AbstractView */ private $catalogRuleProcessor; + /** + * @var array + */ + private $optionsPosition = []; + /** * @param \Magento\Catalog\Block\Product\Context $context * @param \Magento\Framework\Stdlib\ArrayUtils $arrayUtils @@ -172,6 +177,7 @@ public function getJsonConfig() } $optionId = $optionItem->getId(); $options[$optionId] = $this->getOptionItemData($optionItem, $currentProduct, $position); + $this->optionsPosition[$position] = $optionId; // Add attribute default value (if set) if ($preConfiguredFlag) { @@ -368,6 +374,7 @@ private function getConfigData(Product $product, array $options) $config = [ 'options' => $options, 'selected' => $this->selectedOptions, + 'positions' => $this->optionsPosition, 'bundleId' => $product->getId(), 'priceFormat' => $this->localeFormat->getPriceFormat(), 'prices' => [ diff --git a/app/code/Magento/Bundle/Test/Unit/Block/Catalog/Product/View/Type/BundleTest.php b/app/code/Magento/Bundle/Test/Unit/Block/Catalog/Product/View/Type/BundleTest.php index 0f36049a86cac..bda1c32d4d66e 100644 --- a/app/code/Magento/Bundle/Test/Unit/Block/Catalog/Product/View/Type/BundleTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Block/Catalog/Product/View/Type/BundleTest.php @@ -280,6 +280,7 @@ public function testGetJsonConfigFixedPriceBundle() $this->assertEquals(110, $jsonConfig['prices']['oldPrice']['amount']); $this->assertEquals(100, $jsonConfig['prices']['basePrice']['amount']); $this->assertEquals(100, $jsonConfig['prices']['finalPrice']['amount']); + $this->assertEquals([1], $jsonConfig['positions']); } /** diff --git a/app/code/Magento/Bundle/view/frontend/web/js/product-summary.js b/app/code/Magento/Bundle/view/frontend/web/js/product-summary.js index d8d4cb1e99b7f..1e7fe6b6673d6 100644 --- a/app/code/Magento/Bundle/view/frontend/web/js/product-summary.js +++ b/app/code/Magento/Bundle/view/frontend/web/js/product-summary.js @@ -56,8 +56,9 @@ define([ // Clear Summary box this.element.html(''); - - $.each(this.cache.currentElement.selected, $.proxy(this._renderOption, this)); + this.cache.currentElement.positions.forEach(function (optionId) { + this._renderOption(optionId, this.cache.currentElement.selected[optionId]); + }, this); this.element .parents(this.options.bundleSummaryContainer) .toggleClass('empty', !this.cache.currentElementCount); // Zero elements equal '.empty' container From a46a88a5bbc5081a14f820b7bf19e38769b45399 Mon Sep 17 00:00:00 2001 From: Oleksandr Dubovyk <odubovyk@magento.com> Date: Tue, 31 Jul 2018 14:18:05 +0300 Subject: [PATCH 181/211] MAGETWO-93054: Admin logs don't detail quantity changes - Fixed - Added test --- app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php | 1 + .../CatalogInventory/Observer/ProcessInventoryDataObserver.php | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php index 940062cd6cb4c..951bce38fef33 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php @@ -141,6 +141,7 @@ public function execute() ); if ($redirectBack === 'duplicate') { + $product->unsetData('quantity_and_stock_status'); $newProduct = $this->productCopier->copy($product); $this->messageManager->addSuccessMessage(__('You duplicated the product.')); } diff --git a/app/code/Magento/CatalogInventory/Observer/ProcessInventoryDataObserver.php b/app/code/Magento/CatalogInventory/Observer/ProcessInventoryDataObserver.php index cea19c098b928..abd704c1d0406 100644 --- a/app/code/Magento/CatalogInventory/Observer/ProcessInventoryDataObserver.php +++ b/app/code/Magento/CatalogInventory/Observer/ProcessInventoryDataObserver.php @@ -74,7 +74,6 @@ private function processStockData(Product $product) $this->setStockDataToProduct($product, $stockItem, $quantityAndStockStatus); } } - $product->unsetData('quantity_and_stock_status'); } /** From 9055f7da39eaea769e368a1f2aa01979c6671693 Mon Sep 17 00:00:00 2001 From: Daniel Ruf <daniel.ruf@ueberbit.de> Date: Tue, 31 Jul 2018 13:27:52 +0200 Subject: [PATCH 182/211] fix: remove unused ID --- .../view/frontend/web/template/billing-address/form.html | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/template/billing-address/form.html b/app/code/Magento/Checkout/view/frontend/web/template/billing-address/form.html index 093b2295a12b3..17a2d8128f5ec 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/billing-address/form.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/billing-address/form.html @@ -9,9 +9,7 @@ <!-- ko template: getTemplate() --><!-- /ko --> <!--/ko--> <form data-bind="attr: {'data-hasrequired': $t('* Required Fields')}"> - <fieldset - data-bind="attr: { id:'billing-new-address-form-'+index, value:index}" - class="billing-new-address-form fieldset address"> + <fieldset class="billing-new-address-form fieldset address"> <!-- ko foreach: getRegion('additional-fieldsets') --> <!-- ko template: getTemplate() --><!-- /ko --> <!--/ko--> From 85620912879a6ed10f51c60fc51c2d4fddf76346 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Tue, 31 Jul 2018 14:38:12 +0300 Subject: [PATCH 183/211] MAGETWO-73357: Advanced Pricing import validation results show 0 value for checked entities --- .../ImportExport/Model/Import/Entity/AbstractEntity.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php b/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php index e270d2954ea75..e2398e792b817 100644 --- a/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php +++ b/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php @@ -391,7 +391,7 @@ protected function _saveValidatedBunches() $nextRowBackup = []; $maxDataSize = $this->_resourceHelper->getMaxDataSize(); $bunchSize = $this->_importExportData->getBunchSize(); - $entitiesUniq = []; + $skuSet = []; $source->rewind(); $this->_dataSourceModel->cleanBunches(); @@ -408,7 +408,7 @@ protected function _saveValidatedBunches() if ($source->valid()) { try { $rowData = $source->current(); - $entitiesUniq[$rowData['sku']] = 1; + $skuSet[$rowData['sku']] = true; } catch (\InvalidArgumentException $e) { $this->addRowError($e->getMessage(), $this->_processedRowsCount); $this->_processedRowsCount++; @@ -436,7 +436,7 @@ protected function _saveValidatedBunches() $source->next(); } } - $this->_processedEntitiesCount = count($entitiesUniq); + $this->_processedEntitiesCount = count($skuSet); return $this; } From 2a50a5626fb13b434af1e6b14e84a838c02f51fe Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Tue, 31 Jul 2018 15:50:56 +0300 Subject: [PATCH 184/211] MAGETWO-73805: [FT]MoveProductFromShoppingCartToWishlistTest randomly failed on assertProductDetails step on CICD --- .../Test/Constraint/AbstractAssertWishlistProductDetails.php | 3 ++- .../TestCase/MoveProductFromShoppingCartToWishlistTest.xml | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AbstractAssertWishlistProductDetails.php b/dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AbstractAssertWishlistProductDetails.php index ba9fe67b5eb4c..a2ccca69f2199 100644 --- a/dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AbstractAssertWishlistProductDetails.php +++ b/dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AbstractAssertWishlistProductDetails.php @@ -33,7 +33,8 @@ protected function assertProductDetails( $actualOptions = $productBlock->getItemProduct($product)->getOptions(); $cartFixture = $fixtureFactory->createByCode('cart', ['data' => ['items' => ['products' => [$product]]]]); $expectedOptions = $cartFixture->getItems()[0]->getData()['options']; - + // Sku is not present in Wishlist Product Details + unset($expectedOptions[0]['sku']); $errors = $this->verifyData( $this->sortDataByPath($expectedOptions, '::title'), $this->sortDataByPath($actualOptions, '::title') diff --git a/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/MoveProductFromShoppingCartToWishlistTest.xml b/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/MoveProductFromShoppingCartToWishlistTest.xml index fd80d385e4853..95e6a854ed266 100644 --- a/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/MoveProductFromShoppingCartToWishlistTest.xml +++ b/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/MoveProductFromShoppingCartToWishlistTest.xml @@ -37,7 +37,6 @@ <constraint name="Magento\Wishlist\Test\Constraint\AssertProductDetailsInWishlist" /> </variation> <variation name="MoveProductFromShoppingCartToWishlistTestVariation5"> - <data name="tag" xsi:type="string">stable:no</data> <data name="product/0" xsi:type="string">bundleProduct::bundle_dynamic_product</data> <constraint name="Magento\Wishlist\Test\Constraint\AssertMoveProductToWishlistSuccessMessage" /> <constraint name="Magento\Wishlist\Test\Constraint\AssertProductIsPresentInWishlist" /> @@ -45,7 +44,6 @@ <constraint name="Magento\Wishlist\Test\Constraint\AssertProductDetailsInWishlist" /> </variation> <variation name="MoveProductFromShoppingCartToWishlistTestVariation6"> - <data name="tag" xsi:type="string">stable:no</data> <data name="product/0" xsi:type="string">bundleProduct::bundle_fixed_product</data> <constraint name="Magento\Wishlist\Test\Constraint\AssertMoveProductToWishlistSuccessMessage" /> <constraint name="Magento\Wishlist\Test\Constraint\AssertProductIsPresentInWishlist" /> From 12a4b332dfd5da284da0b1a4cfac06fb993605fd Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko <iivashchenko@magento.com> Date: Tue, 31 Jul 2018 18:42:51 +0300 Subject: [PATCH 185/211] MAGETWO-93037: User can place order when product changes status to Out of stock during checkout --- .../testsuite/Magento/Quote/Model/QuoteManagementTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php index b37f86ed1f892..356117f2b3dc8 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php @@ -92,6 +92,7 @@ public function testSubmitWithDeletedItem() * @magentoDataFixture Magento/Sales/_files/quote.php * @expectedException \Magento\Framework\Exception\LocalizedException * @expectedExceptionMessage Some of the products are out of stock. + * @magentoDbIsolation enabled */ public function testSubmitWithItemOutOfStock() { From 1eb6af0f62ee7419970aba4e5435a06f2f3f69a5 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Wed, 1 Aug 2018 10:21:44 +0300 Subject: [PATCH 186/211] MAGETWO-73806: [FT] DeleteStoreGroupEntityTestVariation1 fails randomly on Jenkins --- .../Magento/Store/Test/TestCase/DeleteStoreGroupEntityTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/DeleteStoreGroupEntityTest.xml b/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/DeleteStoreGroupEntityTest.xml index 02fcf3b19fbde..66ae1d8179af5 100644 --- a/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/DeleteStoreGroupEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Store/Test/TestCase/DeleteStoreGroupEntityTest.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\DeleteStoreGroupEntityTest" summary="Delete Store Group" ticketId="MAGETWO-27596"> <variation name="DeleteStoreGroupEntityTestVariation1"> - <data name="tag" xsi:type="string">severity:S3, stable:no</data> + <data name="tag" xsi:type="string">severity:S3</data> <data name="storeGroup/dataset" xsi:type="string">custom</data> <data name="createBackup" xsi:type="string">Yes</data> <constraint name="Magento\Store\Test\Constraint\AssertStoreGroupSuccessDeleteAndBackupMessages" /> From be74ccd6a2abe402e0bef2bcffeafa491c12fe7c Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Wed, 1 Aug 2018 11:11:16 +0300 Subject: [PATCH 187/211] MAGETWO-73819: [FT] Magento\Reports\Test\TestCase\AbandonedCartsReportEntityTest fails on bamboo --- .../Reports/Test/TestCase/AbandonedCartsReportEntityTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/AbandonedCartsReportEntityTest.php b/dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/AbandonedCartsReportEntityTest.php index 72a3b2b5f68d1..610387d9a39eb 100644 --- a/dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/AbandonedCartsReportEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/AbandonedCartsReportEntityTest.php @@ -34,7 +34,7 @@ class AbandonedCartsReportEntityTest extends Injectable { /* tags */ const MVP = 'no'; - const STABLE = 'no'; + const STABLE = 'yes'; /* end tags */ /** From 58dbcae89c154b45148fcfe9cdfada4c3c76d214 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Wed, 1 Aug 2018 12:04:12 +0300 Subject: [PATCH 188/211] MAGETWO-93676: ProductListing: [FT-CE] AdminMultipleWebsitesUseDefaultValuesTest unstable on Jenkins for 2.2-develop --- .../Test/AdminMultipleWebsitesUseDefaultValuesTest.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml index 1deac4262ff7b..2a761f192f29f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMultipleWebsitesUseDefaultValuesTest.xml @@ -40,11 +40,10 @@ <!--Create a Simple Product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToCatalogProductGrid"/> - <waitForPageLoad time="30" stepKey="waitForProductsGridPageLoad"/> + <waitForPageLoad stepKey="waitForProductsGridPageLoad"/> <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickAddSimpleProduct"/> - <!-- Need to wait because fields are loaded a little bit later then it starts to fill them --> - <waitForElementVisible selector="{{AdminProductFormSection.productName}}" stepKey="waitForNameFieldVisible"/> + <waitForPageLoad stepKey="waitForPageProductFormLoad"/> <fillField userInput="{{_defaultProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillProductName"/> <fillField userInput="{{_defaultProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillProductSKU"/> <fillField userInput="{{_defaultProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillProductPrice"/> @@ -54,7 +53,7 @@ <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="openProductInWebsites"/> <click selector="{{ProductInWebsitesSection.website('Second Website')}}" stepKey="selectSecondWebsite"/> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> - <waitForLoadingMaskToDisappear stepKey="waitForProductPageSave"/> + <waitForPageLoad stepKey="waitForProductSave"/> <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessage"/> <!-- switch to the second store view --> From 9f1d3c8f5f954fe19c6a42291fd65643605d53b7 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Wed, 1 Aug 2018 13:11:52 +0300 Subject: [PATCH 189/211] MAGETWO-73805: [FT]MoveProductFromShoppingCartToWishlistTest randomly failed on assertProductDetails step on CICD --- .../Constraint/AbstractAssertWishlistProductDetails.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AbstractAssertWishlistProductDetails.php b/dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AbstractAssertWishlistProductDetails.php index a2ccca69f2199..2df4cc3078370 100644 --- a/dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AbstractAssertWishlistProductDetails.php +++ b/dev/tests/functional/tests/app/Magento/Wishlist/Test/Constraint/AbstractAssertWishlistProductDetails.php @@ -16,6 +16,11 @@ */ abstract class AbstractAssertWishlistProductDetails extends AbstractAssertForm { + /** + * @inheritdoc + */ + protected $skippedFields = ['sku']; + /** * Assert product details. * @@ -33,8 +38,7 @@ protected function assertProductDetails( $actualOptions = $productBlock->getItemProduct($product)->getOptions(); $cartFixture = $fixtureFactory->createByCode('cart', ['data' => ['items' => ['products' => [$product]]]]); $expectedOptions = $cartFixture->getItems()[0]->getData()['options']; - // Sku is not present in Wishlist Product Details - unset($expectedOptions[0]['sku']); + $errors = $this->verifyData( $this->sortDataByPath($expectedOptions, '::title'), $this->sortDataByPath($actualOptions, '::title') From e2b73b4fd9a40a105e5dd960c80b95f08c70235b Mon Sep 17 00:00:00 2001 From: Daniel Ruf <daniel.ruf@ueberbit.de> Date: Wed, 1 Aug 2018 13:23:05 +0200 Subject: [PATCH 190/211] chore: move class to data-form attribute update acceptance test to use new data-form attribute --- .../Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml | 2 +- .../view/frontend/web/template/billing-address/form.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml index 7a2b8f28f77d8..f98998fab59fc 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml @@ -12,7 +12,7 @@ <element name="isPaymentSection" type="text" selector="//*[@class='opc-progress-bar']/li[contains(@class, '_active') and span[contains(.,'Review & Payments')]]"/> <element name="availablePaymentSolutions" type="text" selector="#checkout-payment-method-load>div>div>div:nth-child(2)>div.payment-method-title.field.choice"/> <element name="notAvailablePaymentSolutions" type="text" selector="#checkout-payment-method-load>div>div>div.payment-method._active>div.payment-method-title.field.choice"/> - <element name="billingNewAddressForm" type="text" selector=".billing-new-address-form"/> + <element name="billingNewAddressForm" type="text" selector="[data-form='billing-new-address-form']"/> <element name="placeOrderDisabled" type="button" selector="#checkout-payment-method-load button.disabled"/> <element name="update" type="button" selector=".payment-method-billing-address .action.action-update"/> <element name="guestFirstName" type="input" selector=".billing-address-form input[name*='firstname']"/> diff --git a/app/code/Magento/Checkout/view/frontend/web/template/billing-address/form.html b/app/code/Magento/Checkout/view/frontend/web/template/billing-address/form.html index 17a2d8128f5ec..eca9d145addfa 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/billing-address/form.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/billing-address/form.html @@ -9,7 +9,7 @@ <!-- ko template: getTemplate() --><!-- /ko --> <!--/ko--> <form data-bind="attr: {'data-hasrequired': $t('* Required Fields')}"> - <fieldset class="billing-new-address-form fieldset address"> + <fieldset class="fieldset address" data-form="billing-new-address-form"> <!-- ko foreach: getRegion('additional-fieldsets') --> <!-- ko template: getTemplate() --><!-- /ko --> <!--/ko--> From bb77b849755a8d08c75ff9c110797a558877d77a Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Wed, 1 Aug 2018 15:11:39 +0300 Subject: [PATCH 191/211] MAGETWO-73342: Clicking on area around the label of a toggle element results in the element's state being changed --- .../adminhtml/Magento/backend/web/css/source/forms/_fields.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less index 8f0d4c33a8ee7..6dbef05ea2389 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less @@ -228,7 +228,7 @@ .required > &, // ToDo UI: change classes 'required' to '_required'. ._required > & { - > span { + > label { &:after { color: @validation__color; content: '*'; From 5266a0c7bd11660a5174c9c1577362b81553a91c Mon Sep 17 00:00:00 2001 From: Vlad Veselov <orlangur@users.noreply.github.com> Date: Wed, 1 Aug 2018 15:28:26 +0300 Subject: [PATCH 192/211] Remove unneeded "form" form data-form attribute value --- .../Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml index f98998fab59fc..f9e27ef36c715 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml @@ -12,7 +12,7 @@ <element name="isPaymentSection" type="text" selector="//*[@class='opc-progress-bar']/li[contains(@class, '_active') and span[contains(.,'Review & Payments')]]"/> <element name="availablePaymentSolutions" type="text" selector="#checkout-payment-method-load>div>div>div:nth-child(2)>div.payment-method-title.field.choice"/> <element name="notAvailablePaymentSolutions" type="text" selector="#checkout-payment-method-load>div>div>div.payment-method._active>div.payment-method-title.field.choice"/> - <element name="billingNewAddressForm" type="text" selector="[data-form='billing-new-address-form']"/> + <element name="billingNewAddressForm" type="text" selector="[data-form='billing-new-address']"/> <element name="placeOrderDisabled" type="button" selector="#checkout-payment-method-load button.disabled"/> <element name="update" type="button" selector=".payment-method-billing-address .action.action-update"/> <element name="guestFirstName" type="input" selector=".billing-address-form input[name*='firstname']"/> From 4a3a0ec858e701e7cd04cb713cdfe498b7a76e5f Mon Sep 17 00:00:00 2001 From: Vlad Veselov <orlangur@users.noreply.github.com> Date: Wed, 1 Aug 2018 15:28:51 +0300 Subject: [PATCH 193/211] Remove unneeded "form" form data-form attribute value --- .../view/frontend/web/template/billing-address/form.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/template/billing-address/form.html b/app/code/Magento/Checkout/view/frontend/web/template/billing-address/form.html index eca9d145addfa..54fe9a1f59394 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/billing-address/form.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/billing-address/form.html @@ -9,7 +9,7 @@ <!-- ko template: getTemplate() --><!-- /ko --> <!--/ko--> <form data-bind="attr: {'data-hasrequired': $t('* Required Fields')}"> - <fieldset class="fieldset address" data-form="billing-new-address-form"> + <fieldset class="fieldset address" data-form="billing-new-address"> <!-- ko foreach: getRegion('additional-fieldsets') --> <!-- ko template: getTemplate() --><!-- /ko --> <!--/ko--> From 3d119afdb02d390f139452e587a600258decf688 Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko <iivashchenko@magento.com> Date: Wed, 1 Aug 2018 13:10:56 +0300 Subject: [PATCH 194/211] MAGETWO-93037: User can place order when product changes status to Out of stock during checkout --- .../Magento/Catalog/Model/ProductTest.php | 3 + .../product_simple_with_custom_options.php | 13 +++- .../Magento/Checkout/Controller/CartTest.php | 73 +++++++++++++++---- ...with_simple_product_and_custom_options.php | 46 ++++++++++++ 4 files changed, 121 insertions(+), 14 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Checkout/_files/cart_with_simple_product_and_custom_options.php diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php index 562dd35a2338a..62a918b7e3c5a 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php @@ -550,6 +550,9 @@ public function testGetOptions() '4-2-radio' => 40000.00 ]; foreach ($options as $option) { + if (!$option->getValues()) { + continue; + } foreach ($option->getValues() as $value) { $this->assertEquals($expectedValue[$value->getSku()], floatval($value->getPrice())); } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_options.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_options.php index 059b784978a22..61b549f7729d1 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_options.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_options.php @@ -86,7 +86,18 @@ 'sku' => '4-2-radio', ], ] - ] + ], + [ + 'previous_group' => 'text', + 'title' => 'Test Field', + 'type' => 'field', + 'is_require' => 1, + 'sort_order' => 0, + 'price' => 1, + 'price_type' => 'fixed', + 'sku' => '1-text', + 'max_characters' => 100, + ], ]; $options = []; diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php index 67874d9f3974a..068a9c3529c15 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php @@ -11,8 +11,12 @@ use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Checkout\Model\Session; +use Magento\Checkout\Model\Session as CheckoutSession; use Magento\Customer\Model\ResourceModel\CustomerRepository; use Magento\Framework\Data\Form\FormKey; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Quote\Model\Quote; +use Magento\Quote\Api\CartRepositoryInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\Request; use Magento\Customer\Model\Session as CustomerSession; @@ -25,6 +29,28 @@ */ class CartTest extends \Magento\TestFramework\TestCase\AbstractController { + /** @var CheckoutSession */ + private $checkoutSession; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->checkoutSession = $this->_objectManager->get(CheckoutSession::class); + $this->_objectManager->addSharedInstance($this->checkoutSession, CheckoutSession::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->_objectManager->removeSharedInstance(CheckoutSession::class); + parent::tearDown(); + } + /** * Test for \Magento\Checkout\Controller\Cart::configureAction() with simple product * @@ -32,8 +58,8 @@ class CartTest extends \Magento\TestFramework\TestCase\AbstractController */ public function testConfigureActionWithSimpleProduct() { - /** @var $session \Magento\Checkout\Model\Session */ - $session = $this->_objectManager->create(\Magento\Checkout\Model\Session::class); + /** @var $session CheckoutSession */ + $session = $this->_objectManager->create(CheckoutSession::class); /** @var ProductRepositoryInterface $productRepository */ $productRepository = $this->_objectManager->create(ProductRepositoryInterface::class); @@ -63,19 +89,20 @@ public function testConfigureActionWithSimpleProduct() /** * Test for \Magento\Checkout\Controller\Cart::configureAction() with simple product and custom option * - * @magentoDataFixture Magento/Checkout/_files/quote_with_simple_product_and_custom_option.php + * @magentoDataFixture Magento/Checkout/_files/cart_with_simple_product_and_custom_options.php */ public function testConfigureActionWithSimpleProductAndCustomOption() { - /** @var $session \Magento\Checkout\Model\Session */ - $session = $this->_objectManager->create(\Magento\Checkout\Model\Session::class); + /** @var Quote $quote */ + $quote = $this->getQuote('test_order_item_with_custom_options'); + $this->checkoutSession->setQuoteId($quote->getId()); /** @var ProductRepositoryInterface $productRepository */ $productRepository = $this->_objectManager->create(ProductRepositoryInterface::class); /** @var $product \Magento\Catalog\Model\Product */ - $product = $productRepository->get('simple'); + $product = $productRepository->get('simple_with_custom_options'); - $quoteItem = $this->_getQuoteItemIdByProductId($session->getQuote(), $product->getId()); + $quoteItem = $this->_getQuoteItemIdByProductId($quote, $product->getId()); $this->assertNotNull($quoteItem, 'Cannot get quote item for simple product with custom option'); $this->dispatch( @@ -112,8 +139,8 @@ public function testConfigureActionWithSimpleProductAndCustomOption() */ public function testConfigureActionWithBundleProduct() { - /** @var $session \Magento\Checkout\Model\Session */ - $session = $this->_objectManager->create(\Magento\Checkout\Model\Session::class); + /** @var $session CheckoutSession */ + $session = $this->_objectManager->create(CheckoutSession::class); /** @var ProductRepositoryInterface $productRepository */ $productRepository = $this->_objectManager->create(ProductRepositoryInterface::class); @@ -147,8 +174,8 @@ public function testConfigureActionWithBundleProduct() */ public function testConfigureActionWithDownloadableProduct() { - /** @var $session \Magento\Checkout\Model\Session */ - $session = $this->_objectManager->create(\Magento\Checkout\Model\Session::class); + /** @var $session CheckoutSession */ + $session = $this->_objectManager->create(CheckoutSession::class); /** @var ProductRepositoryInterface $productRepository */ $productRepository = $this->_objectManager->create(ProductRepositoryInterface::class); @@ -201,8 +228,8 @@ public function testUpdatePostAction() $productId = $product->getId(); $originalQuantity = 1; $updatedQuantity = 2; - /** @var $checkoutSession \Magento\Checkout\Model\Session */ - $checkoutSession = $this->_objectManager->create(\Magento\Checkout\Model\Session::class); + /** @var $checkoutSession CheckoutSession */ + $checkoutSession = $this->_objectManager->create(CheckoutSession::class); $quoteItem = $this->_getQuoteItemIdByProductId($checkoutSession->getQuote(), $productId); /** @var FormKey $formKey */ @@ -235,6 +262,26 @@ public function testUpdatePostAction() $this->assertEquals($updatedQuantity, $quoteItem->getQty(), "Invalid quote item quantity"); } + /** + * Gets quote by reserved order id. + * + * @param string $reservedOrderId + * @return Quote + */ + private function getQuote($reservedOrderId) + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->_objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId) + ->create(); + + /** @var CartRepositoryInterface $quoteRepository */ + $quoteRepository = $this->_objectManager->get(CartRepositoryInterface::class); + $items = $quoteRepository->getList($searchCriteria)->getItems(); + + return array_pop($items); + } + /** * Gets \Magento\Quote\Model\Quote\Item from \Magento\Quote\Model\Quote by product id * diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/cart_with_simple_product_and_custom_options.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/cart_with_simple_product_and_custom_options.php new file mode 100644 index 0000000000000..ad4234f266baa --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/cart_with_simple_product_and_custom_options.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Option; +use Magento\Checkout\Model\Session; +use Magento\Framework\DataObject; +use Magento\Quote\Model\Quote; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_with_custom_options.php'; + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = Bootstrap::getObjectManager() + ->create(ProductRepositoryInterface::class); +$product = $productRepository->get('simple_with_custom_options'); + +$options = []; +/** @var $option Option */ +foreach ($product->getOptions() as $option) { + switch ($option->getGroupByType()) { + case ProductCustomOptionInterface::OPTION_GROUP_SELECT: + $value = key($option->getValues()); + break; + default: + $value = 'test'; + break; + } + $options[$option->getId()] = $value; +} + +$requestInfo = new DataObject(['qty' => 1, 'options' => $options]); + +/** @var $cart \Magento\Checkout\Model\Cart */ +$quote = Bootstrap::getObjectManager()->create(Quote::class); +$quote->setReservedOrderId('test_order_item_with_custom_options'); +$quote->addProduct($product, $requestInfo); +$quote->save(); + +/** @var $objectManager \Magento\TestFramework\ObjectManager */ +$objectManager = Bootstrap::getObjectManager(); +$objectManager->removeSharedInstance(Session::class); From 5b2b63be7043df02634896a626ec635df9322eb8 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Wed, 1 Aug 2018 16:46:20 +0300 Subject: [PATCH 195/211] MAGETWO-77742: [2.2.x] - Error message when uploading unsupported file format --- .../Test/StorefrontConfigurableProductChildSearchTest.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml index 43642b2f24385..cac379e11e79a 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml @@ -169,21 +169,21 @@ <waitForPageLoad stepKey="waitForStorefront"/> <fillField selector="{{StorefrontQuickSearchSection.searchPhrase}}" userInput="$$createConfigProductAttributeSelectOption1.option[store_labels][0][label]$$" stepKey="searchStorefront1"/> <click selector="{{StorefrontQuickSearchSection.searchButton}}" stepKey="clickSearch1"/> - <seeElement selector="{{StorefrontCategoryProductSection.ProductTitleByName('$$createConfigProduct.name$$')}}" stepKey="seeProduct1"/> + <seeElement selector="{{StorefrontCategoryProductSection.productTitleByName('$$createConfigProduct.name$$')}}" stepKey="seeProduct1"/> <!-- Quick search the storefront for the second attribute option --> <fillField selector="{{StorefrontQuickSearchSection.searchPhrase}}" userInput="$$createConfigProductAttributeOption2Multiselect.option[store_labels][0][label]$$" stepKey="searchStorefront2"/> <click selector="{{StorefrontQuickSearchSection.searchButton}}" stepKey="clickSearch2"/> - <seeElement selector="{{StorefrontCategoryProductSection.ProductTitleByName('$$createConfigProduct.name$$')}}" stepKey="seeProduct2"/> + <seeElement selector="{{StorefrontCategoryProductSection.productTitleByName('$$createConfigProduct.name$$')}}" stepKey="seeProduct2"/> <!-- Quick search the storefront for the first product description --> <fillField selector="{{StorefrontQuickSearchSection.searchPhrase}}" userInput="'$$createConfigChildProduct1.custom_attributes[short_description]$$'" stepKey="searchStorefront3"/> <click selector="{{StorefrontQuickSearchSection.searchButton}}" stepKey="clickSearch3"/> - <seeElement selector="{{StorefrontCategoryProductSection.ProductTitleByName('$$createConfigProduct.name$$')}}" stepKey="seeProduct3"/> + <seeElement selector="{{StorefrontCategoryProductSection.productTitleByName('$$createConfigProduct.name$$')}}" stepKey="seeProduct3"/> <!-- Quick search the storefront for the first product name --> <fillField selector="{{StorefrontQuickSearchSection.searchPhrase}}" userInput="'$$createConfigChildProduct1.name$$'" stepKey="searchStorefront4"/> <click selector="{{StorefrontQuickSearchSection.searchButton}}" stepKey="clickSearch4"/> - <seeElement selector="{{StorefrontCategoryProductSection.ProductTitleByName('$$createConfigProduct.name$$')}}" stepKey="seeProduct4"/> + <seeElement selector="{{StorefrontCategoryProductSection.productTitleByName('$$createConfigProduct.name$$')}}" stepKey="seeProduct4"/> </test> </tests> From f283e715cfd246b8599f2b521596d4843bc98a6b Mon Sep 17 00:00:00 2001 From: Daniel Ruf <daniel.ruf@ueberbit.de> Date: Thu, 2 Aug 2018 14:11:33 +0200 Subject: [PATCH 196/211] fix: add missing data-th selector for tables --- .../blank/Magento_Checkout/web/css/source/module/_cart.less | 2 +- app/design/frontend/Magento/blank/web/css/source/_extends.less | 2 +- .../luma/Magento_Checkout/web/css/source/module/_cart.less | 2 +- .../Magento/luma/Magento_Sales/web/css/source/_module.less | 2 +- app/design/frontend/Magento/luma/web/css/source/_extends.less | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_cart.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_cart.less index bb14a3c2521b0..d3d15019f0e87 100644 --- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_cart.less +++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_cart.less @@ -305,7 +305,7 @@ white-space: nowrap; width: 33%; - &:before { + &[data-th]:before { content: attr(data-th) ':'; display: block; font-weight: @font-weight__bold; diff --git a/app/design/frontend/Magento/blank/web/css/source/_extends.less b/app/design/frontend/Magento/blank/web/css/source/_extends.less index c177f91e9e7e8..6df78859a1a80 100644 --- a/app/design/frontend/Magento/blank/web/css/source/_extends.less +++ b/app/design/frontend/Magento/blank/web/css/source/_extends.less @@ -846,7 +846,7 @@ white-space: nowrap; width: 33%; - &:before { + &[data-th]:before { content: attr(data-th) ':'; display: block; font-weight: @font-weight__bold; diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less index 4b8db9202b195..10b917635bd40 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less @@ -464,7 +464,7 @@ white-space: nowrap; width: 33%; - &:before { + &[data-th]:before { content: attr(data-th); display: block; font-weight: @font-weight__semibold; 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 692f91ef463b1..a0f734b05cbd1 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 @@ -482,7 +482,7 @@ .options-label + .item-options-container, .item-options-container + .item-options-container { - &:before { + &[data-th]:before { content: attr(data-th) ':'; display: block; font-weight: @font-weight__bold; diff --git a/app/design/frontend/Magento/luma/web/css/source/_extends.less b/app/design/frontend/Magento/luma/web/css/source/_extends.less index 760ec9ed861a9..1292188102868 100644 --- a/app/design/frontend/Magento/luma/web/css/source/_extends.less +++ b/app/design/frontend/Magento/luma/web/css/source/_extends.less @@ -760,7 +760,7 @@ white-space: nowrap; width: 33%; - &:before { + &[data-th]:before { content: attr(data-th) ':'; display: block; font-weight: @font-weight__bold; From 43dd85edfb856bca5fb3fdd6d11cdaec00cc96eb Mon Sep 17 00:00:00 2001 From: Vlad Veselov <orlangur@users.noreply.github.com> Date: Fri, 3 Aug 2018 02:58:17 +0300 Subject: [PATCH 197/211] Fix coding style violations --- app/code/Magento/Eav/Api/Data/AttributeInterface.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Eav/Api/Data/AttributeInterface.php b/app/code/Magento/Eav/Api/Data/AttributeInterface.php index 1e87622244619..49da8f24a6201 100644 --- a/app/code/Magento/Eav/Api/Data/AttributeInterface.php +++ b/app/code/Magento/Eav/Api/Data/AttributeInterface.php @@ -1,18 +1,19 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Eav\Api\Data; +use Magento\Framework\Api\CustomAttributesDataInterface; +use Magento\Framework\Api\MetadataObjectInterface; + /** * Interface AttributeInterface * @api * @since 100.0.2 */ -interface AttributeInterface extends \Magento\Framework\Api\CustomAttributesDataInterface, - \Magento\Framework\Api\MetadataObjectInterface +interface AttributeInterface extends CustomAttributesDataInterface, MetadataObjectInterface { const ATTRIBUTE_ID = 'attribute_id'; From 9d3f6b6a1be830854c6277c00b4ce340728afc73 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Fri, 3 Aug 2018 10:03:07 +0300 Subject: [PATCH 198/211] MAGETWO-73342: Clicking on area around the label of a toggle element results in the element's state being changed --- .../Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml index eb87f410b770d..c59650cfee6c0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml @@ -21,7 +21,6 @@ <element name="fillOptionValueTitle" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody/tr[@data-repeat-index='{{var2}}']//label[text()='Title']/parent::span/parent::div//div[@class='admin__field-control']/input" parameterized="true"/> <element name="fillOptionValuePrice" type="input" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody/tr[@data-repeat-index='{{var2}}']//span[text()='Price']/parent::label/parent::div//div[@class='admin__control-addon']/input" parameterized="true"/> <element name="clickSelectPriceType" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody//tr[@data-repeat-index='{{var2}}']//label[text()='Price Type']/parent::span/parent::div//select" parameterized="true"/> - <element name="clickSelectPriceType" type="select" selector="//span[text()='{{var1}}']/parent::div/parent::div/parent::div//tbody//tr[@data-repeat-index='{{var2}}']//span[text()='Price Type']/parent::label/parent::div//select" parameterized="true"/> <!-- Elements that make it easier to select the most recently added element --> <element name="lastOptionTitle" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@class, '_required')]//input" /> <element name="lastOptionTypeParent" type="block" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@class, 'admin__action-multiselect-text')]" /> From 10b31a0fb82ed4f86ab8fd531368d3b712dfb424 Mon Sep 17 00:00:00 2001 From: Arnoud Beekman <arnoud.beekman@mediact.nl> Date: Sat, 28 Jul 2018 15:49:06 +0200 Subject: [PATCH 199/211] Fixed some minor css issue **lib/internal/Magento/Framework/View/Test/Unit/Url/_files/sourceImport.css:** + `@import` statements should be at the top **lib/web/css/docs/docs.css:** + Use `green` instead of `#green` --- .../Framework/View/Test/Unit/Url/_files/sourceImport.css | 4 ++-- lib/web/css/docs/docs.css | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Url/_files/sourceImport.css b/lib/internal/Magento/Framework/View/Test/Unit/Url/_files/sourceImport.css index d7ce9e81258db..420083613705f 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Url/_files/sourceImport.css +++ b/lib/internal/Magento/Framework/View/Test/Unit/Url/_files/sourceImport.css @@ -2,8 +2,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -body {background: url(body.gif);} @import url(../recursive.css); -p {background: url(1.gif?param);} @import url("deep/recursive.css"); +body {background: url(body.gif);} +p {background: url(1.gif?param);} h1 {background: url('../h1.gif#param');} h2 {background: url(../images/h2.gif?test);} diff --git a/lib/web/css/docs/docs.css b/lib/web/css/docs/docs.css index beb18b8b2194a..a3a372ad4eeb9 100644 --- a/lib/web/css/docs/docs.css +++ b/lib/web/css/docs/docs.css @@ -4187,7 +4187,7 @@ select { color: #000066; } .example-message-4:before { - background: #green; + background: green; width: 30px; content: ''; display: block; @@ -4230,7 +4230,7 @@ select { border: 5px solid transparent; height: 0; width: 0; - border-left-color: #green; + border-left-color: green; left: 30px; } .example-message-4 > *:first-child:after { From d2a55c406be0a3ed564f489e5e8dd22cf4d69e8c Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Sun, 5 Aug 2018 12:00:38 +0200 Subject: [PATCH 200/211] Added unit test for TransactionVoid class --- .../Http/Client/TransactionVoidTest.php | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 app/code/Magento/Braintree/Test/Unit/Gateway/Http/Client/TransactionVoidTest.php diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Http/Client/TransactionVoidTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Http/Client/TransactionVoidTest.php new file mode 100644 index 0000000000000..8291806d2a6e0 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Http/Client/TransactionVoidTest.php @@ -0,0 +1,86 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Braintree\Test\Unit\Gateway\Http\Client; + +use Braintree\Result\Successful; +use Magento\Braintree\Gateway\Http\Client\TransactionVoid; +use Magento\Braintree\Model\Adapter\BraintreeAdapter; +use Magento\Braintree\Model\Adapter\BraintreeAdapterFactory; +use Magento\Payment\Gateway\Http\ClientException; +use Magento\Payment\Gateway\Http\ConverterException; +use Magento\Payment\Gateway\Http\TransferInterface; +use Magento\Payment\Model\Method\Logger; +use PHPUnit_Framework_MockObject_MockObject as MockObject; +use Psr\Log\LoggerInterface; + +/** + * Class TransactionVoidTest + */ +class TransactionVoidTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var TransactionVoid + */ + private $transactionVoidModel; + + /** + * @var BraintreeAdapter|\PHPUnit_Framework_MockObject_MockObject + */ + private $adapterMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + /** @var LoggerInterface|MockObject $criticalLoggerMock */ + $criticalLoggerMock = $this->getMockForAbstractClass(LoggerInterface::class); + /** @var Logger|\PHPUnit_Framework_MockObject_MockObject $loggerMock */ + $loggerMock = $this->getMockBuilder(Logger::class) + ->disableOriginalConstructor() + ->getMock(); + $this->adapterMock = $this->getMockBuilder(BraintreeAdapter::class) + ->disableOriginalConstructor() + ->getMock(); + /** @var BraintreeAdapterFactory|MockObject $adapterFactoryMock */ + $adapterFactoryMock = $this->getMockBuilder(BraintreeAdapterFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $adapterFactoryMock->method('create') + ->willReturn($this->adapterMock); + + $this->transactionVoidModel = new TransactionVoid($criticalLoggerMock, $loggerMock, $adapterFactoryMock); + } + + /** + * @throws ClientException + * @throws ConverterException + */ + public function testVoidRequestWithStoreId() + { + $transactionId = '11223344'; + $data = [ + 'store_id' => 0, + 'transaction_id' => $transactionId + ]; + $successfulResponse = new Successful(); + + /** @var TransferInterface|\PHPUnit_Framework_MockObject_MockObject $transferObjectMock */ + $transferObjectMock = $this->createMock(TransferInterface::class); + $transferObjectMock->method('getBody') + ->willReturn($data); + $this->adapterMock->expects($this->once()) + ->method('void') + ->with($transactionId) + ->willReturn($successfulResponse); + + $response = $this->transactionVoidModel->placeRequest($transferObjectMock); + + self::assertEquals($successfulResponse, $response['object']); + } +} From ff34c959dda77982a7bcc05226d35d0d66733af4 Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Sun, 5 Aug 2018 12:12:37 +0200 Subject: [PATCH 201/211] Added unit test for TransactionRefundTest class --- .../Http/Client/TransactionRefundTest.php | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 app/code/Magento/Braintree/Test/Unit/Gateway/Http/Client/TransactionRefundTest.php diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Http/Client/TransactionRefundTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Http/Client/TransactionRefundTest.php new file mode 100644 index 0000000000000..038687d8821d7 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Http/Client/TransactionRefundTest.php @@ -0,0 +1,89 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Braintree\Test\Unit\Gateway\Http\Client; + +use Braintree\Result\Successful; +use Magento\Braintree\Gateway\Http\Client\TransactionRefund; +use Magento\Braintree\Model\Adapter\BraintreeAdapter; +use Magento\Braintree\Model\Adapter\BraintreeAdapterFactory; +use Magento\Payment\Gateway\Http\ClientException; +use Magento\Payment\Gateway\Http\ConverterException; +use Magento\Payment\Gateway\Http\TransferInterface; +use Magento\Braintree\Gateway\Request\PaymentDataBuilder; +use Magento\Payment\Model\Method\Logger; +use PHPUnit_Framework_MockObject_MockObject as MockObject; +use Psr\Log\LoggerInterface; + +/** + * Class TransactionRefundTest + */ +class TransactionRefundTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var TransactionRefund + */ + private $transactionRefundModel; + + /** + * @var BraintreeAdapter|\PHPUnit_Framework_MockObject_MockObject + */ + private $adapterMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + /** @var LoggerInterface|MockObject $criticalLoggerMock */ + $criticalLoggerMock = $this->getMockForAbstractClass(LoggerInterface::class); + /** @var Logger|\PHPUnit_Framework_MockObject_MockObject $loggerMock */ + $loggerMock = $this->getMockBuilder(Logger::class) + ->disableOriginalConstructor() + ->getMock(); + $this->adapterMock = $this->getMockBuilder(BraintreeAdapter::class) + ->disableOriginalConstructor() + ->getMock(); + /** @var BraintreeAdapterFactory|MockObject $adapterFactoryMock */ + $adapterFactoryMock = $this->getMockBuilder(BraintreeAdapterFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $adapterFactoryMock->method('create') + ->willReturn($this->adapterMock); + + $this->transactionRefundModel = new TransactionRefund($criticalLoggerMock, $loggerMock, $adapterFactoryMock); + } + + /** + * @throws ClientException + * @throws ConverterException + */ + public function testRefundRequestWithStoreId() + { + $transactionId = '11223344'; + $refundAmount = 10; + $data = [ + 'store_id' => 0, + 'transaction_id' => $transactionId, + PaymentDataBuilder::AMOUNT => $refundAmount + ]; + $successfulResponse = new Successful(); + + /** @var TransferInterface|\PHPUnit_Framework_MockObject_MockObject $transferObjectMock */ + $transferObjectMock = $this->createMock(TransferInterface::class); + $transferObjectMock->method('getBody') + ->willReturn($data); + $this->adapterMock->expects($this->once()) + ->method('refund') + ->with($transactionId, $refundAmount) + ->willReturn($successfulResponse); + + $response = $this->transactionRefundModel->placeRequest($transferObjectMock); + + self::assertEquals($successfulResponse, $response['object']); + } +} From 4ea10709f185c9a692982a7c027becbb49aac2f4 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Sun, 5 Aug 2018 13:49:03 +0300 Subject: [PATCH 202/211] Fixed breaking incompatible change --- app/code/Magento/Cms/Controller/Index/Index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Cms/Controller/Index/Index.php b/app/code/Magento/Cms/Controller/Index/Index.php index 3197bac650e96..c027bd1a2b717 100644 --- a/app/code/Magento/Cms/Controller/Index/Index.php +++ b/app/code/Magento/Cms/Controller/Index/Index.php @@ -21,7 +21,7 @@ class Index extends \Magento\Framework\App\Action\Action /** * @var ForwardFactory */ - private $resultForwardFactory; + protected $resultForwardFactory; /** * @var ScopeConfigInterface From 72a2ef6b17a4ae88a12025118ba63d4360825c97 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Mon, 6 Aug 2018 11:40:06 +0300 Subject: [PATCH 203/211] MAGETWO-86609: Disabled wishlist product still appears in wishlist --- app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php | 1 - .../Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml | 1 - 2 files changed, 2 deletions(-) diff --git a/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php b/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php index 82a42604c6283..76c110c41b98e 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Grid/AbstractGrid.php @@ -409,7 +409,6 @@ protected function _addCustomFilter($collection, $filterData) } /** - * * @return array * @throws \Magento\Framework\Exception\LocalizedException */ diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml index 59be71dfd6978..8965b6d7c5754 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml @@ -76,7 +76,6 @@ <!-- Add product visible on default store to wishlist --> <amOnPage url="$$product.name$$.html" stepKey="navigateToProductPageOnDefaultStore"/> <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$product.name$$" stepKey="assertFirstProductNameTitle"/> - <click stepKey="addFirstProductToWishlist" selector="{{StorefrontProductPageSection.addToWishlist}}"/> <!-- Switch to second store and add second product (visible on second store) to wishlist --> <click stepKey="ClickSwitchStoreButtonOnDefaultStore" selector="{{StorefrontFooterSection.SwitchStoreButton}}"/> <click stepKey="SelectSecondStoreToSwitchOn" selector="{{StorefrontFooterSection.StoreLink($$storeGroup.group[name]$$)}}"/> From 457a7678b3cc8f1c9b5480e492e2550f16838bc3 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Mon, 6 Aug 2018 11:56:38 +0300 Subject: [PATCH 204/211] MAGETWO-86609: Disabled wishlist product still appears in wishlist --- .../Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml index 8965b6d7c5754..21a32056cf47b 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml @@ -83,7 +83,6 @@ <amOnPage url="$$secondProduct.name$$.html" stepKey="navigateToProductPageOnSecondStore"/> <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$secondProduct.name$$" stepKey="assertSecondProductNameTitle"/> <click stepKey="addSecondProductToWishlist" selector="{{StorefrontProductPageSection.AddToWishlist}}"/> - <click stepKey="addSecondProductToWishlist" selector="{{StorefrontProductPageSection.addToWishlist}}"/> <see stepKey="seeProduct1InWishlistOnSecondStore" userInput="$$product.name$$" selector="{{StorefrontCustomerWishlistSection.productItemNameText}}"/> <see stepKey="seeProduct2InWishlistOnSecondStore" userInput="$$secondProduct.name$$" selector="{{StorefrontCustomerWishlistSection.productItemNameText}}"/> <click stepKey="ClickSwitchStoreButtonOnSecondStore" selector="{{StorefrontFooterSection.SwitchStoreButton}}"/> From 034b01afe91d8f70cb088bc1c6c0f8994839066e Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Mon, 6 Aug 2018 12:14:45 +0300 Subject: [PATCH 205/211] MAGETWO-86609: Disabled wishlist product still appears in wishlist --- .../Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml index 21a32056cf47b..b9ea513288c81 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml @@ -76,14 +76,14 @@ <!-- Add product visible on default store to wishlist --> <amOnPage url="$$product.name$$.html" stepKey="navigateToProductPageOnDefaultStore"/> <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$product.name$$" stepKey="assertFirstProductNameTitle"/> + <click stepKey="addFirstProductToWishlist" selector="{{StorefrontProductPageSection.addToWishlist}}"/> <!-- Switch to second store and add second product (visible on second store) to wishlist --> <click stepKey="ClickSwitchStoreButtonOnDefaultStore" selector="{{StorefrontFooterSection.SwitchStoreButton}}"/> <click stepKey="SelectSecondStoreToSwitchOn" selector="{{StorefrontFooterSection.StoreLink($$storeGroup.group[name]$$)}}"/> <!-- Verify that both products are visible in wishlist on both stores --> <amOnPage url="$$secondProduct.name$$.html" stepKey="navigateToProductPageOnSecondStore"/> <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$secondProduct.name$$" stepKey="assertSecondProductNameTitle"/> - <click stepKey="addSecondProductToWishlist" selector="{{StorefrontProductPageSection.AddToWishlist}}"/> - <see stepKey="seeProduct1InWishlistOnSecondStore" userInput="$$product.name$$" selector="{{StorefrontCustomerWishlistSection.productItemNameText}}"/> + <click stepKey="addSecondProductToWishlist" selector="{{StorefrontProductPageSection.addToWishlist}}"/> <see stepKey="seeProduct2InWishlistOnSecondStore" userInput="$$secondProduct.name$$" selector="{{StorefrontCustomerWishlistSection.productItemNameText}}"/> <click stepKey="ClickSwitchStoreButtonOnSecondStore" selector="{{StorefrontFooterSection.SwitchStoreButton}}"/> <click stepKey="SelectDefaultStoreToSwitchOn" selector="{{StorefrontFooterSection.StoreLink('Main Website Store')}}"/> From e1557d0966f3f1b6924df58e5f9f528130d14197 Mon Sep 17 00:00:00 2001 From: NazarKlovanych <nazar.klovanich@transoftgroup.com> Date: Mon, 6 Aug 2018 13:01:14 +0300 Subject: [PATCH 206/211] Fix failed CacheTest & PageCacheTest --- .../Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php | 4 ++-- .../Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php index abc2a6fdc5ae2..39b95953c6347 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php @@ -39,7 +39,7 @@ protected function setUp() public function testGetOptions() { $options = $this->configOptionsList->getOptions(); - $this->assertCount(4, $options); + $this->assertCount(5, $options); $this->assertArrayHasKey(0, $options); $this->assertInstanceOf(SelectConfigOption::class, $options[0]); @@ -124,7 +124,7 @@ public function testValidateWithValidInput() ]; $this->validatorMock->expects($this->once()) ->method('isValidConnection') - ->with(['host'=>'localhost', 'db'=>'', 'port'=>'']) + ->with(['host'=>'localhost', 'db'=>'', 'port'=>'', 'password'=> '']) ->willReturn(true); $errors = $this->configOptionsList->validate($options, $this->deploymentConfigMock); diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php index b9cd137530aa9..ed0e567820ad1 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php @@ -39,7 +39,7 @@ protected function setUp() public function testGetOptions() { $options = $this->configList->getOptions(); - $this->assertCount(5, $options); + $this->assertCount(6, $options); $this->assertArrayHasKey(0, $options); $this->assertInstanceOf(SelectConfigOption::class, $options[0]); From e5e4af18485cac2eb7f5df57d73e7426334267fd Mon Sep 17 00:00:00 2001 From: Oleksandr Dubovyk <odubovyk@magento.com> Date: Mon, 6 Aug 2018 18:57:30 +0300 Subject: [PATCH 207/211] MAGETWO-93054: Admin logs don't detail quantity changes - Fixed static test --- app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php index 595d485b981a3..689b946093fb6 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php @@ -120,7 +120,6 @@ public function execute() $extendedData = $data; $extendedData['can_save_custom_options'] = $canSaveCustomOptions; $this->copyToStores($extendedData, $productId); - $this->messageManager->addSuccessMessage(__('You saved the product.')); $this->getDataPersistor()->clear('catalog_product'); if ($product->getSku() != $originalSku) { From 0fc15747f05571cd6b164c01e1f1b6168e37a468 Mon Sep 17 00:00:00 2001 From: vgelani <vishalgelani99@gmail.com> Date: Wed, 4 Jul 2018 19:06:19 +0530 Subject: [PATCH 208/211] Fixed add to wishlist issue on product price 0 --- app/code/Magento/Catalog/Pricing/Price/RegularPrice.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Pricing/Price/RegularPrice.php b/app/code/Magento/Catalog/Pricing/Price/RegularPrice.php index 609255d852da3..1397ceb6bf71c 100644 --- a/app/code/Magento/Catalog/Pricing/Price/RegularPrice.php +++ b/app/code/Magento/Catalog/Pricing/Price/RegularPrice.php @@ -22,14 +22,14 @@ class RegularPrice extends AbstractPrice implements BasePriceProviderInterface /** * Get price value * - * @return float|bool + * @return float */ public function getValue() { if ($this->value === null) { $price = $this->product->getPrice(); $priceInCurrentCurrency = $this->priceCurrency->convertAndRound($price); - $this->value = $priceInCurrentCurrency ? floatval($priceInCurrentCurrency) : false; + $this->value = $priceInCurrentCurrency ? floatval($priceInCurrentCurrency) : 0; } return $this->value; } From 34f2ef765075655e9756e8454cc41f5f629fc2d6 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Mon, 6 Aug 2018 22:31:03 +0300 Subject: [PATCH 209/211] Removed strval from obsolete methods, PHPCS rule should be implemented --- app/design/frontend/Magento/luma/web/css/source/_extends.less | 4 +++- .../testsuite/Magento/Test/Legacy/_files/obsolete_methods.php | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/design/frontend/Magento/luma/web/css/source/_extends.less b/app/design/frontend/Magento/luma/web/css/source/_extends.less index 1292188102868..8d1ce2b2f896e 100644 --- a/app/design/frontend/Magento/luma/web/css/source/_extends.less +++ b/app/design/frontend/Magento/luma/web/css/source/_extends.less @@ -3,6 +3,8 @@ // * See COPYING.txt for license details. // */ +@_column-number: 1; + // // List default styles reset // --------------------------------------------- @@ -705,7 +707,7 @@ // --------------------------------------------- @abs-form-field-revert-column-1: { - .lib-form-field-column-number(@_column-number: 1); + .lib-form-field-column-number(@_column-number); }; .media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) { diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php index 9c5108ef7dd9e..105d44d6016c5 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php @@ -2523,6 +2523,5 @@ ['_isAttributeValueEmpty', 'Magento\Catalog\Model\ResourceModel\AbstractResource'], ['var_dump', ''], ['each', ''], - ['strval', ''], ['isOrderIncrementIdUsed', 'Magento\Quote\Model\ResourceModel\Quote', 'Magento\Sales\Model\OrderIncrementIdChecker::isIncrementIdUsed'] ]; From 366d579e4e49902437dbca38807a892bd1b5368a Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Tue, 7 Aug 2018 11:23:12 +0200 Subject: [PATCH 210/211] Added unit test for instant purchase paypal token formatter --- .../PayPal/TokenFormatterTest.php | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/PayPal/TokenFormatterTest.php diff --git a/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/PayPal/TokenFormatterTest.php b/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/PayPal/TokenFormatterTest.php new file mode 100644 index 0000000000000..ebb9a3e8bfb59 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/PayPal/TokenFormatterTest.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Braintree\Test\Unit\Model\InstantPurchase\Paypal; + +use Magento\Braintree\Model\InstantPurchase\CreditCard\TokenFormatter as PaypalTokenFormatter; +use Magento\Vault\Api\Data\PaymentTokenInterface; + +class TokenFormatterTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var PaymentTokenInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $paymentTokenMock; + + /** + * @var PaypalTokenFormatter + */ + private $paypalTokenFormatter; + + /** + * @var array + */ + private $tokenDetails = [ + 'type' => 'visa', + 'maskedCC' => '4444************9999', + 'expirationDate' => '07-07-2025' + ]; + + protected function setUp() + { + $this->paymentTokenMock = $this->getMockBuilder(PaymentTokenInterface::class) + ->getMockForAbstractClass(); + + $this->paypalTokenFormatter = new PaypalTokenFormatter(); + } + + public function testFormatPaymentTokenWithKnownCardType() + { + $this->tokenDetails['type'] = key(PaypalTokenFormatter::$baseCardTypes); + $this->paymentTokenMock->expects($this->once()) + ->method('getTokenDetails') + ->willReturn(json_encode($this->tokenDetails)); + + $formattedString = sprintf( + '%s: %s, %s: %s (%s: %s)', + __('Credit Card'), + reset(PaypalTokenFormatter::$baseCardTypes), + __('ending'), + $this->tokenDetails['maskedCC'], + __('expires'), + $this->tokenDetails['expirationDate'] + ); + + self::assertEquals($formattedString, $this->paypalTokenFormatter->formatPaymentToken($this->paymentTokenMock)); + } + + public function testFormatPaymentTokenWithUnknownCardType() + { + $this->paymentTokenMock->expects($this->once()) + ->method('getTokenDetails') + ->willReturn(json_encode($this->tokenDetails)); + + $formattedString = sprintf( + '%s: %s, %s: %s (%s: %s)', + __('Credit Card'), + $this->tokenDetails['type'], + __('ending'), + $this->tokenDetails['maskedCC'], + __('expires'), + $this->tokenDetails['expirationDate'] + ); + + self::assertEquals($formattedString, $this->paypalTokenFormatter->formatPaymentToken($this->paymentTokenMock)); + } + + public function testFormatPaymentTokenWithWrongData() + { + unset($this->tokenDetails['type']); + + $this->paymentTokenMock->expects($this->once()) + ->method('getTokenDetails') + ->willReturn(json_encode($this->tokenDetails)); + self::expectException('\InvalidArgumentException'); + + $this->paypalTokenFormatter->formatPaymentToken($this->paymentTokenMock); + } +} From ad7e2509bd61e25fcfdfda92fe3eca7b936cffab Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Tue, 7 Aug 2018 22:07:16 +0300 Subject: [PATCH 211/211] Fixed typo in namespace --- .../Unit/Model/InstantPurchase/PayPal/TokenFormatterTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/PayPal/TokenFormatterTest.php b/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/PayPal/TokenFormatterTest.php index ebb9a3e8bfb59..e552f4f39f1c4 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/PayPal/TokenFormatterTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/InstantPurchase/PayPal/TokenFormatterTest.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\Braintree\Test\Unit\Model\InstantPurchase\Paypal; +namespace Magento\Braintree\Test\Unit\Model\InstantPurchase\PayPal; use Magento\Braintree\Model\InstantPurchase\CreditCard\TokenFormatter as PaypalTokenFormatter; use Magento\Vault\Api\Data\PaymentTokenInterface;