From 607773051b9a7c409d69be3578909de114295749 Mon Sep 17 00:00:00 2001 From: Roman Leshchenko Date: Wed, 11 Jul 2018 14:34:50 +0300 Subject: [PATCH 0001/1295] MAGETWO-90468: Added posibility to use captcha on share wishlist page --- .../Wishlist/Controller/Index/Send.php | 41 ++++++++++++++++++- app/code/Magento/Wishlist/etc/config.xml | 22 ++++++++++ .../frontend/layout/wishlist_index_share.xml | 17 +++++++- .../view/frontend/templates/sharing.phtml | 1 + 4 files changed, 79 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Wishlist/Controller/Index/Send.php b/app/code/Magento/Wishlist/Controller/Index/Send.php index c2389af6a2282..780f4d94f04d9 100644 --- a/app/code/Magento/Wishlist/Controller/Index/Send.php +++ b/app/code/Magento/Wishlist/Controller/Index/Send.php @@ -8,11 +8,15 @@ use Magento\Framework\App\Action; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ResponseInterface; use Magento\Framework\Exception\NotFoundException; use Magento\Framework\Session\Generic as WishlistSession; use Magento\Store\Model\StoreManagerInterface; use Magento\Framework\Controller\ResultFactory; use Magento\Framework\View\Result\Layout as ResultLayout; +use Magento\Captcha\Helper\Data as CaptchaHelper; +use Magento\Captcha\Observer\CaptchaStringResolver; +use Magento\Framework\App\ObjectManager; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -69,6 +73,16 @@ class Send extends \Magento\Wishlist\Controller\AbstractIndex */ protected $storeManager; + /** + * @var CaptchaHelper|null + */ + protected $captchaHelper; + + /** + * @var CaptchaStringResolver|null + */ + protected $captchaStringResolver; + /** * @param Action\Context $context * @param \Magento\Framework\Data\Form\FormKey\Validator $formKeyValidator @@ -81,6 +95,8 @@ class Send extends \Magento\Wishlist\Controller\AbstractIndex * @param WishlistSession $wishlistSession * @param ScopeConfigInterface $scopeConfig * @param StoreManagerInterface $storeManager + * @param CaptchaHelper $captchaHelper|null + * @param CaptchaStringResolver $captchaStringResolver|null * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -94,7 +110,9 @@ public function __construct( \Magento\Customer\Helper\View $customerHelperView, WishlistSession $wishlistSession, ScopeConfigInterface $scopeConfig, - StoreManagerInterface $storeManager + StoreManagerInterface $storeManager, + CaptchaHelper $captchaHelper = null, + CaptchaStringResolver $captchaStringResolver = null ) { $this->_formKeyValidator = $formKeyValidator; $this->_customerSession = $customerSession; @@ -106,6 +124,9 @@ public function __construct( $this->wishlistSession = $wishlistSession; $this->scopeConfig = $scopeConfig; $this->storeManager = $storeManager; + $this->captchaHelper = $captchaHelper ?: ObjectManager::getInstance()->get(CaptchaHelper::class); + $this->captchaStringResolver = $captchaStringResolver ? + : ObjectManager::getInstance()->get(CaptchaStringResolver::class); parent::__construct($context); } @@ -117,16 +138,34 @@ public function __construct( * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @throws \Zend_Validate_Exception */ public function execute() { /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); + $captchaFormName = 'share_wishlist_form'; + /** @var \Magento\Captcha\Model\DefaultModel $captchaModel */ + $captchaModel = $this->captchaHelper->getCaptcha($captchaFormName); + if (!$this->_formKeyValidator->validate($this->getRequest())) { $resultRedirect->setPath('*/*/'); return $resultRedirect; } + if ($captchaModel->isRequired()) { + $word = $this->captchaStringResolver->resolve( + $this->getRequest(), + $captchaFormName + ); + + if (!$captchaModel->isCorrect($word)) { + $this->messageManager->addErrorMessage(__('Incorrect CAPTCHA')); + $resultRedirect->setPath('*/*/share'); + return $resultRedirect; + } + } + $wishlist = $this->wishlistProvider->getWishlist(); if (!$wishlist) { throw new NotFoundException(__('Page not found.')); diff --git a/app/code/Magento/Wishlist/etc/config.xml b/app/code/Magento/Wishlist/etc/config.xml index 1fec2d1baf9d4..d0e7354df41ac 100644 --- a/app/code/Magento/Wishlist/etc/config.xml +++ b/app/code/Magento/Wishlist/etc/config.xml @@ -17,6 +17,28 @@ 10 255 + + + 1 + + + + + + + + + + + + + + + + 1 + + + diff --git a/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_share.xml b/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_share.xml index 09d8675f6b37e..08e1da699e2cb 100644 --- a/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_share.xml +++ b/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_share.xml @@ -9,7 +9,22 @@ - + + + + share_wishlist_form + + 230 + + + 50 + + + + + + + diff --git a/app/code/Magento/Wishlist/view/frontend/templates/sharing.phtml b/app/code/Magento/Wishlist/view/frontend/templates/sharing.phtml index 430ebd384c82b..ff01cb4532cc7 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/sharing.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/sharing.phtml @@ -40,6 +40,7 @@ + getChildHtml('captcha'); ?>
- \ No newline at end of file + From 66f06398a11841df014464f77cca94fe01678e4e Mon Sep 17 00:00:00 2001 From: Roman Leshchenko Date: Thu, 26 Jul 2018 14:11:30 +0300 Subject: [PATCH 0022/1295] MAGETWO-90516: Wrong custom option behavior --- .../Product/View/Options/View/Checkable.php | 2 +- .../fieldset/options/view/checkable.phtml | 98 +++++++++---------- 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Product/View/Options/View/Checkable.php b/app/code/Magento/Catalog/Block/Product/View/Options/View/Checkable.php index 45d7531a83623..6adaf56296439 100644 --- a/app/code/Magento/Catalog/Block/Product/View/Options/View/Checkable.php +++ b/app/code/Magento/Catalog/Block/Product/View/Options/View/Checkable.php @@ -57,4 +57,4 @@ public function getCurrencyByStore(ProductCustomOptionValuesInterface $value) : false ); } -} \ No newline at end of file +} diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/view/checkable.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/view/checkable.phtml index 827843c8a58bc..99f7018846d0a 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/view/checkable.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/view/checkable.phtml @@ -4,39 +4,38 @@ */ $option = $this->getOption(); -if ($option): ?> - getProduct()->getPreconfiguredValues()->getData('options/' . $option->getId()); - $optionType = $option->getType(); - $arraySign = $optionType === 'checkbox' ? '[]' : ''; - $count = 1; - ?> - -
+if ($option) : ?> +getProduct()->getPreconfiguredValues()->getData('options/' . $option->getId()); +$optionType = $option->getType(); +$arraySign = $optionType === 'checkbox' ? '[]' : ''; +$count = 1; +?> - -
- - +
getValues() as $value) : ?> From 3b3edd930f4bf5a05c500dcb4722d1389f309f45 Mon Sep 17 00:00:00 2001 From: Roman Leshchenko Date: Wed, 29 Aug 2018 22:55:18 +0300 Subject: [PATCH 0043/1295] MAGETWO-90516: Wrong custom option behavior --- .../Catalog/Block/Product/View/Options/Select/Multiple.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Block/Product/View/Options/Select/Multiple.php b/app/code/Magento/Catalog/Block/Product/View/Options/Select/Multiple.php index 6395a493f7b44..56702919da5d5 100644 --- a/app/code/Magento/Catalog/Block/Product/View/Options/Select/Multiple.php +++ b/app/code/Magento/Catalog/Block/Product/View/Options/Select/Multiple.php @@ -12,7 +12,7 @@ use Magento\Framework\View\Element\Html\Select; /** - * CLass represents necessary logic for dropdown and multiselect option types + * Class represents necessary logic for dropdown and multiselect option types */ class Multiple extends AbstractOptions { From c5cbfc0da9786b67a6908f9df377a2a020f54272 Mon Sep 17 00:00:00 2001 From: Roman Leshchenko Date: Fri, 31 Aug 2018 16:45:53 +0300 Subject: [PATCH 0044/1295] MAGETWO-92728: Fixed wrong admin notifications behavior --- .../Block/Grid/Renderer/Actions.php | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php index 6f0e42bdcbef1..3c4549ba8017c 100644 --- a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php +++ b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php @@ -37,28 +37,35 @@ public function __construct( */ public function render(\Magento\Framework\DataObject $row) { - $readDetailsHtml = $row->getUrl() ? '' . + $readDetailsHtml = $row->getUrl() ? '' . __('Read Details') . '' : ''; - $markAsReadHtml = !$row->getIsRead() ? '' . __( - 'Mark as Read' - ) . '' : ''; + $markAsReadHtml = !$row->getIsRead() ? '' . __( + 'Mark as Read' + ) . '' : ''; $encodedUrl = $this->_urlHelper->getEncodedUrl(); return sprintf( '%s%s%s', $readDetailsHtml, $markAsReadHtml, - $this->getUrl( - '*/*/remove/', - [ - '_current' => true, - 'id' => $row->getId(), - \Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED => $encodedUrl - ] + $this->escapeHtml( + $this->getUrl( + '*/*/remove/', + [ + '_current' => true, + 'id' => $row->getId(), + \Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED => $encodedUrl + ] + ) ), __('Are you sure?'), __('Remove') From c155cf4bfddb75c0f302c73fb38ae4ea77851e07 Mon Sep 17 00:00:00 2001 From: roman Date: Thu, 6 Sep 2018 12:34:38 +0300 Subject: [PATCH 0045/1295] MAGETWO-92728: Fixed wrong admin notifications behavior --- .../AdminNotification/Block/Grid/Renderer/Actions.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php index 3c4549ba8017c..bc25f56faaf53 100644 --- a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php +++ b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php @@ -38,16 +38,14 @@ public function __construct( public function render(\Magento\Framework\DataObject $row) { $readDetailsHtml = $row->getUrl() ? '' . __('Read Details') . '' : ''; $markAsReadHtml = !$row->getIsRead() ? '' . __( 'Mark as Read' ) . '' : ''; @@ -57,7 +55,6 @@ public function render(\Magento\Framework\DataObject $row) '%s%s%s', $readDetailsHtml, $markAsReadHtml, - $this->escapeHtml( $this->getUrl( '*/*/remove/', [ @@ -65,8 +62,7 @@ public function render(\Magento\Framework\DataObject $row) 'id' => $row->getId(), \Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED => $encodedUrl ] - ) - ), + ), __('Are you sure?'), __('Remove') ); From f854982410be60c809549d1aa052a3fce14dc041 Mon Sep 17 00:00:00 2001 From: roman Date: Thu, 6 Sep 2018 12:36:40 +0300 Subject: [PATCH 0046/1295] MAGETWO-92728: Fixed wrong admin notifications behavior --- .../Block/Grid/Renderer/Actions.php | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php index bc25f56faaf53..e3ebd8c16fe3a 100644 --- a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php +++ b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php @@ -44,8 +44,8 @@ public function render(\Magento\Framework\DataObject $row) $markAsReadHtml = !$row->getIsRead() ? '' . __( 'Mark as Read' ) . '' : ''; @@ -55,14 +55,14 @@ public function render(\Magento\Framework\DataObject $row) '%s%s%s', $readDetailsHtml, $markAsReadHtml, - $this->getUrl( - '*/*/remove/', - [ - '_current' => true, - 'id' => $row->getId(), - \Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED => $encodedUrl - ] - ), + $this->getUrl( + '*/*/remove/', + [ + '_current' => true, + 'id' => $row->getId(), + \Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED => $encodedUrl + ] + ), __('Are you sure?'), __('Remove') ); From f6fe81eda4d7be7aa0c87694026fa378f7e6aeef Mon Sep 17 00:00:00 2001 From: roman Date: Mon, 24 Sep 2018 13:34:45 +0300 Subject: [PATCH 0047/1295] MAGETWO-90516: Wrong custom option behavior --- .../Product/View/Options/Type/Select.php | 4 +- .../Options/{ => Type}/Select/Checkable.php | 5 +- .../Options/{ => Type}/Select/Multiple.php | 2 +- .../fieldset/options/view/checkable.phtml | 113 ++++++++++-------- 4 files changed, 67 insertions(+), 57 deletions(-) rename app/code/Magento/Catalog/Block/Product/View/Options/{ => Type}/Select/Checkable.php (87%) rename app/code/Magento/Catalog/Block/Product/View/Options/{ => Type}/Select/Multiple.php (98%) diff --git a/app/code/Magento/Catalog/Block/Product/View/Options/Type/Select.php b/app/code/Magento/Catalog/Block/Product/View/Options/Type/Select.php index ff8f1ea311305..959a6e76dc1ab 100644 --- a/app/code/Magento/Catalog/Block/Product/View/Options/Type/Select.php +++ b/app/code/Magento/Catalog/Block/Product/View/Options/Type/Select.php @@ -7,8 +7,8 @@ namespace Magento\Catalog\Block\Product\View\Options\Type; use Magento\Catalog\Api\Data\ProductCustomOptionInterface; -use Magento\Catalog\Block\Product\View\Options\Select\CheckableFactory; -use Magento\Catalog\Block\Product\View\Options\Select\MultipleFactory; +use Magento\Catalog\Block\Product\View\Options\Type\Select\CheckableFactory; +use Magento\Catalog\Block\Product\View\Options\Type\Select\MultipleFactory; use Magento\Framework\App\ObjectManager; use Magento\Framework\View\Element\Template\Context; use Magento\Framework\Pricing\Helper\Data; diff --git a/app/code/Magento/Catalog/Block/Product/View/Options/Select/Checkable.php b/app/code/Magento/Catalog/Block/Product/View/Options/Type/Select/Checkable.php similarity index 87% rename from app/code/Magento/Catalog/Block/Product/View/Options/Select/Checkable.php rename to app/code/Magento/Catalog/Block/Product/View/Options/Type/Select/Checkable.php index eac733adab5da..2b000b1e5105d 100644 --- a/app/code/Magento/Catalog/Block/Product/View/Options/Select/Checkable.php +++ b/app/code/Magento/Catalog/Block/Product/View/Options/Type/Select/Checkable.php @@ -4,13 +4,10 @@ * See COPYING.txt for license details. */ -namespace Magento\Catalog\Block\Product\View\Options\Select; +namespace Magento\Catalog\Block\Product\View\Options\Type\Select; use Magento\Catalog\Api\Data\ProductCustomOptionValuesInterface; use Magento\Catalog\Block\Product\View\Options\AbstractOptions; -use Magento\Framework\View\Element\Template\Context; -use Magento\Framework\Pricing\Helper\Data; -use Magento\Catalog\Helper\Data as CatalogHelper; /** * Represent necessary logic for checkbox and radio button option type diff --git a/app/code/Magento/Catalog/Block/Product/View/Options/Select/Multiple.php b/app/code/Magento/Catalog/Block/Product/View/Options/Type/Select/Multiple.php similarity index 98% rename from app/code/Magento/Catalog/Block/Product/View/Options/Select/Multiple.php rename to app/code/Magento/Catalog/Block/Product/View/Options/Type/Select/Multiple.php index 56702919da5d5..20164b0622a6d 100644 --- a/app/code/Magento/Catalog/Block/Product/View/Options/Select/Multiple.php +++ b/app/code/Magento/Catalog/Block/Product/View/Options/Type/Select/Multiple.php @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -namespace Magento\Catalog\Block\Product\View\Options\Select; +namespace Magento\Catalog\Block\Product\View\Options\Type\Select; use Magento\Catalog\Block\Product\View\Options\AbstractOptions; use Magento\Catalog\Api\Data\ProductCustomOptionInterface; diff --git a/app/code/Magento/Catalog/view/base/templates/product/composite/fieldset/options/view/checkable.phtml b/app/code/Magento/Catalog/view/base/templates/product/composite/fieldset/options/view/checkable.phtml index ad8e87414d418..0e2635f27c4b9 100644 --- a/app/code/Magento/Catalog/view/base/templates/product/composite/fieldset/options/view/checkable.phtml +++ b/app/code/Magento/Catalog/view/base/templates/product/composite/fieldset/options/view/checkable.phtml @@ -4,84 +4,97 @@ * See COPYING.txt for license details. */ -use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +// @codingStandardsIgnoreFile +use Magento\Catalog\Model\Product\Option; /** - * @var \Magento\Catalog\Block\Product\View\Options\Select\Checkable $block + * @var \Magento\Catalog\Block\Product\View\Options\Type\Select\Checkable $block */ $option = $block->getOption(); - if ($option) : ?> -getPreconfiguredValue($option); -$optionType = $option->getType(); -$arraySign = $optionType === ProductCustomOptionInterface::OPTION_TYPE_CHECKBOX ? '[]' : ''; -$count = 1; -?> + getPreconfiguredValue($option); + $optionType = $option->getType(); + $arraySign = $optionType === Option::OPTION_TYPE_CHECKBOX ? '[]' : ''; + $count = 1; + ?> -
- - -
- - -
- +
+ getIsRequire()): ?> +
+ + +
+ getValues() as $value) : ?> getOptionTypeId(), $configValue) ? 'checked' : ''; } else { $checked = $configValue == $value->getOptionTypeId() ? 'checked' : ''; } - $dataSelector = 'options[' . $option->getId() . ']'; if ($arraySign) { $dataSelector .= '[' . $value->getOptionTypeId() . ']'; } ?> -
- getIsRequire() ? 'required': '' ?>"> + - data-selector="" - price="getCurrencyByStore($value) ?>" + getSkipJsReloadPrice() ? '' : 'opConfig.reloadPrice()' ?>" + name="options[getId() ?>]" + id="options_getId() . '_' . $count ?>" + value="getOptionTypeId() ?>" + + data-selector="" + price="getCurrencyByStore($value) ?>" />
-
- - +
+ \ No newline at end of file From ecd9dc42e56093a09193a13fac669cd0c3b1825c Mon Sep 17 00:00:00 2001 From: Nick Shatilo Date: Wed, 3 Oct 2018 17:55:42 +0300 Subject: [PATCH 0048/1295] fix(Webapi Xml Renderer - 18361): removed the not needed ampersand replacement --- .../Magento/Framework/Webapi/Rest/Response/Renderer/Xml.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Webapi/Rest/Response/Renderer/Xml.php b/lib/internal/Magento/Framework/Webapi/Rest/Response/Renderer/Xml.php index b4cfc61611a93..82ced99d690af 100644 --- a/lib/internal/Magento/Framework/Webapi/Rest/Response/Renderer/Xml.php +++ b/lib/internal/Magento/Framework/Webapi/Rest/Response/Renderer/Xml.php @@ -111,8 +111,7 @@ protected function _formatValue($value) /** Without the following transformation boolean values are rendered incorrectly */ $value = $value ? 'true' : 'false'; } - $replacementMap = ['&' => '&']; - return str_replace(array_keys($replacementMap), array_values($replacementMap), $value); + return $value; } /** From 82a635b3a4eb0fd0e2f030d82495ab717ee11db5 Mon Sep 17 00:00:00 2001 From: Graham Wharton Date: Mon, 8 Oct 2018 16:44:56 +0100 Subject: [PATCH 0049/1295] Removed setFromByStore and replaced with default parameter to setFrom --- .../Sales/Model/Order/Email/SenderBuilder.php | 16 +---- .../Model/Order/Email/SenderBuilderTest.php | 25 +++---- .../Mail/Template/TransportBuilder.php | 5 +- .../Mail/Template/TransportBuilderByStore.php | 55 --------------- .../Template/TransportBuilderByStoreTest.php | 67 ------------------- .../Unit/Template/TransportBuilderTest.php | 5 +- 6 files changed, 21 insertions(+), 152 deletions(-) delete mode 100644 lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php delete mode 100644 lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php diff --git a/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php b/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php index 7ec089b882972..e5c9c4b4afddc 100644 --- a/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php +++ b/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php @@ -5,9 +5,7 @@ */ namespace Magento\Sales\Model\Order\Email; -use Magento\Framework\App\ObjectManager; use Magento\Framework\Mail\Template\TransportBuilder; -use Magento\Framework\Mail\Template\TransportBuilderByStore; use Magento\Sales\Model\Order\Email\Container\IdentityInterface; use Magento\Sales\Model\Order\Email\Container\Template; @@ -28,29 +26,19 @@ class SenderBuilder */ protected $transportBuilder; - /** - * @var TransportBuilderByStore - */ - private $transportBuilderByStore; - /** * @param Template $templateContainer * @param IdentityInterface $identityContainer * @param TransportBuilder $transportBuilder - * @param TransportBuilderByStore $transportBuilderByStore */ public function __construct( Template $templateContainer, IdentityInterface $identityContainer, - TransportBuilder $transportBuilder, - TransportBuilderByStore $transportBuilderByStore = null + TransportBuilder $transportBuilder ) { $this->templateContainer = $templateContainer; $this->identityContainer = $identityContainer; $this->transportBuilder = $transportBuilder; - $this->transportBuilderByStore = $transportBuilderByStore ?: ObjectManager::getInstance()->get( - TransportBuilderByStore::class - ); } /** @@ -110,7 +98,7 @@ protected function configureEmailTemplate() $this->transportBuilder->setTemplateIdentifier($this->templateContainer->getTemplateId()); $this->transportBuilder->setTemplateOptions($this->templateContainer->getTemplateOptions()); $this->transportBuilder->setTemplateVars($this->templateContainer->getTemplateVars()); - $this->transportBuilderByStore->setFromByStore( + $this->transportBuilder->setFrom( $this->identityContainer->getEmailIdentity(), $this->identityContainer->getStore()->getId() ); diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php index 38209bb22aef4..a3b7f6ef574dc 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php @@ -6,7 +6,6 @@ namespace Magento\Sales\Test\Unit\Model\Order\Email; -use Magento\Framework\Mail\Template\TransportBuilderByStore; use Magento\Sales\Model\Order\Email\SenderBuilder; class SenderBuilderTest extends \PHPUnit\Framework\TestCase @@ -36,11 +35,6 @@ class SenderBuilderTest extends \PHPUnit\Framework\TestCase */ private $storeMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $transportBuilderByStore; - protected function setUp() { $templateId = 'test_template_id'; @@ -82,11 +76,10 @@ protected function setUp() 'setTemplateIdentifier', 'setTemplateOptions', 'setTemplateVars', + 'setFrom', ] ); - $this->transportBuilderByStore = $this->createMock(TransportBuilderByStore::class); - $this->templateContainerMock->expects($this->once()) ->method('getTemplateId') ->will($this->returnValue($templateId)); @@ -109,8 +102,8 @@ protected function setUp() $this->identityContainerMock->expects($this->once()) ->method('getEmailIdentity') ->will($this->returnValue($emailIdentity)); - $this->transportBuilderByStore->expects($this->once()) - ->method('setFromByStore') + $this->transportBuilder->expects($this->once()) + ->method('setFrom') ->with($this->equalTo($emailIdentity)); $this->identityContainerMock->expects($this->once()) @@ -120,8 +113,7 @@ protected function setUp() $this->senderBuilder = new SenderBuilder( $this->templateContainerMock, $this->identityContainerMock, - $this->transportBuilder, - $this->transportBuilderByStore + $this->transportBuilder ); } @@ -129,6 +121,8 @@ public function testSend() { $customerName = 'test_name'; $customerEmail = 'test_email'; + $identity = 'email_identity_test'; + $transportMock = $this->createMock( \Magento\Sales\Test\Unit\Model\Order\Email\Stub\TransportInterfaceMock::class ); @@ -151,6 +145,9 @@ public function testSend() $this->storeMock->expects($this->once()) ->method('getId') ->willReturn(1); + $this->transportBuilder->expects($this->once()) + ->method('setFrom') + ->with($identity, 1); $this->transportBuilder->expects($this->once()) ->method('addTo') ->with($this->equalTo($customerEmail), $this->equalTo($customerName)); @@ -164,6 +161,7 @@ public function testSend() public function testSendCopyTo() { + $identity = 'email_identity_test'; $transportMock = $this->createMock( \Magento\Sales\Test\Unit\Model\Order\Email\Stub\TransportInterfaceMock::class ); @@ -177,6 +175,9 @@ public function testSendCopyTo() $this->transportBuilder->expects($this->once()) ->method('addTo') ->with($this->equalTo('example@mail.com')); + $this->transportBuilder->expects($this->once()) + ->method('setFrom') + ->with($identity, 1); $this->identityContainerMock->expects($this->once()) ->method('getStore') ->willReturn($this->storeMock); diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php index a1c7333f41245..afe5477e84637 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php @@ -174,11 +174,12 @@ public function setReplyTo($email, $name = null) * Set mail from address * * @param string|array $from + * @param string|int $store * @return $this */ - public function setFrom($from) + public function setFrom($from, $store = null) { - $result = $this->_senderResolver->resolve($from); + $result = $this->_senderResolver->resolve($from, $store); $this->message->setFrom($result['email'], $result['name']); return $this; } diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php deleted file mode 100644 index 95f17fed1123c..0000000000000 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php +++ /dev/null @@ -1,55 +0,0 @@ -message = $message; - $this->senderResolver = $senderResolver; - } - - /** - * Set mail from address by store. - * - * @param string|array $from - * @param string|int $store - * - * @return $this - */ - public function setFromByStore($from, $store) - { - $result = $this->senderResolver->resolve($from, $store); - $this->message->clearFrom(); - $this->message->setFrom($result['email'], $result['name']); - - return $this; - } -} diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php deleted file mode 100644 index 58c9b045eed8c..0000000000000 --- a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php +++ /dev/null @@ -1,67 +0,0 @@ -messageMock = $this->createMock(\Magento\Framework\Mail\Message::class); - $this->senderResolverMock = $this->createMock(\Magento\Framework\Mail\Template\SenderResolverInterface::class); - - $this->model = $objectManagerHelper->getObject( - TransportBuilderByStore::class, - [ - 'message' => $this->messageMock, - 'senderResolver' => $this->senderResolverMock, - ] - ); - } - - /** - * @return void - */ - public function testSetFromByStore() - { - $sender = ['email' => 'from@example.com', 'name' => 'name']; - $store = 1; - $this->senderResolverMock->expects($this->once()) - ->method('resolve') - ->with($sender, $store) - ->willReturn($sender); - $this->messageMock->expects($this->once()) - ->method('setFrom') - ->with('from@example.com', 'name') - ->willReturnSelf(); - $this->messageMock->expects($this->once()) - ->method('clearFrom') - ->willReturnSelf(); - - $this->model->setFromByStore($sender, $store); - } -} diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php index c3759bc43f81f..8e9858116ffdb 100644 --- a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php +++ b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php @@ -167,16 +167,17 @@ public function getTransportDataProvider() public function testSetFrom() { $sender = ['email' => 'from@example.com', 'name' => 'name']; + $store = 1; $this->senderResolverMock->expects($this->once()) ->method('resolve') - ->with($sender) + ->with($sender, $store) ->willReturn($sender); $this->messageMock->expects($this->once()) ->method('setFrom') ->with('from@example.com', 'name') ->willReturnSelf(); - $this->builder->setFrom($sender); + $this->builder->setFrom($sender, $store); } /** From 0f75306061dcf6dc2942d6e165e8ba17f2ce3f6c Mon Sep 17 00:00:00 2001 From: Nick Shatilo Date: Thu, 4 Oct 2018 11:17:39 +0300 Subject: [PATCH 0050/1295] fix(Webapi Xml Renderer - 18361): added unit test for the issue --- .../Magento/Framework/Webapi/Rest/Response/Renderer/Xml.php | 2 +- .../Webapi/Test/Unit/Rest/Response/Renderer/XmlTest.php | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Webapi/Rest/Response/Renderer/Xml.php b/lib/internal/Magento/Framework/Webapi/Rest/Response/Renderer/Xml.php index 82ced99d690af..7a4064cf70355 100644 --- a/lib/internal/Magento/Framework/Webapi/Rest/Response/Renderer/Xml.php +++ b/lib/internal/Magento/Framework/Webapi/Rest/Response/Renderer/Xml.php @@ -111,7 +111,7 @@ protected function _formatValue($value) /** Without the following transformation boolean values are rendered incorrectly */ $value = $value ? 'true' : 'false'; } - return $value; + return (string) $value; } /** diff --git a/lib/internal/Magento/Framework/Webapi/Test/Unit/Rest/Response/Renderer/XmlTest.php b/lib/internal/Magento/Framework/Webapi/Test/Unit/Rest/Response/Renderer/XmlTest.php index 396fbcdb1978b..71fb41491cc74 100644 --- a/lib/internal/Magento/Framework/Webapi/Test/Unit/Rest/Response/Renderer/XmlTest.php +++ b/lib/internal/Magento/Framework/Webapi/Test/Unit/Rest/Response/Renderer/XmlTest.php @@ -76,6 +76,11 @@ public function providerXmlRender() 'value', 'Invalid XML render with numeric symbol in data index.' ], + [ + ['key' => 'test & foo'], + 'test & foo', + 'Invalid XML render with ampersand symbol in data index.' + ], [ ['.key' => 'value'], 'value', From f89473bec7cfbce53d915450c1bd4f969f19fb7d Mon Sep 17 00:00:00 2001 From: Cristiano Casciotti Date: Thu, 11 Oct 2018 11:41:46 +0200 Subject: [PATCH 0051/1295] Added option to exclude discount for minimum order amount calculation --- app/code/Magento/Quote/Model/Quote.php | 17 ++++++++++-- .../Magento/Quote/Model/Quote/Address.php | 18 +++++++++++-- .../Test/Unit/Model/Quote/AddressTest.php | 27 +++++++++++++++++++ .../Quote/Test/Unit/Model/QuoteTest.php | 2 ++ .../Magento/Sales/etc/adminhtml/system.xml | 5 ++++ app/code/Magento/Sales/etc/config.xml | 1 + 6 files changed, 66 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Quote/Model/Quote.php b/app/code/Magento/Quote/Model/Quote.php index 640f89844546e..9849c646b4465 100644 --- a/app/code/Magento/Quote/Model/Quote.php +++ b/app/code/Magento/Quote/Model/Quote.php @@ -2218,6 +2218,11 @@ public function validateMinimumAmount($multishipping = false) if (!$minOrderActive) { return true; } + $includeDiscount = $this->_scopeConfig->getValue( + 'sales/minimum_order/include_discount_amount', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + $storeId + ); $minOrderMulti = $this->_scopeConfig->isSetFlag( 'sales/minimum_order/multi_address', \Magento\Store\Model\ScopeInterface::SCOPE_STORE, @@ -2251,7 +2256,11 @@ public function validateMinimumAmount($multishipping = false) $taxes = ($taxInclude) ? $address->getBaseTaxAmount() : 0; foreach ($address->getQuote()->getItemsCollection() as $item) { /** @var \Magento\Quote\Model\Quote\Item $item */ - $amount = $item->getBaseRowTotal() - $item->getBaseDiscountAmount() + $taxes; + if ($includeDiscount) { + $amount = $item->getBaseRowTotal() - $item->getBaseDiscountAmount() + $taxes; + } else { + $amount = $taxInclude ? $item->getBaseRowTotalInclTax() : $item->getBaseRowTotal(); + } if ($amount < $minAmount) { return false; } @@ -2261,7 +2270,11 @@ public function validateMinimumAmount($multishipping = false) $baseTotal = 0; foreach ($addresses as $address) { $taxes = ($taxInclude) ? $address->getBaseTaxAmount() : 0; - $baseTotal += $address->getBaseSubtotalWithDiscount() + $taxes; + if ($includeDiscount) { + $baseTotal += $address->getBaseSubtotalWithDiscount() + $taxes; + } else { + $baseTotal += $taxInclude ? $address->getBaseSubtotalTotalInclTax() : $address->getBaseSubtotal(); + } } if ($baseTotal < $minAmount) { return false; diff --git a/app/code/Magento/Quote/Model/Quote/Address.php b/app/code/Magento/Quote/Model/Quote/Address.php index a738f844c8d8d..3091604ed3021 100644 --- a/app/code/Magento/Quote/Model/Quote/Address.php +++ b/app/code/Magento/Quote/Model/Quote/Address.php @@ -1143,6 +1143,11 @@ public function validateMinimumAmount() return true; } + $includeDiscount = $this->_scopeConfig->getValue( + 'sales/minimum_order/include_discount_amount', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + $storeId + ); $amount = $this->_scopeConfig->getValue( 'sales/minimum_order/amount', \Magento\Store\Model\ScopeInterface::SCOPE_STORE, @@ -1153,9 +1158,18 @@ public function validateMinimumAmount() \Magento\Store\Model\ScopeInterface::SCOPE_STORE, $storeId ); - $taxes = $taxInclude ? $this->getBaseTaxAmount() : 0; - return ($this->getBaseSubtotalWithDiscount() + $taxes >= $amount); + if ($includeDiscount) { + $taxes = $taxInclude ? $this->getBaseTaxAmount() : 0; + + $isMinimumReached = ($this->getBaseSubtotalWithDiscount() + $taxes >= $amount); + } else { + $isMinimumReached = $taxInclude + ? ($this->getBaseSubtotalTotalInclTax() >= $amount) + : ($this->getBaseSubtotal() >= $amount); + } + + return $isMinimumReached; } /** diff --git a/app/code/Magento/Quote/Test/Unit/Model/Quote/AddressTest.php b/app/code/Magento/Quote/Test/Unit/Model/Quote/AddressTest.php index e25b770b7a81e..8784310d540bd 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Quote/AddressTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Quote/AddressTest.php @@ -216,6 +216,7 @@ public function testValidateMiniumumAmountVirtual() $scopeConfigValues = [ ['sales/minimum_order/active', ScopeInterface::SCOPE_STORE, $storeId, true], ['sales/minimum_order/amount', ScopeInterface::SCOPE_STORE, $storeId, 20], + ['sales/minimum_order/include_discount_amount', ScopeInterface::SCOPE_STORE, $storeId, true], ['sales/minimum_order/tax_including', ScopeInterface::SCOPE_STORE, $storeId, true], ]; @@ -240,6 +241,31 @@ public function testValidateMiniumumAmount() $scopeConfigValues = [ ['sales/minimum_order/active', ScopeInterface::SCOPE_STORE, $storeId, true], ['sales/minimum_order/amount', ScopeInterface::SCOPE_STORE, $storeId, 20], + ['sales/minimum_order/include_discount_amount', ScopeInterface::SCOPE_STORE, $storeId, true], + ['sales/minimum_order/tax_including', ScopeInterface::SCOPE_STORE, $storeId, true], + ]; + + $this->quote->expects($this->once()) + ->method('getStoreId') + ->willReturn($storeId); + $this->quote->expects($this->once()) + ->method('getIsVirtual') + ->willReturn(false); + + $this->scopeConfig->expects($this->once()) + ->method('isSetFlag') + ->willReturnMap($scopeConfigValues); + + $this->assertTrue($this->address->validateMinimumAmount()); + } + + public function testValidateMiniumumAmountWithoutDiscount() + { + $storeId = 1; + $scopeConfigValues = [ + ['sales/minimum_order/active', ScopeInterface::SCOPE_STORE, $storeId, true], + ['sales/minimum_order/amount', ScopeInterface::SCOPE_STORE, $storeId, 20], + ['sales/minimum_order/include_discount_amount', ScopeInterface::SCOPE_STORE, $storeId, false], ['sales/minimum_order/tax_including', ScopeInterface::SCOPE_STORE, $storeId, true], ]; @@ -263,6 +289,7 @@ public function testValidateMiniumumAmountNegative() $scopeConfigValues = [ ['sales/minimum_order/active', ScopeInterface::SCOPE_STORE, $storeId, true], ['sales/minimum_order/amount', ScopeInterface::SCOPE_STORE, $storeId, 20], + ['sales/minimum_order/include_discount_amount', ScopeInterface::SCOPE_STORE, $storeId, true], ['sales/minimum_order/tax_including', ScopeInterface::SCOPE_STORE, $storeId, true], ]; diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteTest.php index 6f5e5937a32c8..9e921f744642f 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/QuoteTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteTest.php @@ -951,6 +951,7 @@ public function testValidateMiniumumAmount() ['sales/minimum_order/active', ScopeInterface::SCOPE_STORE, $storeId, true], ['sales/minimum_order/multi_address', ScopeInterface::SCOPE_STORE, $storeId, true], ['sales/minimum_order/amount', ScopeInterface::SCOPE_STORE, $storeId, 20], + ['sales/minimum_order/include_discount_amount', ScopeInterface::SCOPE_STORE, $storeId, true], ['sales/minimum_order/tax_including', ScopeInterface::SCOPE_STORE, $storeId, true], ]; $this->scopeConfig->expects($this->any()) @@ -977,6 +978,7 @@ public function testValidateMiniumumAmountNegative() ['sales/minimum_order/active', ScopeInterface::SCOPE_STORE, $storeId, true], ['sales/minimum_order/multi_address', ScopeInterface::SCOPE_STORE, $storeId, true], ['sales/minimum_order/amount', ScopeInterface::SCOPE_STORE, $storeId, 20], + ['sales/minimum_order/include_discount_amount', ScopeInterface::SCOPE_STORE, $storeId, true], ['sales/minimum_order/tax_including', ScopeInterface::SCOPE_STORE, $storeId, true], ]; $this->scopeConfig->expects($this->any()) diff --git a/app/code/Magento/Sales/etc/adminhtml/system.xml b/app/code/Magento/Sales/etc/adminhtml/system.xml index 157420b3d0c73..60cdb13274db8 100644 --- a/app/code/Magento/Sales/etc/adminhtml/system.xml +++ b/app/code/Magento/Sales/etc/adminhtml/system.xml @@ -82,6 +82,11 @@ Subtotal after discount + + + Magento\Config\Model\Config\Source\Yesno + Choosing yes will be used subtotal after discount, otherwise only subtotal will be used + Magento\Config\Model\Config\Source\Yesno diff --git a/app/code/Magento/Sales/etc/config.xml b/app/code/Magento/Sales/etc/config.xml index d4d10bfa6dcce..945a18304cffb 100644 --- a/app/code/Magento/Sales/etc/config.xml +++ b/app/code/Magento/Sales/etc/config.xml @@ -19,6 +19,7 @@ 1 + 1 1 From 16afc0962463261e4464456b9bdaad4e8f3be924 Mon Sep 17 00:00:00 2001 From: Graham Wharton Date: Tue, 16 Oct 2018 09:31:45 +0100 Subject: [PATCH 0052/1295] Added new setFromByStore function and deprecated old function. TODO : Rest of Magento codebase still uses deprecated setFrom --- .../Sales/Model/Order/Email/SenderBuilder.php | 2 +- .../Model/Order/Email/SenderBuilderTest.php | 10 +++++----- .../Mail/Template/TransportBuilder.php | 17 ++++++++++++++++- .../Test/Unit/Template/TransportBuilderTest.php | 4 ++-- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php b/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php index e5c9c4b4afddc..af3ace9090834 100644 --- a/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php +++ b/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php @@ -98,7 +98,7 @@ protected function configureEmailTemplate() $this->transportBuilder->setTemplateIdentifier($this->templateContainer->getTemplateId()); $this->transportBuilder->setTemplateOptions($this->templateContainer->getTemplateOptions()); $this->transportBuilder->setTemplateVars($this->templateContainer->getTemplateVars()); - $this->transportBuilder->setFrom( + $this->transportBuilder->setFromByStore( $this->identityContainer->getEmailIdentity(), $this->identityContainer->getStore()->getId() ); diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php index a3b7f6ef574dc..86d65cc476668 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php @@ -76,7 +76,7 @@ protected function setUp() 'setTemplateIdentifier', 'setTemplateOptions', 'setTemplateVars', - 'setFrom', + 'setFromByStore', ] ); @@ -103,8 +103,8 @@ protected function setUp() ->method('getEmailIdentity') ->will($this->returnValue($emailIdentity)); $this->transportBuilder->expects($this->once()) - ->method('setFrom') - ->with($this->equalTo($emailIdentity)); + ->method('setFromByStore') + ->with($this->equalTo($emailIdentity), 1); $this->identityContainerMock->expects($this->once()) ->method('getEmailCopyTo') @@ -146,7 +146,7 @@ public function testSend() ->method('getId') ->willReturn(1); $this->transportBuilder->expects($this->once()) - ->method('setFrom') + ->method('setFromByStore') ->with($identity, 1); $this->transportBuilder->expects($this->once()) ->method('addTo') @@ -176,7 +176,7 @@ public function testSendCopyTo() ->method('addTo') ->with($this->equalTo('example@mail.com')); $this->transportBuilder->expects($this->once()) - ->method('setFrom') + ->method('setFromByStore') ->with($identity, 1); $this->identityContainerMock->expects($this->once()) ->method('getStore') diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php index afe5477e84637..81edf165fdf75 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php @@ -172,12 +172,27 @@ public function setReplyTo($email, $name = null) /** * Set mail from address + * + * @deprecated Use setFromByStore + * + * @param string|array $from + * @return $this + */ + public function setFrom($from) + { + $result = $this->_senderResolver->resolve($from); + $this->message->setFrom($result['email'], $result['name']); + return $this; + } + + /** + * Set mail from address by store * * @param string|array $from * @param string|int $store * @return $this */ - public function setFrom($from, $store = null) + public function setFromByStore($from, $store = null) { $result = $this->_senderResolver->resolve($from, $store); $this->message->setFrom($result['email'], $result['name']); diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php index 8e9858116ffdb..a2539c1326a49 100644 --- a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php +++ b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php @@ -173,11 +173,11 @@ public function testSetFrom() ->with($sender, $store) ->willReturn($sender); $this->messageMock->expects($this->once()) - ->method('setFrom') + ->method('setFromByStore') ->with('from@example.com', 'name') ->willReturnSelf(); - $this->builder->setFrom($sender, $store); + $this->builder->setFromByStore($sender, $store); } /** From 1e3d575aa4a1d6b6c449ab248da7bd50de31b7bb Mon Sep 17 00:00:00 2001 From: roman Date: Tue, 16 Oct 2018 11:46:43 +0300 Subject: [PATCH 0053/1295] MAGETWO-88650: Wrong swatches behavior --- .../templates/cart/item/default.phtml | 2 +- .../web/template/minicart/item/default.html | 2 +- .../web/template/summary/item/details.html | 2 +- .../components/dynamic-rows-configurable.js | 11 +- .../view/frontend/web/js/swatch-renderer.js | 155 ++++++++++-------- 5 files changed, 99 insertions(+), 73 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml index 0567c61f0db60..a423d8c8a0896 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml @@ -49,7 +49,7 @@ $canApplyMsrp = $helper->isShowBeforeOrderConfirm($product) && $helper->isMinima - + escapeHtml($_formatedOptionValue['value']) ?> diff --git a/app/code/Magento/Checkout/view/frontend/web/template/minicart/item/default.html b/app/code/Magento/Checkout/view/frontend/web/template/minicart/item/default.html index 357b0e550af0f..41d442a76d510 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/minicart/item/default.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/minicart/item/default.html @@ -45,7 +45,7 @@ - + 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 dd59bd78416c6..730ceadbd914c 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 @@ -35,7 +35,7 @@
-
+
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js index 01abce7696014..df800c9a64a39 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js @@ -6,8 +6,9 @@ define([ 'underscore', 'uiRegistry', - 'Magento_Ui/js/dynamic-rows/dynamic-rows' -], function (_, registry, dynamicRows) { + 'Magento_Ui/js/dynamic-rows/dynamic-rows', + 'jquery' +], function (_, registry, dynamicRows, $) { 'use strict'; return dynamicRows.extend({ @@ -217,6 +218,8 @@ define([ _.each(tmpData, function (row, index) { path = this.dataScope + '.' + this.index + '.' + (this.startIndex + index); + row.attributes = $('').text(row.attributes).html(); + row.sku = $('').text(row.sku).html(); this.source.set(path, row); }, this); @@ -376,8 +379,8 @@ define([ product = { 'id': row.productId, 'product_link': row.productUrl, - 'name': row.name, - 'sku': row.sku, + 'name': $('').text(row.name).html(), + 'sku': $('').text(row.sku).html(), 'status': row.status, 'price': row.price, 'price_currency': row.priceCurrency, 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 1aabab8b1d5d6..e2598e3247fc5 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 @@ -396,45 +396,50 @@ define([ select = $widget._RenderSwatchSelect(item, chooseText), input = $widget._RenderFormInput(item), listLabel = '', - label = ''; + firstSpan = '', + div = '', + subDiv = '', + secondSpan = ''; // Show only swatch controls if ($widget.options.onlySwatches && !$widget.options.jsonSwatchConfig.hasOwnProperty(item.id)) { return; } - if ($widget.options.enableControlLabel) { - label += - '' + - item.label + - '' + - ''; - } - if ($widget.inProductList) { $widget.productForm.append(input); input = ''; - listLabel = 'aria-label="' + item.label + '"'; + listLabel = document.createAttribute('aria-label'); + listLabel.value = item.label; } else { - listLabel = 'aria-labelledby="' + controlLabelId + '"'; + listLabel = document.createAttribute('aria-labelledby'); + listLabel.value = controlLabelId; + } + + div = document.createElement('div'); + subDiv = document.createElement('div'); + div.setAttribute('class', classes.attributeClass + ' ' + item.code); + div.setAttribute('attribute-code', item.code); + div.setAttribute('attribute-id', item.id); + div.innerHTML = input; + subDiv.setAttribute('aria-activedescendant', ''); + subDiv.setAttribute('tabindex', 0); + subDiv.setAttribute('aria-invalid', false); + subDiv.setAttribute('aria-required', true); + subDiv.setAttribute('role', 'listbox'); + subDiv.setAttributeNode(listLabel); + subDiv.setAttribute('class', classes.attributeOptionsWrapper + ' clearfix'); + subDiv.innerHTML = options + select; + + if ($widget.options.enableControlLabel) { + div.appendChild(firstSpan); + div.appendChild(secondSpan); } + div.appendChild(subDiv); + // Create new control - container.append( - '
' + - label + - '
' + - options + select + - '
' + input + - '
' - ); + container.append(div.outerHTML); $widget.optionsMap[item.id] = {}; @@ -501,7 +506,8 @@ define([ label, width, height, - attr; + link, + div; if (!optionConfig.hasOwnProperty(this.id)) { return ''; @@ -509,7 +515,12 @@ define([ // Add more button if (moreLimit === countAttributes++) { - html += '' + moreText + ''; + link = document.createElement('a'); + link.setAttribute('class', moreClass); + link.setAttribute('href', '#'); + link.textContent = moreText; + + html += link.outerHTML; } id = this.id; @@ -519,48 +530,54 @@ define([ width = _.has(sizeConfig, 'swatchThumb') ? sizeConfig.swatchThumb.width : 110; height = _.has(sizeConfig, 'swatchThumb') ? sizeConfig.swatchThumb.height : 90; label = this.label ? this.label : ''; - attr = - ' id="' + controlId + '-item-' + id + '"' + - ' aria-checked="false"' + - ' aria-describedby="' + controlId + '"' + - ' tabindex="0"' + - ' option-type="' + type + '"' + - ' option-id="' + id + '"' + - ' option-label="' + label + '"' + - ' aria-label="' + label + '"' + - ' option-tooltip-thumb="' + thumb + '"' + - ' option-tooltip-value="' + value + '"' + - ' role="option"' + - ' thumb-width="' + width + '"' + - ' thumb-height="' + height + '"'; + + div = document.createElement('div'); + + div.setAttribute('id', controlId + '-item-' + id); + div.setAttribute('aria-checked', false); + div.setAttribute('aria-describedby', controlId); + div.setAttribute('tabindex', 0); + div.setAttribute('option-type', type); + div.setAttribute('option-id', id); + div.setAttribute('option-label', label); + div.setAttribute('aria-label', label); + div.setAttribute('option-tooltip-thumb', thumb); + div.setAttribute('option-tooltip-value', value); + div.setAttribute('role', 'option'); + div.setAttribute('thumb-width', width); + div.setAttribute('thumb-height', height); if (!this.hasOwnProperty('products') || this.products.length <= 0) { - attr += ' option-empty="true"'; + div.setAttribute('option-empty', true); } if (type === 0) { // Text - html += '
' + (value ? value : label) + - '
'; + div.setAttribute('class', optionClass + ' text'); + div.textContent = value ? value : label; } else if (type === 1) { // Color - html += '
' + '' + - '
'; + div.setAttribute('class', optionClass + ' color'); + div.setAttribute('style', 'background: ' + value + ' no-repeat center; background-size: initial;'); + } else if (type === 2) { // Image - html += '
' + '' + - '
'; + div.setAttribute('class', optionClass + ' image'); + div.setAttribute('style', + 'background: url(' + value + + ') no-repeat center;' + + ' background-size: initial;' + + ' width:' + sizeConfig.swatchImage.width + 'px;' + + ' height:' + sizeConfig.swatchImage.height + 'px;'); } else if (type === 3) { // Clear - html += '
'; + div.setAttribute('class', optionClass); } else { // Default - html += '
' + label + '
'; + div.setAttribute('class', optionClass); + div.textContent = label; } + html += div.outerHTML; }); return html; @@ -575,30 +592,36 @@ define([ * @private */ _RenderSwatchSelect: function (config, chooseText) { - var html; + var select, + firstOption, + otherOption; if (this.options.jsonSwatchConfig.hasOwnProperty(config.id)) { return ''; } - html = - ''; - - return html; + return select.outerHTML; }, /** From 47c5f72c9196dc77b50db3c36ec99719a69a18a0 Mon Sep 17 00:00:00 2001 From: Graham Wharton Date: Tue, 16 Oct 2018 09:59:02 +0100 Subject: [PATCH 0054/1295] Addressed review comments --- .../Sales/Model/Order/Email/SenderBuilder.php | 5 +- .../Mail/Template/TransportBuilder.php | 9 +-- .../Mail/Template/TransportBuilderByStore.php | 62 +++++++++++++++++ .../Template/TransportBuilderByStoreTest.php | 67 +++++++++++++++++++ 4 files changed, 138 insertions(+), 5 deletions(-) create mode 100644 lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php create mode 100644 lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php diff --git a/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php b/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php index af3ace9090834..cda3e3a343f4c 100644 --- a/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php +++ b/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php @@ -6,6 +6,7 @@ namespace Magento\Sales\Model\Order\Email; use Magento\Framework\Mail\Template\TransportBuilder; +use Magento\Framework\Mail\Template\TransportBuilderByStore; use Magento\Sales\Model\Order\Email\Container\IdentityInterface; use Magento\Sales\Model\Order\Email\Container\Template; @@ -30,11 +31,13 @@ class SenderBuilder * @param Template $templateContainer * @param IdentityInterface $identityContainer * @param TransportBuilder $transportBuilder + * @param TransportBuilderByStore $transportBuilderByStore */ public function __construct( Template $templateContainer, IdentityInterface $identityContainer, - TransportBuilder $transportBuilder + TransportBuilder $transportBuilder, + TransportBuilderByStore $transportBuilderByStore = null ) { $this->templateContainer = $templateContainer; $this->identityContainer = $identityContainer; diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php index 81edf165fdf75..12ab84eb61c11 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php @@ -173,16 +173,17 @@ public function setReplyTo($email, $name = null) /** * Set mail from address * - * @deprecated Use setFromByStore + * @deprecated This function sets the from address for the first store only. + * new function setFromByStore introduced to allow setting of from address + * based on store. + * @see setFromByStore() * * @param string|array $from * @return $this */ public function setFrom($from) { - $result = $this->_senderResolver->resolve($from); - $this->message->setFrom($result['email'], $result['name']); - return $this; + return($this->setFromByStore($from)); } /** diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php new file mode 100644 index 0000000000000..88ad4f105f4b7 --- /dev/null +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php @@ -0,0 +1,62 @@ +message = $message; + $this->senderResolver = $senderResolver; + } + + /** + * Set mail from address by store. + * + * @param string|array $from + * @param string|int $store + * + * @return $this + */ + public function setFromByStore($from, $store) + { + $result = $this->senderResolver->resolve($from, $store); + $this->message->clearFrom(); + $this->message->setFrom($result['email'], $result['name']); + + return $this; + } +} \ No newline at end of file diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php new file mode 100644 index 0000000000000..d9f9a194165d6 --- /dev/null +++ b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php @@ -0,0 +1,67 @@ +messageMock = $this->createMock(\Magento\Framework\Mail\Message::class); + $this->senderResolverMock = $this->createMock(\Magento\Framework\Mail\Template\SenderResolverInterface::class); + + $this->model = $objectManagerHelper->getObject( + TransportBuilderByStore::class, + [ + 'message' => $this->messageMock, + 'senderResolver' => $this->senderResolverMock, + ] + ); + } + + /** + * @return void + */ + public function testSetFromByStore() + { + $sender = ['email' => 'from@example.com', 'name' => 'name']; + $store = 1; + $this->senderResolverMock->expects($this->once()) + ->method('resolve') + ->with($sender, $store) + ->willReturn($sender); + $this->messageMock->expects($this->once()) + ->method('setFrom') + ->with('from@example.com', 'name') + ->willReturnSelf(); + $this->messageMock->expects($this->once()) + ->method('clearFrom') + ->willReturnSelf(); + + $this->model->setFromByStore($sender, $store); + } +} \ No newline at end of file From 8d7a4513ddf4b759a6c6a0d2ace04aa714a396df Mon Sep 17 00:00:00 2001 From: Graham Wharton Date: Tue, 16 Oct 2018 10:03:56 +0100 Subject: [PATCH 0055/1295] Corrected linefeeds --- .../Mail/Template/TransportBuilderByStore.php | 122 ++++++++-------- .../Template/TransportBuilderByStoreTest.php | 132 +++++++++--------- 2 files changed, 127 insertions(+), 127 deletions(-) diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php index 88ad4f105f4b7..105a497a14bdb 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php @@ -1,62 +1,62 @@ -message = $message; - $this->senderResolver = $senderResolver; - } - - /** - * Set mail from address by store. - * - * @param string|array $from - * @param string|int $store - * - * @return $this - */ - public function setFromByStore($from, $store) - { - $result = $this->senderResolver->resolve($from, $store); - $this->message->clearFrom(); - $this->message->setFrom($result['email'], $result['name']); - - return $this; - } +message = $message; + $this->senderResolver = $senderResolver; + } + + /** + * Set mail from address by store. + * + * @param string|array $from + * @param string|int $store + * + * @return $this + */ + public function setFromByStore($from, $store) + { + $result = $this->senderResolver->resolve($from, $store); + $this->message->clearFrom(); + $this->message->setFrom($result['email'], $result['name']); + + return $this; + } } \ No newline at end of file diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php index d9f9a194165d6..cebf2a8b15b7f 100644 --- a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php +++ b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php @@ -1,67 +1,67 @@ -messageMock = $this->createMock(\Magento\Framework\Mail\Message::class); - $this->senderResolverMock = $this->createMock(\Magento\Framework\Mail\Template\SenderResolverInterface::class); - - $this->model = $objectManagerHelper->getObject( - TransportBuilderByStore::class, - [ - 'message' => $this->messageMock, - 'senderResolver' => $this->senderResolverMock, - ] - ); - } - - /** - * @return void - */ - public function testSetFromByStore() - { - $sender = ['email' => 'from@example.com', 'name' => 'name']; - $store = 1; - $this->senderResolverMock->expects($this->once()) - ->method('resolve') - ->with($sender, $store) - ->willReturn($sender); - $this->messageMock->expects($this->once()) - ->method('setFrom') - ->with('from@example.com', 'name') - ->willReturnSelf(); - $this->messageMock->expects($this->once()) - ->method('clearFrom') - ->willReturnSelf(); - - $this->model->setFromByStore($sender, $store); - } +messageMock = $this->createMock(\Magento\Framework\Mail\Message::class); + $this->senderResolverMock = $this->createMock(\Magento\Framework\Mail\Template\SenderResolverInterface::class); + + $this->model = $objectManagerHelper->getObject( + TransportBuilderByStore::class, + [ + 'message' => $this->messageMock, + 'senderResolver' => $this->senderResolverMock, + ] + ); + } + + /** + * @return void + */ + public function testSetFromByStore() + { + $sender = ['email' => 'from@example.com', 'name' => 'name']; + $store = 1; + $this->senderResolverMock->expects($this->once()) + ->method('resolve') + ->with($sender, $store) + ->willReturn($sender); + $this->messageMock->expects($this->once()) + ->method('setFrom') + ->with('from@example.com', 'name') + ->willReturnSelf(); + $this->messageMock->expects($this->once()) + ->method('clearFrom') + ->willReturnSelf(); + + $this->model->setFromByStore($sender, $store); + } } \ No newline at end of file From a7cd527a6896c3a2f204e2771b92496ef4089f4e Mon Sep 17 00:00:00 2001 From: Graham Wharton Date: Tue, 16 Oct 2018 10:09:44 +0100 Subject: [PATCH 0056/1295] Corrected file encoding --- .../Magento/Framework/Mail/Template/TransportBuilderByStore.php | 2 +- .../Mail/Test/Unit/Template/TransportBuilderByStoreTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php index 105a497a14bdb..82a9c20d59a53 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php @@ -1,6 +1,6 @@ Date: Tue, 16 Oct 2018 10:11:22 +0100 Subject: [PATCH 0057/1295] Fixed whitespace issues --- .../Magento/Framework/Mail/Template/TransportBuilderByStore.php | 2 +- .../Mail/Test/Unit/Template/TransportBuilderByStoreTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php index 82a9c20d59a53..c898f92bbf282 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php @@ -59,4 +59,4 @@ public function setFromByStore($from, $store) return $this; } -} \ No newline at end of file +} diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php index 5ae4d8d4478bc..58c9b045eed8c 100644 --- a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php +++ b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php @@ -64,4 +64,4 @@ public function testSetFromByStore() $this->model->setFromByStore($sender, $store); } -} \ No newline at end of file +} From 490e5d22074224ec1965a391abfffc379c31d0ee Mon Sep 17 00:00:00 2001 From: Graham Wharton Date: Tue, 16 Oct 2018 10:22:41 +0100 Subject: [PATCH 0058/1295] Fixed TransportBuilderTest --- .../Mail/Test/Unit/Template/TransportBuilderTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php index a2539c1326a49..1af4dd87b087e 100644 --- a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php +++ b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php @@ -164,7 +164,7 @@ public function getTransportDataProvider() /** * @return void */ - public function testSetFrom() + public function testSetFromByStore() { $sender = ['email' => 'from@example.com', 'name' => 'name']; $store = 1; @@ -173,7 +173,7 @@ public function testSetFrom() ->with($sender, $store) ->willReturn($sender); $this->messageMock->expects($this->once()) - ->method('setFromByStore') + ->method('setFrom') ->with('from@example.com', 'name') ->willReturnSelf(); From b19b3ed195b870f16a00c673e435b5b9b8184227 Mon Sep 17 00:00:00 2001 From: Graham Wharton Date: Tue, 16 Oct 2018 10:24:30 +0100 Subject: [PATCH 0059/1295] Fixed whitespace violations --- .../Magento/Framework/Mail/Template/TransportBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php index 12ab84eb61c11..087fdf8503a60 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php @@ -172,7 +172,7 @@ public function setReplyTo($email, $name = null) /** * Set mail from address - * + * * @deprecated This function sets the from address for the first store only. * new function setFromByStore introduced to allow setting of from address * based on store. From ef5de2ecddce79e8e7c9d33e673f10cc8c15422b Mon Sep 17 00:00:00 2001 From: roman Date: Tue, 16 Oct 2018 19:54:23 +0300 Subject: [PATCH 0060/1295] MAGETWO-88650: Wrong swatches behavior --- .../Swatches/view/frontend/web/js/swatch-renderer.js | 9 +++++++++ 1 file changed, 9 insertions(+) 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 e2598e3247fc5..5371e3d85e70b 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 @@ -406,6 +406,15 @@ define([ return; } + if ($widget.options.enableControlLabel) { + firstSpan = document.createElement('span'); + secondSpan = document.createElement('span'); + firstSpan.setAttribute('id', controlLabelId); + firstSpan.setAttribute('class', classes.attributeLabelClass); + firstSpan.textContent = item.label; + secondSpan.setAttribute('class', classes.attributeSelectedOptionLabelClass); + } + if ($widget.inProductList) { $widget.productForm.append(input); input = ''; From 9f34fd631a55ded6d8a458d219edfc23c1bc5f9b Mon Sep 17 00:00:00 2001 From: Graham Wharton Date: Tue, 16 Oct 2018 17:55:46 +0100 Subject: [PATCH 0061/1295] Ignore unused parameter in constructor. Left in for backwards compatability purposes. --- app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php b/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php index cda3e3a343f4c..e4dc75ade7c79 100644 --- a/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php +++ b/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php @@ -28,6 +28,8 @@ class SenderBuilder protected $transportBuilder; /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * * @param Template $templateContainer * @param IdentityInterface $identityContainer * @param TransportBuilder $transportBuilder From 66478ec59d6cbf77d538f2ea112f32997e491bf8 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev Date: Wed, 17 Oct 2018 08:11:32 +0300 Subject: [PATCH 0062/1295] magento/magento2#18471 Alternative fix for Multi Store Emails issue Fix small issue --- .../Magento/Framework/Mail/Template/TransportBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php index 087fdf8503a60..35e1651c6a5df 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php @@ -183,7 +183,7 @@ public function setReplyTo($email, $name = null) */ public function setFrom($from) { - return($this->setFromByStore($from)); + return $this->setFromByStore($from, null); } /** From 12f92431d7a36eafb13c54d86b98c9fe65a4dda0 Mon Sep 17 00:00:00 2001 From: roman Date: Wed, 17 Oct 2018 12:42:37 +0300 Subject: [PATCH 0063/1295] MAGETWO-88650: Wrong swatches behavior --- .../Checkout/view/frontend/templates/cart/item/default.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml index a423d8c8a0896..10a64fe755187 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml @@ -49,7 +49,7 @@ $canApplyMsrp = $helper->isShowBeforeOrderConfirm($product) && $helper->isMinima - escapeHtml($_formatedOptionValue['value']) ?> + escapeHtml($_formatedOptionValue['value'], ['span']) ?> From 81fc5aa1135e094fb6a3c8f762e8123918c635e6 Mon Sep 17 00:00:00 2001 From: Sviatoslav Mankivskyi Date: Thu, 18 Oct 2018 15:04:53 -0500 Subject: [PATCH 0064/1295] Fix docblock --- .../Magento/Backend/Block/System/Store/Grid/Render/Group.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Backend/Block/System/Store/Grid/Render/Group.php b/app/code/Magento/Backend/Block/System/Store/Grid/Render/Group.php index 04d173cd3c179..8656518260eb6 100644 --- a/app/code/Magento/Backend/Block/System/Store/Grid/Render/Group.php +++ b/app/code/Magento/Backend/Block/System/Store/Grid/Render/Group.php @@ -9,7 +9,7 @@ * Store render group * * @author Magento Core Team - * @deprecated + * @deprecated since Store Grid is refactored with UI Components */ class Group extends \Magento\Backend\Block\Widget\Grid\Column\Renderer\AbstractRenderer { From c4fbe49677aac50c3fabffc7d8e67e060bccf581 Mon Sep 17 00:00:00 2001 From: Sviatoslav Mankivskyi Date: Thu, 18 Oct 2018 15:05:35 -0500 Subject: [PATCH 0065/1295] Fix docblock --- .../Magento/Backend/Block/System/Store/Grid/Render/Store.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Backend/Block/System/Store/Grid/Render/Store.php b/app/code/Magento/Backend/Block/System/Store/Grid/Render/Store.php index 8ce048bde537e..9be630277d4e5 100644 --- a/app/code/Magento/Backend/Block/System/Store/Grid/Render/Store.php +++ b/app/code/Magento/Backend/Block/System/Store/Grid/Render/Store.php @@ -9,12 +9,12 @@ * Store render store * * @author Magento Core Team - * @deprecated + * @deprecated since Store Grid is refactored with UI Components */ class Store extends \Magento\Backend\Block\Widget\Grid\Column\Renderer\AbstractRenderer { /** - * {@inheritdoc} + * @inheritdoc */ public function render(\Magento\Framework\DataObject $row) { From 8910b68ccf8b33863ff23da35ef941d9eeb153fd Mon Sep 17 00:00:00 2001 From: Sviatoslav Mankivskyi Date: Thu, 18 Oct 2018 15:06:00 -0500 Subject: [PATCH 0066/1295] Fix docblock --- .../Magento/Backend/Block/System/Store/Grid/Render/Group.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Backend/Block/System/Store/Grid/Render/Group.php b/app/code/Magento/Backend/Block/System/Store/Grid/Render/Group.php index 8656518260eb6..37f11754529e5 100644 --- a/app/code/Magento/Backend/Block/System/Store/Grid/Render/Group.php +++ b/app/code/Magento/Backend/Block/System/Store/Grid/Render/Group.php @@ -14,7 +14,7 @@ class Group extends \Magento\Backend\Block\Widget\Grid\Column\Renderer\AbstractRenderer { /** - * {@inheritdoc} + * @inheritdoc */ public function render(\Magento\Framework\DataObject $row) { From 7bc52b96586af3edc74174a477fe0689ee366ab1 Mon Sep 17 00:00:00 2001 From: Sviatoslav Mankivskyi Date: Thu, 18 Oct 2018 15:06:19 -0500 Subject: [PATCH 0067/1295] Fix docblock --- .../Backend/Block/System/Store/Grid/Render/Website.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Backend/Block/System/Store/Grid/Render/Website.php b/app/code/Magento/Backend/Block/System/Store/Grid/Render/Website.php index f97dffc9fa069..d4bb213dce14e 100644 --- a/app/code/Magento/Backend/Block/System/Store/Grid/Render/Website.php +++ b/app/code/Magento/Backend/Block/System/Store/Grid/Render/Website.php @@ -9,12 +9,12 @@ * Store render website * * @author Magento Core Team - * @deprecated + * @deprecated since Store Grid is refactored with UI Components */ class Website extends \Magento\Backend\Block\Widget\Grid\Column\Renderer\AbstractRenderer { /** - * {@inheritdoc} + * @inheritdoc */ public function render(\Magento\Framework\DataObject $row) { From c0e78e0645382bee9e7739966ccb4c5dfd05f67b Mon Sep 17 00:00:00 2001 From: Sviatoslav Mankivskyi Date: Thu, 18 Oct 2018 15:06:51 -0500 Subject: [PATCH 0068/1295] Fix docblock --- app/code/Magento/Backend/Block/System/Store/Store.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Backend/Block/System/Store/Store.php b/app/code/Magento/Backend/Block/System/Store/Store.php index 451794c0e2a95..2145dc524259e 100644 --- a/app/code/Magento/Backend/Block/System/Store/Store.php +++ b/app/code/Magento/Backend/Block/System/Store/Store.php @@ -12,7 +12,7 @@ * @author Magento Core Team * @api * @since 100.0.2 - * @deprecated + * @deprecated since Store Grid is refactored with UI Components */ class Store extends \Magento\Backend\Block\Widget\Grid\Container { From cc34f00113e1e644e92002f82d34c4bf36aa0e26 Mon Sep 17 00:00:00 2001 From: gwharton <30697781+gwharton@users.noreply.github.com> Date: Sat, 20 Oct 2018 14:11:28 +0100 Subject: [PATCH 0069/1295] Fixed Whitespace issue --- .../Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php index 86d65cc476668..759d60d9e6613 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php @@ -122,7 +122,7 @@ public function testSend() $customerName = 'test_name'; $customerEmail = 'test_email'; $identity = 'email_identity_test'; - + $transportMock = $this->createMock( \Magento\Sales\Test\Unit\Model\Order\Email\Stub\TransportInterfaceMock::class ); From f114f2aa61decaaecff02ebc6e2cabcc12569bd3 Mon Sep 17 00:00:00 2001 From: Vladyslav Podorozhnyi Date: Thu, 25 Oct 2018 15:18:26 +0300 Subject: [PATCH 0070/1295] magento/magento2#18387: catalog:images:resize fails to process all images -> Possible underlying Magento/Framework/DB/Query/Generator issue - fix getCountAllProductImages select and cover class with unit tests. (cherry picked from commit 6a6079d) --- .../Model/ResourceModel/Product/Image.php | 8 +- .../Model/ResourceModel/Product/ImageTest.php | 171 ++++++++++++++++++ 2 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php index 5f83f9826abb5..1a515a3df82a4 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php @@ -80,7 +80,13 @@ public function getAllProductImages(): \Generator */ public function getCountAllProductImages(): int { - $select = $this->getVisibleImagesSelect()->reset('columns')->columns('count(*)'); + $select = $this->getVisibleImagesSelect() + ->reset('columns') + ->reset('distinct') + ->columns( + new \Zend_Db_Expr('count(distinct value)') + ); + return (int) $this->connection->fetchOne($select); } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php new file mode 100644 index 0000000000000..16e245ba640cb --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php @@ -0,0 +1,171 @@ +connectionMock = $this->createMock(AdapterInterface::class); + + $this->resourceMock = $this->createMock(ResourceConnection::class); + $this->resourceMock->method('getConnection')->willReturn($this->connectionMock); + $this->resourceMock->method('getTableName')->willReturnArgument(0); + + $this->generatorMock = $this->createMock(Generator::class); + + $this->imageModel = $objectManager->getObject( + Image::class, + [ + 'generator' => $this->generatorMock, + 'resourceConnection' => $this->resourceMock, + 'batchSize' => $this->batchSize + ] + ); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + protected function getVisibleImagesSelectMock(): \PHPUnit_Framework_MockObject_MockObject + { + $selectMock = $this->getMockBuilder(Select::class) + ->disableOriginalConstructor() + ->getMock(); + $selectMock->expects($this->once()) + ->method('distinct') + ->willReturnSelf(); + $selectMock->expects($this->once()) + ->method('from') + ->with( + ['images' => Gallery::GALLERY_TABLE], + 'value as filepath' + )->willReturnSelf(); + $selectMock->expects($this->once()) + ->method('where') + ->with('disabled = 0') + ->willReturnSelf(); + + return $selectMock; + } + + public function testGetCountAllProductImages(): void + { + $selectMock = $this->getVisibleImagesSelectMock(); + $selectMock->expects($this->exactly(2)) + ->method('reset') + ->withConsecutive( + ['columns'], + ['distinct'] + )->willReturnSelf(); + $selectMock->expects($this->once()) + ->method('columns') + ->with(new \Zend_Db_Expr('count(distinct value)')) + ->willReturnSelf(); + + $this->connectionMock->expects($this->once()) + ->method('select') + ->willReturn($selectMock); + $this->connectionMock->expects($this->once()) + ->method('fetchOne') + ->with($selectMock) + ->willReturn($this->imagesCount); + + $this->assertSame($this->imagesCount, $this->imageModel->getCountAllProductImages()); + } + + public function testGetAllProductImages(): void + { + $getBatchIteratorMock = function ($selectMock, $imagesCount, $batchSize): array { + $result = []; + $count = $imagesCount / $batchSize; + while ($count) { + $count--; + $result[$count] = $selectMock; + } + + return $result; + }; + + $getAllProductImagesSelectFetchResults = function ($batchSize): array { + $result = []; + $count = $batchSize; + while ($count) { + $count--; + $result[$count] = $count; + } + + return $result; + }; + + $this->connectionMock->expects($this->once()) + ->method('select') + ->willReturn($this->getVisibleImagesSelectMock()); + + $fetchResult = $getAllProductImagesSelectFetchResults($this->batchSize); + $this->connectionMock->expects($this->exactly($this->imagesCount / $this->batchSize)) + ->method('fetchAll') + ->willReturn($fetchResult); + + /** @var Select | \PHPUnit_Framework_MockObject_MockObject $selectMock */ + $selectMock = $this->getMockBuilder(Select::class) + ->disableOriginalConstructor() + ->getMock(); + + $batchIteratorMock = $getBatchIteratorMock($selectMock, $this->imagesCount, $this->batchSize); + $this->generatorMock->expects($this->once()) + ->method('generate') + ->with( + 'value_id', + $selectMock, + $this->batchSize, + \Magento\Framework\DB\Query\BatchIteratorInterface::NON_UNIQUE_FIELD_ITERATOR + )->willReturn($batchIteratorMock); + + $this->assertCount($this->imagesCount, $this->imageModel->getAllProductImages()); + } +} From 2a6f8340de5fd6fd96c4d0848ba84e9e64cc1cf0 Mon Sep 17 00:00:00 2001 From: Peter O'Callaghan Date: Sat, 6 Oct 2018 10:28:25 +0100 Subject: [PATCH 0071/1295] Fixes #18357 - SQL error when table prefix used. --- .../Model/ResourceModel/Agreement/Grid/Collection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CheckoutAgreements/Model/ResourceModel/Agreement/Grid/Collection.php b/app/code/Magento/CheckoutAgreements/Model/ResourceModel/Agreement/Grid/Collection.php index fe600d64a7c48..2cce918c5edd4 100644 --- a/app/code/Magento/CheckoutAgreements/Model/ResourceModel/Agreement/Grid/Collection.php +++ b/app/code/Magento/CheckoutAgreements/Model/ResourceModel/Agreement/Grid/Collection.php @@ -63,7 +63,7 @@ private function getStoresForAgreements() if (!empty($agreementId)) { $select = $this->getConnection()->select()->from( - ['agreement_store' => 'checkout_agreement_store'] + ['agreement_store' => $this->getResource()->getTable('checkout_agreement_store')] )->where( 'agreement_store.agreement_id IN (?)', $agreementId From 95eeb9f6a022b1195b838965f65486a503d57137 Mon Sep 17 00:00:00 2001 From: Vladyslav Podorozhnyi Date: Thu, 25 Oct 2018 18:15:55 +0300 Subject: [PATCH 0072/1295] magento/magento2#18387: catalog:images:resize fails to process all images -> Possible underlying Magento/Framework/DB/Query/Generator issue - fix code style; (cherry picked from commit 466daaa) --- .../Test/Unit/Model/ResourceModel/Product/ImageTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php index 16e245ba640cb..9c783e237ab24 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php @@ -131,7 +131,7 @@ public function testGetAllProductImages(): void return $result; }; - $getAllProductImagesSelectFetchResults = function ($batchSize): array { + $getFetchResults = function ($batchSize): array { $result = []; $count = $batchSize; while ($count) { @@ -146,7 +146,7 @@ public function testGetAllProductImages(): void ->method('select') ->willReturn($this->getVisibleImagesSelectMock()); - $fetchResult = $getAllProductImagesSelectFetchResults($this->batchSize); + $fetchResult = $getFetchResults($this->batchSize); $this->connectionMock->expects($this->exactly($this->imagesCount / $this->batchSize)) ->method('fetchAll') ->willReturn($fetchResult); From 84590ded5ed62edede5a12a68d384433e51ce0f3 Mon Sep 17 00:00:00 2001 From: Sviatoslav Mankivskyi Date: Sat, 27 Oct 2018 00:50:31 +0300 Subject: [PATCH 0073/1295] ENGCOM-3292 catalog:images:resize total images count calculates incorrectly #18387: #18807 (cherry picked from commit e1a1c7b) --- .../Magento/Catalog/Model/ResourceModel/Product/Image.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php index 1a515a3df82a4..068580927b96a 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php @@ -12,8 +12,8 @@ use Magento\Framework\DB\Select; use Magento\Framework\App\ResourceConnection; -/** - * Class for fast retrieval of all product images +/* + * Class for retrieval of all product images */ class Image { @@ -76,6 +76,7 @@ public function getAllProductImages(): \Generator /** * Get the number of unique pictures of products + * * @return int */ public function getCountAllProductImages(): int @@ -91,6 +92,8 @@ public function getCountAllProductImages(): int } /** + * Return Select to fetch all products images + * * @return Select */ private function getVisibleImagesSelect(): Select From 4653d3e007500133b1b55012b469fd782738c680 Mon Sep 17 00:00:00 2001 From: Vladyslav Podorozhnyi Date: Sat, 27 Oct 2018 17:58:00 +0300 Subject: [PATCH 0074/1295] magento/magento2#18387: catalog:images:resize fails to process all images -> Possible underlying Magento/Framework/DB/Query/Generator issue - fix unit test - now it can test also not only full batches, but partial as well (139 images with batch size 100); (cherry picked from commit f6b0a2a) --- .../Model/ResourceModel/Product/ImageTest.php | 170 +++++++++++------- 1 file changed, 109 insertions(+), 61 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php index 9c783e237ab24..1866138cf16b6 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php @@ -16,6 +16,11 @@ class ImageTest extends \PHPUnit\Framework\TestCase { + /** + * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + */ + protected $objectManager; + /** * @var AdapterInterface | \PHPUnit_Framework_MockObject_MockObject */ @@ -31,41 +36,14 @@ class ImageTest extends \PHPUnit\Framework\TestCase */ protected $resourceMock; - /** - * @var Image - */ - protected $imageModel; - - /** - * @var int - */ - protected $imagesCount = 50; - - /** - * @var int - */ - protected $batchSize = 10; - protected function setUp(): void { - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - + $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->connectionMock = $this->createMock(AdapterInterface::class); - $this->resourceMock = $this->createMock(ResourceConnection::class); $this->resourceMock->method('getConnection')->willReturn($this->connectionMock); $this->resourceMock->method('getTableName')->willReturnArgument(0); - $this->generatorMock = $this->createMock(Generator::class); - - $this->imageModel = $objectManager->getObject( - Image::class, - [ - 'generator' => $this->generatorMock, - 'resourceConnection' => $this->resourceMock, - 'batchSize' => $this->batchSize - ] - ); } /** @@ -93,7 +71,11 @@ protected function getVisibleImagesSelectMock(): \PHPUnit_Framework_MockObject_M return $selectMock; } - public function testGetCountAllProductImages(): void + /** + * @param int $imagesCount + * @dataProvider dataProvider + */ + public function testGetCountAllProductImages(int $imagesCount): void { $selectMock = $this->getVisibleImagesSelectMock(); $selectMock->expects($this->exactly(2)) @@ -113,59 +95,125 @@ public function testGetCountAllProductImages(): void $this->connectionMock->expects($this->once()) ->method('fetchOne') ->with($selectMock) - ->willReturn($this->imagesCount); + ->willReturn($imagesCount); + + $imageModel = $this->objectManager->getObject( + Image::class, + [ + 'generator' => $this->generatorMock, + 'resourceConnection' => $this->resourceMock + ] + ); - $this->assertSame($this->imagesCount, $this->imageModel->getCountAllProductImages()); + $this->assertSame($imagesCount, $imageModel->getCountAllProductImages()); } - public function testGetAllProductImages(): void + /** + * @param int $imagesCount + * @param int $batchSize + * @dataProvider dataProvider + */ + public function testGetAllProductImages(int $imagesCount, int $batchSize): void { - $getBatchIteratorMock = function ($selectMock, $imagesCount, $batchSize): array { - $result = []; - $count = $imagesCount / $batchSize; - while ($count) { - $count--; - $result[$count] = $selectMock; - } - - return $result; - }; - - $getFetchResults = function ($batchSize): array { - $result = []; - $count = $batchSize; - while ($count) { - $count--; - $result[$count] = $count; - } - - return $result; - }; - $this->connectionMock->expects($this->once()) ->method('select') ->willReturn($this->getVisibleImagesSelectMock()); - $fetchResult = $getFetchResults($this->batchSize); - $this->connectionMock->expects($this->exactly($this->imagesCount / $this->batchSize)) + $batchCount = (int)ceil($imagesCount / $batchSize); + $fetchResultsCallback = $this->getFetchResultCallbackForBatches($imagesCount, $batchSize); + $this->connectionMock->expects($this->exactly($batchCount)) ->method('fetchAll') - ->willReturn($fetchResult); + ->will($this->returnCallback($fetchResultsCallback)); /** @var Select | \PHPUnit_Framework_MockObject_MockObject $selectMock */ $selectMock = $this->getMockBuilder(Select::class) ->disableOriginalConstructor() ->getMock(); - $batchIteratorMock = $getBatchIteratorMock($selectMock, $this->imagesCount, $this->batchSize); $this->generatorMock->expects($this->once()) ->method('generate') ->with( 'value_id', $selectMock, - $this->batchSize, + $batchSize, \Magento\Framework\DB\Query\BatchIteratorInterface::NON_UNIQUE_FIELD_ITERATOR - )->willReturn($batchIteratorMock); + )->will($this->returnCallback($this->getBatchIteratorCallback($selectMock, $batchCount))); - $this->assertCount($this->imagesCount, $this->imageModel->getAllProductImages()); + $imageModel = $this->objectManager->getObject( + Image::class, + [ + 'generator' => $this->generatorMock, + 'resourceConnection' => $this->resourceMock, + 'batchSize' => $batchSize + ] + ); + + $this->assertCount($imagesCount, $imageModel->getAllProductImages()); + } + + /** + * @param int $imagesCount + * @param int $batchSize + * @return \Closure + */ + protected function getFetchResultCallbackForBatches(int $imagesCount, int $batchSize): \Closure + { + $fetchResultsCallback = function () use (&$imagesCount, $batchSize) { + $batchSize = ($imagesCount >= $batchSize) ? $batchSize : $imagesCount; + $imagesCount -= $batchSize; + + $getFetchResults = function ($batchSize): array { + $result = []; + $count = $batchSize; + while ($count) { + $count--; + $result[$count] = $count; + } + + return $result; + }; + + return $getFetchResults($batchSize); + }; + + return $fetchResultsCallback; + } + + /** + * @param Select | \PHPUnit_Framework_MockObject_MockObject $selectMock + * @param int $batchCount + * @return \Closure + */ + protected function getBatchIteratorCallback( + \PHPUnit_Framework_MockObject_MockObject $selectMock, + int $batchCount + ): \Closure + { + $getBatchIteratorCallback = function () use ($batchCount, $selectMock): array { + $result = []; + $count = $batchCount; + while ($count) { + $count--; + $result[$count] = $selectMock; + } + + return $result; + }; + + return $getBatchIteratorCallback; + } + + /** + * Data Provider + * @return array + */ + public function dataProvider(): array + { + return [ + [300, 100], + [139, 100], + [67, 10], + [154, 47] + ]; } } From d2e53455f4a79a3dc71e9e267259bfabc799a577 Mon Sep 17 00:00:00 2001 From: Vladyslav Podorozhnyi Date: Sat, 27 Oct 2018 17:59:22 +0300 Subject: [PATCH 0075/1295] magento/magento2#18387: catalog:images:resize fails to process all images -> Possible underlying Magento/Framework/DB/Query/Generator issue - fix unit test - now it can test also not only full batches, but partial as well (139 images with batch size 100); (cherry picked from commit 546e7ce) --- .../Test/Unit/Model/ResourceModel/Product/ImageTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php index 1866138cf16b6..1a1eebf30f7f6 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php @@ -210,10 +210,12 @@ protected function getBatchIteratorCallback( public function dataProvider(): array { return [ + [300, 300], [300, 100], [139, 100], [67, 10], - [154, 47] + [154, 47], + [0, 100] ]; } } From 2539a716f5d54c66c89f64965d6fb6762b6c90e8 Mon Sep 17 00:00:00 2001 From: Vladyslav Podorozhnyi Date: Sat, 27 Oct 2018 18:08:39 +0300 Subject: [PATCH 0076/1295] magento/magento2#18387: catalog:images:resize fails to process all images -> Possible underlying Magento/Framework/DB/Query/Generator issue - remove method return type 'void' which can be used in php7.1 or higher. --- .../Test/Unit/Model/ResourceModel/Product/ImageTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php index 1a1eebf30f7f6..233f59172ee4d 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php @@ -36,7 +36,7 @@ class ImageTest extends \PHPUnit\Framework\TestCase */ protected $resourceMock; - protected function setUp(): void + protected function setUp() { $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->connectionMock = $this->createMock(AdapterInterface::class); @@ -75,7 +75,7 @@ protected function getVisibleImagesSelectMock(): \PHPUnit_Framework_MockObject_M * @param int $imagesCount * @dataProvider dataProvider */ - public function testGetCountAllProductImages(int $imagesCount): void + public function testGetCountAllProductImages(int $imagesCount) { $selectMock = $this->getVisibleImagesSelectMock(); $selectMock->expects($this->exactly(2)) @@ -113,7 +113,7 @@ public function testGetCountAllProductImages(int $imagesCount): void * @param int $batchSize * @dataProvider dataProvider */ - public function testGetAllProductImages(int $imagesCount, int $batchSize): void + public function testGetAllProductImages(int $imagesCount, int $batchSize) { $this->connectionMock->expects($this->once()) ->method('select') From 76bd08924e7e0f8a3fb88b3990623e5872b10760 Mon Sep 17 00:00:00 2001 From: Anton Evers Date: Mon, 11 Dec 2017 14:11:02 +0200 Subject: [PATCH 0077/1295] magento/magento2#18387: catalog:images:resize fails to process all images -> Possible underlying Magento/Framework/DB/Query/Generator issue Ranged selects always miss the last range Fix unit test. 11 batches expected to handle 105 items: 1: 1-10 2: 11-20 3: 21-30 4: 31-40 5: 41-50 6: 51-60 7: 61-70 8: 71-80 9: 81-90 10: 91-100 11: 101-105 (cherry picked from commit 6c24c0e) --- .../Magento/Framework/DB/Query/BatchRangeIterator.php | 4 ++-- .../Framework/Test/Unit/DB/Query/BatchRangeIteratorTest.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/internal/Magento/Framework/DB/Query/BatchRangeIterator.php b/lib/internal/Magento/Framework/DB/Query/BatchRangeIterator.php index cc2f5a91f73fd..af0ddc9b18b9f 100644 --- a/lib/internal/Magento/Framework/DB/Query/BatchRangeIterator.php +++ b/lib/internal/Magento/Framework/DB/Query/BatchRangeIterator.php @@ -107,7 +107,7 @@ public function __construct( public function current() { if (null === $this->currentSelect) { - $this->isValid = ($this->currentOffset + $this->batchSize) <= $this->totalItemCount; + $this->isValid = $this->currentOffset < $this->totalItemCount; $this->currentSelect = $this->initSelectObject(); } return $this->currentSelect; @@ -138,7 +138,7 @@ public function next() if (null === $this->currentSelect) { $this->current(); } - $this->isValid = ($this->batchSize + $this->currentOffset) <= $this->totalItemCount; + $this->isValid = $this->currentOffset < $this->totalItemCount; $select = $this->initSelectObject(); if ($this->isValid) { $this->iteration++; diff --git a/lib/internal/Magento/Framework/Test/Unit/DB/Query/BatchRangeIteratorTest.php b/lib/internal/Magento/Framework/Test/Unit/DB/Query/BatchRangeIteratorTest.php index 22fdf0a05686a..9e2014c1b070a 100644 --- a/lib/internal/Magento/Framework/Test/Unit/DB/Query/BatchRangeIteratorTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/DB/Query/BatchRangeIteratorTest.php @@ -116,6 +116,6 @@ public function testIterations() $iterations++; } - $this->assertEquals(10, $iterations); + $this->assertEquals(11, $iterations); } } From b81b5503a676778793eac3a321d59fd3741935ca Mon Sep 17 00:00:00 2001 From: Vladyslav Podorozhnyi Date: Mon, 11 Dec 2017 14:11:02 +0200 Subject: [PATCH 0078/1295] magento/magento2#18387: catalog:images:resize fails to process all images -> Possible underlying Magento/Framework/DB/Query/Generator issue - fix code style; --- .../Test/Unit/Model/ResourceModel/Product/ImageTest.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php index 233f59172ee4d..16b8ae43640cb 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php @@ -187,9 +187,8 @@ protected function getFetchResultCallbackForBatches(int $imagesCount, int $batch protected function getBatchIteratorCallback( \PHPUnit_Framework_MockObject_MockObject $selectMock, int $batchCount - ): \Closure - { - $getBatchIteratorCallback = function () use ($batchCount, $selectMock): array { + ): \Closure { + $iteratorCallback = function () use ($batchCount, $selectMock): array { $result = []; $count = $batchCount; while ($count) { @@ -200,7 +199,7 @@ protected function getBatchIteratorCallback( return $result; }; - return $getBatchIteratorCallback; + return $iteratorCallback; } /** From 051d82db553171cd784f543747ccff926287605d Mon Sep 17 00:00:00 2001 From: Sviatoslav Mankivskyi Date: Mon, 29 Oct 2018 19:58:56 +0200 Subject: [PATCH 0079/1295] ENGCOM-3292: catalog:images:resize total images count calculates incorrectly #18387: #18807 (cherry picked from commit f38a4fc) --- app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php index 068580927b96a..77f67480619e0 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php @@ -12,7 +12,7 @@ use Magento\Framework\DB\Select; use Magento\Framework\App\ResourceConnection; -/* +/** * Class for retrieval of all product images */ class Image From 10a1a881e8c7cbf983b800fe6479127e159a86e2 Mon Sep 17 00:00:00 2001 From: Vladyslav Podorozhnyi Date: Mon, 11 Dec 2017 14:11:02 +0200 Subject: [PATCH 0080/1295] magento/magento2#18387: catalog:images:resize fails to process all images -> Possible underlying Magento/Framework/DB/Query/Generator issue - fix code style; (cherry picked from commit c95ce3c) --- .../Model/ResourceModel/Product/ImageTest.php | 55 ++++++++++++------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php index 16b8ae43640cb..44f66b6cbf66e 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php @@ -13,6 +13,8 @@ use Magento\Framework\DB\Select; use Magento\Framework\App\ResourceConnection; use Magento\Catalog\Model\ResourceModel\Product\Gallery; +use PHPUnit_Framework_MockObject_MockObject as MockObject; +use Magento\Framework\DB\Query\BatchIteratorInterface; class ImageTest extends \PHPUnit\Framework\TestCase { @@ -22,34 +24,37 @@ class ImageTest extends \PHPUnit\Framework\TestCase protected $objectManager; /** - * @var AdapterInterface | \PHPUnit_Framework_MockObject_MockObject + * @var AdapterInterface | MockObject */ protected $connectionMock; /** - * @var Generator | \PHPUnit_Framework_MockObject_MockObject + * @var Generator | MockObject */ protected $generatorMock; /** - * @var ResourceConnection | \PHPUnit_Framework_MockObject_MockObject + * @var ResourceConnection | MockObject */ protected $resourceMock; protected function setUp() { - $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->objectManager = + new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->connectionMock = $this->createMock(AdapterInterface::class); $this->resourceMock = $this->createMock(ResourceConnection::class); - $this->resourceMock->method('getConnection')->willReturn($this->connectionMock); - $this->resourceMock->method('getTableName')->willReturnArgument(0); + $this->resourceMock->method('getConnection') + ->willReturn($this->connectionMock); + $this->resourceMock->method('getTableName') + ->willReturnArgument(0); $this->generatorMock = $this->createMock(Generator::class); } /** - * @return \PHPUnit_Framework_MockObject_MockObject + * @return MockObject */ - protected function getVisibleImagesSelectMock(): \PHPUnit_Framework_MockObject_MockObject + protected function getVisibleImagesSelectMock(): MockObject { $selectMock = $this->getMockBuilder(Select::class) ->disableOriginalConstructor() @@ -105,7 +110,10 @@ public function testGetCountAllProductImages(int $imagesCount) ] ); - $this->assertSame($imagesCount, $imageModel->getCountAllProductImages()); + $this->assertSame( + $imagesCount, + $imageModel->getCountAllProductImages() + ); } /** @@ -113,8 +121,10 @@ public function testGetCountAllProductImages(int $imagesCount) * @param int $batchSize * @dataProvider dataProvider */ - public function testGetAllProductImages(int $imagesCount, int $batchSize) - { + public function testGetAllProductImages( + int $imagesCount, + int $batchSize + ) { $this->connectionMock->expects($this->once()) ->method('select') ->willReturn($this->getVisibleImagesSelectMock()); @@ -125,7 +135,7 @@ public function testGetAllProductImages(int $imagesCount, int $batchSize) ->method('fetchAll') ->will($this->returnCallback($fetchResultsCallback)); - /** @var Select | \PHPUnit_Framework_MockObject_MockObject $selectMock */ + /** @var Select | MockObject $selectMock */ $selectMock = $this->getMockBuilder(Select::class) ->disableOriginalConstructor() ->getMock(); @@ -136,8 +146,12 @@ public function testGetAllProductImages(int $imagesCount, int $batchSize) 'value_id', $selectMock, $batchSize, - \Magento\Framework\DB\Query\BatchIteratorInterface::NON_UNIQUE_FIELD_ITERATOR - )->will($this->returnCallback($this->getBatchIteratorCallback($selectMock, $batchCount))); + BatchIteratorInterface::NON_UNIQUE_FIELD_ITERATOR + )->will( + $this->returnCallback( + $this->getBatchIteratorCallback($selectMock, $batchCount) + ) + ); $imageModel = $this->objectManager->getObject( Image::class, @@ -156,10 +170,13 @@ public function testGetAllProductImages(int $imagesCount, int $batchSize) * @param int $batchSize * @return \Closure */ - protected function getFetchResultCallbackForBatches(int $imagesCount, int $batchSize): \Closure - { + protected function getFetchResultCallbackForBatches( + int $imagesCount, + int $batchSize + ): \Closure { $fetchResultsCallback = function () use (&$imagesCount, $batchSize) { - $batchSize = ($imagesCount >= $batchSize) ? $batchSize : $imagesCount; + $batchSize = + ($imagesCount >= $batchSize) ? $batchSize : $imagesCount; $imagesCount -= $batchSize; $getFetchResults = function ($batchSize): array { @@ -180,12 +197,12 @@ protected function getFetchResultCallbackForBatches(int $imagesCount, int $batch } /** - * @param Select | \PHPUnit_Framework_MockObject_MockObject $selectMock + * @param Select | MockObject $selectMock * @param int $batchCount * @return \Closure */ protected function getBatchIteratorCallback( - \PHPUnit_Framework_MockObject_MockObject $selectMock, + MockObject $selectMock, int $batchCount ): \Closure { $iteratorCallback = function () use ($batchCount, $selectMock): array { From 9fa1d41d2d295ab1b8ad7c03963a3a0953d4e69e Mon Sep 17 00:00:00 2001 From: roman Date: Wed, 31 Oct 2018 15:18:34 +0200 Subject: [PATCH 0081/1295] MAGETWO-95402: Incorrect send-friend feature flow --- app/code/Magento/SendFriend/Block/Send.php | 21 + .../Magento/SendFriend/Model/SendFriend.php | 86 +- .../Unit/Controller/Product/SendmailTest.php | 906 ------------------ app/code/Magento/SendFriend/composer.json | 3 +- app/code/Magento/SendFriend/etc/config.xml | 16 + app/code/Magento/SendFriend/etc/module.xml | 1 + .../view/frontend/templates/send.phtml | 1 + .../etc/install-config-mysql.php.dist | 4 +- .../Product/CustomerSendmailTest.php | 168 ++++ 9 files changed, 295 insertions(+), 911 deletions(-) delete mode 100644 app/code/Magento/SendFriend/Test/Unit/Controller/Product/SendmailTest.php create mode 100644 dev/tests/integration/testsuite/Magento/SendFriend/Controller/Product/CustomerSendmailTest.php diff --git a/app/code/Magento/SendFriend/Block/Send.php b/app/code/Magento/SendFriend/Block/Send.php index 43e95ebe43d48..967393e81e4c4 100644 --- a/app/code/Magento/SendFriend/Block/Send.php +++ b/app/code/Magento/SendFriend/Block/Send.php @@ -5,6 +5,7 @@ */ namespace Magento\SendFriend\Block; +use Magento\Captcha\Block\Captcha; use Magento\Customer\Model\Context; /** @@ -222,4 +223,24 @@ public function canSend() { return !$this->sendfriend->isExceedLimit(); } + + /** + * @inheritdoc + */ + protected function _prepareLayout() + { + if (!$this->getChildBlock('captcha')) { + $this->addChild( + 'captcha', + Captcha::class, + [ + 'cacheable' => false, + 'after' => '-', + 'form_id' => 'product_sendtofriend_form', + 'image_width' => 230, + 'image_height' => 230 + ] + ); + } + } } diff --git a/app/code/Magento/SendFriend/Model/SendFriend.php b/app/code/Magento/SendFriend/Model/SendFriend.php index c69d6342b4892..4cfc8d29e4278 100644 --- a/app/code/Magento/SendFriend/Model/SendFriend.php +++ b/app/code/Magento/SendFriend/Model/SendFriend.php @@ -5,7 +5,15 @@ */ namespace Magento\SendFriend\Model; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\LocalizedException as CoreException; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\App\RequestInterface; +use Magento\Captcha\Model\DefaultModel as CaptchaModel; +use Magento\Captcha\Helper\Data as CaptchaHelper; +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Captcha\Observer\CaptchaStringResolver; +use Magento\Customer\Model\Customer; /** * SendFriend Log @@ -109,6 +117,26 @@ class SendFriend extends \Magento\Framework\Model\AbstractModel */ protected $remoteAddress; + /** + * @var RequestInterface + */ + private $request; + + /** + * @var CaptchaHelper + */ + private $captchaHelper; + + /** + * @var CaptchaStringResolver + */ + private $captchaStringResolver; + + /** + * @var CustomerSession + */ + private $customerSession; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -123,7 +151,10 @@ class SendFriend extends \Magento\Framework\Model\AbstractModel * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data - * @SuppressWarnings(PHPMD.ExcessiveParameterList) + * @param CaptchaHelper|null $captchaHelper + * @param CaptchaStringResolver|null $captchaStringResolver + * @param CustomerSession|null $customerSession + * @param RequestInterface|null $request */ public function __construct( \Magento\Framework\Model\Context $context, @@ -138,7 +169,11 @@ public function __construct( \Magento\Framework\Translate\Inline\StateInterface $inlineTranslation, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [] + array $data = [], + CaptchaHelper $captchaHelper = null, + CaptchaStringResolver $captchaStringResolver = null, + CustomerSession $customerSession = null, + RequestInterface $request = null ) { $this->_storeManager = $storeManager; $this->_transportBuilder = $transportBuilder; @@ -148,6 +183,12 @@ public function __construct( $this->remoteAddress = $remoteAddress; $this->cookieManager = $cookieManager; $this->inlineTranslation = $inlineTranslation; + $this->captchaHelper = $captchaHelper ?: ObjectManager::getInstance()->create(CaptchaHelper::class); + $this->captchaStringResolver = $captchaStringResolver ?: + ObjectManager::getInstance()->create(CaptchaStringResolver::class); + $this->customerSession = $customerSession ?: ObjectManager::getInstance()->create(CustomerSession::class); + $this->request = $request ?: ObjectManager::getInstance()->get(RequestInterface::class); + parent::__construct($context, $registry, $resource, $resourceCollection, $data); } @@ -420,12 +461,53 @@ public function canEmailToFriend() * Check if user is exceed limit * * @return boolean + * @throws LocalizedException */ public function isExceedLimit() { + $captchaTargetFormName = 'product_sendtofriend_form'; + /** @var CaptchaModel $captchaModel */ + $captchaModel = $this->captchaHelper->getCaptcha($captchaTargetFormName); + + if ($captchaModel->isRequired()) { + $word = $this->captchaStringResolver->resolve( + $this->request, + $captchaTargetFormName + ); + + if (!$word) { + $this->logCaptchaAttempt($captchaModel); + throw new LocalizedException(__('No CAPTCHA word provided')); + } + $isCorrectCaptcha = $captchaModel->isCorrect($word); + $this->logCaptchaAttempt($captchaModel); + + if (!$isCorrectCaptcha) { + throw new LocalizedException(__('Incorrect CAPTCHA')); + } + } + return $this->getSentCount() >= $this->getMaxSendsToFriend(); } + /** + * Logs a try to pass captcha validation + * + * @param CaptchaModel $captchaModel + */ + private function logCaptchaAttempt(CaptchaModel $captchaModel) + { + /** @var Customer $customer */ + $customer = $this->customerSession->getCustomer(); + $email = ''; + + if ($customer->getId()) { + $email = $customer->getEmail(); + } + + $captchaModel->logAttempt($email); + } + /** * Return count of sent in last period * diff --git a/app/code/Magento/SendFriend/Test/Unit/Controller/Product/SendmailTest.php b/app/code/Magento/SendFriend/Test/Unit/Controller/Product/SendmailTest.php deleted file mode 100644 index c7881f366f520..0000000000000 --- a/app/code/Magento/SendFriend/Test/Unit/Controller/Product/SendmailTest.php +++ /dev/null @@ -1,906 +0,0 @@ -requestMock = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) - ->setMethods(['getPost', 'getPostValue', 'getParam']) - ->getMockForAbstractClass(); - $this->registryMock = $this->getMockBuilder(\Magento\Framework\Registry::class) - ->disableOriginalConstructor() - ->getMock(); - $this->validatorMock = $this->getMockBuilder(\Magento\Framework\Data\Form\FormKey\Validator::class) - ->disableOriginalConstructor() - ->getMock(); - $this->sendFriendMock = $this->getMockBuilder(\Magento\SendFriend\Model\SendFriend::class) - ->disableOriginalConstructor() - ->getMock(); - $this->productRepositoryMock = $this->getMockBuilder(\Magento\Catalog\Api\ProductRepositoryInterface::class) - ->getMockForAbstractClass(); - $this->categoryRepositoryMock = $this->getMockBuilder(\Magento\Catalog\Api\CategoryRepositoryInterface::class) - ->getMockForAbstractClass(); - $this->catalogSessionMock = $this->getMockBuilder(\Magento\Catalog\Model\Session::class) - ->setMethods(['getSendfriendFormData', 'setSendfriendFormData']) - ->disableOriginalConstructor() - ->getMock(); - $this->messageManagerMock = $this->getMockBuilder(\Magento\Framework\Message\ManagerInterface::class) - ->getMock(); - $this->resultFactoryMock = $this->getMockBuilder(\Magento\Framework\Controller\ResultFactory::class) - ->disableOriginalConstructor() - ->getMock(); - $this->eventManagerMock = $this->getMockBuilder(\Magento\Framework\Event\ManagerInterface::class) - ->getMock(); - $this->redirectMock = $this->getMockBuilder(\Magento\Framework\App\Response\RedirectInterface::class) - ->getMock(); - $this->urlBuilderMock = $this->getMockBuilder(\Magento\Framework\UrlInterface::class) - ->getMock(); - - $this->objectManagerHelper = new ObjectManagerHelper($this); - $this->model = $this->objectManagerHelper->getObject( - \Magento\SendFriend\Controller\Product\Sendmail::class, - [ - 'request' => $this->requestMock, - 'coreRegistry' => $this->registryMock, - 'formKeyValidator' => $this->validatorMock, - 'sendFriend' => $this->sendFriendMock, - 'productRepository' => $this->productRepositoryMock, - 'categoryRepository' => $this->categoryRepositoryMock, - 'catalogSession' => $this->catalogSessionMock, - 'messageManager' => $this->messageManagerMock, - 'resultFactory' => $this->resultFactoryMock, - 'eventManager' => $this->eventManagerMock, - 'redirect' => $this->redirectMock, - 'url' => $this->urlBuilderMock, - ] - ); - } - - /** - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function testExecute() - { - $productId = 11; - $categoryId = 5; - $sender = 'sender'; - $recipients = 'recipients'; - $formData = [ - 'sender' => $sender, - 'recipients' => $recipients, - ]; - $productUrl = 'product_url'; - - /** @var \Magento\Framework\Controller\Result\Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ - $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->resultFactoryMock->expects($this->once()) - ->method('create') - ->with(\Magento\Framework\Controller\ResultFactory::TYPE_REDIRECT, []) - ->willReturn($redirectMock); - - $this->validatorMock->expects($this->once()) - ->method('validate') - ->with($this->requestMock) - ->willReturn(true); - - $this->requestMock->expects($this->exactly(2)) - ->method('getParam') - ->willReturnMap( - [ - ['id', null, $productId], - ['cat_id', null, $categoryId], - ] - ); - - /** @var \Magento\Catalog\Api\Data\ProductInterface|\PHPUnit_Framework_MockObject_MockObject $productMock */ - $productMock = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class) - ->setMethods(['isVisibleInCatalog', 'setCategory', 'getProductUrl']) - ->getMockForAbstractClass(); - - $this->productRepositoryMock->expects($this->once()) - ->method('getById') - ->with($productId, false, null, false) - ->willReturn($productMock); - - $productMock->expects($this->once()) - ->method('isVisibleInCatalog') - ->willReturn(true); - - /** @var \Magento\Catalog\Api\Data\CategoryInterface|\PHPUnit_Framework_MockObject_MockObject $categoryMock */ - $categoryMock = $this->getMockBuilder(\Magento\Catalog\Api\Data\CategoryInterface::class) - ->getMockForAbstractClass(); - - $this->categoryRepositoryMock->expects($this->once()) - ->method('get') - ->with($categoryId, null) - ->willReturn($categoryMock); - - $productMock->expects($this->once()) - ->method('setCategory') - ->with($categoryMock); - - $this->registryMock->expects($this->exactly(2)) - ->method('register') - ->willReturnMap( - [ - ['product', $productMock, false, null], - ['current_category', $categoryMock, false, null], - ] - ); - - $this->requestMock->expects($this->once()) - ->method('getPostValue') - ->willReturn($formData); - - $this->requestMock->expects($this->exactly(2)) - ->method('getPost') - ->willReturnMap( - [ - ['sender', $sender], - ['recipients', $recipients], - ] - ); - - $this->sendFriendMock->expects($this->once()) - ->method('setSender') - ->with($sender) - ->willReturnSelf(); - $this->sendFriendMock->expects($this->once()) - ->method('setRecipients') - ->with($recipients) - ->willReturnSelf(); - $this->sendFriendMock->expects($this->once()) - ->method('setProduct') - ->with($productMock) - ->willReturnSelf(); - $this->sendFriendMock->expects($this->once()) - ->method('validate') - ->willReturn(true); - $this->sendFriendMock->expects($this->once()) - ->method('send') - ->willReturnSelf(); - - $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') - ->with(__('The link to a friend was sent.')) - ->willReturnSelf(); - - $productMock->expects($this->once()) - ->method('getProductUrl') - ->willReturn($productUrl); - - $this->redirectMock->expects($this->once()) - ->method('success') - ->with($productUrl) - ->willReturnArgument(0); - - $redirectMock->expects($this->once()) - ->method('setUrl') - ->with($productUrl) - ->willReturnSelf(); - - $this->assertEquals($redirectMock, $this->model->execute()); - } - - /** - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function testExecuteWithoutValidationAndCategory() - { - $productId = 11; - $categoryId = 5; - $sender = 'sender'; - $recipients = 'recipients'; - $formData = [ - 'sender' => $sender, - 'recipients' => $recipients, - ]; - $redirectUrl = 'redirect_url'; - - /** @var \Magento\Framework\Controller\Result\Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ - $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->resultFactoryMock->expects($this->once()) - ->method('create') - ->with(\Magento\Framework\Controller\ResultFactory::TYPE_REDIRECT, []) - ->willReturn($redirectMock); - - $this->validatorMock->expects($this->once()) - ->method('validate') - ->with($this->requestMock) - ->willReturn(true); - - $this->requestMock->expects($this->exactly(2)) - ->method('getParam') - ->willReturnMap( - [ - ['id', null, $productId], - ['cat_id', null, $categoryId], - ] - ); - - /** @var \Magento\Catalog\Api\Data\ProductInterface|\PHPUnit_Framework_MockObject_MockObject $productMock */ - $productMock = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class) - ->setMethods(['isVisibleInCatalog', 'setCategory', 'getProductUrl']) - ->getMockForAbstractClass(); - - $this->productRepositoryMock->expects($this->once()) - ->method('getById') - ->with($productId, false, null, false) - ->willReturn($productMock); - - $productMock->expects($this->once()) - ->method('isVisibleInCatalog') - ->willReturn(true); - - $this->categoryRepositoryMock->expects($this->once()) - ->method('get') - ->with($categoryId, null) - ->willThrowException(new \Magento\Framework\Exception\NoSuchEntityException(__('No Category Exception.'))); - - $productMock->expects($this->never()) - ->method('setCategory'); - - $this->registryMock->expects($this->once()) - ->method('register') - ->willReturnMap( - [ - ['product', $productMock, false, null], - ] - ); - - $this->requestMock->expects($this->once()) - ->method('getPostValue') - ->willReturn($formData); - - $this->requestMock->expects($this->exactly(2)) - ->method('getPost') - ->willReturnMap( - [ - ['sender', $sender], - ['recipients', $recipients], - ] - ); - - $this->sendFriendMock->expects($this->once()) - ->method('setSender') - ->with($sender) - ->willReturnSelf(); - $this->sendFriendMock->expects($this->once()) - ->method('setRecipients') - ->with($recipients) - ->willReturnSelf(); - $this->sendFriendMock->expects($this->once()) - ->method('setProduct') - ->with($productMock) - ->willReturnSelf(); - $this->sendFriendMock->expects($this->once()) - ->method('validate') - ->willReturn(['Some error']); - $this->sendFriendMock->expects($this->never()) - ->method('send'); - - $this->messageManagerMock->expects($this->once()) - ->method('addError') - ->with(__('Some error')) - ->willReturnSelf(); - - $this->catalogSessionMock->expects($this->once()) - ->method('setSendfriendFormData') - ->with($formData); - - $this->urlBuilderMock->expects($this->once()) - ->method('getUrl') - ->with('sendfriend/product/send', ['_current' => true]) - ->willReturn($redirectUrl); - - $this->redirectMock->expects($this->once()) - ->method('error') - ->with($redirectUrl) - ->willReturnArgument(0); - - $redirectMock->expects($this->once()) - ->method('setUrl') - ->with($redirectUrl) - ->willReturnSelf(); - - $this->assertEquals($redirectMock, $this->model->execute()); - } - - /** - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function testExecuteWithoutValidationAndCategoryWithProblems() - { - $productId = 11; - $categoryId = 5; - $sender = 'sender'; - $recipients = 'recipients'; - $formData = [ - 'sender' => $sender, - 'recipients' => $recipients, - ]; - $redirectUrl = 'redirect_url'; - - /** @var \Magento\Framework\Controller\Result\Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ - $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->resultFactoryMock->expects($this->once()) - ->method('create') - ->with(\Magento\Framework\Controller\ResultFactory::TYPE_REDIRECT, []) - ->willReturn($redirectMock); - - $this->validatorMock->expects($this->once()) - ->method('validate') - ->with($this->requestMock) - ->willReturn(true); - - $this->requestMock->expects($this->exactly(2)) - ->method('getParam') - ->willReturnMap( - [ - ['id', null, $productId], - ['cat_id', null, $categoryId], - ] - ); - - /** @var \Magento\Catalog\Api\Data\ProductInterface|\PHPUnit_Framework_MockObject_MockObject $productMock */ - $productMock = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class) - ->setMethods(['isVisibleInCatalog', 'setCategory', 'getProductUrl']) - ->getMockForAbstractClass(); - - $this->productRepositoryMock->expects($this->once()) - ->method('getById') - ->with($productId, false, null, false) - ->willReturn($productMock); - - $productMock->expects($this->once()) - ->method('isVisibleInCatalog') - ->willReturn(true); - - $this->categoryRepositoryMock->expects($this->once()) - ->method('get') - ->with($categoryId, null) - ->willThrowException(new \Magento\Framework\Exception\NoSuchEntityException(__('No Category Exception.'))); - - $productMock->expects($this->never()) - ->method('setCategory'); - - $this->registryMock->expects($this->once()) - ->method('register') - ->willReturnMap( - [ - ['product', $productMock, false, null], - ] - ); - - $this->requestMock->expects($this->once()) - ->method('getPostValue') - ->willReturn($formData); - - $this->requestMock->expects($this->exactly(2)) - ->method('getPost') - ->willReturnMap( - [ - ['sender', $sender], - ['recipients', $recipients], - ] - ); - - $this->sendFriendMock->expects($this->once()) - ->method('setSender') - ->with($sender) - ->willReturnSelf(); - $this->sendFriendMock->expects($this->once()) - ->method('setRecipients') - ->with($recipients) - ->willReturnSelf(); - $this->sendFriendMock->expects($this->once()) - ->method('setProduct') - ->with($productMock) - ->willReturnSelf(); - $this->sendFriendMock->expects($this->once()) - ->method('validate') - ->willReturn('Some error'); - $this->sendFriendMock->expects($this->never()) - ->method('send'); - - $this->messageManagerMock->expects($this->once()) - ->method('addError') - ->with(__('We found some problems with the data.')) - ->willReturnSelf(); - - $this->catalogSessionMock->expects($this->once()) - ->method('setSendfriendFormData') - ->with($formData); - - $this->urlBuilderMock->expects($this->once()) - ->method('getUrl') - ->with('sendfriend/product/send', ['_current' => true]) - ->willReturn($redirectUrl); - - $this->redirectMock->expects($this->once()) - ->method('error') - ->with($redirectUrl) - ->willReturnArgument(0); - - $redirectMock->expects($this->once()) - ->method('setUrl') - ->with($redirectUrl) - ->willReturnSelf(); - - $this->assertEquals($redirectMock, $this->model->execute()); - } - - /** - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function testExecuteWithLocalizedException() - { - $productId = 11; - $categoryId = 5; - $sender = 'sender'; - $recipients = 'recipients'; - $formData = [ - 'sender' => $sender, - 'recipients' => $recipients, - ]; - $redirectUrl = 'redirect_url'; - - /** @var \Magento\Framework\Controller\Result\Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ - $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->resultFactoryMock->expects($this->once()) - ->method('create') - ->with(\Magento\Framework\Controller\ResultFactory::TYPE_REDIRECT, []) - ->willReturn($redirectMock); - - $this->validatorMock->expects($this->once()) - ->method('validate') - ->with($this->requestMock) - ->willReturn(true); - - $this->requestMock->expects($this->exactly(2)) - ->method('getParam') - ->willReturnMap( - [ - ['id', null, $productId], - ['cat_id', null, $categoryId], - ] - ); - - /** @var \Magento\Catalog\Api\Data\ProductInterface|\PHPUnit_Framework_MockObject_MockObject $productMock */ - $productMock = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class) - ->setMethods(['isVisibleInCatalog', 'setCategory', 'getProductUrl']) - ->getMockForAbstractClass(); - - $this->productRepositoryMock->expects($this->once()) - ->method('getById') - ->with($productId, false, null, false) - ->willReturn($productMock); - - $productMock->expects($this->once()) - ->method('isVisibleInCatalog') - ->willReturn(true); - - $this->categoryRepositoryMock->expects($this->once()) - ->method('get') - ->with($categoryId, null) - ->willThrowException(new \Magento\Framework\Exception\NoSuchEntityException(__('No Category Exception.'))); - - $productMock->expects($this->never()) - ->method('setCategory'); - - $this->registryMock->expects($this->once()) - ->method('register') - ->willReturnMap( - [ - ['product', $productMock, false, null], - ] - ); - - $this->requestMock->expects($this->once()) - ->method('getPostValue') - ->willReturn($formData); - - $this->requestMock->expects($this->exactly(2)) - ->method('getPost') - ->willReturnMap( - [ - ['sender', $sender], - ['recipients', $recipients], - ] - ); - - $this->sendFriendMock->expects($this->once()) - ->method('setSender') - ->with($sender) - ->willReturnSelf(); - $this->sendFriendMock->expects($this->once()) - ->method('setRecipients') - ->with($recipients) - ->willReturnSelf(); - $this->sendFriendMock->expects($this->once()) - ->method('setProduct') - ->with($productMock) - ->willReturnSelf(); - $this->sendFriendMock->expects($this->once()) - ->method('validate') - ->willThrowException(new \Magento\Framework\Exception\LocalizedException(__('Localized Exception.'))); - $this->sendFriendMock->expects($this->never()) - ->method('send'); - - $this->messageManagerMock->expects($this->once()) - ->method('addError') - ->with(__('Localized Exception.')) - ->willReturnSelf(); - - $this->catalogSessionMock->expects($this->once()) - ->method('setSendfriendFormData') - ->with($formData); - - $this->urlBuilderMock->expects($this->once()) - ->method('getUrl') - ->with('sendfriend/product/send', ['_current' => true]) - ->willReturn($redirectUrl); - - $this->redirectMock->expects($this->once()) - ->method('error') - ->with($redirectUrl) - ->willReturnArgument(0); - - $redirectMock->expects($this->once()) - ->method('setUrl') - ->with($redirectUrl) - ->willReturnSelf(); - - $this->assertEquals($redirectMock, $this->model->execute()); - } - - /** - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function testExecuteWithException() - { - $productId = 11; - $categoryId = 5; - $sender = 'sender'; - $recipients = 'recipients'; - $formData = [ - 'sender' => $sender, - 'recipients' => $recipients, - ]; - $redirectUrl = 'redirect_url'; - - /** @var \Magento\Framework\Controller\Result\Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ - $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->resultFactoryMock->expects($this->once()) - ->method('create') - ->with(\Magento\Framework\Controller\ResultFactory::TYPE_REDIRECT, []) - ->willReturn($redirectMock); - - $this->validatorMock->expects($this->once()) - ->method('validate') - ->with($this->requestMock) - ->willReturn(true); - - $this->requestMock->expects($this->exactly(2)) - ->method('getParam') - ->willReturnMap( - [ - ['id', null, $productId], - ['cat_id', null, $categoryId], - ] - ); - - /** @var \Magento\Catalog\Api\Data\ProductInterface|\PHPUnit_Framework_MockObject_MockObject $productMock */ - $productMock = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class) - ->setMethods(['isVisibleInCatalog', 'setCategory', 'getProductUrl']) - ->getMockForAbstractClass(); - - $this->productRepositoryMock->expects($this->once()) - ->method('getById') - ->with($productId, false, null, false) - ->willReturn($productMock); - - $productMock->expects($this->once()) - ->method('isVisibleInCatalog') - ->willReturn(true); - - $this->categoryRepositoryMock->expects($this->once()) - ->method('get') - ->with($categoryId, null) - ->willThrowException(new \Magento\Framework\Exception\NoSuchEntityException(__('No Category Exception.'))); - - $productMock->expects($this->never()) - ->method('setCategory'); - - $this->registryMock->expects($this->once()) - ->method('register') - ->willReturnMap( - [ - ['product', $productMock, false, null], - ] - ); - - $this->requestMock->expects($this->once()) - ->method('getPostValue') - ->willReturn($formData); - - $this->requestMock->expects($this->exactly(2)) - ->method('getPost') - ->willReturnMap( - [ - ['sender', $sender], - ['recipients', $recipients], - ] - ); - - $this->sendFriendMock->expects($this->once()) - ->method('setSender') - ->with($sender) - ->willReturnSelf(); - $this->sendFriendMock->expects($this->once()) - ->method('setRecipients') - ->with($recipients) - ->willReturnSelf(); - $this->sendFriendMock->expects($this->once()) - ->method('setProduct') - ->with($productMock) - ->willReturnSelf(); - $exception = new \Exception(__('Exception.')); - $this->sendFriendMock->expects($this->once()) - ->method('validate') - ->willThrowException($exception); - $this->sendFriendMock->expects($this->never()) - ->method('send'); - - $this->messageManagerMock->expects($this->once()) - ->method('addException') - ->with($exception, __('Some emails were not sent.')) - ->willReturnSelf(); - - $this->catalogSessionMock->expects($this->once()) - ->method('setSendfriendFormData') - ->with($formData); - - $this->urlBuilderMock->expects($this->once()) - ->method('getUrl') - ->with('sendfriend/product/send', ['_current' => true]) - ->willReturn($redirectUrl); - - $this->redirectMock->expects($this->once()) - ->method('error') - ->with($redirectUrl) - ->willReturnArgument(0); - - $redirectMock->expects($this->once()) - ->method('setUrl') - ->with($redirectUrl) - ->willReturnSelf(); - - $this->assertEquals($redirectMock, $this->model->execute()); - } - - /** - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function testExecuteWithoutProduct() - { - $sender = 'sender'; - $recipients = 'recipients'; - $formData = [ - 'sender' => $sender, - 'recipients' => $recipients, - ]; - - /** @var \Magento\Framework\Controller\Result\Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ - $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) - ->disableOriginalConstructor() - ->getMock(); - - /** @var \Magento\Framework\Controller\Result\Forward|\PHPUnit_Framework_MockObject_MockObject $forwardMock */ - $forwardMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Forward::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->resultFactoryMock->expects($this->exactly(2)) - ->method('create') - ->willReturnMap( - [ - [\Magento\Framework\Controller\ResultFactory::TYPE_REDIRECT, [], $redirectMock], - [\Magento\Framework\Controller\ResultFactory::TYPE_FORWARD, [], $forwardMock], - ] - ); - - $this->validatorMock->expects($this->once()) - ->method('validate') - ->with($this->requestMock) - ->willReturn(true); - - $this->requestMock->expects($this->once()) - ->method('getParam') - ->willReturnMap( - [ - ['id', null, null], - ] - ); - - $this->requestMock->expects($this->once()) - ->method('getPostValue') - ->willReturn($formData); - - $forwardMock->expects($this->once()) - ->method('forward') - ->with('noroute') - ->willReturnSelf(); - - $this->assertEquals($forwardMock, $this->model->execute()); - } - - /** - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function testExecuteWithoutData() - { - $productId = 11; - $formData = ''; - - /** @var \Magento\Framework\Controller\Result\Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ - $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) - ->disableOriginalConstructor() - ->getMock(); - - /** @var \Magento\Framework\Controller\Result\Forward|\PHPUnit_Framework_MockObject_MockObject $forwardMock */ - $forwardMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Forward::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->resultFactoryMock->expects($this->exactly(2)) - ->method('create') - ->willReturnMap( - [ - [\Magento\Framework\Controller\ResultFactory::TYPE_REDIRECT, [], $redirectMock], - [\Magento\Framework\Controller\ResultFactory::TYPE_FORWARD, [], $forwardMock], - ] - ); - - $this->validatorMock->expects($this->once()) - ->method('validate') - ->with($this->requestMock) - ->willReturn(true); - - $this->requestMock->expects($this->once()) - ->method('getParam') - ->willReturnMap( - [ - ['id', null, $productId], - ] - ); - - /** @var \Magento\Catalog\Api\Data\ProductInterface|\PHPUnit_Framework_MockObject_MockObject $productMock */ - $productMock = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class) - ->setMethods(['isVisibleInCatalog', 'setCategory', 'getProductUrl']) - ->getMockForAbstractClass(); - - $this->productRepositoryMock->expects($this->once()) - ->method('getById') - ->with($productId, false, null, false) - ->willReturn($productMock); - - $productMock->expects($this->once()) - ->method('isVisibleInCatalog') - ->willReturn(true); - - $this->registryMock->expects($this->once()) - ->method('register') - ->willReturnMap( - [ - ['product', $productMock, false, null], - ] - ); - - $this->requestMock->expects($this->once()) - ->method('getPostValue') - ->willReturn($formData); - - $forwardMock->expects($this->once()) - ->method('forward') - ->with('noroute') - ->willReturnSelf(); - - $this->assertEquals($forwardMock, $this->model->execute()); - } - - public function testExecuteWithoutFormKey() - { - /** @var \Magento\Framework\Controller\Result\Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ - $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->resultFactoryMock->expects($this->once()) - ->method('create') - ->willReturnMap( - [ - [\Magento\Framework\Controller\ResultFactory::TYPE_REDIRECT, [], $redirectMock], - ] - ); - - $this->validatorMock->expects($this->once()) - ->method('validate') - ->with($this->requestMock) - ->willReturn(false); - - $redirectMock->expects($this->once()) - ->method('setPath') - ->with('sendfriend/product/send', ['_current' => true]) - ->willReturnSelf(); - - $this->assertEquals($redirectMock, $this->model->execute()); - } -} diff --git a/app/code/Magento/SendFriend/composer.json b/app/code/Magento/SendFriend/composer.json index 324465885f82f..806f02b23cc22 100644 --- a/app/code/Magento/SendFriend/composer.json +++ b/app/code/Magento/SendFriend/composer.json @@ -6,7 +6,8 @@ "magento/module-store": "100.2.*", "magento/module-catalog": "102.0.*", "magento/module-customer": "101.0.*", - "magento/framework": "101.0.*" + "magento/framework": "101.0.*", + "magento/module-captcha": "100.2.*" }, "type": "magento2-module", "version": "100.2.2", diff --git a/app/code/Magento/SendFriend/etc/config.xml b/app/code/Magento/SendFriend/etc/config.xml index 9fa005dcd2fd4..d65e5a4a073dd 100644 --- a/app/code/Magento/SendFriend/etc/config.xml +++ b/app/code/Magento/SendFriend/etc/config.xml @@ -17,5 +17,21 @@ 0 + + + + + + + + + + + + + 1 + + + diff --git a/app/code/Magento/SendFriend/etc/module.xml b/app/code/Magento/SendFriend/etc/module.xml index fae2b90f710a3..c874c54cbc672 100644 --- a/app/code/Magento/SendFriend/etc/module.xml +++ b/app/code/Magento/SendFriend/etc/module.xml @@ -10,6 +10,7 @@ + diff --git a/app/code/Magento/SendFriend/view/frontend/templates/send.phtml b/app/code/Magento/SendFriend/view/frontend/templates/send.phtml index 2b25e0efab84a..52b1a4061db22 100644 --- a/app/code/Magento/SendFriend/view/frontend/templates/send.phtml +++ b/app/code/Magento/SendFriend/view/frontend/templates/send.phtml @@ -108,6 +108,7 @@
getChildHtml('form_additional_info') ?> + getChildHtml('captcha'); ?>
-getDisplayIfEmpty()): ?> +isReviewEnabled() && $block->getDisplayIfEmpty()): ?>
-getDisplayIfEmpty()): ?> +isReviewEnabled() && $block->getDisplayIfEmpty()): ?>
From 5a1f1df2b12d72e0cbcd9042da061516d8c49ffc Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky Date: Tue, 11 Dec 2018 13:41:25 +0200 Subject: [PATCH 0136/1295] ENGCOM-3572: Static tests fixed. --- .../Review/Model/ResourceModel/Rating.php | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Review/Model/ResourceModel/Rating.php b/app/code/Magento/Review/Model/ResourceModel/Rating.php index 78b3c57952267..73249e269025a 100644 --- a/app/code/Magento/Review/Model/ResourceModel/Rating.php +++ b/app/code/Magento/Review/Model/ResourceModel/Rating.php @@ -15,6 +15,7 @@ * * @author Magento Core Team * @since 100.0.2 + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Rating extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb { @@ -43,13 +44,13 @@ class Rating extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb private $scopeConfig; /** - * @param \Magento\Framework\Model\ResourceModel\Db\Context $context - * @param \Psr\Log\LoggerInterface $logger - * @param \Magento\Framework\Module\Manager $moduleManager - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Review\Model\ResourceModel\Review\Summary $reviewSummary - * @param string $connectionName - * @param ScopeConfigInterface|null $scopeConfig + * @param \Magento\Framework\Model\ResourceModel\Db\Context $context + * @param \Psr\Log\LoggerInterface $logger + * @param \Magento\Framework\Module\Manager $moduleManager + * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param Review\Summary $reviewSummary + * @param string $connectionName + * @param ScopeConfigInterface|null $scopeConfig */ public function __construct( \Magento\Framework\Model\ResourceModel\Db\Context $context, @@ -189,6 +190,8 @@ protected function _afterSave(\Magento\Framework\Model\AbstractModel $object) } /** + * Process rating codes. + * * @param \Magento\Framework\Model\AbstractModel $object * @return $this */ @@ -212,6 +215,8 @@ protected function processRatingCodes(\Magento\Framework\Model\AbstractModel $ob } /** + * Process rating stores. + * * @param \Magento\Framework\Model\AbstractModel $object * @return $this */ @@ -235,6 +240,8 @@ protected function processRatingStores(\Magento\Framework\Model\AbstractModel $o } /** + * Delete rating data. + * * @param int $ratingId * @param string $table * @param array $storeIds @@ -258,6 +265,8 @@ protected function deleteRatingData($ratingId, $table, array $storeIds) } /** + * Insert rating data. + * * @param string $table * @param array $data * @return void @@ -280,6 +289,7 @@ protected function insertRatingData($table, array $data) /** * Perform actions after object delete + * * Prepare rating data for reaggregate all data for reviews * * @param \Magento\Framework\Model\AbstractModel $object @@ -290,7 +300,7 @@ protected function _afterDelete(\Magento\Framework\Model\AbstractModel $object) parent::_afterDelete($object); if (!$this->moduleManager->isEnabled('Magento_Review') && !$this->scopeConfig->getValue( - \Magento\Review\Observer\PredispatchReviewObserver::XML_PATH_REVIEW_ACTIVE, + \Magento\Review\Observer\PredispatchReviewObserver::XML_PATH_REVIEW_ACTIVE, \Magento\Store\Model\ScopeInterface::SCOPE_STORE ) ) { From e3ec0f68f55f9268c03c4c25152b4cc2c8be737c Mon Sep 17 00:00:00 2001 From: Vasilii Burlacu Date: Fri, 9 Nov 2018 13:48:17 +0200 Subject: [PATCH 0137/1295] magento/magento2:#19101 - API REST and Reserved Order Id - Fixed issue "Can not update cart with a reserved order number like 000000651" --- app/code/Magento/Quote/Api/Data/CartInterface.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Quote/Api/Data/CartInterface.php b/app/code/Magento/Quote/Api/Data/CartInterface.php index 551833e3effb1..b87869de6b3df 100644 --- a/app/code/Magento/Quote/Api/Data/CartInterface.php +++ b/app/code/Magento/Quote/Api/Data/CartInterface.php @@ -223,14 +223,14 @@ public function setBillingAddress(\Magento\Quote\Api\Data\AddressInterface $bill /** * Returns the reserved order ID for the cart. * - * @return int|null Reserved order ID. Otherwise, null. + * @return string|null Reserved order ID. Otherwise, null. */ public function getReservedOrderId(); /** * Sets the reserved order ID for the cart. * - * @param int $reservedOrderId + * @param string $reservedOrderId * @return $this */ public function setReservedOrderId($reservedOrderId); From d764163a42344a6a903adaf463f87d1f4f7cd811 Mon Sep 17 00:00:00 2001 From: DianaRusin Date: Mon, 24 Dec 2018 10:14:53 +0200 Subject: [PATCH 0138/1295] MAGETWO-97195: Automate with Integration test Confirmation email should be delivered to the customer when address contains '+' symbol --- .../Customer/Controller/AccountTest.php | 161 +++++++++++------- .../customer_confirmation_config_disable.php | 27 +++ ...r_confirmation_config_disable_rollback.php | 28 +++ .../customer_confirmation_config_enable.php | 27 +++ ...er_confirmation_config_enable_rollback.php | 28 +++ ...ation_email_address_with_special_chars.php | 33 ++++ ...il_address_with_special_chars_rollback.php | 28 +++ 7 files changed, 275 insertions(+), 57 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_disable.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_disable_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_enable.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_enable_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars_rollback.php diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php index ff0d838cb6f82..9a0676f69e9c5 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php @@ -23,6 +23,11 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\Request; use Magento\TestFramework\Response; +use Magento\TestFramework\Mail\Template\TransportBuilderMock; +use Magento\Framework\App\Config\MutableScopeConfigInterface; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\Stdlib\CookieManagerInterface; +use Magento\Theme\Controller\Result\MessagePlugin; use Zend\Stdlib\Parameters; /** @@ -30,6 +35,23 @@ */ class AccountTest extends \Magento\TestFramework\TestCase\AbstractController { + /** + * @var MutableScopeConfigInterface + */ + private $mutableScopeConfig; + + /** + * @var TransportBuilderMock + */ + private $transportBuilderMock; + + protected function setUp() + { + parent::setUp(); + $this->mutableScopeConfig = $this->_objectManager->get(MutableScopeConfigInterface::class); + $this->transportBuilderMock = $this->_objectManager->get(TransportBuilderMock::class); + } + /** * Login the user * @@ -102,11 +124,7 @@ public function testForgotPasswordEmailMessageWithSpecialCharacters() $this->dispatch('customer/account/forgotPasswordPost'); $this->assertRedirect($this->stringContains('customer/account/')); - /** @var \Magento\TestFramework\Mail\Template\TransportBuilderMock $transportBuilder */ - $transportBuilder = $this->_objectManager->get( - \Magento\TestFramework\Mail\Template\TransportBuilderMock::class - ); - $subject = $transportBuilder->getSentMessage()->getSubject(); + $subject = $this->transportBuilderMock->getSentMessage()->getSubject(); $this->assertContains( 'Test special\' characters', $subject @@ -228,26 +246,10 @@ public function testNoFormKeyCreatePostAction() /** * @magentoDbIsolation enabled * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Customer/_files/customer_confirmation_config_disable.php */ public function testNoConfirmCreatePostAction() { - /** @var \Magento\Framework\App\Config\MutableScopeConfigInterface $mutableScopeConfig */ - $mutableScopeConfig = Bootstrap::getObjectManager() - ->get(\Magento\Framework\App\Config\MutableScopeConfigInterface::class); - - $scopeValue = $mutableScopeConfig->getValue( - 'customer/create_account/confirm', - ScopeInterface::SCOPE_WEBSITES, - null - ); - - $mutableScopeConfig->setValue( - 'customer/create_account/confirm', - 0, - ScopeInterface::SCOPE_WEBSITES, - null - ); - $this->fillRequestWithAccountDataAndFormKey(); $this->dispatch('customer/account/createPost'); $this->assertRedirect($this->stringEndsWith('customer/account/')); @@ -255,38 +257,15 @@ public function testNoConfirmCreatePostAction() $this->equalTo(['Thank you for registering with Main Website Store.']), MessageInterface::TYPE_SUCCESS ); - - $mutableScopeConfig->setValue( - 'customer/create_account/confirm', - $scopeValue, - ScopeInterface::SCOPE_WEBSITES, - null - ); } /** * @magentoDbIsolation enabled * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Customer/_files/customer_confirmation_config_enable.php */ public function testWithConfirmCreatePostAction() { - /** @var \Magento\Framework\App\Config\MutableScopeConfigInterface $mutableScopeConfig */ - $mutableScopeConfig = Bootstrap::getObjectManager() - ->get(\Magento\Framework\App\Config\MutableScopeConfigInterface::class); - - $scopeValue = $mutableScopeConfig->getValue( - 'customer/create_account/confirm', - ScopeInterface::SCOPE_WEBSITES, - null - ); - - $mutableScopeConfig->setValue( - 'customer/create_account/confirm', - 1, - ScopeInterface::SCOPE_WEBSITES, - null - ); - $this->fillRequestWithAccountDataAndFormKey(); $this->dispatch('customer/account/createPost'); $this->assertRedirect($this->stringContains('customer/account/index/')); @@ -298,13 +277,6 @@ public function testWithConfirmCreatePostAction() ]), MessageInterface::TYPE_SUCCESS ); - - $mutableScopeConfig->setValue( - 'customer/create_account/confirm', - $scopeValue, - ScopeInterface::SCOPE_WEBSITES, - null - ); } /** @@ -690,6 +662,49 @@ public function testLoginPostRedirect($redirectDashboard, string $redirectUrl) $this->assertTrue($this->_objectManager->get(Session::class)->isLoggedIn()); } + /** + * Test that confirmation email address displays special characters correctly. + * + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Customer/_files/customer_confirmation_email_address_with_special_chars.php + * + * @return void + */ + public function testConfirmationEmailWithSpecialCharacters() + { + $email = 'customer+confirmation@example.com'; + $this->dispatch('customer/account/confirmation/email/customer%2Bconfirmation%40email.com'); + $this->getRequest()->setPostValue('email', $email); + $this->dispatch('customer/account/confirmation/email/customer%2Bconfirmation%40email.com'); + + $this->assertRedirect($this->stringContains('customer/account/index')); + $this->assertSessionMessages( + $this->equalTo(['Please check your email for confirmation key.']), + MessageInterface::TYPE_SUCCESS + ); + + /** @var $message \Magento\Framework\Mail\Message */ + $message = $this->transportBuilderMock->getSentMessage(); + $rawMessage = $message->getRawMessage(); + + $this->assertContains('To: ' . $email, $rawMessage); + + $content = $message->getBody()->getPartContent(0); + preg_match('.*?)".*>', $content, $matches); + $confirmationUrl = $matches['url']; + $confirmationUrl = str_replace('http://localhost/index.php/','',$confirmationUrl); + $confirmationUrl = html_entity_decode($confirmationUrl); + $this->setRequestInfo($confirmationUrl, 'confirm'); + $this->clearCookieMessagesList(); + $this->dispatch($confirmationUrl); + + $this->assertRedirect($this->stringContains('customer/account/index')); + $this->assertSessionMessages( + $this->equalTo(['Thank you for registering with Main Website Store.']), + MessageInterface::TYPE_SUCCESS + ); + } + /** * Data provider for testLoginPostRedirect. * @@ -705,16 +720,17 @@ public function loginPostRedirectDataProvider() } /** + * @param string $email * @return void */ - private function fillRequestWithAccountData() + private function fillRequestWithAccountData(string $email = 'test1@email.com') { $this->getRequest() ->setMethod('POST') ->setParam('firstname', 'firstname1') ->setParam('lastname', 'lastname1') ->setParam('company', '') - ->setParam('email', 'test1@email.com') + ->setParam('email', $email) ->setParam('password', '_Password1') ->setParam('password_confirmation', '_Password1') ->setParam('telephone', '5123334444') @@ -731,11 +747,12 @@ private function fillRequestWithAccountData() } /** + * @param string $email * @return void */ - private function fillRequestWithAccountDataAndFormKey() + private function fillRequestWithAccountDataAndFormKey(string $email = 'test1@email.com') { - $this->fillRequestWithAccountData(); + $this->fillRequestWithAccountData($email); $formKey = $this->_objectManager->get(FormKey::class); $this->getRequest()->setParam('form_key', $formKey->getFormKey()); } @@ -805,4 +822,34 @@ private function assertResponseRedirect(Response $response, string $redirectUrl) $this->assertTrue($response->isRedirect()); $this->assertSame($redirectUrl, $response->getHeader('Location')->getUri()); } + + /** + * Add new request info (request uri, path info, action name). + * + * @param string $uri + * @param string $actionName + * @return void + */ + private function setRequestInfo(string $uri, string $actionName) + { + $this->getRequest() + ->setRequestUri($uri) + ->setPathInfo() + ->setActionName($actionName); + } + + /** + * Clear cookie messages list. + * + * @return void + */ + private function clearCookieMessagesList() + { + $cookieManager = $this->_objectManager->get(CookieManagerInterface::class); + $jsonSerializer = $this->_objectManager->get(Json::class); + $cookieManager->setPublicCookie( + MessagePlugin::MESSAGES_COOKIES_NAME, + $jsonSerializer->serialize([]) + ); + } } diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_disable.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_disable.php new file mode 100644 index 0000000000000..af47b64c3dc5b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_disable.php @@ -0,0 +1,27 @@ +create(MutableScopeConfigInterface::class); + +$this->confirmationConfigScopeValue = $mutableScopeConfig->getValue( + 'customer/create_account/confirm', + ScopeInterface::SCOPE_WEBSITES, + null +); + +$mutableScopeConfig->setValue( + 'customer/create_account/confirm', + 0, + ScopeInterface::SCOPE_WEBSITES, + null +); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_disable_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_disable_rollback.php new file mode 100644 index 0000000000000..700940f48c2b2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_disable_rollback.php @@ -0,0 +1,28 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +$mutableScopeConfig = Bootstrap::getObjectManager()->create(MutableScopeConfigInterface::class); +$mutableScopeConfig->setValue( + 'customer/create_account/confirm', + $this->confirmationConfigScopeValue, + ScopeInterface::SCOPE_WEBSITES, + null +); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_enable.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_enable.php new file mode 100644 index 0000000000000..85b6f46482705 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_enable.php @@ -0,0 +1,27 @@ +create(MutableScopeConfigInterface::class); + +$this->confirmationConfigScopeValue = $mutableScopeConfig->getValue( + 'customer/create_account/confirm', + ScopeInterface::SCOPE_WEBSITES, + null +); + +$mutableScopeConfig->setValue( + 'customer/create_account/confirm', + 1, + ScopeInterface::SCOPE_WEBSITES, + null +); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_enable_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_enable_rollback.php new file mode 100644 index 0000000000000..700940f48c2b2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_enable_rollback.php @@ -0,0 +1,28 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +$mutableScopeConfig = Bootstrap::getObjectManager()->create(MutableScopeConfigInterface::class); +$mutableScopeConfig->setValue( + 'customer/create_account/confirm', + $this->confirmationConfigScopeValue, + ScopeInterface::SCOPE_WEBSITES, + null +); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars.php new file mode 100644 index 0000000000000..85fe453a0db17 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars.php @@ -0,0 +1,33 @@ +create(Customer::class); +$customer->setWebsiteId(1) + ->setId(1) + ->setEmail('customer+confirmation@example.com') + ->setPassword('password') + ->setConfirmation($customer->getRandomConfirmationKey()) + ->setGroupId(1) + ->setStoreId(1) + ->setIsActive(1) + ->setFirstname('John') + ->setLastname('Smith') + ->setDefaultBilling(1) + ->setDefaultShipping(1) + ->setTaxvat('12') + ->setGender(0); + +$customer->isObjectNew(true); +$customer->save(); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars_rollback.php new file mode 100644 index 0000000000000..ada4907d73063 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars_rollback.php @@ -0,0 +1,28 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var Customer $customer */ +$customer = Bootstrap::getObjectManager()->create(Customer::class); +$customer->load(1); +if ($customer->getId()) { + $customer->delete(); +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); From 42553db56b1bdf02cef31a65b2c2f7acff45032d Mon Sep 17 00:00:00 2001 From: DianaRusin Date: Mon, 24 Dec 2018 10:24:18 +0200 Subject: [PATCH 0139/1295] MAGETWO-97195: Automate with Integration test Confirmation email should be delivered to the customer when address contains '+' symbol --- .../testsuite/Magento/Customer/Controller/AccountTest.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php index 9a0676f69e9c5..eb246357e216c 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php @@ -19,12 +19,10 @@ use Magento\Framework\App\Http; use Magento\Framework\Data\Form\FormKey; use Magento\Framework\Message\MessageInterface; -use Magento\Store\Model\ScopeInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\Request; use Magento\TestFramework\Response; use Magento\TestFramework\Mail\Template\TransportBuilderMock; -use Magento\Framework\App\Config\MutableScopeConfigInterface; use Magento\Framework\Serialize\Serializer\Json; use Magento\Framework\Stdlib\CookieManagerInterface; use Magento\Theme\Controller\Result\MessagePlugin; @@ -35,11 +33,6 @@ */ class AccountTest extends \Magento\TestFramework\TestCase\AbstractController { - /** - * @var MutableScopeConfigInterface - */ - private $mutableScopeConfig; - /** * @var TransportBuilderMock */ @@ -48,7 +41,6 @@ class AccountTest extends \Magento\TestFramework\TestCase\AbstractController protected function setUp() { parent::setUp(); - $this->mutableScopeConfig = $this->_objectManager->get(MutableScopeConfigInterface::class); $this->transportBuilderMock = $this->_objectManager->get(TransportBuilderMock::class); } From b5142180a39266d874fb7b8df63ec84af958999b Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun Date: Mon, 24 Dec 2018 15:46:03 +0200 Subject: [PATCH 0140/1295] MAGETWO-73743: [FT] Magento\Catalog\Test\TestCase\Product\ProductTypeSwitchingOnUpdateTest fails on CI --- .../Product/ProductTypeSwitchingOnUpdateTest.xml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.xml index f3df374a8bac8..f74ffd288c299 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.xml @@ -11,7 +11,6 @@ catalogProductSimple::default configurableProduct::default - - to_maintain:yes @@ -21,7 +20,6 @@ - to_maintain:yes catalogProductSimple::default catalogProductVirtual::default - @@ -29,7 +27,6 @@ - stable:no configurableProduct::default catalogProductSimple::product_without_category deleteVariations @@ -40,12 +37,10 @@ configurableProduct::default catalogProductVirtual::required_fields deleteVariations - to_maintain:yes - to_maintain:yes catalogProductVirtual::default catalogProductSimple::default - @@ -56,7 +51,6 @@ catalogProductVirtual::default configurableProduct::not_virtual_for_type_switching - - to_maintain:yes @@ -69,7 +63,6 @@ catalogProductVirtual::default downloadableProduct::default - - to_maintain:yes @@ -81,7 +74,6 @@ downloadableProduct::default catalogProductSimple::default - - to_maintain:yes @@ -89,7 +81,6 @@ downloadableProduct::default configurableProduct::not_virtual_for_type_switching - - to_maintain:yes @@ -99,7 +90,6 @@ - stable:no downloadableProduct::default catalogProductVirtual::default clearDownloadableData @@ -110,7 +100,6 @@ catalogProductSimple::default downloadableProduct::default - - to_maintain:yes From c258f9823ff766ff8335d255a841b23d020436a3 Mon Sep 17 00:00:00 2001 From: DianaRusin Date: Mon, 24 Dec 2018 16:57:59 +0200 Subject: [PATCH 0141/1295] MAGETWO-97195: Automate with Integration test Confirmation email should be delivered to the customer when address contains '+' symbol --- .../Customer/Controller/AccountTest.php | 27 ++++++++++++++++--- .../customer_confirmation_config_disable.php | 6 ----- ...r_confirmation_config_disable_rollback.php | 13 +++------ .../customer_confirmation_config_enable.php | 6 ----- ...er_confirmation_config_enable_rollback.php | 13 +++------ ...ation_email_address_with_special_chars.php | 16 ++++++----- ...il_address_with_special_chars_rollback.php | 11 ++++---- 7 files changed, 47 insertions(+), 45 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php index eb246357e216c..60c3314305dd5 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php @@ -38,6 +38,9 @@ class AccountTest extends \Magento\TestFramework\TestCase\AbstractController */ private $transportBuilderMock; + /** + * @inheritdoc + */ protected function setUp() { parent::setUp(); @@ -682,10 +685,7 @@ public function testConfirmationEmailWithSpecialCharacters() $this->assertContains('To: ' . $email, $rawMessage); $content = $message->getBody()->getPartContent(0); - preg_match('.*?)".*>', $content, $matches); - $confirmationUrl = $matches['url']; - $confirmationUrl = str_replace('http://localhost/index.php/','',$confirmationUrl); - $confirmationUrl = html_entity_decode($confirmationUrl); + $confirmationUrl = $this->getConfirmationUrlFromMessageContent($content); $this->setRequestInfo($confirmationUrl, 'confirm'); $this->clearCookieMessagesList(); $this->dispatch($confirmationUrl); @@ -844,4 +844,23 @@ private function clearCookieMessagesList() $jsonSerializer->serialize([]) ); } + + /** + * Get confirmation URL from message content. + * + * @param string $content + * @return string + */ + private function getConfirmationUrlFromMessageContent(string $content) + { + $confirmationUrl = ''; + + if (preg_match('.*?)".*>', $content, $matches)) { + $confirmationUrl = $matches['url']; + $confirmationUrl = str_replace('http://localhost/index.php/', '', $confirmationUrl); + $confirmationUrl = html_entity_decode($confirmationUrl); + } + + return $confirmationUrl; + } } diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_disable.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_disable.php index af47b64c3dc5b..7d4e451db514b 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_disable.php +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_disable.php @@ -13,12 +13,6 @@ $objectManager = Bootstrap::getObjectManager(); $mutableScopeConfig = $objectManager->create(MutableScopeConfigInterface::class); -$this->confirmationConfigScopeValue = $mutableScopeConfig->getValue( - 'customer/create_account/confirm', - ScopeInterface::SCOPE_WEBSITES, - null -); - $mutableScopeConfig->setValue( 'customer/create_account/confirm', 0, diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_disable_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_disable_rollback.php index 700940f48c2b2..36743b4a20e9a 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_disable_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_disable_rollback.php @@ -6,9 +6,8 @@ declare(strict_types=1); -use Magento\Framework\App\Config\MutableScopeConfigInterface; +use Magento\Config\Model\ResourceModel\Config; use Magento\Framework\Registry; -use Magento\Store\Model\ScopeInterface; use Magento\TestFramework\Helper\Bootstrap; /** @var Registry $registry */ @@ -16,13 +15,9 @@ $registry->unregister('isSecureArea'); $registry->register('isSecureArea', true); -$mutableScopeConfig = Bootstrap::getObjectManager()->create(MutableScopeConfigInterface::class); -$mutableScopeConfig->setValue( - 'customer/create_account/confirm', - $this->confirmationConfigScopeValue, - ScopeInterface::SCOPE_WEBSITES, - null -); +/** @var Config $config */ +$config = Bootstrap::getObjectManager()->create(Config::class); +$config->deleteConfig('customer/create_account/confirm'); $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_enable.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_enable.php index 85b6f46482705..c8deb7ec2a536 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_enable.php +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_enable.php @@ -13,12 +13,6 @@ $objectManager = Bootstrap::getObjectManager(); $mutableScopeConfig = $objectManager->create(MutableScopeConfigInterface::class); -$this->confirmationConfigScopeValue = $mutableScopeConfig->getValue( - 'customer/create_account/confirm', - ScopeInterface::SCOPE_WEBSITES, - null -); - $mutableScopeConfig->setValue( 'customer/create_account/confirm', 1, diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_enable_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_enable_rollback.php index 700940f48c2b2..36743b4a20e9a 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_enable_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_config_enable_rollback.php @@ -6,9 +6,8 @@ declare(strict_types=1); -use Magento\Framework\App\Config\MutableScopeConfigInterface; +use Magento\Config\Model\ResourceModel\Config; use Magento\Framework\Registry; -use Magento\Store\Model\ScopeInterface; use Magento\TestFramework\Helper\Bootstrap; /** @var Registry $registry */ @@ -16,13 +15,9 @@ $registry->unregister('isSecureArea'); $registry->register('isSecureArea', true); -$mutableScopeConfig = Bootstrap::getObjectManager()->create(MutableScopeConfigInterface::class); -$mutableScopeConfig->setValue( - 'customer/create_account/confirm', - $this->confirmationConfigScopeValue, - ScopeInterface::SCOPE_WEBSITES, - null -); +/** @var Config $config */ +$config = Bootstrap::getObjectManager()->create(Config::class); +$config->deleteConfig('customer/create_account/confirm'); $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars.php index 85fe453a0db17..c4f046bac57a6 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars.php +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars.php @@ -6,22 +6,27 @@ declare(strict_types=1); +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Model\Customer; use Magento\TestFramework\Helper\Bootstrap; include __DIR__ . '/customer_confirmation_config_enable.php'; $objectManager = Bootstrap::getObjectManager(); + /** @var Customer $customer */ $customer = $objectManager->create(Customer::class); -$customer->setWebsiteId(1) - ->setId(1) +/** @var CustomerRepositoryInterface $customerRepository */ +$customerRepository = $objectManager->create(CustomerRepositoryInterface::class); +/** @var CustomerInterface $customerInterface */ +$customerInterface = $objectManager->create(CustomerInterface::class); + +$customerInterface->setWebsiteId(1) ->setEmail('customer+confirmation@example.com') - ->setPassword('password') ->setConfirmation($customer->getRandomConfirmationKey()) ->setGroupId(1) ->setStoreId(1) - ->setIsActive(1) ->setFirstname('John') ->setLastname('Smith') ->setDefaultBilling(1) @@ -29,5 +34,4 @@ ->setTaxvat('12') ->setGender(0); -$customer->isObjectNew(true); -$customer->save(); +$customerRepository->save($customerInterface, 'password'); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars_rollback.php index ada4907d73063..63dd48bb86e11 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars_rollback.php @@ -8,20 +8,21 @@ include __DIR__ . '/customer_confirmation_config_enable_rollback.php'; +use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Framework\Registry; use Magento\TestFramework\Helper\Bootstrap; -use Magento\Customer\Model\Customer; /** @var Registry $registry */ $registry = Bootstrap::getObjectManager()->get(Registry::class); $registry->unregister('isSecureArea'); $registry->register('isSecureArea', true); -/** @var Customer $customer */ -$customer = Bootstrap::getObjectManager()->create(Customer::class); -$customer->load(1); +/** @var CustomerRepositoryInterface $customerRepository */ +$customerRepository = Bootstrap::getObjectManager()->create(CustomerRepositoryInterface::class); +$customer = $customerRepository->get('customer+confirmation@example.com'); + if ($customer->getId()) { - $customer->delete(); + $customerRepository->delete($customer); } $registry->unregister('isSecureArea'); From 317c02a344dac510619013a68eca167a5fd384b2 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun Date: Tue, 25 Dec 2018 08:46:52 +0200 Subject: [PATCH 0142/1295] Fix the reset password issue when customer has address from not allowed country #18170 --- .../Customer/Model/AccountManagement.php | 79 ++++++++++++++++--- 1 file changed, 66 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/Customer/Model/AccountManagement.php b/app/code/Magento/Customer/Model/AccountManagement.php index b5b1905e68d8d..40dfeefd7918e 100644 --- a/app/code/Magento/Customer/Model/AccountManagement.php +++ b/app/code/Magento/Customer/Model/AccountManagement.php @@ -17,6 +17,7 @@ use Magento\Customer\Model\Customer as CustomerModel; use Magento\Customer\Model\Customer\CredentialsValidator; use Magento\Customer\Model\Metadata\Validator; +use Magento\Customer\Model\ResourceModel\Visitor\CollectionFactory; use Magento\Eav\Model\Validator\Attribute\Backend; use Magento\Framework\Api\ExtensibleDataObjectConverter; use Magento\Framework\Api\SearchCriteriaBuilder; @@ -44,14 +45,13 @@ use Magento\Framework\Phrase; use Magento\Framework\Reflection\DataObjectProcessor; use Magento\Framework\Registry; +use Magento\Framework\Session\SaveHandlerInterface; +use Magento\Framework\Session\SessionManagerInterface; use Magento\Framework\Stdlib\DateTime; use Magento\Framework\Stdlib\StringUtils as StringHelper; use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\StoreManagerInterface; use Psr\Log\LoggerInterface as PsrLogger; -use Magento\Framework\Session\SessionManagerInterface; -use Magento\Framework\Session\SaveHandlerInterface; -use Magento\Customer\Model\ResourceModel\Visitor\CollectionFactory; /** * Handle various customer account actions @@ -332,6 +332,11 @@ class AccountManagement implements AccountManagementInterface */ private $searchCriteriaBuilder; + /** + * @var AddressRegistry + */ + private $addressRegistry; + /** * @param CustomerFactory $customerFactory * @param ManagerInterface $eventManager @@ -359,12 +364,13 @@ class AccountManagement implements AccountManagementInterface * @param CredentialsValidator|null $credentialsValidator * @param DateTimeFactory|null $dateTimeFactory * @param AccountConfirmation|null $accountConfirmation - * @param DateTimeFactory $dateTimeFactory * @param SessionManagerInterface|null $sessionManager * @param SaveHandlerInterface|null $saveHandler * @param CollectionFactory|null $visitorCollectionFactory * @param SearchCriteriaBuilder|null $searchCriteriaBuilder + * @param AddressRegistry|null $addressRegistry * @SuppressWarnings(PHPMD.ExcessiveParameterList) + * @SuppressWarnings(PHPMD.NPathComplexity) */ public function __construct( CustomerFactory $customerFactory, @@ -396,7 +402,8 @@ public function __construct( SessionManagerInterface $sessionManager = null, SaveHandlerInterface $saveHandler = null, CollectionFactory $visitorCollectionFactory = null, - SearchCriteriaBuilder $searchCriteriaBuilder = null + SearchCriteriaBuilder $searchCriteriaBuilder = null, + AddressRegistry $addressRegistry = null ) { $this->customerFactory = $customerFactory; $this->eventManager = $eventManager; @@ -434,6 +441,8 @@ public function __construct( ?: ObjectManager::getInstance()->get(CollectionFactory::class); $this->searchCriteriaBuilder = $searchCriteriaBuilder ?: ObjectManager::getInstance()->get(SearchCriteriaBuilder::class); + $this->addressRegistry = $addressRegistry + ?: ObjectManager::getInstance()->get(AddressRegistry::class); } /** @@ -499,8 +508,11 @@ public function activateById($customerId, $confirmationKey) * @param \Magento\Customer\Api\Data\CustomerInterface $customer * @param string $confirmationKey * @return \Magento\Customer\Api\Data\CustomerInterface - * @throws \Magento\Framework\Exception\State\InvalidTransitionException - * @throws \Magento\Framework\Exception\State\InputMismatchException + * @throws InputException + * @throws InputMismatchException + * @throws InvalidTransitionException + * @throws LocalizedException + * @throws NoSuchEntityException */ private function activateCustomer($customer, $confirmationKey) { @@ -574,6 +586,9 @@ public function initiatePasswordReset($email, $template, $websiteId = null) // load customer by email $customer = $this->customerRepository->get($email, $websiteId); + // No need to validate customer address while saving customer reset password token + $this->disableAddressValidation($customer); + $newPasswordToken = $this->mathRandom->getUniqueHash(); $this->changeResetPasswordLinkToken($customer, $newPasswordToken); @@ -611,10 +626,10 @@ public function initiatePasswordReset($email, $template, $websiteId = null) * Match a customer by their RP token. * * @param string $rpToken + * @return CustomerInterface * @throws ExpiredException + * @throws LocalizedException * @throws NoSuchEntityException - * - * @return CustomerInterface */ private function matchCustomerByRpToken(string $rpToken): CustomerInterface { @@ -657,6 +672,10 @@ public function resetPassword($email, $resetToken, $newPassword) } else { $customer = $this->customerRepository->get($email); } + + // No need to validate customer address while saving customer reset password token + $this->disableAddressValidation($customer); + //Validate Token and new password strength $this->validateResetPasswordToken($customer->getId(), $resetToken); $this->credentialsValidator->checkPasswordDifferentFromEmail( @@ -900,6 +919,8 @@ public function getDefaultShippingAddress($customerId) * @param CustomerInterface $customer * @param string $redirectUrl * @return void + * @throws LocalizedException + * @throws NoSuchEntityException */ protected function sendEmailConfirmation(CustomerInterface $customer, $redirectUrl) { @@ -954,7 +975,10 @@ public function changePasswordById($customerId, $currentPassword, $newPassword) * @param string $newPassword * @return bool true on success * @throws InputException + * @throws InputMismatchException * @throws InvalidEmailOrPasswordException + * @throws LocalizedException + * @throws NoSuchEntityException * @throws UserLockedException */ private function changePasswordForCustomer($customer, $currentPassword, $newPassword) @@ -1063,10 +1087,11 @@ public function isCustomerInStore($customerWebsiteId, $storeId) * @param int $customerId * @param string $resetPasswordLinkToken * @return bool - * @throws \Magento\Framework\Exception\State\InputMismatchException If token is mismatched - * @throws \Magento\Framework\Exception\State\ExpiredException If token is expired - * @throws \Magento\Framework\Exception\InputException If token or customer id is invalid - * @throws \Magento\Framework\Exception\NoSuchEntityException If customer doesn't exist + * @throws ExpiredException + * @throws InputException + * @throws InputMismatchException + * @throws LocalizedException + * @throws NoSuchEntityException */ private function validateResetPasswordToken($customerId, $resetPasswordLinkToken) { @@ -1156,6 +1181,8 @@ protected function sendNewAccountEmail( * * @param CustomerInterface $customer * @return $this + * @throws LocalizedException + * @throws NoSuchEntityException * @deprecated 100.1.0 */ protected function sendPasswordResetNotificationEmail($customer) @@ -1169,6 +1196,7 @@ protected function sendPasswordResetNotificationEmail($customer) * @param CustomerInterface $customer * @param int|string|null $defaultStoreId * @return int + * @throws LocalizedException * @deprecated 100.1.0 */ protected function getWebsiteStoreId($customer, $defaultStoreId = null) @@ -1182,6 +1210,8 @@ protected function getWebsiteStoreId($customer, $defaultStoreId = null) } /** + * Get email template types + * * @return array * @deprecated 100.1.0 */ @@ -1215,6 +1245,7 @@ protected function getTemplateTypes() * @param int|null $storeId * @param string $email * @return $this + * @throws MailException * @deprecated 100.1.0 */ protected function sendEmailTemplate( @@ -1321,6 +1352,9 @@ public function isResetPasswordLinkTokenExpired($rpToken, $rpTokenCreatedAt) * @param string $passwordLinkToken * @return bool * @throws InputException + * @throws InputMismatchException + * @throws LocalizedException + * @throws NoSuchEntityException */ public function changeResetPasswordLinkToken($customer, $passwordLinkToken) { @@ -1348,6 +1382,8 @@ public function changeResetPasswordLinkToken($customer, $passwordLinkToken) * * @param CustomerInterface $customer * @return $this + * @throws LocalizedException + * @throws NoSuchEntityException * @deprecated 100.1.0 */ public function sendPasswordReminderEmail($customer) @@ -1375,6 +1411,8 @@ public function sendPasswordReminderEmail($customer) * * @param CustomerInterface $customer * @return $this + * @throws LocalizedException + * @throws NoSuchEntityException * @deprecated 100.1.0 */ public function sendPasswordResetConfirmationEmail($customer) @@ -1419,6 +1457,7 @@ protected function getAddressById(CustomerInterface $customer, $addressId) * * @param CustomerInterface $customer * @return Data\CustomerSecure + * @throws NoSuchEntityException * @deprecated 100.1.0 */ protected function getFullCustomerObject($customer) @@ -1444,6 +1483,20 @@ public function getPasswordHash($password) return $this->encryptor->getHash($password); } + /** + * Disable Customer Address Validation + * + * @param CustomerInterface $customer + * @throws NoSuchEntityException + */ + private function disableAddressValidation($customer) + { + foreach ($customer->getAddresses() as $address) { + $addressModel = $this->addressRegistry->retrieve($address->getId()); + $addressModel->setShouldIgnoreValidation(true); + } + } + /** * Get email notification * From dc3912918b9b820e200f43bd1fba326dd0afa18f Mon Sep 17 00:00:00 2001 From: DianaRusin Date: Tue, 25 Dec 2018 16:36:12 +0200 Subject: [PATCH 0143/1295] MAGETWO-97195: Automate with Integration test Confirmation email should be delivered to the customer when address contains '+' symbol --- .../testsuite/Magento/Customer/Controller/AccountTest.php | 2 +- ...nfirmation_email_address_with_special_chars_rollback.php | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php index 60c3314305dd5..4fed6c84ab09d 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php @@ -851,7 +851,7 @@ private function clearCookieMessagesList() * @param string $content * @return string */ - private function getConfirmationUrlFromMessageContent(string $content) + private function getConfirmationUrlFromMessageContent(string $content): string { $confirmationUrl = ''; diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars_rollback.php index 63dd48bb86e11..7a0ebf74ed8a0 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_address_with_special_chars_rollback.php @@ -19,10 +19,12 @@ /** @var CustomerRepositoryInterface $customerRepository */ $customerRepository = Bootstrap::getObjectManager()->create(CustomerRepositoryInterface::class); -$customer = $customerRepository->get('customer+confirmation@example.com'); -if ($customer->getId()) { +try { + $customer = $customerRepository->get('customer+confirmation@example.com'); $customerRepository->delete($customer); +} catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + // Customer with the specified email does not exist } $registry->unregister('isSecureArea'); From 87856f80e9d1cd13f7a238cbabb98d2b85cdfae2 Mon Sep 17 00:00:00 2001 From: Viktor Sevch Date: Wed, 26 Dec 2018 10:08:00 +0200 Subject: [PATCH 0144/1295] MAGETWO-73742: [FT] Magento\Catalog\Test\TestCase\Product\ProductTypeSwitchingOnCreationTest fails on CI --- .../Test/TestCase/Product/ProductTypeSwitchingOnCreationTest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnCreationTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnCreationTest.xml index 7c4824c604e29..49cd2a347c687 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnCreationTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnCreationTest.xml @@ -58,7 +58,6 @@ - stable:no virtual downloadableProduct::default From c258bdde1216f66d5dc7cb98c08a3def372cc320 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun Date: Wed, 26 Dec 2018 10:13:56 +0200 Subject: [PATCH 0145/1295] MAGETWO-73743: [FT] Magento\Catalog\Test\TestCase\Product\ProductTypeSwitchingOnUpdateTest fails on CI --- .../view/adminhtml/web/js/components/price-configurable.js | 2 ++ .../Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.php | 1 + .../Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.xml | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/price-configurable.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/price-configurable.js index 28e775b984b05..6bbab77a3a0ab 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/price-configurable.js +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/price-configurable.js @@ -53,6 +53,8 @@ define([ if (isConfigurable) { this.disable(); this.clear(); + } else { + this.enable(); } } }); diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.php index 43741393e7968..90cd6bdb76328 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.php @@ -143,5 +143,6 @@ protected function clearDownloadableData() /** @var Downloadable $downloadableInfoTab */ $downloadableInfoTab = $this->catalogProductEdit->getProductForm()->getSection('downloadable_information'); $downloadableInfoTab->getDownloadableBlock('Links')->clearDownloadableData(); + $downloadableInfoTab->setIsDownloadable('No'); } } diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.xml index f74ffd288c299..5fa1cfe5e5911 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.xml @@ -80,7 +80,7 @@ downloadableProduct::default configurableProduct::not_virtual_for_type_switching - - + clearDownloadableData From 9a63dad6a2958d07aa631bb0e971f17e3f6eda3b Mon Sep 17 00:00:00 2001 From: Andriy Kravets Date: Wed, 26 Dec 2018 11:28:08 +0200 Subject: [PATCH 0146/1295] Moved object manager to setup method. --- .../AssignCouponDataAfterOrderCustomerAssignTest.php | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Observer/AssignCouponDataAfterOrderCustomerAssignTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Observer/AssignCouponDataAfterOrderCustomerAssignTest.php index 332e5a254fa22..06478dc1f1571 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Observer/AssignCouponDataAfterOrderCustomerAssignTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Observer/AssignCouponDataAfterOrderCustomerAssignTest.php @@ -75,12 +75,12 @@ class AssignCouponDataAfterOrderCustomerAssignTest extends \PHPUnit\Framework\Te */ private $customer; + /** - * AssignCouponDataAfterOrderCustomerAssignTest constructor. + * @inheritdoc */ - public function __construct($name = null, array $data = [], $dataName = '') + protected function setUp() { - parent::__construct($name, $data, $dataName); $this->objectManager = Bootstrap::getObjectManager(); $this->eventManager = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); $this->orderRepository = $this->objectManager->get(\Magento\Sales\Model\OrderRepository::class); @@ -90,13 +90,7 @@ public function __construct($name = null, array $data = [], $dataName = '') $this->assignCouponToCustomerObserver = $this->objectManager->get( \Magento\SalesRule\Observer\AssignCouponDataAfterOrderCustomerAssignObserver::class ); - } - /** - * @inheritdoc - */ - protected function setUp() - { $this->salesRule = $this->prepareSalesRule(); $this->coupon = $this->attachSalesruleCoupon($this->salesRule); $this->order = $this->makeOrderWithCouponAsGuest($this->coupon); From 089c660a826808545fb3fe9384bca7dd64c90fa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Mart=C3=ADnez?= Date: Sun, 22 Oct 2017 03:07:09 +0200 Subject: [PATCH 0147/1295] Remove unneeded, also mistyped, saveHandler declaration from CatalogSearch indexer.xml file --- app/code/Magento/CatalogSearch/etc/indexer.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/CatalogSearch/etc/indexer.xml b/app/code/Magento/CatalogSearch/etc/indexer.xml index 9726f5372311d..a0b9ca10afccb 100644 --- a/app/code/Magento/CatalogSearch/etc/indexer.xml +++ b/app/code/Magento/CatalogSearch/etc/indexer.xml @@ -9,8 +9,6 @@ Catalog Search Rebuild Catalog product fulltext search index - - From 6e099eb8bc1e0371e0fe1de94c0cb4374cc85ce8 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov Date: Thu, 27 Dec 2018 09:01:35 +0200 Subject: [PATCH 0148/1295] MAGETWO-90521: Quote id incorrect display behavior - minor template fix --- .../Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml index e4d9c64660ce8..2c64df1b13663 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml @@ -132,13 +132,13 @@ - + + - - + From 2b6eb5ff3dd53cba95cdf2b95585608f0f15ae80 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov Date: Thu, 27 Dec 2018 14:56:36 +0200 Subject: [PATCH 0149/1295] MAGETWO-73412: [FT] Magento\Downloadable\Test\TestCase\CreateDownloadableProductEntityTest fails on Jenkins - Drag&Drop fix for samples --- .../Edit/Section/Downloadable/Samples.php | 4 +- .../CreateDownloadableProductEntityTest.xml | 171 +++++++++--------- 2 files changed, 85 insertions(+), 90 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Downloadable/Test/Block/Adminhtml/Catalog/Product/Edit/Section/Downloadable/Samples.php b/dev/tests/functional/tests/app/Magento/Downloadable/Test/Block/Adminhtml/Catalog/Product/Edit/Section/Downloadable/Samples.php index 98c7e1abf0d88..b11943980bc05 100644 --- a/dev/tests/functional/tests/app/Magento/Downloadable/Test/Block/Adminhtml/Catalog/Product/Edit/Section/Downloadable/Samples.php +++ b/dev/tests/functional/tests/app/Magento/Downloadable/Test/Block/Adminhtml/Catalog/Product/Edit/Section/Downloadable/Samples.php @@ -114,8 +114,8 @@ protected function sortSample($position, $sortOrder, SimpleElement $element = nu foreach ($this->sortRowsData as &$sortRowData) { if ($sortRowData['sort_order'] > $currentSortRowData['sort_order']) { // need to reload block because we are changing dom - $target = $this->getRowBlock($sortRowData['current_position_in_grid'], $element)->getSortHandle(); - $this->getRowBlock($currentSortRowData['current_position_in_grid'], $element)->dragAndDropTo($target); + $target = $this->getRowBlock($currentSortRowData['current_position_in_grid'], $element)->getSortHandle(); + $this->getRowBlock($sortRowData['current_position_in_grid'], $element)->dragAndDropTo($target); $currentSortRowData['current_position_in_grid']--; $sortRowData['current_position_in_grid']++; diff --git a/dev/tests/functional/tests/app/Magento/Downloadable/Test/TestCase/CreateDownloadableProductEntityTest.xml b/dev/tests/functional/tests/app/Magento/Downloadable/Test/TestCase/CreateDownloadableProductEntityTest.xml index 5afe815edad45..aa3619e8822bb 100644 --- a/dev/tests/functional/tests/app/Magento/Downloadable/Test/TestCase/CreateDownloadableProductEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Downloadable/Test/TestCase/CreateDownloadableProductEntityTest.xml @@ -18,10 +18,10 @@ one_separately_link downloadable_one_dollar_product_with_separated_link downloadableproduct-%isolation% - - - - + + + + DownloadableProduct_%isolation% @@ -33,16 +33,15 @@ Default Category with_two_separately_links downloadableproduct-%isolation% - - - - - - + + + + + + to_maintain:yes - MAGETWO-67096: [FT] Magento\Downloadable\Test\TestCase\CreateDownloadableProductEntityTest fails on Jenkins DownloadableProduct_%isolation% DownloadableProduct_%isolation% 1 @@ -53,12 +52,12 @@ with_two_samples with_two_separately_links downloadableproduct-%isolation% - - - - - - + + + + + + DownloadableProduct_%isolation% @@ -71,16 +70,15 @@ with_two_separately_links default downloadableproduct-%isolation% - - - - - - + + + + + + to_maintain:yes - MAGETWO-67096: [FT] Magento\Downloadable\Test\TestCase\CreateDownloadableProductEntityTest fails on Jenkins DownloadableProduct_%isolation% DownloadableProduct_%isolation% 55 @@ -91,15 +89,15 @@ with_three_links two_options downloadableproduct-%isolation% - - - - - - - - - + + + + + + + + + DownloadableProduct_%isolation% @@ -111,10 +109,10 @@ Default Category with_two_separately_links downloadableproduct-%isolation% - - - - + + + + DownloadableProduct_%isolation% @@ -127,10 +125,10 @@ 123 with_two_separately_links downloadableproduct-%isolation% - - - - + + + + DownloadableProduct_%isolation% @@ -143,13 +141,12 @@ This is description for downloadable product with_two_separately_links downloadableproduct-%isolation% - - - - + + + + - MAGETWO-59316: Sort order of Customizable Options isn't taken into account while creating Product via Webapi DownloadableProduct_%isolation% DownloadableProduct_%isolation% 57 @@ -163,17 +160,16 @@ default catalogProductSimple::with_two_custom_option,catalogProductSimple::with_all_custom_option downloadableproduct-%isolation% - - - - - - - + + + + + + + to_maintain:yes - MAGETWO-67096: [FT] Magento\Downloadable\Test\TestCase\CreateDownloadableProductEntityTest fails on Jenkins DownloadableProduct_%isolation% DownloadableProduct_%isolation% 65 @@ -187,17 +183,16 @@ with_three_links default downloadableproduct-%isolation% - - - - - - - + + + + + + + to_maintain:yes - MAGETWO-67096: [FT] Magento\Downloadable\Test\TestCase\CreateDownloadableProductEntityTest fails on Jenkins DownloadableProduct_%isolation% DownloadableProduct_%isolation% 65 @@ -209,12 +204,12 @@ with_three_links default downloadableproduct-%isolation% - - - - - - + + + + + + DownloadableProduct_%isolation% @@ -225,10 +220,10 @@ default_category with_two_separately_links downloadableproduct-%isolation% - - - - + + + + DownloadableProduct_%isolation% @@ -241,11 +236,11 @@ with_two_separately_links 5 downloadableproduct-%isolation% - - - - - + + + + + DownloadableProduct_%isolation% @@ -257,10 +252,10 @@ category %isolation% with_two_separately_links downloadableproduct-%isolation% - - - - + + + + DownloadableProduct_%isolation% @@ -273,11 +268,11 @@ with_two_separately_links default downloadableproduct-%isolation% - - - - - + + + + + DownloadableProduct_%isolation% @@ -288,8 +283,8 @@ one_separately_link downloadableproduct-%isolation% custom_store - - + + From e534e342262d43923c96efcc795f8ed4fab72db9 Mon Sep 17 00:00:00 2001 From: Oleg Onufer Date: Thu, 27 Dec 2018 15:39:00 +0200 Subject: [PATCH 0150/1295] MAGETWO-96721: Estimator in Shopping cart must be pre-filled by Customer default shipping address for virtual quote --- ...aultShippingAddressForVirtualQuoteTest.xml | 54 +++++++++++++++++++ .../Customer/Test/Mftf/Data/AddressData.xml | 1 + 2 files changed, 55 insertions(+) create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartCheckCustomerDefaultShippingAddressForVirtualQuoteTest.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartCheckCustomerDefaultShippingAddressForVirtualQuoteTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartCheckCustomerDefaultShippingAddressForVirtualQuoteTest.xml new file mode 100644 index 0000000000000..323d820170579 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartCheckCustomerDefaultShippingAddressForVirtualQuoteTest.xml @@ -0,0 +1,54 @@ + + + + + + + + + + <description value="Estimator in Shopping cart must be pre-filled by Customer default shipping address for virtual quote"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-78596"/> + <group value="checkout"/> + </annotations> + <before> + <createData entity="VirtualProduct" stepKey="createVirtualProduct"/> + <createData entity="Simple_US_CA_Customer" stepKey="createCustomer"/> + </before> + <after> + <actionGroup ref="CustomerLogoutStorefrontActionGroup" stepKey="customerLogout"/> + <deleteData createDataKey="createVirtualProduct" stepKey="deleteVirtualProduct"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + </after> + <!-- Steps --> + <!-- Step 1: Go to Storefront as Customer --> + <actionGroup ref="CustomerLoginOnStorefront" stepKey="customerLogin"> + <argument name="customer" value="$$createCustomer$$" /> + </actionGroup> + <!-- Step 2: Add virtual product to cart --> + <amOnPage url="{{StorefrontProductPage.url($$createVirtualProduct.custom_attributes[url_key]$)}}" stepKey="amOnPage"/> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="addProductToCart"> + <argument name="product" value="$$createVirtualProduct$$"/> + <argument name="productCount" value="1"/> + </actionGroup> + <!-- Step 3: Go to Shopping Cart --> + <actionGroup ref="StorefrontViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingcart"/> + <!-- Step 4: Open Estimate Tax section --> + <click selector="{{StorefrontCheckoutCartSummarySection.estimateShippingAndTax}}" stepKey="openEstimateTaxSection"/> + <seeOptionIsSelected selector="{{StorefrontCheckoutCartSummarySection.country}}" userInput="{{US_Address_CA.country}}" stepKey="checkCountry"/> + <seeOptionIsSelected selector="{{StorefrontCheckoutCartSummarySection.region}}" userInput="{{US_Address_CA.state}}" stepKey="checkState"/> + <scrollTo selector="{{StorefrontCheckoutCartSummarySection.postcode}}" stepKey="scrollToPostCodeField"/> + <grabValueFrom selector="{{StorefrontCheckoutCartSummarySection.postcode}}" stepKey="grabTextPostCode"/> + <assertEquals message="Customer postcode is invalid" stepKey="checkCustomerPostcode"> + <expectedResult type="string">{{US_Address_CA.postcode}}</expectedResult> + <actualResult type="variable">grabTextPostCode</actualResult> + </assertEquals> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml index 4eea31665fe69..0e66679f3d2e6 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml @@ -67,6 +67,7 @@ <data key="state">California</data> <data key="postcode">90230</data> <data key="country_id">US</data> + <data key="country">United States</data> <data key="default_billing">Yes</data> <data key="default_shipping">Yes</data> <requiredEntity type="region">RegionCA</requiredEntity> From 82717db45d14fcb0a2d4d06cc074da2b7c550df3 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Fri, 28 Dec 2018 09:00:52 +0200 Subject: [PATCH 0151/1295] MAGETWO-73412: [FT] Magento\Downloadable\Test\TestCase\CreateDownloadableProductEntityTest fails on Jenkins - Drag&Drop fix for samples --- .../Catalog/Product/Edit/Section/Downloadable/Samples.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/Downloadable/Test/Block/Adminhtml/Catalog/Product/Edit/Section/Downloadable/Samples.php b/dev/tests/functional/tests/app/Magento/Downloadable/Test/Block/Adminhtml/Catalog/Product/Edit/Section/Downloadable/Samples.php index b11943980bc05..bcc8a012c1a63 100644 --- a/dev/tests/functional/tests/app/Magento/Downloadable/Test/Block/Adminhtml/Catalog/Product/Edit/Section/Downloadable/Samples.php +++ b/dev/tests/functional/tests/app/Magento/Downloadable/Test/Block/Adminhtml/Catalog/Product/Edit/Section/Downloadable/Samples.php @@ -114,7 +114,8 @@ protected function sortSample($position, $sortOrder, SimpleElement $element = nu foreach ($this->sortRowsData as &$sortRowData) { if ($sortRowData['sort_order'] > $currentSortRowData['sort_order']) { // need to reload block because we are changing dom - $target = $this->getRowBlock($currentSortRowData['current_position_in_grid'], $element)->getSortHandle(); + $target = $this->getRowBlock($currentSortRowData['current_position_in_grid'], $element) + ->getSortHandle(); $this->getRowBlock($sortRowData['current_position_in_grid'], $element)->dragAndDropTo($target); $currentSortRowData['current_position_in_grid']--; From 782a7c5ff98c2d8294365f1070ff41aef109e739 Mon Sep 17 00:00:00 2001 From: Oleg Onufer <linkedddd@gmail.com> Date: Fri, 28 Dec 2018 13:32:08 +0200 Subject: [PATCH 0152/1295] MAGETWO-96721: Estimator in Shopping cart must be pre-filled by Customer default shipping address for virtual quote --- ...onInShoppingCartForCustomerPhysicalQuoteTest.xml | 13 +++++++++---- ...ionInShoppingCartForCustomerVirtualQuoteTest.xml | 13 +++++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml index 3c6953f08e8d7..3fd06016624d1 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest"> <annotations> <features value="Tax information in shopping cart for Customer with default addresses (physical quote)"/> @@ -62,6 +62,7 @@ <actionGroup ref="AdminResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/> </before> <after> + <actionGroup ref="CustomerLogoutStorefrontActionGroup" stepKey="customerLogout"/> <deleteData createDataKey="createTaxRule" stepKey="deleteTaxRule"/> <deleteData createDataKey="createProductFPTAttribute" stepKey="deleteProductFPTAttribute"/> <createData entity="DefaultTaxConfig" stepKey="defaultTaxConfiguration"/> @@ -87,9 +88,13 @@ <actionGroup ref="StorefrontViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> <!-- Step 4: Open Estimate Shipping and Tax section --> <conditionalClick selector="{{StorefrontCheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{StorefrontCheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> - <see selector="{{StorefrontCheckoutCartSummarySection.country}}" userInput="$$createCustomer.country_id$$" stepKey="checkCustomerCountry" /> - <see selector="{{StorefrontCheckoutCartSummarySection.region}}" userInput="$$createCustomer.state$$" stepKey="checkCustomerRegion" /> - <see selector="{{StorefrontCheckoutCartSummarySection.postcode}}" userInput="$$createCustomer.postcode$$" stepKey="checkCustomerPostcode" /> + <seeOptionIsSelected selector="{{StorefrontCheckoutCartSummarySection.country}}" userInput="{{US_Address_CA.country}}" stepKey="checkCustomerCountry" /> + <seeOptionIsSelected selector="{{StorefrontCheckoutCartSummarySection.region}}" userInput="{{US_Address_CA.state}}" stepKey="checkCustomerRegion" /> + <grabValueFrom selector="{{StorefrontCheckoutCartSummarySection.postcode}}" stepKey="grabTextPostCode"/> + <assertEquals message="Customer postcode is invalid" stepKey="checkCustomerPostcode"> + <expectedResult type="string">{{US_Address_CA.postcode}}</expectedResult> + <actualResult type="variable">grabTextPostCode</actualResult> + </assertEquals> <see selector="{{StorefrontCheckoutCartSummarySection.amountFPT}}" userInput="$10" stepKey="checkAmountFPTCA" /> <see selector="{{StorefrontCheckoutCartSummarySection.taxAmount}}" userInput="$0.83" stepKey="checkTaxAmountCA" /> <scrollTo selector="{{StorefrontCheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary" /> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml index fbb44807c8b14..f26b6d2747e09 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest"> <annotations> <features value="Tax information in shopping cart for Customer with default addresses (virtual quote)"/> @@ -37,6 +37,7 @@ <createData entity="Simple_US_NY_Customer" stepKey="createCustomer"/> </before> <after> + <actionGroup ref="CustomerLogoutStorefrontActionGroup" stepKey="customerLogout"/> <deleteData createDataKey="createTaxRule" stepKey="deleteTaxRule"/> <deleteData createDataKey="createProductFPTAttribute" stepKey="deleteProductFPTAttribute"/> <createData entity="DefaultTaxConfig" stepKey="defaultTaxConfiguration"/> @@ -59,9 +60,13 @@ <actionGroup ref="StorefrontViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> <!-- Step 4: Open Estimate Shipping and Tax section --> <conditionalClick selector="{{StorefrontCheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{StorefrontCheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> - <see selector="{{StorefrontCheckoutCartSummarySection.country}}" userInput="$$createCustomer.country_id$$" stepKey="checkCustomerCountry" /> - <see selector="{{StorefrontCheckoutCartSummarySection.region}}" userInput="$$createCustomer.state$$" stepKey="checkCustomerRegion" /> - <see selector="{{StorefrontCheckoutCartSummarySection.postcode}}" userInput="$$createCustomer.postcode$$" stepKey="checkCustomerPostcode" /> + <seeOptionIsSelected selector="{{StorefrontCheckoutCartSummarySection.country}}" userInput="{{US_Address_NY.country}}" stepKey="checkCustomerCountry" /> + <seeOptionIsSelected selector="{{StorefrontCheckoutCartSummarySection.region}}" userInput="{{US_Address_NY.state}}" stepKey="checkCustomerRegion" /> + <grabValueFrom selector="{{StorefrontCheckoutCartSummarySection.postcode}}" stepKey="grabTextPostCode"/> + <assertEquals message="Customer postcode is invalid" stepKey="checkCustomerPostcode"> + <expectedResult type="string">{{US_Address_NY.postcode}}</expectedResult> + <actualResult type="variable">grabTextPostCode</actualResult> + </assertEquals> <scrollTo selector="{{StorefrontCheckoutCartSummarySection.taxSummary}}" stepKey="scrollToTaxSummary" /> <click selector="{{StorefrontCheckoutCartSummarySection.taxSummary}}" stepKey="expandTaxSummary"/> <see selector="{{StorefrontCheckoutCartSummarySection.rate}}" userInput="US-NY-*-Rate 1 (8.375%)" stepKey="checkRateNY" /> From 3bdded57890bb82cb49e058cd4bcc192e3674d35 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Fri, 28 Dec 2018 15:40:31 +0200 Subject: [PATCH 0153/1295] MAGETWO-72839: Customer can't see available Payment Method for specific country --- .../Model/ShippingInformationManagement.php | 10 ++- ...ckoutFillingShippingSectionActionGroup.xml | 9 +++ ...NewAddressInShippingSectionActionGroup.xml | 12 +++- .../Mftf/Section/CheckoutPaymentSection.xml | 1 + .../Mftf/Section/CheckoutShippingSection.xml | 2 +- ...tWithRestrictedCountriesForPaymentTest.xml | 59 ++++++++++++++++ ...tWithRestrictedCountriesForPaymentTest.xml | 70 +++++++++++++++++++ .../view/frontend/web/js/view/shipping.js | 1 + .../Customer/Test/Mftf/Data/AddressData.xml | 1 + 9 files changed, 160 insertions(+), 5 deletions(-) create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml diff --git a/app/code/Magento/Checkout/Model/ShippingInformationManagement.php b/app/code/Magento/Checkout/Model/ShippingInformationManagement.php index d8142d033f78c..08b7a6e609c69 100644 --- a/app/code/Magento/Checkout/Model/ShippingInformationManagement.php +++ b/app/code/Magento/Checkout/Model/ShippingInformationManagement.php @@ -98,8 +98,8 @@ class ShippingInformationManagement implements \Magento\Checkout\Api\ShippingInf * @param \Magento\Customer\Api\AddressRepositoryInterface $addressRepository * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Quote\Model\Quote\TotalsCollector $totalsCollector - * @param CartExtensionFactory|null $cartExtensionFactory, - * @param ShippingAssignmentFactory|null $shippingAssignmentFactory, + * @param CartExtensionFactory|null $cartExtensionFactory + * @param ShippingAssignmentFactory|null $shippingAssignmentFactory * @param ShippingFactory|null $shippingFactory * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -150,6 +150,10 @@ public function saveAddressInformation( $address->setCustomerAddressId(null); } + if (!$billingAddress->getCustomerAddressId()) { + $billingAddress->setCustomerAddressId(null); + } + if (!$address->getCountryId()) { throw new StateException(__('Shipping address is not set')); } @@ -203,6 +207,8 @@ protected function validateQuote(\Magento\Quote\Model\Quote $quote) } /** + * Prepare shipping assignment. + * * @param CartInterface $quote * @param AddressInterface $address * @param string $method diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillingShippingSectionActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillingShippingSectionActionGroup.xml index 8ff84e7a436e5..19aeeef8e2bc0 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillingShippingSectionActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillingShippingSectionActionGroup.xml @@ -32,4 +32,13 @@ <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask1"/> </actionGroup> + + <actionGroup name="GuestCheckoutFillingShippingSectionWithoutPaymentsActionGroup" extends="GuestCheckoutFillingShippingSectionActionGroup"> + <waitForElement selector="{{CheckoutPaymentSection.isPaymentSection}}" time="30" stepKey="waitForPaymentSectionLoaded"/> + </actionGroup> + + <actionGroup name="GuestCheckoutFillingShippingSectionWithoutRegionActionGroup" extends="GuestCheckoutFillingShippingSectionActionGroup"> + <selectOption selector="{{CheckoutShippingSection.country}}" userInput="{{customerAddressVar.country}}" after="enterPostcode" stepKey="selectCountry"/> + <remove keyForRemoval="selectRegion"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/LoggedInUserCheckoutAddNewAddressInShippingSectionActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/LoggedInUserCheckoutAddNewAddressInShippingSectionActionGroup.xml index 722e6f1ee49ab..9c4d16ff500a0 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/LoggedInUserCheckoutAddNewAddressInShippingSectionActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/LoggedInUserCheckoutAddNewAddressInShippingSectionActionGroup.xml @@ -7,7 +7,7 @@ --> <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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Logged in user checkout add new adress shipping section --> <actionGroup name="LoggedInUserCheckoutAddNewAddressInShippingSectionActionGroup"> <arguments> @@ -21,6 +21,14 @@ <fillField selector="{{CheckoutShippingSection.addTelephone}}" userInput="{{customerAddressVar.telephone}}" stepKey="enterTelephone"/> <click selector="{{CheckoutShippingSection.addSaveButton}}" stepKey="clickSaveAdressAdd"/> <waitForPageLoad stepKey="waitPageLoad"/> - <see stepKey="seeRegionSelected" selector="{{CheckoutShippingSection.selectedShippingAddress}}" userInput="{{customerAddressVar.state}}"/> + <see selector="{{CheckoutShippingSection.selectedShippingAddress}}" userInput="{{customerAddressVar.state}}" stepKey="seeRegionSelected"/> + </actionGroup> + + <actionGroup name="LoggedInUserCheckoutAddNewShippingSectionWithoutRegionActionGroup" + extends="LoggedInUserCheckoutAddNewAddressInShippingSectionActionGroup"> + <remove keyForRemoval="selectRegion"/> + <remove keyForRemoval="seeRegionSelected"/> + <selectOption selector="{{CheckoutShippingSection.addCountry}}" userInput="{{customerAddressVar.country}}" after="enterPostcode" stepKey="enterCountry"/> + <see selector="{{CheckoutShippingSection.selectedShippingAddress}}" userInput="{{customerAddressVar.city}}" after="waitPageLoad" stepKey="seeCitySelected"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml index 543ed2dcfe642..abd5e26c4280f 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml @@ -40,5 +40,6 @@ <element name="goToShipping" type="button" selector="#checkout>ul>li.opc-progress-bar-item._complete>span"/> <element name="orderSummarySubtotal" type="text" selector=".totals.sub span" /> <element name="billingAddressSameAsShipping" type="checkbox" selector=".payment-method._active [name='billing-address-same-as-shipping']"/> + <element name="noPaymentMethods" type="text" selector=".no-payments-block"/> </section> </sections> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml index 2a7437c44eccf..55d8362e27483 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml @@ -30,7 +30,7 @@ <element name="addState" type="select" selector="#shipping-new-address-form select[name='region_id']"/> <element name="addPostcode" type="input" selector="#shipping-new-address-form input[name='postcode']"/> <element name="addTelephone" type="input" selector="#shipping-new-address-form input[name='telephone']"/> - <element name="addcCountry" type="select" selector="#shipping-new-address-form select[name='country_id']"/> + <element name="addCountry" type="select" selector="#shipping-new-address-form select[name='country_id']"/> <element name="addSaveButton" type="button" selector=".action.primary.action-save-address"/> <element name="editActiveAddress" type="button" selector="//div[@class='shipping-address-item selected-item']//span[text()='Edit']" timeout="30"/> </section> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml new file mode 100644 index 0000000000000..724f19909cc30 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest" + extends="StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest"> + <annotations> + <stories value="Checkout via the Storefront"/> + <title value="Checkout via Customer Checkout with restricted countries for payment"/> + <description value="Should be able to place an order as a Customer with restricted countries for payment."/> + <testCaseId value="MC-10831"/> + </annotations> + <before> + <createData entity="Simple_US_Customer" stepKey="createSimpleUsCustomer"/> + </before> + <after> + <actionGroup ref="CustomerLogoutStorefrontActionGroup" stepKey="customerLogoutStorefront"/> + <deleteData createDataKey="createSimpleUsCustomer" stepKey="deleteCustomer"/> + </after> + + <remove keyForRemoval="guestCheckoutFillingShippingSection"/> + <remove keyForRemoval="guestCheckoutFillingShippingSectionUK"/> + <remove keyForRemoval="questPlaceOrder"/> + + <!-- Login as Customer --> + <actionGroup ref="CustomerLoginOnStorefront" before="goToProductPage" stepKey="customerLogin"> + <argument name="customer" value="$$createSimpleUsCustomer$$" /> + </actionGroup> + + <!-- Select address and go to payments page--> + <see selector="{{CheckoutShippingSection.selectedShippingAddress}}" userInput="{{US_Address_TX.state}}" after="shippingStepIsOpened" stepKey="seeRegion" /> + <waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" after="seeRegion" stepKey="waitNextButton"/> + <actionGroup ref="CheckoutSelectFlatRateShippingMethodActionGroup" after="waitNextButton" stepKey="selectShippingMethod"/> + <click selector="{{CheckoutShippingMethodsSection.next}}" after="selectShippingMethod" stepKey="clickNextButton" /> + <waitForPageLoad after="clickNextButton" stepKey="waitBillingForm"/> + <seeElement selector="{{CheckoutPaymentSection.isPaymentSection}}" after="waitBillingForm" stepKey="checkoutPaymentStepIsOpened"/> + + <!-- Fill UK Address and verify that payment available and checkout successful --> + <click selector="{{CheckoutShippingSection.newAdress}}" after="shippingStepIsOpened1" stepKey="fillNewAddress" /> + <actionGroup ref="LoggedInUserCheckoutAddNewShippingSectionWithoutRegionActionGroup" after="fillNewAddress" stepKey="customerCheckoutFillingShippingSectionUK"> + <argument name="customerVar" value="$$createSimpleUsCustomer$$" /> + <argument name="customerAddressVar" value="UK_Default_Address" /> + </actionGroup> + <waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" after="customerCheckoutFillingShippingSectionUK" stepKey="waitNextButton1"/> + <actionGroup ref="CheckoutSelectFlatRateShippingMethodActionGroup" after="waitNextButton1" stepKey="selectShippingMethod1"/> + <click selector="{{CheckoutShippingMethodsSection.next}}" after="selectShippingMethod1" stepKey="clickNextButton1" /> + + <actionGroup ref="CheckoutPlaceOrderActionGroup" after="selectCheckMoneyOrderPayment" stepKey="customerPlaceorder"> + <argument name="orderNumberMessage" value="CONST.successCheckoutOrderNumberMessage" /> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage" /> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml new file mode 100644 index 0000000000000..4bd520bb801b8 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest"> + <annotations> + <features value="Checkout"/> + <stories value="Checkout via Guest Checkout"/> + <title value="Checkout via Guest Checkout with restricted countries for payment"/> + <description value="Should be able to place an order as a Guest with restricted countries for payment."/> + <severity value="MAJOR"/> + <testCaseId value="MC-8243"/> + <group value="checkout"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <magentoCLI command="config:set payment/checkmo/allowspecific 1" stepKey="setAllowSpecificCountiesValue" /> + <magentoCLI command="config:set payment/checkmo/specificcountry GB" stepKey="setSpecificCountryValue" /> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <magentoCLI command="config:set payment/checkmo/allowspecific 0" stepKey="unsetAllowSpecificCountiesValue"/> + <magentoCLI command="config:set payment/checkmo/specificcountry ''" stepKey="unsetSpecificCountryValue" /> + </after> + + <!-- Add product to cart --> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.sku$$)}}" stepKey="goToProductPage"/> + <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProductToCart"> + <argument name="productName" value="$$createProduct.name$$"/> + </actionGroup> + <!-- Go to checkout page --> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart" /> + + <!-- Fill US Address and verify that no payment available --> + <seeElement selector="{{CheckoutShippingSection.isShippingStep}}" stepKey="shippingStepIsOpened"/> + <actionGroup ref="GuestCheckoutFillingShippingSectionWithoutPaymentsActionGroup" stepKey="guestCheckoutFillingShippingSection"> + <argument name="customerVar" value="Simple_US_Customer"/> + <argument name="customerAddressVar" value="US_Address_TX"/> + <argument name="shippingMethod" value="Flat Rate" type="string"/> + </actionGroup> + + <waitForElementVisible selector="{{CheckoutPaymentSection.noPaymentMethods}}" stepKey="waitMessage"/> + <see userInput="No Payment Methods" stepKey="checkMessage"/> + + <!-- Fill UK Address and verify that payment available and checkout successful --> + <click selector="{{CheckoutHeaderSection.shippingMethodStep}}" stepKey="goToShipping" /> + <waitForElementVisible selector="{{CheckoutShippingSection.isShippingStep}}" stepKey="shippingStepIsOpened1"/> + + <actionGroup ref="GuestCheckoutFillingShippingSectionWithoutRegionActionGroup" stepKey="guestCheckoutFillingShippingSectionUK"> + <argument name="customerVar" value="Simple_US_Customer" /> + <argument name="customerAddressVar" value="UK_Default_Address" /> + <argument name="shippingMethod" value="Flat Rate" type="string"/> + </actionGroup> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyOrderPayment" /> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="questPlaceOrder"> + <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage" /> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage" /> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js index df50a5ae94ae9..b4997f9664c81 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js @@ -247,6 +247,7 @@ define([ */ setShippingInformation: function () { if (this.validateShippingInformation()) { + quote.billingAddress(null); checkoutDataResolver.resolveBillingAddress(); setShippingInformationAction().done( function () { diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml index 4eea31665fe69..b55f9bf3df021 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml @@ -101,6 +101,7 @@ <data key="state"></data> <data key="postcode">SE1 7RW</data> <data key="country_id">GB</data> + <data key="country">United Kingdom</data> <data key="telephone">444-44-444-44</data> </entity> </entities> From 6be96a5f0194d7d31a93ef67d58d5db8fe8a8497 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 28 Dec 2018 16:04:31 +0200 Subject: [PATCH 0154/1295] MAGETWO-95734: Adding common elements to AdmiMainActionsSection --- .../Backend/Test/Mftf/Section/AdminMainActionsSection.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminMainActionsSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminMainActionsSection.xml index bba375c2d6bfd..31325c2b72217 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminMainActionsSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminMainActionsSection.xml @@ -10,7 +10,8 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminMainActionsSection"> <element name="save" type="button" selector="#save" timeout="30"/> - <element name="delete" type="button" selector="#delete"/> + <element name="delete" type="button" selector="#delete" timeout="30"/> <element name="add" type="button" selector="#add" timeout="30"/> + <element name="saveAndContinue" type="button" selector="button[id*=save_and_continue]" timeout="30"/> </section> </sections> From f4eae8776d99f7168cb4ab563101bce124c464ff Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Fri, 28 Dec 2018 16:31:15 +0200 Subject: [PATCH 0155/1295] MAGETWO-73412: [FT] Magento\Downloadable\Test\TestCase\CreateDownloadableProductEntityTest fails on Jenkins - Drag&Drop fix for samples --- .../Test/TestCase/CreateDownloadableProductEntityTest.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Downloadable/Test/TestCase/CreateDownloadableProductEntityTest.xml b/dev/tests/functional/tests/app/Magento/Downloadable/Test/TestCase/CreateDownloadableProductEntityTest.xml index aa3619e8822bb..b18f6849e0c8a 100644 --- a/dev/tests/functional/tests/app/Magento/Downloadable/Test/TestCase/CreateDownloadableProductEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Downloadable/Test/TestCase/CreateDownloadableProductEntityTest.xml @@ -41,7 +41,6 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertProductInStock"/> </variation> <variation name="CreateDownloadableProductEntityTestVariation3" summary="Create product with default sets samples and links"> - <data name="tag" xsi:type="string">to_maintain:yes</data> <data name="product/data/name" xsi:type="string">DownloadableProduct_%isolation%</data> <data name="product/data/sku" xsi:type="string">DownloadableProduct_%isolation%</data> <data name="product/data/price/value" xsi:type="string">1</data> @@ -78,7 +77,6 @@ <constraint name="Magento\Downloadable\Test\Constraint\AssertDownloadableLinksData"/> </variation> <variation name="CreateDownloadableProductEntityTestVariation5" summary="Create product without category"> - <data name="tag" xsi:type="string">to_maintain:yes</data> <data name="product/data/name" xsi:type="string">DownloadableProduct_%isolation%</data> <data name="product/data/sku" xsi:type="string">DownloadableProduct_%isolation%</data> <data name="product/data/price/value" xsi:type="string">55</data> @@ -169,7 +167,6 @@ <constraint name="Magento\Downloadable\Test\Constraint\AssertDownloadableLinksData"/> </variation> <variation name="CreateDownloadableProductEntityTestVariation10" summary="Create product with three links"> - <data name="tag" xsi:type="string">to_maintain:yes</data> <data name="product/data/name" xsi:type="string">DownloadableProduct_%isolation%</data> <data name="product/data/sku" xsi:type="string">DownloadableProduct_%isolation%</data> <data name="product/data/price/value" xsi:type="string">65</data> @@ -192,7 +189,6 @@ <constraint name="Magento\Downloadable\Test\Constraint\AssertDownloadableLinksData"/> </variation> <variation name="CreateDownloadableProductEntityTestVariation11" summary="Create product with three links without description"> - <data name="tag" xsi:type="string">to_maintain:yes</data> <data name="product/data/name" xsi:type="string">DownloadableProduct_%isolation%</data> <data name="product/data/sku" xsi:type="string">DownloadableProduct_%isolation%</data> <data name="product/data/price/value" xsi:type="string">65</data> From fb405b1a73e2a516bf4d9f23e169e17759485681 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Mon, 31 Dec 2018 09:14:48 +0200 Subject: [PATCH 0156/1295] MAGETWO-72839: Customer can't see available Payment Method for specific country --- .../Magento/Checkout/Model/ShippingInformationManagement.php | 2 +- .../Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Checkout/Model/ShippingInformationManagement.php b/app/code/Magento/Checkout/Model/ShippingInformationManagement.php index 08b7a6e609c69..d6fe3ece473df 100644 --- a/app/code/Magento/Checkout/Model/ShippingInformationManagement.php +++ b/app/code/Magento/Checkout/Model/ShippingInformationManagement.php @@ -150,7 +150,7 @@ public function saveAddressInformation( $address->setCustomerAddressId(null); } - if (!$billingAddress->getCustomerAddressId()) { + if ($billingAddress && !$billingAddress->getCustomerAddressId()) { $billingAddress->setCustomerAddressId(null); } diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml index abd5e26c4280f..91e92f184f434 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml @@ -40,6 +40,6 @@ <element name="goToShipping" type="button" selector="#checkout>ul>li.opc-progress-bar-item._complete>span"/> <element name="orderSummarySubtotal" type="text" selector=".totals.sub span" /> <element name="billingAddressSameAsShipping" type="checkbox" selector=".payment-method._active [name='billing-address-same-as-shipping']"/> - <element name="noPaymentMethods" type="text" selector=".no-payments-block"/> + <element name="noPaymentMethods" type="text" selector=".no-quotes-block"/> </section> </sections> From 96467a6ded9e8718409aa0b2fb7e436be2318c98 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Mon, 31 Dec 2018 09:25:08 +0200 Subject: [PATCH 0157/1295] MAGETWO-72839: Customer can't see available Payment Method for specific country --- ...ntGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml index 4bd520bb801b8..9999708af1dee 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml @@ -50,7 +50,7 @@ </actionGroup> <waitForElementVisible selector="{{CheckoutPaymentSection.noPaymentMethods}}" stepKey="waitMessage"/> - <see userInput="No Payment Methods" stepKey="checkMessage"/> + <see userInput="No Payment method available." stepKey="checkMessage"/> <!-- Fill UK Address and verify that payment available and checkout successful --> <click selector="{{CheckoutHeaderSection.shippingMethodStep}}" stepKey="goToShipping" /> From c307953fc8a93c0a09090c74443cbcd8aa79402a Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Mon, 31 Dec 2018 11:30:36 +0200 Subject: [PATCH 0158/1295] MAGETWO-97000: Not all sizes are available in the layered navigation (on Sample Data) with ElasticSearch --- .../CatalogSearch/Model/Layer/Filter/Attribute.php | 9 +++------ .../Test/Unit/Model/Layer/Filter/AttributeTest.php | 7 ------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php index 7aac6e98fc044..7b239d84bf962 100644 --- a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php +++ b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php @@ -91,12 +91,10 @@ protected function _getItemsData() return $this->itemDataBuilder->build(); } - $productSize = $productCollection->getSize(); - $options = $attribute->getFrontend() ->getSelectOptions(); foreach ($options as $option) { - $this->buildOptionData($option, $isAttributeFilterable, $optionsFacetedData, $productSize); + $this->buildOptionData($option, $isAttributeFilterable, $optionsFacetedData); } return $this->itemDataBuilder->build(); @@ -108,17 +106,16 @@ protected function _getItemsData() * @param array $option * @param boolean $isAttributeFilterable * @param array $optionsFacetedData - * @param int $productSize * @return void */ - private function buildOptionData($option, $isAttributeFilterable, $optionsFacetedData, $productSize) + private function buildOptionData($option, $isAttributeFilterable, $optionsFacetedData) { $value = $this->getOptionValue($option); if ($value === false) { return; } $count = $this->getOptionCount($value, $optionsFacetedData); - if ($isAttributeFilterable && (!$this->isOptionReducesResults($count, $productSize) || $count === 0)) { + if ($isAttributeFilterable && $count === 0) { return; } diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Filter/AttributeTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Filter/AttributeTest.php index abc0fdd1069fe..69e2c33d02d1a 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Filter/AttributeTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Layer/Filter/AttributeTest.php @@ -321,10 +321,6 @@ public function testGetItemsWithoutApply() ->method('build') ->will($this->returnValue($builtData)); - $this->fulltextCollection->expects($this->once()) - ->method('getSize') - ->will($this->returnValue(50)); - $expectedFilterItems = [ $this->createFilterItem(0, $builtData[0]['label'], $builtData[0]['value'], $builtData[0]['count']), $this->createFilterItem(1, $builtData[1]['label'], $builtData[1]['value'], $builtData[1]['count']), @@ -383,9 +379,6 @@ public function testGetItemsOnlyWithResults() $this->fulltextCollection->expects($this->once()) ->method('getFacetedData') ->willReturn($facetedData); - $this->fulltextCollection->expects($this->once()) - ->method('getSize') - ->will($this->returnValue(50)); $this->itemDataBuilder->expects($this->once()) ->method('addItemData') From 7f08770d2e5d40a521b3ce6d5bad7f93b5cbc99a Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Wed, 2 Jan 2019 11:27:44 +0200 Subject: [PATCH 0159/1295] MAGETWO-73734: MTF tests fix --- .../Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.php b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.php index 6a262798746a6..54cec6cf279f6 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.php +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.php @@ -28,6 +28,13 @@ class PromoQuoteForm extends FormSections */ protected $waitForSelectorVisible = false; + /** + * Selector of name element on the form. + * + * @var string + */ + private $nameElementSelector = 'input[name=name]'; + /** * Fill form with sections. * @@ -39,6 +46,7 @@ class PromoQuoteForm extends FormSections public function fill(FixtureInterface $fixture, SimpleElement $element = null, array $replace = null) { $this->waitForElementNotVisible($this->waitForSelector); + $this->waitForElementVisible($this->nameElementSelector); $sections = $this->getFixtureFieldsByContainers($fixture); if ($replace) { $sections = $this->prepareData($sections, $replace); From e14a92c77c72d3bc749f978d4f9fe11cd7d6101e Mon Sep 17 00:00:00 2001 From: Oleg Onufer <linkedddd@gmail.com> Date: Wed, 2 Jan 2019 11:47:23 +0200 Subject: [PATCH 0160/1295] MAGETWO-96721: Estimator in Shopping cart must be pre-filled by Customer default shipping address for virtual quote --- ...omerDefaultShippingAddressForVirtualQuoteTest.xml | 2 +- .../Magento/Customer/Test/Mftf/Data/AddressData.xml | 8 ++++++++ .../Magento/Customer/Test/Mftf/Data/CustomerData.xml | 12 ++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartCheckCustomerDefaultShippingAddressForVirtualQuoteTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartCheckCustomerDefaultShippingAddressForVirtualQuoteTest.xml index 323d820170579..7750ce0e1686a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartCheckCustomerDefaultShippingAddressForVirtualQuoteTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartCheckCustomerDefaultShippingAddressForVirtualQuoteTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <createData entity="VirtualProduct" stepKey="createVirtualProduct"/> - <createData entity="Simple_US_CA_Customer" stepKey="createCustomer"/> + <createData entity="Customer_With_Different_Default_Billing_Shipping_Addresses" stepKey="createCustomer"/> </before> <after> <actionGroup ref="CustomerLogoutStorefrontActionGroup" stepKey="customerLogout"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml index 0e66679f3d2e6..5af48863b1cca 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml @@ -104,4 +104,12 @@ <data key="country_id">GB</data> <data key="telephone">444-44-444-44</data> </entity> + <entity name="US_Default_Billing_Address_TX" type="address" extends="US_Address_TX"> + <data key="default_billing">false</data> + <data key="default_shipping">true</data> + </entity> + <entity name="US_Default_Shipping_Address_CA" type="address" extends="US_Address_CA"> + <data key="default_billing">true</data> + <data key="default_shipping">false</data> + </entity> </entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml index 74da84c01b861..868c3996ac9dc 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml @@ -74,4 +74,16 @@ <data key="website_id">0</data> <requiredEntity type="address">US_Address_NY</requiredEntity> </entity> + <entity name="Customer_With_Different_Default_Billing_Shipping_Addresses" type="customer"> + <data key="group_id">1</data> + <data key="email" unique="prefix">John.Doe@example.com</data> + <data key="firstname">John</data> + <data key="lastname">Doe</data> + <data key="fullname">John Doe</data> + <data key="password">pwdTest123!</data> + <data key="store_id">0</data> + <data key="website_id">0</data> + <requiredEntity type="address">US_Default_Billing_Address_TX</requiredEntity> + <requiredEntity type="address">US_Default_Shipping_Address_CA</requiredEntity> + </entity> </entities> From 1334635a26f90ae1d93cfbe26d9d85cb30f88136 Mon Sep 17 00:00:00 2001 From: roman <rleshchenko@magento.com> Date: Wed, 2 Jan 2019 16:35:29 +0200 Subject: [PATCH 0161/1295] MAGETWO-97066: Fixed incorrect behavior of catalog attributes actions --- .../Controller/Adminhtml/Product/Attribute/Delete.php | 7 +++++++ .../Controller/Adminhtml/Product/Attribute/Save.php | 5 +++++ .../Controller/Adminhtml/Product/AttributeTest.php | 11 +++++++++++ 3 files changed, 23 insertions(+) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete.php index bef6aee0e2afd..f4b55f081afc4 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete.php @@ -6,13 +6,20 @@ */ namespace Magento\Catalog\Controller\Adminhtml\Product\Attribute; +use Magento\Framework\Exception\NotFoundException; + class Delete extends \Magento\Catalog\Controller\Adminhtml\Product\Attribute { /** * @return \Magento\Backend\Model\View\Result\Redirect + * @throws NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + $id = $this->getRequest()->getParam('attribute_id'); $resultRedirect = $this->resultRedirectFactory->create(); if ($id) { diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php index 3568d15b8048d..e2aa7758ebf21 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php @@ -29,6 +29,7 @@ use Magento\Framework\Registry; use Magento\Framework\View\LayoutFactory; use Magento\Framework\View\Result\PageFactory; +use Magento\Framework\Exception\NotFoundException; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -124,6 +125,10 @@ public function __construct( */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + try { $optionData = $this->formDataSerializer->unserialize( $this->getRequest()->getParam('serialized_options', '[]') diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php index 45c1583d76400..c16ebb24d496f 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php @@ -23,6 +23,7 @@ public function testWrongFrontendInput() 'frontend_input' => 'some_input', ]; $this->getRequest()->setPostValue($postData); + $this->getRequest()->setMethod('POST'); $this->dispatch('backend/catalog/product_attribute/save'); $this->assertEquals(302, $this->getResponse()->getHttpResponseCode()); $this->assertContains( @@ -48,6 +49,7 @@ public function testWithPopup() 'new_attribute_set_name' => 'new_attribute_set', ]; $this->getRequest()->setPostValue($postData); + $this->getRequest()->setMethod('POST'); $this->dispatch('backend/catalog/product_attribute/save'); $this->assertEquals(302, $this->getResponse()->getHttpResponseCode()); $this->assertContains( @@ -67,6 +69,7 @@ public function testWithPopup() public function testWithExceptionWhenSaveAttribute() { $postData = $this->_getAttributeData() + ['attribute_id' => 0, 'frontend_input' => 'boolean']; + $this->getRequest()->setMethod('POST'); $this->getRequest()->setPostValue($postData); $this->dispatch('backend/catalog/product_attribute/save'); $this->assertEquals(302, $this->getResponse()->getHttpResponseCode()); @@ -85,6 +88,7 @@ public function testWithExceptionWhenSaveAttribute() public function testWrongAttributeId() { $postData = $this->_getAttributeData() + ['attribute_id' => 100500]; + $this->getRequest()->setMethod('POST'); $this->getRequest()->setPostValue($postData); $this->dispatch('backend/catalog/product_attribute/save'); $this->assertEquals(302, $this->getResponse()->getHttpResponseCode()); @@ -110,6 +114,7 @@ public function testAttributeWithoutId() 'set' => 4, 'frontend_input' => 'boolean', ]; + $this->getRequest()->setMethod('POST'); $this->getRequest()->setPostValue($postData); $this->dispatch('backend/catalog/product_attribute/save'); $this->assertEquals(302, $this->getResponse()->getHttpResponseCode()); @@ -132,6 +137,7 @@ public function testWrongAttributeCode() { $postData = $this->_getAttributeData() + ['attribute_code' => '_()&&&?']; $this->getRequest()->setPostValue($postData); + $this->getRequest()->setMethod('POST'); $this->dispatch('backend/catalog/product_attribute/save'); $this->assertEquals(302, $this->getResponse()->getHttpResponseCode()); $this->assertContains( @@ -157,6 +163,7 @@ public function testAttributeWithoutEntityTypeId() { $postData = $this->_getAttributeData() + ['attribute_id' => '2', 'new_attribute_set_name' => ' ']; $this->getRequest()->setPostValue($postData); + $this->getRequest()->setMethod('POST'); $this->dispatch('backend/catalog/product_attribute/save'); $this->assertEquals(302, $this->getResponse()->getHttpResponseCode()); $this->assertContains( @@ -172,6 +179,7 @@ public function testSaveActionApplyToDataSystemAttribute() { $postData = $this->_getAttributeData() + ['attribute_id' => '2']; $this->getRequest()->setPostValue($postData); + $this->getRequest()->setMethod('POST'); $this->dispatch('backend/catalog/product_attribute/save'); $model = $this->_objectManager->create(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class); $model->load($postData['attribute_id']); @@ -185,6 +193,7 @@ public function testSaveActionApplyToDataUserDefinedAttribute() { $postData = $this->_getAttributeData() + ['attribute_id' => '1']; $this->getRequest()->setPostValue($postData); + $this->getRequest()->setMethod('POST'); $this->dispatch('backend/catalog/product_attribute/save'); /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $model */ $model = $this->_objectManager->create(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class); @@ -218,6 +227,7 @@ public function testSaveActionCleanAttributeLabelCache() $this->assertEquals('predefined string translation', $this->_translate('string to translate')); $string->saveTranslate('string to translate', 'new string translation'); $postData = $this->_getAttributeData() + ['attribute_id' => 1]; + $this->getRequest()->setMethod('POST'); $this->getRequest()->setPostValue($postData); $this->dispatch('backend/catalog/product_attribute/save'); $this->assertEquals('new string translation', $this->_translate('string to translate')); @@ -293,6 +303,7 @@ public function testLargeOptionsDataSet() $optionsData[] = http_build_query($optionRowData); } $attributeData['serialized_options'] = json_encode($optionsData); + $this->getRequest()->setMethod('POST'); $this->getRequest()->setPostValue($attributeData); $this->dispatch('backend/catalog/product_attribute/save'); $entityTypeId = $this->_objectManager->create( From 6ed9586a9f8cab07a8f2a36c6d8cd4dc1458f552 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Thu, 3 Jan 2019 12:05:14 +0200 Subject: [PATCH 0162/1295] MAGETWO-97196: Automate with Integration test Verify Email functionality for order --- .../TestCase/AbstractBackendController.php | 2 +- .../Adminhtml/Order/Create/SaveTest.php | 157 +++++++++++++++++- .../AbstractCreditmemoControllerTest.php | 92 ++++++++++ .../Order/Creditmemo/AddCommentTest.php | 102 ++++++++++++ .../Adminhtml/Order/Creditmemo/SaveTest.php | 99 +++++++++++ .../Controller/Adminhtml/Order/EmailTest.php | 136 +++++++++++++++ .../Invoice/AbstractInvoiceControllerTest.php | 92 ++++++++++ .../Order/Invoice/AddCommentTest.php | 103 ++++++++++++ .../Adminhtml/Order/Invoice/EmailTest.php | 88 ++++++++++ .../Adminhtml/Order/Invoice/SaveTest.php | 97 +++++++++++ .../Magento/Sales/Model/Order/CreateTest.php | 101 +++++++++++ .../_files/guest_quote_with_addresses.php | 67 ++++++++ .../guest_quote_with_addresses_rollback.php | 32 ++++ .../AbstractShipmentControllerTest.php | 92 ++++++++++ .../Order/Shipment/AddCommentTest.php | 102 ++++++++++++ .../Adminhtml/Order/Shipment/SaveTest.php | 97 +++++++++++ 16 files changed, 1456 insertions(+), 3 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/AbstractCreditmemoControllerTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/AddCommentTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/SaveTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/EmailTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AbstractInvoiceControllerTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AddCommentTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/EmailTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Model/Order/CreateTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/guest_quote_with_addresses.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/guest_quote_with_addresses_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AbstractShipmentControllerTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AddCommentTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/SaveTest.php diff --git a/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractBackendController.php b/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractBackendController.php index 543bac2c6b5b5..0845ef640aa0c 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractBackendController.php +++ b/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractBackendController.php @@ -98,7 +98,7 @@ public function testAclHasAccess() public function testAclNoAccess() { - if ($this->resource === null) { + if ($this->resource === null || $this->uri === null) { $this->markTestIncomplete('Acl test is not complete'); } $this->_objectManager->get(\Magento\Framework\Acl\Builder::class) diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/SaveTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/SaveTest.php index b9573c99b4493..da0f2be856c51 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/SaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/SaveTest.php @@ -8,21 +8,61 @@ use Magento\Backend\Model\Session\Quote; use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Data\Form\FormKey; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Message\MessageInterface; use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\OrderRepository; use Magento\Sales\Model\Service\OrderService; +use Magento\TestFramework\Mail\Template\TransportBuilderMock; use Magento\TestFramework\TestCase\AbstractBackendController; +use PHPUnit\Framework\Constraint\StringContains; use PHPUnit_Framework_MockObject_MockObject as MockObject; +/** + * Class test backend order save. + * + * @magentoAppArea adminhtml + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class SaveTest extends AbstractBackendController { + /** + * @var TransportBuilderMock + */ + private $transportBuilder; + + /** + * @var FormKey + */ + private $formKey; + + /** + * @var string + */ + protected $resource = 'Magento_Sales::create'; + + /** + * @var string + */ + protected $uri = 'backend/sales/order_create/save'; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->transportBuilder = $this->_objectManager->get(TransportBuilderMock::class); + $this->formKey = $this->_objectManager->get(FormKey::class); + } + /** * Checks a case when order creation is failed on payment method processing but new customer already created * in the database and after new controller dispatching the customer should be already loaded in session * to prevent invalid validation. * - * @magentoAppArea adminhtml * @magentoDataFixture Magento/Sales/_files/quote_with_new_customer.php */ public function testExecuteWithPaymentOperation() @@ -35,7 +75,7 @@ public function testExecuteWithPaymentOperation() $email = 'john.doe001@test.com'; $data = [ 'account' => [ - 'email' => $email + 'email' => $email, ] ]; $this->getRequest()->setPostValue(['order' => $data]); @@ -64,6 +104,45 @@ public function testExecuteWithPaymentOperation() $this->_objectManager->removeSharedInstance(OrderService::class); } + /** + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Sales/_files/guest_quote_with_addresses.php + * + * @return void + */ + public function testSendEmailOnOrderSave() + { + $this->prepareRequest(['send_confirmation' => true]); + $this->dispatch('backend/sales/order_create/save'); + $this->assertSessionMessages( + $this->equalTo([(string)__('You created the order.')]), + MessageInterface::TYPE_SUCCESS + ); + + $this->assertRedirect($this->stringContains('sales/order/view/')); + + $orderId = $this->getOrderId(); + if ($orderId === false) { + $this->fail('Order is not created.'); + } + $order = $this->getOrder($orderId); + + $message = $this->transportBuilder->getSentMessage(); + $subject = __('Your %1 order confirmation', $order->getStore()->getFrontendName())->render(); + $assert = $this->logicalAnd( + new StringContains($order->getBillingAddress()->getName()), + new StringContains( + 'Thank you for your order from ' . $order->getStore()->getFrontendName() + ), + new StringContains( + "Your Order <span class=\"no-link\">#{$order->getIncrementId()}</span>" + ) + ); + + $this->assertEquals($message->getSubject(), $subject); + $this->assertThat($message->getRawMessage(), $assert); + } + /** * Gets quote by reserved order id. * @@ -82,4 +161,78 @@ private function getQuote($reservedOrderId) $items = $quoteRepository->getList($searchCriteria)->getItems(); return array_pop($items); } + + /** + * @inheritdoc + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Sales/_files/guest_quote_with_addresses.php + */ + public function testAclHasAccess() + { + $this->prepareRequest(); + + parent::testAclHasAccess(); + } + + /** + * @inheritdoc + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Sales/_files/guest_quote_with_addresses.php + */ + public function testAclNoAccess() + { + $this->prepareRequest(); + + parent::testAclNoAccess(); + } + + /** + * @param int $orderId + * @return OrderInterface + */ + private function getOrder(int $orderId): OrderInterface + { + return $this->_objectManager->get(OrderRepository::class)->get($orderId); + } + + /** + * @param array $params + * @return void + */ + private function prepareRequest(array $params = []) + { + $quote = $this->getQuote('guest_quote'); + $session = $this->_objectManager->get(Quote::class); + $session->setQuoteId($quote->getId()); + $session->setCustomerId(0); + + $email = 'john.doe001@test.com'; + $data = [ + 'account' => [ + 'email' => $email, + ], + ]; + + $data = array_replace_recursive($data, $params); + + $this->getRequest() + ->setMethod('POST') + ->setParams(['form_key' => $this->formKey->getFormKey()]) + ->setPostValue(['order' => $data]); + } + + /** + * @return string|bool + */ + protected function getOrderId() + { + $currentUrl = $this->getResponse()->getHeader('Location'); + $orderId = false; + + if (preg_match('/order_id\/(?<order_id>\d+)/', $currentUrl, $matches)) { + $orderId = $matches['order_id'] ?? ''; + } + + return $orderId; + } } diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/AbstractCreditmemoControllerTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/AbstractCreditmemoControllerTest.php new file mode 100644 index 0000000000000..2a7731715021b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/AbstractCreditmemoControllerTest.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Controller\Adminhtml\Order\Creditmemo; + +use Magento\Framework\Api\SearchCriteria; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Data\Form\FormKey; +use Magento\Sales\Api\Data\CreditmemoInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\OrderRepository; +use Magento\TestFramework\Mail\Template\TransportBuilderMock; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Abstract backend creditmemo test. + */ +class AbstractCreditmemoControllerTest extends AbstractBackendController +{ + /** + * @var TransportBuilderMock + */ + protected $transportBuilder; + + /** + * @var OrderRepository + */ + protected $orderRepository; + + /** + * @var FormKey + */ + protected $formKey; + + /** + * @var string + */ + protected $resource = 'Magento_Sales::sales_creditmemo'; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->transportBuilder = $this->_objectManager->get(TransportBuilderMock::class); + $this->orderRepository = $this->_objectManager->get(OrderRepository::class); + $this->formKey = $this->_objectManager->get(FormKey::class); + } + + /** + * @param string $incrementalId + * @return OrderInterface|null + */ + protected function getOrder(string $incrementalId) + { + /** @var SearchCriteria $searchCriteria */ + $searchCriteria = $this->_objectManager->create(SearchCriteriaBuilder::class) + ->addFilter(OrderInterface::INCREMENT_ID, $incrementalId) + ->create(); + + $orders = $this->orderRepository->getList($searchCriteria)->getItems(); + /** @var OrderInterface|null $order */ + $order = reset($orders); + + return $order; + } + + /** + * @param OrderInterface $order + * @return CreditmemoInterface + */ + protected function getCreditMemo(OrderInterface $order): CreditmemoInterface + { + /** @var \Magento\Sales\Model\ResourceModel\Order\Creditmemo\Collection $creditMemoCollection */ + $creditMemoCollection = $this->_objectManager->create( + \Magento\Sales\Model\ResourceModel\Order\Creditmemo\CollectionFactory::class + )->create(); + + /** @var CreditmemoInterface $creditMemo */ + $creditMemo = $creditMemoCollection + ->setOrderFilter($order) + ->setPageSize(1) + ->getFirstItem(); + + return $creditMemo; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/AddCommentTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/AddCommentTest.php new file mode 100644 index 0000000000000..ac11f777daf9c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/AddCommentTest.php @@ -0,0 +1,102 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Controller\Adminhtml\Order\Creditmemo; + +use PHPUnit\Framework\Constraint\RegularExpression; +use PHPUnit\Framework\Constraint\StringContains; + +/** + * Class verifies creditmemo add comment functionality. + * + * @magentoDbIsolation enabled + * @magentoAppArea adminhtml + * @magentoDataFixture Magento/Sales/_files/creditmemo_for_get.php + */ +class AddCommentTest extends AbstractCreditmemoControllerTest +{ + /** + * @var string + */ + protected $uri = 'backend/sales/order_creditmemo/addComment'; + + /** + * @return void + */ + public function testSendEmailOnAddCreditmemoComment() + { + $comment = 'Test Credit Memo Comment'; + $order = $this->prepareRequest( + [ + 'comment' => ['comment' => $comment, 'is_customer_notified' => true], + ] + ); + $this->dispatch('backend/sales/order_creditmemo/addComment'); + $html = $this->getResponse()->getBody(); + $this->assertContains($comment, $html); + + $message = $this->transportBuilder->getSentMessage(); + $subject =__('Update to your %1 credit memo', $order->getStore()->getFrontendName())->render(); + $messageConstraint = $this->logicalAnd( + new StringContains($order->getBillingAddress()->getName()), + new RegularExpression( + sprintf( + "/Your order #%s has been updated with a status of.*%s/", + $order->getIncrementId(), + $order->getFrontendStatusLabel() + ) + ), + new StringContains($comment) + ); + + $this->assertEquals($message->getSubject(), $subject); + $this->assertThat($message->getRawMessage(), $messageConstraint); + } + + /** + * @inheritdoc + */ + public function testAclHasAccess() + { + $this->prepareRequest(['comment' => ['comment' => 'Comment']]); + + parent::testAclHasAccess(); + } + + /** + * @inheritdoc + */ + public function testAclNoAccess() + { + $this->prepareRequest(['comment' => ['comment' => 'Comment']]); + + parent::testAclNoAccess(); + } + + /** + * @param array $params + * @return \Magento\Sales\Api\Data\OrderInterface|null + */ + private function prepareRequest(array $params = []) + { + $order = $this->getOrder('100000001'); + $creditmemo = $this->getCreditMemo($order); + + $this->getRequest()->setMethod('POST'); + $this->getRequest()->setParams( + [ + 'id' => $creditmemo->getEntityId(), + 'form_key' => $this->formKey->getFormKey(), + ] + ); + + $data = $params ?? []; + $this->getRequest()->setPostValue($data); + + return $order; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/SaveTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/SaveTest.php new file mode 100644 index 0000000000000..4df7710bb4388 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/SaveTest.php @@ -0,0 +1,99 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Controller\Adminhtml\Order\Creditmemo; + +use PHPUnit\Framework\Constraint\StringContains; + +/** + * Class tests creditmemo creation in backend. + * + * @magentoDbIsolation enabled + * @magentoAppArea adminhtml + * @magentoDataFixture Magento/Sales/_files/invoice.php + */ +class SaveTest extends AbstractCreditmemoControllerTest +{ + /** + * @var string + */ + protected $uri = 'backend/sales/order_creditmemo/save'; + + /** + * @return void + */ + public function testSendEmailOnCreditmemoSave() + { + $order = $this->prepareRequest(['creditmemo' => ['send_email' => true]]); + $this->dispatch('backend/sales/order_creditmemo/save'); + + $this->assertSessionMessages( + $this->equalTo([(string)__('You created the credit memo.')]), + \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + ); + $this->assertRedirect($this->stringContains('sales/order/view/order_id/' . $order->getEntityId())); + + $creditMemo = $this->getCreditMemo($order); + $message = $this->transportBuilder->getSentMessage(); + $subject = __('Credit memo for your %1 order', $order->getStore()->getFrontendName())->render(); + $messageConstraint = $this->logicalAnd( + new StringContains($order->getBillingAddress()->getName()), + new StringContains( + 'Thank you for your order from ' . $creditMemo->getStore()->getFrontendName() + ), + new StringContains( + "Your Credit Memo #{$creditMemo->getIncrementId()} for Order #{$order->getIncrementId()}" + ) + ); + + $this->assertEquals($message->getSubject(), $subject); + $this->assertThat($message->getRawMessage(), $messageConstraint); + } + + /** + * @inheritdoc + */ + public function testAclHasAccess() + { + $this->prepareRequest(); + + parent::testAclHasAccess(); + } + + /** + * @inheritdoc + */ + public function testAclNoAccess() + { + $this->prepareRequest(); + + parent::testAclNoAccess(); + } + + /** + * @param array $params + * @return \Magento\Sales\Api\Data\OrderInterface|null + */ + private function prepareRequest(array $params = []) + { + $order = $this->getOrder('100000001'); + $this->getRequest()->setMethod('POST'); + $this->getRequest()->setParams( + [ + 'order_id' => $order->getEntityId(), + 'form_key' => $this->formKey->getFormKey(), + ] + ); + + $data = ['creditmemo' => ['do_offline' => true]]; + $data = array_replace_recursive($data, $params); + + $this->getRequest()->setPostValue($data); + + return $order; + } +} 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 new file mode 100644 index 0000000000000..337ad206ade91 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/EmailTest.php @@ -0,0 +1,136 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Controller\Adminhtml\Order; + +use Magento\Framework\Api\SearchCriteria; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\OrderRepository; +use Magento\TestFramework\Mail\Template\TransportBuilderMock; +use PHPUnit\Framework\Constraint\StringContains; + +/** + * Class verifies order send email functionality. + * + * @magentoDbIsolation enabled + * @magentoAppArea adminhtml + * @magentoDataFixture Magento/Sales/_files/order.php + */ +class EmailTest extends \Magento\TestFramework\TestCase\AbstractBackendController +{ + /** + * @var OrderRepository + */ + private $orderRepository; + + /** + * @var TransportBuilderMock + */ + private $transportBuilder; + + /** + * @var string + */ + protected $resource = 'Magento_Sales::email'; + + /** + * @var string + */ + protected $uri = 'backend/sales/order/email'; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->orderRepository = $this->_objectManager->get(OrderRepository::class); + $this->transportBuilder = $this->_objectManager->get(TransportBuilderMock::class); + } + + /** + * @return void + */ + public function testSendOrderEmail() + { + $order = $this->prepareRequest(); + $this->dispatch('backend/sales/order/email'); + + $this->assertSessionMessages( + $this->equalTo([(string)__('You sent the order email.')]), + \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + ); + + $redirectUrl = 'sales/order/view/order_id/' . $order->getEntityId(); + $this->assertRedirect($this->stringContains($redirectUrl)); + + $message = $this->transportBuilder->getSentMessage(); + $subject = __('Your %1 order confirmation', $order->getStore()->getFrontendName())->render(); + $assert = $this->logicalAnd( + new StringContains($order->getBillingAddress()->getName()), + new StringContains( + 'Thank you for your order from ' . $order->getStore()->getFrontendName() + ), + new StringContains( + "Your Order <span class=\"no-link\">#{$order->getIncrementId()}</span>" + ) + ); + + $this->assertEquals($message->getSubject(), $subject); + $this->assertThat($message->getRawMessage(), $assert); + } + + /** + * @inheritdoc + */ + public function testAclHasAccess() + { + $this->prepareRequest(); + + parent::testAclHasAccess(); + } + + /** + * @inheritdoc + */ + public function testAclNoAccess() + { + $this->prepareRequest(); + + parent::testAclNoAccess(); + } + + /** + * @param string $incrementalId + * @return OrderInterface|null + */ + private function getOrder(string $incrementalId) + { + /** @var SearchCriteria $searchCriteria */ + $searchCriteria = $this->_objectManager->create(SearchCriteriaBuilder::class) + ->addFilter(OrderInterface::INCREMENT_ID, $incrementalId) + ->create(); + + $orders = $this->orderRepository->getList($searchCriteria)->getItems(); + /** @var OrderInterface|null $order */ + $order = reset($orders); + + return $order; + } + + /** + * @return OrderInterface|null + */ + private function prepareRequest() + { + $order = $this->getOrder('100000001'); + $this->getRequest()->setParams(['order_id' => $order->getEntityId()]); + + return $order; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AbstractInvoiceControllerTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AbstractInvoiceControllerTest.php new file mode 100644 index 0000000000000..3ba54418b6c26 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AbstractInvoiceControllerTest.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Controller\Adminhtml\Order\Invoice; + +use Magento\Framework\Api\SearchCriteria; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Data\Form\FormKey; +use Magento\Sales\Api\Data\InvoiceInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\OrderRepository; +use Magento\TestFramework\Mail\Template\TransportBuilderMock; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Abstract backend invoice test. + */ +class AbstractInvoiceControllerTest extends AbstractBackendController +{ + /** + * @var TransportBuilderMock + */ + protected $transportBuilder; + + /** + * @var OrderRepository + */ + protected $orderRepository; + + /** + * @var FormKey + */ + protected $formKey; + + /** + * @var string + */ + protected $resource = 'Magento_Sales::sales_invoice'; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->transportBuilder = $this->_objectManager->get(TransportBuilderMock::class); + $this->orderRepository = $this->_objectManager->get(OrderRepository::class); + $this->formKey = $this->_objectManager->get(FormKey::class); + } + + /** + * @param string $incrementalId + * @return OrderInterface|null + */ + protected function getOrder(string $incrementalId) + { + /** @var SearchCriteria $searchCriteria */ + $searchCriteria = $this->_objectManager->create(SearchCriteriaBuilder::class) + ->addFilter(OrderInterface::INCREMENT_ID, $incrementalId) + ->create(); + + $orders = $this->orderRepository->getList($searchCriteria)->getItems(); + /** @var OrderInterface $order */ + $order = reset($orders); + + return $order; + } + + /** + * @param OrderInterface $order + * @return InvoiceInterface + */ + protected function getInvoiceByOrder(OrderInterface $order): InvoiceInterface + { + /** @var \Magento\Sales\Model\ResourceModel\Order\Invoice\Collection $invoiceCollection */ + $invoiceCollection = $this->_objectManager->create( + \Magento\Sales\Model\ResourceModel\Order\Invoice\CollectionFactory::class + )->create(); + + /** @var InvoiceInterface $invoice */ + $invoice = $invoiceCollection + ->setOrderFilter($order) + ->setPageSize(1) + ->getFirstItem(); + + return $invoice; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AddCommentTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AddCommentTest.php new file mode 100644 index 0000000000000..8643dfc66f1b9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AddCommentTest.php @@ -0,0 +1,103 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Controller\Adminhtml\Order\Invoice; + +use PHPUnit\Framework\Constraint\RegularExpression; +use PHPUnit\Framework\Constraint\StringContains; + +/** + * Class verifies invoice add comment functionality. + * + * @magentoDbIsolation enabled + * @magentoAppArea adminhtml + * @magentoDataFixture Magento/Sales/_files/invoice.php + */ +class AddCommentTest extends AbstractInvoiceControllerTest +{ + /** + * @var string + */ + protected $uri = 'backend/sales/order_invoice/addComment'; + + /** + * @return void + */ + public function testSendEmailOnAddInvoiceComment() + { + $comment = 'Test Invoice Comment'; + $order = $this->prepareRequest( + [ + 'comment' => ['comment' => $comment, 'is_customer_notified' => true], + ] + ); + $this->dispatch('backend/sales/order_invoice/addComment'); + + $html = $this->getResponse()->getBody(); + $this->assertContains($comment, $html); + + $message = $this->transportBuilder->getSentMessage(); + $subject = __('Update to your %1 invoice', $order->getStore()->getFrontendName())->render(); + $messageConstraint = $this->logicalAnd( + new StringContains($order->getBillingAddress()->getName()), + new RegularExpression( + sprintf( + "/Your order #%s has been updated with a status of.*%s/", + $order->getIncrementId(), + $order->getFrontendStatusLabel() + ) + ), + new StringContains($comment) + ); + + $this->assertEquals($message->getSubject(), $subject); + $this->assertThat($message->getRawMessage(), $messageConstraint); + } + + /** + * @inheritdoc + */ + public function testAclHasAccess() + { + $this->prepareRequest(['comment' => ['comment' => 'Comment']]); + + parent::testAclHasAccess(); + } + + /** + * @inheritdoc + */ + public function testAclNoAccess() + { + $this->prepareRequest(['comment' => ['comment' => 'Comment']]); + + parent::testAclNoAccess(); + } + + /** + * @param array $params + * @return \Magento\Sales\Api\Data\OrderInterface|null + */ + private function prepareRequest(array $params = []) + { + $order = $this->getOrder('100000001'); + $invoice = $this->getInvoiceByOrder($order); + + $this->getRequest()->setMethod('POST'); + $this->getRequest()->setParams( + [ + 'id' => $invoice->getEntityId(), + 'form_key' => $this->formKey->getFormKey(), + ] + ); + + $data = $params ?? []; + $this->getRequest()->setPostValue($data); + + return $order; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/EmailTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/EmailTest.php new file mode 100644 index 0000000000000..39b7fc8ef0267 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/EmailTest.php @@ -0,0 +1,88 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Controller\Adminhtml\Order\Invoice; + +use PHPUnit\Framework\Constraint\StringContains; + +/** + * Class verifies invoice send email functionality. + * + * @magentoDbIsolation enabled + * @magentoAppArea adminhtml + * @magentoDataFixture Magento/Sales/_files/invoice.php + */ +class EmailTest extends AbstractInvoiceControllerTest +{ + /** + * @var string + */ + protected $uri = 'backend/sales/order_invoice/email'; + + /** + * @return void + */ + public function testSendInvoiceEmail() + { + $order = $this->getOrder('100000001'); + $invoice = $this->getInvoiceByOrder($order); + + $this->getRequest()->setParams(['invoice_id' => $invoice->getEntityId()]); + $this->dispatch('backend/sales/order_invoice/email'); + + $this->assertSessionMessages( + $this->equalTo([(string)__('You sent the message.')]), + \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + ); + + $redirectUrl = sprintf( + 'sales/invoice/view/order_id/%s/invoice_id/%s', + $order->getEntityId(), + $invoice->getEntityId() + ); + $this->assertRedirect($this->stringContains($redirectUrl)); + + $message = $this->transportBuilder->getSentMessage(); + $subject = __('Invoice for your %1 order', $order->getStore()->getFrontendName())->render(); + $messageConstraint = $this->logicalAnd( + new StringContains($invoice->getBillingAddress()->getName()), + new StringContains( + 'Thank you for your order from ' . $invoice->getStore()->getFrontendName() + ), + new StringContains( + "Your Invoice #{$invoice->getIncrementId()} for Order #{$order->getIncrementId()}" + ) + ); + + $this->assertEquals($message->getSubject(), $subject); + $this->assertThat($message->getRawMessage(), $messageConstraint); + } + + /** + * @inheritdoc + */ + public function testAclHasAccess() + { + $order = $this->getOrder('100000001'); + $invoice = $this->getInvoiceByOrder($order); + $this->uri .= '/invoice_id/' . $invoice->getEntityId(); + + parent::testAclHasAccess(); + } + + /** + * @inheritdoc + */ + public function testAclNoAccess() + { + $order = $this->getOrder('100000001'); + $invoice = $this->getInvoiceByOrder($order); + $this->uri .= '/invoice_id/' . $invoice->getEntityId(); + + parent::testAclNoAccess(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php new file mode 100644 index 0000000000000..2dcc3e374d3c7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php @@ -0,0 +1,97 @@ +<?php +declare(strict_types=1); + +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Sales\Controller\Adminhtml\Order\Invoice; + +use PHPUnit\Framework\Constraint\StringContains; + +/** + * Class tests invoice creation in backend. + * + * @magentoDbIsolation enabled + * @magentoAppArea adminhtml + * @magentoDataFixture Magento/Sales/_files/order.php + */ +class SaveTest extends AbstractInvoiceControllerTest +{ + /** + * @var string + */ + protected $uri = 'backend/sales/order_invoice/save'; + + /** + * @return void + */ + public function testSendEmailOnInvoiceSave() + { + $order = $this->prepareRequest(['invoice' => ['send_email' => true]]); + $this->dispatch('backend/sales/order_invoice/save'); + + $this->assertSessionMessages( + $this->equalTo([(string)__('The invoice has been created.')]), + \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + ); + $this->assertRedirect($this->stringContains('sales/order/view/order_id/' . $order->getEntityId())); + + $invoice = $this->getInvoiceByOrder($order); + $message = $this->transportBuilder->getSentMessage(); + $subject = __('Invoice for your %1 order', $order->getStore()->getFrontendName())->render(); + $messageConstraint = $this->logicalAnd( + new StringContains($invoice->getBillingAddress()->getName()), + new StringContains( + 'Thank you for your order from ' . $invoice->getStore()->getFrontendName() + ), + new StringContains( + "Your Invoice #{$invoice->getIncrementId()} for Order #{$order->getIncrementId()}" + ) + ); + + $this->assertEquals($message->getSubject(), $subject); + $this->assertThat($message->getRawMessage(), $messageConstraint); + } + + /** + * @inheritdoc + */ + public function testAclHasAccess() + { + $this->prepareRequest(); + + parent::testAclHasAccess(); + } + + /** + * @inheritdoc + */ + public function testAclNoAccess() + { + $this->prepareRequest(); + + parent::testAclNoAccess(); + } + + /** + * @param array $params + * @return \Magento\Sales\Api\Data\OrderInterface|null + */ + private function prepareRequest(array $params = []) + { + $order = $this->getOrder('100000001'); + $this->getRequest()->setMethod('POST'); + $this->getRequest()->setParams( + [ + 'order_id' => $order->getEntityId(), + 'form_key' => $this->formKey->getFormKey(), + ] + ); + + $data = $params ?? []; + $this->getRequest()->setPostValue($data); + + return $order; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/CreateTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/CreateTest.php new file mode 100644 index 0000000000000..ada7a4631981f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/CreateTest.php @@ -0,0 +1,101 @@ +<?php +declare(strict_types=1); + +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Sales\Model\Order; + +use Magento\Checkout\Model\Session as CheckoutSession; +use Magento\Framework\Data\Form\FormKey; +use Magento\Framework\ObjectManagerInterface; +use Magento\Quote\Api\GuestCartManagementInterface; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\QuoteIdMask; +use Magento\Quote\Model\QuoteIdMaskFactory; +use Magento\Sales\Model\OrderRepository; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Mail\Template\TransportBuilderMock; +use PHPUnit\Framework\Constraint\StringContains; + +/** + * Class verifies order creation. + * + * @magentoDbIsolation enabled + * @magentoAppArea frontend + */ +class CreateTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var TransportBuilderMock + */ + private $transportBuilder; + + /** + * @var QuoteIdMaskFactory + */ + private $quoteIdMaskFactory; + + /** + * @var FormKey + */ + private $formKey; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->objectManager = Bootstrap::getObjectManager(); + $this->transportBuilder = $this->objectManager->get(TransportBuilderMock::class); + $this->quoteIdMaskFactory = $this->objectManager->get(QuoteIdMaskFactory::class); + $this->formKey = $this->objectManager->get(FormKey::class); + } + + /** + * @magentoDataFixture Magento/Sales/_files/guest_quote_with_addresses.php + * @return void + */ + public function testSendEmailOnOrderPlace() + { + /** @var Quote $quote */ + $quote = $this->objectManager->create(Quote::class); + $quote->load('guest_quote', 'reserved_order_id'); + + $checkoutSession = $this->objectManager->get(CheckoutSession::class); + $checkoutSession->setQuoteId($quote->getId()); + + /** @var QuoteIdMask $quoteIdMask */ + $quoteIdMask = $this->quoteIdMaskFactory->create(); + $quoteIdMask->load($quote->getId(), 'quote_id'); + $cartId = $quoteIdMask->getMaskedId(); + + /** @var GuestCartManagementInterface $cartManagement */ + $cartManagement = $this->objectManager->get(GuestCartManagementInterface::class); + $orderId = $cartManagement->placeOrder($cartId); + $order = $this->objectManager->get(OrderRepository::class)->get($orderId); + + $message = $this->transportBuilder->getSentMessage(); + $subject = __('Your %1 order confirmation', $order->getStore()->getFrontendName())->render(); + $assert = $this->logicalAnd( + new StringContains($order->getBillingAddress()->getName()), + new StringContains( + 'Thank you for your order from ' . $order->getStore()->getFrontendName() + ), + new StringContains( + "Your Order <span class=\"no-link\">#{$order->getIncrementId()}</span>" + ) + ); + + $this->assertEquals($message->getSubject(), $subject); + $this->assertThat($message->getRawMessage(), $assert); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/guest_quote_with_addresses.php b/dev/tests/integration/testsuite/Magento/Sales/_files/guest_quote_with_addresses.php new file mode 100644 index 0000000000000..3b851d7f2b359 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/guest_quote_with_addresses.php @@ -0,0 +1,67 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/address_list.php'; + +\Magento\TestFramework\Helper\Bootstrap::getInstance()->loadArea('frontend'); + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$product = $objectManager->create(\Magento\Catalog\Model\Product::class); +$product->setTypeId('simple') + ->setAttributeSetId(4) + ->setName('Simple Product') + ->setSku('simple-product-guest-quote') + ->setPrice(10) + ->setTaxClassId(0) + ->setMetaTitle('meta title') + ->setMetaKeyword('meta keyword') + ->setMetaDescription('meta description') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStockData( + [ + 'qty' => 100, + 'is_in_stock' => 1, + ] + )->save(); + +$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); +$product = $productRepository->get('simple-product-guest-quote'); + +$addressData = reset($addresses); + +$billingAddress = $objectManager->create( + \Magento\Quote\Model\Quote\Address::class, + ['data' => $addressData] +); +$billingAddress->setAddressType('billing'); + +$shippingAddress = clone $billingAddress; +$shippingAddress->setId(null)->setAddressType('shipping'); + +$store = $objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->getStore(); + +/** @var \Magento\Quote\Model\Quote $quote */ +$quote = $objectManager->create(\Magento\Quote\Model\Quote::class); +$quote->setCustomerIsGuest(true) + ->setStoreId($store->getId()) + ->setReservedOrderId('guest_quote') + ->setBillingAddress($billingAddress) + ->setShippingAddress($shippingAddress) + ->addProduct($product); +$quote->getPayment()->setMethod('checkmo'); +$quote->getShippingAddress()->setShippingMethod('flatrate_flatrate')->setCollectShippingRates(1); +$quote->collectTotals(); + +$quoteRepository = $objectManager->create(\Magento\Quote\Api\CartRepositoryInterface::class); +$quoteRepository->save($quote); + +/** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */ +$quoteIdMask = $objectManager->create(\Magento\Quote\Model\QuoteIdMaskFactory::class)->create(); +$quoteIdMask->setQuoteId($quote->getId()); +$quoteIdMask->setDataChanges(true); +$quoteIdMask->save(); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/guest_quote_with_addresses_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/guest_quote_with_addresses_rollback.php new file mode 100644 index 0000000000000..02c42153b72c3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/guest_quote_with_addresses_rollback.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +/** @var \Magento\Framework\Registry $registry */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$registry = $objectManager->get(\Magento\Framework\Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var $quote \Magento\Quote\Model\Quote */ +$quote = $objectManager->create(\Magento\Quote\Model\Quote::class); +$quote->load('guest_quote', 'reserved_order_id'); +if ($quote->getId()) { + $quote->delete(); +} + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('simple-product-guest-quote', false, null, true); + $productRepository->delete($product); +} catch (\Magento\Framework\Exception\NoSuchEntityException $exception) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AbstractShipmentControllerTest.php b/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AbstractShipmentControllerTest.php new file mode 100644 index 0000000000000..0a1926d58624c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AbstractShipmentControllerTest.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Shipping\Controller\Adminhtml\Order\Shipment; + +use Magento\Framework\Api\SearchCriteria; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Data\Form\FormKey; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\Data\ShipmentInterface; +use Magento\Sales\Model\OrderRepository; +use Magento\TestFramework\Mail\Template\TransportBuilderMock; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Abstract backend shipment test. + */ +class AbstractShipmentControllerTest extends AbstractBackendController +{ + /** + * @var TransportBuilderMock + */ + protected $transportBuilder; + + /** + * @var OrderRepository + */ + protected $orderRepository; + + /** + * @var FormKey + */ + protected $formKey; + + /** + * @var string + */ + protected $resource = 'Magento_Sales::shipment'; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->transportBuilder = $this->_objectManager->get(TransportBuilderMock::class); + $this->orderRepository = $this->_objectManager->get(OrderRepository::class); + $this->formKey = $this->_objectManager->get(FormKey::class); + } + + /** + * @param string $incrementalId + * @return OrderInterface|null + */ + protected function getOrder(string $incrementalId) + { + /** @var SearchCriteria $searchCriteria */ + $searchCriteria = $this->_objectManager->create(SearchCriteriaBuilder::class) + ->addFilter(OrderInterface::INCREMENT_ID, $incrementalId) + ->create(); + + $orders = $this->orderRepository->getList($searchCriteria)->getItems(); + /** @var OrderInterface|null $order */ + $order = reset($orders); + + return $order; + } + + /** + * @param OrderInterface $order + * @return ShipmentInterface + */ + protected function getShipment(OrderInterface $order): ShipmentInterface + { + /** @var \Magento\Sales\Model\ResourceModel\Order\Shipment\Collection $shipmentCollection */ + $shipmentCollection = $this->_objectManager->create( + \Magento\Sales\Model\ResourceModel\Order\Shipment\CollectionFactory::class + )->create(); + + /** @var ShipmentInterface $shipment */ + $shipment = $shipmentCollection + ->setOrderFilter($order) + ->setPageSize(1) + ->getFirstItem(); + + return $shipment; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AddCommentTest.php b/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AddCommentTest.php new file mode 100644 index 0000000000000..c86ad71e7d5ca --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/AddCommentTest.php @@ -0,0 +1,102 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Shipping\Controller\Adminhtml\Order\Shipment; + +use PHPUnit\Framework\Constraint\RegularExpression; +use PHPUnit\Framework\Constraint\StringContains; + +/** + * Class verifies shipment add comment functionality. + * + * @magentoDbIsolation enabled + * @magentoAppArea adminhtml + * @magentoDataFixture Magento/Sales/_files/shipment.php + */ +class AddCommentTest extends AbstractShipmentControllerTest +{ + /** + * @var string + */ + protected $uri = 'backend/admin/order_shipment/addComment'; + + /** + * @return void + */ + public function testSendEmailOnShipmentCommentAdd() + { + $comment = 'Test Shipment Comment'; + $order = $this->prepareRequest( + [ + 'comment' => ['comment' => $comment, 'is_customer_notified' => true], + ] + ); + $this->dispatch('backend/admin/order_shipment/addComment'); + $html = $this->getResponse()->getBody(); + $this->assertContains($comment, $html); + + $message = $this->transportBuilder->getSentMessage(); + $subject =__('Update to your %1 shipment', $order->getStore()->getFrontendName())->render(); + $messageConstraint = $this->logicalAnd( + new StringContains($order->getBillingAddress()->getName()), + new RegularExpression( + sprintf( + "/Your order #%s has been updated with a status of.*%s/", + $order->getIncrementId(), + $order->getFrontendStatusLabel() + ) + ), + new StringContains($comment) + ); + + $this->assertEquals($message->getSubject(), $subject); + $this->assertThat($message->getRawMessage(), $messageConstraint); + } + + /** + * @inheritdoc + */ + public function testAclHasAccess() + { + $this->prepareRequest(['comment', ['comment' => 'Comment']]); + + parent::testAclHasAccess(); + } + + /** + * @inheritdoc + */ + public function testAclNoAccess() + { + $this->prepareRequest(['comment', ['comment' => 'Comment']]); + + parent::testAclNoAccess(); + } + + /** + * @param array $params + * @return \Magento\Sales\Api\Data\OrderInterface|null + */ + private function prepareRequest(array $params = []) + { + $order = $this->getOrder('100000001'); + $shipment = $this->getShipment($order); + + $this->getRequest()->setMethod('POST'); + $this->getRequest()->setParams( + [ + 'id' => $shipment->getEntityId(), + 'form_key' => $this->formKey->getFormKey(), + ] + ); + + $data = $params ?? []; + $this->getRequest()->setPostValue($data); + + return $order; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/SaveTest.php b/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/SaveTest.php new file mode 100644 index 0000000000000..4eb65678583aa --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Shipping/Controller/Adminhtml/Order/Shipment/SaveTest.php @@ -0,0 +1,97 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Shipping\Controller\Adminhtml\Order\Shipment; + +use PHPUnit\Framework\Constraint\StringContains; + +/** + * Class verifies shipment creation functionality. + * + * @magentoDbIsolation enabled + * @magentoAppArea adminhtml + * @magentoDataFixture Magento/Sales/_files/order.php + */ +class SaveTest extends AbstractShipmentControllerTest +{ + /** + * @var string + */ + protected $uri = 'backend/admin/order_shipment/save'; + + /** + * @return void + */ + public function testSendEmailOnShipmentSave() + { + $order = $this->prepareRequest(['shipment' => ['send_email' => true]]); + $this->dispatch('backend/admin/order_shipment/save'); + + $this->assertSessionMessages( + $this->equalTo([(string)__('The shipment has been created.')]), + \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + ); + $this->assertRedirect($this->stringContains('sales/order/view/order_id/' . $order->getEntityId())); + + $shipment = $this->getShipment($order); + $message = $this->transportBuilder->getSentMessage(); + $subject = __('Your %1 order has shipped', $order->getStore()->getFrontendName())->render(); + $messageConstraint = $this->logicalAnd( + new StringContains($order->getBillingAddress()->getName()), + new StringContains( + 'Thank you for your order from ' . $shipment->getStore()->getFrontendName() + ), + new StringContains( + "Your Shipment #{$shipment->getIncrementId()} for Order #{$order->getIncrementId()}" + ) + ); + + $this->assertEquals($message->getSubject(), $subject); + $this->assertThat($message->getRawMessage(), $messageConstraint); + } + + /** + * @inheritdoc + */ + public function testAclHasAccess() + { + $this->prepareRequest(); + + parent::testAclHasAccess(); + } + + /** + * @inheritdoc + */ + public function testAclNoAccess() + { + $this->prepareRequest(); + + parent::testAclNoAccess(); + } + + /** + * @param array $params + * @return \Magento\Sales\Api\Data\OrderInterface|null + */ + private function prepareRequest(array $params = []) + { + $order = $this->getOrder('100000001'); + $this->getRequest()->setMethod('POST'); + $this->getRequest()->setParams( + [ + 'order_id' => $order->getEntityId(), + 'form_key' => $this->formKey->getFormKey(), + ] + ); + + $data = $params ?? []; + $this->getRequest()->setPostValue($data); + + return $order; + } +} From 36c2329505ca63c821fc8d7a553e6f35a4f3ae40 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Thu, 3 Jan 2019 13:20:49 +0000 Subject: [PATCH 0163/1295] magento/magento2#19423: Removed extra blank line --- .../Observer/AssignCouponDataAfterOrderCustomerAssignTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Observer/AssignCouponDataAfterOrderCustomerAssignTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Observer/AssignCouponDataAfterOrderCustomerAssignTest.php index 06478dc1f1571..840ee1a5607d9 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Observer/AssignCouponDataAfterOrderCustomerAssignTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Observer/AssignCouponDataAfterOrderCustomerAssignTest.php @@ -75,7 +75,6 @@ class AssignCouponDataAfterOrderCustomerAssignTest extends \PHPUnit\Framework\Te */ private $customer; - /** * @inheritdoc */ From e6e0d732c46b6275a7edcdae1303b0b0bbcd45d7 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Thu, 3 Jan 2019 16:21:02 +0200 Subject: [PATCH 0164/1295] MAGETWO-97196: Automate with Integration test Verify Email functionality for order --- .../Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php | 4 ++-- .../testsuite/Magento/Sales/Model/Order/CreateTest.php | 3 +-- .../Magento/Sales/_files/guest_quote_with_addresses.php | 3 ++- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php index 2dcc3e374d3c7..d451bdcb287cf 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php @@ -1,10 +1,10 @@ <?php -declare(strict_types=1); - /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sales\Controller\Adminhtml\Order\Invoice; use PHPUnit\Framework\Constraint\StringContains; diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/CreateTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/CreateTest.php index ada7a4631981f..4ff4ad384d3e4 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/CreateTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/CreateTest.php @@ -1,10 +1,9 @@ <?php -declare(strict_types=1); - /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Sales\Model\Order; diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/guest_quote_with_addresses.php b/dev/tests/integration/testsuite/Magento/Sales/_files/guest_quote_with_addresses.php index 3b851d7f2b359..601c500c18429 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/guest_quote_with_addresses.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/guest_quote_with_addresses.php @@ -10,9 +10,10 @@ \Magento\TestFramework\Helper\Bootstrap::getInstance()->loadArea('frontend'); $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +/** @var \Magento\Catalog\Model\Product $product */ $product = $objectManager->create(\Magento\Catalog\Model\Product::class); $product->setTypeId('simple') - ->setAttributeSetId(4) + ->setAttributeSetId($product->getDefaultAttributeSetId()) ->setName('Simple Product') ->setSku('simple-product-guest-quote') ->setPrice(10) From eefc8877b040714212400a58185eb68dae1276c5 Mon Sep 17 00:00:00 2001 From: Dmytro Voskoboinikov <voskoboi@adobe.com> Date: Thu, 3 Jan 2019 11:54:04 -0600 Subject: [PATCH 0165/1295] MAGETWO-95441: Fixed incorrect sitemap request flow --- app/code/Magento/Sitemap/Block/Adminhtml/Edit.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sitemap/Block/Adminhtml/Edit.php b/app/code/Magento/Sitemap/Block/Adminhtml/Edit.php index 0148b1c9c8927..ff3334dc38531 100644 --- a/app/code/Magento/Sitemap/Block/Adminhtml/Edit.php +++ b/app/code/Magento/Sitemap/Block/Adminhtml/Edit.php @@ -78,7 +78,7 @@ protected function _construct() /** * @inheritdoc */ - public function _prepareLayout() + protected function _prepareLayout() { $this->buttonList->update( 'delete', From fe24d04ef27b87845cbea23b0df454b7b5343375 Mon Sep 17 00:00:00 2001 From: Dmytro Voskoboinikov <voskoboi@adobe.com> Date: Thu, 3 Jan 2019 13:26:38 -0600 Subject: [PATCH 0166/1295] MAGETWO-88608: Wrong behavior of static content deploy --- .../Magento/Deploy/Console/InputValidator.php | 39 +++- .../Test/Unit/Console/InputValidatorTest.php | 208 ++++++++++++++++++ 2 files changed, 244 insertions(+), 3 deletions(-) create mode 100644 app/code/Magento/Deploy/Test/Unit/Console/InputValidatorTest.php diff --git a/app/code/Magento/Deploy/Console/InputValidator.php b/app/code/Magento/Deploy/Console/InputValidator.php index b3301f60fec26..0e07f53ee500a 100644 --- a/app/code/Magento/Deploy/Console/InputValidator.php +++ b/app/code/Magento/Deploy/Console/InputValidator.php @@ -5,10 +5,11 @@ */ namespace Magento\Deploy\Console; -use Magento\Setup\Console\Command\DeployStaticContentCommand; +use Magento\Framework\App\ObjectManager; use Magento\Deploy\Console\DeployStaticOptions as Options; use Magento\Framework\Validator\Locale; use Symfony\Component\Console\Input\InputInterface; +use Magento\Framework\Validator\RegexFactory; /** * Command input arguments validator class @@ -55,14 +56,24 @@ class InputValidator */ private $localeValidator; + /** + * @var RegexFactory + */ + private $versionValidatorFactory; + /** * InputValidator constructor * * @param Locale $localeValidator + * @param RegexFactory|null $versionValidatorFactory */ - public function __construct(Locale $localeValidator) - { + public function __construct( + Locale $localeValidator, + RegexFactory $versionValidatorFactory = null + ) { $this->localeValidator = $localeValidator; + $this->versionValidatorFactory = $versionValidatorFactory ?: ObjectManager::getInstance() + ->get(RegexFactory::class); } /** @@ -70,6 +81,7 @@ public function __construct(Locale $localeValidator) * * @param InputInterface $input * @return void + * @throws \InvalidArgumentException */ public function validate(InputInterface $input) { @@ -85,6 +97,9 @@ public function validate(InputInterface $input) $input->getArgument(Options::LANGUAGES_ARGUMENT) ?: ['all'], $input->getOption(Options::EXCLUDE_LANGUAGE) ); + $this->checkVersionInput( + $input->getOption(Options::CONTENT_VERSION) ?: '' + ); } /** @@ -147,4 +162,22 @@ private function checkLanguagesInput(array $languagesInclude, array $languagesEx } } } + + /** + * @param string $contentVersion + * @throws \InvalidArgumentException + */ + private function checkVersionInput(string $contentVersion) + { + if ($contentVersion) { + $versionValidator = $this->versionValidatorFactory->create(['pattern' => '/^[A-Za-z0-9_.]+$/']); + if (!$versionValidator->isValid($contentVersion)) { + throw new \InvalidArgumentException(__( + 'Argument "' . + Options::CONTENT_VERSION + . '" has invalid value, content version should contain only characters, digits and dots' + )); + } + } + } } diff --git a/app/code/Magento/Deploy/Test/Unit/Console/InputValidatorTest.php b/app/code/Magento/Deploy/Test/Unit/Console/InputValidatorTest.php new file mode 100644 index 0000000000000..9268fac9a1b30 --- /dev/null +++ b/app/code/Magento/Deploy/Test/Unit/Console/InputValidatorTest.php @@ -0,0 +1,208 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Deploy\Test\Unit\Console; + +use Magento\Framework\Validator\Regex; +use Magento\Framework\Validator\RegexFactory; +use PHPUnit\Framework\TestCase; +use Magento\Deploy\Console\InputValidator; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Deploy\Console\DeployStaticOptions as Options; +use Magento\Framework\Validator\Locale; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\ArrayInput; +use InvalidArgumentException; +use Symfony\Component\Console\Input\InputArgument; + +/** + * Class InputValidatorTest + * @package Magento\Deploy\Test\Unit\Console + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class InputValidatorTest extends TestCase +{ + /** + * @var ObjectManagerHelper + */ + protected $objectManagerHelper; + + /** + * @var InputValidator + */ + protected $inputValidator; + + /** + * @var Locale + */ + protected $localeValidator; + + /** + * @throws \Zend_Validate_Exception + */ + protected function setUp() + { + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $regexFactoryMock = $this->getMockBuilder(RegexFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $regexObject = new Regex('/^[A-Za-z0-9_.]+$/'); + + $regexFactoryMock->expects($this->any())->method('create') + ->willReturn($regexObject); + + $localeObjectMock = $this->getMockBuilder(Locale::class)->setMethods(['isValid']) + ->disableOriginalConstructor() + ->getMock(); + + $localeObjectMock->expects($this->any())->method('isValid') + ->with('en_US') + ->will($this->returnValue(true)); + + $this->inputValidator = $this->objectManagerHelper->getObject( + InputValidator::class, + [ + 'localeValidator' => $localeObjectMock, + 'versionValidatorFactory' => $regexFactoryMock + ] + ); + } + + /** + * @throws \Zend_Validate_Exception + */ + public function testValidate() + { + $input = $this->getMockBuilder(ArrayInput::class) + ->disableOriginalConstructor() + ->setMethods(['getOption', 'getArgument']) + ->getMock(); + + $input->expects($this->atLeastOnce())->method('getArgument')->willReturn(['all']); + + $input->expects($this->atLeastOnce())->method('getOption') + ->willReturnMap( + [ + [Options::AREA, ['all']], + [Options::EXCLUDE_AREA, ['none']], + [Options::THEME, ['all']], + [Options::EXCLUDE_THEME, ['none']], + [Options::EXCLUDE_LANGUAGE, ['none']], + [Options::CONTENT_VERSION, '12345'] + ] + ); + + /** @noinspection PhpParamsInspection */ + $this->inputValidator->validate($input); + } + + /** + * @covers \Magento\Deploy\Console\InputValidator::checkAreasInput() + */ + public function testCheckAreasInputException() + { + $options = [ + new InputOption(Options::AREA, null, 4, null, ['test']), + new InputOption(Options::EXCLUDE_AREA, null, 4, null, ['test']) + ]; + + $inputDefinition = new InputDefinition($options); + + try { + $this->inputValidator->validate( + new ArrayInput([], $inputDefinition) + ); + } catch (\Exception $e) { + $this->assertContains('--area (-a) and --exclude-area cannot be used at the same time', $e->getMessage()); + $this->assertInstanceOf(InvalidArgumentException::class, $e); + } + } + + /** + * @covers \Magento\Deploy\Console\InputValidator::checkThemesInput() + */ + public function testCheckThemesInputException() + { + $options = [ + new InputOption(Options::AREA, null, 4, null, ['all']), + new InputOption(Options::EXCLUDE_AREA, null, 4, null, ['none']), + new InputOption(Options::THEME, null, 4, '', ['blank']), + new InputOption(Options::EXCLUDE_THEME, null, 4, '', ['luma']) + ]; + + $inputDefinition = new InputDefinition($options); + + try { + $this->inputValidator->validate( + new ArrayInput([], $inputDefinition) + ); + } catch (\Exception $e) { + $this->assertContains('--theme (-t) and --exclude-theme cannot be used at the same time', $e->getMessage()); + $this->assertInstanceOf(InvalidArgumentException::class, $e); + } + } + + public function testCheckLanguagesInputException() + { + $options = [ + new InputOption(Options::AREA, null, 4, null, ['all']), + new InputOption(Options::EXCLUDE_AREA, null, 4, null, ['none']), + new InputOption(Options::THEME, null, 4, '', ['all']), + new InputOption(Options::EXCLUDE_THEME, null, 4, '', ['none']), + new InputArgument(Options::LANGUAGES_ARGUMENT, null, 4, ['en_US']), + new InputOption(Options::EXCLUDE_LANGUAGE, null, 4, '', ['all']) + ]; + + $inputDefinition = new InputDefinition($options); + + try { + $this->inputValidator->validate( + new ArrayInput([], $inputDefinition) + ); + } catch (\Exception $e) { + $this->assertContains( + '--language (-l) and --exclude-language cannot be used at the same time', + $e->getMessage() + ); + + $this->assertInstanceOf(InvalidArgumentException::class, $e); + } + } + + public function testCheckVersionInputException() + { + $options = [ + new InputOption(Options::AREA, null, 4, null, ['all']), + new InputOption(Options::EXCLUDE_AREA, null, 4, null, ['none']), + new InputOption(Options::THEME, null, 4, '', ['all']), + new InputOption(Options::EXCLUDE_THEME, null, 4, '', ['none']), + new InputArgument(Options::LANGUAGES_ARGUMENT, null, 4, ['en_US']), + new InputOption(Options::EXCLUDE_LANGUAGE, null, 4, '', ['none']), + new InputOption(Options::CONTENT_VERSION, null, 4, '', '/*!#') + ]; + + $inputDefinition = new InputDefinition($options); + + try { + $this->inputValidator->validate( + new ArrayInput([], $inputDefinition) + ); + } catch (\Exception $e) { + $this->assertContains( + 'Argument "' . + Options::CONTENT_VERSION + . '" has invalid value, content version should contain only characters, digits and dots', + $e->getMessage() + ); + + $this->assertInstanceOf(InvalidArgumentException::class, $e); + } + } +} From 6e1dfaa80348e87d2695184e1388b18e8d9ec097 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Fri, 4 Jan 2019 10:25:11 +0200 Subject: [PATCH 0167/1295] MAGETWO-85699: Multiselect attribute values is not searchable under Quick Search when more than one value is selected --- .../Model/ResourceModel/Engine.php | 7 ++++-- .../Indexer/Fulltext/Action/FullTest.php | 23 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogSearch/Model/ResourceModel/Engine.php b/app/code/Magento/CatalogSearch/Model/ResourceModel/Engine.php index fac8c4d2a47f6..c6b679a5468c6 100644 --- a/app/code/Magento/CatalogSearch/Model/ResourceModel/Engine.php +++ b/app/code/Magento/CatalogSearch/Model/ResourceModel/Engine.php @@ -107,7 +107,9 @@ public function processAttributeValue($attribute, $value) && in_array($attribute->getFrontendInput(), ['text', 'textarea']) ) { $result = $value; - } elseif ($this->isTermFilterableAttribute($attribute)) { + } elseif ($this->isTermFilterableAttribute($attribute) + || ($attribute->getIsSearchable() && in_array($attribute->getFrontendInput(), ['select', 'multiselect'])) + ) { $result = ''; } @@ -115,7 +117,8 @@ public function processAttributeValue($attribute, $value) } /** - * Prepare index array as a string glued by separator + * Prepare index array as a string glued by separator. + * * Support 2 level array gluing * * @param array $index diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/FullTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/FullTest.php index 59ad91ae7b076..993462350b638 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/FullTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/FullTest.php @@ -29,6 +29,8 @@ protected function setUp() } /** + * Testing fulltext index rebuild. + * * @magentoDataFixture Magento/CatalogSearch/_files/products_for_index.php * @magentoDataFixture Magento/CatalogSearch/_files/product_configurable_not_available.php * @magentoDataFixture Magento/Framework/Search/_files/product_configurable.php @@ -58,6 +60,8 @@ public function testGetIndexData() } /** + * Prepare and return expected index data. + * * @return array */ private function getExpectedIndexData() @@ -68,32 +72,49 @@ private function getExpectedIndexData() $nameId = $attributeRepository->get(ProductInterface::NAME)->getAttributeId(); /** @see dev/tests/integration/testsuite/Magento/Framework/Search/_files/configurable_attribute.php */ $configurableId = $attributeRepository->get('test_configurable')->getAttributeId(); + $statusId = $attributeRepository->get(ProductInterface::STATUS)->getAttributeId(); + $taxClassId = $attributeRepository + ->get(\Magento\Customer\Api\Data\GroupInterface::TAX_CLASS_ID) + ->getAttributeId(); + return [ 'configurable' => [ $skuId => 'configurable', $configurableId => 'Option 1 | Option 2', $nameId => 'Configurable Product | Configurable OptionOption 1 | Configurable OptionOption 2', + $taxClassId => 'Taxable Goods | Taxable Goods | Taxable Goods', + $statusId => 'Enabled | Enabled | Enabled', ], 'index_enabled' => [ $skuId => 'index_enabled', $nameId => 'index enabled', + $taxClassId => 'Taxable Goods', + $statusId => 'Enabled', ], 'index_visible_search' => [ $skuId => 'index_visible_search', $nameId => 'index visible search', + $taxClassId => 'Taxable Goods', + $statusId => 'Enabled', ], 'index_visible_category' => [ $skuId => 'index_visible_category', $nameId => 'index visible category', + $taxClassId => 'Taxable Goods', + $statusId => 'Enabled', ], 'index_visible_both' => [ $skuId => 'index_visible_both', $nameId => 'index visible both', + $taxClassId => 'Taxable Goods', + $statusId => 'Enabled', ] ]; } /** + * Testing fulltext index rebuild with configurations. + * * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php */ public function testRebuildStoreIndexConfigurable() @@ -114,6 +135,8 @@ public function testRebuildStoreIndexConfigurable() } /** + * Get product Id by its SKU. + * * @param string $sku * @return int */ From 5c0f9c9a130d8d59f54c4e90de7ea0341f2987e1 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Fri, 4 Jan 2019 10:28:55 +0200 Subject: [PATCH 0168/1295] MAGETWO-72839: Customer can't see available Payment Method for specific country --- ...eckoutTestWithRestrictedCountriesForPaymentTest.xml | 2 +- ...eckoutTestWithRestrictedCountriesForPaymentTest.xml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml index 724f19909cc30..a4583fb7fa50c 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml @@ -26,7 +26,7 @@ <remove keyForRemoval="guestCheckoutFillingShippingSection"/> <remove keyForRemoval="guestCheckoutFillingShippingSectionUK"/> - <remove keyForRemoval="questPlaceOrder"/> + <remove keyForRemoval="guestPlaceOrder"/> <!-- Login as Customer --> <actionGroup ref="CustomerLoginOnStorefront" before="goToProductPage" stepKey="customerLogin"> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml index 9999708af1dee..354974bf1ef53 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml @@ -23,14 +23,14 @@ <createData entity="ApiSimpleProduct" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <magentoCLI command="config:set payment/checkmo/allowspecific 1" stepKey="setAllowSpecificCountiesValue" /> - <magentoCLI command="config:set payment/checkmo/specificcountry GB" stepKey="setSpecificCountryValue" /> + <magentoCLI command="config:set payment/checkmo/allowspecific" arguments="1" stepKey="setAllowSpecificCountiesValue" /> + <magentoCLI command="config:set payment/checkmo/specificcountry" arguments="GB" stepKey="setSpecificCountryValue" /> </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <magentoCLI command="config:set payment/checkmo/allowspecific 0" stepKey="unsetAllowSpecificCountiesValue"/> - <magentoCLI command="config:set payment/checkmo/specificcountry ''" stepKey="unsetSpecificCountryValue" /> + <magentoCLI command="config:set payment/checkmo/allowspecific" arguments="0" stepKey="unsetAllowSpecificCountiesValue"/> + <magentoCLI command="config:set payment/checkmo/specificcountry" arguments="''" stepKey="unsetSpecificCountryValue" /> </after> <!-- Add product to cart --> @@ -62,7 +62,7 @@ <argument name="shippingMethod" value="Flat Rate" type="string"/> </actionGroup> <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyOrderPayment" /> - <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="questPlaceOrder"> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="guestPlaceOrder"> <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage" /> <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage" /> </actionGroup> From f47ed29abb4634c956be4694a83fae5e475bf1f5 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Fri, 4 Jan 2019 12:51:09 +0200 Subject: [PATCH 0169/1295] MAGETWO-97393: Impossible to enable shared catalog through cli command --- .../Command/ConfigSet/DefaultProcessor.php | 32 ++- app/code/Magento/Config/Model/Config.php | 117 +++++++-- .../Backend/Currency/AbstractCurrency.php | 13 +- .../ConfigSet/DefaultProcessorTest.php | 64 ++--- .../Config/Test/Unit/Model/ConfigTest.php | 227 +++++++++++------- .../Config/Structure/Element/FieldPlugin.php | 16 +- .../Structure/Element/FieldPluginTest.php | 24 +- app/code/Magento/Store/etc/di.xml | 1 + app/etc/di.xml | 7 + .../Console/Command/ConfigSetCommandTest.php | 7 +- .../Magento/Framework/App/ScopeDefault.php | 4 +- 11 files changed, 304 insertions(+), 208 deletions(-) diff --git a/app/code/Magento/Config/Console/Command/ConfigSet/DefaultProcessor.php b/app/code/Magento/Config/Console/Command/ConfigSet/DefaultProcessor.php index d7d513bfad423..86ae1f96749df 100644 --- a/app/code/Magento/Config/Console/Command/ConfigSet/DefaultProcessor.php +++ b/app/code/Magento/Config/Console/Command/ConfigSet/DefaultProcessor.php @@ -7,17 +7,19 @@ use Magento\Config\App\Config\Type\System; use Magento\Config\Console\Command\ConfigSetCommand; +use Magento\Config\Model\Config\Factory as ConfigFactory; use Magento\Framework\App\Config\ConfigPathResolver; use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\CouldNotSaveException; use Magento\Config\Model\PreparedValueFactory; -use Magento\Framework\App\Config\Value; /** * Processes default flow of config:set command. + * * This processor saves the value of configuration into database. * - * {@inheritdoc} + * @inheritdoc * @api * @since 100.2.0 */ @@ -44,26 +46,36 @@ class DefaultProcessor implements ConfigSetProcessorInterface */ private $preparedValueFactory; + /** + * @var ConfigFactory + */ + private $configFactory; + /** * @param PreparedValueFactory $preparedValueFactory The factory for prepared value * @param DeploymentConfig $deploymentConfig The deployment configuration reader * @param ConfigPathResolver $configPathResolver The resolver for configuration paths according to source type + * @param ConfigFactory|null $configFactory */ public function __construct( PreparedValueFactory $preparedValueFactory, DeploymentConfig $deploymentConfig, - ConfigPathResolver $configPathResolver + ConfigPathResolver $configPathResolver, + ConfigFactory $configFactory = null ) { $this->preparedValueFactory = $preparedValueFactory; $this->deploymentConfig = $deploymentConfig; $this->configPathResolver = $configPathResolver; + + $this->configFactory = $configFactory ?? ObjectManager::getInstance()->get(ConfigFactory::class); } /** * Processes database flow of config:set command. + * * Requires installed application. * - * {@inheritdoc} + * @inheritdoc * @since 100.2.0 */ public function process($path, $value, $scope, $scopeCode) @@ -78,12 +90,12 @@ public function process($path, $value, $scope, $scopeCode) } try { - /** @var Value $backendModel */ - $backendModel = $this->preparedValueFactory->create($path, $value, $scope, $scopeCode); - if ($backendModel instanceof Value) { - $resourceModel = $backendModel->getResource(); - $resourceModel->save($backendModel); - } + $config = $this->configFactory->create([ + 'scope' => $scope, + 'scope_code' => $scopeCode, + ]); + $config->setDataByPath($path, $value); + $config->save(); } catch (\Exception $exception) { throw new CouldNotSaveException(__('%1', $exception->getMessage()), $exception); } diff --git a/app/code/Magento/Config/Model/Config.php b/app/code/Magento/Config/Model/Config.php index 0472c5daa276f..b1074e92cc949 100644 --- a/app/code/Magento/Config/Model/Config.php +++ b/app/code/Magento/Config/Model/Config.php @@ -9,15 +9,32 @@ use Magento\Config\Model\Config\Structure\Element\Group; use Magento\Config\Model\Config\Structure\Element\Field; use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\ScopeInterface; +use Magento\Framework\App\ScopeResolverPool; +use Magento\Store\Model\ScopeInterface as StoreScopeInterface; +use Magento\Store\Model\ScopeTypeNormalizer; /** * Backend config model + * * Used to save configuration * * @author Magento Core Team <core@magentocommerce.com> * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @api * @since 100.0.2 + * @method string getSection() + * @method void setSection(string $section) + * @method string getWebsite() + * @method void setWebsite(string $website) + * @method string getStore() + * @method void setStore(string $store) + * @method string getScope() + * @method void setScope(string $scope) + * @method int getScopeId() + * @method void setScopeId(int $scopeId) + * @method string getScopeCode() + * @method void setScopeCode(string $scopeCode) */ class Config extends \Magento\Framework\DataObject { @@ -87,6 +104,16 @@ class Config extends \Magento\Framework\DataObject */ private $settingChecker; + /** + * @var ScopeResolverPool + */ + private $scopeResolverPool; + + /** + * @var ScopeTypeNormalizer + */ + private $scopeTypeNormalizer; + /** * @param \Magento\Framework\App\Config\ReinitableConfigInterface $config * @param \Magento\Framework\Event\ManagerInterface $eventManager @@ -97,6 +124,9 @@ class Config extends \Magento\Framework\DataObject * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param Config\Reader\Source\Deployed\SettingChecker|null $settingChecker * @param array $data + * @param ScopeResolverPool|null $scopeResolverPool + * @param ScopeTypeNormalizer|null $scopeTypeNormalizer + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\Framework\App\Config\ReinitableConfigInterface $config, @@ -107,7 +137,9 @@ public function __construct( \Magento\Framework\App\Config\ValueFactory $configValueFactory, \Magento\Store\Model\StoreManagerInterface $storeManager, SettingChecker $settingChecker = null, - array $data = [] + array $data = [], + ScopeResolverPool $scopeResolverPool = null, + ScopeTypeNormalizer $scopeTypeNormalizer = null ) { parent::__construct($data); $this->_eventManager = $eventManager; @@ -117,11 +149,17 @@ public function __construct( $this->_configLoader = $configLoader; $this->_configValueFactory = $configValueFactory; $this->_storeManager = $storeManager; - $this->settingChecker = $settingChecker ?: ObjectManager::getInstance()->get(SettingChecker::class); + $this->settingChecker = $settingChecker + ?? ObjectManager::getInstance()->get(SettingChecker::class); + $this->scopeResolverPool = $scopeResolverPool + ?? ObjectManager::getInstance()->get(ScopeResolverPool::class); + $this->scopeTypeNormalizer = $scopeTypeNormalizer + ?? ObjectManager::getInstance()->get(ScopeTypeNormalizer::class); } /** * Save config section + * * Require set: section, website, store and groups * * @throws \Exception @@ -504,8 +542,8 @@ public function setDataByPath($path, $value) } /** - * Get scope name and scopeId - * @todo refactor to scope resolver + * Set scope data + * * @return void */ private function initScope() @@ -513,31 +551,66 @@ private function initScope() if ($this->getSection() === null) { $this->setSection(''); } + + $scope = $this->retrieveScope(); + $this->setScope($this->scopeTypeNormalizer->normalize($scope->getScopeType())); + $this->setScopeCode($scope->getCode()); + $this->setScopeId($scope->getId()); + if ($this->getWebsite() === null) { - $this->setWebsite(''); + $this->setWebsite(StoreScopeInterface::SCOPE_WEBSITES === $this->getScope() ? $scope->getId() : ''); } if ($this->getStore() === null) { - $this->setStore(''); + $this->setStore(StoreScopeInterface::SCOPE_STORES === $this->getScope() ? $scope->getId() : ''); } + } - if ($this->getStore()) { - $scope = 'stores'; - $store = $this->_storeManager->getStore($this->getStore()); - $scopeId = (int)$store->getId(); - $scopeCode = $store->getCode(); - } elseif ($this->getWebsite()) { - $scope = 'websites'; - $website = $this->_storeManager->getWebsite($this->getWebsite()); - $scopeId = (int)$website->getId(); - $scopeCode = $website->getCode(); + /** + * Retrieve scope from initial data + * + * @return ScopeInterface + */ + private function retrieveScope(): ScopeInterface + { + $scopeType = $this->getScope(); + if (!$scopeType) { + switch (true) { + case $this->getStore(): + $scopeType = StoreScopeInterface::SCOPE_STORES; + $scopeIdentifier = $this->getStore(); + break; + case $this->getWebsite(): + $scopeType = StoreScopeInterface::SCOPE_WEBSITES; + $scopeIdentifier = $this->getWebsite(); + break; + default: + $scopeType = ScopeInterface::SCOPE_DEFAULT; + $scopeIdentifier = null; + break; + } } else { - $scope = 'default'; - $scopeId = 0; - $scopeCode = ''; + switch (true) { + case $this->getScopeId() !== null: + $scopeIdentifier = $this->getScopeId(); + break; + case $this->getScopeCode() !== null: + $scopeIdentifier = $this->getScopeCode(); + break; + case $this->getStore() !== null: + $scopeIdentifier = $this->getStore(); + break; + case $this->getWebsite() !== null: + $scopeIdentifier = $this->getWebsite(); + break; + default: + $scopeIdentifier = null; + break; + } } - $this->setScope($scope); - $this->setScopeId($scopeId); - $this->setScopeCode($scopeCode); + $scope = $this->scopeResolverPool->get($scopeType) + ->getScope($scopeIdentifier); + + return $scope; } /** 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 4ae66bfd9692b..25303093ace5d 100644 --- a/app/code/Magento/Config/Model/Config/Backend/Currency/AbstractCurrency.php +++ b/app/code/Magento/Config/Model/Config/Backend/Currency/AbstractCurrency.php @@ -14,6 +14,8 @@ namespace Magento\Config\Model\Config\Backend\Currency; /** + * Base currency class + * * @api * @since 100.0.2 */ @@ -26,18 +28,19 @@ abstract class AbstractCurrency extends \Magento\Framework\App\Config\Value */ protected function _getAllowedCurrencies() { - if (!$this->isFormData() || $this->getData('groups/options/fields/allow/inherit')) { - return explode( + $allowValue = $this->getData('groups/options/fields/allow/value'); + $allowedCurrencies = $allowValue === null || $this->getData('groups/options/fields/allow/inherit') + ? explode( ',', (string)$this->_config->getValue( \Magento\Directory\Model\Currency::XML_PATH_CURRENCY_ALLOW, $this->getScope(), $this->getScopeId() ) - ); - } + ) + : (array) $allowValue; - return (array)$this->getData('groups/options/fields/allow/value'); + return $allowedCurrencies; } /** diff --git a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/DefaultProcessorTest.php b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/DefaultProcessorTest.php index 984e0fe842687..edb76c067bf35 100644 --- a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/DefaultProcessorTest.php +++ b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/DefaultProcessorTest.php @@ -7,13 +7,14 @@ use Magento\Config\App\Config\Type\System; use Magento\Config\Console\Command\ConfigSet\DefaultProcessor; +use Magento\Config\Model\Config; +use Magento\Config\Model\Config\Factory as ConfigFactory; use Magento\Framework\App\Config\ConfigPathResolver; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\DeploymentConfig; use Magento\Store\Model\ScopeInterface; use Magento\Config\Model\PreparedValueFactory; use Magento\Framework\App\Config\Value; -use Magento\Framework\App\Config\ValueInterface; use Magento\Framework\Model\ResourceModel\Db\AbstractDb; use PHPUnit_Framework_MockObject_MockObject as Mock; @@ -55,17 +56,18 @@ class DefaultProcessorTest extends \PHPUnit\Framework\TestCase */ private $resourceModelMock; + /** + * @var ConfigFactory|Mock + */ + private $configFactory; + /** * @inheritdoc */ protected function setUp() { - $this->deploymentConfigMock = $this->getMockBuilder(DeploymentConfig::class) - ->disableOriginalConstructor() - ->getMock(); - $this->configPathResolverMock = $this->getMockBuilder(ConfigPathResolver::class) - ->disableOriginalConstructor() - ->getMock(); + $this->deploymentConfigMock = $this->createMock(DeploymentConfig::class); + $this->configPathResolverMock = $this->createMock(ConfigPathResolver::class); $this->resourceModelMock = $this->getMockBuilder(AbstractDb::class) ->disableOriginalConstructor() ->setMethods(['save']) @@ -74,14 +76,14 @@ protected function setUp() ->disableOriginalConstructor() ->setMethods(['getResource']) ->getMock(); - $this->preparedValueFactoryMock = $this->getMockBuilder(PreparedValueFactory::class) - ->disableOriginalConstructor() - ->getMock(); + $this->preparedValueFactoryMock = $this->createMock(PreparedValueFactory::class); + $this->configFactory = $this->createMock(ConfigFactory::class); $this->model = new DefaultProcessor( $this->preparedValueFactoryMock, $this->deploymentConfigMock, - $this->configPathResolverMock + $this->configPathResolverMock, + $this->configFactory ); } @@ -98,15 +100,16 @@ public function testProcess($path, $value, $scope, $scopeCode) { $this->configMockForProcessTest($path, $scope, $scopeCode); - $this->preparedValueFactoryMock->expects($this->once()) + $config = $this->createMock(Config::class); + $this->configFactory->expects($this->once()) ->method('create') - ->willReturn($this->valueMock); - $this->valueMock->expects($this->once()) - ->method('getResource') - ->willReturn($this->resourceModelMock); - $this->resourceModelMock->expects($this->once()) + ->with(['scope' => $scope, 'scope_code' => $scopeCode]) + ->willReturn($config); + $config->expects($this->once()) + ->method('setDataByPath') + ->with($path, $value); + $config->expects($this->once()) ->method('save') - ->with($this->valueMock) ->willReturnSelf(); $this->model->process($path, $value, $scope, $scopeCode); @@ -124,28 +127,6 @@ public function processDataProvider() ]; } - public function testProcessWithWrongValueInstance() - { - $path = 'test/test/test'; - $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT; - $scopeCode = null; - $value = 'value'; - $valueInterfaceMock = $this->getMockBuilder(ValueInterface::class) - ->getMockForAbstractClass(); - - $this->configMockForProcessTest($path, $scope, $scopeCode); - - $this->preparedValueFactoryMock->expects($this->once()) - ->method('create') - ->willReturn($valueInterfaceMock); - $this->valueMock->expects($this->never()) - ->method('getResource'); - $this->resourceModelMock->expects($this->never()) - ->method('save'); - - $this->model->process($path, $value, $scope, $scopeCode); - } - /** * @param string $path * @param string $scope @@ -185,6 +166,9 @@ public function testProcessLockedValue() ->method('resolve') ->willReturn('system/default/test/test/test'); + $this->configFactory->expects($this->never()) + ->method('create'); + $this->model->process($path, $value, ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null); } } diff --git a/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php b/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php index fcc1ff8b9c70c..73698da618525 100644 --- a/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php @@ -5,6 +5,8 @@ */ namespace Magento\Config\Test\Unit\Model; +use PHPUnit\Framework\MockObject\MockObject; + /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -13,130 +15,158 @@ class ConfigTest extends \PHPUnit\Framework\TestCase /** * @var \Magento\Config\Model\Config */ - protected $_model; + private $model; + + /** + * @var \Magento\Framework\Event\ManagerInterface|MockObject + */ + private $eventManagerMock; + + /** + * @var \Magento\Config\Model\Config\Structure\Reader|MockObject + */ + private $structureReaderMock; + + /** + * @var \Magento\Framework\DB\TransactionFactory|MockObject + */ + private $transFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Config\ReinitableConfigInterface|MockObject */ - protected $_eventManagerMock; + private $appConfigMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Config\Model\Config\Loader|MockObject */ - protected $_structureReaderMock; + private $configLoaderMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\Config\ValueFactory|MockObject */ - protected $_transFactoryMock; + private $dataFactoryMock; /** - * @var \Magento\Framework\App\Config\ReinitableConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Store\Model\StoreManagerInterface|MockObject */ - protected $_appConfigMock; + private $storeManager; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Config\Model\Config\Structure|MockObject */ - protected $_applicationMock; + private $configStructure; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Config\Model\Config\Reader\Source\Deployed\SettingChecker|MockObject */ - protected $_configLoaderMock; + private $settingsChecker; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\App\ScopeResolverPool|MockObject */ - protected $_dataFactoryMock; + private $scopeResolverPool; /** - * @var \Magento\Store\Model\StoreManagerInterface + * @var \Magento\Framework\App\ScopeResolverInterface|MockObject */ - protected $_storeManager; + private $scopeResolver; /** - * @var \Magento\Config\Model\Config\Structure + * @var \Magento\Framework\App\ScopeInterface|MockObject */ - protected $_configStructure; + private $scope; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Store\Model\ScopeTypeNormalizer|MockObject */ - private $_settingsChecker; + private $scopeTypeNormalizer; protected function setUp() { - $this->_eventManagerMock = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); - $this->_structureReaderMock = $this->createPartialMock( + $this->eventManagerMock = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); + $this->structureReaderMock = $this->createPartialMock( \Magento\Config\Model\Config\Structure\Reader::class, ['getConfiguration'] ); - $this->_configStructure = $this->createMock(\Magento\Config\Model\Config\Structure::class); + $this->configStructure = $this->createMock(\Magento\Config\Model\Config\Structure::class); - $this->_structureReaderMock->expects( + $this->structureReaderMock->expects( $this->any() )->method( 'getConfiguration' )->will( - $this->returnValue($this->_configStructure) + $this->returnValue($this->configStructure) ); - $this->_transFactoryMock = $this->createPartialMock( + $this->transFactoryMock = $this->createPartialMock( \Magento\Framework\DB\TransactionFactory::class, ['create', 'addObject'] ); - $this->_appConfigMock = $this->createMock(\Magento\Framework\App\Config\ReinitableConfigInterface::class); - $this->_configLoaderMock = $this->createPartialMock( + $this->appConfigMock = $this->createMock(\Magento\Framework\App\Config\ReinitableConfigInterface::class); + $this->configLoaderMock = $this->createPartialMock( \Magento\Config\Model\Config\Loader::class, ['getConfigByPath'] ); - $this->_dataFactoryMock = $this->createMock(\Magento\Framework\App\Config\ValueFactory::class); + $this->dataFactoryMock = $this->createMock(\Magento\Framework\App\Config\ValueFactory::class); - $this->_storeManager = $this->getMockForAbstractClass(\Magento\Store\Model\StoreManagerInterface::class); + $this->storeManager = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); - $this->_settingsChecker = $this + $this->settingsChecker = $this ->createMock(\Magento\Config\Model\Config\Reader\Source\Deployed\SettingChecker::class); - $this->_model = new \Magento\Config\Model\Config( - $this->_appConfigMock, - $this->_eventManagerMock, - $this->_configStructure, - $this->_transFactoryMock, - $this->_configLoaderMock, - $this->_dataFactoryMock, - $this->_storeManager, - $this->_settingsChecker + $this->scopeResolverPool = $this->createMock(\Magento\Framework\App\ScopeResolverPool::class); + $this->scopeResolver = $this->createMock(\Magento\Framework\App\ScopeResolverInterface::class); + $this->scopeResolverPool->method('get') + ->willReturn($this->scopeResolver); + $this->scope = $this->createMock(\Magento\Framework\App\ScopeInterface::class); + $this->scopeResolver->method('getScope') + ->willReturn($this->scope); + + $this->scopeTypeNormalizer = $this->createMock(\Magento\Store\Model\ScopeTypeNormalizer::class); + + $this->model = new \Magento\Config\Model\Config( + $this->appConfigMock, + $this->eventManagerMock, + $this->configStructure, + $this->transFactoryMock, + $this->configLoaderMock, + $this->dataFactoryMock, + $this->storeManager, + $this->settingsChecker, + [], + $this->scopeResolverPool, + $this->scopeTypeNormalizer ); } public function testSaveDoesNotDoAnythingIfGroupsAreNotPassed() { - $this->_configLoaderMock->expects($this->never())->method('getConfigByPath'); - $this->_model->save(); + $this->configLoaderMock->expects($this->never())->method('getConfigByPath'); + $this->model->save(); } public function testSaveEmptiesNonSetArguments() { - $this->_structureReaderMock->expects($this->never())->method('getConfiguration'); - $this->assertNull($this->_model->getSection()); - $this->assertNull($this->_model->getWebsite()); - $this->assertNull($this->_model->getStore()); - $this->_model->save(); - $this->assertSame('', $this->_model->getSection()); - $this->assertSame('', $this->_model->getWebsite()); - $this->assertSame('', $this->_model->getStore()); + $this->structureReaderMock->expects($this->never())->method('getConfiguration'); + $this->assertNull($this->model->getSection()); + $this->assertNull($this->model->getWebsite()); + $this->assertNull($this->model->getStore()); + $this->model->save(); + $this->assertSame('', $this->model->getSection()); + $this->assertSame('', $this->model->getWebsite()); + $this->assertSame('', $this->model->getStore()); } public function testSaveToCheckAdminSystemConfigChangedSectionEvent() { $transactionMock = $this->createMock(\Magento\Framework\DB\Transaction::class); - $this->_transFactoryMock->expects($this->any())->method('create')->will($this->returnValue($transactionMock)); + $this->transFactoryMock->expects($this->any())->method('create')->will($this->returnValue($transactionMock)); - $this->_configLoaderMock->expects($this->any())->method('getConfigByPath')->will($this->returnValue([])); + $this->configLoaderMock->expects($this->any())->method('getConfigByPath')->will($this->returnValue([])); - $this->_eventManagerMock->expects( + $this->eventManagerMock->expects( $this->at(0) )->method( 'dispatch' @@ -145,7 +175,7 @@ public function testSaveToCheckAdminSystemConfigChangedSectionEvent() $this->arrayHasKey('website') ); - $this->_eventManagerMock->expects( + $this->eventManagerMock->expects( $this->at(0) )->method( 'dispatch' @@ -154,20 +184,20 @@ public function testSaveToCheckAdminSystemConfigChangedSectionEvent() $this->arrayHasKey('store') ); - $this->_model->setGroups(['1' => ['data']]); - $this->_model->save(); + $this->model->setGroups(['1' => ['data']]); + $this->model->save(); } public function testDoNotSaveReadOnlyFields() { $transactionMock = $this->createMock(\Magento\Framework\DB\Transaction::class); - $this->_transFactoryMock->expects($this->any())->method('create')->will($this->returnValue($transactionMock)); + $this->transFactoryMock->expects($this->any())->method('create')->will($this->returnValue($transactionMock)); - $this->_settingsChecker->expects($this->any())->method('isReadOnly')->will($this->returnValue(true)); - $this->_configLoaderMock->expects($this->any())->method('getConfigByPath')->will($this->returnValue([])); + $this->settingsChecker->expects($this->any())->method('isReadOnly')->will($this->returnValue(true)); + $this->configLoaderMock->expects($this->any())->method('getConfigByPath')->will($this->returnValue([])); - $this->_model->setGroups(['1' => ['fields' => ['key' => ['data']]]]); - $this->_model->setSection('section'); + $this->model->setGroups(['1' => ['fields' => ['key' => ['data']]]]); + $this->model->setSection('section'); $group = $this->createMock(\Magento\Config\Model\Config\Structure\Element\Group::class); $group->method('getPath')->willReturn('section/1'); @@ -176,15 +206,15 @@ public function testDoNotSaveReadOnlyFields() $field->method('getGroupPath')->willReturn('section/1'); $field->method('getId')->willReturn('key'); - $this->_configStructure->expects($this->at(0)) + $this->configStructure->expects($this->at(0)) ->method('getElement') ->with('section/1') ->will($this->returnValue($group)); - $this->_configStructure->expects($this->at(1)) + $this->configStructure->expects($this->at(1)) ->method('getElement') ->with('section/1') ->will($this->returnValue($group)); - $this->_configStructure->expects($this->at(2)) + $this->configStructure->expects($this->at(2)) ->method('getElement') ->with('section/1/key') ->will($this->returnValue($field)); @@ -193,28 +223,28 @@ public function testDoNotSaveReadOnlyFields() \Magento\Framework\App\Config\Value::class, ['addData'] ); - $this->_dataFactoryMock->expects($this->any())->method('create')->will($this->returnValue($backendModel)); + $this->dataFactoryMock->expects($this->any())->method('create')->will($this->returnValue($backendModel)); - $this->_transFactoryMock->expects($this->never())->method('addObject'); + $this->transFactoryMock->expects($this->never())->method('addObject'); $backendModel->expects($this->never())->method('addData'); - $this->_model->save(); + $this->model->save(); } public function testSaveToCheckScopeDataSet() { $transactionMock = $this->createMock(\Magento\Framework\DB\Transaction::class); - $this->_transFactoryMock->expects($this->any())->method('create')->will($this->returnValue($transactionMock)); + $this->transFactoryMock->expects($this->any())->method('create')->will($this->returnValue($transactionMock)); - $this->_configLoaderMock->expects($this->any())->method('getConfigByPath')->will($this->returnValue([])); + $this->configLoaderMock->expects($this->any())->method('getConfigByPath')->will($this->returnValue([])); - $this->_eventManagerMock->expects($this->at(0)) + $this->eventManagerMock->expects($this->at(0)) ->method('dispatch') ->with( $this->equalTo('admin_system_config_changed_section_section'), $this->arrayHasKey('website') ); - $this->_eventManagerMock->expects($this->at(0)) + $this->eventManagerMock->expects($this->at(0)) ->method('dispatch') ->with( $this->equalTo('admin_system_config_changed_section_section'), @@ -228,36 +258,51 @@ public function testSaveToCheckScopeDataSet() $field->method('getGroupPath')->willReturn('section/1'); $field->method('getId')->willReturn('key'); - $this->_configStructure->expects($this->at(0)) + $this->configStructure->expects($this->at(0)) ->method('getElement') ->with('section/1') ->will($this->returnValue($group)); - $this->_configStructure->expects($this->at(1)) + $this->configStructure->expects($this->at(1)) ->method('getElement') ->with('section/1') ->will($this->returnValue($group)); - $this->_configStructure->expects($this->at(2)) + $this->configStructure->expects($this->at(2)) ->method('getElement') ->with('section/1/key') ->will($this->returnValue($field)); - $this->_configStructure->expects($this->at(3)) + $this->configStructure->expects($this->at(3)) ->method('getElement') ->with('section/1') ->will($this->returnValue($group)); - $this->_configStructure->expects($this->at(4)) + $this->configStructure->expects($this->at(4)) ->method('getElement') ->with('section/1/key') ->will($this->returnValue($field)); + $this->scopeResolver->expects($this->atLeastOnce()) + ->method('getScope') + ->with('1') + ->willReturn($this->scope); + $this->scope->expects($this->atLeastOnce()) + ->method('getScopeType') + ->willReturn('website'); + $this->scope->expects($this->atLeastOnce()) + ->method('getId') + ->willReturn(1); + $this->scope->expects($this->atLeastOnce()) + ->method('getCode') + ->willReturn('website_code'); + $this->scopeTypeNormalizer->expects($this->atLeastOnce()) + ->method('normalize') + ->with('website') + ->willReturn('websites'); $website = $this->createMock(\Magento\Store\Model\Website::class); - $website->expects($this->any())->method('getCode')->will($this->returnValue('website_code')); - $this->_storeManager->expects($this->any())->method('getWebsite')->will($this->returnValue($website)); - $this->_storeManager->expects($this->any())->method('getWebsites')->will($this->returnValue([$website])); - $this->_storeManager->expects($this->any())->method('isSingleStoreMode')->will($this->returnValue(true)); + $this->storeManager->expects($this->any())->method('getWebsites')->will($this->returnValue([$website])); + $this->storeManager->expects($this->any())->method('isSingleStoreMode')->will($this->returnValue(true)); - $this->_model->setWebsite('website'); - $this->_model->setSection('section'); - $this->_model->setGroups(['1' => ['fields' => ['key' => ['data']]]]); + $this->model->setWebsite('1'); + $this->model->setSection('section'); + $this->model->setGroups(['1' => ['fields' => ['key' => ['data']]]]); $backendModel = $this->createPartialMock( \Magento\Framework\App\Config\Value::class, @@ -270,7 +315,7 @@ public function testSaveToCheckScopeDataSet() 'groups' => [1 => ['fields' => ['key' => ['data']]]], 'group_id' => null, 'scope' => 'websites', - 'scope_id' => 0, + 'scope_id' => 1, 'scope_code' => 'website_code', 'field_config' => null, 'fieldset_data' => ['key' => null], @@ -280,16 +325,16 @@ public function testSaveToCheckScopeDataSet() ->with('section/1/key') ->will($this->returnValue($backendModel)); - $this->_dataFactoryMock->expects($this->any())->method('create')->will($this->returnValue($backendModel)); + $this->dataFactoryMock->expects($this->any())->method('create')->will($this->returnValue($backendModel)); - $this->_model->save(); + $this->model->save(); } public function testSetDataByPath() { $value = 'value'; $path = '<section>/<group>/<field>'; - $this->_model->setDataByPath($path, $value); + $this->model->setDataByPath($path, $value); $expected = [ 'section' => '<section>', 'groups' => [ @@ -300,7 +345,7 @@ public function testSetDataByPath() ], ], ]; - $this->assertSame($expected, $this->_model->getData()); + $this->assertSame($expected, $this->model->getData()); } /** @@ -309,7 +354,7 @@ public function testSetDataByPath() */ public function testSetDataByPathEmpty() { - $this->_model->setDataByPath('', 'value'); + $this->model->setDataByPath('', 'value'); } /** @@ -324,7 +369,7 @@ public function testSetDataByPathWrongDepth($path, $expectedException) $this->expectException('\UnexpectedValueException'); $this->expectExceptionMessage($expectedException); $value = 'value'; - $this->_model->setDataByPath($path, $value); + $this->model->setDataByPath($path, $value); } /** diff --git a/app/code/Magento/Paypal/Model/Config/Structure/Element/FieldPlugin.php b/app/code/Magento/Paypal/Model/Config/Structure/Element/FieldPlugin.php index c2056aea08c00..5d5db0128b1eb 100644 --- a/app/code/Magento/Paypal/Model/Config/Structure/Element/FieldPlugin.php +++ b/app/code/Magento/Paypal/Model/Config/Structure/Element/FieldPlugin.php @@ -5,7 +5,6 @@ */ namespace Magento\Paypal\Model\Config\Structure\Element; -use Magento\Framework\App\RequestInterface; use Magento\Config\Model\Config\Structure\Element\Field as FieldConfigStructure; use Magento\Paypal\Model\Config\StructurePlugin as ConfigStructurePlugin; @@ -14,19 +13,6 @@ */ class FieldPlugin { - /** - * @var RequestInterface - */ - private $request; - - /** - * @param RequestInterface $request - */ - public function __construct(RequestInterface $request) - { - $this->request = $request; - } - /** * Get original configPath (not changed by PayPal configuration inheritance) * @@ -36,7 +22,7 @@ public function __construct(RequestInterface $request) */ public function afterGetConfigPath(FieldConfigStructure $subject, $result) { - if (!$result && $this->request->getParam('section') == 'payment') { + if (!$result && strpos($subject->getPath(), 'payment_') === 0) { $result = preg_replace( '@^(' . implode('|', ConfigStructurePlugin::getPaypalConfigCountries(true)) . ')/@', 'payment/', diff --git a/app/code/Magento/Paypal/Test/Unit/Model/Config/Structure/Element/FieldPluginTest.php b/app/code/Magento/Paypal/Test/Unit/Model/Config/Structure/Element/FieldPluginTest.php index 8615b91383aaa..f0dda20b71c76 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/Config/Structure/Element/FieldPluginTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/Config/Structure/Element/FieldPluginTest.php @@ -7,7 +7,6 @@ use Magento\Paypal\Model\Config\Structure\Element\FieldPlugin as FieldConfigStructurePlugin; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; -use Magento\Framework\App\RequestInterface; use Magento\Config\Model\Config\Structure\Element\Field as FieldConfigStructureMock; class FieldPluginTest extends \PHPUnit\Framework\TestCase @@ -22,11 +21,6 @@ class FieldPluginTest extends \PHPUnit\Framework\TestCase */ private $objectManagerHelper; - /** - * @var RequestInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $requestMock; - /** * @var FieldConfigStructureMock|\PHPUnit_Framework_MockObject_MockObject */ @@ -34,16 +28,13 @@ class FieldPluginTest extends \PHPUnit\Framework\TestCase protected function setUp() { - $this->requestMock = $this->getMockBuilder(RequestInterface::class) - ->getMockForAbstractClass(); $this->subjectMock = $this->getMockBuilder(FieldConfigStructureMock::class) ->disableOriginalConstructor() ->getMock(); $this->objectManagerHelper = new ObjectManagerHelper($this); $this->plugin = $this->objectManagerHelper->getObject( - FieldConfigStructurePlugin::class, - ['request' => $this->requestMock] + FieldConfigStructurePlugin::class ); } @@ -56,10 +47,9 @@ public function testAroundGetConfigPathHasResult() public function testAroundGetConfigPathNonPaymentSection() { - $this->requestMock->expects(static::once()) - ->method('getParam') - ->with('section') - ->willReturn('non-payment'); + $this->subjectMock->expects($this->once()) + ->method('getPath') + ->willReturn('non-payment/group/field'); $this->assertNull($this->plugin->afterGetConfigPath($this->subjectMock, null)); } @@ -72,11 +62,7 @@ public function testAroundGetConfigPathNonPaymentSection() */ public function testAroundGetConfigPath($subjectPath, $expectedConfigPath) { - $this->requestMock->expects(static::once()) - ->method('getParam') - ->with('section') - ->willReturn('payment'); - $this->subjectMock->expects(static::once()) + $this->subjectMock->expects($this->exactly(2)) ->method('getPath') ->willReturn($subjectPath); diff --git a/app/code/Magento/Store/etc/di.xml b/app/code/Magento/Store/etc/di.xml index 3d740bfee2093..83f891d06647d 100644 --- a/app/code/Magento/Store/etc/di.xml +++ b/app/code/Magento/Store/etc/di.xml @@ -232,6 +232,7 @@ <type name="Magento\Framework\App\ScopeResolverPool"> <arguments> <argument name="scopeResolvers" xsi:type="array"> + <item name="default" xsi:type="object">Magento\Framework\App\ScopeResolver</item> <item name="store" xsi:type="object">Magento\Store\Model\Resolver\Store</item> <item name="stores" xsi:type="object">Magento\Store\Model\Resolver\Store</item> <item name="group" xsi:type="object">Magento\Store\Model\Resolver\Group</item> diff --git a/app/etc/di.xml b/app/etc/di.xml index ad77ae3adc566..05fd34a178ded 100755 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -1356,4 +1356,11 @@ <argument name="scopeType" xsi:type="const">Magento\Framework\App\Config\ScopeConfigInterface::SCOPE_TYPE_DEFAULT</argument> </arguments> </type> + <type name="Magento\Framework\App\ScopeResolverPool"> + <arguments> + <argument name="scopeResolvers" xsi:type="array"> + <item name="default" xsi:type="object">Magento\Framework\App\ScopeResolver</item> + </argument> + </arguments> + </type> </config> diff --git a/dev/tests/integration/testsuite/Magento/Config/Console/Command/ConfigSetCommandTest.php b/dev/tests/integration/testsuite/Magento/Config/Console/Command/ConfigSetCommandTest.php index 3f333a36c9c93..a1998c6c89536 100644 --- a/dev/tests/integration/testsuite/Magento/Config/Console/Command/ConfigSetCommandTest.php +++ b/dev/tests/integration/testsuite/Magento/Config/Console/Command/ConfigSetCommandTest.php @@ -27,7 +27,7 @@ /** * Tests the different flows of config:set command. * - * {@inheritdoc} + * @inheritdoc * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @magentoDbIsolation enabled */ @@ -291,8 +291,7 @@ public function runExtendedDataProvider() * @param string $scope * @param $scopeCode string|null * @dataProvider configSetValidationErrorDataProvider - * - * @magentoDbIsolation enabled + * @magentoDbIsolation disabled */ public function testConfigSetValidationError( $path, @@ -306,6 +305,7 @@ public function testConfigSetValidationError( /** * Data provider for testConfigSetValidationError + * * @return array */ public function configSetValidationErrorDataProvider() @@ -398,7 +398,6 @@ public function testConfigSetCurrency() * Saving values with successful validation * * @dataProvider configSetValidDataProvider - * * @magentoDbIsolation enabled */ public function testConfigSetValid() diff --git a/lib/internal/Magento/Framework/App/ScopeDefault.php b/lib/internal/Magento/Framework/App/ScopeDefault.php index 2ea62387145bf..e62d19f9ffbb4 100644 --- a/lib/internal/Magento/Framework/App/ScopeDefault.php +++ b/lib/internal/Magento/Framework/App/ScopeDefault.php @@ -17,7 +17,7 @@ class ScopeDefault implements ScopeInterface */ public function getCode() { - return 'default'; + return ''; } /** @@ -27,7 +27,7 @@ public function getCode() */ public function getId() { - return 1; + return 0; } /** From b9ba4cb0628c550174372bfdf770be5bad5b6d08 Mon Sep 17 00:00:00 2001 From: serhii balko <serhii.balko@transoftgroup.com> Date: Fri, 4 Jan 2019 14:34:31 +0200 Subject: [PATCH 0170/1295] MAGETWO-97281: [Magento Cloud] Custom Price not shown in Quotes --- .../Adminhtml/Order/Create/Sidebar/Cart.php | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/Cart.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/Cart.php index 34d7a3f8ee25e..8179c0e8d282a 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/Cart.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/Cart.php @@ -3,8 +3,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sales\Block\Adminhtml\Order\Create\Sidebar; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Pricing\Price\FinalPrice; + /** * Adminhtml sales order create sidebar cart block * @@ -58,6 +63,17 @@ public function getItemCollection() return $collection; } + /** + * @inheritdoc + */ + public function getItemPrice(Product $product) + { + $customPrice = $this->getCartItemCustomPrice($product); + $price = $customPrice ?? $product->getPriceInfo()->getPrice(FinalPrice::PRICE_CODE)->getValue(); + + return $this->convertPrice($price); + } + /** * Retrieve display item qty availability * @@ -111,4 +127,23 @@ protected function _prepareLayout() return parent::_prepareLayout(); } + + /** + * Returns cart item custom price. + * + * @param Product $product + * @return float|null + */ + private function getCartItemCustomPrice(Product $product) + { + $items = $this->getItemCollection(); + foreach ($items as $item) { + $productItemId = $this->getProduct($item)->getId(); + if ($productItemId === $product->getId() && $item->getCustomPrice()) { + return (float)$item->getCustomPrice(); + } + } + + return null; + } } From 51fbc83e3031a0af3bbef92dfc8fcc45c550d4b3 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Fri, 4 Jan 2019 15:26:12 +0200 Subject: [PATCH 0171/1295] MAGETWO-94808: Item row total display incorrect value in API response --- .../Order/Item/Renderer/DefaultRenderer.php | 17 ++++ .../Model/Order/Webapi/ChangeOutputArray.php | 59 ++++++++++++ .../Item/Renderer/DefaultRendererTest.php | 29 +++--- .../Order/Webapi/ChangeOutputArrayTest.php | 92 +++++++++++++++++++ app/code/Magento/Sales/etc/webapi_rest/di.xml | 7 ++ app/code/Magento/Sales/etc/webapi_soap/di.xml | 7 ++ .../Sales/Service/V1/OrderItemGetTest.php | 33 +++++++ .../Sales/_files/order_with_discount.php | 73 +++++++++++++++ .../_files/order_with_discount_rollback.php | 8 ++ .../Reflection/DataObjectProcessor.php | 31 ++++++- 10 files changed, 343 insertions(+), 13 deletions(-) create mode 100644 app/code/Magento/Sales/Model/Order/Webapi/ChangeOutputArray.php create mode 100644 app/code/Magento/Sales/Test/Unit/Model/Order/Webapi/ChangeOutputArrayTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_discount.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_discount_rollback.php diff --git a/app/code/Magento/Sales/Block/Order/Item/Renderer/DefaultRenderer.php b/app/code/Magento/Sales/Block/Order/Item/Renderer/DefaultRenderer.php index 4c6a2b586cfc4..ad6e0082929ac 100644 --- a/app/code/Magento/Sales/Block/Order/Item/Renderer/DefaultRenderer.php +++ b/app/code/Magento/Sales/Block/Order/Item/Renderer/DefaultRenderer.php @@ -278,4 +278,21 @@ public function getItemRowTotalAfterDiscountHtml($item = null) $block->setItem($item); return $block->toHtml(); } + + /** + * Return the base total amount minus discount. + * + * @param OrderItem|InvoiceItem|CreditmemoItem $item + * @return float|null + */ + public function getBaseTotalAmount($item) + { + $baseTotalAmount = $item->getBaseRowTotal() + + $item->getBaseTaxAmount() + + $item->getBaseDiscountTaxCompensationAmount() + + $item->getBaseWeeeTaxAppliedAmount() + - $item->getBaseDiscountAmount(); + + return $baseTotalAmount; + } } diff --git a/app/code/Magento/Sales/Model/Order/Webapi/ChangeOutputArray.php b/app/code/Magento/Sales/Model/Order/Webapi/ChangeOutputArray.php new file mode 100644 index 0000000000000..a1015c102b3af --- /dev/null +++ b/app/code/Magento/Sales/Model/Order/Webapi/ChangeOutputArray.php @@ -0,0 +1,59 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Model\Order\Webapi; + +use Magento\Sales\Api\Data\OrderItemInterface; +use Magento\Sales\Block\Adminhtml\Items\Column\DefaultColumn; +use Magento\Sales\Block\Order\Item\Renderer\DefaultRenderer; + +/** + * Class for changing row total in response. + */ +class ChangeOutputArray +{ + /** + * @var DefaultColumn + */ + private $priceRenderer; + + /** + * @var DefaultRenderer + */ + private $defaultRenderer; + + /** + * @param DefaultColumn $priceRenderer + * @param DefaultRenderer $defaultRenderer + */ + public function __construct( + DefaultColumn $priceRenderer, + DefaultRenderer $defaultRenderer + ) { + $this->priceRenderer = $priceRenderer; + $this->defaultRenderer = $defaultRenderer; + } + + /** + * Changing row total for webapi order item response. + * + * @param OrderItemInterface $dataObject + * @param array $result + * @return array + */ + public function execute( + OrderItemInterface $dataObject, + array $result + ): array { + $result[OrderItemInterface::ROW_TOTAL] = $this->priceRenderer->getTotalAmount($dataObject); + $result[OrderItemInterface::BASE_ROW_TOTAL] = $this->priceRenderer->getBaseTotalAmount($dataObject); + $result[OrderItemInterface::ROW_TOTAL_INCL_TAX] = $this->defaultRenderer->getTotalAmount($dataObject); + $result[OrderItemInterface::BASE_ROW_TOTAL_INCL_TAX] = $this->defaultRenderer->getBaseTotalAmount($dataObject); + + return $result; + } +} diff --git a/app/code/Magento/Sales/Test/Unit/Block/Order/Item/Renderer/DefaultRendererTest.php b/app/code/Magento/Sales/Test/Unit/Block/Order/Item/Renderer/DefaultRendererTest.php index 9561baf6bd5f4..7863fc20b7396 100644 --- a/app/code/Magento/Sales/Test/Unit/Block/Order/Item/Renderer/DefaultRendererTest.php +++ b/app/code/Magento/Sales/Test/Unit/Block/Order/Item/Renderer/DefaultRendererTest.php @@ -60,18 +60,7 @@ protected function setUp() ->setMethods(['setItem', 'toHtml']) ->getMock(); - $itemMockMethods = [ - '__wakeup', - 'getRowTotal', - 'getTaxAmount', - 'getDiscountAmount', - 'getDiscountTaxCompensationAmount', - 'getWeeeTaxAppliedRowAmount', - ]; - $this->itemMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Item::class) - ->disableOriginalConstructor() - ->setMethods($itemMockMethods) - ->getMock(); + $this->itemMock = $this->createMock(\Magento\Sales\Model\Order\Item::class); } public function testGetItemPriceHtml() @@ -161,4 +150,20 @@ public function testGetTotalAmount() $this->assertEquals($expectedResult, $this->block->getTotalAmount($this->itemMock)); } + + /** + * @return void + */ + public function testGetBaseTotalAmount() + { + $expectedBaseTotalAmount = 10; + + $this->itemMock->expects($this->once())->method('getBaseRowTotal')->willReturn(8); + $this->itemMock->expects($this->once())->method('getBaseTaxAmount')->willReturn(1); + $this->itemMock->expects($this->once())->method('getBaseDiscountTaxCompensationAmount')->willReturn(1); + $this->itemMock->expects($this->once())->method('getBaseWeeeTaxAppliedAmount')->willReturn(1); + $this->itemMock->expects($this->once())->method('getBaseDiscountAmount')->willReturn(1); + + $this->assertEquals($expectedBaseTotalAmount, $this->block->getBaseTotalAmount($this->itemMock)); + } } diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Webapi/ChangeOutputArrayTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Webapi/ChangeOutputArrayTest.php new file mode 100644 index 0000000000000..83c40707c0079 --- /dev/null +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Webapi/ChangeOutputArrayTest.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Test\Unit\Model\Order\Webapi; + +use Magento\Sales\Api\Data\OrderItemInterface; +use Magento\Sales\Block\Adminhtml\Items\Column\DefaultColumn; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Sales\Block\Order\Item\Renderer\DefaultRenderer; +use Magento\Sales\Model\Order\Webapi\ChangeOutputArray; + +/** + * Test for Magento\Sales\Model\Order\Webapi\ChangeOutputArray class. + */ +class ChangeOutputArrayTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var DefaultColumn|\PHPUnit_Framework_MockObject_MockObject + */ + private $priceRendererMock; + + /** + * @var DefaultRenderer|\PHPUnit_Framework_MockObject_MockObject + */ + private $defaultRendererMock; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var ChangeOutputArray + */ + private $model; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = new ObjectManager($this); + + $this->priceRendererMock = $this->createMock(DefaultColumn::class); + $this->defaultRendererMock = $this->createMock(DefaultRenderer::class); + + $this->model = $this->objectManager->getObject( + ChangeOutputArray::class, + [ + 'priceRenderer' => $this->priceRendererMock, + 'defaultRenderer' => $this->defaultRendererMock, + ] + ); + } + + /** + * @return void + */ + public function testExecute() + { + $expectedResult = [ + OrderItemInterface::ROW_TOTAL => 10, + OrderItemInterface::BASE_ROW_TOTAL => 10, + OrderItemInterface::ROW_TOTAL_INCL_TAX => 11, + OrderItemInterface::BASE_ROW_TOTAL_INCL_TAX => 11, + ]; + $orderItemInterfaceMock = $this->createMock(OrderItemInterface::class); + + $this->priceRendererMock->expects($this->once()) + ->method('getTotalAmount') + ->with($orderItemInterfaceMock) + ->willReturn(10); + $this->priceRendererMock->expects($this->once()) + ->method('getBaseTotalAmount') + ->with($orderItemInterfaceMock) + ->willReturn(10); + $this->defaultRendererMock->expects($this->once()) + ->method('getTotalAmount') + ->with($orderItemInterfaceMock) + ->willReturn(11); + $this->defaultRendererMock->expects($this->once()) + ->method('getBaseTotalAmount') + ->with($orderItemInterfaceMock) + ->willReturn(11); + + $this->assertEquals($expectedResult, $this->model->execute($orderItemInterfaceMock, [])); + } +} diff --git a/app/code/Magento/Sales/etc/webapi_rest/di.xml b/app/code/Magento/Sales/etc/webapi_rest/di.xml index 47fb3f188513c..6435445e0ef93 100644 --- a/app/code/Magento/Sales/etc/webapi_rest/di.xml +++ b/app/code/Magento/Sales/etc/webapi_rest/di.xml @@ -15,4 +15,11 @@ <type name="Magento\Sales\Api\ShipmentRepositoryInterface"> <plugin name="convert_blob_to_string" type="Magento\Sales\Plugin\ShippingLabelConverter" /> </type> + <type name="Magento\Framework\Reflection\DataObjectProcessor"> + <arguments> + <argument name="processors" xsi:type="array"> + <item name="\Magento\Sales\Model\Order\Item" xsi:type="object">Magento\Sales\Model\Order\Webapi\ChangeOutputArray\Proxy</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Sales/etc/webapi_soap/di.xml b/app/code/Magento/Sales/etc/webapi_soap/di.xml index 47fb3f188513c..6435445e0ef93 100644 --- a/app/code/Magento/Sales/etc/webapi_soap/di.xml +++ b/app/code/Magento/Sales/etc/webapi_soap/di.xml @@ -15,4 +15,11 @@ <type name="Magento\Sales\Api\ShipmentRepositoryInterface"> <plugin name="convert_blob_to_string" type="Magento\Sales\Plugin\ShippingLabelConverter" /> </type> + <type name="Magento\Framework\Reflection\DataObjectProcessor"> + <arguments> + <argument name="processors" xsi:type="array"> + <item name="\Magento\Sales\Model\Order\Item" xsi:type="object">Magento\Sales\Model\Order\Webapi\ChangeOutputArray\Proxy</item> + </argument> + </arguments> + </type> </config> diff --git a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderItemGetTest.php b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderItemGetTest.php index 3ab93f9aecb99..a527c69c7e92d 100644 --- a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderItemGetTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderItemGetTest.php @@ -77,4 +77,37 @@ protected function assertOrderItem(\Magento\Sales\Model\Order\Item $orderItem, a $this->assertEquals($orderItem->getBasePrice(), $response['base_price']); $this->assertEquals($orderItem->getRowTotal(), $response['row_total']); } + + /** + * @return void + * @magentoApiDataFixture Magento/Sales/_files/order_with_discount.php + */ + public function testGetOrderWithDiscount() + { + /** @var \Magento\Sales\Model\Order $order */ + $order = $this->objectManager->create(\Magento\Sales\Model\Order::class); + $order->loadByIncrementId(self::ORDER_INCREMENT_ID); + /** @var \Magento\Sales\Model\Order\Item $orderItem */ + $orderItem = current($order->getItems()); + + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH . '/' . $orderItem->getId(), + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'get', + ], + ]; + + $response = $this->_webApiCall($serviceInfo, ['id' => $orderItem->getId()]); + + $this->assertTrue(is_array($response)); + $this->assertEquals(8.00, $response['row_total']); + $this->assertEquals(8.00, $response['base_row_total']); + $this->assertEquals(9.00, $response['row_total_incl_tax']); + $this->assertEquals(9.00, $response['base_row_total_incl_tax']); + } } diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_discount.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_discount.php new file mode 100644 index 0000000000000..29a7aa4d90334 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_discount.php @@ -0,0 +1,73 @@ +<?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 as OrderAddress; +use Magento\Sales\Model\Order\Item as OrderItem; +use Magento\Sales\Model\Order\Payment; +use Magento\Store\Model\StoreManagerInterface; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; +/** @var \Magento\Catalog\Model\Product $product */ + +$addressData = include __DIR__ . '/address_data.php'; + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +$billingAddress = $objectManager->create(OrderAddress::class, ['data' => $addressData]); +$billingAddress->setAddressType('billing'); + +$shippingAddress = clone $billingAddress; +$shippingAddress->setId(null)->setAddressType('shipping'); + +/** @var Payment $payment */ +$payment = $objectManager->create(Payment::class); +$payment->setMethod('checkmo') + ->setAdditionalInformation('last_trans_id', '11122') + ->setAdditionalInformation( + 'metadata', + [ + 'type' => 'free', + 'fraudulent' => false, + ] + ); + +/** @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') + ->setDiscountAmount(2) + ->setBaseRowTotal($product->getPrice()) + ->setBaseDiscountAmount(2) + ->setTaxAmount(1) + ->setBaseTaxAmount(1); + +/** @var Order $order */ +$order = $objectManager->create(Order::class); +$order->setIncrementId('100000001') + ->setState(Order::STATE_PROCESSING) + ->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_PROCESSING)) + ->setSubtotal(100) + ->setGrandTotal(100) + ->setBaseSubtotal(100) + ->setBaseGrandTotal(100) + ->setCustomerIsGuest(true) + ->setCustomerEmail('customer@null.com') + ->setBillingAddress($billingAddress) + ->setShippingAddress($shippingAddress) + ->setStoreId($objectManager->get(StoreManagerInterface::class)->getStore()->getId()) + ->addItem($orderItem) + ->setPayment($payment); + +/** @var OrderRepositoryInterface $orderRepository */ +$orderRepository = $objectManager->create(OrderRepositoryInterface::class); +$orderRepository->save($order); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_discount_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_discount_rollback.php new file mode 100644 index 0000000000000..1fb4b4636ab29 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_discount_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require 'default_rollback.php'; diff --git a/lib/internal/Magento/Framework/Reflection/DataObjectProcessor.php b/lib/internal/Magento/Framework/Reflection/DataObjectProcessor.php index 6311003bd2ad5..2f3caf08c534e 100644 --- a/lib/internal/Magento/Framework/Reflection/DataObjectProcessor.php +++ b/lib/internal/Magento/Framework/Reflection/DataObjectProcessor.php @@ -40,25 +40,33 @@ class DataObjectProcessor */ private $customAttributesProcessor; + /** + * @var array + */ + private $processors; + /** * @param MethodsMap $methodsMapProcessor * @param TypeCaster $typeCaster * @param FieldNamer $fieldNamer * @param CustomAttributesProcessor $customAttributesProcessor * @param ExtensionAttributesProcessor $extensionAttributesProcessor + * @param array $processors */ public function __construct( MethodsMap $methodsMapProcessor, TypeCaster $typeCaster, FieldNamer $fieldNamer, CustomAttributesProcessor $customAttributesProcessor, - ExtensionAttributesProcessor $extensionAttributesProcessor + ExtensionAttributesProcessor $extensionAttributesProcessor, + array $processors = [] ) { $this->methodsMapProcessor = $methodsMapProcessor; $this->typeCaster = $typeCaster; $this->fieldNamer = $fieldNamer; $this->extensionAttributesProcessor = $extensionAttributesProcessor; $this->customAttributesProcessor = $customAttributesProcessor; + $this->processors = $processors; } /** @@ -121,6 +129,27 @@ public function buildOutputDataArray($dataObject, $dataObjectType) $outputData[$key] = $value; } + + $outputData = $this->changeOutputArray($dataObject, $outputData); + + return $outputData; + } + + /** + * Change output array if needed. + * + * @param mixed $dataObject + * @param array $outputData + * @return array + */ + private function changeOutputArray($dataObject, array $outputData): array + { + foreach ($this->processors as $dataObjectClassName => $processor) { + if ($dataObject instanceof $dataObjectClassName) { + $outputData = $processor->execute($dataObject, $outputData); + } + } + return $outputData; } } From b1a8513739a15a5eee4eb0b0e71fc8e0de0f0077 Mon Sep 17 00:00:00 2001 From: Dmytro Voskoboinikov <voskoboi@adobe.com> Date: Fri, 4 Jan 2019 10:25:38 -0600 Subject: [PATCH 0172/1295] MAGETWO-88608: Wrong behavior of static content deploy --- .../Test/Unit/Console/InputValidatorTest.php | 416 +++++++++--------- 1 file changed, 208 insertions(+), 208 deletions(-) diff --git a/app/code/Magento/Deploy/Test/Unit/Console/InputValidatorTest.php b/app/code/Magento/Deploy/Test/Unit/Console/InputValidatorTest.php index 9268fac9a1b30..2dc9eca7c5e14 100644 --- a/app/code/Magento/Deploy/Test/Unit/Console/InputValidatorTest.php +++ b/app/code/Magento/Deploy/Test/Unit/Console/InputValidatorTest.php @@ -1,208 +1,208 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Deploy\Test\Unit\Console; - -use Magento\Framework\Validator\Regex; -use Magento\Framework\Validator\RegexFactory; -use PHPUnit\Framework\TestCase; -use Magento\Deploy\Console\InputValidator; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; -use Magento\Deploy\Console\DeployStaticOptions as Options; -use Magento\Framework\Validator\Locale; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Input\InputDefinition; -use Symfony\Component\Console\Input\ArrayInput; -use InvalidArgumentException; -use Symfony\Component\Console\Input\InputArgument; - -/** - * Class InputValidatorTest - * @package Magento\Deploy\Test\Unit\Console - * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class InputValidatorTest extends TestCase -{ - /** - * @var ObjectManagerHelper - */ - protected $objectManagerHelper; - - /** - * @var InputValidator - */ - protected $inputValidator; - - /** - * @var Locale - */ - protected $localeValidator; - - /** - * @throws \Zend_Validate_Exception - */ - protected function setUp() - { - $this->objectManagerHelper = new ObjectManagerHelper($this); - - $regexFactoryMock = $this->getMockBuilder(RegexFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - - $regexObject = new Regex('/^[A-Za-z0-9_.]+$/'); - - $regexFactoryMock->expects($this->any())->method('create') - ->willReturn($regexObject); - - $localeObjectMock = $this->getMockBuilder(Locale::class)->setMethods(['isValid']) - ->disableOriginalConstructor() - ->getMock(); - - $localeObjectMock->expects($this->any())->method('isValid') - ->with('en_US') - ->will($this->returnValue(true)); - - $this->inputValidator = $this->objectManagerHelper->getObject( - InputValidator::class, - [ - 'localeValidator' => $localeObjectMock, - 'versionValidatorFactory' => $regexFactoryMock - ] - ); - } - - /** - * @throws \Zend_Validate_Exception - */ - public function testValidate() - { - $input = $this->getMockBuilder(ArrayInput::class) - ->disableOriginalConstructor() - ->setMethods(['getOption', 'getArgument']) - ->getMock(); - - $input->expects($this->atLeastOnce())->method('getArgument')->willReturn(['all']); - - $input->expects($this->atLeastOnce())->method('getOption') - ->willReturnMap( - [ - [Options::AREA, ['all']], - [Options::EXCLUDE_AREA, ['none']], - [Options::THEME, ['all']], - [Options::EXCLUDE_THEME, ['none']], - [Options::EXCLUDE_LANGUAGE, ['none']], - [Options::CONTENT_VERSION, '12345'] - ] - ); - - /** @noinspection PhpParamsInspection */ - $this->inputValidator->validate($input); - } - - /** - * @covers \Magento\Deploy\Console\InputValidator::checkAreasInput() - */ - public function testCheckAreasInputException() - { - $options = [ - new InputOption(Options::AREA, null, 4, null, ['test']), - new InputOption(Options::EXCLUDE_AREA, null, 4, null, ['test']) - ]; - - $inputDefinition = new InputDefinition($options); - - try { - $this->inputValidator->validate( - new ArrayInput([], $inputDefinition) - ); - } catch (\Exception $e) { - $this->assertContains('--area (-a) and --exclude-area cannot be used at the same time', $e->getMessage()); - $this->assertInstanceOf(InvalidArgumentException::class, $e); - } - } - - /** - * @covers \Magento\Deploy\Console\InputValidator::checkThemesInput() - */ - public function testCheckThemesInputException() - { - $options = [ - new InputOption(Options::AREA, null, 4, null, ['all']), - new InputOption(Options::EXCLUDE_AREA, null, 4, null, ['none']), - new InputOption(Options::THEME, null, 4, '', ['blank']), - new InputOption(Options::EXCLUDE_THEME, null, 4, '', ['luma']) - ]; - - $inputDefinition = new InputDefinition($options); - - try { - $this->inputValidator->validate( - new ArrayInput([], $inputDefinition) - ); - } catch (\Exception $e) { - $this->assertContains('--theme (-t) and --exclude-theme cannot be used at the same time', $e->getMessage()); - $this->assertInstanceOf(InvalidArgumentException::class, $e); - } - } - - public function testCheckLanguagesInputException() - { - $options = [ - new InputOption(Options::AREA, null, 4, null, ['all']), - new InputOption(Options::EXCLUDE_AREA, null, 4, null, ['none']), - new InputOption(Options::THEME, null, 4, '', ['all']), - new InputOption(Options::EXCLUDE_THEME, null, 4, '', ['none']), - new InputArgument(Options::LANGUAGES_ARGUMENT, null, 4, ['en_US']), - new InputOption(Options::EXCLUDE_LANGUAGE, null, 4, '', ['all']) - ]; - - $inputDefinition = new InputDefinition($options); - - try { - $this->inputValidator->validate( - new ArrayInput([], $inputDefinition) - ); - } catch (\Exception $e) { - $this->assertContains( - '--language (-l) and --exclude-language cannot be used at the same time', - $e->getMessage() - ); - - $this->assertInstanceOf(InvalidArgumentException::class, $e); - } - } - - public function testCheckVersionInputException() - { - $options = [ - new InputOption(Options::AREA, null, 4, null, ['all']), - new InputOption(Options::EXCLUDE_AREA, null, 4, null, ['none']), - new InputOption(Options::THEME, null, 4, '', ['all']), - new InputOption(Options::EXCLUDE_THEME, null, 4, '', ['none']), - new InputArgument(Options::LANGUAGES_ARGUMENT, null, 4, ['en_US']), - new InputOption(Options::EXCLUDE_LANGUAGE, null, 4, '', ['none']), - new InputOption(Options::CONTENT_VERSION, null, 4, '', '/*!#') - ]; - - $inputDefinition = new InputDefinition($options); - - try { - $this->inputValidator->validate( - new ArrayInput([], $inputDefinition) - ); - } catch (\Exception $e) { - $this->assertContains( - 'Argument "' . - Options::CONTENT_VERSION - . '" has invalid value, content version should contain only characters, digits and dots', - $e->getMessage() - ); - - $this->assertInstanceOf(InvalidArgumentException::class, $e); - } - } -} +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Deploy\Test\Unit\Console; + +use Magento\Framework\Validator\Regex; +use Magento\Framework\Validator\RegexFactory; +use PHPUnit\Framework\TestCase; +use Magento\Deploy\Console\InputValidator; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Deploy\Console\DeployStaticOptions as Options; +use Magento\Framework\Validator\Locale; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\ArrayInput; +use InvalidArgumentException; +use Symfony\Component\Console\Input\InputArgument; + +/** + * Class InputValidatorTest + * @package Magento\Deploy\Test\Unit\Console + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class InputValidatorTest extends TestCase +{ + /** + * @var ObjectManagerHelper + */ + protected $objectManagerHelper; + + /** + * @var InputValidator + */ + protected $inputValidator; + + /** + * @var Locale + */ + protected $localeValidator; + + /** + * @throws \Zend_Validate_Exception + */ + protected function setUp() + { + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $regexFactoryMock = $this->getMockBuilder(RegexFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $regexObject = new Regex('/^[A-Za-z0-9_.]+$/'); + + $regexFactoryMock->expects($this->any())->method('create') + ->willReturn($regexObject); + + $localeObjectMock = $this->getMockBuilder(Locale::class)->setMethods(['isValid']) + ->disableOriginalConstructor() + ->getMock(); + + $localeObjectMock->expects($this->any())->method('isValid') + ->with('en_US') + ->will($this->returnValue(true)); + + $this->inputValidator = $this->objectManagerHelper->getObject( + InputValidator::class, + [ + 'localeValidator' => $localeObjectMock, + 'versionValidatorFactory' => $regexFactoryMock + ] + ); + } + + /** + * @throws \Zend_Validate_Exception + */ + public function testValidate() + { + $input = $this->getMockBuilder(ArrayInput::class) + ->disableOriginalConstructor() + ->setMethods(['getOption', 'getArgument']) + ->getMock(); + + $input->expects($this->atLeastOnce())->method('getArgument')->willReturn(['all']); + + $input->expects($this->atLeastOnce())->method('getOption') + ->willReturnMap( + [ + [Options::AREA, ['all']], + [Options::EXCLUDE_AREA, ['none']], + [Options::THEME, ['all']], + [Options::EXCLUDE_THEME, ['none']], + [Options::EXCLUDE_LANGUAGE, ['none']], + [Options::CONTENT_VERSION, '12345'] + ] + ); + + /** @noinspection PhpParamsInspection */ + $this->inputValidator->validate($input); + } + + /** + * @covers \Magento\Deploy\Console\InputValidator::checkAreasInput() + */ + public function testCheckAreasInputException() + { + $options = [ + new InputOption(Options::AREA, null, 4, null, ['test']), + new InputOption(Options::EXCLUDE_AREA, null, 4, null, ['test']) + ]; + + $inputDefinition = new InputDefinition($options); + + try { + $this->inputValidator->validate( + new ArrayInput([], $inputDefinition) + ); + } catch (\Exception $e) { + $this->assertContains('--area (-a) and --exclude-area cannot be used at the same time', $e->getMessage()); + $this->assertInstanceOf(InvalidArgumentException::class, $e); + } + } + + /** + * @covers \Magento\Deploy\Console\InputValidator::checkThemesInput() + */ + public function testCheckThemesInputException() + { + $options = [ + new InputOption(Options::AREA, null, 4, null, ['all']), + new InputOption(Options::EXCLUDE_AREA, null, 4, null, ['none']), + new InputOption(Options::THEME, null, 4, '', ['blank']), + new InputOption(Options::EXCLUDE_THEME, null, 4, '', ['luma']) + ]; + + $inputDefinition = new InputDefinition($options); + + try { + $this->inputValidator->validate( + new ArrayInput([], $inputDefinition) + ); + } catch (\Exception $e) { + $this->assertContains('--theme (-t) and --exclude-theme cannot be used at the same time', $e->getMessage()); + $this->assertInstanceOf(InvalidArgumentException::class, $e); + } + } + + public function testCheckLanguagesInputException() + { + $options = [ + new InputOption(Options::AREA, null, 4, null, ['all']), + new InputOption(Options::EXCLUDE_AREA, null, 4, null, ['none']), + new InputOption(Options::THEME, null, 4, '', ['all']), + new InputOption(Options::EXCLUDE_THEME, null, 4, '', ['none']), + new InputArgument(Options::LANGUAGES_ARGUMENT, null, 4, ['en_US']), + new InputOption(Options::EXCLUDE_LANGUAGE, null, 4, '', ['all']) + ]; + + $inputDefinition = new InputDefinition($options); + + try { + $this->inputValidator->validate( + new ArrayInput([], $inputDefinition) + ); + } catch (\Exception $e) { + $this->assertContains( + '--language (-l) and --exclude-language cannot be used at the same time', + $e->getMessage() + ); + + $this->assertInstanceOf(InvalidArgumentException::class, $e); + } + } + + public function testCheckVersionInputException() + { + $options = [ + new InputOption(Options::AREA, null, 4, null, ['all']), + new InputOption(Options::EXCLUDE_AREA, null, 4, null, ['none']), + new InputOption(Options::THEME, null, 4, '', ['all']), + new InputOption(Options::EXCLUDE_THEME, null, 4, '', ['none']), + new InputArgument(Options::LANGUAGES_ARGUMENT, null, 4, ['en_US']), + new InputOption(Options::EXCLUDE_LANGUAGE, null, 4, '', ['none']), + new InputOption(Options::CONTENT_VERSION, null, 4, '', '/*!#') + ]; + + $inputDefinition = new InputDefinition($options); + + try { + $this->inputValidator->validate( + new ArrayInput([], $inputDefinition) + ); + } catch (\Exception $e) { + $this->assertContains( + 'Argument "' . + Options::CONTENT_VERSION + . '" has invalid value, content version should contain only characters, digits and dots', + $e->getMessage() + ); + + $this->assertInstanceOf(InvalidArgumentException::class, $e); + } + } +} From 4ddd2cb2ab24972a7fef1bb740af485def3a8850 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Fri, 4 Jan 2019 14:07:28 -0600 Subject: [PATCH 0173/1295] MC-5926: Conflict of simultaneous write in Redis cache --- .../Magento/Config/App/Config/Type/System.php | 33 +- app/code/Magento/Config/etc/di.xml | 6 + .../integration/etc/di/preferences/ce.php | 4 +- .../Config/App/Config/Type/System.php | 319 ------------------ .../Lock/Backend/DummyLocker.php | 40 +++ .../Magento/Framework/Lock/Backend/Cache.php | 52 +++ .../Framework/Lock/Backend/Database.php | 10 +- 7 files changed, 130 insertions(+), 334 deletions(-) delete mode 100644 dev/tests/integration/framework/Magento/TestFramework/Config/App/Config/Type/System.php create mode 100644 dev/tests/integration/framework/Magento/TestFramework/Lock/Backend/DummyLocker.php create mode 100644 lib/internal/Magento/Framework/Lock/Backend/Cache.php diff --git a/app/code/Magento/Config/App/Config/Type/System.php b/app/code/Magento/Config/App/Config/Type/System.php index 6b50f7bb0da6e..37029dcdb4814 100644 --- a/app/code/Magento/Config/App/Config/Type/System.php +++ b/app/code/Magento/Config/App/Config/Type/System.php @@ -210,30 +210,38 @@ private function getWithParts($path) * Make lock on data load. * * @param callable $dataLoader + * @param bool $flush * @return array */ - private function lockedLoadData(callable $dataLoader): array + private function lockedLoadData(callable $dataLoader, bool $flush = false): array { $cachedData = $dataLoader(); //optimistic read while ($cachedData === false && $this->locker->isLocked(self::$lockName)) { - usleep(200); + usleep(200000); $cachedData = $dataLoader(); } while ($cachedData === false) { try { - if ($this->locker->lock(self::$lockName, 8)) { - $data = $this->readData(); - $this->cacheData($data); - return $data; + if ($this->locker->lock(self::$lockName, 60)) { + if (!$flush) { + $data = $this->readData(); + $this->cacheData($data); + $cachedData = $data; + } else { + $this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [self::CACHE_TAG]); + $cachedData = []; + } } } finally { $this->locker->unlock(self::$lockName); } - usleep(200); - $cachedData = $dataLoader() ?: []; + if ($cachedData === false) { + usleep(200000); + $cachedData = $dataLoader(); + } } return $cachedData; @@ -386,8 +394,11 @@ private function readData(): array public function clean() { $this->data = []; - $this->lockedLoadData(function () { - return false; - }); + $this->lockedLoadData( + function () { + return false; + }, + true + ); } } diff --git a/app/code/Magento/Config/etc/di.xml b/app/code/Magento/Config/etc/di.xml index a5dd18097fb47..87a0e666d2d7b 100644 --- a/app/code/Magento/Config/etc/di.xml +++ b/app/code/Magento/Config/etc/di.xml @@ -77,6 +77,11 @@ </argument> </arguments> </type> + <type name="Magento\Framework\Lock\Backend\Cache"> + <arguments> + <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Config</argument> + </arguments> + </type> <type name="Magento\Config\App\Config\Type\System"> <arguments> <argument name="source" xsi:type="object">systemConfigSourceAggregatedProxy</argument> @@ -85,6 +90,7 @@ <argument name="preProcessor" xsi:type="object">Magento\Framework\App\Config\PreProcessorComposite</argument> <argument name="serializer" xsi:type="object">Magento\Framework\Serialize\Serializer\Serialize</argument> <argument name="reader" xsi:type="object">Magento\Config\App\Config\Type\System\Reader\Proxy</argument> + <argument name="locker" xsi:type="object">Magento\Framework\Lock\Backend\Cache</argument> </arguments> </type> <type name="Magento\Config\App\Config\Type\System\Reader"> diff --git a/dev/tests/integration/etc/di/preferences/ce.php b/dev/tests/integration/etc/di/preferences/ce.php index a5c3292dd211a..6d9c0a1d143c6 100644 --- a/dev/tests/integration/etc/di/preferences/ce.php +++ b/dev/tests/integration/etc/di/preferences/ce.php @@ -25,6 +25,6 @@ \Magento\TestFramework\Db\ConnectionAdapter::class, \Magento\Framework\Filesystem\DriverInterface::class => \Magento\Framework\Filesystem\Driver\File::class, \Magento\Framework\App\Config\ScopeConfigInterface::class => \Magento\TestFramework\App\Config::class, - \Magento\Config\App\Config\Type\System::class => - \Magento\TestFramework\Config\App\Config\Type\System::class, + \Magento\Framework\Lock\Backend\Cache::class => + \Magento\TestFramework\Lock\Backend\DummyLocker::class, ]; diff --git a/dev/tests/integration/framework/Magento/TestFramework/Config/App/Config/Type/System.php b/dev/tests/integration/framework/Magento/TestFramework/Config/App/Config/Type/System.php deleted file mode 100644 index 81615952c7d16..0000000000000 --- a/dev/tests/integration/framework/Magento/TestFramework/Config/App/Config/Type/System.php +++ /dev/null @@ -1,319 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\TestFramework\Config\App\Config\Type; - -use Magento\Config\App\Config\Type\System as SystemCache; -use Magento\Framework\App\Config\ConfigSourceInterface; -use Magento\Framework\App\Config\Spi\PostProcessorInterface; -use Magento\Framework\App\Config\Spi\PreProcessorInterface; -use Magento\Framework\App\ObjectManager; -use Magento\Config\App\Config\Type\System\Reader; -use Magento\Framework\Serialize\Serializer\Sensitive as SensitiveSerializer; -use Magento\Framework\Serialize\Serializer\SensitiveFactory as SensitiveSerializerFactory; -use Magento\Framework\App\ScopeInterface; -use Magento\Framework\Cache\FrontendInterface; -use Magento\Framework\Serialize\SerializerInterface; -use Magento\Store\Model\Config\Processor\Fallback; -use Magento\Store\Model\ScopeInterface as StoreScope; - -/** - * System configuration type - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class System extends SystemCache -{ - /** - * @var array - */ - private $data = []; - - /** - * @var PostProcessorInterface - */ - private $postProcessor; - - /** - * @var FrontendInterface - */ - private $cache; - - /** - * @var SensitiveSerializer - */ - private $serializer; - - /** - * The type of config. - * - * @var string - */ - private $configType; - - /** - * @var Reader - */ - private $reader; - - /** - * List of scopes that were retrieved from configuration storage - * - * Is used to make sure that we don't try to load non-existing configuration scopes. - * - * @var array - */ - private $availableDataScopes; - - /** - * @param ConfigSourceInterface $source - * @param PostProcessorInterface $postProcessor - * @param Fallback $fallback - * @param FrontendInterface $cache - * @param SerializerInterface $serializer - * @param PreProcessorInterface $preProcessor - * @param int $cachingNestedLevel - * @param string $configType - * @param Reader $reader - * @param SensitiveSerializerFactory|null $sensitiveFactory - * - * @SuppressWarnings(PHPMD.ExcessiveParameterList) - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function __construct( - ConfigSourceInterface $source, - PostProcessorInterface $postProcessor, - Fallback $fallback, - FrontendInterface $cache, - SerializerInterface $serializer, - PreProcessorInterface $preProcessor, - $cachingNestedLevel = 1, - $configType = self::CONFIG_TYPE, - Reader $reader = null, - SensitiveSerializerFactory $sensitiveFactory = null - ) { - $this->postProcessor = $postProcessor; - $this->cache = $cache; - $this->configType = $configType; - $this->reader = $reader ?: ObjectManager::getInstance() - ->get(Reader::class); - $sensitiveFactory = $sensitiveFactory ?? ObjectManager::getInstance() - ->get(SensitiveSerializerFactory::class); - //Using sensitive serializer because any kind of information may - //be stored in configs. - $this->serializer = $sensitiveFactory->create( - ['serializer' => $serializer] - ); - } - - /** - * @inheritdoc - */ - public function get($path = '') - { - if ($path === '') { - $this->data = array_replace_recursive($this->loadAllData(), $this->data); - - return $this->data; - } - - return $this->getWithParts($path); - } - - /** - * Proceed with parts extraction from path. - * - * @param string $path - * @return array|int|string|boolean - */ - private function getWithParts($path) - { - $pathParts = explode('/', $path); - - if (count($pathParts) === 1 && $pathParts[0] !== ScopeInterface::SCOPE_DEFAULT) { - if (!isset($this->data[$pathParts[0]])) { - $data = $this->readData(); - $this->data = array_replace_recursive($data, $this->data); - } - - return $this->data[$pathParts[0]]; - } - - $scopeType = array_shift($pathParts); - - if ($scopeType === ScopeInterface::SCOPE_DEFAULT) { - if (!isset($this->data[$scopeType])) { - $this->data = array_replace_recursive($this->loadDefaultScopeData($scopeType), $this->data); - } - - return $this->getDataByPathParts($this->data[$scopeType], $pathParts); - } - - $scopeId = array_shift($pathParts); - - if (!isset($this->data[$scopeType][$scopeId])) { - $scopeData = $this->loadScopeData($scopeType, $scopeId); - - if (!isset($this->data[$scopeType][$scopeId])) { - $this->data = array_replace_recursive($scopeData, $this->data); - } - } - - return isset($this->data[$scopeType][$scopeId]) - ? $this->getDataByPathParts($this->data[$scopeType][$scopeId], $pathParts) - : null; - } - - /** - * Load configuration data for all scopes - * - * @return array - */ - private function loadAllData() - { - $cachedData = $this->cache->load($this->configType); - - if ($cachedData === false) { - $data = $this->readData(); - } else { - $data = $this->serializer->unserialize($cachedData); - } - - return $data; - } - - /** - * Load configuration data for default scope - * - * @param string $scopeType - * @return array - */ - private function loadDefaultScopeData($scopeType) - { - $cachedData = $this->cache->load($this->configType . '_' . $scopeType); - - if ($cachedData === false) { - $data = $this->readData(); - $this->cacheData($data); - } else { - $data = [$scopeType => $this->serializer->unserialize($cachedData)]; - } - - return $data; - } - - /** - * Load configuration data for a specified scope - * - * @param string $scopeType - * @param string $scopeId - * @return array - */ - private function loadScopeData($scopeType, $scopeId) - { - $cachedData = $this->cache->load($this->configType . '_' . $scopeType . '_' . $scopeId); - - if ($cachedData === false) { - if ($this->availableDataScopes === null) { - $cachedScopeData = $this->cache->load($this->configType . '_scopes'); - if ($cachedScopeData !== false) { - $this->availableDataScopes = $this->serializer->unserialize($cachedScopeData); - } - } - if (is_array($this->availableDataScopes) && !isset($this->availableDataScopes[$scopeType][$scopeId])) { - return [$scopeType => [$scopeId => []]]; - } - $data = $this->readData(); - $this->cacheData($data); - } else { - $data = [$scopeType => [$scopeId => $this->serializer->unserialize($cachedData)]]; - } - - return $data; - } - - /** - * Cache configuration data. - * Caches data per scope to avoid reading data for all scopes on every request - * - * @param array $data - * @return void - */ - private function cacheData(array $data) - { - $this->cache->save( - $this->serializer->serialize($data), - $this->configType, - [self::CACHE_TAG] - ); - $this->cache->save( - $this->serializer->serialize($data['default']), - $this->configType . '_default', - [self::CACHE_TAG] - ); - $scopes = []; - foreach ([StoreScope::SCOPE_WEBSITES, StoreScope::SCOPE_STORES] as $curScopeType) { - foreach ($data[$curScopeType] ?? [] as $curScopeId => $curScopeData) { - $scopes[$curScopeType][$curScopeId] = 1; - $this->cache->save( - $this->serializer->serialize($curScopeData), - $this->configType . '_' . $curScopeType . '_' . $curScopeId, - [self::CACHE_TAG] - ); - } - } - $this->cache->save( - $this->serializer->serialize($scopes), - $this->configType . '_scopes', - [self::CACHE_TAG] - ); - } - - /** - * Walk nested hash map by keys from $pathParts - * - * @param array $data to walk in - * @param array $pathParts keys path - * @return mixed - */ - private function getDataByPathParts($data, $pathParts) - { - foreach ($pathParts as $key) { - if ((array)$data === $data && isset($data[$key])) { - $data = $data[$key]; - } elseif ($data instanceof \Magento\Framework\DataObject) { - $data = $data->getDataByKey($key); - } else { - return null; - } - } - - return $data; - } - - /** - * The freshly read data. - * - * @return array - */ - private function readData(): array - { - $this->data = $this->reader->read(); - $this->data = $this->postProcessor->process( - $this->data - ); - - return $this->data; - } - - /** - * @inheritdoc - */ - public function clean() - { - $this->data = []; - $this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [self::CACHE_TAG]); - } -} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Lock/Backend/DummyLocker.php b/dev/tests/integration/framework/Magento/TestFramework/Lock/Backend/DummyLocker.php new file mode 100644 index 0000000000000..41125493643e3 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Lock/Backend/DummyLocker.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Lock\Backend; + +use Magento\Framework\Lock\LockManagerInterface; + +/** + * Dummy locker for the integration framework. + */ +class DummyLocker implements LockManagerInterface +{ + /** + * @inheritdoc + */ + public function lock(string $name, int $timeout = -1): bool + { + return true; + } + + /** + * @inheritdoc + */ + public function unlock(string $name): bool + { + return true; + } + + /** + * @inheritdoc + */ + public function isLocked(string $name): bool + { + return false; + } +} diff --git a/lib/internal/Magento/Framework/Lock/Backend/Cache.php b/lib/internal/Magento/Framework/Lock/Backend/Cache.php new file mode 100644 index 0000000000000..001a94190b7ee --- /dev/null +++ b/lib/internal/Magento/Framework/Lock/Backend/Cache.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Lock\Backend; + +use Magento\Framework\Cache\FrontendInterface; + +/** + * Implementation of the lock manager on the basis of the caching system. + */ +class Cache implements \Magento\Framework\Lock\LockManagerInterface +{ + /** + * @var FrontendInterface + */ + private $cache; + + /** + * @param FrontendInterface $cache + */ + public function __construct(FrontendInterface $cache) + { + $this->cache = $cache; + } + /** + * @inheritdoc + */ + public function lock(string $name, int $timeout = -1): bool + { + return $this->cache->save('1', $name, [], $timeout); + } + + /** + * @inheritdoc + */ + public function unlock(string $name): bool + { + return $this->cache->remove($name); + } + + /** + * @inheritdoc + */ + public function isLocked(string $name): bool + { + return (bool)$this->cache->test($name); + } +} \ No newline at end of file diff --git a/lib/internal/Magento/Framework/Lock/Backend/Database.php b/lib/internal/Magento/Framework/Lock/Backend/Database.php index 8fc544c2dd290..a9dbecedab238 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Database.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Database.php @@ -3,8 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - declare(strict_types=1); + namespace Magento\Framework\Lock\Backend; use Magento\Framework\App\DeploymentConfig; @@ -14,6 +14,9 @@ use Magento\Framework\Exception\InputException; use Magento\Framework\Phrase; +/** + * Implementation of the lock manager on the basis of MySQL. + */ class Database implements \Magento\Framework\Lock\LockManagerInterface { /** @var ResourceConnection */ @@ -53,6 +56,7 @@ public function __construct( * @return bool * @throws InputException * @throws AlreadyExistsException + * @throws \Zend_Db_Statement_Exception */ public function lock(string $name, int $timeout = -1): bool { @@ -69,7 +73,7 @@ public function lock(string $name, int $timeout = -1): bool if ($this->currentLock) { throw new AlreadyExistsException( new Phrase( - 'Current connection is already holding lock for $1, only single lock allowed', + 'Current connection is already holding lock for %1, only single lock allowed', [$this->currentLock] ) ); @@ -93,6 +97,7 @@ public function lock(string $name, int $timeout = -1): bool * @param string $name lock name * @return bool * @throws InputException + * @throws \Zend_Db_Statement_Exception */ public function unlock(string $name): bool { @@ -119,6 +124,7 @@ public function unlock(string $name): bool * @param string $name lock name * @return bool * @throws InputException + * @throws \Zend_Db_Statement_Exception */ public function isLocked(string $name): bool { From 9480a637bf0cfa90645788c5a7d05cec90769915 Mon Sep 17 00:00:00 2001 From: Shikha Mishra <shikhamishra@cedcoss.com> Date: Fri, 14 Dec 2018 20:40:34 +0530 Subject: [PATCH 0174/1295] updated form.phtml Fixed #19780 Incorrect class name on Orders and returns page. --- app/code/Magento/Sales/view/frontend/templates/guest/form.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/view/frontend/templates/guest/form.phtml b/app/code/Magento/Sales/view/frontend/templates/guest/form.phtml index 3ebca4d08b349..89be190588677 100644 --- a/app/code/Magento/Sales/view/frontend/templates/guest/form.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/guest/form.phtml @@ -10,7 +10,7 @@ <form class="form form-orders-search" id="oar-widget-orders-and-returns-form" data-mage-init='{"ordersReturns":{}, "validation":{}}' action="<?= /* @escapeNotVerified */ $block->getActionUrl() ?>" method="post" name="guest_post"> <fieldset class="fieldset"> - <legend class="admin__legend"><span><?= /* @escapeNotVerified */ __('Order Information') ?></span></legend> + <legend class="legend"><span><?= /* @escapeNotVerified */ __('Order Information') ?></span></legend> <br> <div class="field id required"> From 192a429c43aa3b52868a5e4e723a0bab7cda8908 Mon Sep 17 00:00:00 2001 From: GovindaSharma <govindpokhrelsharma@cedcoss.com> Date: Mon, 26 Nov 2018 16:55:35 +0530 Subject: [PATCH 0175/1295] Fixed issue related to 'product_attribute' attribute. Added condition for validating product attribute. --- .../Catalog/Controller/Adminhtml/Product/Attribute/Validate.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php index e56428a1ae77e..3a81b4633b2ff 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php @@ -100,7 +100,7 @@ public function execute() $attributeCode ); - if ($attribute->getId() && !$attributeId) { + if ($attribute->getId() && !$attributeId || $attributeCode === 'product_type') { $message = strlen($this->getRequest()->getParam('attribute_code')) ? __('An attribute with this code already exists.') : __('An attribute with the same code (%1) already exists.', $attributeCode); From 4802e00643bae377a254e8d9b92e2bad72c33e65 Mon Sep 17 00:00:00 2001 From: Govind Sharma <govindpokhrelsharma@cedcoss.com> Date: Sat, 22 Dec 2018 13:59:48 +0530 Subject: [PATCH 0176/1295] issue resolved:Undefined Variable $itemsOrderItemId issue resolved:Undefined Variable $itemsOrderItemId --- app/code/Magento/Shipping/Block/Adminhtml/Order/Packaging.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Shipping/Block/Adminhtml/Order/Packaging.php b/app/code/Magento/Shipping/Block/Adminhtml/Order/Packaging.php index b4ff445c63f4e..c561a0982dce2 100644 --- a/app/code/Magento/Shipping/Block/Adminhtml/Order/Packaging.php +++ b/app/code/Magento/Shipping/Block/Adminhtml/Order/Packaging.php @@ -86,7 +86,7 @@ public function getConfigDataJson() $itemsName = []; $itemsWeight = []; $itemsProductId = []; - + $itemsOrderItemId = []; if ($shipmentId) { $urlParams['shipment_id'] = $shipmentId; $createLabelUrl = $this->getUrl('adminhtml/order_shipment/createLabel', $urlParams); From 39a740570d2548ca6b5cd3af502921f77e3bf912 Mon Sep 17 00:00:00 2001 From: Arvinda kumar <arvindakumar@cedcoss.com> Date: Fri, 21 Dec 2018 17:12:16 +0530 Subject: [PATCH 0177/1295] fixed issue #19925 Close button overlapping in shipping address label whenever any user adding new shipping address in mobile view in checkout --- .../Magento/luma/web/css/source/components/_modals_extend.less | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/luma/web/css/source/components/_modals_extend.less b/app/design/frontend/Magento/luma/web/css/source/components/_modals_extend.less index ed01ef7d027f5..f0c6b5105527b 100644 --- a/app/design/frontend/Magento/luma/web/css/source/components/_modals_extend.less +++ b/app/design/frontend/Magento/luma/web/css/source/components/_modals_extend.less @@ -83,7 +83,8 @@ .modal-slide { .action-close { - padding: @modal-slide-action-close__padding; + padding: 0; + margin: 15px; } .page-main-actions { From 8ecf64fe507f26e2efd89966debcad84cf45c443 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Wed, 26 Dec 2018 13:01:44 +0200 Subject: [PATCH 0178/1295] ENGCOM-3731: Static test fix. --- .../Magento/luma/web/css/source/components/_modals_extend.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/luma/web/css/source/components/_modals_extend.less b/app/design/frontend/Magento/luma/web/css/source/components/_modals_extend.less index f0c6b5105527b..0e34d7b87387d 100644 --- a/app/design/frontend/Magento/luma/web/css/source/components/_modals_extend.less +++ b/app/design/frontend/Magento/luma/web/css/source/components/_modals_extend.less @@ -83,8 +83,8 @@ .modal-slide { .action-close { - padding: 0; margin: 15px; + padding: 0; } .page-main-actions { From cb15b010ddf4bbe9caddac141dfb829926c4a631 Mon Sep 17 00:00:00 2001 From: Milind Singh <milind7@live.com> Date: Sat, 5 Jan 2019 18:15:57 +0530 Subject: [PATCH 0179/1295] Static test suppressed --- app/code/Magento/Shipping/Block/Adminhtml/Order/Packaging.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Shipping/Block/Adminhtml/Order/Packaging.php b/app/code/Magento/Shipping/Block/Adminhtml/Order/Packaging.php index c561a0982dce2..e5e419328eea4 100644 --- a/app/code/Magento/Shipping/Block/Adminhtml/Order/Packaging.php +++ b/app/code/Magento/Shipping/Block/Adminhtml/Order/Packaging.php @@ -74,6 +74,7 @@ public function getShipment() * Configuration for popup window for packaging * * @return string + * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) */ public function getConfigDataJson() { From 79eb23da3d33355aef1f897258dd2eda43e8e852 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Sun, 6 Jan 2019 14:24:15 -0600 Subject: [PATCH 0180/1295] MC-5926: Conflict of simultaneous write in Redis cache --- app/code/Magento/Deploy/etc/di.xml | 14 ---- .../Developer/Model/Logger/Handler/Debug.php | 2 +- .../Unit/Model/Logger/Handler/DebugTest.php | 77 +++++++++++++------ .../Developer/etc/adminhtml/system.xml | 2 +- 4 files changed, 54 insertions(+), 41 deletions(-) diff --git a/app/code/Magento/Deploy/etc/di.xml b/app/code/Magento/Deploy/etc/di.xml index fd604aa1b397b..0c32baebf12df 100644 --- a/app/code/Magento/Deploy/etc/di.xml +++ b/app/code/Magento/Deploy/etc/di.xml @@ -71,18 +71,4 @@ </argument> </arguments> </type> - <type name="Magento\Deploy\App\Mode\ConfigProvider"> - <arguments> - <argument name="config" xsi:type="array"> - <item name="developer" xsi:type="array"> - <item name="production" xsi:type="array"> - <item name="dev/debug/debug_logging" xsi:type="string">0</item> - </item> - <item name="developer" xsi:type="array"> - <item name="dev/debug/debug_logging" xsi:type="null" /> - </item> - </item> - </argument> - </arguments> - </type> </config> diff --git a/app/code/Magento/Developer/Model/Logger/Handler/Debug.php b/app/code/Magento/Developer/Model/Logger/Handler/Debug.php index 9bfee42fa6a83..4d23da86b5ecd 100644 --- a/app/code/Magento/Developer/Model/Logger/Handler/Debug.php +++ b/app/code/Magento/Developer/Model/Logger/Handler/Debug.php @@ -60,7 +60,7 @@ public function isHandling(array $record) if ($this->deploymentConfig->isAvailable()) { return parent::isHandling($record) - && $this->scopeConfig->getValue('dev/debug/debug_logging', ScopeInterface::SCOPE_STORE); + && $this->state->getMode() !== State::MODE_PRODUCTION; } return parent::isHandling($record); diff --git a/app/code/Magento/Developer/Test/Unit/Model/Logger/Handler/DebugTest.php b/app/code/Magento/Developer/Test/Unit/Model/Logger/Handler/DebugTest.php index c116775d582bb..0e009465872cd 100644 --- a/app/code/Magento/Developer/Test/Unit/Model/Logger/Handler/DebugTest.php +++ b/app/code/Magento/Developer/Test/Unit/Model/Logger/Handler/DebugTest.php @@ -10,7 +10,6 @@ use Magento\Framework\App\State; use Magento\Framework\Filesystem\DriverInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Store\Model\ScopeInterface; use Monolog\Formatter\FormatterInterface; use Monolog\Logger; use Magento\Framework\App\DeploymentConfig; @@ -51,6 +50,9 @@ class DebugTest extends \PHPUnit\Framework\TestCase */ private $deploymentConfigMock; + /** + * @inheritdoc + */ protected function setUp() { $this->filesystemMock = $this->getMockBuilder(DriverInterface::class) @@ -80,70 +82,95 @@ protected function setUp() $this->model->setFormatter($this->formatterMock); } - public function testHandle() + /** + * @return void + */ + public function testHandleEnabledInDeveloperMode() { $this->deploymentConfigMock->expects($this->once()) ->method('isAvailable') ->willReturn(true); - $this->stateMock->expects($this->never()) - ->method('getMode'); - $this->scopeConfigMock->expects($this->once()) - ->method('getValue') - ->with('dev/debug/debug_logging', ScopeInterface::SCOPE_STORE, null) - ->willReturn(true); + $this->stateMock + ->expects($this->once()) + ->method('getMode') + ->willReturn(State::MODE_DEVELOPER); + $this->scopeConfigMock + ->expects($this->never()) + ->method('getValue'); $this->assertTrue($this->model->isHandling(['formatted' => false, 'level' => Logger::DEBUG])); } - public function testHandleDisabledByProduction() + /** + * @return void + */ + public function testHandleEnabledInDefaultMode() { $this->deploymentConfigMock->expects($this->once()) ->method('isAvailable') ->willReturn(true); - $this->stateMock->expects($this->never()) - ->method('getMode'); - $this->scopeConfigMock->expects($this->once()) + $this->stateMock + ->expects($this->once()) + ->method('getMode') + ->willReturn(State::MODE_DEFAULT); + $this->scopeConfigMock + ->expects($this->never()) ->method('getValue'); - $this->assertFalse($this->model->isHandling(['formatted' => false, 'level' => Logger::DEBUG])); + $this->assertTrue($this->model->isHandling(['formatted' => false, 'level' => Logger::DEBUG])); } - public function testHandleDisabledByConfig() + /** + * @return void + */ + public function testHandleDisabledByProduction() { $this->deploymentConfigMock->expects($this->once()) ->method('isAvailable') ->willReturn(true); - $this->stateMock->expects($this->never()) - ->method('getMode'); - $this->scopeConfigMock->expects($this->once()) - ->method('getValue') - ->with('dev/debug/debug_logging', ScopeInterface::SCOPE_STORE, null) - ->willReturn(false); + $this->stateMock + ->expects($this->once()) + ->method('getMode') + ->willReturn(State::MODE_PRODUCTION); + $this->scopeConfigMock + ->expects($this->never()) + ->method('getValue'); $this->assertFalse($this->model->isHandling(['formatted' => false, 'level' => Logger::DEBUG])); } + /** + * @return void + */ public function testHandleDisabledByLevel() { $this->deploymentConfigMock->expects($this->once()) ->method('isAvailable') ->willReturn(true); - $this->stateMock->expects($this->never()) - ->method('getMode'); - $this->scopeConfigMock->expects($this->never()) + $this->stateMock + ->expects($this->never()) + ->method('getMode') + ->willReturn(State::MODE_DEVELOPER); + $this->scopeConfigMock + ->expects($this->never()) ->method('getValue'); $this->assertFalse($this->model->isHandling(['formatted' => false, 'level' => Logger::API])); } + /** + * @return void + */ public function testDeploymentConfigIsNotAvailable() { $this->deploymentConfigMock->expects($this->once()) ->method('isAvailable') ->willReturn(false); - $this->stateMock->expects($this->never()) + $this->stateMock + ->expects($this->never()) ->method('getMode'); - $this->scopeConfigMock->expects($this->never()) + $this->scopeConfigMock + ->expects($this->never()) ->method('getValue'); $this->assertTrue($this->model->isHandling(['formatted' => false, 'level' => Logger::DEBUG])); diff --git a/app/code/Magento/Developer/etc/adminhtml/system.xml b/app/code/Magento/Developer/etc/adminhtml/system.xml index db685a5453cd4..5626c4e501295 100644 --- a/app/code/Magento/Developer/etc/adminhtml/system.xml +++ b/app/code/Magento/Developer/etc/adminhtml/system.xml @@ -26,7 +26,7 @@ </field> </group> <group id="debug" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> - <field id="debug_logging" translate="label comment" type="select" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="debug_logging" translate="label comment" type="select" sortOrder="30" showInDefault="0" showInWebsite="0" showInStore="0"> <label>Log to File</label> <comment>Not available in production mode.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> From 981acf8b434225e7269ae65a2f79da5b5bf0351a Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Mon, 7 Jan 2019 17:04:49 -0600 Subject: [PATCH 0181/1295] MC-5926: Conflict of simultaneous write in Redis cache --- app/code/Magento/Deploy/Model/Filesystem.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Deploy/Model/Filesystem.php b/app/code/Magento/Deploy/Model/Filesystem.php index 0557914f48d24..25b45c8aef0eb 100644 --- a/app/code/Magento/Deploy/Model/Filesystem.php +++ b/app/code/Magento/Deploy/Model/Filesystem.php @@ -123,6 +123,7 @@ public function __construct( * * @param OutputInterface $output * @return void + * @throws \Exception */ public function regenerateStatic( OutputInterface $output @@ -190,9 +191,14 @@ private function getAdminUserInterfaceLocales() * * @return array * @throws \InvalidArgumentException if unknown locale is provided by the store configuration + * @throws \Magento\Framework\Exception\FileSystemException */ private function getUsedLocales() { + /** init cache directory */ + $this->filesystem + ->getDirectoryWrite(DirectoryList::CACHE) + ->create(); $usedLocales = array_merge( $this->storeView->retrieveLocales(), $this->getAdminUserInterfaceLocales() @@ -221,13 +227,6 @@ function ($locale) { protected function compile(OutputInterface $output) { $output->writeln('Starting compilation'); - $this->cleanupFilesystem( - [ - DirectoryList::CACHE, - DirectoryList::GENERATED_CODE, - DirectoryList::GENERATED_METADATA, - ] - ); $cmd = $this->functionCallPath . 'setup:di:compile'; /** @@ -251,6 +250,7 @@ protected function compile(OutputInterface $output) * * @param array $directoryCodeList * @return void + * @throws \Magento\Framework\Exception\FileSystemException */ public function cleanupFilesystem($directoryCodeList) { @@ -294,6 +294,7 @@ public function cleanupFilesystem($directoryCodeList) * of inverse mask for setting access permissions to files and directories generated by Magento. * @link http://devdocs.magento.com/guides/v2.0/install-gde/install/post-install-umask.html * @link http://devdocs.magento.com/guides/v2.0/install-gde/prereq/file-system-perms.html + * @throws \Magento\Framework\Exception\FileSystemException */ protected function changePermissions($directoryCodeList, $dirPermissions, $filePermissions) { @@ -319,6 +320,7 @@ protected function changePermissions($directoryCodeList, $dirPermissions, $fileP * of inverse mask for setting access permissions to files and directories generated by Magento. * @link http://devdocs.magento.com/guides/v2.0/install-gde/install/post-install-umask.html * @link http://devdocs.magento.com/guides/v2.0/install-gde/prereq/file-system-perms.html + * @throws \Magento\Framework\Exception\FileSystemException */ public function lockStaticResources() { From 63c592a1b35fbd5dc2917d49b6fc10a6ab6fb46c Mon Sep 17 00:00:00 2001 From: Seth Daugherty <sdaugherty@human-element.com> Date: Mon, 7 Jan 2019 21:49:21 -0500 Subject: [PATCH 0182/1295] Add support for validation message callback - Adds a type check for message just before attempting to process validation params on it - Resolves the callback if it was one using the validation rule for the 'this' context --- .../Magento/Ui/view/base/web/js/lib/validation/validator.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/Ui/view/base/web/js/lib/validation/validator.js b/app/code/Magento/Ui/view/base/web/js/lib/validation/validator.js index 31362644d415a..b7488cf994028 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/validation/validator.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/validation/validator.js @@ -48,6 +48,10 @@ define([ params : [params]; + if (typeof message === 'function') { + message = message.call(rule); + } + message = params.reduce(function (msg, param, idx) { return msg.replace(new RegExp('\\{' + idx + '\\}', 'g'), param); }, message); From fd5366b9759733c413bf282c6c17edbbcb7e4932 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Tue, 8 Jan 2019 10:38:24 +0200 Subject: [PATCH 0183/1295] MAGETWO-94200: Autoscroll is missed on JS error on Storefront PDP [mobile] --- lib/web/mage/validation.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/web/mage/validation.js b/lib/web/mage/validation.js index aa8282d5b6f46..a742b8e6bbb27 100644 --- a/lib/web/mage/validation.js +++ b/lib/web/mage/validation.js @@ -1944,6 +1944,9 @@ } if (firstActive.length) { + $('html, body').animate({ + scrollTop: firstActive.offset().top + }); firstActive.focus(); } } From 41f8ecad0e9580b8b6c3d7e442058b32431d86f4 Mon Sep 17 00:00:00 2001 From: Stas Puga <stas.puga@transoftgroup.com> Date: Tue, 8 Jan 2019 12:07:29 +0200 Subject: [PATCH 0184/1295] MAGETWO-96229: [Staging] Cart Price Rule >> Schedule New Update - Select from existing Scheduled Update --- .../AdminFilterCartPriceRuleActionGroup.xml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminFilterCartPriceRuleActionGroup.xml diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminFilterCartPriceRuleActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminFilterCartPriceRuleActionGroup.xml new file mode 100644 index 0000000000000..35e1bee0952cf --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminFilterCartPriceRuleActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!--Search grid with keyword search--> + <actionGroup name="AdminFilterCartPriceRuleActionGroup"> + <arguments> + <argument name="ruleName"/> + </arguments> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <fillField selector="{{AdminCartPriceRulesSection.filterByNameInput}}" userInput="{{ruleName}}" stepKey="filterByName"/> + <click selector="{{AdminCartPriceRulesSection.searchButton}}" stepKey="doFilter"/> + <click selector="{{AdminCartPriceRulesSection.rowByIndex('1')}}" stepKey="goToEditRulePage"/> + </actionGroup> +</actionGroups> From b4d63421c91169bac436c8dbc5030d61a047055e Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Tue, 8 Jan 2019 12:14:12 +0200 Subject: [PATCH 0185/1295] MAGETWO-95875: [Magento Cloud] Customer receives newsletter unsubscription email after registering for new account - Update status method fix - Unit test fix --- app/code/Magento/Newsletter/Model/Subscriber.php | 9 +++++++-- .../Newsletter/Test/Unit/Model/SubscriberTest.php | 10 ++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Newsletter/Model/Subscriber.php b/app/code/Magento/Newsletter/Model/Subscriber.php index 48a89129f3098..2c6f494053efc 100644 --- a/app/code/Magento/Newsletter/Model/Subscriber.php +++ b/app/code/Magento/Newsletter/Model/Subscriber.php @@ -552,7 +552,7 @@ public function updateSubscription($customerId) } /** - * Saving customer subscription status + * Saving customer subscription status. * * @param int $customerId * @param bool $subscribe indicates whether the customer should be subscribed or unsubscribed @@ -588,7 +588,12 @@ protected function _updateCustomerSubscription($customerId, $subscribe) if (AccountManagementInterface::ACCOUNT_CONFIRMATION_REQUIRED == $this->customerAccountManagement->getConfirmationStatus($customerId) ) { - $status = self::STATUS_UNCONFIRMED; + if ($this->getId() && $this->getStatus() == self::STATUS_SUBSCRIBED) { + // if a customer was already subscribed then keep the subscribed + $status = self::STATUS_SUBSCRIBED; + } else { + $status = self::STATUS_UNCONFIRMED; + } } elseif ($isConfirmNeed) { if ($this->getStatus() != self::STATUS_SUBSCRIBED) { $status = self::STATUS_NOT_ACTIVE; diff --git a/app/code/Magento/Newsletter/Test/Unit/Model/SubscriberTest.php b/app/code/Magento/Newsletter/Test/Unit/Model/SubscriberTest.php index d8c770f02e8a7..0bf6f24a6b989 100644 --- a/app/code/Magento/Newsletter/Test/Unit/Model/SubscriberTest.php +++ b/app/code/Magento/Newsletter/Test/Unit/Model/SubscriberTest.php @@ -209,6 +209,11 @@ public function testSubscribeNotLoggedIn() $this->assertEquals(Subscriber::STATUS_NOT_ACTIVE, $this->subscriber->subscribe($email)); } + /** + * Update status with Confirmation Status - required. + * + * @return void + */ public function testUpdateSubscription() { $websiteId = 1; @@ -225,7 +230,7 @@ public function testUpdateSubscription() ->willReturn( [ 'subscriber_id' => 1, - 'subscriber_status' => Subscriber::STATUS_SUBSCRIBED + 'subscriber_status' => Subscriber::STATUS_SUBSCRIBED, ] ); $customerDataMock->expects($this->atLeastOnce())->method('getId')->willReturn('id'); @@ -245,8 +250,9 @@ public function testUpdateSubscription() ->getMock(); $this->storeManager->expects($this->any())->method('getStore')->willReturn($storeModel); $storeModel->expects($this->exactly(2))->method('getWebsiteId')->willReturn($websiteId); + $data = $this->subscriber->updateSubscription($customerId); - $this->assertEquals($this->subscriber, $this->subscriber->updateSubscription($customerId)); + $this->assertEquals(Subscriber::STATUS_SUBSCRIBED, $data->getSubscriberStatus()); } public function testUnsubscribeCustomerById() From b58256d7c967e91519fefcaacf757b968efac38c Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Tue, 8 Jan 2019 13:39:32 +0200 Subject: [PATCH 0186/1295] MAGETWO-97393: Impossible to enable shared catalog through cli command --- .../ConfigSet/DefaultProcessorTest.php | 6 +- .../Config/Test/Unit/Model/ConfigTest.php | 177 ++++++++++-------- .../Structure/Element/FieldPluginTest.php | 6 +- 3 files changed, 105 insertions(+), 84 deletions(-) diff --git a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/DefaultProcessorTest.php b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/DefaultProcessorTest.php index edb76c067bf35..3fb7d9ad21cd4 100644 --- a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/DefaultProcessorTest.php +++ b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/DefaultProcessorTest.php @@ -101,12 +101,10 @@ public function testProcess($path, $value, $scope, $scopeCode) $this->configMockForProcessTest($path, $scope, $scopeCode); $config = $this->createMock(Config::class); - $this->configFactory->expects($this->once()) - ->method('create') + $this->configFactory->method('create') ->with(['scope' => $scope, 'scope_code' => $scopeCode]) ->willReturn($config); - $config->expects($this->once()) - ->method('setDataByPath') + $config->method('setDataByPath') ->with($path, $value); $config->expects($this->once()) ->method('save') diff --git a/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php b/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php index 73698da618525..bb772f51c0dac 100644 --- a/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php @@ -5,7 +5,26 @@ */ namespace Magento\Config\Test\Unit\Model; -use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit_Framework_MockObject_MockObject as MockObject; +use Magento\Config\Model\Config; +use Magento\Framework\App\Config\ReinitableConfigInterface; +use Magento\Framework\Event\ManagerInterface; +use Magento\Config\Model\Config\Structure\Reader; +use Magento\Framework\DB\TransactionFactory; +use Magento\Config\Model\Config\Loader; +use Magento\Framework\App\Config\ValueFactory; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Config\Model\Config\Structure; +use Magento\Config\Model\Config\Reader\Source\Deployed\SettingChecker; +use Magento\Framework\App\ScopeResolverPool; +use Magento\Framework\App\ScopeResolverInterface; +use Magento\Framework\App\ScopeInterface; +use Magento\Store\Model\ScopeTypeNormalizer; +use Magento\Framework\DB\Transaction; +use Magento\Framework\App\Config\Value; +use Magento\Store\Model\Website; +use Magento\Config\Model\Config\Structure\Element\Group; +use Magento\Config\Model\Config\Structure\Element\Field; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -13,119 +32,113 @@ class ConfigTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Config\Model\Config + * @var Config */ private $model; /** - * @var \Magento\Framework\Event\ManagerInterface|MockObject + * @var ManagerInterface|MockObject */ private $eventManagerMock; /** - * @var \Magento\Config\Model\Config\Structure\Reader|MockObject + * @var Reader|MockObject */ private $structureReaderMock; /** - * @var \Magento\Framework\DB\TransactionFactory|MockObject + * @var TransactionFactory|MockObject */ private $transFactoryMock; /** - * @var \Magento\Framework\App\Config\ReinitableConfigInterface|MockObject + * @var ReinitableConfigInterface|MockObject */ private $appConfigMock; /** - * @var \Magento\Config\Model\Config\Loader|MockObject + * @var Loader|MockObject */ private $configLoaderMock; /** - * @var \Magento\Framework\App\Config\ValueFactory|MockObject + * @var ValueFactory|MockObject */ private $dataFactoryMock; /** - * @var \Magento\Store\Model\StoreManagerInterface|MockObject + * @var StoreManagerInterface|MockObject */ private $storeManager; /** - * @var \Magento\Config\Model\Config\Structure|MockObject + * @var Structure|MockObject */ private $configStructure; /** - * @var \Magento\Config\Model\Config\Reader\Source\Deployed\SettingChecker|MockObject + * @var SettingChecker|MockObject */ private $settingsChecker; /** - * @var \Magento\Framework\App\ScopeResolverPool|MockObject + * @var ScopeResolverPool|MockObject */ private $scopeResolverPool; /** - * @var \Magento\Framework\App\ScopeResolverInterface|MockObject + * @var ScopeResolverInterface|MockObject */ private $scopeResolver; /** - * @var \Magento\Framework\App\ScopeInterface|MockObject + * @var ScopeInterface|MockObject */ private $scope; /** - * @var \Magento\Store\Model\ScopeTypeNormalizer|MockObject + * @var ScopeTypeNormalizer|MockObject */ private $scopeTypeNormalizer; protected function setUp() { - $this->eventManagerMock = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); + $this->eventManagerMock = $this->createMock(ManagerInterface::class); $this->structureReaderMock = $this->createPartialMock( - \Magento\Config\Model\Config\Structure\Reader::class, + Reader::class, ['getConfiguration'] ); - $this->configStructure = $this->createMock(\Magento\Config\Model\Config\Structure::class); + $this->configStructure = $this->createMock(Structure::class); - $this->structureReaderMock->expects( - $this->any() - )->method( - 'getConfiguration' - )->will( - $this->returnValue($this->configStructure) - ); + $this->structureReaderMock->method('getConfiguration') + ->willReturn($this->configStructure); $this->transFactoryMock = $this->createPartialMock( - \Magento\Framework\DB\TransactionFactory::class, + TransactionFactory::class, ['create', 'addObject'] ); - $this->appConfigMock = $this->createMock(\Magento\Framework\App\Config\ReinitableConfigInterface::class); + $this->appConfigMock = $this->createMock(ReinitableConfigInterface::class); $this->configLoaderMock = $this->createPartialMock( - \Magento\Config\Model\Config\Loader::class, + Loader::class, ['getConfigByPath'] ); - $this->dataFactoryMock = $this->createMock(\Magento\Framework\App\Config\ValueFactory::class); + $this->dataFactoryMock = $this->createMock(ValueFactory::class); - $this->storeManager = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); + $this->storeManager = $this->createMock(StoreManagerInterface::class); - $this->settingsChecker = $this - ->createMock(\Magento\Config\Model\Config\Reader\Source\Deployed\SettingChecker::class); + $this->settingsChecker = $this->createMock(SettingChecker::class); - $this->scopeResolverPool = $this->createMock(\Magento\Framework\App\ScopeResolverPool::class); - $this->scopeResolver = $this->createMock(\Magento\Framework\App\ScopeResolverInterface::class); + $this->scopeResolverPool = $this->createMock(ScopeResolverPool::class); + $this->scopeResolver = $this->createMock(ScopeResolverInterface::class); $this->scopeResolverPool->method('get') ->willReturn($this->scopeResolver); - $this->scope = $this->createMock(\Magento\Framework\App\ScopeInterface::class); + $this->scope = $this->createMock(ScopeInterface::class); $this->scopeResolver->method('getScope') ->willReturn($this->scope); - $this->scopeTypeNormalizer = $this->createMock(\Magento\Store\Model\ScopeTypeNormalizer::class); + $this->scopeTypeNormalizer = $this->createMock(ScopeTypeNormalizer::class); - $this->model = new \Magento\Config\Model\Config( + $this->model = new Config( $this->appConfigMock, $this->eventManagerMock, $this->configStructure, @@ -160,11 +173,13 @@ public function testSaveEmptiesNonSetArguments() public function testSaveToCheckAdminSystemConfigChangedSectionEvent() { - $transactionMock = $this->createMock(\Magento\Framework\DB\Transaction::class); + $transactionMock = $this->createMock(Transaction::class); - $this->transFactoryMock->expects($this->any())->method('create')->will($this->returnValue($transactionMock)); + $this->transFactoryMock->method('create') + ->willReturn($transactionMock); - $this->configLoaderMock->expects($this->any())->method('getConfigByPath')->will($this->returnValue([])); + $this->configLoaderMock->method('getConfigByPath') + ->willReturn([]); $this->eventManagerMock->expects( $this->at(0) @@ -190,53 +205,64 @@ public function testSaveToCheckAdminSystemConfigChangedSectionEvent() public function testDoNotSaveReadOnlyFields() { - $transactionMock = $this->createMock(\Magento\Framework\DB\Transaction::class); - $this->transFactoryMock->expects($this->any())->method('create')->will($this->returnValue($transactionMock)); + $transactionMock = $this->createMock(Transaction::class); + $this->transFactoryMock->method('create') + ->willReturn($transactionMock); - $this->settingsChecker->expects($this->any())->method('isReadOnly')->will($this->returnValue(true)); - $this->configLoaderMock->expects($this->any())->method('getConfigByPath')->will($this->returnValue([])); + $this->settingsChecker->method('isReadOnly') + ->willReturn(true); + $this->configLoaderMock->method('getConfigByPath') + ->willReturn([]); $this->model->setGroups(['1' => ['fields' => ['key' => ['data']]]]); $this->model->setSection('section'); - $group = $this->createMock(\Magento\Config\Model\Config\Structure\Element\Group::class); - $group->method('getPath')->willReturn('section/1'); + $group = $this->createMock(Group::class); + $group->method('getPath') + ->willReturn('section/1'); - $field = $this->createMock(\Magento\Config\Model\Config\Structure\Element\Field::class); - $field->method('getGroupPath')->willReturn('section/1'); - $field->method('getId')->willReturn('key'); + $field = $this->createMock(Field::class); + $field->method('getGroupPath') + ->willReturn('section/1'); + $field->method('getId') + ->willReturn('key'); $this->configStructure->expects($this->at(0)) ->method('getElement') ->with('section/1') - ->will($this->returnValue($group)); + ->willReturn($group); $this->configStructure->expects($this->at(1)) ->method('getElement') ->with('section/1') - ->will($this->returnValue($group)); + ->willReturn($group); $this->configStructure->expects($this->at(2)) ->method('getElement') ->with('section/1/key') - ->will($this->returnValue($field)); + ->willReturn($field); $backendModel = $this->createPartialMock( - \Magento\Framework\App\Config\Value::class, + Value::class, ['addData'] ); - $this->dataFactoryMock->expects($this->any())->method('create')->will($this->returnValue($backendModel)); + $this->dataFactoryMock->method('create') + ->willReturn($backendModel); - $this->transFactoryMock->expects($this->never())->method('addObject'); - $backendModel->expects($this->never())->method('addData'); + $this->transFactoryMock->expects($this->never()) + ->method('addObject'); + $backendModel->expects($this->never()) + ->method('addData'); $this->model->save(); } public function testSaveToCheckScopeDataSet() { - $transactionMock = $this->createMock(\Magento\Framework\DB\Transaction::class); - $this->transFactoryMock->expects($this->any())->method('create')->will($this->returnValue($transactionMock)); + $transactionMock = $this->createMock(Transaction::class); + $this->transFactoryMock->method('create') + ->willReturn($transactionMock); - $this->configLoaderMock->expects($this->any())->method('getConfigByPath')->will($this->returnValue([])); + $this->configLoaderMock->method('getConfigByPath') + ->willReturn([]); $this->eventManagerMock->expects($this->at(0)) ->method('dispatch') @@ -251,36 +277,35 @@ public function testSaveToCheckScopeDataSet() $this->arrayHasKey('store') ); - $group = $this->createMock(\Magento\Config\Model\Config\Structure\Element\Group::class); + $group = $this->createMock(Group::class); $group->method('getPath')->willReturn('section/1'); - $field = $this->createMock(\Magento\Config\Model\Config\Structure\Element\Field::class); + $field = $this->createMock(Field::class); $field->method('getGroupPath')->willReturn('section/1'); $field->method('getId')->willReturn('key'); $this->configStructure->expects($this->at(0)) ->method('getElement') ->with('section/1') - ->will($this->returnValue($group)); + ->willReturn($group); $this->configStructure->expects($this->at(1)) ->method('getElement') ->with('section/1') - ->will($this->returnValue($group)); + ->willReturn($group); $this->configStructure->expects($this->at(2)) ->method('getElement') ->with('section/1/key') - ->will($this->returnValue($field)); + ->willReturn($field); $this->configStructure->expects($this->at(3)) ->method('getElement') ->with('section/1') - ->will($this->returnValue($group)); + ->willReturn($group); $this->configStructure->expects($this->at(4)) ->method('getElement') ->with('section/1/key') - ->will($this->returnValue($field)); + ->willReturn($field); - $this->scopeResolver->expects($this->atLeastOnce()) - ->method('getScope') + $this->scopeResolver->method('getScope') ->with('1') ->willReturn($this->scope); $this->scope->expects($this->atLeastOnce()) @@ -296,20 +321,19 @@ public function testSaveToCheckScopeDataSet() ->method('normalize') ->with('website') ->willReturn('websites'); - $website = $this->createMock(\Magento\Store\Model\Website::class); - $this->storeManager->expects($this->any())->method('getWebsites')->will($this->returnValue([$website])); - $this->storeManager->expects($this->any())->method('isSingleStoreMode')->will($this->returnValue(true)); + $website = $this->createMock(Website::class); + $this->storeManager->method('getWebsites')->willReturn([$website]); + $this->storeManager->method('isSingleStoreMode')->willReturn(true); $this->model->setWebsite('1'); $this->model->setSection('section'); $this->model->setGroups(['1' => ['fields' => ['key' => ['data']]]]); $backendModel = $this->createPartialMock( - \Magento\Framework\App\Config\Value::class, + Value::class, ['setPath', 'addData', '__sleep', '__wakeup'] ); - $backendModel->expects($this->once()) - ->method('addData') + $backendModel->method('addData') ->with([ 'field' => 'key', 'groups' => [1 => ['fields' => ['key' => ['data']]]], @@ -323,9 +347,10 @@ public function testSaveToCheckScopeDataSet() $backendModel->expects($this->once()) ->method('setPath') ->with('section/1/key') - ->will($this->returnValue($backendModel)); + ->willReturn($backendModel); - $this->dataFactoryMock->expects($this->any())->method('create')->will($this->returnValue($backendModel)); + $this->dataFactoryMock->method('create') + ->willReturn($backendModel); $this->model->save(); } diff --git a/app/code/Magento/Paypal/Test/Unit/Model/Config/Structure/Element/FieldPluginTest.php b/app/code/Magento/Paypal/Test/Unit/Model/Config/Structure/Element/FieldPluginTest.php index f0dda20b71c76..72c13c80b31e4 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/Config/Structure/Element/FieldPluginTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/Config/Structure/Element/FieldPluginTest.php @@ -47,8 +47,7 @@ public function testAroundGetConfigPathHasResult() public function testAroundGetConfigPathNonPaymentSection() { - $this->subjectMock->expects($this->once()) - ->method('getPath') + $this->subjectMock->method('getPath') ->willReturn('non-payment/group/field'); $this->assertNull($this->plugin->afterGetConfigPath($this->subjectMock, null)); @@ -62,8 +61,7 @@ public function testAroundGetConfigPathNonPaymentSection() */ public function testAroundGetConfigPath($subjectPath, $expectedConfigPath) { - $this->subjectMock->expects($this->exactly(2)) - ->method('getPath') + $this->subjectMock->method('getPath') ->willReturn($subjectPath); $this->assertEquals($expectedConfigPath, $this->plugin->afterGetConfigPath($this->subjectMock, null)); From fdd4952662788017c109699a6cd8c609b003ca27 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Tue, 8 Jan 2019 14:38:21 +0200 Subject: [PATCH 0187/1295] MAGETWO-85699: Multiselect attribute values is not searchable under Quick Search when more than one value is selected --- app/code/Magento/CatalogSearch/Model/ResourceModel/Engine.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogSearch/Model/ResourceModel/Engine.php b/app/code/Magento/CatalogSearch/Model/ResourceModel/Engine.php index c6b679a5468c6..831780631c124 100644 --- a/app/code/Magento/CatalogSearch/Model/ResourceModel/Engine.php +++ b/app/code/Magento/CatalogSearch/Model/ResourceModel/Engine.php @@ -108,7 +108,8 @@ public function processAttributeValue($attribute, $value) ) { $result = $value; } elseif ($this->isTermFilterableAttribute($attribute) - || ($attribute->getIsSearchable() && in_array($attribute->getFrontendInput(), ['select', 'multiselect'])) + || ($attribute->getIsSearchable() + && in_array($attribute->getFrontendInput(), ['select', 'multiselect'])) ) { $result = ''; } From fdf7da48088ffae823021e5a8be4429afa941404 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Tue, 8 Jan 2019 15:29:29 +0200 Subject: [PATCH 0188/1295] ENGCOM-3784: Static test fix. --- .../Observer/AssignCouponDataAfterOrderCustomerAssignTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Observer/AssignCouponDataAfterOrderCustomerAssignTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Observer/AssignCouponDataAfterOrderCustomerAssignTest.php index 840ee1a5607d9..397650df416e9 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Observer/AssignCouponDataAfterOrderCustomerAssignTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Observer/AssignCouponDataAfterOrderCustomerAssignTest.php @@ -1,4 +1,8 @@ <?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ namespace Magento\SalesRule\Model\Observer; From 7bc881a39305761ac5911532d6d8f6f5717bc29c Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Tue, 8 Jan 2019 11:40:56 -0600 Subject: [PATCH 0189/1295] MC-5926: Conflict of simultaneous write in Redis cache --- .../Developer/etc/adminhtml/system.xml | 7 -- .../Model/Logger/Handler/DebugTest.php | 68 ++++++------------- .../Magento/Framework/Lock/Backend/Cache.php | 2 +- 3 files changed, 23 insertions(+), 54 deletions(-) diff --git a/app/code/Magento/Developer/etc/adminhtml/system.xml b/app/code/Magento/Developer/etc/adminhtml/system.xml index 5626c4e501295..c64abd6eae725 100644 --- a/app/code/Magento/Developer/etc/adminhtml/system.xml +++ b/app/code/Magento/Developer/etc/adminhtml/system.xml @@ -25,13 +25,6 @@ <backend_model>Magento\Developer\Model\Config\Backend\AllowedIps</backend_model> </field> </group> - <group id="debug" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> - <field id="debug_logging" translate="label comment" type="select" sortOrder="30" showInDefault="0" showInWebsite="0" showInStore="0"> - <label>Log to File</label> - <comment>Not available in production mode.</comment> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - </field> - </group> </section> </system> </config> diff --git a/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php b/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php index 3bef48d8801f7..bc0e1a96b26c0 100644 --- a/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php +++ b/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php @@ -7,7 +7,6 @@ use Magento\Config\Console\Command\ConfigSetCommand; use Magento\Framework\App\Config; -use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem; use Magento\Framework\Filesystem\Directory\WriteInterface; @@ -33,6 +32,7 @@ * - Try to log message into debug file * - Assert that log file is exists * - Assert that log file contain logged message + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class DebugTest extends \PHPUnit\Framework\TestCase { @@ -71,6 +71,10 @@ class DebugTest extends \PHPUnit\Framework\TestCase */ private $appConfig; + /** + * @inheritdoc + * @throws \Exception + */ public function setUp() { /** @var Filesystem $filesystem */ @@ -90,67 +94,39 @@ public function setUp() 'output' => $this->outputMock ] ); - $this->configSetCommand = Bootstrap::getObjectManager()->create(ConfigSetCommand::class); - $this->appConfig = Bootstrap::getObjectManager()->create(Config::class); - - // Preconditions - $this->mode->enableDeveloperMode(); - $this->enableDebugging(); - if (file_exists($this->getDebuggerLogPath())) { - unlink($this->getDebuggerLogPath()); - } } + /** + * @inheritdoc + * @throws \Magento\Framework\Exception\FileSystemException + */ public function tearDown() { $this->etcDirectory->delete('env.php'); $this->etcDirectory->renameFile('env.base.php', 'env.php'); } - private function enableDebugging() - { - $this->inputMock = $this->getMockBuilder(InputInterface::class) - ->getMockForAbstractClass(); - $this->outputMock = $this->getMockBuilder(OutputInterface::class) - ->getMockForAbstractClass(); - $this->inputMock->expects($this->exactly(4)) - ->method('getOption') - ->withConsecutive( - [ConfigSetCommand::OPTION_LOCK_ENV], - [ConfigSetCommand::OPTION_LOCK_CONFIG], - [ConfigSetCommand::OPTION_SCOPE], - [ConfigSetCommand::OPTION_SCOPE_CODE] - ) - ->willReturnOnConsecutiveCalls( - true, - false, - ScopeConfigInterface::SCOPE_TYPE_DEFAULT, - null - ); - $this->inputMock->expects($this->exactly(2)) - ->method('getArgument') - ->withConsecutive([ConfigSetCommand::ARG_PATH], [ConfigSetCommand::ARG_VALUE]) - ->willReturnOnConsecutiveCalls('dev/debug/debug_logging', 1); - $this->outputMock->expects($this->once()) - ->method('writeln') - ->with('<info>Value was saved in app/etc/env.php and locked.</info>'); - $this->assertFalse((bool)$this->configSetCommand->run($this->inputMock, $this->outputMock)); - } - + /** + * @throws \Exception + */ public function testDebugInProductionMode() { $message = 'test message'; - $this->mode->enableProductionModeMinimal(); - $this->logger->debug($message); - $this->assertFileNotExists($this->getDebuggerLogPath()); - $this->assertFalse((bool)$this->appConfig->getValue('dev/debug/debug_logging')); + $this->mode->enableDeveloperMode(); + if (file_exists($this->getDebuggerLogPath())) { + unlink($this->getDebuggerLogPath()); + } - $this->enableDebugging(); $this->logger->debug($message); - $this->assertFileExists($this->getDebuggerLogPath()); $this->assertContains($message, file_get_contents($this->getDebuggerLogPath())); + + unlink($this->getDebuggerLogPath()); + $this->mode->enableProductionModeMinimal(); + + $this->logger->debug($message); + $this->assertFileNotExists($this->getDebuggerLogPath()); } /** diff --git a/lib/internal/Magento/Framework/Lock/Backend/Cache.php b/lib/internal/Magento/Framework/Lock/Backend/Cache.php index 001a94190b7ee..61818cbb8c53c 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Cache.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Cache.php @@ -49,4 +49,4 @@ public function isLocked(string $name): bool { return (bool)$this->cache->test($name); } -} \ No newline at end of file +} From 0d6381d4c5811d86ec6563c15fd4148580f01570 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Tue, 8 Jan 2019 13:48:37 -0600 Subject: [PATCH 0190/1295] MC-5926: Conflict of simultaneous write in Redis cache --- app/code/Magento/Config/App/Config/Type/System.php | 4 ++-- .../Developer/Model/Logger/Handler/DebugTest.php | 12 ------------ 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Config/App/Config/Type/System.php b/app/code/Magento/Config/App/Config/Type/System.php index 37029dcdb4814..13a3198096fe4 100644 --- a/app/code/Magento/Config/App/Config/Type/System.php +++ b/app/code/Magento/Config/App/Config/Type/System.php @@ -218,7 +218,7 @@ private function lockedLoadData(callable $dataLoader, bool $flush = false): arra $cachedData = $dataLoader(); //optimistic read while ($cachedData === false && $this->locker->isLocked(self::$lockName)) { - usleep(200000); + usleep(100000); $cachedData = $dataLoader(); } @@ -239,7 +239,7 @@ private function lockedLoadData(callable $dataLoader, bool $flush = false): arra } if ($cachedData === false) { - usleep(200000); + usleep(100000); $cachedData = $dataLoader(); } } diff --git a/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php b/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php index bc0e1a96b26c0..b74737149cd5a 100644 --- a/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php +++ b/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php @@ -5,8 +5,6 @@ */ namespace Magento\Developer\Model\Logger\Handler; -use Magento\Config\Console\Command\ConfigSetCommand; -use Magento\Framework\App\Config; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem; use Magento\Framework\Filesystem\Directory\WriteInterface; @@ -56,21 +54,11 @@ class DebugTest extends \PHPUnit\Framework\TestCase */ private $outputMock; - /** - * @var ConfigSetCommand - */ - private $configSetCommand; - /** * @var WriteInterface */ private $etcDirectory; - /** - * @var Config - */ - private $appConfig; - /** * @inheritdoc * @throws \Exception From c6df596fe40afdd14ea82e6e238dc1be8feabea1 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Wed, 9 Jan 2019 09:11:27 +0200 Subject: [PATCH 0191/1295] MAGETWO-94862: Acceptance tests improvement --- ...refrontFillOrderInformationActionGroup.xml | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 app/code/Magento/Sales/Test/Mftf/ActionGroup/StorefrontFillOrderInformationActionGroup.xml diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/StorefrontFillOrderInformationActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/StorefrontFillOrderInformationActionGroup.xml new file mode 100644 index 0000000000000..4b64c87c85e55 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/StorefrontFillOrderInformationActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!--Fill order information fields and click continue--> + <actionGroup name="StorefrontFillOrderInformationActionGroup"> + <arguments> + <argument name="orderId" type="string"/> + <argument name="orderLastName"/> + <argument name="orderEmail"/> + </arguments> + <amOnPage url="{{StorefrontGuestOrderSearchPage.url}}" stepKey="navigateToOrderAndReturnPage"/> + <fillField selector="{{StorefrontGuestOrderSearchSection.orderId}}" userInput="{{orderId}}" stepKey="fillOrderId"/> + <fillField selector="{{StorefrontGuestOrderSearchSection.billingLastName}}" userInput="{{orderLastName}}" stepKey="fillBillingLastName"/> + <fillField selector="{{StorefrontGuestOrderSearchSection.email}}" userInput="{{orderEmail}}" stepKey="fillEmail"/> + <click selector="{{StorefrontGuestOrderSearchSection.continue}}" stepKey="clickContinue"/> + <waitForPageLoad stepKey="waitForOrderInformationPageLoad"/> + <seeInCurrentUrl url="{{StorefrontGuestOrderViewPage.url}}" stepKey="seeOrderInformationUrl"/> + </actionGroup> +</actionGroups> From 5ee6e5ea2736e2447e274fe6120b29bb0d50dc41 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@transoftgroup.com> Date: Wed, 9 Jan 2019 14:07:32 +0200 Subject: [PATCH 0192/1295] MAGETWO-94862: Acceptance tests improvement --- ...nGroup.xml => StorefrontSearchGuestOrderActionGroup.xml} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename app/code/Magento/Sales/Test/Mftf/ActionGroup/{StorefrontFillOrderInformationActionGroup.xml => StorefrontSearchGuestOrderActionGroup.xml} (87%) diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/StorefrontFillOrderInformationActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/StorefrontSearchGuestOrderActionGroup.xml similarity index 87% rename from app/code/Magento/Sales/Test/Mftf/ActionGroup/StorefrontFillOrderInformationActionGroup.xml rename to app/code/Magento/Sales/Test/Mftf/ActionGroup/StorefrontSearchGuestOrderActionGroup.xml index 4b64c87c85e55..5a5943da91ce6 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/StorefrontFillOrderInformationActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/StorefrontSearchGuestOrderActionGroup.xml @@ -9,11 +9,11 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!--Fill order information fields and click continue--> - <actionGroup name="StorefrontFillOrderInformationActionGroup"> + <actionGroup name="StorefrontSearchGuestOrderActionGroup"> <arguments> <argument name="orderId" type="string"/> - <argument name="orderLastName"/> - <argument name="orderEmail"/> + <argument name="orderLastName" type="string"/> + <argument name="orderEmail" type="string"/> </arguments> <amOnPage url="{{StorefrontGuestOrderSearchPage.url}}" stepKey="navigateToOrderAndReturnPage"/> <fillField selector="{{StorefrontGuestOrderSearchSection.orderId}}" userInput="{{orderId}}" stepKey="fillOrderId"/> From b0c8c442a9edf7f214c08a6f41da83ec446d5756 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Wed, 9 Jan 2019 15:34:01 -0600 Subject: [PATCH 0193/1295] MAGETWO-97043: Template Callbacks --- .../Magento/Framework/Filter/Template.php | 36 +++++++++++++++---- .../Filter/Test/Unit/TemplateTest.php | 11 ++++++ 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/lib/internal/Magento/Framework/Filter/Template.php b/lib/internal/Magento/Framework/Filter/Template.php index 40799bfe0a6b5..fb475be2d2c61 100644 --- a/lib/internal/Magento/Framework/Filter/Template.php +++ b/lib/internal/Magento/Framework/Filter/Template.php @@ -52,6 +52,11 @@ class Template implements \Zend_Filter_Interface */ protected $string; + /** + * @var string[] + */ + private $restrictedMethods = ['addafterfiltercallback']; + /** * @param \Magento\Framework\Stdlib\StringUtils $string * @param array $variables @@ -297,6 +302,25 @@ protected function getParameters($value) return $params; } + /** + * Validate method call initiated in a template. + * + * Deny calls for methods that may disrupt template processing. + * + * @param object $object + * @param string $method + * @return void + * @throws \InvalidArgumentException + */ + private function validateVariableMethodCall($object, string $method) + { + if ($object === $this) { + if (in_array(mb_strtolower($method), $this->restrictedMethods)) { + throw new \InvalidArgumentException("Method $method cannot be called from template."); + } + } + } + /** * Return variable value for var construction * @@ -345,12 +369,12 @@ protected function getVariable($value, $default = '{no_value_defined}') $last = $i; } elseif (isset($stackVars[$i - 1]['variable']) && $stackVars[$i]['type'] == 'method') { // Calling object methods - if (method_exists($stackVars[$i - 1]['variable'], $stackVars[$i]['name'])) { - $stackVars[$i]['args'] = $this->getStackArgs($stackVars[$i]['args']); - $stackVars[$i]['variable'] = call_user_func_array( - [$stackVars[$i - 1]['variable'], $stackVars[$i]['name']], - $stackVars[$i]['args'] - ); + $object = $stackVars[$i - 1]['variable']; + $method = $stackVars[$i]['name']; + if (method_exists($object, $method)) { + $args = $this->getStackArgs($stackVars[$i]['args']); + $this->validateVariableMethodCall($object, $method); + $stackVars[$i]['variable'] = call_user_func_array([$object, $method], $args); } $last = $i; } diff --git a/lib/internal/Magento/Framework/Filter/Test/Unit/TemplateTest.php b/lib/internal/Magento/Framework/Filter/Test/Unit/TemplateTest.php index e7376d9b7d264..5c91931bfe996 100644 --- a/lib/internal/Magento/Framework/Filter/Test/Unit/TemplateTest.php +++ b/lib/internal/Magento/Framework/Filter/Test/Unit/TemplateTest.php @@ -205,4 +205,15 @@ public function varDirectiveDataProvider() ], ]; } + + /** + * Test adding callbacks when already filtering. + * + * @expectedException \InvalidArgumentException + */ + public function testInappropriateCallbacks() + { + $this->templateFilter->setVariables(['filter' => $this->templateFilter]); + $this->templateFilter->filter('Test {{var filter.addAfterFilterCallback(\'mb_strtolower\')}}'); + } } From f07f4a3f47c4a5189aeae12f3b4fe528a435c55c Mon Sep 17 00:00:00 2001 From: Dmitriy Kogut <kogut.dmitriy@gmail.com> Date: Thu, 10 Jan 2019 11:37:03 +0200 Subject: [PATCH 0194/1295] MAGETWO-97025: Check Url Rewrites Correctly Generated for Multiple Storeviews During Product Import --- .../Mftf/Section/AdminMessagesSection.xml | 4 +- .../AdminProductGridActionGroup.xml | 11 ++ .../SearchForProductOnBackendActionGroup.xml | 9 +- .../Mftf/Section/AdminProductGridSection.xml | 3 +- .../Store/Test/Mftf/Data/StoreData.xml | 20 ++- .../AdminNewStoreViewActionsSection.xml | 3 +- .../Section/AdminUrlRewriteIndexSection.xml | 3 +- ...tipleStoreviewsDuringProductImportTest.xml | 116 ++++++++++++++++++ .../acceptance/tests/_data/import_updated.csv | 4 + 9 files changed, 167 insertions(+), 6 deletions(-) create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml create mode 100644 dev/tests/acceptance/tests/_data/import_updated.csv diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml index 4af66986d9aa8..5040a08967fa3 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml @@ -6,11 +6,13 @@ */ --> -<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"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminMessagesSection"> <element name="test" type="input" selector=".test"/> <element name="success" type="text" selector="#messages div.message-success"/> <element name="successMessageByIndex" type="text" selector=".message.message-success.success:nth-of-type({{n}})>div" parameterized="true"/> <element name="error" type="text" selector="#messages div.message-error"/> + <element name="notice" type="text" selector=".message.message-notice.notice"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml index 054a5204004b6..59413a19a84bc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml @@ -52,6 +52,17 @@ <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial2"/> </actionGroup> + <actionGroup name="deleteProductByName" extends="deleteProductUsingProductGrid"> + <arguments> + <argument name="product" type="string" defaultValue=""/> + <argument name="name" type="string"/> + </arguments> + <remove keyForRemoval="fillProductSkuFilter"/> + <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{name}}" stepKey="fillProductSkuFilter" after="openProductFilters"/> + <remove keyForRemoval="seeProductSkuInGrid"/> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="{{name}}" stepKey="seeProductNameInGrid" after="clickApplyFilters"/> + </actionGroup> + <!--Disabled a product by filtering grid and using change status action--> <actionGroup name="ChangeStatusProductUsingProductGridActionGroup"> <arguments> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchForProductOnBackendActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchForProductOnBackendActionGroup.xml index ebeee87b1c89e..6cb939a7af410 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchForProductOnBackendActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchForProductOnBackendActionGroup.xml @@ -7,7 +7,7 @@ --> <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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="SearchForProductOnBackendActionGroup"> <arguments> <argument name="product" defaultValue="product"/> @@ -19,4 +19,11 @@ <fillField stepKey="fillSkuFieldOnFiltersSection" userInput="{{product.sku}}" selector="{{AdminProductFiltersSection.SkuInput}}"/> <click stepKey="clickApplyFiltersButton" selector="{{AdminProductFiltersSection.Apply}}"/> </actionGroup> + <actionGroup name="SearchForProductOnBackendByNameActionGroup" extends="SearchForProductOnBackendActionGroup"> + <arguments> + <argument name="productName" type="string"/> + </arguments> + <remove keyForRemoval="fillSkuFieldOnFiltersSection"/> + <fillField userInput="{{productName}}" selector="{{AdminProductFiltersSection.NameInput}}" after="cleanFiltersIfTheySet" stepKey="fillNameFieldOnFiltersSection"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml index 6ffecc341123d..e6d9cae3e9442 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridSection.xml @@ -7,8 +7,9 @@ --> <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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductGridSection"> + <element name="productRowBySku" type="block" selector="//div[@id='container']//tr//td[count(../../..//th[./*[.='SKU']]/preceding-sibling::th) + 1][./*[.='{{sku}}']]" parameterized="true" /> <element name="loadingMask" type="text" selector=".admin__data-grid-loading-mask[data-component*='product_listing']"/> <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="productGridElement1" type="input" selector="#addselector" /> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml index f85125bcf3291..9c9f6668a611e 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<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"> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="_defaultStore" type="store"> <data key="name">Default Store View</data> <data key="code">default</data> @@ -39,6 +39,24 @@ <data key="store_type">store</data> <requiredEntity type="storeGroup">customStoreGroup</requiredEntity> </entity> + <entity name="customStoreENNotUnique" type="store"> + <data key="name">EN</data> + <data key="code">en</data> + <data key="is_active">1</data> + <data key="store_id">null</data> + <data key="store_action">add</data> + <data key="store_type">store</data> + <requiredEntity type="storeGroup">customStoreGroup</requiredEntity> + </entity> + <entity name="customStoreNLNotUnique" type="store"> + <data key="name">NL</data> + <data key="code">nl</data> + <data key="is_active">1</data> + <data key="store_id">null</data> + <data key="store_action">add</data> + <data key="store_type">store</data> + <requiredEntity type="storeGroup">customStoreGroup</requiredEntity> + </entity> <entity name="secondStore" type="store"> <data key="name">Second Store View</data> <data key="code">second_store_view</data> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreViewActionsSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreViewActionsSection.xml index a3b5d1e616319..0e3a74fccf9f0 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreViewActionsSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreViewActionsSection.xml @@ -5,11 +5,12 @@ * 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"> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminNewStoreViewActionsSection"> <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="60"/> + <element name="loadingMask" type="text" selector=".loading-mask"/> </section> </sections> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteIndexSection.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteIndexSection.xml index 455748a0da534..ab847f924b5cf 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteIndexSection.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteIndexSection.xml @@ -7,9 +7,10 @@ --> <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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminUrlRewriteIndexSection"> <element name="requestPathFilter" type="input" selector="#urlrewriteGrid_filter_request_path"/> <element name="requestPathColumnValue" type="text" selector="//*[@id='urlrewriteGrid']//tbody//td[@data-column='request_path' and normalize-space(.)='{{columnValue}}']" parameterized="true"/> + <element name="targetPathColumnValue" type="text" selector="//*[@id='urlrewriteGrid']//tbody//td[@data-column='target_path' and normalize-space(.)='{{columnValue}}']" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml new file mode 100644 index 0000000000000..7e654b8d62c25 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest"> + <annotations> + <features value="Url Rewrite"/> + <stories value="Url Rewrites for Multiple Storeviews"/> + <title value="Url Rewrites Correctly Generated for Multiple Storeviews During Product Import"/> + <description value="Check Url Rewrites Correctly Generated for Multiple Storeviews During Product Import."/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-76287"/> + <group value="urlRewrite"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!-- Create Store View EN --> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewEn"> + <argument name="customStore" value="customStoreENNotUnique"/> + </actionGroup> + <!-- Create Store View NL --> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewNl"> + <argument name="customStore" value="customStoreNLNotUnique"/> + </actionGroup> + <createData entity="ApiCategory" stepKey="createCategory"> + <field key="name">category-admin</field> + </createData> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="deleteProductByName" stepKey="deleteImportedProduct"> + <argument name="product" value="productformagetwo76287"/> + <argument name="name" value="productformagetwo76287"/> + </actionGroup> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFiltersIfSet"/> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewEn"> + <argument name="customStore" value="customStoreENNotUnique"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewNl"> + <argument name="customStore" value="customStoreNLNotUnique"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <actionGroup ref="switchCategoryStoreView" stepKey="switchToStoreViewEn"> + <argument name="store" value="customStoreENNotUnique.name"/> + <argument name="catName" value="$$createCategory.name$$"/> + </actionGroup> + <uncheckOption selector="{{AdminCategoryBasicFieldSection.categoryNameUseDefault}}" stepKey="uncheckUseDefaultValueENStoreView"/> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="category-english" stepKey="changeNameField"/> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="clickOnSectionHeader"/> + <actionGroup ref="ChangeSeoUrlKeyForSubCategory" stepKey="changeSeoUrlKeyENStoreView"> + <argument name="value" value="category-english"/> + </actionGroup> + <actionGroup ref="switchCategoryStoreView" stepKey="switchToStoreViewNl"> + <argument name="store" value="customStoreNLNotUnique.name"/> + <argument name="catName" value="$$createCategory.name$$"/> + </actionGroup> + <uncheckOption selector="{{AdminCategoryBasicFieldSection.categoryNameUseDefault}}" stepKey="uncheckUseDefaultValue1"/> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="category-dutch" stepKey="changeNameFieldNLStoreView"/> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="clickOnSectionHeader2"/> + <actionGroup ref="ChangeSeoUrlKeyForSubCategory" stepKey="changeSeoUrlKeyNLStoreView"> + <argument name="value" value="category-dutch"/> + </actionGroup> + <amOnPage url="{{AdminImportIndexPage.url}}" stepKey="navigateToSystemImport"/> + <selectOption selector="{{AdminImportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> + <waitForElementVisible selector="{{AdminImportMainSection.importBehavior}}" stepKey="waitForImportBehaviorElementVisible"/> + <selectOption selector="{{AdminImportMainSection.importBehavior}}" userInput="Add/Update" stepKey="selectAddUpdateOption"/> + <attachFile selector="{{AdminImportMainSection.selectFileToImport}}" userInput="import_updated.csv" stepKey="attachFileForImport"/> + <click selector="{{AdminImportHeaderSection.checkDataButton}}" stepKey="clickCheckDataButton"/> + <see selector="{{AdminMessagesSection.notice}}" userInput="Checked rows: 3, checked entities: 1, invalid rows: 0, total errors: 0" stepKey="assertNotice"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="File is valid! To start import process press "Import" button" stepKey="assertSuccessMessage"/> + <click selector="{{AdminImportMainSection.importButton}}" stepKey="clickImportButton"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="Import successfully done" stepKey="assertSuccessMessage1"/> + <actionGroup ref="SearchForProductOnBackendByNameActionGroup" stepKey="searchForProductOnBackend"> + <argument name="productName" value="productformagetwo76287"/> + </actionGroup> + <click selector="{{AdminProductGridSection.productRowBySku('productformagetwo76287')}}" stepKey="clickOnProductRow"/> + <grabFromCurrentUrl regex="~/id/(\d+)/~" stepKey="grabProductIdFromUrl"/> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="goToUrlRewritesIndexPage"/> + + <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="category-english.html" stepKey="inputCategoryUrlForENStoreView"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('category-english.html')}}" stepKey="seeUrlInRequestPathColumn"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.targetPathColumnValue(catalog/category/view/id/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn"/> + + <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="category-dutch.html" stepKey="inputCategoryUrlForNLStoreView"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton1"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('category-dutch.html')}}" stepKey="seeUrlInRequestPathColumn1"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.targetPathColumnValue(catalog/category/view/id/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn1"/> + + <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="productformagetwo76287-english.html" stepKey="inputProductUrlForENStoreView"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton2"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('productformagetwo76287-english.html')}}" stepKey="seeUrlInRequestPathColumn2"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.targetPathColumnValue('catalog/product/view/id/$grabProductIdFromUrl')}}" stepKey="seeUrlInTargetPathColumn2"/> + + <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="productformagetwo76287-dutch.html" stepKey="inputProductUrlForENStoreView1"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton3"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('productformagetwo76287-dutch.html')}}" stepKey="seeUrlInRequestPathColumn3"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.targetPathColumnValue('catalog/product/view/id/$grabProductIdFromUrl')}}" stepKey="seeUrlInTargetPathColumn3"/> + + <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="category-english/productformagetwo76287-english.html" stepKey="inputProductUrlForENStoreView2"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton4"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('category-english/productformagetwo76287-english.html')}}" stepKey="seeUrlInRequestPathColumn4"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.targetPathColumnValue(catalog/product/view/id/$grabProductIdFromUrl/category/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn4"/> + + <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="category-dutch/productformagetwo76287-dutch.html" stepKey="inputProductUrlForENStoreView3"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton5"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('category-dutch/productformagetwo76287-dutch.html')}}" stepKey="seeUrlInRequestPathColumn5"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.targetPathColumnValue(catalog/product/view/id/$grabProductIdFromUrl/category/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn5"/> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/_data/import_updated.csv b/dev/tests/acceptance/tests/_data/import_updated.csv new file mode 100644 index 0000000000000..620af02641ecc --- /dev/null +++ b/dev/tests/acceptance/tests/_data/import_updated.csv @@ -0,0 +1,4 @@ +product_websites,store_view_code,attribute_set_code,product_type,categories,sku,price,name,url_key +base,,Default,simple,Default Category/category-admin,productformagetwo76287,123,productformagetwo76287,productformagetwo76287 +,en,Default,simple,,productformagetwo76287,,productformagetwo76287-english,productformagetwo76287-english +,nl,Default,simple,,productformagetwo76287,,productformagetwo76287-dutch,productformagetwo76287-dutch From 59a4e57a0c36671ff84e8398f413253610a512ac Mon Sep 17 00:00:00 2001 From: Graham Wharton <graham@gwharton.me.uk> Date: Thu, 10 Jan 2019 10:19:52 +0000 Subject: [PATCH 0195/1295] Reverted changes made in MAGETWO-96712 --- .../Mail/Template/TransportBuilder.php | 16 ---------- .../Unit/Template/TransportBuilderTest.php | 32 +++---------------- 2 files changed, 4 insertions(+), 44 deletions(-) diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php index 1580c0b5eef91..35e1651c6a5df 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php @@ -48,13 +48,6 @@ class TransportBuilder */ protected $templateOptions; - /** - * Mail from address - * - * @var string|array - */ - private $from; - /** * Mail Transport * @@ -280,7 +273,6 @@ protected function reset() $this->templateIdentifier = null; $this->templateVars = null; $this->templateOptions = null; - $this->from = null; return $this; } @@ -314,14 +306,6 @@ protected function prepareMessage() ->setBody($body) ->setSubject(html_entity_decode($template->getSubject(), ENT_QUOTES)); - if ($this->from) { - $from = $this->_senderResolver->resolve( - $this->from, - $template->getDesignConfig()->getStore() - ); - $this->message->setFrom($from['email'], $from['name']); - } - return $this; } } diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php index b18f5d2753ee6..8b3cc7df9a013 100644 --- a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php +++ b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php @@ -7,7 +7,6 @@ namespace Magento\Framework\Mail\Test\Unit\Template; use Magento\Framework\App\TemplateTypesInterface; -use Magento\Framework\DataObject; use Magento\Framework\Mail\MessageInterface; /** @@ -100,37 +99,17 @@ protected function setUp() */ public function testGetTransport($templateType, $messageType, $bodyText, $templateNamespace) { + $this->builder->setTemplateModel($templateNamespace); + $vars = ['reason' => 'Reason', 'customer' => 'Customer']; $options = ['area' => 'frontend', 'store' => 1]; - $from = 'email_from'; - $sender = ['email' => 'from@example.com', 'name' => 'name']; - - $this->builder->setTemplateModel($templateNamespace); - $this->builder->setFrom($from); - $template = $this->createPartialMock( - \Magento\Framework\Mail\TemplateInterface::class, - [ - 'setVars', - 'isPlain', - 'setOptions', - 'getSubject', - 'getType', - 'processTemplate', - 'getDesignConfig', - ] - ); + $template = $this->createMock(\Magento\Framework\Mail\TemplateInterface::class); $template->expects($this->once())->method('setVars')->with($this->equalTo($vars))->willReturnSelf(); $template->expects($this->once())->method('setOptions')->with($this->equalTo($options))->willReturnSelf(); $template->expects($this->once())->method('getSubject')->willReturn('Email Subject'); $template->expects($this->once())->method('getType')->willReturn($templateType); $template->expects($this->once())->method('processTemplate')->willReturn($bodyText); - $template->method('getDesignConfig')->willReturn(new DataObject($options)); - - $this->senderResolverMock->expects($this->once()) - ->method('resolve') - ->with($from, 1) - ->willReturn($sender); $this->templateFactoryMock->expects($this->once()) ->method('get') @@ -149,9 +128,6 @@ public function testGetTransport($templateType, $messageType, $bodyText, $templa ->method('setBody') ->with($this->equalTo($bodyText)) ->willReturnSelf(); - $this->messageMock->method('setFrom') - ->with($sender['email'], $sender['name']) - ->willReturnSelf(); $transport = $this->createMock(\Magento\Framework\Mail\TransportInterface::class); @@ -184,7 +160,7 @@ public function getTransportDataProvider() ] ]; } - + /** * @return void */ From a8bcb8bb8442a649bf1e62400ca6f147023e53c6 Mon Sep 17 00:00:00 2001 From: Graham Wharton <graham@gwharton.me.uk> Date: Thu, 10 Jan 2019 10:31:49 +0000 Subject: [PATCH 0196/1295] Deprecated Magento\Framework\Mail\Message::setFrom function, introduced new function setFromAddress --- lib/internal/Magento/Framework/Mail/Message.php | 15 ++++++++++++++- .../Framework/Mail/Template/TransportBuilder.php | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Mail/Message.php b/lib/internal/Magento/Framework/Mail/Message.php index c8f0e75c52800..96566ad82b8eb 100644 --- a/lib/internal/Magento/Framework/Mail/Message.php +++ b/lib/internal/Magento/Framework/Mail/Message.php @@ -89,10 +89,23 @@ public function getBody() /** * @inheritdoc + * + * @deprecated This function is missing the from name. The + * setFromAddress() function sets both from address and from name. + * @see setFromAddress() */ public function setFrom($fromAddress) { - $this->zendMessage->setFrom($fromAddress); + $this->setFromAddress($fromAddress, null); + return $this; + } + + /** + * @inheritdoc + */ + public function setFromAddress($fromAddress, $fromName = null) + { + $this->zendMessage->setFrom($fromAddress, $fromName); return $this; } diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php index 35e1651c6a5df..25cbaf73b8d76 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php @@ -196,7 +196,7 @@ public function setFrom($from) public function setFromByStore($from, $store = null) { $result = $this->_senderResolver->resolve($from, $store); - $this->message->setFrom($result['email'], $result['name']); + $this->message->setFromAddress($result['email'], $result['name']); return $this; } From 573cf7a9fee7c2d8ca9c1c25fe909c93ccf06c49 Mon Sep 17 00:00:00 2001 From: Graham Wharton <graham@gwharton.me.uk> Date: Thu, 10 Jan 2019 11:26:35 +0000 Subject: [PATCH 0197/1295] Fixed testSetFromByStore unit test --- .../Framework/Mail/Test/Unit/Template/TransportBuilderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php index 8b3cc7df9a013..77c41cceeb285 100644 --- a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php +++ b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php @@ -173,7 +173,7 @@ public function testSetFromByStore() ->with($sender, $store) ->willReturn($sender); $this->messageMock->expects($this->once()) - ->method('setFrom') + ->method('setFromAddress') ->with('from@example.com', 'name') ->willReturnSelf(); From b853f997ae07780ff80ece1c06de2c5f9a9ba27b Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Thu, 10 Jan 2019 13:58:43 +0200 Subject: [PATCH 0198/1295] MAGETWO-94112: Configurable "As low As" Product Price Not Updating Correctly --- .../Catalog/view/base/web/js/price-box.js | 6 +- .../view/frontend/web/js/configurable.js | 63 +++++-------------- .../view/frontend/web/js/swatch-renderer.js | 6 +- 3 files changed, 17 insertions(+), 58 deletions(-) diff --git a/app/code/Magento/Catalog/view/base/web/js/price-box.js b/app/code/Magento/Catalog/view/base/web/js/price-box.js index de68d769885fd..783d39cddbc76 100644 --- a/app/code/Magento/Catalog/view/base/web/js/price-box.js +++ b/app/code/Magento/Catalog/view/base/web/js/price-box.js @@ -78,11 +78,7 @@ define([ pricesCode = [], priceValue, origin, finalPrice; - if (typeof newPrices !== 'undefined' && newPrices.hasOwnProperty('prices')) { - this.cache.additionalPriceObject = {}; - } else { - this.cache.additionalPriceObject = this.cache.additionalPriceObject || {}; - } + this.cache.additionalPriceObject = this.cache.additionalPriceObject || {}; if (newPrices) { $.extend(this.cache.additionalPriceObject, newPrices); diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js index 6357bbd6c7c0c..1df84d27a5c30 100644 --- a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js +++ b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js @@ -376,7 +376,8 @@ define([ basePrice = parseFloat(this.options.spConfig.prices.basePrice.amount), optionFinalPrice, optionPriceDiff, - optionPrices = this.options.spConfig.optionPrices; + optionPrices = this.options.spConfig.optionPrices, + allowedProductMinPrice; this._clearSelect(element); element.options[0] = new Option('', ''); @@ -407,8 +408,8 @@ define([ if (typeof allowedProducts[0] !== 'undefined' && typeof optionPrices[allowedProducts[0]] !== 'undefined') { - - optionFinalPrice = parseFloat(optionPrices[allowedProducts[0]].finalPrice.amount); + allowedProductMinPrice = this._getAllowedProductWithMinPrice(allowedProducts); + optionFinalPrice = parseFloat(optionPrices[allowedProductMinPrice].finalPrice.amount); optionPriceDiff = optionFinalPrice - basePrice; if (optionPriceDiff !== 0) { @@ -489,36 +490,27 @@ define([ _getPrices: function () { var prices = {}, elements = _.toArray(this.options.settings), - hasProductPrice = false, - optionPriceDiff = 0, - allowedProduct, optionPrices, basePrice, optionFinalPrice; + allowedProduct; _.each(elements, function (element) { var selected = element.options[element.selectedIndex], config = selected && selected.config, priceValue = {}; - if (config && config.allowedProducts.length === 1 && !hasProductPrice) { - prices = {}; + if (config && config.allowedProducts.length === 1) { priceValue = this._calculatePrice(config); - hasProductPrice = true; } else if (element.value) { allowedProduct = this._getAllowedProductWithMinPrice(config.allowedProducts); - optionPrices = this.options.spConfig.optionPrices; - basePrice = parseFloat(this.options.spConfig.prices.basePrice.amount); - - if (!_.isEmpty(allowedProduct)) { - optionFinalPrice = parseFloat(optionPrices[allowedProduct].finalPrice.amount); - optionPriceDiff = optionFinalPrice - basePrice; - } - - if (optionPriceDiff !== 0) { - prices = {}; - priceValue = this._calculatePriceDifference(allowedProduct); - } + priceValue = this._calculatePrice({ + 'allowedProducts': [ + allowedProduct + ] + }); } - prices[element.attributeId] = priceValue; + if (!_.isEmpty(priceValue)) { + prices.prices = priceValue; + } }, this); return prices; @@ -539,40 +531,15 @@ define([ _.each(allowedProducts, function (allowedProduct) { optionFinalPrice = parseFloat(optionPrices[allowedProduct].finalPrice.amount); - if (_.isEmpty(product)) { + if (_.isEmpty(product) || optionFinalPrice < optionMinPrice) { optionMinPrice = optionFinalPrice; product = allowedProduct; } - - if (optionFinalPrice < optionMinPrice) { - product = allowedProduct; - } }, this); return product; }, - /** - * Calculate price difference for allowed product - * - * @param {*} allowedProduct - Product - * @returns {*} - * @private - */ - _calculatePriceDifference: function (allowedProduct) { - var displayPrices = $(this.options.priceHolderSelector).priceBox('option').prices, - newPrices = this.options.spConfig.optionPrices[allowedProduct]; - - _.each(displayPrices, function (price, code) { - - if (newPrices[code]) { - displayPrices[code].amount = newPrices[code].amount - displayPrices[code].amount; - } - }); - - return displayPrices; - }, - /** * Returns prices for configured products * 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 4b8a96f57e53e..1145d3acc11ed 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 @@ -1030,14 +1030,10 @@ define([ _.each(allowedProducts, function (allowedProduct) { optionFinalPrice = parseFloat(optionPrices[allowedProduct].finalPrice.amount); - if (_.isEmpty(product)) { + if (_.isEmpty(product) || optionFinalPrice < optionMinPrice) { optionMinPrice = optionFinalPrice; product = allowedProduct; } - - if (optionFinalPrice < optionMinPrice) { - product = allowedProduct; - } }, this); return product; From 3b4581c25627735acb63aedec5d0acd234d8c98b Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Thu, 10 Jan 2019 16:57:30 +0200 Subject: [PATCH 0199/1295] ISSUE-16198: Category image remain after deleted. --- .../RedundantCategoryImageChecker.php | 52 ++++++++++++ .../Category/RemoveRedundantImagePlugin.php | 75 +++++++++++++++++ app/code/Magento/Catalog/etc/di.xml | 8 ++ .../Model/RemoveRedundantImageTest.php | 82 +++++++++++++++++++ .../Catalog/_files/categories_with_image.php | 53 ++++++++++++ .../_files/categories_with_image_rollback.php | 23 ++++++ 6 files changed, 293 insertions(+) create mode 100644 app/code/Magento/Catalog/Model/ResourceModel/Category/RedundantCategoryImageChecker.php create mode 100644 app/code/Magento/Catalog/Plugin/Model/ResourceModel/Category/RemoveRedundantImagePlugin.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/RemoveRedundantImageTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/categories_with_image.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/categories_with_image_rollback.php diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category/RedundantCategoryImageChecker.php b/app/code/Magento/Catalog/Model/ResourceModel/Category/RedundantCategoryImageChecker.php new file mode 100644 index 0000000000000..b683bcd803bd3 --- /dev/null +++ b/app/code/Magento/Catalog/Model/ResourceModel/Category/RedundantCategoryImageChecker.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\ResourceModel\Category; + +use Magento\Catalog\Api\CategoryListInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; + +/** + * Check if Image is currently used in any category as Category Image. + */ +class RedundantCategoryImageChecker +{ + /** + * @var SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + + /** + * @var CategoryListInterface + */ + private $categoryList; + + public function __construct( + CategoryListInterface $categoryList, + SearchCriteriaBuilder $searchCriteriaBuilder + ) { + $this->categoryList = $categoryList; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + } + + /** + * Checks if Image is currently used in any category as Category Image. + * + * Returns true if not. + * + * @param string $imageName + * @return bool + */ + public function execute(string $imageName): bool + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteria = $this->searchCriteriaBuilder->addFilter('image', $imageName)->create(); + $categories = $this->categoryList->getList($searchCriteria)->getItems(); + + return empty($categories); + } +} diff --git a/app/code/Magento/Catalog/Plugin/Model/ResourceModel/Category/RemoveRedundantImagePlugin.php b/app/code/Magento/Catalog/Plugin/Model/ResourceModel/Category/RemoveRedundantImagePlugin.php new file mode 100644 index 0000000000000..2d2f8eb50d52d --- /dev/null +++ b/app/code/Magento/Catalog/Plugin/Model/ResourceModel/Category/RemoveRedundantImagePlugin.php @@ -0,0 +1,75 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Plugin\Model\ResourceModel\Category; + +use Magento\Catalog\Model\ResourceModel\Category as CategoryResource; +use Magento\Catalog\Model\ImageUploader; +use Magento\Catalog\Model\ResourceModel\Category\RedundantCategoryImageChecker; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Filesystem; +use Magento\Framework\Model\AbstractModel; + +/** + * Remove old Category Image file from pub/media/catalog/category directory if such Image is not used anymore. + */ +class RemoveRedundantImagePlugin +{ + /** + * @var Filesystem + */ + private $filesystem; + + /** + * @var ImageUploader + */ + private $imageUploader; + + /** + * @var RedundantCategoryImageChecker + */ + private $redundantCategoryImageChecker; + + public function __construct( + Filesystem $filesystem, + ImageUploader $imageUploader, + RedundantCategoryImageChecker $redundantCategoryImageChecker + ) { + $this->filesystem = $filesystem; + $this->imageUploader = $imageUploader; + $this->redundantCategoryImageChecker = $redundantCategoryImageChecker; + } + + /** + * Removes Image file if it is not used anymore. + * + * @param CategoryResource $subject + * @param CategoryResource $result + * @param AbstractModel $category + * @return CategoryResource + * + * @throws \Magento\Framework\Exception\FileSystemException + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterSave(CategoryResource $subject, CategoryResource $result, AbstractModel $category): CategoryResource + { + $originalImage = $category->getOrigData('image'); + if ( + null !== $originalImage + && $originalImage !== $category->getImage() + && $this->redundantCategoryImageChecker->execute($originalImage) + ) { + $basePath = $this->imageUploader->getBasePath(); + $baseImagePath = $this->imageUploader->getFilePath($basePath, $originalImage); + /** @var \Magento\Framework\Filesystem\Directory\WriteInterface $mediaDirectory */ + $mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA); + $mediaDirectory->delete($baseImagePath); + } + + return $result; + } +} diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index 5b87c7d6ac030..1d3e9a931b677 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -907,6 +907,14 @@ <type name="Magento\Quote\Model\Quote\Item\ToOrderItem"> <plugin name="copy_quote_files_to_order" type="Magento\Catalog\Model\Plugin\QuoteItemProductOption"/> </type> + <type name="Magento\Catalog\Model\ResourceModel\Category"> + <plugin name="remove_redundant_image" type="Magento\Catalog\Plugin\Model\ResourceModel\Category\RemoveRedundantImagePlugin"/> + </type> + <type name="Magento\Catalog\Plugin\Model\ResourceModel\Category\RemoveRedundantImagePlugin"> + <arguments> + <argument name="imageUploader" xsi:type="object">Magento\Catalog\CategoryImageUpload</argument> + </arguments> + </type> <preference for="Magento\Catalog\Model\ResourceModel\Product\BaseSelectProcessorInterface" type="Magento\Catalog\Model\ResourceModel\Product\CompositeWithWebsiteProcessor" /> <type name="Magento\Catalog\Model\ResourceModel\Product\CompositeBaseSelectProcessor"> <arguments> diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/RemoveRedundantImageTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/RemoveRedundantImageTest.php new file mode 100644 index 0000000000000..1bc545a25ac32 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/RemoveRedundantImageTest.php @@ -0,0 +1,82 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model; + +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\WriteInterface; +use Magento\Framework\ObjectManagerInterface; + +/** + * Test removing old Category Image file from pub/media/catalog/category directory if such Image is not used anymore. + */ +class RemoveRedundantImageTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var Filesystem + */ + private $filesystem; + + /** + * @var WriteInterface + */ + private $mediaDirectory; + + /** + * @var CategoryRepository + */ + private $categoryRepository; + + protected function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + /** @var Filesystem $filesystem */ + $this->filesystem = $this->objectManager->get(Filesystem::class); + $this->mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA); + $this->categoryRepository = $this->objectManager->get(CategoryRepository::class); + } + + /** + * Tests removing Image file if it is not used anymore. + * + * @magentoDataFixture Magento/Catalog/_files/categories_with_image.php + * + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + */ + public function testRemoveRedundantImage() + { + $imagesPath = 'catalog' . DIRECTORY_SEPARATOR . 'category'; + $absoluteImagesPath = $this->mediaDirectory->getAbsolutePath($imagesPath); + $filePath1 = $absoluteImagesPath . DIRECTORY_SEPARATOR . 'test_image_1.jpg'; + $filePath2 = $absoluteImagesPath . DIRECTORY_SEPARATOR . 'test_image_2.jpg'; + $this->mediaDirectory->create($absoluteImagesPath); + $this->mediaDirectory->touch($filePath1); + $this->mediaDirectory->touch($filePath2); + + $category1 = $this->categoryRepository->get(3); + $category1->setImage('test_image_3.jpg'); + $this->categoryRepository->save($category1); + $category2 = $this->categoryRepository->get(5); + $category2->setImage('test_image_3.jpg'); + $this->categoryRepository->save($category2); + + $this->assertTrue($this->mediaDirectory->isExist($filePath1)); + $this->assertFalse($this->mediaDirectory->isExist($filePath2)); + } + + protected function tearDown() + { + $this->mediaDirectory->delete(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_with_image.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_with_image.php new file mode 100644 index 0000000000000..6fc0f4e8b6efa --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_with_image.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** + * After installation system has two categories: root one with ID:1 and Default category with ID:2 + */ +/** @var $category \Magento\Catalog\Model\Category */ +$category = $objectManager->create(\Magento\Catalog\Model\Category::class); +$category->isObjectNew(true); +$category->setId(3) + ->setName('Category 1') + ->setParentId(2) + ->setPath('1/2/3') + ->setLevel(2) + ->setAvailableSortBy('name') + ->setDefaultSortBy('name') + ->setIsActive(true) + ->setPosition(1) + ->setImage('test_image_1.jpg') + ->save(); + +$category = $objectManager->create(\Magento\Catalog\Model\Category::class); +$category->isObjectNew(true); +$category->setId(4) + ->setName('Category 1.1') + ->setParentId(3) + ->setPath('1/2/3/4') + ->setLevel(3) + ->setAvailableSortBy('name') + ->setDefaultSortBy('name') + ->setIsActive(true) + ->setIsAnchor(true) + ->setPosition(1) + ->setImage('test_image_1.jpg') + ->save(); + +$category = $objectManager->create(\Magento\Catalog\Model\Category::class); +$category->isObjectNew(true); +$category->setId(5) + ->setName('Category 2') + ->setParentId(2) + ->setPath('1/2/5') + ->setLevel(2) + ->setAvailableSortBy('name') + ->setDefaultSortBy('name') + ->setIsActive(true) + ->setPosition(2) + ->setImage('test_image_2.jpg') + ->save(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_with_image_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_with_image_rollback.php new file mode 100644 index 0000000000000..d290a164f639e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_with_image_rollback.php @@ -0,0 +1,23 @@ +<?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); + +//Remove categories +/** @var Magento\Catalog\Model\ResourceModel\Category\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); From 5099a0afb0a34e3525566e8f0e5e800045e48e46 Mon Sep 17 00:00:00 2001 From: roman <rleshchenko@magento.com> Date: Thu, 10 Jan 2019 17:36:46 +0200 Subject: [PATCH 0200/1295] MAGETWO-90516: Wrong custom option behavior --- .../Catalog/Block/Product/View/Options/Type/Select.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Product/View/Options/Type/Select.php b/app/code/Magento/Catalog/Block/Product/View/Options/Type/Select.php index 959a6e76dc1ab..429e8fb44316d 100644 --- a/app/code/Magento/Catalog/Block/Product/View/Options/Type/Select.php +++ b/app/code/Magento/Catalog/Block/Product/View/Options/Type/Select.php @@ -25,12 +25,12 @@ class Select extends \Magento\Catalog\Block\Product\View\Options\AbstractOptions /** * @var CheckableFactory */ - protected $checkableFactory; + private $checkableFactory; /** * @var MultipleFactory */ - protected $multipleFactory; + private $multipleFactory; /** * Select constructor. From 478b8d33e3775b89377179e9cacb6f96c3a5e730 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Thu, 10 Jan 2019 18:09:41 +0200 Subject: [PATCH 0201/1295] MAGETWO-97563: [FT] [MFTF] StorefrontCheckAmountLimitWishlistTest fails because of bad design - MFTF test fix --- .../Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml index 4267da896ea96..723acc011ed50 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml @@ -95,11 +95,13 @@ <argument name="quantity" type="string"/> <argument name="errorNum" type="string"/> </arguments> + <scrollToTopOfPage stepKey="scrollToTop1"/> <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.productInfoByName(product.name)}}" stepKey="mouseOverOnProduct" /> <fillField selector="{{StorefrontCustomerWishlistProductSection.productDescription(product.name)}}" userInput="{{description}}" stepKey="fillDescription"/> <fillField selector="{{StorefrontCustomerWishlistProductSection.productQuantity(product.name)}}" userInput="{{quantity}}" stepKey="fillQuantity"/> <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.productAddAllToCart}}" stepKey="mouseOver"/> <click selector="{{StorefrontCustomerWishlistProductSection.productUpdateWishList}}" stepKey="clickAddToWishlistButton"/> + <scrollToTopOfPage stepKey="scrollToTop2"/> <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.productInfoByName(product.name)}}" stepKey="wishlistMoveMouseOverProduct" /> <!--Check error message--> <see selector="{{StorefrontCustomerWishlistProductSection.productQtyError(product.name)}}" userInput="The maximum you may purchase is {{errorNum}}." stepKey="checkQtyError"/> From 03c657da9ecf05c190a914f5563e65aff5ddd28a Mon Sep 17 00:00:00 2001 From: Dmytro Voskoboinikov <voskoboi@adobe.com> Date: Thu, 10 Jan 2019 10:48:46 -0600 Subject: [PATCH 0202/1295] MAGETWO-90468: Added posibility to use captcha on share wishlist page --- app/code/Magento/Wishlist/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Wishlist/composer.json b/app/code/Magento/Wishlist/composer.json index 7f2cbce61d0c1..4553fa4fc3d86 100644 --- a/app/code/Magento/Wishlist/composer.json +++ b/app/code/Magento/Wishlist/composer.json @@ -13,7 +13,7 @@ "magento/module-sales": "101.0.*", "magento/framework": "101.0.*", "magento/module-ui": "101.0.*", - "magento/module-captcha": "100.2.2" + "magento/module-captcha": "100.2.*" }, "suggest": { "magento/module-configurable-product": "100.2.*", From f8df2dfd8a99202b52480428618f4c33966a7ba1 Mon Sep 17 00:00:00 2001 From: NazarKlovanych <nazarn96@gmail.com> Date: Wed, 26 Dec 2018 15:57:44 +0200 Subject: [PATCH 0203/1295] Fix issue 19887 creating new shipment: getting all trackers. --- .../Sales/view/frontend/templates/email/shipment/track.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/view/frontend/templates/email/shipment/track.phtml b/app/code/Magento/Sales/view/frontend/templates/email/shipment/track.phtml index 9f7146ab084df..46566c2773f45 100644 --- a/app/code/Magento/Sales/view/frontend/templates/email/shipment/track.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/email/shipment/track.phtml @@ -19,7 +19,7 @@ </tr> </thead> <tbody> - <?php foreach ($_shipment->getAllTracks() as $_item): ?> + <?php foreach ($_order->getTracksCollection($_shipment->getId()) as $_item): ?> <tr> <td><?= $block->escapeHtml($_item->getTitle()) ?>:</td> <td><?= $block->escapeHtml($_item->getNumber()) ?></td> From 8b59fc0a3870b5af87256e83353f65e8d676986a Mon Sep 17 00:00:00 2001 From: NazarKlovanych <nazarn96@gmail.com> Date: Wed, 26 Dec 2018 16:26:02 +0200 Subject: [PATCH 0204/1295] Refactoring --- .../templates/email/shipment/track.phtml | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Sales/view/frontend/templates/email/shipment/track.phtml b/app/code/Magento/Sales/view/frontend/templates/email/shipment/track.phtml index 46566c2773f45..f1cd5f2b99865 100644 --- a/app/code/Magento/Sales/view/frontend/templates/email/shipment/track.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/email/shipment/track.phtml @@ -9,22 +9,23 @@ ?> <?php $_shipment = $block->getShipment() ?> <?php $_order = $block->getOrder() ?> -<?php if ($_shipment && $_order && $_shipment->getAllTracks()): ?> -<br /> -<table class="shipment-track"> - <thead> +<?php $trackCollection = $_order->getTracksCollection($_shipment->getId()) ?> +<?php if ($_shipment && $_order && $trackCollection): ?> + <br /> + <table class="shipment-track"> + <thead> <tr> <th><?= /* @escapeNotVerified */ __('Shipped By') ?></th> <th><?= /* @escapeNotVerified */ __('Tracking Number') ?></th> </tr> - </thead> - <tbody> - <?php foreach ($_order->getTracksCollection($_shipment->getId()) as $_item): ?> - <tr> - <td><?= $block->escapeHtml($_item->getTitle()) ?>:</td> - <td><?= $block->escapeHtml($_item->getNumber()) ?></td> - </tr> - <?php endforeach ?> - </tbody> -</table> + </thead> + <tbody> + <?php foreach ($trackCollection as $_item): ?> + <tr> + <td><?= $block->escapeHtml($_item->getTitle()) ?>:</td> + <td><?= $block->escapeHtml($_item->getNumber()) ?></td> + </tr> + <?php endforeach ?> + </tbody> + </table> <?php endif; ?> From 53c188d570b7ccf69984413051cbe37e0a62f00c Mon Sep 17 00:00:00 2001 From: bradleyfrye <bradleyfrye@gmail.com> Date: Thu, 20 Dec 2018 17:33:21 -0600 Subject: [PATCH 0205/1295] Move website_name column into columnSet One column was outside the columnset in this layout, for some reason. Moving this so the grid shows the user's website name --- .../layout/sales_order_create_customer_block.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_create_customer_block.xml b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_create_customer_block.xml index c321bee460e46..0f5a3559f3008 100644 --- a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_create_customer_block.xml +++ b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_create_customer_block.xml @@ -80,13 +80,13 @@ <argument name="align" xsi:type="string">center</argument> </arguments> </block> - </block> - <block class="Magento\Backend\Block\Widget\Grid\Column" name="adminhtml.customer.grid.columnSet.website_name" as="website_name"> - <arguments> - <argument name="header" xsi:type="string" translate="true">Website</argument> - <argument name="index" xsi:type="string">website_name</argument> - <argument name="align" xsi:type="string">center</argument> - </arguments> + <block class="Magento\Backend\Block\Widget\Grid\Column" name="adminhtml.customer.grid.columnSet.website_name" as="website_name"> + <arguments> + <argument name="header" xsi:type="string" translate="true">Website</argument> + <argument name="index" xsi:type="string">website_name</argument> + <argument name="align" xsi:type="string">center</argument> + </arguments> + </block> </block> </block> </referenceBlock> From 6e30ee3e9df0a0f116bd035140c36a93a125c122 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Fri, 11 Jan 2019 11:05:10 +0200 Subject: [PATCH 0206/1295] MAGETWO-97563: [FT] [MFTF] StorefrontCheckAmountLimitWishlistTest fails because of bad design - MFTF test fix --- .../ActionGroup/StorefrontCustomerWishlistActionGroup.xml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml index 723acc011ed50..920b387441ada 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml @@ -99,11 +99,14 @@ <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.productInfoByName(product.name)}}" stepKey="mouseOverOnProduct" /> <fillField selector="{{StorefrontCustomerWishlistProductSection.productDescription(product.name)}}" userInput="{{description}}" stepKey="fillDescription"/> <fillField selector="{{StorefrontCustomerWishlistProductSection.productQuantity(product.name)}}" userInput="{{quantity}}" stepKey="fillQuantity"/> - <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.productAddAllToCart}}" stepKey="mouseOver"/> + <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.productAddAllToCart}}" stepKey="mouseOver1"/> <click selector="{{StorefrontCustomerWishlistProductSection.productUpdateWishList}}" stepKey="clickAddToWishlistButton"/> + <waitForElement selector="{{StorefrontCustomerWishlistProductSection.productQtyError(product.name)}}" stepKey="waitForErrorMessage"/> <scrollToTopOfPage stepKey="scrollToTop2"/> - <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.productInfoByName(product.name)}}" stepKey="wishlistMoveMouseOverProduct" /> + <!--Check error message--> + <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.productInfoByName(product.name)}}" stepKey="wishlistMoveMouseOverProduct" /> <see selector="{{StorefrontCustomerWishlistProductSection.productQtyError(product.name)}}" userInput="The maximum you may purchase is {{errorNum}}." stepKey="checkQtyError"/> + <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.productAddAllToCart}}" stepKey="mouseOver2"/> </actionGroup> </actionGroups> From 073b8442dae83069336df80b8021ed818cab1aa9 Mon Sep 17 00:00:00 2001 From: Dmitriy Kogut <kogut.dmitriy@gmail.com> Date: Fri, 11 Jan 2019 11:13:47 +0200 Subject: [PATCH 0207/1295] MAGETWO-97025: Check Url Rewrites Correctly Generated for Multiple Storeviews During Product Import --- .../ActionGroup/AdminProductGridActionGroup.xml | 9 ++++----- .../Magento/Store/Test/Mftf/Data/StoreData.xml | 4 ++-- .../Section/AdminNewStoreViewActionsSection.xml | 1 - ...MultipleStoreviewsDuringProductImportTest.xml | 16 +++++++--------- 4 files changed, 13 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml index 59413a19a84bc..720314c652dc7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml @@ -52,15 +52,14 @@ <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial2"/> </actionGroup> - <actionGroup name="deleteProductByName" extends="deleteProductUsingProductGrid"> + <actionGroup name="DeleteProductByName" extends="deleteProductUsingProductGrid"> <arguments> - <argument name="product" type="string" defaultValue=""/> - <argument name="name" type="string"/> + <argument name="product" type="string"/> </arguments> <remove keyForRemoval="fillProductSkuFilter"/> - <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{name}}" stepKey="fillProductSkuFilter" after="openProductFilters"/> + <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{product}}" stepKey="fillProductSkuFilter" after="openProductFilters"/> <remove keyForRemoval="seeProductSkuInGrid"/> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="{{name}}" stepKey="seeProductNameInGrid" after="clickApplyFilters"/> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="{{product}}" stepKey="seeProductNameInGrid" after="clickApplyFilters"/> </actionGroup> <!--Disabled a product by filtering grid and using change status action--> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml index 9c9f6668a611e..6d4fb7e9b8154 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml @@ -39,7 +39,7 @@ <data key="store_type">store</data> <requiredEntity type="storeGroup">customStoreGroup</requiredEntity> </entity> - <entity name="customStoreENNotUnique" type="store"> + <entity name="CustomStoreENNotUnique" type="store"> <data key="name">EN</data> <data key="code">en</data> <data key="is_active">1</data> @@ -48,7 +48,7 @@ <data key="store_type">store</data> <requiredEntity type="storeGroup">customStoreGroup</requiredEntity> </entity> - <entity name="customStoreNLNotUnique" type="store"> + <entity name="CustomStoreNLNotUnique" type="store"> <data key="name">NL</data> <data key="code">nl</data> <data key="is_active">1</data> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreViewActionsSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreViewActionsSection.xml index 0e3a74fccf9f0..faffc69dc6975 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreViewActionsSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminNewStoreViewActionsSection.xml @@ -11,6 +11,5 @@ <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="60"/> - <element name="loadingMask" type="text" selector=".loading-mask"/> </section> </sections> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml index 7e654b8d62c25..cfe96fc1c33f7 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml @@ -21,11 +21,11 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!-- Create Store View EN --> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewEn"> - <argument name="customStore" value="customStoreENNotUnique"/> + <argument name="customStore" value="CustomStoreENNotUnique"/> </actionGroup> <!-- Create Store View NL --> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewNl"> - <argument name="customStore" value="customStoreNLNotUnique"/> + <argument name="customStore" value="CustomStoreNLNotUnique"/> </actionGroup> <createData entity="ApiCategory" stepKey="createCategory"> <field key="name">category-admin</field> @@ -33,21 +33,19 @@ </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="deleteProductByName" stepKey="deleteImportedProduct"> + <actionGroup ref="DeleteProductByName" stepKey="deleteImportedProduct"> <argument name="product" value="productformagetwo76287"/> - <argument name="name" value="productformagetwo76287"/> </actionGroup> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFiltersIfSet"/> <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewEn"> - <argument name="customStore" value="customStoreENNotUnique"/> + <argument name="customStore" value="CustomStoreENNotUnique"/> </actionGroup> <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewNl"> - <argument name="customStore" value="customStoreNLNotUnique"/> + <argument name="customStore" value="CustomStoreNLNotUnique"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> </after> <actionGroup ref="switchCategoryStoreView" stepKey="switchToStoreViewEn"> - <argument name="store" value="customStoreENNotUnique.name"/> + <argument name="store" value="CustomStoreENNotUnique.name"/> <argument name="catName" value="$$createCategory.name$$"/> </actionGroup> <uncheckOption selector="{{AdminCategoryBasicFieldSection.categoryNameUseDefault}}" stepKey="uncheckUseDefaultValueENStoreView"/> @@ -57,7 +55,7 @@ <argument name="value" value="category-english"/> </actionGroup> <actionGroup ref="switchCategoryStoreView" stepKey="switchToStoreViewNl"> - <argument name="store" value="customStoreNLNotUnique.name"/> + <argument name="store" value="CustomStoreNLNotUnique.name"/> <argument name="catName" value="$$createCategory.name$$"/> </actionGroup> <uncheckOption selector="{{AdminCategoryBasicFieldSection.categoryNameUseDefault}}" stepKey="uncheckUseDefaultValue1"/> From bad9cf5887824a25833bc9b16b8b2123d5c7f5cf Mon Sep 17 00:00:00 2001 From: Stas Puga <stas.puga@transoftgroup.com> Date: Fri, 11 Jan 2019 12:11:36 +0200 Subject: [PATCH 0208/1295] MAGETWO-96229: [Staging] Cart Price Rule >> Schedule New Update - Select from existing Scheduled Update --- .../Mftf/ActionGroup/AdminFilterCartPriceRuleActionGroup.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminFilterCartPriceRuleActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminFilterCartPriceRuleActionGroup.xml index 35e1bee0952cf..2c44fdf3e900f 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminFilterCartPriceRuleActionGroup.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminFilterCartPriceRuleActionGroup.xml @@ -16,6 +16,5 @@ <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> <fillField selector="{{AdminCartPriceRulesSection.filterByNameInput}}" userInput="{{ruleName}}" stepKey="filterByName"/> <click selector="{{AdminCartPriceRulesSection.searchButton}}" stepKey="doFilter"/> - <click selector="{{AdminCartPriceRulesSection.rowByIndex('1')}}" stepKey="goToEditRulePage"/> </actionGroup> </actionGroups> From 49607ac12e9c2e2316e6adc6ee926b043cbae212 Mon Sep 17 00:00:00 2001 From: Nikita Fomin <fmnnkt@gmail.com> Date: Fri, 11 Jan 2019 13:45:04 +0200 Subject: [PATCH 0209/1295] MAGETWO-93326: Automate with MFTF Move Update to Another existing Update --- .../Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml index 28ee9efc9f0a2..cbe5d9e158bb3 100644 --- a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml @@ -21,5 +21,6 @@ <element name="rowActionSelect" type="button" selector="[data-role='grid'] tbody tr .action-select-wrap"/> <element name="rowEditAction" type="button" selector="[data-role='grid'] tbody tr .action-select-wrap._active [data-action='item-edit']" timeout="30"/> <element name="dataGridEmpty" type="block" selector=".data-grid-tr-no-data td"/> + <element name="dataGridWrap" type="block" selector=".admin__data-grid-wrap"/> </section> </sections> From 979e55290165d75d8675ba6bf5ab0923e3470539 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Fri, 11 Jan 2019 14:17:45 +0200 Subject: [PATCH 0210/1295] ISSUE-16198: Static test fix. --- .../Category/RemoveRedundantImagePlugin.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Plugin/Model/ResourceModel/Category/RemoveRedundantImagePlugin.php b/app/code/Magento/Catalog/Plugin/Model/ResourceModel/Category/RemoveRedundantImagePlugin.php index 2d2f8eb50d52d..59f1051b8ed56 100644 --- a/app/code/Magento/Catalog/Plugin/Model/ResourceModel/Category/RemoveRedundantImagePlugin.php +++ b/app/code/Magento/Catalog/Plugin/Model/ResourceModel/Category/RemoveRedundantImagePlugin.php @@ -7,8 +7,8 @@ namespace Magento\Catalog\Plugin\Model\ResourceModel\Category; -use Magento\Catalog\Model\ResourceModel\Category as CategoryResource; use Magento\Catalog\Model\ImageUploader; +use Magento\Catalog\Model\ResourceModel\Category as CategoryResource; use Magento\Catalog\Model\ResourceModel\Category\RedundantCategoryImageChecker; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem; @@ -55,11 +55,13 @@ public function __construct( * @throws \Magento\Framework\Exception\FileSystemException * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function afterSave(CategoryResource $subject, CategoryResource $result, AbstractModel $category): CategoryResource - { + public function afterSave( + CategoryResource $subject, + CategoryResource $result, + AbstractModel $category + ): CategoryResource { $originalImage = $category->getOrigData('image'); - if ( - null !== $originalImage + if (null !== $originalImage && $originalImage !== $category->getImage() && $this->redundantCategoryImageChecker->execute($originalImage) ) { From 8ddc45b97fb87c7e65cf3ed5f1146568fb437ccf Mon Sep 17 00:00:00 2001 From: amol 2jcommerce <amol@2jcommerce.in> Date: Fri, 11 Jan 2019 17:51:16 +0530 Subject: [PATCH 0211/1295] Changes-Hamburger-Icon-was-available-on-a-page --- .../Magento/luma/Magento_Theme/web/css/source/_module.less | 6 ++++++ 1 file changed, 6 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 68938ed206038..0ba135f69a315 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 @@ -151,6 +151,12 @@ } } + .page-print { + .nav-toggle { + display: none; + } + } + .page-main { > .page-title-wrapper { .page-title + .action { From 1a0ecd63ad5d7688469bd56bd9ca3f547069f50e Mon Sep 17 00:00:00 2001 From: Tristan Hofman <tristan@baldwin.be> Date: Fri, 11 Jan 2019 14:38:58 +0100 Subject: [PATCH 0212/1295] For UPS, Las Palmas and Santa Cruz de Tenerife will be represented by Canary Islands country --- app/code/Magento/Ups/Model/Carrier.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/code/Magento/Ups/Model/Carrier.php b/app/code/Magento/Ups/Model/Carrier.php index b7a10e6f37944..a9e289d57300b 100644 --- a/app/code/Magento/Ups/Model/Carrier.php +++ b/app/code/Magento/Ups/Model/Carrier.php @@ -334,6 +334,14 @@ public function setRequest(RateRequest $request) $destCountry = self::GUAM_COUNTRY_ID; } + // For UPS, Las Palmas and Santa Cruz de Tenerife will be represented by Canary Islands country + if ( + $destCountry == self::SPAIN_COUNTRY_ID && + ($request->getDestRegionCode() == self::LAS_PALMAS_REGION_ID || $request->getDestRegionCode() == self::SANTA_CRUZ_DE_TENERIFE_REGION_ID) + ) { + $destCountry = self::CANARY_ISLANDS_COUNTRY_ID; + } + $country = $this->_countryFactory->create()->load($destCountry); $rowRequest->setDestCountry($country->getData('iso2_code') ?: $destCountry); From 3b1be5810df04b5048634886a80d9c5cbcad47a7 Mon Sep 17 00:00:00 2001 From: amol 2jcommerce <amol@2jcommerce.in> Date: Fri, 11 Jan 2019 19:53:02 +0530 Subject: [PATCH 0213/1295] changes-add-your-review-text-is-not-show-uniformly-in-Mobile-view --- .../Magento/luma/Magento_Review/web/css/source/_module.less | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/design/frontend/Magento/luma/Magento_Review/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Review/web/css/source/_module.less index da78406f92212..f40762ee6fc6d 100644 --- a/app/design/frontend/Magento/luma/Magento_Review/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Review/web/css/source/_module.less @@ -298,6 +298,9 @@ a:not(:last-child) { margin-right: 30px; } + .action.add { + white-space: nowrap; + } } } From 0085a4a290dc9c8c9f01be40e00e9a7be5f74c48 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Thu, 10 Jan 2019 14:34:24 -0600 Subject: [PATCH 0214/1295] MC-5926: Conflict of simultaneous write in Redis cache --- .../Magento/Config/App/Config/Type/System.php | 24 ++++- .../Config/Setup/ConfigOptionsList.php | 88 +++++++++++++++++++ .../Developer/Model/Logger/Handler/Debug.php | 21 ++++- 3 files changed, 128 insertions(+), 5 deletions(-) create mode 100644 app/code/Magento/Config/Setup/ConfigOptionsList.php diff --git a/app/code/Magento/Config/App/Config/Type/System.php b/app/code/Magento/Config/App/Config/Type/System.php index 13a3198096fe4..c07872a630830 100644 --- a/app/code/Magento/Config/App/Config/Type/System.php +++ b/app/code/Magento/Config/App/Config/Type/System.php @@ -44,6 +44,24 @@ class System implements ConfigTypeInterface */ private static $lockName = 'SYSTEM_CONFIG'; + /** + * Timeout between retrieves to load the configuration from the cache. + * + * Value of the variable in microseconds. + * + * @var int + */ + private static $delayTimeout = 50000; + + /** + * Lifetime of the lock for write in cache. + * + * Value of the variable in seconds. + * + * @var int + */ + private static $lockTimeout = 8; + /** * @var array */ @@ -218,13 +236,13 @@ private function lockedLoadData(callable $dataLoader, bool $flush = false): arra $cachedData = $dataLoader(); //optimistic read while ($cachedData === false && $this->locker->isLocked(self::$lockName)) { - usleep(100000); + usleep(self::$delayTimeout); $cachedData = $dataLoader(); } while ($cachedData === false) { try { - if ($this->locker->lock(self::$lockName, 60)) { + if ($this->locker->lock(self::$lockName, self::$lockTimeout)) { if (!$flush) { $data = $this->readData(); $this->cacheData($data); @@ -239,7 +257,7 @@ private function lockedLoadData(callable $dataLoader, bool $flush = false): arra } if ($cachedData === false) { - usleep(100000); + usleep(self::$delayTimeout); $cachedData = $dataLoader(); } } diff --git a/app/code/Magento/Config/Setup/ConfigOptionsList.php b/app/code/Magento/Config/Setup/ConfigOptionsList.php new file mode 100644 index 0000000000000..45e3987d282f1 --- /dev/null +++ b/app/code/Magento/Config/Setup/ConfigOptionsList.php @@ -0,0 +1,88 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Config\Setup; + +use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\Config\Data\ConfigDataFactory; +use Magento\Framework\Config\File\ConfigFilePool; +use Magento\Framework\Setup\ConfigOptionsListInterface; +use Magento\Framework\Setup\Option\SelectConfigOption; + +/** + * Deployment configuration options required for the Config module. + */ +class ConfigOptionsList implements ConfigOptionsListInterface +{ + /** + * Input key for the option. + */ + const INPUT_KEY_DEBUG_LOGGING = 'enable-debug-logging'; + + /** + * Path to the value in the deployment config. + */ + const CONFIG_PATH_DEBUG_LOGGING = 'dev/debug/debug_logging'; + + /** + * @var ConfigDataFactory + */ + private $configDataFactory; + + /** + * @param ConfigDataFactory $configDataFactory + */ + public function __construct(ConfigDataFactory $configDataFactory) + { + $this->configDataFactory = $configDataFactory; + } + + /** + * @inheritdoc + */ + public function getOptions() + { + return [ + new SelectConfigOption( + self::INPUT_KEY_DEBUG_LOGGING, + SelectConfigOption::FRONTEND_WIZARD_RADIO, + [true, false, 1, 0], + self::CONFIG_PATH_DEBUG_LOGGING, + 'Enable debug logging' + ) + ]; + } + + /** + * @inheritdoc + */ + public function createConfig(array $options, DeploymentConfig $deploymentConfig) + { + $config = []; + if (isset($options[self::INPUT_KEY_DEBUG_LOGGING])) { + $configData = $this->configDataFactory->create(ConfigFilePool::APP_ENV); + if ($options[self::INPUT_KEY_DEBUG_LOGGING] === 'true' + || $options[self::INPUT_KEY_DEBUG_LOGGING] === '1') { + $value = 1; + } else { + $value = 0; + } + $configData->set(self::CONFIG_PATH_DEBUG_LOGGING, $value); + $config[] = $configData; + } + + return $config; + } + + /** + * @inheritdoc + */ + public function validate(array $options, DeploymentConfig $deploymentConfig) + { + return []; + } +} diff --git a/app/code/Magento/Developer/Model/Logger/Handler/Debug.php b/app/code/Magento/Developer/Model/Logger/Handler/Debug.php index 4d23da86b5ecd..d14da17b6ef74 100644 --- a/app/code/Magento/Developer/Model/Logger/Handler/Debug.php +++ b/app/code/Magento/Developer/Model/Logger/Handler/Debug.php @@ -5,10 +5,10 @@ */ namespace Magento\Developer\Model\Logger\Handler; +use Magento\Config\Setup\ConfigOptionsList; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\State; use Magento\Framework\Filesystem\DriverInterface; -use Magento\Store\Model\ScopeInterface; use Magento\Framework\App\DeploymentConfig; /** @@ -60,9 +60,26 @@ public function isHandling(array $record) if ($this->deploymentConfig->isAvailable()) { return parent::isHandling($record) - && $this->state->getMode() !== State::MODE_PRODUCTION; + && $this->isLoggingEnabled(); } return parent::isHandling($record); } + + /** + * Check that logging functionality is enabled. + * + * @return bool + */ + private function isLoggingEnabled(): bool + { + $configValue = $this->deploymentConfig->get(ConfigOptionsList::CONFIG_PATH_DEBUG_LOGGING); + if ($configValue === null) { + $isEnabled = $this->state->getMode() !== State::MODE_PRODUCTION; + } else { + $isEnabled = (bool)$configValue; + } + + return $isEnabled; + } } From 0f549cad14d631eefacecefb4bf50f7fff282afe Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Fri, 11 Jan 2019 14:37:35 -0600 Subject: [PATCH 0215/1295] MC-5926: Conflict of simultaneous write in Redis cache --- .../Model/Logger/Handler/DebugTest.php | 224 +++++++++++++----- 1 file changed, 166 insertions(+), 58 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php b/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php index b74737149cd5a..fa8f1cb64806b 100644 --- a/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php +++ b/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php @@ -5,31 +5,19 @@ */ namespace Magento\Developer\Model\Logger\Handler; +use Magento\Config\Setup\ConfigOptionsList; +use Magento\Framework\App\DeploymentConfig; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\App\State; use Magento\Framework\Filesystem; use Magento\Framework\Filesystem\Directory\WriteInterface; use Magento\Framework\Logger\Monolog; +use Magento\Framework\Shell; +use Magento\Setup\Mvc\Bootstrap\InitParamListener; use Magento\TestFramework\Helper\Bootstrap; -use Magento\Deploy\Model\Mode; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; +use Magento\TestFramework\ObjectManager; /** - * Preconditions - * - Developer mode enabled - * - Log file isn't exists - * - 'Log to file' setting are enabled - * - * Test steps - * - Enable production mode without compilation - * - Try to log message into log file - * - Assert that log file isn't exists - * - Assert that 'Log to file' setting are disabled - * - * - Enable 'Log to file' setting - * - Try to log message into debug file - * - Assert that log file is exists - * - Assert that log file contain logged message * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class DebugTest extends \PHPUnit\Framework\TestCase @@ -40,48 +28,61 @@ class DebugTest extends \PHPUnit\Framework\TestCase private $logger; /** - * @var Mode + * @var WriteInterface */ - private $mode; + private $etcDirectory; /** - * @var InputInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ObjectManager */ - private $inputMock; + private $objectManager; /** - * @var OutputInterface|\PHPUnit_Framework_MockObject_MockObject + * @var Shell */ - private $outputMock; + private $shell; /** - * @var WriteInterface + * @var DeploymentConfig */ - private $etcDirectory; + private $deploymentConfig; + + /** + * @var string + */ + private $debugLogPath = ''; + + /** + * @var string + */ + private static $backupFile = 'env.base.php'; + + /** + * @var string + */ + private static $configFile = 'env.php'; + + /** + * @var Debug + */ + private $debugHandler; /** * @inheritdoc + * @throws \Magento\Framework\Exception\FileSystemException * @throws \Exception */ public function setUp() { + $this->objectManager = Bootstrap::getObjectManager(); + $this->shell = $this->objectManager->get(Shell::class); + $this->logger = $this->objectManager->get(Monolog::class); + $this->deploymentConfig = $this->objectManager->get(DeploymentConfig::class); + /** @var Filesystem $filesystem */ - $filesystem = Bootstrap::getObjectManager()->create(Filesystem::class); + $filesystem = $this->objectManager->create(Filesystem::class); $this->etcDirectory = $filesystem->getDirectoryWrite(DirectoryList::CONFIG); - $this->etcDirectory->copyFile('env.php', 'env.base.php'); - - $this->inputMock = $this->getMockBuilder(InputInterface::class) - ->getMockForAbstractClass(); - $this->outputMock = $this->getMockBuilder(OutputInterface::class) - ->getMockForAbstractClass(); - $this->logger = Bootstrap::getObjectManager()->get(Monolog::class); - $this->mode = Bootstrap::getObjectManager()->create( - Mode::class, - [ - 'input' => $this->inputMock, - 'output' => $this->outputMock - ] - ); + $this->etcDirectory->copyFile(self::$configFile, self::$backupFile); } /** @@ -90,43 +91,150 @@ public function setUp() */ public function tearDown() { - $this->etcDirectory->delete('env.php'); - $this->etcDirectory->renameFile('env.base.php', 'env.php'); + $this->reinitDeploymentConfig(); + $this->etcDirectory->delete(self::$backupFile); } /** - * @throws \Exception + * @param bool $flag + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function enableDebugging(bool $flag) + { + $result = $this->shell->execute( + PHP_BINARY . ' -f %s setup:config:set -n --%s=%s --%s=%s', + [ + BP . '/bin/magento', + ConfigOptionsList::INPUT_KEY_DEBUG_LOGGING, + (int)$flag, + InitParamListener::BOOTSTRAP_PARAM, + urldecode(http_build_query(Bootstrap::getInstance()->getAppInitParams())), + ] + ); + $this->assertEquals('You saved the new configuration.', $result); + $this->deploymentConfig->resetData(); + $this->assertSame((int)$flag, $this->deploymentConfig->get(ConfigOptionsList::CONFIG_PATH_DEBUG_LOGGING)); + } + + /** + * @throws \Magento\Framework\Exception\LocalizedException */ public function testDebugInProductionMode() { $message = 'test message'; + $this->reinitDebugHandler(State::MODE_PRODUCTION); - $this->mode->enableDeveloperMode(); - if (file_exists($this->getDebuggerLogPath())) { - unlink($this->getDebuggerLogPath()); - } + $this->removeDebugLog(); + $this->logger->debug($message); + $this->assertFileNotExists($this->getDebuggerLogPath()); + $this->assertNull($this->deploymentConfig->get(ConfigOptionsList::CONFIG_PATH_DEBUG_LOGGING)); + + $this->checkCommonFlow($message); + $this->reinitDeploymentConfig(); + } + /** + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function testDebugInDeveloperMode() + { + $message = 'test message'; + $this->reinitDebugHandler(State::MODE_DEVELOPER); + + $this->removeDebugLog(); $this->logger->debug($message); $this->assertFileExists($this->getDebuggerLogPath()); $this->assertContains($message, file_get_contents($this->getDebuggerLogPath())); + $this->assertNull($this->deploymentConfig->get(ConfigOptionsList::CONFIG_PATH_DEBUG_LOGGING)); - unlink($this->getDebuggerLogPath()); - $this->mode->enableProductionModeMinimal(); - - $this->logger->debug($message); - $this->assertFileNotExists($this->getDebuggerLogPath()); + $this->checkCommonFlow($message); + $this->reinitDeploymentConfig(); } /** - * @return bool|string + * @return string */ private function getDebuggerLogPath() { - foreach ($this->logger->getHandlers() as $handler) { - if ($handler instanceof Debug) { - return $handler->getUrl(); + if (!$this->debugLogPath) { + foreach ($this->logger->getHandlers() as $handler) { + if ($handler instanceof Debug) { + $this->debugLogPath = $handler->getUrl(); + } } } - return false; + + return $this->debugLogPath; + } + + /** + * @throws \Magento\Framework\Exception\FileSystemException + */ + private function reinitDeploymentConfig() + { + $this->etcDirectory->delete(self::$configFile); + $this->etcDirectory->copyFile(self::$backupFile, self::$configFile); + } + + /** + * @param string $instanceMode + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function reinitDebugHandler(string $instanceMode) + { + $this->debugHandler = $this->objectManager->create( + Debug::class, + [ + 'filePath' => Bootstrap::getInstance()->getAppTempDir(), + 'state' => $this->objectManager->create( + State::class, + [ + 'mode' => $instanceMode, + ] + ), + ] + ); + $this->logger->setHandlers( + [ + $this->debugHandler, + ] + ); + } + + /** + * @return void + */ + private function detachLogger() + { + $this->debugHandler->close(); + } + + /** + * @return void + */ + private function removeDebugLog() + { + $this->detachLogger(); + if (file_exists($this->getDebuggerLogPath())) { + unlink($this->getDebuggerLogPath()); + } + } + + /** + * @param string $message + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function checkCommonFlow(string $message) + { + $this->enableDebugging(true); + $this->removeDebugLog(); + $this->logger->debug($message); + $this->assertFileExists($this->getDebuggerLogPath()); + $this->assertContains($message, file_get_contents($this->getDebuggerLogPath())); + + $this->enableDebugging(false); + $this->removeDebugLog(); + $this->logger->debug($message); + $this->assertFileNotExists($this->getDebuggerLogPath()); } } From d9e1f575f7f149ef90b7ece5edaa7664f11d117e Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Fri, 11 Jan 2019 16:17:48 -0600 Subject: [PATCH 0216/1295] MC-5926: Conflict of simultaneous write in Redis cache --- .../Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml | 4 ++++ .../Test/Mftf/Test/AdminCheckingCreditMemoTotalsTest.xml | 4 ++++ .../Magento/Developer/Model/Logger/Handler/DebugTest.php | 3 +-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml index a74fd8ad08eb8..0b33e7e13c4a0 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml @@ -116,6 +116,10 @@ <waitForElementVisible selector="{{AdminCreditMemoTotalSection.submitRefundOfflineEnabled}}" stepKey="waitForSubmitRefundOfflineEnabled"/> <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="submitRefundOffline"/> <!--Verify Credit Memo created successfully--> + <waitForElementVisible + selector="{{AdminMessagesSection.success}}" + time="20" + stepKey="waitForMessage"/> <see selector="{{AdminMessagesSection.success}}" userInput="You created the credit memo." after="submitRefundOffline" stepKey="seeCreditMemoSuccessMsg"/> <scrollTo selector="{{AdminOrderItemsOrderedSection.itemStatus('1')}}" stepKey="scrollToOrderItems2"/> <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Refunded 5" stepKey="see5itemsRefunded"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoTotalsTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoTotalsTest.xml index 0c64bf94fc49e..77dacc3224140 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoTotalsTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoTotalsTest.xml @@ -91,6 +91,10 @@ <click selector="{{AdminCreditMemoTotalSection.updateTotals}}" stepKey="clickUpdateTotals"/> <waitForLoadingMaskToDisappear stepKey="waitForUpdateTotals"/> <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickRefundOffline"/> + <waitForElementVisible + selector="{{AdminMessagesSection.success}}" + time="10" + stepKey="waitForMessage"/> <see selector="{{AdminMessagesSection.success}}" userInput="You created the credit memo." stepKey="seeCreatedCreditMemoSuccessMessage"/> diff --git a/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php b/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php index fa8f1cb64806b..f7a47017f8b18 100644 --- a/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php +++ b/dev/tests/integration/testsuite/Magento/Developer/Model/Logger/Handler/DebugTest.php @@ -101,7 +101,7 @@ public function tearDown() */ private function enableDebugging(bool $flag) { - $result = $this->shell->execute( + $this->shell->execute( PHP_BINARY . ' -f %s setup:config:set -n --%s=%s --%s=%s', [ BP . '/bin/magento', @@ -111,7 +111,6 @@ private function enableDebugging(bool $flag) urldecode(http_build_query(Bootstrap::getInstance()->getAppInitParams())), ] ); - $this->assertEquals('You saved the new configuration.', $result); $this->deploymentConfig->resetData(); $this->assertSame((int)$flag, $this->deploymentConfig->get(ConfigOptionsList::CONFIG_PATH_DEBUG_LOGGING)); } From 1ad527b46a7e2148a75bcbf660ae98dfb431e8c7 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Mon, 14 Jan 2019 11:07:38 +0200 Subject: [PATCH 0217/1295] MAGETWO-89039: Allow Validation of customer address attributes to include spaces --- .../Block/Checkout/AttributeMerger.php | 13 +- .../Block/Checkout/AttributeMergerTest.php | 119 +++++++++++++ .../Model/Metadata/Form/AbstractData.php | 13 +- .../Model/Metadata/Form/AbstractDataTest.php | 43 +++-- .../Listing/Column/ValidationRulesTest.php | 44 +++-- .../Listing/Column/ValidationRules.php | 4 + .../Eav/Model/Attribute/Data/AbstractData.php | 9 +- .../Attribute/Frontend/AbstractFrontend.php | 5 + .../Unit/Model/Attribute/Data/TextTest.php | 133 ++++++++++++-- .../Frontend/DefaultFrontendTest.php | 167 +++++++++--------- 10 files changed, 420 insertions(+), 130 deletions(-) create mode 100644 app/code/Magento/Checkout/Test/Unit/Block/Checkout/AttributeMergerTest.php diff --git a/app/code/Magento/Checkout/Block/Checkout/AttributeMerger.php b/app/code/Magento/Checkout/Block/Checkout/AttributeMerger.php index de996bed02439..bf0884a8c83ea 100644 --- a/app/code/Magento/Checkout/Block/Checkout/AttributeMerger.php +++ b/app/code/Magento/Checkout/Block/Checkout/AttributeMerger.php @@ -6,10 +6,14 @@ namespace Magento\Checkout\Block\Checkout; use Magento\Customer\Api\CustomerRepositoryInterface as CustomerRepository; +use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Helper\Address as AddressHelper; use Magento\Customer\Model\Session; use Magento\Directory\Helper\Data as DirectoryHelper; +/** + * Fields attribute merger. + */ class AttributeMerger { /** @@ -46,6 +50,7 @@ class AttributeMerger 'alpha' => 'validate-alpha', 'numeric' => 'validate-number', 'alphanumeric' => 'validate-alphanum', + 'alphanum-with-spaces' => 'validate-alphanum-with-spaces', 'url' => 'validate-url', 'email' => 'email2', 'length' => 'validate-length', @@ -67,7 +72,7 @@ class AttributeMerger private $customerRepository; /** - * @var \Magento\Customer\Api\Data\CustomerInterface + * @var CustomerInterface */ private $customer; @@ -309,6 +314,8 @@ protected function getMultilineFieldConfig($attributeCode, array $attributeConfi } /** + * Returns default attribute value. + * * @param string $attributeCode * @return null|string */ @@ -346,7 +353,9 @@ protected function getDefaultValue($attributeCode) } /** - * @return \Magento\Customer\Api\Data\CustomerInterface|null + * Returns logged customer. + * + * @return CustomerInterface|null */ protected function getCustomer() { diff --git a/app/code/Magento/Checkout/Test/Unit/Block/Checkout/AttributeMergerTest.php b/app/code/Magento/Checkout/Test/Unit/Block/Checkout/AttributeMergerTest.php new file mode 100644 index 0000000000000..5e87b1dab46c7 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Unit/Block/Checkout/AttributeMergerTest.php @@ -0,0 +1,119 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Checkout\Test\Unit\Block\Checkout; + +use Magento\Customer\Api\CustomerRepositoryInterface as CustomerRepository; +use Magento\Customer\Helper\Address as AddressHelper; +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Directory\Helper\Data as DirectoryHelper; +use Magento\Checkout\Block\Checkout\AttributeMerger; + +class AttributeMergerTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var CustomerRepository + */ + private $customerRepositoryMock; + + /** + * @var CustomerSession + */ + private $customerSessionMock; + + /** + * @var AddressHelper + */ + private $addressHelperMock; + + /** + * @var DirectoryHelper + */ + private $directoryHelperMock; + + /** + * @var AttributeMerger + */ + private $attributeMerger; + + protected function setUp() + { + + $this->customerRepositoryMock = $this->createMock(CustomerRepository::class); + $this->customerSessionMock = $this->createMock(CustomerSession::class); + $this->addressHelperMock = $this->createMock(AddressHelper::class); + $this->directoryHelperMock = $this->createMock(DirectoryHelper::class); + + $this->attributeMerger = new AttributeMerger( + $this->addressHelperMock, + $this->customerSessionMock, + $this->customerRepositoryMock, + $this->directoryHelperMock + ); + } + + /** + * Tests of element attributes merging. + * + * @param string $validationRule + * @param string $expectedValidation + * @return void + * @dataProvider validationRulesDataProvider + */ + public function testMerge($validationRule, $expectedValidation) + { + $elements = [ + 'field' => [ + 'visible' => true, + 'formElement' => 'input', + 'label' => __('City'), + 'value' => null, + 'sortOrder' => 1, + 'validation' => [ + 'input_validation' => $validationRule, + ], + ] + ]; + + $actualResult = $this->attributeMerger->merge( + $elements, + 'provider', + 'dataScope', + [ + 'field' => + [ + 'validation' => ['length' => true], + ] + ] + ); + + $expectedResult = [ + $expectedValidation => true, + 'length' => true, + ]; + + $this->assertEquals($expectedResult, $actualResult['field']['validation']); + } + + /** + * Provides possible validation types. + * + * @return array + */ + public function validationRulesDataProvider() + { + return [ + ['alpha', 'validate-alpha'], + ['numeric', 'validate-number'], + ['alphanumeric', 'validate-alphanum'], + ['alphanum-with-spaces', 'validate-alphanum-with-spaces'], + ['url', 'validate-url'], + ['email', 'email2'], + ['length', 'validate-length'], + ]; + } +} diff --git a/app/code/Magento/Customer/Model/Metadata/Form/AbstractData.php b/app/code/Magento/Customer/Model/Metadata/Form/AbstractData.php index f28cce0ea2ae1..0e4fc68503122 100644 --- a/app/code/Magento/Customer/Model/Metadata/Form/AbstractData.php +++ b/app/code/Magento/Customer/Model/Metadata/Form/AbstractData.php @@ -1,7 +1,5 @@ <?php /** - * Form Element Abstract Data Model - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -14,6 +12,8 @@ use Magento\Framework\Validator\EmailAddress; /** + * Form Element Abstract Data Model. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ abstract class AbstractData @@ -138,7 +138,8 @@ public function setRequestScope($scope) } /** - * Set scope visibility + * Set scope visibility. + * * Search value only in scope or search value in scope and global * * @param boolean $flag @@ -283,9 +284,13 @@ protected function _validateInputRule($value) ); if (!is_null($inputValidation)) { + $allowWhiteSpace = false; switch ($inputValidation) { + case 'alphanum-with-spaces': + $allowWhiteSpace = true; + // continue to alphanumeric validation case 'alphanumeric': - $validator = new \Zend_Validate_Alnum(true); + $validator = new \Zend_Validate_Alnum($allowWhiteSpace); $validator->setMessage(__('"%1" invalid type entered.', $label), \Zend_Validate_Alnum::INVALID); $validator->setMessage( __('"%1" contains non-alphabetic or non-numeric characters.', $label), diff --git a/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/AbstractDataTest.php b/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/AbstractDataTest.php index e4dc22ba40e31..1c6de64a46064 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/AbstractDataTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/AbstractDataTest.php @@ -205,6 +205,8 @@ public function applyOutputFilterDataProvider() } /** + * Tests input validation rules. + * * @param null|string $value * @param null|string $label * @param null|string $inputValidation @@ -217,25 +219,18 @@ public function testValidateInputRule($value, $label, $inputValidation, $expecte ->disableOriginalConstructor() ->setMethods(['getName', 'getValue']) ->getMockForAbstractClass(); - $validationRule->expects($this->any()) - ->method('getName') - ->will($this->returnValue('input_validation')); - $validationRule->expects($this->any()) - ->method('getValue') - ->will($this->returnValue($inputValidation)); - - $this->_attributeMock->expects($this->any())->method('getStoreLabel')->will($this->returnValue($label)); - $this->_attributeMock->expects( - $this->any() - )->method( - 'getValidationRules' - )->will( - $this->returnValue( - [ - $validationRule, - ] - ) - ); + + $validationRule->method('getName') + ->willReturn('input_validation'); + + $validationRule->method('getValue') + ->willReturn($inputValidation); + + $this->_attributeMock->method('getStoreLabel') + ->willReturn($label); + + $this->_attributeMock->method('getValidationRules') + ->willReturn([$validationRule]); $this->assertEquals($expectedOutput, $this->_model->validateInputRule($value)); } @@ -256,6 +251,16 @@ public function validateInputRuleDataProvider() \Zend_Validate_Alnum::NOT_ALNUM => '"mylabel" contains non-alphabetic or non-numeric characters.' ] ], + [ + 'abc qaz', + 'mylabel', + 'alphanumeric', + [ + \Zend_Validate_Alnum::NOT_ALNUM => '"mylabel" contains non-alphabetic or non-numeric characters.' + ] + ], + ['abcqaz', 'mylabel', 'alphanumeric', true], + ['abc qaz', 'mylabel', 'alphanum-with-spaces', true], [ '!@#$', 'mylabel', diff --git a/app/code/Magento/Customer/Test/Unit/Ui/Component/Listing/Column/ValidationRulesTest.php b/app/code/Magento/Customer/Test/Unit/Ui/Component/Listing/Column/ValidationRulesTest.php index 130b3acd11e76..3f89dd6d2be01 100644 --- a/app/code/Magento/Customer/Test/Unit/Ui/Component/Listing/Column/ValidationRulesTest.php +++ b/app/code/Magento/Customer/Test/Unit/Ui/Component/Listing/Column/ValidationRulesTest.php @@ -18,12 +18,6 @@ class ValidationRulesTest extends \PHPUnit\Framework\TestCase protected function setUp() { - $this->validationRules = $this->getMockBuilder( - \Magento\Customer\Ui\Component\Listing\Column\ValidationRules::class - ) - ->disableOriginalConstructor() - ->getMock(); - $this->validationRule = $this->getMockBuilder(\Magento\Customer\Api\Data\ValidationRuleInterface::class) ->disableOriginalConstructor() ->getMock(); @@ -31,18 +25,25 @@ protected function setUp() $this->validationRules = new ValidationRules(); } - public function testGetValidationRules() + /** + * Tests input validation rules. + * + * @param string $validationRule + * @param string $validationClass + * @return void + * @dataProvider validationRulesDataProvider + */ + public function testGetValidationRules($validationRule, $validationClass) { $expectsRules = [ 'required-entry' => true, - 'validate-number' => true, + $validationClass => true, ]; - $this->validationRule->expects($this->atLeastOnce()) - ->method('getName') + $this->validationRule->method('getName') ->willReturn('input_validation'); - $this->validationRule->expects($this->atLeastOnce()) - ->method('getValue') - ->willReturn('numeric'); + + $this->validationRule->method('getValue') + ->willReturn($validationRule); $this->assertEquals( $expectsRules, @@ -66,4 +67,21 @@ public function testGetValidationRulesWithOnlyRequiredRule() $this->validationRules->getValidationRules(true, []) ); } + + /** + * Provides possible validation rules. + * + * @return array + */ + public function validationRulesDataProvider() + { + return [ + ['alpha', 'validate-alpha'], + ['numeric', 'validate-number'], + ['alphanumeric', 'validate-alphanum'], + ['alphanum-with-spaces', 'validate-alphanum-with-spaces'], + ['url', 'validate-url'], + ['email', 'validate-email'], + ]; + } } diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Column/ValidationRules.php b/app/code/Magento/Customer/Ui/Component/Listing/Column/ValidationRules.php index b8f83421a6d62..6befec8e942a1 100644 --- a/app/code/Magento/Customer/Ui/Component/Listing/Column/ValidationRules.php +++ b/app/code/Magento/Customer/Ui/Component/Listing/Column/ValidationRules.php @@ -7,6 +7,9 @@ use Magento\Customer\Api\Data\ValidationRuleInterface; +/** + * Provides validation classes according to corresponding rules. + */ class ValidationRules { /** @@ -16,6 +19,7 @@ class ValidationRules 'alpha' => 'validate-alpha', 'numeric' => 'validate-number', 'alphanumeric' => 'validate-alphanum', + 'alphanum-with-spaces' => 'validate-alphanum-with-spaces', 'url' => 'validate-url', 'email' => 'validate-email', ]; diff --git a/app/code/Magento/Eav/Model/Attribute/Data/AbstractData.php b/app/code/Magento/Eav/Model/Attribute/Data/AbstractData.php index 12023acc3b33b..f4bcdf7764d95 100644 --- a/app/code/Magento/Eav/Model/Attribute/Data/AbstractData.php +++ b/app/code/Magento/Eav/Model/Attribute/Data/AbstractData.php @@ -144,7 +144,8 @@ public function setRequestScope($scope) } /** - * Set scope visibility + * Set scope visibility. + * * Search value only in scope or search value in scope and global * * @param bool $flag @@ -313,9 +314,13 @@ protected function _validateInputRule($value) if (!empty($validateRules['input_validation'])) { $label = $this->getAttribute()->getStoreLabel(); + $allowWhiteSpace = false; switch ($validateRules['input_validation']) { + case 'alphanum-with-spaces': + $allowWhiteSpace = true; + // continue to alphanumeric validation case 'alphanumeric': - $validator = new \Zend_Validate_Alnum(true); + $validator = new \Zend_Validate_Alnum($allowWhiteSpace); $validator->setMessage(__('"%1" invalid type entered.', $label), \Zend_Validate_Alnum::INVALID); $validator->setMessage( __('"%1" contains non-alphabetic or non-numeric characters.', $label), diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/AbstractFrontend.php b/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/AbstractFrontend.php index 13c0b7a627edb..cd7639080509f 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/AbstractFrontend.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/AbstractFrontend.php @@ -20,6 +20,8 @@ use Magento\Eav\Model\Entity\Attribute\Source\BooleanFactory; /** + * EAV entity attribute form renderer. + * * @api * @since 100.0.2 */ @@ -234,6 +236,9 @@ protected function _getInputValidateClass() case 'alphanumeric': $class = 'validate-alphanum'; break; + case 'alphanum-with-spaces': + $class = 'validate-alphanum-with-spaces'; + break; case 'numeric': $class = 'validate-digits'; break; diff --git a/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/TextTest.php b/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/TextTest.php index 217a04045b939..49b55d8ce8151 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/TextTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/TextTest.php @@ -6,22 +6,24 @@ namespace Magento\Eav\Test\Unit\Model\Attribute\Data; +use Magento\Framework\Stdlib\StringUtils; + class TextTest extends \PHPUnit\Framework\TestCase { /** * @var \Magento\Eav\Model\Attribute\Data\Text */ - protected $_model; + private $model; protected function setUp() { $locale = $this->createMock(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class); $localeResolver = $this->createMock(\Magento\Framework\Locale\ResolverInterface::class); $logger = $this->createMock(\Psr\Log\LoggerInterface::class); - $helper = $this->createMock(\Magento\Framework\Stdlib\StringUtils::class); + $helper = new StringUtils; - $this->_model = new \Magento\Eav\Model\Attribute\Data\Text($locale, $logger, $localeResolver, $helper); - $this->_model->setAttribute( + $this->model = new \Magento\Eav\Model\Attribute\Data\Text($locale, $logger, $localeResolver, $helper); + $this->model->setAttribute( $this->createAttribute( [ 'store_label' => 'Test', @@ -35,24 +37,36 @@ protected function setUp() protected function tearDown() { - $this->_model = null; + $this->model = null; } + /** + * Test of string validation. + * @return void + */ public function testValidateValueString() { $inputValue = '0'; $expectedResult = true; - $this->assertEquals($expectedResult, $this->_model->validateValue($inputValue)); + $this->assertEquals($expectedResult, $this->model->validateValue($inputValue)); } + /** + * Test of integer validation. + * @return void + */ public function testValidateValueInteger() { $inputValue = 0; $expectedResult = ['"Test" is a required value.']; - $result = $this->_model->validateValue($inputValue); + $result = $this->model->validateValue($inputValue); $this->assertEquals($expectedResult, [(string)$result[0]]); } + /** + * Test without length validation. + * @return void + */ public function testWithoutLengthValidation() { $expectedResult = true; @@ -64,12 +78,109 @@ public function testWithoutLengthValidation() ]; $defaultAttributeData['validate_rules']['min_text_length'] = 2; - $this->_model->setAttribute($this->createAttribute($defaultAttributeData)); - $this->assertEquals($expectedResult, $this->_model->validateValue('t')); + $this->model->setAttribute($this->createAttribute($defaultAttributeData)); + $this->assertEquals($expectedResult, $this->model->validateValue('t')); $defaultAttributeData['validate_rules']['max_text_length'] = 3; - $this->_model->setAttribute($this->createAttribute($defaultAttributeData)); - $this->assertEquals($expectedResult, $this->_model->validateValue('test')); + $this->model->setAttribute($this->createAttribute($defaultAttributeData)); + $this->assertEquals($expectedResult, $this->model->validateValue('test')); + } + + /** + * Test of alphanumeric validation. + * + * @param string $value + * @param bool|array $expectedResult + * @return void + * @dataProvider alphanumDataProvider + */ + public function testAlphanumericValidation($value, $expectedResult) + { + $defaultAttributeData = [ + 'store_label' => 'Test', + 'attribute_code' => 'test', + 'is_required' => 1, + 'validate_rules' => [ + 'min_text_length' => 0, + 'max_text_length' => 10, + 'input_validation' => 'alphanumeric', + ], + ]; + + $this->model->setAttribute($this->createAttribute($defaultAttributeData)); + $this->assertEquals($expectedResult, $this->model->validateValue($value)); + } + + /** + * Provides possible input values. + * + * @return array + */ + public function alphanumDataProvider() + { + return [ + ['QazWsx', true], + ['QazWsx123', true], + [ + 'QazWsx 123', + [\Zend_Validate_Alnum::NOT_ALNUM => '"Test" contains non-alphabetic or non-numeric characters.'], + ], + [ + 'QazWsx_123', + [\Zend_Validate_Alnum::NOT_ALNUM => '"Test" contains non-alphabetic or non-numeric characters.'], + ], + [ + 'QazWsx12345', + [__('"%1" length must be equal or less than %2 characters.', 'Test', 10)], + ], + ]; + } + + /** + * Test of alphanumeric validation with spaces. + * + * @param string $value + * @param bool|array $expectedResult + * @return void + * @dataProvider alphanumWithSpacesDataProvider + */ + public function testAlphanumericValidationWithSpaces($value, $expectedResult) + { + $defaultAttributeData = [ + 'store_label' => 'Test', + 'attribute_code' => 'test', + 'is_required' => 1, + 'validate_rules' => [ + 'min_text_length' => 0, + 'max_text_length' => 10, + 'input_validation' => 'alphanum-with-spaces', + ], + ]; + + $this->model->setAttribute($this->createAttribute($defaultAttributeData)); + $this->assertEquals($expectedResult, $this->model->validateValue($value)); + } + + /** + * Provides possible input values. + * + * @return array + */ + public function alphanumWithSpacesDataProvider() + { + return [ + ['QazWsx', true], + ['QazWsx123', true], + ['QazWsx 123', true], + [ + 'QazWsx_123', + [\Zend_Validate_Alnum::NOT_ALNUM => '"Test" contains non-alphabetic or non-numeric characters.'], + ], + [ + 'QazWsx12345', + [__('"%1" length must be equal or less than %2 characters.', 'Test', 10)], + ], + ]; } /** diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DefaultFrontendTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DefaultFrontendTest.php index a61c9ef447458..f09014e33ec8a 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DefaultFrontendTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DefaultFrontendTest.php @@ -13,41 +13,42 @@ use Magento\Framework\App\CacheInterface; use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource; +use PHPUnit\Framework\MockObject\MockObject; class DefaultFrontendTest extends \PHPUnit\Framework\TestCase { /** * @var DefaultFrontend */ - protected $model; + private $model; /** - * @var BooleanFactory|\PHPUnit_Framework_MockObject_MockObject + * @var BooleanFactory|MockObject */ - protected $booleanFactory; + private $booleanFactory; /** - * @var Serializer|\PHPUnit_Framework_MockObject_MockObject + * @var Serializer|MockObject */ private $serializerMock; /** - * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreManagerInterface|MockObject */ private $storeManagerMock; /** - * @var StoreInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreInterface|MockObject */ private $storeMock; /** - * @var CacheInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CacheInterface|MockObject */ private $cacheMock; /** - * @var AbstractAttribute|\PHPUnit_Framework_MockObject_MockObject + * @var AbstractAttribute|MockObject */ private $attributeMock; @@ -57,7 +58,7 @@ class DefaultFrontendTest extends \PHPUnit\Framework\TestCase private $cacheTags; /** - * @var AbstractSource|\PHPUnit_Framework_MockObject_MockObject + * @var AbstractSource|MockObject */ private $sourceMock; @@ -76,44 +77,31 @@ protected function setUp() ->getMockForAbstractClass(); $this->cacheMock = $this->getMockBuilder(CacheInterface::class) ->getMockForAbstractClass(); - $this->attributeMock = $this->getMockBuilder(AbstractAttribute::class) - ->disableOriginalConstructor() - ->setMethods(['getAttributeCode', 'getSource']) - ->getMockForAbstractClass(); + $this->attributeMock = $this->createAttributeMock(); $this->sourceMock = $this->getMockBuilder(AbstractSource::class) ->disableOriginalConstructor() ->setMethods(['getAllOptions']) ->getMockForAbstractClass(); - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->model = $objectManager->getObject( - DefaultFrontend::class, - [ - '_attrBooleanFactory' => $this->booleanFactory, - 'cache' => $this->cacheMock, - 'storeManager' => $this->storeManagerMock, - 'serializer' => $this->serializerMock, - '_attribute' => $this->attributeMock, - 'cacheTags' => $this->cacheTags - ] + $this->model = new DefaultFrontend( + $this->booleanFactory, + $this->cacheMock, + null, + $this->cacheTags, + $this->storeManagerMock, + $this->serializerMock ); + + $this->model->setAttribute($this->attributeMock); } public function testGetClassEmpty() { - $attributeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class) - ->disableOriginalConstructor() - ->setMethods([ - 'getIsRequired', - 'getFrontendClass', - 'getValidateRules', - ]) - ->getMock(); - $attributeMock->expects($this->once()) - ->method('getIsRequired') + /** @var AbstractAttribute|MockObject $attributeMock */ + $attributeMock = $this->createAttributeMock(); + $attributeMock->method('getIsRequired') ->willReturn(false); - $attributeMock->expects($this->once()) - ->method('getFrontendClass') + $attributeMock->method('getFrontendClass') ->willReturn(''); $attributeMock->expects($this->exactly(2)) ->method('getValidateRules') @@ -123,26 +111,26 @@ public function testGetClassEmpty() $this->assertEmpty($this->model->getClass()); } - public function testGetClass() + /** + * Validates generated html classes. + * + * @param string $validationRule + * @param string $expectedClass + * @return void + * @dataProvider validationRulesDataProvider + */ + public function testGetClass($validationRule, $expectedClass) { - $attributeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class) - ->disableOriginalConstructor() - ->setMethods([ - 'getIsRequired', - 'getFrontendClass', - 'getValidateRules', - ]) - ->getMock(); - $attributeMock->expects($this->once()) - ->method('getIsRequired') + /** @var AbstractAttribute|MockObject $attributeMock */ + $attributeMock = $this->createAttributeMock(); + $attributeMock->method('getIsRequired') ->willReturn(true); - $attributeMock->expects($this->once()) - ->method('getFrontendClass') + $attributeMock->method('getFrontendClass') ->willReturn(''); $attributeMock->expects($this->exactly(3)) ->method('getValidateRules') ->willReturn([ - 'input_validation' => 'alphanumeric', + 'input_validation' => $validationRule, 'min_text_length' => 1, 'max_text_length' => 2, ]); @@ -150,7 +138,7 @@ public function testGetClass() $this->model->setAttribute($attributeMock); $result = $this->model->getClass(); - $this->assertContains('validate-alphanum', $result); + $this->assertContains($expectedClass, $result); $this->assertContains('minimum-length-1', $result); $this->assertContains('maximum-length-2', $result); $this->assertContains('validate-length', $result); @@ -158,19 +146,11 @@ public function testGetClass() public function testGetClassLength() { - $attributeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class) - ->disableOriginalConstructor() - ->setMethods([ - 'getIsRequired', - 'getFrontendClass', - 'getValidateRules', - ]) - ->getMock(); - $attributeMock->expects($this->once()) - ->method('getIsRequired') + /** @var AbstractAttribute|MockObject $attributeMock */ + $attributeMock = $this->createAttributeMock(); + $attributeMock->method('getIsRequired') ->willReturn(true); - $attributeMock->expects($this->once()) - ->method('getFrontendClass') + $attributeMock->method('getFrontendClass') ->willReturn(''); $attributeMock->expects($this->exactly(3)) ->method('getValidateRules') @@ -196,33 +176,62 @@ public function testGetSelectOptions() $options = ['option1', 'option2']; $serializedOptions = "{['option1', 'option2']}"; - $this->storeManagerMock->expects($this->once()) - ->method('getStore') + $this->storeManagerMock->method('getStore') ->willReturn($this->storeMock); - $this->storeMock->expects($this->once()) - ->method('getId') + $this->storeMock->method('getId') ->willReturn($storeId); - $this->attributeMock->expects($this->once()) - ->method('getAttributeCode') + $this->attributeMock->method('getAttributeCode') ->willReturn($attributeCode); - $this->cacheMock->expects($this->once()) - ->method('load') + $this->cacheMock->method('load') ->with($cacheKey) ->willReturn(false); - $this->attributeMock->expects($this->once()) - ->method('getSource') + $this->attributeMock->method('getSource') ->willReturn($this->sourceMock); - $this->sourceMock->expects($this->once()) - ->method('getAllOptions') + $this->sourceMock->method('getAllOptions') ->willReturn($options); - $this->serializerMock->expects($this->once()) - ->method('serialize') + $this->serializerMock->method('serialize') ->with($options) ->willReturn($serializedOptions); - $this->cacheMock->expects($this->once()) - ->method('save') + $this->cacheMock->method('save') ->with($serializedOptions, $cacheKey, $this->cacheTags); $this->assertSame($options, $this->model->getSelectOptions()); } + + /** + * Provides possible validation types. + * + * @return array + */ + public function validationRulesDataProvider() + { + return [ + ['alphanumeric', 'validate-alphanum'], + ['alphanum-with-spaces', 'validate-alphanum-with-spaces'], + ['alpha', 'validate-alpha'], + ['numeric', 'validate-digits'], + ['url', 'validate-url'], + ['email', 'validate-email'], + ['length', 'validate-length'], + ]; + } + + /** + * Entity attribute factory. + * + * @return AbstractAttribute|MockObject + */ + private function createAttributeMock() + { + return $this->getMockBuilder(AbstractAttribute::class) + ->disableOriginalConstructor() + ->setMethods([ + 'getIsRequired', + 'getFrontendClass', + 'getValidateRules', + 'getAttributeCode', + 'getSource' + ]) + ->getMockForAbstractClass(); + } } From 4a46cd178ebf1ba7b06d784c29ccbd3296cb27f1 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Mon, 14 Jan 2019 12:01:47 +0200 Subject: [PATCH 0218/1295] MAGETWO-97312: Order Summary doesn't show Qty of Bundle Product ordered --- .../sales/order/items/renderer.phtml | 141 ++++++++++-------- 1 file changed, 81 insertions(+), 60 deletions(-) diff --git a/app/code/Magento/Bundle/view/frontend/templates/sales/order/items/renderer.phtml b/app/code/Magento/Bundle/view/frontend/templates/sales/order/items/renderer.phtml index 063d66edb9e70..74e1c5f874954 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/sales/order/items/renderer.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/sales/order/items/renderer.phtml @@ -7,95 +7,111 @@ // @codingStandardsIgnoreFile /** @var $block \Magento\Bundle\Block\Sales\Order\Items\Renderer */ +$parentItem = $block->getItem(); +$items = array_merge([$parentItem], $parentItem->getChildrenItems()); +$index = 0; +$prevOptionId = ''; ?> -<?php $parentItem = $block->getItem() ?> -<?php $items = array_merge([$parentItem], $parentItem->getChildrenItems()); ?> -<?php $_index = 0 ?> -<?php $_prevOptionId = '' ?> +<?php foreach ($items as $item): ?> -<?php foreach ($items as $_item): ?> - - <?php if ($block->getItemOptions() || $parentItem->getDescription() || $this->helper('Magento\GiftMessage\Helper\Message')->isMessagesAllowed('order_item', $parentItem) && $parentItem->getGiftMessageId()): ?> - <?php $_showlastRow = true ?> + <?php if ($block->getItemOptions() + || $parentItem->getDescription() + || $this->helper('Magento\GiftMessage\Helper\Message')->isMessagesAllowed('order_item', $parentItem) + && $parentItem->getGiftMessageId()): ?> + <?php $showLastRow = true; ?> <?php else: ?> - <?php $_showlastRow = false ?> + <?php $showLastRow = false; ?> <?php endif; ?> - <?php if ($_item->getParentItem()): ?> - <?php $attributes = $block->getSelectionAttributes($_item) ?> - <?php if ($_prevOptionId != $attributes['option_id']): ?> + <?php if ($item->getParentItem()): ?> + <?php $attributes = $block->getSelectionAttributes($item) ?> + <?php if ($prevOptionId != $attributes['option_id']): ?> <tr class="options-label"> - <td class="col label" colspan="5"><?= /* @escapeNotVerified */ $attributes['option_label'] ?></td> + <td class="col label" colspan="5"><?= $block->escapeHtml($attributes['option_label']); ?></td> </tr> - <?php $_prevOptionId = $attributes['option_id'] ?> + <?php $prevOptionId = $attributes['option_id'] ?> <?php endif; ?> <?php endif; ?> -<tr id="order-item-row-<?= /* @escapeNotVerified */ $_item->getId() ?>" class="<?php if ($_item->getParentItem()): ?>item-options-container<?php else: ?>item-parent<?php endif; ?>"<?php if ($_item->getParentItem()): ?> data-th="<?= /* @escapeNotVerified */ $attributes['option_label'] ?>"<?php endif; ?>> - <?php if (!$_item->getParentItem()): ?> - <td class="col name" data-th="<?= $block->escapeHtml(__('Product Name')) ?>"> - <strong class="product name product-item-name"><?= $block->escapeHtml($_item->getName()) ?></strong> +<tr id="order-item-row-<?= /* @noEscape */ $item->getId() ?>" + class="<?php if ($item->getParentItem()): ?> + item-options-container + <?php else: ?> + item-parent + <?php endif; ?>" + <?php if ($item->getParentItem()): ?> + data-th="<?= $block->escapeHtml($attributes['option_label']); ?>" + <?php endif; ?>> + <?php if (!$item->getParentItem()): ?> + <td class="col name" data-th="<?= $block->escapeHtml(__('Product Name')); ?>"> + <strong class="product name product-item-name"><?= $block->escapeHtml($item->getName()); ?></strong> </td> <?php else: ?> - <td class="col value" data-th="<?= $block->escapeHtml(__('Product Name')) ?>"><?= $block->getValueHtml($_item) ?></td> + <td class="col value" data-th="<?= $block->escapeHtml(__('Product Name')); ?>"> + <?= $block->getValueHtml($item); ?> + </td> <?php endif; ?> - <td class="col sku" data-th="<?= $block->escapeHtml(__('SKU')) ?>"><?= /* @escapeNotVerified */ $block->prepareSku($_item->getSku()) ?></td> - <td class="col price" data-th="<?= $block->escapeHtml(__('Price')) ?>"> - <?php if (!$_item->getParentItem()): ?> - <?= $block->getItemPriceHtml() ?> + <td class="col sku" data-th="<?= $block->escapeHtml(__('SKU')); ?>"> + <?= /* @noEscape */ $block->prepareSku($item->getSku()); ?> + </td> + <td class="col price" data-th="<?= $block->escapeHtml(__('Price')); ?>"> + <?php if (!$item->getParentItem()): ?> + <?= /* @noEscape */ $block->getItemPriceHtml(); ?> <?php else: ?>   <?php endif; ?> </td> - <td class="col qty" data-th="<?= $block->escapeHtml(__('Quantity')) ?>"> + <td class="col qty" data-th="<?= $block->escapeHtml(__('Quantity')); ?>"> <?php if ( - ($_item->getParentItem() && $block->isChildCalculated()) || - (!$_item->getParentItem() && !$block->isChildCalculated()) || ($_item->getQtyShipped() > 0 && $_item->getParentItem() && $block->isShipmentSeparately())):?> + ($item->getParentItem() && $block->isChildCalculated()) || + (!$item->getParentItem() && !$block->isChildCalculated()) || + ($item->getQtyShipped() > 0 && $item->getParentItem() && $block->isShipmentSeparately())): ?> <ul class="items-qty"> <?php endif; ?> - <?php if (($_item->getParentItem() && $block->isChildCalculated()) || - (!$_item->getParentItem() && !$block->isChildCalculated())): ?> - <?php if ($_item->getQtyOrdered() > 0): ?> + <?php if (($item->getParentItem() && $block->isChildCalculated()) || + (!$item->getParentItem() && !$block->isChildCalculated())): ?> + <?php if ($item->getQtyOrdered() > 0): ?> <li class="item"> - <span class="title"><?= /* @escapeNotVerified */ __('Ordered') ?></span> - <span class="content"><?= /* @escapeNotVerified */ $_item->getQtyOrdered()*1 ?></span> + <span class="title"><?= $block->escapeHtml(__('Ordered')); ?></span> + <span class="content"><?= /* @noEscape */ $item->getQtyOrdered() * 1; ?></span> </li> <?php endif; ?> - <?php if ($_item->getQtyShipped() > 0 && !$block->isShipmentSeparately()): ?> + <?php if ($item->getQtyShipped() > 0 && !$block->isShipmentSeparately()): ?> <li class="item"> - <span class="title"><?= /* @escapeNotVerified */ __('Shipped') ?></span> - <span class="content"><?= /* @escapeNotVerified */ $_item->getQtyShipped()*1 ?></span> + <span class="title"><?= $block->escapeHtml(__('Shipped')); ?></span> + <span class="content"><?= /* @noEscape */ $item->getQtyShipped() * 1; ?></span> </li> <?php endif; ?> - <?php if ($_item->getQtyCanceled() > 0): ?> + <?php if ($item->getQtyCanceled() > 0): ?> <li class="item"> - <span class="title"><?= /* @escapeNotVerified */ __('Canceled') ?></span> - <span class="content"><?= /* @escapeNotVerified */ $_item->getQtyCanceled()*1 ?></span> + <span class="title"><?= $block->escapeHtml(__('Canceled')); ?></span> + <span class="content"><?= /* @noEscape */ $item->getQtyCanceled() * 1; ?></span> </li> <?php endif; ?> - <?php if ($_item->getQtyRefunded() > 0): ?> + <?php if ($item->getQtyRefunded() > 0): ?> <li class="item"> - <span class="title"><?= /* @escapeNotVerified */ __('Refunded') ?></span> - <span class="content"><?= /* @escapeNotVerified */ $_item->getQtyRefunded()*1 ?></span> + <span class="title"><?= $block->escapeHtml(__('Refunded')); ?></span> + <span class="content"><?= /* @noEscape */ $item->getQtyRefunded() * 1; ?></span> </li> <?php endif; ?> - <?php elseif ($_item->getQtyShipped() > 0 && $_item->getParentItem() && $block->isShipmentSeparately()): ?> + <?php elseif ($item->getQtyShipped() > 0 && $item->getParentItem() && $block->isShipmentSeparately()): ?> <li class="item"> - <span class="title"><?= /* @escapeNotVerified */ __('Shipped') ?></span> - <span class="content"><?= /* @escapeNotVerified */ $_item->getQtyShipped()*1 ?></span> + <span class="title"><?= $block->escapeHtml(__('Shipped')); ?></span> + <span class="content"><?= /* @noEscape */ $item->getQtyShipped() * 1; ?></span> </li> <?php else: ?> -   + <span class="content"><?= /* @noEscape */ $parentItem->getQtyOrdered() * 1; ?></span> <?php endif; ?> <?php if ( - ($_item->getParentItem() && $block->isChildCalculated()) || - (!$_item->getParentItem() && !$block->isChildCalculated()) || ($_item->getQtyShipped() > 0 && $_item->getParentItem() && $block->isShipmentSeparately())):?> + ($item->getParentItem() && $block->isChildCalculated()) || + (!$item->getParentItem() && !$block->isChildCalculated()) || + ($item->getQtyShipped() > 0 && $item->getParentItem() && $block->isShipmentSeparately())):?> </ul> <?php endif; ?> </td> <td class="col subtotal" data-th="<?= $block->escapeHtml(__('Subtotal')) ?>"> - <?php if (!$_item->getParentItem()): ?> - <?= $block->getItemRowTotalHtml() ?> + <?php if (!$item->getParentItem()): ?> + <?= /* @noEscape */ $block->getItemRowTotalHtml(); ?> <?php else: ?>   <?php endif; ?> @@ -103,33 +119,38 @@ </tr> <?php endforeach; ?> -<?php if ($_showlastRow && (($_options = $block->getItemOptions()) || $block->escapeHtml($_item->getDescription()))): ?> +<?php if ($showLastRow && (($options = $block->getItemOptions()) || $block->escapeHtml($item->getDescription()))): ?> <tr> <td class="col options" colspan="5"> - <?php if ($_options = $block->getItemOptions()): ?> + <?php if ($options = $block->getItemOptions()): ?> <dl class="item-options"> - <?php foreach ($_options as $_option) : ?> - <dt><?= $block->escapeHtml($_option['label']) ?></dt> + <?php foreach ($options as $option) : ?> + <dt><?= $block->escapeHtml($option['label']) ?></dt> <?php if (!$block->getPrintStatus()): ?> - <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?> - <dd<?php if (isset($_formatedOptionValue['full_view'])): ?> class="tooltip wrapper"<?php endif; ?>> - <?= /* @escapeNotVerified */ $_formatedOptionValue['value'] ?> - <?php if (isset($_formatedOptionValue['full_view'])): ?> + <?php $formattedOptionValue = $block->getFormatedOptionValue($option) ?> + <dd<?php if (isset($formattedOptionValue['full_view'])): ?> + class="tooltip wrapper" + <?php endif; ?>> + <?= /* @noEscape */ $formattedOptionValue['value'] ?> + <?php if (isset($formattedOptionValue['full_view'])): ?> <div class="tooltip content"> <dl class="item options"> - <dt><?= $block->escapeHtml($_option['label']) ?></dt> - <dd><?= /* @escapeNotVerified */ $_formatedOptionValue['full_view'] ?></dd> + <dt><?= $block->escapeHtml($option['label']); ?></dt> + <dd><?= /* @noEscape */ $formattedOptionValue['full_view']; ?></dd> </dl> </div> <?php endif; ?> </dd> <?php else: ?> - <dd><?= $block->escapeHtml((isset($_option['print_value']) ? $_option['print_value'] : $_option['value'])) ?></dd> + <dd><?= $block->escapeHtml((isset($option['print_value']) ? + $option['print_value'] : + $option['value'])); ?> + </dd> <?php endif; ?> <?php endforeach; ?> </dl> <?php endif; ?> - <?= $block->escapeHtml($_item->getDescription()) ?> + <?= $block->escapeHtml($item->getDescription()); ?> </td> </tr> <?php endif; ?> From fe674a6c48542fea07d86abd9b22b42400c11179 Mon Sep 17 00:00:00 2001 From: serhii balko <serhii.balko@transoftgroup.com> Date: Mon, 14 Jan 2019 12:04:47 +0200 Subject: [PATCH 0219/1295] MAGETWO-96886: A new customer created via an admin order has their Customer Group set to the Default customer group on the default website scope --- .../Adminhtml/Order/Create/Form/Account.php | 50 ++++++++-- .../Order/Create/Form/AccountTest.php | 93 +++++++++++++++---- 2 files changed, 115 insertions(+), 28 deletions(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php index 0f92fa2320e89..b61a5cf83734b 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php @@ -11,6 +11,7 @@ use Magento\Framework\Api\ExtensibleDataObjectConverter; use Magento\Framework\Data\Form\Element\AbstractElement; use Magento\Framework\Pricing\PriceCurrencyInterface; +use Magento\Store\Model\ScopeInterface; /** * Create order account form @@ -134,15 +135,8 @@ protected function _prepareForm() $this->_addAttributesToForm($attributes, $fieldset); $this->_form->addFieldNameSuffix('order[account]'); - - $formValues = $this->getFormValues(); - foreach ($attributes as $code => $attribute) { - $defaultValue = $attribute->getDefaultValue(); - if (isset($defaultValue) && !isset($formValues[$code])) { - $formValues[$code] = $defaultValue; - } - } - $this->_form->setValues($formValues); + $storeId = (int)$this->_sessionQuote->getStoreId(); + $this->_form->setValues($this->extractValuesFromAttributes($attributes, $storeId)); return $this; } @@ -189,4 +183,42 @@ public function getFormValues() return $data; } + + /** + * Extract the form values from attributes. + * + * @param array $attributes + * @param int $storeId + * @return array + */ + private function extractValuesFromAttributes(array $attributes, int $storeId): array + { + $formValues = $this->getFormValues(); + foreach ($attributes as $code => $attribute) { + $defaultValue = $attribute->getDefaultValue(); + if (isset($defaultValue) && !isset($formValues[$code])) { + $formValues[$code] = $defaultValue; + } + if ($code === 'group_id' && empty($defaultValue)) { + $formValues[$code] = $this->getDefaultCustomerGroup($storeId); + } + } + + return $formValues; + } + + /** + * Gets default customer group. + * + * @param int $storeId + * @return string|null + */ + private function getDefaultCustomerGroup(int $storeId) + { + return $this->_scopeConfig->getValue( + 'customer/create_account/default_group', + ScopeInterface::SCOPE_STORE, + $storeId + ); + } } diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php index 999522a49e006..b289e9b94558e 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php @@ -11,26 +11,37 @@ namespace Magento\Sales\Block\Adminhtml\Order\Create\Form; use Magento\Backend\Model\Session\Quote as SessionQuote; +use Magento\Customer\Api\Data\AttributeMetadataInterface; use Magento\Customer\Api\Data\AttributeMetadataInterfaceFactory; +use Magento\Customer\Model\Data\Option; use Magento\Customer\Model\Metadata\Form; use Magento\Customer\Model\Metadata\FormFactory; use Magento\Framework\View\LayoutInterface; use Magento\Quote\Model\Quote; use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; /** * @magentoAppArea adminhtml */ class AccountTest extends \PHPUnit\Framework\TestCase { - /** @var Account */ + /** + * @var Account + */ private $accountBlock; /** - * @var Bootstrap + * @var ObjectManager */ private $objectManager; + /** + * @var SessionQuote|MockObject + */ + private $session; + /** * @magentoDataFixture Magento/Sales/_files/quote.php */ @@ -38,19 +49,23 @@ protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); $quote = $this->objectManager->create(Quote::class)->load(1); - $sessionQuoteMock = $this->getMockBuilder( - SessionQuote::class - )->disableOriginalConstructor()->setMethods( - ['getCustomerId', 'getStore', 'getStoreId', 'getQuote'] - )->getMock(); - $sessionQuoteMock->expects($this->any())->method('getCustomerId')->will($this->returnValue(1)); - $sessionQuoteMock->expects($this->any())->method('getQuote')->will($this->returnValue($quote)); + + $this->session = $this->getMockBuilder(SessionQuote::class) + ->disableOriginalConstructor() + ->setMethods(['getCustomerId', 'getStore', 'getStoreId', 'getQuote', 'getQuoteId']) + ->getMock(); + $this->session->method('getCustomerId') + ->willReturn(1); + $this->session->method('getQuote') + ->willReturn($quote); + $this->session->method('getQuoteId') + ->willReturn($quote->getId()); /** @var LayoutInterface $layout */ $layout = $this->objectManager->get(LayoutInterface::class); $this->accountBlock = $layout->createBlock( Account::class, 'address_block' . rand(), - ['sessionQuote' => $sessionQuoteMock] + ['sessionQuote' => $this->session] ); parent::setUp(); } @@ -62,13 +77,13 @@ public function testGetForm() { $expectedFields = ['group_id', 'email']; $form = $this->accountBlock->getForm(); - $this->assertEquals(1, $form->getElements()->count(), "Form has invalid number of fieldsets"); + self::assertEquals(1, $form->getElements()->count(), "Form has invalid number of fieldsets"); $fieldset = $form->getElements()[0]; - $this->assertEquals(count($expectedFields), $fieldset->getElements()->count()); + self::assertEquals(count($expectedFields), $fieldset->getElements()->count()); foreach ($fieldset->getElements() as $element) { - $this->assertTrue( + self::assertTrue( in_array($element->getId(), $expectedFields), sprintf('Unexpected field "%s" in form.', $element->getId()) ); @@ -79,6 +94,7 @@ public function testGetForm() * Tests a case when user defined custom attribute has default value. * * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoConfigFixture current_store customer/create_account/default_group 3 */ public function testGetFormWithUserDefinedAttribute() { @@ -91,18 +107,27 @@ public function testGetFormWithUserDefinedAttribute() $form = $accountBlock->getForm(); $form->setUseContainer(true); + $content = $form->toHtml(); - $this->assertContains( + self::assertContains( '<option value="1" selected="selected">Yes</option>', - $form->toHtml(), - 'Default value for user defined custom attribute should be selected' + $content, + 'Default value for user defined custom attribute should be selected.' + ); + + self::assertContains( + '<option value="3" selected="selected">Customer Group 1</option>', + $content, + 'The Customer Group specified for the chosen store should be selected.' ); } /** - * @return \PHPUnit_Framework_MockObject_MockObject + * Creates a mock for Form object. + * + * @return MockObject */ - private function getFormFactoryMock(): \PHPUnit_Framework_MockObject_MockObject + private function getFormFactoryMock(): MockObject { /** @var AttributeMetadataInterfaceFactory $attributeMetadataFactory */ $attributeMetadataFactory = $this->objectManager->create(AttributeMetadataInterfaceFactory::class); @@ -113,11 +138,12 @@ private function getFormFactoryMock(): \PHPUnit_Framework_MockObject_MockObject ->setDefaultValue('1') ->setFrontendLabel('Yes/No'); + /** @var Form|MockObject $form */ $form = $this->getMockBuilder(Form::class) ->disableOriginalConstructor() ->getMock(); $form->method('getUserAttributes')->willReturn([$booleanAttribute]); - $form->method('getSystemAttributes')->willReturn([]); + $form->method('getSystemAttributes')->willReturn([$this->createCustomerGroupAttribute()]); $formFactory = $this->getMockBuilder(FormFactory::class) ->disableOriginalConstructor() @@ -126,4 +152,33 @@ private function getFormFactoryMock(): \PHPUnit_Framework_MockObject_MockObject return $formFactory; } + + /** + * Creates a customer group attribute object. + * + * @return AttributeMetadataInterface + */ + private function createCustomerGroupAttribute(): AttributeMetadataInterface + { + /** @var Option $option1 */ + $option1 = $this->objectManager->create(Option::class); + $option1->setValue(3); + $option1->setLabel('Customer Group 1'); + + /** @var Option $option2 */ + $option2 = $this->objectManager->create(Option::class); + $option2->setValue(4); + $option2->setLabel('Customer Group 2'); + + /** @var AttributeMetadataInterfaceFactory $attributeMetadataFactory */ + $attributeMetadataFactory = $this->objectManager->create(AttributeMetadataInterfaceFactory::class); + $attribute = $attributeMetadataFactory->create() + ->setAttributeCode('group_id') + ->setBackendType('static') + ->setFrontendInput('select') + ->setOptions([$option1, $option2]) + ->setIsRequired(true); + + return $attribute; + } } From cf0493787bd37f71d27c85df9b23765b69274a2d Mon Sep 17 00:00:00 2001 From: serhii balko <serhii.balko@transoftgroup.com> Date: Mon, 14 Jan 2019 13:22:29 +0200 Subject: [PATCH 0220/1295] MAGETWO-96886: A new customer created via an admin order has their Customer Group set to the Default customer group on the default website scope --- .../Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php index b289e9b94558e..ce3f3a3e1fc8e 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php @@ -127,7 +127,7 @@ public function testGetFormWithUserDefinedAttribute() * * @return MockObject */ - private function getFormFactoryMock(): MockObject + private function getFormFactoryMock() { /** @var AttributeMetadataInterfaceFactory $attributeMetadataFactory */ $attributeMetadataFactory = $this->objectManager->create(AttributeMetadataInterfaceFactory::class); From 96c14b6ff8ac4a3200777b94d7f549196055a912 Mon Sep 17 00:00:00 2001 From: roman <rleshchenko@magento.com> Date: Mon, 14 Jan 2019 13:24:12 +0200 Subject: [PATCH 0221/1295] MAGETWO-97066: Fixed incorrect behavior of catalog attributes actions --- .../Adminhtml/Product/Attribute/SaveTest.php | 339 ------------------ .../Adminhtml/Product/AttributeTest.php | 61 +++- 2 files changed, 55 insertions(+), 345 deletions(-) delete mode 100644 app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/SaveTest.php diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/SaveTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/SaveTest.php deleted file mode 100644 index a1aaab0995d73..0000000000000 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/SaveTest.php +++ /dev/null @@ -1,339 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Catalog\Test\Unit\Controller\Adminhtml\Product\Attribute; - -use Magento\Catalog\Api\Data\ProductAttributeInterface; -use Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save; -use Magento\Catalog\Test\Unit\Controller\Adminhtml\Product\AttributeTest; -use Magento\Catalog\Model\Product\AttributeSet\BuildFactory; -use Magento\Catalog\Model\Product\AttributeSet\Build; -use Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory; -use Magento\Eav\Api\Data\AttributeSetInterface; -use Magento\Eav\Model\Adminhtml\System\Config\Source\Inputtype\ValidatorFactory; -use Magento\Eav\Model\ResourceModel\Entity\Attribute\Group\CollectionFactory; -use Magento\Framework\Message\ManagerInterface; -use Magento\Framework\Serialize\Serializer\FormData; -use Magento\Framework\Controller\ResultFactory; -use Magento\Framework\Filter\FilterManager; -use Magento\Catalog\Helper\Product as ProductHelper; -use Magento\Framework\View\Element\Messages; -use Magento\Framework\View\LayoutFactory; -use Magento\Backend\Model\View\Result\Redirect as ResultRedirect; -use Magento\Eav\Model\Adminhtml\System\Config\Source\Inputtype\Validator as InputTypeValidator; -use Magento\Framework\View\LayoutInterface; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class SaveTest extends AttributeTest -{ - /** - * @var BuildFactory|\PHPUnit_Framework_MockObject_MockObject - */ - protected $buildFactoryMock; - - /** - * @var FilterManager|\PHPUnit_Framework_MockObject_MockObject - */ - protected $filterManagerMock; - - /** - * @var ProductHelper|\PHPUnit_Framework_MockObject_MockObject - */ - protected $productHelperMock; - - /** - * @var AttributeFactory|\PHPUnit_Framework_MockObject_MockObject - */ - protected $attributeFactoryMock; - - /** - * @var ValidatorFactory|\PHPUnit_Framework_MockObject_MockObject - */ - protected $validatorFactoryMock; - - /** - * @var CollectionFactory|\PHPUnit_Framework_MockObject_MockObject - */ - protected $groupCollectionFactoryMock; - - /** - * @var LayoutFactory|\PHPUnit_Framework_MockObject_MockObject - */ - protected $layoutFactoryMock; - - /** - * @var ResultRedirect|\PHPUnit_Framework_MockObject_MockObject - */ - protected $redirectMock; - - /** - * @var AttributeSet|\PHPUnit_Framework_MockObject_MockObject - */ - protected $attributeSetMock; - - /** - * @var Build|\PHPUnit_Framework_MockObject_MockObject - */ - protected $builderMock; - - /** - * @var InputTypeValidator|\PHPUnit_Framework_MockObject_MockObject - */ - protected $inputTypeValidatorMock; - - /** - * @var ManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $messageManagerMock; - - /** - * @var FormData|\PHPUnit_Framework_MockObject_MockObject - */ - private $formDataSerializerMock; - - /** - * @var ProductAttributeInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $productAttributeMock; - - protected function setUp() - { - parent::setUp(); - $this->buildFactoryMock = $this->getMockBuilder(BuildFactory::class) - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); - $this->filterManagerMock = $this->getMockBuilder(FilterManager::class) - ->disableOriginalConstructor() - ->getMock(); - $this->productHelperMock = $this->getMockBuilder(ProductHelper::class) - ->disableOriginalConstructor() - ->getMock(); - $this->attributeFactoryMock = $this->getMockBuilder(AttributeFactory::class) - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); - $this->validatorFactoryMock = $this->getMockBuilder(ValidatorFactory::class) - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); - $this->groupCollectionFactoryMock = $this->getMockBuilder(CollectionFactory::class) - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); - $this->layoutFactoryMock = $this->getMockBuilder(LayoutFactory::class) - ->disableOriginalConstructor() - ->getMock(); - $this->redirectMock = $this->getMockBuilder(ResultRedirect::class) - ->setMethods(['setData', 'setPath']) - ->disableOriginalConstructor() - ->getMock(); - $this->attributeSetMock = $this->getMockBuilder(AttributeSetInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->builderMock = $this->getMockBuilder(Build::class) - ->disableOriginalConstructor() - ->getMock(); - $this->inputTypeValidatorMock = $this->getMockBuilder(InputTypeValidator::class) - ->disableOriginalConstructor() - ->getMock(); - $this->messageManagerMock = $this->getMockBuilder(\Magento\Framework\Message\ManagerInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $this->formDataSerializerMock = $this->getMockBuilder(FormData::class) - ->disableOriginalConstructor() - ->getMock(); - $this->productAttributeMock = $this->getMockBuilder(ProductAttributeInterface::class) - ->setMethods(['getId', 'get']) - ->getMockForAbstractClass(); - - $this->buildFactoryMock->expects($this->any()) - ->method('create') - ->willReturn($this->builderMock); - $this->validatorFactoryMock->expects($this->any()) - ->method('create') - ->willReturn($this->inputTypeValidatorMock); - $this->attributeFactoryMock - ->method('create') - ->willReturn($this->productAttributeMock); - } - - /** - * {@inheritdoc} - */ - protected function getModel() - { - return $this->objectManager->getObject(Save::class, [ - 'context' => $this->contextMock, - 'messageManager' => $this->messageManagerMock, - 'attributeLabelCache' => $this->attributeLabelCacheMock, - 'coreRegistry' => $this->coreRegistryMock, - 'resultPageFactory' => $this->resultPageFactoryMock, - 'buildFactory' => $this->buildFactoryMock, - 'filterManager' => $this->filterManagerMock, - 'productHelper' => $this->productHelperMock, - 'attributeFactory' => $this->attributeFactoryMock, - 'validatorFactory' => $this->validatorFactoryMock, - 'groupCollectionFactory' => $this->groupCollectionFactoryMock, - 'layoutFactory' => $this->layoutFactoryMock, - 'formDataSerializer' => $this->formDataSerializerMock, - ]); - } - - public function testExecuteWithEmptyData() - { - $this->requestMock->expects($this->any()) - ->method('getParam') - ->willReturnMap([ - ['isAjax', null, null], - ['serialized_options', '[]', ''], - ]); - $this->formDataSerializerMock->expects($this->once()) - ->method('unserialize') - ->with('') - ->willReturn([]); - $this->requestMock->expects($this->once()) - ->method('getPostValue') - ->willReturn([]); - $this->resultFactoryMock->expects($this->once()) - ->method('create') - ->willReturn($this->redirectMock); - $this->redirectMock->expects($this->any()) - ->method('setPath') - ->willReturnSelf(); - - $this->assertInstanceOf(ResultRedirect::class, $this->getModel()->execute()); - } - - public function testExecute() - { - $data = [ - 'new_attribute_set_name' => 'Test attribute set name', - 'frontend_input' => 'test_frontend_input', - ]; - - $this->requestMock->expects($this->any()) - ->method('getParam') - ->willReturnMap([ - ['isAjax', null, null], - ['serialized_options', '[]', ''], - ]); - $this->formDataSerializerMock->expects($this->once()) - ->method('unserialize') - ->with('') - ->willReturn([]); - $this->productAttributeMock->expects($this->once()) - ->method('getId') - ->willReturn(1); - $this->productAttributeMock->expects($this->once()) - ->method('getAttributeCode') - ->willReturn('test_code'); - $this->requestMock->expects($this->once()) - ->method('getPostValue') - ->willReturn($data); - $this->resultFactoryMock->expects($this->once()) - ->method('create') - ->willReturn($this->redirectMock); - $this->redirectMock->expects($this->any()) - ->method('setPath') - ->willReturnSelf(); - $this->builderMock->expects($this->once()) - ->method('setEntityTypeId') - ->willReturnSelf(); - $this->builderMock->expects($this->once()) - ->method('setSkeletonId') - ->willReturnSelf(); - $this->builderMock->expects($this->once()) - ->method('setName') - ->willReturnSelf(); - $this->builderMock->expects($this->once()) - ->method('getAttributeSet') - ->willReturn($this->attributeSetMock); - $this->requestMock->expects($this->any()) - ->method('getParam') - ->willReturnMap([ - ['set', null, 1], - ['attribute_code', null, 'test_attribute_code'] - ]); - $this->inputTypeValidatorMock->expects($this->once()) - ->method('getMessages') - ->willReturn([]); - - $this->assertInstanceOf(ResultRedirect::class, $this->getModel()->execute()); - } - - /** - * @return void - * @throws \Magento\Framework\Exception\NotFoundException - */ - public function testExecuteWithOptionsDataError() - { - $serializedOptions = '{"key":"value"}'; - $message = "The attribute couldn't be saved due to an error. Verify your information and try again. " - . "If the error persists, please try again later."; - - $this->requestMock->expects($this->any()) - ->method('getParam') - ->willReturnMap([ - ['isAjax', null, true], - ['serialized_options', '[]', $serializedOptions], - ]); - $this->formDataSerializerMock->expects($this->once()) - ->method('unserialize') - ->with($serializedOptions) - ->willThrowException(new \InvalidArgumentException('Some exception')); - $this->messageManagerMock->expects($this->once()) - ->method('addErrorMessage') - ->with($message); - $this->addReturnResultConditions('catalog/*/edit', ['_current' => true], ['error' => true]); - - $this->getModel()->execute(); - } - - /** - * @param string $path - * @param array $params - * @param array $response - * @return void - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - private function addReturnResultConditions(string $path = '', array $params = [], array $response = []) - { - $layoutMock = $this->getMockBuilder(LayoutInterface::class) - ->setMethods(['initMessages', 'getMessagesBlock']) - ->getMockForAbstractClass(); - $this->layoutFactoryMock->expects($this->once()) - ->method('create') - ->with() - ->willReturn($layoutMock); - $layoutMock->expects($this->once()) - ->method('initMessages') - ->with(); - $messageBlockMock = $this->getMockBuilder(Messages::class) - ->disableOriginalConstructor() - ->getMock(); - $layoutMock->expects($this->once()) - ->method('getMessagesBlock') - ->willReturn($messageBlockMock); - $messageBlockMock->expects($this->once()) - ->method('getGroupedHtml') - ->willReturn('message1'); - $this->resultFactoryMock->expects($this->once()) - ->method('create') - ->with(ResultFactory::TYPE_JSON) - ->willReturn($this->redirectMock); - $response = array_merge($response, [ - 'messages' => ['message1'], - 'params' => $params, - ]); - $this->redirectMock->expects($this->once()) - ->method('setData') - ->with($response) - ->willReturnSelf(); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php index c16ebb24d496f..abf8480708dad 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php @@ -6,6 +6,7 @@ namespace Magento\Catalog\Controller\Adminhtml\Product; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Data\Form\FormKey; /** * @magentoAppArea adminhtml @@ -18,9 +19,12 @@ class AttributeTest extends \Magento\TestFramework\TestCase\AbstractBackendContr */ public function testWrongFrontendInput() { + /** @var FormKey $formKey */ + $formKey = $this->_objectManager->get(FormKey::class); $postData = $this->_getAttributeData() + [ 'attribute_id' => 100500, 'frontend_input' => 'some_input', + 'form_key' => $formKey->getFormKey() ]; $this->getRequest()->setPostValue($postData); $this->getRequest()->setMethod('POST'); @@ -43,10 +47,13 @@ public function testWrongFrontendInput() */ public function testWithPopup() { + /** @var FormKey $formKey */ + $formKey = $this->_objectManager->get(FormKey::class); $postData = $this->_getAttributeData() + [ 'attribute_id' => 5, 'popup' => 'true', 'new_attribute_set_name' => 'new_attribute_set', + 'form_key' => $formKey->getFormKey() ]; $this->getRequest()->setPostValue($postData); $this->getRequest()->setMethod('POST'); @@ -68,7 +75,14 @@ public function testWithPopup() */ public function testWithExceptionWhenSaveAttribute() { - $postData = $this->_getAttributeData() + ['attribute_id' => 0, 'frontend_input' => 'boolean']; + /** @var FormKey $formKey */ + $formKey = $this->_objectManager->get(FormKey::class); + $postData = $this->_getAttributeData() + [ + 'attribute_id' => 0, + 'frontend_input' => 'boolean', + 'form_key' => $formKey->getFormKey() + ]; + $this->getRequest()->setMethod('POST'); $this->getRequest()->setPostValue($postData); $this->dispatch('backend/catalog/product_attribute/save'); @@ -87,7 +101,13 @@ public function testWithExceptionWhenSaveAttribute() */ public function testWrongAttributeId() { - $postData = $this->_getAttributeData() + ['attribute_id' => 100500]; + /** @var FormKey $formKey */ + $formKey = $this->_objectManager->get(FormKey::class); + $postData = $this->_getAttributeData() + [ + 'attribute_id' => 100500, + 'form_key' => $formKey->getFormKey() + ]; + $this->getRequest()->setMethod('POST'); $this->getRequest()->setPostValue($postData); $this->dispatch('backend/catalog/product_attribute/save'); @@ -109,10 +129,13 @@ public function testWrongAttributeId() */ public function testAttributeWithoutId() { + /** @var FormKey $formKey */ + $formKey = $this->_objectManager->get(FormKey::class); $postData = $this->_getAttributeData() + [ 'attribute_code' => uniqid('attribute_'), 'set' => 4, 'frontend_input' => 'boolean', + 'form_key' => $formKey->getFormKey() ]; $this->getRequest()->setMethod('POST'); $this->getRequest()->setPostValue($postData); @@ -135,7 +158,12 @@ public function testAttributeWithoutId() */ public function testWrongAttributeCode() { - $postData = $this->_getAttributeData() + ['attribute_code' => '_()&&&?']; + /** @var FormKey $formKey */ + $formKey = $this->_objectManager->get(FormKey::class); + $postData = $this->_getAttributeData() + [ + 'attribute_code' => '_()&&&?', + 'form_key' => $formKey->getFormKey() + ]; $this->getRequest()->setPostValue($postData); $this->getRequest()->setMethod('POST'); $this->dispatch('backend/catalog/product_attribute/save'); @@ -161,7 +189,13 @@ public function testWrongAttributeCode() */ public function testAttributeWithoutEntityTypeId() { - $postData = $this->_getAttributeData() + ['attribute_id' => '2', 'new_attribute_set_name' => ' ']; + /** @var FormKey $formKey */ + $formKey = $this->_objectManager->get(FormKey::class); + $postData = $this->_getAttributeData() + [ + 'attribute_id' => '2', + 'new_attribute_set_name' => ' ', + 'form_key' => $formKey->getFormKey() + ]; $this->getRequest()->setPostValue($postData); $this->getRequest()->setMethod('POST'); $this->dispatch('backend/catalog/product_attribute/save'); @@ -177,7 +211,13 @@ public function testAttributeWithoutEntityTypeId() */ public function testSaveActionApplyToDataSystemAttribute() { - $postData = $this->_getAttributeData() + ['attribute_id' => '2']; + /** @var FormKey $formKey */ + $formKey = $this->_objectManager->get(FormKey::class); + $postData = $this->_getAttributeData() + [ + 'attribute_id' => '2', + 'form_key' => $formKey->getFormKey() + ]; + $this->getRequest()->setPostValue($postData); $this->getRequest()->setMethod('POST'); $this->dispatch('backend/catalog/product_attribute/save'); @@ -191,7 +231,13 @@ public function testSaveActionApplyToDataSystemAttribute() */ public function testSaveActionApplyToDataUserDefinedAttribute() { - $postData = $this->_getAttributeData() + ['attribute_id' => '1']; + /** @var FormKey $formKey */ + $formKey = $this->_objectManager->get(FormKey::class); + $postData = $this->_getAttributeData() + [ + 'attribute_id' => '1', + 'form_key' => $formKey->getFormKey() + ]; + $this->getRequest()->setPostValue($postData); $this->getRequest()->setMethod('POST'); $this->dispatch('backend/catalog/product_attribute/save'); @@ -303,8 +349,11 @@ public function testLargeOptionsDataSet() $optionsData[] = http_build_query($optionRowData); } $attributeData['serialized_options'] = json_encode($optionsData); + /** @var FormKey $formKey */ + $formKey = $this->_objectManager->get(FormKey::class); $this->getRequest()->setMethod('POST'); $this->getRequest()->setPostValue($attributeData); + $this->getRequest()->setParam('form_key', $formKey->getFormKey()); $this->dispatch('backend/catalog/product_attribute/save'); $entityTypeId = $this->_objectManager->create( \Magento\Eav\Model\Entity::class From 936d71fda5b37941dba89a08c79bd0aa1fba820d Mon Sep 17 00:00:00 2001 From: amol 2jcommerce <amol@2jcommerce.in> Date: Mon, 14 Jan 2019 17:19:45 +0530 Subject: [PATCH 0222/1295] Fixed-Widget-option-labels-are-misalinged --- app/design/adminhtml/Magento/backend/web/css/styles-old.less | 1 + 1 file changed, 1 insertion(+) diff --git a/app/design/adminhtml/Magento/backend/web/css/styles-old.less b/app/design/adminhtml/Magento/backend/web/css/styles-old.less index 6d46145df388c..d7a9a77455c13 100644 --- a/app/design/adminhtml/Magento/backend/web/css/styles-old.less +++ b/app/design/adminhtml/Magento/backend/web/css/styles-old.less @@ -2739,6 +2739,7 @@ #widget_instace_tabs_properties_section_content .widget-option-label { margin-top: 6px; + display: inline-block; } // From 7e3ea1b3313bc99e0f8c3ee458a96aee4b1bdedc Mon Sep 17 00:00:00 2001 From: Tommy Quissens <tommy.quissens@storefront.be> Date: Fri, 15 Jun 2018 10:48:04 +0200 Subject: [PATCH 0223/1295] added json hex tag serializer and use it. code formatting Backwards compatibility fix use JSON_HEX_TAG option for not breaking things use new serializer instead of json_encode use correct serializer in test declare return type use serializer updated unit testing, now uses serializer removed space declare strict type improved comment Test new serializer Test for greater than and lesser than symbol, required by JSON_HEX_TAG option split comment for readability Declare strict type on test Use di to inject cleanup curly brackets code cleanup removed empty phpdoc Fix BC --- app/code/Magento/Checkout/Block/Onepage.php | 17 +-- .../Checkout/Test/Unit/Block/OnepageTest.php | 9 +- app/code/Magento/Checkout/etc/frontend/di.xml | 1 + .../Ui/TemplateEngine/Xhtml/Result.php | 14 ++- .../Magento/Framework/Serialize/README.md | 1 + .../Serialize/Serializer/JsonHexTag.php | 35 ++++++ .../Test/Unit/Serializer/JsonHexTagTest.php | 116 ++++++++++++++++++ 7 files changed, 180 insertions(+), 13 deletions(-) create mode 100644 lib/internal/Magento/Framework/Serialize/Serializer/JsonHexTag.php create mode 100644 lib/internal/Magento/Framework/Serialize/Test/Unit/Serializer/JsonHexTagTest.php diff --git a/app/code/Magento/Checkout/Block/Onepage.php b/app/code/Magento/Checkout/Block/Onepage.php index ca6b045ddbb5d..6beba481d4ec8 100644 --- a/app/code/Magento/Checkout/Block/Onepage.php +++ b/app/code/Magento/Checkout/Block/Onepage.php @@ -38,7 +38,7 @@ class Onepage extends \Magento\Framework\View\Element\Template protected $layoutProcessors; /** - * @var \Magento\Framework\Serialize\Serializer\Json + * @var \Magento\Framework\Serialize\SerializerInterface */ private $serializer; @@ -48,8 +48,8 @@ class Onepage extends \Magento\Framework\View\Element\Template * @param \Magento\Checkout\Model\CompositeConfigProvider $configProvider * @param array $layoutProcessors * @param array $data - * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer - * @throws \RuntimeException + * @param \Magento\Framework\Serialize\Serializer\Json $serializer + * @param \Magento\Framework\Serialize\SerializerInterface $serializerInterface */ public function __construct( \Magento\Framework\View\Element\Template\Context $context, @@ -57,7 +57,8 @@ public function __construct( \Magento\Checkout\Model\CompositeConfigProvider $configProvider, array $layoutProcessors = [], array $data = [], - \Magento\Framework\Serialize\Serializer\Json $serializer = null + \Magento\Framework\Serialize\Serializer\Json $serializer = null, + \Magento\Framework\Serialize\SerializerInterface $serializerInterface = null ) { parent::__construct($context, $data); $this->formKey = $formKey; @@ -65,8 +66,8 @@ public function __construct( $this->jsLayout = isset($data['jsLayout']) && is_array($data['jsLayout']) ? $data['jsLayout'] : []; $this->configProvider = $configProvider; $this->layoutProcessors = $layoutProcessors; - $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\Serialize\Serializer\Json::class); + $this->serializer = $serializerInterface ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\Serializer\JsonHexTag::class); } /** @@ -78,7 +79,7 @@ public function getJsLayout() $this->jsLayout = $processor->process($this->jsLayout); } - return json_encode($this->jsLayout, JSON_HEX_TAG); + return $this->serializer->serialize($this->jsLayout); } /** @@ -120,6 +121,6 @@ public function getBaseUrl() */ public function getSerializedCheckoutConfig() { - return json_encode($this->getCheckoutConfig(), JSON_HEX_TAG); + return $this->serializer->serialize($this->getCheckoutConfig()); } } diff --git a/app/code/Magento/Checkout/Test/Unit/Block/OnepageTest.php b/app/code/Magento/Checkout/Test/Unit/Block/OnepageTest.php index 54f77c95148ac..b54339aa2c1d8 100644 --- a/app/code/Magento/Checkout/Test/Unit/Block/OnepageTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Block/OnepageTest.php @@ -35,7 +35,7 @@ class OnepageTest extends \PHPUnit\Framework\TestCase /** * @var \PHPUnit_Framework_MockObject_MockObject */ - private $serializer; + private $serializerMock; protected function setUp() { @@ -49,7 +49,7 @@ protected function setUp() \Magento\Checkout\Block\Checkout\LayoutProcessorInterface::class ); - $this->serializer = $this->createMock(\Magento\Framework\Serialize\Serializer\Json::class); + $this->serializerMock = $this->createMock(\Magento\Framework\Serialize\Serializer\JsonHexTag::class); $this->model = new \Magento\Checkout\Block\Onepage( $contextMock, @@ -57,7 +57,8 @@ protected function setUp() $this->configProviderMock, [$this->layoutProcessorMock], [], - $this->serializer + $this->serializerMock, + $this->serializerMock ); } @@ -93,6 +94,7 @@ public function testGetJsLayout() $processedLayout = ['layout' => ['processed' => true]]; $jsonLayout = '{"layout":{"processed":true}}'; $this->layoutProcessorMock->expects($this->once())->method('process')->with([])->willReturn($processedLayout); + $this->serializerMock->expects($this->once())->method('serialize')->willReturn($jsonLayout); $this->assertEquals($jsonLayout, $this->model->getJsLayout()); } @@ -101,6 +103,7 @@ public function testGetSerializedCheckoutConfig() { $checkoutConfig = ['checkout', 'config']; $this->configProviderMock->expects($this->once())->method('getConfig')->willReturn($checkoutConfig); + $this->serializerMock->expects($this->once())->method('serialize')->willReturn(json_encode($checkoutConfig)); $this->assertEquals(json_encode($checkoutConfig), $this->model->getSerializedCheckoutConfig()); } diff --git a/app/code/Magento/Checkout/etc/frontend/di.xml b/app/code/Magento/Checkout/etc/frontend/di.xml index d80f88786c87b..00bcd2a27005a 100644 --- a/app/code/Magento/Checkout/etc/frontend/di.xml +++ b/app/code/Magento/Checkout/etc/frontend/di.xml @@ -59,6 +59,7 @@ <item name="totalsSortOrder" xsi:type="object">Magento\Checkout\Block\Checkout\TotalsProcessor</item> <item name="directoryData" xsi:type="object">Magento\Checkout\Block\Checkout\DirectoryDataProcessor</item> </argument> + <argument name="serializer" xsi:type="object">Magento\Framework\Serialize\Serializer\JsonHexTag</argument> </arguments> </type> <type name="Magento\Checkout\Block\Cart\Totals"> diff --git a/app/code/Magento/Ui/TemplateEngine/Xhtml/Result.php b/app/code/Magento/Ui/TemplateEngine/Xhtml/Result.php index 066b4494e51d0..14283a8899e50 100644 --- a/app/code/Magento/Ui/TemplateEngine/Xhtml/Result.php +++ b/app/code/Magento/Ui/TemplateEngine/Xhtml/Result.php @@ -5,6 +5,8 @@ */ namespace Magento\Ui\TemplateEngine\Xhtml; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Serialize\Serializer\JsonHexTag; use Magento\Framework\View\Layout\Generator\Structure; use Magento\Framework\View\Element\UiComponentInterface; use Magento\Framework\View\TemplateEngine\Xhtml\Template; @@ -42,25 +44,33 @@ class Result implements ResultInterface */ protected $logger; + /** + * @var JsonHexTag + */ + private $jsonSerializer; + /** * @param Template $template * @param CompilerInterface $compiler * @param UiComponentInterface $component * @param Structure $structure * @param LoggerInterface $logger + * @param JsonHexTag $jsonSerializer */ public function __construct( Template $template, CompilerInterface $compiler, UiComponentInterface $component, Structure $structure, - LoggerInterface $logger + LoggerInterface $logger, + JsonHexTag $jsonSerializer = null ) { $this->template = $template; $this->compiler = $compiler; $this->component = $component; $this->structure = $structure; $this->logger = $logger; + $this->jsonSerializer = $jsonSerializer ?? ObjectManager::getInstance()->get(JsonHexTag::class); } /** @@ -81,7 +91,7 @@ public function getDocumentElement() public function appendLayoutConfiguration() { $layoutConfiguration = $this->wrapContent( - json_encode($this->structure->generate($this->component), JSON_HEX_TAG) + $this->jsonSerializer->serialize($this->structure->generate($this->component)) ); $this->template->append($layoutConfiguration); } diff --git a/lib/internal/Magento/Framework/Serialize/README.md b/lib/internal/Magento/Framework/Serialize/README.md index 5af8fb7f71b6b..d900f89208a54 100644 --- a/lib/internal/Magento/Framework/Serialize/README.md +++ b/lib/internal/Magento/Framework/Serialize/README.md @@ -3,6 +3,7 @@ **Serialize** library provides interface *SerializerInterface* and multiple implementations: * *Json* - default implementation. Uses PHP native json_encode/json_decode functions; + * *JsonHexTag* - default implementation. Uses PHP native json_encode/json_decode functions with `JSON_HEX_TAG` option enabled; * *Serialize* - less secure than *Json*, but gives higher performance on big arrays. Uses PHP native serialize/unserialize functions, does not unserialize objects on PHP 7. Using *Serialize* implementation directly is discouraged, always use *SerializerInterface*, using *Serialize* implementation may lead to security vulnerabilities. \ No newline at end of file diff --git a/lib/internal/Magento/Framework/Serialize/Serializer/JsonHexTag.php b/lib/internal/Magento/Framework/Serialize/Serializer/JsonHexTag.php new file mode 100644 index 0000000000000..4a5406ff3fd99 --- /dev/null +++ b/lib/internal/Magento/Framework/Serialize/Serializer/JsonHexTag.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Framework\Serialize\Serializer; + +use Magento\Framework\Serialize\SerializerInterface; + +/** + * Serialize data to JSON with the JSON_HEX_TAG option enabled + * (All < and > are converted to \u003C and \u003E), + * unserialize JSON encoded data + * + * @api + * @since 100.2.0 + */ +class JsonHexTag extends Json implements SerializerInterface +{ + /** + * @inheritDoc + * @since 100.2.0 + */ + public function serialize($data): string + { + $result = json_encode($data, JSON_HEX_TAG); + if (false === $result) { + throw new \InvalidArgumentException('Unable to serialize value.'); + } + return $result; + } +} diff --git a/lib/internal/Magento/Framework/Serialize/Test/Unit/Serializer/JsonHexTagTest.php b/lib/internal/Magento/Framework/Serialize/Test/Unit/Serializer/JsonHexTagTest.php new file mode 100644 index 0000000000000..f8197a279c62a --- /dev/null +++ b/lib/internal/Magento/Framework/Serialize/Test/Unit/Serializer/JsonHexTagTest.php @@ -0,0 +1,116 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Framework\Serialize\Test\Unit\Serializer; + +use Magento\Framework\DataObject; +use Magento\Framework\Serialize\Serializer\JsonHexTag; + +class JsonHexTagTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $json; + + protected function setUp() + { + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->json = $objectManager->getObject(JsonHexTag::class); + } + + /** + * @param string|int|float|bool|array|null $value + * @param string $expected + * @dataProvider serializeDataProvider + */ + public function testSerialize($value, $expected) + { + $this->assertEquals( + $expected, + $this->json->serialize($value) + ); + } + + public function serializeDataProvider() + { + $dataObject = new DataObject(['something']); + return [ + ['', '""'], + ['string', '"string"'], + [null, 'null'], + [false, 'false'], + [['a' => 'b', 'd' => 123], '{"a":"b","d":123}'], + [123, '123'], + [10.56, '10.56'], + [$dataObject, '{}'], + ['< >', '"\u003C \u003E"'], + ]; + } + + /** + * @param string $value + * @param string|int|float|bool|array|null $expected + * @dataProvider unserializeDataProvider + */ + public function testUnserialize($value, $expected) + { + $this->assertEquals( + $expected, + $this->json->unserialize($value) + ); + } + + /** + * @return array + */ + public function unserializeDataProvider(): array { + return [ + ['""', ''], + ['"string"', 'string'], + ['null', null], + ['false', false], + ['{"a":"b","d":123}', ['a' => 'b', 'd' => 123]], + ['123', 123], + ['10.56', 10.56], + ['{}', []], + ['"\u003C \u003E"', '< >'], + ]; + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Unable to serialize value. + */ + public function testSerializeException() + { + $this->json->serialize(STDOUT); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Unable to unserialize value. + * @dataProvider unserializeExceptionDataProvider + */ + public function testUnserializeException($value) + { + $this->json->unserialize($value); + } + + /** + * @return array + */ + public function unserializeExceptionDataProvider(): array { + return [ + [''], + [false], + [null], + ['{'] + ]; + } +} From f72fb4222a562a162b1ac36e9f79ea721124192a Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Thu, 3 Jan 2019 14:24:46 +0200 Subject: [PATCH 0224/1295] Fix static tests. --- app/code/Magento/Checkout/Block/Onepage.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/Block/Onepage.php b/app/code/Magento/Checkout/Block/Onepage.php index 6beba481d4ec8..e01d5835b4cf0 100644 --- a/app/code/Magento/Checkout/Block/Onepage.php +++ b/app/code/Magento/Checkout/Block/Onepage.php @@ -50,6 +50,7 @@ class Onepage extends \Magento\Framework\View\Element\Template * @param array $data * @param \Magento\Framework\Serialize\Serializer\Json $serializer * @param \Magento\Framework\Serialize\SerializerInterface $serializerInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( \Magento\Framework\View\Element\Template\Context $context, @@ -71,7 +72,7 @@ public function __construct( } /** - * @return string + * @inheritdoc */ public function getJsLayout() { @@ -116,6 +117,8 @@ public function getBaseUrl() } /** + * Retrieve serialized checkout config. + * * @return bool|string * @since 100.2.0 */ From dbb905ca08ebd47d3ef9ebfff77424ccf7fecc56 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Thu, 3 Jan 2019 17:00:49 +0200 Subject: [PATCH 0225/1295] Fix static test. --- .../Serialize/Test/Unit/Serializer/JsonHexTagTest.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Serialize/Test/Unit/Serializer/JsonHexTagTest.php b/lib/internal/Magento/Framework/Serialize/Test/Unit/Serializer/JsonHexTagTest.php index f8197a279c62a..c867dced0fc6e 100644 --- a/lib/internal/Magento/Framework/Serialize/Test/Unit/Serializer/JsonHexTagTest.php +++ b/lib/internal/Magento/Framework/Serialize/Test/Unit/Serializer/JsonHexTagTest.php @@ -69,7 +69,8 @@ public function testUnserialize($value, $expected) /** * @return array */ - public function unserializeDataProvider(): array { + public function unserializeDataProvider(): array + { return [ ['""', ''], ['"string"', 'string'], @@ -105,7 +106,8 @@ public function testUnserializeException($value) /** * @return array */ - public function unserializeExceptionDataProvider(): array { + public function unserializeExceptionDataProvider(): array + { return [ [''], [false], From a987dc536ee722495a235dcc1c2370fc79f530e1 Mon Sep 17 00:00:00 2001 From: amol 2jcommerce <amol@2jcommerce.in> Date: Mon, 14 Jan 2019 17:38:56 +0530 Subject: [PATCH 0226/1295] Fixed-Review-Details-Detailed-Rating-misaligned --- .../Magento/backend/Magento_Review/web/css/source/_module.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Review/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Review/web/css/source/_module.less index 17be2ca706076..faa5845405ab2 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Review/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Review/web/css/source/_module.less @@ -34,7 +34,7 @@ .admin__field-control { direction: rtl; display: inline-block; - margin: -4px 0 0; + margin: -2px 0 0; unicode-bidi: bidi-override; vertical-align: top; width: 125px; From aa9f06f545977644fdb097d4eace9800a309f0b2 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Mon, 14 Jan 2019 14:17:25 +0200 Subject: [PATCH 0227/1295] MAGETWO-72843: "Recently Ordered" widget conatins no more 5 products if qty of products > 5 - Random items collection method fix - Test added --- app/code/Magento/Sales/Model/Order.php | 11 +-- .../CustomerData/LastOrderedItemsTest.php | 55 +++++++++++++ ...with_customer_and_multiple_order_items.php | 12 +++ .../_files/order_with_multiple_items.php | 81 +++++++++++++++++++ 4 files changed, 154 insertions(+), 5 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Sales/CustomerData/LastOrderedItemsTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_customer_and_multiple_order_items.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_multiple_items.php diff --git a/app/code/Magento/Sales/Model/Order.php b/app/code/Magento/Sales/Model/Order.php index 72e90e290f1de..005b7e1bb0638 100644 --- a/app/code/Magento/Sales/Model/Order.php +++ b/app/code/Magento/Sales/Model/Order.php @@ -1383,7 +1383,7 @@ public function getParentItemsRandomCollection($limit = 1) } /** - * Get random items collection with or without related children + * Get random items collection with or without related children. * * @param int $limit * @param bool $nonChildrenOnly @@ -1391,7 +1391,10 @@ public function getParentItemsRandomCollection($limit = 1) */ protected function _getItemsRandomCollection($limit, $nonChildrenOnly = false) { - $collection = $this->_orderItemCollectionFactory->create()->setOrderFilter($this)->setRandomOrder(); + $collection = $this->_orderItemCollectionFactory->create() + ->setOrderFilter($this) + ->setRandomOrder() + ->setPageSize($limit); if ($nonChildrenOnly) { $collection->filterByParent(); @@ -1405,9 +1408,7 @@ protected function _getItemsRandomCollection($limit, $nonChildrenOnly = false) $products )->setVisibility( $this->_productVisibility->getVisibleInSiteIds() - )->addPriceData()->setPageSize( - $limit - )->load(); + )->addPriceData()->load(); foreach ($collection as $item) { $product = $productsCollection->getItemById($item->getProductId()); diff --git a/dev/tests/integration/testsuite/Magento/Sales/CustomerData/LastOrderedItemsTest.php b/dev/tests/integration/testsuite/Magento/Sales/CustomerData/LastOrderedItemsTest.php new file mode 100644 index 0000000000000..adeff2c89a241 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/CustomerData/LastOrderedItemsTest.php @@ -0,0 +1,55 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Sales\CustomerData; + +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; +use Magento\Customer\Model\Session; + +/** + * Test for LastOrderedItems. + * + * @magentoAppIsolation enabled + */ +class LastOrderedItemsTest extends TestCase +{ + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @inheritdoc + */ + public function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + } + + /** + * Test to check count in items collection. + * + * @magentoDataFixture Magento/Sales/_files/order_with_customer_and_multiple_order_items.php + */ + public function testDefaultFormatterIsAppliedWhenBasicIntegration() + { + /** @var Session $customerSession */ + $customerSession = $this->objectManager->get(Session::class); + $customerSession->loginById(1); + + /** @var LastOrderedItems $customerDataSectionSource */ + $customerDataSectionSource = $this->objectManager->get(LastOrderedItems::class); + $data = $customerDataSectionSource->getSectionData(); + + $this->assertEquals( + LastOrderedItems::SIDEBAR_ORDER_LIMIT, + count($data['items']), + 'Section items count should not be greater then ' . LastOrderedItems::SIDEBAR_ORDER_LIMIT + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_customer_and_multiple_order_items.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_customer_and_multiple_order_items.php new file mode 100644 index 0000000000000..586863dd07696 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_customer_and_multiple_order_items.php @@ -0,0 +1,12 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require __DIR__ . '/order_with_multiple_items.php'; +require __DIR__ . '/../../../Magento/Customer/_files/customer.php'; + +$customerIdFromFixture = 1; +/** @var $order \Magento\Sales\Model\Order */ +$order->setCustomerId($customerIdFromFixture)->setCustomerIsGuest(false)->save(); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_multiple_items.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_multiple_items.php new file mode 100644 index 0000000000000..8375ae71fd10b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_multiple_items.php @@ -0,0 +1,81 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +require 'order.php'; +/** @var \Magento\Catalog\Model\Product $product */ +/** @var \Magento\Sales\Model\Order $order */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; +$orderItems[] = [ + 'product_id' => $product->getId(), + 'base_price' => 123, + 'order_id' => $order->getId(), + 'price' => 123, + 'row_total' => 126, + 'product_type' => 'simple' +]; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; +$orderItems[] = [ + 'product_id' => $product->getId(), + 'base_price' => 123, + 'order_id' => $order->getId(), + 'price' => 123, + 'row_total' => 126, + 'product_type' => 'simple' +]; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_with_decimal_qty.php'; +$orderItems[] = [ + 'product_id' => $product->getId(), + 'base_price' => 123, + 'order_id' => $order->getId(), + 'price' => 123, + 'row_total' => 126, + 'product_type' => 'simple' +]; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_with_url_key.php'; +$orderItems[] = [ + 'product_id' => $product->getId(), + 'base_price' => 123, + 'order_id' => $order->getId(), + 'price' => 123, + 'row_total' => 126, + 'product_type' => 'simple' +]; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_sku_with_slash.php'; +$orderItems[] = [ + 'product_id' => $product->getId(), + 'base_price' => 123, + 'order_id' => $order->getId(), + 'price' => 123, + 'row_total' => 126, + 'product_type' => 'simple' +]; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_xss.php'; +$orderItems[] = [ + 'product_id' => $product->getId(), + 'base_price' => 123, + 'order_id' => $order->getId(), + 'price' => 123, + 'row_total' => 126, + 'product_type' => 'simple' +]; + +/** @var array $orderItemData */ +foreach ($orderItems as $orderItemData) { + /** @var $orderItem \Magento\Sales\Model\Order\Item */ + $orderItem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Sales\Model\Order\Item::class + ); + $orderItem + ->setData($orderItemData) + ->save(); +} From 0600244d2c4b9931b72da41ff0cf729d96e031eb Mon Sep 17 00:00:00 2001 From: Tegan Elizabeth Bold <tegan.bold@gmail.com> Date: Fri, 7 Dec 2018 13:56:08 -0500 Subject: [PATCH 0228/1295] Fix issue causing attribute not loading with getList As specified in issue #17759, CustomerRepository::getList() is unexpectedly returning NULL, when CustomerRepository::get() is correctly returning the value. Specifying the attribute as billing_company instead of company removes the conflict. --- .../Magento/Customer/Model/ResourceModel/CustomerRepository.php | 2 +- .../Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php index 9e745769e2c36..d96182202eb7d 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php +++ b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php @@ -365,7 +365,7 @@ public function getList(SearchCriteriaInterface $searchCriteria) ->joinAttribute('billing_telephone', 'customer_address/telephone', 'default_billing', null, 'left') ->joinAttribute('billing_region', 'customer_address/region', 'default_billing', null, 'left') ->joinAttribute('billing_country_id', 'customer_address/country_id', 'default_billing', null, 'left') - ->joinAttribute('company', 'customer_address/company', 'default_billing', null, 'left'); + ->joinAttribute('billing_company', 'customer_address/company', 'default_billing', null, 'left'); $this->collectionProcessor->process($searchCriteria, $collection); diff --git a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php index 215cc32193b18..01951fac7172e 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php @@ -740,7 +740,7 @@ public function testGetList() ->willReturnSelf(); $collection->expects($this->at(7)) ->method('joinAttribute') - ->with('company', 'customer_address/company', 'default_billing', null, 'left') + ->with('billing_company', 'customer_address/company', 'default_billing', null, 'left') ->willReturnSelf(); $this->collectionProcessorMock->expects($this->once()) ->method('process') From a59c0b4e91e61ee242aceba30c16a639f27e2708 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Fri, 21 Dec 2018 13:15:10 +0200 Subject: [PATCH 0229/1295] Fix integration test. --- app/code/Magento/Backend/Model/Search/Customer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Backend/Model/Search/Customer.php b/app/code/Magento/Backend/Model/Search/Customer.php index 35a7359ce9980..e76a1b77ab2d6 100644 --- a/app/code/Magento/Backend/Model/Search/Customer.php +++ b/app/code/Magento/Backend/Model/Search/Customer.php @@ -89,7 +89,7 @@ public function load() $this->searchCriteriaBuilder->setCurrentPage($this->getStart()); $this->searchCriteriaBuilder->setPageSize($this->getLimit()); - $searchFields = ['firstname', 'lastname', 'company']; + $searchFields = ['firstname', 'lastname', 'billing_company']; $filters = []; foreach ($searchFields as $field) { $filters[] = $this->filterBuilder From fba4bc81a623533b7f913571ec29dfdd29060a77 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Thu, 6 Dec 2018 16:03:45 +0200 Subject: [PATCH 0230/1295] Static tests fix. --- .../base/templates/product/price/tier_price.phtml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/view/base/templates/product/price/tier_price.phtml b/app/code/Magento/ConfigurableProduct/view/base/templates/product/price/tier_price.phtml index 9116f04f6928b..325ee1d5d79b3 100644 --- a/app/code/Magento/ConfigurableProduct/view/base/templates/product/price/tier_price.phtml +++ b/app/code/Magento/ConfigurableProduct/view/base/templates/product/price/tier_price.phtml @@ -15,10 +15,13 @@ + '</span>' + '</span>'; %> <li class="item"> - <%= '<?= $block->escapeHtml(__('Buy %1 for %2 each and')) ?>'.replace('%1', item.qty).replace('%2', priceStr) %> - <strong class="benefit"> - <?= $block->escapeHtml(__('save')) ?><span class="percent tier-<%= key %>"> <%= item.percentage %></span>% - </strong> + <%= '<?= $block->escapeHtml(__('Buy %1 for %2 each and', '%1', '%2')) ?>' + .replace('%1', item.qty) + .replace('%2', priceStr) %> + <strong class="benefit"> + <?= $block->escapeHtml(__('save')) ?><span + class="percent tier-<%= key %>"> <%= item.percentage %></span>% + </strong> </li> <% }); %> </ul> From d741b302fa4454318a030532d1390b66b3d3ca08 Mon Sep 17 00:00:00 2001 From: Tristan Hofman <tristan@baldwin.be> Date: Fri, 11 Jan 2019 14:38:58 +0100 Subject: [PATCH 0231/1295] For UPS, Las Palmas and Santa Cruz de Tenerife will be represented by Canary Islands country added missing constants --- .../Shipping/Model/Carrier/AbstractCarrierOnline.php | 8 ++++++++ app/code/Magento/Ups/Model/Carrier.php | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierOnline.php b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierOnline.php index 762ffed75fc34..019baeef062c5 100644 --- a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierOnline.php +++ b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierOnline.php @@ -28,6 +28,14 @@ abstract class AbstractCarrierOnline extends AbstractCarrier const GUAM_REGION_CODE = 'GU'; + const SPAIN_COUNTRY_ID = 'ES'; + + const CANARY_ISLANDS_COUNTRY_ID = 'IC'; + + const SANTA_CRUZ_DE_TENERIFE_REGION_ID = 'Santa Cruz de Tenerife'; + + const LAS_PALMAS_REGION_ID = 'Las Palmas'; + /** * Array of quotes * diff --git a/app/code/Magento/Ups/Model/Carrier.php b/app/code/Magento/Ups/Model/Carrier.php index b7a10e6f37944..a9e289d57300b 100644 --- a/app/code/Magento/Ups/Model/Carrier.php +++ b/app/code/Magento/Ups/Model/Carrier.php @@ -334,6 +334,14 @@ public function setRequest(RateRequest $request) $destCountry = self::GUAM_COUNTRY_ID; } + // For UPS, Las Palmas and Santa Cruz de Tenerife will be represented by Canary Islands country + if ( + $destCountry == self::SPAIN_COUNTRY_ID && + ($request->getDestRegionCode() == self::LAS_PALMAS_REGION_ID || $request->getDestRegionCode() == self::SANTA_CRUZ_DE_TENERIFE_REGION_ID) + ) { + $destCountry = self::CANARY_ISLANDS_COUNTRY_ID; + } + $country = $this->_countryFactory->create()->load($destCountry); $rowRequest->setDestCountry($country->getData('iso2_code') ?: $destCountry); From 0247fc371ac7461cd35b8571dbedaef5a0a30834 Mon Sep 17 00:00:00 2001 From: Erik Pellikka <erik@pellikka.org> Date: Tue, 20 Nov 2018 18:48:06 +0200 Subject: [PATCH 0232/1295] Don't return categoryId from registry if the product doesn't belong in current category --- app/code/Magento/Catalog/Model/Product.php | 2 +- .../Magento/Catalog/Test/Unit/Model/ProductTest.php | 12 +++++++++++- .../Magento/Catalog/Model/ProductExternalTest.php | 3 ++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php index 8a9233f176c61..831644a553b4b 100644 --- a/app/code/Magento/Catalog/Model/Product.php +++ b/app/code/Magento/Catalog/Model/Product.php @@ -714,7 +714,7 @@ public function getIdBySku($sku) public function getCategoryId() { $category = $this->_registry->registry('current_category'); - if ($category) { + if ($category && in_array($category->getId(), $this->getCategoryIds())) { return $category->getId(); } return false; diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php index 483283e777118..677a45c41f846 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php @@ -543,6 +543,7 @@ public function testSetCategoryCollection() public function testGetCategory() { + $this->model->setData('category_ids', [10]); $this->category->expects($this->any())->method('getId')->will($this->returnValue(10)); $this->registry->expects($this->any())->method('registry')->will($this->returnValue($this->category)); $this->categoryRepository->expects($this->any())->method('get')->will($this->returnValue($this->category)); @@ -551,7 +552,8 @@ public function testGetCategory() public function testGetCategoryId() { - $this->category->expects($this->once())->method('getId')->will($this->returnValue(10)); + $this->model->setData('category_ids', [10]); + $this->category->expects($this->any())->method('getId')->will($this->returnValue(10)); $this->registry->expects($this->at(0))->method('registry'); $this->registry->expects($this->at(1))->method('registry')->will($this->returnValue($this->category)); @@ -559,6 +561,14 @@ public function testGetCategoryId() $this->assertEquals(10, $this->model->getCategoryId()); } + public function testGetCategoryIdWhenProductNotInCurrentCategory() + { + $this->model->setData('category_ids', [12]); + $this->category->expects($this->once())->method('getId')->will($this->returnValue(10)); + $this->registry->expects($this->any())->method('registry')->will($this->returnValue($this->category)); + $this->assertFalse($this->model->getCategoryId()); + } + public function testGetIdBySku() { $this->resource->expects($this->once())->method('getIdBySku')->will($this->returnValue(5)); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductExternalTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductExternalTest.php index 8370e514dc2f2..a1923e63972ee 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductExternalTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductExternalTest.php @@ -69,7 +69,7 @@ public function testGetCategoryId() { $this->assertFalse($this->_model->getCategoryId()); $category = new \Magento\Framework\DataObject(['id' => 5]); - + $this->_model->setCategoryIds([5]); $this->objectManager->get(\Magento\Framework\Registry::class)->register('current_category', $category); try { $this->assertEquals(5, $this->_model->getCategoryId()); @@ -83,6 +83,7 @@ public function testGetCategoryId() public function testGetCategory() { $this->assertEmpty($this->_model->getCategory()); + $this->_model->setCategoryIds([3]); $this->objectManager->get(\Magento\Framework\Registry::class) ->register('current_category', new \Magento\Framework\DataObject(['id' => 3])); From 138009172a1c245b28d62c38f35fb8e19afcd812 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Mon, 14 Jan 2019 17:14:06 +0200 Subject: [PATCH 0233/1295] MAGETWO-97331: Error when trying to create a shipping label --- dev/tests/integration/testsuite/Magento/Sales/_files/order.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order.php index 9f9f2f740c84e..65b143d9d68f7 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order.php @@ -43,7 +43,8 @@ ->setBasePrice($product->getPrice()) ->setPrice($product->getPrice()) ->setRowTotal($product->getPrice()) - ->setProductType('simple'); + ->setProductType('simple') + ->setName($product->getName()); /** @var Order $order */ $order = $objectManager->create(Order::class); From f989d67c762e6a345ef1371b9ceb2c80ffa79182 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Mon, 14 Jan 2019 17:37:44 +0200 Subject: [PATCH 0234/1295] MAGETWO-89079: Different order statuses with zero value and none zero statuses - Order States fix - Tests fix --- app/code/Magento/Sales/Model/Order.php | 21 +- .../ResourceModel/Order/Handler/State.php | 34 ++- .../Sales/Test/Mftf/Data/ConstData.xml | 20 ++ ...inAbleToShipPartiallyInvoicedItemsTest.xml | 6 + ...ditMemoTotalAfterShippingDiscountTest.xml} | 9 +- .../ResourceModel/Order/Handler/StateTest.php | 201 ++++++++---------- .../Service/V1/CreditMemoCreateRefundTest.php | 10 +- .../Sales/Service/V1/RefundOrderTest.php | 10 +- .../Block/Adminhtml/Order/AbstractForm.php | 21 +- ...AssertOrderCancelMassActionFailMessage.php | 10 +- .../Constraint/AssertOrderStatusIsCorrect.php | 4 +- .../Test/TestCase/MassOrdersUpdateTest.xml | 10 +- .../Magento/Paypal/Model/IpnTest.php | 4 +- .../order_with_shipping_and_invoice.php | 12 +- 14 files changed, 212 insertions(+), 160 deletions(-) create mode 100644 app/code/Magento/Sales/Test/Mftf/Data/ConstData.xml rename app/code/Magento/Sales/Test/Mftf/Test/{CreditMemoTotalAfterShippingDiscountTest.xml => AdminCreditMemoTotalAfterShippingDiscountTest.xml} (94%) diff --git a/app/code/Magento/Sales/Model/Order.php b/app/code/Magento/Sales/Model/Order.php index 72e90e290f1de..1972aa71080c8 100644 --- a/app/code/Magento/Sales/Model/Order.php +++ b/app/code/Magento/Sales/Model/Order.php @@ -12,6 +12,7 @@ use Magento\Framework\Locale\ResolverInterface; use Magento\Framework\Pricing\PriceCurrencyInterface; use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\Data\OrderItemInterface; use Magento\Sales\Api\Data\OrderStatusHistoryInterface; use Magento\Sales\Model\Order\Payment; use Magento\Sales\Model\Order\ProductOption; @@ -750,7 +751,7 @@ public function canComment() } /** - * Retrieve order shipment availability + * Retrieve order shipment availability. * * @return bool * @SuppressWarnings(PHPMD.CyclomaticComplexity) @@ -770,13 +771,29 @@ public function canShip() } foreach ($this->getAllItems() as $item) { - if ($item->getQtyToShip() > 0 && !$item->getIsVirtual() && !$item->getLockedDoShip()) { + if ($item->getQtyToShip() > 0 + && !$item->getIsVirtual() + && !$item->getLockedDoShip() + && !$this->isRefunded($item) + ) { return true; } } + return false; } + /** + * Check if item is refunded. + * + * @param OrderItemInterface $item + * @return bool + */ + private function isRefunded(OrderItemInterface $item): bool + { + return $item->getQtyRefunded() == $item->getQtyOrdered(); + } + /** * Retrieve order edit availability * 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 3b127abbda732..f18118447f95f 100644 --- a/app/code/Magento/Sales/Model/ResourceModel/Order/Handler/State.php +++ b/app/code/Magento/Sales/Model/ResourceModel/Order/Handler/State.php @@ -9,12 +9,12 @@ use Magento\Sales\Model\Order; /** - * Class State + * Class to check order State. */ class State { /** - * Check order status before save + * Check order status and adjust the status before save. * * @param Order $order * @return $this @@ -23,25 +23,23 @@ class State */ public function check(Order $order) { - if (!$order->isCanceled() && !$order->canUnhold() && !$order->canInvoice() && !$order->canShip()) { - if (0 == $order->getBaseGrandTotal() || $order->canCreditmemo()) { - if ($order->getState() !== Order::STATE_COMPLETE) { - $order->setState(Order::STATE_COMPLETE) - ->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_COMPLETE)); - } - } elseif ((float)$order->getTotalRefunded() - || !$order->getTotalRefunded() && $order->hasForcedCanCreditmemo() - ) { - if ($order->getState() !== Order::STATE_CLOSED) { - $order->setState(Order::STATE_CLOSED) - ->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_CLOSED)); - } - } - } - if ($order->getState() == Order::STATE_NEW && $order->getIsInProcess()) { + $currentState = $order->getState(); + if ($currentState == Order::STATE_NEW && $order->getIsInProcess()) { $order->setState(Order::STATE_PROCESSING) ->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_PROCESSING)); + $currentState = Order::STATE_PROCESSING; + } + + if (!$order->isCanceled() && !$order->canUnhold() && !$order->canInvoice()) { + if (in_array($currentState, [Order::STATE_PROCESSING, Order::STATE_COMPLETE]) && !$order->canCreditmemo()) { + $order->setState(Order::STATE_CLOSED) + ->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_CLOSED)); + } elseif ($currentState === Order::STATE_PROCESSING && !$order->canShip()) { + $order->setState(Order::STATE_COMPLETE) + ->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_COMPLETE)); + } } + return $this; } } diff --git a/app/code/Magento/Sales/Test/Mftf/Data/ConstData.xml b/app/code/Magento/Sales/Test/Mftf/Data/ConstData.xml new file mode 100644 index 0000000000000..f5c2fbb794031 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Data/ConstData.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="CONST" type="CONST"> + <data key="Complete">Complete</data> + <data key="Closed">Closed</data> + <data key="Pending">Pending</data> + <data key="Processing">Processing</data> + </entity> +</entities> + + + diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml index a74fd8ad08eb8..b61d7d69f8742 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml @@ -59,6 +59,7 @@ <grabFromCurrentUrl regex="~/order_id/(\d+)/~" stepKey="grabOrderId"/> <seeInCurrentUrl url="{{AdminOrderDetailsPage.url('$grabOrderId')}}" after="grabOrderId" stepKey="seeViewOrderPage"/> <see selector="{{AdminMessagesSection.success}}" userInput="You created the order." after="seeViewOrderPage" stepKey="seeSuccessMessage"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{CONST.Pending}}" stepKey="seeOrderPending"/> <grabTextFrom selector="|Order # (\d+)|" after="seeSuccessMessage" stepKey="getOrderId"/> <scrollTo selector="{{AdminOrderItemsOrderedSection.qtyColumn}}" after="getOrderId" stepKey="scrollToItemsOrdered"/> <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Ordered 10" after="scrollToItemsOrdered" stepKey="seeQtyOfItemsOrdered"/> @@ -79,6 +80,7 @@ <waitForPageLoad stepKey="waitForInvoiceToSubmit1"/> <!--Invoice created successfully--> <see selector="{{AdminMessagesSection.success}}" userInput="The invoice has been created." stepKey="seeInvoiceSuccessMessage"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{CONST.Processing}}" stepKey="seeOrderProcessing1"/> <scrollTo selector="{{AdminOrderItemsOrderedSection.itemStatus('1')}}" stepKey="scrollToOrderItems"/> <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Invoiced 5" stepKey="see5itemsInvoiced"/> <scrollTo selector="{{AdminHeaderSection.pageTitle}}" stepKey="scrollToTopOfPage"/> @@ -98,6 +100,7 @@ <waitForLoadingMaskToDisappear stepKey="waitForShipmentToSubmit"/> <!--Verify shipment created successfully--> <see selector="{{AdminMessagesSection.success}}" userInput="The shipment has been created." after="submitShipment" stepKey="successfullShipmentCreation"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{CONST.Processing}}" stepKey="seeOrderProcessing2"/> <see selector="{{AdminHeaderSection.pageTitle}}" userInput="$getOrderId" stepKey="seeOrderIdInPageTitleAfterShip"/> <scrollTo selector="{{AdminOrderItemsOrderedSection.itemStatus('1')}}" stepKey="scrollToOrderItems1"/> <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Shipped 5" stepKey="see5itemsShipped"/> @@ -117,6 +120,7 @@ <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="submitRefundOffline"/> <!--Verify Credit Memo created successfully--> <see selector="{{AdminMessagesSection.success}}" userInput="You created the credit memo." after="submitRefundOffline" stepKey="seeCreditMemoSuccessMsg"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{CONST.Processing}}" stepKey="seeOrderProcessing3"/> <scrollTo selector="{{AdminOrderItemsOrderedSection.itemStatus('1')}}" stepKey="scrollToOrderItems2"/> <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Refunded 5" stepKey="see5itemsRefunded"/> <scrollTo selector="{{AdminHeaderSection.pageTitle}}" stepKey="scrollToTopOfPage2"/> @@ -138,6 +142,7 @@ <waitForPageLoad stepKey="waitForInvoiceToSubmit2"/> <!--Invoice created successfully for the rest of the ordered items--> <see selector="{{AdminMessagesSection.success}}" userInput="The invoice has been created." stepKey="seeInvoiceSuccessMessage2"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{CONST.Processing}}" stepKey="seeOrderProcessing4"/> <scrollTo selector="{{AdminOrderItemsOrderedSection.itemStatus('1')}}" stepKey="scrollToOrderItems3"/> <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Invoiced 10" stepKey="see10itemsInvoiced"/> <scrollTo selector="{{AdminHeaderSection.pageTitle}}" stepKey="scrollToTopOfPage3"/> @@ -156,6 +161,7 @@ <!--Submit Shipment--> <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" after="fillRestOfItemsToShip" stepKey="submitShipment2" /> <see selector="{{AdminMessagesSection.success}}" userInput="The shipment has been created." after="submitShipment2" stepKey="successfullyCreatedShipment"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{CONST.Complete}}" stepKey="seeOrderComplete"/> <!--Verify Items Status and Shipped Qty in the Items Ordered section--> <scrollTo selector="{{AdminOrderItemsOrderedSection.itemStatus('1')}}" stepKey="scrollToItemsOrdered2"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreditMemoTotalAfterShippingDiscountTest.xml similarity index 94% rename from app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml rename to app/code/Magento/Sales/Test/Mftf/Test/AdminCreditMemoTotalAfterShippingDiscountTest.xml index 260ed8226f321..ffcd658f75087 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreditMemoTotalAfterShippingDiscountTest.xml @@ -7,8 +7,8 @@ --> <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="CreditMemoTotalAfterShippingDiscountTest"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreditMemoTotalAfterShippingDiscountTest"> <annotations> <features value="Credit memo"/> <title value="Verify credit memo grand total after shipping discount is applied via Cart Price Rule"/> @@ -97,7 +97,9 @@ <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask4"/> <!-- Create invoice --> - <click selector="{{OrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> + <click selector="{{AdminDataGridTableSection.firstRow}}" stepKey="clickOrderRow"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{CONST.Pending}}" stepKey="seeOrderPending"/> <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoiceButton"/> <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Invoice" stepKey="seeNewInvoiceInPageTitle" after="clickInvoiceButton"/> @@ -115,6 +117,7 @@ <grabTextFrom selector="{{AdminInvoiceTotalSection.grandTotal}}" stepKey="grabInvoiceGrandTotal" after="seeCorrectGrandTotal"/> <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> <see selector="{{OrderDetailsMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="seeSuccessMessage1"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{CONST.Processing}}" stepKey="seeOrderProcessing"/> <!--Create Credit Memo--> <comment userInput="Admin creates credit memo" stepKey="createCreditMemoComment"/> diff --git a/app/code/Magento/Sales/Test/Unit/Model/ResourceModel/Order/Handler/StateTest.php b/app/code/Magento/Sales/Test/Unit/Model/ResourceModel/Order/Handler/StateTest.php index e120d613e323c..48f4a282a2be2 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/ResourceModel/Order/Handler/StateTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/ResourceModel/Order/Handler/StateTest.php @@ -8,7 +8,7 @@ use Magento\Sales\Model\Order; /** - * Class StateTest + * Tests for State. */ class StateTest extends \PHPUnit\Framework\TestCase { @@ -22,9 +22,14 @@ class StateTest extends \PHPUnit\Framework\TestCase */ protected $orderMock; + /** + * @inheritdoc + */ protected function setUp() { - $this->orderMock = $this->createPartialMock(\Magento\Sales\Model\Order::class, [ + $this->orderMock = $this->createPartialMock( + \Magento\Sales\Model\Order::class, + [ '__wakeup', 'getId', 'hasCustomerNoteNotify', @@ -35,13 +40,12 @@ protected function setUp() 'canShip', 'getBaseGrandTotal', 'canCreditmemo', - 'getState', - 'setState', 'getTotalRefunded', 'hasForcedCanCreditmemo', 'getIsInProcess', 'getConfig', - ]); + ] + ); $this->orderMock->expects($this->any()) ->method('getConfig') ->willReturnSelf(); @@ -53,127 +57,96 @@ protected function setUp() } /** - * test check order - order without id + * Test for check method with different states. + * + * @param bool $isCanceled + * @param bool $canUnhold + * @param bool $canInvoice + * @param bool $canShip + * @param int $callCanSkipNum + * @param bool $canCreditmemo + * @param int $callCanCreditmemoNum + * @param string $currentState + * @param string $expectedState + * @param int $callSetStateNum + * @return void + * @dataProvider stateCheckDataProvider + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ - public function testCheckOrderEmpty() - { - $this->orderMock->expects($this->once()) - ->method('getBaseGrandTotal') - ->willReturn(100); - $this->orderMock->expects($this->never()) - ->method('setState'); - - $this->state->check($this->orderMock); - } - - /** - * test check order - set state complete - */ - public function testCheckSetStateComplete() - { + public function testCheck( + bool $canCreditmemo, + int $callCanCreditmemoNum, + bool $canShip, + int $callCanSkipNum, + string $currentState, + string $expectedState = '', + bool $isInProcess = false, + int $callGetIsInProcessNum = 0, + bool $isCanceled = false, + bool $canUnhold = false, + bool $canInvoice = false + ) { + $this->orderMock->setState($currentState); $this->orderMock->expects($this->any()) - ->method('getId') - ->will($this->returnValue(1)); - $this->orderMock->expects($this->once()) ->method('isCanceled') - ->will($this->returnValue(false)); - $this->orderMock->expects($this->once()) - ->method('canUnhold') - ->will($this->returnValue(false)); - $this->orderMock->expects($this->once()) - ->method('canInvoice') - ->will($this->returnValue(false)); - $this->orderMock->expects($this->once()) - ->method('canShip') - ->will($this->returnValue(false)); - $this->orderMock->expects($this->once()) - ->method('getBaseGrandTotal') - ->will($this->returnValue(100)); - $this->orderMock->expects($this->once()) - ->method('canCreditmemo') - ->will($this->returnValue(true)); - $this->orderMock->expects($this->exactly(2)) - ->method('getState') - ->will($this->returnValue(Order::STATE_PROCESSING)); - $this->orderMock->expects($this->once()) - ->method('setState') - ->with(Order::STATE_COMPLETE) - ->will($this->returnSelf()); - $this->assertEquals($this->state, $this->state->check($this->orderMock)); - } - - /** - * test check order - set state closed - */ - public function testCheckSetStateClosed() - { + ->willReturn($isCanceled); $this->orderMock->expects($this->any()) - ->method('getId') - ->will($this->returnValue(1)); - $this->orderMock->expects($this->once()) - ->method('isCanceled') - ->will($this->returnValue(false)); - $this->orderMock->expects($this->once()) ->method('canUnhold') - ->will($this->returnValue(false)); - $this->orderMock->expects($this->once()) + ->willReturn($canUnhold); + $this->orderMock->expects($this->any()) ->method('canInvoice') - ->will($this->returnValue(false)); - $this->orderMock->expects($this->once()) + ->willReturn($canInvoice); + $this->orderMock->expects($this->exactly($callCanSkipNum)) ->method('canShip') - ->will($this->returnValue(false)); - $this->orderMock->expects($this->once()) - ->method('getBaseGrandTotal') - ->will($this->returnValue(100)); - $this->orderMock->expects($this->once()) + ->willReturn($canShip); + $this->orderMock->expects($this->exactly($callCanCreditmemoNum)) ->method('canCreditmemo') - ->will($this->returnValue(false)); - $this->orderMock->expects($this->exactly(2)) - ->method('getTotalRefunded') - ->will($this->returnValue(null)); - $this->orderMock->expects($this->once()) - ->method('hasForcedCanCreditmemo') - ->will($this->returnValue(true)); - $this->orderMock->expects($this->exactly(2)) - ->method('getState') - ->will($this->returnValue(Order::STATE_PROCESSING)); - $this->orderMock->expects($this->once()) - ->method('setState') - ->with(Order::STATE_CLOSED) - ->will($this->returnSelf()); - $this->assertEquals($this->state, $this->state->check($this->orderMock)); + ->willReturn($canCreditmemo); + $this->orderMock->expects($this->exactly($callGetIsInProcessNum)) + ->method('getIsInProcess') + ->willReturn($isInProcess); + $this->state->check($this->orderMock); + $this->assertEquals($expectedState, $this->orderMock->getState()); } /** - * test check order - set state processing + * Data provider for testCheck method. + * + * @return array */ - public function testCheckSetStateProcessing() + public function stateCheckDataProvider(): array { - $this->orderMock->expects($this->any()) - ->method('getId') - ->will($this->returnValue(1)); - $this->orderMock->expects($this->once()) - ->method('isCanceled') - ->will($this->returnValue(false)); - $this->orderMock->expects($this->once()) - ->method('canUnhold') - ->will($this->returnValue(false)); - $this->orderMock->expects($this->once()) - ->method('canInvoice') - ->will($this->returnValue(false)); - $this->orderMock->expects($this->once()) - ->method('canShip') - ->will($this->returnValue(true)); - $this->orderMock->expects($this->once()) - ->method('getState') - ->will($this->returnValue(Order::STATE_NEW)); - $this->orderMock->expects($this->once()) - ->method('getIsInProcess') - ->will($this->returnValue(true)); - $this->orderMock->expects($this->once()) - ->method('setState') - ->with(Order::STATE_PROCESSING) - ->will($this->returnSelf()); - $this->assertEquals($this->state, $this->state->check($this->orderMock)); + return [ + 'processing - !canCreditmemo!canShip -> closed' => + [false, 1, false, 0, Order::STATE_PROCESSING, Order::STATE_CLOSED], + 'complete - !canCreditmemo,!canShip -> closed' => + [false, 1, false, 0, Order::STATE_COMPLETE, Order::STATE_CLOSED], + 'processing - !canCreditmemo,canShip -> closed' => + [false, 1, true, 0, Order::STATE_PROCESSING, Order::STATE_CLOSED], + 'complete - !canCreditmemo,canShip -> closed' => + [false, 1, true, 0, Order::STATE_COMPLETE, Order::STATE_CLOSED], + 'processing - canCreditmemo,!canShip -> complete' => + [true, 1, false, 1, Order::STATE_PROCESSING, Order::STATE_COMPLETE], + 'complete - canCreditmemo,!canShip -> complete' => + [true, 1, false, 0, Order::STATE_COMPLETE, Order::STATE_COMPLETE], + 'processing - canCreditmemo, canShip -> processing' => + [true, 1, true, 1, Order::STATE_PROCESSING, Order::STATE_PROCESSING], + 'complete - canCreditmemo, canShip -> complete' => + [true, 1, true, 0, Order::STATE_COMPLETE, Order::STATE_COMPLETE], + 'new - canCreditmemo, canShip, IsInProcess -> processing' => + [true, 1, true, 1, Order::STATE_NEW, Order::STATE_PROCESSING, true, 1], + 'new - canCreditmemo, !canShip, IsInProcess -> processing' => + [true, 1, false, 1, Order::STATE_NEW, Order::STATE_COMPLETE, true, 1], + 'new - canCreditmemo, canShip, !IsInProcess -> new' => + [true, 0, true, 0, Order::STATE_NEW, Order::STATE_NEW, false, 1], + 'hold - canUnhold -> hold' => + [true, 0, true, 0, Order::STATE_HOLDED, Order::STATE_HOLDED, false, 0, false, true], + 'payment_review - canUnhold -> payment_review' => + [true, 0, true, 0, Order::STATE_PAYMENT_REVIEW, Order::STATE_PAYMENT_REVIEW, false, 0, false, true], + 'pending_payment - canUnhold -> pending_payment' => + [true, 0, true, 0, Order::STATE_PENDING_PAYMENT, Order::STATE_PENDING_PAYMENT, false, 0, false, true], + 'cancelled - isCanceled -> cancelled' => + [true, 0, true, 0, Order::STATE_HOLDED, Order::STATE_HOLDED, false, 0, true], + ]; } } diff --git a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/CreditMemoCreateRefundTest.php b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/CreditMemoCreateRefundTest.php index eae0e600434a6..2b9c539f64e66 100644 --- a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/CreditMemoCreateRefundTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/CreditMemoCreateRefundTest.php @@ -10,7 +10,7 @@ use Magento\TestFramework\TestCase\WebapiAbstract; /** - * Class CreditMemoCreateRefundTest + * API tests for CreditMemoCreateRefund. */ class CreditMemoCreateRefundTest extends WebapiAbstract { @@ -25,12 +25,17 @@ class CreditMemoCreateRefundTest extends WebapiAbstract */ protected $objectManager; + /** + * @inheritdoc + */ protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); } /** + * Test for Invoke method. + * * @magentoApiDataFixture Magento/Sales/_files/invoice.php */ public function testInvoke() @@ -115,8 +120,7 @@ public function testInvoke() ); $this->assertNotEmpty($result); $order = $this->objectManager->get(OrderRepositoryInterface::class)->get($order->getId()); - //Totally refunded orders still can be processed and shipped. - $this->assertEquals(Order::STATE_PROCESSING, $order->getState()); + $this->assertEquals(Order::STATE_CLOSED, $order->getState()); } private function getItemsForRest($order) diff --git a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/RefundOrderTest.php b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/RefundOrderTest.php index aacda763ca2aa..69bbecc1317a7 100644 --- a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/RefundOrderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/RefundOrderTest.php @@ -5,8 +5,10 @@ */ namespace Magento\Sales\Service\V1; +use Magento\Sales\Model\Order; + /** - * API test for creation of Creditmemo for certain Order. + * API tests for creation of Creditmemo for certain Order. */ class RefundOrderTest extends \Magento\TestFramework\TestCase\WebapiAbstract { @@ -23,6 +25,9 @@ class RefundOrderTest extends \Magento\TestFramework\TestCase\WebapiAbstract */ private $creditmemoRepository; + /** + * @inheritdoc + */ protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); @@ -86,9 +91,8 @@ public function testShortRequest() 'Failed asserting that proper shipping amount of the Order was refunded' ); - //Totally refunded orders can be processed. $this->assertEquals( - $existingOrder->getStatus(), + Order::STATE_COMPLETE, $updatedOrder->getStatus(), 'Failed asserting that order status has not changed' ); diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/AbstractForm.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/AbstractForm.php index 5572c06816a39..aea48e891a976 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/AbstractForm.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/AbstractForm.php @@ -7,6 +7,7 @@ namespace Magento\Sales\Test\Block\Adminhtml\Order; use Magento\Mtf\Block\Form; +use Magento\Mtf\Client\Locator; /** * Abstract Form block. @@ -42,6 +43,24 @@ function () { ); } + /** + * Wait for element is enabled. + * + * @param string $selector + * @param string $strategy + * @return bool|null + */ + protected function waitForElementEnabled(string $selector, string $strategy = Locator::SELECTOR_CSS) + { + $browser = $this->browser; + return $browser->waitUntil( + function () use ($browser, $selector, $strategy) { + $element = $browser->find($selector, $strategy); + return !$element->isDisabled() ? true : null; + } + ); + } + /** * Fill form data. * @@ -113,7 +132,7 @@ abstract protected function getItemsBlock(); */ public function submit() { - $this->waitLoader(); + $this->waitForElementEnabled($this->send); $this->_rootElement->find($this->send)->click(); } diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertOrderCancelMassActionFailMessage.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertOrderCancelMassActionFailMessage.php index 6d3b99ce81a04..9f4181b70e801 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertOrderCancelMassActionFailMessage.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertOrderCancelMassActionFailMessage.php @@ -11,17 +11,17 @@ /** * Class AssertOrderCancelMassActionFailMessage - * Assert cancel fail message is displayed on order index page + * Assert cancel fail message is displayed on order index page. */ class AssertOrderCancelMassActionFailMessage extends AbstractConstraint { /** - * Text value to be checked + * Text value to be checked. */ - const FAIL_CANCEL_MESSAGE = '1 order(s) cannot be canceled.'; + const FAIL_CANCEL_MESSAGE = 'You cannot cancel the order(s).'; /** - * Assert cancel fail message is displayed on order index page + * Assert cancel fail message is displayed on order index page. * * @param OrderIndex $orderIndex * @return void @@ -35,7 +35,7 @@ public function processAssert(OrderIndex $orderIndex) } /** - * Returns a string representation of the object + * Returns a string representation of the object. * * @return string */ diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertOrderStatusIsCorrect.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertOrderStatusIsCorrect.php index 46f6ebba51fe1..7a46d0c5ef820 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertOrderStatusIsCorrect.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertOrderStatusIsCorrect.php @@ -39,8 +39,8 @@ public function processAssert( /** @var \Magento\Sales\Test\Block\Adminhtml\Order\View\Tab\Info $infoTab */ $infoTab = $salesOrderView->getOrderForm()->openTab('info')->getTab('info'); \PHPUnit_Framework_Assert::assertEquals( - $infoTab->getOrderStatus(), - $orderStatus + $orderStatus, + $infoTab->getOrderStatus() ); } diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MassOrdersUpdateTest.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MassOrdersUpdateTest.xml index eddc321e9ca52..1f75b07c8ca1e 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MassOrdersUpdateTest.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MassOrdersUpdateTest.xml @@ -8,7 +8,6 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Sales\Test\TestCase\MassOrdersUpdateTest" summary="Mass Update Orders" ticketId="MAGETWO-27897"> <variation name="MassOrdersUpdateTestVariation1"> - <data name="tag" xsi:type="string">stable:no</data> <data name="description" xsi:type="string">cancel orders in status Pending and Processing</data> <data name="steps" xsi:type="string">-</data> <data name="action" xsi:type="string">Cancel</data> @@ -18,20 +17,20 @@ <constraint name="Magento\Sales\Test\Constraint\AssertOrdersInOrdersGrid" /> </variation> <variation name="MassOrdersUpdateTestVariation2"> - <data name="description" xsi:type="string">try to cancel orders in status Complete, Canceled</data> + <data name="description" xsi:type="string">try to cancel orders in status Complete, Closed</data> <data name="steps" xsi:type="string">invoice, shipment|invoice, credit memo</data> <data name="action" xsi:type="string">Cancel</data> <data name="ordersCount" xsi:type="string">2</data> - <data name="resultStatuses" xsi:type="string">Complete,Canceled</data> + <data name="resultStatuses" xsi:type="string">Complete,Closed</data> <constraint name="Magento\Sales\Test\Constraint\AssertOrderCancelMassActionFailMessage" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrdersInOrdersGrid" /> </variation> <variation name="MassOrdersUpdateTestVariation3"> - <data name="description" xsi:type="string">try to cancel orders in status Pending, Closed</data> + <data name="description" xsi:type="string">try to cancel orders in status Processing, Closed</data> <data name="steps" xsi:type="string">invoice|invoice, credit memo</data> <data name="action" xsi:type="string">Cancel</data> <data name="ordersCount" xsi:type="string">2</data> - <data name="resultStatuses" xsi:type="string">Processing,Canceled</data> + <data name="resultStatuses" xsi:type="string">Processing,Closed</data> <constraint name="Magento\Sales\Test\Constraint\AssertOrderCancelMassActionFailMessage" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrdersInOrdersGrid" /> </variation> @@ -45,7 +44,6 @@ <constraint name="Magento\Sales\Test\Constraint\AssertOrdersInOrdersGrid" /> </variation> <variation name="MassOrdersUpdateTestVariation5"> - <data name="tag" xsi:type="string">stable:no</data> <data name="description" xsi:type="string">Try to put order in status Complete on Hold</data> <data name="steps" xsi:type="string">invoice, shipment</data> <data name="action" xsi:type="string">Hold</data> diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/IpnTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Model/IpnTest.php index 6b92338e9932a..3c126bcdc2c5b 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/Model/IpnTest.php +++ b/dev/tests/integration/testsuite/Magento/Paypal/Model/IpnTest.php @@ -64,7 +64,7 @@ public function testProcessIpnRequestFullRefund() $creditmemoItems = $order->getCreditmemosCollection()->getItems(); $creditmemo = current($creditmemoItems); - $this->assertEquals(Order::STATE_PROCESSING, $order->getState()); + $this->assertEquals(Order::STATE_CLOSED, $order->getState()); $this->assertEquals(1, count($creditmemoItems)); $this->assertEquals(Creditmemo::STATE_REFUNDED, $creditmemo->getState()); $this->assertEquals(10, $order->getSubtotalRefunded()); @@ -146,7 +146,7 @@ public function testProcessIpnRequestRestRefund() $creditmemoItems = $order->getCreditmemosCollection()->getItems(); - $this->assertEquals(Order::STATE_PROCESSING, $order->getState()); + $this->assertEquals(Order::STATE_CLOSED, $order->getState()); $this->assertEquals(1, count($creditmemoItems)); $this->assertEquals(10, $order->getSubtotalRefunded()); $this->assertEquals(10, $order->getBaseSubtotalRefunded()); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_shipping_and_invoice.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_shipping_and_invoice.php index a889235ea1862..61d8be98bdd22 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_shipping_and_invoice.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_shipping_and_invoice.php @@ -3,6 +3,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +use Magento\Sales\Model\Order\ShipmentFactory; + require 'order.php'; $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); @@ -36,4 +39,11 @@ $order->setIsInProcess(true); -$transaction->addObject($invoice)->addObject($order)->save(); +$items = []; +foreach ($order->getItems() as $orderItem) { + $items[$orderItem->getId()] = $orderItem->getQtyOrdered(); +} +$shipment = $objectManager->get(ShipmentFactory::class)->create($order, $items); +$shipment->register(); + +$transaction->addObject($invoice)->addObject($shipment)->addObject($order)->save(); From 10a3ed2c609a0f222d4070809cf12e12aed7d381 Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Mon, 14 Jan 2019 20:07:40 +0200 Subject: [PATCH 0235/1295] MAGETWO-96659: Zip code is not validated for address entered in My Account and for new address entered during checkout --- .../web/js/model/shipping-rates-validator.js | 19 ++-- .../PostCodesPatternsAttributeData.php | 42 ++++++++ .../frontend/layout/customer_address_form.xml | 1 + .../frontend/templates/address/edit.phtml | 7 +- .../view/frontend/web/js/addressValidation.js | 100 +++++++++++++++++- ...ultishipping_checkout_customer_address.xml | 1 + 6 files changed, 160 insertions(+), 10 deletions(-) create mode 100644 app/code/Magento/Customer/Block/DataProviders/PostCodesPatternsAttributeData.php diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js index fde88ebadb393..8b07c02e4d380 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js @@ -42,6 +42,7 @@ define([ return { validateAddressTimeout: 0, + validateZipCodeTimeout: 0, validateDelay: 2000, /** @@ -133,16 +134,20 @@ define([ }); } else { element.on('value', function () { + clearTimeout(self.validateZipCodeTimeout); + self.validateZipCodeTimeout = setTimeout(function () { + if (element.index === postcodeElementName) { + self.postcodeValidation(element); + } else { + $.each(postcodeElements, function (index, elem) { + self.postcodeValidation(elem); + }); + } + }, delay); + if (!formPopUpState.isVisible()) { clearTimeout(self.validateAddressTimeout); self.validateAddressTimeout = setTimeout(function () { - if (element.index === postcodeElementName) { - self.postcodeValidation(element); - } else { - $.each(postcodeElements, function (index, elem) { - self.postcodeValidation(elem); - }); - } self.validateFields(); }, delay); } diff --git a/app/code/Magento/Customer/Block/DataProviders/PostCodesPatternsAttributeData.php b/app/code/Magento/Customer/Block/DataProviders/PostCodesPatternsAttributeData.php new file mode 100644 index 0000000000000..220dc5d9f1aaa --- /dev/null +++ b/app/code/Magento/Customer/Block/DataProviders/PostCodesPatternsAttributeData.php @@ -0,0 +1,42 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Block\DataProviders; + +use Magento\Framework\View\Element\Block\ArgumentInterface; +use Magento\Directory\Model\Country\Postcode\Config as PostCodeConfig; + +/** + * Provides postcodes patterns into template. + */ +class PostCodesPatternsAttributeData implements ArgumentInterface +{ + /** + * @var PostCodeConfig + */ + private $postCodeConfig; + + /** + * Constructor + * + * @param PostCodeConfig $postCodeConfig + */ + public function __construct(PostCodeConfig $postCodeConfig) + { + $this->postCodeConfig = $postCodeConfig; + } + + /** + * Get post codes in json format + * + * @return string + */ + public function getPostCodesJson(): string + { + return json_encode($this->postCodeConfig->getPostCodes()); + } +} diff --git a/app/code/Magento/Customer/view/frontend/layout/customer_address_form.xml b/app/code/Magento/Customer/view/frontend/layout/customer_address_form.xml index f053805409fe5..f5ee2b347a5b2 100644 --- a/app/code/Magento/Customer/view/frontend/layout/customer_address_form.xml +++ b/app/code/Magento/Customer/view/frontend/layout/customer_address_form.xml @@ -20,6 +20,7 @@ <block class="Magento\Customer\Block\Address\Edit" name="customer_address_edit" template="Magento_Customer::address/edit.phtml" cacheable="false"> <arguments> <argument name="attribute_data" xsi:type="object">Magento\Customer\Block\DataProviders\AddressAttributeData</argument> + <argument name="post_code_config" xsi:type="object">Magento\Customer\Block\DataProviders\PostCodesPatternsAttributeData</argument> </arguments> </block> </referenceContainer> diff --git a/app/code/Magento/Customer/view/frontend/templates/address/edit.phtml b/app/code/Magento/Customer/view/frontend/templates/address/edit.phtml index 644238e3949c5..57f67e45f9a13 100644 --- a/app/code/Magento/Customer/view/frontend/templates/address/edit.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/address/edit.phtml @@ -126,6 +126,9 @@ title="<?= /* @noEscape */ $block->getAttributeData()->getFrontendLabel('postcode') ?>" id="zip" class="input-text validate-zip-international <?= $block->escapeHtmlAttr($this->helper(\Magento\Customer\Helper\Address::class)->getAttributeValidationClass('postcode')) ?>"> + <div role="alert" class="message warning" style="display:none"> + <span></span> + </div> </div> </div> <div class="field country required"> @@ -184,7 +187,9 @@ <script type="text/x-magento-init"> { "#form-validate": { - "addressValidation": {} + "addressValidation": { + "postCodes": <?= /* @noEscape */ $block->getPostCodeConfig()->getPostCodesJson(); ?> + } }, "#country": { "regionUpdater": { diff --git a/app/code/Magento/Customer/view/frontend/web/js/addressValidation.js b/app/code/Magento/Customer/view/frontend/web/js/addressValidation.js index be2960701deed..a7314a35127b5 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/addressValidation.js +++ b/app/code/Magento/Customer/view/frontend/web/js/addressValidation.js @@ -5,18 +5,27 @@ define([ 'jquery', + 'underscore', + 'mageUtils', + 'mage/translate', 'jquery/ui', 'validation' -], function ($) { +], function ($, __, utils, $t) { 'use strict'; $.widget('mage.addressValidation', { options: { selectors: { - button: '[data-action=save-address]' + button: '[data-action=save-address]', + zip: '#zip', + country: 'select[name="country_id"]:visible' } }, + validatedPostCodeExample: [], + zipInput: null, + countrySelect: null, + /** * Validation creation * @protected @@ -24,6 +33,9 @@ define([ _create: function () { var button = $(this.options.selectors.button, this.element); + this.zipInput = $(this.options.selectors.zip, this.element); + this.countrySelect = $(this.options.selectors.country, this.element); + this.element.validation({ /** @@ -36,6 +48,90 @@ define([ form.submit(); } }); + + this._addPostCodeValidation(); + }, + + /** + * Add postcode validation + */ + _addPostCodeValidation: function () { + var self = this; + + this.zipInput.on('keyup', __.debounce(function (event) { + var valid = self._validatePostCode(event.target.value); + + self._renderValidationResult(valid); + }, 500) + ); + + this.countrySelect.on('change', function () { + var valid = self._validatePostCode(self.zipInput.val()); + + self._renderValidationResult(valid); + }); + }, + + /** + * Validate post code value. + * + * @param {String} postCode - post code + * @return {Boolean} Whether is post code valid + */ + _validatePostCode: function (postCode) { + var countryId = this.countrySelect.val(), + patterns = this.options.postCodes[countryId], + pattern, regex; + + if (postCode === null) { + return true; + } + + this.validatedPostCodeExample = []; + + if (!utils.isEmpty(postCode) && !utils.isEmpty(patterns)) { + for (pattern in patterns) { + if (patterns.hasOwnProperty(pattern)) { //eslint-disable-line max-depth + this.validatedPostCodeExample.push(patterns[pattern].example); + regex = new RegExp(patterns[pattern].pattern); + + if (regex.test(postCode)) { //eslint-disable-line max-depth + return true; + } + } + } + + return false; + } + + return true; + }, + + /** + * Renders warning messages for invalid post code. + * + * @param {Boolean} valid + */ + _renderValidationResult: function (valid) { + var warnMessage, + alertDiv = this.zipInput.next(); + + if (!valid) { + warnMessage = $t('Provided Zip/Postal Code seems to be invalid.'); + + if (this.validatedPostCodeExample.length) { + warnMessage += $t(' Example: ') + this.validatedPostCodeExample.join('; ') + '. '; + } + warnMessage += $t('If you believe it is the right one you can ignore this notice.'); + } + + alertDiv.children(':first').text(warnMessage); + + if (valid) { + alertDiv.hide(); + } else { + alertDiv.show(); + } } }); diff --git a/app/code/Magento/Multishipping/view/frontend/layout/multishipping_checkout_customer_address.xml b/app/code/Magento/Multishipping/view/frontend/layout/multishipping_checkout_customer_address.xml index c6bcdeb7b0413..fee3cb790a522 100644 --- a/app/code/Magento/Multishipping/view/frontend/layout/multishipping_checkout_customer_address.xml +++ b/app/code/Magento/Multishipping/view/frontend/layout/multishipping_checkout_customer_address.xml @@ -12,6 +12,7 @@ <block class="Magento\Customer\Block\Address\Edit" name="customer_address_edit" template="Magento_Customer::address/edit.phtml" cacheable="false"> <arguments> <argument name="attribute_data" xsi:type="object">Magento\Customer\Block\DataProviders\AddressAttributeData</argument> + <argument name="post_code_config" xsi:type="object">Magento\Customer\Block\DataProviders\PostCodesPatternsAttributeData</argument> </arguments> </block> </referenceContainer> From 1d66ad76f475fd778c967ea41d1d59d892792698 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Mon, 14 Jan 2019 21:41:30 +0200 Subject: [PATCH 0236/1295] MAGETWO-89039: Allow Validation of customer address attributes to include spaces --- .../Test/Unit/Block/Checkout/AttributeMergerTest.php | 7 +++++-- .../Unit/Model/Metadata/Form/AbstractDataTest.php | 4 ++-- .../Component/Listing/Column/ValidationRulesTest.php | 4 ++-- .../Eav/Test/Unit/Model/Attribute/Data/TextTest.php | 11 +++++++---- .../Entity/Attribute/Frontend/DefaultFrontendTest.php | 4 ++-- 5 files changed, 18 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Unit/Block/Checkout/AttributeMergerTest.php b/app/code/Magento/Checkout/Test/Unit/Block/Checkout/AttributeMergerTest.php index 5e87b1dab46c7..bff2243f30d03 100644 --- a/app/code/Magento/Checkout/Test/Unit/Block/Checkout/AttributeMergerTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Block/Checkout/AttributeMergerTest.php @@ -40,6 +40,9 @@ class AttributeMergerTest extends \PHPUnit\Framework\TestCase */ private $attributeMerger; + /** + * @inheritdoc + */ protected function setUp() { @@ -87,7 +90,7 @@ public function testMerge($validationRule, $expectedValidation) 'field' => [ 'validation' => ['length' => true], - ] + ], ] ); @@ -104,7 +107,7 @@ public function testMerge($validationRule, $expectedValidation) * * @return array */ - public function validationRulesDataProvider() + public function validationRulesDataProvider(): array { return [ ['alpha', 'validate-alpha'], diff --git a/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/AbstractDataTest.php b/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/AbstractDataTest.php index 1c6de64a46064..667fc87b6a82b 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/AbstractDataTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/AbstractDataTest.php @@ -256,8 +256,8 @@ public function validateInputRuleDataProvider() 'mylabel', 'alphanumeric', [ - \Zend_Validate_Alnum::NOT_ALNUM => '"mylabel" contains non-alphabetic or non-numeric characters.' - ] + \Zend_Validate_Alnum::NOT_ALNUM => '"mylabel" contains non-alphabetic or non-numeric characters.', + ], ], ['abcqaz', 'mylabel', 'alphanumeric', true], ['abc qaz', 'mylabel', 'alphanum-with-spaces', true], diff --git a/app/code/Magento/Customer/Test/Unit/Ui/Component/Listing/Column/ValidationRulesTest.php b/app/code/Magento/Customer/Test/Unit/Ui/Component/Listing/Column/ValidationRulesTest.php index 3f89dd6d2be01..b57bc53ef09f9 100644 --- a/app/code/Magento/Customer/Test/Unit/Ui/Component/Listing/Column/ValidationRulesTest.php +++ b/app/code/Magento/Customer/Test/Unit/Ui/Component/Listing/Column/ValidationRulesTest.php @@ -33,7 +33,7 @@ protected function setUp() * @return void * @dataProvider validationRulesDataProvider */ - public function testGetValidationRules($validationRule, $validationClass) + public function testGetValidationRules(string $validationRule, string $validationClass) { $expectsRules = [ 'required-entry' => true, @@ -73,7 +73,7 @@ public function testGetValidationRulesWithOnlyRequiredRule() * * @return array */ - public function validationRulesDataProvider() + public function validationRulesDataProvider(): array { return [ ['alpha', 'validate-alpha'], diff --git a/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/TextTest.php b/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/TextTest.php index 49b55d8ce8151..8b16d286471b4 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/TextTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Attribute/Data/TextTest.php @@ -42,6 +42,7 @@ protected function tearDown() /** * Test of string validation. + * * @return void */ public function testValidateValueString() @@ -53,6 +54,7 @@ public function testValidateValueString() /** * Test of integer validation. + * * @return void */ public function testValidateValueInteger() @@ -65,6 +67,7 @@ public function testValidateValueInteger() /** * Test without length validation. + * * @return void */ public function testWithoutLengthValidation() @@ -94,7 +97,7 @@ public function testWithoutLengthValidation() * @return void * @dataProvider alphanumDataProvider */ - public function testAlphanumericValidation($value, $expectedResult) + public function testAlphanumericValidation(string $value, $expectedResult) { $defaultAttributeData = [ 'store_label' => 'Test', @@ -116,7 +119,7 @@ public function testAlphanumericValidation($value, $expectedResult) * * @return array */ - public function alphanumDataProvider() + public function alphanumDataProvider(): array { return [ ['QazWsx', true], @@ -144,7 +147,7 @@ public function alphanumDataProvider() * @return void * @dataProvider alphanumWithSpacesDataProvider */ - public function testAlphanumericValidationWithSpaces($value, $expectedResult) + public function testAlphanumericValidationWithSpaces(string $value, $expectedResult) { $defaultAttributeData = [ 'store_label' => 'Test', @@ -166,7 +169,7 @@ public function testAlphanumericValidationWithSpaces($value, $expectedResult) * * @return array */ - public function alphanumWithSpacesDataProvider() + public function alphanumWithSpacesDataProvider(): array { return [ ['QazWsx', true], diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DefaultFrontendTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DefaultFrontendTest.php index f09014e33ec8a..2c5b1aeb7bbfd 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DefaultFrontendTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DefaultFrontendTest.php @@ -119,7 +119,7 @@ public function testGetClassEmpty() * @return void * @dataProvider validationRulesDataProvider */ - public function testGetClass($validationRule, $expectedClass) + public function testGetClass(string $validationRule, string $expectedClass) { /** @var AbstractAttribute|MockObject $attributeMock */ $attributeMock = $this->createAttributeMock(); @@ -203,7 +203,7 @@ public function testGetSelectOptions() * * @return array */ - public function validationRulesDataProvider() + public function validationRulesDataProvider(): array { return [ ['alphanumeric', 'validate-alphanum'], From 83ff79f4f60734549fdbd90e93cf67f7ecb87ac3 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Mon, 14 Jan 2019 16:41:51 -0600 Subject: [PATCH 0237/1295] MC-5926: Conflict of simultaneous write in Redis cache --- .../functional/lib/Magento/Mtf/App/State/State1.php | 8 +++++++- .../app/Magento/Backend/Test/Repository/ConfigData.xml | 9 --------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/dev/tests/functional/lib/Magento/Mtf/App/State/State1.php b/dev/tests/functional/lib/Magento/Mtf/App/State/State1.php index 60abe18f5a7ba..fc54e73ff1ac2 100644 --- a/dev/tests/functional/lib/Magento/Mtf/App/State/State1.php +++ b/dev/tests/functional/lib/Magento/Mtf/App/State/State1.php @@ -7,6 +7,7 @@ namespace Magento\Mtf\App\State; use Magento\Mtf\ObjectManager; +use Magento\Mtf\Util\Command\Cli; use Magento\Mtf\Util\Protocol\CurlInterface; use Magento\Mtf\Util\Protocol\CurlTransport; @@ -27,7 +28,7 @@ class State1 extends AbstractState * * @var string */ - protected $config ='admin_session_lifetime_1_hour, wysiwyg_disabled, admin_account_sharing_enable, log_to_file'; + protected $config ='admin_session_lifetime_1_hour, wysiwyg_disabled, admin_account_sharing_enable'; /** * HTTP CURL Adapter. @@ -55,6 +56,7 @@ public function __construct( * Apply set up configuration profile. * * @return void + * @throws \Exception */ public function apply() { @@ -67,6 +69,10 @@ public function apply() ['configData' => $this->config] )->run(); } + + /** @var Cli $cli */ + $cli = $this->objectManager->create(Cli::class); + $cli->execute('setup:config:set', ['--enable-debug-logging=true']); } /** diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Repository/ConfigData.xml b/dev/tests/functional/tests/app/Magento/Backend/Test/Repository/ConfigData.xml index 1792ddb5abdc9..5587d605e14b3 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Repository/ConfigData.xml +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Repository/ConfigData.xml @@ -175,15 +175,6 @@ </field> </dataset> - <dataset name="log_to_file"> - <field name="dev/debug/debug_logging" xsi:type="array"> - <item name="scope" xsi:type="string">default</item> - <item name="scope_id" xsi:type="number">0</item> - <item name="label" xsi:type="string">Yes</item> - <item name="value" xsi:type="number">1</item> - </field> - </dataset> - <dataset name="minify_js_files"> <field name="dev/js/minify_files" xsi:type="array"> <item name="scope" xsi:type="string">default</item> From 35e38af8e812e69083525a5340904cf6d1db1e47 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Tue, 15 Jan 2019 10:33:37 +0200 Subject: [PATCH 0238/1295] MAGETWO-89079: Different order statuses with zero value and none zero statuses - Order States fix - Tests fix --- app/code/Magento/Sales/Test/Mftf/Data/ConstData.xml | 8 ++++---- .../AdminAbleToShipPartiallyInvoicedItemsTest.xml | 12 ++++++------ ...AdminCreditMemoTotalAfterShippingDiscountTest.xml | 4 ++-- .../Test/Block/Adminhtml/Order/AbstractForm.php | 2 ++ 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Data/ConstData.xml b/app/code/Magento/Sales/Test/Mftf/Data/ConstData.xml index f5c2fbb794031..523b13ae99c38 100644 --- a/app/code/Magento/Sales/Test/Mftf/Data/ConstData.xml +++ b/app/code/Magento/Sales/Test/Mftf/Data/ConstData.xml @@ -9,10 +9,10 @@ <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="CONST" type="CONST"> - <data key="Complete">Complete</data> - <data key="Closed">Closed</data> - <data key="Pending">Pending</data> - <data key="Processing">Processing</data> + <data key="orderStatusComplete">Complete</data> + <data key="orderStatusClosed">Closed</data> + <data key="orderStatusPending">Pending</data> + <data key="orderStatusProcessing">Processing</data> </entity> </entities> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml index b61d7d69f8742..b85bea61f3bb9 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml @@ -59,7 +59,7 @@ <grabFromCurrentUrl regex="~/order_id/(\d+)/~" stepKey="grabOrderId"/> <seeInCurrentUrl url="{{AdminOrderDetailsPage.url('$grabOrderId')}}" after="grabOrderId" stepKey="seeViewOrderPage"/> <see selector="{{AdminMessagesSection.success}}" userInput="You created the order." after="seeViewOrderPage" stepKey="seeSuccessMessage"/> - <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{CONST.Pending}}" stepKey="seeOrderPending"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{CONST.orderStatusPending}}" stepKey="seeOrderPending"/> <grabTextFrom selector="|Order # (\d+)|" after="seeSuccessMessage" stepKey="getOrderId"/> <scrollTo selector="{{AdminOrderItemsOrderedSection.qtyColumn}}" after="getOrderId" stepKey="scrollToItemsOrdered"/> <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Ordered 10" after="scrollToItemsOrdered" stepKey="seeQtyOfItemsOrdered"/> @@ -80,7 +80,7 @@ <waitForPageLoad stepKey="waitForInvoiceToSubmit1"/> <!--Invoice created successfully--> <see selector="{{AdminMessagesSection.success}}" userInput="The invoice has been created." stepKey="seeInvoiceSuccessMessage"/> - <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{CONST.Processing}}" stepKey="seeOrderProcessing1"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{CONST.orderStatusProcessing}}" stepKey="seeOrderProcessing1"/> <scrollTo selector="{{AdminOrderItemsOrderedSection.itemStatus('1')}}" stepKey="scrollToOrderItems"/> <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Invoiced 5" stepKey="see5itemsInvoiced"/> <scrollTo selector="{{AdminHeaderSection.pageTitle}}" stepKey="scrollToTopOfPage"/> @@ -100,7 +100,7 @@ <waitForLoadingMaskToDisappear stepKey="waitForShipmentToSubmit"/> <!--Verify shipment created successfully--> <see selector="{{AdminMessagesSection.success}}" userInput="The shipment has been created." after="submitShipment" stepKey="successfullShipmentCreation"/> - <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{CONST.Processing}}" stepKey="seeOrderProcessing2"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{CONST.orderStatusProcessing}}" stepKey="seeOrderProcessing2"/> <see selector="{{AdminHeaderSection.pageTitle}}" userInput="$getOrderId" stepKey="seeOrderIdInPageTitleAfterShip"/> <scrollTo selector="{{AdminOrderItemsOrderedSection.itemStatus('1')}}" stepKey="scrollToOrderItems1"/> <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Shipped 5" stepKey="see5itemsShipped"/> @@ -120,7 +120,7 @@ <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="submitRefundOffline"/> <!--Verify Credit Memo created successfully--> <see selector="{{AdminMessagesSection.success}}" userInput="You created the credit memo." after="submitRefundOffline" stepKey="seeCreditMemoSuccessMsg"/> - <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{CONST.Processing}}" stepKey="seeOrderProcessing3"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{CONST.orderStatusProcessing}}" stepKey="seeOrderProcessing3"/> <scrollTo selector="{{AdminOrderItemsOrderedSection.itemStatus('1')}}" stepKey="scrollToOrderItems2"/> <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Refunded 5" stepKey="see5itemsRefunded"/> <scrollTo selector="{{AdminHeaderSection.pageTitle}}" stepKey="scrollToTopOfPage2"/> @@ -142,7 +142,7 @@ <waitForPageLoad stepKey="waitForInvoiceToSubmit2"/> <!--Invoice created successfully for the rest of the ordered items--> <see selector="{{AdminMessagesSection.success}}" userInput="The invoice has been created." stepKey="seeInvoiceSuccessMessage2"/> - <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{CONST.Processing}}" stepKey="seeOrderProcessing4"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{CONST.orderStatusProcessing}}" stepKey="seeOrderProcessing4"/> <scrollTo selector="{{AdminOrderItemsOrderedSection.itemStatus('1')}}" stepKey="scrollToOrderItems3"/> <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Invoiced 10" stepKey="see10itemsInvoiced"/> <scrollTo selector="{{AdminHeaderSection.pageTitle}}" stepKey="scrollToTopOfPage3"/> @@ -161,7 +161,7 @@ <!--Submit Shipment--> <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" after="fillRestOfItemsToShip" stepKey="submitShipment2" /> <see selector="{{AdminMessagesSection.success}}" userInput="The shipment has been created." after="submitShipment2" stepKey="successfullyCreatedShipment"/> - <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{CONST.Complete}}" stepKey="seeOrderComplete"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{CONST.orderStatusComplete}}" stepKey="seeOrderComplete"/> <!--Verify Items Status and Shipped Qty in the Items Ordered section--> <scrollTo selector="{{AdminOrderItemsOrderedSection.itemStatus('1')}}" stepKey="scrollToItemsOrdered2"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreditMemoTotalAfterShippingDiscountTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreditMemoTotalAfterShippingDiscountTest.xml index ffcd658f75087..2f4271d56038b 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreditMemoTotalAfterShippingDiscountTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreditMemoTotalAfterShippingDiscountTest.xml @@ -99,7 +99,7 @@ <!-- Create invoice --> <click selector="{{AdminDataGridTableSection.firstRow}}" stepKey="clickOrderRow"/> <waitForPageLoad stepKey="waitForPageLoad"/> - <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{CONST.Pending}}" stepKey="seeOrderPending"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{CONST.orderStatusPending}}" stepKey="seeOrderPending"/> <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoiceButton"/> <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Invoice" stepKey="seeNewInvoiceInPageTitle" after="clickInvoiceButton"/> @@ -117,7 +117,7 @@ <grabTextFrom selector="{{AdminInvoiceTotalSection.grandTotal}}" stepKey="grabInvoiceGrandTotal" after="seeCorrectGrandTotal"/> <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> <see selector="{{OrderDetailsMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="seeSuccessMessage1"/> - <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{CONST.Processing}}" stepKey="seeOrderProcessing"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{CONST.orderStatusProcessing}}" stepKey="seeOrderProcessing"/> <!--Create Credit Memo--> <comment userInput="Admin creates credit memo" stepKey="createCreditMemoComment"/> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/AbstractForm.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/AbstractForm.php index aea48e891a976..c3a6d5b3cd29c 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/AbstractForm.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/AbstractForm.php @@ -53,9 +53,11 @@ function () { protected function waitForElementEnabled(string $selector, string $strategy = Locator::SELECTOR_CSS) { $browser = $this->browser; + return $browser->waitUntil( function () use ($browser, $selector, $strategy) { $element = $browser->find($selector, $strategy); + return !$element->isDisabled() ? true : null; } ); From c2dc028ef4385070c250ea0bf806d1254889e7d8 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Tue, 15 Jan 2019 11:53:47 +0200 Subject: [PATCH 0239/1295] MAGETWO-73409: Function test Magento\GroupedProduct\Test\TestCase\CreateGroupedProductEntityTest fails randomly --- .../Test/TestCase/CreateGroupedProductEntityTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/TestCase/CreateGroupedProductEntityTest.xml b/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/TestCase/CreateGroupedProductEntityTest.xml index 38ef02ff49441..39f4fd08bb922 100644 --- a/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/TestCase/CreateGroupedProductEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/TestCase/CreateGroupedProductEntityTest.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\GroupedProduct\Test\TestCase\CreateGroupedProductEntityTest" summary="Create Grouped Product" ticketId="MAGETWO-24877"> <variation name="CreateGroupedProductEntityTestVariation1" summary="Create Grouped Product and Assign It to the Category" ticketId="MAGETWO-13610"> - <data name="tag" xsi:type="string">test_type:acceptance_test, test_type:extended_acceptance_test, stable:no</data> + <data name="tag" xsi:type="string">test_type:acceptance_test, test_type:extended_acceptance_test</data> <data name="product/data/url_key" xsi:type="string">test-grouped-product-%isolation%</data> <data name="product/data/name" xsi:type="string">GroupedProduct %isolation%</data> <data name="product/data/sku" xsi:type="string">GroupedProduct_sku%isolation%</data> From 4d2f636e4616a9763f19dbc8d6a6e86916dc3ed9 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 15 Jan 2019 12:01:28 +0200 Subject: [PATCH 0240/1295] MC-10825: [2.2] Update GB country name in USPS API --- app/code/Magento/Usps/Model/Carrier.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Usps/Model/Carrier.php b/app/code/Magento/Usps/Model/Carrier.php index 1e8faf3cc9614..afe34cac575ac 100644 --- a/app/code/Magento/Usps/Model/Carrier.php +++ b/app/code/Magento/Usps/Model/Carrier.php @@ -1246,7 +1246,7 @@ protected function _getCountryName($countryId) 'FO' => 'Faroe Islands', 'FR' => 'France', 'GA' => 'Gabon', - 'GB' => 'Great Britain and Northern Ireland', + 'GB' => 'United Kingdom of Great Britain and Northern Ireland', 'GD' => 'Grenada', 'GE' => 'Georgia, Republic of', 'GF' => 'French Guiana', @@ -1364,7 +1364,7 @@ protected function _getCountryName($countryId) 'ST' => 'Sao Tome and Principe', 'SV' => 'El Salvador', 'SY' => 'Syrian Arab Republic', - 'SZ' => 'Swaziland', + 'SZ' => 'Eswatini', 'TC' => 'Turks and Caicos Islands', 'TD' => 'Chad', 'TG' => 'Togo', From 4ec34a2b5948fd0356e6f6d239793336d94c60d6 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Fri, 7 Sep 2018 17:02:03 +0300 Subject: [PATCH 0241/1295] ISSUE-15950: Magento2 CSV product import qty and is_in_stock not working correct. --- .../CatalogImportExport/Model/Import/Product.php | 4 +--- .../Model/Import/ProductTest.php | 14 ++++++++++++++ ...port_with_backorders_disabled_and_not_0_qty.csv | 2 ++ 3 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_backorders_disabled_and_not_0_qty.csv diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 743bcddede9c3..1f8c6d822d90f 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -2847,9 +2847,7 @@ private function formatStockDataForRow(array $rowData) ) ) { $stockItemDo->setData($row); - $row['is_in_stock'] = $stockItemDo->getBackorders() && isset($row['is_in_stock']) - ? $row['is_in_stock'] - : $this->stockStateProvider->verifyStock($stockItemDo); + $row['is_in_stock'] = $row['is_in_stock'] ?? $this->stockStateProvider->verifyStock($stockItemDo); if ($this->stockStateProvider->verifyNotification($stockItemDo)) { $row['low_stock_date'] = $this->dateTime->gmDate( 'Y-m-d H:i:s', diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index 0251c2b4c34cb..866c05505f574 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -2199,6 +2199,20 @@ public function testImportWithBackordersEnabled() $this->assertFalse($product->getDataByKey('quantity_and_stock_status')['is_in_stock']); } + /** + * Test that imported product stock status with stock quantity > 0 and backorders functionality disabled + * can be set to 'out of stock'. + * + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + */ + public function testImportWithBackordersDisabled() + { + $this->importFile('products_to_import_with_backorders_disabled_and_not_0_qty.csv'); + $product = $this->getProductBySku('simple_new'); + $this->assertFalse($product->getDataByKey('quantity_and_stock_status')['is_in_stock']); + } + /** * Import file by providing import filename in parameters * diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_backorders_disabled_and_not_0_qty.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_backorders_disabled_and_not_0_qty.csv new file mode 100644 index 0000000000000..b22427a8af120 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_backorders_disabled_and_not_0_qty.csv @@ -0,0 +1,2 @@ +sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,related_skus,crosssell_skus,upsell_skus,additional_images,additional_image_labels,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,associated_skus +simple_new,,Default,simple,,base,New Product,,,,1,Taxable Goods,"Catalog, Search",10,,,,new-product,New Product,New Product,New Product ,,,,,,,10/20/2015 7:05,10/20/2015 7:05,,,Block after Info Column,,,,,,,,,,,,,"has_options=1,quantity_and_stock_status=In Stock,required_options=1",100,0,1,0,0,0,1,1,10000,1,0,1,1,1,0,1,1,0,0,0,1,,,,,,,,,,,,, From 06a32e1df9bc245362b3b5c3fd698b9bcc5b1665 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Tue, 15 Jan 2019 12:35:47 +0200 Subject: [PATCH 0242/1295] MAGETWO-89079: Different order statuses with zero value and none zero statuses - Order States fix - Tests fix --- .../Magento/Sales/Test/Block/Adminhtml/Order/AbstractForm.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/AbstractForm.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/AbstractForm.php index c3a6d5b3cd29c..bc7ee4372d61b 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/AbstractForm.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/AbstractForm.php @@ -6,6 +6,7 @@ namespace Magento\Sales\Test\Block\Adminhtml\Order; +use function GuzzleHttp\Psr7\str; use Magento\Mtf\Block\Form; use Magento\Mtf\Client\Locator; @@ -57,8 +58,9 @@ protected function waitForElementEnabled(string $selector, string $strategy = Lo return $browser->waitUntil( function () use ($browser, $selector, $strategy) { $element = $browser->find($selector, $strategy); + $class = $element->getAttribute('class'); - return !$element->isDisabled() ? true : null; + return (!$element->isDisabled() && !strpos($class, 'disabled')) ? true : null; } ); } From de64b09ef51d0d5d2ded8676ffc067dadd3c715d Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Tue, 15 Jan 2019 15:53:50 +0200 Subject: [PATCH 0243/1295] ISSUE-20296: "@magentoDataIsolation" is used instead of "@magentoDbIsolation" in some integration tests. --- .../Magento/CatalogImportExport/Model/Import/ProductTest.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index 2e1e6b6be970e..139d1d97525bc 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -741,7 +741,6 @@ protected function getOptionValues(\Magento\Catalog\Model\Product\Option $option /** * Test that product import with images works properly * - * @magentoDataIsolation enabled * @magentoDataFixture mediaImportImageFixture * @magentoAppIsolation enabled * @SuppressWarnings(PHPMD.ExcessiveMethodLength) @@ -792,7 +791,6 @@ public function testSaveMediaImage() /** * Test that errors occurred during importing images are logged. * - * @magentoDataIsolation enabled * @magentoAppIsolation enabled * @magentoDataFixture mediaImportImageFixture * @magentoDataFixture mediaImportImageFixtureError @@ -2190,7 +2188,6 @@ public function testImportWithDifferentSkuCase() /** * Test that product import with images for non-default store works properly. * - * @magentoDataIsolation enabled * @magentoDataFixture mediaImportImageFixture * @magentoAppIsolation enabled */ @@ -2237,7 +2234,6 @@ public function testProductsWithMultipleStoresWhenMediaIsDisabled() /** * Test that imported product stock status with backorders functionality enabled can be set to 'out of stock'. * - * @magentoDataIsolation enabled * @magentoAppIsolation enabled */ public function testImportWithBackordersEnabled() @@ -2364,7 +2360,6 @@ public function testImportProductWithUpdateUrlKey() /** * Test that product import with non existing images does not broke roles on existing images. * - * @magentoDataIsolation enabled * @magentoDataFixture mediaImportImageFixture * @magentoAppIsolation enabled * @return void From e3f12c51f635956250e37935c2d6c4c53ec47f22 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Tue, 15 Jan 2019 11:08:56 -0600 Subject: [PATCH 0244/1295] MC-5926: Conflict of simultaneous write in Redis cache - integration test to switch from dev to production mode regardless if the integration test framework works in developer or production mode --- app/code/Magento/Deploy/Model/Filesystem.php | 6 +- .../Console/Command/SetModeCommandTest.php | 218 ++++++++++++++++++ 2 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 dev/tests/integration/testsuite/Magento/Deploy/Console/Command/SetModeCommandTest.php diff --git a/app/code/Magento/Deploy/Model/Filesystem.php b/app/code/Magento/Deploy/Model/Filesystem.php index 25b45c8aef0eb..94f01f9b8fd14 100644 --- a/app/code/Magento/Deploy/Model/Filesystem.php +++ b/app/code/Magento/Deploy/Model/Filesystem.php @@ -138,7 +138,11 @@ public function regenerateStatic( DirectoryList::STATIC_VIEW ] ); - + + // Cache flush will regenerate needed folders structure for compilation and deploy that were deleted previously + $cmd = $this->functionCallPath . 'cache:flush '; + $this->shell->execute($cmd); + // Trigger code generation $this->compile($output); // Trigger static assets compilation and deployment diff --git a/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/SetModeCommandTest.php b/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/SetModeCommandTest.php new file mode 100644 index 0000000000000..0b65d76bf1ac3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/SetModeCommandTest.php @@ -0,0 +1,218 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Deploy\Console\Command; + +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Console\Cli; +use Magento\Framework\Filesystem; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\App\State; +use Symfony\Component\Console\Tester\CommandTester; +use Magento\Deploy\Console\ConsoleLogger; +use Magento\Deploy\Console\ConsoleLoggerFactory; +use Magento\Framework\Config\File\ConfigFilePool; +use Magento\Framework\App\DeploymentConfig\FileReader; +use Magento\Framework\App\DeploymentConfig\Writer; + +/** + * Tests working status of deploy:mode:set command. + * + * {@inheritdoc} + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + + */ +class SetModeCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var CommandTester + */ + private $commandTester; + + /** + * @var Filesystem + */ + private $filesystem; + + /** + * @var string + */ + private $prevMode; + + /** + * @var FileReader + */ + private $reader; + + /** + * @var Writer + */ + private $writer; + + /** + * @var array + */ + private $config; + + /** + * @var array + */ + private $envConfig; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->reader = $this->objectManager->get(FileReader::class); + $this->writer = $this->objectManager->get(Writer::class); + $this->prevMode = $this->objectManager->get(State::class)->getMode(); + $this->filesystem = $this->objectManager->get(Filesystem::class); + + $this->config = $this->loadConfig(); + $this->envConfig = $this->loadEnvConfig(); + } + + /** + * @inheritdoc + */ + public function tearDown() + { + $this->writer->saveConfig([ConfigFilePool::APP_CONFIG => $this->config]); + $this->writer->saveConfig([ConfigFilePool::APP_ENV => $this->envConfig]); + + $this->clearStaticFiles(); + } + + /** + * Clear pub/static and var/view_preprocessed directories + * + * @return void + */ + private function clearStaticFiles() + { + $this->filesystem->getDirectoryWrite(DirectoryList::PUB)->delete(DirectoryList::STATIC_VIEW); + $this->filesystem->getDirectoryWrite(DirectoryList::VAR_DIR)->delete(DirectoryList::TMP_MATERIALIZATION_DIR); + } + + public function testSwitchMode() + { + if ($this->prevMode === 'production') { + //in production mode, so we have to switch to dev, then to production + $this->clearStaticFiles(); + $this->enableAndAssertDeveloperMode(); + $this->enableAndAssertProductionMode(); + } else { + //already in non production mode + //$this->clearStaticFiles(); + $this->enableAndAssertProductionMode(); + + // enable previous mode + $this->clearStaticFiles(); + $this->commandTester = new CommandTester($this->getStaticContentDeployCommand()); + $this->commandTester->execute( + ['mode' => $this->prevMode] + ); + $commandOutput = $this->commandTester->getDisplay(); + $this->assertEquals(Cli::RETURN_SUCCESS, $this->commandTester->getStatusCode()); + $this->assertContains('Enabled ' . $this->prevMode . ' mode', $commandOutput); + } + } + + private function enableAndAssertProductionMode() + { + // enable production mode + $this->clearStaticFiles(); + $this->commandTester = new CommandTester($this->getStaticContentDeployCommand()); + $this->commandTester->execute( + ['mode' => 'production'] + ); + $commandOutput = $this->commandTester->getDisplay(); + + $this->assertEquals(Cli::RETURN_SUCCESS, $this->commandTester->getStatusCode()); + + $this->assertContains('Deploy using quick strategy', $commandOutput); + $this->assertContains('frontend/Magento/blank/en_US', $commandOutput); + $this->assertContains('adminhtml/Magento/backend', $commandOutput); + $this->assertContains('Execution time:', $commandOutput); + $this->assertContains('Deployment of static content complete', $commandOutput); + $this->assertContains('Enabled production mode', $commandOutput); + } + + /** + * Enable + */ + private function enableAndAssertDeveloperMode() + { + $this->commandTester = new CommandTester($this->getStaticContentDeployCommand()); + $this->commandTester->execute( + ['mode' => 'developer'] + ); + $commandOutput = $this->commandTester->getDisplay(); + + $this->assertEquals(Cli::RETURN_SUCCESS, $this->commandTester->getStatusCode()); + $this->assertContains('Enabled developer mode', $commandOutput); + } + + /** + * Create SetModeCommand instance with mocked loggers + * + * @return SetModeCommand + */ + private function getStaticContentDeployCommand() + { + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $consoleLoggerFactoryMock = $this->getMockBuilder(ConsoleLoggerFactory::class) + ->setMethods(['getLogger']) + ->disableOriginalConstructor() + ->getMock(); + $consoleLoggerFactoryMock + ->method('getLogger') + ->will($this->returnCallback( + function ($output) use ($objectManager) { + return $objectManager->create(ConsoleLogger::class, ['output' => $output]); + } + )); + $objectManagerProviderMock = $this->getMockBuilder(ObjectManagerProvider::class) + ->setMethods(['get']) + ->disableOriginalConstructor() + ->getMock(); + $objectManagerProviderMock + ->method('get') + ->willReturn(\Magento\TestFramework\Helper\Bootstrap::getObjectManager()); + $deployStaticContentCommand = $objectManager->create( + SetModeCommand::class, + [ + 'consoleLoggerFactory' => $consoleLoggerFactoryMock, + 'objectManagerProvider' => $objectManagerProviderMock + ] + ); + + return $deployStaticContentCommand; + } + + /** + * @return array + */ + private function loadConfig() + { + return $this->reader->load(ConfigFilePool::APP_CONFIG); + } + + /** + * @return array + */ + private function loadEnvConfig() + { + return $this->reader->load(ConfigFilePool::APP_ENV); + } +} From 93a381b34e32e389b63f46dfd6637c768a3de373 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Tue, 15 Jan 2019 11:23:19 -0600 Subject: [PATCH 0245/1295] MC-5926: Conflict of simultaneous write in Redis cache - integration test to switch from dev to production mode regardless if the integration test framework works in developer or production mode --- .../Console/Command/SetModeCommandTest.php | 33 ++++++++----------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/SetModeCommandTest.php b/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/SetModeCommandTest.php index 0b65d76bf1ac3..5fc87d8f200a7 100644 --- a/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/SetModeCommandTest.php +++ b/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/SetModeCommandTest.php @@ -78,8 +78,9 @@ protected function setUp() $this->prevMode = $this->objectManager->get(State::class)->getMode(); $this->filesystem = $this->objectManager->get(Filesystem::class); - $this->config = $this->loadConfig(); - $this->envConfig = $this->loadEnvConfig(); + // Load the original config to restore it on teardown + $this->config = $this->reader->load(ConfigFilePool::APP_CONFIG); + $this->envConfig = $this->reader->load(ConfigFilePool::APP_ENV); } /** @@ -87,6 +88,7 @@ protected function setUp() */ public function tearDown() { + // Restore the original config $this->writer->saveConfig([ConfigFilePool::APP_CONFIG => $this->config]); $this->writer->saveConfig([ConfigFilePool::APP_ENV => $this->envConfig]); @@ -128,9 +130,14 @@ public function testSwitchMode() } } + /** + * Enable production mode + * + * @return void + */ private function enableAndAssertProductionMode() { - // enable production mode + // Enable production mode $this->clearStaticFiles(); $this->commandTester = new CommandTester($this->getStaticContentDeployCommand()); $this->commandTester->execute( @@ -149,7 +156,9 @@ private function enableAndAssertProductionMode() } /** - * Enable + * Enable developer mode + * + * @return void */ private function enableAndAssertDeveloperMode() { @@ -199,20 +208,4 @@ function ($output) use ($objectManager) { return $deployStaticContentCommand; } - - /** - * @return array - */ - private function loadConfig() - { - return $this->reader->load(ConfigFilePool::APP_CONFIG); - } - - /** - * @return array - */ - private function loadEnvConfig() - { - return $this->reader->load(ConfigFilePool::APP_ENV); - } } From c72cbfc878925fb122b14df7c7aedae7f2cb5728 Mon Sep 17 00:00:00 2001 From: Mahesh Singh <mahesh721@webkul.com> Date: Wed, 16 Jan 2019 00:13:55 +0530 Subject: [PATCH 0246/1295] issue #19609 fixed for 2.2-develop --- .../Config/Block/System/Config/Form.php | 41 +++++++++++-------- .../Unit/Block/System/Config/FormTest.php | 6 ++- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/Config/Block/System/Config/Form.php b/app/code/Magento/Config/Block/System/Config/Form.php index 81e39a83296d7..f9bd732736d78 100644 --- a/app/code/Magento/Config/Block/System/Config/Form.php +++ b/app/code/Magento/Config/Block/System/Config/Form.php @@ -143,13 +143,15 @@ public function __construct( \Magento\Config\Model\Config\Structure $configStructure, \Magento\Config\Block\System\Config\Form\Fieldset\Factory $fieldsetFactory, \Magento\Config\Block\System\Config\Form\Field\Factory $fieldFactory, - array $data = [] + array $data = [], + SettingChecker $settingChecker = null ) { parent::__construct($context, $registry, $formFactory, $data); $this->_configFactory = $configFactory; $this->_configStructure = $configStructure; $this->_fieldsetFactory = $fieldsetFactory; $this->_fieldFactory = $fieldFactory; + $this->settingChecker = $settingChecker ?: ObjectManager::getInstance()->get(SettingChecker::class); $this->_scopeLabels = [ self::SCOPE_DEFAULT => __('[GLOBAL]'), @@ -158,18 +160,6 @@ public function __construct( ]; } - /** - * @deprecated 100.1.2 - * @return SettingChecker - */ - private function getSettingChecker() - { - if ($this->settingChecker === null) { - $this->settingChecker = ObjectManager::getInstance()->get(SettingChecker::class); - } - return $this->settingChecker; - } - /** * Initialize objects required to render config form * @@ -366,9 +356,8 @@ protected function _initElement( $sharedClass = $this->_getSharedCssClass($field); $requiresClass = $this->_getRequiresCssClass($field, $fieldPrefix); + $isReadOnly = $this->isReadOnly($field, $path); - $isReadOnly = $this->getElementVisibility()->isDisabled($field->getPath()) - ?: $this->getSettingChecker()->isReadOnly($path, $this->getScope(), $this->getStringScopeCode()); $formField = $fieldset->addField( $elementId, $field->getType(), @@ -417,7 +406,7 @@ private function getFieldData(\Magento\Config\Model\Config\Structure\Element\Fie { $data = $this->getAppConfigDataValue($path); - $placeholderValue = $this->getSettingChecker()->getPlaceholderValue( + $placeholderValue = $this->settingChecker->getPlaceholderValue( $path, $this->getScope(), $this->getStringScopeCode() @@ -797,6 +786,26 @@ private function getAppConfig() return $this->appConfig; } + /** + * Check Path is Readonly + * + * @param \Magento\Config\Model\Config\Structure\Element\Field $field + * @param string $path + * @return boolean + */ + private function isReadOnly(\Magento\Config\Model\Config\Structure\Element\Field $field, $path) + { + $isReadOnly = $this->settingChecker->isReadOnly( + $path, + ScopeConfigInterface::SCOPE_TYPE_DEFAULT + ); + if (!$isReadOnly) { + $isReadOnly = $this->getElementVisibility()->isDisabled($field->getPath()) + ?: $this->settingChecker->isReadOnly($path, $this->getScope(), $this->getStringScopeCode()); + } + return $isReadOnly; + } + /** * Retrieve deployment config data value by path * diff --git a/app/code/Magento/Config/Test/Unit/Block/System/Config/FormTest.php b/app/code/Magento/Config/Test/Unit/Block/System/Config/FormTest.php index 83b7bd5fda42e..528d141306cce 100644 --- a/app/code/Magento/Config/Test/Unit/Block/System/Config/FormTest.php +++ b/app/code/Magento/Config/Test/Unit/Block/System/Config/FormTest.php @@ -103,6 +103,9 @@ protected function setUp() $this->_fieldsetFactoryMock = $this->createMock(\Magento\Config\Block\System\Config\Form\Fieldset\Factory::class); $this->_fieldFactoryMock = $this->createMock(\Magento\Config\Block\System\Config\Form\Field\Factory::class); $this->_coreConfigMock = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); + $settingCheckerMock = $this->getMockBuilder(SettingChecker::class) + ->disableOriginalConstructor() + ->getMock(); $this->_backendConfigMock = $this->createMock(\Magento\Config\Model\Config::class); @@ -150,6 +153,7 @@ protected function setUp() 'fieldsetFactory' => $this->_fieldsetFactoryMock, 'fieldFactory' => $this->_fieldFactoryMock, 'context' => $context, + 'settingChecker' => $settingCheckerMock, ]; $objectArguments = $helper->getConstructArguments(\Magento\Config\Block\System\Config\Form::class, $data); @@ -529,7 +533,7 @@ public function testInitFields( $elementVisibilityMock = $this->getMockBuilder(ElementVisibilityInterface::class) ->getMockForAbstractClass(); - $elementVisibilityMock->expects($this->once()) + $elementVisibilityMock->expects($this->any()) ->method('isDisabled') ->willReturn($isDisabled); From f9a03d854be62438601eb214107a119807504a18 Mon Sep 17 00:00:00 2001 From: Arvinda kumar <arvindakumar@cedcoss.com> Date: Thu, 10 Jan 2019 13:11:38 +0530 Subject: [PATCH 0247/1295] issus fixed #20158 Store switcher not aligned proper in tab view issus fixed #20158 Store switcher not aligned proper in tab view --- .../Magento_Theme/web/css/source/_module.less | 17 +++++++++++++++++ 1 file changed, 17 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 68938ed206038..45bf3585a8e36 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 @@ -319,6 +319,23 @@ } } } + .page-header{ + .switcher { + .options { + ul.dropdown { + right: 0; + &:before{ + right: 10px; + left: auto; + } + &:after { + right: 9px; + left: auto; + } + } + } + } + } // // Widgets From 82ad1cad7f76dcc72dee6d43caf95843096bb3e1 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Fri, 11 Jan 2019 14:45:45 +0200 Subject: [PATCH 0248/1295] ENGCOM-3825: Static test fix. --- .../luma/Magento_Theme/web/css/source/_module.less | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 45bf3585a8e36..45a71d239c466 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 @@ -319,18 +319,18 @@ } } } - .page-header{ + .page-header { .switcher { .options { ul.dropdown { right: 0; - &:before{ - right: 10px; + &:before { left: auto; + right: 10px; } &:after { - right: 9px; left: auto; + right: 9px; } } } From 6c5cba75a855c7b395331a29026a37d94abe723b Mon Sep 17 00:00:00 2001 From: Kajal Solanki <kajal.solanki@krishtechnolabs.com> Date: Sat, 12 Jan 2019 11:16:16 +0530 Subject: [PATCH 0249/1295] checkbox alignment issue resolved --- .../Magento/Braintree/view/adminhtml/templates/form/cc.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Braintree/view/adminhtml/templates/form/cc.phtml b/app/code/Magento/Braintree/view/adminhtml/templates/form/cc.phtml index 535a5a852fe70..4c15fffa8189f 100644 --- a/app/code/Magento/Braintree/view/adminhtml/templates/form/cc.phtml +++ b/app/code/Magento/Braintree/view/adminhtml/templates/form/cc.phtml @@ -83,7 +83,7 @@ $ccType = $block->getInfoData('cc_type'); id="<?= /* @noEscape */ $code ?>_vault" name="payment[is_active_payment_token_enabler]" class="admin__control-checkbox"/> - <label class="label" for="<?= /* @noEscape */ $code ?>_vault"> + <label class="label admin__field-label" for="<?= /* @noEscape */ $code ?>_vault"> <span><?= $block->escapeHtml(__('Save for later use.')) ?></span> </label> </div> From 40f64f9f70be41d0191246cf9d647d20ee4812a4 Mon Sep 17 00:00:00 2001 From: rajneesh1dev <rajneeshgupta@cedcoss.com> Date: Tue, 8 Jan 2019 22:50:26 +0530 Subject: [PATCH 0250/1295] Update uploader.php fix issue 20098 --- .../Magento/CatalogImportExport/Model/Import/Uploader.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php b/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php index 0578cc41d0739..081934dfdfb14 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php @@ -191,9 +191,9 @@ public function move($fileName, $renameFileOff = false) } $fileName = preg_replace('/[^a-z0-9\._-]+/i', '', $fileName); - $filePath = $this->_directory->getRelativePath($filePath . $fileName); + $relativePath = $this->_directory->getRelativePath($filePath . $fileName); $this->_directory->writeFile( - $filePath, + $relativePath, $read->readAll() ); } From 285c6f4644ed716110f5706647fa6b02e330932e Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Wed, 16 Jan 2019 11:25:10 +0200 Subject: [PATCH 0251/1295] MAGETWO-96010: [2.2] Restricted admin still able to send order comment email modifying POST request - Controller fix - Added Unit test --- .../Controller/Adminhtml/Order/AddComment.php | 24 ++- .../Adminhtml/Order/AddCommentTest.php | 185 ++++++++++++++++++ 2 files changed, 205 insertions(+), 4 deletions(-) create mode 100644 app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/AddCommentTest.php diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/AddComment.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/AddComment.php index 12038ee375059..88e05a80f3797 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/AddComment.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/AddComment.php @@ -9,17 +9,27 @@ use Magento\Backend\App\Action; use Magento\Sales\Model\Order\Email\Sender\OrderCommentSender; +/** + * Controller to execute Adding Comments. + */ class AddComment extends \Magento\Sales\Controller\Adminhtml\Order { /** - * Authorization level of a basic admin session + * Authorization level of a basic admin session. * * @see _isAllowed() */ const ADMIN_RESOURCE = 'Magento_Sales::comment'; /** - * Add order comment action + * ACL resource needed to send comment email notification. + * + * @see _isAllowed() + */ + const ADMIN_SALES_EMAIL_RESOURCE = 'Magento_Sales::emails'; + + /** + * Add order comment action. * * @return \Magento\Framework\Controller\ResultInterface */ @@ -33,8 +43,12 @@ public function execute() throw new \Magento\Framework\Exception\LocalizedException(__('Please enter a comment.')); } - $notify = isset($data['is_customer_notified']) ? $data['is_customer_notified'] : false; - $visible = isset($data['is_visible_on_front']) ? $data['is_visible_on_front'] : false; + $notify = $data['is_customer_notified'] ?? false; + $visible = $data['is_visible_on_front'] ?? false; + + if ($notify && !$this->_authorization->isAllowed(self::ADMIN_SALES_EMAIL_RESOURCE)) { + $notify = false; + } $history = $order->addStatusHistoryComment($data['comment'], $data['status']); $history->setIsVisibleOnFront($visible); @@ -59,9 +73,11 @@ public function execute() if (is_array($response)) { $resultJson = $this->resultJsonFactory->create(); $resultJson->setData($response); + return $resultJson; } } + return $this->resultRedirectFactory->create()->setPath('sales/*/'); } } diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/AddCommentTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/AddCommentTest.php new file mode 100644 index 0000000000000..e6384b23bcefc --- /dev/null +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/AddCommentTest.php @@ -0,0 +1,185 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Sales\Test\Unit\Controller\Adminhtml\Order; + +/** + * Test for AddComment. + * + */ +class AddCommentTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Sales\Controller\Adminhtml\Order\AddComment + */ + private $addCommentController; + + /** + * @var \Magento\Backend\App\Action\Context|\PHPUnit_Framework_MockObject_MockObject + */ + private $contextMock; + + /** + * @var \Magento\Sales\Model\Order|\PHPUnit_Framework_MockObject_MockObject + */ + private $orderMock; + + /** + * @var \Magento\Backend\Model\View\Result\RedirectFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $resultRedirectFactoryMock; + + /** + * @var \Magento\Backend\Model\View\Result\Redirect|\PHPUnit_Framework_MockObject_MockObject + */ + private $resultRedirectMock; + + /** + * @var \Magento\Framework\App\Request\Http|\PHPUnit_Framework_MockObject_MockObject + */ + private $requestMock; + + /** + * @var \Magento\Sales\Api\OrderRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $orderRepositoryMock; + + /** + * @var \Magento\Framework\AuthorizationInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $authorizationMock; + + /** + * @var \Magento\Sales\Model\Order\Status\History|\PHPUnit_Framework_MockObject_MockObject + */ + private $statusHistoryCommentMock; + + /** + * @var \Magento\Framework\ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $objectManagerMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->contextMock = $this->createMock(\Magento\Backend\App\Action\Context::class); + $this->requestMock = $this->createMock(\Magento\Framework\App\Request\Http::class); + $this->orderRepositoryMock = $this->createMock(\Magento\Sales\Api\OrderRepositoryInterface::class); + $this->orderMock = $this->createMock(\Magento\Sales\Model\Order::class); + $this->resultRedirectFactoryMock = $this->createMock(\Magento\Backend\Model\View\Result\RedirectFactory::class); + $this->resultRedirectMock = $this->createMock(\Magento\Backend\Model\View\Result\Redirect::class); + $this->authorizationMock = $this->createMock(\Magento\Framework\AuthorizationInterface::class); + $this->statusHistoryCommentMock = $this->createMock(\Magento\Sales\Model\Order\Status\History::class); + $this->objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); + + $this->contextMock->expects($this->once())->method('getRequest')->willReturn($this->requestMock); + + $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->addCommentController = $objectManagerHelper->getObject( + \Magento\Sales\Controller\Adminhtml\Order\AddComment::class, + [ + 'context' => $this->contextMock, + 'orderRepository' => $this->orderRepositoryMock, + '_authorization' => $this->authorizationMock, + '_objectManager' => $this->objectManagerMock, + ] + ); + } + + /** + * Test for execute method with different data. + * + * @param array $historyData + * @param bool $userHasResource + * @param bool $expectedNotify + * + * @return void + * @dataProvider executeWillNotifyCustomerDataProvider + */ + public function testExecuteWillNotifyCustomer(array $historyData, bool $userHasResource, bool $expectedNotify) + { + $orderId = 30; + $this->requestMock->expects($this->once())->method('getParam')->with('order_id')->willReturn($orderId); + $this->orderRepositoryMock->expects($this->once()) + ->method('get') + ->willReturn($this->orderMock); + $this->requestMock->expects($this->once())->method('getPost')->with('history')->willReturn($historyData); + $this->authorizationMock->expects($this->any())->method('isAllowed')->willReturn($userHasResource); + $this->orderMock->expects($this->once()) + ->method('addStatusHistoryComment') + ->willReturn($this->statusHistoryCommentMock); + $this->statusHistoryCommentMock->expects($this->once())->method('setIsCustomerNotified')->with($expectedNotify); + $this->objectManagerMock->expects($this->once())->method('create')->willReturn( + $this->createMock(\Magento\Sales\Model\Order\Email\Sender\OrderCommentSender::class) + ); + + $this->addCommentController->execute(); + } + + /** + * Data provider for testExecuteWillNotifyCustomer method. + * + * @return array + */ + public function executeWillNotifyCustomerDataProvider(): array + { + return [ + 'User Has Access - Notify True' => [ + 'postData' => [ + 'comment' => 'Great Product!', + 'is_customer_notified' => true, + 'status' => 'Processing', + ], + 'userHasResource' => true, + 'expectedNotify' => true, + ], + 'User Has Access - Notify False' => [ + 'postData' => [ + 'comment' => 'Great Product!', + 'is_customer_notified' => false, + 'status' => 'Processing', + ], + 'userHasResource' => true, + 'expectedNotify' => false, + ], + 'User Has Access - Notify Unset' => [ + 'postData' => [ + 'comment' => 'Great Product!', + 'status' => 'Processing', + ], + 'userHasResource' => true, + 'expectedNotify' => false, + ], + 'User No Access - Notify True' => [ + 'postData' => [ + 'comment' => 'Great Product!', + 'is_customer_notified' => true, + 'status' => 'Processing', + ], + 'userHasResource' => false, + 'expectedNotify' => false, + ], + 'User No Access - Notify False' => [ + 'postData' => [ + 'comment' => 'Great Product!', + 'is_customer_notified' => false, + 'status' => 'Processing', + ], + 'userHasResource' => false, + 'expectedNotify' => false, + ], + 'User No Access - Notify Unset' => [ + 'postData' => [ + 'comment' => 'Great Product!', + 'status' => 'Processing', + ], + 'userHasResource' => false, + 'expectedNotify' => false, + ], + ]; + } +} From 7088ce7e2ae71cb4b3e4604123e9875f32a578fe Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Wed, 16 Jan 2019 15:59:29 +0200 Subject: [PATCH 0252/1295] MAGETWO-96010: [2.2] Restricted admin still able to send order comment email modifying POST request - Unit test fix --- .../Test/Unit/Controller/Adminhtml/Order/AddCommentTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/AddCommentTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/AddCommentTest.php index e6384b23bcefc..d72121878e350 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/AddCommentTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/AddCommentTest.php @@ -3,11 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sales\Test\Unit\Controller\Adminhtml\Order; /** * Test for AddComment. - * */ class AddCommentTest extends \PHPUnit\Framework\TestCase { From 31bf4b223e1a49f575b8c17f2adc9d62db482993 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Wed, 16 Jan 2019 16:39:38 +0200 Subject: [PATCH 0253/1295] Fix unit test. --- .../Test/Unit/TemplateEngine/Xhtml/ResultTest.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/code/Magento/Ui/Test/Unit/TemplateEngine/Xhtml/ResultTest.php b/app/code/Magento/Ui/Test/Unit/TemplateEngine/Xhtml/ResultTest.php index 30a8e8005bbe8..9babfefd87f76 100644 --- a/app/code/Magento/Ui/Test/Unit/TemplateEngine/Xhtml/ResultTest.php +++ b/app/code/Magento/Ui/Test/Unit/TemplateEngine/Xhtml/ResultTest.php @@ -6,6 +6,7 @@ namespace Magento\Ui\Test\Unit\TemplateEngine\Xhtml; +use Magento\Framework\Serialize\Serializer\JsonHexTag; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\View\Layout\Generator\Structure; use Magento\Framework\View\Element\UiComponentInterface; @@ -58,6 +59,11 @@ class ResultTest extends \PHPUnit\Framework\TestCase */ private $objectManager; + /** + * @var JsonHexTag|\PHPUnit_Framework_MockObject_MockObject + */ + private $serializer; + protected function setUp() { $this->template = $this->createPartialMock(Template::class, ['append']); @@ -65,6 +71,9 @@ protected function setUp() $this->component = $this->createMock(UiComponentInterface::class); $this->structure = $this->createPartialMock(Structure::class, ['generate']); $this->logger = $this->createMock(LoggerInterface::class); + $this->serializer = $this->getMockBuilder(JsonHexTag::class) + ->disableOriginalConstructor() + ->getMock(); $this->objectManager = new ObjectManager($this); $this->testModel = $this->objectManager->getObject(Result::class, [ @@ -73,6 +82,7 @@ protected function setUp() 'component' => $this->component, 'structure' => $this->structure, 'logger' => $this->logger, + 'jsonSerializer' => $this->serializer ]); } @@ -82,6 +92,10 @@ protected function setUp() public function testAppendLayoutConfiguration() { $configWithCdata = 'text before <![CDATA[cdata text]]>'; + $this->serializer->expects($this->once()) + ->method('serialize') + ->with([$configWithCdata]) + ->willReturn('["text before \u003C![CDATA[cdata text]]\u003E"]'); $this->structure->expects($this->once()) ->method('generate') ->with($this->component) From d328b4c71b91283f4a6fb6ba448b17687e16cb8c Mon Sep 17 00:00:00 2001 From: roman <rleshchenko@magento.com> Date: Wed, 16 Jan 2019 17:23:18 +0200 Subject: [PATCH 0254/1295] MAGETWO-97066: Fixed incorrect behavior of catalog attributes actions --- .../Adminhtml/Product/Attribute/Edit.php | 19 ++++++++++++++++++- .../Adminhtml/Product/AttributeTest.php | 1 + 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit.php index 6ab039aa27849..1ef4200c7721d 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit.php @@ -91,7 +91,24 @@ protected function _construct() if (!$entityAttribute || !$entityAttribute->getIsUserDefined()) { $this->buttonList->remove('delete'); } else { - $this->buttonList->update('delete', 'label', __('Delete Attribute')); + $this->buttonList->update( + 'delete', + 'onclick', + sprintf( + "deleteConfirm('%s','%s', %s)", + __('Are you sure you want to do this?'), + $this->getDeleteUrl(), + json_encode( + [ + 'action' => '', + 'data' => [ + 'form_key' => $this->getFormKey() + ] + ] + ) + ) + ); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php index abf8480708dad..3f033352eb7c1 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php @@ -11,6 +11,7 @@ /** * @magentoAppArea adminhtml * @magentoDbIsolation enabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class AttributeTest extends \Magento\TestFramework\TestCase\AbstractBackendController { From 816be4b43af11f49aa345f4f92eb31e81a559b5a Mon Sep 17 00:00:00 2001 From: Mahesh Singh <mahesh721@webkul.com> Date: Wed, 16 Jan 2019 23:16:40 +0530 Subject: [PATCH 0255/1295] Code Mess warnings removed --- app/code/Magento/Config/Block/System/Config/Form.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Config/Block/System/Config/Form.php b/app/code/Magento/Config/Block/System/Config/Form.php index f9bd732736d78..a05151153daa2 100644 --- a/app/code/Magento/Config/Block/System/Config/Form.php +++ b/app/code/Magento/Config/Block/System/Config/Form.php @@ -707,6 +707,7 @@ protected function _getAdditionalElementTypes() * * @TODO delete this methods when {^see above^} is done * @return string + * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) */ public function getSectionCode() { @@ -718,6 +719,7 @@ public function getSectionCode() * * @TODO delete this methods when {^see above^} is done * @return string + * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) */ public function getWebsiteCode() { @@ -729,6 +731,7 @@ public function getWebsiteCode() * * @TODO delete this methods when {^see above^} is done * @return string + * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) */ public function getStoreCode() { From c0730dc827a417f09c42f96dacd6d1349dd7a565 Mon Sep 17 00:00:00 2001 From: Mahesh Singh <mahesh721@webkul.com> Date: Thu, 17 Jan 2019 00:03:42 +0530 Subject: [PATCH 0256/1295] displaying html content for file type option on order view admin area --- .../Sales/view/adminhtml/templates/items/column/name.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/view/adminhtml/templates/items/column/name.phtml b/app/code/Magento/Sales/view/adminhtml/templates/items/column/name.phtml index ebdf79fe7f008..98cc0d1d0ad07 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/items/column/name.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/items/column/name.phtml @@ -28,7 +28,7 @@ <dt><?= $block->escapeHtml($_option['label']) ?>:</dt> <dd> <?php if (isset($_option['custom_view']) && $_option['custom_view']): ?> - <?= $block->escapeHtml($block->getCustomizedOptionValue($_option)) ?> + <?= /* @escapeNotVerified */ $block->getCustomizedOptionValue($_option) ?> <?php else: ?> <?php $_option = $block->getFormattedOption($_option['value']); ?> <?= $block->escapeHtml($_option['value']) ?> From 37ad0993ef1c3b9fa6e60d95269c7641e354ab68 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Wed, 16 Jan 2019 20:23:33 +0000 Subject: [PATCH 0257/1295] MAGETWO-97664: Skipped randomly failing AdminAbleToShipPartiallyInvoicedItemsTest --- .../Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml index a74fd8ad08eb8..157b17940fbb7 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml @@ -14,6 +14,9 @@ <severity value="CRITICAL"/> <testCaseId value="MAGETWO-95524"/> <group value="sales"/> + <skip> + <issueId value="MAGETWO-97664"/> + </skip> </annotations> <before> <createData entity="_defaultCategory" stepKey="createCategory"/> From 6df3be7a561d0439963459dfb22d344f1d92187d Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Thu, 17 Jan 2019 10:01:21 +0200 Subject: [PATCH 0258/1295] MAGETWO-95663: If number of entities to be updated in flat tables exceed the batch size, the flat tables aren't correctly updated for each store view --- .../Model/Indexer/Product/Flat/TableBuilder.php | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/TableBuilder.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/TableBuilder.php index 5f8be83872021..a32379b8c0a67 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/TableBuilder.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/TableBuilder.php @@ -34,13 +34,6 @@ class TableBuilder */ private $tableBuilderFactory; - /** - * Check whether builder was executed - * - * @var bool - */ - protected $_isExecuted = false; - /** * Constructor * @@ -70,9 +63,6 @@ public function __construct( */ public function build($storeId, $changedIds, $valueFieldSuffix) { - if ($this->_isExecuted) { - return; - } $entityTableName = $this->_productIndexerHelper->getTable('catalog_product_entity'); $attributes = $this->_productIndexerHelper->getAttributes(); $eavAttributes = $this->_productIndexerHelper->getTablesStructure($attributes); @@ -117,7 +107,6 @@ public function build($storeId, $changedIds, $valueFieldSuffix) //Fill temporary tables with attributes grouped by it type $this->_fillTemporaryTable($tableName, $columns, $changedIds, $valueFieldSuffix, $storeId); } - $this->_isExecuted = true; } /** From 60f12e68ced5dcefb7008581a9532aa9009f4a19 Mon Sep 17 00:00:00 2001 From: roman <rleshchenko@magento.com> Date: Thu, 17 Jan 2019 11:22:59 +0200 Subject: [PATCH 0259/1295] MAGETWO-97066: Fixed incorrect behavior of catalog attributes actions --- .../Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit.php index 1ef4200c7721d..695ea6a7288e3 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit.php @@ -108,7 +108,6 @@ protected function _construct() ) ) ); - } } From ba5550c23e20627944fc4e00bd91dfacbab47438 Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Thu, 17 Jan 2019 11:45:44 +0200 Subject: [PATCH 0260/1295] MAGETWO-96659: Zip code is not validated for address entered in My Account and for new address entered during checkout --- .../web/js/model/postcode-validator.js | 8 +++-- .../PostCodesPatternsAttributeData.php | 12 +++++-- .../view/frontend/web/js/addressValidation.js | 36 ++++++------------- 3 files changed, 26 insertions(+), 30 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/postcode-validator.js b/app/code/Magento/Checkout/view/frontend/web/js/model/postcode-validator.js index a95471d90dab8..0a5334a42c7e5 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/postcode-validator.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/postcode-validator.js @@ -14,11 +14,13 @@ define([ /** * @param {*} postCode * @param {*} countryId + * @param {Array} postCodesPatterns * @return {Boolean} */ - validate: function (postCode, countryId) { - var patterns = window.checkoutConfig.postCodes[countryId], - pattern, regex; + validate: function (postCode, countryId, postCodesPatterns) { + var pattern, regex, + patterns = postCodesPatterns ? postCodesPatterns[countryId] : + window.checkoutConfig.postCodes[countryId]; this.validatedPostCodeExample = []; diff --git a/app/code/Magento/Customer/Block/DataProviders/PostCodesPatternsAttributeData.php b/app/code/Magento/Customer/Block/DataProviders/PostCodesPatternsAttributeData.php index 220dc5d9f1aaa..ac83602548abb 100644 --- a/app/code/Magento/Customer/Block/DataProviders/PostCodesPatternsAttributeData.php +++ b/app/code/Magento/Customer/Block/DataProviders/PostCodesPatternsAttributeData.php @@ -7,6 +7,7 @@ namespace Magento\Customer\Block\DataProviders; +use Magento\Framework\Serialize\Serializer\Json; use Magento\Framework\View\Element\Block\ArgumentInterface; use Magento\Directory\Model\Country\Postcode\Config as PostCodeConfig; @@ -20,14 +21,21 @@ class PostCodesPatternsAttributeData implements ArgumentInterface */ private $postCodeConfig; + /** + * @var Json + */ + private $serializer; + /** * Constructor * * @param PostCodeConfig $postCodeConfig + * @param Json $serializer */ - public function __construct(PostCodeConfig $postCodeConfig) + public function __construct(PostCodeConfig $postCodeConfig, Json $serializer) { $this->postCodeConfig = $postCodeConfig; + $this->serializer = $serializer; } /** @@ -37,6 +45,6 @@ public function __construct(PostCodeConfig $postCodeConfig) */ public function getPostCodesJson(): string { - return json_encode($this->postCodeConfig->getPostCodes()); + return $this->serializer->serialize($this->postCodeConfig->getPostCodes()); } } diff --git a/app/code/Magento/Customer/view/frontend/web/js/addressValidation.js b/app/code/Magento/Customer/view/frontend/web/js/addressValidation.js index a7314a35127b5..c014b814ea98b 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/addressValidation.js +++ b/app/code/Magento/Customer/view/frontend/web/js/addressValidation.js @@ -8,9 +8,10 @@ define([ 'underscore', 'mageUtils', 'mage/translate', + 'Magento_Checkout/js/model/postcode-validator', 'jquery/ui', 'validation' -], function ($, __, utils, $t) { +], function ($, __, utils, $t, postCodeValidator) { 'use strict'; $.widget('mage.addressValidation', { @@ -22,12 +23,12 @@ define([ } }, - validatedPostCodeExample: [], zipInput: null, countrySelect: null, /** * Validation creation + * * @protected */ _create: function () { @@ -54,6 +55,8 @@ define([ /** * Add postcode validation + * + * @protected */ _addPostCodeValidation: function () { var self = this; @@ -75,41 +78,24 @@ define([ /** * Validate post code value. * + * @protected * @param {String} postCode - post code * @return {Boolean} Whether is post code valid */ _validatePostCode: function (postCode) { - var countryId = this.countrySelect.val(), - patterns = this.options.postCodes[countryId], - pattern, regex; + var countryId = this.countrySelect.val(); if (postCode === null) { return true; } - this.validatedPostCodeExample = []; - - if (!utils.isEmpty(postCode) && !utils.isEmpty(patterns)) { - for (pattern in patterns) { - if (patterns.hasOwnProperty(pattern)) { //eslint-disable-line max-depth - this.validatedPostCodeExample.push(patterns[pattern].example); - regex = new RegExp(patterns[pattern].pattern); - - if (regex.test(postCode)) { //eslint-disable-line max-depth - return true; - } - } - } - - return false; - } - - return true; + return postCodeValidator.validate(postCode, countryId, this.options.postCodes); }, /** * Renders warning messages for invalid post code. * + * @protected * @param {Boolean} valid */ _renderValidationResult: function (valid) { @@ -119,8 +105,8 @@ define([ if (!valid) { warnMessage = $t('Provided Zip/Postal Code seems to be invalid.'); - if (this.validatedPostCodeExample.length) { - warnMessage += $t(' Example: ') + this.validatedPostCodeExample.join('; ') + '. '; + if (postCodeValidator.validatedPostCodeExample.length) { + warnMessage += $t(' Example: ') + postCodeValidator.validatedPostCodeExample.join('; ') + '. '; } warnMessage += $t('If you believe it is the right one you can ignore this notice.'); } From 4de166f5a78c1a08ca0b0074785493be5685f380 Mon Sep 17 00:00:00 2001 From: Nainesh <nainesh@2jcommerce.in> Date: Thu, 17 Jan 2019 16:54:48 +0530 Subject: [PATCH 0261/1295] 'Fixes-for-customer-login-page-input-field' :: On customer login page input field are short width on tablet view --- .../Magento/blank/Magento_Customer/web/css/source/_module.less | 2 +- .../Magento/luma/Magento_Customer/web/css/source/_module.less | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less index 3ffaeb82cdc2a..7da0433108703 100644 --- a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less @@ -421,7 +421,7 @@ > .field { > .control { - width: 55%; + width: 80%; } } } diff --git a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less index d7ae6c3b28f4a..ade56d7f929c6 100755 --- a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less @@ -371,7 +371,7 @@ .fieldset { > .field { > .control { - width: 55%; + width: 80%; } } } From c874bf5176c292b13e403724cb91a7d7ebc40fc6 Mon Sep 17 00:00:00 2001 From: Dipti 2Jcommerce <dipti@2jcommerce.in> Date: Thu, 17 Jan 2019 17:32:15 +0530 Subject: [PATCH 0262/1295] iPhone5-device-newsletter-subscription-#20167 --- .../web/css/source/_module.less | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/app/design/frontend/Magento/luma/Magento_Newsletter/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Newsletter/web/css/source/_module.less index 9ccd6c190ec0e..aab895392e6dd 100644 --- a/app/design/frontend/Magento/luma/Magento_Newsletter/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Newsletter/web/css/source/_module.less @@ -81,3 +81,24 @@ width: 34%; } } + +// +// Mobile +// _____________________________________________ + +.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { + .block{ + &.newsletter { + input { + font-size: 12px; + padding-left: 30px; + } + + .field { + .control:before{ + font-size: 13px; + } + } + } + } +} From a8b4f385eb34de541bd579b64592fca3e2c01fb3 Mon Sep 17 00:00:00 2001 From: Kajal Solanki <kajal.solanki@krishtechnolabs.com> Date: Thu, 20 Dec 2018 19:00:01 +0530 Subject: [PATCH 0263/1295] Store view label and dropdown alignment solved --- .../css/source/module/main/actions-bar/_store-switcher.less | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/actions-bar/_store-switcher.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/actions-bar/_store-switcher.less index 80bebb22a9043..8607831ab7ffb 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/actions-bar/_store-switcher.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/actions-bar/_store-switcher.less @@ -13,7 +13,7 @@ color: @text__color; // ToDo UI: Delete with admin scope float: left; font-size: round(@font-size__base - .1rem, 1); - margin-top: .7rem; + margin-top: .59rem; .admin__action-dropdown { background-color: @page-main-actions__background-color; @@ -239,7 +239,6 @@ .store-switcher-label { display: inline-block; - margin-top: @indent__s; } } From afa512cebc8b2c90f7a9d83a5a0bd1776d54343c Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Thu, 17 Jan 2019 16:32:26 +0200 Subject: [PATCH 0264/1295] ENGCOM-3881: Semantic test fix. --- app/code/Magento/Config/etc/module.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Config/etc/module.xml b/app/code/Magento/Config/etc/module.xml index cdf31ab7a5d19..b7df33554af90 100644 --- a/app/code/Magento/Config/etc/module.xml +++ b/app/code/Magento/Config/etc/module.xml @@ -6,7 +6,7 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_Config" setup_version="2.0.0"> + <module name="Magento_Config" setup_version="2.1.0"> <sequence> <module name="Magento_Store"/> </sequence> From 304e7e8dfbe3676769b42c6cf90755dadf00830b Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Fri, 18 Jan 2019 09:58:56 +0200 Subject: [PATCH 0265/1295] Fix static test. --- .../CatalogInventory/Block/Stockqty/AbstractStockqty.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogInventory/Block/Stockqty/AbstractStockqty.php b/app/code/Magento/CatalogInventory/Block/Stockqty/AbstractStockqty.php index 7b27125f73fa6..6614b418da920 100644 --- a/app/code/Magento/CatalogInventory/Block/Stockqty/AbstractStockqty.php +++ b/app/code/Magento/CatalogInventory/Block/Stockqty/AbstractStockqty.php @@ -131,7 +131,8 @@ public function getPlaceholderId() */ public function isMsgVisible() { - return $this->getStockQty() > 0 && $this->getStockQtyLeft() > 0 && $this->getStockQtyLeft() <= $this->getThresholdQty(); + return $this->getStockQty() > 0 && $this->getStockQtyLeft() > 0 + && $this->getStockQtyLeft() <= $this->getThresholdQty(); } /** From c825d408e99518a43ffd74f9b4e5f63fa00a44af Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Fri, 18 Jan 2019 10:04:58 +0200 Subject: [PATCH 0266/1295] MAGETWO-96984: [Magento Cloud] Can't delete video images on Duplicate products --- .../Model/Product/Gallery/CreateHandler.php | 2 +- .../Model/ResourceModel/Product/Gallery.php | 21 ++++---- .../Catalog/Product/Gallery/CreateHandler.php | 54 +++++++++++++++++++ 3 files changed, 66 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php b/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php index 1a3d03bf2c353..8b9703b6623ad 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php @@ -308,7 +308,7 @@ protected function duplicate($product) $this->resourceModel->duplicate( $this->getAttribute()->getAttributeId(), - isset($mediaGalleryData['duplicate']) ? $mediaGalleryData['duplicate'] : [], + $mediaGalleryData['duplicate'] ?? [], $product->getOriginalLinkId(), $product->getData($this->metadata->getLinkField()) ); diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php index b68c43e40ff2f..9a7af68948a21 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Gallery.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Model\ResourceModel\Product; use Magento\Store\Model\Store; @@ -141,7 +142,7 @@ public function loadProductGalleryByAttributeId($product, $attributeId) */ protected function createBaseLoadSelect($entityId, $storeId, $attributeId) { - $select = $this->createBatchBaseSelect($storeId, $attributeId); + $select = $this->createBatchBaseSelect($storeId, $attributeId); $select = $select->where( 'entity.' . $this->metadata->getLinkField() . ' = ?', @@ -362,9 +363,9 @@ public function deleteGalleryValueInStore($valueId, $entityId, $storeId) $conditions = implode( ' AND ', [ - $this->getConnection()->quoteInto('value_id = ?', (int) $valueId), - $this->getConnection()->quoteInto($this->metadata->getLinkField() . ' = ?', (int) $entityId), - $this->getConnection()->quoteInto('store_id = ?', (int) $storeId) + $this->getConnection()->quoteInto('value_id = ?', (int)$valueId), + $this->getConnection()->quoteInto($this->metadata->getLinkField() . ' = ?', (int)$entityId), + $this->getConnection()->quoteInto('store_id = ?', (int)$storeId) ] ); @@ -392,7 +393,7 @@ public function duplicate($attributeId, $newFiles, $originalProductId, $newProdu $select = $this->getConnection()->select()->from( [$this->getMainTableAlias() => $this->getMainTable()], - ['value_id', 'value'] + ['value_id', 'value', 'media_type', 'disabled'] )->joinInner( ['entity' => $this->getTable(self::GALLERY_VALUE_TO_ENTITY_TABLE)], $this->getMainTableAlias() . '.value_id = entity.value_id', @@ -409,16 +410,16 @@ public function duplicate($attributeId, $newFiles, $originalProductId, $newProdu // Duplicate main entries of gallery foreach ($this->getConnection()->fetchAll($select) as $row) { - $data = [ - 'attribute_id' => $attributeId, - 'value' => isset($newFiles[$row['value_id']]) ? $newFiles[$row['value_id']] : $row['value'], - ]; + $data = $row; + $data['attribute_id'] = $attributeId; + $data['value'] = $newFiles[$row['value_id']] ?? $row['value']; + unset($data['value_id']); $valueIdMap[$row['value_id']] = $this->insertGallery($data); $this->bindValueToEntity($valueIdMap[$row['value_id']], $newProductId); } - if (count($valueIdMap) == 0) { + if (count($valueIdMap) === 0) { return []; } diff --git a/app/code/Magento/ProductVideo/Model/Plugin/Catalog/Product/Gallery/CreateHandler.php b/app/code/Magento/ProductVideo/Model/Plugin/Catalog/Product/Gallery/CreateHandler.php index c56e5e3139517..d554b5dd68db2 100644 --- a/app/code/Magento/ProductVideo/Model/Plugin/Catalog/Product/Gallery/CreateHandler.php +++ b/app/code/Magento/ProductVideo/Model/Plugin/Catalog/Product/Gallery/CreateHandler.php @@ -20,6 +20,8 @@ class CreateHandler extends AbstractHandler const ADDITIONAL_STORE_DATA_KEY = 'additional_store_data'; /** + * Execute before Plugin + * * @param \Magento\Catalog\Model\Product\Gallery\CreateHandler $mediaGalleryCreateHandler * @param \Magento\Catalog\Model\Product $product * @param array $arguments @@ -45,6 +47,8 @@ public function beforeExecute( } /** + * Execute plugin + * * @param \Magento\Catalog\Model\Product\Gallery\CreateHandler $mediaGalleryCreateHandler * @param \Magento\Catalog\Model\Product $product * @return \Magento\Catalog\Model\Product @@ -59,6 +63,9 @@ public function afterExecute( ); if (!empty($mediaCollection)) { + if ($product->getIsDuplicate() === true) { + $mediaCollection = $this->makeAllNewVideos($product->getId(), $mediaCollection); + } $newVideoCollection = $this->collectNewVideos($mediaCollection); $this->saveVideoData($newVideoCollection, 0); @@ -71,6 +78,8 @@ public function afterExecute( } /** + * Saves video data + * * @param array $videoDataCollection * @param int $storeId * @return void @@ -84,6 +93,8 @@ protected function saveVideoData(array $videoDataCollection, $storeId) } /** + * Saves additioanal video data + * * @param array $videoDataCollection * @return void */ @@ -100,6 +111,8 @@ protected function saveAdditionalStoreData(array $videoDataCollection) } /** + * Saves video data + * * @param array $item * @return void */ @@ -112,6 +125,8 @@ protected function saveVideoValuesItem(array $item) } /** + * Excludes current store data + * * @param array $mediaCollection * @param int $currentStoreId * @return array @@ -127,6 +142,8 @@ function ($item) use ($currentStoreId) { } /** + * Prepare video data for saving + * * @param array $rowData * @return array */ @@ -144,6 +161,8 @@ protected function prepareVideoRowDataForSave(array $rowData) } /** + * Loads video data + * * @param array $mediaCollection * @param int $excludedStore * @return array @@ -166,6 +185,8 @@ protected function loadStoreViewVideoData(array $mediaCollection, $excludedStore } /** + * Collect video data + * * @param array $mediaCollection * @return array */ @@ -183,6 +204,8 @@ protected function collectVideoData(array $mediaCollection) } /** + * Extract video data + * * @param array $rowData * @return array */ @@ -195,6 +218,8 @@ protected function extractVideoDataFromRowData(array $rowData) } /** + * Collect items for additional data adding + * * @param array $mediaCollection * @return array */ @@ -210,6 +235,8 @@ protected function collectVideoEntriesIdsToAdditionalLoad(array $mediaCollection } /** + * Add additional data + * * @param array $mediaCollection * @param array $data * @return array @@ -230,6 +257,8 @@ protected function addAdditionalStoreData(array $mediaCollection, array $data): } /** + * Creates additional video data + * * @param array $storeData * @param int $valueId * @return array @@ -248,6 +277,8 @@ protected function createAdditionalStoreDataCollection(array $storeData, $valueI } /** + * Collect new videos + * * @param array $mediaCollection * @return array */ @@ -263,6 +294,8 @@ private function collectNewVideos(array $mediaCollection): array } /** + * Checks if gallery item is video + * * @param $item * @return bool */ @@ -274,6 +307,8 @@ private function isVideoItem($item): bool } /** + * Checks if video is new + * * @param $item * @return bool */ @@ -283,4 +318,23 @@ private function isNewVideo($item): bool || empty($item['video_url_default']) || empty($item['video_title_default']); } + + /** + * Mark all videos as new + * + * @param int $entityId + * @param array $mediaCollection + * @return array + */ + private function makeAllNewVideos($entityId, array $mediaCollection): array + { + foreach ($mediaCollection as $key => $video) { + if ($this->isVideoItem($video)) { + unset($video['video_url_default'], $video['video_title_default']); + $video['entity_id'] = $entityId; + $mediaCollection[$key] = $video; + } + } + return $mediaCollection; + } } From 77a2260e0bba3e3df66cd5db560da78fe3413a64 Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Fri, 18 Jan 2019 10:31:27 +0200 Subject: [PATCH 0267/1295] magento:magento2 -Backport. Success message is not showing when creating invoice & shipment simultaniously #19942 --- .../Sales/view/frontend/email/shipment_new.html | 2 +- .../view/frontend/email/shipment_new_guest.html | 2 +- .../layout/sales_email_order_shipment_track.xml | 13 +++++++++++++ .../luma/Magento_Sales/email/shipment_new.html | 2 +- .../Magento_Sales/email/shipment_new_guest.html | 2 +- 5 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 app/code/Magento/Sales/view/frontend/layout/sales_email_order_shipment_track.xml diff --git a/app/code/Magento/Sales/view/frontend/email/shipment_new.html b/app/code/Magento/Sales/view/frontend/email/shipment_new.html index 8af49f322c682..84f5acb29ea3b 100644 --- a/app/code/Magento/Sales/view/frontend/email/shipment_new.html +++ b/app/code/Magento/Sales/view/frontend/email/shipment_new.html @@ -53,7 +53,7 @@ <h1>{{trans "Your Shipment #%shipment_id for Order #%order_id" shipment_id=$ship </tr> </table> {{/depend}} - {{block class='Magento\\Framework\\View\\Element\\Template' area='frontend' template='Magento_Sales::email/shipment/track.phtml' shipment=$shipment order=$order}} + {{layout handle="sales_email_order_shipment_track" shipment=$shipment order=$order}} <table class="order-details"> <tr> <td class="address-details"> diff --git a/app/code/Magento/Sales/view/frontend/email/shipment_new_guest.html b/app/code/Magento/Sales/view/frontend/email/shipment_new_guest.html index df1677f56a500..bb181126724da 100644 --- a/app/code/Magento/Sales/view/frontend/email/shipment_new_guest.html +++ b/app/code/Magento/Sales/view/frontend/email/shipment_new_guest.html @@ -51,7 +51,7 @@ <h1>{{trans "Your Shipment #%shipment_id for Order #%order_id" shipment_id=$ship </tr> </table> {{/depend}} - {{block class='Magento\\Framework\\View\\Element\\Template' area='frontend' template='Magento_Sales::email/shipment/track.phtml' shipment=$shipment order=$order}} + {{layout handle="sales_email_order_shipment_track" shipment=$shipment order=$order}} <table class="order-details"> <tr> <td class="address-details"> diff --git a/app/code/Magento/Sales/view/frontend/layout/sales_email_order_shipment_track.xml b/app/code/Magento/Sales/view/frontend/layout/sales_email_order_shipment_track.xml new file mode 100644 index 0000000000000..91414663951d3 --- /dev/null +++ b/app/code/Magento/Sales/view/frontend/layout/sales_email_order_shipment_track.xml @@ -0,0 +1,13 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> + <update handle="sales_email_order_shipment_renderers"/> + <body> + <block class="Magento\Framework\View\Element\Template" name="sales.order.email.shipment.track" template="Magento_Sales::email/shipment/track.phtml"/> + </body> +</page> \ No newline at end of file diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new.html b/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new.html index 8c4084fcaf496..e467aa843e2f4 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new.html @@ -51,7 +51,7 @@ <h1>{{trans "Your Shipment #%shipment_id for Order #%order_id" shipment_id=$ship </tr> </table> {{/depend}} - {{block class='Magento\\Framework\\View\\Element\\Template' area='frontend' template='Magento_Sales::email/shipment/track.phtml' shipment=$shipment order=$order}} + {{layout handle="sales_email_order_shipment_track" shipment=$shipment order=$order}} <table class="order-details"> <tr> <td class="address-details"> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new_guest.html b/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new_guest.html index 68f1886986c5b..385110f8f037e 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new_guest.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new_guest.html @@ -49,7 +49,7 @@ <h1>{{trans "Your Shipment #%shipment_id for Order #%order_id" shipment_id=$ship </tr> </table> {{/depend}} - {{block class='Magento\\Framework\\View\\Element\\Template' area='frontend' template='Magento_Sales::email/shipment/track.phtml' shipment=$shipment order=$order}} + {{layout handle="sales_email_order_shipment_track" shipment=$shipment order=$order}} <table class="order-details"> <tr> <td class="address-details"> From 2bc86d4f5f908c4e05ff325b7b3d17da0e90da1e Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Fri, 18 Jan 2019 11:42:56 +0200 Subject: [PATCH 0268/1295] MAGETWO-96659: Zip code is not validated for address entered in My Account and for new address entered during checkout --- .../DataProviders/PostCodesPatternsAttributeData.php | 12 ++++++------ .../view/frontend/templates/address/edit.phtml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Customer/Block/DataProviders/PostCodesPatternsAttributeData.php b/app/code/Magento/Customer/Block/DataProviders/PostCodesPatternsAttributeData.php index ac83602548abb..280948439e1f8 100644 --- a/app/code/Magento/Customer/Block/DataProviders/PostCodesPatternsAttributeData.php +++ b/app/code/Magento/Customer/Block/DataProviders/PostCodesPatternsAttributeData.php @@ -7,7 +7,7 @@ namespace Magento\Customer\Block\DataProviders; -use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\View\Element\Block\ArgumentInterface; use Magento\Directory\Model\Country\Postcode\Config as PostCodeConfig; @@ -22,7 +22,7 @@ class PostCodesPatternsAttributeData implements ArgumentInterface private $postCodeConfig; /** - * @var Json + * @var SerializerInterface */ private $serializer; @@ -30,20 +30,20 @@ class PostCodesPatternsAttributeData implements ArgumentInterface * Constructor * * @param PostCodeConfig $postCodeConfig - * @param Json $serializer + * @param SerializerInterface $serializer */ - public function __construct(PostCodeConfig $postCodeConfig, Json $serializer) + public function __construct(PostCodeConfig $postCodeConfig, SerializerInterface $serializer) { $this->postCodeConfig = $postCodeConfig; $this->serializer = $serializer; } /** - * Get post codes in json format + * Get serialized post codes * * @return string */ - public function getPostCodesJson(): string + public function getSerializedPostCodes(): string { return $this->serializer->serialize($this->postCodeConfig->getPostCodes()); } diff --git a/app/code/Magento/Customer/view/frontend/templates/address/edit.phtml b/app/code/Magento/Customer/view/frontend/templates/address/edit.phtml index 57f67e45f9a13..992c866316d79 100644 --- a/app/code/Magento/Customer/view/frontend/templates/address/edit.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/address/edit.phtml @@ -188,7 +188,7 @@ { "#form-validate": { "addressValidation": { - "postCodes": <?= /* @noEscape */ $block->getPostCodeConfig()->getPostCodesJson(); ?> + "postCodes": <?= /* @noEscape */ $block->getPostCodeConfig()->getSerializedPostCodes(); ?> } }, "#country": { From 2eeecdb5501e68e53ee1f2e40d5d1012232860c6 Mon Sep 17 00:00:00 2001 From: amol 2jcommerce <amol@2jcommerce.in> Date: Fri, 18 Jan 2019 18:38:50 +0530 Subject: [PATCH 0269/1295] Changes-Schedule-Update-Form-filed-misalign --- .../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 186c2c822f3d5..6550762328145 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 @@ -638,8 +638,8 @@ &.admin__field { > .admin__field-control { &:extend(.abs-field-size-small all); - float: left; position: relative; + display: inline-block; } } From cf630d02c7a0ea8ba891b2b45497e8cfc6e7abff Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Fri, 18 Jan 2019 16:08:34 +0200 Subject: [PATCH 0270/1295] MAGETWO-97453: Media attributes changes --- .../Model/Config/CatalogClone/Media/Image.php | 16 +++- .../Config/CatalogClone/Media/ImageTest.php | 75 +++++++++++++------ 2 files changed, 66 insertions(+), 25 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Config/CatalogClone/Media/Image.php b/app/code/Magento/Catalog/Model/Config/CatalogClone/Media/Image.php index e2b0a91574021..e1b62b5f9b9a7 100644 --- a/app/code/Magento/Catalog/Model/Config/CatalogClone/Media/Image.php +++ b/app/code/Magento/Catalog/Model/Config/CatalogClone/Media/Image.php @@ -5,6 +5,9 @@ */ namespace Magento\Catalog\Model\Config\CatalogClone\Media; +use Magento\Framework\Escaper; +use Magento\Framework\App\ObjectManager; + /** * Clone model for media images related config fields * @@ -26,6 +29,13 @@ class Image extends \Magento\Framework\App\Config\Value */ protected $_attributeCollectionFactory; + /** + * Escaper + * + * @var Escaper + */ + private $escaper; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -46,8 +56,10 @@ public function __construct( \Magento\Eav\Model\Config $eavConfig, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [] + array $data = [], + Escaper $escaper = null ) { + $this->escaper = $escaper ?? ObjectManager::getInstance()->get(Escaper::class); $this->_attributeCollectionFactory = $attributeCollectionFactory; $this->_eavConfig = $eavConfig; parent::__construct($context, $registry, $config, $cacheTypeList, $resource, $resourceCollection, $data); @@ -74,7 +86,7 @@ public function getPrefixes() /* @var $attribute \Magento\Eav\Model\Entity\Attribute */ $prefixes[] = [ 'field' => $attribute->getAttributeCode() . '_', - 'label' => $attribute->getFrontend()->getLabel(), + 'label' => $this->escaper->escapeHtml($attribute->getFrontend()->getLabel()), ]; } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Config/CatalogClone/Media/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Config/CatalogClone/Media/ImageTest.php index 5b1d3bf7943fc..0f10e46d0dce7 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Config/CatalogClone/Media/ImageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Config/CatalogClone/Media/ImageTest.php @@ -36,6 +36,11 @@ class ImageTest extends \PHPUnit\Framework\TestCase */ private $attribute; + /** + * @var \Magento\Framework\Escaper|\PHPUnit_Framework_MockObject_MockObject + */ + private $escaperMock; + protected function setUp() { $this->eavConfig = $this->getMockBuilder(\Magento\Eav\Model\Config::class) @@ -62,54 +67,78 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); + $this->escaperMock = $this->getMockBuilder( + \Magento\Framework\Escaper::class + ) + ->disableOriginalConstructor() + ->setMethods(['escapeHtml']) + ->getMock(); + $helper = new ObjectManager($this); $this->model = $helper->getObject( \Magento\Catalog\Model\Config\CatalogClone\Media\Image::class, [ 'eavConfig' => $this->eavConfig, - 'attributeCollectionFactory' => $this->attributeCollectionFactory + 'attributeCollectionFactory' => $this->attributeCollectionFactory, + 'escaper' => $this->escaperMock, ] ); } - public function testGetPrefixes() + /** + * @param string $actualLabel + * @param string $expectedLabel + * @return void + * @dataProvider getPrefixesDataProvider * + */ + public function testGetPrefixes(string $actualLabel, string $expectedLabel) { $entityTypeId = 3; /** @var \Magento\Eav\Model\Entity\Type|\PHPUnit_Framework_MockObject_MockObject $entityType */ $entityType = $this->getMockBuilder(\Magento\Eav\Model\Entity\Type::class) ->disableOriginalConstructor() ->getMock(); - $entityType->expects($this->once())->method('getId')->will($this->returnValue($entityTypeId)); + $entityType->expects($this->once())->method('getId')->willReturn($entityTypeId); /** @var AbstractFrontend|\PHPUnit_Framework_MockObject_MockObject $frontend */ $frontend = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\Frontend\AbstractFrontend::class) ->setMethods(['getLabel']) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $frontend->expects($this->once())->method('getLabel')->will($this->returnValue('testLabel')); + $frontend->expects($this->once())->method('getLabel')->willReturn($actualLabel); - $this->attributeCollection->expects($this->once())->method('setEntityTypeFilter')->with( - $this->equalTo($entityTypeId) - ); - $this->attributeCollection->expects($this->once())->method('setFrontendInputTypeFilter')->with( - $this->equalTo('media_image') - ); + $this->attributeCollection->expects($this->once())->method('setEntityTypeFilter')->with($entityTypeId); + $this->attributeCollection->expects($this->once())->method('setFrontendInputTypeFilter')->with('media_image'); - $this->attribute->expects($this->once())->method('getAttributeCode')->will( - $this->returnValue('attributeCode') - ); - $this->attribute->expects($this->once())->method('getFrontend')->will( - $this->returnValue($frontend) - ); + $this->attribute->expects($this->once())->method('getAttributeCode')->willReturn('attributeCode'); + $this->attribute->expects($this->once())->method('getFrontend')->willReturn($frontend); - $this->attributeCollection->expects($this->any())->method('getIterator')->will( - $this->returnValue(new \ArrayIterator([$this->attribute])) - ); + $this->attributeCollection->expects($this->any())->method('getIterator') + ->willReturn(new \ArrayIterator([$this->attribute])); - $this->eavConfig->expects($this->any())->method('getEntityType')->with( - $this->equalTo(Product::ENTITY) - )->will($this->returnValue($entityType)); + $this->eavConfig->expects($this->any())->method('getEntityType')->with(Product::ENTITY) + ->willReturn($entityType); + + $this->escaperMock->expects($this->once())->method('escapeHtml')->with($actualLabel) + ->willReturn($expectedLabel); + + $this->assertEquals([['field' => 'attributeCode_', 'label' => $expectedLabel]], $this->model->getPrefixes()); + } - $this->assertEquals([['field' => 'attributeCode_', 'label' => 'testLabel']], $this->model->getPrefixes()); + /** + * @return array + */ + public function getPrefixesDataProvider(): array + { + return [ + [ + 'actual_label' => 'testLabel', + 'expected_label' => 'testLabel', + ], + [ + 'actual_label' => '"<script>alert(\'media-image-attributelabel\')</script>', + 'expected_label' => '"<script>alert('media-image-attributelabel')</script>', + ], + ]; } } From 3b9ba7c6af3b74eb2dea568db860959516abbf71 Mon Sep 17 00:00:00 2001 From: Igor Ludgero Miura <igor@imaginemage.com> Date: Fri, 18 Jan 2019 14:35:47 -0200 Subject: [PATCH 0271/1295] Remove sku from this condition --- .../Magento/Rule/Model/Condition/Product/AbstractProduct.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php b/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php index c02bbd64e7ca3..b7dd1d6c55f4e 100644 --- a/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php +++ b/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php @@ -713,7 +713,7 @@ protected function _getAttributeSetId($productId) public function getOperatorForValidate() { $operator = $this->getOperator(); - if (in_array($this->getInputType(), ['category', 'sku'])) { + if (in_array($this->getInputType(), ['category'])) { if ($operator == '==') { $operator = '{}'; } elseif ($operator == '!=') { From 16d74614e47dce5da7a61dd9d8e5a43ec926b5db Mon Sep 17 00:00:00 2001 From: Arvinda kumar <arvindakumar@cedcoss.com> Date: Tue, 15 Jan 2019 17:32:14 +0530 Subject: [PATCH 0272/1295] issue fixed #20304 No space between step title and saved address in checkout issue fixed #20304 No space between step title and saved address in checkout --- .../web/css/source/module/checkout/_checkout.less | 1 + 1 file changed, 1 insertion(+) diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_checkout.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_checkout.less index 0df0cace338c0..3ea1f5b7f6842 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_checkout.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_checkout.less @@ -48,6 +48,7 @@ .step-title { &:extend(.abs-checkout-title all); .lib-css(border-bottom, @checkout-step-title__border); + margin-bottom: 15px; } .step-content { From b8fa8f3069b3559f4276783a9b9d254e8bb9568f Mon Sep 17 00:00:00 2001 From: niravkrish <nirav.patel@krishtechnolabs.com> Date: Sat, 12 Jan 2019 14:18:37 +0530 Subject: [PATCH 0273/1295] Fixed admin multiselect and select ui arrow toggle issue --- .../base/web/templates/grid/filters/elements/ui-select.html | 4 ++-- .../Magento/backend/web/css/source/forms/_controls.less | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/filters/elements/ui-select.html b/app/code/Magento/Ui/view/base/web/templates/grid/filters/elements/ui-select.html index 4038f65738041..a96a4163caf7e 100644 --- a/app/code/Magento/Ui/view/base/web/templates/grid/filters/elements/ui-select.html +++ b/app/code/Magento/Ui/view/base/web/templates/grid/filters/elements/ui-select.html @@ -36,7 +36,7 @@ class="action-select admin__action-multiselect" data-role="advanced-select" data-bind=" - css: {_active: multiselectFocus}, + css: {_active: listVisible}, click: function(data, event) { toggleListVisible(data, event) } @@ -52,7 +52,7 @@ class="action-select admin__action-multiselect" data-role="advanced-select" data-bind=" - css: {_active: multiselectFocus}, + css: {_active: listVisible}, click: function(data, event) { toggleListVisible(data, event) } diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less index 5f1cee13b5b88..44a086ac61392 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less @@ -85,7 +85,7 @@ cursor: pointer; } - &:focus { + &:active { background-image+: url('../images/arrows-bg.svg'); background-position+: ~'calc(100% - 12px)' 13px; From 072a0776f3c0283cce69f71d286da62499fa5328 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Mon, 21 Jan 2019 10:25:16 +0200 Subject: [PATCH 0274/1295] MAGETWO-97453: Media attributes changes --- .../Magento/Catalog/Model/Config/CatalogClone/Media/Image.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/Catalog/Model/Config/CatalogClone/Media/Image.php b/app/code/Magento/Catalog/Model/Config/CatalogClone/Media/Image.php index e1b62b5f9b9a7..0496122d4758a 100644 --- a/app/code/Magento/Catalog/Model/Config/CatalogClone/Media/Image.php +++ b/app/code/Magento/Catalog/Model/Config/CatalogClone/Media/Image.php @@ -12,6 +12,7 @@ * Clone model for media images related config fields * * @SuppressWarnings(PHPMD.LongVariable) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Image extends \Magento\Framework\App\Config\Value { @@ -46,6 +47,9 @@ class Image extends \Magento\Framework\App\Config\Value * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data + * @param Escaper|null $escaper + * + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\Framework\Model\Context $context, From 5331a127b38ebfc37dcdcd532894929584927148 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Mon, 21 Jan 2019 10:30:16 +0200 Subject: [PATCH 0275/1295] MAGETWO-96711: One page Checkout resets Customer data if Product Qty was changed --- ...ntOnePageCheckoutDataWhenChangeQtyTest.xml | 94 +++++++++++++++++++ .../view/frontend/web/js/checkout-data.js | 36 ++++--- 2 files changed, 118 insertions(+), 12 deletions(-) create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml new file mode 100644 index 0000000000000..36222432df1cc --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml @@ -0,0 +1,94 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontOnePageCheckoutDataWhenChangeQtyTest"> + <annotations> + <features value="Checkout"/> + <title value="One page Checkout Customer data when changing Product Qty"/> + <description value="One page Checkout Customer data when changing Product Qty"/> + <severity value="MAJOR"/> + <testCaseId value="MC-13609"/> + <useCaseId value="MAGETWO-96711"/> + <group value="checkout"/> + </annotations> + <before> + <!--Create a product--> + <createData entity="SimpleProduct3" stepKey="createProduct"/> + </before> + <after> + <!--Delete created data--> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + </after> + + <!--Add product to cart and checkout--> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="amOnSimpleProductPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> + <argument name="productName" value="$createProduct.name$$"/> + </actionGroup> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> + <fillField selector="{{CheckoutShippingSection.email}}" userInput="{{CustomerEntityOne.email}}" stepKey="enterEmail"/> + <fillField selector="{{CheckoutShippingSection.firstName}}" userInput="{{CustomerEntityOne.firstname}}" stepKey="enterFirstName"/> + <fillField selector="{{CheckoutShippingSection.lastName}}" userInput="{{CustomerEntityOne.lastname}}" stepKey="enterLastName"/> + <fillField selector="{{CheckoutShippingSection.street}}" userInput="{{CustomerAddressSimple.street[0]}}" stepKey="enterStreet"/> + <fillField selector="{{CheckoutShippingSection.city}}" userInput="{{CustomerAddressSimple.city}}" stepKey="enterCity"/> + <selectOption selector="{{CheckoutShippingSection.region}}" userInput="{{CustomerAddressSimple.state}}" stepKey="selectRegion"/> + <fillField selector="{{CheckoutShippingSection.postcode}}" userInput="{{CustomerAddressSimple.postcode}}" stepKey="enterPostcode"/> + <fillField selector="{{CheckoutShippingSection.telephone}}" userInput="{{CustomerAddressSimple.telephone}}" stepKey="enterTelephone"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> + + <!--Grab customer data to check it--> + <grabValueFrom selector="{{CheckoutShippingSection.email}}" stepKey="grabEmail"/> + <grabValueFrom selector="{{CheckoutShippingSection.firstName}}" stepKey="grabFirstName"/> + <grabValueFrom selector="{{CheckoutShippingSection.lastName}}" stepKey="grabLastName"/> + <grabValueFrom selector="{{CheckoutShippingSection.street}}" stepKey="grabStreet"/> + <grabValueFrom selector="{{CheckoutShippingSection.city}}" stepKey="grabCity"/> + <grabTextFrom selector="{{CheckoutShippingSection.region}}" stepKey="grabRegion"/> + <grabValueFrom selector="{{CheckoutShippingSection.postcode}}" stepKey="grabPostcode"/> + <grabValueFrom selector="{{CheckoutShippingSection.telephone}}" stepKey="grabTelephone"/> + + <!--Select shipping method and finalize checkout--> + <click selector="{{CheckoutShippingSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod"/> + <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/> + <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> + <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> + <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> + + <!--Go to cart page, update qty and proceed to checkout--> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> + <waitForPageLoad stepKey="waitForCartPageLoad"/> + <see userInput="Shopping Cart" stepKey="seeCartPageIsOpened"/> + <fillField selector="{{CheckoutCartProductSection.productQuantityByName($$createProduct.name$$)}}" userInput="2" stepKey="updateProductQty"/> + <click selector="{{CheckoutCartProductSection.updateShoppingCartButton}}" stepKey="clickUpdateShoppingCart"/> + <waitForAjaxLoad stepKey="waitForAjaxLoad"/> + <grabValueFrom selector="{{CheckoutCartProductSection.qty($$createProduct.name$$)}}" stepKey="grabQty"/> + <assertEquals expected="2" actual="$grabQty" stepKey="assertQty"/> + <click selector="{{StorefrontCheckoutCartSummarySection.proceedToCheckout}}" stepKey="clickProceedToCheckout"/> + + <!--Check that form is filled with customer data--> + <grabValueFrom selector="{{CheckoutShippingSection.email}}" stepKey="grabEmail1"/> + <grabValueFrom selector="{{CheckoutShippingSection.firstName}}" stepKey="grabFirstName1"/> + <grabValueFrom selector="{{CheckoutShippingSection.lastName}}" stepKey="grabLastName1"/> + <grabValueFrom selector="{{CheckoutShippingSection.street}}" stepKey="grabStreet1"/> + <grabValueFrom selector="{{CheckoutShippingSection.city}}" stepKey="grabCity1"/> + <grabTextFrom selector="{{CheckoutShippingSection.region}}" stepKey="grabRegion1"/> + <grabValueFrom selector="{{CheckoutShippingSection.postcode}}" stepKey="grabPostcode1"/> + <grabValueFrom selector="{{CheckoutShippingSection.telephone}}" stepKey="grabTelephone1"/> + + <assertEquals expected="$grabEmail" actual="$grabEmail1" stepKey="assertEmail"/> + <assertEquals expected="$grabFirstName" actual="$grabFirstName1" stepKey="assertFirstName"/> + <assertEquals expected="$grabLastName" actual="$grabLastName1" stepKey="assertLastName"/> + <assertEquals expected="$grabStreet" actual="$grabStreet1" stepKey="assertStreet"/> + <assertEquals expected="$grabCity" actual="$grabCity1" stepKey="assertCity"/> + <assertEquals expected="$grabRegion" actual="$grabRegion1" stepKey="assertRegion"/> + <assertEquals expected="$grabPostcode" actual="$grabPostcode1" stepKey="assertPostcode"/> + <assertEquals expected="$grabTelephone" actual="$grabTelephone1" stepKey="assertTelephone"/> + </test> +</tests> diff --git a/app/code/Magento/Checkout/view/frontend/web/js/checkout-data.js b/app/code/Magento/Checkout/view/frontend/web/js/checkout-data.js index 22b37b2da0b2f..1858ce946fb07 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/checkout-data.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/checkout-data.js @@ -10,7 +10,8 @@ */ define([ 'jquery', - 'Magento_Customer/js/customer-data' + 'Magento_Customer/js/customer-data', + 'jquery/jquery-storageapi' ], function ($, storage) { 'use strict'; @@ -23,6 +24,22 @@ define([ storage.set(cacheKey, data); }, + /** + * @return {*} + */ + initData = function () { + return { + 'selectedShippingAddress': null, //Selected shipping address pulled from persistence storage + 'shippingAddressFromData': null, //Shipping address pulled from persistence storage + 'newCustomerShippingAddress': null, //Shipping address pulled from persistence storage for customer + 'selectedShippingRate': null, //Shipping rate pulled from persistence storage + 'selectedPaymentMethod': null, //Payment method pulled from persistence storage + 'selectedBillingAddress': null, //Selected billing address pulled from persistence storage + 'billingAddressFromData': null, //Billing address pulled from persistence storage + 'newCustomerBillingAddress': null //Billing address pulled from persistence storage for new customer + }; + }, + /** * @return {*} */ @@ -30,17 +47,12 @@ define([ var data = storage.get(cacheKey)(); if ($.isEmptyObject(data)) { - data = { - 'selectedShippingAddress': null, //Selected shipping address pulled from persistence storage - 'shippingAddressFromData': null, //Shipping address pulled from persistence storage - 'newCustomerShippingAddress': null, //Shipping address pulled from persistence storage for customer - 'selectedShippingRate': null, //Shipping rate pulled from persistence storage - 'selectedPaymentMethod': null, //Payment method pulled from persistence storage - 'selectedBillingAddress': null, //Selected billing address pulled from persistence storage - 'billingAddressFromData': null, //Billing address pulled from persistence storage - 'newCustomerBillingAddress': null //Billing address pulled from persistence storage for new customer - }; - saveData(data); + data = $.initNamespaceStorage('mage-cache-storage').localStorage.get(cacheKey); + + if ($.isEmptyObject(data)) { + data = initData(); + saveData(data); + } } return data; From ff6eadd130953b0b67569a3f3b7c8189afb94d33 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Mon, 21 Jan 2019 10:45:33 +0200 Subject: [PATCH 0276/1295] MAGETWO-95974: Offline refund warning not displayed for Express Checkout orders --- app/code/Magento/Paypal/Model/Express.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Paypal/Model/Express.php b/app/code/Magento/Paypal/Model/Express.php index 196e59c6593b9..a44fb9e7c15b0 100644 --- a/app/code/Magento/Paypal/Model/Express.php +++ b/app/code/Magento/Paypal/Model/Express.php @@ -44,7 +44,7 @@ class Express extends \Magento\Payment\Model\Method\AbstractMethod * * @var bool */ - protected $_isGateway = false; + protected $_isGateway = true; /** * Availability option From 2e844fa78eba46f6b776a0cdc7ca92e51a037047 Mon Sep 17 00:00:00 2001 From: Chris Snedaker <df2002@gmail.com> Date: Mon, 14 Jan 2019 00:42:43 -0500 Subject: [PATCH 0277/1295] Missing echo of php vars in widget template file - tabshoriz.phtml --- .../Backend/view/adminhtml/templates/widget/tabshoriz.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Backend/view/adminhtml/templates/widget/tabshoriz.phtml b/app/code/Magento/Backend/view/adminhtml/templates/widget/tabshoriz.phtml index 062528e742201..7b68a379caae9 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/widget/tabshoriz.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/widget/tabshoriz.phtml @@ -18,7 +18,7 @@ <?php $_tabType = (!preg_match('/\s?ajax\s?/', $_tabClass) && $block->getTabUrl($_tab) != '#') ? 'link' : '' ?> <?php $_tabHref = $block->getTabUrl($_tab) == '#' ? '#' . $block->getTabId($_tab) . '_content' : $block->getTabUrl($_tab) ?> <li> - <a href="<?= /* @escapeNotVerified */ $_tabHref ?>" id="<?= /* @escapeNotVerified */ $block->getTabId($_tab) ?>" title="<?= /* @escapeNotVerified */ $block->getTabTitle($_tab) ?>" class="<?php $_tabClass ?>" data-tab-type="<?php $_tabType ?>"> + <a href="<?= /* @escapeNotVerified */ $_tabHref ?>" id="<?= /* @escapeNotVerified */ $block->getTabId($_tab) ?>" title="<?= /* @escapeNotVerified */ $block->getTabTitle($_tab) ?>" class="<?= $_tabClass ?>" data-tab-type="<?= $_tabType ?>"> <span> <span class="changed" title="<?= /* @escapeNotVerified */ __('The information in this tab has been changed.') ?>"></span> <span class="error" title="<?= /* @escapeNotVerified */ __('This tab contains invalid data. Please resolve this before saving.') ?>"></span> From cf0d2598ada2ac2e3f42a73c28096a04160dbdf7 Mon Sep 17 00:00:00 2001 From: Chris Snedaker <df2002@gmail.com> Date: Mon, 14 Jan 2019 02:29:49 -0500 Subject: [PATCH 0278/1295] Escaped output of html attributes --- .../Backend/view/adminhtml/templates/widget/tabshoriz.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Backend/view/adminhtml/templates/widget/tabshoriz.phtml b/app/code/Magento/Backend/view/adminhtml/templates/widget/tabshoriz.phtml index 7b68a379caae9..c76f10da0f927 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/widget/tabshoriz.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/widget/tabshoriz.phtml @@ -18,7 +18,7 @@ <?php $_tabType = (!preg_match('/\s?ajax\s?/', $_tabClass) && $block->getTabUrl($_tab) != '#') ? 'link' : '' ?> <?php $_tabHref = $block->getTabUrl($_tab) == '#' ? '#' . $block->getTabId($_tab) . '_content' : $block->getTabUrl($_tab) ?> <li> - <a href="<?= /* @escapeNotVerified */ $_tabHref ?>" id="<?= /* @escapeNotVerified */ $block->getTabId($_tab) ?>" title="<?= /* @escapeNotVerified */ $block->getTabTitle($_tab) ?>" class="<?= $_tabClass ?>" data-tab-type="<?= $_tabType ?>"> + <a href="<?= $block->escapeHtmlAttr($_tabHref) ?>" id="<?= $block->escapeHtmlAttr($block->getTabId($_tab)) ?>" title="<?= $block->escapeHtmlAttr($block->getTabTitle($_tab)) ?>" class="<?= $block->escapeHtmlAttr($_tabClass) ?>" data-tab-type="<?= $block->escapeHtmlAttr($_tabType) ?>"> <span> <span class="changed" title="<?= /* @escapeNotVerified */ __('The information in this tab has been changed.') ?>"></span> <span class="error" title="<?= /* @escapeNotVerified */ __('This tab contains invalid data. Please resolve this before saving.') ?>"></span> From 0fb027c4f92de8a7c65745af9a8aafbb88c96787 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Mon, 21 Jan 2019 12:55:50 +0200 Subject: [PATCH 0279/1295] MAGETWO-97466: Refreshing checkout page deletes shipping address on guest checkout --- .../Checkout/etc/frontend/sections.xml | 1 - .../view/frontend/web/js/model/place-order.js | 22 +++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Checkout/etc/frontend/sections.xml b/app/code/Magento/Checkout/etc/frontend/sections.xml index 35733a6119a25..90c2878f501cf 100644 --- a/app/code/Magento/Checkout/etc/frontend/sections.xml +++ b/app/code/Magento/Checkout/etc/frontend/sections.xml @@ -46,7 +46,6 @@ </action> <action name="rest/*/V1/guest-carts/*/payment-information"> <section name="cart"/> - <section name="checkout-data"/> </action> <action name="rest/*/V1/guest-carts/*/selected-payment-method"> <section name="cart"/> diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/place-order.js b/app/code/Magento/Checkout/view/frontend/web/js/model/place-order.js index c3c5b9d68cec0..c07878fcaea92 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/place-order.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/place-order.js @@ -9,9 +9,10 @@ define( [ 'mage/storage', 'Magento_Checkout/js/model/error-processor', - 'Magento_Checkout/js/model/full-screen-loader' + 'Magento_Checkout/js/model/full-screen-loader', + 'Magento_Customer/js/customer-data' ], - function (storage, errorProcessor, fullScreenLoader) { + function (storage, errorProcessor, fullScreenLoader, customerData) { 'use strict'; return function (serviceUrl, payload, messageContainer) { @@ -23,6 +24,23 @@ define( function (response) { errorProcessor.process(response, messageContainer); } + ).success( + function (response) { + var clearData = { + 'selectedShippingAddress': null, + 'shippingAddressFromData': null, + 'newCustomerShippingAddress': null, + 'selectedShippingRate': null, + 'selectedPaymentMethod': null, + 'selectedBillingAddress': null, + 'billingAddressFromData': null, + 'newCustomerBillingAddress': null + }; + + if (response.responseType !== 'error') { + customerData.set('checkout-data', clearData); + } + } ).always( function () { fullScreenLoader.stopLoader(); From 7eed56af01b5b317ef382a6065de1bfa9a116112 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Mon, 21 Jan 2019 13:11:08 +0200 Subject: [PATCH 0280/1295] MAGETWO-96711: One page Checkout resets Customer data if Product Qty was changed --- .../Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml | 1 + .../Mftf/ActionGroup/GoToCheckoutFromMinicartActionGroup.xml | 5 +++-- .../Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml index 24b41a2fa673e..02dadf0d00337 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml @@ -27,6 +27,7 @@ <waitForElementNotVisible selector="{{StorefrontProductPageSection.addToCartButtonTitleIsAdded}}" stepKey="waitForElementNotVisibleAddToCartButtonTitleIsAdded"/> <waitForElementVisible selector="{{StorefrontProductPageSection.addToCartButtonTitleIsAddToCart}}" stepKey="waitForElementVisibleAddToCartButtonTitleIsAddToCart"/> <waitForPageLoad stepKey="waitForPageLoad"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" time="30" stepKey="waitForProductAddedMessage"/> <see selector="{{StorefrontMessagesSection.success}}" userInput="You added {{productName}} to your shopping cart." stepKey="seeAddToCartSuccessMessage"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GoToCheckoutFromMinicartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GoToCheckoutFromMinicartActionGroup.xml index 5d91be6517097..f03be61cccd3a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GoToCheckoutFromMinicartActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GoToCheckoutFromMinicartActionGroup.xml @@ -7,12 +7,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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Go to checkout from minicart --> <actionGroup name="GoToCheckoutFromMinicartActionGroup"> <waitForElement selector="{{StorefrontMinicartSection.showCart}}" stepKey="waitMiniCartSectionShow" /> <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> - <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> + <waitForElementVisible selector="{{StorefrontMinicartSection.goToCheckout}}" time="30" stepKey="waitForGoToCheckoutButtonVisible"/> + <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="clickGoToCheckoutButton"/> <waitForPageLoad stepKey="waitForPageLoad"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml index 36222432df1cc..7817182525c1e 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml @@ -68,7 +68,7 @@ <fillField selector="{{CheckoutCartProductSection.productQuantityByName($$createProduct.name$$)}}" userInput="2" stepKey="updateProductQty"/> <click selector="{{CheckoutCartProductSection.updateShoppingCartButton}}" stepKey="clickUpdateShoppingCart"/> <waitForAjaxLoad stepKey="waitForAjaxLoad"/> - <grabValueFrom selector="{{CheckoutCartProductSection.qty($$createProduct.name$$)}}" stepKey="grabQty"/> + <grabValueFrom selector="{{CheckoutCartProductSection.productQuantityByName($$createProduct.name$$)}}" stepKey="grabQty"/> <assertEquals expected="2" actual="$grabQty" stepKey="assertQty"/> <click selector="{{StorefrontCheckoutCartSummarySection.proceedToCheckout}}" stepKey="clickProceedToCheckout"/> From 5febcfe0f6659162141550c68058ebd298f328bc Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Mon, 21 Jan 2019 13:17:17 +0200 Subject: [PATCH 0281/1295] MAGETWO-72879: Update product from mini shopping cart doesn't reflect in the shopping cart --- .../Section/StorefrontMinicartSection.xml | 2 + ...nShoppingCartAfterUpdateInMinicartTest.xml | 70 +++++++++++++++++++ .../Checkout/view/frontend/web/js/sidebar.js | 5 ++ 3 files changed, 77 insertions(+) create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateQtyInShoppingCartAfterUpdateInMinicartTest.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMinicartSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMinicartSection.xml index 4bdb05b99b634..00e7d5a011513 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMinicartSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMinicartSection.xml @@ -22,6 +22,8 @@ <element name="viewAndEditCart" type="button" selector=".action.viewcart" timeout="30"/> <element name="miniCartItemsText" type="text" selector=".minicart-items"/> <element name="miniCartSubtotalField" type="text" selector=".block-minicart .amount span.price"/> + <element name="itemQuantity" type="input" selector="//a[text()='{{productName}}']/../..//input[contains(@class,'cart-item-qty')]" parameterized="true"/> + <element name="itemQuantityUpdate" type="button" selector="//a[text()='{{productName}}']/../..//span[text()='Update']" parameterized="true"/> <element name="emptyCart" type="text" selector=".counter.qty.empty"/> </section> </sections> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateQtyInShoppingCartAfterUpdateInMinicartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateQtyInShoppingCartAfterUpdateInMinicartTest.xml new file mode 100644 index 0000000000000..dd46a1587ce1c --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateQtyInShoppingCartAfterUpdateInMinicartTest.xml @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontUpdateQtyInShoppingCartAfterUpdateInMinicartTest"> + <annotations> + <features value="Checkout"/> + <stories + value="MAGETWO-72879: Update product from mini shopping cart doesn't reflect in the shopping cart"/> + <title value="Check updating shopping cart while updating items from minicart"/> + <description value="Check updating shopping cart while updating items from minicart"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-13626"/> + <group value="checkout"/> + </annotations> + <before> + <!--Create category--> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <!--Create product--> + <createData entity="SimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <after> + <!--Delete product--> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <!--Delete category--> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + + <!--Open Product Page--> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="openProductPage"/> + <waitForPageLoad stepKey="waitForProductPage"/> + <!--Add product to cart--> + <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProductToCart"> + <argument name="productName" value="$$createProduct.name$$"/> + </actionGroup> + + <!--Go to Shopping cart--> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="openShoppingCart"/> + <!--Check quantity in Shopping cart--> + <grabValueFrom selector="{{CheckoutCartProductSection.productQuantityByName($$createProduct.name$$)}}" + stepKey="grabQtyFromShoppingCart"/> + <assertEquals expected="1" actual="$grabQtyFromShoppingCart" stepKey="assertQtyInShoppingCart"/> + + <!--Open minicart--> + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="openMiniCart"/> + <waitForElementVisible selector="{{StorefrontMinicartSection.itemQuantity($$createProduct.name$$)}}" + stepKey="waitForItemQuantity"/> + <pressKey selector="{{StorefrontMinicartSection.itemQuantity($$createProduct.name$$)}}" + parameterArray="[\Facebook\WebDriver\WebDriverKeys::BACKSPACE]" stepKey="clearQtyField"/> + <fillField selector="{{StorefrontMinicartSection.itemQuantity($$createProduct.name$$)}}" userInput="5" + stepKey="fillQtyField"/> + <waitForElementVisible selector="{{StorefrontMinicartSection.itemQuantityUpdate($$createProduct.name$$)}}" + stepKey="waitForUpdateButton"/> + <click selector="{{StorefrontMinicartSection.itemQuantityUpdate($$createProduct.name$$)}}" + stepKey="clickUpdateButton"/> + <waitForAjaxLoad stepKey="waitForAjaxLoad"/> + <!--Check quantity in shopping cart after updating--> + <grabValueFrom selector="{{CheckoutCartProductSection.productQuantityByName($$createProduct.name$$)}}" + stepKey="grabQtyFromShoppingCart1"/> + <assertEquals expected="5" actual="$grabQtyFromShoppingCart1" stepKey="assertQtyInShoppingCart1"/> + </test> +</tests> 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 dde1ad72ba15e..e66c66006246c 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js @@ -25,6 +25,7 @@ define([ } }, scrollHeight: 0, + shoppingCartUrl: window.checkout.shoppingCartUrl, /** * Create sidebar. @@ -227,6 +228,10 @@ define([ if (!_.isUndefined(productData)) { $(document).trigger('ajax:updateCartItemQty'); + + if (window.location.href === this.shoppingCartUrl) { + window.location.reload(false); + } } this._hideItemButton(elem); }, From ecaffb4329116f8c05156f23c697ecb440fced9e Mon Sep 17 00:00:00 2001 From: Kajal Solanki <kajal.solanki@krishtechnolabs.com> Date: Fri, 11 Jan 2019 15:07:14 +0530 Subject: [PATCH 0282/1295] meassage icon alignment issue resolved --- .../Magento/backend/web/css/source/components/_messages.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less index de24bf89620d4..4964a691e6453 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less @@ -76,7 +76,7 @@ position: absolute; speak: none; text-shadow: none; - top: 1.3rem; + top: 1.5rem; width: auto; } } From cb0dae73e08660850a2fea5961a902561c31f23c Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Tue, 15 Jan 2019 17:21:17 +0200 Subject: [PATCH 0283/1295] ENGCOM-3851: Static test fix. --- .../Magento/backend/web/css/source/components/_messages.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less index 4964a691e6453..8773de61185e2 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less @@ -110,7 +110,7 @@ content: @alert-icon__error__content; font-size: @alert-icon__error__font-size; left: 2.2rem; - margin-top: 0.5rem; + margin-top: .5rem; } } From 58d49a9e81aaaa10bfcab3f685040fd1e0332939 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Mon, 21 Jan 2019 14:58:06 +0200 Subject: [PATCH 0284/1295] ENGCOM-3893: Static test fix. --- .../module/main/actions-bar/_store-switcher.less | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/actions-bar/_store-switcher.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/actions-bar/_store-switcher.less index 8607831ab7ffb..0bd6cc62bd509 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/actions-bar/_store-switcher.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/actions-bar/_store-switcher.less @@ -10,11 +10,6 @@ // ToDo UI: Consist old styles, should be changed with new design .store-switcher { - color: @text__color; // ToDo UI: Delete with admin scope - float: left; - font-size: round(@font-size__base - .1rem, 1); - margin-top: .59rem; - .admin__action-dropdown { background-color: @page-main-actions__background-color; margin-left: .5em; @@ -47,8 +42,8 @@ width: 7px; } &::-webkit-scrollbar-thumb { - border-radius: 4px; background-color: rgba(0, 0, 0, .5); + border-radius: 4px; } li { @@ -116,6 +111,12 @@ } } } + + color: @text__color; // ToDo UI: Delete with admin scope + float: left; + font-size: round(@font-size__base - .1rem, 1); + margin-top: .59rem; + } .store-switcher-label { From ed542833ddbbf0be0dfe70062e54aa94fcf6705f Mon Sep 17 00:00:00 2001 From: amol 2jcommerce <amol@2jcommerce.in> Date: Mon, 21 Jan 2019 18:29:01 +0530 Subject: [PATCH 0285/1295] Fixed-Shipping-method-title-overlapping-on-edit-icon-in-mobile --- .../web/css/source/module/checkout/_order-summary.less | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_order-summary.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_order-summary.less index 5ecc4d4713bf1..a1ff377ca9123 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_order-summary.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_order-summary.less @@ -204,6 +204,12 @@ &:extend(.abs-sidebar-totals-mobile all); } } + + .opc-block-shipping-information { + .shipping-information-title { + font-size: 2.4rem; + } + } } // From 1018cf0aa05163dc4b1515d7b2974f333426d011 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Mon, 21 Jan 2019 16:23:25 +0200 Subject: [PATCH 0286/1295] MAGETWO-97453: Media attributes changes --- .../Catalog/Model/Config/CatalogClone/Media/Image.php | 2 -- .../Unit/Model/Config/CatalogClone/Media/ImageTest.php | 9 ++++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Config/CatalogClone/Media/Image.php b/app/code/Magento/Catalog/Model/Config/CatalogClone/Media/Image.php index 0496122d4758a..d7d342f357519 100644 --- a/app/code/Magento/Catalog/Model/Config/CatalogClone/Media/Image.php +++ b/app/code/Magento/Catalog/Model/Config/CatalogClone/Media/Image.php @@ -31,8 +31,6 @@ class Image extends \Magento\Framework\App\Config\Value protected $_attributeCollectionFactory; /** - * Escaper - * * @var Escaper */ private $escaper; diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Config/CatalogClone/Media/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Config/CatalogClone/Media/ImageTest.php index 0f10e46d0dce7..0688ad5bde19d 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Config/CatalogClone/Media/ImageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Config/CatalogClone/Media/ImageTest.php @@ -41,6 +41,9 @@ class ImageTest extends \PHPUnit\Framework\TestCase */ private $escaperMock; + /** + * @inheritdoc + */ protected function setUp() { $this->eavConfig = $this->getMockBuilder(\Magento\Eav\Model\Config::class) @@ -89,7 +92,7 @@ protected function setUp() * @param string $actualLabel * @param string $expectedLabel * @return void - * @dataProvider getPrefixesDataProvider * + * @dataProvider getPrefixesDataProvider */ public function testGetPrefixes(string $actualLabel, string $expectedLabel) { @@ -136,8 +139,8 @@ public function getPrefixesDataProvider(): array 'expected_label' => 'testLabel', ], [ - 'actual_label' => '"<script>alert(\'media-image-attributelabel\')</script>', - 'expected_label' => '"<script>alert('media-image-attributelabel')</script>', + 'actual_label' => '<media-image-attributelabel', + 'expected_label' => '<media-image-attributelabel', ], ]; } From f7a2237e80d9d6b92ce9f44b918fbc47c4f7f8f6 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Mon, 21 Jan 2019 16:44:36 +0200 Subject: [PATCH 0287/1295] MAGETWO-96444: Impossible to sort Root Categories via drag'n'drop --- .../Adminhtml/Category/AbstractCategory.php | 104 ++++++++++++------ .../templates/catalog/category/tree.phtml | 1 + .../catalog/category/widget/tree.phtml | 4 +- 3 files changed, 75 insertions(+), 34 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Category/AbstractCategory.php b/app/code/Magento/Catalog/Block/Adminhtml/Category/AbstractCategory.php index 331679874629b..bfeab3f71ebc1 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Category/AbstractCategory.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Category/AbstractCategory.php @@ -7,6 +7,11 @@ use Magento\Framework\Data\Tree\Node; use Magento\Store\Model\Store; +use Magento\Framework\Registry; +use Magento\Catalog\Model\ResourceModel\Category\Tree; +use Magento\Catalog\Model\CategoryFactory; +use Magento\Backend\Block\Template\Context; +use Magento\Catalog\Model\Category; /** * Class AbstractCategory @@ -16,17 +21,17 @@ class AbstractCategory extends \Magento\Backend\Block\Template /** * Core registry * - * @var \Magento\Framework\Registry + * @var Registry */ protected $_coreRegistry = null; /** - * @var \Magento\Catalog\Model\ResourceModel\Category\Tree + * @var Tree */ protected $_categoryTree; /** - * @var \Magento\Catalog\Model\CategoryFactory + * @var CategoryFactory */ protected $_categoryFactory; @@ -36,17 +41,17 @@ class AbstractCategory extends \Magento\Backend\Block\Template protected $_withProductCount; /** - * @param \Magento\Backend\Block\Template\Context $context - * @param \Magento\Catalog\Model\ResourceModel\Category\Tree $categoryTree - * @param \Magento\Framework\Registry $registry - * @param \Magento\Catalog\Model\CategoryFactory $categoryFactory + * @param Context $context + * @param Tree $categoryTree + * @param Registry $registry + * @param CategoryFactory $categoryFactory * @param array $data */ public function __construct( - \Magento\Backend\Block\Template\Context $context, - \Magento\Catalog\Model\ResourceModel\Category\Tree $categoryTree, - \Magento\Framework\Registry $registry, - \Magento\Catalog\Model\CategoryFactory $categoryFactory, + Context $context, + Tree $categoryTree, + Registry $registry, + CategoryFactory $categoryFactory, array $data = [] ) { $this->_categoryTree = $categoryTree; @@ -67,36 +72,47 @@ public function getCategory() } /** + * Get category id + * * @return int|string|null */ public function getCategoryId() { if ($this->getCategory()) { - return $this->getCategory()->getId(); + return $this->getCategory() + ->getId(); } - return \Magento\Catalog\Model\Category::TREE_ROOT_ID; + return Category::TREE_ROOT_ID; } /** + * Get category name + * * @return string */ public function getCategoryName() { - return $this->getCategory()->getName(); + return $this->getCategory() + ->getName(); } /** + * Get category path + * * @return mixed */ public function getCategoryPath() { if ($this->getCategory()) { - return $this->getCategory()->getPath(); + return $this->getCategory() + ->getPath(); } - return \Magento\Catalog\Model\Category::TREE_ROOT_ID; + return Category::TREE_ROOT_ID; } /** + * Check store root category + * * @return bool */ public function hasStoreRootCategory() @@ -109,15 +125,20 @@ public function hasStoreRootCategory() } /** + * Get store from request + * * @return Store */ public function getStore() { - $storeId = (int)$this->getRequest()->getParam('store'); + $storeId = (int)$this->getRequest() + ->getParam('store'); return $this->_storeManager->getStore($storeId); } /** + * Get root category for tree + * * @param mixed|null $parentNodeCategory * @param int $recursionLevel * @return Node|array|null @@ -130,13 +151,14 @@ public function getRoot($parentNodeCategory = null, $recursionLevel = 3) } $root = $this->_coreRegistry->registry('root'); if ($root === null) { - $storeId = (int)$this->getRequest()->getParam('store'); + $storeId = (int)$this->getRequest() + ->getParam('store'); if ($storeId) { $store = $this->_storeManager->getStore($storeId); $rootId = $store->getRootCategoryId(); } else { - $rootId = \Magento\Catalog\Model\Category::TREE_ROOT_ID; + $rootId = Category::TREE_ROOT_ID; } $tree = $this->_categoryTree->load(null, $recursionLevel); @@ -149,10 +171,11 @@ public function getRoot($parentNodeCategory = null, $recursionLevel = 3) $root = $tree->getNodeById($rootId); - if ($root && $rootId != \Magento\Catalog\Model\Category::TREE_ROOT_ID) { + if ($root) { $root->setIsVisible(true); - } elseif ($root && $root->getId() == \Magento\Catalog\Model\Category::TREE_ROOT_ID) { - $root->setName(__('Root')); + if ($root->getId() == Category::TREE_ROOT_ID) { + $root->setName(__('Root')); + } } $this->_coreRegistry->register('root', $root); @@ -162,22 +185,28 @@ public function getRoot($parentNodeCategory = null, $recursionLevel = 3) } /** + * Get Default Store Id + * * @return int */ protected function _getDefaultStoreId() { - return \Magento\Store\Model\Store::DEFAULT_STORE_ID; + return Store::DEFAULT_STORE_ID; } /** + * Get category collection + * * @return \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection */ public function getCategoryCollection() { - $storeId = $this->getRequest()->getParam('store', $this->_getDefaultStoreId()); + $storeId = $this->getRequest() + ->getParam('store', $this->_getDefaultStoreId()); $collection = $this->getData('category_collection'); if ($collection === null) { - $collection = $this->_categoryFactory->create()->getCollection(); + $collection = $this->_categoryFactory->create() + ->getCollection(); $collection->addAttributeToSelect( 'name' @@ -212,11 +241,11 @@ public function getRootByIds($ids) if (null === $root) { $ids = $this->_categoryTree->getExistingCategoryIdsBySpecifiedIds($ids); $tree = $this->_categoryTree->loadByIds($ids); - $rootId = \Magento\Catalog\Model\Category::TREE_ROOT_ID; + $rootId = Category::TREE_ROOT_ID; $root = $tree->getNodeById($rootId); - if ($root && $rootId != \Magento\Catalog\Model\Category::TREE_ROOT_ID) { + if ($root && $rootId != Category::TREE_ROOT_ID) { $root->setIsVisible(true); - } elseif ($root && $root->getId() == \Magento\Catalog\Model\Category::TREE_ROOT_ID) { + } elseif ($root && $root->getId() == Category::TREE_ROOT_ID) { $root->setName(__('Root')); } @@ -227,6 +256,8 @@ public function getRootByIds($ids) } /** + * Get category node for tree + * * @param mixed $parentNodeCategory * @param int $recursionLevel * @return Node @@ -237,9 +268,9 @@ public function getNode($parentNodeCategory, $recursionLevel = 2) $node = $this->_categoryTree->loadNode($nodeId); $node->loadChildren($recursionLevel); - if ($node && $nodeId != \Magento\Catalog\Model\Category::TREE_ROOT_ID) { + if ($node && $nodeId != Category::TREE_ROOT_ID) { $node->setIsVisible(true); - } elseif ($node && $node->getId() == \Magento\Catalog\Model\Category::TREE_ROOT_ID) { + } elseif ($node && $node->getId() == Category::TREE_ROOT_ID) { $node->setName(__('Root')); } @@ -249,17 +280,26 @@ public function getNode($parentNodeCategory, $recursionLevel = 2) } /** + * Get category save url + * * @param array $args * @return string */ public function getSaveUrl(array $args = []) { - $params = ['_current' => false, '_query' => false, 'store' => $this->getStore()->getId()]; + $params = [ + '_current' => false, + '_query' => false, + 'store' => $this->getStore() + ->getId() + ]; $params = array_merge($params, $args); return $this->getUrl('catalog/*/save', $params); } /** + * Get category edit url + * * @return string */ public function getEditUrl() @@ -279,7 +319,7 @@ public function getRootIds() { $ids = $this->getData('root_ids'); if ($ids === null) { - $ids = [\Magento\Catalog\Model\Category::TREE_ROOT_ID]; + $ids = [Category::TREE_ROOT_ID]; foreach ($this->_storeManager->getGroups() as $store) { $ids[] = $store->getRootCategoryId(); } diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/tree.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/tree.phtml index 9865589556e7b..ba386f89d6ccd 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/tree.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/tree.phtml @@ -302,6 +302,7 @@ } <?php endif;?> //updateContent(url); //commented since ajax requests replaced with http ones to load a category + jQuery('#tree-div').find('.x-tree-node-el').first().remove(); } jQuery(function () { diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/widget/tree.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/widget/tree.phtml index dbe66ef1aecd3..69737b8a37c1c 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/widget/tree.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/widget/tree.phtml @@ -160,7 +160,7 @@ jQuery(function() loader: categoryLoader, enableDD: false, containerScroll: true, - rootVisible: '<?= /* @escapeNotVerified */ $block->getRoot()->getIsVisible() ?>', + rootVisible: false, useAjax: true, currentNodeId: <?= (int) $block->getCategoryId() ?>, addNodeTo: false @@ -177,7 +177,7 @@ jQuery(function() text: 'Psw', draggable: false, id: <?= (int) $block->getRoot()->getId() ?>, - expanded: <?= (int) $block->getIsWasExpanded() ?>, + expanded: true, category_id: <?= (int) $block->getCategoryId() ?> }; From d7dc15aaa2fba967abebb0f21ebd4ef4c4ac5f41 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Mon, 21 Jan 2019 17:01:43 +0200 Subject: [PATCH 0288/1295] MAGETWO-96711: One page Checkout resets Customer data if Product Qty was changed --- .../Checkout/Test/Mftf/Section/CheckoutShippingSection.xml | 2 +- .../Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml index 2a7437c44eccf..2f515ab5f01fe 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml @@ -19,7 +19,7 @@ <element name="postcode" type="input" selector="input[name=postcode]"/> <element name="country" type="select" selector="select[name=country_id]"/> <element name="telephone" type="input" selector="input[name=telephone]"/> - <element name="firstShippingMethod" type="radio" selector="#checkout-shipping-method-load input[type='radio']"/> + <element name="firstShippingMethod" type="radio" selector="#checkout-shipping-method-load input[type='radio']" timeout="30"/> <element name="selectedShippingAddress" type="text" selector=".shipping-address-item.selected-item"/> <element name="newAddressButton" type="button" selector="#checkout-step-shipping button"/> <element name="next" type="button" selector="[data-role='opc-continue']"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml index 7817182525c1e..a24cc28064b5a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml @@ -11,6 +11,7 @@ <test name="StorefrontOnePageCheckoutDataWhenChangeQtyTest"> <annotations> <features value="Checkout"/> + <stories value="Checkout via Guest Checkout"/> <title value="One page Checkout Customer data when changing Product Qty"/> <description value="One page Checkout Customer data when changing Product Qty"/> <severity value="MAJOR"/> @@ -29,7 +30,6 @@ <!--Add product to cart and checkout--> <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="amOnSimpleProductPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> <argument name="productName" value="$createProduct.name$$"/> </actionGroup> @@ -63,11 +63,9 @@ <!--Go to cart page, update qty and proceed to checkout--> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> - <waitForPageLoad stepKey="waitForCartPageLoad"/> <see userInput="Shopping Cart" stepKey="seeCartPageIsOpened"/> <fillField selector="{{CheckoutCartProductSection.productQuantityByName($$createProduct.name$$)}}" userInput="2" stepKey="updateProductQty"/> <click selector="{{CheckoutCartProductSection.updateShoppingCartButton}}" stepKey="clickUpdateShoppingCart"/> - <waitForAjaxLoad stepKey="waitForAjaxLoad"/> <grabValueFrom selector="{{CheckoutCartProductSection.productQuantityByName($$createProduct.name$$)}}" stepKey="grabQty"/> <assertEquals expected="2" actual="$grabQty" stepKey="assertQty"/> <click selector="{{StorefrontCheckoutCartSummarySection.proceedToCheckout}}" stepKey="clickProceedToCheckout"/> From 8c731140c46c9bc718ec47457205aa4371eae57f Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Fri, 18 Jan 2019 13:13:56 +0000 Subject: [PATCH 0289/1295] MAGETWO-97826: Skipped Randomly failing MFTF test AdminCheckingCreditMemoTotalsTest --- .../Sales/Test/Mftf/Test/AdminCheckingCreditMemoTotalsTest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoTotalsTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoTotalsTest.xml index 0c64bf94fc49e..e74d6178dd8ed 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoTotalsTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoTotalsTest.xml @@ -18,6 +18,9 @@ <testCaseId value="MAGETWO-97140"/> <group value="sales"/> <group value="tax"/> + <skip> + <issueId value="MAGETWO-97826"/> + </skip> </annotations> <before> <!--Create category--> From 679008f2769a2a1afb5bc840e53659b318ffb4cd Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Fri, 18 Jan 2019 13:14:32 +0000 Subject: [PATCH 0290/1295] MAGETWO-97825: Skipped Randomly failing test AdminCreateCreditMemoWhenCartRuleDeletedTest --- .../Mftf/Test/AdminCreateCreditMemoWhenCartRuleDeletedTest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWhenCartRuleDeletedTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWhenCartRuleDeletedTest.xml index 310a9f1a46a4f..5386ade5dffd2 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWhenCartRuleDeletedTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWhenCartRuleDeletedTest.xml @@ -16,6 +16,9 @@ <severity value="MAJOR"/> <testCaseId value="MAGETWO-97232"/> <group value="sales"/> + <skip> + <issueId value="MAGETWO-97825"/> + </skip> </annotations> <before> <!--Create product with category--> From 2e9e75c14650d2b75a075f2ef08c43f06d59c008 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Tue, 22 Jan 2019 09:13:49 +0200 Subject: [PATCH 0291/1295] MAGETWO-96918: Wrong behavior of Delete button on backend --- app/code/Magento/Backend/Block/Widget/Form/Container.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Backend/Block/Widget/Form/Container.php b/app/code/Magento/Backend/Block/Widget/Form/Container.php index 8b7babc1bb9b6..7c6e8ec5a9b73 100644 --- a/app/code/Magento/Backend/Block/Widget/Form/Container.php +++ b/app/code/Magento/Backend/Block/Widget/Form/Container.php @@ -83,7 +83,7 @@ protected function _construct() -1 ); - $objId = $this->getRequest()->getParam($this->_objectId); + $objId = (int)$this->getRequest()->getParam($this->_objectId); if (!empty($objId)) { $this->addButton( @@ -155,7 +155,7 @@ public function getBackUrl() */ public function getDeleteUrl() { - return $this->getUrl('*/*/delete', [$this->_objectId => $this->getRequest()->getParam($this->_objectId)]); + return $this->getUrl('*/*/delete', [$this->_objectId => (int)$this->getRequest()->getParam($this->_objectId)]); } /** From e8dc4614d733bc4915aca63fbc5c9a3dcc2c4cf0 Mon Sep 17 00:00:00 2001 From: suryakant <suryakant.makwana@krishtechnolabs.com> Date: Sat, 15 Dec 2018 15:15:40 +0530 Subject: [PATCH 0292/1295] Fixed-19800: Contact us : design improvement. --- .../view/frontend/web/css/source/_module.less | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 app/code/Magento/Contact/view/frontend/web/css/source/_module.less diff --git a/app/code/Magento/Contact/view/frontend/web/css/source/_module.less b/app/code/Magento/Contact/view/frontend/web/css/source/_module.less new file mode 100644 index 0000000000000..d9314523bd2f7 --- /dev/null +++ b/app/code/Magento/Contact/view/frontend/web/css/source/_module.less @@ -0,0 +1,50 @@ +// /** +// * Copyright © Magento, Inc. All rights reserved. +// * See COPYING.txt for license details. +// */ + + + +// +// Common +// _____________________________________________ + +& when (@media-common = true) { + .contact-index-index { + .column:not(.sidebar-main){ + .form.contact { + width: 50%; + float: none; + } + } + .column:not(.sidebar-additional) { + .form.contact { + width: 50%; + float: none; + } + } + } + +} + +// +// Mobile +// _____________________________________________ + +.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { + .contact-index-index { + .column:not(.sidebar-main){ + .form.contact { + width: 100%; + float: none; + } + } + .column:not(.sidebar-additional) { + .form.contact { + width: 100%; + float: none; + } + } + } +} + From 3fcc2eb7f7e24c6c914f7db30de6aacdc5cbe632 Mon Sep 17 00:00:00 2001 From: suryakant <suryakant.makwana@krishtechnolabs.com> Date: Tue, 25 Dec 2018 10:52:55 +0530 Subject: [PATCH 0293/1295] Fixed 19800 Contact us : design improvement --- .../Magento/Contact/view/frontend/web/css/source/_module.less | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Contact/view/frontend/web/css/source/_module.less b/app/code/Magento/Contact/view/frontend/web/css/source/_module.less index d9314523bd2f7..881cad2874bdf 100644 --- a/app/code/Magento/Contact/view/frontend/web/css/source/_module.less +++ b/app/code/Magento/Contact/view/frontend/web/css/source/_module.less @@ -1,4 +1,3 @@ -// /** // * Copyright © Magento, Inc. All rights reserved. // * See COPYING.txt for license details. // */ From c1432e67b89db5c562387e558bae2405fef6f931 Mon Sep 17 00:00:00 2001 From: suryakant <suryakant.makwana@krishtechnolabs.com> Date: Wed, 26 Dec 2018 10:50:55 +0530 Subject: [PATCH 0294/1295] Fixed 19800 Contact us : design improvement --- .../view/frontend/web/css/source/_module.less | 65 ++++++++----------- 1 file changed, 28 insertions(+), 37 deletions(-) diff --git a/app/code/Magento/Contact/view/frontend/web/css/source/_module.less b/app/code/Magento/Contact/view/frontend/web/css/source/_module.less index 881cad2874bdf..160eb73c570eb 100644 --- a/app/code/Magento/Contact/view/frontend/web/css/source/_module.less +++ b/app/code/Magento/Contact/view/frontend/web/css/source/_module.less @@ -1,49 +1,40 @@ // * Copyright © Magento, Inc. All rights reserved. // * See COPYING.txt for license details. -// */ - - - -// // Common // _____________________________________________ - & when (@media-common = true) { - .contact-index-index { - .column:not(.sidebar-main){ - .form.contact { - width: 50%; - float: none; - } - } - .column:not(.sidebar-additional) { - .form.contact { - width: 50%; - float: none; - } - } - } - + .contact-index-index { + .column:not(.sidebar-main){ + .form.contact { + width: 50%; + float: none; + } + } + .column:not(.sidebar-additional) { + .form.contact { + width: 50%; + float: none; + } + } + } } - // // Mobile // _____________________________________________ - .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { - .contact-index-index { - .column:not(.sidebar-main){ - .form.contact { - width: 100%; - float: none; - } - } - .column:not(.sidebar-additional) { - .form.contact { - width: 100%; - float: none; - } - } - } + .contact-index-index { + .column:not(.sidebar-main){ + .form.contact { + width: 100%; + float: none; + } + } + .column:not(.sidebar-additional) { + .form.contact { + width: 100%; + float: none; + } + } + } } From c57fb365eecb98c78acb35d86893e6eb31717c11 Mon Sep 17 00:00:00 2001 From: suryakant <suryakant.makwana@krishtechnolabs.com> Date: Wed, 26 Dec 2018 14:45:29 +0530 Subject: [PATCH 0295/1295] Fixed 19800 Contact us : design improvement --- .../view/frontend/web/css/source/_module.less | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Contact/view/frontend/web/css/source/_module.less b/app/code/Magento/Contact/view/frontend/web/css/source/_module.less index 160eb73c570eb..6d1d77444cc33 100644 --- a/app/code/Magento/Contact/view/frontend/web/css/source/_module.less +++ b/app/code/Magento/Contact/view/frontend/web/css/source/_module.less @@ -1,7 +1,6 @@ -// * Copyright © Magento, Inc. All rights reserved. -// * See COPYING.txt for license details. -// Common -// _____________________________________________ +/** Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + Common */ & when (@media-common = true) { .contact-index-index { .column:not(.sidebar-main){ @@ -18,9 +17,8 @@ } } } -// -// Mobile -// _____________________________________________ + +// Mobile .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { .contact-index-index { .column:not(.sidebar-main){ From 6f3207f5a9b8247a98628e3ae122152c4d632ebd Mon Sep 17 00:00:00 2001 From: suryakant <suryakant.makwana@krishtechnolabs.com> Date: Wed, 26 Dec 2018 15:32:05 +0530 Subject: [PATCH 0296/1295] Fixed 19800 Contact us : design improvement --- .../Magento/Contact/view/frontend/web/css/source/_module.less | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Contact/view/frontend/web/css/source/_module.less b/app/code/Magento/Contact/view/frontend/web/css/source/_module.less index 6d1d77444cc33..6c8c387100f4a 100644 --- a/app/code/Magento/Contact/view/frontend/web/css/source/_module.less +++ b/app/code/Magento/Contact/view/frontend/web/css/source/_module.less @@ -1,6 +1,7 @@ /** Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. - Common */ + Common +*/ & when (@media-common = true) { .contact-index-index { .column:not(.sidebar-main){ From ab99ef71c8bc2931fb2685c7e89135b5f58f970e Mon Sep 17 00:00:00 2001 From: suryakant <suryakant.makwana@krishtechnolabs.com> Date: Wed, 26 Dec 2018 18:08:50 +0530 Subject: [PATCH 0297/1295] Fixed 19800 Contact us : design improvement --- .../Contact/view/frontend/web/css/source/_module.less | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Contact/view/frontend/web/css/source/_module.less b/app/code/Magento/Contact/view/frontend/web/css/source/_module.less index 6c8c387100f4a..e907c61b39e7b 100644 --- a/app/code/Magento/Contact/view/frontend/web/css/source/_module.less +++ b/app/code/Magento/Contact/view/frontend/web/css/source/_module.less @@ -1,6 +1,6 @@ -/** Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - Common +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. */ & when (@media-common = true) { .contact-index-index { From 0641711a740193a73a1fb9e33fb5cf3ceefb068f Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Mon, 31 Dec 2018 15:33:04 +0200 Subject: [PATCH 0298/1295] ENGCOM-3763: Static test fix. --- .../view/frontend/web/css/source/_module.less | 61 ++++++++++--------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/app/code/Magento/Contact/view/frontend/web/css/source/_module.less b/app/code/Magento/Contact/view/frontend/web/css/source/_module.less index e907c61b39e7b..0aaec05aa2afe 100644 --- a/app/code/Magento/Contact/view/frontend/web/css/source/_module.less +++ b/app/code/Magento/Contact/view/frontend/web/css/source/_module.less @@ -2,38 +2,41 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + & when (@media-common = true) { - .contact-index-index { - .column:not(.sidebar-main){ - .form.contact { - width: 50%; - float: none; - } - } - .column:not(.sidebar-additional) { - .form.contact { - width: 50%; - float: none; - } - } - } + .contact-index-index { + .column:not(.sidebar-main) { + .form.contact { + float: none; + width: 50%; + } + } + + .column:not(.sidebar-additional) { + .form.contact { + float: none; + width: 50%; + } + } + } } -// Mobile +// Mobile .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { - .contact-index-index { - .column:not(.sidebar-main){ - .form.contact { - width: 100%; - float: none; - } - } - .column:not(.sidebar-additional) { - .form.contact { - width: 100%; - float: none; - } - } - } + .contact-index-index { + .column:not(.sidebar-main) { + .form.contact { + float: none; + width: 100%; + } + } + + .column:not(.sidebar-additional) { + .form.contact { + float: none; + width: 100%; + } + } + } } From 7b235def709ae6de06a3bf5a4d683cf1bc9ad234 Mon Sep 17 00:00:00 2001 From: suryakant <suryakant.makwana@krishtechnolabs.com> Date: Sat, 15 Dec 2018 11:53:27 +0530 Subject: [PATCH 0299/1295] Fixed-19791: Logo vertical misalignment. --- .../backend/Magento_Backend/web/css/source/module/_menu.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less index a3142b56abd27..42c9b289afdb7 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less @@ -15,9 +15,9 @@ @menu__background-color: @color-very-dark-grayish-orange; -@menu-logo__padding-bottom: 2.2rem; +@menu-logo__padding-bottom: 1.7rem; @menu-logo__outer-size: @menu-logo__padding-top + @menu-logo-img__height + @menu-logo__padding-bottom; -@menu-logo__padding-top: 1.2rem; +@menu-logo__padding-top: 1.7rem; @menu-logo-img__height: 4.1rem; @menu-logo-img__width: 3.5rem; From 4cdc2f0781647eb6d3d06d4adccaebe365361683 Mon Sep 17 00:00:00 2001 From: suryakant <suryakant.makwana@krishtechnolabs.com> Date: Sat, 8 Dec 2018 15:56:56 +0530 Subject: [PATCH 0300/1295] Fixed-Checkbox-Alignement: Checkbox alignment issue. --- lib/web/css/source/lib/_forms.less | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/web/css/source/lib/_forms.less b/lib/web/css/source/lib/_forms.less index b1c7a49da4a7a..5b6ff81bb4a65 100644 --- a/lib/web/css/source/lib/_forms.less +++ b/lib/web/css/source/lib/_forms.less @@ -300,6 +300,8 @@ input[type="checkbox"] { .lib-form-element-choice(@_type: input-checkbox); + position: relative; + top: 2px; } input[type="radio"] { From e6b7c820f2ed1e381fa9a9500e551b71eae62d09 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Tue, 22 Jan 2019 11:14:12 +0200 Subject: [PATCH 0301/1295] MAGETWO-72879: Update product from mini shopping cart doesn't reflect in the shopping cart --- ...nShoppingCartAfterUpdateInMinicartTest.xml | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateQtyInShoppingCartAfterUpdateInMinicartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateQtyInShoppingCartAfterUpdateInMinicartTest.xml index dd46a1587ce1c..b0a2c0bfb7e13 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateQtyInShoppingCartAfterUpdateInMinicartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateQtyInShoppingCartAfterUpdateInMinicartTest.xml @@ -11,8 +11,7 @@ <test name="StorefrontUpdateQtyInShoppingCartAfterUpdateInMinicartTest"> <annotations> <features value="Checkout"/> - <stories - value="MAGETWO-72879: Update product from mini shopping cart doesn't reflect in the shopping cart"/> + <stories value="Checkout via Guest Checkout"/> <title value="Check updating shopping cart while updating items from minicart"/> <description value="Check updating shopping cart while updating items from minicart"/> <severity value="AVERAGE"/> @@ -36,7 +35,6 @@ <!--Open Product Page--> <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="openProductPage"/> - <waitForPageLoad stepKey="waitForProductPage"/> <!--Add product to cart--> <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProductToCart"> <argument name="productName" value="$$createProduct.name$$"/> @@ -45,26 +43,19 @@ <!--Go to Shopping cart--> <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="openShoppingCart"/> <!--Check quantity in Shopping cart--> - <grabValueFrom selector="{{CheckoutCartProductSection.productQuantityByName($$createProduct.name$$)}}" - stepKey="grabQtyFromShoppingCart"/> + <grabValueFrom selector="{{CheckoutCartProductSection.productQuantityByName($$createProduct.name$$)}}" stepKey="grabQtyFromShoppingCart"/> <assertEquals expected="1" actual="$grabQtyFromShoppingCart" stepKey="assertQtyInShoppingCart"/> <!--Open minicart--> <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="openMiniCart"/> - <waitForElementVisible selector="{{StorefrontMinicartSection.itemQuantity($$createProduct.name$$)}}" - stepKey="waitForItemQuantity"/> - <pressKey selector="{{StorefrontMinicartSection.itemQuantity($$createProduct.name$$)}}" - parameterArray="[\Facebook\WebDriver\WebDriverKeys::BACKSPACE]" stepKey="clearQtyField"/> - <fillField selector="{{StorefrontMinicartSection.itemQuantity($$createProduct.name$$)}}" userInput="5" - stepKey="fillQtyField"/> - <waitForElementVisible selector="{{StorefrontMinicartSection.itemQuantityUpdate($$createProduct.name$$)}}" - stepKey="waitForUpdateButton"/> - <click selector="{{StorefrontMinicartSection.itemQuantityUpdate($$createProduct.name$$)}}" - stepKey="clickUpdateButton"/> + <waitForElementVisible selector="{{StorefrontMinicartSection.itemQuantity($$createProduct.name$$)}}" stepKey="waitForItemQuantity"/> + <pressKey selector="{{StorefrontMinicartSection.itemQuantity($$createProduct.name$$)}}" parameterArray="[\Facebook\WebDriver\WebDriverKeys::BACKSPACE]" stepKey="clearQtyField"/> + <fillField selector="{{StorefrontMinicartSection.itemQuantity($$createProduct.name$$)}}" userInput="5" stepKey="fillQtyField"/> + <waitForElementVisible selector="{{StorefrontMinicartSection.itemQuantityUpdate($$createProduct.name$$)}}" stepKey="waitForUpdateButton"/> + <click selector="{{StorefrontMinicartSection.itemQuantityUpdate($$createProduct.name$$)}}" stepKey="clickUpdateButton"/> <waitForAjaxLoad stepKey="waitForAjaxLoad"/> <!--Check quantity in shopping cart after updating--> - <grabValueFrom selector="{{CheckoutCartProductSection.productQuantityByName($$createProduct.name$$)}}" - stepKey="grabQtyFromShoppingCart1"/> + <grabValueFrom selector="{{CheckoutCartProductSection.productQuantityByName($$createProduct.name$$)}}" stepKey="grabQtyFromShoppingCart1"/> <assertEquals expected="5" actual="$grabQtyFromShoppingCart1" stepKey="assertQtyInShoppingCart1"/> </test> </tests> From 9d2828df7917352d8620e4c9be1c3b67fa67cd12 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 22 Jan 2019 11:36:36 +0200 Subject: [PATCH 0302/1295] MAGETWO-96711: One page Checkout resets Customer data if Product Qty was changed --- .../Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml index a24cc28064b5a..093435b8c8f26 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml @@ -55,7 +55,7 @@ <grabValueFrom selector="{{CheckoutShippingSection.telephone}}" stepKey="grabTelephone"/> <!--Select shipping method and finalize checkout--> - <click selector="{{CheckoutShippingSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod"/> + <actionGroup ref="CheckoutSelectFlatRateShippingMethodActionGroup" stepKey="selectFlatRateShippingMethod"/> <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/> <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> From 684d6a58c966fd40f67560bdf90548d3539d3173 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Tue, 22 Jan 2019 12:28:01 +0200 Subject: [PATCH 0303/1295] MAGETWO-94348: The cart rule cannot affect the Bundle product with Fixed Price --- .../BundleProductsOnAdminActionGroup.xml | 10 ++ .../Section/AdminProductFormBundleSection.xml | 8 ++ .../Mftf/Section/CheckoutPaymentSection.xml | 1 + .../Rule/Condition/Product/Subselect.php | 8 +- .../AdminCreateCartPriceRuleActionGroup.xml | 31 +++++ .../Test/Mftf/Data/SalesRuleData.xml | 8 ++ .../AdminCartPriceRulesFormSection.xml | 10 ++ ...inCartRulesAppliedForProductInCartTest.xml | 114 ++++++++++++++++++ 8 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml index 231ae13cab2b1..c600e80f7265f 100644 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml @@ -51,4 +51,14 @@ <actionGroup name="CreateBundleProductForTwoSimpleProductsWithRadioTypeOptions" extends="CreateBundleProductForTwoSimpleProducts"> <selectOption selector="{{AdminProductFormBundleSection.bundleOptionXInputType('0')}}" userInput="Radio Buttons" after="fillOptionTitle" stepKey="selectInputType"/> </actionGroup> + + <actionGroup name="CreateBundleProductForOneSimpleProductsWithRadioTypeOption" extends="CreateBundleProductForTwoSimpleProducts"> + <remove keyForRemoval="clickOnFiltersButton2"/> + <remove keyForRemoval="clearFilters2"/> + <remove keyForRemoval="fillNameFilter2"/> + <remove keyForRemoval="applyFilters2"/> + <remove keyForRemoval="selectSecondSimple"/> + <remove keyForRemoval="fillDefaultQuantityForSecondProduct"/> + <selectOption selector="{{AdminProductFormBundleSection.bundleOptionXInputType('0')}}" userInput="Radio Buttons" after="fillOptionTitle" stepKey="selectInputType"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml b/app/code/Magento/Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml index a843b942174d5..84911b12e40ef 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml @@ -16,5 +16,13 @@ <element name="addProductsToOption" type="button" selector="[data-index='modal_set']" timeout="30"/> <element name="bundleOptionXProductYQuantity" type="input" selector="[name='bundle_options[bundle_options][{{x}}][bundle_selections][{{y}}][selection_qty]']" parameterized="true"/> <element name="bundledItems" type="block" selector="[data-index=bundle-items]"/> + <element name="dynamicPrice" type="button" selector="//div[@data-index='price_type']//div[@data-role='switcher']" timeout="30"/> + <element name="priceField" type="input" selector="//div[@data-index='price']//input"/> + <element name="categoriesDropDown" type="block" selector="//div[@data-index='category_ids']//div[@class='admin__field-control']" timeout="30"/> + <element name="searchForCategory" type="input" selector="div.action-menu._active > div.admin__action-multiselect-search-wrap input" timeout="30"/> + <element name="selectCategory" type="multiselect" selector="//div[@class='action-menu _active']//label[@class='admin__action-multiselect-label']"/> + <element name="categoriesLabel" type="text" selector="//div[@class='action-menu _active']//button[@data-action='close-advanced-select']"/> + <element name="bundlePriceType" type="select" selector="bundle_options[bundle_options][0][bundle_selections][0][selection_price_type]"/> + <element name="bundlePriceValue" type="input" selector="bundle_options[bundle_options][0][bundle_selections][0][selection_price_value]"/> </section> </sections> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml index e0e9ec638a763..e7755aafa2ca7 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml @@ -42,5 +42,6 @@ <element name="billingAddressSameAsShipping" type="checkbox" selector=".payment-method._active [name='billing-address-same-as-shipping']"/> <element name="orderSummaryTotal" type="text" selector="tr.grand.totals span.price" /> <element name="checkPaymentMethodByName" type="radio" selector="//div[@id='checkout-payment-method-load']//div[contains(., '{{paymentName}}')]/..//input[@type='radio']" parameterized="true"/> + <element name="orderSummaryShippingTotal" type="text" selector="//tr[@class='totals shipping excl']//span[@class='price']" /> </section> </sections> diff --git a/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Subselect.php b/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Subselect.php index 1e8fbf43ec3bc..b3a44fcc56011 100644 --- a/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Subselect.php +++ b/app/code/Magento/SalesRule/Model/Rule/Condition/Product/Subselect.php @@ -5,6 +5,9 @@ */ namespace Magento\SalesRule\Model\Rule\Condition\Product; +/** + * Subselect conditions for product. + */ class Subselect extends \Magento\SalesRule\Model\Rule\Condition\Product\Combine { /** @@ -161,9 +164,12 @@ public function validate(\Magento\Framework\Model\AbstractModel $model) } } if ($hasValidChild || parent::validate($item)) { - $total += (($hasValidChild && $useChildrenTotal) ? $childrenAttrTotal : $item->getData($attr)); + $total += ($hasValidChild && $useChildrenTotal) + ? $childrenAttrTotal * $item->getQty() + : $item->getData($attr); } } + return $this->validateAttribute($total); } } diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionGroup.xml index 26a71e4167ffa..d169f50090698 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionGroup.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionGroup.xml @@ -23,4 +23,35 @@ <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> <see selector="{{AdminMessagesSection.success}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> </actionGroup> + + <actionGroup name="AdminCreateCartPriceRuleWithConditions" extends="AdminCreateCartPriceRuleActionGroup"> + <arguments> + <argument name="condition1" type="string" defaultValue="Products subselection" /> + <argument name="condition2" type="string" defaultValue="Category" /> + <argument name="ruleToChange1" type="string" defaultValue="is" /> + <argument name="rule1" type="string" defaultValue="equals or greater than" /> + <argument name="ruleToChange2" type="string" defaultValue="..." /> + <argument name="rule2" type="string" defaultValue="2" /> + <argument name="categoryName" type="string" defaultValue="_defaultCategory.name" /> + </arguments> + <remove keyForRemoval="fillDiscountAmount" /> + <!--Go to Conditions section--> + <click selector="{{AdminCartPriceRulesFormSection.conditionsHeader}}" after="selectActionType" stepKey="openConditionsSection"/> + <click selector="{{AdminCartPriceRulesFormSection.addCondition('1')}}" after="openConditionsSection" stepKey="addFirstCondition"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.ruleCondition('1')}}" userInput="{{condition1}}" after="addFirstCondition" stepKey="selectRule"/> + <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.ruleParameter(ruleToChange1)}}" after="selectRule" stepKey="waitForFirstRuleElement"/> + <click selector="{{AdminCartPriceRulesFormSection.ruleParameter(ruleToChange1)}}" after="waitForFirstRuleElement" stepKey="clickToChangeRule"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.ruleParameterSelect('1--1')}}" userInput="{{rule1}}" after="clickToChangeRule" stepKey="selectRule1"/> + <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.ruleParameter(ruleToChange2)}}" after="selectRule1" stepKey="waitForSecondRuleElement"/> + <click selector="{{AdminCartPriceRulesFormSection.ruleParameter(ruleToChange2)}}" after="waitForSecondRuleElement" stepKey="clickToChangeRule1"/> + <fillField selector="{{AdminCartPriceRulesFormSection.ruleParameterInput('1--1')}}" userInput="{{rule2}}" after="clickToChangeRule1" stepKey="fillRule"/> + <click selector="{{AdminCartPriceRulesFormSection.addCondition('1--1')}}" after="fillRule" stepKey="addSecondCondition"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.ruleCondition('1--1')}}" userInput="{{condition2}}" after="addSecondCondition" stepKey="selectSecondCondition"/> + <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.ruleParameter(ruleToChange2)}}" after="selectSecondCondition" stepKey="waitForThirdRuleElement"/> + <click selector="{{AdminCartPriceRulesFormSection.ruleParameter(ruleToChange2)}}" after="waitForThirdRuleElement" stepKey="addThirdCondition"/> + <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.openChooser('1--1--1')}}" after="addThirdCondition" stepKey="waitForForthRuleElement"/> + <click selector="{{AdminCartPriceRulesFormSection.openChooser('1--1--1')}}" after="waitForForthRuleElement" stepKey="openChooser"/> + <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.categoryCheckbox(categoryName)}}" after="openChooser" stepKey="waitForCategoryVisible"/> + <checkOption selector="{{AdminCartPriceRulesFormSection.categoryCheckbox(categoryName)}}" after="waitForCategoryVisible" stepKey="checkCategoryName"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml index 4f3dd3b374b7d..ab8cafc4856a7 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml @@ -168,4 +168,12 @@ <data key="uses_per_coupon">10</data> <data key="simple_free_shipping">1</data> </entity> + + <entity name="PriceRuleWithCondition" type="SalesRule"> + <data key="name" unique="suffix">SalesRule</data> + <data key="websites">Main Website</data> + <data key="customerGroups">'NOT LOGGED IN', 'General', 'Wholesale', 'Retailer'</data> + <data key="apply">Fixed amount discount for whole cart</data> + <data key="discountAmount">50</data> + </entity> </entities> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml index afe4f8a6ae980..2a783a98d0c83 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml @@ -24,6 +24,16 @@ <element name="userPerCustomer" type="input" selector="//input[@name='uses_per_customer']"/> <element name="priority" type="input" selector="//*[@name='sort_order']"/> + <!-- Conditions sub-form --> + <element name="conditionsHeader" type="button" selector="div[data-index='conditions']" timeout="30"/> + <element name="addCondition" type="button" selector="//*[@id='conditions__{{arg}}__children']//span" parameterized="true"/> + <element name="ruleCondition" type="select" selector="rule[conditions][{{arg}}][new_child]" parameterized="true"/> + <element name="ruleParameter" type="text" selector="//span[@class='rule-param']/a[contains(text(), '{{arg}}')]" parameterized="true"/> + <element name="ruleParameterSelect" type="select" selector="rule[conditions][{{arg}}][operator]" parameterized="true"/> + <element name="ruleParameterInput" type="input" selector="rule[conditions][{{arg}}][value]" parameterized="true"/> + <element name="openChooser" type="button" selector="//label[@for='conditions__{{arg}}__value']" parameterized="true"/> + <element name="categoryCheckbox" type="checkbox" selector="//span[contains(text(), '{{arg}}')]/parent::a/preceding-sibling::input[@type='checkbox']" parameterized="true"/> + <!-- Actions sub-form --> <element name="actionsHeader" type="button" selector="div[data-index='actions']" timeout="30"/> <element name="actionsHeaderOpen" type="button" selector="div[data-index='actions'] div[data-state-collapsible='open']" timeout="30"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml new file mode 100644 index 0000000000000..abfd3da9d4cd0 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml @@ -0,0 +1,114 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCartRulesAppliedForProductInCartTest"> + <annotations> + <features value="SalesRule"/> + <stories value="The cart rule cannot effect the cart"/> + <title value="Check that cart rules applied for product in cart"/> + <description value="Check that cart rules applied for product in cart"/> + <severity value="MAJOR"/> + <testCaseId value="MC-13629"/> + <useCaseId value="MAGETWO-94348"/> + <group value="SalesRule"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!--Create category and product--> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct"> + <field key="price">200</field> + <field key="quantity">500</field> + <requiredEntity createDataKey="createPreReqCategory"/> + </createData> + </before> + <after> + <!--Delete created data--> + <deleteData createDataKey="createPreReqCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> + + <actionGroup ref="DeleteProductOnProductsGridPageByName" stepKey="deleteProductOnProductsGridPageByName"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + + <actionGroup ref="DeleteCartPriceRuleByName" stepKey="deleteCartPriceRule"> + <argument name="ruleName" value="{{PriceRuleWithCondition.name}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Start creating a bundle product--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> + <waitForPageLoad stepKey="waitForProductList"/> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + + <!--Off dynamic price and set value--> + <click selector="{{AdminProductFormBundleSection.dynamicPrice}}" stepKey="offDynamicPrice"/> + <fillField selector="{{AdminProductFormBundleSection.priceField}}" userInput="0" stepKey="setProductPrice"/> + + <!-- Add category to product --> + <click selector="{{AdminProductFormBundleSection.categoriesDropDown}}" stepKey="dropDownCategories"/> + <fillField selector="{{AdminProductFormBundleSection.searchForCategory}}" userInput="$$createPreReqCategory.name$$" stepKey="searchForCategory"/> + <click selector="{{AdminProductFormBundleSection.selectCategory}}" stepKey="selectCategory"/> + <click selector="{{AdminProductFormBundleSection.categoriesLabel}}" stepKey="clickOnCategoriesLabelToCloseOptions"/> + + <!-- Add option, a "Radio Buttons" type option, with one product and set fixed price 200--> + <actionGroup ref="CreateBundleProductForOneSimpleProductsWithRadioTypeOption" stepKey="createBundleProductWithRadioTypeOption"> + <argument name="bundleProduct" value="BundleProduct"/> + <argument name="simpleProductFirst" value="$$simpleProduct$$"/> + <argument name="simpleProductSecond" value=""/> + </actionGroup> + <selectOption selector="{{AdminProductFormBundleSection.bundlePriceType}}" userInput="Fixed" stepKey="selectPriceType"/> + <fillField selector="{{AdminProductFormBundleSection.bundlePriceValue}}" userInput="200" stepKey="fillPriceValue"/> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + + <!--Create cart price rule--> + <actionGroup ref="AdminCreateCartPriceRuleWithConditions" stepKey="createRule"> + <argument name="rule" value="PriceRuleWithCondition"/> + <argument name="condition1" value="Products subselection"/> + <argument name="condition2" value="Category"/> + <argument name="ruleToChange1" value="is"/> + <argument name="rule1" value="equals or greater than"/> + <argument name="ruleToChange2" value="..."/> + <argument name="rule2" value="2"/> + <argument name="categoryName" value="{{_defaultCategory.name}}"/> + </actionGroup> + + <!--Go to Storefront and add product to cart and checkout from cart--> + <amOnPage url="/$$simpleProduct.name$$.html" stepKey="goToProduct"/> + <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="2" stepKey="setQuantity"/> + <actionGroup ref="StorefrontAddToCartCustomOptionsProductPageActionGroup" stepKey="addProductToCard"> + <argument name="productName" value="$$simpleProduct.name$$"/> + </actionGroup> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> + <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShipping"/> + + <!--Check totals--> + <grabTextFrom selector="{{CheckoutPaymentSection.orderSummarySubtotal}}" stepKey="grabSubtotal"/> + <grabTextFrom selector="{{CheckoutPaymentSection.orderSummaryShippingTotal}}" stepKey="grabShippingTotal"/> + <grabTextFrom selector="{{CheckoutPaymentSection.orderSummaryTotal}}" stepKey="grabTotal"/> + <assertEquals stepKey="assertSubtotal"> + <expectedResult type="string">$400.00</expectedResult> + <actualResult type="variable">$grabSubtotal</actualResult> + </assertEquals> + <assertEquals stepKey="assertShippingTotal"> + <expectedResult type="string">$10.00</expectedResult> + <actualResult type="variable">$grabShippingTotal</actualResult> + </assertEquals> + <assertEquals stepKey="assertTotal"> + <expectedResult type="string">$410.00</expectedResult> + <actualResult type="variable">$grabTotal</actualResult> + </assertEquals> + </test> +</tests> From 9e8173ff4930a9760007e8e37daadaeddafe6938 Mon Sep 17 00:00:00 2001 From: Parag Chavare <parag@2jcommerce.in> Date: Tue, 22 Jan 2019 18:42:59 +0530 Subject: [PATCH 0304/1295] Fixed-Product-page-tabbing-content-misalignment-in-mobile-view-2-2 :: Product Page tabbing Content Misaligned in mobile view --- .../Magento/blank/web/css/source/_sections.less | 1 + .../Magento/luma/web/css/source/_sections.less | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/app/design/frontend/Magento/blank/web/css/source/_sections.less b/app/design/frontend/Magento/blank/web/css/source/_sections.less index f0a3518c92a8b..90e82a114d10c 100644 --- a/app/design/frontend/Magento/blank/web/css/source/_sections.less +++ b/app/design/frontend/Magento/blank/web/css/source/_sections.less @@ -34,5 +34,6 @@ .data.item { display: block; } + } } diff --git a/app/design/frontend/Magento/luma/web/css/source/_sections.less b/app/design/frontend/Magento/luma/web/css/source/_sections.less index 73665fd22da23..38b936c83cb95 100644 --- a/app/design/frontend/Magento/luma/web/css/source/_sections.less +++ b/app/design/frontend/Magento/luma/web/css/source/_sections.less @@ -75,3 +75,16 @@ } } } + +.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { + .product.data.items{ + .item.title { + >.switch{ + padding: 1px 15px 1px; + } + } + >.item.content{ + padding: 10px 15px 30px; + } + } +} From 0b0dcfb9c7a943045d7724ee1832da2cbd3e04ff Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <svizev.igor@gmail.com> Date: Tue, 22 Jan 2019 16:25:51 +0200 Subject: [PATCH 0305/1295] magento/magento2#19098 2.2.6 Use batches and direct queries to fix sales address upgrade Revert not needed changes --- app/code/Magento/Sales/Setup/UpgradeData.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/code/Magento/Sales/Setup/UpgradeData.php b/app/code/Magento/Sales/Setup/UpgradeData.php index 83f239546960e..b35632d6e12d2 100644 --- a/app/code/Magento/Sales/Setup/UpgradeData.php +++ b/app/code/Magento/Sales/Setup/UpgradeData.php @@ -14,7 +14,10 @@ use Magento\Framework\Setup\ModuleContextInterface; use Magento\Framework\Setup\ModuleDataSetupInterface; use Magento\Framework\Setup\UpgradeDataInterface; +use Magento\Quote\Model\QuoteFactory; use Magento\Sales\Model\Order\Address; +use Magento\Sales\Model\OrderFactory; +use Magento\Sales\Model\ResourceModel\Order\Address\CollectionFactory as AddressCollectionFactory; /** * Data upgrade script @@ -49,12 +52,18 @@ class UpgradeData implements UpgradeDataInterface * @param SalesSetupFactory $salesSetupFactory * @param Config $eavConfig * @param AggregatedFieldDataConverter $aggregatedFieldConverter + * @param AddressCollectionFactory $addressCollFactory + * @param OrderFactory $orderFactory + * @param QuoteFactory $quoteFactory * @param State $state */ public function __construct( SalesSetupFactory $salesSetupFactory, Config $eavConfig, AggregatedFieldDataConverter $aggregatedFieldConverter, + AddressCollectionFactory $addressCollFactory, + OrderFactory $orderFactory, + QuoteFactory $quoteFactory, State $state ) { $this->salesSetupFactory = $salesSetupFactory; From ed497dc7fe6d704795ffbe01e78ae3015f782d7a Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Tue, 22 Jan 2019 14:33:29 +0000 Subject: [PATCH 0306/1295] magento/magento2#18362: Fixed static tests --- .../Magento/Framework/Webapi/Rest/Response/Renderer/Xml.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/internal/Magento/Framework/Webapi/Rest/Response/Renderer/Xml.php b/lib/internal/Magento/Framework/Webapi/Rest/Response/Renderer/Xml.php index 7a4064cf70355..f25cd219e3eae 100644 --- a/lib/internal/Magento/Framework/Webapi/Rest/Response/Renderer/Xml.php +++ b/lib/internal/Magento/Framework/Webapi/Rest/Response/Renderer/Xml.php @@ -7,6 +7,9 @@ */ namespace Magento\Framework\Webapi\Rest\Response\Renderer; +/** + * Renders response data in Xml format. + */ class Xml implements \Magento\Framework\Webapi\Rest\Response\RendererInterface { /** From 0a8c5aec6a75afed12f060e9ec49e2205c2790a3 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Tue, 22 Jan 2019 17:27:34 +0200 Subject: [PATCH 0307/1295] MAGETWO-96444: Impossible to sort Root Categories via drag'n'drop --- .../templates/catalog/category/checkboxes/tree.phtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/checkboxes/tree.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/checkboxes/tree.phtml index 00a1580923a7b..ee67acd0ebd46 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/checkboxes/tree.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/checkboxes/tree.phtml @@ -20,7 +20,7 @@ "categoryCheckboxTree": { "dataUrl": "<?= $block->escapeUrl($block->getLoadTreeUrl()) ?>", "divId": "<?= /* @noEscape */ $divId ?>", - "rootVisible": <?= /* @noEscape */ $block->getRoot()->getIsVisible() ? 'true' : 'false' ?>, + "rootVisible": false, "useAjax": <?= $block->escapeHtml($block->getUseAjax()) ?>, "currentNodeId": <?= (int)$block->getCategoryId() ?>, "jsFormObject": "<?= /* @noEscape */ $block->getJsFormObject() ?>", @@ -28,7 +28,7 @@ "checked": "<?= $block->escapeHtml($block->getRoot()->getChecked()) ?>", "allowdDrop": <?= /* @noEscape */ $block->getRoot()->getIsVisible() ? 'true' : 'false' ?>, "rootId": <?= (int)$block->getRoot()->getId() ?>, - "expanded": <?= (int)$block->getIsWasExpanded() ?>, + "expanded": true, "categoryId": <?= (int)$block->getCategoryId() ?>, "treeJson": <?= /* @noEscape */ $block->getTreeJson() ?> } From b6d9feddee38476d0cff2fccb67052f9b20f570c Mon Sep 17 00:00:00 2001 From: amol 2jcommerce <amol@2jcommerce.in> Date: Wed, 23 Jan 2019 11:52:36 +0530 Subject: [PATCH 0308/1295] Fixed-checkout-tooltip-dropdown --- .../css/source/module/checkout/_tooltip.less | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_tooltip.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_tooltip.less index bf264a98f33b8..e42d8eb7a8dd7 100644 --- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_tooltip.less +++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_tooltip.less @@ -147,3 +147,30 @@ } } } + +// +// Tablet +// _____________________________________________ +@media only screen and (max-width: @screen__m) { + .field-tooltip .field-tooltip-content { + right: -10px; + top: 40px; + left: auto; + } + .field-tooltip .field-tooltip-content::before, .field-tooltip .field-tooltip-content::after { + border: 10px solid transparent; + height: 0; + width: 0; + margin-top: -21px; + right: 10px; + left: auto; + top: 0; + } + .field-tooltip .field-tooltip-content::before { + border-bottom-color: #666; + } + .field-tooltip .field-tooltip-content::after { + border-bottom-color: #f4f4f4; + top: 1px; + } +} \ No newline at end of file From e2a3def24331f105e89106c51c6dee66ac4f113e Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Wed, 23 Jan 2019 12:35:40 +0200 Subject: [PATCH 0309/1295] MAGETWO-97441: Attribute output --- .../catalog/product/composite/fieldset/configurable.phtml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/composite/fieldset/configurable.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/composite/fieldset/configurable.phtml index a8712cdc183de..78fa8b1c68b7a 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/composite/fieldset/configurable.phtml +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/composite/fieldset/configurable.phtml @@ -20,9 +20,8 @@ <div class="product-options"> <div class="field admin__field _required required"> <?php foreach ($_attributes as $_attribute): ?> - <label class="label admin__field-label"><?php - /* @escapeNotVerified */ echo $_attribute->getProductAttribute() - ->getStoreLabel($_product->getStoreId()); + <label class="label admin__field-label"><?= + $block->escapeHtml($_attribute->getProductAttribute()->getStoreLabel($_product->getStoreId())) ?></label> <div class="control admin__field-control <?php if ($_attribute->getDecoratedIsLast()): From 4c4ff037f964c27b3376b2c7b8ed02c1cea48f28 Mon Sep 17 00:00:00 2001 From: Tiago Sampaio <tiago@tiagosampaio.com> Date: Wed, 2 Jan 2019 12:58:40 -0200 Subject: [PATCH 0310/1295] Added constants to unit codes to make it easier to reuse it if necessary. --- .../Directory/Model/Config/Source/WeightUnit.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Directory/Model/Config/Source/WeightUnit.php b/app/code/Magento/Directory/Model/Config/Source/WeightUnit.php index 4e2758b362a43..6c0c50d620146 100644 --- a/app/code/Magento/Directory/Model/Config/Source/WeightUnit.php +++ b/app/code/Magento/Directory/Model/Config/Source/WeightUnit.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Directory\Model\Config\Source; /** @@ -13,11 +14,24 @@ */ class WeightUnit implements \Magento\Framework\Option\ArrayInterface { + /** + * @var string + */ + const CODE_LBS = 'lbs'; + + /** + * @var string + */ + const CODE_KGS = 'kgs'; + /** * {@inheritdoc} */ public function toOptionArray() { - return [['value' => 'lbs', 'label' => __('lbs')], ['value' => 'kgs', 'label' => __('kgs')]]; + return [ + ['value' => self::CODE_LBS, 'label' => __('lbs')], + ['value' => self::CODE_KGS, 'label' => __('kgs')] + ]; } } From efaa4f379e569f348fc7981b87017f5e637545b0 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Thu, 3 Jan 2019 13:09:11 +0200 Subject: [PATCH 0311/1295] ENGCOM-3774: Static test fix. --- app/code/Magento/Directory/Model/Config/Source/WeightUnit.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Directory/Model/Config/Source/WeightUnit.php b/app/code/Magento/Directory/Model/Config/Source/WeightUnit.php index 6c0c50d620146..97d22633af03c 100644 --- a/app/code/Magento/Directory/Model/Config/Source/WeightUnit.php +++ b/app/code/Magento/Directory/Model/Config/Source/WeightUnit.php @@ -25,7 +25,7 @@ class WeightUnit implements \Magento\Framework\Option\ArrayInterface const CODE_KGS = 'kgs'; /** - * {@inheritdoc} + * @inheritdoc */ public function toOptionArray() { From c9000f42e2a18f079ad4197585252c4760d65e84 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Wed, 23 Jan 2019 13:14:49 +0200 Subject: [PATCH 0312/1295] MAGETWO-96815: There is no option to remove the Store Credit from the payment page of checkout --- .../ActionGroup/OpenEditCustomerFromAdminActionGroup.xml | 7 +++---- .../Mftf/ActionGroup/AdminDataGridFilterActionGroup.xml | 2 ++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml index 3d6e0fb54b054..79b876ff6ecf7 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml @@ -6,13 +6,12 @@ */ --> <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="OpenEditCustomerFromAdminActionGroup"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="OpenEditCustomerFromAdminActionGroup" extends="clearFiltersAdminDataGrid"> <arguments> <argument name="customer"/> </arguments> - <amOnPage url="{{AdminCustomerPage.url}}" stepKey="navigateToCustomers"/> - <waitForPageLoad stepKey="waitForPageLoad1" /> + <amOnPage url="{{AdminCustomerPage.url}}" before="waitForPageLoad" stepKey="navigateToCustomers"/> <click selector="{{AdminCustomerFiltersSection.filtersButton}}" stepKey="openFilter"/> <fillField userInput="{{customer.email}}" selector="{{AdminCustomerFiltersSection.emailInput}}" stepKey="filterEmail"/> <click selector="{{AdminCustomerFiltersSection.apply}}" stepKey="applyFilter"/> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridFilterActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridFilterActionGroup.xml index c1379b9fce575..85672505ac707 100644 --- a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridFilterActionGroup.xml +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridFilterActionGroup.xml @@ -31,6 +31,8 @@ <actionGroup name="clearFiltersAdminDataGrid"> <waitForPageLoad stepKey="waitForPageLoad"/> <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFilters"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> + <waitForElementNotVisible selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="waitClearFiltersDisappear"/> </actionGroup> <actionGroup name="AdminGridFilterSearchResultsByInput"> From 605a52be40c8f2258b4219b91b266c4d594847b2 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Wed, 23 Jan 2019 12:07:35 +0200 Subject: [PATCH 0313/1295] MAGETWO-96930: Terms & Conditions logic fix - Change checkbox behavior - Unit tests fix --- .../Model/AgreementsConfigProvider.php | 7 +-- .../Model/AgreementsConfigProviderTest.php | 52 ++++++++++++++----- 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/CheckoutAgreements/Model/AgreementsConfigProvider.php b/app/code/Magento/CheckoutAgreements/Model/AgreementsConfigProvider.php index 331307292e40a..61326207d24ec 100644 --- a/app/code/Magento/CheckoutAgreements/Model/AgreementsConfigProvider.php +++ b/app/code/Magento/CheckoutAgreements/Model/AgreementsConfigProvider.php @@ -45,17 +45,18 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function getConfig() { $agreements = []; $agreements['checkoutAgreements'] = $this->getAgreementsConfig(); + return $agreements; } /** - * Returns agreements config + * Returns agreements config. * * @return array */ @@ -75,7 +76,7 @@ protected function getAgreementsConfig() 'content' => $agreement->getIsHtml() ? $agreement->getContent() : nl2br($this->escaper->escapeHtml($agreement->getContent())), - 'checkboxText' => $agreement->getCheckboxText(), + 'checkboxText' => $this->escaper->escapeHtml($agreement->getCheckboxText()), 'mode' => $agreement->getMode(), 'agreementId' => $agreement->getAgreementId() ]; diff --git a/app/code/Magento/CheckoutAgreements/Test/Unit/Model/AgreementsConfigProviderTest.php b/app/code/Magento/CheckoutAgreements/Test/Unit/Model/AgreementsConfigProviderTest.php index eae347e27aa11..cacc1c1226cff 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Unit/Model/AgreementsConfigProviderTest.php +++ b/app/code/Magento/CheckoutAgreements/Test/Unit/Model/AgreementsConfigProviderTest.php @@ -8,6 +8,9 @@ use Magento\CheckoutAgreements\Model\AgreementsProvider; use Magento\Store\Model\ScopeInterface; +/** + * Tests for AgreementsConfigProvider. + */ class AgreementsConfigProviderTest extends \PHPUnit\Framework\TestCase { /** @@ -30,6 +33,9 @@ class AgreementsConfigProviderTest extends \PHPUnit\Framework\TestCase */ protected $escaperMock; + /** + * @inheritdoc + */ protected function setUp() { $this->scopeConfigMock = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); @@ -45,10 +51,16 @@ protected function setUp() ); } + /** + * Test for getConfig if content is HTML. + * + * @return void + */ public function testGetConfigIfContentIsHtml() { $content = 'content'; $checkboxText = 'checkbox_text'; + $escapedCheckboxText = 'escaped_checkbox_text'; $mode = \Magento\CheckoutAgreements\Model\AgreementModeOptions::MODE_AUTO; $agreementId = 100; $expectedResult = [ @@ -57,12 +69,12 @@ public function testGetConfigIfContentIsHtml() 'agreements' => [ [ 'content' => $content, - 'checkboxText' => $checkboxText, + 'checkboxText' => $escapedCheckboxText, 'mode' => $mode, - 'agreementId' => $agreementId - ] - ] - ] + 'agreementId' => $agreementId, + ], + ], + ], ]; $this->scopeConfigMock->expects($this->once()) @@ -71,8 +83,12 @@ public function testGetConfigIfContentIsHtml() ->willReturn(true); $agreement = $this->createMock(\Magento\CheckoutAgreements\Api\Data\AgreementInterface::class); - $this->agreementsRepositoryMock->expects($this->any())->method('getList')->willReturn([$agreement]); + $this->agreementsRepositoryMock->expects($this->once())->method('getList')->willReturn([$agreement]); + $this->escaperMock->expects($this->once()) + ->method('escapeHtml') + ->with($checkboxText) + ->willReturn($escapedCheckboxText); $agreement->expects($this->once())->method('getIsHtml')->willReturn(true); $agreement->expects($this->once())->method('getContent')->willReturn($content); $agreement->expects($this->once())->method('getCheckboxText')->willReturn($checkboxText); @@ -82,11 +98,17 @@ public function testGetConfigIfContentIsHtml() $this->assertEquals($expectedResult, $this->model->getConfig()); } + /** + * Test for getConfig if content is not HTML. + * + * @return void + */ public function testGetConfigIfContentIsNotHtml() { $content = 'content'; $escapedContent = 'escaped_content'; $checkboxText = 'checkbox_text'; + $escapedCheckboxText = 'escaped_checkbox_text'; $mode = \Magento\CheckoutAgreements\Model\AgreementModeOptions::MODE_AUTO; $agreementId = 100; $expectedResult = [ @@ -95,12 +117,12 @@ public function testGetConfigIfContentIsNotHtml() 'agreements' => [ [ 'content' => $escapedContent, - 'checkboxText' => $checkboxText, + 'checkboxText' => $escapedCheckboxText, 'mode' => $mode, - 'agreementId' => $agreementId - ] - ] - ] + 'agreementId' => $agreementId, + ], + ], + ], ]; $this->scopeConfigMock->expects($this->once()) @@ -109,9 +131,13 @@ public function testGetConfigIfContentIsNotHtml() ->willReturn(true); $agreement = $this->createMock(\Magento\CheckoutAgreements\Api\Data\AgreementInterface::class); - $this->agreementsRepositoryMock->expects($this->any())->method('getList')->willReturn([$agreement]); - $this->escaperMock->expects($this->once())->method('escapeHtml')->with($content)->willReturn($escapedContent); + $this->agreementsRepositoryMock->expects($this->once())->method('getList')->willReturn([$agreement]); + $this->escaperMock->expects($this->at(0))->method('escapeHtml')->with($content)->willReturn($escapedContent); + $this->escaperMock->expects($this->at(1)) + ->method('escapeHtml') + ->with($checkboxText) + ->willReturn($escapedCheckboxText); $agreement->expects($this->once())->method('getIsHtml')->willReturn(false); $agreement->expects($this->once())->method('getContent')->willReturn($content); $agreement->expects($this->once())->method('getCheckboxText')->willReturn($checkboxText); From d46a076cd1d8ae2c0dc67f35d6bc6c9458576f3b Mon Sep 17 00:00:00 2001 From: Wojtek Naruniec <wojtek@naruniec.me> Date: Thu, 20 Dec 2018 11:46:13 +0100 Subject: [PATCH 0314/1295] Fix negative credit memo bug caused by credit memo discount total calculator --- app/code/Magento/Sales/Model/Order/CreditmemoFactory.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Sales/Model/Order/CreditmemoFactory.php b/app/code/Magento/Sales/Model/Order/CreditmemoFactory.php index 50523015d87eb..1f471e906ff5e 100644 --- a/app/code/Magento/Sales/Model/Order/CreditmemoFactory.php +++ b/app/code/Magento/Sales/Model/Order/CreditmemoFactory.php @@ -200,6 +200,7 @@ protected function initData($creditmemo, $data) { if (isset($data['shipping_amount'])) { $creditmemo->setBaseShippingAmount((double)$data['shipping_amount']); + $creditmemo->setBaseShippingInclTax((double)$data['shipping_amount']); } if (isset($data['adjustment_positive'])) { $creditmemo->setAdjustmentPositive($data['adjustment_positive']); From c830d613b1b678e3b0370d61ae7b0978a223e722 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Fri, 21 Dec 2018 16:57:47 +0200 Subject: [PATCH 0315/1295] ENGCOM-3720: Static test fix. --- app/code/Magento/Sales/Model/Order/CreditmemoFactory.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Sales/Model/Order/CreditmemoFactory.php b/app/code/Magento/Sales/Model/Order/CreditmemoFactory.php index 1f471e906ff5e..da41f99a65c83 100644 --- a/app/code/Magento/Sales/Model/Order/CreditmemoFactory.php +++ b/app/code/Magento/Sales/Model/Order/CreditmemoFactory.php @@ -211,6 +211,8 @@ protected function initData($creditmemo, $data) } /** + * Calculate product options. + * * @param Item $orderItem * @param int $parentQty * @return int From ce11dd11fbb182aa8687e2c1128924076a9a82d9 Mon Sep 17 00:00:00 2001 From: Vinoth Kumar <vinogcs@users.noreply.github.com> Date: Wed, 19 Dec 2018 14:37:50 +0530 Subject: [PATCH 0316/1295] Added required error message. If any JSON error occurs user will be be shown error message if the app mode is not in Production. --- .../Framework/Serialize/Serializer/Json.php | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Serialize/Serializer/Json.php b/lib/internal/Magento/Framework/Serialize/Serializer/Json.php index e352d0c2d7124..dc56ee8867589 100644 --- a/lib/internal/Magento/Framework/Serialize/Serializer/Json.php +++ b/lib/internal/Magento/Framework/Serialize/Serializer/Json.php @@ -15,6 +15,12 @@ */ class Json implements SerializerInterface { + private $appState = NULL; + + public function __construct(){ + $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); + $this->appState = $this->objectmanager->get('Magento\Framework\App\State'); + } /** * {@inheritDoc} * @since 100.2.0 @@ -23,7 +29,11 @@ public function serialize($data) { $result = json_encode($data); if (false === $result) { - throw new \InvalidArgumentException('Unable to serialize value.'); + $errorMessage = "Unable to serialize value."; + if(!$this->isOnProduction()){ + $errorMessage .= "Error: " . json_last_error_msg(); + } + throw new \InvalidArgumentException($errorMessage); } return $result; } @@ -36,8 +46,16 @@ public function unserialize($string) { $result = json_decode($string, true); if (json_last_error() !== JSON_ERROR_NONE) { - throw new \InvalidArgumentException('Unable to unserialize value.'); + $errorMessage = "Unable to unserialize value."; + if(!$this->isOnProduction()){ + $errorMessage .= "Error: " . json_last_error_msg(); + } + throw new \InvalidArgumentException($errorMessage); } return $result; } + + private function isOnProduction(){ + return $this->appState === \Magento\Framework\App\State::MODE_PRODUCTION; + } } From a10e31317509c027988ae7d3177db8e1bcf52fb6 Mon Sep 17 00:00:00 2001 From: Vinoth Kumar <vinogcs@users.noreply.github.com> Date: Wed, 19 Dec 2018 15:32:41 +0530 Subject: [PATCH 0317/1295] Fixed Unused Local variable --- lib/internal/Magento/Framework/Serialize/Serializer/Json.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Serialize/Serializer/Json.php b/lib/internal/Magento/Framework/Serialize/Serializer/Json.php index dc56ee8867589..d3b56e97bd58c 100644 --- a/lib/internal/Magento/Framework/Serialize/Serializer/Json.php +++ b/lib/internal/Magento/Framework/Serialize/Serializer/Json.php @@ -19,7 +19,7 @@ class Json implements SerializerInterface public function __construct(){ $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); - $this->appState = $this->objectmanager->get('Magento\Framework\App\State'); + $this->appState = $objectmanager->get('Magento\Framework\App\State'); } /** * {@inheritDoc} From b14ad816a0886637ce27bd17339c4a1fdaf69e43 Mon Sep 17 00:00:00 2001 From: Vinoth Kumar <vinogcs@users.noreply.github.com> Date: Wed, 19 Dec 2018 15:35:03 +0530 Subject: [PATCH 0318/1295] Fixed unused local variable --- lib/internal/Magento/Framework/Serialize/Serializer/Json.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Serialize/Serializer/Json.php b/lib/internal/Magento/Framework/Serialize/Serializer/Json.php index d3b56e97bd58c..053576eda8b74 100644 --- a/lib/internal/Magento/Framework/Serialize/Serializer/Json.php +++ b/lib/internal/Magento/Framework/Serialize/Serializer/Json.php @@ -19,7 +19,7 @@ class Json implements SerializerInterface public function __construct(){ $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); - $this->appState = $objectmanager->get('Magento\Framework\App\State'); + $this->appState = $objectManager->get('Magento\Framework\App\State'); } /** * {@inheritDoc} From 62352667fd2d5ecb066a5c2c3bf6fd77a19399f6 Mon Sep 17 00:00:00 2001 From: Vinoth Kumar <vinogcs@users.noreply.github.com> Date: Wed, 19 Dec 2018 16:48:29 +0530 Subject: [PATCH 0319/1295] Removed use of Class ObjectManager in Construct --- .../Magento/Framework/Serialize/Serializer/Json.php | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/internal/Magento/Framework/Serialize/Serializer/Json.php b/lib/internal/Magento/Framework/Serialize/Serializer/Json.php index 053576eda8b74..7f1e69d3f2c4d 100644 --- a/lib/internal/Magento/Framework/Serialize/Serializer/Json.php +++ b/lib/internal/Magento/Framework/Serialize/Serializer/Json.php @@ -15,12 +15,6 @@ */ class Json implements SerializerInterface { - private $appState = NULL; - - public function __construct(){ - $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); - $this->appState = $objectManager->get('Magento\Framework\App\State'); - } /** * {@inheritDoc} * @since 100.2.0 @@ -56,6 +50,8 @@ public function unserialize($string) } private function isOnProduction(){ - return $this->appState === \Magento\Framework\App\State::MODE_PRODUCTION; + $appState = \Magento\Framework\App\ObjectManager::getInstance() + ->get('Magento\Framework\App\State'); + return $appState === \Magento\Framework\App\State::MODE_PRODUCTION; } } From fc675823b9b947f28a66f36440a4324605258654 Mon Sep 17 00:00:00 2001 From: Vinoth Kumar <vinogcs@users.noreply.github.com> Date: Fri, 28 Dec 2018 15:23:35 +0530 Subject: [PATCH 0320/1295] Updated to show error message to all Env. --- .../Framework/Serialize/Serializer/Json.php | 20 +++---------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/lib/internal/Magento/Framework/Serialize/Serializer/Json.php b/lib/internal/Magento/Framework/Serialize/Serializer/Json.php index 7f1e69d3f2c4d..05d1f299ce761 100644 --- a/lib/internal/Magento/Framework/Serialize/Serializer/Json.php +++ b/lib/internal/Magento/Framework/Serialize/Serializer/Json.php @@ -23,11 +23,7 @@ public function serialize($data) { $result = json_encode($data); if (false === $result) { - $errorMessage = "Unable to serialize value."; - if(!$this->isOnProduction()){ - $errorMessage .= "Error: " . json_last_error_msg(); - } - throw new \InvalidArgumentException($errorMessage); + throw new \InvalidArgumentException("Unable to serialize value. Error: " . json_last_error_msg()); } return $result; } @@ -40,18 +36,8 @@ public function unserialize($string) { $result = json_decode($string, true); if (json_last_error() !== JSON_ERROR_NONE) { - $errorMessage = "Unable to unserialize value."; - if(!$this->isOnProduction()){ - $errorMessage .= "Error: " . json_last_error_msg(); - } - throw new \InvalidArgumentException($errorMessage); + throw new \InvalidArgumentException("Unable to unserialize value. Error: " . json_last_error_msg()); } return $result; - } - - private function isOnProduction(){ - $appState = \Magento\Framework\App\ObjectManager::getInstance() - ->get('Magento\Framework\App\State'); - return $appState === \Magento\Framework\App\State::MODE_PRODUCTION; - } + } } From aea6e3bfa31759e45b972890d51794944a2d1811 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Thu, 3 Jan 2019 12:22:47 +0530 Subject: [PATCH 0321/1295] Update lib/internal/Magento/Framework/Serialize/Serializer/Json.php Co-Authored-By: vinogcs <vinogcs@users.noreply.github.com> --- lib/internal/Magento/Framework/Serialize/Serializer/Json.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Serialize/Serializer/Json.php b/lib/internal/Magento/Framework/Serialize/Serializer/Json.php index 05d1f299ce761..ddcbf86614583 100644 --- a/lib/internal/Magento/Framework/Serialize/Serializer/Json.php +++ b/lib/internal/Magento/Framework/Serialize/Serializer/Json.php @@ -39,5 +39,5 @@ public function unserialize($string) throw new \InvalidArgumentException("Unable to unserialize value. Error: " . json_last_error_msg()); } return $result; - } + } } From c0d6a65418e0d3b17308d0991a8cb05c92202f86 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Fri, 4 Jan 2019 13:37:51 +0200 Subject: [PATCH 0322/1295] ENGCOM-3782: Static test fix. --- lib/internal/Magento/Framework/Serialize/Serializer/Json.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Serialize/Serializer/Json.php b/lib/internal/Magento/Framework/Serialize/Serializer/Json.php index ddcbf86614583..7ce9756ff243d 100644 --- a/lib/internal/Magento/Framework/Serialize/Serializer/Json.php +++ b/lib/internal/Magento/Framework/Serialize/Serializer/Json.php @@ -16,7 +16,7 @@ class Json implements SerializerInterface { /** - * {@inheritDoc} + * @inheritDoc * @since 100.2.0 */ public function serialize($data) @@ -29,7 +29,7 @@ public function serialize($data) } /** - * {@inheritDoc} + * @inheritDoc * @since 100.2.0 */ public function unserialize($string) From 4a13ccf26db5844f9d9a6993514a0fc34ce1da7a Mon Sep 17 00:00:00 2001 From: Ravi Chandra <ravi.chandra@krishtechnolabs.com> Date: Sat, 15 Dec 2018 16:42:41 +0530 Subject: [PATCH 0323/1295] Fixed The ui-component field validation error not opening accordion tab that owns the field (field does not get focused) --- .../Magento/Ui/view/base/web/js/form/components/fieldset.js | 4 ++++ app/code/Magento/Ui/view/base/web/js/form/element/abstract.js | 1 + 2 files changed, 5 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 6d33386fa1f1c..5f2fda830f5ba 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 @@ -162,6 +162,10 @@ define([ } this.error(hasErrors || message); + + if (hasErrors || message) { + this.open(); + } }, /** diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js index 013910bbd2e96..5617110590e50 100755 --- a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js @@ -408,6 +408,7 @@ define([ isValid = this.disabled() || !this.visible() || result.passed; this.error(message); + this.error.valueHasMutated(); this.bubble('error', message); //TODO: Implement proper result propagation for form From 3520de6eb20b7c7bb7d729b6819804957b6d2ae6 Mon Sep 17 00:00:00 2001 From: Craig Carnell <craig_carnell@hotmail.com> Date: Tue, 11 Dec 2018 15:39:20 +0000 Subject: [PATCH 0324/1295] Add useful debug info for which website has not been found --- app/code/Magento/Store/Model/WebsiteRepository.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Store/Model/WebsiteRepository.php b/app/code/Magento/Store/Model/WebsiteRepository.php index 94fd59c7634df..10f99dd484941 100644 --- a/app/code/Magento/Store/Model/WebsiteRepository.php +++ b/app/code/Magento/Store/Model/WebsiteRepository.php @@ -77,7 +77,9 @@ public function get($code) ]); if ($website->getId() === null) { - throw new NoSuchEntityException(); + throw new NoSuchEntityException( + __(sprintf("The website %s that was requested wasn't found. Verify the website and try again.", $code)) + ); } $this->entities[$code] = $website; $this->entitiesById[$website->getId()] = $website; @@ -99,7 +101,9 @@ public function getById($id) ]); if ($website->getId() === null) { - throw new NoSuchEntityException(); + throw new NoSuchEntityException( + __(sprintf("The website %s that was requested wasn't found. Verify the website and try again.", $id)) + ); } $this->entities[$website->getCode()] = $website; $this->entitiesById[$id] = $website; From 3bc62e65ca40df4e1f9704944ad9bd126afef3ae Mon Sep 17 00:00:00 2001 From: Craig Carnell <craig_carnell@hotmail.com> Date: Tue, 11 Dec 2018 16:57:32 +0000 Subject: [PATCH 0325/1295] Add with code/with id text --- app/code/Magento/Store/Model/WebsiteRepository.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Store/Model/WebsiteRepository.php b/app/code/Magento/Store/Model/WebsiteRepository.php index 10f99dd484941..dd77291f2fc28 100644 --- a/app/code/Magento/Store/Model/WebsiteRepository.php +++ b/app/code/Magento/Store/Model/WebsiteRepository.php @@ -78,7 +78,7 @@ public function get($code) if ($website->getId() === null) { throw new NoSuchEntityException( - __(sprintf("The website %s that was requested wasn't found. Verify the website and try again.", $code)) + __(sprintf("The website with code %s that was requested wasn't found. Verify the website and try again.", $code)) ); } $this->entities[$code] = $website; @@ -102,7 +102,7 @@ public function getById($id) if ($website->getId() === null) { throw new NoSuchEntityException( - __(sprintf("The website %s that was requested wasn't found. Verify the website and try again.", $id)) + __(sprintf("The website with id %s that was requested wasn't found. Verify the website and try again.", $id)) ); } $this->entities[$website->getCode()] = $website; From a1c40dcf99ba4263bd50176aa3b5c401bbbf2bb5 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Wed, 23 Jan 2019 15:38:43 +0200 Subject: [PATCH 0326/1295] MAGETWO-73598: CLONE - Quans: Orders API without key/value on ADDITIONAL_INFORMATION (only values) --- .../Data/PaymentAdditionalInfoInterface.php | 17 ++++++ .../Payment/Model/PaymentAdditionalInfo.php | 60 +++++++++++++++++++ app/code/Magento/Payment/etc/di.xml | 1 + .../Magento/Sales/Model/OrderRepository.php | 53 +++++++++++++++- .../Test/Unit/Model/OrderRepositoryTest.php | 22 ++++++- .../Sales/etc/extension_attributes.xml | 3 + .../Magento/Sales/Service/V1/OrderGetTest.php | 2 + .../Sales/Service/V1/OrderListTest.php | 2 + .../Magento/Sales/_files/order_list.php | 34 ++++++++++- 9 files changed, 190 insertions(+), 4 deletions(-) create mode 100644 app/code/Magento/Payment/Api/Data/PaymentAdditionalInfoInterface.php create mode 100644 app/code/Magento/Payment/Model/PaymentAdditionalInfo.php diff --git a/app/code/Magento/Payment/Api/Data/PaymentAdditionalInfoInterface.php b/app/code/Magento/Payment/Api/Data/PaymentAdditionalInfoInterface.php new file mode 100644 index 0000000000000..8afa064efd3ea --- /dev/null +++ b/app/code/Magento/Payment/Api/Data/PaymentAdditionalInfoInterface.php @@ -0,0 +1,17 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Payment\Api\Data; + +use Magento\Framework\DataObject\KeyValueObjectInterface; + +/** + * Payment additional info interface. + */ +interface PaymentAdditionalInfoInterface extends KeyValueObjectInterface +{ +} diff --git a/app/code/Magento/Payment/Model/PaymentAdditionalInfo.php b/app/code/Magento/Payment/Model/PaymentAdditionalInfo.php new file mode 100644 index 0000000000000..c4f135d5e0044 --- /dev/null +++ b/app/code/Magento/Payment/Model/PaymentAdditionalInfo.php @@ -0,0 +1,60 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Payment\Model; + +use Magento\Payment\Api\Data\PaymentAdditionalInfoInterface; + +/** + * Payment additional info class. + */ +class PaymentAdditionalInfo implements PaymentAdditionalInfoInterface +{ + /** + * @var string + */ + private $key; + + /** + * @var string + */ + private $value; + + /** + * @inheritdoc + */ + public function getKey() + { + return $this->key; + } + + /** + * @inheritdoc + */ + public function getValue() + { + return $this->value; + } + + /** + * @inheritdoc + */ + public function setKey($key) + { + $this->key = $key; + return $key; + } + + /** + * @inheritdoc + */ + public function setValue($value) + { + $this->value = $value; + return $value; + } +} diff --git a/app/code/Magento/Payment/etc/di.xml b/app/code/Magento/Payment/etc/di.xml index 74f553cc64094..b7422bb00d543 100644 --- a/app/code/Magento/Payment/etc/di.xml +++ b/app/code/Magento/Payment/etc/di.xml @@ -7,6 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <preference for="Magento\Payment\Api\Data\PaymentMethodInterface" type="Magento\Payment\Model\PaymentMethod"/> + <preference for="Magento\Payment\Api\Data\PaymentAdditionalInfoInterface" type="Magento\Payment\Model\PaymentAdditionalInfo"/> <preference for="Magento\Payment\Api\PaymentMethodListInterface" type="Magento\Payment\Model\PaymentMethodList"/> <preference for="Magento\Payment\Gateway\Validator\ResultInterface" type="Magento\Payment\Gateway\Validator\Result"/> <preference for="Magento\Payment\Gateway\ConfigFactoryInterface" type="Magento\Payment\Gateway\Config\ConfigFactory" /> diff --git a/app/code/Magento/Sales/Model/OrderRepository.php b/app/code/Magento/Sales/Model/OrderRepository.php index 88a9048daaa11..6b1228ebd52b7 100644 --- a/app/code/Magento/Sales/Model/OrderRepository.php +++ b/app/code/Magento/Sales/Model/OrderRepository.php @@ -18,6 +18,9 @@ use Magento\Sales\Model\Order\ShippingAssignmentBuilder; use Magento\Sales\Model\ResourceModel\Metadata; use Magento\Tax\Api\OrderTaxManagementInterface; +use Magento\Payment\Api\Data\PaymentAdditionalInfoInterface; +use Magento\Payment\Api\Data\PaymentAdditionalInfoInterfaceFactory; +use Magento\Framework\Serialize\Serializer\Json as JsonSerializer; /** * Repository class @@ -66,6 +69,16 @@ class OrderRepository implements \Magento\Sales\Api\OrderRepositoryInterface */ private $orderTaxManagement; + /** + * @var PaymentAdditionalInfoFactory + */ + private $paymentAdditionalInfoFactory; + + /** + * @var JsonSerializer + */ + private $serializer; + /** * Constructor * @@ -75,6 +88,8 @@ class OrderRepository implements \Magento\Sales\Api\OrderRepositoryInterface * @param \Magento\Sales\Api\Data\OrderExtensionFactory|null $orderExtensionFactory * @param JoinProcessorInterface $extensionAttributesJoinProcessor * @param OrderTaxManagementInterface|null $orderTaxManagement + * @param PaymentAdditionalInfoInterfaceFactory|null $paymentAdditionalInfoFactory + * @param JsonSerializer|null $serializer */ public function __construct( Metadata $metadata, @@ -82,7 +97,9 @@ public function __construct( CollectionProcessorInterface $collectionProcessor = null, \Magento\Sales\Api\Data\OrderExtensionFactory $orderExtensionFactory = null, JoinProcessorInterface $extensionAttributesJoinProcessor = null, - OrderTaxManagementInterface $orderTaxManagement = null + OrderTaxManagementInterface $orderTaxManagement = null, + PaymentAdditionalInfoInterfaceFactory $paymentAdditionalInfoFactory = null, + JsonSerializer $serializer = null ) { $this->metadata = $metadata; $this->searchResultFactory = $searchResultFactory; @@ -94,6 +111,10 @@ public function __construct( ?: ObjectManager::getInstance()->get(JoinProcessorInterface::class); $this->orderTaxManagement = $orderTaxManagement ?: ObjectManager::getInstance() ->get(OrderTaxManagementInterface::class); + $this->paymentAdditionalInfoFactory = $paymentAdditionalInfoFactory ?: ObjectManager::getInstance() + ->get(PaymentAdditionalInfoInterfaceFactory::class); + $this->serializer = $serializer ?: ObjectManager::getInstance() + ->get(JsonSerializer::class); } /** @@ -117,6 +138,7 @@ public function get($id) } $this->setOrderTaxDetails($entity); $this->setShippingAssignments($entity); + $this->setPaymentAdditionalInfo($entity); $this->registry[$id] = $entity; } return $this->registry[$id]; @@ -145,6 +167,34 @@ private function setOrderTaxDetails(OrderInterface $order) $order->setExtensionAttributes($extensionAttributes); } + /** + * Set payment additional info to the order. + * + * @param OrderInterface $order + * @return void + */ + private function setPaymentAdditionalInfo(OrderInterface $order) + { + $extensionAttributes = $order->getExtensionAttributes(); + $paymentAdditionalInformation = $order->getPayment()->getAdditionalInformation(); + + $objects = []; + foreach ($paymentAdditionalInformation as $key => $value) { + /** @var PaymentAdditionalInfoInterface $additionalInformationObject */ + $additionalInformationObject = $this->paymentAdditionalInfoFactory->create(); + $additionalInformationObject->setKey($key); + + if (!is_string($value)) { + $value = $this->serializer->serialize($value); + } + + $additionalInformationObject->setValue($value); + $objects[] = $additionalInformationObject; + } + $extensionAttributes->setPaymentAdditionalInfo($objects); + $order->setExtensionAttributes($extensionAttributes); + } + /** * Find entities by criteria * @@ -161,6 +211,7 @@ public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCr foreach ($searchResult->getItems() as $order) { $this->setShippingAssignments($order); $this->setOrderTaxDetails($order); + $this->setPaymentAdditionalInfo($order); } return $searchResult; } diff --git a/app/code/Magento/Sales/Test/Unit/Model/OrderRepositoryTest.php b/app/code/Magento/Sales/Test/Unit/Model/OrderRepositoryTest.php index 80f2f8142e885..04b774c8a74fd 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/OrderRepositoryTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/OrderRepositoryTest.php @@ -10,6 +10,7 @@ use Magento\Sales\Api\Data\OrderSearchResultInterfaceFactory as SearchResultFactory; use Magento\Sales\Model\ResourceModel\Metadata; use Magento\Tax\Api\OrderTaxManagementInterface; +use Magento\Payment\Api\Data\PaymentAdditionalInfoInterfaceFactory; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -46,6 +47,11 @@ class OrderRepositoryTest extends \PHPUnit\Framework\TestCase */ private $orderTaxManagementMock; + /** + * @var PaymentAdditionalInfoInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $paymentAdditionalInfoFactory; + /** * @inheritdoc */ @@ -67,6 +73,8 @@ protected function setUp() $this->orderTaxManagementMock = $this->getMockBuilder(OrderTaxManagementInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); + $this->paymentAdditionalInfoFactory = $this->getMockBuilder(PaymentAdditionalInfoInterfaceFactory::class) + ->disableOriginalConstructor()->setMethods(['create'])->getMockForAbstractClass(); $this->orderRepository = $this->objectManager->getObject( \Magento\Sales\Model\OrderRepository::class, [ @@ -75,6 +83,7 @@ protected function setUp() 'collectionProcessor' => $this->collectionProcessor, 'orderExtensionFactory' => $orderExtensionFactoryMock, 'orderTaxManagement' => $this->orderTaxManagementMock, + 'paymentAdditionalInfoFactory' => $this->paymentAdditionalInfoFactory, ] ); } @@ -93,12 +102,16 @@ public function testGetList() $orderTaxDetailsMock = $this->getMockBuilder(\Magento\Tax\Api\Data\OrderTaxDetailsInterface::class) ->disableOriginalConstructor() ->setMethods(['getAppliedTaxes', 'getItems'])->getMockForAbstractClass(); + $paymentMock = $this->getMockBuilder(\Magento\Sales\Api\Data\OrderPaymentInterface::class) + ->disableOriginalConstructor()->getMockForAbstractClass(); + $paymentAdditionalInfo = $this->getMockBuilder(\Magento\Payment\Api\Data\PaymentAdditionalInfoInterface::class) + ->disableOriginalConstructor()->setMethods(['setKey', 'setValue'])->getMockForAbstractClass(); $extensionAttributes = $this->createPartialMock( \Magento\Sales\Api\Data\OrderExtension::class, [ 'getShippingAssignments', 'setShippingAssignments', 'setConvertingFromQuote', - 'setAppliedTaxes', 'setItemAppliedTaxes', + 'setAppliedTaxes', 'setItemAppliedTaxes', 'setPaymentAdditionalInfo', ] ); $shippingAssignmentBuilder = $this->createMock( @@ -109,6 +122,13 @@ public function testGetList() ->method('process') ->with($searchCriteriaMock, $collectionMock); $itemsMock->expects($this->atLeastOnce())->method('getExtensionAttributes')->willReturn($extensionAttributes); + $itemsMock->expects($this->atleastOnce())->method('getPayment')->willReturn($paymentMock); + $paymentMock->expects($this->atLeastOnce())->method('getAdditionalInformation') + ->willReturn(['method' => 'checkmo']); + $this->paymentAdditionalInfoFactory->expects($this->atLeastOnce())->method('create') + ->willReturn($paymentAdditionalInfo); + $paymentAdditionalInfo->expects($this->atLeastOnce())->method('setKey')->willReturnSelf(); + $paymentAdditionalInfo->expects($this->atLeastOnce())->method('setValue')->willReturnSelf(); $this->orderTaxManagementMock->expects($this->atLeastOnce())->method('getOrderTaxDetails') ->willReturn($orderTaxDetailsMock); $extensionAttributes->expects($this->any()) diff --git a/app/code/Magento/Sales/etc/extension_attributes.xml b/app/code/Magento/Sales/etc/extension_attributes.xml index 7280a1a071548..222f61cdc7324 100644 --- a/app/code/Magento/Sales/etc/extension_attributes.xml +++ b/app/code/Magento/Sales/etc/extension_attributes.xml @@ -10,4 +10,7 @@ <extension_attributes for="Magento\Sales\Api\Data\OrderInterface"> <attribute code="shipping_assignments" type="Magento\Sales\Api\Data\ShippingAssignmentInterface[]" /> </extension_attributes> + <extension_attributes for="Magento\Sales\Api\Data\OrderInterface"> + <attribute code="payment_additional_info" type="Magento\Payment\Api\Data\PaymentAdditionalInfoInterface[]" /> + </extension_attributes> </config> diff --git a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderGetTest.php b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderGetTest.php index 3a3796c221ec4..0eb9e4229b957 100644 --- a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderGetTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderGetTest.php @@ -232,5 +232,7 @@ public function testOrderGetExtensionAttributes() $this->assertEquals($expectedTax['type'], $appliedTaxes[0]['type']); $this->assertNotEmpty($appliedTaxes[0]['applied_taxes']); $this->assertEquals(true, $result['extension_attributes']['converting_from_quote']); + $this->assertArrayHasKey('payment_additional_info', $result['extension_attributes']); + $this->assertNotEmpty($result['extension_attributes']['payment_additional_info']); } } diff --git a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderListTest.php b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderListTest.php index 5143f2c88fe2d..5e092bb1ebd69 100644 --- a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderListTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderListTest.php @@ -92,6 +92,8 @@ public function testOrderListExtensionAttributes() $this->assertEquals($expectedTax['type'], $appliedTaxes[0]['type']); $this->assertNotEmpty($appliedTaxes[0]['applied_taxes']); $this->assertEquals(true, $result['items'][0]['extension_attributes']['converting_from_quote']); + $this->assertArrayHasKey('payment_additional_info', $result['items'][0]['extension_attributes']); + $this->assertNotEmpty($result['items'][0]['extension_attributes']['payment_additional_info']); } /** diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_list.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_list.php index 890c475b0c316..1f4253f18487c 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_list.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_list.php @@ -5,6 +5,9 @@ */ use Magento\Sales\Model\Order; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order\Address as OrderAddress; +use Magento\Sales\Model\Order\Payment; require 'order.php'; /** @var Order $order */ @@ -49,17 +52,44 @@ ]; $orderList = []; +/** @var OrderRepositoryInterface $orderRepository */ +$orderRepository = $objectManager->create(OrderRepositoryInterface::class); /** @var array $orderData */ foreach ($orders as $orderData) { /** @var $order \Magento\Sales\Model\Order */ $order = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( \Magento\Sales\Model\Order::class ); + + // Reset addresses + /** @var Order\Address $billingAddress */ + $billingAddress = $objectManager->create(OrderAddress::class, ['data' => $addressData]); + $billingAddress->setAddressType('billing'); + + $shippingAddress = clone $billingAddress; + $shippingAddress->setId(null)->setAddressType('shipping'); + + /** @var Payment $payment */ + $payment = $objectManager->create(Payment::class); + $payment->setMethod('checkmo') + ->setAdditionalInformation('last_trans_id', '11122') + ->setAdditionalInformation( + 'metadata', + [ + 'type' => 'free', + 'fraudulent' => false, + ] + ); + $order ->setData($orderData) ->addItem($orderItem) + ->setCustomerIsGuest(true) + ->setCustomerEmail('customer@null.com') ->setBillingAddress($billingAddress) - ->setBillingAddress($shippingAddress) - ->save(); + ->setShippingAddress($shippingAddress) + ->setPayment($payment); + + $orderRepository->save($order); $orderList[] = $order; } From 7996bdd734fd242f6024820d188ad5931d592370 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Wed, 23 Jan 2019 16:10:42 +0200 Subject: [PATCH 0327/1295] MAGETWO-97202: Add Product by SKU to category causes the browser to hang --- .../UrlRewrite/Model/Storage/DbStorage.php | 64 ++++++++++++------- .../Test/Unit/Model/Storage/DbStorageTest.php | 8 --- 2 files changed, 41 insertions(+), 31 deletions(-) diff --git a/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php b/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php index 499fb9925a54a..60b845f95e5cc 100644 --- a/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php +++ b/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php @@ -79,7 +79,7 @@ protected function prepareSelect(array $data) } /** - * {@inheritdoc} + * @inheritdoc */ protected function doFindAllByData(array $data) { @@ -87,7 +87,7 @@ protected function doFindAllByData(array $data) } /** - * {@inheritdoc} + * @inheritdoc */ protected function doFindOneByData(array $data) { @@ -152,26 +152,22 @@ private function deleteOldUrls(array $urls) $oldUrlsSelect->from( $this->resource->getTableName(self::TABLE_NAME) ); - /** @var UrlRewrite $url */ - foreach ($urls as $url) { - $oldUrlsSelect->orWhere( - $this->connection->quoteIdentifier( - UrlRewrite::ENTITY_TYPE - ) . ' = ?', - $url->getEntityType() - ); - $oldUrlsSelect->where( - $this->connection->quoteIdentifier( - UrlRewrite::ENTITY_ID - ) . ' = ?', - $url->getEntityId() - ); - $oldUrlsSelect->where( - $this->connection->quoteIdentifier( - UrlRewrite::STORE_ID - ) . ' = ?', - $url->getStoreId() - ); + + $uniqueEntities = $this->prepareUniqueEntities($urls); + foreach ($uniqueEntities as $storeId => $entityTypes) { + foreach ($entityTypes as $entityType => $entities) { + $oldUrlsSelect->orWhere( + $this->connection->quoteIdentifier( + UrlRewrite::STORE_ID + ) . ' = ' . $this->connection->quote($storeId, 'INTEGER') . + ' AND ' . $this->connection->quoteIdentifier( + UrlRewrite::ENTITY_ID + ) . ' IN (' . $this->connection->quote($entities, 'INTEGER') . ')' . + ' AND ' . $this->connection->quoteIdentifier( + UrlRewrite::ENTITY_TYPE + ) . ' = ' . $this->connection->quote($entityType) + ); + } } // prevent query locking in a case when nothing to delete @@ -189,6 +185,28 @@ private function deleteOldUrls(array $urls) } } + /** + * Prepare array with unique entities + * + * @param UrlRewrite[] $urls + * @return array + */ + private function prepareUniqueEntities(array $urls): array + { + $uniqueEntities = []; + /** @var UrlRewrite $url */ + foreach ($urls as $url) { + $entityIds = (!empty($uniqueEntities[$url->getStoreId()][$url->getEntityType()])) ? + $uniqueEntities[$url->getStoreId()][$url->getEntityType()] : []; + + if (!\in_array($url->getEntityId(), $entityIds)) { + $entityIds[] = $url->getEntityId(); + } + $uniqueEntities[$url->getStoreId()][$url->getEntityType()] = $entityIds; + } + return $uniqueEntities; + } + /** * @inheritDoc */ @@ -279,7 +297,7 @@ protected function createFilterDataBasedOnUrls($urls) } /** - * {@inheritdoc} + * @inheritdoc */ public function deleteByData(array $data) { diff --git a/app/code/Magento/UrlRewrite/Test/Unit/Model/Storage/DbStorageTest.php b/app/code/Magento/UrlRewrite/Test/Unit/Model/Storage/DbStorageTest.php index 408bcaf7a489e..08fe9a6375b24 100644 --- a/app/code/Magento/UrlRewrite/Test/Unit/Model/Storage/DbStorageTest.php +++ b/app/code/Magento/UrlRewrite/Test/Unit/Model/Storage/DbStorageTest.php @@ -478,10 +478,6 @@ public function testReplace() ->with(DbStorage::TABLE_NAME) ->will($this->returnValue('table_name')); - $this->connectionMock->expects($this->any()) - ->method('query') - ->with('sql delete query'); - // insert $urlFirst->expects($this->any()) @@ -496,10 +492,6 @@ public function testReplace() ->with(DbStorage::TABLE_NAME) ->will($this->returnValue('table_name')); - $this->connectionMock->expects($this->once()) - ->method('insertMultiple') - ->with('table_name', [['row1'], ['row2']]); - $this->storage->replace([$urlFirst, $urlSecond]); } From 1ea5c3ed78e437a773c74d243a24281d5b5d9feb Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Wed, 23 Jan 2019 16:34:04 +0200 Subject: [PATCH 0328/1295] MAGETWO-96815: There is no option to remove the Store Credit from the payment page of checkout --- .../Ui/Test/Mftf/ActionGroup/AdminDataGridFilterActionGroup.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridFilterActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridFilterActionGroup.xml index 85672505ac707..c1379b9fce575 100644 --- a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridFilterActionGroup.xml +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridFilterActionGroup.xml @@ -31,8 +31,6 @@ <actionGroup name="clearFiltersAdminDataGrid"> <waitForPageLoad stepKey="waitForPageLoad"/> <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFilters"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> - <waitForElementNotVisible selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="waitClearFiltersDisappear"/> </actionGroup> <actionGroup name="AdminGridFilterSearchResultsByInput"> From 7c4cea2a56aecc92249ca376d5baaa8166bb045b Mon Sep 17 00:00:00 2001 From: amol 2jcommerce <amol@2jcommerce.in> Date: Wed, 23 Jan 2019 20:32:02 +0530 Subject: [PATCH 0329/1295] Fixed-Widget-left-navigation-block-2.2 --- .../Magento_Backend/web/css/source/module/main/_page-nav.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_page-nav.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_page-nav.less index 42b3ecfb71122..7b60a8d871702 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_page-nav.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_page-nav.less @@ -23,7 +23,7 @@ @admin__page-nav-item__hover__background-color: darken(@admin__page-nav__background-color, 5%); @admin__page-nav-link__color: @color-very-dark-gray-black; -@admin__page-nav-link__padding: @indent__base 4rem @indent__base @indent__s; +@admin__page-nav-link__padding: @indent__base 0rem @indent__base @indent__s; @admin__page-nav-link__hover__color: @color-very-dark-gray-black; @admin__page-nav-link__changed__color: @color-very-dark-gray; From 867f47465bfee044b51b621392ca3228fb803a2c Mon Sep 17 00:00:00 2001 From: Arvinda kumar <arvindakumar@cedcoss.com> Date: Tue, 15 Jan 2019 16:46:44 +0530 Subject: [PATCH 0330/1295] issue fixed #20299 Order item details label not aligned in mobile view issue fixed #20299 Order item details label not aligned in mobile view --- .../Magento_Checkout/web/css/source/module/_cart.less | 11 +++++++++++ 1 file changed, 11 insertions(+) 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 1015bb584ff7b..ce4ba47cc2337 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 @@ -501,6 +501,17 @@ } } } + + .cart.table-wrapper, + .order-items.table-wrapper { + .col.price, + .col.qty, + .col.subtotal, + .col.msrp { + text-align: left; + } + } + } // From 362f81fada5975a193b1a41415725cf9bdad4ef7 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Wed, 16 Jan 2019 11:52:32 +0200 Subject: [PATCH 0331/1295] Fix static tests. --- .../luma/Magento_Checkout/web/css/source/module/_cart.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 ce4ba47cc2337..fa41682cbd635 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 @@ -501,7 +501,7 @@ } } } - + .cart.table-wrapper, .order-items.table-wrapper { .col.price, @@ -511,7 +511,7 @@ text-align: left; } } - + } // From 4741489674e363357de9fddb3920924ab1177009 Mon Sep 17 00:00:00 2001 From: Arvinda kumar <arvindakumar@cedcoss.com> Date: Mon, 14 Jan 2019 12:17:05 +0530 Subject: [PATCH 0332/1295] issue fixed #20259 Store switcher not sliding up and down, only dropdown arrow working issue fixed #20259 Store switcher not sliding up and down, only dropdown arrow working --- .../Magento/blank/web/css/source/_navigation.less | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/blank/web/css/source/_navigation.less b/app/design/frontend/Magento/blank/web/css/source/_navigation.less index 4499886ef0f10..88d597ec0d0e7 100644 --- a/app/design/frontend/Magento/blank/web/css/source/_navigation.less +++ b/app/design/frontend/Magento/blank/web/css/source/_navigation.less @@ -135,8 +135,15 @@ .switcher-dropdown { .lib-list-reset-styles(); padding: @indent__s 0; + display: none; } - + .switcher-options{ + &.active{ + .switcher-dropdown{ + display: block; + } + } + } .header.links { .lib-list-reset-styles(); border-bottom: 1px solid @color-gray82; From cd75f2928d56785e86b5235e56727a571cc1d8ff Mon Sep 17 00:00:00 2001 From: Arvinda kumar <arvindakumar@cedcoss.com> Date: Mon, 14 Jan 2019 13:03:16 -0800 Subject: [PATCH 0333/1295] _navigation.less updated _navigation.less updated --- .../frontend/Magento/blank/web/css/source/_navigation.less | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/design/frontend/Magento/blank/web/css/source/_navigation.less b/app/design/frontend/Magento/blank/web/css/source/_navigation.less index 88d597ec0d0e7..10c7e3ed7eeea 100644 --- a/app/design/frontend/Magento/blank/web/css/source/_navigation.less +++ b/app/design/frontend/Magento/blank/web/css/source/_navigation.less @@ -131,15 +131,14 @@ ); } } - .switcher-dropdown { .lib-list-reset-styles(); padding: @indent__s 0; display: none; } - .switcher-options{ - &.active{ - .switcher-dropdown{ + .switcher-options { + &.active { + .switcher-dropdown { display: block; } } From 1f01d9258b7768e3156be05bb0b2cb3537bcfa1e Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Wed, 16 Jan 2019 16:50:24 +0200 Subject: [PATCH 0334/1295] ENGCOM-3869: Static test fix. --- .../frontend/Magento/blank/web/css/source/_navigation.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/design/frontend/Magento/blank/web/css/source/_navigation.less b/app/design/frontend/Magento/blank/web/css/source/_navigation.less index 10c7e3ed7eeea..21b7315779764 100644 --- a/app/design/frontend/Magento/blank/web/css/source/_navigation.less +++ b/app/design/frontend/Magento/blank/web/css/source/_navigation.less @@ -133,8 +133,8 @@ } .switcher-dropdown { .lib-list-reset-styles(); - padding: @indent__s 0; display: none; + padding: @indent__s 0; } .switcher-options { &.active { @@ -213,7 +213,7 @@ } .nav-toggle { - &:after{ + &:after { background: rgba(0, 0, 0, @overlay__opacity); content: ''; display: block; From 8d949bda2465de4036f700c04b425982e6b48991 Mon Sep 17 00:00:00 2001 From: Arvinda kumar <arvindakumar@cedcoss.com> Date: Thu, 27 Dec 2018 13:40:39 +0530 Subject: [PATCH 0335/1295] Issue fixed #19985 Send email confirmation popup close button area overlapping to content Issue fixed #19985 Send email confirmation popup close button area overlapping to content --- .../backend/web/css/source/components/_modals_extend.less | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_modals_extend.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_modals_extend.less index 63d97dc52e453..4176a7e9214e6 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_modals_extend.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_modals_extend.less @@ -146,13 +146,13 @@ } .action-close { - padding: @modal-popup__padding; + padding: 1rem; &:active, &:focus { background: transparent; - padding-right: @modal-popup__padding + (@modal-action-close__font-size - @modal-action-close__active__font-size) / 2; - padding-top: @modal-popup__padding + (@modal-action-close__font-size - @modal-action-close__active__font-size) / 2; + padding-right: 1rem; + padding-top: 1rem; } } } From 2e4d7e1e707576f786c82d17997d7c904ff82a05 Mon Sep 17 00:00:00 2001 From: Arvinda kumar <arvindakumar@cedcoss.com> Date: Thu, 27 Dec 2018 19:59:14 +0530 Subject: [PATCH 0336/1295] Issue fixed #19985 Send email confirmation popup close button area overlapping Issue fixed #19985 Send email confirmation popup close button area overlapping --- .../backend/web/css/source/components/_modals_extend.less | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_modals_extend.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_modals_extend.less index 4176a7e9214e6..565b127ccad3e 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_modals_extend.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_modals_extend.less @@ -146,13 +146,13 @@ } .action-close { - padding: 1rem; + padding: @modal-popup__padding - 2; &:active, &:focus { background: transparent; - padding-right: 1rem; - padding-top: 1rem; + padding-right: @modal-popup__padding - 2; + padding-top: @modal-popup__padding - 2; } } } From d47fe0138454432b730176401cdbfa10e1dc3d39 Mon Sep 17 00:00:00 2001 From: Milind Singh <milind7@live.com> Date: Thu, 10 Jan 2019 17:27:15 +0530 Subject: [PATCH 0337/1295] Order API resources updated. --- app/code/Magento/Sales/etc/webapi.xml | 90 +++++++++++++-------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/app/code/Magento/Sales/etc/webapi.xml b/app/code/Magento/Sales/etc/webapi.xml index cee245e348393..492dff8057039 100644 --- a/app/code/Magento/Sales/etc/webapi.xml +++ b/app/code/Magento/Sales/etc/webapi.xml @@ -10,271 +10,271 @@ <route url="/V1/orders/:id" method="GET"> <service class="Magento\Sales\Api\OrderRepositoryInterface" method="get"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::actions_view" /> </resources> </route> <route url="/V1/orders" method="GET"> <service class="Magento\Sales\Api\OrderRepositoryInterface" method="getList"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::actions_view" /> </resources> </route> <route url="/V1/orders/:id/statuses" method="GET"> <service class="Magento\Sales\Api\OrderManagementInterface" method="getStatus"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::actions_view" /> </resources> </route> <route url="/V1/orders/:id/cancel" method="POST"> <service class="Magento\Sales\Api\OrderManagementInterface" method="cancel"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::cancel" /> </resources> </route> <route url="/V1/orders/:id/emails" method="POST"> <service class="Magento\Sales\Api\OrderManagementInterface" method="notify"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::emails" /> </resources> </route> <route url="/V1/orders/:id/hold" method="POST"> <service class="Magento\Sales\Api\OrderManagementInterface" method="hold"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::hold" /> </resources> </route> <route url="/V1/orders/:id/unhold" method="POST"> <service class="Magento\Sales\Api\OrderManagementInterface" method="unHold"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::unhold" /> </resources> </route> <route url="/V1/orders/:id/comments" method="POST"> <service class="Magento\Sales\Api\OrderManagementInterface" method="addComment"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::comment" /> </resources> </route> <route url="/V1/orders/:id/comments" method="GET"> <service class="Magento\Sales\Api\OrderManagementInterface" method="getCommentsList"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::actions_view" /> </resources> </route> <route url="/V1/orders/create" method="PUT"> <service class="Magento\Sales\Api\OrderRepositoryInterface" method="save"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::create" /> </resources> </route> <route url="/V1/orders/:parent_id" method="PUT"> <service class="Magento\Sales\Api\OrderAddressRepositoryInterface" method="save"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::create" /> </resources> </route> <route url="/V1/orders/items/:id" method="GET"> <service class="Magento\Sales\Api\OrderItemRepositoryInterface" method="get"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::actions_view" /> </resources> </route> <route url="/V1/orders/items" method="GET"> <service class="Magento\Sales\Api\OrderItemRepositoryInterface" method="getList"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::actions_view" /> </resources> </route> <route url="/V1/invoices/:id" method="GET"> <service class="Magento\Sales\Api\InvoiceRepositoryInterface" method="get"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::sales_invoice" /> </resources> </route> <route url="/V1/invoices" method="GET"> <service class="Magento\Sales\Api\InvoiceRepositoryInterface" method="getList"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::sales_invoice" /> </resources> </route> <route url="/V1/invoices/:id/comments" method="GET"> <service class="Magento\Sales\Api\InvoiceManagementInterface" method="getCommentsList"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::sales_invoice" /> </resources> </route> <route url="/V1/invoices/:id/emails" method="POST"> <service class="Magento\Sales\Api\InvoiceManagementInterface" method="notify"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::sales_invoice" /> </resources> </route> <route url="/V1/invoices/:id/void" method="POST"> <service class="Magento\Sales\Api\InvoiceManagementInterface" method="setVoid"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::sales_invoice" /> </resources> </route> <route url="/V1/invoices/:id/capture" method="POST"> <service class="Magento\Sales\Api\InvoiceManagementInterface" method="setCapture"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::sales_invoice" /> </resources> </route> <route url="/V1/invoices/comments" method="POST"> <service class="Magento\Sales\Api\InvoiceCommentRepositoryInterface" method="save"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::sales_invoice" /> </resources> </route> <route url="/V1/invoices/" method="POST"> <service class="Magento\Sales\Api\InvoiceRepositoryInterface" method="save"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::sales_invoice" /> </resources> </route> <route url="/V1/invoice/:invoiceId/refund" method="POST"> <service class="Magento\Sales\Api\RefundInvoiceInterface" method="execute"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::sales_invoice" /> </resources> </route> <route url="/V1/creditmemo/:id/comments" method="GET"> <service class="Magento\Sales\Api\CreditmemoManagementInterface" method="getCommentsList"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::sales_creditmemo" /> </resources> </route> <route url="/V1/creditmemos" method="GET"> <service class="Magento\Sales\Api\CreditmemoRepositoryInterface" method="getList"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::sales_creditmemo" /> </resources> </route> <route url="/V1/creditmemo/:id" method="GET"> <service class="Magento\Sales\Api\CreditmemoRepositoryInterface" method="get"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::sales_creditmemo" /> </resources> </route> <route url="/V1/creditmemo/:id" method="PUT"> <service class="Magento\Sales\Api\CreditmemoManagementInterface" method="cancel"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::sales_creditmemo" /> </resources> </route> <route url="/V1/creditmemo/:id/emails" method="POST"> <service class="Magento\Sales\Api\CreditmemoManagementInterface" method="notify"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::sales_creditmemo" /> </resources> </route> <route url="/V1/creditmemo/refund" method="POST"> <service class="Magento\Sales\Api\CreditmemoManagementInterface" method="refund"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::sales_creditmemo" /> </resources> </route> <route url="/V1/creditmemo/:id/comments" method="POST"> <service class="Magento\Sales\Api\CreditmemoCommentRepositoryInterface" method="save"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::sales_creditmemo" /> </resources> </route> <route url="/V1/creditmemo" method="POST"> <service class="Magento\Sales\Api\CreditmemoRepositoryInterface" method="save"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::sales_creditmemo" /> </resources> </route> <route url="/V1/order/:orderId/refund" method="POST"> <service class="Magento\Sales\Api\RefundOrderInterface" method="execute"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::creditmemo" /> </resources> </route> <route url="/V1/shipment/:id" method="GET"> <service class="Magento\Sales\Api\ShipmentRepositoryInterface" method="get"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::shipment" /> </resources> </route> <route url="/V1/shipments" method="GET"> <service class="Magento\Sales\Api\ShipmentRepositoryInterface" method="getList"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::shipment" /> </resources> </route> <route url="/V1/shipment/:id/comments" method="GET"> <service class="Magento\Sales\Api\ShipmentManagementInterface" method="getCommentsList"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::shipment" /> </resources> </route> <route url="/V1/shipment/:id/comments" method="POST"> <service class="Magento\Sales\Api\ShipmentCommentRepositoryInterface" method="save"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::shipment" /> </resources> </route> <route url="/V1/shipment/:id/emails" method="POST"> <service class="Magento\Sales\Api\ShipmentManagementInterface" method="notify"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::shipment" /> </resources> </route> <route url="/V1/shipment/track" method="POST"> <service class="Magento\Sales\Api\ShipmentTrackRepositoryInterface" method="save"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::shipment" /> </resources> </route> <route url="/V1/shipment/track/:id" method="DELETE"> <service class="Magento\Sales\Api\ShipmentTrackRepositoryInterface" method="deleteById"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::shipment" /> </resources> </route> <route url="/V1/shipment/" method="POST"> <service class="Magento\Sales\Api\ShipmentRepositoryInterface" method="save"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::shipment" /> </resources> </route> <route url="/V1/shipment/:id/label" method="GET"> <service class="Magento\Sales\Api\ShipmentManagementInterface" method="getLabel"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::shipment" /> </resources> </route> <route url="/V1/order/:orderId/ship" method="POST"> <service class="Magento\Sales\Api\ShipOrderInterface" method="execute"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::ship" /> </resources> </route> <route url="/V1/orders/" method="POST"> <service class="Magento\Sales\Api\OrderRepositoryInterface" method="save"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::create" /> </resources> </route> <route url="/V1/transactions/:id" method="GET"> <service class="Magento\Sales\Api\TransactionRepositoryInterface" method="get"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::transactions_fetch" /> </resources> </route> <route url="/V1/transactions" method="GET"> <service class="Magento\Sales\Api\TransactionRepositoryInterface" method="getList"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::transactions_fetch" /> </resources> </route> <route url="/V1/order/:orderId/invoice" method="POST"> <service class="Magento\Sales\Api\InvoiceOrderInterface" method="execute"/> <resources> - <resource ref="Magento_Sales::sales" /> + <resource ref="Magento_Sales::invoice" /> </resources> </route> </routes> From dd6773010624731c3a9a22863137f2d0f2cc6af9 Mon Sep 17 00:00:00 2001 From: Nainesh <nainesh@2jcommerce.in> Date: Fri, 18 Jan 2019 17:45:35 +0530 Subject: [PATCH 0338/1295] 'wishlist-page-edit-remove-item-misalign' :: On wish list page edit, remove item misalign in 640 X 767 resolution --- .../Magento/blank/Magento_Wishlist/web/css/source/_module.less | 1 + .../Magento/luma/Magento_Wishlist/web/css/source/_module.less | 1 + 2 files changed, 2 insertions(+) diff --git a/app/design/frontend/Magento/blank/Magento_Wishlist/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Wishlist/web/css/source/_module.less index 0e8350261e002..f34ed404e86d9 100644 --- a/app/design/frontend/Magento/blank/Magento_Wishlist/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Wishlist/web/css/source/_module.less @@ -194,6 +194,7 @@ &-actions { display: block; + float: left; .action { margin-right: 15px; diff --git a/app/design/frontend/Magento/luma/Magento_Wishlist/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Wishlist/web/css/source/_module.less index 584eefb9bc643..3b6f724508220 100644 --- a/app/design/frontend/Magento/luma/Magento_Wishlist/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Wishlist/web/css/source/_module.less @@ -203,6 +203,7 @@ &-actions { display: block; + float: left; .action { margin-right: 15px; From b850eb0e46fcc72036b32793f88d03d2267c771d Mon Sep 17 00:00:00 2001 From: Ranee 2Jcommerce <ranee@2jcommerce.in> Date: Thu, 17 Jan 2019 18:59:08 +0530 Subject: [PATCH 0339/1295] Order-view-invoices :: Order view invoices template not display proper on ipad --- .../Magento_Sales/web/css/source/module/_order.less | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/_order.less b/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/_order.less index 1e76679f594c1..fa1ae25628986 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/_order.less +++ b/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/_order.less @@ -92,6 +92,14 @@ margin: 0; padding: 0; } + .admin__data-grid-pager-wrap{ + .selectmenu { + margin-bottom: 10px; + } + } + .data-grid-search-control-wrap { + margin-bottom: 10px; + } } // From 331b5c1456f901dffcb0fc92a86f650ea57b3783 Mon Sep 17 00:00:00 2001 From: Shikha Mishra <shikhamishra@cedcoss.com> Date: Fri, 11 Jan 2019 08:20:24 +0530 Subject: [PATCH 0340/1295] Updated CancelOrderItemObserver.php Fixed Issue #20121 Cancel order increases stock although "Set Items' Status to be In Stock When Order is Cancelled" is set to No --- .../Observer/CancelOrderItemObserver.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogInventory/Observer/CancelOrderItemObserver.php b/app/code/Magento/CatalogInventory/Observer/CancelOrderItemObserver.php index 1e99794d68a40..c919c7057bcc6 100644 --- a/app/code/Magento/CatalogInventory/Observer/CancelOrderItemObserver.php +++ b/app/code/Magento/CatalogInventory/Observer/CancelOrderItemObserver.php @@ -6,6 +6,7 @@ namespace Magento\CatalogInventory\Observer; +use Magento\CatalogInventory\Model\Configuration; use Magento\Framework\Event\ObserverInterface; use Magento\CatalogInventory\Api\StockManagementInterface; use Magento\Framework\Event\Observer as EventObserver; @@ -15,6 +16,11 @@ */ class CancelOrderItemObserver implements ObserverInterface { + /** + * @var \Magento\CatalogInventory\Model\Configuration + */ + protected $configuration; + /** * @var StockManagementInterface */ @@ -30,9 +36,11 @@ class CancelOrderItemObserver implements ObserverInterface * @param \Magento\Catalog\Model\Indexer\Product\Price\Processor $priceIndexer */ public function __construct( + Configuration $configuration, StockManagementInterface $stockManagement, \Magento\Catalog\Model\Indexer\Product\Price\Processor $priceIndexer ) { + $this->configuration = $configuration; $this->stockManagement = $stockManagement; $this->priceIndexer = $priceIndexer; } @@ -49,7 +57,8 @@ public function execute(EventObserver $observer) $item = $observer->getEvent()->getItem(); $children = $item->getChildrenItems(); $qty = $item->getQtyOrdered() - max($item->getQtyShipped(), $item->getQtyInvoiced()) - $item->getQtyCanceled(); - if ($item->getId() && $item->getProductId() && empty($children) && $qty) { + if ($item->getId() && $item->getProductId() && empty($children) && $qty && $this->configuration + ->getCanBackInStock()) { $this->stockManagement->backItemQty($item->getProductId(), $qty, $item->getStore()->getWebsiteId()); } $this->priceIndexer->reindexRow($item->getProductId()); From cc6b848d242d4458e2c270273cc36e55b822b4be Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Wed, 16 Jan 2019 15:57:50 +0200 Subject: [PATCH 0341/1295] ENGCOM-3845: Static test fix. --- .../CatalogInventory/Observer/CancelOrderItemObserver.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Observer/CancelOrderItemObserver.php b/app/code/Magento/CatalogInventory/Observer/CancelOrderItemObserver.php index c919c7057bcc6..098e254d785a5 100644 --- a/app/code/Magento/CatalogInventory/Observer/CancelOrderItemObserver.php +++ b/app/code/Magento/CatalogInventory/Observer/CancelOrderItemObserver.php @@ -6,10 +6,10 @@ namespace Magento\CatalogInventory\Observer; -use Magento\CatalogInventory\Model\Configuration; -use Magento\Framework\Event\ObserverInterface; use Magento\CatalogInventory\Api\StockManagementInterface; +use Magento\CatalogInventory\Model\Configuration; use Magento\Framework\Event\Observer as EventObserver; +use Magento\Framework\Event\ObserverInterface; /** * Catalog inventory module observer @@ -32,6 +32,7 @@ class CancelOrderItemObserver implements ObserverInterface protected $priceIndexer; /** + * @param Configuration $configuration * @param StockManagementInterface $stockManagement * @param \Magento\Catalog\Model\Indexer\Product\Price\Processor $priceIndexer */ From 1e704e71d398722014a5cc5f455043166585b47a Mon Sep 17 00:00:00 2001 From: amol 2jcommerce <amol@2jcommerce.in> Date: Thu, 24 Jan 2019 10:14:46 +0530 Subject: [PATCH 0342/1295] Fixed-Bundle-Product-add-to-cart-button-misaligned-2.2 --- .../Magento/luma/Magento_Bundle/web/css/source/_module.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/luma/Magento_Bundle/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Bundle/web/css/source/_module.less index 43ae23bab7895..eeb17653c877b 100644 --- a/app/design/frontend/Magento/luma/Magento_Bundle/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Bundle/web/css/source/_module.less @@ -253,7 +253,7 @@ .box-tocart { .action.primary { margin-right: 1%; - width: 49%; + width: auto; } } From 2ddaaff1e4f1116ae083680eb45a31fd2c866c37 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Thu, 24 Jan 2019 09:09:44 +0200 Subject: [PATCH 0343/1295] MAGETWO-74604: Event clean_cache_by_tags didn't clean all tags pointed in getIdentity --- .../Magento/CatalogRule/Model/Indexer/AbstractIndexer.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogRule/Model/Indexer/AbstractIndexer.php b/app/code/Magento/CatalogRule/Model/Indexer/AbstractIndexer.php index 5d93e6f216866..6b7c12dfdf463 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/AbstractIndexer.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/AbstractIndexer.php @@ -10,6 +10,9 @@ use Magento\Framework\DataObject\IdentityInterface; use Magento\Framework\Indexer\CacheContext; +/** + * Abstract class for CatalogRule indexers. + */ abstract class AbstractIndexer implements IndexerActionInterface, MviewActionInterface, IdentityInterface { /** @@ -66,7 +69,6 @@ public function executeFull() { $this->indexBuilder->reindexFull(); $this->_eventManager->dispatch('clean_cache_by_tags', ['object' => $this]); - //TODO: remove after fix fpc. MAGETWO-50668 $this->getCacheManager()->clean($this->getIdentities()); } @@ -137,8 +139,9 @@ public function executeRow($id) abstract protected function doExecuteRow($id); /** - * @return \Magento\Framework\App\CacheInterface|mixed + * Get cache manager * + * @return \Magento\Framework\App\CacheInterface|mixed * @deprecated 100.0.7 */ private function getCacheManager() From 5e2908a749bdad86ae36420ede2c08c301487f3e Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Thu, 24 Jan 2019 09:55:09 +0200 Subject: [PATCH 0344/1295] MAGETWO-96690: Incorrect Rss Wishlist response --- .../Magento/Wishlist/Model/Rss/Wishlist.php | 15 ++- .../Test/Unit/Model/Rss/WishlistTest.php | 47 ++++++-- .../_files/two_customers_rollback.php | 31 ++++++ .../Magento/Rss/Controller/Feed/IndexTest.php | 104 ++++++++++++++++++ .../two_wishlists_for_two_diff_customers.php | 25 +++++ ...hlists_for_two_diff_customers_rollback.php | 41 +++++++ 6 files changed, 246 insertions(+), 17 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/two_customers_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Rss/Controller/Feed/IndexTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Wishlist/_files/two_wishlists_for_two_diff_customers.php create mode 100644 dev/tests/integration/testsuite/Magento/Wishlist/_files/two_wishlists_for_two_diff_customers_rollback.php diff --git a/app/code/Magento/Wishlist/Model/Rss/Wishlist.php b/app/code/Magento/Wishlist/Model/Rss/Wishlist.php index 75df3027ad9a9..4b3f45c0bbda1 100644 --- a/app/code/Magento/Wishlist/Model/Rss/Wishlist.php +++ b/app/code/Magento/Wishlist/Model/Rss/Wishlist.php @@ -7,6 +7,7 @@ namespace Magento\Wishlist\Model\Rss; use Magento\Framework\App\Rss\DataProviderInterface; +use Magento\Store\Model\ScopeInterface; /** * Wishlist RSS model @@ -114,10 +115,8 @@ public function __construct( */ public function isAllowed() { - return (bool)$this->scopeConfig->getValue( - 'rss/wishlist/active', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ); + return (bool)$this->scopeConfig->getValue('rss/wishlist/active', ScopeInterface::SCOPE_STORE) + && $this->getWishlist()->getCustomerId() === $this->wishlistHelper->getCustomer()->getId(); } /** @@ -180,8 +179,8 @@ public function getRssData() } } else { $data = [ - 'title' => __('We cannot retrieve the Wish List.'), - 'description' => __('We cannot retrieve the Wish List.'), + 'title' => __('We cannot retrieve the Wish List.')->render(), + 'description' => __('We cannot retrieve the Wish List.')->render(), 'link' => $this->urlBuilder->getUrl(), 'charset' => 'UTF-8', ]; @@ -195,7 +194,7 @@ public function getRssData() */ public function getCacheKey() { - return 'rss_wishlist_data'; + return 'rss_wishlist_data_' . $this->getWishlist()->getId(); } /** @@ -215,7 +214,7 @@ public function getHeader() { $customerId = $this->getWishlist()->getCustomerId(); $customer = $this->customerFactory->create()->load($customerId); - $title = __('%1\'s Wishlist', $customer->getName()); + $title = __('%1\'s Wishlist', $customer->getName())->render(); $newUrl = $this->urlBuilder->getUrl( 'wishlist/shared/index', ['code' => $this->getWishlist()->getSharingCode()] diff --git a/app/code/Magento/Wishlist/Test/Unit/Model/Rss/WishlistTest.php b/app/code/Magento/Wishlist/Test/Unit/Model/Rss/WishlistTest.php index 98d36dea28a2a..a3aecad892968 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Model/Rss/WishlistTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Model/Rss/WishlistTest.php @@ -19,37 +19,37 @@ class WishlistTest extends \PHPUnit\Framework\TestCase protected $model; /** - * @var \Magento\Wishlist\Block\Customer\Wishlist + * @var \Magento\Wishlist\Block\Customer\Wishlist|\PHPUnit_Framework_MockObject_MockObject */ protected $wishlistBlock; /** - * @var \Magento\Rss\Model\RssFactory + * @var \Magento\Rss\Model\RssFactory|\PHPUnit_Framework_MockObject_MockObject */ protected $rssFactoryMock; /** - * @var \Magento\Framework\UrlInterface + * @var \Magento\Framework\UrlInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $urlBuilderMock; /** - * @var \Magento\Wishlist\Helper\Rss + * @var \Magento\Wishlist\Helper\Rss|\PHPUnit_Framework_MockObject_MockObject */ protected $wishlistHelperMock; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $scopeConfig; /** - * @var \Magento\Catalog\Helper\Image + * @var \Magento\Catalog\Helper\Image|\PHPUnit_Framework_MockObject_MockObject */ protected $imageHelperMock; /** - * @var \Magento\Catalog\Helper\Output + * @var \Magento\Catalog\Helper\Output|\PHPUnit_Framework_MockObject_MockObject */ protected $catalogOutputMock; @@ -59,7 +59,7 @@ class WishlistTest extends \PHPUnit\Framework\TestCase protected $layoutMock; /** - * @var \Magento\Customer\Model\CustomerFactory + * @var \Magento\Customer\Model\CustomerFactory|\PHPUnit_Framework_MockObject_MockObject */ protected $customerFactory; @@ -276,17 +276,46 @@ protected function processWishlistItemDescription($wishlistModelMock, $staticArg return $description; } + /** + * @return void + */ public function testIsAllowed() { + $customerId = 1; + $customerServiceMock = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); + $wishlist = $this->getMockBuilder(\Magento\Wishlist\Model\Wishlist::class) + ->setMethods(['getCustomerId']) + ->disableOriginalConstructor() + ->getMock(); + $wishlist->expects($this->once())->method('getCustomerId')->willReturn($customerId); + $this->wishlistHelperMock->expects($this->once())->method('getWishlist') + ->willReturn($wishlist); + $this->wishlistHelperMock->expects($this->once()) + ->method('getCustomer') + ->willReturn($customerServiceMock); + $customerServiceMock->expects($this->once())->method('getId')->willReturn($customerId); + $this->scopeConfig->expects($this->once())->method('getValue') ->with('rss/wishlist/active', \Magento\Store\Model\ScopeInterface::SCOPE_STORE) ->will($this->returnValue(true)); + $this->assertTrue($this->model->isAllowed()); } + /** + * @return void + */ public function testGetCacheKey() { - $this->assertEquals('rss_wishlist_data', $this->model->getCacheKey()); + $wishlistId = 1; + $wishlist = $this->getMockBuilder(\Magento\Wishlist\Model\Wishlist::class) + ->setMethods(['getId']) + ->disableOriginalConstructor() + ->getMock(); + $wishlist->expects($this->once())->method('getId')->willReturn($wishlistId); + $this->wishlistHelperMock->expects($this->once())->method('getWishlist')->willReturn($wishlist); + + $this->assertEquals('rss_wishlist_data_1', $this->model->getCacheKey()); } public function testGetCacheLifetime() diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/two_customers_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/two_customers_rollback.php new file mode 100644 index 0000000000000..ee68c9f9db7d8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/two_customers_rollback.php @@ -0,0 +1,31 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require 'customer_rollback.php'; + +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var \Magento\Framework\Registry $registry */ +$registry = Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var CustomerRepositoryInterface $customerRepository */ +$customerRepository = Bootstrap::getObjectManager()->get(CustomerRepositoryInterface::class); +try { + $customer = $customerRepository->get('customer_two@example.com'); + $customerRepository->delete($customer); +} catch (NoSuchEntityException $e) { + /** + * Tests which are wrapped with MySQL transaction clear all data by transaction rollback. + */ +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Rss/Controller/Feed/IndexTest.php b/dev/tests/integration/testsuite/Magento/Rss/Controller/Feed/IndexTest.php new file mode 100644 index 0000000000000..0da05b022bde0 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Rss/Controller/Feed/IndexTest.php @@ -0,0 +1,104 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Rss\Controller\Feed; + +class IndexTest extends \Magento\TestFramework\TestCase\AbstractBackendController +{ + /** + * @var \Magento\Rss\Model\UrlBuilder + */ + private $urlBuilder; + + /** + * @var \Magento\Customer\Api\CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * @var \Magento\Wishlist\Model\Wishlist + */ + private $wishlist; + + /** + * @var \Magento\Customer\Model\Session + */ + private $customerSession; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->urlBuilder = $this->_objectManager->get(\Magento\Rss\Model\UrlBuilder::class); + $this->customerRepository = $this->_objectManager->get( + \Magento\Customer\Api\CustomerRepositoryInterface::class + ); + $this->wishlist = $this->_objectManager->get(\Magento\Wishlist\Model\Wishlist::class); + $this->customerSession = $this->_objectManager->get(\Magento\Customer\Model\Session::class); + } + + /** + * Check Rss response. + * + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Wishlist/_files/two_wishlists_for_two_diff_customers.php + * @magentoConfigFixture current_store rss/wishlist/active 1 + * @magentoConfigFixture current_store rss/config/active 1 + */ + public function testRssResponse() + { + $customerEmail = 'customer@example.com'; + $customer = $this->customerRepository->get($customerEmail); + $customerId = $customer->getId(); + $this->customerSession->setCustomerId($customerId); + $wishlistId = $this->wishlist->loadByCustomerId($customerId)->getId(); + $this->dispatch($this->getLink($customerId, $customerEmail, $wishlistId)); + $body = $this->getResponse()->getBody(); + + $this->assertContains('John Smith\'s Wishlist', $body); + } + + /** + * Check Rss with incorrect wishlist id. + * + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Wishlist/_files/two_wishlists_for_two_diff_customers.php + * @magentoConfigFixture current_store rss/wishlist/active 1 + * @magentoConfigFixture current_store rss/config/active 1 + */ + public function testRssResponseWithIncorrectWishlistId() + { + $firstCustomerEmail = 'customer@example.com'; + $secondCustomerEmail = 'customer_two@example.com'; + $firstCustomer = $this->customerRepository->get($firstCustomerEmail); + $secondCustomer = $this->customerRepository->get($secondCustomerEmail); + + $firstCustomerId = $firstCustomer->getId(); + $secondCustomerId = $secondCustomer->getId(); + $this->customerSession->setCustomerId($firstCustomerId); + $wishlistId = $this->wishlist->loadByCustomerId($secondCustomerId, true)->getId(); + $this->dispatch($this->getLink($firstCustomerId, $firstCustomerEmail, $wishlistId)); + $body = $this->getResponse()->getBody(); + + $this->assertContains('<title>404 Not Found', $body); + } + + /** + * @param mixed $customerId + * @param string $customerEmail + * @param mixed $wishlistId + * @return string + */ + private function getLink($customerId, string $customerEmail, $wishlistId): string + { + return 'rss/feed/index/type/wishlist/data/' + . base64_encode($customerId . ',' . $customerEmail) + . '/wishlist_id/' . $wishlistId; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/two_wishlists_for_two_diff_customers.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/two_wishlists_for_two_diff_customers.php new file mode 100644 index 0000000000000..d12dc4a61f8f1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/two_wishlists_for_two_diff_customers.php @@ -0,0 +1,25 @@ +create(\Magento\Customer\Api\CustomerRepositoryInterface::class); +$firstCustomer = $customerRepository->get('customer@example.com'); + +$wishlistForFirstCustomer = $objectManager->create(\Magento\Wishlist\Model\Wishlist::class); +$wishlistForFirstCustomer->loadByCustomerId($firstCustomer->getId(), true); +$item = $wishlistForFirstCustomer->addNewItem($product, new \Magento\Framework\DataObject([])); +$wishlistForFirstCustomer->save(); + +$secondCustomer = $customerRepository->get('customer_two@example.com'); +$wishlistForSecondCustomer = $objectManager->create(\Magento\Wishlist\Model\Wishlist::class); +$wishlistForSecondCustomer->loadByCustomerId($secondCustomer->getId(), true); +$item = $wishlistForSecondCustomer->addNewItem($product, new \Magento\Framework\DataObject([])); +$wishlistForSecondCustomer->save(); diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/two_wishlists_for_two_diff_customers_rollback.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/two_wishlists_for_two_diff_customers_rollback.php new file mode 100644 index 0000000000000..6d2619f63c640 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/two_wishlists_for_two_diff_customers_rollback.php @@ -0,0 +1,41 @@ +get(\Magento\Framework\Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Framework\ObjectManagerInterface $objectManager */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var \Magento\Wishlist\Model\Wishlist $wishlist */ +$wishlist = $objectManager->create(\Magento\Wishlist\Model\Wishlist::class); + +/** @var CustomerRepositoryInterface $customerRepository */ +$customerRepository = $objectManager->get(CustomerRepositoryInterface::class); +try { + $firstCustomer = $customerRepository->get('customer@example.com'); + $wishlist->loadByCustomerId($firstCustomer->getId()); + $wishlist->delete(); + $secondCustomer = $customerRepository->get('customer_two@example.com'); + $wishlist->loadByCustomerId($secondCustomer->getId()); + $wishlist->delete(); +} catch (NoSuchEntityException $e) { + /** + * Tests which are wrapped with MySQL transaction clear all data by transaction rollback. + */ +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/../../../Magento/Customer/_files/two_customers_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_rollback.php'; From ca9bb265526f1461ee8647ed9de8ad81d13e36ce Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun Date: Thu, 24 Jan 2019 10:24:00 +0200 Subject: [PATCH 0345/1295] [Backport] Number of Lines in a Street Address not setting to default when you checked Use system value --- .../Model/Config/Backend/Address/Street.php | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Customer/Model/Config/Backend/Address/Street.php b/app/code/Magento/Customer/Model/Config/Backend/Address/Street.php index fc0fa3ebc073d..40a10a1db0935 100644 --- a/app/code/Magento/Customer/Model/Config/Backend/Address/Street.php +++ b/app/code/Magento/Customer/Model/Config/Backend/Address/Street.php @@ -87,15 +87,20 @@ public function afterDelete() { $result = parent::afterDelete(); - if ($this->getScope() == \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITES) { - $attribute = $this->_eavConfig->getAttribute('customer_address', 'street'); - $website = $this->_storeManager->getWebsite($this->getScopeCode()); - $attribute->setWebsite($website); - $attribute->load($attribute->getId()); - $attribute->setData('scope_multiline_count', null); - $attribute->save(); - } + $attribute = $this->_eavConfig->getAttribute('customer_address', 'street'); + switch ($this->getScope()) { + case \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITES: + $website = $this->_storeManager->getWebsite($this->getScopeCode()); + $attribute->setWebsite($website); + $attribute->load($attribute->getId()); + $attribute->setData('scope_multiline_count', null); + break; + case ScopeConfigInterface::SCOPE_TYPE_DEFAULT: + $attribute->setData('multiline_count', 2); + break; + } + $attribute->save(); return $result; } } From 71323031a355b28f51cf6520f70b78b0be09a661 Mon Sep 17 00:00:00 2001 From: Viktor Petryk Date: Thu, 24 Jan 2019 11:02:17 +0200 Subject: [PATCH 0346/1295] MAGETWO-73598: CLONE - Quans: Orders API without key/value on ADDITIONAL_INFORMATION (only values) --- .../Magento/Payment/Api/Data/PaymentAdditionalInfoInterface.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Payment/Api/Data/PaymentAdditionalInfoInterface.php b/app/code/Magento/Payment/Api/Data/PaymentAdditionalInfoInterface.php index 8afa064efd3ea..c658baece7779 100644 --- a/app/code/Magento/Payment/Api/Data/PaymentAdditionalInfoInterface.php +++ b/app/code/Magento/Payment/Api/Data/PaymentAdditionalInfoInterface.php @@ -3,7 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -declare(strict_types=1); namespace Magento\Payment\Api\Data; From 4f1f7bcbbee72db5864f647987c128be9eb2e8c2 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra Date: Thu, 24 Jan 2019 11:57:53 +0200 Subject: [PATCH 0347/1295] MAGETWO-97899: [FT] [MFTF] AdminAvailabilityCreditMemoWithNoPaymentTest fails because of bad design --- .../Mftf/ActionGroup/AdminInvoiceActionGroup.xml | 3 ++- .../Mftf/Section/AdminInvoiceMainActionsSection.xml | 2 +- .../AdminAvailabilityCreditMemoWithNoPaymentTest.xml | 12 ++++-------- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml index 39a885d790b47..1ce5c38256295 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml @@ -47,8 +47,9 @@ + + - diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceMainActionsSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceMainActionsSection.xml index f2a230adda019..48dc5e5b109fd 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceMainActionsSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceMainActionsSection.xml @@ -9,6 +9,6 @@
- +
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminAvailabilityCreditMemoWithNoPaymentTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminAvailabilityCreditMemoWithNoPaymentTest.xml index 3ad2c4cbdb6b1..9bd906a8abe08 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminAvailabilityCreditMemoWithNoPaymentTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminAvailabilityCreditMemoWithNoPaymentTest.xml @@ -72,18 +72,14 @@ - - - - - - + + - - + +
From 1ee27c5f3951117d01e47aaebf5b451b688be6ab Mon Sep 17 00:00:00 2001 From: Myroslav Dobra Date: Thu, 24 Jan 2019 12:03:58 +0200 Subject: [PATCH 0348/1295] MAGETWO-94157: Issue with products with customizable options --- lib/web/mage/dataPost.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/web/mage/dataPost.js b/lib/web/mage/dataPost.js index 5d052f12db8fb..cc56ee266e08a 100644 --- a/lib/web/mage/dataPost.js +++ b/lib/web/mage/dataPost.js @@ -57,7 +57,7 @@ define([ */ postData: function (params) { var formKey = $(this.options.formKeyInputSelector).val(), - $form; + $form, input; if (formKey) { params.data['form_key'] = formKey; @@ -67,6 +67,19 @@ define([ data: params })); + if (params.files) { + $form[0].enctype = 'multipart/form-data'; + $.each(params.files, function (key, files) { + if (files instanceof FileList) { + input = document.createElement('input'); + input.type = 'file'; + input.name = key; + input.files = files; + $form[0].appendChild(input); + } + }); + } + if (params.data.confirmation) { uiConfirm({ content: params.data.confirmationMessage, From b419a63338f9b14e91c6cf1d810de0fe573f7f09 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh Date: Thu, 24 Jan 2019 15:10:06 +0200 Subject: [PATCH 0349/1295] MAGETWO-96241: High Database Load for Sales Rule Validation --- .../Model/ResourceModel/Rule/Collection.php | 130 +++++++++++------- 1 file changed, 81 insertions(+), 49 deletions(-) diff --git a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php index 59f24fa8b6e03..5e6f3847c8e31 100644 --- a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php +++ b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php @@ -80,6 +80,8 @@ protected function _construct() } /** + * Map data for associated entities + * * @param string $entityType * @param string $objectField * @throws \Magento\Framework\Exception\LocalizedException @@ -114,6 +116,8 @@ protected function mapAssociatedEntities($entityType, $objectField) } /** + * Add website ids and customer group ids to rules data + * * @return $this * @throws \Exception * @since 100.1.0 @@ -158,60 +162,15 @@ public function setValidationFilter( $connection = $this->getConnection(); if (strlen($couponCode)) { - $select->joinLeft( - ['rule_coupons' => $this->getTable('salesrule_coupon')], - $connection->quoteInto( - 'main_table.rule_id = rule_coupons.rule_id AND main_table.coupon_type != ?', - \Magento\SalesRule\Model\Rule::COUPON_TYPE_NO_COUPON - ), - ['code'] - ); - $noCouponWhereCondition = $connection->quoteInto( - 'main_table.coupon_type = ? ', + 'main_table.coupon_type = ?', \Magento\SalesRule\Model\Rule::COUPON_TYPE_NO_COUPON ); - - $autoGeneratedCouponCondition = [ - $connection->quoteInto( - "main_table.coupon_type = ?", - \Magento\SalesRule\Model\Rule::COUPON_TYPE_AUTO - ), - $connection->quoteInto( - "rule_coupons.type = ?", - \Magento\SalesRule\Api\Data\CouponInterface::TYPE_GENERATED - ), - ]; - - $orWhereConditions = [ - "(" . implode($autoGeneratedCouponCondition, " AND ") . ")", - $connection->quoteInto( - '(main_table.coupon_type = ? AND main_table.use_auto_generation = 1 AND rule_coupons.type = 1)', - \Magento\SalesRule\Model\Rule::COUPON_TYPE_SPECIFIC - ), - $connection->quoteInto( - '(main_table.coupon_type = ? AND main_table.use_auto_generation = 0 AND rule_coupons.type = 0)', - \Magento\SalesRule\Model\Rule::COUPON_TYPE_SPECIFIC - ), - ]; - - $andWhereConditions = [ - $connection->quoteInto( - 'rule_coupons.code = ?', - $couponCode - ), - $connection->quoteInto( - '(rule_coupons.expiration_date IS NULL OR rule_coupons.expiration_date >= ?)', - $this->_date->date()->format('Y-m-d') - ), - ]; - - $orWhereCondition = implode(' OR ', $orWhereConditions); - $andWhereCondition = implode(' AND ', $andWhereConditions); + $relatedRulesIds = $this->getCouponRelatedRuleIds($couponCode); $select->where( - $noCouponWhereCondition . ' OR ((' . $orWhereCondition . ') AND ' . $andWhereCondition . ')', - null, + $noCouponWhereCondition . ' OR main_table.rule_id IN (?)', + $relatedRulesIds, Select::TYPE_CONDITION ); } else { @@ -227,6 +186,75 @@ public function setValidationFilter( return $this; } + /** + * Get rules ids related to coupon code + * + * @param string $couponCode + * @return array + */ + private function getCouponRelatedRuleIds(string $couponCode): array + { + $connection = $this->getConnection(); + $select = $connection->select()->from( + ['main_table' => $this->getTable('salesrule')], + 'rule_id' + ); + $select->joinLeft( + ['rule_coupons' => $this->getTable('salesrule_coupon')], + $connection->quoteInto( + 'main_table.rule_id = rule_coupons.rule_id AND main_table.coupon_type != ?', + \Magento\SalesRule\Model\Rule::COUPON_TYPE_NO_COUPON, + null + ) + ); + + $autoGeneratedCouponCondition = [ + $connection->quoteInto( + "main_table.coupon_type = ?", + \Magento\SalesRule\Model\Rule::COUPON_TYPE_AUTO + ), + $connection->quoteInto( + "rule_coupons.type = ?", + \Magento\SalesRule\Api\Data\CouponInterface::TYPE_GENERATED + ), + ]; + + $orWhereConditions = [ + "(" . implode($autoGeneratedCouponCondition, " AND ") . ")", + $connection->quoteInto( + '(main_table.coupon_type = ? AND main_table.use_auto_generation = 1 AND rule_coupons.type = 1)', + \Magento\SalesRule\Model\Rule::COUPON_TYPE_SPECIFIC + ), + $connection->quoteInto( + '(main_table.coupon_type = ? AND main_table.use_auto_generation = 0 AND rule_coupons.type = 0)', + \Magento\SalesRule\Model\Rule::COUPON_TYPE_SPECIFIC + ), + ]; + + $andWhereConditions = [ + $connection->quoteInto( + 'rule_coupons.code = ?', + $couponCode + ), + $connection->quoteInto( + '(rule_coupons.expiration_date IS NULL OR rule_coupons.expiration_date >= ?)', + $this->_date->date()->format('Y-m-d') + ), + ]; + + $orWhereCondition = implode(' OR ', $orWhereConditions); + $andWhereCondition = implode(' AND ', $andWhereConditions); + + $select->where( + '(' . $orWhereCondition . ') AND ' . $andWhereCondition, + null, + Select::TYPE_CONDITION + ); + $select->group('main_table.rule_id'); + + return $connection->fetchCol($select); + } + /** * Filter collection by website(s), customer group(s) and date. * Filter collection to only active rules. @@ -366,6 +394,8 @@ public function addCustomerGroupFilter($customerGroupId) } /** + * Getter for _associatedEntitiesMap property + * * @return array * @deprecated 100.1.0 */ @@ -380,6 +410,8 @@ private function getAssociatedEntitiesMap() } /** + * Getter for dateApplier property + * * @return DateApplier * @deprecated 100.1.0 */ From a3521445dd4e5cf5de8d595e50708573611fe5a2 Mon Sep 17 00:00:00 2001 From: Viktor Sevch Date: Thu, 24 Jan 2019 15:11:52 +0200 Subject: [PATCH 0350/1295] MAGETWO-94348: The cart rule cannot affect the Bundle product with Fixed Price --- .../BundleProductsOnAdminActionGroup.xml | 2 +- .../Section/AdminProductFormBundleSection.xml | 11 ++--- .../Mftf/Section/CheckoutPaymentSection.xml | 2 +- .../AdminCreateCartPriceRuleActionGroup.xml | 33 +++++++-------- .../Test/Mftf/Data/SalesRuleData.xml | 2 +- .../AdminCartPriceRulesFormSection.xml | 6 +-- ...inCartRulesAppliedForProductInCartTest.xml | 41 ++++++++----------- 7 files changed, 41 insertions(+), 56 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml index c600e80f7265f..06d7f6eadacd7 100644 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml @@ -12,7 +12,7 @@ - + diff --git a/app/code/Magento/Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml b/app/code/Magento/Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml index 84911b12e40ef..8f60227926099 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml @@ -16,13 +16,8 @@ - - - - - - - - + + + diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml index 7d27f89166254..ae8f9aa5f2aa7 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml @@ -42,7 +42,7 @@ - + diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionGroup.xml index d169f50090698..4328d3dcbfb92 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionGroup.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionGroup.xml @@ -24,34 +24,31 @@ - + - - - - + + - - + + - - - - - + + + + - + - + - - - - + + + + diff --git a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml index ab8cafc4856a7..595aedc2b604f 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml @@ -174,6 +174,6 @@ Main Website 'NOT LOGGED IN', 'General', 'Wholesale', 'Retailer' Fixed amount discount for whole cart - 50 + 0 diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml index 2a783a98d0c83..8c364879ab1f4 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml @@ -29,9 +29,9 @@ - - - + + + diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml index abfd3da9d4cd0..f5a2b0a18441e 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml @@ -11,13 +11,13 @@ - + <description value="Check that cart rules applied for product in cart"/> <severity value="MAJOR"/> <testCaseId value="MC-13629"/> <useCaseId value="MAGETWO-94348"/> - <group value="SalesRule"/> + <group value="salesRule"/> </annotations> <before> @@ -39,57 +39,50 @@ <actionGroup ref="DeleteProductOnProductsGridPageByName" stepKey="deleteProductOnProductsGridPageByName"> <argument name="product" value="BundleProduct"/> </actionGroup> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFilters"/> <actionGroup ref="DeleteCartPriceRuleByName" stepKey="deleteCartPriceRule"> <argument name="ruleName" value="{{PriceRuleWithCondition.name}}"/> </actionGroup> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFilters1"/> + <actionGroup ref="logout" stepKey="logout"/> </after> <!--Start creating a bundle product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> - <waitForPageLoad stepKey="waitForProductList"/> <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> <argument name="product" value="BundleProduct"/> </actionGroup> <!--Off dynamic price and set value--> - <click selector="{{AdminProductFormBundleSection.dynamicPrice}}" stepKey="offDynamicPrice"/> - <fillField selector="{{AdminProductFormBundleSection.priceField}}" userInput="0" stepKey="setProductPrice"/> - - <!-- Add category to product --> - <click selector="{{AdminProductFormBundleSection.categoriesDropDown}}" stepKey="dropDownCategories"/> - <fillField selector="{{AdminProductFormBundleSection.searchForCategory}}" userInput="$$createPreReqCategory.name$$" stepKey="searchForCategory"/> - <click selector="{{AdminProductFormBundleSection.selectCategory}}" stepKey="selectCategory"/> - <click selector="{{AdminProductFormBundleSection.categoriesLabel}}" stepKey="clickOnCategoriesLabelToCloseOptions"/> + <click selector="{{AdminProductFormBundleSection.priceTypeSwitcher}}" stepKey="offDynamicPrice"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="0" stepKey="setProductPrice"/> <!-- Add option, a "Radio Buttons" type option, with one product and set fixed price 200--> <actionGroup ref="CreateBundleProductForOneSimpleProductsWithRadioTypeOption" stepKey="createBundleProductWithRadioTypeOption"> <argument name="bundleProduct" value="BundleProduct"/> <argument name="simpleProductFirst" value="$$simpleProduct$$"/> - <argument name="simpleProductSecond" value=""/> </actionGroup> - <selectOption selector="{{AdminProductFormBundleSection.bundlePriceType}}" userInput="Fixed" stepKey="selectPriceType"/> - <fillField selector="{{AdminProductFormBundleSection.bundlePriceValue}}" userInput="200" stepKey="fillPriceValue"/> + <selectOption selector="{{AdminProductFormBundleSection.bundleSelectionPriceType}}" userInput="Fixed" stepKey="selectPriceType"/> + <fillField selector="{{AdminProductFormBundleSection.bundleSelectionPriceValue}}" userInput="200" stepKey="fillPriceValue"/> <actionGroup ref="saveProductForm" stepKey="saveProduct"/> <!--Create cart price rule--> - <actionGroup ref="AdminCreateCartPriceRuleWithConditions" stepKey="createRule"> + <actionGroup ref="AdminCreateCartPriceRuleWithProductSubselectionCondition" stepKey="createRule"> <argument name="rule" value="PriceRuleWithCondition"/> - <argument name="condition1" value="Products subselection"/> - <argument name="condition2" value="Category"/> - <argument name="ruleToChange1" value="is"/> - <argument name="rule1" value="equals or greater than"/> + <argument name="condition" value="Category"/> + <argument name="operation" value="equals or greater than"/> <argument name="ruleToChange2" value="..."/> - <argument name="rule2" value="2"/> - <argument name="categoryName" value="{{_defaultCategory.name}}"/> + <argument name="totalQuantity" value="2"/> + <argument name="value" value="{{_defaultCategory.name}}"/> </actionGroup> <!--Go to Storefront and add product to cart and checkout from cart--> - <amOnPage url="/$$simpleProduct.name$$.html" stepKey="goToProduct"/> - <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="2" stepKey="setQuantity"/> - <actionGroup ref="StorefrontAddToCartCustomOptionsProductPageActionGroup" stepKey="addProductToCard"> + <amOnPage url="{{StorefrontProductPage.url($$simpleProduct.name$$)}}" stepKey="goToProduct"/> + <actionGroup ref="StorefrontAddProductToCartQuantityActionGroup" stepKey="addToCart"> <argument name="productName" value="$$simpleProduct.name$$"/> + <argument name="quantity" value="2"/> </actionGroup> <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShipping"/> From f2d28eda7739b0a4e5eac4fc387bf8b2ceef2358 Mon Sep 17 00:00:00 2001 From: amol 2jcommerce <amol@2jcommerce.in> Date: Thu, 24 Jan 2019 19:35:12 +0530 Subject: [PATCH 0351/1295] Fixed-Apply-discount-code-placeholder-2.2 --- .../luma/Magento_Checkout/web/css/source/module/_cart.less | 3 +++ 1 file changed, 3 insertions(+) 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 1015bb584ff7b..67057967451d8 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 @@ -698,6 +698,9 @@ position: static; } } + &.discount { + width: auto; + } } } From 38481cbcaf59be2ba3d5fc0ce0a134918f255094 Mon Sep 17 00:00:00 2001 From: amol 2jcommerce <amol@2jcommerce.in> Date: Thu, 24 Jan 2019 19:56:54 +0530 Subject: [PATCH 0352/1295] Fixed-Wishlist-alignment-issue-at-mobile-2.2 --- .../Magento_MultipleWishlist/web/css/source/_module.less | 2 +- .../blank/Magento_Wishlist/web/css/source/_module.less | 4 ++-- .../luma/Magento_MultipleWishlist/web/css/source/_module.less | 2 +- .../Magento/luma/Magento_Wishlist/web/css/source/_module.less | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_MultipleWishlist/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_MultipleWishlist/web/css/source/_module.less index 6baa2432ff035..4ded1c5f349b6 100644 --- a/app/design/frontend/Magento/blank/Magento_MultipleWishlist/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_MultipleWishlist/web/css/source/_module.less @@ -342,7 +342,7 @@ .product { &-item { &-checkbox { - left: 20px; + left: 0px; position: absolute; top: 20px; } diff --git a/app/design/frontend/Magento/blank/Magento_Wishlist/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Wishlist/web/css/source/_module.less index 0e8350261e002..b8ae5b6713297 100644 --- a/app/design/frontend/Magento/blank/Magento_Wishlist/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Wishlist/web/css/source/_module.less @@ -177,10 +177,10 @@ .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { .products-grid.wishlist { margin-bottom: @indent__l; - margin-right: -@indent__s; + margin-right: 0; .product { &-item { - padding: @indent__base @indent__s @indent__base @indent__base; + padding: @indent__base 0 @indent__base 0; position: relative; &-photo { diff --git a/app/design/frontend/Magento/luma/Magento_MultipleWishlist/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_MultipleWishlist/web/css/source/_module.less index e10278e3abbec..9cd4d16d64214 100644 --- a/app/design/frontend/Magento/luma/Magento_MultipleWishlist/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_MultipleWishlist/web/css/source/_module.less @@ -435,7 +435,7 @@ .product { &-item { &-checkbox { - left: 20px; + left: 0px; position: absolute; top: 20px; } diff --git a/app/design/frontend/Magento/luma/Magento_Wishlist/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Wishlist/web/css/source/_module.less index 584eefb9bc643..c41270d8e427d 100644 --- a/app/design/frontend/Magento/luma/Magento_Wishlist/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Wishlist/web/css/source/_module.less @@ -185,11 +185,11 @@ .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { .products-grid.wishlist { margin-bottom: @indent__l; - margin-right: -@indent__s; + margin-right: 0; .product { &-item { - padding: @indent__base @indent__s @indent__base @indent__base; + padding: @indent__base 0 @indent__base 0; position: relative; &-photo { From f76cfd772fffb092a2eb5b7e4f417b3eb2aee8f3 Mon Sep 17 00:00:00 2001 From: Nikita Fomin <fmnnkt@gmail.com> Date: Thu, 24 Jan 2019 16:28:36 +0200 Subject: [PATCH 0353/1295] MAGETWO-93832: Automate with MFTF Copy Single Update to Another Update --- .../Backend/Test/Mftf/Section/AdminMainActionsSection.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminMainActionsSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminMainActionsSection.xml index bba375c2d6bfd..06d4a508acdd9 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminMainActionsSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminMainActionsSection.xml @@ -12,5 +12,6 @@ <element name="save" type="button" selector="#save" timeout="30"/> <element name="delete" type="button" selector="#delete"/> <element name="add" type="button" selector="#add" timeout="30"/> + <element name="back" type="button" selector="#back" timeout="30"/> </section> </sections> From 6b9224c9f43189b07e4b711ffc24095bbe0773b7 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Thu, 24 Jan 2019 17:52:25 +0200 Subject: [PATCH 0354/1295] MAGETWO-96759: Fixed incorrect displaying of the sales rule conditions --- app/code/Magento/Rule/Block/Editable.php | 6 ++++-- .../Framework/Data/Form/Element/AbstractElement.php | 8 ++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Rule/Block/Editable.php b/app/code/Magento/Rule/Block/Editable.php index 67e4671236ea0..32bcc015e2121 100644 --- a/app/code/Magento/Rule/Block/Editable.php +++ b/app/code/Magento/Rule/Block/Editable.php @@ -9,6 +9,8 @@ use Magento\Framework\View\Element\AbstractBlock; /** + * Renderer for Editable sales rules. + * * @api * @since 100.0.2 */ @@ -52,9 +54,9 @@ public function render(\Magento\Framework\Data\Form\Element\AbstractElement $ele if ($element->getShowAsText()) { $html = ' <input type="hidden" class="hidden" id="' . - $element->getHtmlId() . + $this->escapeHtmlAttr($element->getHtmlId()) . '" name="' . - $element->getName() . + $this->escapeHtmlAttr($element->getName()) . '" value="' . $element->getValue() . '" data-form-part="' . diff --git a/lib/internal/Magento/Framework/Data/Form/Element/AbstractElement.php b/lib/internal/Magento/Framework/Data/Form/Element/AbstractElement.php index ac9cdd3822ec5..1b7e9ad990ce4 100644 --- a/lib/internal/Magento/Framework/Data/Form/Element/AbstractElement.php +++ b/lib/internal/Magento/Framework/Data/Form/Element/AbstractElement.php @@ -170,7 +170,11 @@ public function setId($id) */ public function getHtmlId() { - return $this->getForm()->getHtmlIdPrefix() . $this->getData('html_id') . $this->getForm()->getHtmlIdSuffix(); + return $this->_escaper->escapeHtml( + $this->getForm()->getHtmlIdPrefix() . + $this->getData('html_id') . + $this->getForm()->getHtmlIdSuffix() + ); } /** @@ -184,7 +188,7 @@ public function getName() if ($suffix = $this->getForm()->getFieldNameSuffix()) { $name = $this->getForm()->addSuffixToName($name, $suffix); } - return $name; + return $this->_escaper->escapeHtml($name); } /** From 71b9475b7e709fa3f1031abafbc285cd61971147 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Thu, 24 Jan 2019 18:22:17 +0200 Subject: [PATCH 0355/1295] MAGETWO-73598: CLONE - Quans: Orders API without key/value on ADDITIONAL_INFORMATION (only values) --- app/code/Magento/Payment/Model/PaymentAdditionalInfo.php | 4 ++-- .../testsuite/Magento/Sales/_files/order_list.php | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Payment/Model/PaymentAdditionalInfo.php b/app/code/Magento/Payment/Model/PaymentAdditionalInfo.php index c4f135d5e0044..4ce41181a008a 100644 --- a/app/code/Magento/Payment/Model/PaymentAdditionalInfo.php +++ b/app/code/Magento/Payment/Model/PaymentAdditionalInfo.php @@ -46,7 +46,7 @@ public function getValue() public function setKey($key) { $this->key = $key; - return $key; + return $this; } /** @@ -55,6 +55,6 @@ public function setKey($key) public function setValue($value) { $this->value = $value; - return $value; + return $this; } } diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_list.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_list.php index 1f4253f18487c..221f5b559c9f2 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_list.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_list.php @@ -56,10 +56,8 @@ $orderRepository = $objectManager->create(OrderRepositoryInterface::class); /** @var array $orderData */ foreach ($orders as $orderData) { - /** @var $order \Magento\Sales\Model\Order */ - $order = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Sales\Model\Order::class - ); + /** @var Order $order */ + $order = $objectManager->create(Order::class); // Reset addresses /** @var Order\Address $billingAddress */ From ebca6b3aba89ece40077ecd44d3680116d1072cd Mon Sep 17 00:00:00 2001 From: amol 2jcommerce <amol@2jcommerce.in> Date: Fri, 25 Jan 2019 10:40:25 +0530 Subject: [PATCH 0356/1295] Fixed-Start-time-field-Date-Field-order-misaligned-in-iPad-2.2 --- .../web/css/source/module/main/_collapsible-blocks.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_collapsible-blocks.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_collapsible-blocks.less index 6420738c6fb9b..b12b4a71d5ce6 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_collapsible-blocks.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_collapsible-blocks.less @@ -322,7 +322,7 @@ } .value { - padding-right: 4rem; + padding-right: 2rem; } } From 2003ebd4d56d8032530b7264f6fab3f9cc595965 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Fri, 25 Jan 2019 08:37:49 +0200 Subject: [PATCH 0357/1295] MAGETWO-96690: Incorrect Rss Wishlist response --- .../testsuite/Magento/Rss/Controller/Feed/IndexTest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Rss/Controller/Feed/IndexTest.php b/dev/tests/integration/testsuite/Magento/Rss/Controller/Feed/IndexTest.php index 0da05b022bde0..e0c38775101a3 100644 --- a/dev/tests/integration/testsuite/Magento/Rss/Controller/Feed/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Rss/Controller/Feed/IndexTest.php @@ -7,6 +7,9 @@ namespace Magento\Rss\Controller\Feed; +/** + * Test for \Magento\Rss\Controller\Feed\Index + */ class IndexTest extends \Magento\TestFramework\TestCase\AbstractBackendController { /** @@ -50,6 +53,7 @@ protected function setUp() * @magentoDataFixture Magento/Wishlist/_files/two_wishlists_for_two_diff_customers.php * @magentoConfigFixture current_store rss/wishlist/active 1 * @magentoConfigFixture current_store rss/config/active 1 + * @return void */ public function testRssResponse() { @@ -71,6 +75,7 @@ public function testRssResponse() * @magentoDataFixture Magento/Wishlist/_files/two_wishlists_for_two_diff_customers.php * @magentoConfigFixture current_store rss/wishlist/active 1 * @magentoConfigFixture current_store rss/config/active 1 + * @return void */ public function testRssResponseWithIncorrectWishlistId() { From 3e9ba6ec155d2881e0dfe02bb30796f76fadce61 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Fri, 25 Jan 2019 10:40:39 +0200 Subject: [PATCH 0358/1295] Static test fix. --- app/code/Magento/Store/Model/WebsiteRepository.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Store/Model/WebsiteRepository.php b/app/code/Magento/Store/Model/WebsiteRepository.php index dd77291f2fc28..1b12164e42cee 100644 --- a/app/code/Magento/Store/Model/WebsiteRepository.php +++ b/app/code/Magento/Store/Model/WebsiteRepository.php @@ -78,7 +78,12 @@ public function get($code) if ($website->getId() === null) { throw new NoSuchEntityException( - __(sprintf("The website with code %s that was requested wasn't found. Verify the website and try again.", $code)) + __( + sprintf( + "The website with code %s that was requested wasn't found. Verify the website and try again.", + $code + ) + ) ); } $this->entities[$code] = $website; @@ -102,7 +107,12 @@ public function getById($id) if ($website->getId() === null) { throw new NoSuchEntityException( - __(sprintf("The website with id %s that was requested wasn't found. Verify the website and try again.", $id)) + __( + sprintf( + "The website with id %s that was requested wasn't found. Verify the website and try again.", + $id + ) + ) ); } $this->entities[$website->getCode()] = $website; From 485ad0b67779ea3a59c89daa64f0e290e3311657 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 25 Jan 2019 10:47:43 +0200 Subject: [PATCH 0359/1295] MAGETWO-95276: Varnish "Connection reset by peer" error when large catalog is reindexed on schedule --- .../CacheInvalidate/Model/PurgeCache.php | 76 +++++++++++++++++-- 1 file changed, 71 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/CacheInvalidate/Model/PurgeCache.php b/app/code/Magento/CacheInvalidate/Model/PurgeCache.php index 8acf170d43cfb..d9567a9b7f4fd 100644 --- a/app/code/Magento/CacheInvalidate/Model/PurgeCache.php +++ b/app/code/Magento/CacheInvalidate/Model/PurgeCache.php @@ -7,6 +7,9 @@ use Magento\Framework\Cache\InvalidateLogger; +/** + * Purge cache action. + */ class PurgeCache { const HEADER_X_MAGENTO_TAGS_PATTERN = 'X-Magento-Tags-Pattern'; @@ -26,6 +29,18 @@ class PurgeCache */ private $logger; + /** + * Batch size of the purge request. + * + * Based on default Varnish 4 http_req_hdr_len size minus a 512 bytes margin for method, + * header name, line feeds etc. + * + * @see https://varnish-cache.org/docs/4.1/reference/varnishd.html + * + * @var int + */ + private $requestSize = 7680; + /** * Constructor * @@ -44,18 +59,68 @@ public function __construct( } /** - * Send curl purge request - * to invalidate cache by tags pattern + * Send curl purge request to invalidate cache by tags pattern. * * @param string $tagsPattern * @return bool Return true if successful; otherwise return false */ public function sendPurgeRequest($tagsPattern) { + $successful = true; $socketAdapter = $this->socketAdapterFactory->create(); $servers = $this->cacheServer->getUris(); - $headers = [self::HEADER_X_MAGENTO_TAGS_PATTERN => $tagsPattern]; $socketAdapter->setOptions(['timeout' => 10]); + + $formattedTagsChunks = $this->splitTags($tagsPattern); + foreach ($formattedTagsChunks as $formattedTagsChunk) { + if (!$this->sendPurgeRequestToServers($socketAdapter, $servers, $formattedTagsChunk)) { + $successful = false; + } + } + + return $successful; + } + + /** + * Split tags by batches + * + * @param string $tagsPattern + * @return \Generator + */ + private function splitTags(string $tagsPattern) : \Generator + { + $tagsBatchSize = 0; + $formattedTagsChunk = []; + $formattedTags = explode('|', $tagsPattern); + foreach ($formattedTags as $formattedTag) { + if ($tagsBatchSize + strlen($formattedTag) > $this->requestSize - count($formattedTagsChunk) - 1) { + yield implode('|', array_unique($formattedTagsChunk)); + $formattedTagsChunk = []; + $tagsBatchSize = 0; + } + + $tagsBatchSize += strlen($formattedTag); + $formattedTagsChunk[] = $formattedTag; + } + if (!empty($formattedTagsChunk)) { + yield implode('|', array_unique($formattedTagsChunk)); + } + } + + /** + * Send curl purge request to servers to invalidate cache by tags pattern. + * + * @param \Zend\Http\Client\Adapter\Socket $socketAdapter + * @param \Zend\Uri\Uri[] $servers + * @param string $formattedTagsChunk + * @return bool Return true if successful; otherwise return false + */ + private function sendPurgeRequestToServers( + \Zend\Http\Client\Adapter\Socket $socketAdapter, + array $servers, + string $formattedTagsChunk + ): bool { + $headers = [self::HEADER_X_MAGENTO_TAGS_PATTERN => $formattedTagsChunk]; foreach ($servers as $server) { $headers['Host'] = $server->getHost(); try { @@ -69,12 +134,13 @@ public function sendPurgeRequest($tagsPattern) $socketAdapter->read(); $socketAdapter->close(); } catch (\Exception $e) { - $this->logger->critical($e->getMessage(), compact('server', 'tagsPattern')); + $this->logger->critical($e->getMessage(), compact('server', 'formattedTagsChunk')); + return false; } } + $this->logger->execute(compact('servers', 'formattedTagsChunk')); - $this->logger->execute(compact('servers', 'tagsPattern')); return true; } } From 720f05ce559e45b2003bc515702d8c5e154347f8 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Fri, 25 Jan 2019 11:02:02 +0200 Subject: [PATCH 0360/1295] MAGETWO-97456: Cart's customer and address mismatch --- .../Quote/Address/BillingAddressPersister.php | 9 +- .../Quote/Model/QuoteAddressValidator.php | 68 +++++++--- .../Quote/Model/ShippingAddressManagement.php | 6 +- .../Unit/Model/QuoteAddressValidatorTest.php | 8 +- .../Model/ShippingAddressManagementTest.php | 10 +- ...GuestShippingInformationManagementTest.php | 126 ++++++++++++++++++ .../Api/ShippingInformationManagementTest.php | 105 +++++++++++++++ .../_files/customer_with_addresses.php | 74 ++++++++++ .../customer_with_addresses_rollback.php | 33 +++++ 9 files changed, 406 insertions(+), 33 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Checkout/Api/GuestShippingInformationManagementTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Checkout/Api/ShippingInformationManagementTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_addresses.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_addresses_rollback.php diff --git a/app/code/Magento/Quote/Model/Quote/Address/BillingAddressPersister.php b/app/code/Magento/Quote/Model/Quote/Address/BillingAddressPersister.php index c5b8dc1c4b124..81b0bed2592ea 100644 --- a/app/code/Magento/Quote/Model/Quote/Address/BillingAddressPersister.php +++ b/app/code/Magento/Quote/Model/Quote/Address/BillingAddressPersister.php @@ -12,6 +12,9 @@ use Magento\Quote\Model\QuoteAddressValidator; use Magento\Customer\Api\AddressRepositoryInterface; +/** + * Saves billing address for quotes. + */ class BillingAddressPersister { /** @@ -37,17 +40,17 @@ public function __construct( } /** + * Save address for billing. + * * @param CartInterface $quote * @param AddressInterface $address * @param bool $useForShipping * @return void - * @throws NoSuchEntityException - * @throws InputException */ public function save(CartInterface $quote, AddressInterface $address, $useForShipping = false) { /** @var \Magento\Quote\Model\Quote $quote */ - $this->addressValidator->validate($address); + $this->addressValidator->validateForCart($quote, $address); $customerAddressId = $address->getCustomerAddressId(); $shippingAddress = null; $addressData = []; diff --git a/app/code/Magento/Quote/Model/QuoteAddressValidator.php b/app/code/Magento/Quote/Model/QuoteAddressValidator.php index 9a86829bfc4ce..3436dff6c906b 100644 --- a/app/code/Magento/Quote/Model/QuoteAddressValidator.php +++ b/app/code/Magento/Quote/Model/QuoteAddressValidator.php @@ -6,10 +6,13 @@ namespace Magento\Quote\Model; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Quote\Api\Data\AddressInterface; +use Magento\Quote\Api\Data\CartInterface; /** * Quote shipping/billing address validator service. * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class QuoteAddressValidator { @@ -28,7 +31,7 @@ class QuoteAddressValidator protected $customerRepository; /** - * @var \Magento\Customer\Model\Session + * @deprecated This class is not a part of HTML presentation layer and should not use sessions. */ protected $customerSession; @@ -50,44 +53,77 @@ public function __construct( } /** - * Validates the fields in a specified address data object. + * Validate address. * - * @param \Magento\Quote\Api\Data\AddressInterface $addressData The address data object. - * @return bool - * @throws \Magento\Framework\Exception\InputException The specified address belongs to another customer. + * @param AddressInterface $address + * @param int|null $customerId Cart belongs to + * @return void * @throws \Magento\Framework\Exception\NoSuchEntityException The specified customer ID or address ID is not valid. */ - public function validate(\Magento\Quote\Api\Data\AddressInterface $addressData) + private function doValidate(AddressInterface $address, $customerId) { //validate customer id - if ($addressData->getCustomerId()) { - $customer = $this->customerRepository->getById($addressData->getCustomerId()); + if ($customerId) { + $customer = $this->customerRepository->getById($customerId); if (!$customer->getId()) { throw new \Magento\Framework\Exception\NoSuchEntityException( - __('Invalid customer id %1', $addressData->getCustomerId()) + __('Invalid customer id %1', $customerId) ); } } - if ($addressData->getCustomerAddressId()) { + if ($address->getCustomerAddressId()) { + //Existing address cannot belong to a guest + if (!$customerId) { + throw new \Magento\Framework\Exception\NoSuchEntityException( + __('Invalid customer address id %1', $address->getCustomerAddressId()) + ); + } + //Validating address ID try { - $this->addressRepository->getById($addressData->getCustomerAddressId()); + $this->addressRepository->getById($address->getCustomerAddressId()); } catch (NoSuchEntityException $e) { throw new \Magento\Framework\Exception\NoSuchEntityException( - __('Invalid address id %1', $addressData->getId()) + __('Invalid address id %1', $address->getId()) ); } - + //Finding available customer's addresses $applicableAddressIds = array_map(function ($address) { /** @var \Magento\Customer\Api\Data\AddressInterface $address */ return $address->getId(); - }, $this->customerRepository->getById($addressData->getCustomerId())->getAddresses()); - if (!in_array($addressData->getCustomerAddressId(), $applicableAddressIds)) { + }, $this->customerRepository->getById($customerId)->getAddresses()); + if (!in_array($address->getCustomerAddressId(), $applicableAddressIds)) { throw new \Magento\Framework\Exception\NoSuchEntityException( - __('Invalid customer address id %1', $addressData->getCustomerAddressId()) + __('Invalid customer address id %1', $address->getCustomerAddressId()) ); } } + } + + /** + * Validates the fields in a specified address data object. + * + * @param \Magento\Quote\Api\Data\AddressInterface $addressData The address data object. + * @return bool + * @throws \Magento\Framework\Exception\NoSuchEntityException The specified customer ID or address ID is not valid. + */ + public function validate(AddressInterface $addressData): bool + { + $this->doValidate($addressData, $addressData->getCustomerId()); + return true; } + + /** + * Validate address to be used for cart. + * + * @param CartInterface $cart + * @param AddressInterface $address + * @return void + * @throws \Magento\Framework\Exception\NoSuchEntityException The specified customer ID or address ID is not valid. + */ + public function validateForCart(CartInterface $cart, AddressInterface $address) + { + $this->doValidate($address, $cart->getCustomerIsGuest() ? null : $cart->getCustomer()->getId()); + } } diff --git a/app/code/Magento/Quote/Model/ShippingAddressManagement.php b/app/code/Magento/Quote/Model/ShippingAddressManagement.php index 0e2be5c9e3692..8f977e30e2699 100644 --- a/app/code/Magento/Quote/Model/ShippingAddressManagement.php +++ b/app/code/Magento/Quote/Model/ShippingAddressManagement.php @@ -78,7 +78,7 @@ public function __construct( } /** - * {@inheritDoc} + * @inheritDoc * @SuppressWarnings(PHPMD.NPathComplexity) */ public function assign($cartId, \Magento\Quote\Api\Data\AddressInterface $address) @@ -94,7 +94,7 @@ public function assign($cartId, \Magento\Quote\Api\Data\AddressInterface $addres $saveInAddressBook = $address->getSaveInAddressBook() ? 1 : 0; $sameAsBilling = $address->getSameAsBilling() ? 1 : 0; $customerAddressId = $address->getCustomerAddressId(); - $this->addressValidator->validate($address); + $this->addressValidator->validateForCart($quote, $address); $quote->setShippingAddress($address); $address = $quote->getShippingAddress(); @@ -122,7 +122,7 @@ public function assign($cartId, \Magento\Quote\Api\Data\AddressInterface $addres } /** - * {@inheritDoc} + * @inheritDoc */ public function get($cartId) { diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteAddressValidatorTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteAddressValidatorTest.php index 08f5f6a808561..dfa10af7aff8e 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/QuoteAddressValidatorTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteAddressValidatorTest.php @@ -77,17 +77,13 @@ public function testValidateInvalidCustomer() /** * @expectedException \Magento\Framework\Exception\NoSuchEntityException - * @expectedExceptionMessage Invalid address id 101 + * @expectedExceptionMessage Invalid customer address id 101 */ public function testValidateInvalidAddress() { $address = $this->createMock(\Magento\Quote\Api\Data\AddressInterface::class); $this->customerRepositoryMock->expects($this->never())->method('getById'); - $address->expects($this->atLeastOnce())->method('getCustomerAddressId')->willReturn(101); - $address->expects($this->once())->method('getId')->willReturn(101); - - $this->addressRepositoryMock->expects($this->once())->method('getById') - ->willThrowException(new \Magento\Framework\Exception\NoSuchEntityException()); + $address->expects($this->exactly(2))->method('getCustomerAddressId')->willReturn(101); $this->model->validate($address); } diff --git a/app/code/Magento/Quote/Test/Unit/Model/ShippingAddressManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/ShippingAddressManagementTest.php index 59445c3999899..cc7cc49e11c81 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/ShippingAddressManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/ShippingAddressManagementTest.php @@ -110,7 +110,7 @@ public function testSetAddressValidationFailed() ->with('cart654') ->will($this->returnValue($quoteMock)); - $this->validatorMock->expects($this->once())->method('validate') + $this->validatorMock->expects($this->once())->method('validateForCart') ->will($this->throwException(new \Magento\Framework\Exception\NoSuchEntityException(__('error345')))); $this->service->assign('cart654', $this->quoteAddressMock); @@ -143,8 +143,8 @@ public function testSetAddress() ->with($customerAddressId) ->willReturn($customerAddressMock); - $this->validatorMock->expects($this->once())->method('validate') - ->with($this->quoteAddressMock) + $this->validatorMock->expects($this->once())->method('validateForCart') + ->with($quoteMock, $this->quoteAddressMock) ->willReturn(true); $quoteMock->expects($this->exactly(3))->method('getShippingAddress')->willReturn($this->quoteAddressMock); @@ -218,8 +218,8 @@ public function testSetAddressWithInabilityToSaveQuote() ->with($customerAddressId) ->willReturn($customerAddressMock); - $this->validatorMock->expects($this->once())->method('validate') - ->with($this->quoteAddressMock) + $this->validatorMock->expects($this->once())->method('validateForCart') + ->with($quoteMock, $this->quoteAddressMock) ->willReturn(true); $this->quoteAddressMock->expects($this->once())->method('getSaveInAddressBook')->willReturn(1); diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Api/GuestShippingInformationManagementTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Api/GuestShippingInformationManagementTest.php new file mode 100644 index 0000000000000..26d9651263cef --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Checkout/Api/GuestShippingInformationManagementTest.php @@ -0,0 +1,126 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Checkout\Api; + +use Magento\Checkout\Api\Data\ShippingInformationInterface; +use Magento\Checkout\Api\Data\ShippingInformationInterfaceFactory; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\Data\ShippingAssignmentInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; +use Magento\Quote\Model\QuoteIdMask; +use Magento\Quote\Model\QuoteIdMaskFactory; + +/** + * Test GuestShippingInformationManagement API. + */ +class GuestShippingInformationManagementTest extends TestCase +{ + /** + * @var GuestShippingInformationManagementInterface + */ + private $management; + + /** + * @var CartRepositoryInterface + */ + private $cartRepo; + + /** + * @var CustomerRepositoryInterface + */ + private $customerRepo; + + /** + * @var ShippingInformationInterfaceFactory + */ + private $shippingFactory; + + /** + * @var SearchCriteriaBuilder + */ + private $searchCriteria; + + /** + * @var QuoteIdMaskFactory + */ + private $maskFactory; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->management = $objectManager->get(GuestShippingInformationManagementInterface::class); + $this->cartRepo = $objectManager->get(CartRepositoryInterface::class); + $this->customerRepo = $objectManager->get(CustomerRepositoryInterface::class); + $this->shippingFactory = $objectManager->get(ShippingInformationInterfaceFactory::class); + $this->searchCriteria = $objectManager->get(SearchCriteriaBuilder::class); + $this->maskFactory = $objectManager->get(QuoteIdMaskFactory::class); + } + + /** + * Test using another address for quote. + * + * @param bool $swapShipping Whether to swap shipping or billing addresses. + * @return void + * + * @magentoDataFixture Magento/Sales/_files/quote.php + * @magentoDataFixture Magento/Customer/_files/customer_with_addresses.php + * @dataProvider differentAddressesDataProvider + * @expectedException \Magento\Framework\Exception\InputException + * @expectedExceptionMessage Unable to save shipping information. Please check input data. + */ + public function testDifferentAddresses(bool $swapShipping) + { + $carts = $this->cartRepo->getList( + $this->searchCriteria->addFilter('reserved_order_id', 'test01')->create() + )->getItems(); + $cart = array_pop($carts); + $otherCustomer = $this->customerRepo->get('customer_with_addresses@test.com'); + $otherAddresses = $otherCustomer->getAddresses(); + $otherAddress = array_pop($otherAddresses); + + //Setting invalid IDs. + /** @var ShippingAssignmentInterface $shippingAssignment */ + $shippingAssignment = $cart->getExtensionAttributes()->getShippingAssignments()[0]; + $shippingAddress = $shippingAssignment->getShipping()->getAddress(); + $billingAddress = $cart->getBillingAddress(); + if ($swapShipping) { + $address = $shippingAddress; + } else { + $address = $billingAddress; + } + $address->setCustomerAddressId($otherAddress->getId()); + $address->setCustomerId($otherCustomer->getId()); + $address->setId(null); + /** @var ShippingInformationInterface $shippingInformation */ + $shippingInformation = $this->shippingFactory->create(); + $shippingInformation->setBillingAddress($billingAddress); + $shippingInformation->setShippingAddress($shippingAddress); + $shippingInformation->setShippingMethodCode('flatrate'); + /** @var QuoteIdMask $idMask */ + $idMask = $this->maskFactory->create(); + $idMask->load($cart->getId(), 'quote_id'); + $this->management->saveAddressInformation($idMask->getMaskedId(), $shippingInformation); + } + + /** + * @return array + */ + public function differentAddressesDataProvider(): array + { + return [ + 'Shipping address swap' => [true], + 'Billing address swap' => [false], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Api/ShippingInformationManagementTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Api/ShippingInformationManagementTest.php new file mode 100644 index 0000000000000..ff795a73fec35 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Checkout/Api/ShippingInformationManagementTest.php @@ -0,0 +1,105 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Checkout\Api; + +use Magento\Checkout\Api\Data\ShippingInformationInterface; +use Magento\Checkout\Api\Data\ShippingInformationInterfaceFactory; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\Data\ShippingAssignmentInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Test ShippingInformationManagement API. + */ +class ShippingInformationManagementTest extends TestCase +{ + /** + * @var ShippingInformationManagementInterface + */ + private $management; + + /** + * @var CartRepositoryInterface + */ + private $cartRepo; + + /** + * @var CustomerRepositoryInterface + */ + private $customerRepo; + + /** + * @var ShippingInformationInterfaceFactory + */ + private $shippingFactory; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->management = $objectManager->get(ShippingInformationManagementInterface::class); + $this->cartRepo = $objectManager->get(CartRepositoryInterface::class); + $this->customerRepo = $objectManager->get(CustomerRepositoryInterface::class); + $this->shippingFactory = $objectManager->get(ShippingInformationInterfaceFactory::class); + } + + /** + * Test using another address for quote. + * + * @param bool $swapShipping Whether to swap shipping or billing addresses. + * @return void + * + * @magentoDataFixture Magento/Sales/_files/quote_with_customer.php + * @magentoDataFixture Magento/Customer/_files/customer_with_addresses.php + * @dataProvider differentAddressesDataProvider + * @expectedException \Magento\Framework\Exception\InputException + * @expectedExceptionMessage Unable to save shipping information. Please check input data. + */ + public function testDifferentAddresses(bool $swapShipping) + { + $cart = $this->cartRepo->getForCustomer(1); + $otherCustomer = $this->customerRepo->get('customer_with_addresses@test.com'); + $otherAddresses = $otherCustomer->getAddresses(); + $otherAddress = array_pop($otherAddresses); + + //Setting invalid IDs. + /** @var ShippingAssignmentInterface $shippingAssignment */ + $shippingAssignment = $cart->getExtensionAttributes()->getShippingAssignments()[0]; + $shippingAddress = $shippingAssignment->getShipping()->getAddress(); + $billingAddress = $cart->getBillingAddress(); + if ($swapShipping) { + $address = $shippingAddress; + } else { + $address = $billingAddress; + } + $address->setCustomerAddressId($otherAddress->getId()); + $address->setCustomerId($otherCustomer->getId()); + $address->setId(null); + /** @var ShippingInformationInterface $shippingInformation */ + $shippingInformation = $this->shippingFactory->create(); + $shippingInformation->setBillingAddress($billingAddress); + $shippingInformation->setShippingAddress($shippingAddress); + $shippingInformation->setShippingMethodCode('flatrate'); + $this->management->saveAddressInformation($cart->getId(), $shippingInformation); + } + + /** + * @return array + */ + public function differentAddressesDataProvider(): array + { + return [ + 'Shipping address swap' => [true], + 'Billing address swap' => [false], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_addresses.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_addresses.php new file mode 100644 index 0000000000000..a07010249319c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_addresses.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Customer\Model\CustomerRegistry; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Model\Customer; +use Magento\Customer\Model\Address; +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Model\AddressRegistry; + +$objectManager = Bootstrap::getObjectManager(); +//Creating customer +/** @var $repository CustomerRepositoryInterface */ +$repository = $objectManager->create(CustomerRepositoryInterface::class); +/** @var Customer $customer */ +$customer = $objectManager->create(Customer::class); +/** @var CustomerRegistry $customerRegistry */ +$customerRegistry = $objectManager->get(CustomerRegistry::class); +$customer->setWebsiteId(1) + ->setEmail('customer_with_addresses@test.com') + ->setPassword('password') + ->setGroupId(1) + ->setStoreId(1) + ->setIsActive(1) + ->setPrefix('Mr.') + ->setFirstname('John') + ->setMiddlename('A') + ->setLastname('Smith') + ->setSuffix('Esq.') + ->setDefaultBilling(1) + ->setDefaultShipping(1) + ->setTaxvat('12') + ->setGender(0); + +$customer->isObjectNew(true); +$customer->save(); +$customerRegistry->remove($customer->getId()); + +//Creating address +/** @var Address $customerAddress */ +$customerAddress = $objectManager->create(Address::class); +$customerAddress->isObjectNew(true); +$customerAddress->setData( + [ + 'attribute_set_id' => 2, + 'telephone' => 3468676, + 'postcode' => 75477, + 'country_id' => 'US', + 'city' => 'CityM', + 'company' => 'CompanyName', + 'street' => 'CustomerAddress1', + 'lastname' => 'Smith', + 'firstname' => 'John', + 'parent_id' => $customer->getId(), + 'region_id' => 1, + ] +); +$customerAddress->save(); +/** @var AddressRepositoryInterface $addressRepository */ +$addressRepository = $objectManager->get(AddressRepositoryInterface::class); +$customerAddress = $addressRepository->getById($customerAddress->getId()); +$customerAddress->setCustomerId($customer->getId()); +$customerAddress->isDefaultBilling(true); +$customerAddress->setIsDefaultShipping(true); +$customerAddress = $addressRepository->save($customerAddress); +$customerRegistry->remove($customerAddress->getCustomerId()); +/** @var AddressRegistry $addressRegistry */ +$addressRegistry = $objectManager->get(AddressRegistry::class); +$addressRegistry->remove($customerAddress->getId()); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_addresses_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_addresses_rollback.php new file mode 100644 index 0000000000000..45e70f9c3c852 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_addresses_rollback.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var CustomerRepositoryInterface $customerRepo */ +$customerRepo = $objectManager->get(CustomerRepositoryInterface::class); +try { + $customer = $customerRepo->get('customer_with_addresses@test.com'); + /** @var AddressRepositoryInterface $addressRepo */ + $addressRepo = $objectManager->get(AddressRepositoryInterface::class); + foreach ($customer->getAddresses() as $address) { + $addressRepo->delete($address); + } + $customerRepo->delete($customer); +} catch (NoSuchEntityException $exception) { + //Already deleted +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); From cf316b4ade0d22282ef154f71d3c673501f39f63 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Fri, 25 Jan 2019 11:09:59 +0200 Subject: [PATCH 0361/1295] MAGETWO-95176: Detaching category from product causes massive product url regeneration --- ...ategoryProcessUrlRewriteSavingObserver.php | 53 ++++++++++++++++--- .../Observer/UrlRewriteHandler.php | 36 +++++++++++-- 2 files changed, 77 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteSavingObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteSavingObserver.php index 5130b43333d47..745c302d619a1 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteSavingObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteSavingObserver.php @@ -100,16 +100,19 @@ public function execute(\Magento\Framework\Event\Observer $observer) } $mapsGenerated = false; - if ($category->dataHasChangedFor('url_key') - || $category->dataHasChangedFor('is_anchor') - || $category->getChangedProductIds() - ) { + if ($this->isCategoryHasChanged($category)) { if ($category->dataHasChangedFor('url_key')) { $categoryUrlRewriteResult = $this->categoryUrlRewriteGenerator->generate($category); $this->urlRewriteBunchReplacer->doBunchReplace($categoryUrlRewriteResult); } - $productUrlRewriteResult = $this->urlRewriteHandler->generateProductUrlRewrites($category); - $this->urlRewriteBunchReplacer->doBunchReplace($productUrlRewriteResult); + if ($this->isChangedOnlyProduct($category)) { + $productUrlRewriteResult = + $this->urlRewriteHandler->updateProductUrlRewritesForChangedProduct($category); + $this->urlRewriteBunchReplacer->doBunchReplace($productUrlRewriteResult); + } else { + $productUrlRewriteResult = $this->urlRewriteHandler->generateProductUrlRewrites($category); + $this->urlRewriteBunchReplacer->doBunchReplace($productUrlRewriteResult); + } $mapsGenerated = true; } @@ -120,8 +123,42 @@ public function execute(\Magento\Framework\Event\Observer $observer) } /** - * in case store_id is not set for category then we can assume that it was passed through product import. - * store group must have only one root category, so receiving category's path and checking if one of it parts + * Check is category changed changed. + * + * @param Category $category + * @return bool + */ + private function isCategoryHasChanged(Category $category): bool + { + if ($category->dataHasChangedFor('url_key') + || $category->dataHasChangedFor('is_anchor') + || !empty($category->getChangedProductIds())) { + return true; + } + + return false; + } + + /** + * Check is only product changed. + * + * @param Category $category + * @return bool + */ + private function isChangedOnlyProduct(Category $category): bool + { + if (!empty($category->getChangedProductIds()) + && !$category->dataHasChangedFor('is_anchor') + && !$category->dataHasChangedFor('url_key')) { + return true; + } + + return false; + } + + /** + * In case store_id is not set for category then we can assume that it was passed through product import. + * Store group must have only one root category, so receiving category's path and checking if one of it parts * is the root category for store group, we can set default_store_id value from it to category. * it prevents urls duplication for different stores * ("Default Category/category/sub" and "Default Category2/category/sub") diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php b/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php index 18360dedf0693..b4a35f323e1bc 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php @@ -24,6 +24,8 @@ use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; /** + * Class for management url rewrites. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class UrlRewriteHandler @@ -125,7 +127,7 @@ public function generateProductUrlRewrites(Category $category): array { $mergeDataProvider = clone $this->mergeDataProviderPrototype; $this->isSkippedProduct[$category->getEntityId()] = []; - $saveRewriteHistory = $category->getData('save_rewrites_history'); + $saveRewriteHistory = (bool)$category->getData('save_rewrites_history'); $storeId = (int)$category->getStoreId(); if ($category->getChangedProductIds()) { @@ -156,6 +158,30 @@ public function generateProductUrlRewrites(Category $category): array } /** + * Update product url rewrites for changed product. + * + * @param Category $category + * @return array + */ + public function updateProductUrlRewritesForChangedProduct(Category $category): array + { + $mergeDataProvider = clone $this->mergeDataProviderPrototype; + $this->isSkippedProduct[$category->getEntityId()] = []; + $saveRewriteHistory = (bool)$category->getData('save_rewrites_history'); + $storeIds = $this->getCategoryStoreIds($category); + + if ($category->getChangedProductIds()) { + foreach ($storeIds as $storeId) { + $this->generateChangedProductUrls($mergeDataProvider, $category, (int)$storeId, $saveRewriteHistory); + } + } + + return $mergeDataProvider->getData(); + } + + /** + * Delete category rewrites for children. + * * @param Category $category * @return void */ @@ -184,6 +210,8 @@ public function deleteCategoryRewritesForChildren(Category $category) } /** + * Get category products url rewrites. + * * @param Category $category * @param int $storeId * @param bool $saveRewriteHistory @@ -230,15 +258,15 @@ private function getCategoryProductsUrlRewrites( * * @param MergeDataProvider $mergeDataProvider * @param Category $category - * @param Product $product * @param int $storeId - * @param $saveRewriteHistory + * @param bool $saveRewriteHistory + * @return void */ private function generateChangedProductUrls( MergeDataProvider $mergeDataProvider, Category $category, int $storeId, - $saveRewriteHistory + bool $saveRewriteHistory ) { $this->isSkippedProduct[$category->getEntityId()] = $category->getAffectedProductIds(); From de76c07b955d96c0fc441a89f295f62ab79553b7 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Fri, 25 Jan 2019 11:10:59 +0200 Subject: [PATCH 0362/1295] MAGETWO-94348: The cart rule cannot affect the Bundle product with Fixed Price --- .../Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml | 5 ++++- .../Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml index 06d7f6eadacd7..7a428ea80476d 100644 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml @@ -12,7 +12,7 @@ <arguments> <argument name="bundleProduct"/> <argument name="simpleProductFirst"/> - <argument name="simpleProductSecond" type="string" defaultValue=""/> + <argument name="simpleProductSecond"/> </arguments> <fillField userInput="{{bundleProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillProductSku"/> <fillField userInput="{{bundleProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillProductName"/> @@ -53,6 +53,9 @@ </actionGroup> <actionGroup name="CreateBundleProductForOneSimpleProductsWithRadioTypeOption" extends="CreateBundleProductForTwoSimpleProducts"> + <arguments> + <argument name="simpleProductSecond" type="string" defaultValue=""/> + </arguments> <remove keyForRemoval="clickOnFiltersButton2"/> <remove keyForRemoval="clearFilters2"/> <remove keyForRemoval="fillNameFilter2"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml index f5a2b0a18441e..f09c8077ba2b6 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml @@ -63,6 +63,7 @@ <actionGroup ref="CreateBundleProductForOneSimpleProductsWithRadioTypeOption" stepKey="createBundleProductWithRadioTypeOption"> <argument name="bundleProduct" value="BundleProduct"/> <argument name="simpleProductFirst" value="$$simpleProduct$$"/> + <argument name="simpleProductSecond" value=""/> </actionGroup> <selectOption selector="{{AdminProductFormBundleSection.bundleSelectionPriceType}}" userInput="Fixed" stepKey="selectPriceType"/> <fillField selector="{{AdminProductFormBundleSection.bundleSelectionPriceValue}}" userInput="200" stepKey="fillPriceValue"/> From 3b17822285e54135aafb8c35828bf4816f74297e Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 25 Jan 2019 11:24:17 +0200 Subject: [PATCH 0363/1295] MAGETWO-73625: Products change in one category will cause cache miss for other categories (Only reproducible by unassigning products from backend category page) --- app/code/Magento/CacheInvalidate/Model/PurgeCache.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CacheInvalidate/Model/PurgeCache.php b/app/code/Magento/CacheInvalidate/Model/PurgeCache.php index d9567a9b7f4fd..8e0c00b587460 100644 --- a/app/code/Magento/CacheInvalidate/Model/PurgeCache.php +++ b/app/code/Magento/CacheInvalidate/Model/PurgeCache.php @@ -94,7 +94,7 @@ private function splitTags(string $tagsPattern) : \Generator $formattedTags = explode('|', $tagsPattern); foreach ($formattedTags as $formattedTag) { if ($tagsBatchSize + strlen($formattedTag) > $this->requestSize - count($formattedTagsChunk) - 1) { - yield implode('|', array_unique($formattedTagsChunk)); + yield implode('|', $formattedTagsChunk); $formattedTagsChunk = []; $tagsBatchSize = 0; } @@ -103,7 +103,7 @@ private function splitTags(string $tagsPattern) : \Generator $formattedTagsChunk[] = $formattedTag; } if (!empty($formattedTagsChunk)) { - yield implode('|', array_unique($formattedTagsChunk)); + yield implode('|', $formattedTagsChunk); } } From 7bf7ce1c3b33a7e0fc9b7c095865dce3bb1f7209 Mon Sep 17 00:00:00 2001 From: Dipti 2Jcommerce <dipti@2jcommerce.in> Date: Fri, 25 Jan 2019 15:04:31 +0530 Subject: [PATCH 0364/1295] admin-order-info-issue2.2 --- .../Sales/view/adminhtml/templates/order/view/info.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/view/info.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/view/info.phtml index 5384a00dc894d..bbd6394097f9e 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/view/info.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/view/info.phtml @@ -104,7 +104,7 @@ $customerUrl = $block->getCustomerViewUrl(); <?php if ($order->getBaseCurrencyCode() != $order->getOrderCurrencyCode()): ?> <tr> <th><?= $block->escapeHtml(__('%1 / %2 rate:', $order->getOrderCurrencyCode(), $order->getBaseCurrencyCode())) ?></th> - <th><?= $block->escapeHtml($order->getBaseToOrderRate()) ?></th> + <td><?= $block->escapeHtml($order->getBaseToOrderRate()) ?></td> </tr> <?php endif; ?> </table> From c8670b2b0c34193e0e60f5c49ad2f954531b81be Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Fri, 25 Jan 2019 13:58:53 +0200 Subject: [PATCH 0365/1295] MAGETWO-96690: Incorrect Rss Wishlist response --- app/code/Magento/Wishlist/Model/Rss/Wishlist.php | 4 ++-- .../Magento/Customer/_files/two_customers_rollback.php | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Wishlist/Model/Rss/Wishlist.php b/app/code/Magento/Wishlist/Model/Rss/Wishlist.php index 4b3f45c0bbda1..e85a3f3d4bb00 100644 --- a/app/code/Magento/Wishlist/Model/Rss/Wishlist.php +++ b/app/code/Magento/Wishlist/Model/Rss/Wishlist.php @@ -115,8 +115,8 @@ public function __construct( */ public function isAllowed() { - return (bool)$this->scopeConfig->getValue('rss/wishlist/active', ScopeInterface::SCOPE_STORE) - && $this->getWishlist()->getCustomerId() === $this->wishlistHelper->getCustomer()->getId(); + return (bool)$this->scopeConfig->isSetFlag('rss/wishlist/active', ScopeInterface::SCOPE_STORE) + && $this->getWishlist()->getCustomerId() == $this->wishlistHelper->getCustomer()->getId(); } /** diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/two_customers_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/two_customers_rollback.php index ee68c9f9db7d8..cde7569cc2467 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/_files/two_customers_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/two_customers_rollback.php @@ -22,9 +22,7 @@ $customer = $customerRepository->get('customer_two@example.com'); $customerRepository->delete($customer); } catch (NoSuchEntityException $e) { - /** - * Tests which are wrapped with MySQL transaction clear all data by transaction rollback. - */ + /** Tests which are wrapped with MySQL transaction clear all data by transaction rollback. */ } $registry->unregister('isSecureArea'); From b2f20e2c3c6602f1f7601a6e8cd40baa8422fb40 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Fri, 25 Jan 2019 14:24:53 +0200 Subject: [PATCH 0366/1295] MAGETWO-94348: The cart rule cannot affect the Bundle product with Fixed Price --- .../Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml index 7a428ea80476d..c600e80f7265f 100644 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml @@ -53,9 +53,6 @@ </actionGroup> <actionGroup name="CreateBundleProductForOneSimpleProductsWithRadioTypeOption" extends="CreateBundleProductForTwoSimpleProducts"> - <arguments> - <argument name="simpleProductSecond" type="string" defaultValue=""/> - </arguments> <remove keyForRemoval="clickOnFiltersButton2"/> <remove keyForRemoval="clearFilters2"/> <remove keyForRemoval="fillNameFilter2"/> From 827ff88002066cb0a4b61efeaaf6ce297cfd4370 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Fri, 25 Jan 2019 14:37:09 +0200 Subject: [PATCH 0367/1295] MAGETWO-94348: The cart rule cannot affect the Bundle product with Fixed Price --- .../Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml index f09c8077ba2b6..d14963d375fa5 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml @@ -63,7 +63,7 @@ <actionGroup ref="CreateBundleProductForOneSimpleProductsWithRadioTypeOption" stepKey="createBundleProductWithRadioTypeOption"> <argument name="bundleProduct" value="BundleProduct"/> <argument name="simpleProductFirst" value="$$simpleProduct$$"/> - <argument name="simpleProductSecond" value=""/> + <argument name="simpleProductSecond"/> </actionGroup> <selectOption selector="{{AdminProductFormBundleSection.bundleSelectionPriceType}}" userInput="Fixed" stepKey="selectPriceType"/> <fillField selector="{{AdminProductFormBundleSection.bundleSelectionPriceValue}}" userInput="200" stepKey="fillPriceValue"/> From ea27437b3763735236c157bcf621b2c8dd876b4c Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Fri, 25 Jan 2019 15:09:07 +0200 Subject: [PATCH 0368/1295] MAGETWO-96690: Incorrect Rss Wishlist response --- app/code/Magento/Wishlist/Test/Unit/Model/Rss/WishlistTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Wishlist/Test/Unit/Model/Rss/WishlistTest.php b/app/code/Magento/Wishlist/Test/Unit/Model/Rss/WishlistTest.php index a3aecad892968..763812ce39dab 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Model/Rss/WishlistTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Model/Rss/WishlistTest.php @@ -295,7 +295,7 @@ public function testIsAllowed() ->willReturn($customerServiceMock); $customerServiceMock->expects($this->once())->method('getId')->willReturn($customerId); - $this->scopeConfig->expects($this->once())->method('getValue') + $this->scopeConfig->expects($this->once())->method('isSetFlag') ->with('rss/wishlist/active', \Magento\Store\Model\ScopeInterface::SCOPE_STORE) ->will($this->returnValue(true)); From ef635bf6b8ecf67c729deb38df05c81b9e5b5359 Mon Sep 17 00:00:00 2001 From: Graham Wharton <graham@gwharton.me.uk> Date: Fri, 25 Jan 2019 13:21:24 +0000 Subject: [PATCH 0369/1295] Changed references to "Store" to "Scope" in framework components. --- .../Sales/Model/Order/Email/SenderBuilder.php | 2 +- .../Model/Order/Email/SenderBuilderTest.php | 8 ++++---- .../Mail/Template/TransportBuilder.php | 17 ++++++++--------- .../Test/Unit/Template/TransportBuilderTest.php | 8 ++++---- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php b/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php index e4dc75ade7c79..5fb89b7855056 100644 --- a/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php +++ b/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php @@ -103,7 +103,7 @@ protected function configureEmailTemplate() $this->transportBuilder->setTemplateIdentifier($this->templateContainer->getTemplateId()); $this->transportBuilder->setTemplateOptions($this->templateContainer->getTemplateOptions()); $this->transportBuilder->setTemplateVars($this->templateContainer->getTemplateVars()); - $this->transportBuilder->setFromByStore( + $this->transportBuilder->setFromByScope( $this->identityContainer->getEmailIdentity(), $this->identityContainer->getStore()->getId() ); diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php index 759d60d9e6613..24cd54e3a46b3 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php @@ -76,7 +76,7 @@ protected function setUp() 'setTemplateIdentifier', 'setTemplateOptions', 'setTemplateVars', - 'setFromByStore', + 'setFromByScope', ] ); @@ -103,7 +103,7 @@ protected function setUp() ->method('getEmailIdentity') ->will($this->returnValue($emailIdentity)); $this->transportBuilder->expects($this->once()) - ->method('setFromByStore') + ->method('setFromByScope') ->with($this->equalTo($emailIdentity), 1); $this->identityContainerMock->expects($this->once()) @@ -146,7 +146,7 @@ public function testSend() ->method('getId') ->willReturn(1); $this->transportBuilder->expects($this->once()) - ->method('setFromByStore') + ->method('setFromByScope') ->with($identity, 1); $this->transportBuilder->expects($this->once()) ->method('addTo') @@ -176,7 +176,7 @@ public function testSendCopyTo() ->method('addTo') ->with($this->equalTo('example@mail.com')); $this->transportBuilder->expects($this->once()) - ->method('setFromByStore') + ->method('setFromByScope') ->with($identity, 1); $this->identityContainerMock->expects($this->once()) ->method('getStore') diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php index 25cbaf73b8d76..b5be1cbf52cfd 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php @@ -173,29 +173,28 @@ public function setReplyTo($email, $name = null) /** * Set mail from address * - * @deprecated This function sets the from address for the first store only. - * new function setFromByStore introduced to allow setting of from address - * based on store. - * @see setFromByStore() + * @deprecated This function sets the from address but does not provide + * a way of setting the correct from addresses based on the scope. + * @see setFromByScope() * * @param string|array $from * @return $this */ public function setFrom($from) { - return $this->setFromByStore($from, null); + return $this->setFromByScope($from, null); } /** - * Set mail from address by store + * Set mail from address by Scope * * @param string|array $from - * @param string|int $store + * @param string|int $scopeId * @return $this */ - public function setFromByStore($from, $store = null) + public function setFromByScope($from, $scopeId = null) { - $result = $this->_senderResolver->resolve($from, $store); + $result = $this->_senderResolver->resolve($from, $scopeId); $this->message->setFromAddress($result['email'], $result['name']); return $this; } diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php index 77c41cceeb285..6c5c48fba1b9b 100644 --- a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php +++ b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php @@ -164,20 +164,20 @@ public function getTransportDataProvider() /** * @return void */ - public function testSetFromByStore() + public function testSetFromByScope() { $sender = ['email' => 'from@example.com', 'name' => 'name']; - $store = 1; + $scopeId = 1; $this->senderResolverMock->expects($this->once()) ->method('resolve') - ->with($sender, $store) + ->with($sender, $scopeId) ->willReturn($sender); $this->messageMock->expects($this->once()) ->method('setFromAddress') ->with('from@example.com', 'name') ->willReturnSelf(); - $this->builder->setFromByStore($sender, $store); + $this->builder->setFromByScope($sender, $scopeId); } /** From c3d5010efe9b2ce61220ef9fec3a98c3952276fc Mon Sep 17 00:00:00 2001 From: U1PR01 <nirav@krishtechnolabs.com> Date: Fri, 25 Jan 2019 19:38:17 +0530 Subject: [PATCH 0370/1295] Fix static test. --- .../Magento/Quote/Model/Quote/Address/Total/Shipping.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Quote/Model/Quote/Address/Total/Shipping.php b/app/code/Magento/Quote/Model/Quote/Address/Total/Shipping.php index 2c8ed6f901fa9..e9a63dad6e169 100644 --- a/app/code/Magento/Quote/Model/Quote/Address/Total/Shipping.php +++ b/app/code/Magento/Quote/Model/Quote/Address/Total/Shipping.php @@ -9,6 +9,9 @@ use Magento\Quote\Api\Data\AddressInterface; use Magento\Quote\Model\Quote\Address\FreeShippingInterface; +/** + * Collect totals for shipping. + */ class Shipping extends \Magento\Quote\Model\Quote\Address\Total\AbstractTotal { /** @@ -227,7 +230,7 @@ private function getAssignmentWeightData(AddressInterface $address, array $items * @param bool $addressFreeShipping * @param float $itemWeight * @param float $itemQty - * @param $freeShipping + * @param bool $freeShipping * @return float */ private function getItemRowWeight( From 6a22b76c15b3c1deedc6afe2b9dc205fdeaf5e6c Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Fri, 25 Jan 2019 15:45:27 +0200 Subject: [PATCH 0371/1295] MAGETWO-95368: Synonyms controller minor fix --- .../Adminhtml/Synonyms/MassDelete.php | 11 ++- .../Adminhtml/Synonyms/MassDeleteTest.php | 93 +++++++++++++++++++ 2 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Synonyms/MassDeleteTest.php diff --git a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/MassDelete.php b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/MassDelete.php index f2770f77cc533..f0248dc7225bb 100644 --- a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/MassDelete.php +++ b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/MassDelete.php @@ -6,8 +6,10 @@ namespace Magento\Search\Controller\Adminhtml\Synonyms; +use Magento\Framework\Exception\NotFoundException; + /** - * Mass-Delete Controller + * Mass-Delete Controller. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -56,13 +58,17 @@ public function __construct( } /** - * Execute action + * Execute action. * * @return \Magento\Backend\Model\View\Result\Redirect * @throws \Magento\Framework\Exception\LocalizedException|\Exception */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found.')); + } + $collection = $this->filter->getCollection($this->collectionFactory->create()); $collectionSize = $collection->getSize(); @@ -88,6 +94,7 @@ public function execute() } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(\Magento\Framework\Controller\ResultFactory::TYPE_REDIRECT); + return $resultRedirect->setPath('*/*/'); } } diff --git a/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Synonyms/MassDeleteTest.php b/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Synonyms/MassDeleteTest.php new file mode 100644 index 0000000000000..4023811d8d2cd --- /dev/null +++ b/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Synonyms/MassDeleteTest.php @@ -0,0 +1,93 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Search\Test\Unit\Controller\Adminhtml\Synonyms; + +/** + * Unit tests for MassDelete controller. + */ +class MassDeleteTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Search\Controller\Adminhtml\Synonyms\MassDelete + */ + private $controller; + + /** + * @var \Magento\Backend\App\Action\Context|\PHPUnit_Framework_MockObject_MockObject + */ + private $contextMock; + + /** + * @var \Magento\Ui\Component\MassAction\Filter|\PHPUnit_Framework_MockObject_MockObject + */ + private $filterMock; + + /** + * @var \Magento\Search\Model\ResourceModel\SynonymGroup\CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $collectionFactoryMock; + + /** + * @var \Magento\Search\Api\SynonymGroupRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $synGroupRepositoryMock; + + /** + * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $requestMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->requestMock = $this->getMockForAbstractClass( + \Magento\Framework\App\RequestInterface::class, + [], + '', + false, + true, + true, + ['isPost'] + ); + $this->contextMock = $this->createMock(\Magento\Backend\App\Action\Context::class); + $this->filterMock = $this->createMock(\Magento\Ui\Component\MassAction\Filter::class); + $this->collectionFactoryMock = $this->createMock( + \Magento\Search\Model\ResourceModel\SynonymGroup\CollectionFactory::class + ); + $this->synGroupRepositoryMock = $this->createMock(\Magento\Search\Api\SynonymGroupRepositoryInterface::class); + + $this->contextMock->expects($this->once())->method('getRequest')->willReturn($this->requestMock); + + $this->controller = $objectManagerHelper->getObject( + \Magento\Search\Controller\Adminhtml\Synonyms\MassDelete::class, + [ + 'context' => $this->contextMock, + 'filter' => $this->filterMock, + 'collectionFactory' => $this->collectionFactoryMock, + 'synGroupRepository' => $this->synGroupRepositoryMock, + ] + ); + } + + /** + * Check that error throws when request is not POST. + * + * @return void + * @expectedException \Magento\Framework\Exception\NotFoundException + */ + public function testExecuteWithNotPostRequest() + { + $this->requestMock->expects($this->once())->method('isPost')->willReturn(false); + + $this->controller->execute(); + } +} From 50c4d10c829e115a1678a8a1ea8896287f3f1acd Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Fri, 25 Jan 2019 16:54:36 +0200 Subject: [PATCH 0372/1295] MAGETWO-97456: Cart's customer and address mismatch --- .../Quote/Model/QuoteAddressValidator.php | 17 ++++++++--------- .../Unit/Model/QuoteAddressValidatorTest.php | 3 +-- .../_files/customer_with_addresses_rollback.php | 3 --- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Quote/Model/QuoteAddressValidator.php b/app/code/Magento/Quote/Model/QuoteAddressValidator.php index 3436dff6c906b..3aea3f9c197fe 100644 --- a/app/code/Magento/Quote/Model/QuoteAddressValidator.php +++ b/app/code/Magento/Quote/Model/QuoteAddressValidator.php @@ -58,15 +58,16 @@ public function __construct( * @param AddressInterface $address * @param int|null $customerId Cart belongs to * @return void - * @throws \Magento\Framework\Exception\NoSuchEntityException The specified customer ID or address ID is not valid. + * @throws NoSuchEntityException The specified customer ID or address ID is not valid. */ private function doValidate(AddressInterface $address, $customerId) { //validate customer id if ($customerId) { - $customer = $this->customerRepository->getById($customerId); - if (!$customer->getId()) { - throw new \Magento\Framework\Exception\NoSuchEntityException( + try { + $customer = $this->customerRepository->getById($customerId); + } catch (NoSuchEntityException $exception) { + throw new NoSuchEntityException( __('Invalid customer id %1', $customerId) ); } @@ -75,7 +76,7 @@ private function doValidate(AddressInterface $address, $customerId) if ($address->getCustomerAddressId()) { //Existing address cannot belong to a guest if (!$customerId) { - throw new \Magento\Framework\Exception\NoSuchEntityException( + throw new NoSuchEntityException( __('Invalid customer address id %1', $address->getCustomerAddressId()) ); } @@ -83,7 +84,7 @@ private function doValidate(AddressInterface $address, $customerId) try { $this->addressRepository->getById($address->getCustomerAddressId()); } catch (NoSuchEntityException $e) { - throw new \Magento\Framework\Exception\NoSuchEntityException( + throw new NoSuchEntityException( __('Invalid address id %1', $address->getId()) ); } @@ -93,7 +94,7 @@ private function doValidate(AddressInterface $address, $customerId) return $address->getId(); }, $this->customerRepository->getById($customerId)->getAddresses()); if (!in_array($address->getCustomerAddressId(), $applicableAddressIds)) { - throw new \Magento\Framework\Exception\NoSuchEntityException( + throw new NoSuchEntityException( __('Invalid customer address id %1', $address->getCustomerAddressId()) ); } @@ -105,7 +106,6 @@ private function doValidate(AddressInterface $address, $customerId) * * @param \Magento\Quote\Api\Data\AddressInterface $addressData The address data object. * @return bool - * @throws \Magento\Framework\Exception\NoSuchEntityException The specified customer ID or address ID is not valid. */ public function validate(AddressInterface $addressData): bool { @@ -120,7 +120,6 @@ public function validate(AddressInterface $addressData): bool * @param CartInterface $cart * @param AddressInterface $address * @return void - * @throws \Magento\Framework\Exception\NoSuchEntityException The specified customer ID or address ID is not valid. */ public function validateForCart(CartInterface $cart, AddressInterface $address) { diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteAddressValidatorTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteAddressValidatorTest.php index dfa10af7aff8e..02210e9afa78a 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/QuoteAddressValidatorTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteAddressValidatorTest.php @@ -71,7 +71,7 @@ public function testValidateInvalidCustomer() $address->expects($this->atLeastOnce())->method('getCustomerId')->willReturn($customerId); $this->customerRepositoryMock->expects($this->once())->method('getById')->with($customerId) - ->willReturn($customerMock); + ->willThrowException(new \Magento\Framework\Exception\NoSuchEntityException()); $this->model->validate($address); } @@ -111,7 +111,6 @@ public function testValidateWithValidAddress() $customerAddress = $this->createMock(\Magento\Quote\Api\Data\AddressInterface::class); $this->customerRepositoryMock->expects($this->exactly(2))->method('getById')->willReturn($customerMock); - $customerMock->expects($this->once())->method('getId')->willReturn($addressCustomer); $this->addressRepositoryMock->expects($this->once())->method('getById')->willReturn($this->quoteAddressMock); $this->quoteAddressMock->expects($this->any())->method('getCustomerId')->willReturn($addressCustomer); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_addresses_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_addresses_rollback.php index 45e70f9c3c852..fa44ee4c84ece 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_addresses_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_addresses_rollback.php @@ -22,9 +22,6 @@ $customer = $customerRepo->get('customer_with_addresses@test.com'); /** @var AddressRepositoryInterface $addressRepo */ $addressRepo = $objectManager->get(AddressRepositoryInterface::class); - foreach ($customer->getAddresses() as $address) { - $addressRepo->delete($address); - } $customerRepo->delete($customer); } catch (NoSuchEntityException $exception) { //Already deleted From 842dc16d3299687a893d151d82be2692e8935c92 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Fri, 25 Jan 2019 17:01:53 +0200 Subject: [PATCH 0373/1295] MAGETWO-97456: Cart's customer and address mismatch --- .../Quote/Model/QuoteAddressValidator.php | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Quote/Model/QuoteAddressValidator.php b/app/code/Magento/Quote/Model/QuoteAddressValidator.php index 3aea3f9c197fe..aabd69337538a 100644 --- a/app/code/Magento/Quote/Model/QuoteAddressValidator.php +++ b/app/code/Magento/Quote/Model/QuoteAddressValidator.php @@ -67,26 +67,20 @@ private function doValidate(AddressInterface $address, $customerId) try { $customer = $this->customerRepository->getById($customerId); } catch (NoSuchEntityException $exception) { - throw new NoSuchEntityException( - __('Invalid customer id %1', $customerId) - ); + throw new NoSuchEntityException(__('Invalid customer id %1', $customerId)); } } if ($address->getCustomerAddressId()) { //Existing address cannot belong to a guest if (!$customerId) { - throw new NoSuchEntityException( - __('Invalid customer address id %1', $address->getCustomerAddressId()) - ); + throw new NoSuchEntityException(__('Invalid customer address id %1', $address->getCustomerAddressId())); } //Validating address ID try { $this->addressRepository->getById($address->getCustomerAddressId()); } catch (NoSuchEntityException $e) { - throw new NoSuchEntityException( - __('Invalid address id %1', $address->getId()) - ); + throw new NoSuchEntityException(__('Invalid address id %1', $address->getId())); } //Finding available customer's addresses $applicableAddressIds = array_map(function ($address) { @@ -94,9 +88,7 @@ private function doValidate(AddressInterface $address, $customerId) return $address->getId(); }, $this->customerRepository->getById($customerId)->getAddresses()); if (!in_array($address->getCustomerAddressId(), $applicableAddressIds)) { - throw new NoSuchEntityException( - __('Invalid customer address id %1', $address->getCustomerAddressId()) - ); + throw new NoSuchEntityException(__('Invalid customer address id %1', $address->getCustomerAddressId())); } } } From 66676d5a626a7951e3a0d99d1ac83cec0d764a7d Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Fri, 25 Jan 2019 17:13:13 +0200 Subject: [PATCH 0374/1295] MAGETWO-97456: Cart's customer and address mismatch --- .../Magento/Quote/Test/Unit/Model/QuoteAddressValidatorTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteAddressValidatorTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteAddressValidatorTest.php index 02210e9afa78a..c0ffbc997590e 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/QuoteAddressValidatorTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteAddressValidatorTest.php @@ -67,7 +67,6 @@ public function testValidateInvalidCustomer() { $customerId = 100; $address = $this->createMock(\Magento\Quote\Api\Data\AddressInterface::class); - $customerMock = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); $address->expects($this->atLeastOnce())->method('getCustomerId')->willReturn($customerId); $this->customerRepositoryMock->expects($this->once())->method('getById')->with($customerId) From ee86554cc29222cf0403fd75ff8eb3432d651ef2 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Fri, 25 Jan 2019 18:05:24 +0200 Subject: [PATCH 0375/1295] MAGETWO-95176: Detaching category from product causes massive product url regeneration --- ...oryProcessUrlRewriteSavingObserverTest.php | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/CategoryProcessUrlRewriteSavingObserverTest.php diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/CategoryProcessUrlRewriteSavingObserverTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/CategoryProcessUrlRewriteSavingObserverTest.php new file mode 100644 index 0000000000000..634dae5643c02 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/CategoryProcessUrlRewriteSavingObserverTest.php @@ -0,0 +1,129 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogUrlRewrite\Test\Unit\Observer; + +use Magento\Catalog\Model\Category; +use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator; +use Magento\CatalogUrlRewrite\Model\Map\DatabaseMapPool; +use Magento\CatalogUrlRewrite\Model\Map\DataCategoryUrlRewriteDatabaseMap; +use Magento\CatalogUrlRewrite\Model\Map\DataProductUrlRewriteDatabaseMap; +use Magento\CatalogUrlRewrite\Model\UrlRewriteBunchReplacer; +use Magento\CatalogUrlRewrite\Observer\CategoryProcessUrlRewriteSavingObserver; +use Magento\CatalogUrlRewrite\Observer\UrlRewriteHandler; +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Store\Model\ResourceModel\Group\CollectionFactory; + +/** + * Tests Magento\CatalogUrlRewrite\Observer\CategoryProcessUrlRewriteSavingObserver. + */ +class CategoryProcessUrlRewriteSavingObserverTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var CategoryProcessUrlRewriteSavingObserver + */ + private $observer; + + /** + * @var CategoryUrlRewriteGenerator|\PHPUnit_Framework_MockObject_MockObject + */ + private $categoryUrlRewriteGeneratorMock; + + /** + * @var UrlRewriteHandler|\PHPUnit_Framework_MockObject_MockObject + */ + private $urlRewriteHandlerMock; + + /** + * @var UrlRewriteBunchReplacer|\PHPUnit_Framework_MockObject_MockObject $urlRewriteMock + */ + private $urlRewriteBunchReplacerMock; + + /** + * @var DatabaseMapPool|\PHPUnit_Framework_MockObject_MockObject + */ + private $databaseMapPoolMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = new ObjectManager($this); + $this->categoryUrlRewriteGeneratorMock = $this->createMock(CategoryUrlRewriteGenerator::class); + $this->urlRewriteHandlerMock = $this->createMock(UrlRewriteHandler::class); + $this->urlRewriteBunchReplacerMock = $this->createMock(UrlRewriteBunchReplacer::class); + $this->databaseMapPoolMock = $this->createMock(DatabaseMapPool::class); + /** @var CollectionFactory|\PHPUnit_Framework_MockObject_MockObject $storeGroupFactoryMock */ + $storeGroupCollectionFactoryMock = $this->createMock(CollectionFactory::class); + + $this->observer = $objectManager->getObject( + CategoryProcessUrlRewriteSavingObserver::class, + [ + 'categoryUrlRewriteGenerator' => $this->categoryUrlRewriteGeneratorMock, + 'urlRewriteHandler' => $this->urlRewriteHandlerMock, + 'urlRewriteBunchReplacer' => $this->urlRewriteBunchReplacerMock, + 'databaseMapPool' => $this->databaseMapPoolMock, + 'dataUrlRewriteClassNames' => [ + DataCategoryUrlRewriteDatabaseMap::class, + DataProductUrlRewriteDatabaseMap::class + ], + 'storeGroupFactory' => $storeGroupCollectionFactoryMock, + ] + ); + } + + /** + * Covers case when only associated products are changed for category. + * + * @return void + */ + public function testExecuteCategoryOnlyProductHasChanged() + { + $productId = 120; + $productRewrites = ['product-url-rewrite']; + + /** @var Observer|\PHPUnit_Framework_MockObject_MockObject $observerMock */ + $observerMock = $this->createMock(Observer::class); + /** @var Event|\PHPUnit_Framework_MockObject_MockObject $eventMock */ + $eventMock = $this->createMock(Event::class); + /** @var Category|\PHPUnit_Framework_MockObject_MockObject $categoryMock */ + $categoryMock = $this->createPartialMock( + Category::class, + [ + 'hasData', + 'dataHasChangedFor', + 'getChangedProductIds', + ] + ); + + $categoryMock->expects($this->once())->method('hasData')->with('store_id')->willReturn(true); + $categoryMock->expects($this->exactly(2))->method('getChangedProductIds')->willReturn([$productId]); + $categoryMock->expects($this->any())->method('dataHasChangedFor') + ->willReturnMap( + [ + ['url_key', false], + ['is_anchor', false], + ] + ); + $eventMock->expects($this->once())->method('getData')->with('category')->willReturn($categoryMock); + $observerMock->expects($this->once())->method('getEvent')->willReturn($eventMock); + + $this->urlRewriteHandlerMock->expects($this->once()) + ->method('updateProductUrlRewritesForChangedProduct') + ->with($categoryMock) + ->willReturn($productRewrites); + + $this->urlRewriteBunchReplacerMock->expects($this->once()) + ->method('doBunchReplace') + ->with($productRewrites, 10000); + + $this->observer->execute($observerMock); + } +} From b9390c9f3a5d9be797a460a0e4d09d9932d70910 Mon Sep 17 00:00:00 2001 From: Serhiy Zhovnir <s.zhovnir@atwix.com> Date: Sat, 26 Jan 2019 19:06:54 +0200 Subject: [PATCH 0376/1295] #20376 Fix issue with file uploading if an upload field is disabled --- app/code/Magento/Ui/i18n/en_US.csv | 1 + .../Ui/view/base/web/js/form/element/file-uploader.js | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/app/code/Magento/Ui/i18n/en_US.csv b/app/code/Magento/Ui/i18n/en_US.csv index 981d444b31d53..040ec32cf0b74 100644 --- a/app/code/Magento/Ui/i18n/en_US.csv +++ b/app/code/Magento/Ui/i18n/en_US.csv @@ -202,3 +202,4 @@ CSV,CSV "Please enter at least {0} characters.","Please enter at least {0} characters." "Please enter a value between {0} and {1} characters long.","Please enter a value between {0} and {1} characters long." "Please enter a value between {0} and {1}.","Please enter a value between {0} and {1}." +"The file upload field is disabled.","The file upload field is disabled." diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js index b81119f2bd5f3..1d7023e50b042 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js @@ -328,6 +328,12 @@ define([ allowed = this.isFileAllowed(file), target = $(e.target); + if (this.disabled()) { + this.notifyError($t('The file upload field is disabled.')); + + return; + } + if (allowed.passed) { target.on('fileuploadsend', function (event, postData) { postData.data.append('param_name', this.paramName); From 0b24b39b12e34dfc7ac60746caf420abbd3c2b76 Mon Sep 17 00:00:00 2001 From: Serhiy Zhovnir <s.zhovnir@atwix.com> Date: Sat, 26 Jan 2019 19:23:38 +0200 Subject: [PATCH 0377/1295] #20461 Add translate component --- .../Magento/Ui/view/base/web/js/form/element/file-uploader.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js index 1d7023e50b042..4b178622c28cf 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js @@ -13,8 +13,9 @@ define([ 'Magento_Ui/js/modal/alert', 'Magento_Ui/js/lib/validation/validator', 'Magento_Ui/js/form/element/abstract', + 'mage/translate', 'jquery/file-uploader' -], function ($, _, utils, uiAlert, validator, Element) { +], function ($, _, utils, uiAlert, validator, Element, $t) { 'use strict'; return Element.extend({ From 3ecdb4f7855d9b08fa1b616d76de6b047869bed9 Mon Sep 17 00:00:00 2001 From: Jayanka <jayan@codilar.com> Date: Fri, 21 Sep 2018 23:45:45 +0530 Subject: [PATCH 0378/1295] (Fixes issue #18027) Round the total amount to 4 decimal places, to avoid floating point overflows --- app/code/Magento/Quote/Model/Quote/Address/Total.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Quote/Model/Quote/Address/Total.php b/app/code/Magento/Quote/Model/Quote/Address/Total.php index 42224c970ed27..49bf7bb967b09 100644 --- a/app/code/Magento/Quote/Model/Quote/Address/Total.php +++ b/app/code/Magento/Quote/Model/Quote/Address/Total.php @@ -54,6 +54,9 @@ public function __construct( */ public function setTotalAmount($code, $amount) { + /* (Fixes issue #18027) Round the total amount to 4 decimal places, to avoid floating point overflows */ + $amount = number_format($amount, 4); + $this->totalAmounts[$code] = $amount; if ($code != 'subtotal') { $code = $code . '_amount'; From c26719dba5acd817ddb667e46fd422b8845eeaf0 Mon Sep 17 00:00:00 2001 From: Jayanka <jayan@codilar.com> Date: Sat, 22 Sep 2018 00:43:11 +0530 Subject: [PATCH 0379/1295] round the total amount only if it is float --- app/code/Magento/Quote/Model/Quote/Address/Total.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Quote/Model/Quote/Address/Total.php b/app/code/Magento/Quote/Model/Quote/Address/Total.php index 49bf7bb967b09..e4ceeac3905f0 100644 --- a/app/code/Magento/Quote/Model/Quote/Address/Total.php +++ b/app/code/Magento/Quote/Model/Quote/Address/Total.php @@ -55,7 +55,7 @@ public function __construct( public function setTotalAmount($code, $amount) { /* (Fixes issue #18027) Round the total amount to 4 decimal places, to avoid floating point overflows */ - $amount = number_format($amount, 4); + $amount = is_float($amount) ? number_format($amount, 4) : $amount; $this->totalAmounts[$code] = $amount; if ($code != 'subtotal') { From 63cc54b542e5c17cb5519c399e6ce972c55540f6 Mon Sep 17 00:00:00 2001 From: Jayanka <jayan@codilar.com> Date: Sun, 23 Sep 2018 14:58:43 +0530 Subject: [PATCH 0380/1295] round base total amount --- app/code/Magento/Quote/Model/Quote/Address/Total.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/code/Magento/Quote/Model/Quote/Address/Total.php b/app/code/Magento/Quote/Model/Quote/Address/Total.php index e4ceeac3905f0..44a81b5011cf2 100644 --- a/app/code/Magento/Quote/Model/Quote/Address/Total.php +++ b/app/code/Magento/Quote/Model/Quote/Address/Total.php @@ -6,6 +6,7 @@ namespace Magento\Quote\Model\Quote\Address; /** + * Class Total * @method string getCode() * * @api @@ -75,6 +76,9 @@ public function setTotalAmount($code, $amount) */ public function setBaseTotalAmount($code, $amount) { + /* (Fixes issue #18027) Round the total amount to 4 decimal places, to avoid floating point overflows */ + $amount = is_float($amount) ? number_format($amount, 4) : $amount; + $this->baseTotalAmounts[$code] = $amount; if ($code != 'subtotal') { $code = $code . '_amount'; @@ -170,6 +174,7 @@ public function getAllBaseTotalAmounts() /** * Set the full info, which is used to capture tax related information. + * * If a string is used, it is assumed to be serialized. * * @param array|string $info From 785a38a936acd94618e3d49f951be3f6b2b1d682 Mon Sep 17 00:00:00 2001 From: Jayanka <jayan@codilar.com> Date: Fri, 28 Sep 2018 23:48:49 +0530 Subject: [PATCH 0381/1295] cast formatted number to float --- app/code/Magento/Quote/Model/Quote/Address/Total.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Quote/Model/Quote/Address/Total.php b/app/code/Magento/Quote/Model/Quote/Address/Total.php index 44a81b5011cf2..30ae186ad6230 100644 --- a/app/code/Magento/Quote/Model/Quote/Address/Total.php +++ b/app/code/Magento/Quote/Model/Quote/Address/Total.php @@ -56,7 +56,7 @@ public function __construct( public function setTotalAmount($code, $amount) { /* (Fixes issue #18027) Round the total amount to 4 decimal places, to avoid floating point overflows */ - $amount = is_float($amount) ? number_format($amount, 4) : $amount; + $amount = is_float($amount) ? (float)number_format($amount, 4) : $amount; $this->totalAmounts[$code] = $amount; if ($code != 'subtotal') { @@ -77,7 +77,7 @@ public function setTotalAmount($code, $amount) public function setBaseTotalAmount($code, $amount) { /* (Fixes issue #18027) Round the total amount to 4 decimal places, to avoid floating point overflows */ - $amount = is_float($amount) ? number_format($amount, 4) : $amount; + $amount = is_float($amount) ? (float)number_format($amount, 4) : $amount; $this->baseTotalAmounts[$code] = $amount; if ($code != 'subtotal') { @@ -173,9 +173,7 @@ public function getAllBaseTotalAmounts() //@codeCoverageIgnoreEnd /** - * Set the full info, which is used to capture tax related information. - * - * If a string is used, it is assumed to be serialized. + * Set the full info, which is used to capture tax related information. If a string is used, it is assumed to be serialized. * * @param array|string $info * @return $this From b6f9b6e92a9958e235aa239057b2b6b51fb2f14a Mon Sep 17 00:00:00 2001 From: Jayanka <jayan@codilar.com> Date: Sat, 6 Oct 2018 17:27:20 +0530 Subject: [PATCH 0382/1295] thousand separator removed from number format --- app/code/Magento/Quote/Model/Quote/Address/Total.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Quote/Model/Quote/Address/Total.php b/app/code/Magento/Quote/Model/Quote/Address/Total.php index 30ae186ad6230..e45678ea2138c 100644 --- a/app/code/Magento/Quote/Model/Quote/Address/Total.php +++ b/app/code/Magento/Quote/Model/Quote/Address/Total.php @@ -56,7 +56,7 @@ public function __construct( public function setTotalAmount($code, $amount) { /* (Fixes issue #18027) Round the total amount to 4 decimal places, to avoid floating point overflows */ - $amount = is_float($amount) ? (float)number_format($amount, 4) : $amount; + $amount = is_float($amount) ? (float)number_format($amount, 4, ".", "") : $amount; $this->totalAmounts[$code] = $amount; if ($code != 'subtotal') { @@ -77,7 +77,7 @@ public function setTotalAmount($code, $amount) public function setBaseTotalAmount($code, $amount) { /* (Fixes issue #18027) Round the total amount to 4 decimal places, to avoid floating point overflows */ - $amount = is_float($amount) ? (float)number_format($amount, 4) : $amount; + $amount = is_float($amount) ? (float)number_format($amount, 4, ".", "") : $amount; $this->baseTotalAmounts[$code] = $amount; if ($code != 'subtotal') { @@ -173,7 +173,8 @@ public function getAllBaseTotalAmounts() //@codeCoverageIgnoreEnd /** - * Set the full info, which is used to capture tax related information. If a string is used, it is assumed to be serialized. + * Set the full info, which is used to capture tax related information. + * If a string is used, it is assumed to be serialized. * * @param array|string $info * @return $this From 6c9a97c67ea89bb3585135d206321618cbc99586 Mon Sep 17 00:00:00 2001 From: Jayanka <jayan@codilar.com> Date: Sat, 6 Oct 2018 17:59:28 +0530 Subject: [PATCH 0383/1295] use round instead of number_format to round the total amount --- app/code/Magento/Quote/Model/Quote/Address/Total.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Quote/Model/Quote/Address/Total.php b/app/code/Magento/Quote/Model/Quote/Address/Total.php index e45678ea2138c..7b24e60f9749f 100644 --- a/app/code/Magento/Quote/Model/Quote/Address/Total.php +++ b/app/code/Magento/Quote/Model/Quote/Address/Total.php @@ -56,7 +56,7 @@ public function __construct( public function setTotalAmount($code, $amount) { /* (Fixes issue #18027) Round the total amount to 4 decimal places, to avoid floating point overflows */ - $amount = is_float($amount) ? (float)number_format($amount, 4, ".", "") : $amount; + $amount = is_float($amount) ? round($amount, 4) : $amount; $this->totalAmounts[$code] = $amount; if ($code != 'subtotal') { @@ -77,7 +77,7 @@ public function setTotalAmount($code, $amount) public function setBaseTotalAmount($code, $amount) { /* (Fixes issue #18027) Round the total amount to 4 decimal places, to avoid floating point overflows */ - $amount = is_float($amount) ? (float)number_format($amount, 4, ".", "") : $amount; + $amount = is_float($amount) ? round($amount, 4) : $amount; $this->baseTotalAmounts[$code] = $amount; if ($code != 'subtotal') { From 422ab6774ddd50f9bc9d424f8ba218866c162b64 Mon Sep 17 00:00:00 2001 From: Jayanka <jayan@codilar.com> Date: Wed, 14 Nov 2018 18:04:02 +0300 Subject: [PATCH 0384/1295] unnecessary comments removed --- app/code/Magento/Quote/Model/Quote/Address/Total.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/Quote/Model/Quote/Address/Total.php b/app/code/Magento/Quote/Model/Quote/Address/Total.php index 7b24e60f9749f..8179b2b19707f 100644 --- a/app/code/Magento/Quote/Model/Quote/Address/Total.php +++ b/app/code/Magento/Quote/Model/Quote/Address/Total.php @@ -55,7 +55,6 @@ public function __construct( */ public function setTotalAmount($code, $amount) { - /* (Fixes issue #18027) Round the total amount to 4 decimal places, to avoid floating point overflows */ $amount = is_float($amount) ? round($amount, 4) : $amount; $this->totalAmounts[$code] = $amount; @@ -76,7 +75,6 @@ public function setTotalAmount($code, $amount) */ public function setBaseTotalAmount($code, $amount) { - /* (Fixes issue #18027) Round the total amount to 4 decimal places, to avoid floating point overflows */ $amount = is_float($amount) ? round($amount, 4) : $amount; $this->baseTotalAmounts[$code] = $amount; From 8aa89dd3e3814eb086d271e026cd84994b57549d Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Wed, 12 Dec 2018 16:51:35 +0200 Subject: [PATCH 0385/1295] ENGCOM-3464: Static tests fixed. --- app/code/Magento/Quote/Model/Quote/Address/Total.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Quote/Model/Quote/Address/Total.php b/app/code/Magento/Quote/Model/Quote/Address/Total.php index 8179b2b19707f..00060c15c10d8 100644 --- a/app/code/Magento/Quote/Model/Quote/Address/Total.php +++ b/app/code/Magento/Quote/Model/Quote/Address/Total.php @@ -7,6 +7,7 @@ /** * Class Total + * * @method string getCode() * * @api @@ -172,6 +173,7 @@ public function getAllBaseTotalAmounts() /** * Set the full info, which is used to capture tax related information. + * * If a string is used, it is assumed to be serialized. * * @param array|string $info From 59c0d0dc29902d3ac3065770701c8fad0de93130 Mon Sep 17 00:00:00 2001 From: Mahesh Singh <mahesh721@webkul.com> Date: Tue, 13 Nov 2018 21:28:28 +0530 Subject: [PATCH 0386/1295] issue #18349 for 2.3-develop --- app/code/Magento/Quote/Model/Quote/Item/ToOrderItem.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Quote/Model/Quote/Item/ToOrderItem.php b/app/code/Magento/Quote/Model/Quote/Item/ToOrderItem.php index 32687499274f8..d1e559845c479 100644 --- a/app/code/Magento/Quote/Model/Quote/Item/ToOrderItem.php +++ b/app/code/Magento/Quote/Model/Quote/Item/ToOrderItem.php @@ -63,6 +63,9 @@ public function convert($item, $data = []) 'to_order_item', $item ); + if ($item instanceof \Magento\Quote\Model\Quote\Address\Item) { + $orderItemData['quote_item_id'] = $item->getQuoteItemId(); + } if (!$item->getNoDiscount()) { $data = array_merge( $data, From c1499b1ce2abdd95ec5d5e5f59d1d86fa567dbb3 Mon Sep 17 00:00:00 2001 From: Mahesh Singh <mahesh721@webkul.com> Date: Tue, 20 Nov 2018 01:10:30 +0530 Subject: [PATCH 0387/1295] new field added in fieldset.xml --- app/code/Magento/Quote/Model/Quote/Item/ToOrderItem.php | 9 ++++++++- app/code/Magento/Quote/etc/fieldset.xml | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Quote/Model/Quote/Item/ToOrderItem.php b/app/code/Magento/Quote/Model/Quote/Item/ToOrderItem.php index d1e559845c479..38bc6aeb6b242 100644 --- a/app/code/Magento/Quote/Model/Quote/Item/ToOrderItem.php +++ b/app/code/Magento/Quote/Model/Quote/Item/ToOrderItem.php @@ -64,7 +64,14 @@ public function convert($item, $data = []) $item ); if ($item instanceof \Magento\Quote\Model\Quote\Address\Item) { - $orderItemData['quote_item_id'] = $item->getQuoteItemId(); + $data = array_merge( + $data, + $this->objectCopyService->getDataFromFieldset( + 'quote_convert_item', + 'to_order_item_id', + $item + ) + ); } if (!$item->getNoDiscount()) { $data = array_merge( diff --git a/app/code/Magento/Quote/etc/fieldset.xml b/app/code/Magento/Quote/etc/fieldset.xml index 55ec76a647fcd..4b7c344d6219b 100644 --- a/app/code/Magento/Quote/etc/fieldset.xml +++ b/app/code/Magento/Quote/etc/fieldset.xml @@ -205,6 +205,9 @@ <field name="qty"> <aspect name="to_order_item" targetField="qty_ordered" /> </field> + <field name="quote_item_id"> + <aspect name="to_order_item_id" targetField="quote_item_id" /> + </field> <field name="id"> <aspect name="to_order_item" targetField="quote_item_id" /> </field> From 2d0616422c90e6915c9a1fda0147ee55f31bc8bc Mon Sep 17 00:00:00 2001 From: Mahesh Singh <mahesh721@webkul.com> Date: Tue, 20 Nov 2018 21:33:09 +0530 Subject: [PATCH 0388/1295] added new fieldset --- .../Quote/Model/Quote/Item/ToOrderItem.php | 18 +++++++++--------- app/code/Magento/Quote/etc/fieldset.xml | 8 +++++--- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Quote/Model/Quote/Item/ToOrderItem.php b/app/code/Magento/Quote/Model/Quote/Item/ToOrderItem.php index 38bc6aeb6b242..ec786153e7ab4 100644 --- a/app/code/Magento/Quote/Model/Quote/Item/ToOrderItem.php +++ b/app/code/Magento/Quote/Model/Quote/Item/ToOrderItem.php @@ -64,15 +64,15 @@ public function convert($item, $data = []) $item ); if ($item instanceof \Magento\Quote\Model\Quote\Address\Item) { - $data = array_merge( - $data, - $this->objectCopyService->getDataFromFieldset( - 'quote_convert_item', - 'to_order_item_id', - $item - ) - ); - } + $orderItemData= array_merge( + $orderItemData, + $this->objectCopyService->getDataFromFieldset( + 'quote_convert_address_item', + 'to_order_item', + $item + ) + ); + } if (!$item->getNoDiscount()) { $data = array_merge( $data, diff --git a/app/code/Magento/Quote/etc/fieldset.xml b/app/code/Magento/Quote/etc/fieldset.xml index 4b7c344d6219b..85ee20c7f8520 100644 --- a/app/code/Magento/Quote/etc/fieldset.xml +++ b/app/code/Magento/Quote/etc/fieldset.xml @@ -186,6 +186,11 @@ <aspect name="to_order_address" /> </field> </fieldset> + <fieldset id="quote_convert_address_item"> + <field name="quote_item_id"> + <aspect name="to_order_item" /> + </field> + </fieldset> <fieldset id="quote_convert_item"> <field name="sku"> <aspect name="to_order_item" /> @@ -205,9 +210,6 @@ <field name="qty"> <aspect name="to_order_item" targetField="qty_ordered" /> </field> - <field name="quote_item_id"> - <aspect name="to_order_item_id" targetField="quote_item_id" /> - </field> <field name="id"> <aspect name="to_order_item" targetField="quote_item_id" /> </field> From 33571425d2b4db5a8c481ce3dc7a36daa1f57438 Mon Sep 17 00:00:00 2001 From: Mahesh Singh <mahesh721@webkul.com> Date: Tue, 20 Nov 2018 23:33:22 +0530 Subject: [PATCH 0389/1295] space added in declaration --- app/code/Magento/Quote/Model/Quote/Item/ToOrderItem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Quote/Model/Quote/Item/ToOrderItem.php b/app/code/Magento/Quote/Model/Quote/Item/ToOrderItem.php index ec786153e7ab4..ca6b83aa50e75 100644 --- a/app/code/Magento/Quote/Model/Quote/Item/ToOrderItem.php +++ b/app/code/Magento/Quote/Model/Quote/Item/ToOrderItem.php @@ -64,7 +64,7 @@ public function convert($item, $data = []) $item ); if ($item instanceof \Magento\Quote\Model\Quote\Address\Item) { - $orderItemData= array_merge( + $orderItemData = array_merge( $orderItemData, $this->objectCopyService->getDataFromFieldset( 'quote_convert_address_item', From 592d6d86582040751f18eefad46f7d8191d459a4 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Thu, 10 Jan 2019 10:56:42 +0200 Subject: [PATCH 0390/1295] Fix static tests. --- .../Quote/Model/Quote/Item/ToOrderItem.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Quote/Model/Quote/Item/ToOrderItem.php b/app/code/Magento/Quote/Model/Quote/Item/ToOrderItem.php index ca6b83aa50e75..6192d3471ccb0 100644 --- a/app/code/Magento/Quote/Model/Quote/Item/ToOrderItem.php +++ b/app/code/Magento/Quote/Model/Quote/Item/ToOrderItem.php @@ -48,6 +48,8 @@ public function __construct( } /** + * Convert quote item(quote address item) into order item. + * * @param Item|AddressItem $item * @param array $data * @return OrderItemInterface @@ -66,13 +68,13 @@ public function convert($item, $data = []) if ($item instanceof \Magento\Quote\Model\Quote\Address\Item) { $orderItemData = array_merge( $orderItemData, - $this->objectCopyService->getDataFromFieldset( - 'quote_convert_address_item', - 'to_order_item', - $item - ) - ); - } + $this->objectCopyService->getDataFromFieldset( + 'quote_convert_address_item', + 'to_order_item', + $item + ) + ); + } if (!$item->getNoDiscount()) { $data = array_merge( $data, From f01e5888554cb7b53d03c93f94181bbe6b62288b Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Fri, 11 Jan 2019 14:33:17 +0200 Subject: [PATCH 0391/1295] Fix integration tests. --- .../Multishipping/Model/Checkout/Type/Multishipping.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php b/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php index ea7838085ddfb..0ea0abc135f9e 100644 --- a/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php +++ b/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php @@ -1168,7 +1168,7 @@ private function removePlacedItemsFromQuote(array $shippingAddresses, array $pla { foreach ($shippingAddresses as $address) { foreach ($address->getAllItems() as $addressItem) { - if (in_array($addressItem->getId(), $placedAddressItems)) { + if (in_array($addressItem->getQuoteItemId(), $placedAddressItems)) { if ($addressItem->getProduct()->getIsVirtual()) { $addressItem->isDeleted(true); } else { @@ -1218,7 +1218,7 @@ private function searchQuoteAddressId(OrderInterface $order, array $addresses): $item = array_pop($items); foreach ($addresses as $address) { foreach ($address->getAllItems() as $addressItem) { - if ($addressItem->getId() == $item->getQuoteItemId()) { + if ($addressItem->getQuoteItemId() == $item->getQuoteItemId()) { return (int)$address->getId(); } } From f40cdc12fafbcf3fe7f2d39581d08018be3cb44e Mon Sep 17 00:00:00 2001 From: "v.shatylo" <v.shatylo@ism-ukraine.com> Date: Fri, 18 Jan 2019 15:41:57 +0200 Subject: [PATCH 0392/1295] magento/magento2#12194: Tier price on configurable product sorting sometimes-wrong --- .../Magento/Catalog/Model/ResourceModel/Product/Collection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index 749a3d754570f..7db77faaafcaf 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -2205,7 +2205,7 @@ private function getTierPriceSelect(array $productIds) $this->getLinkField() . ' IN(?)', $productIds )->order( - $this->getLinkField() + 'qty' ); return $select; } From abddea84f8aa4edf949c04663c9285ce5a24f489 Mon Sep 17 00:00:00 2001 From: Oleksii Gorbulin <a.gorbulin@ism-ukraine.com> Date: Thu, 27 Dec 2018 23:54:55 +0200 Subject: [PATCH 0393/1295] 19482-Increase-product-quantity-with-disabled-Manage-Stock-when-place-order-is-failed Magento2:#19482 Increase product quantity with disabled Manage Stock when place order is failed --- .../CatalogInventory/Model/StockManagement.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogInventory/Model/StockManagement.php b/app/code/Magento/CatalogInventory/Model/StockManagement.php index 107645a45a390..1a01675ea6a21 100644 --- a/app/code/Magento/CatalogInventory/Model/StockManagement.php +++ b/app/code/Magento/CatalogInventory/Model/StockManagement.php @@ -148,7 +148,17 @@ public function revertProductsSale($items, $websiteId = null) //if (!$websiteId) { $websiteId = $this->stockConfiguration->getDefaultScopeId(); //} - $this->qtyCounter->correctItemsQty($items, $websiteId, '+'); + $revertItems = []; + foreach ($items as $productId => $qty) { + $stockItem = $this->stockRegistryProvider->getStockItem($productId, $websiteId); + $canSubtractQty = $stockItem->getItemId() && $this->canSubtractQty($stockItem); + if (!$canSubtractQty || !$this->stockConfiguration->isQty($stockItem->getTypeId())) { + continue; + } + $revertItems[$productId] = $qty; + } + $this->qtyCounter->correctItemsQty($revertItems, $websiteId, '+'); + return true; } From 5d38ec623a83128be1fcaade22e9418d12a6c8a0 Mon Sep 17 00:00:00 2001 From: Oleksii Gorbulin <a.gorbulin@ism-ukraine.com> Date: Fri, 28 Dec 2018 00:43:40 +0200 Subject: [PATCH 0394/1295] 19482-Increase-product-quantity-with-disabled-Manage-Stock-when-place-order-is-failed Increase product quantity with disabled Manage Stock when place order is failed #19482 --- .../Magento/CatalogInventory/Model/StockManagement.php | 7 ++++--- .../Observer/RevertQuoteInventoryObserver.php | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Model/StockManagement.php b/app/code/Magento/CatalogInventory/Model/StockManagement.php index 1a01675ea6a21..78524ffecaa32 100644 --- a/app/code/Magento/CatalogInventory/Model/StockManagement.php +++ b/app/code/Magento/CatalogInventory/Model/StockManagement.php @@ -140,8 +140,9 @@ public function registerProductsSale($items, $websiteId = null) /** * @param string[] $items - * @param int $websiteId - * @return bool + * @param int|null $websiteId + * + * @return array|bool */ public function revertProductsSale($items, $websiteId = null) { @@ -159,7 +160,7 @@ public function revertProductsSale($items, $websiteId = null) } $this->qtyCounter->correctItemsQty($revertItems, $websiteId, '+'); - return true; + return $revertItems; } /** diff --git a/app/code/Magento/CatalogInventory/Observer/RevertQuoteInventoryObserver.php b/app/code/Magento/CatalogInventory/Observer/RevertQuoteInventoryObserver.php index 93a50cc9a7a4d..ab21f32b3f62c 100644 --- a/app/code/Magento/CatalogInventory/Observer/RevertQuoteInventoryObserver.php +++ b/app/code/Magento/CatalogInventory/Observer/RevertQuoteInventoryObserver.php @@ -64,8 +64,8 @@ public function execute(EventObserver $observer) { $quote = $observer->getEvent()->getQuote(); $items = $this->productQty->getProductQty($quote->getAllItems()); - $this->stockManagement->revertProductsSale($items, $quote->getStore()->getWebsiteId()); - $productIds = array_keys($items); + $revertedItems = $this->stockManagement->revertProductsSale($items, $quote->getStore()->getWebsiteId()); + $productIds = array_keys($revertedItems); if (!empty($productIds)) { $this->stockIndexerProcessor->reindexList($productIds); $this->priceIndexer->reindexList($productIds); From 582a1e7beee224839ee21b554d55c55af56f38e4 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Mon, 21 Jan 2019 17:06:06 +0200 Subject: [PATCH 0395/1295] ENGCOM-3771: Static test fix. --- .../Magento/CatalogInventory/Model/StockManagement.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Model/StockManagement.php b/app/code/Magento/CatalogInventory/Model/StockManagement.php index 78524ffecaa32..ed8fcef5dea03 100644 --- a/app/code/Magento/CatalogInventory/Model/StockManagement.php +++ b/app/code/Magento/CatalogInventory/Model/StockManagement.php @@ -83,6 +83,7 @@ public function __construct( /** * Subtract product qtys from stock. + * * Return array of items that require full save. * * @param string[] $items @@ -139,10 +140,7 @@ public function registerProductsSale($items, $websiteId = null) } /** - * @param string[] $items - * @param int|null $websiteId - * - * @return array|bool + * @inheritdoc */ public function revertProductsSale($items, $websiteId = null) { @@ -204,6 +202,8 @@ protected function getProductType($productId) } /** + * Get stock resource. + * * @return ResourceStock */ protected function getResource() From bf132891fa8ef081b9690584276537089568d420 Mon Sep 17 00:00:00 2001 From: Alexandre Jardin <info@ajardin.fr> Date: Tue, 4 Dec 2018 18:41:34 +0100 Subject: [PATCH 0396/1295] Avoid duplicate loading of configuration files --- .../Theme/Model/PageLayout/Config/Builder.php | 16 ++++++++++++---- .../Unit/Model/PageLayout/Config/BuilderTest.php | 2 +- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Theme/Model/PageLayout/Config/Builder.php b/app/code/Magento/Theme/Model/PageLayout/Config/Builder.php index 98fa12ab987b6..2f0ffc4d7059e 100644 --- a/app/code/Magento/Theme/Model/PageLayout/Config/Builder.php +++ b/app/code/Magento/Theme/Model/PageLayout/Config/Builder.php @@ -27,6 +27,11 @@ class Builder implements \Magento\Framework\View\Model\PageLayout\Config\Builder */ protected $themeCollection; + /** + * @var array + */ + private $configFiles = []; + /** * @param \Magento\Framework\View\PageLayout\ConfigFactory $configFactory * @param \Magento\Framework\View\PageLayout\File\Collector\Aggregated $fileCollector @@ -56,11 +61,14 @@ public function getPageLayoutsConfig() */ protected function getConfigFiles() { - $configFiles = []; - foreach ($this->themeCollection->loadRegisteredThemes() as $theme) { - $configFiles = array_merge($configFiles, $this->fileCollector->getFilesContent($theme, 'layouts.xml')); + if (empty($this->configFiles)) { + $configFiles = []; + foreach ($this->themeCollection->loadRegisteredThemes() as $theme) { + $configFiles[] = $this->fileCollector->getFilesContent($theme, 'layouts.xml'); + } + $this->configFiles = array_merge(...$configFiles); } - return $configFiles; + return $this->configFiles; } } diff --git a/app/code/Magento/Theme/Test/Unit/Model/PageLayout/Config/BuilderTest.php b/app/code/Magento/Theme/Test/Unit/Model/PageLayout/Config/BuilderTest.php index e5d69cbc820a1..8429be84cae44 100644 --- a/app/code/Magento/Theme/Test/Unit/Model/PageLayout/Config/BuilderTest.php +++ b/app/code/Magento/Theme/Test/Unit/Model/PageLayout/Config/BuilderTest.php @@ -83,7 +83,7 @@ public function testGetPageLayoutsConfig() ->disableOriginalConstructor() ->getMock(); - $this->themeCollection->expects($this->any()) + $this->themeCollection->expects($this->once()) ->method('loadRegisteredThemes') ->willReturn([$theme1, $theme2]); From eb6f0e624fadb3fc538f3efe5d37c12a99fa32ac Mon Sep 17 00:00:00 2001 From: Alexandre Jardin <info@ajardin.fr> Date: Tue, 11 Dec 2018 21:14:08 +0100 Subject: [PATCH 0397/1295] Remove unneeded internal caching of options --- .../Model/Category/Attribute/Source/Layout.php | 18 ++++++++++++------ .../Model/Product/Attribute/Source/Layout.php | 18 ++++++++++++------ .../Cms/Model/Page/Source/PageLayout.php | 11 +++-------- .../Theme/Model/PageLayout/Config/Builder.php | 4 ++-- 4 files changed, 29 insertions(+), 22 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Source/Layout.php b/app/code/Magento/Catalog/Model/Category/Attribute/Source/Layout.php index 1890ea0f7d99e..20ea899a3d0d7 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Source/Layout.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Source/Layout.php @@ -17,6 +17,12 @@ class Layout extends \Magento\Eav\Model\Entity\Attribute\Source\AbstractSource */ protected $pageLayoutBuilder; + /** + * @inheritdoc + * @deprecated since the cache is now handled by \Magento\Theme\Model\PageLayout\Config\Builder::$configFiles + */ + protected $_options = null; + /** * @param \Magento\Framework\View\Model\PageLayout\Config\BuilderInterface $pageLayoutBuilder */ @@ -26,14 +32,14 @@ public function __construct(\Magento\Framework\View\Model\PageLayout\Config\Buil } /** - * {@inheritdoc} + * @inheritdoc */ public function getAllOptions() { - if (!$this->_options) { - $this->_options = $this->pageLayoutBuilder->getPageLayoutsConfig()->toOptionArray(); - array_unshift($this->_options, ['value' => '', 'label' => __('No layout updates')]); - } - return $this->_options; + $options = $this->pageLayoutBuilder->getPageLayoutsConfig()->toOptionArray(); + array_unshift($options, ['value' => '', 'label' => __('No layout updates')]); + $this->_options = $options; + + return $options; } } diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Source/Layout.php b/app/code/Magento/Catalog/Model/Product/Attribute/Source/Layout.php index 63b1444d1db07..dbc7535dccfa9 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Source/Layout.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Source/Layout.php @@ -17,6 +17,12 @@ class Layout extends \Magento\Eav\Model\Entity\Attribute\Source\AbstractSource */ protected $pageLayoutBuilder; + /** + * @inheritdoc + * @deprecated since the cache is now handled by \Magento\Theme\Model\PageLayout\Config\Builder::$configFiles + */ + protected $_options = null; + /** * @param \Magento\Framework\View\Model\PageLayout\Config\BuilderInterface $pageLayoutBuilder */ @@ -26,14 +32,14 @@ public function __construct(\Magento\Framework\View\Model\PageLayout\Config\Buil } /** - * @return array + * @inheritdoc */ public function getAllOptions() { - if (!$this->_options) { - $this->_options = $this->pageLayoutBuilder->getPageLayoutsConfig()->toOptionArray(); - array_unshift($this->_options, ['value' => '', 'label' => __('No layout updates')]); - } - return $this->_options; + $options = $this->pageLayoutBuilder->getPageLayoutsConfig()->toOptionArray(); + array_unshift($options, ['value' => '', 'label' => __('No layout updates')]); + $this->_options = $options; + + return $options; } } diff --git a/app/code/Magento/Cms/Model/Page/Source/PageLayout.php b/app/code/Magento/Cms/Model/Page/Source/PageLayout.php index fb759348759b2..23a452c0fe58c 100644 --- a/app/code/Magento/Cms/Model/Page/Source/PageLayout.php +++ b/app/code/Magento/Cms/Model/Page/Source/PageLayout.php @@ -20,6 +20,7 @@ class PageLayout implements OptionSourceInterface /** * @var array + * @deprecated since the cache is now handled by \Magento\Theme\Model\PageLayout\Config\Builder::$configFiles */ protected $options; @@ -34,16 +35,10 @@ public function __construct(BuilderInterface $pageLayoutBuilder) } /** - * Get options - * - * @return array + * @inheritdoc */ public function toOptionArray() { - if ($this->options !== null) { - return $this->options; - } - $configOptions = $this->pageLayoutBuilder->getPageLayoutsConfig()->getOptions(); $options = []; foreach ($configOptions as $key => $value) { @@ -54,6 +49,6 @@ public function toOptionArray() } $this->options = $options; - return $this->options; + return $options; } } diff --git a/app/code/Magento/Theme/Model/PageLayout/Config/Builder.php b/app/code/Magento/Theme/Model/PageLayout/Config/Builder.php index 2f0ffc4d7059e..3306e5e32eb57 100644 --- a/app/code/Magento/Theme/Model/PageLayout/Config/Builder.php +++ b/app/code/Magento/Theme/Model/PageLayout/Config/Builder.php @@ -49,7 +49,7 @@ public function __construct( } /** - * @return \Magento\Framework\View\PageLayout\Config + * @inheritdoc */ public function getPageLayoutsConfig() { @@ -61,7 +61,7 @@ public function getPageLayoutsConfig() */ protected function getConfigFiles() { - if (empty($this->configFiles)) { + if (!$this->configFiles) { $configFiles = []; foreach ($this->themeCollection->loadRegisteredThemes() as $theme) { $configFiles[] = $this->fileCollector->getFilesContent($theme, 'layouts.xml'); From 3667eed3d748120c8032d32989d9882aa3c99c6d Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Tue, 22 Jan 2019 16:07:31 +0200 Subject: [PATCH 0398/1295] Fix static test. --- app/code/Magento/Theme/Model/PageLayout/Config/Builder.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Theme/Model/PageLayout/Config/Builder.php b/app/code/Magento/Theme/Model/PageLayout/Config/Builder.php index 3306e5e32eb57..13b8aa23073ce 100644 --- a/app/code/Magento/Theme/Model/PageLayout/Config/Builder.php +++ b/app/code/Magento/Theme/Model/PageLayout/Config/Builder.php @@ -57,6 +57,8 @@ public function getPageLayoutsConfig() } /** + * Retrieve configuration files. + * * @return array */ protected function getConfigFiles() From a8c0b35aac06b118d1adcf396910d2bc2a796475 Mon Sep 17 00:00:00 2001 From: niravkrish <nirav.patel@krishtechnolabs.com> Date: Sat, 24 Nov 2018 15:11:29 +0530 Subject: [PATCH 0399/1295] fixed Negative order amount in dashboard - #18754 --- .../Magento/Reports/Model/ResourceModel/Order/Collection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php index 82ebc74a0468e..06b71ee0490bb 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php @@ -825,7 +825,7 @@ protected function getTotalsExpression( ) { $template = ($storeId != 0) ? '(main_table.base_subtotal - %2$s - %1$s - ABS(main_table.base_discount_amount) - %3$s)' - : '((main_table.base_subtotal - %1$s - %2$s - ABS(main_table.base_discount_amount) - %3$s) ' + : '((main_table.base_subtotal - %1$s - %2$s - ABS(main_table.base_discount_amount) + %3$s) ' . ' * main_table.base_to_global_rate)'; return sprintf($template, $baseSubtotalRefunded, $baseSubtotalCanceled, $baseDiscountCanceled); } From 5eba9a4ae1a38e423e5c4e1901b2f7c9dc479b77 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Tue, 11 Dec 2018 14:59:07 +0200 Subject: [PATCH 0400/1295] Fix static test. --- .../Magento/Reports/Model/ResourceModel/Order/Collection.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php index 06b71ee0490bb..fd9adbe734101 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php @@ -3,7 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Reports\Model\ResourceModel\Order; use Magento\Framework\DB\Select; @@ -81,7 +80,7 @@ class Collection extends \Magento\Sales\Model\ResourceModel\Order\Collection * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate * @param \Magento\Sales\Model\Order\Config $orderConfig * @param \Magento\Sales\Model\ResourceModel\Report\OrderFactory $reportOrderFactory - * @param null $connection + * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection * @param \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource * * @SuppressWarnings(PHPMD.ExcessiveParameterList) From 935d3d2c648982b7b3b62b206b2815c3dffb6336 Mon Sep 17 00:00:00 2001 From: Amol Chaudhari <amol@2jcommerce.in> Date: Sun, 27 Jan 2019 18:02:23 +0530 Subject: [PATCH 0401/1295] Update styles-old.less --- app/design/adminhtml/Magento/backend/web/css/styles-old.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/adminhtml/Magento/backend/web/css/styles-old.less b/app/design/adminhtml/Magento/backend/web/css/styles-old.less index d7a9a77455c13..d8e8b5b3738ab 100644 --- a/app/design/adminhtml/Magento/backend/web/css/styles-old.less +++ b/app/design/adminhtml/Magento/backend/web/css/styles-old.less @@ -2738,7 +2738,7 @@ // --------------------------------------------- #widget_instace_tabs_properties_section_content .widget-option-label { - margin-top: 6px; + margin-top: 7px; display: inline-block; } From 06144f7b8befd592c4ce798d78c76e4883d2deb6 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Sun, 27 Jan 2019 16:06:56 +0200 Subject: [PATCH 0402/1295] MAGETWO-96759: Fixed incorrect displaying of the sales rule conditions --- .../System/Config/AdditionalCommentTest.php | 2 +- .../System/Config/CollectionTimeLabelTest.php | 2 +- .../System/Config/SubscriptionStatusLabelTest.php | 2 +- .../Block/Adminhtml/System/Config/VerticalTest.php | 2 +- .../Unit/Block/System/Config/Form/Field/FileTest.php | 12 +++++++++++- .../Block/System/Config/Form/Field/ImageTest.php | 8 ++++++++ .../Config/Form/Field/Select/AllowspecificTest.php | 10 +++++++++- .../Customer/Test/Unit/Model/Renderer/RegionTest.php | 2 +- .../Unit/Block/Adminhtml/Form/Field/ImportTest.php | 12 +++++++++++- .../Config/Field/Enable/AbstractEnableTest.php | 1 + .../Test/Unit/Form/Element/AbstractElementTest.php | 4 +--- .../Data/Test/Unit/Form/Element/LinkTest.php | 1 + .../Data/Test/Unit/Form/Element/MultiselectTest.php | 12 +++++++++++- .../Data/Test/Unit/Form/Element/NoteTest.php | 1 + 14 files changed, 59 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/AdditionalCommentTest.php b/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/AdditionalCommentTest.php index 407e323aeaae6..e30f1434dd23a 100644 --- a/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/AdditionalCommentTest.php +++ b/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/AdditionalCommentTest.php @@ -36,7 +36,7 @@ class AdditionalCommentTest extends \PHPUnit\Framework\TestCase protected function setUp() { $this->abstractElementMock = $this->getMockBuilder(AbstractElement::class) - ->setMethods(['getComment', 'getLabel']) + ->setMethods(['getComment', 'getLabel', 'getHtmlId', 'getName']) ->disableOriginalConstructor() ->getMock(); $this->contextMock = $this->getMockBuilder(Context::class) diff --git a/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/CollectionTimeLabelTest.php b/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/CollectionTimeLabelTest.php index 54612076a757f..4adbb236ab952 100644 --- a/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/CollectionTimeLabelTest.php +++ b/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/CollectionTimeLabelTest.php @@ -37,7 +37,7 @@ class CollectionTimeLabelTest extends \PHPUnit\Framework\TestCase protected function setUp() { $this->abstractElementMock = $this->getMockBuilder(AbstractElement::class) - ->setMethods(['getComment']) + ->setMethods(['getComment', 'getHtmlId', 'getName']) ->disableOriginalConstructor() ->getMock(); $this->contextMock = $this->getMockBuilder(Context::class) diff --git a/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/SubscriptionStatusLabelTest.php b/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/SubscriptionStatusLabelTest.php index 0806187ebac01..3e0307c9d86e1 100644 --- a/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/SubscriptionStatusLabelTest.php +++ b/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/SubscriptionStatusLabelTest.php @@ -51,7 +51,7 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); $this->abstractElementMock = $this->getMockBuilder(AbstractElement::class) - ->setMethods(['getComment']) + ->setMethods(['getComment', 'getHtmlId', 'getName']) ->disableOriginalConstructor() ->getMock(); $this->formMock = $this->getMockBuilder(Form::class) diff --git a/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/VerticalTest.php b/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/VerticalTest.php index 6a0cecc781062..8ca562385236a 100644 --- a/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/VerticalTest.php +++ b/app/code/Magento/Analytics/Test/Unit/Block/Adminhtml/System/Config/VerticalTest.php @@ -36,7 +36,7 @@ class VerticalTest extends \PHPUnit\Framework\TestCase protected function setUp() { $this->abstractElementMock = $this->getMockBuilder(AbstractElement::class) - ->setMethods(['getComment', 'getLabel', 'getHint']) + ->setMethods(['getComment', 'getLabel', 'getHint', 'getHtmlId', 'getName']) ->disableOriginalConstructor() ->getMock(); $this->contextMock = $this->getMockBuilder(Context::class) diff --git a/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/FileTest.php b/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/FileTest.php index de18d45d26864..011bcfee64af5 100644 --- a/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/FileTest.php +++ b/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/FileTest.php @@ -11,6 +11,11 @@ class FileTest extends \PHPUnit\Framework\TestCase { + /** + * @var \Magento\Framework\Escaper|\PHPUnit_Framework_MockObject_MockObject + */ + private $escaperMock; + /** * @var \Magento\Config\Block\System\Config\Form\Field\File */ @@ -24,6 +29,8 @@ class FileTest extends \PHPUnit\Framework\TestCase protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->escaperMock = $this->createMock(\Magento\Framework\Escaper::class); + $this->escaperMock->method('escapeHtml')->willReturnArgument(0); $this->testData = [ 'before_element_html' => 'test_before_element_html', @@ -40,7 +47,10 @@ protected function setUp() $this->file = $objectManager->getObject( \Magento\Config\Block\System\Config\Form\Field\File::class, - ['data' => $this->testData] + [ + 'escaper' => $this->escaperMock, + 'data' => $this->testData + ] ); $formMock = new \Magento\Framework\DataObject(); diff --git a/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/ImageTest.php b/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/ImageTest.php index e62aa37af47dc..6f771a2e38078 100644 --- a/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/ImageTest.php +++ b/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/ImageTest.php @@ -13,6 +13,11 @@ class ImageTest extends \PHPUnit\Framework\TestCase { + /** + * @var \Magento\Framework\Escaper|\PHPUnit_Framework_MockObject_MockObject + */ + private $escaperMock; + /** * @var \Magento\Framework\Url|\PHPUnit_Framework_MockObject_MockObject */ @@ -31,10 +36,13 @@ class ImageTest extends \PHPUnit\Framework\TestCase protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->escaperMock = $this->createMock(\Magento\Framework\Escaper::class); + $this->escaperMock->method('escapeHtml')->willReturnArgument(0); $this->urlBuilderMock = $this->createMock(\Magento\Framework\Url::class); $this->image = $objectManager->getObject( \Magento\Config\Block\System\Config\Form\Field\Image::class, [ + 'escaper' => $this->escaperMock, 'urlBuilder' => $this->urlBuilderMock, ] ); diff --git a/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/Select/AllowspecificTest.php b/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/Select/AllowspecificTest.php index f5c65e848b3bf..3799136aea9c0 100644 --- a/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/Select/AllowspecificTest.php +++ b/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/Field/Select/AllowspecificTest.php @@ -7,6 +7,11 @@ class AllowspecificTest extends \PHPUnit\Framework\TestCase { + /** + * @var \Magento\Framework\Escaper|\PHPUnit_Framework_MockObject_MockObject + */ + private $escaperMock; + /** * @var \Magento\Config\Block\System\Config\Form\Field\Select\Allowspecific */ @@ -20,8 +25,11 @@ class AllowspecificTest extends \PHPUnit\Framework\TestCase protected function setUp() { $testHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->escaperMock = $this->createMock(\Magento\Framework\Escaper::class); + $this->escaperMock->method('escapeHtml')->willReturnArgument(0); $this->_object = $testHelper->getObject( - \Magento\Config\Block\System\Config\Form\Field\Select\Allowspecific::class + \Magento\Config\Block\System\Config\Form\Field\Select\Allowspecific::class, + ['escaper' => $this->escaperMock] ); $this->_object->setData('html_id', 'spec_element'); $this->_formMock = $this->createPartialMock( diff --git a/app/code/Magento/Customer/Test/Unit/Model/Renderer/RegionTest.php b/app/code/Magento/Customer/Test/Unit/Model/Renderer/RegionTest.php index c655ff7056ed6..8421320dc7322 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Renderer/RegionTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Renderer/RegionTest.php @@ -23,7 +23,7 @@ public function testRender($regionCollection) $escaperMock = $this->createMock(\Magento\Framework\Escaper::class); $elementMock = $this->createPartialMock( \Magento\Framework\Data\Form\Element\AbstractElement::class, - ['getForm', 'getHtmlAttributes'] + ['getForm', 'getHtmlAttributes', 'getHtmlId', 'getName'] ); $countryMock = $this->createPartialMock( \Magento\Framework\Data\Form\Element\AbstractElement::class, diff --git a/app/code/Magento/OfflineShipping/Test/Unit/Block/Adminhtml/Form/Field/ImportTest.php b/app/code/Magento/OfflineShipping/Test/Unit/Block/Adminhtml/Form/Field/ImportTest.php index 8d75cc32914b4..635d0c636ca36 100644 --- a/app/code/Magento/OfflineShipping/Test/Unit/Block/Adminhtml/Form/Field/ImportTest.php +++ b/app/code/Magento/OfflineShipping/Test/Unit/Block/Adminhtml/Form/Field/ImportTest.php @@ -13,6 +13,11 @@ class ImportTest extends \PHPUnit\Framework\TestCase { + /** + * @var \Magento\Framework\Escaper|\PHPUnit_Framework_MockObject_MockObject + */ + private $escaperMock; + /** * @var \Magento\OfflineShipping\Block\Adminhtml\Form\Field\Import */ @@ -29,11 +34,16 @@ protected function setUp() \Magento\Framework\Data\Form::class, ['getFieldNameSuffix', 'addSuffixToName', 'getHtmlIdPrefix', 'getHtmlIdSuffix'] ); + $this->escaperMock = $this->createMock(\Magento\Framework\Escaper::class); + $this->escaperMock->method('escapeHtml')->willReturnArgument(0); $testData = ['name' => 'test_name', 'html_id' => 'test_html_id']; $testHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->_object = $testHelper->getObject( \Magento\OfflineShipping\Block\Adminhtml\Form\Field\Import::class, - ['data' => $testData] + [ + 'escaper' => $this->escaperMock, + 'data' => $testData, + ] ); $this->_object->setForm($this->_formMock); } diff --git a/app/code/Magento/Paypal/Test/Unit/Block/Adminhtml/System/Config/Field/Enable/AbstractEnableTest.php b/app/code/Magento/Paypal/Test/Unit/Block/Adminhtml/System/Config/Field/Enable/AbstractEnableTest.php index b33d2f5723961..dd113562783aa 100644 --- a/app/code/Magento/Paypal/Test/Unit/Block/Adminhtml/System/Config/Field/Enable/AbstractEnableTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Block/Adminhtml/System/Config/Field/Enable/AbstractEnableTest.php @@ -37,6 +37,7 @@ protected function setUp() ->setMethods( [ 'getHtmlId', + 'getName', 'getTooltip', 'getForm', ] diff --git a/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/AbstractElementTest.php b/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/AbstractElementTest.php index a85c1f4aa450c..a207f45cb805a 100644 --- a/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/AbstractElementTest.php +++ b/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/AbstractElementTest.php @@ -38,6 +38,7 @@ protected function setUp() $this->_collectionFactoryMock = $this->createMock(\Magento\Framework\Data\Form\Element\CollectionFactory::class); $this->_escaperMock = $this->createMock(\Magento\Framework\Escaper::class); + $this->_escaperMock->method('escapeHtml')->willReturnArgument(0); $this->_model = $this->getMockForAbstractClass( \Magento\Framework\Data\Form\Element\AbstractElement::class, @@ -423,9 +424,6 @@ public function testGetHtmlContainerIdWithFieldContainerIdPrefix() */ public function testAddElementValues(array $initialData, $expectedValue) { - $this->_escaperMock->expects($this->any()) - ->method('escapeHtml') - ->will($this->returnArgument(0)); $this->_model->setValues($initialData['initial_values']); $this->_model->addElementValues($initialData['add_values'], $initialData['overwrite']); diff --git a/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/LinkTest.php b/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/LinkTest.php index a2a40ee03b044..d347fed13ed65 100644 --- a/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/LinkTest.php +++ b/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/LinkTest.php @@ -26,6 +26,7 @@ protected function setUp() $factoryMock = $this->createMock(\Magento\Framework\Data\Form\Element\Factory::class); $collectionFactoryMock = $this->createMock(\Magento\Framework\Data\Form\Element\CollectionFactory::class); $escaperMock = $this->createMock(\Magento\Framework\Escaper::class); + $escaperMock->method('escapeHtml')->willReturnArgument(0); $this->_link = new \Magento\Framework\Data\Form\Element\Link( $factoryMock, $collectionFactoryMock, diff --git a/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/MultiselectTest.php b/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/MultiselectTest.php index 6d1680a9f38a6..ed2b04e47b7a0 100644 --- a/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/MultiselectTest.php +++ b/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/MultiselectTest.php @@ -7,6 +7,11 @@ class MultiselectTest extends \PHPUnit\Framework\TestCase { + /** + * @var \Magento\Framework\Escaper|\PHPUnit_Framework_MockObject_MockObject + */ + private $escaperMock; + /** * @var \Magento\Framework\Data\Form\Element\Multiselect */ @@ -15,7 +20,12 @@ class MultiselectTest extends \PHPUnit\Framework\TestCase protected function setUp() { $testHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->_model = $testHelper->getObject(\Magento\Framework\Data\Form\Element\Editablemultiselect::class); + $this->escaperMock = $this->createMock(\Magento\Framework\Escaper::class); + $this->escaperMock->method('escapeHtml')->willReturnArgument(0); + $this->_model = $testHelper->getObject( + \Magento\Framework\Data\Form\Element\Editablemultiselect::class, + ['escaper' => $this->escaperMock] + ); $this->_model->setForm(new \Magento\Framework\DataObject()); } diff --git a/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/NoteTest.php b/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/NoteTest.php index f77f4a816a1af..d58bc8639e82f 100644 --- a/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/NoteTest.php +++ b/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/NoteTest.php @@ -26,6 +26,7 @@ protected function setUp() $factoryMock = $this->createMock(\Magento\Framework\Data\Form\Element\Factory::class); $collectionFactoryMock = $this->createMock(\Magento\Framework\Data\Form\Element\CollectionFactory::class); $escaperMock = $this->createMock(\Magento\Framework\Escaper::class); + $escaperMock->method('escapeHtml')->willReturnArgument(0); $this->_model = new \Magento\Framework\Data\Form\Element\Note( $factoryMock, $collectionFactoryMock, From 4b069204298c4c0056174eeda47ca1b75802742b Mon Sep 17 00:00:00 2001 From: priti <priti@2jcommerce.in> Date: Tue, 15 Jan 2019 15:40:39 +0530 Subject: [PATCH 0403/1295] update-button-issue-while-updating-billing-and-shipping-address --- app/design/frontend/Magento/blank/web/css/source/_forms.less | 2 +- app/design/frontend/Magento/luma/web/css/source/_forms.less | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/design/frontend/Magento/blank/web/css/source/_forms.less b/app/design/frontend/Magento/blank/web/css/source/_forms.less index 94b993b53b508..c9f3c3d72ef4c 100644 --- a/app/design/frontend/Magento/blank/web/css/source/_forms.less +++ b/app/design/frontend/Magento/blank/web/css/source/_forms.less @@ -18,7 +18,7 @@ .fieldset { .lib-form-fieldset(); &:last-child { - margin-bottom: 0; + margin-bottom: @indent__base; } > .field, diff --git a/app/design/frontend/Magento/luma/web/css/source/_forms.less b/app/design/frontend/Magento/luma/web/css/source/_forms.less index 7c5027aef113b..6701d5f9e9d21 100644 --- a/app/design/frontend/Magento/luma/web/css/source/_forms.less +++ b/app/design/frontend/Magento/luma/web/css/source/_forms.less @@ -20,7 +20,7 @@ .lib-form-fieldset(); &:last-child { - margin-bottom: 0; + margin-bottom: @indent__base; } > .field, From 5e38f1d9d817a8a3db7166957e354e6b46bb1acd Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Mon, 28 Jan 2019 08:58:45 +0200 Subject: [PATCH 0404/1295] MAGETWO-95752: Unable to create Configurations for Configurable Products --- .../AdminProductGridActionGroup.xml | 5 +- .../Data/ProductConfigurableAttributeData.xml | 3 +- .../Mftf/Section/AdminProductFormSection.xml | 1 + .../AdminProductFormConfigurationsSection.xml | 4 +- ...nCheckValidatorConfigurableProductTest.xml | 128 ++++++++++++++++++ .../adminhtml/web/js/variations/variations.js | 6 +- 6 files changed, 142 insertions(+), 5 deletions(-) create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml index 054a5204004b6..835ddb93a7aa3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml @@ -7,7 +7,7 @@ --> <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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!--Filter the product grid by new from date filter--> <actionGroup name="filterProductGridBySetNewFromDate"> <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> @@ -33,6 +33,7 @@ <actionGroup name="deleteProductUsingProductGrid"> <arguments> <argument name="product"/> + <argument name="productCount" type="string" defaultValue="1"/> </arguments> <!--TODO use other action group for filtering grid when MQE-539 is implemented --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> @@ -48,7 +49,7 @@ <click selector="{{AdminProductGridSection.bulkActionOption('Delete')}}" stepKey="clickDeleteAction"/> <waitForElementVisible selector="{{AdminProductGridConfirmActionSection.title}}" stepKey="waitForConfirmModal"/> <click selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="confirmProductDelete"/> - <see selector="{{AdminMessagesSection.success}}" userInput="A total of 1 record(s) have been deleted." stepKey="seeSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="A total of {{productCount}} record(s) have been deleted." stepKey="seeSuccessMessage"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial2"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductConfigurableAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductConfigurableAttributeData.xml index 4deebbe09af34..0446a9db14f1a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductConfigurableAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductConfigurableAttributeData.xml @@ -7,10 +7,11 @@ --> <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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/dataProfileSchema.xsd"> <entity name="colorProductAttribute" type="product_attribute"> <data key="default_label" unique="suffix">Color</data> <data key="attribute_quantity">1</data> + <data key="input_type">Dropdown</data> </entity> <entity name="colorProductAttribute1" type="product_attribute"> <data key="name" unique="suffix">White</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml index 5fb9ac223c7d4..dd610c1b1a914 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml @@ -188,5 +188,6 @@ </section> <section name="AdminChooseAffectedAttributeSetPopup"> <element name="confirm" type="button" selector="button[data-index='confirm_button']" timeout="30"/> + <element name="closePopUp" type="button" selector="//*[contains(@class,'product_form_product_form_configurable_attribute_set')]//button[@data-role='closeBtn']" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml index 5ae70488d164d..ad77823cbf44b 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml @@ -7,7 +7,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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf: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"/> @@ -22,5 +22,7 @@ <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']"/> + <element name="confProductSku" type="input" selector="//*[@name='configurable-matrix[{{arg}}][sku]']" parameterized="true"/> + <element name="confProductSkuMessage" type="text" selector="//*[@name='configurable-matrix[{{arg}}][sku]']/following-sibling::label" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml new file mode 100644 index 0000000000000..ad7adc865f24d --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckValidatorConfigurableProductTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Create a Configurable Product via the Admin"/> + <title value="Check that validator works correctly when creating Configurations for Configurable Products"/> + <description value="Verify validator works correctly for Configurable Products"/> + <severity value="MAJOR"/> + <testCaseId value="MC-13719"/> + <group value="configurableProduct"/> + </annotations> + + <before> + <!--Login as admin--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--Create Category--> + <createData entity="ApiCategory" stepKey="createCategory"/> + <!--Create Configurable product--> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + + <after> + <!--Delete created data--> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <argument name="product" value="ApiConfigurableProduct"/> + <argument name="productCount" value="2"/> + </actionGroup> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="openAdminProductPage"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="resetProductsFilter" /> + + <!-- Remove attribute --> + <actionGroup ref="deleteProductAttribute" stepKey="deleteAttribute"> + <argument name="ProductAttribute" value="productAttributeWithDropdownTwoOptions"/> + </actionGroup> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributesGridPage"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="resetAttributesFilter" /> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Find the product that we just created using the product grid --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearProductsFilter" /> + <actionGroup ref="filterProductGridBySku" stepKey="findCreatedProduct"> + <argument name="product" value="ApiConfigurableProduct"/> + </actionGroup> + <waitForPageLoad stepKey="waitForProductFilterLoad"/> + <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProduct"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + + <!-- Create configurations based off the Text Swatch we created earlier --> + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickCreateConfigurations"/> + + <!--Create new attribute--> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.createNewAttribute}}" stepKey="waitForNewAttributePageOpened"/> + <click selector="{{AdminCreateProductConfigurationsPanel.createNewAttribute}}" stepKey="clickCreateNewAttribute"/> + <switchToIFrame selector="{{AdminNewAttributePanelSection.newAttributeIFrame}}" stepKey="enterAttributePanelIFrame"/> + <waitForElementVisible selector="{{AdminNewAttributePanel.defaultLabel}}" time="30" stepKey="waitForIframeLoad"/> + <fillField selector="{{AdminNewAttributePanel.defaultLabel}}" userInput="{{productAttributeWithDropdownTwoOptions.attribute_code}}" stepKey="fillDefaultLabel"/> + <selectOption selector="{{AdminNewAttributePanelSection.inputType}}" userInput="{{colorProductAttribute.input_type}}" stepKey="selectAttributeInputType"/> + <!--Add option to attribute--> + <click selector="{{AdminNewAttributePanelSection.addOption}}" stepKey="clickAddOption"/> + <waitForElementVisible selector="{{AdminNewAttributePanelSection.isDefault('1')}}" time="30" stepKey="waitForOptionRow"/> + <fillField selector="{{AdminNewAttributePanelSection.optionAdminValue('0')}}" userInput="ThisIsLongNameNameLengthMoreThanSixtyFourThisIsLongNameNameLength" stepKey="fillAdminLabel"/> + <fillField selector="{{AdminNewAttributePanelSection.optionDefaultStoreValue('0')}}" userInput="{{colorProductAttribute1.name}}" stepKey="fillDefaultLabel1"/> + + <!--Save attribute--> + <click selector="{{AdminNewAttributePanelSection.saveAttribute}}" stepKey="clickOnNewAttributePanel"/> + <waitForPageLoad stepKey="waitForSaveAttribute"/> + <switchToIFrame stepKey="switchOutOfIFrame"/> + + <!--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="{{AdminProductAttributeGridSection.attributeCodeFilterInput}}" userInput="{{productAttributeWithDropdownTwoOptions.attribute_code}}" stepKey="fillFilterAttributeCodeField"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFiltersButton"/> + <click selector="{{AdminDataGridTableSection.rowCheckbox('1')}}" stepKey="clickOnFirstCheckbox"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickNextStep"/> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanelSection.selectAllByAttribute(productAttributeWithDropdownTwoOptions.attribute_code)}}" stepKey="waitForNextPageOpened"/> + <click selector="{{AdminCreateProductConfigurationsPanelSection.selectAllByAttribute(productAttributeWithDropdownTwoOptions.attribute_code)}}" stepKey="clickSelectAll"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickNextStep1"/> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.applySinglePriceToAllSkus}}" stepKey="waitForNextPageOpened1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applySinglePriceToAllSkus}}" stepKey="clickOnApplySinglePriceToAllSkus"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.singlePrice}}" userInput="10" stepKey="enterAttributePrice"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="100" stepKey="enterAttributeQuantity"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickNextStep2"/> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="waitForNextPageOpened2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="generateProducts"/> + <waitForElementVisible selector="{{AdminProductFormActionSection.saveButton}}" stepKey="waitForSaveButtonVisible"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> + <waitForElementVisible selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="waitForPopUpVisible"/> + <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmInPopup"/> + <dontSeeElement selector="{{AdminMessagesSection.success}}" stepKey="dontSeeSaveProductMessage"/> + + <!--Close modal window--> + <click selector="{{AdminChooseAffectedAttributeSetPopup.closePopUp}}" stepKey="clickOnClosePopup"/> + <waitForElementNotVisible selector="{{AdminChooseAffectedAttributeSetPopup.closePopUp}}" stepKey="waitForDialogClosed"/> + + <!--See that validation message is shown under the fields--> + <scrollTo selector="{{AdminProductFormConfigurationsSection.currentVariationsSkuCells}}" stepKey="scrollTConfigurationTab"/> + <see userInput="Please enter less or equal than 64 symbols." selector="{{AdminProductFormConfigurationsSection.confProductSkuMessage('0')}}" stepKey="seeValidationMessage"/> + + <!--Edit "SKU" with valid quantity--> + <fillField selector="{{AdminProductFormConfigurationsSection.confProductSku('0')}}" userInput="{{ApiConfigurableProduct.sku}}-thisIsShortName" stepKey="fillValidValue"/> + + <!--Click on "Save"--> + <waitForElementVisible selector="{{AdminProductFormActionSection.saveButton}}" stepKey="waitForSaveBtnVisible"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProductAgain"/> + + <!--Click on "Confirm". Product is saved, success message appears --> + <waitForElementVisible selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="waitPopUpVisible"/> + <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmPopup"/> + <seeElement selector="{{AdminMessagesSection.success}}" stepKey="seeSaveProductMessage"/> + </test> +</tests> 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 e2e0faec3b805..1d251f8ecc333 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 @@ -383,7 +383,11 @@ define([ * Chose action for the form save button */ saveFormHandler: function () { - this.serializeData(); + this.formElement().validate(); + + if (this.formElement().source.get('params.invalid') === false) { + this.serializeData(); + } if (this.checkForNewAttributes()) { this.formSaveParams = arguments; From e152f2ebee0cf10a6e957b3670751cf00874ba1e Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Mon, 28 Jan 2019 09:30:17 +0200 Subject: [PATCH 0405/1295] MAGETWO-96690: Incorrect Rss Wishlist response --- app/code/Magento/Wishlist/Model/Rss/Wishlist.php | 2 +- ...two_wishlists_for_two_diff_customers_rollback.php | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Wishlist/Model/Rss/Wishlist.php b/app/code/Magento/Wishlist/Model/Rss/Wishlist.php index e85a3f3d4bb00..b73cae240b369 100644 --- a/app/code/Magento/Wishlist/Model/Rss/Wishlist.php +++ b/app/code/Magento/Wishlist/Model/Rss/Wishlist.php @@ -115,7 +115,7 @@ public function __construct( */ public function isAllowed() { - return (bool)$this->scopeConfig->isSetFlag('rss/wishlist/active', ScopeInterface::SCOPE_STORE) + return $this->scopeConfig->isSetFlag('rss/wishlist/active', ScopeInterface::SCOPE_STORE) && $this->getWishlist()->getCustomerId() == $this->wishlistHelper->getCustomer()->getId(); } diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/two_wishlists_for_two_diff_customers_rollback.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/two_wishlists_for_two_diff_customers_rollback.php index 6d2619f63c640..31cab3f9cbc74 100644 --- a/dev/tests/integration/testsuite/Magento/Wishlist/_files/two_wishlists_for_two_diff_customers_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/two_wishlists_for_two_diff_customers_rollback.php @@ -8,14 +8,14 @@ use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Framework\Exception\NoSuchEntityException; +/** @var \Magento\Framework\ObjectManagerInterface $objectManager */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + /** @var \Magento\Framework\Registry $registry */ -$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); +$registry = $objectManager->get(\Magento\Framework\Registry::class); $registry->unregister('isSecureArea'); $registry->register('isSecureArea', true); -/** @var \Magento\Framework\ObjectManagerInterface $objectManager */ -$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - /** @var \Magento\Wishlist\Model\Wishlist $wishlist */ $wishlist = $objectManager->create(\Magento\Wishlist\Model\Wishlist::class); @@ -29,9 +29,7 @@ $wishlist->loadByCustomerId($secondCustomer->getId()); $wishlist->delete(); } catch (NoSuchEntityException $e) { - /** - * Tests which are wrapped with MySQL transaction clear all data by transaction rollback. - */ + /** Tests which are wrapped with MySQL transaction clear all data by transaction rollback. */ } $registry->unregister('isSecureArea'); From 15cb4cbe8599737ee1a99fcd7683e961f77b0f43 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Mon, 28 Jan 2019 10:22:43 +0200 Subject: [PATCH 0406/1295] MAGETWO-97456: Cart's customer and address mismatch --- app/code/Magento/Quote/Model/QuoteAddressValidator.php | 2 +- app/code/Magento/Quote/Model/ShippingAddressManagement.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Quote/Model/QuoteAddressValidator.php b/app/code/Magento/Quote/Model/QuoteAddressValidator.php index aabd69337538a..06f21ee119bd5 100644 --- a/app/code/Magento/Quote/Model/QuoteAddressValidator.php +++ b/app/code/Magento/Quote/Model/QuoteAddressValidator.php @@ -65,7 +65,7 @@ private function doValidate(AddressInterface $address, $customerId) //validate customer id if ($customerId) { try { - $customer = $this->customerRepository->getById($customerId); + $this->customerRepository->getById($customerId); } catch (NoSuchEntityException $exception) { throw new NoSuchEntityException(__('Invalid customer id %1', $customerId)); } diff --git a/app/code/Magento/Quote/Model/ShippingAddressManagement.php b/app/code/Magento/Quote/Model/ShippingAddressManagement.php index 8f977e30e2699..71a93e4604200 100644 --- a/app/code/Magento/Quote/Model/ShippingAddressManagement.php +++ b/app/code/Magento/Quote/Model/ShippingAddressManagement.php @@ -78,7 +78,7 @@ public function __construct( } /** - * @inheritDoc + * @inheritdoc * @SuppressWarnings(PHPMD.NPathComplexity) */ public function assign($cartId, \Magento\Quote\Api\Data\AddressInterface $address) @@ -122,7 +122,7 @@ public function assign($cartId, \Magento\Quote\Api\Data\AddressInterface $addres } /** - * @inheritDoc + * @inheritdoc */ public function get($cartId) { From aa8747547d865a5c0b7b15b03d2f730be1cb413d Mon Sep 17 00:00:00 2001 From: Cristiano Casciotti <teknoman84@gmail.com> Date: Fri, 18 Jan 2019 13:01:54 +0100 Subject: [PATCH 0407/1295] Changed way to calculate values --- app/code/Magento/Quote/Model/Quote.php | 17 +++++++---------- app/code/Magento/Quote/Model/Quote/Address.php | 14 ++++---------- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/app/code/Magento/Quote/Model/Quote.php b/app/code/Magento/Quote/Model/Quote.php index 9849c646b4465..a3f5d1aaa6a6a 100644 --- a/app/code/Magento/Quote/Model/Quote.php +++ b/app/code/Magento/Quote/Model/Quote.php @@ -2256,11 +2256,10 @@ public function validateMinimumAmount($multishipping = false) $taxes = ($taxInclude) ? $address->getBaseTaxAmount() : 0; foreach ($address->getQuote()->getItemsCollection() as $item) { /** @var \Magento\Quote\Model\Quote\Item $item */ - if ($includeDiscount) { - $amount = $item->getBaseRowTotal() - $item->getBaseDiscountAmount() + $taxes; - } else { - $amount = $taxInclude ? $item->getBaseRowTotalInclTax() : $item->getBaseRowTotal(); - } + $amount = $includeDiscount ? + $item->getBaseRowTotal() - $item->getBaseDiscountAmount() + $taxes : + $item->getBaseRowTotal() + $taxes; + if ($amount < $minAmount) { return false; } @@ -2270,11 +2269,9 @@ public function validateMinimumAmount($multishipping = false) $baseTotal = 0; foreach ($addresses as $address) { $taxes = ($taxInclude) ? $address->getBaseTaxAmount() : 0; - if ($includeDiscount) { - $baseTotal += $address->getBaseSubtotalWithDiscount() + $taxes; - } else { - $baseTotal += $taxInclude ? $address->getBaseSubtotalTotalInclTax() : $address->getBaseSubtotal(); - } + $baseTotal += $includeDiscount ? + $address->getBaseSubtotalWithDiscount() + $taxes : + $address->getBaseSubtotal() + $taxes; } if ($baseTotal < $minAmount) { return false; diff --git a/app/code/Magento/Quote/Model/Quote/Address.php b/app/code/Magento/Quote/Model/Quote/Address.php index 3091604ed3021..c091d4c63786d 100644 --- a/app/code/Magento/Quote/Model/Quote/Address.php +++ b/app/code/Magento/Quote/Model/Quote/Address.php @@ -1159,17 +1159,11 @@ public function validateMinimumAmount() $storeId ); - if ($includeDiscount) { - $taxes = $taxInclude ? $this->getBaseTaxAmount() : 0; + $taxes = $taxInclude ? $this->getBaseTaxAmount() : 0; - $isMinimumReached = ($this->getBaseSubtotalWithDiscount() + $taxes >= $amount); - } else { - $isMinimumReached = $taxInclude - ? ($this->getBaseSubtotalTotalInclTax() >= $amount) - : ($this->getBaseSubtotal() >= $amount); - } - - return $isMinimumReached; + return $includeDiscount ? + ($this->getBaseSubtotalWithDiscount() + $taxes >= $amount) : + ($this->getBaseSubtotal() + $taxes >= $amount); } /** From 5c9c771e83bf04f6938f8c8b08ec96b919faa209 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Mon, 28 Jan 2019 10:35:05 +0200 Subject: [PATCH 0408/1295] MAGETWO-96507: Fixed incorrect stacktrace displaying --- .htaccess | 2 ++ lib/internal/Magento/Framework/App/Http.php | 23 +++++++++++++++---- .../Magento/Framework/App/StaticResource.php | 19 +++++++++++---- .../Magento/Framework/Message/Manager.php | 21 +++++++++++++---- 4 files changed, 51 insertions(+), 14 deletions(-) diff --git a/.htaccess b/.htaccess index ad123614ae08e..cc59be5480798 100644 --- a/.htaccess +++ b/.htaccess @@ -29,6 +29,8 @@ ############################################ ## default index file +## Specifies option, to use methods arguments in backtrace or not + SetEnv MAGE_DEBUG_SHOW_ARGS 1 DirectoryIndex index.php diff --git a/lib/internal/Magento/Framework/App/Http.php b/lib/internal/Magento/Framework/App/Http.php index 3c6dee49f97b4..23024a44c2def 100644 --- a/lib/internal/Magento/Framework/App/Http.php +++ b/lib/internal/Magento/Framework/App/Http.php @@ -6,6 +6,7 @@ namespace Magento\Framework\App; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Debug; use Magento\Framework\ObjectManager\ConfigLoaderInterface; use Magento\Framework\App\Request\Http as RequestHttp; use Magento\Framework\App\Response\Http as ResponseHttp; @@ -79,7 +80,7 @@ class Http implements \Magento\Framework\AppInterface * @param ResponseHttp $response * @param ConfigLoaderInterface $configLoader * @param State $state - * @param Filesystem $filesystem, + * @param Filesystem $filesystem * @param \Magento\Framework\Registry $registry */ public function __construct( @@ -149,7 +150,7 @@ public function launch() } /** - * {@inheritdoc} + * @inheritdoc */ public function catchException(Bootstrap $bootstrap, \Exception $exception) { @@ -198,6 +199,7 @@ private function buildContentFromException(\Exception $exception) { /** @var \Exception[] $exceptions */ $exceptions = []; + do { $exceptions[] = $exception; } while ($exception = $exception->getPrevious()); @@ -214,7 +216,12 @@ private function buildContentFromException(\Exception $exception) $index, get_class($exception), $exception->getMessage(), - $exception->getTraceAsString() + Debug::trace( + $exception->getTrace(), + true, + true, + (bool)getenv('MAGE_DEBUG_SHOW_ARGS') + ) ); } @@ -312,7 +319,15 @@ private function handleInitException(\Exception $exception) */ private function handleGenericReport(Bootstrap $bootstrap, \Exception $exception) { - $reportData = [$exception->getMessage(), $exception->getTraceAsString()]; + $reportData = [ + $exception->getMessage(), + Debug::trace( + $exception->getTrace(), + true, + true, + (bool)getenv('MAGE_DEBUG_SHOW_ARGS') + ) + ]; $params = $bootstrap->getParams(); if (isset($params['REQUEST_URI'])) { $reportData['url'] = $params['REQUEST_URI']; diff --git a/lib/internal/Magento/Framework/App/StaticResource.php b/lib/internal/Magento/Framework/App/StaticResource.php index 87a2c37f94768..b5e4b2828d93b 100644 --- a/lib/internal/Magento/Framework/App/StaticResource.php +++ b/lib/internal/Magento/Framework/App/StaticResource.php @@ -10,6 +10,7 @@ use Magento\Framework\Filesystem; use Magento\Framework\Config\ConfigOptionsListConstants; use Psr\Log\LoggerInterface; +use Magento\Framework\Debug; /** * Entry point for retrieving static resources like JS, CSS, images by requested public path @@ -54,12 +55,12 @@ class StaticResource implements \Magento\Framework\AppInterface private $objectManager; /** - * @var \Magento\Framework\ObjectManager\ConfigLoaderInterface + * @var ConfigLoaderInterface */ private $configLoader; /** - * @var \Magento\Framework\Filesystem + * @var Filesystem */ private $filesystem; @@ -69,7 +70,7 @@ class StaticResource implements \Magento\Framework\AppInterface private $deploymentConfig; /** - * @var \Psr\Log\LoggerInterface + * @var LoggerInterface */ private $logger; @@ -138,7 +139,7 @@ public function launch() } /** - * {@inheritdoc} + * @inheritdoc */ public function catchException(Bootstrap $bootstrap, \Exception $exception) { @@ -146,7 +147,15 @@ public function catchException(Bootstrap $bootstrap, \Exception $exception) if ($bootstrap->isDeveloperMode()) { $this->response->setHttpResponseCode(404); $this->response->setHeader('Content-Type', 'text/plain'); - $this->response->setBody($exception->getMessage() . "\n" . $exception->getTraceAsString()); + $this->response->setBody( + $exception->getMessage() . "\n" . + Debug::trace( + $exception->getTrace(), + true, + true, + (bool)getenv('MAGE_DEBUG_SHOW_ARGS') + ) + ); $this->response->sendResponse(); } else { require $this->getFilesystem()->getDirectoryRead(DirectoryList::PUB)->getAbsolutePath('errors/404.php'); diff --git a/lib/internal/Magento/Framework/Message/Manager.php b/lib/internal/Magento/Framework/Message/Manager.php index f31892a938fb1..37ca1e7e2b725 100644 --- a/lib/internal/Magento/Framework/Message/Manager.php +++ b/lib/internal/Magento/Framework/Message/Manager.php @@ -8,6 +8,7 @@ use Magento\Framework\Event; use Psr\Log\LoggerInterface; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Debug; /** * Message manager model @@ -67,7 +68,7 @@ class Manager implements ManagerInterface * @param Event\ManagerInterface $eventManager * @param LoggerInterface $logger * @param string $defaultGroup - * @param ExceptionMessageFactoryInterface|null exceptionMessageFactory + * @param ExceptionMessageFactoryInterface|null $exceptionMessageFactory */ public function __construct( Session $session, @@ -89,7 +90,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function getDefaultGroup() { @@ -110,8 +111,8 @@ protected function prepareGroup($group) /** * @inheritdoc * - * @param string|null $group * @param bool $clear + * @param string|null $group * @return Collection */ public function getMessages($clear = false, $group = null) @@ -248,7 +249,12 @@ public function addException(\Exception $exception, $alternativeText = null, $gr 'Exception message: %s%sTrace: %s', $exception->getMessage(), "\n", - $exception->getTraceAsString() + Debug::trace( + $exception->getTrace(), + true, + true, + (bool)getenv('MAGE_DEBUG_SHOW_ARGS') + ) ); $this->logger->critical($message); @@ -286,7 +292,12 @@ public function addExceptionMessage(\Exception $exception, $alternativeText = nu 'Exception message: %s%sTrace: %s', $exception->getMessage(), "\n", - $exception->getTraceAsString() + Debug::trace( + $exception->getTrace(), + true, + true, + (bool)getenv('MAGE_DEBUG_SHOW_ARGS') + ) ); $this->logger->critical($message); From 12507a8d972e91a9155b702d4fddf1cfd491edfb Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Mon, 28 Jan 2019 13:02:52 +0200 Subject: [PATCH 0409/1295] MAGETWO-97456: Cart's customer and address mismatch --- .../Customer/_files/customer_with_addresses_rollback.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_addresses_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_addresses_rollback.php index fa44ee4c84ece..c3acf62cddefa 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_addresses_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_addresses_rollback.php @@ -7,7 +7,6 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\Customer\Api\CustomerRepositoryInterface; -use Magento\Customer\Api\AddressRepositoryInterface; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Registry; @@ -20,8 +19,6 @@ $customerRepo = $objectManager->get(CustomerRepositoryInterface::class); try { $customer = $customerRepo->get('customer_with_addresses@test.com'); - /** @var AddressRepositoryInterface $addressRepo */ - $addressRepo = $objectManager->get(AddressRepositoryInterface::class); $customerRepo->delete($customer); } catch (NoSuchEntityException $exception) { //Already deleted From 615a59504aed5401e9aa866e913623505b75cbe2 Mon Sep 17 00:00:00 2001 From: amol 2jcommerce <amol@2jcommerce.in> Date: Mon, 28 Jan 2019 17:36:21 +0530 Subject: [PATCH 0410/1295] My-account-page-title-extra-space-on-mobile-2.2 --- .../Magento/blank/Magento_Customer/web/css/source/_module.less | 2 +- .../Magento/luma/Magento_Customer/web/css/source/_module.less | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less index 3ffaeb82cdc2a..a22e5baeb57fc 100644 --- a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less @@ -368,7 +368,7 @@ .account { .page.messages { - margin-bottom: @indent__xl; + margin-bottom: 0; } .toolbar { diff --git a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less index d7ae6c3b28f4a..fe423a236c80e 100755 --- a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less @@ -551,7 +551,7 @@ .account { .page.messages { - margin-bottom: @indent__xl; + margin-bottom: 0; } .column.main { From 3831b72706f55058f880f0b30ec9ce15fb129a05 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Mon, 28 Jan 2019 14:56:08 +0200 Subject: [PATCH 0411/1295] MAGETWO-95368: Synonyms controller minor fix --- .../Magento/Search/Controller/Adminhtml/Synonyms/MassDelete.php | 1 + .../Test/Unit/Controller/Adminhtml/Synonyms/MassDeleteTest.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/MassDelete.php b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/MassDelete.php index f0248dc7225bb..1318bda51e2d5 100644 --- a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/MassDelete.php +++ b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/MassDelete.php @@ -62,6 +62,7 @@ public function __construct( * * @return \Magento\Backend\Model\View\Result\Redirect * @throws \Magento\Framework\Exception\LocalizedException|\Exception + * @throws \Magento\Framework\Exception\NotFoundException|\Exception */ public function execute() { diff --git a/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Synonyms/MassDeleteTest.php b/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Synonyms/MassDeleteTest.php index 4023811d8d2cd..8b98959225528 100644 --- a/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Synonyms/MassDeleteTest.php +++ b/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Synonyms/MassDeleteTest.php @@ -8,7 +8,7 @@ namespace Magento\Search\Test\Unit\Controller\Adminhtml\Synonyms; /** - * Unit tests for MassDelete controller. + * Unit tests for Magento\Search\Controller\Adminhtml\Synonyms\MassDelete controller. */ class MassDeleteTest extends \PHPUnit\Framework\TestCase { From daa78fa87ac8d18101346f60442d76fa11b58c41 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Mon, 28 Jan 2019 15:42:20 +0200 Subject: [PATCH 0412/1295] MAGETWO-95368: Synonyms controller minor fix --- .../Search/Controller/Adminhtml/Synonyms/MassDelete.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/MassDelete.php b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/MassDelete.php index 1318bda51e2d5..2f3d574e21485 100644 --- a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/MassDelete.php +++ b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/MassDelete.php @@ -61,8 +61,7 @@ public function __construct( * Execute action. * * @return \Magento\Backend\Model\View\Result\Redirect - * @throws \Magento\Framework\Exception\LocalizedException|\Exception - * @throws \Magento\Framework\Exception\NotFoundException|\Exception + * @throws \Magento\Framework\Exception\NotFoundException */ public function execute() { From 1cb5e515a0aad074fb67dcc73ad9075214cbdcac Mon Sep 17 00:00:00 2001 From: Ankur <ankurverma@cedcoss.com> Date: Wed, 16 Jan 2019 19:09:49 +0530 Subject: [PATCH 0413/1295] Updated AfterImportDataObserver.php Fixed #20282 Module Catalog Url Rewrite: Permanent Redirect for old URL is missed when product was imported --- .../CatalogUrlRewrite/Observer/AfterImportDataObserver.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/AfterImportDataObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/AfterImportDataObserver.php index 022a78be00197..cdee3aaaa331e 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/AfterImportDataObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/AfterImportDataObserver.php @@ -135,6 +135,7 @@ class AfterImportDataObserver implements ObserverInterface 'url_path', 'name', 'visibility', + 'save_rewrites_history' ]; /** From 7de5e7ea7319bb6c1ab0c44e0ccbc898939d2983 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Tue, 22 Jan 2019 14:18:14 +0200 Subject: [PATCH 0414/1295] Fix static tests. --- .../Observer/AfterImportDataObserver.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/AfterImportDataObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/AfterImportDataObserver.php index cdee3aaaa331e..9aaa384776855 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/AfterImportDataObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/AfterImportDataObserver.php @@ -200,6 +200,7 @@ public function __construct( /** * Action after data import. + * * Save new url rewrites and remove old if exist. * * @param Observer $observer @@ -268,6 +269,8 @@ protected function _populateForUrlGeneration($rowData) } /** + * Add store id to product data. + * * @param \Magento\Catalog\Model\Product $product * @param array $rowData * @return void @@ -437,6 +440,8 @@ protected function currentUrlRewritesRegenerate() } /** + * Generate url-rewrite for outogenerated url-rewirte. + * * @param UrlRewrite $url * @param Category $category * @return array @@ -471,6 +476,8 @@ protected function generateForAutogenerated($url, $category) } /** + * Generate url-rewrite for custom url-rewirte. + * * @param UrlRewrite $url * @param Category $category * @return array @@ -504,6 +511,8 @@ protected function generateForCustom($url, $category) } /** + * Retrieve category from url metadata. + * * @param UrlRewrite $url * @return Category|null|bool */ @@ -518,6 +527,8 @@ protected function retrieveCategoryFromMetadata($url) } /** + * Check, category suited for url-rewrite generation. + * * @param \Magento\Catalog\Model\Category $category * @param int $storeId * @return bool From 3fb9d5990b3a63b5a0e5969e5cc8564420279217 Mon Sep 17 00:00:00 2001 From: Parag Chavare <parag@2jcommerce.in> Date: Fri, 25 Jan 2019 15:05:41 +0530 Subject: [PATCH 0415/1295] minicart-three-digit-quantity-cutoff --- .../blank/Magento_Checkout/web/css/source/module/_minicart.less | 2 +- .../luma/Magento_Checkout/web/css/source/module/_minicart.less | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less index 54457353f90ae..27655417984d0 100644 --- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less +++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less @@ -341,7 +341,7 @@ .item-qty { margin-right: @indent__s; text-align: center; - width: 40px; + width: 45px; } .update-cart-item { diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less index 7090da10b46c0..05ce84995afae 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less @@ -352,7 +352,7 @@ .item-qty { margin-right: @indent__s; text-align: center; - width: 40px; + width: 45px; } .update-cart-item { From 6e7281d36556d907c4db9311845aca523604ddb3 Mon Sep 17 00:00:00 2001 From: Arvinda kumar <arvindakumar@cedcommerce.com> Date: Thu, 24 Jan 2019 13:40:47 +0530 Subject: [PATCH 0416/1295] issue fixed #20563 Go to shipping information, Update qty & Addresses and Enter a new address button Not aligned from left and right in 767px screen size issue fixed #20563 Go to shipping information, Update qty & Addresses and Enter a new address button Not aligned from left and right in 767px screen size --- .../Magento_Multishipping/web/css/source/_module.less | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/luma/Magento_Multishipping/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Multishipping/web/css/source/_module.less index 7662c60734a1b..9761f36b96344 100644 --- a/app/design/frontend/Magento/luma/Magento_Multishipping/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Multishipping/web/css/source/_module.less @@ -374,7 +374,7 @@ text-align: right; .action { - margin-left: @indent__s; + margin-left: 0; &.back { display: block; @@ -496,4 +496,12 @@ margin-left: @indent__xl; } } + + .multicheckout { + .actions-toolbar { + > .primary { + margin-right: 0; + } + } + } } From f0032eef4ddebeec30caafac764c1c5262ee4ec3 Mon Sep 17 00:00:00 2001 From: Parag Chavare <parag@2jcommerce.in> Date: Wed, 23 Jan 2019 17:55:52 +0530 Subject: [PATCH 0417/1295] bundle-product-radio-button-misalign --- .../Magento/luma/Magento_Bundle/web/css/source/_module.less | 1 + 1 file changed, 1 insertion(+) diff --git a/app/design/frontend/Magento/luma/Magento_Bundle/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Bundle/web/css/source/_module.less index 43ae23bab7895..99c8aa1ad2bae 100644 --- a/app/design/frontend/Magento/luma/Magento_Bundle/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Bundle/web/css/source/_module.less @@ -58,6 +58,7 @@ .field.choice { input { float: left; + margin-top: 4px; } .label { From a9712700ab7436b88987446671722df80a15dc36 Mon Sep 17 00:00:00 2001 From: Parag Chavare <parag@2jcommerce.in> Date: Wed, 23 Jan 2019 16:08:54 +0530 Subject: [PATCH 0418/1295] recent-order-product-title-misaligned --- .../Magento/blank/Magento_Catalog/web/css/source/_module.less | 1 + .../Magento/luma/Magento_Catalog/web/css/source/_module.less | 1 + 2 files changed, 2 insertions(+) diff --git a/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less index 2dd8463308a2c..7c8fb068c26a8 100644 --- a/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less @@ -489,6 +489,7 @@ .product-items-names { .product-item { margin-bottom: @indent__s; + display: flex; } .product-item-name { diff --git a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less index 228c6947c938b..caa69b4fbd34d 100644 --- a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less @@ -564,6 +564,7 @@ .product-items-names { .product-item { margin-bottom: @indent__s; + display: flex; } .product-item-name { From 81d878183d7b675d1155885825257ffb5d9353b7 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Thu, 24 Jan 2019 13:31:49 +0200 Subject: [PATCH 0419/1295] Fix static tests. --- .../Magento/blank/Magento_Catalog/web/css/source/_module.less | 2 +- .../Magento/luma/Magento_Catalog/web/css/source/_module.less | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less index 7c8fb068c26a8..b5d582d1dc3e9 100644 --- a/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less @@ -489,7 +489,7 @@ .product-items-names { .product-item { margin-bottom: @indent__s; - display: flex; + display: flex; } .product-item-name { diff --git a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less index caa69b4fbd34d..fbcd8b2e5723b 100644 --- a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less @@ -564,7 +564,7 @@ .product-items-names { .product-item { margin-bottom: @indent__s; - display: flex; + display: flex; } .product-item-name { From 73cb5c824efa9e7b41ef52eff7c1f40385c699ee Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Thu, 24 Jan 2019 16:14:28 +0200 Subject: [PATCH 0420/1295] Fix static tests. --- .../Magento/blank/Magento_Catalog/web/css/source/_module.less | 2 +- .../Magento/luma/Magento_Catalog/web/css/source/_module.less | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less index b5d582d1dc3e9..85ef048ef0fc7 100644 --- a/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less @@ -488,8 +488,8 @@ .product-items-names { .product-item { - margin-bottom: @indent__s; display: flex; + margin-bottom: @indent__s; } .product-item-name { diff --git a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less index fbcd8b2e5723b..6aa0e50f3c393 100644 --- a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less @@ -563,8 +563,8 @@ .product-items-names { .product-item { - margin-bottom: @indent__s; display: flex; + margin-bottom: @indent__s; } .product-item-name { From 4ad7bd32b1fcae3ca93071105a25d751336e5362 Mon Sep 17 00:00:00 2001 From: Pieter Hoste <hoste.pieter@gmail.com> Date: Sat, 19 Jan 2019 13:44:43 +0100 Subject: [PATCH 0421/1295] Fixes incorrect where condition when deleting swatch option, it deleted all options instead of a specific one. --- app/code/Magento/Swatches/Model/ResourceModel/Swatch.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Swatches/Model/ResourceModel/Swatch.php b/app/code/Magento/Swatches/Model/ResourceModel/Swatch.php index 8ca694725511d..b0c10847437db 100644 --- a/app/code/Magento/Swatches/Model/ResourceModel/Swatch.php +++ b/app/code/Magento/Swatches/Model/ResourceModel/Swatch.php @@ -49,7 +49,7 @@ public function clearSwatchOptionByOptionIdAndType($optionIDs, $type = null) { if (count($optionIDs)) { foreach ($optionIDs as $optionId) { - $where = ['option_id' => $optionId]; + $where = ['option_id = ?' => $optionId]; if ($type !== null) { $where['type = ?'] = $type; } From b136710f0573d88c9830b97237cdb652ce476433 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Tue, 22 Jan 2019 11:35:33 +0200 Subject: [PATCH 0422/1295] Fix static test. --- app/code/Magento/Swatches/Model/ResourceModel/Swatch.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Swatches/Model/ResourceModel/Swatch.php b/app/code/Magento/Swatches/Model/ResourceModel/Swatch.php index b0c10847437db..39245941df948 100644 --- a/app/code/Magento/Swatches/Model/ResourceModel/Swatch.php +++ b/app/code/Magento/Swatches/Model/ResourceModel/Swatch.php @@ -7,8 +7,9 @@ namespace Magento\Swatches\Model\ResourceModel; /** - * @codeCoverageIgnore * Swatch Resource Model + * + * @codeCoverageIgnore * @api * @since 100.0.2 */ @@ -25,8 +26,10 @@ protected function _construct() } /** - * @param string $defaultValue + * Update default swatch option value. + * * @param integer $id + * @param string $defaultValue * @return void */ public function saveDefaultSwatchOption($id, $defaultValue) From 2eeee1aab7dea70568d2fa805aea67a381b36fc8 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Tue, 29 Jan 2019 11:16:41 +0200 Subject: [PATCH 0423/1295] MAGETWO-94348: The cart rule cannot affect the Bundle product with Fixed Price --- .../ActionGroup/AdminCreateCartPriceRuleActionGroup.xml | 9 ++++----- .../Test/AdminCartRulesAppliedForProductInCartTest.xml | 1 - 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionGroup.xml index 4328d3dcbfb92..c5f35aef6f480 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionGroup.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionGroup.xml @@ -28,7 +28,6 @@ <arguments> <argument name="condition" type="string" defaultValue="Category" /> <argument name="operation" type="string" defaultValue="equals or greater than" /> - <argument name="ruleToChange2" type="string" defaultValue="..." /> <argument name="totalQuantity" type="string" defaultValue="2" /> <argument name="value" type="string" defaultValue="_defaultCategory.name" /> </arguments> @@ -39,13 +38,13 @@ <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.ruleParameter('is')}}" after="selectRule" stepKey="waitForFirstRuleElement"/> <click selector="{{AdminCartPriceRulesFormSection.ruleParameter('is')}}" after="waitForFirstRuleElement" stepKey="clickToChangeRule"/> <selectOption selector="{{AdminCartPriceRulesFormSection.ruleOperatorSelect('1--1')}}" userInput="{{operation}}" after="clickToChangeRule" stepKey="selectRule1"/> - <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.ruleParameter(ruleToChange2)}}" after="selectRule1" stepKey="waitForSecondRuleElement"/> - <click selector="{{AdminCartPriceRulesFormSection.ruleParameter(ruleToChange2)}}" after="waitForSecondRuleElement" stepKey="clickToChangeRule1"/> + <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.ruleParameter('...')}}" after="selectRule1" stepKey="waitForSecondRuleElement"/> + <click selector="{{AdminCartPriceRulesFormSection.ruleParameter('...')}}" after="waitForSecondRuleElement" stepKey="clickToChangeRule1"/> <fillField selector="{{AdminCartPriceRulesFormSection.ruleValueInput('1--1')}}" userInput="{{totalQuantity}}" after="clickToChangeRule1" stepKey="fillRule"/> <click selector="{{AdminCartPriceRulesFormSection.addCondition('1--1')}}" after="fillRule" stepKey="addSecondCondition"/> <selectOption selector="{{AdminCartPriceRulesFormSection.ruleCondition('1--1')}}" userInput="{{condition}}" after="addSecondCondition" stepKey="selectSecondCondition"/> - <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.ruleParameter(ruleToChange2)}}" after="selectSecondCondition" stepKey="waitForThirdRuleElement"/> - <click selector="{{AdminCartPriceRulesFormSection.ruleParameter(ruleToChange2)}}" after="waitForThirdRuleElement" stepKey="addThirdCondition"/> + <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.ruleParameter('...')}}" after="selectSecondCondition" stepKey="waitForThirdRuleElement"/> + <click selector="{{AdminCartPriceRulesFormSection.ruleParameter('...')}}" after="waitForThirdRuleElement" stepKey="addThirdCondition"/> <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.chooseValue('1--1--1')}}" after="addThirdCondition" stepKey="waitForForthRuleElement"/> <click selector="{{AdminCartPriceRulesFormSection.chooseValue('1--1--1')}}" after="waitForForthRuleElement" stepKey="chooseValue"/> <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.categoryCheckbox(value)}}" after="chooseValue" stepKey="waitForCategoryVisible"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml index d14963d375fa5..7da6ca3fbe072 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml @@ -74,7 +74,6 @@ <argument name="rule" value="PriceRuleWithCondition"/> <argument name="condition" value="Category"/> <argument name="operation" value="equals or greater than"/> - <argument name="ruleToChange2" value="..."/> <argument name="totalQuantity" value="2"/> <argument name="value" value="{{_defaultCategory.name}}"/> </actionGroup> From 8023476f1fdc85668a9ead8a4820b9e211aacf86 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Tue, 29 Jan 2019 11:21:20 +0200 Subject: [PATCH 0424/1295] MAGETWO-95752: Unable to create Configurations for Configurable Products --- ...inCreateConfigurableProductActionGroup.xml | 25 ++++++++++++++ .../AdminProductFormConfigurationsSection.xml | 4 +-- ...nCheckValidatorConfigurableProductTest.xml | 34 ++++++------------- 3 files changed, 37 insertions(+), 26 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateConfigurableProductActionGroup.xml index 3b4e7f55d186c..f655589c66a80 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateConfigurableProductActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateConfigurableProductActionGroup.xml @@ -65,4 +65,29 @@ <conditionalClick selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" dependentSelector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" visible="true" stepKey="clickConfirm"/> <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="You saved the product." stepKey="assertSuccess"/> </actionGroup> + + <actionGroup name="AdminGenerateConfigurations"> + <arguments> + <argument name="attributeCode" type="string"/> + <argument name="qty" type="string"/> + <argument name="price" type="string"/> + </arguments> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFilters"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="clickOnFilters"/> + <fillField selector="{{AdminProductAttributeGridSection.attributeCodeFilterInput}}" userInput="{{attributeCode}}" stepKey="fillFilterAttributeCodeField"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFiltersButton"/> + <click selector="{{AdminDataGridTableSection.rowCheckbox('1')}}" stepKey="clickOnFirstCheckbox"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickNextStep"/> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanelSection.selectAllByAttribute(attributeCode)}}" stepKey="waitForNextPageOpened"/> + <click selector="{{AdminCreateProductConfigurationsPanelSection.selectAllByAttribute(productAttributeWithDropdownTwoOptions.attribute_code)}}" stepKey="clickSelectAll"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickNextStep1"/> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.applySinglePriceToAllSkus}}" stepKey="waitForNextPageOpened1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applySinglePriceToAllSkus}}" stepKey="clickOnApplySinglePriceToAllSkus"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.singlePrice}}" userInput="{{price}}" stepKey="enterAttributePrice"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="{{qty}}" stepKey="enterAttributeQuantity"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickNextStep2"/> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="waitForNextPageOpened2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="generateProducts"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml index ad77823cbf44b..c774dad1ed607 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml @@ -22,7 +22,7 @@ <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']"/> - <element name="confProductSku" type="input" selector="//*[@name='configurable-matrix[{{arg}}][sku]']" parameterized="true"/> - <element name="confProductSkuMessage" type="text" selector="//*[@name='configurable-matrix[{{arg}}][sku]']/following-sibling::label" parameterized="true"/> + <element name="configurableMatrixSku" type="input" selector="input[name='configurable-matrix[{{index}}][sku]']" parameterized="true"/> + <element name="skuValidationMessage" type="text" selector="input[name='configurable-matrix[{{index}}][sku]'] + label" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml index ad7adc865f24d..7c79c399b0be2 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml @@ -52,15 +52,13 @@ <!-- Find the product that we just created using the product grid --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearProductsFilter" /> <actionGroup ref="filterProductGridBySku" stepKey="findCreatedProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> - <waitForPageLoad stepKey="waitForProductFilterLoad"/> <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProduct"/> <waitForPageLoad stepKey="waitForProductPageLoad"/> - <!-- Create configurations based off the Text Swatch we created earlier --> + <!-- Create configurations for product we created earlier --> <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickCreateConfigurations"/> <!--Create new attribute--> @@ -81,24 +79,12 @@ <waitForPageLoad stepKey="waitForSaveAttribute"/> <switchToIFrame stepKey="switchOutOfIFrame"/> - <!--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="{{AdminProductAttributeGridSection.attributeCodeFilterInput}}" userInput="{{productAttributeWithDropdownTwoOptions.attribute_code}}" stepKey="fillFilterAttributeCodeField"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFiltersButton"/> - <click selector="{{AdminDataGridTableSection.rowCheckbox('1')}}" stepKey="clickOnFirstCheckbox"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickNextStep"/> - <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanelSection.selectAllByAttribute(productAttributeWithDropdownTwoOptions.attribute_code)}}" stepKey="waitForNextPageOpened"/> - <click selector="{{AdminCreateProductConfigurationsPanelSection.selectAllByAttribute(productAttributeWithDropdownTwoOptions.attribute_code)}}" stepKey="clickSelectAll"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickNextStep1"/> - <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.applySinglePriceToAllSkus}}" stepKey="waitForNextPageOpened1"/> - <click selector="{{AdminCreateProductConfigurationsPanel.applySinglePriceToAllSkus}}" stepKey="clickOnApplySinglePriceToAllSkus"/> - <fillField selector="{{AdminCreateProductConfigurationsPanel.singlePrice}}" userInput="10" stepKey="enterAttributePrice"/> - <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> - <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="100" stepKey="enterAttributeQuantity"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickNextStep2"/> - <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="waitForNextPageOpened2"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="generateProducts"/> + <!-- Generate products --> + <actionGroup ref="AdminGenerateConfigurations" stepKey="generateProducts"> + <argument name="attributeCode" value="{{productAttributeWithDropdownTwoOptions.attribute_code}}"/> + <argument name="qty" value="100"/> + <argument name="price" value="10"/> + </actionGroup> <waitForElementVisible selector="{{AdminProductFormActionSection.saveButton}}" stepKey="waitForSaveButtonVisible"/> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> <waitForElementVisible selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="waitForPopUpVisible"/> @@ -111,10 +97,10 @@ <!--See that validation message is shown under the fields--> <scrollTo selector="{{AdminProductFormConfigurationsSection.currentVariationsSkuCells}}" stepKey="scrollTConfigurationTab"/> - <see userInput="Please enter less or equal than 64 symbols." selector="{{AdminProductFormConfigurationsSection.confProductSkuMessage('0')}}" stepKey="seeValidationMessage"/> + <see userInput="Please enter less or equal than 64 symbols." selector="{{AdminProductFormConfigurationsSection.skuValidationMessage('0')}}" stepKey="seeValidationMessage"/> <!--Edit "SKU" with valid quantity--> - <fillField selector="{{AdminProductFormConfigurationsSection.confProductSku('0')}}" userInput="{{ApiConfigurableProduct.sku}}-thisIsShortName" stepKey="fillValidValue"/> + <fillField selector="{{AdminProductFormConfigurationsSection.configurableMatrixSku('0')}}" userInput="{{ApiConfigurableProduct.sku}}-thisIsShortName" stepKey="fillValidValue"/> <!--Click on "Save"--> <waitForElementVisible selector="{{AdminProductFormActionSection.saveButton}}" stepKey="waitForSaveBtnVisible"/> @@ -123,6 +109,6 @@ <!--Click on "Confirm". Product is saved, success message appears --> <waitForElementVisible selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="waitPopUpVisible"/> <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmPopup"/> - <seeElement selector="{{AdminMessagesSection.success}}" stepKey="seeSaveProductMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the product." stepKey="seeSaveProductMessage"/> </test> </tests> From 17c6bdc5d864220edd76ddd2b2867a7e71c6289a Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Tue, 29 Jan 2019 11:40:42 +0200 Subject: [PATCH 0425/1295] MAGETWO-95752: Unable to create Configurations for Configurable Products --- .../ActionGroup/AdminCreateConfigurableProductActionGroup.xml | 4 ++-- .../Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateConfigurableProductActionGroup.xml index f655589c66a80..e16ee43978a1e 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateConfigurableProductActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateConfigurableProductActionGroup.xml @@ -66,7 +66,7 @@ <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="You saved the product." stepKey="assertSuccess"/> </actionGroup> - <actionGroup name="AdminGenerateConfigurations"> + <actionGroup name="AdminGenerateProductConfigurations"> <arguments> <argument name="attributeCode" type="string"/> <argument name="qty" type="string"/> @@ -79,7 +79,7 @@ <click selector="{{AdminDataGridTableSection.rowCheckbox('1')}}" stepKey="clickOnFirstCheckbox"/> <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickNextStep"/> <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanelSection.selectAllByAttribute(attributeCode)}}" stepKey="waitForNextPageOpened"/> - <click selector="{{AdminCreateProductConfigurationsPanelSection.selectAllByAttribute(productAttributeWithDropdownTwoOptions.attribute_code)}}" stepKey="clickSelectAll"/> + <click selector="{{AdminCreateProductConfigurationsPanelSection.selectAllByAttribute(attributeCode)}}" stepKey="clickSelectAll"/> <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickNextStep1"/> <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.applySinglePriceToAllSkus}}" stepKey="waitForNextPageOpened1"/> <click selector="{{AdminCreateProductConfigurationsPanel.applySinglePriceToAllSkus}}" stepKey="clickOnApplySinglePriceToAllSkus"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml index 7c79c399b0be2..03f4d8461cebb 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml @@ -80,12 +80,13 @@ <switchToIFrame stepKey="switchOutOfIFrame"/> <!-- Generate products --> - <actionGroup ref="AdminGenerateConfigurations" stepKey="generateProducts"> + <actionGroup ref="AdminGenerateProductConfigurations" stepKey="generateProducts"> <argument name="attributeCode" value="{{productAttributeWithDropdownTwoOptions.attribute_code}}"/> <argument name="qty" value="100"/> <argument name="price" value="10"/> </actionGroup> <waitForElementVisible selector="{{AdminProductFormActionSection.saveButton}}" stepKey="waitForSaveButtonVisible"/> + <scrollToTopOfPage stepKey="scrollToTop"/> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> <waitForElementVisible selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="waitForPopUpVisible"/> <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmInPopup"/> @@ -104,6 +105,7 @@ <!--Click on "Save"--> <waitForElementVisible selector="{{AdminProductFormActionSection.saveButton}}" stepKey="waitForSaveBtnVisible"/> + <scrollToTopOfPage stepKey="scrollToTop1"/> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProductAgain"/> <!--Click on "Confirm". Product is saved, success message appears --> From 769c3f1c4e1522486232d08582e784fab7b0f12e Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Tue, 29 Jan 2019 12:06:44 +0200 Subject: [PATCH 0426/1295] MAGETWO-95752: Unable to create Configurations for Configurable Products --- .../Catalog/Test/Mftf/Section/AdminProductFormSection.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml index dd610c1b1a914..7a97a75556769 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml @@ -188,6 +188,6 @@ </section> <section name="AdminChooseAffectedAttributeSetPopup"> <element name="confirm" type="button" selector="button[data-index='confirm_button']" timeout="30"/> - <element name="closePopUp" type="button" selector="//*[contains(@class,'product_form_product_form_configurable_attribute_set')]//button[@data-role='closeBtn']" timeout="30"/> + <element name="closePopUp" type="button" selector=".modal-popup._show [data-role='closeBtn']" timeout="30"/> </section> </sections> From 5cab3ff0b3fdbca752c340c32a24b799eb83bacc Mon Sep 17 00:00:00 2001 From: Kajal Solanki <kajal.solanki@krishtechnolabs.com> Date: Tue, 29 Jan 2019 18:58:45 +0530 Subject: [PATCH 0427/1295] Create universal solution for success icon vertically middle and other icon along with it --- .../web/css/source/components/_messages.less | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less index ed1c75bb6d3da..19d076bd20cc5 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less @@ -76,7 +76,8 @@ position: absolute; speak: none; text-shadow: none; - top: 1.5rem; + top: 50%; + margin-top: -1.25rem; width: auto; } } @@ -121,17 +122,6 @@ } } -.adminhtml-import-index { - .message-success { - &:before { - color: @alert-icon__success__color; - content: @alert-icon__success__content; - top: 2.3rem; - } - } -} - - .message-spinner { &:before { display: none; From b47cde5da2fc78536af6b99153b2e4fc5a109c42 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Tue, 29 Jan 2019 16:29:33 +0200 Subject: [PATCH 0428/1295] MAGETWO-94114: Cannot refresh Captcha image in production mode --- app/design/adminhtml/Magento/backend/etc/view.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/design/adminhtml/Magento/backend/etc/view.xml b/app/design/adminhtml/Magento/backend/etc/view.xml index f10f7789b0888..18c2d8f1b1722 100644 --- a/app/design/adminhtml/Magento/backend/etc/view.xml +++ b/app/design/adminhtml/Magento/backend/etc/view.xml @@ -23,6 +23,8 @@ </images> </media> <exclude> + <item type="file">Lib::mage/captcha.js</item> + <item type="file">Lib::mage/captcha.min.js</item> <item type="file">Lib::mage/common.js</item> <item type="file">Lib::mage/cookies.js</item> <item type="file">Lib::mage/dataPost.js</item> From 61d6803496bb41a2de6badbc264ba0a0c03ad33d Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Tue, 29 Jan 2019 17:50:08 +0200 Subject: [PATCH 0429/1295] MAGETWO-94461: Fix currency issue for backend order creation --- app/code/Magento/Store/Model/Store.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Store/Model/Store.php b/app/code/Magento/Store/Model/Store.php index 1203f748c0615..6807ca8311822 100644 --- a/app/code/Magento/Store/Model/Store.php +++ b/app/code/Magento/Store/Model/Store.php @@ -888,7 +888,10 @@ public function setCurrentCurrencyCode($code) if (in_array($code, $this->getAvailableCurrencyCodes())) { $this->_getSession()->setCurrencyCode($code); - $defaultCode = $this->_storeManager->getWebsite()->getDefaultStore()->getDefaultCurrency()->getCode(); + $defaultCode = ($this->_storeManager->getStore() !== null) + ? $this->_storeManager->getStore()->getDefaultCurrency()->getCode() + : $this->_storeManager->getWebsite()->getDefaultStore()->getDefaultCurrency()->getCode(); + $this->_httpContext->setValue(Context::CONTEXT_CURRENCY, $code, $defaultCode); } return $this; @@ -1328,12 +1331,14 @@ public function getIdentities() } /** + * Return Store Path + * * @return string */ public function getStorePath() { $parsedUrl = parse_url($this->getBaseUrl()); - return isset($parsedUrl['path']) ? $parsedUrl['path'] : '/'; + return $parsedUrl['path'] ?? '/'; } /** From 0ec113f8a5d680210ebfa6e84d950e7c5f23c71e Mon Sep 17 00:00:00 2001 From: Govind Sharma <govindpokhrelsharma@cedcoss.com> Date: Wed, 30 Jan 2019 00:13:50 +0530 Subject: [PATCH 0430/1295] Fixed issue #19942 in 2.2 --- .../Adminhtml/Order/Invoice/Save.php | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Save.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Save.php index c45a1982784e1..932a688fba0fb 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Save.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Save.php @@ -190,13 +190,7 @@ public function execute() } } $transactionSave->save(); - - if (!empty($data['do_shipment'])) { - $this->messageManager->addSuccess(__('You created the invoice and shipment.')); - } else { - $this->messageManager->addSuccess(__('The invoice has been created.')); - } - + // send invoice/shipment emails try { if (!empty($data['send_email'])) { @@ -206,6 +200,12 @@ public function execute() $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); $this->messageManager->addError(__('We can\'t send the invoice email right now.')); } + if (!empty($data['do_shipment'])) { + $this->messageManager->addSuccess(__('You created the invoice and shipment.')); + } else { + $this->messageManager->addSuccess(__('The invoice has been created.')); + } + if ($shipment) { try { if (!empty($data['send_email'])) { @@ -216,6 +216,13 @@ public function execute() $this->messageManager->addError(__('We can\'t send the shipment right now.')); } } + + if (!empty($data['do_shipment'])) { + $this->messageManager->addSuccess(__('You created the invoice and shipment.')); + } else { + $this->messageManager->addSuccess(__('The invoice has been created.')); + } + $this->_objectManager->get(\Magento\Backend\Model\Session::class)->getCommentText(true); return $resultRedirect->setPath('sales/order/view', ['order_id' => $orderId]); } catch (LocalizedException $e) { From a44cb0e9082c73a8512e9f42ab2ef124b1ceb03e Mon Sep 17 00:00:00 2001 From: Govind Sharma <govindpokhrelsharma@cedcoss.com> Date: Wed, 30 Jan 2019 10:21:21 +0530 Subject: [PATCH 0431/1295] Updated Save.php file --- .../Sales/Controller/Adminhtml/Order/Invoice/Save.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Save.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Save.php index 932a688fba0fb..8e2f1e951606d 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Save.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Save.php @@ -200,12 +200,7 @@ public function execute() $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); $this->messageManager->addError(__('We can\'t send the invoice email right now.')); } - if (!empty($data['do_shipment'])) { - $this->messageManager->addSuccess(__('You created the invoice and shipment.')); - } else { - $this->messageManager->addSuccess(__('The invoice has been created.')); - } - + if ($shipment) { try { if (!empty($data['send_email'])) { From 7383946a868dbe8c36ffeb1605097addfb08a2f5 Mon Sep 17 00:00:00 2001 From: amol 2jcommerce <amol@2jcommerce.in> Date: Wed, 30 Jan 2019 10:22:33 +0530 Subject: [PATCH 0432/1295] cms-page-top-spacing-issue-2.2 --- .../Magento/luma/Magento_Theme/web/css/source/_module.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 68938ed206038..9d17ee7256ee4 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 @@ -441,7 +441,7 @@ .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { .cms-page-view .page-main { - padding-top: 41px; + padding-top: 0; position: relative; } } From 880362a5c89210225ede192d419439c14d51a18d Mon Sep 17 00:00:00 2001 From: amol 2jcommerce <amol@2jcommerce.in> Date: Wed, 30 Jan 2019 10:47:11 +0530 Subject: [PATCH 0433/1295] My-account-page-title-extra-space-on-mobile-2.2 --- .../Magento/blank/Magento_Customer/web/css/source/_module.less | 2 +- .../Magento/luma/Magento_Customer/web/css/source/_module.less | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less index a22e5baeb57fc..d914f691604cd 100644 --- a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less @@ -367,7 +367,7 @@ } .account { - .page.messages { + .messages { margin-bottom: 0; } diff --git a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less index fe423a236c80e..4f11c4c725437 100755 --- a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less @@ -550,7 +550,7 @@ } .account { - .page.messages { + .messages { margin-bottom: 0; } From dc633c9d42442db8eab3febc7532a14d012ec2c1 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 30 Jan 2019 00:01:52 -0600 Subject: [PATCH 0434/1295] MC-5926: Conflict of simultaneous write in Redis cache - fix test to run integration test bin/magento with any shell command for dependent commands without mocking --- .../integration/etc/di/preferences/ce.php | 2 ++ .../Magento/TestFramework/App/Shell.php | 34 +++++++++++++++++++ .../Console/Command/SetModeCommandTest.php | 7 ++-- .../Operation/ApplicationCodeGenerator.php | 2 +- 4 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 dev/tests/integration/framework/Magento/TestFramework/App/Shell.php diff --git a/dev/tests/integration/etc/di/preferences/ce.php b/dev/tests/integration/etc/di/preferences/ce.php index 6d9c0a1d143c6..6839d0ae9a7ff 100644 --- a/dev/tests/integration/etc/di/preferences/ce.php +++ b/dev/tests/integration/etc/di/preferences/ce.php @@ -27,4 +27,6 @@ \Magento\Framework\App\Config\ScopeConfigInterface::class => \Magento\TestFramework\App\Config::class, \Magento\Framework\Lock\Backend\Cache::class => \Magento\TestFramework\Lock\Backend\DummyLocker::class, + \Magento\Framework\ShellInterface::class => \Magento\TestFramework\App\Shell::class, + \Magento\Framework\App\Shell::class => \Magento\TestFramework\App\Shell::class, ]; diff --git a/dev/tests/integration/framework/Magento/TestFramework/App/Shell.php b/dev/tests/integration/framework/Magento/TestFramework/App/Shell.php new file mode 100644 index 0000000000000..1ec1d620c6135 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/App/Shell.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\TestFramework\App; +/** + * Shell command line wrapper encapsulates command execution and arguments escaping + */ +class Shell extends \Magento\Framework\App\Shell +{ + /** + * @inheritdoc + */ + public function execute($command, array $arguments = []) + { + if (strpos($command, BP . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'magento ') !== false) { + $command = str_replace( + BP . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'magento ', + BP . DIRECTORY_SEPARATOR . 'dev' . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'integration' + . DIRECTORY_SEPARATOR. 'bin' . DIRECTORY_SEPARATOR . 'magento ', + $command + ); + } + + $params = \Magento\TestFramework\Helper\Bootstrap::getInstance()->getAppInitParams(); + + $params['MAGE_DIRS']['base']['path'] = BP; + $params = 'INTEGRATION_TEST_PARAMS="' . urldecode(http_build_query($params)) . '"'; + $integrationTestCommand = $params . ' ' . $command; + $output = parent::execute($integrationTestCommand, $arguments); + return $output; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/SetModeCommandTest.php b/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/SetModeCommandTest.php index 5fc87d8f200a7..4d426be01a1d5 100644 --- a/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/SetModeCommandTest.php +++ b/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/SetModeCommandTest.php @@ -23,7 +23,8 @@ * * {@inheritdoc} * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled */ class SetModeCommandTest extends \PHPUnit\Framework\TestCase { @@ -147,10 +148,6 @@ private function enableAndAssertProductionMode() $this->assertEquals(Cli::RETURN_SUCCESS, $this->commandTester->getStatusCode()); - $this->assertContains('Deploy using quick strategy', $commandOutput); - $this->assertContains('frontend/Magento/blank/en_US', $commandOutput); - $this->assertContains('adminhtml/Magento/backend', $commandOutput); - $this->assertContains('Execution time:', $commandOutput); $this->assertContains('Deployment of static content complete', $commandOutput); $this->assertContains('Enabled production mode', $commandOutput); } diff --git a/setup/src/Magento/Setup/Module/Di/App/Task/Operation/ApplicationCodeGenerator.php b/setup/src/Magento/Setup/Module/Di/App/Task/Operation/ApplicationCodeGenerator.php index 76cd6a20bf669..07b9a7110e643 100644 --- a/setup/src/Magento/Setup/Module/Di/App/Task/Operation/ApplicationCodeGenerator.php +++ b/setup/src/Magento/Setup/Module/Di/App/Task/Operation/ApplicationCodeGenerator.php @@ -74,7 +74,7 @@ public function doOperation() $this->directoryScanner->scan($path, $this->data['filePatterns'], $this->data['excludePatterns']) ); } - $entities = $this->phpScanner->collectEntities($files['php']); + $entities = isset($files['php']) ? $this->phpScanner->collectEntities($files['php']) : []; foreach ($entities as $entityName) { class_exists($entityName); } From 4a9a98a9b6548cecb4c479e7cfb567faa51825d7 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> Date: Wed, 30 Jan 2019 08:30:56 +0200 Subject: [PATCH 0435/1295] magento/magento2#19098 2.2.6 Use batches and direct queries to fix sales address upgrade Fix static test failures --- app/code/Magento/Sales/Setup/UpgradeData.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Sales/Setup/UpgradeData.php b/app/code/Magento/Sales/Setup/UpgradeData.php index b35632d6e12d2..0b69458b7656c 100644 --- a/app/code/Magento/Sales/Setup/UpgradeData.php +++ b/app/code/Magento/Sales/Setup/UpgradeData.php @@ -49,6 +49,8 @@ class UpgradeData implements UpgradeDataInterface private $state; /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * * @param SalesSetupFactory $salesSetupFactory * @param Config $eavConfig * @param AggregatedFieldDataConverter $aggregatedFieldConverter From adf8d4cccb28e168d9accae2edad202bb4914d7f Mon Sep 17 00:00:00 2001 From: ajay-2jcommerce <ajay@2jcommerce.in> Date: Wed, 30 Jan 2019 12:28:40 +0530 Subject: [PATCH 0436/1295] Gift-option-message-overlap-edit-and-remove-button-2.2 --- .../luma/Magento_GiftMessage/web/css/source/_module.less | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/design/frontend/Magento/luma/Magento_GiftMessage/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_GiftMessage/web/css/source/_module.less index 0c329f32d3739..621bf40b03093 100644 --- a/app/design/frontend/Magento/luma/Magento_GiftMessage/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_GiftMessage/web/css/source/_module.less @@ -246,6 +246,10 @@ .gift-messages-order { margin-bottom: @indent__m; } + + .gift-message-summary { + padding-right: 7rem; + } } // @@ -282,10 +286,6 @@ } } - .gift-message-summary { - padding-right: 7rem; - } - // // In-table block // --------------------------------------------- From 7c6026e2f62e5edd35b0f75877b556b07801e598 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <svizev.igor@gmail.com> Date: Wed, 30 Jan 2019 09:13:37 +0200 Subject: [PATCH 0437/1295] magento/magento2#19098 2.2.6 Use batches and direct queries to fix sales address upgrade Fix statis test failures --- app/code/Magento/Sales/Setup/UpgradeData.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Sales/Setup/UpgradeData.php b/app/code/Magento/Sales/Setup/UpgradeData.php index 0b69458b7656c..56cbf31c3d695 100644 --- a/app/code/Magento/Sales/Setup/UpgradeData.php +++ b/app/code/Magento/Sales/Setup/UpgradeData.php @@ -171,7 +171,7 @@ public function fillQuoteAddressIdInSalesOrderAddress(ModuleDataSetupInterface $ * @param ModuleDataSetupInterface $setup * @param string $addressType */ - public function fillQuoteAddressIdInSalesOrderAddressByType(ModuleDataSetupInterface $setup, $addressType) + private function fillQuoteAddressIdInSalesOrderAddressByType(ModuleDataSetupInterface $setup, $addressType) { $salesConnection = $setup->getConnection('sales'); @@ -205,12 +205,13 @@ public function fillQuoteAddressIdInSalesOrderAddressByType(ModuleDataSetupInter $this->fillQuoteAddressIdInSalesOrderAddressProcessBatch($setup, $result, $addressType); } } + /** * @param ModuleDataSetupInterface $setup * @param array $orderAddresses * @param string $addressType */ - public function fillQuoteAddressIdInSalesOrderAddressProcessBatch( + private function fillQuoteAddressIdInSalesOrderAddressProcessBatch( ModuleDataSetupInterface $setup, array $orderAddresses, $addressType From 0e67c0b7262897ea0d4a6bceb33885ec068f15da Mon Sep 17 00:00:00 2001 From: DmytroPaidych <dimonovp@gmail.com> Date: Wed, 30 Jan 2019 17:30:54 +0200 Subject: [PATCH 0438/1295] MAGETWO-96245: Verify that Gift Wrapping can be applied on Order Level and Ordered Items for Additional Website --- .../ActionGroup/AdminProductActionGroup.xml | 20 +++++++++++++++++ ...CategoryProductsUsingScopeSelectorTest.xml | 1 - .../Mftf/Section/AdminSalesConfigSection.xml | 2 +- .../AdminDeleteWebsiteActionGroup.xml | 1 + .../AdminSwitchWebsiteActionGroup.xml | 22 +++++++++++++++++++ .../Store/Test/Mftf/Data/StoreData.xml | 18 +++++++++++++++ .../Mftf/Section/AdminMainActionsSection.xml | 3 ++- 7 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchWebsiteActionGroup.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index d8e7f7ea710d6..8dac6237015ed 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -226,4 +226,24 @@ <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSectionAssert"/> <seeInField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="assertFieldUrlKey"/> </actionGroup> + + <!--Create a Simple Product--> + <actionGroup name="CreateSimpleProductAndAddToWebsite"> + <arguments> + <argument name="product"/> + <argument name="website" type="string"/> + </arguments> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToCatalogProductGrid"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> + <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickAddSimpleProduct"/> + <fillField userInput="{{product.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillProductName"/> + <fillField userInput="{{product.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillProductSKU"/> + <fillField userInput="{{product.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillProductPrice"/> + <fillField userInput="{{product.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillProductQuantity"/> + <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="openProductInWebsites"/> + <click selector="{{ProductInWebsitesSection.website(website)}}" stepKey="selectWebsite"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> + <waitForPageLoad stepKey="waitForProductPageSave"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the product." stepKey="seeSaveConfirmation"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml index fb64e66f8aabd..96403d5dc887a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml @@ -105,7 +105,6 @@ <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteSecondWebsite"> <argument name="websiteName" value="{{SecondWebsite.name}}"/> </actionGroup> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearWebsitesFilters"/> <!--Clear products filter--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearProductsFilters"/> diff --git a/app/code/Magento/Config/Test/Mftf/Section/AdminSalesConfigSection.xml b/app/code/Magento/Config/Test/Mftf/Section/AdminSalesConfigSection.xml index c58e77d200bfb..62b5cdcf9ceec 100644 --- a/app/code/Magento/Config/Test/Mftf/Section/AdminSalesConfigSection.xml +++ b/app/code/Magento/Config/Test/Mftf/Section/AdminSalesConfigSection.xml @@ -7,7 +7,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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminSalesConfigSection"> <element name="enableMAPUseSystemValue" type="checkbox" selector="#sales_msrp_enabled_inherit"/> <element name="enableMAPSelect" type="select" selector="#sales_msrp_enabled"/> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml index 558205bbc8071..395ba02d5a9de 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml @@ -23,5 +23,6 @@ <click selector="{{AdminStoresDeleteStoreGroupSection.deleteStoreGroupButton}}" stepKey="clickDeleteWebsiteButton"/> <waitForElementVisible selector="{{AdminStoresGridSection.websiteFilterTextField}}" stepKey="waitForStoreGridToReload"/> <see userInput="You deleted the website." stepKey="seeSavedMessage"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFilters"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchWebsiteActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchWebsiteActionGroup.xml new file mode 100644 index 0000000000000..06622d3a53177 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchWebsiteActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSwitchWebsiteActionGroup"> + <arguments> + <argument name="website"/> + </arguments> + <scrollToTopOfPage stepKey="scrollToTopOfPage" /> + <click selector="{{AdminMainActionsSection.storeViewDropdown}}" stepKey="clickWebsiteSwitchDropdown"/> + <waitForElementVisible selector="{{AdminMainActionsSection.websiteByName('Main Website')}}" stepKey="waitForWebsiteAreVisible"/> + <click selector="{{AdminMainActionsSection.websiteByName(website.name)}}" stepKey="clickWebsiteByName"/> + <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitingForInformationModal"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmStoreSwitch"/> + <see userInput="{{website.name}}" selector="{{AdminMainActionsSection.storeSwitcher}}" stepKey="seeNewWebsiteName"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml index 6d4fb7e9b8154..5877ed383ae16 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml @@ -57,6 +57,24 @@ <data key="store_type">store</data> <requiredEntity type="storeGroup">customStoreGroup</requiredEntity> </entity> + <entity name="CustomStoreENNotUnique" type="store"> + <data key="name">EN</data> + <data key="code">en</data> + <data key="is_active">1</data> + <data key="store_id">null</data> + <data key="store_action">add</data> + <data key="store_type">store</data> + <requiredEntity type="storeGroup">customStoreGroup</requiredEntity> + </entity> + <entity name="CustomStoreNLNotUnique" type="store"> + <data key="name">NL</data> + <data key="code">nl</data> + <data key="is_active">1</data> + <data key="store_id">null</data> + <data key="store_action">add</data> + <data key="store_type">store</data> + <requiredEntity type="storeGroup">customStoreGroup</requiredEntity> + </entity> <entity name="secondStore" type="store"> <data key="name">Second Store View</data> <data key="code">second_store_view</data> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminMainActionsSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminMainActionsSection.xml index 14429c298b5e5..1a95d88d454e4 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminMainActionsSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminMainActionsSection.xml @@ -7,10 +7,11 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminMainActionsSection"> <element name="storeSwitcher" type="text" selector=".store-switcher"/> <element name="storeViewDropdown" type="button" selector="#store-change-button"/> <element name="storeViewByName" type="button" selector="//*[@class='store-switcher-store-view ']/a[contains(text(), '{{storeViewName}}')]" timeout="30" parameterized="true"/> + <element name="websiteByName" type="button" selector="//*[@class='store-switcher-website ']/a[contains(text(), '{{websiteName}}')]" timeout="30" parameterized="true"/> </section> </sections> From 572af81d28ac7fcce73e4cbe23fd602b24339f1e Mon Sep 17 00:00:00 2001 From: serhii balko <serhii.balko@transoftgroup.com> Date: Wed, 30 Jan 2019 11:04:38 +0200 Subject: [PATCH 0439/1295] MAGETWO-97580: Update configurable in the cart. We can't find a quote item --- app/code/Magento/Msrp/view/base/web/js/msrp.js | 17 ++++++++++++++++- .../web/js/fotorama-add-video-events.js | 14 ++++++++------ .../view/frontend/web/js/swatch-renderer.js | 5 ++++- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Msrp/view/base/web/js/msrp.js b/app/code/Magento/Msrp/view/base/web/js/msrp.js index deeadd9b55b82..72dd1d8bbecbe 100644 --- a/app/code/Magento/Msrp/view/base/web/js/msrp.js +++ b/app/code/Magento/Msrp/view/base/web/js/msrp.js @@ -73,6 +73,7 @@ define([ this.initTierPopup(); } $(this.options.cartButtonId).on('click', this._addToCartSubmit.bind(this)); + $(this.options.cartForm).on('submit', this._onSubmitForm.bind(this)); }, /** @@ -249,8 +250,10 @@ define([ /** * Handler for addToCart action + * + * @param {Object} e */ - _addToCartSubmit: function () { + _addToCartSubmit: function (e) { this.element.trigger('addToCart', this.element); if (this.element.data('stop-processing')) { @@ -266,8 +269,20 @@ define([ if (this.options.addToCartUrl) { $('.mage-dropdown-dialog > .ui-dialog-content').dropdownDialog('close'); } + + e.preventDefault(); $(this.options.cartForm).submit(); + }, + /** + * Handler for submit form + * + * @private + */ + _onSubmitForm: function () { + if ($(this.options.cartForm).valid()) { + $(this.options.cartButtonId).prop('disabled', true); + } } }); diff --git a/app/code/Magento/ProductVideo/view/frontend/web/js/fotorama-add-video-events.js b/app/code/Magento/ProductVideo/view/frontend/web/js/fotorama-add-video-events.js index 3966321f6072c..b7f4adb857a91 100644 --- a/app/code/Magento/ProductVideo/view/frontend/web/js/fotorama-add-video-events.js +++ b/app/code/Magento/ProductVideo/view/frontend/web/js/fotorama-add-video-events.js @@ -179,12 +179,14 @@ define([ * @private */ clearEvents: function () { - this.fotoramaItem.off( - 'fotorama:show.' + this.PV + - ' fotorama:showend.' + this.PV + - ' fotorama:fullscreenenter.' + this.PV + - ' fotorama:fullscreenexit.' + this.PV - ); + if (this.fotoramaItem !== undefined) { + this.fotoramaItem.off( + 'fotorama:show.' + this.PV + + ' fotorama:showend.' + this.PV + + ' fotorama:fullscreenenter.' + this.PV + + ' fotorama:fullscreenexit.' + this.PV + ); + } }, /** 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 1145d3acc11ed..d2ccda412de77 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 @@ -1248,7 +1248,10 @@ define([ } imagesToUpdate = this._setImageIndex(imagesToUpdate); - gallery.updateData(imagesToUpdate); + + if (!_.isUndefined(gallery)) { + gallery.updateData(imagesToUpdate); + } if (isInitial) { $(this.options.mediaGallerySelector).AddFotoramaVideoEvents(); From 1b9f91fabaef27f09722a15722dc8241a48aeb77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torben=20Ho=CC=88hn?= <torhoehn@gmail.com> Date: Wed, 30 Jan 2019 13:00:30 +0100 Subject: [PATCH 0440/1295] removed return type to be compatible with PHP 7.0 --- .../Test/Unit/Observer/PredispatchReviewObserverTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Review/Test/Unit/Observer/PredispatchReviewObserverTest.php b/app/code/Magento/Review/Test/Unit/Observer/PredispatchReviewObserverTest.php index cb01e55e4f491..2a8f8d8e38a64 100644 --- a/app/code/Magento/Review/Test/Unit/Observer/PredispatchReviewObserverTest.php +++ b/app/code/Magento/Review/Test/Unit/Observer/PredispatchReviewObserverTest.php @@ -55,7 +55,7 @@ class PredispatchReviewObserverTest extends TestCase /** * @inheritdoc */ - protected function setUp() : void + protected function setUp() { $this->configMock = $this->getMockBuilder(ScopeConfigInterface::class) ->disableOriginalConstructor() @@ -82,7 +82,7 @@ protected function setUp() : void /** * Test with enabled review active config. */ - public function testReviewEnabled() : void + public function testReviewEnabled() { $observerMock = $this->getMockBuilder(Observer::class) ->disableOriginalConstructor() @@ -107,7 +107,7 @@ public function testReviewEnabled() : void /** * Test with disabled review active config. */ - public function testReviewDisabled() : void + public function testReviewDisabled() { $observerMock = $this->getMockBuilder(Observer::class) ->disableOriginalConstructor() From 756cefa0b7ff6bc687866809b3f3e25c12943d92 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Wed, 30 Jan 2019 12:11:31 +0000 Subject: [PATCH 0441/1295] MAGETWO-97630: [Magento Cloud] news_from_date and news_to_date dates incorrect in database with scheduled updates Due to unstable work of intl parsing on different platforms --- app/code/Magento/Cron/Model/Schedule.php | 30 ++-- .../Cron/Test/Unit/Model/ScheduleTest.php | 140 ++++++++++++++---- 2 files changed, 136 insertions(+), 34 deletions(-) diff --git a/app/code/Magento/Cron/Model/Schedule.php b/app/code/Magento/Cron/Model/Schedule.php index 39a58ef360cb3..a9ae04cb0c5d1 100644 --- a/app/code/Magento/Cron/Model/Schedule.php +++ b/app/code/Magento/Cron/Model/Schedule.php @@ -9,6 +9,7 @@ use Magento\Framework\Exception\CronException; use Magento\Framework\App\ObjectManager; use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Framework\Intl\DateTimeFactory; /** * Crontab schedule model @@ -50,13 +51,19 @@ class Schedule extends \Magento\Framework\Model\AbstractModel */ private $timezoneConverter; + /** + * @var DateTimeFactory + */ + private $dateTimeFactory; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data - * @param TimezoneInterface $timezoneConverter + * @param TimezoneInterface|null $timezoneConverter + * @param DateTimeFactory|null $dateTimeFactory */ public function __construct( \Magento\Framework\Model\Context $context, @@ -64,10 +71,12 @@ public function __construct( \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [], - TimezoneInterface $timezoneConverter = null + TimezoneInterface $timezoneConverter = null, + DateTimeFactory $dateTimeFactory = null ) { parent::__construct($context, $registry, $resource, $resourceCollection, $data); $this->timezoneConverter = $timezoneConverter ?: ObjectManager::getInstance()->get(TimezoneInterface::class); + $this->dateTimeFactory = $dateTimeFactory ?: ObjectManager::getInstance()->get(DateTimeFactory::class); } /** @@ -109,17 +118,20 @@ public function trySchedule() if (!$e || !$time) { return false; } + $configTimeZone = $this->timezoneConverter->getConfigTimezone(); + $storeDateTime = $this->dateTimeFactory->create(null, new \DateTimeZone($configTimeZone)); if (!is_numeric($time)) { //convert time from UTC to admin store timezone //we assume that all schedules in configuration (crontab.xml and DB tables) are in admin store timezone - $time = $this->timezoneConverter->date($time)->format('Y-m-d H:i'); - $time = strtotime($time); + $dateTimeUtc = $this->dateTimeFactory->create($time); + $time = $dateTimeUtc->getTimestamp(); } - $match = $this->matchCronExpression($e[0], strftime('%M', $time)) - && $this->matchCronExpression($e[1], strftime('%H', $time)) - && $this->matchCronExpression($e[2], strftime('%d', $time)) - && $this->matchCronExpression($e[3], strftime('%m', $time)) - && $this->matchCronExpression($e[4], strftime('%w', $time)); + $time = $storeDateTime->setTimestamp($time); + $match = $this->matchCronExpression($e[0], $time->format('i')) + && $this->matchCronExpression($e[1], $time->format('H')) + && $this->matchCronExpression($e[2], $time->format('d')) + && $this->matchCronExpression($e[3], $time->format('m')) + && $this->matchCronExpression($e[4], $time->format('w')); return $match; } diff --git a/app/code/Magento/Cron/Test/Unit/Model/ScheduleTest.php b/app/code/Magento/Cron/Test/Unit/Model/ScheduleTest.php index e9f4c61c7f551..76e9627ad7098 100644 --- a/app/code/Magento/Cron/Test/Unit/Model/ScheduleTest.php +++ b/app/code/Magento/Cron/Test/Unit/Model/ScheduleTest.php @@ -6,6 +6,9 @@ namespace Magento\Cron\Test\Unit\Model; use Magento\Cron\Model\Schedule; +use Magento\Framework\Intl\DateTimeFactory; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; /** * Class \Magento\Cron\Test\Unit\Model\ObserverTest @@ -18,11 +21,27 @@ class ScheduleTest extends \PHPUnit\Framework\TestCase */ protected $helper; + /** + * @var \Magento\Cron\Model\ResourceModel\Schedule + */ protected $resourceJobMock; + /** + * @var TimezoneInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $timezoneConverter; + + /** + * @var DateTimeFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $dateTimeFactory; + + /** + * @inheritdoc + */ protected function setUp() { - $this->helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->helper = new ObjectManager($this); $this->resourceJobMock = $this->getMockBuilder(\Magento\Cron\Model\ResourceModel\Schedule::class) ->disableOriginalConstructor() @@ -32,18 +51,30 @@ protected function setUp() $this->resourceJobMock->expects($this->any()) ->method('getIdFieldName') ->will($this->returnValue('id')); + + $this->timezoneConverter = $this->getMockBuilder(TimezoneInterface::class) + ->setMethods(['date']) + ->getMockForAbstractClass(); + + $this->dateTimeFactory = $this->getMockBuilder(DateTimeFactory::class) + ->setMethods(['create']) + ->getMock(); } /** + * Test for SetCronExpr + * * @param string $cronExpression * @param array $expected + * + * @return void * @dataProvider setCronExprDataProvider */ public function testSetCronExpr($cronExpression, $expected) { // 1. Create mocks - /** @var \Magento\Cron\Model\Schedule $model */ - $model = $this->helper->getObject(\Magento\Cron\Model\Schedule::class); + /** @var Schedule $model */ + $model = $this->helper->getObject(Schedule::class); // 2. Run tested method $model->setCronExpr($cronExpression); @@ -61,7 +92,7 @@ public function testSetCronExpr($cronExpression, $expected) * * @return array */ - public function setCronExprDataProvider() + public function setCronExprDataProvider(): array { return [ ['1 2 3 4 5', [1, 2, 3, 4, 5]], @@ -121,27 +152,33 @@ public function setCronExprDataProvider() } /** + * Test for SetCronExprException + * * @param string $cronExpression + * + * @return void * @expectedException \Magento\Framework\Exception\CronException * @dataProvider setCronExprExceptionDataProvider */ public function testSetCronExprException($cronExpression) { // 1. Create mocks - /** @var \Magento\Cron\Model\Schedule $model */ - $model = $this->helper->getObject(\Magento\Cron\Model\Schedule::class); + /** @var Schedule $model */ + $model = $this->helper->getObject(Schedule::class); // 2. Run tested method $model->setCronExpr($cronExpression); } /** + * Data provider + * * Here is a list of allowed characters and values for Cron expression * http://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm * * @return array */ - public function setCronExprExceptionDataProvider() + public function setCronExprExceptionDataProvider(): array { return [ [''], @@ -153,17 +190,31 @@ public function setCronExprExceptionDataProvider() } /** + * Test for trySchedule + * * @param int $scheduledAt * @param array $cronExprArr * @param $expected + * + * @return void * @dataProvider tryScheduleDataProvider */ public function testTrySchedule($scheduledAt, $cronExprArr, $expected) { // 1. Create mocks + $this->timezoneConverter->method('getConfigTimezone') + ->willReturn('UTC'); + + $this->dateTimeFactory->method('create') + ->willReturn(new \DateTime()); + /** @var \Magento\Cron\Model\Schedule $model */ $model = $this->helper->getObject( - \Magento\Cron\Model\Schedule::class + \Magento\Cron\Model\Schedule::class, + [ + 'timezoneConverter' => $this->timezoneConverter, + 'dateTimeFactory' => $this->dateTimeFactory, + ] ); // 2. Set fixtures @@ -177,22 +228,29 @@ public function testTrySchedule($scheduledAt, $cronExprArr, $expected) $this->assertEquals($expected, $result); } + /** + * Test for tryScheduleWithConversionToAdminStoreTime + * + * @return void + */ public function testTryScheduleWithConversionToAdminStoreTime() { $scheduledAt = '2011-12-13 14:15:16'; $cronExprArr = ['*', '*', '*', '*', '*']; - // 1. Create mocks - $timezoneConverter = $this->createMock(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class); - $timezoneConverter->expects($this->once()) - ->method('date') - ->with($scheduledAt) - ->willReturn(new \DateTime($scheduledAt)); + $this->timezoneConverter->method('getConfigTimezone') + ->willReturn('UTC'); + + $this->dateTimeFactory->method('create') + ->willReturn(new \DateTime()); /** @var \Magento\Cron\Model\Schedule $model */ $model = $this->helper->getObject( \Magento\Cron\Model\Schedule::class, - ['timezoneConverter' => $timezoneConverter] + [ + 'timezoneConverter' => $this->timezoneConverter, + 'dateTimeFactory' => $this->dateTimeFactory, + ] ); // 2. Set fixtures @@ -207,11 +265,15 @@ public function testTryScheduleWithConversionToAdminStoreTime() } /** + * Data provider + * * @return array */ - public function tryScheduleDataProvider() + public function tryScheduleDataProvider(): array { $date = '2011-12-13 14:15:16'; + $timestamp = (new \DateTime($date))->getTimestamp(); + $day = 'Monday'; return [ [$date, [], false], [$date, null, false], @@ -219,19 +281,23 @@ public function tryScheduleDataProvider() [$date, [], false], [$date, null, false], [$date, false, false], - [strtotime($date), ['*', '*', '*', '*', '*'], true], - [strtotime($date), ['15', '*', '*', '*', '*'], true], - [strtotime($date), ['*', '14', '*', '*', '*'], true], - [strtotime($date), ['*', '*', '13', '*', '*'], true], - [strtotime($date), ['*', '*', '*', '12', '*'], true], - [strtotime('Monday'), ['*', '*', '*', '*', '1'], true], + [$timestamp, ['*', '*', '*', '*', '*'], true], + [$timestamp, ['15', '*', '*', '*', '*'], true], + [$timestamp, ['*', '14', '*', '*', '*'], true], + [$timestamp, ['*', '*', '13', '*', '*'], true], + [$timestamp, ['*', '*', '*', '12', '*'], true], + [(new \DateTime($day))->getTimestamp(), ['*', '*', '*', '*', '1'], true], ]; } /** + * Test for matchCronExpression + * * @param string $cronExpressionPart * @param int $dateTimePart * @param bool $expectedResult + * + * @return void * @dataProvider matchCronExpressionDataProvider */ public function testMatchCronExpression($cronExpressionPart, $dateTimePart, $expectedResult) @@ -248,9 +314,11 @@ public function testMatchCronExpression($cronExpressionPart, $dateTimePart, $exp } /** + * Data provider + * * @return array */ - public function matchCronExpressionDataProvider() + public function matchCronExpressionDataProvider(): array { return [ ['*', 0, true], @@ -287,7 +355,11 @@ public function matchCronExpressionDataProvider() } /** + * Test for matchCronExpressionException + * * @param string $cronExpressionPart + * + * @return void * @expectedException \Magento\Framework\Exception\CronException * @dataProvider matchCronExpressionExceptionDataProvider */ @@ -304,9 +376,11 @@ public function testMatchCronExpressionException($cronExpressionPart) } /** + * Data provider + * * @return array */ - public function matchCronExpressionExceptionDataProvider() + public function matchCronExpressionExceptionDataProvider(): array { return [ ['1/2/3'], //Invalid cron expression, expecting 'match/modulus': 1/2/3 @@ -317,8 +391,12 @@ public function matchCronExpressionExceptionDataProvider() } /** + * Test for GetNumeric + * * @param mixed $param * @param int $expectedResult + * + * @return void * @dataProvider getNumericDataProvider */ public function testGetNumeric($param, $expectedResult) @@ -335,9 +413,11 @@ public function testGetNumeric($param, $expectedResult) } /** + * Data provider + * * @return array */ - public function getNumericDataProvider() + public function getNumericDataProvider(): array { return [ [null, false], @@ -362,6 +442,11 @@ public function getNumericDataProvider() ]; } + /** + * Test for tryLockJobSuccess + * + * @return void + */ public function testTryLockJobSuccess() { $scheduleId = 1; @@ -386,6 +471,11 @@ public function testTryLockJobSuccess() $this->assertEquals(Schedule::STATUS_RUNNING, $model->getStatus()); } + /** + * Test for tryLockJobFailure + * + * @return void + */ public function testTryLockJobFailure() { $scheduleId = 1; From 930194b36d4364bd82e5d46200d3e645f37f6a61 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Wed, 30 Jan 2019 14:26:08 +0200 Subject: [PATCH 0442/1295] MAGETWO-95969: Made changes in tests --- .../Test/Mftf/Section/AdminPopupModalSection.xml | 14 ++++++++++++++ .../Mftf/ActionGroup/AdminOrderGridActionGroup.xml | 4 ++++ 2 files changed, 18 insertions(+) create mode 100644 app/code/Magento/Backend/Test/Mftf/Section/AdminPopupModalSection.xml diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminPopupModalSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminPopupModalSection.xml new file mode 100644 index 0000000000000..4ea184598663f --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminPopupModalSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminPopupModalSection"> + <element name="message" type="text" selector="aside.modal-popup .modal-content .popup-window-content"/> + <element name="ok" type="button" selector="//span[contains(text(),'Ok')]/ancestor::button"/> + </section> +</sections> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml index c5307fa7ba73a..d99551de24db0 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml @@ -23,4 +23,8 @@ <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToGridOrdersPage"/> <conditionalClick selector="{{AdminOrdersGridSection.clearFilters}}" dependentSelector="{{AdminOrdersGridSection.enabledFilters}}" visible="true" stepKey="clickOnButtonToRemoveFiltersIfPresent"/> </actionGroup> + <actionGroup name="openOrderById" extends="filterOrderGridById"> + <click selector="{{AdminDataGridTableSection.firstRow}}" after="clickOrderApplyFilters" stepKey="openOrderViewPage"/> + <waitForPageLoad after="openOrderViewPage" stepKey="waitForOrderViewPageOpened"/> + </actionGroup> </actionGroups> From d104066b39463dc2897053662433e0b9d2c159cd Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Wed, 30 Jan 2019 15:44:13 +0200 Subject: [PATCH 0443/1295] MAGETWO-97976: Product export creates 2 rows for Simple product with custom options --- .../Model/Export/Product.php | 111 ++++++++++++------ .../Model/Export/ProductTest.php | 52 +++++++- 2 files changed, 125 insertions(+), 38 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Export/Product.php b/app/code/Magento/CatalogImportExport/Model/Export/Product.php index 413209626b794..395ae6ec1a4dc 100644 --- a/app/code/Magento/CatalogImportExport/Model/Export/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Export/Product.php @@ -5,6 +5,7 @@ */ namespace Magento\CatalogImportExport\Model\Export; +use Magento\Catalog\Model\ResourceModel\Product\Option\Collection; use Magento\ImportExport\Model\Import; use \Magento\Store\Model\Store; use \Magento\CatalogImportExport\Model\Import\Product as ImportProduct; @@ -202,7 +203,7 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity protected $_itemFactory; /** - * @var \Magento\Catalog\Model\ResourceModel\Product\Option\Collection + * @var Collection */ protected $_optionColFactory; @@ -1277,11 +1278,23 @@ private function appendMultirowData(&$dataRow, $multiRawData) } if (!empty($multiRawData['customOptionsData'][$productLinkId][$storeId])) { + $shouldBeMerged = true; $customOptionsRows = $multiRawData['customOptionsData'][$productLinkId][$storeId]; - $multiRawData['customOptionsData'][$productLinkId][$storeId] = []; - $customOptions = implode(ImportProduct::PSEUDO_MULTI_LINE_SEPARATOR, $customOptionsRows); - $dataRow = array_merge($dataRow, ['custom_options' => $customOptions]); + if ($storeId != Store::DEFAULT_STORE_ID + && !empty($multiRawData['customOptionsData'][$productLinkId][Store::DEFAULT_STORE_ID]) + ) { + $defaultCustomOptions = $multiRawData['customOptionsData'][$productLinkId][Store::DEFAULT_STORE_ID]; + if (!array_diff($defaultCustomOptions, $customOptionsRows)) { + $shouldBeMerged = false; + } + } + + if ($shouldBeMerged) { + $multiRawData['customOptionsData'][$productLinkId][$storeId] = []; + $customOptions = implode(ImportProduct::PSEUDO_MULTI_LINE_SEPARATOR, $customOptionsRows); + $dataRow = array_merge($dataRow, ['custom_options' => $customOptions]); + } } if (empty($dataRow)) { @@ -1373,56 +1386,55 @@ protected function optionRowToCellString($option) protected function getCustomOptionsData($productIds) { $customOptionsData = []; + $defaultOptionsData = []; foreach (array_keys($this->_storeIdToCode) as $storeId) { $options = $this->_optionColFactory->create(); - /* @var \Magento\Catalog\Model\ResourceModel\Product\Option\Collection $options*/ - $options->reset()->addOrder( - 'sort_order', - \Magento\Catalog\Model\ResourceModel\Product\Option\Collection::SORT_ORDER_ASC - )->addTitleToResult( - $storeId - )->addPriceToResult( - $storeId - )->addProductToFilter( - $productIds - )->addValuesToResult( - $storeId - ); + /* @var Collection $options*/ + $options->reset() + ->addOrder('sort_order', Collection::SORT_ORDER_ASC) + ->addTitleToResult($storeId) + ->addPriceToResult($storeId) + ->addProductToFilter($productIds) + ->addValuesToResult($storeId); foreach ($options as $option) { + $optionData = $option->toArray(); $row = []; $productId = $option['product_id']; $row['name'] = $option['title']; $row['type'] = $option['type']; - if (Store::DEFAULT_STORE_ID === $storeId) { - $row['required'] = $option['is_require']; - $row['price'] = $option['price']; - $row['price_type'] = ($option['price_type'] === 'percent') ? 'percent' : 'fixed'; - $row['sku'] = $option['sku']; - if ($option['max_characters']) { - $row['max_characters'] = $option['max_characters']; - } - foreach (['file_extension', 'image_size_x', 'image_size_y'] as $fileOptionKey) { - if (!isset($option[$fileOptionKey])) { - continue; - } - - $row[$fileOptionKey] = $option[$fileOptionKey]; + $row['required'] = $this->getOptionValue('is_require', $defaultOptionsData, $optionData); + $row['price'] = $this->getOptionValue('price', $defaultOptionsData, $optionData); + $row['sku'] = $this->getOptionValue('sku', $defaultOptionsData, $optionData); + if (array_key_exists('max_characters', $optionData) + || array_key_exists('max_characters', $defaultOptionsData) + ) { + $row['max_characters'] = $this->getOptionValue('max_characters', $defaultOptionsData, $optionData); + } + foreach (['file_extension', 'image_size_x', 'image_size_y'] as $fileOptionKey) { + if (isset($option[$fileOptionKey]) || isset($defaultOptionsData[$fileOptionKey])) { + $row[$fileOptionKey] = $this->getOptionValue($fileOptionKey, $defaultOptionsData, $optionData); } } + $percentType = $this->getOptionValue('price_type', $defaultOptionsData, $optionData); + $row['price_type'] = ($percentType === 'percent') ? 'percent' : 'fixed'; + + if (Store::DEFAULT_STORE_ID === $storeId) { + $optionId = $option['option_id']; + $defaultOptionsData[$optionId] = $option->toArray(); + } + $values = $option->getValues(); if ($values) { foreach ($values as $value) { $row['option_title'] = $value['title']; - if (Store::DEFAULT_STORE_ID === $storeId) { - $row['option_title'] = $value['title']; - $row['price'] = $value['price']; - $row['price_type'] = ($value['price_type'] === 'percent') ? 'percent' : 'fixed'; - $row['sku'] = $value['sku']; - } + $row['option_title'] = $value['title']; + $row['price'] = $value['price']; + $row['price_type'] = ($value['price_type'] === 'percent') ? 'percent' : 'fixed'; + $row['sku'] = $value['sku']; $customOptionsData[$productId][$storeId][] = $this->optionRowToCellString($row); } } else { @@ -1436,6 +1448,31 @@ protected function getCustomOptionsData($productIds) return $customOptionsData; } + /** + * Get value for custom option according to store or default value + * + * @param string $optionName + * @param array $defaultOptionsData + * @param array $optionData + * @return mixed + */ + private function getOptionValue($optionName, $defaultOptionsData, $optionData) + { + $optionId = $optionData['option_id']; + + if (array_key_exists($optionName, $optionData) && $optionData[$optionName] !== null) { + return $optionData[$optionName]; + } + + if (array_key_exists($optionId, $defaultOptionsData) + && array_key_exists($optionName, $defaultOptionsData[$optionId]) + ) { + return $defaultOptionsData[$optionId][$optionName]; + } + + return null; + } + /** * Clean up already loaded attribute collection. * diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php index 81592b6901f1c..ea5d6f30cc380 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php @@ -326,7 +326,7 @@ public function testExportWithMedia() /** * @magentoDataFixture Magento/CatalogImportExport/_files/product_export_data.php */ - public function testExportWithCustomOptions() + public function testExportWithCustomOptionsAndSecondStore() { $storeCode = 'default'; $expectedData = []; @@ -380,6 +380,56 @@ public function testExportWithCustomOptions() self::assertSame($expectedData, $customOptionData); } + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_custom_options.php + * + * @return void + */ + public function testExportWithCustomOptions() + { + $expectedData = []; + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); + /** @var \Magento\Catalog\Api\Data\ProductInterface $product */ + $product = $productRepository->get('simple_with_custom_options', true); + + foreach ($product->getOptions() as $customOption) { + $optionTitle = $customOption->getTitle(); + $expectedData[$optionTitle] = []; + if ($customOption->getValues()) { + foreach ($customOption->getValues() as $customOptionValue) { + $expectedData[$optionTitle][] = $customOptionValue->getTitle(); + } + } + } + + ksort($expectedData); + + $this->model->setWriter( + $this->objectManager->create( + \Magento\ImportExport\Model\Export\Adapter\Csv::class + ) + ); + $exportData = $this->model->export(); + /** @var $varDirectory \Magento\Framework\Filesystem\Directory\WriteInterface */ + $varDirectory = $this->objectManager->get(\Magento\Framework\Filesystem::class) + ->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::VAR_DIR); + $varDirectory->writeFile('test_product_with_custom_options.csv', $exportData); + /** @var \Magento\Framework\File\Csv $csv */ + $csv = $this->objectManager->get(\Magento\Framework\File\Csv::class); + $data = $csv->getData($varDirectory->getAbsolutePath('test_product_with_custom_options.csv')); + + foreach ($data[0] as $columnNumber => $columnName) { + if ($columnName === 'custom_options') { + $exportedCustomOptionData = $this->parseExportedCustomOption($data[1][$columnNumber]); + } + } + + ksort($exportedCustomOptionData); + + self::assertSame($expectedData, $exportedCustomOptionData); + } + /** * @param $exportedCustomOption * @return array From 3846b448838c17c10aeee3a0aea1db24b67168a1 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 30 Jan 2019 08:23:17 -0600 Subject: [PATCH 0444/1295] MC-5926: Conflict of simultaneous write in Redis cache - add output to set command test failure --- .../Magento/Deploy/Console/Command/SetModeCommandTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/SetModeCommandTest.php b/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/SetModeCommandTest.php index 4d426be01a1d5..423e8534aa048 100644 --- a/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/SetModeCommandTest.php +++ b/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/SetModeCommandTest.php @@ -146,7 +146,7 @@ private function enableAndAssertProductionMode() ); $commandOutput = $this->commandTester->getDisplay(); - $this->assertEquals(Cli::RETURN_SUCCESS, $this->commandTester->getStatusCode()); + $this->assertEquals(Cli::RETURN_SUCCESS, $this->commandTester->getStatusCode(), $commandOutput); $this->assertContains('Deployment of static content complete', $commandOutput); $this->assertContains('Enabled production mode', $commandOutput); From 25f231166e7f336e899b054569e979e121c79744 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Wed, 30 Jan 2019 16:38:22 +0200 Subject: [PATCH 0445/1295] ENGCOM-4015: Static test fix. --- .../Test/Unit/Model/ResourceModel/Product/CollectionTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php index dbbb3fb29513b..6d3316a0610cd 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php @@ -318,7 +318,7 @@ public function testAddTierPriceDataByGroupId() [ '(customer_group_id=? AND all_groups=0) OR all_groups=1', $customerGroupId] ) ->willReturnSelf(); - $select->expects($this->once())->method('order')->with('entity_id')->willReturnSelf(); + $select->expects($this->once())->method('order')->with('qty')->willReturnSelf(); $this->connectionMock->expects($this->once()) ->method('fetchAll') ->with($select) @@ -370,7 +370,7 @@ public function testAddTierPriceData() $select->expects($this->exactly(1))->method('where') ->with('entity_id IN(?)', [1]) ->willReturnSelf(); - $select->expects($this->once())->method('order')->with('entity_id')->willReturnSelf(); + $select->expects($this->once())->method('order')->with('qty')->willReturnSelf(); $this->connectionMock->expects($this->once()) ->method('fetchAll') ->with($select) From bbbc3ddf871ab22418bc063848599b95c061e5d2 Mon Sep 17 00:00:00 2001 From: Oleg Onufer <linkedddd@gmail.com> Date: Wed, 30 Jan 2019 16:55:07 +0200 Subject: [PATCH 0446/1295] MAGETWO-97239: Category rules should apply to complex products --- .../ActionGroup/AdminProductActionGroup.xml | 11 ++ .../Catalog/Test/Mftf/Data/ProductData.xml | 13 ++ .../StorefrontCheckoutCartSummarySection.xml | 1 + ...reateApiConfigurableProductActionGroup.xml | 13 ++ .../AdminCartPriceRuleActionGroup.xml | 23 ++++ .../Test/Mftf/Data/SalesRuleData.xml | 4 + .../AdminCartPriceRulesFormSection.xml | 3 + .../StorefrontCheckoutCartSummarySection.xml | 15 +++ ...yRulesShouldApplyToComplexProductsTest.xml | 118 ++++++++++++++++++ 9 files changed, 201 insertions(+) create mode 100644 app/code/Magento/SalesRule/Test/Mftf/Section/StorefrontCheckoutCartSummarySection.xml create mode 100644 app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index d8e7f7ea710d6..1049be8073cae 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -226,4 +226,15 @@ <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSectionAssert"/> <seeInField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="assertFieldUrlKey"/> </actionGroup> + + <actionGroup name="AdminAssignProductToCategory"> + <arguments> + <argument name="productId" type="string"/> + <argument name="categoryName" type="string"/> + </arguments> + <amOnPage url="{{AdminProductEditPage.url(productId)}}" stepKey="amOnPage"/> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{categoryName}}]" stepKey="selectCategory"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the product." stepKey="seeSaveProductMessage"/> + </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 39f4f0f2d997d..d158243d42540 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -260,6 +260,19 @@ <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> <requiredEntity type="custom_attribute">CustomAttributeProductAttribute</requiredEntity> </entity> + <entity name="ApiSimpleTwoHidden" type="product2"> + <data key="sku" unique="suffix">api-simple-product-two</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="visibility">1</data> + <data key="name" unique="suffix">Api Simple Product Two</data> + <data key="price">234.00</data> + <data key="urlKey" unique="suffix">api-simple-product-two</data> + <data key="status">1</data> + <data key="quantity">100</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute">CustomAttributeProductAttribute</requiredEntity> + </entity> <entity name="ProductWithOptions2" type="product"> <var key="sku" entityType="product" entityKey="sku" /> <requiredEntity type="product_option">ProductOptionDropDownWithLongValuesTitle</requiredEntity> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCheckoutCartSummarySection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCheckoutCartSummarySection.xml index a58fa77bca18b..7c68ecf543874 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCheckoutCartSummarySection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCheckoutCartSummarySection.xml @@ -22,5 +22,6 @@ <element name="blockSummary" type="button" selector="#block-summary"/> <element name="discountAmount" type="text" selector="td[data-th='Discount']"/> <element name="totalsElementByPosition" type="text" selector=".data.table.totals > tbody tr:nth-of-type({{value}}) > th" parameterized="true"/> + <element name="tableTotals" type="text" selector="#cart-totals .data.table.totals"/> </section> </sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml index d918649ed4914..161441736f940 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml @@ -69,4 +69,17 @@ <requiredEntity createDataKey="getConfigAttributeOption1"/> </createData> </actionGroup> + + <!-- Create the configurable product, children are not visible individually --> + <actionGroup name="AdminCreateApiConfigurableProductWithHiddenChildActionGroup" extends="AdminCreateApiConfigurableProductActionGroup"> + <!-- Create the 2 children that will be a part of the configurable product --> + <createData entity="ApiSimpleOneHidden" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwoHidden" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCartPriceRuleActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCartPriceRuleActionGroup.xml index 9152016e709e5..53419068222e8 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCartPriceRuleActionGroup.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCartPriceRuleActionGroup.xml @@ -23,4 +23,27 @@ <!-- This actionGroup was created to be merged from B2B because B2B has a very different form control here --> <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/> </actionGroup> + + <actionGroup name="SetConditionForActionsInCartPriceRuleActionGroup"> + <arguments> + <argument name="actionsAggregator" type="string" defaultValue="ANY"/> + <argument name="actionsValue" type="string" defaultValue="FALSE"/> + <argument name="childAttribute" type="string" defaultValue="Category"/> + <argument name="actionValue" type="string"/> + </arguments> + <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickOnActionTab"/> + <click selector="{{AdminCartPriceRulesFormSection.condition('ALL')}}" stepKey="clickToChooseOption"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.actionsAggregator}}" userInput="{{actionsAggregator}}" stepKey="selectCondition"/> + <click selector="{{AdminCartPriceRulesFormSection.condition('TRUE')}}" stepKey="clickToChooseOption2"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.actionsValue}}" userInput="{{actionsValue}}" stepKey="selectCondition2"/> + <click selector="{{AdminCartPriceRulesFormSection.conditions}}" stepKey="selectActionConditions"/> + <waitForPageLoad stepKey="waitForDropDownOpened"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.childAttribute}}" userInput="{{childAttribute}}" stepKey="selectAttribute"/> + <waitForPageLoad stepKey="waitForOperatorOpened"/> + <click selector="{{AdminCartPriceRulesFormSection.condition('...')}}" stepKey="clickToChooseOption3"/> + <fillField selector="{{AdminCartPriceRulesFormSection.actionValue}}" userInput="{{actionValue}}" stepKey="fillActionValue"/> + <click selector="{{AdminCartPriceRulesFormSection.applyAction}}" stepKey="applyAction"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveButton"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml index 4f3dd3b374b7d..5547c94cfef63 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml @@ -168,4 +168,8 @@ <data key="uses_per_coupon">10</data> <data key="simple_free_shipping">1</data> </entity> + + <entity name="SalesRuleNoCouponWithFixedDiscount" extends="ApiCartRule"> + <data key="simple_action">by_fixed</data> + </entity> </entities> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml index afe4f8a6ae980..673aad94da151 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml @@ -35,10 +35,13 @@ <element name="freeShipping" type="select" selector="select[name='simple_free_shipping']"/> <element name="conditions" type="button" selector=".rule-param.rule-param-new-child > a"/> <element name="condition" type="text" selector="//span[@class='rule-param']/a[text()='{{arg}}']" parameterized="true"/> + <element name="actionsAggregator" type="select" selector="#actions__1__aggregator"/> + <element name="actionsValue" type="select" selector="#actions__1__value"/> <element name="operator" type="select" selector="select[name*='[operator]']"/> <element name="childAttribute" type="select" selector="select[name*='new_child']"/> <element name="optionInput" type="input" selector="ul[class*='rule-param-children'] input[name*='[value]']"/> <element name="actionValue" type="input" selector=".rule-param-edit input"/> + <element name="applyAction" type="text" selector=".rule-param-apply" timeout="30"/> <element name="actionOperator" type="select" selector=".rule-param-edit select"/> <!-- Manage Coupon Codes sub-form --> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/StorefrontCheckoutCartSummarySection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/StorefrontCheckoutCartSummarySection.xml new file mode 100644 index 0000000000000..26b52d4610c37 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/StorefrontCheckoutCartSummarySection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCheckoutCartSummarySection"> + <element name="discountLabel" type="text" selector="//*[@id='cart-totals']//tr[.//th//span[contains(@class, 'discount coupon')]]"/> + <element name="discountTotal" type="text" selector="//*[@id='cart-totals']//tr[.//th//span[contains(@class, 'discount coupon')]]//td//span//span[@class='price']"/> + </section> +</sections> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml new file mode 100644 index 0000000000000..175d20b82a9b1 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml @@ -0,0 +1,118 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCategoryRulesShouldApplyToComplexProductsTest"> + <annotations> + <features value="CatalogRule"/> + <stories value="Create cart price rule"/> + <title value="Category rules should apply to complex products"/> + <description value="Sales rules filtering on category should apply to all products, including complex products."/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-76029"/> + <group value="catalogRule"/> + </annotations> + <before> + <!-- Create two Categories: CAT1 and CAT2 --> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleSubCategory" stepKey="createCategory2"/> + <!--Create config1 and config2--> + <actionGroup ref="AdminCreateApiConfigurableProductWithHiddenChildActionGroup" stepKey="createConfigurableProduct1"> + <argument name="productName" value="config1"/> + </actionGroup> + <actionGroup ref="AdminCreateApiConfigurableProductWithHiddenChildActionGroup" stepKey="createConfigurableProduct2"> + <argument name="productName" value="config2"/> + </actionGroup> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!-- Assign config1 and the associated child products to CAT1 --> + <actionGroup ref="AdminAssignProductToCategory" stepKey="assignConfigurableProduct1ToCategory"> + <argument name="productId" value="$$createConfigProductCreateConfigurableProduct1.id$$"/> + <argument name="categoryName" value="$$createCategory.name$$"/> + </actionGroup> + <actionGroup ref="AdminAssignProductToCategory" stepKey="assignConfig1ChildProduct1ToCategory"> + <argument name="productId" value="$$createConfigChildProduct1CreateConfigurableProduct1.id$$"/> + <argument name="categoryName" value="$$createCategory.name$$"/> + </actionGroup> + <actionGroup ref="AdminAssignProductToCategory" stepKey="assignConfig1ChildProduct2ToCategory"> + <argument name="productId" value="$$createConfigChildProduct2CreateConfigurableProduct1.id$$"/> + <argument name="categoryName" value="$$createCategory.name$$"/> + </actionGroup> + <!-- Assign config12 and the associated child products to CAT2 --> + <actionGroup ref="AdminAssignProductToCategory" stepKey="assignConfigurableProduct2ToCategory2"> + <argument name="productId" value="$$createConfigProductCreateConfigurableProduct2.id$$"/> + <argument name="categoryName" value="$$createCategory2.name$$"/> + </actionGroup> + <actionGroup ref="AdminAssignProductToCategory" stepKey="assignConfig2ChildProduct1ToCategory2"> + <argument name="productId" value="$$createConfigChildProduct1CreateConfigurableProduct2.id$$"/> + <argument name="categoryName" value="$$createCategory2.name$$"/> + </actionGroup> + <actionGroup ref="AdminAssignProductToCategory" stepKey="assignConfig2ChildProduct2ToCategory2"> + <argument name="productId" value="$$createConfigChildProduct2CreateConfigurableProduct2.id$$"/> + <argument name="categoryName" value="$$createCategory2.name$$"/> + </actionGroup> + </before> + <after> + <!--Delete configurable product 1--> + <deleteData createDataKey="createConfigProductCreateConfigurableProduct1" stepKey="deleteConfigProduct1"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory1"/> + <deleteData createDataKey="createConfigChildProduct1CreateConfigurableProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2CreateConfigurableProduct1" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProductAttributeCreateConfigurableProduct1" stepKey="deleteConfigProductAttribute1"/> + <!--Delete configurable product 2--> + <deleteData createDataKey="createConfigProductCreateConfigurableProduct2" stepKey="deleteConfigProduct2"/> + <deleteData createDataKey="createCategory2" stepKey="deleteCategory2"/> + <deleteData createDataKey="createConfigChildProduct1CreateConfigurableProduct2" stepKey="deleteConfigChildProduct3"/> + <deleteData createDataKey="createConfigChildProduct2CreateConfigurableProduct2" stepKey="deleteConfigChildProduct4"/> + <deleteData createDataKey="createConfigProductAttributeCreateConfigurableProduct2" stepKey="deleteConfigProductAttribute2"/> + <!--Delete Cart Price Rule --> + <deleteData createDataKey="createCartPriceRule" stepKey="deleteCartPriceRule"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- 1: Create a cart price rule applying to CAT1 with discount --> + <createData entity="SalesRuleNoCouponWithFixedDiscount" stepKey="createCartPriceRule"/> + <amOnPage url="{{AdminCartPriceRuleEditPage.url($$createCartPriceRule.rule_id$$)}}" stepKey="goToCartPriceRuleEditPage"/> + <actionGroup ref="SetConditionForActionsInCartPriceRuleActionGroup" stepKey="setConditionForActionsInCartPriceRuleActionGroup"> + <argument name="actionValue" value="$$createCategory.id$$"/> + </actionGroup> + <!-- 2: Go to frontend and add an item from both CAT1 and CAT2 to your cart --> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontend"/> + <!-- 3: Open configurable product 1 and add all his child products to cart --> + <amOnPage url="{{StorefrontProductPage.url($$createConfigProductCreateConfigurableProduct1.custom_attributes[url_key]$$)}}" stepKey="amOnConfigurableProductPage"/> + <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect('$$createConfigProductAttributeCreateConfigurableProduct1.attribute[frontend_labels][0][label]$$')}}" userInput="$$createConfigProductAttributeOption1CreateConfigurableProduct1.option[store_labels][0][label]$$" stepKey="selectOption"/> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddConfigurableProductToCart"> + <argument name="product" value="$$createConfigProductCreateConfigurableProduct1$$"/> + <argument name="productCount" value="1"/> + </actionGroup> + <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect('$$createConfigProductAttributeCreateConfigurableProduct1.attribute[frontend_labels][0][label]$$')}}" userInput="$$createConfigProductAttributeOption2CreateConfigurableProduct1.option[store_labels][0][label]$$" stepKey="selectOption2"/> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddConfigurableProductToCart2"> + <argument name="product" value="$$createConfigProductCreateConfigurableProduct1$$"/> + <argument name="productCount" value="2"/> + </actionGroup> + <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToCart"/> + <waitForElementVisible selector="{{StorefrontCheckoutCartSummarySection.tableTotals}}" stepKey="waitForCartTotalsBlockLoad"/> + <!-- Discount amount is not applied --> + <dontSee selector="{{StorefrontCheckoutCartSummarySection.discountLabel}}" stepKey="discountIsNotApply"/> + <!-- 3: Open configurable product 2 and add all his child products to cart --> + <amOnPage url="{{StorefrontProductPage.url($$createConfigProductCreateConfigurableProduct2.custom_attributes[url_key]$$)}}" stepKey="amOnConfigurableProductPage2"/> + <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect('$$createConfigProductAttributeCreateConfigurableProduct2.attribute[frontend_labels][0][label]$$')}}" userInput="$$createConfigProductAttributeOption1CreateConfigurableProduct2.option[store_labels][0][label]$$" stepKey="selectOption3"/> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddConfigurableProductToCart3"> + <argument name="product" value="$$createConfigProductCreateConfigurableProduct2$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect('$$createConfigProductAttributeCreateConfigurableProduct2.attribute[frontend_labels][0][label]$$')}}" userInput="$$createConfigProductAttributeOption2CreateConfigurableProduct2.option[store_labels][0][label]$$" stepKey="selectOption4"/> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddConfigurableProductToCart4"> + <argument name="product" value="$$createConfigProductCreateConfigurableProduct2$$"/> + <argument name="productCount" value="4"/> + </actionGroup> + <!-- Discount amount is applied --> + <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToCart2"/> + <waitForElementVisible selector="{{StorefrontCheckoutCartSummarySection.tableTotals}}" stepKey="waitForCartTotalsBlockLoad2"/> + <see selector="{{StorefrontCheckoutCartSummarySection.discountTotal}}" userInput="-$100.00" stepKey="discountIsApply"/> + </test> +</tests> From 8131a112b7b1214b5a112d09dcb9381e587bdb9e Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Wed, 30 Jan 2019 09:39:17 -0600 Subject: [PATCH 0447/1295] MQE-1420: Bump MFTF version in Magento - MFTF version bump + allure-codeception dependency update. --- composer.json | 2 +- composer.lock | 32 +++++++++++++++++--------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/composer.json b/composer.json index 34341f7c3230a..d04bf269d4485 100644 --- a/composer.json +++ b/composer.json @@ -74,7 +74,7 @@ "ramsey/uuid": "~3.7.3" }, "require-dev": { - "magento/magento2-functional-testing-framework": "2.3.11", + "magento/magento2-functional-testing-framework": "2.3.13", "phpunit/phpunit": "~6.2.0", "squizlabs/php_codesniffer": "3.2.2", "phpmd/phpmd": "@stable", diff --git a/composer.lock b/composer.lock index ad8cb4ba740cc..384ff14618a80 100644 --- a/composer.lock +++ b/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "2fd90a780b9d54a8f73f0554a4b5df98", + "content-hash": "d57f148fd874b8685bf73bbf0a0ea75a", "packages": [ { "name": "braintree/braintree_php", @@ -1756,7 +1756,7 @@ }, { "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" + "email": "backendtea@gmail.com" } ], "description": "Symfony polyfill for ctype functions", @@ -4058,16 +4058,16 @@ "packages-dev": [ { "name": "allure-framework/allure-codeception", - "version": "1.2.7", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/allure-framework/allure-codeception.git", - "reference": "48598f4b4603b50b663bfe977260113a40912131" + "reference": "9d31d781b3622b028f1f6210bc76ba88438bd518" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/allure-framework/allure-codeception/zipball/48598f4b4603b50b663bfe977260113a40912131", - "reference": "48598f4b4603b50b663bfe977260113a40912131", + "url": "https://api.github.com/repos/allure-framework/allure-codeception/zipball/9d31d781b3622b028f1f6210bc76ba88438bd518", + "reference": "9d31d781b3622b028f1f6210bc76ba88438bd518", "shasum": "" }, "require": { @@ -4105,7 +4105,7 @@ "steps", "testing" ], - "time": "2018-03-07T11:18:27+00:00" + "time": "2018-12-18T19:47:23+00:00" }, { "name": "allure-framework/allure-php-api", @@ -5972,23 +5972,24 @@ }, { "name": "magento/magento2-functional-testing-framework", - "version": "2.3.11", + "version": "2.3.13", "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "3ca1bd74228a61bd05520bed1ef88b5a19764d92" + "reference": "2c8a4c3557c9a8412eb2ea50ce3f69abc2f47ba1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/3ca1bd74228a61bd05520bed1ef88b5a19764d92", - "reference": "3ca1bd74228a61bd05520bed1ef88b5a19764d92", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/2c8a4c3557c9a8412eb2ea50ce3f69abc2f47ba1", + "reference": "2c8a4c3557c9a8412eb2ea50ce3f69abc2f47ba1", "shasum": "" }, "require": { - "allure-framework/allure-codeception": "~1.2.6", - "codeception/codeception": "~2.3.4", + "allure-framework/allure-codeception": "~1.3.0", + "codeception/codeception": "~2.3.4 || ~2.4.0 ", "consolidation/robo": "^1.0.0", "epfremme/swagger-php": "^2.0", + "ext-curl": "*", "flow/jsonpath": ">0.2", "fzaninotto/faker": "^1.6", "monolog/monolog": "^1.0", @@ -6005,6 +6006,7 @@ "goaop/framework": "2.2.0", "php-coveralls/php-coveralls": "^1.0", "phpmd/phpmd": "^2.6.0", + "phpunit/phpunit": "~6.5.0 || ~7.0.0", "rregeer/phpunit-coverage-check": "^0.1.4", "sebastian/phpcpd": "~3.0 || ~4.0", "squizlabs/php_codesniffer": "~3.2", @@ -6039,7 +6041,7 @@ "magento", "testing" ], - "time": "2018-11-13T18:22:25+00:00" + "time": "2019-01-29T15:31:14+00:00" }, { "name": "moontoast/math", From 06f74305096998fef2434966adec7308fbc20bf8 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Wed, 30 Jan 2019 18:54:22 +0200 Subject: [PATCH 0448/1295] Fixing the styling issue on customizable options --- .../web/css/source/module/main/_collapsible-blocks.less | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_collapsible-blocks.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_collapsible-blocks.less index ed607102a1084..749783f90aaa9 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_collapsible-blocks.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_collapsible-blocks.less @@ -163,6 +163,14 @@ &.collapsible-block-wrapper-last { border-bottom: 0; } + + .admin__dynamic-rows.admin__control-collapsible { + td { + &.admin__collapsible-block-wrapper { + border-bottom: none; + } + } + } } .admin__collapsible-content { From da142dfdce5b9317fb8f71c33d070b8abca112a9 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 30 Jan 2019 11:20:17 -0600 Subject: [PATCH 0449/1295] MC-5926: Conflict of simultaneous write in Redis cache - add integration test bin magento to EE --- dev/tests/integration/bin/magento | 42 +++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100755 dev/tests/integration/bin/magento diff --git a/dev/tests/integration/bin/magento b/dev/tests/integration/bin/magento new file mode 100755 index 0000000000000..303fbfb217d2b --- /dev/null +++ b/dev/tests/integration/bin/magento @@ -0,0 +1,42 @@ +#!/usr/bin/env php +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +if (PHP_SAPI !== 'cli') { + echo 'bin/magento must be run as a CLI application'; + exit(1); +} + +if (isset($_SERVER['INTEGRATION_TEST_PARAMS'])) { + parse_str($_SERVER['INTEGRATION_TEST_PARAMS'], $params); + foreach ($params as $paramName => $paramValue) { + $_SERVER[$paramName] = $paramValue; + } +} else { + echo 'Test parameters are required'; + exit(1); +} + +try { + require $_SERVER['MAGE_DIRS']['base']['path'] . '/app/bootstrap.php'; +} catch (\Exception $e) { + echo 'Autoload error: ' . $e->getMessage(); + exit(1); +} +try { + $handler = new \Magento\Framework\App\ErrorHandler(); + set_error_handler([$handler, 'handler']); + $application = new Magento\Framework\Console\Cli('Magento CLI'); + $application->run(); +} catch (\Exception $e) { + while ($e) { + echo $e->getMessage(); + echo $e->getTraceAsString(); + echo "\n\n"; + $e = $e->getPrevious(); + } + exit(Cli::RETURN_FAILURE); +} From 79a13b483db3bf066cda6bcb69737cec02f78d47 Mon Sep 17 00:00:00 2001 From: roman <rleshchenko@magento.com> Date: Mon, 24 Dec 2018 18:58:43 +0200 Subject: [PATCH 0450/1295] MAGETWO-97081: Fixed incorrect behaviour of sync actions --- .../ProductFrontendAction/Synchronizer.php | 62 ++++++++++--------- .../Framework/DB/Adapter/Pdo/Mysql.php | 4 +- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/ProductFrontendAction/Synchronizer.php b/app/code/Magento/Catalog/Model/Product/ProductFrontendAction/Synchronizer.php index 7a1926cf642ec..3c8d49cbdd744 100644 --- a/app/code/Magento/Catalog/Model/Product/ProductFrontendAction/Synchronizer.php +++ b/app/code/Magento/Catalog/Model/Product/ProductFrontendAction/Synchronizer.php @@ -138,7 +138,9 @@ private function getProductIdsByActions(array $actions) $productIds = []; foreach ($actions as $action) { - $productIds[] = $action['product_id']; + if (isset($action['product_id']) && is_int($action['product_id'])) { + $productIds[] = $action['product_id']; + } } return $productIds; @@ -159,33 +161,37 @@ public function syncActions(array $productsData, $typeId) $customerId = $this->session->getCustomerId(); $visitorId = $this->visitor->getId(); $collection = $this->getActionsByType($typeId); - $collection->addFieldToFilter('product_id', $this->getProductIdsByActions($productsData)); - - /** - * Note that collection is also filtered by visitor id and customer id - * This collection shouldnt be flushed when visitor has products and then login - * It can remove only products for visitor, or only products for customer - * - * ['product_id' => 'added_at'] - * @var ProductFrontendActionInterface $item - */ - foreach ($collection as $item) { - $this->entityManager->delete($item); - } - - foreach ($productsData as $productId => $productData) { - /** @var ProductFrontendActionInterface $action */ - $action = $this->productFrontendActionFactory->create([ - 'data' => [ - 'visitor_id' => $customerId ? null : $visitorId, - 'customer_id' => $this->session->getCustomerId(), - 'added_at' => $productData['added_at'], - 'product_id' => $productId, - 'type_id' => $typeId - ] - ]); - - $this->entityManager->save($action); + $productIds = $this->getProductIdsByActions($productsData); + + if ($productIds) { + $collection->addFieldToFilter('product_id', $productIds); + + /** + * Note that collection is also filtered by visitor id and customer id + * This collection shouldnt be flushed when visitor has products and then login + * It can remove only products for visitor, or only products for customer + * + * ['product_id' => 'added_at'] + * @var ProductFrontendActionInterface $item + */ + foreach ($collection as $item) { + $this->entityManager->delete($item); + } + + foreach ($productsData as $productId => $productData) { + /** @var ProductFrontendActionInterface $action */ + $action = $this->productFrontendActionFactory->create([ + 'data' => [ + 'visitor_id' => $customerId ? null : $visitorId, + 'customer_id' => $this->session->getCustomerId(), + 'added_at' => $productData['added_at'], + 'product_id' => $productId, + 'type_id' => $typeId + ] + ]); + + $this->entityManager->save($action); + } } } diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php index 3d06e27542f07..acdc2b0f01f73 100644 --- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php +++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php @@ -2904,7 +2904,9 @@ public function prepareSqlCondition($fieldName, $condition) if (isset($condition['to'])) { $query .= empty($query) ? '' : ' AND '; $to = $this->_prepareSqlDateCondition($condition, 'to'); - $query = $this->_prepareQuotedSqlCondition($query . $conditionKeyMap['to'], $to, $fieldName); + $query = $query . $this->_prepareQuotedSqlCondition($conditionKeyMap['to'], $to, $fieldName); + + } } elseif (array_key_exists($key, $conditionKeyMap)) { $value = $condition[$key]; From c5d7f4c0aa35d3ec118e7d836512c11b58fd215b Mon Sep 17 00:00:00 2001 From: roman <rleshchenko@magento.com> Date: Wed, 26 Dec 2018 16:20:42 +0200 Subject: [PATCH 0451/1295] MAGETWO-97081: Fixed incorrect behaviour of sync actions --- lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php index acdc2b0f01f73..a6c0dba6e1751 100644 --- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php +++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php @@ -2905,8 +2905,6 @@ public function prepareSqlCondition($fieldName, $condition) $query .= empty($query) ? '' : ' AND '; $to = $this->_prepareSqlDateCondition($condition, 'to'); $query = $query . $this->_prepareQuotedSqlCondition($conditionKeyMap['to'], $to, $fieldName); - - } } elseif (array_key_exists($key, $conditionKeyMap)) { $value = $condition[$key]; From 70c332f87654d230c017e649d7becaf48b0eb56f Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Wed, 30 Jan 2019 13:26:30 -0600 Subject: [PATCH 0452/1295] MQE-1420: Bump MFTF version in Magento - changing argument from its own attribute to part of the command --- ...GuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml index 354974bf1ef53..3d1dc2cf66689 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml @@ -29,8 +29,8 @@ <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <magentoCLI command="config:set payment/checkmo/allowspecific" arguments="0" stepKey="unsetAllowSpecificCountiesValue"/> - <magentoCLI command="config:set payment/checkmo/specificcountry" arguments="''" stepKey="unsetSpecificCountryValue" /> + <magentoCLI command="config:set payment/checkmo/allowspecific 0" stepKey="unsetAllowSpecificCountiesValue"/> + <magentoCLI command="config:set payment/checkmo/specificcountry ''" stepKey="unsetSpecificCountryValue" /> </after> <!-- Add product to cart --> From 39665d69fa72ff6a7296e4751528a92887bd9a6e Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 30 Jan 2019 21:57:21 -0600 Subject: [PATCH 0453/1295] MC-5926: Conflict of simultaneous write in Redis cache - fix integration test --- .../Magento/TestFramework/App/Shell.php | 2 ++ .../Isolation/DeploymentConfig.php | 4 ++++ .../Console/Command/SetModeCommandTest.php | 21 +++++++------------ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/dev/tests/integration/framework/Magento/TestFramework/App/Shell.php b/dev/tests/integration/framework/Magento/TestFramework/App/Shell.php index 1ec1d620c6135..f3dd79680e4d3 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/App/Shell.php +++ b/dev/tests/integration/framework/Magento/TestFramework/App/Shell.php @@ -10,6 +10,8 @@ class Shell extends \Magento\Framework\App\Shell { /** + * Override app/shell by running bin/magento located in the integration test and pass environment parameters + * * @inheritdoc */ public function execute($command, array $arguments = []) diff --git a/dev/tests/integration/framework/Magento/TestFramework/Isolation/DeploymentConfig.php b/dev/tests/integration/framework/Magento/TestFramework/Isolation/DeploymentConfig.php index 873fb0366a8e1..7b87379c8d70c 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Isolation/DeploymentConfig.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Isolation/DeploymentConfig.php @@ -58,6 +58,10 @@ public function startTestSuite() public function endTest(\PHPUnit\Framework\TestCase $test) { $config = $this->reader->load(); + // ignore compiled_config setting because is not set in default mode + if (isset($config['cache_types']['compiled_config'])) { + unset($config['cache_types']['compiled_config']); + } if ($this->config != $config) { $error = "\n\nERROR: deployment configuration is corrupted. The application state is no longer valid.\n" . 'Further tests may fail.' diff --git a/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/SetModeCommandTest.php b/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/SetModeCommandTest.php index 423e8534aa048..687c80cc952af 100644 --- a/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/SetModeCommandTest.php +++ b/dev/tests/integration/testsuite/Magento/Deploy/Console/Command/SetModeCommandTest.php @@ -94,6 +94,14 @@ public function tearDown() $this->writer->saveConfig([ConfigFilePool::APP_ENV => $this->envConfig]); $this->clearStaticFiles(); + // enable default mode + $this->commandTester = new CommandTester($this->getStaticContentDeployCommand()); + $this->commandTester->execute( + ['mode' => 'default'] + ); + $commandOutput = $this->commandTester->getDisplay(); + $this->assertEquals(Cli::RETURN_SUCCESS, $this->commandTester->getStatusCode()); + $this->assertContains('Enabled default mode', $commandOutput); } /** @@ -111,23 +119,11 @@ public function testSwitchMode() { if ($this->prevMode === 'production') { //in production mode, so we have to switch to dev, then to production - $this->clearStaticFiles(); $this->enableAndAssertDeveloperMode(); $this->enableAndAssertProductionMode(); } else { //already in non production mode - //$this->clearStaticFiles(); $this->enableAndAssertProductionMode(); - - // enable previous mode - $this->clearStaticFiles(); - $this->commandTester = new CommandTester($this->getStaticContentDeployCommand()); - $this->commandTester->execute( - ['mode' => $this->prevMode] - ); - $commandOutput = $this->commandTester->getDisplay(); - $this->assertEquals(Cli::RETURN_SUCCESS, $this->commandTester->getStatusCode()); - $this->assertContains('Enabled ' . $this->prevMode . ' mode', $commandOutput); } } @@ -139,7 +135,6 @@ public function testSwitchMode() private function enableAndAssertProductionMode() { // Enable production mode - $this->clearStaticFiles(); $this->commandTester = new CommandTester($this->getStaticContentDeployCommand()); $this->commandTester->execute( ['mode' => 'production'] From 4a3354ae407110e26831798414a48e3047d046d5 Mon Sep 17 00:00:00 2001 From: Arvinda kumar <arvindakumar@cedcoss.com> Date: Wed, 9 Jan 2019 18:54:36 +0530 Subject: [PATCH 0454/1295] iessue fixed #20137 On checkout page apply discount button is not align with input box iessue fixed #20137 On checkout page apply discount button is not align with input box --- .../web/css/source/module/checkout/_payment-options.less | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payment-options.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payment-options.less index 0b27454b206e3..d87b4b70caffa 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payment-options.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payment-options.less @@ -69,6 +69,9 @@ .payment-option-content { .lib-css(padding, 0 0 @indent__base @checkout-payment-option-content__padding__xl); + .action-apply{ + margin-right: 0; + } } .payment-option-inner { From 1a75856e3bcdea4943c723e8127b17647813b487 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Thu, 10 Jan 2019 11:56:45 +0000 Subject: [PATCH 0455/1295] magento/magento2#20144: Fixed code style --- .../web/css/source/module/checkout/_payment-options.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payment-options.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payment-options.less index d87b4b70caffa..47754c9acee4d 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payment-options.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payment-options.less @@ -69,7 +69,7 @@ .payment-option-content { .lib-css(padding, 0 0 @indent__base @checkout-payment-option-content__padding__xl); - .action-apply{ + .action-apply { margin-right: 0; } } From 496c6af02d6bc905c120a27a9231abb78a135d84 Mon Sep 17 00:00:00 2001 From: Arvinda kumar <arvindakumar@cedcommerce.com> Date: Tue, 29 Jan 2019 19:13:10 +0530 Subject: [PATCH 0456/1295] _payment-options.less updated _payment-options.less updated --- .../web/css/source/module/checkout/_payment-options.less | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payment-options.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payment-options.less index 47754c9acee4d..3b584bc26fe34 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payment-options.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payment-options.less @@ -69,8 +69,12 @@ .payment-option-content { .lib-css(padding, 0 0 @indent__base @checkout-payment-option-content__padding__xl); - .action-apply { - margin-right: 0; + .primary { + .action { + &.action-apply { + margin-right: 0; + } + } } } From 44ae98378a6cbab538fc11b84c9462484b968687 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Thu, 31 Jan 2019 11:31:54 +0200 Subject: [PATCH 0457/1295] MAGETWO-97976: Product export creates 2 rows for Simple product with custom options --- .../Magento/CatalogImportExport/Model/Export/Product.php | 8 +++----- .../CatalogImportExport/Model/Export/ProductTest.php | 2 ++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Export/Product.php b/app/code/Magento/CatalogImportExport/Model/Export/Product.php index 395ae6ec1a4dc..dc172bacb32f9 100644 --- a/app/code/Magento/CatalogImportExport/Model/Export/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Export/Product.php @@ -1456,17 +1456,15 @@ protected function getCustomOptionsData($productIds) * @param array $optionData * @return mixed */ - private function getOptionValue($optionName, $defaultOptionsData, $optionData) + private function getOptionValue(string $optionName, array $defaultOptionsData, array $optionData) { $optionId = $optionData['option_id']; - if (array_key_exists($optionName, $optionData) && $optionData[$optionName] !== null) { + if (isset($optionData[$optionName])) { return $optionData[$optionName]; } - if (array_key_exists($optionId, $defaultOptionsData) - && array_key_exists($optionName, $defaultOptionsData[$optionId]) - ) { + if (isset($defaultOptionsData[$optionId][$optionName])) { return $defaultOptionsData[$optionId][$optionName]; } diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php index ea5d6f30cc380..47d74fcfd6719 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php @@ -325,6 +325,8 @@ public function testExportWithMedia() /** * @magentoDataFixture Magento/CatalogImportExport/_files/product_export_data.php + * + * @return void */ public function testExportWithCustomOptionsAndSecondStore() { From 3a4a1d2470ec662093d2eb5075fb96e8ca1fd548 Mon Sep 17 00:00:00 2001 From: Pratik Oza <magepratik@gmail.com> Date: Fri, 25 Jan 2019 15:29:03 +0530 Subject: [PATCH 0458/1295] Fixed Minicart close button overlapping --- .../Magento/luma/web/css/source/components/_modals_extend.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/luma/web/css/source/components/_modals_extend.less b/app/design/frontend/Magento/luma/web/css/source/components/_modals_extend.less index 0e34d7b87387d..834423912e8a1 100644 --- a/app/design/frontend/Magento/luma/web/css/source/components/_modals_extend.less +++ b/app/design/frontend/Magento/luma/web/css/source/components/_modals_extend.less @@ -59,7 +59,7 @@ .modal-custom { .action-close { - .lib-css(margin, @indent__m); + .lib-css(margin, 15px); } } From e1e4f1beec3aecf6332db20e3d063ac25fab151a Mon Sep 17 00:00:00 2001 From: Pratik Oza <magepratik@gmail.com> Date: Sat, 26 Jan 2019 15:00:44 +0530 Subject: [PATCH 0459/1295] Empty block rendering in My Account page sidebar fixed using designing changes --- .../Magento_Customer/web/css/source/_module.less | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less index ade56d7f929c6..bab0c86ef106c 100755 --- a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less @@ -417,6 +417,12 @@ .column.main { width: 77.7%; } + + .sidebar-main { + .block { + margin-bottom: 0; + } + } } .account { @@ -528,11 +534,18 @@ .column.main, .sidebar-additional { margin: 0; + padding: 0; } .data.table { &:extend(.abs-table-striped-mobile all); } + + .sidebar-main { + .account-nav { + margin-bottom: 0; + } + } } } From e13fb9fe98e24a6ea0843628c62337eb78f5eeff Mon Sep 17 00:00:00 2001 From: Oleg Onufer <linkedddd@gmail.com> Date: Thu, 31 Jan 2019 14:30:02 +0200 Subject: [PATCH 0460/1295] MAGETWO-97239: Category rules should apply to complex products --- ...torefrontCategoryRulesShouldApplyToComplexProductsTest.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml index 175d20b82a9b1..047b64c68e7e3 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml @@ -85,11 +85,13 @@ <!-- 3: Open configurable product 1 and add all his child products to cart --> <amOnPage url="{{StorefrontProductPage.url($$createConfigProductCreateConfigurableProduct1.custom_attributes[url_key]$$)}}" stepKey="amOnConfigurableProductPage"/> <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect('$$createConfigProductAttributeCreateConfigurableProduct1.attribute[frontend_labels][0][label]$$')}}" userInput="$$createConfigProductAttributeOption1CreateConfigurableProduct1.option[store_labels][0][label]$$" stepKey="selectOption"/> + <waitForPageLoad stepKey="waitForProductDataChange"/> <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddConfigurableProductToCart"> <argument name="product" value="$$createConfigProductCreateConfigurableProduct1$$"/> <argument name="productCount" value="1"/> </actionGroup> <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect('$$createConfigProductAttributeCreateConfigurableProduct1.attribute[frontend_labels][0][label]$$')}}" userInput="$$createConfigProductAttributeOption2CreateConfigurableProduct1.option[store_labels][0][label]$$" stepKey="selectOption2"/> + <waitForPageLoad stepKey="waitForProductDataChange2"/> <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddConfigurableProductToCart2"> <argument name="product" value="$$createConfigProductCreateConfigurableProduct1$$"/> <argument name="productCount" value="2"/> @@ -101,11 +103,13 @@ <!-- 3: Open configurable product 2 and add all his child products to cart --> <amOnPage url="{{StorefrontProductPage.url($$createConfigProductCreateConfigurableProduct2.custom_attributes[url_key]$$)}}" stepKey="amOnConfigurableProductPage2"/> <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect('$$createConfigProductAttributeCreateConfigurableProduct2.attribute[frontend_labels][0][label]$$')}}" userInput="$$createConfigProductAttributeOption1CreateConfigurableProduct2.option[store_labels][0][label]$$" stepKey="selectOption3"/> + <waitForPageLoad stepKey="waitForProductDataChange3"/> <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddConfigurableProductToCart3"> <argument name="product" value="$$createConfigProductCreateConfigurableProduct2$$"/> <argument name="productCount" value="3"/> </actionGroup> <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect('$$createConfigProductAttributeCreateConfigurableProduct2.attribute[frontend_labels][0][label]$$')}}" userInput="$$createConfigProductAttributeOption2CreateConfigurableProduct2.option[store_labels][0][label]$$" stepKey="selectOption4"/> + <waitForPageLoad stepKey="waitForProductDataChange4"/> <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddConfigurableProductToCart4"> <argument name="product" value="$$createConfigProductCreateConfigurableProduct2$$"/> <argument name="productCount" value="4"/> From 79bc30e4f2e2474a63d622aad589e6ff1e9e45d1 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Thu, 31 Jan 2019 14:37:26 +0200 Subject: [PATCH 0461/1295] MAGETWO-94198: [MAGENTO CLOUD] Unable to add more attributes in size --- .../AdminCreateProductAttributeSection.xml | 5 + .../AdminCreateStoreViewActionGroup.xml | 12 +- .../AdminDeleteStoreViewActionGroup.xml | 8 +- ...minAttributeTextSwatchesCanBeFiledTest.xml | 136 ++++++++++++++++++ .../catalog/product/attribute/text.phtml | 6 +- .../view/adminhtml/web/css/swatches.css | 4 + 6 files changed, 166 insertions(+), 5 deletions(-) create mode 100644 app/code/Magento/Swatches/Test/Mftf/Test/AdminAttributeTextSwatchesCanBeFiledTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index 83116baba4bee..8f7425b5a19e0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -28,6 +28,7 @@ <element name="scope" type="select" selector="#is_global"/> <element name="addToColumnOptions" type="select" selector="#is_used_in_grid"/> <element name="useInFilterOptions" type="select" selector="#is_filterable_in_grid"/> + <element name="addSwatch" type="button" selector="#add_new_swatch_text_option_button"/> </section> <section name="AdminAttributeOptionsSection"> <element name="addOption" type="button" selector="#add_new_option_button"/> @@ -38,4 +39,8 @@ <element name="storefrontPropertiesTab" selector="#product_attribute_tabs_front" type="button" timeout="30"/> <element name="useForPromoRuleConditions" type="select" selector="#is_used_for_promo_rules"/> </section> + <section name="AttributeManageSwatchSection"> + <element name="swatchField" type="input" selector="input[name='swatchtext[value][option_{{option_index}}][{{index}}]'][placeholder='Swatch']" parameterized="true"/> + <element name="descriptionField" type="input" selector="input[name='optiontext[value][option_{{option_index}}][{{index}}]'][placeholder='Description']" parameterized="true"/> + </section> </sections> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml index 6fdfb2566fee9..f1c6f4d87e0d6 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml @@ -7,7 +7,7 @@ --> <!-- Test XML Example --> <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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminCreateStoreViewActionGroup"> <arguments> <argument name="storeGroup" defaultValue="_defaultStoreGroup"/> @@ -27,4 +27,14 @@ <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" stepKey="waitForPageReolad"/> <see userInput="You saved the store view." stepKey="seeSavedMessage" /> </actionGroup> + <actionGroup name="AdminCreateStoreViewUseStringArgumentsActionGroup" extends="AdminCreateStoreViewActionGroup"> + <arguments> + <argument name="storeGroupName" type="string"/> + <argument name="customStoreName" type="string"/> + <argument name="customStoreCode" type="string"/> + </arguments> + <selectOption selector="{{AdminNewStoreSection.storeGrpDropdown}}" userInput="{{storeGroupName}}" stepKey="selectStore" /> + <fillField selector="{{AdminNewStoreSection.storeNameTextField}}" userInput="{{customStoreName}}" stepKey="enterStoreViewName" /> + <fillField selector="{{AdminNewStoreSection.storeCodeTextField}}" userInput="{{customStoreCode}}" stepKey="enterStoreViewCode" /> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml index c32953540a77a..8b059ac164c0f 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml @@ -7,7 +7,7 @@ --> <!-- Test XML Example --> <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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminDeleteStoreViewActionGroup"> <arguments> <argument name="customStore" defaultValue="customStore"/> @@ -25,4 +25,10 @@ <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmStoreDelete"/> <see userInput="You deleted the store view." stepKey="seeDeleteMessage"/> </actionGroup> + <actionGroup name="AdminDeleteStoreViewUseStringArgumentsActionGroup" extends="AdminDeleteStoreViewActionGroup"> + <arguments> + <argument name="customStoreName" type="string"/> + </arguments> + <fillField selector="{{AdminStoresGridSection.storeFilterTextField}}" userInput="{{customStoreName}}" stepKey="fillStoreViewFilterField"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminAttributeTextSwatchesCanBeFiledTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminAttributeTextSwatchesCanBeFiledTest.xml new file mode 100644 index 0000000000000..c64ba0e89de18 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminAttributeTextSwatchesCanBeFiledTest.xml @@ -0,0 +1,136 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminAttributeTextSwatchesCanBeFiledTest"> + <annotations> + <features value="Backend"/> + <stories value="Create/configure swatches product attribute"/> + <title value="Check that attribute text swatches can be filed"/> + <description value="Check that attribute text swatches can be filed"/> + <severity value="MAJOR"/> + <testCaseId value="MC-13771"/> + <group value="swatches"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!-- Create 10 store views --> + <actionGroup ref="AdminCreateStoreViewUseStringArgumentsActionGroup" stepKey="createStoreView"> + <argument name="storeGroupName" value="{{_defaultStoreGroup.name}}" /> + <argument name="customStoreName" value="{{customStore.name}}" /> + <argument name="customStoreCode" value="{{customStore.code}}" /> + </actionGroup> + <actionGroup ref="AdminCreateStoreViewUseStringArgumentsActionGroup" stepKey="createStoreView1"> + <argument name="storeGroupName" value="{{_defaultStoreGroup.name}}" /> + <argument name="customStoreName" value="{{customStore.name}}1" /> + <argument name="customStoreCode" value="{{customStore.code}}1" /> + </actionGroup> + <actionGroup ref="AdminCreateStoreViewUseStringArgumentsActionGroup" stepKey="createStoreView2"> + <argument name="storeGroupName" value="{{_defaultStoreGroup.name}}" /> + <argument name="customStoreName" value="{{customStore.name}}2" /> + <argument name="customStoreCode" value="{{customStore.code}}2" /> + </actionGroup> + <actionGroup ref="AdminCreateStoreViewUseStringArgumentsActionGroup" stepKey="createStoreView3"> + <argument name="storeGroupName" value="{{_defaultStoreGroup.name}}" /> + <argument name="customStoreName" value="{{customStore.name}}3" /> + <argument name="customStoreCode" value="{{customStore.code}}3" /> + </actionGroup> + <actionGroup ref="AdminCreateStoreViewUseStringArgumentsActionGroup" stepKey="createStoreView4"> + <argument name="storeGroupName" value="{{_defaultStoreGroup.name}}" /> + <argument name="customStoreName" value="{{customStore.name}}4" /> + <argument name="customStoreCode" value="{{customStore.code}}4" /> + </actionGroup> + <actionGroup ref="AdminCreateStoreViewUseStringArgumentsActionGroup" stepKey="createStoreView5"> + <argument name="storeGroupName" value="{{_defaultStoreGroup.name}}" /> + <argument name="customStoreName" value="{{customStore.name}}5" /> + <argument name="customStoreCode" value="{{customStore.code}}5" /> + </actionGroup> + <actionGroup ref="AdminCreateStoreViewUseStringArgumentsActionGroup" stepKey="createStoreView6"> + <argument name="storeGroupName" value="{{_defaultStoreGroup.name}}" /> + <argument name="customStoreName" value="{{customStore.name}}6" /> + <argument name="customStoreCode" value="{{customStore.code}}6" /> + </actionGroup> + <actionGroup ref="AdminCreateStoreViewUseStringArgumentsActionGroup" stepKey="createStoreView7"> + <argument name="storeGroupName" value="{{_defaultStoreGroup.name}}" /> + <argument name="customStoreName" value="{{customStore.name}}7" /> + <argument name="customStoreCode" value="{{customStore.code}}7" /> + </actionGroup> + <actionGroup ref="AdminCreateStoreViewUseStringArgumentsActionGroup" stepKey="createStoreView8"> + <argument name="storeGroupName" value="{{_defaultStoreGroup.name}}" /> + <argument name="customStoreName" value="{{customStore.name}}8" /> + <argument name="customStoreCode" value="{{customStore.code}}8" /> + </actionGroup> + <actionGroup ref="AdminCreateStoreViewUseStringArgumentsActionGroup" stepKey="createStoreView9"> + <argument name="storeGroupName" value="{{_defaultStoreGroup.name}}" /> + <argument name="customStoreName" value="{{customStore.name}}9" /> + <argument name="customStoreCode" value="{{customStore.code}}9" /> + </actionGroup> + </before> + <after> + <!-- Delete all 10 store views --> + <actionGroup ref="AdminDeleteStoreViewUseStringArgumentsActionGroup" stepKey="deleteStoreView"> + <argument name="customStoreName" value="{{customStore.name}}"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewUseStringArgumentsActionGroup" stepKey="deleteStoreView1"> + <argument name="customStoreName" value="{{customStore.name}}1"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewUseStringArgumentsActionGroup" stepKey="deleteStoreView2"> + <argument name="customStoreName" value="{{customStore.name}}2"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewUseStringArgumentsActionGroup" stepKey="deleteStoreView3"> + <argument name="customStoreName" value="{{customStore.name}}3"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewUseStringArgumentsActionGroup" stepKey="deleteStoreView4"> + <argument name="customStoreName" value="{{customStore.name}}4"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewUseStringArgumentsActionGroup" stepKey="deleteStoreView5"> + <argument name="customStoreName" value="{{customStore.name}}5"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewUseStringArgumentsActionGroup" stepKey="deleteStoreView6"> + <argument name="customStoreName" value="{{customStore.name}}6"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewUseStringArgumentsActionGroup" stepKey="deleteStoreView7"> + <argument name="customStoreName" value="{{customStore.name}}7"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewUseStringArgumentsActionGroup" stepKey="deleteStoreView8"> + <argument name="customStoreName" value="{{customStore.name}}8"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewUseStringArgumentsActionGroup" stepKey="deleteStoreView9"> + <argument name="customStoreName" value="{{customStore.name}}9"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Navigate to Product attribute page--> + <amOnPage url="{{ProductAttributePage.url}}" stepKey="navigateToNewProductAttributePage"/> + <fillField userInput="test_label" selector="{{AttributePropertiesSection.defaultLabel}}" stepKey="fillDefaultLabel"/> + <selectOption selector="{{AttributePropertiesSection.inputType}}" userInput="Text Swatch" stepKey="selectInputType"/> + <click selector="{{AdvancedAttributePropertiesSection.addSwatch}}" stepKey="clickAddSwatch"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + + <!-- Get field outer width --> + <executeJS function="return jQuery("{{AttributeManageSwatchSection.descriptionField('0','0')}}").outerWidth();" stepKey="getElementWidth"/> + <assertGreaterThanOrEqual stepKey="assertElementsWidthIsGreaterOrEqual"> + <expectedResult type="int">30</expectedResult> + <actualResult type="variable">getElementWidth</actualResult> + </assertGreaterThanOrEqual> + + <!-- Fill Swatch and Description fields for Admin --> + <fillField selector="{{AttributeManageSwatchSection.swatchField('0','0')}}" userInput="test" stepKey="fillSwatchForAdmin"/> + <fillField selector="{{AttributeManageSwatchSection.descriptionField('0','0')}}" userInput="test" stepKey="fillDescriptionForAdmin"/> + + <!-- Grab value Swatch and Description fields for Admin --> + <grabValueFrom selector="{{AttributeManageSwatchSection.swatchField('0','0')}}" stepKey="grabSwatchForAdmin"/> + <grabValueFrom selector="{{AttributeManageSwatchSection.descriptionField('0','0'')}}" stepKey="grabDescriptionForAdmin"/> + + <!-- Check that Swatch and Description fields for Admin are not empty--> + <assertNotEmpty actual="$grabSwatchForAdmin" stepKey="checkSwatchFieldForAdmin"/> + <assertNotEmpty actual="$grabDescriptionForAdmin" stepKey="checkDescriptionFieldForAdmin"/> + </test> +</tests> diff --git a/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/text.phtml b/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/text.phtml index 8d4400b3d0477..e00c41d371c9e 100644 --- a/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/text.phtml +++ b/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/text.phtml @@ -21,7 +21,7 @@ $stores = $block->getStoresSortedBySortOrder(); <th class="col-draggable"></th> <th class="col-default"><span><?= $block->escapeHtml(__('Is Default')) ?></span></th> <?php foreach ($stores as $_store): ?> - <th class="col-swatch col-<%- data.id %> + <th class="col-swatch col-swatch-min-width col-<%- data.id %> <?php if ($_store->getId() == \Magento\Store\Model\Store::DEFAULT_STORE_ID): ?> _required<?php endif; ?>" colspan="2"> <span><?= $block->escapeHtml($_store->getName()) ?></span> @@ -75,7 +75,7 @@ $stores = $block->getStoresSortedBySortOrder(); </td> <?php foreach ($stores as $_store): ?> <?php $storeId = (int)$_store->getId(); ?> - <td class="col-swatch col-<%- data.id %>"> + <td class="col-swatch col-swatch-min-width col-<%- data.id %>"> <input class="input-text swatch-text-field-<?= /* @noEscape */ $storeId ?> <?php if ($storeId == \Magento\Store\Model\Store::DEFAULT_STORE_ID): ?> required-option required-unique<?php endif; ?>" @@ -83,7 +83,7 @@ $stores = $block->getStoresSortedBySortOrder(); type="text" value="<%- data.swatch<?= /* @noEscape */ $storeId ?> %>" placeholder="<?= $block->escapeHtml(__("Swatch")) ?>"/> </td> - <td class="swatch-col-<%- data.id %>"> + <td class="col-swatch-min-width swatch-col-<%- data.id %>"> <input name="optiontext[value][<%- data.id %>][<?= /* @noEscape */ $storeId ?>]" value="<%- data.store<?= /* @noEscape */ $storeId ?> %>" class="input-text<?php if ($storeId == \Magento\Store\Model\Store::DEFAULT_STORE_ID): ?> required-option<?php endif; ?>" diff --git a/app/code/Magento/Swatches/view/adminhtml/web/css/swatches.css b/app/code/Magento/Swatches/view/adminhtml/web/css/swatches.css index 495b234edf40e..02a3f0324d955 100644 --- a/app/code/Magento/Swatches/view/adminhtml/web/css/swatches.css +++ b/app/code/Magento/Swatches/view/adminhtml/web/css/swatches.css @@ -149,6 +149,10 @@ width: 50px; } +.col-swatch-min-width { + min-width: 30px; +} + .swatches-visual-col.unavailable:after { position: absolute; width: 35px; From 05bc8a603a563bf54601d11e5af5f46eb9b6a716 Mon Sep 17 00:00:00 2001 From: Rajneesh Gupta <er.rajneeshgupta@gmail.com> Date: Fri, 25 Jan 2019 17:47:58 +0000 Subject: [PATCH 0462/1295] =?UTF-8?q?Update=20Filter.php=20fix=20issue=20?= =?UTF-8?q?=C2=A320624?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix issue https://github.com/magento/magento2/issues/20624 --- .../Magento/ImportExport/Block/Adminhtml/Export/Filter.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/ImportExport/Block/Adminhtml/Export/Filter.php b/app/code/Magento/ImportExport/Block/Adminhtml/Export/Filter.php index 94dc0ee7493b0..f9b5d039f5b69 100644 --- a/app/code/Magento/ImportExport/Block/Adminhtml/Export/Filter.php +++ b/app/code/Magento/ImportExport/Block/Adminhtml/Export/Filter.php @@ -236,8 +236,8 @@ protected function _getSelectHtmlWithValue(Attribute $attribute, $value) if ($attribute->getFilterOptions()) { $options = []; - foreach ($attribute->getFilterOptions() as $value => $label) { - $options[] = ['value' => $value, 'label' => $label]; + foreach ($attribute->getFilterOptions() as $filterOptions => $label) { + $options[] = ['value' => $filterOptions, 'label' => $label]; } } else { $options = $attribute->getSource()->getAllOptions(false); From 34da4ca890562dc90d7f4f9610cafd79d2694f7b Mon Sep 17 00:00:00 2001 From: Rajneesh Gupta <er.rajneeshgupta@gmail.com> Date: Sat, 26 Jan 2019 00:11:39 +0530 Subject: [PATCH 0463/1295] update filter.php $value param was overridden by optionValues. --- .../Magento/ImportExport/Block/Adminhtml/Export/Filter.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/ImportExport/Block/Adminhtml/Export/Filter.php b/app/code/Magento/ImportExport/Block/Adminhtml/Export/Filter.php index f9b5d039f5b69..7655b0e8929f6 100644 --- a/app/code/Magento/ImportExport/Block/Adminhtml/Export/Filter.php +++ b/app/code/Magento/ImportExport/Block/Adminhtml/Export/Filter.php @@ -236,8 +236,8 @@ protected function _getSelectHtmlWithValue(Attribute $attribute, $value) if ($attribute->getFilterOptions()) { $options = []; - foreach ($attribute->getFilterOptions() as $filterOptions => $label) { - $options[] = ['value' => $filterOptions, 'label' => $label]; + foreach ($attribute->getFilterOptions() as $optionValue => $label) { + $options[] = ['value' => $optionValue, 'label' => $label]; } } else { $options = $attribute->getSource()->getAllOptions(false); From 2eb575bee617f7f4afda7818558fe0959ec4f5d9 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Thu, 31 Jan 2019 09:32:45 -0600 Subject: [PATCH 0464/1295] MC-5926: Conflict of simultaneous write in Redis cache - fix tests --- .../Magento/Deploy/Test/Unit/Model/FilesystemTest.php | 9 +++++++-- .../framework/Magento/TestFramework/App/Shell.php | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Deploy/Test/Unit/Model/FilesystemTest.php b/app/code/Magento/Deploy/Test/Unit/Model/FilesystemTest.php index d14c86c4a3264..80313e9ec2521 100644 --- a/app/code/Magento/Deploy/Test/Unit/Model/FilesystemTest.php +++ b/app/code/Magento/Deploy/Test/Unit/Model/FilesystemTest.php @@ -116,17 +116,22 @@ public function testRegenerateStatic() $this->storeView->method('retrieveLocales') ->willReturn($storeLocales); - $setupDiCompileCmd = $this->cmdPrefix . 'setup:di:compile'; + $setupDiCompileCmd = $this->cmdPrefix . 'cache:flush '; $this->shell->expects(self::at(0)) ->method('execute') ->with($setupDiCompileCmd); + $setupDiCompileCmd = $this->cmdPrefix . 'setup:di:compile'; + $this->shell->expects(self::at(1)) + ->method('execute') + ->with($setupDiCompileCmd); + $this->initAdminLocaleMock('en_US'); $usedLocales = ['fr_FR', 'de_DE', 'nl_NL', 'en_US']; $staticContentDeployCmd = $this->cmdPrefix . 'setup:static-content:deploy -f ' . implode(' ', $usedLocales); - $this->shell->expects(self::at(1)) + $this->shell->expects(self::at(2)) ->method('execute') ->with($staticContentDeployCmd); diff --git a/dev/tests/integration/framework/Magento/TestFramework/App/Shell.php b/dev/tests/integration/framework/Magento/TestFramework/App/Shell.php index f3dd79680e4d3..89f64aec16d06 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/App/Shell.php +++ b/dev/tests/integration/framework/Magento/TestFramework/App/Shell.php @@ -4,6 +4,7 @@ * See COPYING.txt for license details. */ namespace Magento\TestFramework\App; + /** * Shell command line wrapper encapsulates command execution and arguments escaping */ From 44f174b8d24c5b130515e2da1745d4f5ce20cd53 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Thu, 31 Jan 2019 11:49:04 -0600 Subject: [PATCH 0465/1295] MC-5926: Conflict of simultaneous write in Redis cache - create ignore list --- .../Isolation/DeploymentConfig.php | 40 ++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/dev/tests/integration/framework/Magento/TestFramework/Isolation/DeploymentConfig.php b/dev/tests/integration/framework/Magento/TestFramework/Isolation/DeploymentConfig.php index 7b87379c8d70c..cd6957199914b 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Isolation/DeploymentConfig.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Isolation/DeploymentConfig.php @@ -28,6 +28,17 @@ class DeploymentConfig */ private $config; + /** + * Ignore values in the config nested array, paths are separated by single slash "/". + * + * Example: compiled_config is not set in default mode, and once set it can't be unset + * + * @var array + */ + private $ignoreValues = [ + 'cache_types/compiled_config', + ]; + /** * Memorizes the initial value of configuration reader and the configuration value * @@ -57,11 +68,7 @@ public function startTestSuite() */ public function endTest(\PHPUnit\Framework\TestCase $test) { - $config = $this->reader->load(); - // ignore compiled_config setting because is not set in default mode - if (isset($config['cache_types']['compiled_config'])) { - unset($config['cache_types']['compiled_config']); - } + $config = $this->filterIgnoredConfigValues($this->reader->load()); if ($this->config != $config) { $error = "\n\nERROR: deployment configuration is corrupted. The application state is no longer valid.\n" . 'Further tests may fail.' @@ -70,4 +77,27 @@ public function endTest(\PHPUnit\Framework\TestCase $test) $test->fail($error); } } + + /** + * Filter ignored config values which are not set by default and appear when tests would change state. + * + * Example: compiled_config is not set in default mode, and once set it can't be unset + * + * @param array $config + * @param string $path + * @return array + */ + private function filterIgnoredConfigValues(array $config, string $path = '') { + foreach ($config as $configKeyName => $configValue) { + $newPath = !empty($path) ? $path . '/' . $configKeyName : $configKeyName; + if (is_array($configValue)) { + $config[$configKeyName] = $this->filterIgnoredConfigValues($configValue, $newPath); + } else { + if (array_key_exists($newPath, array_flip($this->ignoreValues))) { + unset($config[$configKeyName]); + } + } + } + return $config; + } } From c495f31900cc2250bb63bcd57242d518aa894d92 Mon Sep 17 00:00:00 2001 From: Mahesh Singh <mahesh721@webkul.com> Date: Fri, 1 Feb 2019 00:44:04 +0530 Subject: [PATCH 0466/1295] issue #20380 fixed for 2.2 --- app/code/Magento/Sales/Model/Order.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order.php b/app/code/Magento/Sales/Model/Order.php index 1972aa71080c8..8b58f7abb6a27 100644 --- a/app/code/Magento/Sales/Model/Order.php +++ b/app/code/Magento/Sales/Model/Order.php @@ -1298,12 +1298,12 @@ public function getTrackingNumbers() * Retrieve shipping method * * @param bool $asObject return carrier code and shipping method data as object - * @return string|\Magento\Framework\DataObject + * @return string|null|\Magento\Framework\DataObject */ public function getShippingMethod($asObject = false) { $shippingMethod = parent::getShippingMethod(); - if (!$asObject) { + if (!$asObject || !$shippingMethod) { return $shippingMethod; } else { list($carrierCode, $method) = explode('_', $shippingMethod, 2); From 72ee1b6919af10cc228d1ac56caeb98ee5dd54b8 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Thu, 31 Jan 2019 13:36:21 -0600 Subject: [PATCH 0467/1295] MC-5926: Conflict of simultaneous write in Redis cache - fix static --- .../Magento/TestFramework/Isolation/DeploymentConfig.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev/tests/integration/framework/Magento/TestFramework/Isolation/DeploymentConfig.php b/dev/tests/integration/framework/Magento/TestFramework/Isolation/DeploymentConfig.php index cd6957199914b..3132aed4d21e3 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Isolation/DeploymentConfig.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Isolation/DeploymentConfig.php @@ -87,7 +87,8 @@ public function endTest(\PHPUnit\Framework\TestCase $test) * @param string $path * @return array */ - private function filterIgnoredConfigValues(array $config, string $path = '') { + private function filterIgnoredConfigValues(array $config, string $path = '') + { foreach ($config as $configKeyName => $configValue) { $newPath = !empty($path) ? $path . '/' . $configKeyName : $configKeyName; if (is_array($configValue)) { From 81f60515dd9254fd95709ff319dd84f11f703d68 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Thu, 31 Jan 2019 16:40:55 -0600 Subject: [PATCH 0468/1295] MC-5926: Conflict of simultaneous write in Redis cache - adding cache flush after compile, to prevent compile deleting cache folder in some environments like travis --- app/code/Magento/Deploy/Model/Filesystem.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/code/Magento/Deploy/Model/Filesystem.php b/app/code/Magento/Deploy/Model/Filesystem.php index 94f01f9b8fd14..68fd25310e261 100644 --- a/app/code/Magento/Deploy/Model/Filesystem.php +++ b/app/code/Magento/Deploy/Model/Filesystem.php @@ -145,6 +145,11 @@ public function regenerateStatic( // Trigger code generation $this->compile($output); + + // Cache flush will regenerate needed folders structure for compilation and deploy that were deleted previously + $cmd = $this->functionCallPath . 'cache:flush '; + $this->shell->execute($cmd); + // Trigger static assets compilation and deployment $this->deployStaticContent($output); } From b91f3fe0cce59ea8a0cf7208ce1415437d3dac66 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Thu, 31 Jan 2019 17:43:11 -0600 Subject: [PATCH 0469/1295] MC-5926: Conflict of simultaneous write in Redis cache - consolidate cache flush into method --- app/code/Magento/Deploy/Model/Filesystem.php | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Deploy/Model/Filesystem.php b/app/code/Magento/Deploy/Model/Filesystem.php index 68fd25310e261..eb9c0a637ff06 100644 --- a/app/code/Magento/Deploy/Model/Filesystem.php +++ b/app/code/Magento/Deploy/Model/Filesystem.php @@ -139,16 +139,12 @@ public function regenerateStatic( ] ); - // Cache flush will regenerate needed folders structure for compilation and deploy that were deleted previously - $cmd = $this->functionCallPath . 'cache:flush '; - $this->shell->execute($cmd); + $this->reinitCacheDirectories(); // Trigger code generation $this->compile($output); - // Cache flush will regenerate needed folders structure for compilation and deploy that were deleted previously - $cmd = $this->functionCallPath . 'cache:flush '; - $this->shell->execute($cmd); + $this->reinitCacheDirectories(); // Trigger static assets compilation and deployment $this->deployStaticContent($output); @@ -344,4 +340,15 @@ public function lockStaticResources() self::PERMISSIONS_FILE ); } + + /** + * Flush cache and restore the basic cache directories. + * + * @throws LocalizedException + */ + private function reinitCacheDirectories() + { + $command = $this->functionCallPath . 'cache:flush'; + $this->shell->execute($command); + } } From c60d395347569dd4feb3be07dab5b8a5738b6e85 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> Date: Fri, 1 Feb 2019 09:22:04 +0200 Subject: [PATCH 0470/1295] magento/magento2#19098 2.2.6 Use batches and direct queries to fix sales address upgrade Fix static test failure --- app/code/Magento/Sales/Setup/UpgradeData.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Setup/UpgradeData.php b/app/code/Magento/Sales/Setup/UpgradeData.php index 56cbf31c3d695..0f9833ed8f7f2 100644 --- a/app/code/Magento/Sales/Setup/UpgradeData.php +++ b/app/code/Magento/Sales/Setup/UpgradeData.php @@ -50,7 +50,7 @@ class UpgradeData implements UpgradeDataInterface /** * @SuppressWarnings(PHPMD.UnusedFormalParameter) - * + * * @param SalesSetupFactory $salesSetupFactory * @param Config $eavConfig * @param AggregatedFieldDataConverter $aggregatedFieldConverter From bef07b690a96fbc6bbe7b53d59b746a10c7cb949 Mon Sep 17 00:00:00 2001 From: DmytroPaidych <dimonovp@gmail.com> Date: Fri, 1 Feb 2019 09:50:55 +0200 Subject: [PATCH 0471/1295] MAGETWO-97236: Delete Product Staging Update when the Product is used --- .../Catalog/Test/Mftf/Data/ProductData.xml | 5 +- .../Test/Mftf/Data/ProductLinkData.xml | 19 +++ .../Test/Mftf/Data/ProductLinksData.xml | 14 +++ .../Catalog/Test/Mftf/Data/WidgetsData.xml | 15 +++ .../Test/Mftf/Metadata/product-meta.xml | 3 + .../Test/Mftf/Page/AdminNewWidgetPage.xml | 14 +++ .../Test/Mftf/Page/StorefrontProductPage.xml | 3 +- .../Mftf/Section/AdminNewWidgetSection.xml | 14 +++ ...dminNewWidgetSelectProductPopupSection.xml | 15 +++ ...StorefrontProducRelatedProductsSection.xml | 14 +++ .../Mftf/Page/AdminUrlRewriteEditPage.xml | 14 +++ .../Section/AdminUrlRewriteEditSection.xml | 14 +++ .../AdminCreateWidgetActionGroup.xml | 108 +++++++++--------- .../Test/Mftf/Page/AdminNewWidgetPage.xml | 4 +- .../Mftf/Section/AdminNewWidgetSection.xml | 3 +- 15 files changed, 203 insertions(+), 56 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Data/ProductLinkData.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Data/ProductLinksData.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Data/WidgetsData.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Page/AdminNewWidgetPage.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminNewWidgetSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminNewWidgetSelectProductPopupSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProducRelatedProductsSection.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 39f4f0f2d997d..86bdb1a102172 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -7,7 +7,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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="_defaultProduct" type="product"> <data key="sku" unique="suffix">testSku</data> <data key="type_id">simple</data> @@ -282,4 +282,7 @@ <data key="quantity">1</data> <requiredEntity type="product_extension_attribute">EavStock1</requiredEntity> </entity> + <entity name="GetProduct2" type="product2"> + <var key="sku" entityKey="sku" entityType="product2"/> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductLinkData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductLinkData.xml new file mode 100644 index 0000000000000..000bb2095002c --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductLinkData.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="RelatedProductLink" type="product_link"> + <var key="sku" entityKey="sku" entityType="product2"/> + <var key="linked_product_sku" entityKey="sku" entityType="product"/> + <data key="link_type">related</data> + <data key="linked_product_type">simple</data> + <data key="position">1</data> + <requiredEntity type="product_link_extension_attribute">Qty1000</requiredEntity> + </entity> +</entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductLinksData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductLinksData.xml new file mode 100644 index 0000000000000..bd4f807880ab8 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductLinksData.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="OneRelatedProductLink" type="product_links"> + <requiredEntity type="product_link">RelatedProductLink</requiredEntity> + </entity> +</entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/WidgetsData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/WidgetsData.xml new file mode 100644 index 0000000000000..83f0a56c21545 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Data/WidgetsData.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="ProductLinkWidget" extends="ProductsListWidget"> + <data key="type">Catalog Product Link</data> + <data key="template">Product Link Block Template</data> + </entity> +</entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/product-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/product-meta.xml index 1bf7d0b0d988f..23be8408d4a1e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Metadata/product-meta.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/product-meta.xml @@ -121,4 +121,7 @@ <operation name="deleteProduct2" dataType="product2" type="delete" auth="adminOauth" url="/V1/products/{sku}" method="DELETE"> <contentType>application/json</contentType> </operation> + <operation name="GetProduct2" dataType="product2" type="get" auth="adminOauth" url="/V1/products/{sku}" method="GET"> + <contentType>application/json</contentType> + </operation> </operations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminNewWidgetPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminNewWidgetPage.xml new file mode 100644 index 0000000000000..e23a503266e33 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminNewWidgetPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminNewWidgetPage" url="admin/widget_instance/new/" area="admin" module="Magento_Widget"> + <section name="AdminNewWidgetSelectProductPopupSection"/> + </page> +</pages> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/StorefrontProductPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/StorefrontProductPage.xml index 9fcbcc199176b..fdfee62f6dc0b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/StorefrontProductPage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/StorefrontProductPage.xml @@ -7,11 +7,12 @@ --> <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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="StorefrontProductPage" url="/{{var1}}.html" area="storefront" module="Magento_Catalog" parameterized="true"> <section name="StorefrontProductPageSection"/> <section name="StorefrontProductAdditionalInformationSection"/> <section name="StorefrontProductMediaSection"/> <section name="StorefrontProductInfoMainSection"/> + <section name="StorefrontProductRelatedProductsSection"/> </page> </pages> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminNewWidgetSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminNewWidgetSection.xml new file mode 100644 index 0000000000000..5329ad48c8f43 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminNewWidgetSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminNewWidgetSection"> + <element name="selectProduct" type="button" selector=".btn-chooser" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminNewWidgetSelectProductPopupSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminNewWidgetSelectProductPopupSection.xml new file mode 100644 index 0000000000000..0da67849f85c6 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminNewWidgetSelectProductPopupSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminNewWidgetSelectProductPopupSection"> + <element name="filterBySku" type="input" selector=".data-grid-filters input[name='chooser_sku']"/> + <element name="firstRow" type="select" selector=".even>td" timeout="20"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProducRelatedProductsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProducRelatedProductsSection.xml new file mode 100644 index 0000000000000..a7b72bbaa78aa --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProducRelatedProductsSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontProductRelatedProductsSection"> + <element name="relatedProductName" type="button" selector="//*[@class='block related']//a[contains(text(), '{{productName}}')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml new file mode 100644 index 0000000000000..b43e0e05ad55d --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminUrlRewriteEditPage" url="admin/url_rewrite/edit/id/{{url_rewrite_id}}/" area="admin" module="Magento_UrlRewrite"> + <section name="AdminUrlRewriteEditSection"/> + </page> +</pages> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml new file mode 100644 index 0000000000000..7095214e72c43 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminUrlRewriteEditSection"> + <element name="requestPathField" type="input" selector="#request_path"/> + </section> +</sections> diff --git a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml index b6dfc00c0ff9e..f146e793db882 100644 --- a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml +++ b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml @@ -7,58 +7,64 @@ --> <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="AdminCreateProductsListWidgetActionGroup"> - <arguments> - <argument name="widget"/> - </arguments> - <amOnPage url="{{AdminDashboardPage.url}}" stepKey="amOnAdminDashboard"/> - <click selector="{{AdminMenuSection.content}}" stepKey="clickContent"/> - <waitForLoadingMaskToDisappear stepKey="waitForWidgets" /> - <click selector="{{AdminMenuSection.widgets}}" stepKey="clickWidgets"/> - <waitForPageLoad stepKey="waitForWidgetsLoad"/> - <click selector="{{AdminMainActionsSection.add}}" stepKey="addNewWidget"/> - <selectOption selector="{{AdminNewWidgetSection.widgetType}}" userInput="{{widget.type}}" stepKey="setWidgetType"/> - <selectOption selector="{{AdminNewWidgetSection.widgetDesignTheme}}" userInput="{{widget.design_theme}}" stepKey="setWidgetDesignTheme"/> - <click selector="{{AdminNewWidgetSection.continue}}" stepKey="clickContinue"/> - <fillField selector="{{AdminNewWidgetSection.widgetTitle}}" userInput="{{widget.name}}" stepKey="fillTitle"/> - <selectOption selector="{{AdminNewWidgetSection.widgetStoreIds}}" userInput="{{widget.store_ids[0]}}" stepKey="setWidgetStoreIds"/> - <click selector="{{AdminNewWidgetSection.addLayoutUpdate}}" stepKey="clickAddLayoutUpdate"/> - <selectOption selector="{{AdminNewWidgetSection.selectDisplayOn}}" userInput="{{widget.display_on}}" stepKey="setDisplayOn"/> - <waitForAjaxLoad stepKey="waitForLoad"/> - <selectOption selector="{{AdminNewWidgetSection.selectContainer}}" userInput="{{widget.container}}" stepKey="setContainer"/> - <waitForAjaxLoad stepKey="waitForPageLoad"/> - <scrollToTopOfPage stepKey="scrollToTopOfPage"/> - <click selector="{{AdminNewWidgetSection.widgetOptions}}" stepKey="clickWidgetOptions"/> - <click selector="{{AdminNewWidgetSection.addNewCondition}}" stepKey="clickAddNewCondition"/> - <selectOption selector="{{AdminNewWidgetSection.selectCondition}}" userInput="{{widget.condition}}" stepKey="selectCondition"/> - <waitForElement selector="{{AdminNewWidgetSection.ruleParameter}}" stepKey="waitRuleParameter"/> - <click selector="{{AdminNewWidgetSection.ruleParameter}}" stepKey="clickRuleParameter"/> - <click selector="{{AdminNewWidgetSection.openChooser}}" stepKey="clickChooser"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadChooser"/> - <click selector="{{AdminNewWidgetSection.sortById}}" stepKey="clickSortById"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskDisappear"/> - <click selector="{{AdminNewWidgetSection.sortByIdAscend}}" stepKey="clickSortByIdAscend"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> - <click selector="{{AdminNewWidgetSection.selectAll}}" stepKey="clickSelectAll"/> - <click selector="{{AdminNewWidgetSection.applyParameter}}" stepKey="clickApplyRuleParameter"/> - <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveWidget"/> - <waitForPageLoad stepKey="waitForSaveLoad"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="The widget instance has been saved" stepKey="seeSuccess"/> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateWidgetActionGroup"> + <arguments> + <argument name="widget"/> + </arguments> + <amOnPage url="{{AdminNewWidgetPage.url}}" stepKey="amOnAdminNewWidgetPage"/> + <selectOption selector="{{AdminNewWidgetSection.widgetType}}" userInput="{{widget.type}}" stepKey="setWidgetType"/> + <selectOption selector="{{AdminNewWidgetSection.widgetDesignTheme}}" userInput="{{widget.design_theme}}" stepKey="setWidgetDesignTheme"/> + <click selector="{{AdminNewWidgetSection.continue}}" stepKey="clickContinue"/> + <fillField selector="{{AdminNewWidgetSection.widgetTitle}}" userInput="{{widget.name}}" stepKey="fillTitle"/> + <selectOption selector="{{AdminNewWidgetSection.widgetStoreIds}}" userInput="{{widget.store_ids[0]}}" stepKey="setWidgetStoreIds"/> + <click selector="{{AdminNewWidgetSection.addLayoutUpdate}}" stepKey="clickAddLayoutUpdate"/> + <selectOption selector="{{AdminNewWidgetSection.selectDisplayOn}}" userInput="{{widget.display_on}}" stepKey="setDisplayOn"/> + <waitForAjaxLoad stepKey="waitForLoad"/> + <selectOption selector="{{AdminNewWidgetSection.selectContainer}}" userInput="{{widget.container}}" stepKey="setContainer"/> + <waitForAjaxLoad stepKey="waitForPageLoad"/> + <scrollToTopOfPage stepKey="scrollToTopOfPage"/> + <click selector="{{AdminNewWidgetSection.widgetOptions}}" stepKey="clickWidgetOptions"/> + </actionGroup> + + <!--Create Product List Widget--> + <actionGroup name="AdminCreateProductsListWidgetActionGroup" extends="AdminCreateWidgetActionGroup"> + <click selector="{{AdminNewWidgetSection.addNewCondition}}" stepKey="clickAddNewCondition"/> + <selectOption selector="{{AdminNewWidgetSection.selectCondition}}" userInput="{{widget.condition}}" stepKey="selectCondition"/> + <waitForElement selector="{{AdminNewWidgetSection.ruleParameter}}" stepKey="waitRuleParameter"/> + <click selector="{{AdminNewWidgetSection.ruleParameter}}" stepKey="clickRuleParameter"/> + <click selector="{{AdminNewWidgetSection.openChooser}}" stepKey="clickChooser"/> + <waitForPageLoad stepKey="waitForAjaxLoad"/> + <click selector="{{AdminNewWidgetSection.selectAll}}" stepKey="clickSelectAll"/> + <click selector="{{AdminNewWidgetSection.applyParameter}}" stepKey="clickApplyRuleParameter"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveWidget"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="The widget instance has been saved" stepKey="seeSuccess"/> </actionGroup> <actionGroup name="AdminDeleteWidgetActionGroup"> - <arguments> - <argument name="widget"/> - </arguments> - <amOnPage url="{{AdminWidgetsPage.url}}" stepKey="amOnAdmin"/> - <waitForPageLoad stepKey="waitWidgetsLoad"/> - <fillField selector="{{AdminWidgetsSection.widgetTitleSearch}}" userInput="{{widget.name}}" stepKey="fillTitle"/> - <click selector="{{AdminWidgetsSection.searchButton}}" stepKey="clickContinue"/> - <click selector="{{AdminWidgetsSection.searchResult}}" stepKey="clickSearchResult"/> - <waitForPageLoad stepKey="waitForResultLoad"/> - <click selector="{{AdminMainActionsSection.delete}}" stepKey="clickDelete"/> - <waitForAjaxLoad stepKey="waitForAjaxLoad"/> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDelete"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="The widget instance has been deleted" stepKey="seeSuccess"/> + <arguments> + <argument name="widget"/> + </arguments> + <amOnPage url="{{AdminWidgetsPage.url}}" stepKey="amOnAdmin"/> + <fillField selector="{{AdminWidgetsSection.widgetTitleSearch}}" userInput="{{widget.name}}" stepKey="fillTitle"/> + <click selector="{{AdminWidgetsSection.searchButton}}" stepKey="clickContinue"/> + <click selector="{{AdminWidgetsSection.searchResult}}" stepKey="clickSearchResult"/> + <waitForPageLoad stepKey="waitForResultLoad"/> + <click selector="{{AdminMainActionsSection.delete}}" stepKey="clickDelete"/> + <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForAjaxLoad"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDelete"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="The widget instance has been deleted" stepKey="seeSuccess"/> + </actionGroup> + <actionGroup name="AdminCreateProductLinkWidgetActionGroup" extends="AdminCreateWidgetActionGroup"> + <arguments> + <argument name="product"/> + </arguments> + <selectOption selector="{{AdminNewWidgetSection.selectTemplate}}" userInput="{{widget.template}}" after="waitForPageLoad" stepKey="setTemplate"/> + <waitForAjaxLoad after="setTemplate" stepKey="waitForPageLoad2"/> + <click selector="{{AdminNewWidgetSection.selectProduct}}" after="clickWidgetOptions" stepKey="clickSelectProduct"/> + <fillField selector="{{AdminNewWidgetSelectProductPopupSection.filterBySku}}" userInput="{{product.sku}}" after="clickSelectProduct" stepKey="fillProductNameInFilter"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" after="fillProductNameInFilter" stepKey="applyFilter"/> + <click selector="{{AdminNewWidgetSelectProductPopupSection.firstRow}}" after="applyFilter" stepKey="selectProduct"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveWidget"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="The widget instance has been saved" stepKey="seeSuccess"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Widget/Test/Mftf/Page/AdminNewWidgetPage.xml b/app/code/Magento/Widget/Test/Mftf/Page/AdminNewWidgetPage.xml index 8eb0a5f65318e..5946bb81d9485 100644 --- a/app/code/Magento/Widget/Test/Mftf/Page/AdminNewWidgetPage.xml +++ b/app/code/Magento/Widget/Test/Mftf/Page/AdminNewWidgetPage.xml @@ -7,8 +7,8 @@ --> <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="AdminNewWidgetPage" url="admin/admin/widget_instance/new/" area="admin" module="Magento_Widget"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminNewWidgetPage" url="admin/widget_instance/new/" area="admin" module="Magento_Widget"> <section name="AdminNewWidgetSection"/> </page> </pages> diff --git a/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml b/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml index adf234baede72..57f07d9795b68 100644 --- a/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml +++ b/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml @@ -7,7 +7,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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminNewWidgetSection"> <element name="widgetType" type="select" selector="#code"/> <element name="widgetDesignTheme" type="select" selector="#theme_id"/> @@ -17,6 +17,7 @@ <element name="addLayoutUpdate" type="button" selector=".action-default.scalable.action-add"/> <element name="selectDisplayOn" type="select" selector="#widget_instance[0][page_group]"/> <element name="selectContainer" type="select" selector="#all_pages_0>table>tbody>tr>td:nth-child(1)>div>div>select"/> + <element name="selectTemplate" type="select" selector=".widget-layout-updates .block_template_container .select"/> <element name="widgetOptions" type="select" selector="#widget_instace_tabs_properties_section"/> <element name="addNewCondition" type="select" selector=".rule-param.rule-param-new-child"/> <element name="selectCondition" type="input" selector="#conditions__1__new_child"/> From 49d09cf331cd1ec6644b19ee7666ccfd87500c38 Mon Sep 17 00:00:00 2001 From: NazarKlovanych <nazarn96@gmail.com> Date: Fri, 1 Feb 2019 10:20:50 +0200 Subject: [PATCH 0472/1295] backport-fix-issue-20716 --- .../Framework/Search/Adapter/Mysql/Query/Builder/Match.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/Builder/Match.php b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/Builder/Match.php index 28e321d4c5d47..ffd5b41f7933d 100644 --- a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/Builder/Match.php +++ b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/Builder/Match.php @@ -24,7 +24,7 @@ class Match implements QueryInterface /** * @var string */ - const SPECIAL_CHARACTERS = '+~/\\<>\'":*$#@()!,.?`=%&^'; + const SPECIAL_CHARACTERS = '-+~/\\<>\'":*$#@()!,.?`=%&^'; const MINIMAL_CHARACTER_LENGTH = 3; From 9f1970045ec04d78fe2c999aeb8707f12a0ab1ba Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Fri, 1 Feb 2019 11:37:18 +0200 Subject: [PATCH 0473/1295] MAGETWO-97872: File Read Configuration --- app/bootstrap.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/bootstrap.php b/app/bootstrap.php index 8e901cac9bfb8..4a923cd0c910b 100644 --- a/app/bootstrap.php +++ b/app/bootstrap.php @@ -8,6 +8,7 @@ * Environment initialization */ error_reporting(E_ALL); +stream_wrapper_unregister('phar'); #ini_set('display_errors', 1); /* PHP version validation */ From d49964a5598c79445c0dc53aef7bfd0a1c67b1b1 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Fri, 1 Feb 2019 12:42:16 +0200 Subject: [PATCH 0474/1295] MAGETWO-88920: Updated functional acceptance tests --- .../Test/Mftf/Section/StorefrontCustomerOrdersGridSection.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrdersGridSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrdersGridSection.xml index de3975fd82d01..419387fd92d1f 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrdersGridSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrdersGridSection.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> <!-- /** * Copyright © Magento, Inc. All rights reserved. From 82dd38537149a172534f8b79c095d1eeaa3175ca Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Fri, 1 Feb 2019 14:06:17 +0200 Subject: [PATCH 0475/1295] MAGETWO-87866: Password reset email cannot be sent if the customer does not have customer attribute set that is changed to required after original account creation --- .../Customer/Controller/Account/EditPost.php | 40 +- .../Controller/Adminhtml/Index/InlineEdit.php | 32 +- .../Adminhtml/Index/MassAssignGroup.php | 18 +- .../Controller/Adminhtml/Index/Save.php | 123 ++++++- .../Customer/Model/AccountManagement.php | 29 +- .../Customer/Model/ResourceModel/Address.php | 12 +- .../Customer/Model/ResourceModel/Customer.php | 8 +- .../Model/ResourceModel/Customer/Relation.php | 66 ++-- .../ResourceModel/CustomerRepository.php | 22 +- .../UpgradeCustomerPasswordObserver.php | 19 +- .../Unit/Controller/Account/EditPostTest.php | 15 +- .../Adminhtml/Index/InlineEditTest.php | 55 ++- .../Adminhtml/Index/MassAssignGroupTest.php | 263 +++++++++++++ .../Controller/Adminhtml/Index/SaveTest.php | 8 + .../Test/Unit/Model/AccountManagementTest.php | 347 ++++++++++++++++++ .../UpgradeCustomerPasswordObserverTest.php | 14 +- .../Newsletter/Controller/Manage/Save.php | 19 +- 17 files changed, 1022 insertions(+), 68 deletions(-) create mode 100644 app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php create mode 100644 app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php diff --git a/app/code/Magento/Customer/Controller/Account/EditPost.php b/app/code/Magento/Customer/Controller/Account/EditPost.php index da0ad29c5c72f..4d9ec962c292d 100644 --- a/app/code/Magento/Customer/Controller/Account/EditPost.php +++ b/app/code/Magento/Customer/Controller/Account/EditPost.php @@ -6,6 +6,8 @@ */ namespace Magento\Customer\Controller\Account; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Model\AddressRegistry; use Magento\Customer\Model\AuthenticationInterface; use Magento\Customer\Model\Customer\Mapper; use Magento\Customer\Model\EmailNotificationInterface; @@ -23,7 +25,8 @@ use Magento\Framework\Escaper; /** - * Class EditPost + * Class to editing post. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class EditPost extends \Magento\Customer\Controller\AbstractAccount @@ -73,9 +76,16 @@ class EditPost extends \Magento\Customer\Controller\AbstractAccount */ private $customerMapper; - /** @var Escaper */ + /** + * @var Escaper + */ private $escaper; + /** + * @var AddressRegistry + */ + private $addressRegistry; + /** * @param Context $context * @param Session $customerSession @@ -84,6 +94,7 @@ class EditPost extends \Magento\Customer\Controller\AbstractAccount * @param Validator $formKeyValidator * @param CustomerExtractor $customerExtractor * @param Escaper|null $escaper + * @param AddressRegistry|null $addressRegistry */ public function __construct( Context $context, @@ -92,7 +103,8 @@ public function __construct( CustomerRepositoryInterface $customerRepository, Validator $formKeyValidator, CustomerExtractor $customerExtractor, - Escaper $escaper = null + Escaper $escaper = null, + AddressRegistry $addressRegistry = null ) { parent::__construct($context); $this->session = $customerSession; @@ -101,6 +113,7 @@ public function __construct( $this->formKeyValidator = $formKeyValidator; $this->customerExtractor = $customerExtractor; $this->escaper = $escaper ?: ObjectManager::getInstance()->get(Escaper::class); + $this->addressRegistry = $addressRegistry ?: ObjectManager::getInstance()->get(AddressRegistry::class); } /** @@ -138,7 +151,7 @@ private function getEmailNotification() } /** - * Change customer email or password action + * Change customer email or password action. * * @return \Magento\Framework\Controller\Result\Redirect */ @@ -162,6 +175,9 @@ public function execute() // whether a customer enabled change password option $isPasswordChanged = $this->changeCustomerPassword($currentCustomerDataObject->getEmail()); + // No need to validate customer address while editing customer profile + $this->disableAddressValidation($customerCandidateDataObject); + $this->customerRepository->save($customerCandidateDataObject); $this->getEmailNotification()->credentialsChanged( $customerCandidateDataObject, @@ -170,6 +186,7 @@ public function execute() ); $this->dispatchSuccessEvent($customerCandidateDataObject); $this->messageManager->addSuccess(__('You saved the account information.')); + return $resultRedirect->setPath('customer/account'); } catch (InvalidEmailOrPasswordException $e) { $this->messageManager->addError($e->getMessage()); @@ -180,6 +197,7 @@ public function execute() $this->session->logout(); $this->session->start(); $this->messageManager->addError($message); + return $resultRedirect->setPath('customer/account/login'); } catch (InputException $e) { $this->messageManager->addErrorMessage($this->escaper->escapeHtml($e->getMessage())); @@ -313,4 +331,18 @@ private function getCustomerMapper() } return $this->customerMapper; } + + /** + * Disable Customer Address Validation. + * + * @param CustomerInterface $customer + * @return void + */ + private function disableAddressValidation(CustomerInterface $customer) + { + foreach ($customer->getAddresses() as $address) { + $addressModel = $this->addressRegistry->retrieve($address->getId()); + $addressModel->setShouldIgnoreValidation(true); + } + } } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php index 6753a48d02d6a..1b5160ef31185 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php @@ -8,12 +8,14 @@ use Magento\Backend\App\Action; use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Model\AddressRegistry; use Magento\Customer\Model\EmailNotificationInterface; use Magento\Customer\Ui\Component\Listing\AttributeRepository; use Magento\Framework\Message\MessageInterface; +use Magento\Framework\App\ObjectManager; /** - * Customer inline edit action + * Customer inline edit action. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -61,6 +63,11 @@ class InlineEdit extends \Magento\Backend\App\Action */ private $emailNotification; + /** + * @var AddressRegistry + */ + private $addressRegistry; + /** * @param Action\Context $context * @param CustomerRepositoryInterface $customerRepository @@ -68,6 +75,7 @@ class InlineEdit extends \Magento\Backend\App\Action * @param \Magento\Customer\Model\Customer\Mapper $customerMapper * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper * @param \Psr\Log\LoggerInterface $logger + * @param AddressRegistry|null $addressRegistry */ public function __construct( Action\Context $context, @@ -75,13 +83,15 @@ public function __construct( \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory, \Magento\Customer\Model\Customer\Mapper $customerMapper, \Magento\Framework\Api\DataObjectHelper $dataObjectHelper, - \Psr\Log\LoggerInterface $logger + \Psr\Log\LoggerInterface $logger, + AddressRegistry $addressRegistry = null ) { $this->customerRepository = $customerRepository; $this->resultJsonFactory = $resultJsonFactory; $this->customerMapper = $customerMapper; $this->dataObjectHelper = $dataObjectHelper; $this->logger = $logger; + $this->addressRegistry = $addressRegistry ?: ObjectManager::getInstance()->get(AddressRegistry::class); parent::__construct($context); } @@ -210,7 +220,7 @@ protected function updateDefaultBilling(array $data) } /** - * Save customer with error catching + * Save customer with error catching. * * @param CustomerInterface $customer * @return void @@ -218,6 +228,8 @@ protected function updateDefaultBilling(array $data) protected function saveCustomer(CustomerInterface $customer) { try { + // No need to validate customer address during inline edit action + $this->disableAddressValidation($customer); $this->customerRepository->save($customer); } catch (\Magento\Framework\Exception\InputException $e) { $this->getMessageManager()->addError($this->getErrorWithCustomerId($e->getMessage())); @@ -303,4 +315,18 @@ protected function getErrorWithCustomerId($errorText) { return '[Customer ID: ' . $this->getCustomer()->getId() . '] ' . __($errorText); } + + /** + * Disable Customer Address Validation. + * + * @param CustomerInterface $customer + * @return void + */ + private function disableAddressValidation(CustomerInterface $customer) + { + foreach ($customer->getAddresses() as $address) { + $addressModel = $this->addressRegistry->retrieve($address->getId()); + $addressModel->setShouldIgnoreValidation(true); + } + } } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroup.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroup.php index 762b872b97b6d..49a51052beb90 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroup.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroup.php @@ -5,6 +5,7 @@ */ namespace Magento\Customer\Controller\Adminhtml\Index; +use Magento\Customer\Api\Data\CustomerInterface; use Magento\Backend\App\Action\Context; use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory; use Magento\Eav\Model\Entity\Collection\AbstractCollection; @@ -13,7 +14,7 @@ use Magento\Framework\Controller\ResultFactory; /** - * Class MassAssignGroup + * Class to execute MassAssignGroup action. */ class MassAssignGroup extends AbstractMassAction { @@ -39,7 +40,7 @@ public function __construct( } /** - * Customer mass assign group action + * Customer mass assign group action. * * @param AbstractCollection $collection * @return \Magento\Backend\Model\View\Result\Redirect @@ -51,6 +52,8 @@ protected function massAction(AbstractCollection $collection) // Verify customer exists $customer = $this->customerRepository->getById($customerId); $customer->setGroupId($this->getRequest()->getParam('group')); + // No need to validate customer and customer address during assigning customer to the group + $this->setIgnoreValidationFlag($customer); $this->customerRepository->save($customer); $customersUpdated++; } @@ -64,4 +67,15 @@ protected function massAction(AbstractCollection $collection) return $resultRedirect; } + + /** + * Set ignore_validation_flag to skip unnecessary address and customer validation. + * + * @param CustomerInterface $customer + * @return void + */ + private function setIgnoreValidationFlag(CustomerInterface $customer) + { + $customer->setData('ignore_validation_flag', true); + } } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php index 12732f81f78a0..3a03e9064a0a3 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php @@ -5,6 +5,15 @@ */ namespace Magento\Customer\Controller\Adminhtml\Index; +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Model\Address\Mapper; +use Magento\Customer\Model\AddressRegistry; +use Magento\Framework\Api\DataObjectHelper; +use Magento\Customer\Api\Data\AddressInterfaceFactory; +use Magento\Customer\Api\Data\CustomerInterfaceFactory; +use Magento\Framework\DataObjectFactory as ObjectFactory; use Magento\Customer\Api\AddressMetadataInterface; use Magento\Customer\Api\CustomerMetadataInterface; use Magento\Customer\Api\Data\CustomerInterface; @@ -12,8 +21,11 @@ use Magento\Customer\Model\EmailNotificationInterface; use Magento\Customer\Model\Metadata\Form; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\App\ObjectManager; /** + * Class to Save customer. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Save extends \Magento\Customer\Controller\Adminhtml\Index @@ -23,6 +35,98 @@ class Save extends \Magento\Customer\Controller\Adminhtml\Index */ private $emailNotification; + /** + * @var AddressRegistry + */ + private $addressRegistry; + + /** + * @param \Magento\Backend\App\Action\Context $context + * @param \Magento\Framework\Registry $coreRegistry + * @param \Magento\Framework\App\Response\Http\FileFactory $fileFactory + * @param \Magento\Customer\Model\CustomerFactory $customerFactory + * @param \Magento\Customer\Model\AddressFactory $addressFactory + * @param \Magento\Customer\Model\Metadata\FormFactory $formFactory + * @param \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory + * @param \Magento\Customer\Helper\View $viewHelper + * @param \Magento\Framework\Math\Random $random + * @param CustomerRepositoryInterface $customerRepository + * @param \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter + * @param Mapper $addressMapper + * @param AccountManagementInterface $customerAccountManagement + * @param AddressRepositoryInterface $addressRepository + * @param CustomerInterfaceFactory $customerDataFactory + * @param AddressInterfaceFactory $addressDataFactory + * @param \Magento\Customer\Model\Customer\Mapper $customerMapper + * @param \Magento\Framework\Reflection\DataObjectProcessor $dataObjectProcessor + * @param DataObjectHelper $dataObjectHelper + * @param ObjectFactory $objectFactory + * @param \Magento\Framework\View\LayoutFactory $layoutFactory + * @param \Magento\Framework\View\Result\LayoutFactory $resultLayoutFactory + * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory + * @param \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory + * @param \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory + * @param AddressRegistry|null $addressRegistry + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + */ + public function __construct( + \Magento\Backend\App\Action\Context $context, + \Magento\Framework\Registry $coreRegistry, + \Magento\Framework\App\Response\Http\FileFactory $fileFactory, + \Magento\Customer\Model\CustomerFactory $customerFactory, + \Magento\Customer\Model\AddressFactory $addressFactory, + \Magento\Customer\Model\Metadata\FormFactory $formFactory, + \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory, + \Magento\Customer\Helper\View $viewHelper, + \Magento\Framework\Math\Random $random, + CustomerRepositoryInterface $customerRepository, + \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter, + Mapper $addressMapper, + AccountManagementInterface $customerAccountManagement, + AddressRepositoryInterface $addressRepository, + CustomerInterfaceFactory $customerDataFactory, + AddressInterfaceFactory $addressDataFactory, + \Magento\Customer\Model\Customer\Mapper $customerMapper, + \Magento\Framework\Reflection\DataObjectProcessor $dataObjectProcessor, + DataObjectHelper $dataObjectHelper, + ObjectFactory $objectFactory, + \Magento\Framework\View\LayoutFactory $layoutFactory, + \Magento\Framework\View\Result\LayoutFactory $resultLayoutFactory, + \Magento\Framework\View\Result\PageFactory $resultPageFactory, + \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory, + \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory, + AddressRegistry $addressRegistry = null + ) { + parent::__construct( + $context, + $coreRegistry, + $fileFactory, + $customerFactory, + $addressFactory, + $formFactory, + $subscriberFactory, + $viewHelper, + $random, + $customerRepository, + $extensibleDataObjectConverter, + $addressMapper, + $customerAccountManagement, + $addressRepository, + $customerDataFactory, + $addressDataFactory, + $customerMapper, + $dataObjectProcessor, + $dataObjectHelper, + $objectFactory, + $layoutFactory, + $resultLayoutFactory, + $resultPageFactory, + $resultForwardFactory, + $resultJsonFactory + ); + $this->addressRegistry = $addressRegistry ?: ObjectManager::getInstance()->get(AddressRegistry::class); + } + /** * Reformat customer account data to be compatible with customer service interface * @@ -169,7 +273,7 @@ protected function _extractCustomerAddressData(array & $extractedCustomerData) } /** - * Save customer action + * Save customer action. * * @return \Magento\Backend\Model\View\Result\Redirect * @SuppressWarnings(PHPMD.CyclomaticComplexity) @@ -191,6 +295,8 @@ public function execute() if ($customerId) { $currentCustomer = $this->_customerRepository->getById($customerId); + // No need to validate customer address while editing customer profile + $this->disableAddressValidation($currentCustomer); $customerData = array_merge( $this->customerMapper->toFlatArray($currentCustomer), $customerData @@ -306,6 +412,7 @@ public function execute() } else { $resultRedirect->setPath('customer/index'); } + return $resultRedirect; } @@ -380,4 +487,18 @@ private function getCurrentCustomerId() return $customerId; } + + /** + * Disable Customer Address Validation + * + * @param CustomerInterface $customer + * @return void + */ + private function disableAddressValidation(CustomerInterface $customer) + { + foreach ($customer->getAddresses() as $address) { + $addressModel = $this->addressRegistry->retrieve($address->getId()); + $addressModel->setShouldIgnoreValidation(true); + } + } } diff --git a/app/code/Magento/Customer/Model/AccountManagement.php b/app/code/Magento/Customer/Model/AccountManagement.php index 6387555385e99..e837517762473 100644 --- a/app/code/Magento/Customer/Model/AccountManagement.php +++ b/app/code/Magento/Customer/Model/AccountManagement.php @@ -16,6 +16,7 @@ use Magento\Customer\Model\Config\Share as ConfigShare; use Magento\Customer\Model\Customer as CustomerModel; use Magento\Customer\Model\Customer\CredentialsValidator; +use Magento\Customer\Model\Data\Customer; use Magento\Customer\Model\Metadata\Validator; use Magento\Customer\Model\ResourceModel\Visitor\CollectionFactory; use Magento\Eav\Model\Validator\Attribute\Backend; @@ -54,11 +55,12 @@ use Psr\Log\LoggerInterface as PsrLogger; /** - * Handle various customer account actions + * Handle various customer account actions. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class AccountManagement implements AccountManagementInterface { @@ -526,6 +528,8 @@ private function activateCustomer($customer, $confirmationKey) } $customer->setConfirmation(null); + // No need to validate customer and customer address while activating customer + $this->setIgnoreValidationFlag($customer); $this->customerRepository->save($customer); $this->getEmailNotification()->newAccount($customer, 'confirmed', '', $this->storeManager->getStore()->getId()); return $customer; @@ -673,8 +677,9 @@ public function resetPassword($email, $resetToken, $newPassword) $customer = $this->customerRepository->get($email); } - // No need to validate customer address while saving customer reset password token + // No need to validate customer and customer address while saving customer reset password token $this->disableAddressValidation($customer); + $this->setIgnoreValidationFlag($customer); //Validate Token and new password strength $this->validateResetPasswordToken($customer->getId(), $resetToken); @@ -981,7 +986,7 @@ public function changePasswordById($customerId, $currentPassword, $newPassword) } /** - * Change customer password + * Change customer password. * * @param CustomerInterface $customer * @param string $currentPassword @@ -1009,6 +1014,7 @@ private function changePasswordForCustomer($customer, $currentPassword, $newPass $this->checkPasswordStrength($newPassword); $customerSecure->setPasswordHash($this->createPasswordHash($newPassword)); $this->destroyCustomerSessions($customer->getId()); + $this->disableAddressValidation($customer); $this->customerRepository->save($customer); return true; @@ -1357,9 +1363,7 @@ public function isResetPasswordLinkTokenExpired($rpToken, $rpTokenCreatedAt) } /** - * Change reset password link token - * - * Stores new reset password link token + * Set a new reset password link token. * * @param CustomerInterface $customer * @param string $passwordLinkToken @@ -1385,8 +1389,10 @@ public function changeResetPasswordLinkToken($customer, $passwordLinkToken) $customerSecure->setRpTokenCreatedAt( $this->dateTimeFactory->create()->format(DateTime::DATETIME_PHP_FORMAT) ); + $this->setIgnoreValidationFlag($customer); $this->customerRepository->save($customer); } + return true; } @@ -1555,4 +1561,15 @@ private function destroyCustomerSessions($customerId) $this->saveHandler->destroy($sessionId); } } + + /** + * Set ignore_validation_flag for reset password flow to skip unnecessary address and customer validation. + * + * @param Customer $customer + * @return void + */ + private function setIgnoreValidationFlag(Customer $customer) + { + $customer->setData('ignore_validation_flag', true); + } } diff --git a/app/code/Magento/Customer/Model/ResourceModel/Address.php b/app/code/Magento/Customer/Model/ResourceModel/Address.php index a52c372310843..8b5c9a08931b5 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Address.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Address.php @@ -13,7 +13,8 @@ use Magento\Framework\App\ObjectManager; /** - * Class Address + * Customer Address resource model. + * * @package Magento\Customer\Model\ResourceModel * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -31,8 +32,8 @@ class Address extends \Magento\Eav\Model\Entity\VersionControl\AbstractEntity /** * @param \Magento\Eav\Model\Entity\Context $context - * @param \Magento\Framework\Model\ResourceModel\Db\VersionControl\Snapshot $entitySnapshot, - * @param \Magento\Framework\Model\ResourceModel\Db\VersionControl\RelationComposite $entityRelationComposite, + * @param \Magento\Framework\Model\ResourceModel\Db\VersionControl\Snapshot $entitySnapshot + * @param \Magento\Framework\Model\ResourceModel\Db\VersionControl\RelationComposite $entityRelationComposite * @param \Magento\Framework\Validator\Factory $validatorFactory * @param \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository * @param array $data @@ -90,7 +91,7 @@ protected function _beforeSave(\Magento\Framework\DataObject $address) } /** - * Validate customer address entity + * Validate customer address entity. * * @param \Magento\Framework\DataObject $address * @return void @@ -98,6 +99,9 @@ protected function _beforeSave(\Magento\Framework\DataObject $address) */ protected function _validate($address) { + if ($address->getDataByKey('should_ignore_validation')) { + return; + }; $validator = $this->_validatorFactory->createValidator('customer_address', 'save'); if (!$validator->isValid($address)) { diff --git a/app/code/Magento/Customer/Model/ResourceModel/Customer.php b/app/code/Magento/Customer/Model/ResourceModel/Customer.php index 7e5f9d51549ec..daacb2655f588 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Customer.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Customer.php @@ -11,7 +11,7 @@ use Magento\Framework\Exception\AlreadyExistsException; /** - * Customer entity resource model + * Customer entity resource model. * * @api * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -92,7 +92,7 @@ protected function _getDefaultAttributes() } /** - * Check customer scope, email and confirmation key before saving + * Check customer scope, email and confirmation key before saving. * * @param \Magento\Framework\DataObject $customer * @return $this @@ -150,7 +150,9 @@ protected function _beforeSave(\Magento\Framework\DataObject $customer) $customer->setConfirmation(null); } - $this->_validate($customer); + if (!$customer->getData('ignore_validation_flag')) { + $this->_validate($customer); + } return $this; } diff --git a/app/code/Magento/Customer/Model/ResourceModel/Customer/Relation.php b/app/code/Magento/Customer/Model/ResourceModel/Customer/Relation.php index e55c5d443c9d1..d55a5c0aea2be 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Customer/Relation.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Customer/Relation.php @@ -7,12 +7,12 @@ namespace Magento\Customer\Model\ResourceModel\Customer; /** - * Class Relation + * Class to process object relations. */ class Relation implements \Magento\Framework\Model\ResourceModel\Db\VersionControl\RelationInterface { /** - * Save relations for Customer + * Save relations for Customer. * * @param \Magento\Framework\Model\AbstractModel $customer * @return void @@ -23,41 +23,43 @@ public function processRelation(\Magento\Framework\Model\AbstractModel $customer $defaultBillingId = $customer->getData('default_billing'); $defaultShippingId = $customer->getData('default_shipping'); - /** @var \Magento\Customer\Model\Address $address */ - foreach ($customer->getAddresses() as $address) { - if ($address->getData('_deleted')) { - if ($address->getId() == $defaultBillingId) { - $customer->setData('default_billing', null); - } + if (!$customer->getData('ignore_validation_flag')) { + /** @var \Magento\Customer\Model\Address $address */ + foreach ($customer->getAddresses() as $address) { + if ($address->getData('_deleted')) { + if ($address->getId() == $defaultBillingId) { + $customer->setData('default_billing', null); + } - if ($address->getId() == $defaultShippingId) { - $customer->setData('default_shipping', null); - } + if ($address->getId() == $defaultShippingId) { + $customer->setData('default_shipping', null); + } - $removedAddressId = $address->getId(); - $address->delete(); + $removedAddressId = $address->getId(); + $address->delete(); - // Remove deleted address from customer address collection - $customer->getAddressesCollection()->removeItemByKey($removedAddressId); - } else { - $address->setParentId( - $customer->getId() - )->setStoreId( - $customer->getStoreId() - )->setIsCustomerSaveTransaction( - true - )->save(); + // Remove deleted address from customer address collection + $customer->getAddressesCollection()->removeItemByKey($removedAddressId); + } else { + $address->setParentId( + $customer->getId() + )->setStoreId( + $customer->getStoreId() + )->setIsCustomerSaveTransaction( + true + )->save(); - if (($address->getIsPrimaryBilling() || - $address->getIsDefaultBilling()) && $address->getId() != $defaultBillingId - ) { - $customer->setData('default_billing', $address->getId()); - } + if (($address->getIsPrimaryBilling() || + $address->getIsDefaultBilling()) && $address->getId() != $defaultBillingId + ) { + $customer->setData('default_billing', $address->getId()); + } - if (($address->getIsPrimaryShipping() || - $address->getIsDefaultShipping()) && $address->getId() != $defaultShippingId - ) { - $customer->setData('default_shipping', $address->getId()); + if (($address->getIsPrimaryShipping() || + $address->getIsDefaultShipping()) && $address->getId() != $defaultShippingId + ) { + $customer->setData('default_shipping', $address->getId()); + } } } } diff --git a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php index d96182202eb7d..d0efdde3d8c59 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php +++ b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php @@ -12,6 +12,7 @@ use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface; use Magento\Customer\Model\CustomerFactory; use Magento\Customer\Model\CustomerRegistry; +use \Magento\Customer\Model\Customer as CustomerModel; use Magento\Customer\Model\Data\CustomerSecureFactory; use Magento\Customer\Model\Customer\NotificationStorage; use Magento\Customer\Model\Delegation\Data\NewOperation; @@ -170,7 +171,8 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc + * * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.ExcessiveMethodLength) @@ -240,7 +242,7 @@ public function save(\Magento\Customer\Api\Data\CustomerInterface $customer, $pa $prevCustomerDataArr['default_shipping'] ); } - + $this->setIgnoreValidationFlag($customerArr, $customerModel); $customerModel->save(); $this->customerRegistry->push($customerModel); $customerId = $customerModel->getId(); @@ -253,7 +255,7 @@ public function save(\Magento\Customer\Api\Data\CustomerInterface $customer, $pa $delegatedNewOperation->getCustomer()->getAddresses() ); } - if ($customer->getAddresses() !== null) { + if ($customer->getAddresses() !== null && !$customerModel->getData('ignore_validation_flag')) { if ($customer->getId()) { $existingAddresses = $this->getById($customer->getId())->getAddresses(); $getIdFunc = function ($address) { @@ -420,4 +422,18 @@ protected function addFilterGroupToCollection(FilterGroup $filterGroup, Collecti $collection->addFieldToFilter($fields); } } + + /** + * Set ignore_validation_flag to skip model validation. + * + * @param array $customerArray + * @param CustomerModel $customerModel + * @return void + */ + private function setIgnoreValidationFlag(array $customerArray, CustomerModel $customerModel) + { + if (isset($customerArray['ignore_validation_flag'])) { + $customerModel->setData('ignore_validation_flag', true); + } + } } diff --git a/app/code/Magento/Customer/Observer/UpgradeCustomerPasswordObserver.php b/app/code/Magento/Customer/Observer/UpgradeCustomerPasswordObserver.php index eb7e81009c92c..831506af17cf6 100644 --- a/app/code/Magento/Customer/Observer/UpgradeCustomerPasswordObserver.php +++ b/app/code/Magento/Customer/Observer/UpgradeCustomerPasswordObserver.php @@ -6,11 +6,15 @@ namespace Magento\Customer\Observer; +use Magento\Customer\Api\Data\CustomerInterface; use Magento\Framework\Encryption\EncryptorInterface; use Magento\Framework\Event\ObserverInterface; use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Model\CustomerRegistry; +/** + * Observer to execute upgrading customer password hash when customer has logged in. + */ class UpgradeCustomerPasswordObserver implements ObserverInterface { /** @@ -46,7 +50,7 @@ public function __construct( } /** - * Upgrade customer password hash when customer has logged in + * Upgrade customer password hash when customer has logged in. * * @param \Magento\Framework\Event\Observer $observer * @return void @@ -61,7 +65,20 @@ public function execute(\Magento\Framework\Event\Observer $observer) if (!$this->encryptor->validateHashVersion($customerSecure->getPasswordHash(), true)) { $customerSecure->setPasswordHash($this->encryptor->getHash($password, true)); + // No need to validate customer and customer address while upgrading customer password + $this->setIgnoreValidationFlag($customer); $this->customerRepository->save($customer); } } + + /** + * Set ignore_validation_flag to skip unnecessary address and customer validation. + * + * @param CustomerInterface $customer + * @return void + */ + private function setIgnoreValidationFlag(CustomerInterface $customer) + { + $customer->setData('ignore_validation_flag', true); + } } diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Account/EditPostTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Account/EditPostTest.php index f2860725dbbae..0e8cffc0bf434 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Account/EditPostTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Account/EditPostTest.php @@ -19,6 +19,8 @@ use Magento\Framework\Message\ManagerInterface; /** + * Unit tests for Magento\Customer\Controller\Account\EditPost. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class EditPostTest extends \PHPUnit\Framework\TestCase @@ -98,6 +100,9 @@ class EditPostTest extends \PHPUnit\Framework\TestCase */ private $customerMapperMock; + /** + * @inheritdoc + */ protected function setUp() { $this->prepareContext(); @@ -707,6 +712,8 @@ protected function prepareContext() } /** + * Executes methods needed for new Customer. + * * @param int $customerId * @param \PHPUnit_Framework_MockObject_MockObject $address * @return \PHPUnit_Framework_MockObject_MockObject @@ -720,9 +727,9 @@ protected function getNewCustomerMock($customerId, $address) ->method('setId') ->with($customerId) ->willReturnSelf(); - $newCustomerMock->expects($this->once()) + $newCustomerMock->expects($this->atLeastOnce()) ->method('getAddresses') - ->willReturn(null); + ->willReturn([]); $newCustomerMock->expects($this->once()) ->method('setAddresses') ->with([$address]) @@ -732,6 +739,8 @@ protected function getNewCustomerMock($customerId, $address) } /** + * Executes methods needed for existing Customer. + * * @param int $customerId * @param \PHPUnit_Framework_MockObject_MockObject $address * @return \PHPUnit_Framework_MockObject_MockObject @@ -741,7 +750,7 @@ protected function getCurrentCustomerMock($customerId, $address) $currentCustomerMock = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) ->getMockForAbstractClass(); - $currentCustomerMock->expects($this->once()) + $currentCustomerMock->expects($this->atLeastOnce()) ->method('getAddresses') ->willReturn([$address]); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php index 78d9dd7003522..fe7868ef3eb3f 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php @@ -5,10 +5,14 @@ */ namespace Magento\Customer\Test\Unit\Controller\Adminhtml\Index; +use Magento\Customer\Model\AddressRegistry; use Magento\Customer\Model\EmailNotificationInterface; +use Magento\Framework\DataObject; use Magento\Framework\Message\MessageInterface; /** + * Unit tests for Inline customer edit. + * * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -68,14 +72,27 @@ class InlineEditTest extends \PHPUnit\Framework\TestCase /** @var EmailNotificationInterface|\PHPUnit_Framework_MockObject_MockObject */ private $emailNotification; + /** @var AddressRegistry|\PHPUnit_Framework_MockObject_MockObject */ + private $addressRegistry; + /** @var array */ private $items; + /** + * @inheritdoc + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->request = $this->getMockForAbstractClass(\Magento\Framework\App\RequestInterface::class, [], '', false); + $this->request = $this->getMockForAbstractClass( + \Magento\Framework\App\RequestInterface::class, + [], + '', + false + ); $this->messageManager = $this->getMockForAbstractClass( \Magento\Framework\Message\ManagerInterface::class, [], @@ -125,8 +142,12 @@ protected function setUp() '', false ); - $this->logger = $this->getMockForAbstractClass(\Psr\Log\LoggerInterface::class, [], '', false); - + $this->logger = $this->getMockForAbstractClass( + \Psr\Log\LoggerInterface::class, + [], + '', + false + ); $this->emailNotification = $this->getMockBuilder(EmailNotificationInterface::class) ->disableOriginalConstructor() ->getMock(); @@ -138,6 +159,7 @@ protected function setUp() 'messageManager' => $this->messageManager, ] ); + $this->addressRegistry = $this->createMock(\Magento\Customer\Model\AddressRegistry::class); $this->controller = $objectManager->getObject( \Magento\Customer\Controller\Adminhtml\Index\InlineEdit::class, [ @@ -150,6 +172,7 @@ protected function setUp() 'addressDataFactory' => $this->addressDataFactory, 'addressRepository' => $this->addressRepository, 'logger' => $this->logger, + 'addressRegistry' => $this->addressRegistry, ] ); $reflection = new \ReflectionClass(get_class($this->controller)); @@ -204,6 +227,11 @@ protected function prepareMocksForTesting($populateSequence = 0) ->willReturn(12); } + /** + * Prepare mocks for update customers default billing address use case. + * + * @return void + */ protected function prepareMocksForUpdateDefaultBilling() { $this->prepareMocksForProcessAddressData(); @@ -212,12 +240,15 @@ protected function prepareMocksForUpdateDefaultBilling() 'firstname' => 'Firstname', 'lastname' => 'Lastname', ]; - $this->customerData->expects($this->once()) + $this->customerData->expects($this->exactly(2)) ->method('getAddresses') ->willReturn([$this->address]); $this->address->expects($this->once()) ->method('isDefaultBilling') ->willReturn(true); + $this->addressRegistry->expects($this->once()) + ->method('retrieve') + ->willReturn(new DataObject()); $this->dataObjectHelper->expects($this->at(0)) ->method('populateWithArray') ->with( @@ -305,6 +336,11 @@ public function testExecuteWithoutItems() $this->assertSame($this->resultJson, $this->controller->execute()); } + /** + * Unit test for verifying Localized Exception during inline edit. + * + * @return void + */ public function testExecuteLocalizedException() { $exception = new \Magento\Framework\Exception\LocalizedException(__('Exception message')); @@ -312,6 +348,9 @@ public function testExecuteLocalizedException() $this->customerData->expects($this->once()) ->method('getDefaultBilling') ->willReturn(false); + $this->customerData->expects($this->once()) + ->method('getAddresses') + ->willReturn([]); $this->customerRepository->expects($this->once()) ->method('save') ->with($this->customerData) @@ -327,6 +366,11 @@ public function testExecuteLocalizedException() $this->assertSame($this->resultJson, $this->controller->execute()); } + /** + * Unit test for verifying Execute Exception during inline edit. + * + * @return void + */ public function testExecuteException() { $exception = new \Exception('Exception message'); @@ -334,6 +378,9 @@ public function testExecuteException() $this->customerData->expects($this->once()) ->method('getDefaultBilling') ->willReturn(false); + $this->customerData->expects($this->once()) + ->method('getAddresses') + ->willReturn([]); $this->customerRepository->expects($this->once()) ->method('save') ->with($this->customerData) diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php new file mode 100644 index 0000000000000..023525d10c52f --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php @@ -0,0 +1,263 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Customer\Test\Unit\Controller\Adminhtml\Index; + +use Magento\Framework\App\Action\Context; +use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory; +use Magento\Customer\Model\ResourceModel\Customer\Collection; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +/** + * Unit tests for Magento\Customer\Controller\Adminhtml\Index\MassAssignGroup. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class MassAssignGroupTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Customer\Controller\Adminhtml\Index\MassAssignGroup + */ + private $massAction; + + /** + * @var Context|\PHPUnit_Framework_MockObject_MockObject + */ + private $contextMock; + + /** + * @var \Magento\Backend\Model\View\Result\Redirect|\PHPUnit_Framework_MockObject_MockObject + */ + private $resultRedirectMock; + + /** + * @var \Magento\Framework\App\Request\Http|\PHPUnit_Framework_MockObject_MockObject + */ + private $requestMock; + + /** + * @var \Magento\Framework\App\ResponseInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $responseMock; + + /** + * @var \Magento\Framework\Message\Manager|\PHPUnit_Framework_MockObject_MockObject + */ + private $messageManagerMock; + + /** + * @var \Magento\Framework\ObjectManager\ObjectManager|\PHPUnit_Framework_MockObject_MockObject + */ + private $objectManagerMock; + + /** + * @var Collection|\PHPUnit_Framework_MockObject_MockObject + */ + private $customerCollectionMock; + + /** + * @var CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $customerCollectionFactoryMock; + + /** + * @var \Magento\Ui\Component\MassAction\Filter|\PHPUnit_Framework_MockObject_MockObject + */ + private $filterMock; + + /** + * @var \Magento\Customer\Api\CustomerRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $customerRepositoryMock; + + /** + * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $requestInterfaceMock; + + /** + * @var \Magento\Framework\Controller\ResultFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $resultFactoryMock; + + /** + * @var \Magento\Backend\Model\View\Result\Redirect|\PHPUnit_Framework_MockObject_MockObject + */ + private $redirectMock; + + /** + * @var \Magento\Backend\Model\View\Result\RedirectFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $resultRedirectFactoryMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManagerHelper = new ObjectManagerHelper($this); + + $this->contextMock = $this->createMock(\Magento\Backend\App\Action\Context::class); + $this->resultRedirectFactoryMock = $this->createMock( + \Magento\Backend\Model\View\Result\RedirectFactory::class + ); + $this->responseMock = $this->createMock(\Magento\Framework\App\ResponseInterface::class); + $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) + ->disableOriginalConstructor() + ->getMock(); + $this->objectManagerMock = $this->createPartialMock( + \Magento\Framework\ObjectManager\ObjectManager::class, + ['create'] + ); + $this->requestInterfaceMock = $this->getMockForAbstractClass( + \Magento\Framework\App\RequestInterface::class, + [], + '', + false, + true, + true, + ['isPost'] + ); + $this->messageManagerMock = $this->createMock(\Magento\Framework\Message\Manager::class); + $this->customerCollectionMock = $this->getMockBuilder(Collection::class) + ->disableOriginalConstructor() + ->getMock(); + $this->customerCollectionFactoryMock = $this->getMockBuilder(CollectionFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->redirectMock = $this->getMockBuilder(\Magento\Backend\Model\View\Result\Redirect::class) + ->disableOriginalConstructor() + ->getMock(); + $this->resultFactoryMock = $this->getMockBuilder(\Magento\Framework\Controller\ResultFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->resultRedirectMock = $this->getMockBuilder(\Magento\Backend\Model\View\Result\Redirect::class) + ->disableOriginalConstructor() + ->getMock(); + $this->filterMock = $this->createMock(\Magento\Ui\Component\MassAction\Filter::class); + + $this->resultRedirectFactoryMock->expects($this->any())->method('create')->willReturn($this->resultRedirectMock); + + $this->contextMock->expects($this->once())->method('getMessageManager')->willReturn($this->messageManagerMock); + $this->contextMock->expects($this->once())->method('getRequest')->willReturn($this->requestMock); + $this->contextMock->expects($this->once())->method('getResponse')->willReturn($this->responseMock); + $this->contextMock->expects($this->once())->method('getObjectManager')->willReturn($this->objectManagerMock); + $this->contextMock->expects($this->once()) + ->method('getResultRedirectFactory') + ->willReturn($this->resultRedirectFactoryMock); + $this->contextMock->expects($this->once()) + ->method('getResultFactory') + ->willReturn($this->resultFactoryMock); + $this->customerRepositoryMock = $this + ->getMockBuilder(\Magento\Customer\Api\CustomerRepositoryInterface::class) + ->getMockForAbstractClass(); + $this->massAction = $objectManagerHelper->getObject( + \Magento\Customer\Controller\Adminhtml\Index\MassAssignGroup::class, + [ + 'context' => $this->contextMock, + 'filter' => $this->filterMock, + 'collectionFactory' => $this->customerCollectionFactoryMock, + 'customerRepository' => $this->customerRepositoryMock, + ] + ); + } + + /** + * Execute Create resultFactory and Create and Get customerCollectionFactory. + * + * @return void + */ + private function expectsCreateAndGetCollectionMethods() + { + $this->resultFactoryMock->expects($this->once()) + ->method('create') + ->with(\Magento\Framework\Controller\ResultFactory::TYPE_REDIRECT) + ->willReturn($this->redirectMock); + $this->customerCollectionFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->customerCollectionMock); + $this->filterMock->expects($this->once()) + ->method('getCollection') + ->with($this->customerCollectionMock) + ->willReturnArgument(0); + } + + /** + * Unit test to verify mass customer group assignment use case. + * + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function testExecute() + { + + $customersIds = [10, 11, 12]; + $customerMock = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) + ->setMethods(['setData']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->expectsCreateAndGetCollectionMethods(); + $this->requestMock->expects($this->once())->method('isPost')->willReturn(true); + $this->customerCollectionMock->expects($this->once()) + ->method('getAllIds') + ->willReturn($customersIds); + + $this->customerRepositoryMock->expects($this->any()) + ->method('getById') + ->willReturnMap([[10, $customerMock], [11, $customerMock], [12, $customerMock]]); + + $this->messageManagerMock->expects($this->once()) + ->method('addSuccess') + ->with(__('A total of %1 record(s) were updated.', count($customersIds))); + + $this->resultRedirectMock->expects($this->any()) + ->method('setPath') + ->with('customer/*/index') + ->willReturnSelf(); + + $this->massAction->execute(); + } + + /** + * Unit test to verify expected error during mass customer group assignment use case. + * + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function testExecuteWithException() + { + $customersIds = [10, 11, 12]; + $this->expectsCreateAndGetCollectionMethods(); + $this->requestMock->expects($this->once())->method('isPost')->willReturn(true); + $this->customerCollectionMock->expects($this->once()) + ->method('getAllIds') + ->willReturn($customersIds); + + $this->customerRepositoryMock->expects($this->once()) + ->method('getById') + ->willThrowException(new \Exception('Some message.')); + + $this->messageManagerMock->expects($this->once()) + ->method('addError') + ->with('Some message.'); + + $this->massAction->execute(); + } + + /** + * Check that error throws when request is not a POST. + * + * @return void + * @expectedException \Magento\Framework\Exception\NotFoundException + */ + public function testExecuteWithNotPostRequest() + { + $this->requestMock->expects($this->once())->method('isPost')->willReturn(false); + + $this->massAction->execute(); + } +} diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php index 5372bb11a89b5..e703e7499d731 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php @@ -15,6 +15,8 @@ use Magento\Framework\Controller\Result\Redirect; /** + * Testing Save Customer use case from admin page. + * * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @covers \Magento\Customer\Controller\Adminhtml\Index\Save @@ -275,6 +277,8 @@ protected function setUp() } /** + * Test for Execute method with existent customer. + * * @covers \Magento\Customer\Controller\Adminhtml\Index\Index::execute * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ @@ -540,6 +544,10 @@ public function testExecuteWithExistentCustomer() $customerEmail = 'customer@email.com'; $customerMock->expects($this->once())->method('getEmail')->willReturn($customerEmail); + $customerMock->expects($this->once()) + ->method('getAddresses') + ->willReturn([]); + $this->emailNotificationMock->expects($this->once()) ->method('credentialsChanged') ->with($customerMock, $customerEmail) diff --git a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php new file mode 100644 index 0000000000000..f5688c1f87481 --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php @@ -0,0 +1,347 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Test\Unit\Model; + +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Model\AccountConfirmation; +use Magento\Customer\Model\AccountManagement; +use Magento\Customer\Model\AuthenticationInterface; +use Magento\Customer\Model\EmailNotificationInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Intl\DateTimeFactory; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +/** + * Unit test for Magento\Customer\Model\AccountManagement. + * + * @SuppressWarnings(PHPMD.TooManyFields) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class AccountManagementTest extends \PHPUnit\Framework\TestCase +{ + /** @var AccountManagement */ + private $accountManagement; + + /** @var ObjectManagerHelper */ + private $objectManagerHelper; + + /** @var \Magento\Customer\Model\CustomerFactory|\PHPUnit_Framework_MockObject_MockObject */ + private $customerFactoryMock; + + /** @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $managerMock; + + /** @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $storeManagerMock; + + /** @var \Magento\Framework\Math\Random|\PHPUnit_Framework_MockObject_MockObject */ + private $randomMock; + + /** @var \Magento\Customer\Model\Metadata\Validator|\PHPUnit_Framework_MockObject_MockObject */ + private $validatorMock; + + /** @var \Magento\Customer\Api\Data\ValidationResultsInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject */ + private $validationResultsInterfaceFactoryMock; + + /** @var \Magento\Customer\Api\AddressRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $addressRepositoryMock; + + /** @var \Magento\Customer\Api\CustomerMetadataInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $customerMetadataMock; + + /** @var \Magento\Customer\Model\CustomerRegistry|\PHPUnit_Framework_MockObject_MockObject */ + private $customerRegistryMock; + + /** @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $loggerMock; + + /** @var \Magento\Framework\Encryption\EncryptorInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $encryptorMock; + + /** @var \Magento\Customer\Model\Config\Share|\PHPUnit_Framework_MockObject_MockObject */ + private $shareMock; + + /** @var \Magento\Framework\Stdlib\StringUtils|\PHPUnit_Framework_MockObject_MockObject */ + private $stringMock; + + /** @var \Magento\Customer\Api\CustomerRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $customerRepositoryMock; + + /** @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $scopeConfigMock; + + /** @var \Magento\Framework\Mail\Template\TransportBuilder|\PHPUnit_Framework_MockObject_MockObject */ + private $transportBuilderMock; + + /** @var \Magento\Framework\Reflection\DataObjectProcessor|\PHPUnit_Framework_MockObject_MockObject */ + private $dataObjectProcessorMock; + + /** @var \Magento\Framework\Registry|\PHPUnit_Framework_MockObject_MockObject */ + private $registryMock; + + /** @var \Magento\Customer\Helper\View|\PHPUnit_Framework_MockObject_MockObject */ + private $customerViewHelperMock; + + /** @var \Magento\Framework\Stdlib\DateTime|\PHPUnit_Framework_MockObject_MockObject */ + private $dateTimeMock; + + /** @var \Magento\Customer\Model\Customer|\PHPUnit_Framework_MockObject_MockObject */ + private $customerMock; + + /** @var \Magento\Framework\DataObjectFactory|\PHPUnit_Framework_MockObject_MockObject */ + private $objectFactoryMock; + + /** @var \Magento\Framework\Api\ExtensibleDataObjectConverter|\PHPUnit_Framework_MockObject_MockObject */ + private $extensibleDataObjectConverterMock; + + /** @var \Magento\Customer\Model\Data\CustomerSecure|\PHPUnit_Framework_MockObject_MockObject */ + private $customerSecureMock; + + /** @var AuthenticationInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $authenticationMock; + + /** @var EmailNotificationInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $emailNotificationMock; + + /** @var DateTimeFactory|\PHPUnit_Framework_MockObject_MockObject */ + private $dateTimeFactoryMock; + + /** @var AccountConfirmation|\PHPUnit_Framework_MockObject_MockObject */ + private $accountConfirmationMock; + + /** @var \Magento\Framework\Session\SessionManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $sessionManagerMock; + + /** @var \Magento\Customer\Model\ResourceModel\Visitor\CollectionFactory|\PHPUnit_Framework_MockObject_MockObject */ + private $visitorCollectionFactoryMock; + + /** @var \Magento\Framework\Session\SaveHandlerInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $saveHandlerMock; + + /** @var \Magento\Customer\Model\AddressRegistry|\PHPUnit_Framework_MockObject_MockObject */ + private $addressRegistryMock; + + /** @var SearchCriteriaBuilder|\PHPUnit_Framework_MockObject_MockObject */ + private $searchCriteriaBuilderMock; + + /** + * @inheritdoc + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + protected function setUp() + { + $this->customerFactoryMock = $this->createPartialMock( + \Magento\Customer\Model\CustomerFactory::class, + ['create'] + ); + $this->managerMock = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); + $this->storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); + $this->randomMock = $this->createMock(\Magento\Framework\Math\Random::class); + $this->validatorMock = $this->createMock(\Magento\Customer\Model\Metadata\Validator::class); + $this->validationResultsInterfaceFactoryMock = $this->createMock( + \Magento\Customer\Api\Data\ValidationResultsInterfaceFactory::class + ); + $this->addressRepositoryMock = $this->createMock(\Magento\Customer\Api\AddressRepositoryInterface::class); + $this->customerMetadataMock = $this->createMock(\Magento\Customer\Api\CustomerMetadataInterface::class); + $this->customerRegistryMock = $this->createMock(\Magento\Customer\Model\CustomerRegistry::class); + $this->loggerMock = $this->createMock(\Psr\Log\LoggerInterface::class); + $this->encryptorMock = $this->createMock(\Magento\Framework\Encryption\EncryptorInterface::class); + $this->shareMock = $this->createMock(\Magento\Customer\Model\Config\Share::class); + $this->stringMock = $this->createMock(\Magento\Framework\Stdlib\StringUtils::class); + $this->customerRepositoryMock = $this->createMock(\Magento\Customer\Api\CustomerRepositoryInterface::class); + $this->scopeConfigMock = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); + $this->transportBuilderMock = $this->createMock(\Magento\Framework\Mail\Template\TransportBuilder::class); + $this->dataObjectProcessorMock = $this->createMock(\Magento\Framework\Reflection\DataObjectProcessor::class); + $this->registryMock = $this->createMock(\Magento\Framework\Registry::class); + $this->customerViewHelperMock = $this->createMock(\Magento\Customer\Helper\View::class); + $this->dateTimeMock = $this->createMock(\Magento\Framework\Stdlib\DateTime::class); + $this->customerMock = $this->createMock(\Magento\Customer\Model\Customer::class); + $this->objectFactoryMock = $this->createMock(\Magento\Framework\DataObjectFactory::class); + $this->addressRegistryMock = $this->createMock(\Magento\Customer\Model\AddressRegistry::class); + $this->extensibleDataObjectConverterMock = $this->createMock( + \Magento\Framework\Api\ExtensibleDataObjectConverter::class + ); + $this->authenticationMock = $this->createMock(AuthenticationInterface::class); + $this->emailNotificationMock = $this->createMock(EmailNotificationInterface::class); + + $this->customerSecureMock = $this->getMockBuilder(\Magento\Customer\Model\Data\CustomerSecure::class) + ->setMethods(['setRpToken', 'addData', 'setRpTokenCreatedAt', 'setData']) + ->disableOriginalConstructor() + ->getMock(); + + $this->accountConfirmationMock = $this->createMock(AccountConfirmation::class); + $this->searchCriteriaBuilderMock = $this->createMock(SearchCriteriaBuilder::class); + + $this->visitorCollectionFactoryMock = $this->getMockBuilder( + \Magento\Customer\Model\ResourceModel\Visitor\CollectionFactory::class + )->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->sessionManagerMock = $this->createMock(\Magento\Framework\Session\SessionManagerInterface::class); + $this->saveHandlerMock = $this->createMock(\Magento\Framework\Session\SaveHandlerInterface::class); + + $this->dateTimeInit(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->accountManagement = $this->objectManagerHelper->getObject( + AccountManagement::class, + [ + 'customerFactory' => $this->customerFactoryMock, + 'eventManager' => $this->managerMock, + 'storeManager' => $this->storeManagerMock, + 'mathRandom' => $this->randomMock, + 'validator' => $this->validatorMock, + 'validationResultsDataFactory' => $this->validationResultsInterfaceFactoryMock, + 'addressRepository' => $this->addressRepositoryMock, + 'customerMetadataService' => $this->customerMetadataMock, + 'customerRegistry' => $this->customerRegistryMock, + 'logger' => $this->loggerMock, + 'encryptor' => $this->encryptorMock, + 'configShare' => $this->shareMock, + 'stringHelper' => $this->stringMock, + 'customerRepository' => $this->customerRepositoryMock, + 'scopeConfig' => $this->scopeConfigMock, + 'transportBuilder' => $this->transportBuilderMock, + 'dataProcessor' => $this->dataObjectProcessorMock, + 'registry' => $this->registryMock, + 'customerViewHelper' => $this->customerViewHelperMock, + 'dateTime' => $this->dateTimeMock, + 'customerModel' => $this->customerMock, + 'objectFactory' => $this->objectFactoryMock, + 'extensibleDataObjectConverter' => $this->extensibleDataObjectConverterMock, + 'dateTimeFactory' => $this->dateTimeFactoryMock, + 'accountConfirmation' => $this->accountConfirmationMock, + 'sessionManager' => $this->sessionManagerMock, + 'saveHandler' => $this->saveHandlerMock, + 'visitorCollectionFactory' => $this->visitorCollectionFactoryMock, + 'searchCriteriaBuilder' => $this->searchCriteriaBuilderMock, + 'addressRegistry' => $this->addressRegistryMock, + ] + ); + } + + /** + * Init DateTimeFactory. + * + * @return void + */ + private function dateTimeInit() + { + $dateTime = '2017-10-25 18:57:08'; + $timestamp = '1508983028'; + $dateTimeMock = $this->getMockBuilder(\DateTime::class) + ->disableOriginalConstructor() + ->setMethods(['format', 'getTimestamp', 'setTimestamp']) + ->getMock(); + + $dateTimeMock->expects($this->once()) + ->method('format') + ->with(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT) + ->willReturn($dateTime); + $dateTimeMock->expects($this->once()) + ->method('getTimestamp') + ->willReturn($timestamp); + $dateTimeMock->expects($this->once()) + ->method('setTimestamp') + ->willReturnSelf(); + $this->dateTimeFactoryMock = $this->getMockBuilder(DateTimeFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->dateTimeFactoryMock->expects($this->once())->method('create')->willReturn($dateTimeMock); + } + + /** + * Test for changePassword method. + * + * @return void + */ + public function testChangePassword() + { + $customerId = 7; + $email = 'test@example.com'; + $currentPassword = '1234567'; + $newPassword = 'abcdefg'; + + $customer = $this->createMock(CustomerInterface::class); + + $customer->expects($this->atLeastOnce()) + ->method('getId') + ->willReturn($customerId); + $customer->expects($this->once()) + ->method('getAddresses') + ->willReturn([]); + $this->customerRepositoryMock->expects($this->once()) + ->method('get') + ->with($email) + ->willReturn($customer); + $this->customerSecureMock->expects($this->once()) + ->method('setRpToken') + ->with(null) + ->willReturnSelf(); + $this->customerSecureMock->expects($this->once()) + ->method('setRpTokenCreatedAt') + ->with(null) + ->willReturnSelf(); + $this->customerRegistryMock->expects($this->once()) + ->method('retrieveSecureData') + ->with($customerId) + ->willReturn($this->customerSecureMock); + + $this->scopeConfigMock->expects($this->atLeastOnce()) + ->method('getValue') + ->willReturnMap( + [ + [ + AccountManagement::XML_PATH_MINIMUM_PASSWORD_LENGTH, + 'default', + null, + 7, + ], + [ + AccountManagement::XML_PATH_REQUIRED_CHARACTER_CLASSES_NUMBER, + 'default', + null, + 1, + ], + ] + ); + $this->stringMock->expects($this->atLeastOnce()) + ->method('strlen') + ->with($newPassword) + ->willReturn(7); + $this->customerRepositoryMock + ->expects($this->once()) + ->method('save') + ->with($customer); + $this->sessionManagerMock->expects($this->atLeastOnce())->method('getSessionId'); + $visitor = $this->getMockBuilder(\Magento\Customer\Model\Visitor::class) + ->disableOriginalConstructor() + ->setMethods(['getSessionId']) + ->getMock(); + $visitor->expects($this->atLeastOnce())->method('getSessionId') + ->willReturnOnConsecutiveCalls('session_id_1', 'session_id_2'); + $visitorCollection = $this->getMockBuilder( + \Magento\Customer\Model\ResourceModel\Visitor\CollectionFactory::class + )->disableOriginalConstructor() + ->setMethods(['addFieldToFilter', 'getItems']) + ->getMock(); + $visitorCollection->expects($this->atLeastOnce())->method('addFieldToFilter')->willReturnSelf(); + $visitorCollection->expects($this->once())->method('getItems')->willReturn([$visitor, $visitor]); + $this->visitorCollectionFactoryMock->expects($this->once())->method('create') + ->willReturn($visitorCollection); + $this->saveHandlerMock->expects($this->atLeastOnce())->method('destroy') + ->withConsecutive( + ['session_id_1'], + ['session_id_2'] + ); + + $this->assertTrue($this->accountManagement->changePassword($email, $currentPassword, $newPassword)); + } +} diff --git a/app/code/Magento/Customer/Test/Unit/Observer/UpgradeCustomerPasswordObserverTest.php b/app/code/Magento/Customer/Test/Unit/Observer/UpgradeCustomerPasswordObserverTest.php index 8971f155f782e..313121604e567 100644 --- a/app/code/Magento/Customer/Test/Unit/Observer/UpgradeCustomerPasswordObserverTest.php +++ b/app/code/Magento/Customer/Test/Unit/Observer/UpgradeCustomerPasswordObserverTest.php @@ -7,6 +7,9 @@ use Magento\Customer\Observer\UpgradeCustomerPasswordObserver; +/** + * Unit test for Magento\Customer\Observer\UpgradeCustomerPasswordObserver. + */ class UpgradeCustomerPasswordObserverTest extends \PHPUnit\Framework\TestCase { /** @@ -29,9 +32,13 @@ class UpgradeCustomerPasswordObserverTest extends \PHPUnit\Framework\TestCase */ protected $customerRegistry; + /** + * @inheritdoc + */ protected function setUp() { - $this->customerRepository = $this->getMockBuilder(\Magento\Customer\Api\CustomerRepositoryInterface::class) + $this->customerRepository = $this + ->getMockBuilder(\Magento\Customer\Api\CustomerRepositoryInterface::class) ->getMockForAbstractClass(); $this->customerRegistry = $this->getMockBuilder(\Magento\Customer\Model\CustomerRegistry::class) ->disableOriginalConstructor() @@ -47,6 +54,9 @@ protected function setUp() ); } + /** + * Unit test for verifying customers password upgrade observer + */ public function testUpgradeCustomerPassword() { $customerId = '1'; @@ -57,6 +67,8 @@ public function testUpgradeCustomerPassword() ->setMethods(['getId']) ->getMock(); $customer = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) + ->setMethods(['setData']) + ->disableOriginalConstructor() ->getMockForAbstractClass(); $customerSecure = $this->getMockBuilder(\Magento\Customer\Model\Data\CustomerSecure::class) ->disableOriginalConstructor() diff --git a/app/code/Magento/Newsletter/Controller/Manage/Save.php b/app/code/Magento/Newsletter/Controller/Manage/Save.php index 419cbac10ffd1..1aa2a4505d518 100644 --- a/app/code/Magento/Newsletter/Controller/Manage/Save.php +++ b/app/code/Magento/Newsletter/Controller/Manage/Save.php @@ -8,8 +8,12 @@ namespace Magento\Newsletter\Controller\Manage; use Magento\Customer\Api\CustomerRepositoryInterface as CustomerRepository; +use Magento\Customer\Model\Data\Customer; use Magento\Newsletter\Model\Subscriber; +/** + * Controller for customer newsletter subscription save. + */ class Save extends \Magento\Newsletter\Controller\Manage { /** @@ -58,7 +62,7 @@ public function __construct( } /** - * Save newsletter subscription preference action + * Save newsletter subscription preference action. * * @return void|null */ @@ -81,6 +85,8 @@ public function execute() $isSubscribedParam = (boolean)$this->getRequest() ->getParam('is_subscribed', false); if ($isSubscribedParam !== $isSubscribedState) { + // No need to validate customer and customer address while saving subscription preferences + $this->setIgnoreValidationFlag($customer); $this->customerRepository->save($customer); if ($isSubscribedParam) { $subscribeModel = $this->subscriberFactory->create() @@ -105,4 +111,15 @@ public function execute() } $this->_redirect('customer/account/'); } + + /** + * Set ignore_validation_flag to skip unnecessary address and customer validation. + * + * @param Customer $customer + * @return void + */ + private function setIgnoreValidationFlag(Customer $customer) + { + $customer->setData('ignore_validation_flag', true); + } } From 427245854951c74e827e1c9f7f1c3dde1bf2121a Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Fri, 1 Feb 2019 15:43:31 +0200 Subject: [PATCH 0476/1295] MAGETWO-94198: [MAGENTO CLOUD] Unable to add more attributes in size --- .../Catalog/Test/Mftf/Page/AdminProductAttributeFormPage.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeFormPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeFormPage.xml index 33c2c15a00e9d..685781dabab85 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeFormPage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeFormPage.xml @@ -12,5 +12,6 @@ <section name="StorefrontPropertiesSection"/> <section name="AdvancedAttributePropertiesSection"/> <section name="AdminAttributeOptionsSection"/> + <section name="AttributeManageSwatchSection"/> </page> </pages> From 8b4fbc8f93057ad7e83eebd4991e5b523710ddd0 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Fri, 1 Feb 2019 16:41:44 +0200 Subject: [PATCH 0477/1295] MAGETWO-87866: Password reset email cannot be sent if the customer does not have customer attribute set that is changed to required after original account creation --- .../Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php index 023525d10c52f..01f26a8906cc7 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php @@ -140,7 +140,9 @@ protected function setUp() ->getMock(); $this->filterMock = $this->createMock(\Magento\Ui\Component\MassAction\Filter::class); - $this->resultRedirectFactoryMock->expects($this->any())->method('create')->willReturn($this->resultRedirectMock); + $this->resultRedirectFactoryMock->expects($this->any()) + ->method('create') + ->willReturn($this->resultRedirectMock); $this->contextMock->expects($this->once())->method('getMessageManager')->willReturn($this->messageManagerMock); $this->contextMock->expects($this->once())->method('getRequest')->willReturn($this->requestMock); From 7458befe0895bbf4c1f2f32026e24a5271bbdaa1 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Fri, 1 Feb 2019 16:53:39 +0200 Subject: [PATCH 0478/1295] MAGETWO-96457: [Magento Cloud] [QUANS] Tiered block displayed on configurable products without any tiered pricing --- ...eForProductOptionsWithoutTierPriceTest.xml | 94 +++++++++++++++++++ .../Pricing/Render/TierPriceBox.php | 25 ++++- 2 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml new file mode 100644 index 0000000000000..cf4117162def8 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml @@ -0,0 +1,94 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="View configurable product details in storefront"/> + <title value="Check that 'trie price' block not available for simple product from options without 'trie price'"/> + <description value="Check that 'trie price' block not available for simple product from options without 'trie price'"/> + <severity value="MAJOR"/> + <testCaseId value="MC-13789"/> + <useCaseId value="MAGETWO-96457"/> + <group value="catalog"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Create the category --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <!-- Create the configurable product and add it to the category --> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!-- Create an attribute with two options to be used in the first child product --> + <createData entity="productAttributeWithDropdownTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <!-- Add the attribute we just created to default attribute set --> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <!-- Get the option of the attribute we created --> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <!-- Create a simple product and give it the attribute with option --> + <createData entity="SimpleOption" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="SimpleOption" stepKey="createConfigChildProduct2"> + <field key="sku">SimpleTwoOption</field> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <!-- Create the configurable product --> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <!-- Add simple product to the configurable product --> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + </before> + <after> + <!--Delete created data--> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + </after> + + <!--Go to storefront product page an check price box css--> + <amOnPage url="{{StorefrontProductPage.url($$createConfigProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToSimpleProductPage"/> + <selectOption selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" userInput="$$getConfigAttributeOption1.value$$" stepKey="selectOption"/> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="class" stepKey="grabGrabPriceClass"/> + <assertNotContains actual="$grabGrabPriceClass" expected=".price-box .price-tier_price" expectedType="string" stepKey="assertNotEquals"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Pricing/Render/TierPriceBox.php b/app/code/Magento/ConfigurableProduct/Pricing/Render/TierPriceBox.php index 611523a60b06d..4106c2553cfba 100644 --- a/app/code/Magento/ConfigurableProduct/Pricing/Render/TierPriceBox.php +++ b/app/code/Magento/ConfigurableProduct/Pricing/Render/TierPriceBox.php @@ -5,6 +5,8 @@ */ namespace Magento\ConfigurableProduct\Pricing\Render; +use Magento\Catalog\Pricing\Price\TierPrice; + /** * Responsible for displaying tier price box on configurable product page. * @@ -17,9 +19,28 @@ class TierPriceBox extends FinalPriceBox */ public function toHtml() { - // Hide tier price block in case of MSRP. - if (!$this->isMsrpPriceApplicable()) { + // Hide tier price block in case of MSRP or in case when no options with tier price. + if (!$this->isMsrpPriceApplicable() && $this->isTierPriceApplicable()) { return parent::toHtml(); } } + + /** + * Check if at least one of simple products has tier price. + * + * @return bool + */ + private function isTierPriceApplicable(): bool + { + $product = $this->getSaleableItem(); + foreach ($product->getTypeInstance()->getUsedProducts($product) as $simpleProduct) { + if ($simpleProduct->isSalable() && + !empty($simpleProduct->getPriceInfo()->getPrice(TierPrice::PRICE_CODE)->getTierPriceList()) + ) { + return true; + } + } + + return false; + } } From 7dcb3eb0e3517232df6d9c9ac5f6a0caf5204249 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Fri, 1 Feb 2019 17:20:30 +0200 Subject: [PATCH 0479/1295] MAGETWO-96457: [Magento Cloud] [QUANS] Tiered block displayed on configurable products without any tiered pricing --- .../Sales/Block/Adminhtml/Items/Column/Name.php | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) 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 2175d83c67d07..f4e2a50ac62c9 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Items/Column/Name.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Items/Column/Name.php @@ -33,13 +33,8 @@ class Name extends \Magento\Sales\Block\Adminhtml\Items\Column\DefaultColumn * @return string * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function truncateString( - string $value, - int $length = 80, - string $etc = '...', - string &$remainder = '', - bool $breakWords = true - ): string { + public function truncateString($value, $length = 80, $etc = '...', &$remainder = '', $breakWords = true) + { $this->truncateResult = $this->filterManager->truncateFilter( $value, ['length' => $length, 'etc' => $etc, 'breakWords' => $breakWords] @@ -53,7 +48,7 @@ public function truncateString( * @param string $value * @return array */ - public function getFormattedOption(string $value): array + public function getFormattedOption($value) { $remainder = ''; $this->truncateString($value, 55, '', $remainder); From 2769d2e0bccab809527db767f2e0c60200a6d77a Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Fri, 1 Feb 2019 09:35:20 -0600 Subject: [PATCH 0480/1295] MC-5926: Conflict of simultaneous write in Redis cache - fix unit test --- app/code/Magento/Deploy/Model/Filesystem.php | 2 +- .../Magento/Deploy/Test/Unit/Model/FilesystemTest.php | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Deploy/Model/Filesystem.php b/app/code/Magento/Deploy/Model/Filesystem.php index eb9c0a637ff06..64c0fa3a3302b 100644 --- a/app/code/Magento/Deploy/Model/Filesystem.php +++ b/app/code/Magento/Deploy/Model/Filesystem.php @@ -348,7 +348,7 @@ public function lockStaticResources() */ private function reinitCacheDirectories() { - $command = $this->functionCallPath . 'cache:flush'; + $command = $this->functionCallPath . 'cache:flush '; $this->shell->execute($command); } } diff --git a/app/code/Magento/Deploy/Test/Unit/Model/FilesystemTest.php b/app/code/Magento/Deploy/Test/Unit/Model/FilesystemTest.php index 80313e9ec2521..19bc31004ec2b 100644 --- a/app/code/Magento/Deploy/Test/Unit/Model/FilesystemTest.php +++ b/app/code/Magento/Deploy/Test/Unit/Model/FilesystemTest.php @@ -126,12 +126,18 @@ public function testRegenerateStatic() ->method('execute') ->with($setupDiCompileCmd); + + $setupDiCompileCmd = $this->cmdPrefix . 'cache:flush '; + $this->shell->expects(self::at(2)) + ->method('execute') + ->with($setupDiCompileCmd); + $this->initAdminLocaleMock('en_US'); $usedLocales = ['fr_FR', 'de_DE', 'nl_NL', 'en_US']; $staticContentDeployCmd = $this->cmdPrefix . 'setup:static-content:deploy -f ' . implode(' ', $usedLocales); - $this->shell->expects(self::at(2)) + $this->shell->expects(self::at(3)) ->method('execute') ->with($staticContentDeployCmd); From 2c86d780bc8cdcc25e258b4f703435a0a568e8eb Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Fri, 1 Feb 2019 12:12:02 -0600 Subject: [PATCH 0481/1295] MC-5926: Conflict of simultaneous write in Redis cache - fix static --- app/code/Magento/Deploy/Test/Unit/Model/FilesystemTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Deploy/Test/Unit/Model/FilesystemTest.php b/app/code/Magento/Deploy/Test/Unit/Model/FilesystemTest.php index 19bc31004ec2b..c1391cb7d9c5d 100644 --- a/app/code/Magento/Deploy/Test/Unit/Model/FilesystemTest.php +++ b/app/code/Magento/Deploy/Test/Unit/Model/FilesystemTest.php @@ -126,7 +126,6 @@ public function testRegenerateStatic() ->method('execute') ->with($setupDiCompileCmd); - $setupDiCompileCmd = $this->cmdPrefix . 'cache:flush '; $this->shell->expects(self::at(2)) ->method('execute') From 467a2a8cedf087bf9440ff8d711057188d2084b8 Mon Sep 17 00:00:00 2001 From: Govind Sharma <govindpokhrelsharma@cedcoss.com> Date: Sat, 26 Jan 2019 21:09:19 +0530 Subject: [PATCH 0482/1295] Fixed Issue #20631 --- .../Magento/Ui/view/base/web/js/form/element/post-code.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js b/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js index 911574a0fb438..5bfba724dfbe8 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js @@ -26,14 +26,17 @@ define([ update: function (value) { var country = registry.get(this.parentName + '.' + 'country_id'), options = country.indexedOptions, - option; + option = null; if (!value) { return; } option = options[value]; - + if (!option) { + return; + } + if (option['is_zipcode_optional']) { this.error(false); this.validation = _.omit(this.validation, 'required-entry'); From a48a4dec39556645fc2511c52065f78401d16ac8 Mon Sep 17 00:00:00 2001 From: Oleksii Korshenko <korshenk@adobe.com> Date: Mon, 28 Jan 2019 16:44:17 -0600 Subject: [PATCH 0483/1295] Update post-code.js --- .../Magento/Ui/view/base/web/js/form/element/post-code.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js b/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js index 5bfba724dfbe8..72177a1804df0 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js @@ -33,11 +33,8 @@ define([ } option = options[value]; - if (!option) { - return; - } - if (option['is_zipcode_optional']) { + if (option && option['is_zipcode_optional']) { this.error(false); this.validation = _.omit(this.validation, 'required-entry'); } else { From 8d6721317e84b2382add21b5f21c4627becf0c67 Mon Sep 17 00:00:00 2001 From: Oleksii Korshenko <korshenk@adobe.com> Date: Mon, 28 Jan 2019 16:51:01 -0600 Subject: [PATCH 0484/1295] Update post-code.js --- .../Magento/Ui/view/base/web/js/form/element/post-code.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js b/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js index 72177a1804df0..b8bab2f72f38a 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js @@ -34,7 +34,11 @@ define([ option = options[value]; - if (option && option['is_zipcode_optional']) { + if (!option) { + return; + } + + if (option['is_zipcode_optional']) { this.error(false); this.validation = _.omit(this.validation, 'required-entry'); } else { From 9092e7a98807d2aa611c2cb7161f737984cdd32f Mon Sep 17 00:00:00 2001 From: Milind Singh <milind7@live.com> Date: Fri, 18 Jan 2019 20:14:31 +0530 Subject: [PATCH 0485/1295] #20409 Fixed Unnecessary slash in namespace --- app/code/Magento/CatalogInventory/etc/di.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogInventory/etc/di.xml b/app/code/Magento/CatalogInventory/etc/di.xml index 7cddb4f8f0b54..535e91ba30f52 100644 --- a/app/code/Magento/CatalogInventory/etc/di.xml +++ b/app/code/Magento/CatalogInventory/etc/di.xml @@ -111,7 +111,7 @@ <argument name="batchSizeManagement" xsi:type="object">Magento\CatalogInventory\Model\Indexer\Stock\BatchSizeManagement</argument> </arguments> </type> - <type name="\Magento\Framework\Data\CollectionModifier"> + <type name="Magento\Framework\Data\CollectionModifier"> <arguments> <argument name="conditions" xsi:type="array"> <item name="stockStatusCondition" xsi:type="object">Magento\CatalogInventory\Model\ProductCollectionStockCondition</item> From 7706f15bb73857a5e881c6df888e06f06d6ed960 Mon Sep 17 00:00:00 2001 From: Neeta Kangiya <neeta@wagento.com> Date: Sun, 3 Feb 2019 15:00:47 +0530 Subject: [PATCH 0486/1295] resolve typo errors for js record.js --- app/code/Magento/Ui/view/base/web/js/dynamic-rows/record.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/record.js b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/record.js index 54309ca068513..3987507ece54f 100644 --- a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/record.js +++ b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/record.js @@ -25,7 +25,7 @@ define([ }, listens: { position: 'initPosition', - elems: 'setColumnVisibileListener' + elems: 'setColumnVisibleListener' }, links: { position: '${ $.name }.${ $.positionProvider }:value' @@ -123,7 +123,7 @@ define([ /** * Set column visibility listener */ - setColumnVisibileListener: function () { + setColumnVisibleListener: function () { var elem = _.find(this.elems(), function (curElem) { return !curElem.hasOwnProperty('visibleListener'); }); From 9b6ef590ed0fc95c5da7e5ebfacf5b2452dead5e Mon Sep 17 00:00:00 2001 From: Serhiy Zhovnir <s.zhovnir@atwix.com> Date: Sun, 3 Feb 2019 19:19:10 +0200 Subject: [PATCH 0487/1295] #18698 Fixed order email sending via order async email sending when order was created with disabled email sending --- app/code/Magento/Sales/Model/Order/Email/Sender/OrderSender.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Model/Order/Email/Sender/OrderSender.php b/app/code/Magento/Sales/Model/Order/Email/Sender/OrderSender.php index f06da0de0fd00..7ac11ca073d26 100644 --- a/app/code/Magento/Sales/Model/Order/Email/Sender/OrderSender.php +++ b/app/code/Magento/Sales/Model/Order/Email/Sender/OrderSender.php @@ -97,7 +97,7 @@ public function __construct( */ public function send(Order $order, $forceSyncMode = false) { - $order->setSendEmail(true); + $order->setSendEmail($this->identityContainer->isEnabled()); if (!$this->globalConfig->getValue('sales_email/general/async_sending') || $forceSyncMode) { if ($this->checkAndSend($order)) { From 3c9435e865233002ef12d0e2b9bf5739b71b9c30 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Mon, 4 Feb 2019 01:03:26 +0200 Subject: [PATCH 0488/1295] MAGETWO-82385: [CE 2.1.0 rc3] - Cancel an order [configurable product] #5313 --- .../Catalog/Test/Mftf/Data/ProductData.xml | 4 + .../Data/ProductExtensionAttributeData.xml | 3 + .../Catalog/Test/Mftf/Data/StockItemData.xml | 4 + ...CheckingProductQtyAfterOrderCancelTest.xml | 136 ++++++++++++++++++ .../Sales/Model/Service/InvoiceService.php | 60 +++++--- .../ActionGroup/AdminOrderActionGroup.xml | 6 + 6 files changed, 192 insertions(+), 21 deletions(-) create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckingProductQtyAfterOrderCancelTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 39f4f0f2d997d..d1e0be2955d9f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -282,4 +282,8 @@ <data key="quantity">1</data> <requiredEntity type="product_extension_attribute">EavStock1</requiredEntity> </entity> + <entity name="ApiSimpleWithQty100" extends="ApiSimpleOne"> + <data key="quantity">100</data> + <requiredEntity type="product_extension_attribute">EavStock100</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductExtensionAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductExtensionAttributeData.xml index 5424e48261085..5b2dc5e691a2b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductExtensionAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductExtensionAttributeData.xml @@ -14,4 +14,7 @@ <entity name="EavStock1" type="product_extension_attribute"> <requiredEntity type="stock_item">Qty_1</requiredEntity> </entity> + <entity name="EavStock100" type="product_extension_attribute"> + <requiredEntity type="stock_item">Qty_100</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/StockItemData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/StockItemData.xml index 99e072b91c3a9..a071c068b575d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/StockItemData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/StockItemData.xml @@ -16,4 +16,8 @@ <data key="qty">1</data> <data key="is_in_stock">true</data> </entity> + <entity name="Qty_100" type="stock_item"> + <data key="qty">100</data> + <data key="is_in_stock">true</data> + </entity> </entities> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckingProductQtyAfterOrderCancelTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckingProductQtyAfterOrderCancelTest.xml new file mode 100644 index 0000000000000..8d76e842070a8 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckingProductQtyAfterOrderCancelTest.xml @@ -0,0 +1,136 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckingProductQtyAfterOrderCancelTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Product quantity after order cancel"/> + <title value="Products quantity return after order cancel"/> + <description value="Checking product quantity after the order cancel"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-13790"/> + <group value="configurableProduct"/> + </annotations> + <before> + <!--Create category--> + <createData entity="ApiCategory" stepKey="createCategory"/> + <!--Create configurable product and add it to the category--> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!--Create attribute--> + <createData entity="productAttributeWithDropdownTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <!--Add the attribute to default attribute set--> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <!--Get the option of the attribute--> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <!--Create simple product and give it the attribute with option--> + <createData entity="ApiSimpleWithQty100" stepKey="createConfigChildProduct"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption"/> + </createData> + <!--Create configurable product--> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption"/> + </createData> + <!--Add simple product to the configurable product--> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct"/> + </createData> + <!--Create customer--> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <!--Login--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <!--Clear grid filters--> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrderGridPage"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearGridFilter"/> + <!--Delete entities--> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigChildProduct" stepKey="deleteConfigChildProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <!--Logout--> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <actionGroup ref="CustomerLogoutStorefrontActionGroup" stepKey="logoutFromStorefront"/> + </after> + + <!--Go to Storefront as Customer--> + <actionGroup ref="CustomerLoginOnStorefront" stepKey="customerLogin"> + <argument name="customer" value="$$createCustomer$$" /> + </actionGroup> + + <!--Go to the configurable product page on Storefront--> + <amOnPage url="{{StorefrontProductPage.url($$createConfigProduct.sku$$)}}" stepKey="goToProductPage"/> + <!--Select option--> + <selectOption selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" userInput="$$getConfigAttributeOption.label$$" stepKey="selectOption"/> + <!--Add product to the Shopping cart--> + <actionGroup ref="StorefrontAddProductToCartQuantityActionGroup" stepKey="addProductToCart"> + <argument name="productName" value="$createConfigProduct.name$"/> + <argument name="quantity" value="4"/> + </actionGroup> + + <!--Open Shopping cart--> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="openShoppingCartFromMinicart"/> + <!--Place order--> + <actionGroup ref="PlaceOrderWithLoggedUserActionGroup" stepKey="placeOrder"> + <argument name="shippingMethod" value="Flat Rate"/> + <argument name="paymentMethod" value="Check / Money order"/> + </actionGroup> + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> + + <!--Open order--> + <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> + <argument name="orderId" value="{$grabOrderNumber}"/> + </actionGroup> + <click selector="{{AdminDataGridTableSection.firstRow}}" stepKey="openOrderViewPage"/> + <waitForPageLoad stepKey="waitForOrderViewPageOpen"/> + + <!--Create Invoice--> + <actionGroup ref="StartCreateInvoiceFromOrderPage" stepKey="startCreateInvoice"/> + <fillField selector="{{AdminInvoiceItemsSection.itemQtyToInvoice('1')}}" userInput="1" stepKey="changeQtyToInvoice"/> + <waitForElementVisible selector="{{AdminInvoiceItemsSection.updateQtyEnabled}}" stepKey="waitForUpdateQtyEnabled"/> + <click selector="{{AdminInvoiceItemsSection.updateQty}}" stepKey="updateQty"/> + <waitForLoadingMaskToDisappear stepKey="waitForQtyToUpdate"/> + <waitForElementVisible selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="waitForSubmitInvoiceButton"/> + <actionGroup ref="SubmitInvoice" stepKey="submitInvoice"/> + <!--Create Shipment--> + <actionGroup ref="StartCreateShipmentFromOrderPage" stepKey="startCreateShipment"/> + <fillField selector="{{AdminShipmentItemsSection.itemQtyToShip('1')}}" userInput="1" stepKey="changeItemQtyToShip"/> + <actionGroup ref="SubmitShipment" stepKey="submitShipment"/> + + <!--Cancel order--> + <actionGroup ref="cancelCompleteOrder" stepKey="cancelOrder"/> + <!--Check quantities in "Items Ordered" table--> + <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Invoiced 1" stepKey="seeInvoicedQuantity"/> + <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Shipped 1" stepKey="seeShippedQuantity"/> + <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Canceled 3" stepKey="seeCanceledQuantity"/> + + <!--Go to catalog products page on Admin--> + <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogProductPage"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterProductGrid"> + <argument name="product" value="$$createConfigChildProduct$$"/> + </actionGroup> + + <!--Check quantity of configurable child product--> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Quantity')}}" userInput="99" stepKey="seeProductSkuInGrid"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Model/Service/InvoiceService.php b/app/code/Magento/Sales/Model/Service/InvoiceService.php index 718f55c3e551c..b66f59d2a2962 100644 --- a/app/code/Magento/Sales/Model/Service/InvoiceService.php +++ b/app/code/Magento/Sales/Model/Service/InvoiceService.php @@ -5,6 +5,7 @@ */ namespace Magento\Sales\Model\Service; +use Magento\Sales\Api\Data\OrderItemInterface; use Magento\Sales\Api\InvoiceManagementInterface; use Magento\Sales\Model\Order; @@ -136,14 +137,14 @@ public function prepareInvoice(Order $order, array $qtys = []) $totalQty = 0; $qtys = $this->prepareItemsQty($order, $qtys); foreach ($order->getAllItems() as $orderItem) { - if (!$this->_canInvoiceItem($orderItem)) { + if (!$this->_canInvoiceItem($orderItem, $qtys)) { continue; } $item = $this->orderConverter->itemToInvoiceItem($orderItem); - if ($orderItem->isDummy()) { - $qty = $orderItem->getQtyOrdered() ? $orderItem->getQtyOrdered() : 1; - } elseif (isset($qtys[$orderItem->getId()])) { + if (isset($qtys[$orderItem->getId()])) { $qty = (double) $qtys[$orderItem->getId()]; + } elseif ($orderItem->isDummy()) { + $qty = $orderItem->getQtyOrdered() ? $orderItem->getQtyOrdered() : 1; } elseif (empty($qtys)) { $qty = $orderItem->getQtyToInvoice(); } else { @@ -170,38 +171,55 @@ private function prepareItemsQty(Order $order, array $qtys = []) { foreach ($order->getAllItems() as $orderItem) { if (empty($qtys[$orderItem->getId()])) { - continue; - } - if ($orderItem->isDummy()) { - if ($orderItem->getHasChildren()) { - foreach ($orderItem->getChildrenItems() as $child) { - if (!isset($qtys[$child->getId()])) { - $qtys[$child->getId()] = $child->getQtyToInvoice(); - } - } - } elseif ($orderItem->getParentItem()) { - $parent = $orderItem->getParentItem(); - if (!isset($qtys[$parent->getId()])) { - $qtys[$parent->getId()] = $parent->getQtyToInvoice(); - } + $parentId = $orderItem->getParentItemId(); + if ($parentId && array_key_exists($parentId, $qtys)) { + $qtys[$orderItem->getId()] = $qtys[$parentId]; + } else { + continue; } } + $this->prepareItemQty($orderItem, $qtys); } return $qtys; } + /** + * Prepare qty to invoice item. + * + * @param OrderItemInterface $orderItem + * @param array $qtys + * @return void + */ + private function prepareItemQty(OrderItemInterface $orderItem, array &$qtys) + { + if ($orderItem->isDummy()) { + if ($orderItem->getHasChildren()) { + foreach ($orderItem->getChildrenItems() as $child) { + if (!isset($qtys[$child->getId()])) { + $qtys[$child->getId()] = $child->getQtyToInvoice(); + } + } + } elseif ($orderItem->getParentItem()) { + $parent = $orderItem->getParentItem(); + if (!isset($qtys[$parent->getId()])) { + $qtys[$parent->getId()] = $parent->getQtyToInvoice(); + } + } + } + } + /** * Check if order item can be invoiced. Dummy item can be invoiced or with his children or * with parent item which is included to invoice * - * @param \Magento\Sales\Api\Data\OrderItemInterface $item + * @param OrderItemInterface $item + * @param array $qtys * @return bool * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ - protected function _canInvoiceItem(\Magento\Sales\Api\Data\OrderItemInterface $item) + protected function _canInvoiceItem(OrderItemInterface $item, array $qtys = []) { - $qtys = []; if ($item->getLockedDoInvoice()) { return false; } diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml index bb3bfdc03d340..6e0d7ee542f58 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml @@ -18,6 +18,12 @@ <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Canceled" stepKey="seeOrderStatusCanceled"/> </actionGroup> + <!--Cancel order that is in complete status--> + <actionGroup name="cancelCompleteOrder" extends="cancelPendingOrder"> + <remove keyForRemoval="seeOrderStatusCanceled"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" after="seeCancelSuccessMessage" userInput="Complete" stepKey="seeOrderStatusComplete"/> + </actionGroup> + <!--Navigate to create order page (New Order -> Create New Customer)--> <actionGroup name="navigateToNewOrderPageNewCustomerSingleStore"> <arguments> From 78c45bfcbae3190b87dd3e0234a363447fbca587 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Mon, 4 Feb 2019 10:01:54 +0200 Subject: [PATCH 0489/1295] MAGETWO-97680: Issue with Email template preview --- lib/internal/Magento/Framework/Filter/Template.php | 5 ++++- .../Framework/Filter/Test/Unit/TemplateTest.php | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Filter/Template.php b/lib/internal/Magento/Framework/Filter/Template.php index fb475be2d2c61..92bcdd2082509 100644 --- a/lib/internal/Magento/Framework/Filter/Template.php +++ b/lib/internal/Magento/Framework/Filter/Template.php @@ -367,7 +367,10 @@ protected function getVariable($value, $default = '{no_value_defined}') } } $last = $i; - } elseif (isset($stackVars[$i - 1]['variable']) && $stackVars[$i]['type'] == 'method') { + } elseif (isset($stackVars[$i - 1]['variable']) + && is_object($stackVars[$i - 1]['variable']) + && $stackVars[$i]['type'] == 'method' + ) { // Calling object methods $object = $stackVars[$i - 1]['variable']; $method = $stackVars[$i]['name']; diff --git a/lib/internal/Magento/Framework/Filter/Test/Unit/TemplateTest.php b/lib/internal/Magento/Framework/Filter/Test/Unit/TemplateTest.php index 5c91931bfe996..bc27d55e25aa8 100644 --- a/lib/internal/Magento/Framework/Filter/Test/Unit/TemplateTest.php +++ b/lib/internal/Magento/Framework/Filter/Test/Unit/TemplateTest.php @@ -216,4 +216,18 @@ public function testInappropriateCallbacks() $this->templateFilter->setVariables(['filter' => $this->templateFilter]); $this->templateFilter->filter('Test {{var filter.addAfterFilterCallback(\'mb_strtolower\')}}'); } + + /** + * Check that if calling a method of an object fails expected result is returned. + * + * @return void + */ + public function testInvalidMethodCall() + { + $this->templateFilter->setVariables(['dateTime' => '\DateTime']); + $this->assertEquals( + '\DateTime', + $this->templateFilter->filter('{{var dateTime.createFromFormat(\'d\',\'1548201468\')}}') + ); + } } From 522f5f8ec5f8db37aaf7584293266850f96fa72f Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Mon, 4 Feb 2019 11:39:01 +0200 Subject: [PATCH 0490/1295] MAGETWO-97960: Fixed incorrect behaviour of template variables --- .../Magento/Framework/Filter/Template.php | 44 ++++++++++++++++--- .../Filter/Test/Unit/TemplateTest.php | 39 ++++++++++++++++ 2 files changed, 78 insertions(+), 5 deletions(-) diff --git a/lib/internal/Magento/Framework/Filter/Template.php b/lib/internal/Magento/Framework/Filter/Template.php index fb475be2d2c61..681343d7dc7ce 100644 --- a/lib/internal/Magento/Framework/Filter/Template.php +++ b/lib/internal/Magento/Framework/Filter/Template.php @@ -9,6 +9,9 @@ */ namespace Magento\Framework\Filter; +use Magento\Framework\Model\AbstractExtensibleModel; +use Magento\Framework\Model\AbstractModel; + /** * @api */ @@ -55,7 +58,14 @@ class Template implements \Zend_Filter_Interface /** * @var string[] */ - private $restrictedMethods = ['addafterfiltercallback']; + private $restrictedMethods = [ + 'addafterfiltercallback', + 'getresourcecollection', + 'load', + 'save', + 'getcollection', + 'getresource' + ]; /** * @param \Magento\Framework\Stdlib\StringUtils $string @@ -321,6 +331,27 @@ private function validateVariableMethodCall($object, string $method) } } + /** + * Check allowed methods for data objects. + * + * Deny calls for methods that may disrupt template processing. + * + * @param object $object + * @param string $method + * @return bool + * @throws \InvalidArgumentException + */ + private function isAllowedDataObjectMethod($object, string $method): bool + { + if ($object instanceof AbstractExtensibleModel || $object instanceof AbstractModel) { + if (in_array(mb_strtolower($method), $this->restrictedMethods)) { + throw new \InvalidArgumentException("Method $method cannot be called from template."); + } + } + + return true; + } + /** * Return variable value for var construction * @@ -360,10 +391,13 @@ protected function getVariable($value, $default = '{no_value_defined}') || substr($stackVars[$i]['name'], 0, 3) == 'get' ) { $stackVars[$i]['args'] = $this->getStackArgs($stackVars[$i]['args']); - $stackVars[$i]['variable'] = call_user_func_array( - [$stackVars[$i - 1]['variable'], $stackVars[$i]['name']], - $stackVars[$i]['args'] - ); + + if ($this->isAllowedDataObjectMethod($stackVars[$i - 1]['variable'], $stackVars[$i]['name'])) { + $stackVars[$i]['variable'] = call_user_func_array( + [$stackVars[$i - 1]['variable'], $stackVars[$i]['name']], + $stackVars[$i]['args'] + ); + } } } $last = $i; diff --git a/lib/internal/Magento/Framework/Filter/Test/Unit/TemplateTest.php b/lib/internal/Magento/Framework/Filter/Test/Unit/TemplateTest.php index 5c91931bfe996..43e86e9ab305d 100644 --- a/lib/internal/Magento/Framework/Filter/Test/Unit/TemplateTest.php +++ b/lib/internal/Magento/Framework/Filter/Test/Unit/TemplateTest.php @@ -6,6 +6,8 @@ namespace Magento\Framework\Filter\Test\Unit; +use Magento\Store\Model\Store; + class TemplateTest extends \PHPUnit\Framework\TestCase { /** @@ -13,10 +15,16 @@ class TemplateTest extends \PHPUnit\Framework\TestCase */ private $templateFilter; + /** + * @var Store + */ + private $store; + protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->templateFilter = $objectManager->getObject(\Magento\Framework\Filter\Template::class); + $this->store = $objectManager->getObject(Store::class); } public function testFilter() @@ -216,4 +224,35 @@ public function testInappropriateCallbacks() $this->templateFilter->setVariables(['filter' => $this->templateFilter]); $this->templateFilter->filter('Test {{var filter.addAfterFilterCallback(\'mb_strtolower\')}}'); } + + /** + * Test adding callbacks when already filtering. + * + * @param string $method + * @dataProvider disallowedMethods + * @expectedException \InvalidArgumentException + * + * @return void + */ + public function testDisallowedMethods(string $method) + { + $this->templateFilter->setVariables(['store' => $this->store]); + $this->templateFilter->filter('{{var store.'.$method.'()}}'); + } + + /** + * Data for testDisallowedMethods method. + * + * @return array + */ + public function disallowedMethods(): array + { + return [ + ['getResourceCollection'], + ['load'], + ['save'], + ['getCollection'], + ['getResource'], + ]; + } } From 76ea0c3667031db6dd860d29ad30932533558814 Mon Sep 17 00:00:00 2001 From: bshevchenko <bohdan.shevchenko@transoftgroup.com> Date: Mon, 4 Feb 2019 12:03:00 +0200 Subject: [PATCH 0491/1295] MAGETWO-96705: Cart Price Rule with related Banner for specific Customer Segment is persisted under long-term cookie --- .../Mftf/Section/AdminMainActionsSection.xml | 1 + .../Mftf/ActionGroup/CheckoutActionGroup.xml | 4 +- .../Section/StorefrontMinicartSection.xml | 1 + .../Test/Mftf/Page/AdminCmsBlockEditPage.xml | 1 + .../Cms/Test/Mftf/Page/StorefrontHomePage.xml | 3 +- .../Mftf/Section/AdminMediaGallerySection.xml | 17 +++ .../Mftf/Section/StorefrontCmsPageSection.xml | 14 +++ .../OpenEditCustomerFromAdminActionGroup.xml | 4 +- .../RemoveCustomerFromAdminActionGroup.xml | 5 +- ...SignUpNewUserFromStorefrontActionGroup.xml | 3 +- ...bscribedNewsletterDisplayedActionGroup.xml | 22 ++++ .../Page/StorefrontNewsletterManagePage.xml | 13 +++ .../StorefrontCustomerCreateFormSection.xml | 14 +++ ...omerDashboardAccountInformationSection.xml | 14 +++ .../StorefrontNewsletterManageSection.xml | 14 +++ .../AdminCartPriceRuleActionGroup.xml | 22 ++++ .../ApplyCartRuleOnStorefrontActionGroup.xml | 8 ++ .../Test/Mftf/Data/SalesCouponData.xml | 18 +++ .../Data/SalesRuleAddressConditionsData.xml | 21 ++++ .../Test/Mftf/Data/SalesRuleData.xml | 27 +++++ .../AdminCartPriceRulesFormSection.xml | 6 + .../AdminCreateWidgetActionGroup.xml | 104 +++++++++--------- .../Widget/Test/Mftf/Data/WidgetsData.xml | 15 ++- .../Test/Mftf/Page/AdminNewWidgetPage.xml | 4 +- .../Mftf/Section/AdminNewWidgetSection.xml | 4 +- dev/tests/acceptance/tests/_data/magento2.jpg | Bin 0 -> 73131 bytes 26 files changed, 294 insertions(+), 65 deletions(-) create mode 100644 app/code/Magento/Cms/Test/Mftf/Section/AdminMediaGallerySection.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/Section/StorefrontCmsPageSection.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/ActionGroup/VerifySubscribedNewsletterDisplayedActionGroup.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/Page/StorefrontNewsletterManagePage.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection.xml create mode 100644 app/code/Magento/Newsletter/Test/Mftf/Section/StorefrontNewsletterManageSection.xml create mode 100644 app/code/Magento/SalesRule/Test/Mftf/Data/SalesCouponData.xml create mode 100644 app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleAddressConditionsData.xml create mode 100644 dev/tests/acceptance/tests/_data/magento2.jpg diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminMainActionsSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminMainActionsSection.xml index 06d4a508acdd9..344a209b93f60 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminMainActionsSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminMainActionsSection.xml @@ -10,6 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminMainActionsSection"> <element name="save" type="button" selector="#save" timeout="30"/> + <element name="saveAndContinue" type="button" selector="button[id*=save_and_]" timeout="30"/> <element name="delete" type="button" selector="#delete"/> <element name="add" type="button" selector="#add" timeout="30"/> <element name="back" type="button" selector="#back" timeout="30"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml index 7bd6931309148..f79d59028c468 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml @@ -46,8 +46,8 @@ <!-- Logged in user checkout filling shipping section --> <actionGroup name="LoggedInUserCheckoutFillingShippingSectionActionGroup"> <arguments> - <argument name="customerVar"/> - <argument name="customerAddressVar"/> + <argument name="customerVar" defaultValue="CustomerEntityOne"/> + <argument name="customerAddressVar" defaultValue="CustomerAddressSimple"/> </arguments> <waitForElementVisible selector="{{CheckoutShippingSection.firstName}}" stepKey="waitForFirstNameFieldAppears" time="30"/> <fillField selector="{{CheckoutShippingSection.firstName}}" userInput="{{customerVar.firstname}}" stepKey="enterFirstName"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMinicartSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMinicartSection.xml index 00e7d5a011513..17ea090ad14d0 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMinicartSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMinicartSection.xml @@ -25,5 +25,6 @@ <element name="itemQuantity" type="input" selector="//a[text()='{{productName}}']/../..//input[contains(@class,'cart-item-qty')]" parameterized="true"/> <element name="itemQuantityUpdate" type="button" selector="//a[text()='{{productName}}']/../..//span[text()='Update']" parameterized="true"/> <element name="emptyCart" type="text" selector=".counter.qty.empty"/> + <element name="minicartContent" type="block" selector="#minicart-content-wrapper"/> </section> </sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsBlockEditPage.xml b/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsBlockEditPage.xml index a2e16b8f279df..e3584427f4767 100644 --- a/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsBlockEditPage.xml +++ b/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsBlockEditPage.xml @@ -10,5 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminCmsBlockEditPage" url="/cms/block/edit/id/{{var1}}" area="admin" module="Magento_Cms" parameterized="true"> <section name="AdminCmsBlockContentSection" /> + <section name="AdminMediaGallerySection" /> </page> </pages> diff --git a/app/code/Magento/Cms/Test/Mftf/Page/StorefrontHomePage.xml b/app/code/Magento/Cms/Test/Mftf/Page/StorefrontHomePage.xml index 571eb702bacfc..5468d08bd4e0b 100644 --- a/app/code/Magento/Cms/Test/Mftf/Page/StorefrontHomePage.xml +++ b/app/code/Magento/Cms/Test/Mftf/Page/StorefrontHomePage.xml @@ -7,10 +7,11 @@ --> <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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="StorefrontHomePage" url="/" module="Magento_Cms" area="storefront"> <section name="StorefrontHeaderSection"/> <section name="StorefrontQuickSearchSection"/> <section name="StorefrontHeaderCurrencySwitcherSection"/> + <section name="StorefrontCmsPageSection"/> </page> </pages> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/AdminMediaGallerySection.xml b/app/code/Magento/Cms/Test/Mftf/Section/AdminMediaGallerySection.xml new file mode 100644 index 0000000000000..9d08bc708aef9 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/AdminMediaGallerySection.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminMediaGallerySection"> + <element name="imageSelected" type="text" selector="//small[text()='{{imageName}}']/parent::*[@class='filecnt selected']" parameterized="true"/> + <element name="uploadImage" type="file" selector="input.fileupload" /> + <element name="insertFile" type="text" selector="#insert_files"/> + <element name="imageBlockByName" type="block" selector="//div[@data-row='file'][contains(., '{{imageName}}')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/StorefrontCmsPageSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/StorefrontCmsPageSection.xml new file mode 100644 index 0000000000000..2a2a80098b92e --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/StorefrontCmsPageSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCmsPageSection"> + <element name="imageSource" type="text" selector="img[src*='{{imageName}}']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml index 79b876ff6ecf7..3b7aab22f749e 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml @@ -9,7 +9,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="OpenEditCustomerFromAdminActionGroup" extends="clearFiltersAdminDataGrid"> <arguments> - <argument name="customer"/> + <argument name="customer" defaultValue="CustomerEntityOne"/> </arguments> <amOnPage url="{{AdminCustomerPage.url}}" before="waitForPageLoad" stepKey="navigateToCustomers"/> <click selector="{{AdminCustomerFiltersSection.filtersButton}}" stepKey="openFilter"/> @@ -17,6 +17,6 @@ <click selector="{{AdminCustomerFiltersSection.apply}}" stepKey="applyFilter"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> <click selector="{{AdminCustomerGridSection.firstRowEditLink}}" stepKey="clickEdit"/> - <waitForPageLoad stepKey="waitForPageLoad2" /> + <waitForPageLoad stepKey="waitForPageLoad" /> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/RemoveCustomerFromAdminActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/RemoveCustomerFromAdminActionGroup.xml index a53968a920806..3ec06d8353a45 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/RemoveCustomerFromAdminActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/RemoveCustomerFromAdminActionGroup.xml @@ -10,10 +10,9 @@ <!--Delete a customer by Email by filtering grid and using delete action--> <actionGroup name="RemoveCustomerFromAdminActionGroup"> <arguments> - <argument name="customer"/> + <argument name="customer" defaultValue="CustomerEntityOne"/> </arguments> <amOnPage url="{{AdminCustomerPage.url}}" stepKey="navigateToCustomers"/> - <waitForPageLoad stepKey="waitForPageLoad1" /> <conditionalClick selector="{{AdminCustomerFiltersSection.clearFilters}}" dependentSelector="{{AdminCustomerFiltersSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> <click selector="{{AdminCustomerFiltersSection.filtersButton}}" stepKey="openFilter"/> <fillField userInput="{{customer.email}}" selector="{{AdminCustomerFiltersSection.emailInput}}" stepKey="filterEmail"/> @@ -28,6 +27,6 @@ <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmProductDelete"/> <see selector="{{AdminCustomerMessagesSection.successMessage}}" userInput="A total of 1 record(s) were deleted." stepKey="seeSuccessMessage"/> <conditionalClick selector="{{AdminCustomerFiltersSection.clearFilters}}" dependentSelector="{{AdminCustomerFiltersSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> + <waitForPageLoad stepKey="waitForPageLoad"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml index a8ee604edee0a..d86591b799a33 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml @@ -9,10 +9,9 @@ xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> <actionGroup name="SignUpNewUserFromStorefrontActionGroup"> <arguments> - <argument name="Customer"/> + <argument name="Customer" defaultValue="CustomerEntityOne"/> </arguments> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> - <waitForPageLoad stepKey="waitForStorefrontPage"/> <click selector="{{StorefrontPanelHeaderSection.createAnAccountLink}}" stepKey="clickOnCreateAccountLink"/> <fillField userInput="{{Customer.firstname}}" selector="{{StorefrontCustomerCreateFormSection.firstnameField}}" stepKey="fillFirstName"/> <fillField userInput="{{Customer.lastname}}" selector="{{StorefrontCustomerCreateFormSection.lastnameField}}" stepKey="fillLastName"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/VerifySubscribedNewsletterDisplayedActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/VerifySubscribedNewsletterDisplayedActionGroup.xml new file mode 100644 index 0000000000000..81b444fb5c1dc --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/VerifySubscribedNewsletterDisplayedActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + + <!--Create an Account. Check Sign Up for Newsletter checkbox --> + <actionGroup name="StorefrontCreateNewAccountNewsletterChecked" extends="SignUpNewUserFromStorefrontActionGroup"> + <checkOption selector="{{StorefrontCustomerCreateFormSection.signUpForNewsletter}}" stepKey="selectSignUpForNewsletterCheckbox" after="fillLastName"/> + <see stepKey="seenewsletterDescription" userInput='You are subscribed to "General Subscription".' selector="{{StorefrontCustomerDashboardAccountInformationSection.newsletterDescription}}" /> + </actionGroup> + + <!--Check Subscribed Newsletter via StoreFront--> + <actionGroup name="CheckSubscribedNewsletterActionGroup"> + <amOnPage url="{{StorefrontNewsletterManagePage.url}}" stepKey="goToNewsletterManage"/> + <seeCheckboxIsChecked selector="{{StorefrontNewsletterManageSection.subscriptionCheckbox}}" stepKey="checkSubscribedNewsletter"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Page/StorefrontNewsletterManagePage.xml b/app/code/Magento/Newsletter/Test/Mftf/Page/StorefrontNewsletterManagePage.xml new file mode 100644 index 0000000000000..81fd3eb7c391c --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Page/StorefrontNewsletterManagePage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="StorefrontNewsletterManagePage" url="newsletter/manage/" area="storefront" module="Magento_Newsletter"> + <section name="StorefrontNewsletterManageSection"/> + </page> +</pages> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml new file mode 100644 index 0000000000000..36870fbfb0182 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCustomerCreateFormSection"> + <element name="signUpForNewsletter" type="checkbox" selector="#is_subscribed"/> + </section> +</sections> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection.xml new file mode 100644 index 0000000000000..15d6debd7ef25 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCustomerDashboardAccountInformationSection"> + <element name="newsletterDescription" type="text" selector=".box-newsletter p"/> + </section> +</sections> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/StorefrontNewsletterManageSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/StorefrontNewsletterManageSection.xml new file mode 100644 index 0000000000000..96a944a4952ac --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Section/StorefrontNewsletterManageSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontNewsletterManageSection"> + <element name="subscriptionCheckbox" type="checkbox" selector="#subscription" /> + </section> +</sections> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCartPriceRuleActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCartPriceRuleActionGroup.xml index 9152016e709e5..b8e4bfadc0341 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCartPriceRuleActionGroup.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCartPriceRuleActionGroup.xml @@ -23,4 +23,26 @@ <!-- This actionGroup was created to be merged from B2B because B2B has a very different form control here --> <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/> </actionGroup> + + <!--Set Subtotal condition for Customer Segment--> + <actionGroup name="SetCartAttributeConditionForCartPriceRuleActionGroup"> + <arguments> + <argument name="attributeName" type="string"/> + <argument name="operatorType" defaultValue="is" type="string"/> + <argument name="value" type="string"/> + </arguments> + <scrollTo selector="{{AdminCartPriceRulesFormSection.conditionsHeader}}" stepKey="scrollToActionTab"/> + <conditionalClick selector="{{AdminCartPriceRulesFormSection.conditionsHeader}}" dependentSelector="{{AdminCartPriceRulesFormSection.conditionsHeaderOpen}}" + visible="false" stepKey="openActionTab"/> + <click selector="{{AdminCartPriceRulesFormSection.conditions}}" stepKey="applyRuleForConditions"/> + <waitForPageLoad stepKey="waitForDropDownOpened"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.childAttribute}}" userInput="{{attributeName}}" stepKey="selectAttribute"/> + <waitForPageLoad stepKey="waitForOperatorOpened"/> + <click selector="{{AdminCartPriceRulesFormSection.condition('is')}}" stepKey="clickToChooseOption"/> + <selectOption userInput="{{operatorType}}" selector="{{AdminCartPriceRulesFormSection.conditionsOperator}}" stepKey="setOperatorType"/> + <click selector="{{AdminCartPriceRulesFormSection.condition('...')}}" stepKey="clickToChooseOption1"/> + <fillField userInput="{{value}}" selector="{{AdminCartPriceRulesFormSection.conditionsValue}}" stepKey="fillActionValue"/> + <click selector="{{AdminMainActionsSection.saveAndContinue}}" stepKey="clickSaveButton"/> + <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml index c800053fb1d2b..0ea7ec06ca869 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml @@ -36,6 +36,14 @@ <see userInput='You used coupon code "{{couponCode}}".' selector="{{StorefrontMessagesSection.successMessage}}" stepKey="seeSuccessMessage"/> </actionGroup> + <!-- Apply Sales Rule Coupon to the cart --> + <actionGroup name="StorefrontTryingToApplyCouponActionGroup" extends="StorefrontApplyCouponActionGroup"> + <remove keyForRemoval="seeSuccessMessage"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.error}}" stepKey="waitError"/> + <see selector="{{StorefrontMessagesSection.error}}" userInput='The coupon code "{{couponCode}}" is not valid.' + stepKey="seeErrorMessages"/> + </actionGroup> + <!-- Cancel Sales Rule Coupon applied to the cart --> <actionGroup name="StorefrontCancelCouponActionGroup"> <waitForElement selector="{{AdminCartPriceRuleDiscountSection.discountTab}}" time="30" stepKey="waitForCouponHeader" /> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesCouponData.xml b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesCouponData.xml new file mode 100644 index 0000000000000..99d02998fac23 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesCouponData.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="ApiSalesRuleCoupon" type="SalesRuleCoupon"> + <data key="code" unique="suffix">salesCoupon</data> + <data key="times_used">0</data> + <data key="is_primary">1</data> + <data key="type">0</data> + <var key="rule_id" entityType="SalesRule" entityKey="rule_id"/> + </entity> +</entities> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleAddressConditionsData.xml b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleAddressConditionsData.xml new file mode 100644 index 0000000000000..cc695b347c4fb --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleAddressConditionsData.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="SalesRuleAddressConditions" type="SalesRuleConditionAttribute"> + <data key="subtotal">Magento\SalesRule\Model\Rule\Condition\Address|base_subtotal</data> + <data key="totalItemsQty">Magento\SalesRule\Model\Rule\Condition\Address|total_qty</data> + <data key="totalWeight">Magento\SalesRule\Model\Rule\Condition\Address|weight</data> + <data key="shippingMethod">Magento\SalesRule\Model\Rule\Condition\Address|shipping_method</data> + <data key="shippingPostCode">Magento\SalesRule\Model\Rule\Condition\Address|postcode</data> + <data key="shippingRegion">Magento\SalesRule\Model\Rule\Condition\Address|region</data> + <data key="shippingState">Magento\SalesRule\Model\Rule\Condition\Address|region_id</data> + <data key="shippingCountry">Magento\SalesRule\Model\Rule\Condition\Address|country_id</data> + </entity> +</entities> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml index 595aedc2b604f..ae4f21a315a27 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml @@ -176,4 +176,31 @@ <data key="apply">Fixed amount discount for whole cart</data> <data key="discountAmount">0</data> </entity> + + <entity name="SalesRuleSpecificCouponWithPercentDiscount" type="SalesRule"> + <data key="name" unique="suffix">SimpleSalesRule</data> + <data key="description">Sales Rule Description</data> + <array key="website_ids"> + <item>1</item> + </array> + <array key="customer_group_ids"> + <item>1</item> + </array> + <data key="uses_per_customer">10</data> + <data key="is_active">true</data> + <data key="stop_rules_processing">false</data> + <data key="is_advanced">true</data> + <data key="sort_order">1</data> + <data key="simple_action">by_percent</data> + <data key="discount_amount">10</data> + <data key="discount_qty">10</data> + <data key="discount_step">0</data> + <data key="apply_to_shipping">false</data> + <data key="times_used">0</data> + <data key="is_rss">false</data> + <data key="coupon_type">SPECIFIC_COUPON</data> + <data key="use_auto_generation">false</data> + <data key="uses_per_coupon">10</data> + <data key="simple_free_shipping">1</data> + </entity> </entities> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml index 8c364879ab1f4..9e83d3045b942 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml @@ -34,6 +34,12 @@ <element name="chooseValue" type="button" selector="label[for='conditions__{{arg}}__value']" parameterized="true"/> <element name="categoryCheckbox" type="checkbox" selector="//span[contains(text(), '{{arg}}')]/parent::a/preceding-sibling::input[@type='checkbox']" parameterized="true"/> + <!-- Conditions sub-form --> + <element name="conditionsHeader" type="button" selector="div[data-index='conditions']" timeout="30"/> + <element name="conditionsHeaderOpen" type="button" selector="div[data-index='conditions'] div[data-state-collapsible='open']" timeout="30"/> + <element name="conditionsValue" type="input" selector=".rule-param-edit input"/> + <element name="conditionsOperator" type="select" selector=".rule-param-edit select"/> + <!-- Actions sub-form --> <element name="actionsHeader" type="button" selector="div[data-index='actions']" timeout="30"/> <element name="actionsHeaderOpen" type="button" selector="div[data-index='actions'] div[data-state-collapsible='open']" timeout="30"/> diff --git a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml index b6dfc00c0ff9e..dfb33b197d74f 100644 --- a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml +++ b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml @@ -7,58 +7,60 @@ --> <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="AdminCreateProductsListWidgetActionGroup"> - <arguments> - <argument name="widget"/> - </arguments> - <amOnPage url="{{AdminDashboardPage.url}}" stepKey="amOnAdminDashboard"/> - <click selector="{{AdminMenuSection.content}}" stepKey="clickContent"/> - <waitForLoadingMaskToDisappear stepKey="waitForWidgets" /> - <click selector="{{AdminMenuSection.widgets}}" stepKey="clickWidgets"/> - <waitForPageLoad stepKey="waitForWidgetsLoad"/> - <click selector="{{AdminMainActionsSection.add}}" stepKey="addNewWidget"/> - <selectOption selector="{{AdminNewWidgetSection.widgetType}}" userInput="{{widget.type}}" stepKey="setWidgetType"/> - <selectOption selector="{{AdminNewWidgetSection.widgetDesignTheme}}" userInput="{{widget.design_theme}}" stepKey="setWidgetDesignTheme"/> - <click selector="{{AdminNewWidgetSection.continue}}" stepKey="clickContinue"/> - <fillField selector="{{AdminNewWidgetSection.widgetTitle}}" userInput="{{widget.name}}" stepKey="fillTitle"/> - <selectOption selector="{{AdminNewWidgetSection.widgetStoreIds}}" userInput="{{widget.store_ids[0]}}" stepKey="setWidgetStoreIds"/> - <click selector="{{AdminNewWidgetSection.addLayoutUpdate}}" stepKey="clickAddLayoutUpdate"/> - <selectOption selector="{{AdminNewWidgetSection.selectDisplayOn}}" userInput="{{widget.display_on}}" stepKey="setDisplayOn"/> - <waitForAjaxLoad stepKey="waitForLoad"/> - <selectOption selector="{{AdminNewWidgetSection.selectContainer}}" userInput="{{widget.container}}" stepKey="setContainer"/> - <waitForAjaxLoad stepKey="waitForPageLoad"/> - <scrollToTopOfPage stepKey="scrollToTopOfPage"/> - <click selector="{{AdminNewWidgetSection.widgetOptions}}" stepKey="clickWidgetOptions"/> - <click selector="{{AdminNewWidgetSection.addNewCondition}}" stepKey="clickAddNewCondition"/> - <selectOption selector="{{AdminNewWidgetSection.selectCondition}}" userInput="{{widget.condition}}" stepKey="selectCondition"/> - <waitForElement selector="{{AdminNewWidgetSection.ruleParameter}}" stepKey="waitRuleParameter"/> - <click selector="{{AdminNewWidgetSection.ruleParameter}}" stepKey="clickRuleParameter"/> - <click selector="{{AdminNewWidgetSection.openChooser}}" stepKey="clickChooser"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadChooser"/> - <click selector="{{AdminNewWidgetSection.sortById}}" stepKey="clickSortById"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskDisappear"/> - <click selector="{{AdminNewWidgetSection.sortByIdAscend}}" stepKey="clickSortByIdAscend"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> - <click selector="{{AdminNewWidgetSection.selectAll}}" stepKey="clickSelectAll"/> - <click selector="{{AdminNewWidgetSection.applyParameter}}" stepKey="clickApplyRuleParameter"/> - <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveWidget"/> - <waitForPageLoad stepKey="waitForSaveLoad"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="The widget instance has been saved" stepKey="seeSuccess"/> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateWidgetActionGroup"> + <arguments> + <argument name="widget"/> + </arguments> + <amOnPage url="{{AdminNewWidgetPage.url}}" stepKey="amOnAdminNewWidgetPage"/> + <selectOption selector="{{AdminNewWidgetSection.widgetType}}" userInput="{{widget.type}}" stepKey="setWidgetType"/> + <selectOption selector="{{AdminNewWidgetSection.widgetDesignTheme}}" userInput="{{widget.design_theme}}" stepKey="setWidgetDesignTheme"/> + <click selector="{{AdminNewWidgetSection.continue}}" stepKey="clickContinue"/> + <fillField selector="{{AdminNewWidgetSection.widgetTitle}}" userInput="{{widget.name}}" stepKey="fillTitle"/> + <selectOption selector="{{AdminNewWidgetSection.widgetStoreIds}}" userInput="{{widget.store_ids[0]}}" stepKey="setWidgetStoreIds"/> + <click selector="{{AdminNewWidgetSection.addLayoutUpdate}}" stepKey="clickAddLayoutUpdate"/> + <selectOption selector="{{AdminNewWidgetSection.selectDisplayOn}}" userInput="{{widget.display_on}}" stepKey="setDisplayOn"/> + <waitForAjaxLoad stepKey="waitForLoad"/> + <selectOption selector="{{AdminNewWidgetSection.selectContainer}}" userInput="{{widget.container}}" stepKey="setContainer"/> + <waitForAjaxLoad stepKey="waitForPageLoad"/> + <scrollToTopOfPage stepKey="scrollToTopOfPage"/> + <click selector="{{AdminNewWidgetSection.widgetOptions}}" stepKey="clickWidgetOptions"/> </actionGroup> + + <!--Create Product List Widget--> + <actionGroup name="AdminCreateProductsListWidgetActionGroup" extends="AdminCreateWidgetActionGroup"> + <click selector="{{AdminNewWidgetSection.addNewCondition}}" stepKey="clickAddNewCondition"/> + <selectOption selector="{{AdminNewWidgetSection.selectCondition}}" userInput="{{widget.condition}}" stepKey="selectCondition"/> + <waitForElement selector="{{AdminNewWidgetSection.ruleParameter}}" stepKey="waitRuleParameter"/> + <click selector="{{AdminNewWidgetSection.ruleParameter}}" stepKey="clickRuleParameter"/> + <click selector="{{AdminNewWidgetSection.openChooser}}" stepKey="clickChooser"/> + <waitForPageLoad stepKey="waitForAjaxLoad"/> + <click selector="{{AdminNewWidgetSection.selectAll}}" stepKey="clickSelectAll"/> + <click selector="{{AdminNewWidgetSection.applyParameter}}" stepKey="clickApplyRuleParameter"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveWidget"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="The widget instance has been saved" stepKey="seeSuccess"/> + </actionGroup> + + <!--Create Dynamic Block Rotate Widget--> + <actionGroup name="AdminCreateDynamicBlocksRotatorWidgetActionGroup" extends="AdminCreateWidgetActionGroup"> + <selectOption selector="{{AdminNewWidgetSection.displayMode}}" userInput="{{widget.display_mode}}" stepKey="selectDisplayMode"/> + <selectOption selector="{{AdminNewWidgetSection.restrictTypes}}" userInput="{{widget.restrict_type}}" stepKey="selectRestrictType"/> + <click selector="{{AdminMainActionsSection.saveAndContinue}}" stepKey="clickSaveWidget"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="The widget instance has been saved" stepKey="seeSuccess"/> + </actionGroup> + <actionGroup name="AdminDeleteWidgetActionGroup"> - <arguments> - <argument name="widget"/> - </arguments> - <amOnPage url="{{AdminWidgetsPage.url}}" stepKey="amOnAdmin"/> - <waitForPageLoad stepKey="waitWidgetsLoad"/> - <fillField selector="{{AdminWidgetsSection.widgetTitleSearch}}" userInput="{{widget.name}}" stepKey="fillTitle"/> - <click selector="{{AdminWidgetsSection.searchButton}}" stepKey="clickContinue"/> - <click selector="{{AdminWidgetsSection.searchResult}}" stepKey="clickSearchResult"/> - <waitForPageLoad stepKey="waitForResultLoad"/> - <click selector="{{AdminMainActionsSection.delete}}" stepKey="clickDelete"/> - <waitForAjaxLoad stepKey="waitForAjaxLoad"/> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDelete"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="The widget instance has been deleted" stepKey="seeSuccess"/> + <arguments> + <argument name="widget"/> + </arguments> + <amOnPage url="{{AdminWidgetsPage.url}}" stepKey="amOnAdmin"/> + <fillField selector="{{AdminWidgetsSection.widgetTitleSearch}}" userInput="{{widget.name}}" stepKey="fillTitle"/> + <click selector="{{AdminWidgetsSection.searchButton}}" stepKey="clickContinue"/> + <click selector="{{AdminWidgetsSection.searchResult}}" stepKey="clickSearchResult"/> + <waitForPageLoad stepKey="waitForResultLoad"/> + <click selector="{{AdminMainActionsSection.delete}}" stepKey="clickDelete"/> + <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForAjaxLoad"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDelete"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="The widget instance has been deleted" stepKey="seeSuccess"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Widget/Test/Mftf/Data/WidgetsData.xml b/app/code/Magento/Widget/Test/Mftf/Data/WidgetsData.xml index 26864c60b6494..d7db8fe50cb7f 100644 --- a/app/code/Magento/Widget/Test/Mftf/Data/WidgetsData.xml +++ b/app/code/Magento/Widget/Test/Mftf/Data/WidgetsData.xml @@ -7,7 +7,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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="ProductsListWidget" type="widget"> <data key="type">Catalog Products List</data> <data key="design_theme">Magento Luma</data> @@ -19,4 +19,17 @@ <data key="display_on">All Pages</data> <data key="container">Main Content Area</data> </entity> + <entity name="DynamicBlocksRotatorWidget" type="widget"> + <data key="type">Banner Rotator</data> + <data key="design_theme">Magento Luma</data> + <data key="name" unique="suffix">TestBannerWidget</data> + <array key="store_ids"> + <item>All Store Views</item> + </array> + <data key="condition">SKU</data> + <data key="display_on">All Pages</data> + <data key="container">Main Content Area</data> + <data key="display_mode">salesrule</data> + <data key="restrict_type">header</data> + </entity> </entities> diff --git a/app/code/Magento/Widget/Test/Mftf/Page/AdminNewWidgetPage.xml b/app/code/Magento/Widget/Test/Mftf/Page/AdminNewWidgetPage.xml index 8eb0a5f65318e..d495a36f68d0a 100644 --- a/app/code/Magento/Widget/Test/Mftf/Page/AdminNewWidgetPage.xml +++ b/app/code/Magento/Widget/Test/Mftf/Page/AdminNewWidgetPage.xml @@ -7,8 +7,8 @@ --> <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="AdminNewWidgetPage" url="admin/admin/widget_instance/new/" area="admin" module="Magento_Widget"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminNewWidgetPage" url="admin/widget_instance/new/" area="admin" module="Magento_Widget"> <section name="AdminNewWidgetSection"/> </page> </pages> diff --git a/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml b/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml index adf234baede72..37ad425114d5c 100644 --- a/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml +++ b/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml @@ -7,7 +7,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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminNewWidgetSection"> <element name="widgetType" type="select" selector="#code"/> <element name="widgetDesignTheme" type="select" selector="#theme_id"/> @@ -31,5 +31,7 @@ <element name="widgetTypeDropDown" type="select" selector="#select_widget_type"/> <element name="conditionOperator" type="text" selector="//*[@id='conditions__1--1__attribute']/following-sibling::span[1]"/> <element name="conditionOperatorSelect" type="select" selector="#conditions__1--{{arg1}}__operator" parameterized="true"/> + <element name="displayMode" type="select" selector="select[id*='display_mode']"/> + <element name="restrictTypes" type="select" selector="select[id*='types']"/> </section> </sections> diff --git a/dev/tests/acceptance/tests/_data/magento2.jpg b/dev/tests/acceptance/tests/_data/magento2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d0b76b45d46bea8c18c0df47ed5d2706ffc6ed90 GIT binary patch literal 73131 zcma%h19)c3vi6r`V%xSSwrwX9+qUgwV%yfl&dkKNZQGgX&+K#0-RInY?{jZ|&+{$x zT2*gXS9M|a`{Mg106|hzLKFZ51ONbi`~dGO0HKeo|8oQWvw{3~`%UCuC;$Tk0R#Vc z=l}KOy&HfC0rVO85d;Vk0E`F(f(Z293&8sT`9bW1I^drT3>*{^<P!ui)bD=W-yPup z(hC3pgM0!71BU>Dd|w8@fPB1;0D|x_+CjVKncP2Un0OV|<qe!dH?ho?l>S_?@iV1U z?OGGz@iiZ^{89(Q)~NSSR)2W}X-U!>sa<-YQA=hgPL7<I$o2zY=n_A_x1k`ev~E@L zWPF;D`O7!|>n6}(h%IRTWO?5WY9a=-%1RBD-NzUVtTJEcXF>~VDvlh&gk61@`Tqlr zYbDsW@fe)s)k?Q=suyEaJAhtiWyEN8rc~p@OFRIRa~Y?~`jhO6XC=W9(Ug|l-_wE_ zNNkd3e+_i*Z@Vthvv!pS^m!`jih0&pdR>a1@2`cM`Q&6sHBAuKySyX<Fi(mqYFTBx zEru-3>R|X9|CTP?vPvM`kjbjOP4AGxip<V7h954zDL+xCutDof=cVlV{s#V;HRd;1 z%o*OMxt01fn0ajBV`x5o0LVFok(wo+j@I(<TH?YCy|?h>-w47xFXN|K{jq)Knsqyf zRok7B9Kbn&Aa0$|Ub`g@LgoN()>QEdU(%U7AmpG0PwgD03U<&1K~}$hY_q{358zTV zIH7g+MUpy7UnW72xc!Cbule9f5;iZ@8YVy&MWTynN95H2?DK=fTmt&sPYM8T^e#89 zu}xj!IO0v9C2ipI>5h2-+}^TPH1kZ>nr1(kDrYdorVxC~u6g-gd$(%v+`nQ1ECdUC zrWxoU0tU9=@&@qg3=?tk6LUWz2Y@MZ*`)vK2`N~B=HC+GXx4Sv81|lcNstHN-lpB6 zsXMVwl=%5d-%Za9z&OdEIpRgM{1pR$_*QP`$)z-G^(i2-&%P$VbNjUv0CSP(@(E1} z{_{c?;gOHVHKHog%>sDq(@a@eCAQ5LPXz!d;X3<A$PiV?DqupJmRSVM6(^$Oe__Co zJ*$ei%Pwc}1B7jl<PDo98Vl$ccBa6awE4CLAm8@g@(Ew)7RQF_*BkXZ_Hr%&37^7x z$ZhaLw~E7Z$9Lk?{QimqBn~rrrw^om<64j?b(TJ!AYjy8iZ5h@EMy(<ASUfFmz1ji zMKPGJTV9w36Ye{QW;Op;5j#H-k6uq@z#xfsT`d9Af|LxRK-K_J&0jG9AhDa#d%Pfn zShhoaRpadD4uafewFHAE05}ZL8}iuqzv)%wO<f)5cwH|<wdX%%w%m;jz9vK!Q~^W` zTIQ~Hb>T$?h|ZG2fwJ2)%pzdUIZ)mHoexv14d`*_$oNBuZ57!<hb<|qoG8S0Q^lE7 zxM`Q#9M^d3yk^lsE&veMBjCBPnToBw_+c*dh`R*p-wbacto48r+g4l%=e<0i8YEs( z5<rlAj5@P^+1yOPUO%V`)P5}lP=vRpKoQ1@w;LPqn^q^M69sZbi|G723{6i{A*-7L z4k%5=F94u<ZpcbDwV0=ZdIV_zfW&T6=kAg0Lk$rzs-1(}A<(QaUb?Zr;3^N|xTc}0 z#aM$EKaSj#7QYg>f!w5^oIO3^hqNwK>bIcj|DK6_vVARzkpC$L;P_FNhlwuHbF`cq zo_=LTqq5^2HEfxgFw-gOqGWvMTB$16Hbc4}m7}h-rz2UJ1uZL!)wX+MaV15`BlEo8 zY$Yu7Ykv%a>j{j!`Ml@piY<!(ehZBR+zjOF92@st5km<l#@`E0g^5vopc#6Ab^e+1 z@LX`xHt|{P9$(M>B8%rnlvnPR$P=&Y7GKGIL-Q=7OO@zvh5<6MG+r01eTuupf-Xv$ z{nXAk$L`UF$<ubRlEim0;gMi8T$PgUOa0R*`MIHb!6Rg?l6Pqq7v!Mh<cqxsjLnw$ zolX7f=I?=k7dJ1^T2;8-$m1%hwb7SQRPjwJb~FPovq@hC=V@Dm3_oVOR`Uv8Tmpc_ zU_mc*13uPfeZ+`^FJiI&CO>0F2^;;DrH6=lSLt7F)U0gtIp`7hcoVXfyPg<t*5*nU zyZSc05ogvd=QTqvlEVD{MlWy}M)m{rwxV^Jkv~AO8>WnX`tgtd-ylKOaE*VicaPmQ z8NFpYeA~ks+|kX4C&KGJeB)gNTb}%(h<4Ic-jZ`f001Hm33#aa`I)u-TUeiCCGjAC zz23k+!t#%DGNZU08}v8NiYB(nmz0F-oTE*Q+c$cHOS;gbcL3`N&rjFkUVaqe)sbL? z|7fN_cPc^;j`c3*F6OwyV?X6okbk<kiP0N&y8qBA8UCin-5PO#wZ03V%MbVzjRn2_ z{nL--^KN3)@ozDNz+-{<sX~^(rD^e^-Yq)nP0dSUVh(6>!@ebchBJ?ykveT4<zCO# zO^qAWhyVp-M>-TCf;gAqUcWz=J)r$H|8nm7x`u-W7QB}>2Ap3ZW6s}WS*xS&@ygay zs?lrxehUo%7*JMJdl<Et>c9DY1<c`7a@ZxjIKDEO>4()i*(t$)1eg5xmWC=k8c)bt z-n9PG(>0b*Qs<-e4$v`U-bjm)+rFno+I*(6pU6@;)WC5A5|$ZlQGf{$S)oP(;c;r1 zLcv^hpt=MPaJ6N1OR#f%C~=-&T`J+a>o5PM8D*xmqG?4e;&f5lD0+V+TN0{WsRS)^ z(@9pSPfh#CIpWgwki)3cqSJrRTDm@EnUv8<R>CG`_jr$2xWsKQ5S=4MoZB|3-qmnv zb9?|>cY|)>QZ(K@H)(iGX{<Uj2ml&$tb6t?a$8u*09StXb9clTWBER~cJoqP^&bXH zvKpz@2{PKk6I%9NqhD^=%W`5!{nsJG(Th&slLld`jd$C+_2K$$*^j@Ol+CP_vDk{v zz+r4QsSD~TpD^>7DmS|B+TFLp`IV6A{u^A&TYU0Ww`|w_s^GF_z9HN*A(OTpUF@0E z3mP{48FdrzHaPK%C_CG8<tj!TNo2Qy*PB8Q)w%w9S|z6eOexxwayj~iXY+ly`AfWu z{c^d_3oG!GuR;usy3R{o?vAulnn$$0jra6<oT&iPrSaM&bZPsnBe#Jcct#&I?5~?0 zoGQ}@{$2)H=gYqqG}}kAnMzKB;-BPZ+@BcuG(&j!YEE^dx1nwfHC*jZb){_){>Fwc zWzY6U?_fuIG7_?Rj4NGLk?J<Ib!%-EPP}`Ls@$6ntroOI=v;I`&%11v;W?b&0hvs% zDp)NV-aADed=lD$Hy181p10@{+Wt2;W?Fj-cP?U~Pca6^w55)^M&>1zk6fNw+P9`U z&oilYPIoD2lv!>EmWNplH{g~>AhkXoXZRV<P<y>MV=(v4V`qo<DN_<MP1m3!El(#V z+Mq+%OYwe3+354_3H@3di@GT2`~blJ=gdJf!1z9g%yS|a9!gcZqaivp!_WUb{7(6% z<0CgQPZ%Rt_8NKp$(uWQTBM6t=9Cf^*Y2-f4aiTf<_xA;HVWJeoXhB=1KjTG9LGmF zC&!%9yRNMo+m=516g}Q|z|<>4Wp>!Eb==K6Ky{&c)gW}tx%VSbJRUP`t#44nWpT~{ zw^mB`q(4}6Xqc;eR=H7p<?F-l*!iBv`LLeZOQ$8reKo!?#r2^W3sdOkUOTJ06c5@( zQTv=oXE_;~<-4-4-%dIBZ^<6^&QdPuOa}c_QPf~<Zw}N|G%`H1{r+AE6sr}8)8um3 z3s8i=SXj09_+}=CpMK>s9KC_x(~lwE-jMI{u5-*3y~_AoG5m)$z_ipx&4ln93+Aks zSZ6ieFP5^dtnKf=)k0{$Z3kIJ=<pQ8;-uN@dViLqrjC1{))^H#{lvX0{~bV-+h)Zj z><pK~Srg*zUH<SD=`Pyj@EmV=c4g<^<v@Q*Y<&D&;12RzbKbNTe|Zo))oEbt>+i1x zmb0-9X&=4btduz1bj@%gCwOk+Sr0J2+Fozi?AWjVj#U57Z}iE<?s!lgN6T-Cd|<J4 zM4WIL$+oa9$w?)?{be|F=NxDa3vG-cZLw!E^d#37ZE#$%f-(w<$i}!O%m|5YRVfbh z5fcl`o7xXn{6Ixh3UVctt-l)WnMj-ec1s7@z<rTzadekJlB7wm!?V0hH!@JS+DL2l zYbgFWu(+k&$m<<2*P$S=1KXTON2AGdPgk;`Bf0cTQNp(E*UT524iOdfY^E{YV{glr zqtW_LXm8Y+Za0=@tz#*~w|KDiUf~T5A*}fc3m(m`2WHjkG?9oa-}PRev&Kt69#s!8 z00;$Yt6aA|Pei5v;;ecg&$MNjNKeS1Sz&ziVJn@<Bwya{Ch+<#DP4@m*Q`%hj89Cx z*?OPUax#!_v&E3_az?ewb}d$1YTATi3hTHx28ixeWkvY^VH%dhQqejzU>-;2`4mho zyDFO>@&*7Fq967y*iK=UIO~mYrj*X4vN@AYjfU+QRPPf0cmQ7aHWcp^-RkfoD+N&R z$aVaGIDa^Mr)l?je>nPjf<K)6w6BcAtEPLC4S3>nLOY|*n8$=asS2}icKt2&c2nbS z+s>Zs{?;|Em@_@X_>r;ALQ2~YC?Xr<7jpTf19|l)1pogLGP}oK%X$ve9mM~@2g6^k zBvHvU^ZP&pv3KU!#B05VI{!N-{A+ALe)r9f;@tTlDj73ks%o6xaW@yLF}mmDs`V}| z1=G$VtS5!aPYa4ps6ZvZU@TWWuV0pomO}y$?WiHHv4OG4eTNACM+6TytmA=2lM>ID z3jq2N365$)zMQl3fdJ^iq23cemqU)=0}YTrclfxr_e&Bt08DAva=Zd^?88_->I1zD zACZ)9yO6HZ!ia8b!+`MD2$2dV)cNGc13ve|?4t0Y4xk;G);u~aF1gk_)sHwAd{ut% z{N9R`rTc5qSgp_OYu=c<#K<l?dj-3K9{}IXF?QO7yXg6McEJ}ax6}e}>A^exaq-W_ z5i6%(x00g86(PI|TXzfqxa(3jKjkp>yoQ#u<>D`7!>)BKUEq>V`@z#&jZq)NIB-29 zj{X3^+>OtT@T!PejOr=qw8R;`luQVTE%LKxZxL1YJgZ#a0=VA9lX`4^^8t#vcT%N} z{U^(hBbd+-n$xIFe~aOla;z6)Tr+?(aY#)~Mri(sN12~nr2WlCv(tefoG{a()imYe zxuo=YFQOlqY+WR^wt&{kwMM^=M9jV0M2|@Nb;7TYk%=u$iC6OjZCndiWB=Ag0E};a zeyx_@@6&&_2_X^dxowBG3Q^z%mlKIh01m|F)Y=xJ_M^hAyDXxO!BUs+Ff|=CnB1@( z05@&g@j*&jG5JDK(Kr>Of*(oTjB#BF05~BI6>|o_y#wM&?N<DL(>8p~LXFt{Czn5t zZyhN;mMwJbE~1=fd)aq42f0AA_eOBkv#Qil{-E)Ou$6${b)XA0Ehkjw*LH@F9R7_5 z07=4(X-nw`aE|LKX!8U29YSic{_&e0!`pa7pTi%N|M>?r8t~(Kz-o;Fu9N$Pv}ynM zea!Zx`dS5|;>NwZcR=$viXPxoJQhrI;tv4l;MUX{Kgjppa4pu&-;ZLv158B>T7RGZ z*+2+)BL!_(4AgeqBA`P!<G+0<Be9xTN=@^E6DFxHHR-@J_s1<hSTxIFxkJDkF8m)f z{xi@ISdV5^$D@LBN|rGi)h|fJe&F0oir7{83^GooX4fyDRB}Hc`05yS#ARPc*6)IZ zqrp*ih&Afc|F3`nK(FqfLl*2-zpWojd(5~!-E7o&#jaJpG9SCFqzP@e`QrKg9^SK1 z+I33Gpq;(S6*1sHD(3H}0gk!fRH+RP!mbH+#FSPTkDt6ZMgQpd_;VmUbEhvjompR2 zu7<WH{_$}O{Id61>0fId@E^rSHUt0w6a)kS4)Iaq1pWQ?AGJmR5I6z>Ga~Y521XWE z2qY9#G;}6;emz2BauQN9HnEQ~B*aI75(pId4cLghywExr*^`)*C0a}0e>SNf4ZDC; zCYcq*c{Y!S{*F<cg+IO_Q5z*N7g!6a%5(j>%5yoOg=}#amq2-1A<-VJ1pRh1NT^NK zb3L9dOz$MVumJY+);(#uKJiI>MUnM30F`NdmLLrMEjg)lrwrM-%BKp8Q@(PKE3t)K zMx<+fKejLlZ8=MQsG>r&F1guhy0{?xEj|tMxD4A|cztkK&M*Z=U^!18va)FM=4j|A zmj)S4Kxr$biEq#Z2rQDjSg1L5KY})<Bu(wNg6bm3Bj5aZfs$})0plqOdOJunh{g+h zUHxMTIb*v0G_}r)Vug4w!SsGc+bAi1j1eT$$mK}p`oOw{78}%D-T{J6zp~tVifvc| z-AQ0XxFhuo57BkP&`QzhDE66wZ!?i3R63v@2tiy&L~gX<d4ho|_%b$BSZcY-po2~{ zGmszj<Y9pP7Zv(rd26wTzpN90D)F2NLiqcx7g@6Z65i0;C>M`Tq~Q>q`brus)mJ5o zrQf}jwBEMo0(zkadR9!~^VoD2lx+?yeF-EljJRUp97<kYMl`Ikp|7o^-s^K!J>NE4 zi1Zn&Lk(<XeZDTmVWc1tCs)%dc0F%RdsUmch1MgoJ%aoBjSZo2_d)txA<1(2eo^u2 z3se{hz^Spa`9Vf&+N<3lMG>+HYC_4oEMu0UaI9=*#>pv7ywGPD$tb{~J58X{FmO}~ z2}UVywVdU&k<F1|8!DMmG7$>dR<`nXLHbx=MfUnY$M!tEweZO$Vh)s0Qms^}l@k=o zoXNm}WnhRCdWA=9zIi&i;b1z53fY#w@_Kysg3fvW?WiJMF2(GwZ!rrhm^rPA1KU8N z3o|;UrMZzDPrqLzbN+t(QHGEFu{cV(5y6>4IFEI<qv~In#b{}3D8`zx`te>)q0SPI zs4KK%GP2ehYn`9M8;RNEi^VvEps55i30g(R{&?Q2q%Kvdl~MqkJDc)Gx^Cne$#VZ* zYHOu+=qXF2WgWkLSm!|pk3uycE^4hWm|swrX^zC3Y4^ldzp8NNryZ%roI^Kt5?Miy zZRtIXoHXE(f%lzmzZeXyWkiHdFlr1Z9IBE$HK7b>cL=)H)>HcGFeRyMUF&4*m0Oac zTDCDOt}&eMCY(~hvBTSB(gpUn?8U0-Dm$!Y*z<vlHVsO@2<NMO37cMKTJM^Jl2D3Y zlwjb(6TmFplfW5PXqdnYch|_;8}C?)Pb#18*d-moE|B&6auADLs*9H*HrX<&nJE|F z^kY4qPX8-!7i=*th{nO?>7eSMR%(_uv~tzj%cw<@tY<QxlbxehZZTVvno5}u3;l4i zx80d5Qc4@B6RDa_5<~f{m^y@>#iHQ}p%BTnGKj(_VwhvD+ne?KYI9!gf(3L{9oFjA z;Q_ygJ=8OJXvfeo1|1#!!7At2Yb#q^mK}<das6SHM4`!-jJR)qf=+11Np(2bZ`JfH zkVMbS+(0)nFIK6fFqDX7W?#A{=nlXokgrp&{M03861T(!ja&ht+{Tge4yd{|v?vZ5 zH^AnKn5t!$w0s9tNPZ%7XO0@{GV!&I#5!#fDmy?)O|#k><}X=CW$xmrD+?czNMJch z*6hxU^!#($)NG6mHJyga_L8cF7ntw$;~J`<O&f+C4Bo~Zho}}iBqC5|)Ro4O9au4U zrs%1aI$9Sp8c362h1CsD{nU+D<k8e!&aM=jlJ~DyHOH%889ifT`x|@q(@c3B*xGM> z{#`!(&F_Gr^0nodK6S-+z(e9{REXQH)bKwvKco2WB05sFFr7;HMTDJ~i!-H~#sJ$< zwN>9cVc?<BQR4axucfW-s?lX%l?%O{yu#Ij57p@EcJSZ8)f0`O3yD8(zXRkY<cmg~ zW68jT(z_nN&+{fxr_}qxYBUTYvy2hLOSOL?ffxt`M)2d@6?^9Cy0F+!*jwhYcQ)R! zI2&c(PDqMjB+a~T)NIRF4-wzVEcKxpP3T!<oDtluW?eLm5&nnO*^Hj_K8bxt@0P=d zyHZcv*eRSQi;2e#@}?9klTG|yzWsLXOqrCD-%Pguj7%0)in2l^uC>9yHs?X@Wn_!R zMs|QO*Qd8grIbo4g+xe++#{8ALQ+MLszZPQotkV<c39ij*+Ge~jsxBS`gKakK1bve zA;5Vsfq=QF^OtSse=QbsDNz-J0ia4lWievgds|k6Nj7`=Er<eO1QTXHRAUugd=x)T z$op{;C<zU`|1`p-EVo$Em~2D0m8!*!XhjJ~4GC6rwo$%q;}86NDc(fG(*x9l<5Zsm zFCj%cf8kvII0c`#DqvPZw~Z3bE|INC)gG#C1EQd#di_{MhujJEk_YUiLj<FRP~!RL z4JoJMgB)YTe&8f94bSCg?k1VW9(Hd<6_F1cK7oCHi90IG>pRmS`N}7jxZ8S}zst%v zR6LTC>k#&neT0O%ZLD(ChQ53|R&{ZcRmK6>g`Yy+p`ab?PlIbsmL)M!Qhx_P?M8{C zku*ERJ`d#rnpY+9!vwzxQQEaP#IHSC=x||{W%K;k+SblggADHeX)R~hA?f<99zh>0 zy0M4BV*=ur!dz6^AyA58n1raw<Dv+Irwmct_V*i*aau_mAFQ}07HaQArdrlK;i5jn z?!3<;agb~SD@;Ox=EV}>62z%TZY#wfW36j`ncJdLK2$PQ?gN#DKz&{PdDy{q!kOL* zyQ~tWA!!jxD=HKqBAmM38Rfl(=^WmpMKX+;hMlTt;Sqq8KtEKVpbsJR-C%Bve5Ao+ z`*@^(O3{x*hS-_q>_S@a=;o`Z&6Z{%`(;;VfzXvKF7OCkg=;e{N28~dzid_8pC($* zdG*;f))Xzq>7<PJs5V1vXlp5Q-c4*eEnWFrLhU>+Oo#YY2PJ|7mNG;!d(EV}qKzh% z+Z#T|Sx_Zf+fIbxJw=i%1XfreC8aXAcVXb%6%7WfNaWVo2{~R&;IHobPQ8dxyvUxe z`Z6p>BymD0KslitxW7_|1pBneZBVydTy`9h5#?Rn2v&jc5b61}P&@0N6PHFQ5l#ql zWq$hO$`r=d=D2^)X`c!|a#`SHkG-pE<BPZ6TrV@zK)#M@iMwMBn~C$b-UY#+H9lvU z*mCdvJ>b(f6MFS6d`Xk0q)&W?+yN0(O5?^cvB|>0Qy|rp2qGY33OA8DeR0MNySi@0 zPHX9fMmU4lOO$3Q{VKid1?qW(W~S;|@MDkG{-b*zv-a~<Uw!?Xh_SF}Rj`N=xybr9 z2X2$jR_eA%iEd1tc79ordlXUAB8q@?)lR#(mi!S@fKe}zT;;rn5J06;vE^;*PdhB! z10UTsaVx8;vh1EPRb?^|YIZgZcn2K(9w24rk%H{#$r74oS8coL2$9C0GghY#W0{(Q z8qOw+q%+@H@Howf?OI01Y>?R6mp^ue_8{5-Pm#2_9|#+@J-F&EoP+&sc-4@h!CrN# zVrnA(*>Dj$-nP+>YFNJbOQT)}6%u(eS;=k0r6WfO^H%CKhy+RD9%nPs`SM&?fi;{8 zO%RK8IXQ$y$YcOw@CNk20!7G95W?L+tYo~lU4y-Tsa+r<8EX@PnS=yVW}~$I*RUJa zpF%st=u@>GzgX#YMf*+;7dySN(aUgDIg>(OpZ^GSB4;4g!4ma}rc$Ev_P$)>DQRO@ z8J1pRVdhH(`9GEbIe}W(t8yH_y+&EsDVzg86h#v`dS?$vd6_haBLXW{Ap%Jz2yuH5 zlFXo3xTbL4`1;4zdd4@CP+xzN{5jZ4D?;RNB+>oYVBAGEZeSM?BJckKBRybD{mh|T zT(i;odlD?nN%2svMNtdrv9<C;H_2-6-vQ~|eyjjU#@)=|G0vd9RiZ9V@)GVe&o6S( zy@Z0)&)uDs)RNd;Due@JKKh?$CollSpOQWbe#y9hgjJW&vV@b60M_;SPK@Lr?9e7Z zHuW~zrQuf=pVzUEc;&lA@wW1egCSB<wS(B)_wCs%uLMywr*2`1Ce*7((1+QcDvKnZ zLV3|NZPEzOZgGzzyu!vzw?S!1y)&+nHTTYMPzZ+&Ti^x+pPURVm)khGKEDI}Nk<IC zQ!WS_A7YoE2d%w#S-Y;Wb=_fWIzkrKg)INOgVR_EXQjD-cPNpqCC=eItp3bXi|v%j zL$(o)fcSNpT-iaN$O&=|NtLA4+_5g$lkte}@aLdMQ+D@!OO268oGCaGjYiLDk;oS| zRJ7!;GM1Bm1TJWeeGb!eRhBLxNQhHG5Z@;OKa)vfw9#qZp@AI`gSf6OfM^YS`QHI~ z3&r=P2OTtN_0+M1wT38sAkgcQh+$;(gcvHFPOBy`KNZY_sDSjr@?nDwk><`kZRB>& z65seHV+(f|Y`q#o=ZV!Nk#I8XcacgHufUpeu_Ov3zYOy2RskczNMKQFT7d2*Dj1<R zatb9be2Kzk=hfKZkr=kk=hxU;wF9v``v<wHB0C-yHl8o4$4He0lAq(OKj)7@C3|2R z(kQPX5w;0QV2`UvOd}<7MNsqv&ytGC(yPKIvE!9k@C~$VfX%+~F3Ih@k~@>SGM9^= zY6JuYk%ioMHsc701xvB_2oP+g#ub8IG}d@PAta*O^p#L5wH7b<?E1#ocfppuc-Q3& z{L;f<42nA5YC$Lef;<KD?Y@u*(;sX1u)FqnyJZwJe?8{vob6{LUrdwI<40T~SdgII z`jD%0&64P$l%QKnNI7hKLHq%$lZZ!Q*mkiH!wPpG6&u{b7vHMn>aC1A{HUz;lnB13 zSWOg7UvOF71dBdYIzCgU8=>5uN6tbc4$RL?tTY5+Dx$`t^TK=RAE3I9CC|;3lNK9E zVPU_*yJX#dVx^(Vij-C?ag?ckTVH4~Yg9I<&<J9Hd!BiGHs*`DD@xTD6n?M^qeiBM zA0Ex4kq)EQIVZ%>4~cx-g6$I|&~FNg_=y{=?=mb6-~RIbBgHmB1OR>P9r^?f0R;mI z4GjKKX!@OFBLcvXzzG=y5ReIo7!~yG9sFxiJ~J^p#^wElKqY2j6;w2+n?l2oS5k2b zh)<Z^x+Edx*E4j^ukS`@6A}$7Xz1yko*@e)7Zy_uNi6&$BmdY_#0T^yfQT{5Ym8MB zrom{?SS0EEBLqC7a&gqaP<DaJ)cs-3&X>eojW<$Aa>>|x@>k@PFRktt7V=z6cv9W< zb6zl-!JCF}FEe#@w;}Q;m(~idiLR2sCm~7qTMama22v{XA5HKXrOS8>sb7bfbrIjp z+it8%kRx&%gva{kXf70vUX#PpqiW?ZX;ZcvX9BY08`WDi<DjHw)SBjw`A3&M8nc!3 zfcYU&u=9cap-|BrRChTOZ_CcgJSH8Rs;k5jHSjE0dwT|0zeLRGK($dVn7L;}P0`!N z9lbW0@2|^}%_+Q0D8~CK7YHvCnSm<7alTcE$zbU_IU5fanemSo%#}JEHam)qI`-F? zhdeLCJ>DjAA)8>BV1Ao{4QgV6yM2b~ZPM`mRy<x{vmcXU8lbZV`_Pzmd02W+HOVBJ zqm89*L&+)cNa1XW(?$!=3>F@S>#H4KSP4op$2U3{9x0<jdy#V3;59@c;lWBkir?j5 zy<*jGoQ_L|hrCDKjTiRVaWG~k;l+%=rRY6*q%dAE?kayt|2-3m`0L^bro1AS?^;61 zN4jE~x28AM>#bSZ>cwn^ZQ(s>CO?`tYTYzy+?tPMK*+1;MQ{WkHO@Wnmquz1FCYv` z{4mX;foCw&AeAxqi!Y4<(W>V%O0CbVHDXmVVjf}e^(F%|;VP(+$`WCIyPm+&QccxT zXgJ+^{4m0<6ZJZ&B0mensh4^R+%4+@uR>@f%_KtS$tQ-TxSm3tyNk*gqg<#VKyH>p zrg~qg0IM_3{)QCaVh0U4;h{F~lauoz+PXCrijpKM)=#i0qz0;Ty)ABy$`d+9yOs#t zS>du0obxc1WP>+F=i7wbB_{ICK?{p<#IoK4frI(%j`^U}s)oX*Z%uL+smpk@snL-6 zkKa?PJ60%rw@Yaene3gVxMuB7UX#Pq^XO9Dl+kzCL;R;zD~L2s6$59~?6YJ_m<)3? zVt8$&NGSyc<ztc9n`yqC>__D{g(kDEq1->|#H{E6MwzXQL+(;b8?;N=czz~?ud5u( z?f$xF6!HAp4XftCaoqQfYKadMpFE2v14>Z!a}|qBPEw$XP<DYSwx(qFwwWuHq-uOp zAxZ|7#wk@~U5TB2Q{`E<wK?}vD=jRGU>3raIE|=1E_i;GOK4h41nJo|``z0L^NlSz zvPJ2eUYUeAmBNQLOhOgoC||v*#-uaBSt4vH3pS9LD(vIvL|QXpM!OYZxar0weCHvB z#TjbgW<sb6$y<gLBin1rbpE0#Ba^phj-o73wq2)~2uO?|H@TI=YOfPNDhM(VbS;Q7 z4dA|y2=`zleQXI%;@*x3-{V4ZFO*I;hGYs-!86ypi84(6I-fAQb-*~tkx5lPG6;Tx zE5<q!l{dR(PY@|AIqob}QTpnlfwuguidl7^2s5mBL;)6*IC(|7UppIBln8y7;$lEV zOpdV%h}nc-9=AI+=tyETZ=AgoEi@fOxEBhKuX;#P893g8_d-}&!=y~~X+or+n2?fQ zlox67#u<KfrHu@kX3~sC(ECcr%Y$-mP$qY8a?)85jC?$)f-^>Ek5X|^5?Mt9PgMpL z!6P_!8A}#z8nC*T@7vt}a}ZOlwT_{t&<QRtr@Bw?3k~kq$aSU&y0cz-wjj$Fp@mlG z<aJqeL<O^OjTx2C7VM2n5{eR%!EpPG+OU`+H2Pj<8XxxifdOfCDvZ9{`-<3$Q(|FQ zB-eyc0$!jCW$DH_W_9_)l4d0zsy%qmr6mxe<*!_pd8r7S^|uJI!AK|bp;{;*dla2t z^reJ`V^cm!8wNjiG37cbRX6~V`I$Cw*Y-`x;ZSZbJk!j;e*WAPVN*~XJ4w@VJf{Uo zG83yhvs6$Y-q3AuT;F!YqtS`71VyyqW7kTv4Qw*So^FRc%o1H|_9bL{D>cGc#k@Bd zu5)JcjF)1Y+H^0P*lVaaM<$G5^6PVIxFX6{c%WS^dOtH>yz??J72$vZA<+sjX>Pov z8x>TaAj^xArd0UnsM)Lg*OfH4{HFoye4fXoC-P^F)t-@Tmk6CZybK1YQT7MsgPnyo zAC3?vR2475iGi+H5xasgjd#H4YgM~K5miC;1-MR*h^snnhpn)7zkYIuDCQRmAGy8> z6XGwvpH<L%U2TP!5O}6I9`BZkG;UOsJa;`@Kfk@ma3|naFKcdx9q@d2+1H`yvR^Ta z@gen*c$;t1&znQqpHGmUYv4xy{Pd&zas23U9%HoVH-b@ok6avz594%b|4e#hR7>sw zWOs9j7d0<Q#wEN?rBl%1-gZ}Vj~hY;&%|GK&EeC|fYB=ks;rB+)-S}kBio|z9g?hL zCCt}{EL$Ra!J76;*fD#f-H{$em;J0k>^stTl6QcmCSwkUuFyOieLn+yx9U3}O#LZx z!bJ8CCr9Ui0<m+&gLurfY6?G{vaKyH>!(|h0?T?RKlcFZR(Rx?0`&&*%t1Y(m9QOZ z*EXjs125e>fdAfx@bxj>os3Jnp}7xn+|%c5v_rvbr)diBMe-%#waRMPOWzy*%c^v~ zj<vZtRKBFEY=xzD-xaVmQ`mXgS0@=ao6@K8aUa?U%X;eNHS?cERN!Ogj<0h-;7Gh& z=DhZ;21Yl^a33!38w=pH_p$vIWT_?eVcUE+^~_~@14WGE>^Z^nI?BbyLdp@Ns?Jb) zlud+VP$lydoC@NNU=`2E-chvT6iKiNXq0G%WtVeQ&FhAC!Nu5BS`x;{7*?(`xN_o~ z0hH2V{S*TOIf}60Dj$dN{VD576^;yhVMXlpWJ=vFKZ0HjilsMtW=RGrymk?$^>@WS z0^xoZj319R1gcrAmsB0)R40uyBhJe?w51Yl(rtR>Kwr0w#p0G>_@4a<ybAOL{Rn39 zZWymbZlYI&J+G**<?b8nDX)TWc?|7YFAonr#*dv!FyqIF6qyK<Opm{b;YM%a;M`Me zHx9dfcOivRjh&R+-(Ms4%eGF<LHEt|WJCCOZF69(O%ift9cg#X!dox(EJzE}!xf5} zW;z)46POmkb3Y8#@*$j~y-P2XBq<X<BFWs%W?FYJOfnm(MT8~G>L{^UjsoJCgV+6< zWn6oeG1o2XQ6$r{GS+lM4Q0-cAZjNt4#D~J0{O2s3fUGeI<)c}+k7A?xM6k~Pc?B$ zE<Kx8b(lB+Z*Kw^6vz@jGb-5pWR<PJkd&}xBn4`vtW2mH{!D{RX9rc0ZS0pT#U^=% z33=bfA>LRo-*;p=k^#%6<i~uD37h>#vHotkYG{O7S=NyoP{JSa{2DHGb`@XMc<ORK zM<=bZZSPH4nve+hd=+PARvP@IY#%6O-eW)GRh%zyw(CUGARZ)&;p)=XLKNxD5w@9* zvmA<=XHcxB^Za9NZi0xo@XAiMLs$Tg^QYwwIIyCW<z`TM`&LHs=}2+Px!S!7OGiD1 z0$O7-X0WHZ5G|=OM5#EJ!8lfF4Z?&p(TsS;66H|mp7K6Lu`@)9;;bM9lzl^7BTom{ z9EAxn!&s(i!yA;L%XwI*o!YeVS)_E+rqFrOytd-Exw_?8>WD%Za)id64mt@sC!?qS z2x4IhN)e{BaX|8v3@;)-qE)6e+3N^vdrCB|oZMcGUn9kwr1fs7h63kp8TSz88DWYv zJ@@=`Io0jh7le8>b#t2U!Ib=v#2$sECN(T98t1a6u%bE@Wn7UfJ9}qE{rR7RDCe~W zD5*eL7nd-U^SZfn6R^2Es&~`(2l&U<q<>Md6yHzRhl|*7$D&o<$r>%l>gUfuZy>S` zWxm<MH40ATscYJjl0_O2)&cih?eISFF-L34k~z8usf@Ha90kUXL<g^I6CrO2$>gy} z8O=x9n*=+NaylN=vw2*KlPiYq-lQ%rF-1^BSWvz;<gvTfFgLSp2HV9#LV+`i6eIX7 z3Va7&lVkd7x2y_|oZUVrKNBe$=_!NzCjZv=Hd4Yn|17np!}NWHtHfd^B-QS?ILjrA z%3^f*WEE3+pAwi5p8G{(79F{v$<iO1D0Q|-Zy=;pT!uP(CP<pm7n?9mNUp$OH;y5y zN0<ra$SbOuQOCURVp?8AIX$tw4g4DV%w4lh&BByZ1$s~vEp*sfJ(i=QOKl8nN<jg! zmH{!&CWR<VT`|$AOp=A<Ci@}Qa=kUEi|Sa6>r0sMj!Byo$&|!9U_}AtsRCdk=l1V@ zXesfr`xjnqONN`16OXAq&6UEq3uEOzbQ<nWvj5Q{r|cFEVr^XTb~^*a69d@9BX}Em zy+bsxdy|2e_M89ds?hTl^R=`~ySZ%yapwKG!`GB8oHn)a&(&O@bgAI@5TKG$k`e-r z5aWW>O0vUo-WMd<Pa_1?ee+)tXV^=NQO{Kfizl~LOK_R<ktn5aIc_!VNmAtM+QK9< zY$$`)e$@IeXs`#F<B+KaBZ}bk&S+DC7NziBNO(3nw7^!A5n`ZiC+}1zURg(%S@slo zM#AZ5uoNOgH`BFQuS(rvl|lvq&!RxBnp%n5OIpusi7?~|XdspEv!E2^VY_2e4l{+e z@l?AoQpy*d8cPNj2o%BA`vwK2t4WkKZnr$xXxrm-Ah}EGAAI{3)ysOLB00T{l!R)Y zhh+~8Q9|db<CglohizYdn$J?$QH;ps-koXf=f|2U{>eacO<_GAOuu+Aw1e%#kxP(0 z1eo+nb1&6@YGj_DM%wnC#z*L_lvL;q%BGobTWC95Tk4lj&5~IT^>!>#PAqSUuq#vs zKyvgkX`JQL*|T-IPA!UIB=`2@h3RceqBSiG)^aME391$-t8w6}Sia2ePW)QOej_|F zwot()4$9Wy;8IB$C2R`Y@BgZMNaQ~4J?5n^q)s9;zZ+q0R}*+zr6{V03Xf1&SXzf; z4GNc7h#Zg~$-Ex@q4IFrOGTG$S8R-?i0Gdkj8gKON_6ZCnX)9QWn7B;sZ7Qyl?>kj zl^bsi?*MJf&BnnR>xC{vT8hDC<3qRLLR4#PV<=pCRCWd^iW$#&&>H1+bzcLzz14O^ zB3g-5CWPmx1M_5!GE%9{9Tl@6)xx7<i-&iB5%-y7MC{u9LWdwKLhW$ArfntQ;K0Vg zIIabv)r)qkGm3Jol~6`#wul|hscPAjRES-KA98CplhWXHZP3Ie%>5G*<~B?d_*CQB z;OlIM{CGJqECRBIt(G~vf^PO?zBuO~q-xtSQ@F5Gh$%Kj8cP66@Rt1Cpul_*6iSv} zV9QYAoMPmiD-m#Ma4(ojU+{&)#2^Exvs#DrUj`8}9h{ta^)1P6<?iGCYEg8K9W@0D z-<+}{V1w*L`$gcdKJ{G1sN`J2JIR^s;e5KW`pbA>jj(v7udyj{cJ^aW_A|A4^XIdu zB4rj-)Dkui>*8flTGqsfh0n!XO&R9w8rGC@1SQun)uS*y6v%f553GqHp=9$xfv+86 zHTDjL`ByrvFj?jm^ujQPznE=T-?~*k5_(AyBv$WO`N=b#RwE%j@HKyoC%cw!Nq8Ez z?(u9$Fh1);F6C!g^JA37?FQ!SWd9x|8uNbc3azvuTgSzPS6`LlR^dJ{r0#C!SQnc* z`RDQ=AoU&3ZJS5c_Xp1uPe!60@>o6c_Qfr{$u|B*cuBlr<BF7*0?tA6IoI`&m5OJ% zPEXR4xMai8G|SR+lH;~zv$nZAxA*v**A`3t6V1?zSWr6*EH_ev`5`TjmPX~9@U`2S zORG|qldkKFg-Z3lM0MkQ76VUGE9#m|ksfFjmFSB;$2U-uPySs8)g#qSJ+j<4{xo?c zrI|l~GUJ^A%qtYlg*qn-pVOIr{feWQcH=bgM@XfLE0tG`=D(D6{%V)cW05paJZ__T zF00{%a-R9W%(%)_^!|X?T<$3@xm`5G;^2%in|j|&(63Ik|4j~#Nz|_lEwR_GMVHbY z2CAD}CNzt{;>D=&Nw!T`*KeQv-vOQ<+k=+ayCl0FJXge)tXpQen^q;MgAheXB4#J^ zB<39#QZ*@4_C_Mfg1!dnHydvF0xP!7KuA!l8o*f}9>_PZz`FYae^fa;KfYO7rRh?f zToFcQh;=v2jP;e}V)sdsHl=7^ku|E{!0*P@ev4le+bPJAlD8&DO00s!F`15hbuzvA zG+cO-$cLv`q%VmxnMIDsFIU1_{p+Oa^GXc&w*0V&WOy#XoFwxH9G9^gHQ1$fv&T8N zaEwrO#K#wdF{~b;@vazX`&j7m;`CIV!{QeLnp)j>2)atg(oY~asw@xTDG~IUF3ZFl z;kxm@ja6xKU+MEsu)~`}#f0WvktR^(IO;Fzp+@2m;etN<D;A^zS>+j%DsA4T%TrQT zKbcA;Zg0yK{+IMy(gY*gDi9PXSB!>LolZoY%6Mz5CvPA)D(l;G@!<qpmI$J4)_I26 zA@`TUuUvnD*W1Hi^v(=1ET0v1QA9hgwBCz6Ce2^hyrP|*5o%A!Y+Lif85E<B7Rxt1 zl`7Wiu)s-q;+tlT@T7}t>$dhWH4*rV&&P6179meAj*MPP+gc(e&#CHRK=LXq)9NlQ z53o=4(o4sjdOt}c&4>s8U_AaMJLmKgE9gsJy*@DEpTKv*#@7<M7#<lMXw`{{v?Bdd zR7&DL2HG`x?!!}aTCLfnns}aUsY7KrV`kR!Wj=7yp0;gbSTG<o6N%NZc)C>&Dor|X zYvz?WOl+EHRA;3ErL!9AMrzeDoB^ivDc~#-r*z%Db2~Ctn<6FM-?)3(CE~1xmIX>) z(e=>+4k0j$7_rLHNbPju9iaG{;%WL07&+5k`uN5z7h}2dofr>4Lt7JZA>v{Ly-~X4 zo87rR`;8f|F@*-OHC1|HwK`L1IRE@in4PatRxl-ngyJl)NE|xUb^vy`wG?N7KkL^R z{}e_8GQ=nb`0Gp!`5ewLw3G>w1Op}NOu^o`hZ6aNNrlDXj&9t@K=*he#HQr<2xa8& z&Ja^2x+--zJJz~Qswe)G=W31#GJ<1WJ#))Cf@Qq|r;E|)Bcx_<()%<*P0nrGnt0BL zX4rkM`!?2!OQ$$O9Wouu6KPaD+um-Z=oUJ=<Bk}~`bq;uV!L_7oQZCs9HWp;@(yJz zU6xufbQG2ya3()DIAoyQqPX=zJaRJmRrj}7Tb|E2Ep*|a2;xMz&zM-FM=d6UPB<P3 z;^_T?lK8u&^e|z6*7*gISb!|_J0)Zu{4Yre>4}uxm-%u6<pisOjR)ILQt@eqMT{nM znK8G8mV9hs&Zk+w7m%fvhh?f0;i^z&H40D0nQLc5If{jTnx4}>##td~C}V@%C_}ry zkG#C&d`O);D&7Gi?MC}z_agBbS2R*?`OtgTPom4#W+fc5cm~$a<dW?R@iLq^X!w#z zlare#0`m1&c9)m7Vr`q}%B)&cb3rh|PxZ@^JrB<mpSwv(9-Qp$fA+3PMdA!uA#hfu z@PocM0cEsep0eKadcmZ<1JqkzdwJ?oyx+P>a2MeGS}Ts1m%#a%%af}=`&Vd0h(^Sb zwlB<3If|D?AJel(SjX_zVT;m2qJX6{5jIQ$<wA>i;?-c=?G38WV%Gxf;F#57sY1(h z;TUOw7&}ssJT4qLcWI_^N9l;SD<C&%S@I{nW?3XAtzAlF);VCxU8X;WB@0IQNH$Qi zEClmMR~UaRJtAw6MkJq#zL+AWmUeO}nHQ4+T_?&QmfA+g(mI`Iz5^&`N<)?-Nc9Ze zA0(4P+Nd1MB-Q$`4jug<8@C5ZmKCVOQ%A_fziLuYD|srI3zH~sZnU73n}NeRo91$` zbirzk432t9GopVwT+U?PXbMJD$-RnDj`^A%#Y}*Zi;^I3os?N2R?SOws-4<Bi%pVu z5Np<0vqK1lIURJVECQ9JpoK$S6_+vPL7<uO9buM)xCe=KRirQQ>e4@rYF0T{HWq?l z`DV3!AtCJWONkDpgj%ccw<4f0i|RXZZKla1)NU0a>9{@Al`Y_xI-AY=H%u0?#1+k{ zCV7+I1pYR8Qwc0_Ou<K3k9Po~>tTk`v^6AAmqTl>Lm4#Os^r{=xLPSVRTL73c!L#~ z0%Hv}BCmn+$z&2?Z8(K-LrmC`v$$m0RaklWc;$UqNTzmco(Unmz*wpp6<T0v0Mp?( z3M|ApENy(`4zd^B_!@&MWhGOFQpx9mR1~_v&%Lm)yo?f~u(XLQd2X8}5Am0MlJntA zaTSy<rFqZ_J*m`aK9&-Nj+#AKq|;`MFXj@e2>heWSyv!4R^i&v11>~0APGwvTa!1| zpOkYKAhq47IH_opGK=UAdRQnE%E&H-$r?F&Xb%TuEEP6E6jIAH0*V-+*AgMW3dLIU zV4@cdXz3U&h?1k3kESm$bTesO8*M*oV>peH$>aa{iFZ65FdeA1Sk@)#As-9s2%>3} zKvmn%iFTr6R(C>|NyA;=CHY2eshR|uIjFD!ijbZd7wYI+mqXS{ONF6G=2dv1F0r{F zn5EV$hp<&458_3Sdyu8m8c?iMTDP--%89V-BV|4rcf_eIG11zZLS*k<EC{Eyjv*Y^ z)FFD+@D2zU+s-xR4hI!HUNcvjOl+h~Y!vNxuDo2;X{@}uHUsuExnTN_Wevs@&lDhP zi}N4t;wk0Y#NuK@FI8R{fraOYKe5RLnxtwN1IE@PbuJIgawNSCYyNMmh9{m6tn51g z?;Vi1Ru@S8jN8SuWS}Gp(r5j|#xn(Q68v@(XrA@7QiluvLi{*%%ugNT-d!V!OH}ep zmMLfPf%sz!Nk#WI+gDahF`1rlVTU40kTjQ(nx2L4sHWdm7N0H|O<zq89uV#`*N#wL zVaeR74M=I#;1*-;=2O3AQC5MPIZmpH#Cg}LB<vd)JERsuwW$_wU3GrXmDrfg#XZDu z`6)}7J^oBcm#-#jj0{TrwW#nH>rBQ&QO(?Da3@>2C2v5eru@X2lVn+#zBvR-xA=Hr zLWy=AtHe;}YT5HU;6-ZwUPvN;R64?I#aLdTk+F`M@JnrR$CoRrEIf-{yxU=t+WIu< zX;K&}dP^lsbw~*%x1RzEqmX^dYBCVh{Ea)E*VL_4k*WRC>gAfG5WP^DDr{wv0%g69 z_OK*=kPThHbe6b~bpA%I7}woMag{l$b<QyN+5J>!T2G0Z78T#@pt{}WLk?p&%gjBt zyz2rSOvB~5JfIuK^S=t=GDT#v%rl+TIo#MQ0xOQ1iNA&OJ<<Bs)i~5>&!tOzF`;iq zL(-(OCIBrG;9tNj4oJulAX=hLqf_&>*(udVI(e3C6<R-#8<!_egf@oWWI1^e*~qsh zNcME*P8g;ZKt@_Ix&o~v$<DPjqhMF}BH@NXhO|K}{8U7V0)PcxA8Zj-KzBBbkJ*A_ z{e|K-y<}E0s->2_K0rzhdnw|Y+nvMbr1njd&KF?t9n<tklTa64{YB;cXRYu4xNJ?* zcC@{UoHH%tA+t+^uQ5?aW0S;g<xn-vSJp*%;pR1H<~#*guk;%>f8tmTUN9_E5Olh) zwkXtRWzab0@_7x0aKrlw@;|%rL%k(RN9|ejso3iywU5Fx#YyT_?6&na>~5-ngq@K@ zcPQNrEfft%*c4`4nNX!-_9@Ixq&x&yu_i#V*oI4`E;^BJY7yzm2Ieh_DB|;F<0Zyr z56G7Y)7SGyhpo^d38rBwc6xq&b|QJbjPQ!BQgmmo6QVazrwo$%Ec0~;FJ3l@;zXg0 z^Lcip4}F}=8u6mHW{7Wjezc??eey1<ccrHw?}Up1J|8nQABU@#B!{JV2Hj+aVkoe@ z%-$&4Hi9!`^N8ys1ss~R20=UhWW;O;RZvw);jE#Jwb~Dc#!?Lu26Yql_i2*jY2+1k z)t>Gz+LgtdOAFV>hnM2B(~!7L9}%}@QR5={b36r==6M0fGUC#{UJTVusex9c?PTN% ztN=xoaX}=`dUMtxl9*+#ND9FO@vv9}^zkz&uQ9LzmAaG5yNf>c|KYuFybAKR32nZ1 z-m!M4ui#TMQ}>Ds869;4sN^RrCAOQS9E>t9OIw46DXk^)<`L}dR<#*W0xXb_AgGwh zy#QFfaFRBkfPt~wquAY6sqAtl6!7%GN$c{!X;U^@Z&8YIn{u(01St6P6<tEE{5ahz zMl_9VUsl#8?qeQHs*<I1>j{FNF@gikpMsQFbQXbEtn0+FH@-H?Z`FYLs~^+4IAFYP zwL3h`r1;Ya_m*HOG#ZKvY|d#K$PyRsGf$Q9q}_9lLI<|la#33DHx%JBXowXr(3813 z>>(qA1@o~`=D80WfPCS{jD;tA{Msk1Nnv*`qUAX~BUo`xj`|dccnd9#5g495q+bk4 z1~k~DxCxfMUxI|5!@|}$p<sJWnlSNG_AP7Uxw10C=!~FDaR6BC_WT{NW(E_hrEcVe zGK{3LZLqZ_6vpLP94QzOZ>A!Y3DdvyFAn%&Yi#z#y{3?rCvM}|_F4Cpr8AoEB;}d> zRi^IX5{&vP*(a$E_3|#KPv;aHy&QEBd(4dx5ASf*vgdWX{suAAL6dw+reDjm^u!Ah zFwD8aZ>^J<6@DsYpU~Q?DO$yaay-D8ses$m`ne*Y^}zsj>b%s?pd-{$g1B^lX}DiC zVpL?YtyXg!`U-;6ZN*$jwW!j3vxG%rRdSDsa@CUYNEvG*N++n$LpB$z5eYDFZC6K0 z6L-eMHI9hRI*mO&B9{870>=Uaq9_5ReQmrQd4Ss#_OPwCn{#GqqO=2j=+VDzy)`v? zmz8n<YpXhQYkv$q8002X(Az}}+|DVho1NI`kPDMBTuWw2mT7aH0_P{huX}|ba;&3r zk>4kIGXDKj(b_~EeQ$MhlJ8-&t>MJavW=aM>x?F45}ysN@Lek8FCiH#anq*00>g#D z<kwX$Bqtr{A#!B=eBJnxazxZBIYayk$Mx2E#gjAUKn;EXEWb(G6y4AoAF2uc=GvQ0 zqlsT*6X%3yDNi)YEIa<$`ea~aI7A(?tTIaT@$u{r8)QqQ!&Y7U^|<6&%m<A%Tunu0 z7!f$#i4{mriDi_s_L+#e+ojoi+owGOEp*RvBBQeue)95*s`(wt3ivEUB_c}23&q;( z$>X}xk2%&llK2j7_Pnu2eK-CJP*`ECgTvGRkGQvrsw>L6262Kr3GObzEkMxV?(WXT zU4py26YS#7y|}x(ySq!!kfv(XS6%guzx$;h`t^=;&pBi7x%XOU%{kYu%+|Y3%AvaU z`;!)Dggf?U#kVXO=M-SY5}VIhscMWQCoPHJn>3JDBA8&)mcp>-MvB3ZCPAi1w+-xv z-L8&L>V-aB3{u2tdUoyV`Wf=))5`_@*q_r-Uuqj@BrGJ|ZK&115L80R&(i?+quH!I z3Tq1p$L{vT6e?|H7bb8ltL{rUX=ETu&A?1di}O>2&4`8*4i3v9ut|5Yi;fSMlOaAz zkH1rxli3*ap-0dpOOER>P?fM`P+PSqxgs__fcLSLDOtCn$&<={*zTU7vPU3ei7-z) z=@v6N#Rzdxx?H8BmmUxw1?Kw6>7Gb;G(&AhFC*2T7pALzpUqhizg%4o2v*@ksSmlv ze|RUqn&BF`>dPv7x-*cp^`_{W-}>G4V&hHdxAgV5!b_G}qW=9)g(CtH>qZ&7ggeE$ zs5@opI8yLL`_=kuAFO~`42V0N4yP{$5e7pWpU80f2%59{hZ+_4uK9gAq7M4FsKddm zMi2@nh&%`}qDr09nrjR^=7npTwFS|`!U{q4f(l*em6A)@oQe2W_ls}9$t)$o*+@5W z(n!}!A-oK>)x@$Qn6@Ck2&;1k3P+Ep!9cc6XmHdR*rbhd-`^e>c$QDsor1^!C6X*% zmX0OM=e$O}L`?rYr74mjILlI78gklKVgrBBQji&<R^I*Lg5ny~W{WK|k=MhcxQ}xr z>BZ$}3_*kANv+HI$PXhe)Kr>VmgpUQ%|+leH*v9c^cBD9h$f#YJ(c1u495eJ3Z3@d z*&u{R^Z#s!ZV+h%JG||cI+EH;2uNKe{l8_8=bOiZS8dLk+E%tC6N(z;!`tUl`*#TE z&dSd=u#Sj#kc0AhOy0M47xRfCve~g~#@L5A{@i8bR-#2m`?&uk1Z;AW((G*uxD3NL z%I3c!LgGlrw&qaGS*^#Kx$kACjW5&scqa4Ax<(Pf8}D{|8@O#NNz--XdXB0**cSdj zF@|kfCk{oAC#7)3GptO-MACGts=-S{kkwr4&*aI#YQSiUuE&>B1X(fB=+?_+86YBO zFIE;5I-PL8=edv~onk3h8!uJcXJt2<*`<O7Y^Yx4-7H#p{-IAOR9Yd*^o8k}OUA!V zGcOJBT1(h@mgmKBXCpZp11)p#l1C@JE}W}f>i1<xQlx+R4!e^uC!R>BL`aFyvXDU~ z3oU3b2^8OY+P>(&RRcby<>}5Q9g{|H$B3be7y8Cwa_cK^)7&uDE5J&MChx<q-=S*f z>gdVF>gm*B&kuN;+V;SesVqB?o+|4=rH2+2loOuh18aUt+a26xlH0RIL|M~vr2ZL@ z15#3!20DToqI~5z^6;u$s*7MJQt<d2sUD5o78n6QSfTP1Xee}+!|KTS>^F_~pMQ3` zu&5?-otwWzsnALOh4?XM)9nxgDTXW&^J8QrRL;CMKCXe($e~<B8h0(W1k);g7x1Cz zS+vok5$GD;l=}<u>iietIP#-fXIrO>06WE&K7v2LO0Eh@RV`>W%I{AsP?Nic!F07~ zs#_Qifb}BK^Ns@;mP8SM=9&_+>rl_#Sm8VjQ0;Vu3flo|uu45kDi$Pq3h(n)5uAG@ z@SUi_ywIhd<y(CW3k=|A=BiY@O$n1H1{Q7FUUDKTjuOh@p0xXrU_q-2E8IBCI6G5c zA4lr)N1~7*1ThVP=MOqmL*q}8rM$7xHY(6ov8K&aM8xKiLc}?O&z!Xl-;g3)G~fwN zm&L(CKGl`fUZJC+E<{JBoJ)`OjXFy@;e7qwwC0gs{F-Ll2P#9TUJ1fw1hhQXNvr7D zlpmPB$T<qs|1O}Wn><~;0gQ^2f%p2tpaFlb7r_hCHrnXB*lQ7p0FhwA?Vuuz0jo`+ z%ke?F;S62xYt0yG2vjaU(j8?9YI3-8Bwg7Q89R=F=Su#Wr|_mPOoYO8KQN0JGZ8Zv zp!bwhCaP!9&&6x+8CB?<@cNU>=y9Y2W<T+Ub7k)%o=LPf9XQsZYj%<r#>5&{J-ywH z$jEfWxx0obw<I6ZlR@tmQRR6)S>Ivj>PQ1IR|v6`Ph>H_1pWvvnU$}X2V+Vtx_uou zF+bX9ejZC^g)p+B*=wzaI3%R<nJ=GlbY#8xU<u<slW%Bg&;N@ee1_ds%$52sX7|4o zA%+E5$iF#JPOuy78c!X$%40JAS&Mbkx8<9wB0`iR_Lg8VGBF_=o(xmv?>{$fJm;o} zZz4SX9!=h@99#P<@|RYlB5-p>kFHI~y8t8lr&NuZ?LT5a`4W`cg}{7@C>@8Z<Y0B; z-5MRYUc~^J=i$to2{PieNB0g*di2}w=#r(nte6ZkjdUt}_9{*S8gd`PbTH&0!9U4( z*2OiW`6`{=MEI=#RlI!*qkAUF`Y};xPqbvopB`f38tGr#(5uWKlvmhE-du+zWRt1K zK590C7#4)}g-dvw35a`t^2rs^o_7(>n}&WxJ%T_6gMnFPx3^L4&kS*lTS)WDkFF$` zACt2$m@XYSsyhmp1RQFd{>d;JyyRf;Vw>`735~JR*D?|3GK=8dDFo|tXk{V%%CPwy zKE^Q24)|v{{TljW?pWe0wxerFY^eOfJAc}{oge5t5t>)E`cw$}N%uc4|2vU#n5$;1 zlq(ONo}*qb(BG_?dFA>S?_$u*yxTqp6VR@OM3OAnrk7#Sgmt3YnzckM$-gjl++ZnV zDQbx=EzX2J91$8GrUBbeC+H|6OEymFV@rq4)LQf$K@e5N#2&F<t`933N8SGMr+Rb( z5%e4g<$hSG6qFP2T}lS$ezwqTIBFtqO6NZ^N#8Zk1E$$Bne;d|=Sz%k1<vH6JRBxf zxdQ6Ue`-7Fq_ya^%d7rd9btcq)1GWOXVwb5vlFJ^X>i0(G0dxi?`%E&Vl|YbhA$#T z6OMd1mytA=0UEV^e$)(G+<60H$yY&l_IM=IBr41hwEQ<OFwfy!drwIH)L9`R7#@ZW z^7&Qcn9@vC&8$S6kTE!)G@0V7K75WI-r+l)uUbDEr!~%Hzo}qBye@0*wCJ`-<WK>z zgIZ;x_bVteM9Yqx$Wn6;#+DTx2fKEipKr0DAE8nDl5$szUG~-y3Cxu`ETYz14xUp7 zWP^L)Xr`4Si1A6j^GOk<j)+mO@kc}zn`gXeb?AhdGqL$6+h>=k<%YZ7Y2ZBA<^PxR z$opkonj?$pfkDDkdOinkq8~a#y~=xICon2Q(t_<69zA_ITti1#iHuA}Sd!9T-g=4U zLp5RW_16xs#R<tD{Vkh=yEhufKNKJCxU~zPPQT!TUqyZo-o7<yQ`gxI_i^tv_Tby+ zcdXQ(2_NkvE`E0!eD8Idn*ILL(YZ8s^dztKBJmeOqiOZ|qq_vc*!Pd{aQgfI!~IcL ztcgNXdZi)mP}Wtbeq*o7p%gnc&F?XXVbAM$)K*AtDd)w&;G^15o1fDMsi?bk(!d9O z|NXE+FFlK$q?&t|;=$B<+WiAECp}K2!EKFf`Y%K;O#;VsFwLx+L6|iek1mnd1#2>= z=myKNYc(6g5qLS3^>Z>$It5}#J(0VYP+w+i3lno<_gFWRXx`U&2yQ&5uy+K?$YLKG zg@VQrN@4`tUao9zmUqiP9S73Vm>)9aD0=gA<tXK1w@GfaAd*K{Z7l|0X8sdL`GJ-& zOc2V2<wuk$I|4CKg<`J8$oDA9J6n)*QV)kyuz|D~q(Q{jl9FW&3HV6`{YgcS{d`4Q zO^NNR@7qkD9H|)OR4iL0mDRuyia+W8yxQ(pQ$t#-)_L%(YCzoiBw1P&+VPMT#A`)A z(=W~NeY*PGh{8^gz!QeI7>x67G;rD|hViDy;~X<BKoAHCgORx;93NdVF`IlS6Zavb zSOaIkz^YvaQb_QCyMr%fs+dUdy4Ag==D+pJ&NQyGOqYKB;gfuNLiAC9*Om2}Ez7*Q z;mCzJdj2t;7@u4MfFSizdi|~SNz@8K#4;zOGx<<Hi*Den{HUr*!9>5cUI6@$j|MM( zCcVDBUPL7WgbGVm(FtF<6!(M|eJRp@|3eAu9#z^ef)-ixi8t9p&+`I-1O*>;zRLw) z*_qZk=$8u!*v|xx%ohq8MNP0GU>hN@2@CWWWul$m-rM5>EN=Wm4c^@Pkz(8!-`sxw z8Nc!`VK}t~5dGb+d?r4A4rV*Xh`9i~Wie&GCMqc-M@m)84%r$N7a9l+FlZ%0z?=8< zp8^z{u-=4x@esXH*+JtAgRc+(&WhD9ivr*>?sE-RZO6aKB69tzF{cuvrEV!}GqXm7 zcKHhd-Bq_ExGN^qfRUT*9PM?%o<L>Fvn%Of>4+Ep=NYh}G<$LG#C2@YNohgE@iLS- zF2;_z>>37@#sA??Yg~u`qXW=5rqcr!1uMub9gIcFb0XA6!%{Xqa}H&#lh5Uu4)I}j zNl*H13Zsmvc}u-wm>qrHeo`$5Y2ai<sb4&W8f7teZp)a&ZY&V7Zgv-1g}m;DI!`N9 zK36d&P{A~>N@w%w>O1-Ulo9Q2K+8>Wj(%xk)kk->Tic15dqaI63Vw|$VFk(7HPIJ0 zmG3URGT~9Gpnc9)HQH_AK|GQVnkpX7<VCF=U&tx*N@Z{KdT?Dv&qI{^aZ-|PrkSOO zl7ASkhWV7gw_wIUi_u8e6F9?8)uPJUAwk@=(nd*%Stx8aK^U%Ot~$BeVZ)n6<n>Yo zPpNVRUyy7}-r|99C?7CYJUu(OR*<vB5T$X^7~SWJvjfj{(fMWLd-v73heGq22%$Vq zaT&-opP5P+6vE{KH0T0krw)wGt1;?fRROR4D6>xlqeRA6JtvPmVN1K>Yp!!llHHHX zvSeG<&-2<o-(tN}lsaW`GexD-(wn!Is7P+VE8)%i;szyCfr@zIQwM`f9?$F1L+3&e z3cUL@f(h3epG^K|cK16_=XPUhYV?YgDfy$BIS6fDML&>hm?7hX$I6l|(Myw_`&r*x zL_+jOQ7YjU3LS4UZ_m_b!Crn|BGRROY;g1`f44;=Lh@KI779W?KY7dW_JakeQ7lv` zq%jEKXL<WRXKu7=s04aAyHcW~64w4e7a~1eR>>35pE!g^W^&l{$V{VuqANax0ia0h zVd;qP_tV?m)V9l;)!sJj_4NB-j_w|3hG%Ioyp{u9L#17=XQ|6)eFv(#%C$BMbAlCT zZ{6=(8t*V?N4!;x@xc`UNd}J-M=!shHvhndsI0u>FAZNE1a-&aQGnzumvUki)I?uE zj<ziXKDWa4w^w4UOY`g@@dC4$7UjtYUF|Njg8isaPX*!9zW(RwYM0|Pe8%+|*-BOp z8!LBZP-C^3^;@|ny14K}tt3>@VMCHQe|>*dRm+C;^s47V&8>~46#1^c%uEz3hM*}; z1|Q#KmZ%JYUC704Gx+D4<v54?E#$cEz92{oeg8-;F+M?flVscdH$Tn#L1P$JNdE#? zOcGf?9i60(gtVHO(2p@y*%mb$9fp}_dfvGur4f63{A<1OBX{4+jJFq-w=-%eF4H`7 z`a<oDA916Klhx&@GQv{vtC%U2Wcm4Y^#r0;y{neL4q%1(<PvXD$IAM%7fE9%x>!Pt z2V}R#zJGXl4F2iifq{lcL4yB;0R7P@<9|FnWUQE^!thw+9}b=XbZizjP7!6N4-ZdW zAI@hAE=3jN5A#kByO3%@{p=hrC5NF&!_4&$wV>dn!rq-5DjHF43D=PRf0}oo{$bvE z>yBrP-z)zFpe#Md#eDK$?Udcvd0X|i2YGMl6;R8!#_S}A=KEm(g_w8NkT2pNSxji> zk3at!So0!pbzGO$H;8hpd4+bku;!bz<#nF7Cr#@}No&gmLvos9WnAFYz}A;XPLZ=| z)Z%UCQMP6_9^b1vUR8BLpzf&M>V_(8EFD2Pe+H#23>P|_L3R(!DAd{IVl)KZUl4{) z$)E;=2e|(X-M>iMqcIB%pIoSU5UPlruEoGGZ02D^UaZ9AbITQuX+4C4{={KF^PMNZ zhnQZWO5O9ZfRMR!M<vYaT(<N6TfibAodW>(Ed9$H1x9=S+pYx6(+DwWl^%*59UcuK zC~+=QLB4O;u~viRfunp}v|qfXqjp8PYbtYXzSAIDBv?!%Fn8KxsPmB6TYRYh2S#Vb z_Mp@kN@o;92IEzKMAGSl8UzvyIQjdUAl{<gq)Da@nZqxArJ@5K^cQH6-kT;z4c9m5 zQJY(~K!c;aWrmkTNnA)fS~7&PZ;Vvqy%75|8(4Lq-G-?;0iIQI^WB+tQBroK?GpQF zXTJF=e^4jvbFwKvHk<X`hwbTfw~yC7e}~ln?8!6{XD9cG*f&*KHG~ccTakn7!yfSA z5ea?Sq=Gy)fYsqwNv}(*!$nGLa*3O$fQ<!29uM|20kN6>HAhTV#?=h0jM`ae;Z4vi z?H262F^#GNrKYJsfF(*bp}HSTtgWXlkj}JBno2@PqM`-7wMn+XA0g4(666!H>g9)8 ze7xjdxvRn(Y!`o-OT_gx?!xN@$ge%3950esfX$c@<tT+Bhd;OLU|9D=>%2E+aS$cr z>-cHg>vOa@zfpaUu3ME~sko1NNI*n%d3+`ZyHXED$@S#geWyV%z37cTei+0DZYFKt zL}%nb8#t8twD$0OCE}?hl@&2UeN$ry8BWN!(<TbtNw+3So++Y)oSDxMV&(V%PPm+o zBgf&c06c+go_?x?Sa)Y8&uZB_e37-~jSVaMJ;Og>VL;V%;?wu3D3rXNBNp-|sUcgN z9*!C{QrmiFL=-#sSKl|-o3~C?*bJkm5@=g4)lM0Ebj&y7dE&@xtkpO?1AmZiFFA9B zRv`stG$tP>$m5}jHQ8RVj@@0Q&*8Xa053PGtkosivTt%lVcOsi&GjPPhb>7=M2(iy z^e`z>LA)s(A(SN&dB6klLLsuId*zTM#G&!kjNzbG@jEjEgRrP*^SYR-POvCQ`4drn zB&ZZ_@{!;>yLP<M4gqrS4f0fURcjC51!?=4Y+;kOKjHrDY$>G-uvSqL8>JCmeoMY+ zQ$M1pkr}clD4e3isNuYt=uy)_sh>7zrdp-4f5J*AEXq~@so$BHzEPVCpQv$XH=mH2 zXf_Y>B2lGK(an8Xi`S>IvI=cIBQ3>Z^>lW=7;Gz7&Dgd<FaU>5Kc~w+lUAI$mOu8= zd(C%?b*&I1nESGTe-Gv~{+7gaTjR|@^EGuLCI2;v?Y8n&zZ3_|Ss|lQ(V~iipCPNt zN=fQ$?#s7UD)V@v%IGD5kVZFM^X3W(wyFM6B;jP%sdi;%dT_g15Blz8uCMmXW<lNy z46LYdBz>8@5Q=n2Q~R@5<weH#BFqt1OjRJkGo(nMp?Xk;v7TX{;=S@;ul}HtTM*k| zHRlL&jAvpp`3U8T_ZRq*cPJK0ogBNU;*{C~v5m1&VFG)R#3tpa`D4lyEo@u#kM;FV z&(xQ?Ee8jRx&-*>Y6A<~u`%WR5%9@T((V-Ty)f05uXXYlIGZzv3TO{6ObD)a>z^b; z6M6%=QfGm_2T(O~Sw^YicxJSO{%9IXmZE{Nq_j?ZmQh_~QP>elA~iWh8dK7%CM~Y> zv51VK%y@HPFuw1?2~8fPO;3yMt#V4gOv+Mx5b>u%e~<JN-#~>KT+%?{FTZijEPw8g zw^s-*m1jTT<bo1Os*7(A>DKsv85njlx_0bm7AIF^8h8`;I?ab)$~KL5oL6RdO0K_b zzk>65vq@i*(ID~civZtOf*xb>kaYl!I-;_Tn6u&#Q2vm$kn!i-r`w_mT3!|mmfU=j zRdA4(;=Y1Z7bBX+DfxgZKyzXmrls&WM!G<NmNrQX`&arY@M(#FxJ5aZP3q#aCs5ST zsngIaNX78fE_z!0z~@}WQZNc*8C5%hXe9Mnqa!6LwQ4OmBs_WZ$RJE)-5Qw=CNjA~ z7BQAWMpu$Y^R;)L-;__!#M9%rR8^I$fPZjyp?9pBt@!;wq>IpeZ9?B(Az*RG<xA|7 zm)tL7L|@l}61o(2B(|_fK%N#XN-N=&3j!$<Zz5f&Tkel;J)XZ14pg_2G%p);sI9L$ zr#X&Nh-wHI=p3a`mJXBC$Qvgi4~f*;17-=Zu(qX`==G>sKFlkm`;K3>9Y6*2i0&RS zTaj%ndggl-!YXoQP3W+ewV;ZQH6<A-)8R~gNh@i>*^saCK-<lDDea0~$}9s-Ubnvx z%sEm<eNN$v5z<0=(rfC!cde#Vd@JM%4fmB_E5F2d^BMSyMlpknK1f|uGAW+9+_?-} z@URu^c1qusVnUQMp?RxtlVTn=raT`KTpnQu6uugTy$iz_(r0GdQOywT$Jzwpg3yr} z0#ug+uAsK)v8(<9-CGq5THpREAckt|=zy}PM;tAv5l#<o;HaH7-qM$)=1R7{W+V+4 zXk1IDnopk3OIW#osKA{ntDPZZlwWKKDa(tx_*i7q3+OKl6ecPU$L7rv?tdtfQTo<r zcl=n5LP_ix0=Ufnddt0WwSV4J(=oUCovdeilY1Q024Dh@nE{O<m+3Ap(`CLWnaMTI z^C>G2KGn%lQ4%kZT3Idy7ud+iq`-Uc6i+LR8s#5Gi!%MzHL9Kdwq?5(t^g}6v|X<1 z^ls$3AJxu2jA!ge2~@L)0PPk|$->j&8ig8Zn&T7Ih7w3h3k^g16UDh!G^g_neuZIh zF(*B4JD^jnX^giS%{;8`{`bE1kHz0GFRv#zH3Rg_=fx~s=9ZJAsjP@eSeE}!JdQDm zk>5x~9ox~fA?H_7h?Vu1(vp`zsMR;N&Pt`M#75>$?L;#oA@lLLzz&qb(uo>_l{i;I zfDMu(@<-C)0ou^Tu7$m#c8IREygVt$-GeCy{i}Sp^3=Z8=}#S8c<PCYn4!~X#6ecl zHe=Ge0Pdii!A;oBu`)#R?cM1_)JURf^+071Pl$%N6oq9m1^y}-fWZYRH7?+6UX&%> ze=|1RZwDzTpr@LGSw^Zx_LA$NK!Gj&Gf)Ww$qdjUk{E)S=|os2sw0^m+@{!6?#vqE z03uRFvP7KGQ<r}hN4Hd7{xY3?Tc4YxP#rz1M;qXp20X)%LUQ(@LfjS*Ey$!IkEDp5 zJ8S!vJ!a=zKjO4$#WpxA34|=;Nfi2`?N;h(+j_o{EJn7xNUTzmOhgH^qgATS51^CW zQ9)es`y<pv^3MAg;!>yaDCv_`kyy11t5ZU}y?t#3zrkCjGe(~R?KcSAG%74jq&2ry zN4iX2Tw*hNGP2o_Qo33>xD*s+u6qLE0%vHqqe^{ET%+w77#$P2#`xWwVVF#-%Gu_< zq4~cM7r*`~0e%L!&1Qf%f9W;i!YmBodnPBsz2++xK{?D8(-A!ig97!4TrzUxODfYH zO1bf@kb3M|4<Kgbz370dm1QZ8Z+(s}n#gh`3<?O+$VIn^Oh28J9$oDxPm~;P5-`2n zYh%ujn}1tKnkQ>O%0||4URX(|StOOLZ|P_Cmu<40o}Ga=H;AvLasjN4^ilp-@jK;Z z_v#)(xA-MV0ARxxEf6xtAi@N#hKiyebiZ(SOt7^<I?T9T(Me)v8FCu@H(h?-d#kx+ zoPUSMa_4z<uZXd-mf@;>`w@og`Rd|RqUE65?{P-F)?)6|ItTh6%}KRKwU^{LlVmFv zq!%NP86%J6`@vPcNKurxd!^v2zM|dHl(D!&f}Ec4Z@*}<cbZdpey&E4R1}tZ9-7O! zAI?!`@~qKF3~9iUREkGXCdv!w4Q+XjbrFvKg`iwlnmG}XBKG;YShOXvo{{^@YVlGd zOXxT3e+Qcar&BuzD^G%3>9=oir|=(&$zYP@cY)yl485`AF1FwIDTpK@foMt%8o9UZ z&vb8w#9E`CpfdBChieN^Zrdzo&FY7Zr*n<7HRQ&=DV?heulO&-i8O$31JPG7^&5Sx z*ms@EqQvC*YJ{5LAD%mhr;Q=4AdXeUY^6MCOsQd16`KsQclnJcNqG3X!anCAb(8Z* znluUJ*CA>>R~q}I$aJPy_n*irS<)Ru|0__HX18YjV)$_<atjo#wM%cEn$D$8`tSXq zx0OYs?j*@Efw>V+`qT8xkYoj4(15+?2+7MV!@I$y_KRN8hSiiN$L<nh@hpf=wG19w z+bA&LxI}qRPgTDqS&T!p!VaStlw33mE=Bkf$l8Cev*{%94*H;{(q2=$xUF%|KHS&W zam{-+VKOOt49ZPdvJid#-#J1;!ehd%ad8ToLo%r-*Z{1p+>uIfCWp!coS(2^r4usi zyJ<h#%&aJ`ZfFx&!OGK4Lq<@ZLtBBn*Po;wTgFfF%sY13QJiu@rdDq1=Qq55rV$3W z)t+4l;%s^;AsoY$-j&ocFo-@<n;mHV?od4{t$e^r$+TKp7iG*VTfm24r(WXQM-aBQ zVC76#fjJlVwW!q}%R=+OG*0Kk;)q5aP$8}9Ugbiih9X;*Y>Xrk&WEoh1e5<y+Q+0f z_#LS5mZc9~+mIs|G#wLYM|b_GQc4-*6^;YiU@H7lgyg1k3BP3`-Vp8`YC>PQa#N+a z=4`qd)?FvqCH=-wEqMS<v4TQvF8fqICI1SL)rlz?u#2dXg4N^N2;1X8n^*dCw(-Mg zD?-znD5UuaQ<!F)=`Eu@lh1GDX#-Kct*@)iVrQ2utwXG~aeY^jPS9i_ByDJj=nE}P z?ss8Cjq@N2yZb-ECtVcLB0TeBL5o?suVLhAQu&9EA))J$j*u3<A0|Du)j*t{YHe{H z4~n#pGR^+fd&FDL8Ob}~uBxf#9OUv?11{9p2AqnF6yJ=@1*V9#$8vCh(H7uloSvRw zVTOd-M>{jI<5r<dA(ap$%VSbdUocf0aj`~G^7F|HbvF%nvR$uW5$FYtK5DP3v-*&6 zsh!>5c~)m$g<QG62YIe-P*A1Vds(YlTJ->=rwZ1}$?WvFF;@}P-#0aHRsehp+=I!v zj`Br}FfLw^q=q@3K9VA7ucWK=ay$V)o(bq9%sM$ipg2DNbIc6>LC-JYc&4n5cZbk1 z5|ptj6FJqsJ!u=s#mwY!lb;5BPkH)GEN~NcND5Fm?ez|WTW<#j1;oV};IdZ1KaVFQ z3=ha>5*#u9LBHAI^tToOiP`)np$+@~H<SpZQ<ak=OS5z<^49Z%WhoN0GKbz+2d0LW zYg`wMuSIY`@fnHmkh*?icQ1^usD1o<XnDYSd~}b8$^Qt*)q5nfCGT=I+84gmoqGna z6@(f;M1>h{aKS)*Lz@|!9!9JT#z{?Y3vE)5mArdeX!HC{FRErSis#-`bog27<hxb# zJB=SlotkQVi`Zm@6?q~G(!<ni$$KtqG=j~}*Nx}j@$!Ba#9irwhg!ErGrDxz3)10| zicj!nK?sE9d&`i#vva%yE6ps(?Co&z)icFOeQNB~MUK0`3Su_f!E<G`cJ#M;GUO*+ z9M1uXyxc7D9w~cMAi=BUPsn{W#8rkZ(!&(ASB!MfrDhA`rV)cSD2f?-DcZdR$icO~ zd)&9la~$3hX~Pr>RN1LgD^QrhE^mbsTfNrD6CX=uYx+_kHDguWEoYSj2T4aDKh92x zB-NyoHg2*K7@JCu$4pDbumv?B4=ocIgE1DvwG&g*vF>y`LKtlHKuy(99H5Ul54m0H zbS`i*6s;ME0Am7}a{vA@A6P5<EKX#TSaKEEz;D?3VCiw_Kyfs{>Qs|#f@DD&vsFsJ z7<V9uLj+U8yT~mj4+>7L6j-E@d9|gf8Sb_=OX@?xT!bahXPiRa_Cv=?2{6NK%)G9m zG`mmYM8W|raN&8Z!G{rP6AMFF*Rq9Q{TE^}>raJ&r;pg``H+l}ie^}9uY|0qX08w` zf8cbTJGb+bx*79O<GMJcKB7vLAZ9OS0mEjHKb}_FK|?dd9=$cS?(x2|Dpw#{5n-&` z2HZu1+fmj$zSUP>d`rCJJ#q39EYa|NQj?r&&b`C|Y07$YiVp&TOhr{m4WDkuMA~S| zDti`60nvn7iLBH*pvc;h*A$`glsi^NAS|gsg*YvFb$vEZ5h!A{1~~wjvXi!_xRj+9 zdW|0~lWkf!Dl~vWU5<8y0FWDTw>60%B#Sk#a|lWT5c;1=CGWDECyJUDFZF4n36Tx% z644;7=;#Toba6rrF|A_gKE?)yHsq%-MHou!@~mr7EMgMais(e%8XAhVI|x|eV#hdn z&yzOQJu1Az_roV9_UZO_*{V$sn-+HcRZlUZ7ApGYTxO~dRfW|(vOhG6&V-j)lm4gv z!LQO}qT6dk^;6rw5F{W+Es@{-SlXT8sj*<MClMqs6mudJXo|8GjMUY`E@j6?8!o)c zrj)c!?d;uEaBoXk0Sl=yA1#fVUj%%UTq>Fq0|Y}P&}jT%f5I2){zoLYzGVGF@3+7D zP`y_7=%n#rVA-boT+_yFr$Pu+o863jb8)~qqL4{ng*(0SE*E?Id`EqT%hRk^ld)^H ztK&vj{RqvNl&?#puB#Cljiv8O`>O#k`jd|PA?!PnUEfV6{3-4G!<mc)mX*jlTl1rq z@(D0cau#T%lmL`tNKqh4alQI;?HGO%%ol@>nbJ|P1y>Y{0u^T~=YryK+u$9$36<H# z8Z6x$;PA?7Q$`aWKTJx+sz7BAE>X^$3*Jqp(<X%SZ#>a7&{}%^dWqk-mqhC@zNici zz*Hxaybj?|Q@{;ue+Mj@V&P>oHkv^6=AgeASxJ^bdT}U1W5UHD<?W*sH8}zJ(nWhj zg<E<xXVoJRgjRusA@EYes;g|w#89~8Tuj~J=838W9%0XY(K}YMY!>c`a74y2{R1&y zab(Ina6rb!e4}*eeB=yXigwKqRap7s405U85;wt^Kwd<t_Zfp0o5GeqM{Rdccv;n= z5A{+=2-@Y!Mez<ZILn^h;6DMfzOy-GQe}XuYbV^NpLr6j;K`>2_jl0Ng|<D>rD?et zE6_a@x79JZD4tIqHzMLE8`*JbiS#a^61~!R5@3*=NR|H`yJB0hF&Un|FF@ZH=DDg( z`bWx|8jpnEz5Vw+z-T8o>%Q)Tj;np7ysuH$P?-zWajIS!2RYogUey{r6FGy*!WqD{ zx!w4c?vk7zOz&fIEa@g(_Sr1|;dFvR_kif2=+_@)z!1vYqIqq+#l7uxpTg~2OZtp$ z&|+?uykT&oJ$qX0LV6fFZ(;#_q4M6Pb&J0_z=MD`U;@P$`-uKGkWjQygS0qFj-$`g zEVUP3G7mx#vL9lhlc1nm61tY{;!8xQ>J`Cv+34YGI6(}#Vs_e-cAdJpFCpJB+eaQC z<Dm_|p!Wawhu>-K=q~X`Zzk7LG=ZYq-v>_1Jh&mymc+g#Q!5lk+A+c5!JeE}{pel* zmLOdiTUmmfHXDj@*VzAKAZjCVr}9(#9rXbtJ?i3mwF>O`TX?eVuXFiFwPB`W%(Ps5 zJCX?WYXymt#I9I?>BG*`&6dc33Pz%X@8_@fOAG>B>m|EMewzOod>Z$gtZ@zv+LdBp zTM4JFJ1!vT4Fox)R>U1w-wjgqRQq~*SkkENX=FlH^ErzXphCnlr&W;=_<zv<h1h%o z<1oAI!ec@yepiJ)0{2J6M7KJnX9SHoF&UYb{V1pK)sGcqq8kCscJfVjEq~NO7MeI) zF6Fi!U7ynMgN;UMgpR9hAh3Fu6O^>wHmbj@dbU<5d(NBIVFsQt=3rI{Ko_y+O{+sQ z-b_wjPQanNXJCa)YCsSN|5B^re9F=H3y4tG_J_+aG8<NPRyyjllmt(BBI}ny#}cFI z=K1awKCRlF%V1_?U7IIZhB*}4Ba<fYG~w%Pi9EgMZoN_eg>dV0c|9z^Oy9@!tG9my zd~W4Diw%wl437~G^8}NLdJC1SBFc00LEGfqlwbX^>RJWgJXLrImLHuzaN~aX(ET?4 zbTnCgXvMlV1wxwWpVo&GoEHEKopihpp&#y#qJVpG$^9FYT&SI_@1-oqqf0#@efkyM zt9Pt+4Efd!#386fWhs5q2ep@oJnP@A_;(-(^uvP(^m)%N-sP0+=CWj`?;qR+*RLtr z1%B1_P86%M?dk-@^+=7e$3V=-r3*{%=*Far0ZUa6fnLocyHG{v;EN4GKC4cl;mG`h zXPo`hzYvc6LBVbb%DKb;3!Uq!7#LnuOpFdL$vzX6??hQH9;Tw3>;>Oxr?rmg4W4ZN z&snvb;HC6?#l>fv2+l|H`_W%YPoF<`Fl0=k<*RClmo!k0?BCI4#_ek>htZey5Ygw= zFP7kz;FecrCXE7G6b7i@YmasS%RkwL{)m%ob^wOWZ3U>JQmmBe!XnECLuSK1Snz?K zy~bmAe74&*1h$ms*bV?bEIkLJ_($WTUc^`rbZ6{Al@^Yhr)0d|wXgvA5OG+>m{yK* z3BFjo=>scd+g2{^Ss=$5Tyk-OZ0T$yEbk{A!qBy1%BV9QN<&fAzYu}TZ%i%7HzlM6 zMMzR-q4T;@#jx~1;cuV#J%d8kv)37#1fo=ZOv!__+sw$s`E>W_Z)e-x9?Mz{xH0}h zV7)KKt%nNTNmP>mGNv%1WD+3t_Zh{_8a3y(v))J6oQbpyOhFExV367s3<~Hc(6bpp z@;pEuY*bmoE-by)XhNmP^cZ3ScyLohI<YQCbzx42XjT8-p`)$tkH<#zs1Sksa%7Xy zlDS_uAy>l6)PlW9D?+cF;vuaBRb&gHNN8=z)Y^ZmJ5O&K%{6;yhpwvzsbcvhK({y5 zs<9QP8WHzD5+9Ptrl8a6pHnK1_V;Ojw$|3%e%;&r*<03e)@;oG>eJ-QA(IcdFQbu| zEJw}K*TT|BRKTHk-8iofp$EFCCMjBlc*Igb0le&xdr!N;ERiv0gX-6|FemCjdNoJv zE7UbRCC|+80B$FyRdF^c>yBGgDvId5`MDk*!z7!#mXr3nPZ?tdqAE;8IIv6NhNjk# zWTK2Q;%va`$pZ|<1~`h685CT6^>{$Nd;*>oCYIO|KH?H1lW&cxz)Es!QM16jD<xYF z_<CyQhfi@3prQY0etq=d(?P+F9R9myh!3K|&-|@l1xac6Em*O@{r20nIvC@^Gz|sI z<%>m6UU;9%FkXmFhAEEr8FOmG#~##~x>ZsqR6$Ncg^!*?k)zXpMlw9reQVTsPZ<QO z7NX7w`WPa1^#pIF{fH|^&EV;Ro!B_1Q&mp(2^-teR@!<v!UM&ae-Dc*OYIQ4#bhoe zzHqx2v((yzY!N717ogJZ=B-)jE+RU93LpOLk}2rm5x#f2rFq%e&RS8W9H__}nDWjy z#3UKJKju@hIXW_?e!jU9Gw;MS;>^g{j*0q&?X5kV$sj?(RKs)}*TE_)K8|OG%C+gg z+3OFK8j_M3BK`&?3_}Ru-$3;CpfXp%pXW0gk)lG-R0cWXl+UyGUpo`<rTMwgEv%&> zm?Cq~rz6pq&(@b$3^-EE*o=5m)QCB&AOy*g?Q;-7*5=V-1KmfJs#M)>jI3Nnxm3Q) z`*f0mv7^HuFcRP`d9**fNmT%X2S&d*PI8n<-aGuk25)cB`w4{U;=7RLEd&O+&MK~A z*V~AKQ1GKs#H%llu__uM%3P3G&cc{+-pJJ@DItVr9C?g%8>p8euf;dD!djIoIl^Pk zX|{X)IjRt5m17)>Jt0G?gY(M-76WBFid@%fcY1xeabw7eJOcPABDlCuU&nUI@to{V zjiJJo)q@$tmN3Ngj;GJ@Oj(*b)jm$w(DNHHc~W2Be4MzVX+@t?1zMX@hCv`)lyj)U z!!@faXE`*U@5HvoW6xE)H@UQH7mCi6oPdg**%Agmjp%A;`X!O`Bqs%i@!YQxxJi|x zV|}JG8n^*UOB9A#6`yH@ast_5#uUp0Zmog$IEK9UHXsZOJ=aIvyGfkZLS?Bkn&>PU zlg*TI(nFRLDc>}xZZ#Kc*1=$WmnhjL<#sG7AJbNHRAiPrGZ-MVmEul_9(f0RncLUk z$&-hVk_pR4TLDoSA4HDnk8Mjm4tWv0Vl(AK?cPx7J7W}VkMH$uS+P1d#-<4T1eIL8 zMkOth2%-%MQv5L{{ivHOTpLQUgC9AAJxiHyF-V?wcN{dNqd)c+g1~Eg)!z%U*l7CL z8LFT>xQsWOPMrAh8{1v&W#cO<9UAEZJzOwG0^IDz63ulCRq2zuvDFrSzD0IP&`WB} zMx@gzA}O$vtq_#LBf)LWzrAY28k;%5OTZGLQYSqj{on|}1?hjPr-js%=!(E3E=qaA zQv(b7J(T^s>_tqQm?-@RUP!1dIT0urNI?n*;U-yg!b;57;dT1&Ri}z?%^+AI!^F21 z#8FBhvxULJ;t28kqUgE%)L`ZGu)(#LtLxC=NX)6ix~q4Ii@VYuyQS9yQ9a^S)9aze zr*DIyhs6iKF~zbZgR2GWV5E403+%_{b6n|_a&+%Xu7@nzd0Q_nb+1h|`zCtDz)AZ{ zu<rjRrMg&~vkJ#{fY<d}cOgJF4#f-l#DG<x>Hrllw6t<c@$xW3oEM^g1d9?g#u`<B z$ek6Lu!kd7NQ#5Ipy5>whkqOpz4m)Mahuo{Qk)Cv5)y+BgW^c37k$YIql4#Yvf8kf zj6U8yU_YaSJ*GzjrpPoHIZ?nOI?-*lEB#W2QkF2~q_6bd5x1Ns0fdj%UPUnq<;|#Z zpB_h~-{Jc2%BL#v2ZzvH=j-kUJf#WtSN1|6u23-Pm`Iiwd>po&`(>6|0H(9%C%5eb zhOKy*hd>{4f(Ux4G4V->HxL9J%LHOESHyhG97WBuUm3wTWzxJ!hLWT~G1c>at;)PU zY98XP#4gtg<x7KKD=*h)Mz5p1C~`Dya2&1s|EKh8kiMkfk<qw#7H(CiR_f0?0j!l9 zGrLy)-9<enCG<+3axzsv;3pM+vv9Wb?~gv$0ANIQ@qwe4)A5stqi70Yzh6v~RK%v; z`z<PSE4)zugI&x^N&ytE7Q2?!@4;}T3kCTx_RmO|{`>qQxIz71aRa@5q%9ce!K5G5 zsoz)IMnQv1Rq&r1al_Ks#UL}c2X~f8=27yUh_|d}XAA?qCdJ6jqS|Tjz>?ob{{_C3 zrGL|pT`V=mHKs}8qTLg~ggQ&*{0v_tML7gAdPGvyfEb>EjwidRW7aLlnmwJZ(DU~o zqDTC9&i@~N)H(<wcI`<_R>(d}P_pEyLrzQg^xtPso+W0o6vo3)F_Y1bkaAPM{Po+S ziKJu3j7o}%9$WGJTbj7NibtujgJaFg;97adHCr*m7VS$bs462oB2-P&*Yc23+Yb+W zp+6+Tn#?MkiMtn`(5i8=UrY?YUl5$DEMI(J@58?kmzQ(`Y*LAuOYiPk0@=%4`R&yL z>Hk?hOk%ReBU>qKC0U7Gf4`C|B=48jGD?MZm{=0?m@lI|l!NupJ6&8jS36&LhWS`0 za-|`fvUpLDErr_B&;7sme)WRH>ISEc$x?}fuag=E@q(6_Z4I<G@k~-CyuN#*4X%|{ zJfDL}R+3H@+CRQ7)>iE4@gJ-&^;_jBFb1dgZ0>NDG%}6dNP|%WmdOrcc>*vnYsYDV zt2$}Nv+#mg{F@kT#0_|=*Y7;I_+*q+BB}JWPnUFmJKkbItc2~75pEf+(`h_U#jc%q z8U>x8-{_-Xpz*BCSeAYX7vY}w!TX2q!kA?dFaX*m0;oDt=iOjfo`8BSLA<PyDixA> zG*Rj|U;jebo3`h7Db3UGf;dc7@yBXw%9?n3fZ2|ARq{Lqy1YqMNYEI#^7nB;c%|D3 zlk&J*X18x&B*P=G<1BeYH?sua#B{RGMMI?jLhw3jLhzO7+7M5&Kcg2Bh)VSLszF5> z9;O&4y?Zh(POG3|V?LJt%2MinihM)MonpD?8?$dzt{bYx8Cpo!4=dbPWaAWtX`J9u zoAi$^8ylLZPOWlXF**(jUA8T=h};0ce%<*|;~Vh|%7%}v>Gr(9BEkABhEElcs1hjg zd{8TiUs9j@@$fGtmXi~*DS2djnP{T7zxxK%eQ|Z`(26=3y?x~8332(j?ll7wg}<DI zxFUkP=}94D5*S4h&}26^=jxzwa$|D1+)S&W(V(WyNs<}`*&2>(<>zu+&Py7i-zDX< ztdXot867Q*NYK7;w-oAr&yh!b6{SLxD=@3Bk8L&+URFT(9*1#0J-ehYq065)PZM0z z7ri{x*AsK8zJzw97TGyGLNRA%)m+e|E<Hu;8~j^kWzdf@9wmR5oOG#Ev|0G!76YUs zt;g~PB-?!&+MnXN@?rt0VvFJXdlF(Ld?~ua2r~`|43AbqE(~O+fr)i;XiRQ3c;*um zzxg#1YqaIEV;LSEK5&(Qis`HCZ;K0o+V4$wzV|(GR2an3OOB2^@fq1!r0#<+LUJu4 z^h4A?4Ei|zw&4|VZ6YVfY!O$-vpyvzOsiPW*Qim4UTc|YG2VfYE-2IfZ58dJ!h*{! zB3(?}f4P7xR8k&+WX~}E8xyBhwKvby{svLuH*R(+C}P9Kx4|hC{?Rv->{R%8gVeT8 zFqoH#|77jAjqxWA3gSGNOvsq5R5(SFjBg#Bb-Y!&BP+klET~OM`^Hu`HP?13at!<% zW9NPya)7@fe7t+ow62-apt^D$$Plm;UpFr{(($>|m4XD69Vd>))G^?G+u^jO<#XV0 zX92wbxT2O*4BSD}=_*Q6pB*Nrz?7=%X*QIq+5p=1<EuFoy_@<F2b!WoKte;pKqDZ0 zLiq#-kMMDZ79=zb1_Uf785XNB96C1XKh%Lv6l@~O&MpCU^*Agl#(@d>4V3JhLZXT$ zu8GM#GxNBHZb2ah;8}PM)xv>m@uZ^OzWzBg^RN`);KI)CzYg4j`wsQmTfn39Br@zv zAl(}i*#2@#C7tq<^hvPIB>RZLR6PY};Wi|b`>Zh{|I5ugY-Pm(QkSFGQzng=sl?M~ z=aMAmKj70w9%iS;0aqm=1!^sd+%`-e#Icjg1X{JHy&bO$WkR_((nG<V!ne8JX*%Xj z(+lRJdaOUoZw5Ujg<3DRu_GLjgX2;gC|D+PXF3cH1k;H73d~M~&-xHGY}(~uwbgd( zf*BQSErQ(5Y!xz3w)m~l>jB=>(Q^|iSwL^jG=>}BCa6A%sQ=@RHHF=cpM}NUoWGj6 ze!UZbft5^CCRei!qqBM@(|uR6z1L}hU?==`Bpn0de*<2{oq#D2CB6UT&s>`9XMWYv z(sdc0@FCZ((a`9ih{1y_31$#KJq1%nN_<?Ce`$80O`^Hh>Mua}y7VKKLwCLCO5vnF zTdih4!ugr1Y0eA_2UE_nO#I!PjCWn<L|)4kbxIDTgr`iks6BR&xgT+_(?&0wa4lD} zyxo;7VCPEJVkRViQ@7uUFixIrO~2>7-S>WbN`RiuRlw2Dl;&dBTBoLAP_SS*<2j^6 zqj|RHgqYXyYBp*+mmUIGS0fvKqWOI8Y%r4B_>#IxXIq+R%_No+B%2}_MaR97bu6#> z5W6LDM-$H@Y7-Lw!f#f)gX4LHr(9tWE{Ik=?7ZIHI?LdqF$Ugl%<)_k>5euRMCmX) zK<gCbc41Z@9AY>4F(sEQ+T1kGFsk41XE8}o$;h1Dbmy*$6VW+=sc1Y`=F<1{I4F9t z5M3pSaP8I-q#p%cMJyd|xXlcrEJshYRjo%3o<)o0#dj?(Xrzuo;JPc(wvp!wxshV@ zA;=sW`Q?MF;(y4wSeIQODDVrm|8#O7TrDlBSFB)ZFD~S)iO~^m2%h!+sp4(mKhZP) z5A<Aoqo|1=h^vj@6`vmSq^nA4hAdoGMhzi-X~zw#5X_=aEH6J((=LCg!L80jW3!<; zC2~_TX@QS<68{rqU2WZ_;bHfwWZi_Gmm6{4i61U)g!*UBZu8+cEmnAJ5zdL_EqJ}6 zsfLbl1fe9$D#jp6n`fb=+c4!9riemO;<z<1PiaAUWM&F(WR8rfore_{vVYz%Y8ULx z*;Mi984O$D1)03!J^!XPk{R{4gr}|`gH_z#W7_ITv-4`;yJ21=9p<T+bomSBuu~eM z4|=s9ZKD@cNa#SZXs4qm3Ys8=p4oj>TOeiA-1Q}zBhFiv%$93aU8||tq>O=xGtFm$ zV*i|PUP;<j$tKj;Pa;>HlBY6scyyR&${*(qw)LMx6fb<ix!xz1Ln}JJPm7RSTBjkT znoy0fZkwt3G!U{p8Sy)`z1ASoB&9&k{=#XGos@3;n9LVvtAC~RGvCjR$W-A(j<-B& z16n`VIxb1$HgTJQ!I}8aJXHcnlc|@-2_y!6jRO5?w=p!{i>bY@#%DB}&Do7{nV}hE zzFaDi%}Lw;S{In&|6CUeBJYAC&dp7;+4ZMHxmlLGY(+LzR^dw^9js%pDW-J7`{e8+ zsljMVQyG(pE>oPxu#*n<im*I=LgJ@lFjaxp50;Pduh@^>lB?sfHk<5f$cn-}ux!DQ z2$m{d%D#g1Ci_*qzIIzp2ftB$AjM+cUKn~?E;viWVR78G=;+8{jdcH2+ggWom=NhY zDBVj%^ADE@rU=e_DOjEf){#r^m1fj72_O#8+)ps|YiR8lUb>aHJ=L*1s4uW8vkIQ8 z*HJmHPx4*q3_gg{ytx0#Z&w<XQ0;S!B;GJfiATZ6AX0#!K}h40E@mUF06sD^=b<#` zSG<}UZK-?$tBHv%RwPm-n-nLv+kc>!!PsTKC8<X~e?Hs7j3<-!oMUfr=U~_pdqm7N zRdm7XF2b=gf;m0$jhLSE{Q@fj?p9YDZhdA9eQ{qR33^9<bb(KKg`S=XTa8^DXI$#D zHN9i`OzB;0oqpYd?bqV)b^xH}f<O%i=d*ILWxJ}m5M?UA%2nS8Zzbb+_@VUJ>2Y4B z(%?uHF&Vw8#zltKC^@xIi*qTJ#?6{<nm`EXiYMqaZ9vJsjrT?)KF6$S#_rfB$3Fwg zi|Zyq(Sds}SL#+pOB*!XvX?tKd@3W=vY%BYo`x!;Ol0KHy38}f6^c}WiJ_UjKJn;o zfV`AA{^o89lDGbo^rdVn+IG96`E}yPUT0dorLx>Pf5_ps5qf>#iK;xxZS*h1T)l(9 z;8rYo6>Bn^8K~x4T!mq(?V~XB<@!BmuY`o}%rEi9AiPMHFa^AM@R|!d*)ufmMHErX zM;qdLTz1RRq`d4LksJZ8GE|l)p$UO+5^?KOOnEJVFN}d#j)J*2UQre$QSg&AUIki{ zV?TR;ezUJXs%^|ULibgEybq>VV}y#>qo+S?!%DKIGqm6CqkPtpiBTdXyADhA;z~)7 z;;8l*D*HxO@zZueb~u#h>wvtLFTXiefm5MCfH!JM8Mb?JSaZuH!ifH~HCWOSz8B8q zWK}Tt8xz7Ky9=aHgER&?GYO7Iyw9$CIaT&~(>cvv<14c_lGUao{K8is{^mbA?fOE~ zg6Ov-KW6W>KD{<%?`J2UqHPU?XX*9d{)Ny4D-?PFH=Xl0W+ccHxMWy0#g`+`4k6Wm zAlNpBLMd%0xjnm1O+0F=+p1n>o7t<5FVe<NFpX57jK`HG5OSl_GA!HXR#q!9<AmYq z1a!qoFKKSP2hPwo>@-yc(Va>DLi|#|<xaCWgOB)s7^l9mxP`WUnrviKW+QCo2U>D) zUzvs=OI)<bcfQ#ft%X9Pyg%q-I!LD|6J7Smfb~b?)E^?*Wr^42Uqp;tPc@Jdv(zZ3 zXtuHlbqnu~+7&Mux>CYTBrIWd`d5jCrZMIW1nPL(bec`)E`*&&xLBPXLNeEqPi9ss zF8NC`!qJo}e(%Je^bNMhBUGkCHC7kryI{){54mive(|nq%aEXC+j2I3h~~ig^$AA< zA+W~HzDKRxNj=bH4-#!M85H^x;RyO(N+q&r$x$%&#%su;U^IKQ?9P7h_WY0G4e4|L zm8uNyiv6;}00lmb29Vz?AWvBvKvm*}0M?+aZSkJ8#%H!6=DAL5qx1-f*9ST>W~v{` z>Q<B_1$_SXN0p+S{9!f7X>dMbjAlc(RnKGTaSg56-Ideg3yz<Zr+UG9ouGX3=OcJn z+h#HW91UAfd-{hG1Ul$@E$Pz^NtqsNXIJsF_<@g;i58g31phD2-ZCn#uGtm_f)m^w z8h3XI?oK!E?(UM{(73z1Tae&R(BSUw?vUj9`aS22@1A?cd+(3iqsQ1kcK6=9dsVGf zt7^_!gfXB(NM~&s(a8bzk=Yj31i$}gvi~_0?vo9TJ4B!0<-uz#6cNp=AWuJ*bS<}I zEAd;+UP<>^<Y-Cw%aH2`U@G!?CI6j!^*ZWLcNu~V&+W11A;~)19l<lp(!l*ALPwhI z6z^@!^$cWlIx?X3PhwTheN%Q0!|3!8@6OYqE;jm=Fqq-i6gOpYm*OrZL2(%aTGc#> zX;GB8iW~%eQLjxLc4N7^23X~Ws=ozH&vyxJMm?|Fh6S}`)a3km=U{6gmJg)Y5~&@x zh+kM)44TQaEH;3AVu)%5)dzDD>+1g4tVcWx(i?0(@%4PU@s@HF_y&IRiuZvP4WGyZ zZgfgpCliZ4S$A^(2up`yA`P39KB_OwQLEIoroJOm!Mpc%$8yHhQRo-Nd)ZB726>Hl z)JcZ1X)i6EGPIy)gND66{iqVsZTQYRw%Yo+CWuC!NLpiB8xil8$LEDh|G*=|&3y85 zoIy|sDSyEuIWItNF0<$h+HP0LnD|u7uP$0mv&yXfSvk1J_2c-P4V_M>V}tr0Dz&vw z4HJrTuE|4X2{=o_Sl4nfz>VrLgw4F;Tlueye}6GNPp<S4Pkw(29S%Lg?onqxvFtPD z?~1V!&d9QJd_KV1a2$k6oRD&vYfr+9Aeir1TVuEA!IDtz93V4UYR4eWA96yY{uKcy z-DuEzX^bBLwdt~*%~WqUJi_i=0XRcX+^y4fi%B1DQH*T>uZ*nt8ge;fqf8AdT{Mj; z8nTZ&mgmwpW3m^f`O2sDrFy6R1NC+5@0k$A^<?jHH{MO0w}h2=<7lEDmVC0RWK`Oe z<iwKw?*?6D{ML1B;z-|77w9!tJr{n`-A<TmHo^7T6q-ryU*nx}IW>LB9zIDlDEPU@ zR{fb5m2)@}u@5k1<-?WyidcG}d&e5L(Hn=Xgm71y17`8cxmzoRDwaKIMI&$A59%Xh z?au=U+a66n9)$*ya*+0UKpHnwyoY#OZev?`r+Z~PodPbV?cUphc)BLg?a-JNggyd{ zfxxyiK0bf>y%Z<@%kgkOn~_L5!w0G?PG{a7s<2V^L8(*;;|jM0%567^w{A8YSAJVj zJz4oT_jTyNUm7V2#XNOxipJVGut!?~TXO|5KAda2N!~_HBzT7~^z{_dtZD%gZHF&B z@b0r{FI~%bJ<c*f9~TKQX&UW5|31-YuyY>HX|>>$mx$+fd+U{_s5RMpc;L1$f2OVe zHP_x3#)Qzg&fG?6AT>mfWn>Sl*j61oOu}27p&AQU&l|}ERK{c1e((RL+F1|%JN3n` z_oBWa!oQ*4wsdxH_SDOCrEMdB({RIY`*Z2>4bn>5^T!b2MGjMyEJQfe8Cmq9ephlx zYXB>)by`&9!Eakz>SLK@&bPm}JZ)e3c0Gn`Pa%-|G6xoyVz?Nz_7cjW1Lu0O1)l@G zY_&%bRUwbH&a*j)Mu8*13JB~~FSgAyY^hfB4aVf}<a3UxYUi=p>;^!ufY?(iJ1^te zA5_~qb1es4@GFCXK4I|mCN{+qa$(fesp`}QX}HW6%E+Y*#uh_HR&iRjD|w;PWPa*i zA4C5uj_4&b_*%D3m2Vgr@fyC0uJx!Iwea%+KzFrm{)fU~UB=YtzGRxv!M>|qoDeFy z^Y%8xmJH4yH8sVu1ZLa}?VQj?;=Ut~+i}PTp;Mu&X(3$FE3<=#wiRzgUx@W7b)!A0 zMbX=!mLT<QM%^QDnkxaHJm5@Cod@LB5;t!Q(;%MU6ZiH_j`A8uvX-f?PJWBqW)<1H z4$jM&(}<h>o%1`HZi6F##Ri+vZ)et-Wppg`T$LxO?|JEe!uGGNeiPitgI$e-Y3~U7 zRl29>)_6%$<6syFWI<H`Ynz;c&BLOe8<f$zu3TV5#DPokXzFHU*|a+{tXGa$8FF}D z2+uA$G35`A0CjMRNqVBU*nv&;B_U=W<>2`}$mP)@Jzd*F1QuLdgFfl`#$|=AkUDo; zH6xc)tUyPHN0L17V{3I%DBW}Fp?^0Oto`k?g{APLZ6Zx`VKadb=^~9@d9}W`^|Lp0 zlAkfUL4^-<vhPODXT04XOl)1JQTlq?ddoipx4dn{ds#1<SuXGbXk0r`<xSfP=(NI$ z%@y_~q!sjCUr5(j`2}4GwgS#rxdzP<W#H%G)A=!M^}gEIjB7}Z^Q@nC-_#6JKYJ^T zU;Dn84(hP9GGtG5F3oD^&kNt}J7|xzT@&^+@ZaXUd%EX4R&dnB^Oe|eK=H!MP7qn< zwQF~_Q^`fb+=h}}cWk82aR8*`lmzRid6i)|>?B9DF0$kL=Kg_t%s=@D3MbEg`%mY_ z2=ecc46D=qc!~Jp`RiV6n(H{M(}X-E-s-w`{LYMQ&>zsiQT&?nxD4Zy&|jp;%?t zwy-#Js$fYyH_bYhjx*x4ZrW{k<G$>IfKq|>uu6{Wznb+qR|h(-k_uJglQ|H<>d3x7 zKOu0iVo57)VVjlk6+h7htNpEUF6f_`r@UeI#B%(~wquWF^-MADH)Y3lJ1%Vc69z!d zNN>a2rj)S9XsQ6pOAq)9kfF!NP<ZB2S5s(GHi-K~Mpva}o}og{l{E3L#G!Gm`z*cC zu}PEDC>ZB^ZOcWHQxO2q44VE78lR3v-M^-6Iy3&4=S{{*<9OFm=Rk2ewZx$ycvpD+ zo)j%ax^WUm=7_s;Y8hM8S)<a_;E|2id^+1<Up6;%%S2IpWNNsm2!gV<?eqEfK><b= z^!c`PF?GUkkuyG#2h^+=kL4EUQ6D?%97Js$ew55)s~BrGw#RQi)UP&D7wn}9I@;ab z5YB$-y>5nbIJo(=y<<B5MuR^d3}s2!9;$`&O{u#e&FbWO+55*Fs@je&r`CP1krIe@ zuGV5h!HA<HgL5q>!TolWn%7sVW-^K42uCu7<s8sQIbqeugYAb85#il|_*~!r!RHR} zprD3-#2^;#8@ZLY?vR)ugu9|qN(^=%7%JW<#0Bw2|44&UQ`gS3Rl46db%1VK34?=U znh~$q#*g{VMu15@kveI%Smhw=l5N>aL4mIX42zd2*Vot@4d4sNEmzg*eOv)mXBD3t z?wd9jZFB0!wyYGv(rrF6@B(_^hI<HRq+BouXIQ8-2=VS$4A}6oo*;6tcxWr=F*S$U z+r*b0mn;>&!{or8+xvqjr#q7Si%Y&?)_%YCoHH;)`~uxq@JCD!ra;mAuPI-vKP~$O z4n6*N-CbXFgylqCg_f{>dFel6--))i2N(;k*NI-(1NDL{kFzsas+<l2j2wLh<|U1K z?-sv1ZgU+@;iZA<G0wVz6{2*2OSUn{+EGtu6hNaM9B%r~c`BX8f~RY&2Dxa~Z7Ph5 zExIW(RUwXG<&m?&v-TU*XM<<Fh3<XyYNJ<Q*Y|dxcwf(F;(dG*5&gNZT&fAbqWZr7 z>sh-$?MvE!tgR(5@3iG=US%|V{hzVFt5dtl2$;dTi=ISY&kly2IdON6f4hgJ_t4-U zsOO!1$LpAB&fzx8FIbw)45!0GC~+86(J~>fTckA;^^dmdS!PP(?pQ+1az9JvVyq6X z>ti=qrQvukm;%Q-AdHW`;dSLBUSXSGO};&_608|Ab?tcAr}kxhC`74gZ@rIx7C%FT zb$088A7aEDuywIkoi6I1oGb<%f2x<Srh`kYvL)toU?wzrdHqFR*@Tw9+nvMJIM#`s zB>PZSX>j_;WIv&Ks$)QlK}lg^Lf$yVh{YCB;9(a6VYT{0ounoF<UJ(Tyu<tWbHhTa z|I7cT<@&OTmt_OobXh?Rf@CF*+m5bldf2z(r&5O!u-uNa2hu{KdJc3JSX#GL>W(+P zo>Tn`dK)V0)jM-t6|nJLYij%FIJQ643WeQO(5JW7d1v@Ngx$NuR8edWUfAw5?nyXI zFMm_z*ie+nLf!i+^ov`}q>q%-$V|CvM0CyX<-phLYvX_4I^w(MyZihxP^5W9>>2&z zb(!4x#wMOX%lI3JZ9Ci`K+`~+HdJV&uAvCicclh$MSRS)me(G?;qeLv)nrq&=`Qgb zOmP~WeyD))OC7yNs7tOlXX)hYd$<qXBtkB0#B9iES0W!Hv|k||2I?mQGpA|W$vp@i zd5@jw<7-U*Y8VjQnGRf!?8M;OMxrCuGj1s2Npk8Ls>phfIeyi*knD>NcvVx^sBAvs zAjHLL)$2A*hOm^j;KtrwYquLo*ya$H9BGd>r@-p_(t%@fADLm_8xYns@bmC>aTDYA zar@)y%`!8J|AG3e2~olRc3Ka2{nGj6LpJQz*yBsh&&d51tLON@JI_H89hz-SDpC+5 zIQ&H1U6EnyVNV5TS?!2}Ub9Y@OKMw#MjU5aUMmXxtGcr`1ijg!Gt3Z%rra06;;FC7 z3$$^dRd}pekDq7bO`3;pF5B`mTv*})hVyclNu3yeid4Ru1qd%EnKzHdeU>}_y*kBc zQqMN%$2`Yx_UYQ=T0I%ryQtMB9U>#qq3p+nUFLgRz2J)$@aiINFPX)VIoPry;WNP5 z77uslsp38i_K`{s_;bOUef|;*KcY0Q4lB)AU6Tv|ID)==r=R8=^RkXCcaQLmsE%LU zRb{S0VZusdNJ-NJ7$Bedm*u*h1RXguDDNejbqvPuK3oRg8ni{7?624SpNyB^suI8g z#$=U?3I&0TEEGvMynhsVvU>nX(#=D9-oDE7(zKIu-@`kf@^|LBOZ4e>=V_(O*P8Xp zFUMh(wX^QCF}=xqv-ll{duRB2p=ouKJ-VTv%OzEv&aI{TKxj=T>w8KUbN4Bi5h<p~ z_R3XOmNG6zrq3hGxp0-4SRh>dsKDl@oBz4LR$)C6KE8YZk-inU9UQlx={%y`%}UMR zY+iUJ%||q}*w)zqyfIa_{b{RL>uE?*q+mT6Z;|e09swpyN0aZ`J=J$Kq^hqrQCJI0 zWHh=oMqt&s7{HcuD-Cf+afOy<-lvtza6q@X^7$rb`7P3rcaJ%!%V)~fZ0o9EePoJJ zMcz}G_8@)+F%28!Nck5-W4e}!@wVHTGLBr{05uvhpQ!L~LtIK~HC;2oZ!4Kxsy--L z5mB*4e93XRfkz#?`j-uf-*va?m*O;1?T#OH_zYl{BAY{YW6t6we8>8Yt;V_1*Sc=y z<xV>PmF$MFxNgR@(-s?VQ<EMxLsldliCtIS{&-;kNSQ0_7^7NUK(P*MCe#flRyNEC zKlg;xUnZgjm(xI{`=dHHX+t?G#`?tY<vU-|h{cW{;}E}7ieq$6E}@1}lkYIjm^Pf3 zby{{7KuA?8Sr|<}8uu#~+7Dz6DzX;ZMLYSuc!Nm`pkDEDbXIZkm7?6{-;*O8o2Jyv zZn{Pt;}l9xFK+K}S02)|!z5J1iC**o{G6GeT~zYQOekDRZl%G;38!MdECO{nV4T+Z z7=4-KgX-0!2AH1ul`V|<cs~&Ul`8vRlG|z;O^{8S*G>17)_d2$=Uj2Iy*b99WEWI8 zQkx~X33UOvss@(%uhuzFH1@rZnL6g;3I4q&0;V0#BrK6a@p-U*S~JEhhVwKs!=?lt zwWFc9MaEA)ksPO2oke?<i&3p>dOkE;-nmo~%dUrw9+kva$YE)?)fjcSJo<|Ki8u7< zwF2<~#M^keAH%sYQGj}0pq`IJjs5%YByawu#XpyCTpR|bV)K$CML=jz!=q=1M5oF! zPQBpH*kE3!;NIWWXF*3s{>Ih;Tff8!nI-UWe-emLa=QWNxLG@t=xyt#x7zhWb~N?1 zIg%;kc5v;GD8+qgC>K<YV}RozFF>!WewyF-SlLwSKTwq2prP_Kidl`0J)&iBvW~TC z_%nUtvISp+qytekg)OLco<kET1&~s>sw=tn_P|n5)Q~6aaz;YX&I)vMB8kLLeQ?g8 zM(mKVSDNz`R*&?SrOP!KYvrBQiaw}kqb4T%CSjcx{DNV%oqD9eamZd%YX<F|ySkFz zgV?g);k{*r#pd+5sX)x`d*{O{|JoTjcn2ECBH=Ug7~6Cimzx^5lalJFuKt+(_j|Sc zJxRBO3CreeiSF`)YKs+#uJ-*K!+A}b1VO5&<2lidf)__T{crYZq*;{hi63UCf8Z?6 zTY~ShJifY6w;c_cIc38HEXck%v!sG;0MSz2V_A@b$I8=j4XD6>8|&ie@W;tjvu9b1 z+d^)QMcC7_RnM8d7o+xop;=yl<iCwWyai795|)NrZ{7|dTCqecBimHrsi_)N?k>7f z$(fiZbDw?)j?VeFuiZB~*-o8*GiUSCIGNvaq@_9WTB&>9y$8r5VlI6pQ@1sue4TEd zpwmqEe(?Bj6NH1ae<#UZLY%IyGExnZlAs}R%!DJlOlD=roIvfL9j|c<S2+Q6sm^)n zJzWCm5>TN0*gnt_0WFDPx_;5_vNxBUASY}~tESS&_TF~Me)KJBvS~GE07&Eyp9+dn zFCog0jS_I;tZ8tpXS@1R{Mrpst!bSJ2(@s<63XyDrmtoejIX)0<vcu_o3F4w>2aDI zc{WWu86W{uKHz23QMm-uAD>l2C6z28&qL}@ODwb=RdfD1v`VtFIecYZ;T}qyv*}#g z=&TmYEs8S1sKtK3_TUJ89FPW=9&MdO_L3J(#W+k&hT+t8ShJGq&zRuwnY=|sDWM@b zSAKmsA#|4cxN_W-h*JJst(sQGHgUWL4;%#mHJ&;qwg$kZJBh`|P^s1Twj#U&fIUZu z;iSm+DM;s4z7hl{t(AAiQcnll7z?(A9$qyS)?!44B&&C;J<8hU-}0Ro)9SmLtA#~x z!5CnkAh`fyu8nPIttrW<F%BXwvq|PQdWsE`R7@#FCpSH0+a0rq_`NII;#@jHY2#>3 z^pm!8`0iBw)Fgq_<R=MZ1sFZqg8uiV@5VV5ACx^`r+BLUivU6*m_-F}@j2>3F$_jI zh6K=<8v|k8haHhfg6{q`3i{fzgkso*j<lZ(=Vj7|ns(rBb9`{n^JAnZ=BbLRMd0;o z&)|#Y4y~g&;^%-Ab@e&p{%0!=4Au%32X`B;1Cy-s$rV_|F=RJ3DPhVShvi$ulIdwj z$~nB%mJsh`kAqh@h{>5L7!*FjCJaRS9(4$2;~hdpqa*d0C5Ao$FamTpy4)v}np+W^ zaVtg7?SQe_tBwJd*D-fqRan*4$JM=$|2BEq^K#hP;usM(e-2&OKs>jv!JzSZv=@c` zhc0^JJiz%{;_JeX!p5%}*a1j0H$V01KwyCp&C|AHVhDqvvg$!;Ti+@Bk!Ps?T4;RH z5a=HA1NB=s^|ne&i~QZV_-R7hheZw!a$C3$*K>#Ky00L2P`GMEz=!3<PTbMj`@p{V z19-bK-Mx9IG1u^y@AScwD~tbMA4r=fnbw)FZG?hohHhZ&A!nS-wAE5EmB(2JOx*e1 z>E6Rc>wej7_-xnhWZG4n)3W*j%953!v<oUmV0mE*kFqC$*}5O|&G9MO`1<YNL`dle zt<#a*9V4HYkbj)@fp!u}GyyXd6ER$^MS`V@$c|ZZ+Lp_D%!_9UY0e}mOj)=C8F7zu zBO>f`sB&Tz3$Cvm_*WFA%c!rW?iAfo602_mqQ?l&rIeyEhtnuw9BOF9hJ!4uWBN4f zX)c8nR#bt7S%dPw-0&`EaA*AV&Ri+SXJlS}&oSn%c-RHpRKt{#Y@oG*>OWyN!z9rY z|BmQ91Dm|)5jm(w@bkHjs){CAR?b&3c>$E7+KE`Wy<~?{Ur?z09C_B3Pv60MN73&c z+s$|y{W3t<B;~}8x9*n4siE%_*EU}Uk?de-RgZW)QpsgmKQnV<kofu#G}VOFUxPcY z3>zjXjf<<5=3cF_<6im?)B|DP`?3a8&m)dPb=?sUCMHjHeGavvQ~}YaJzAV}nx8ZU z?6bzPr`>b(Db>_bCRr^1<!|kLZ~OQwXwOIA+Q01KZD~J~|2CQz{)@0&m;HO|%Npv% zHCL6S)l)aLTbV7xI#z%t$wKi)j%$ykyLfzy-u*s12gyHBJ`*=a$@)O8LnAI@M|Hbs zv~kw~ph0zXxZ{khI~{RH{h=$qPWFgHtf)8(-#oCCmo*)|v#YWc%X;8oW$-RWjZQfm zjGCB07~H?0Tn4MTTwRl0eO%K3j@HlCwcs&BQONcdh<Ykv(H=CG)@IVu?4&PLsFk!T z9txOLSo+DZhpU|p&gkV^b6?e0d599N^4~l9dA_wTaI~`O{Pk~O*`Up$nDqTim<-BC zy{pKKrl#Lv&xtg(F?1CpSC&fswU$cV)ilp^=iJ}RNc?AKmIhilP5D5jaF4<j9>+lj zHbxJM(U}Hc!37`J>IS&{OZNJHJ7yXwm3X7Kf1k3?eaQKFzOVQ4`wr6bEY5zLzsxxH zXD7#5_L<3xC#G`g1wbsX#?V`B<#MgIdB42L9FGC*8?Zmgj~TU%9%?lTDqar2AdaIP z74<(BpU41H4X`M->tpJ8hdRz(OZRFP2j$6;yPoBWKko}v8dU@jfS;;-IL)!mP*$%R zll#CS-W-HWs%Q|$DsyDy9UL9&%6}~zi^eLUTJwX9L*iD<POm!q@^*(m{Vsh2KQ--3 zn=KojglS1t(<~oQo)=5+P|BdcfrB;VMbCBNQJDqXBgmwww0UX!!_F+8k1~&v*9^I` ze4~zaVo!a>FycCtqa<Rzx_Roy9+f!QPGYW(1Os$yAI__P)$V=%>90RsCkN}rpkTE* z-Uh#OF^H+e19fzPjR0?Y8zVy*ONW?^bKh-U(DW;F(60#o@2i!qN@>i$5!6Rw%T>0T zmvG^g3=BdfmdfmYDD&bMPmOF*>xQ!=%De_p+U})>WbuEf2##~#mv{5eW{9fPh`1ZL zTF8Kzk~j@|2;_WIfgsa#JwEmFP2dq@8qlrS+N|$Th$W|*UT})mqdpYdl0R%CvUoV? z0#jVkk~=zeN^7I^_YB{;!bZ;TJCut^f49Dkg4-CUnh*59Z1xKh(b5>j)0E0~3s|32 zdX$?L>}Pqw0o*HyGTcFrmLf!=L(?fv@F_^9%;z!EN06z#jT&jr$O<JTwv-8Z8Ga{r zFbE<Kz+je~gAGdYxLwl#bL-nZ%tcqrX0B5y_a1S=AMu8yy^7CpDv~bv$$m9lDWxkR zaX_IZj%P81bzE>DF&#Ye9xDc|=*tCnj4eRFIi{egWTSG&a_?6DXj*q%iP~1z1EiA( zK70o#V;Qn<I&S=uDV!_bmkR<G_tB=74^%m{VpA)hlhIzEuBw=yH6Xv#wn3r>YugM? zkRNVklO8-(BjScKjGC0@bN$kKw`8z4y<E={y%Dw=V-|KkO2SE^!?Hf6K^n^mdEox& zK|EF#ChHRDW%+;Bt7U~dKTvOW)XqR|>_nRR!Vwn#K<(bN!+&CYxc(Z2w&2o@L2Qh# z|L{X%JC1`r3q#7bgQI%Ko#rS_&s_xKpcmjie9z=>9fL6t2d%eH7v4s0<X5_Pl*M-L ze&^mmJYOyhVoBpP^y1eJWn3u9W^HugCAQo{UK3pKyZX$ppy@d6Dk?O$D*fK`@NWZ0 zQ$F85k>C(#VgtC<t170`)%27w=@g(ckw$7ul(zkXv7J&rj-|jTi7Uz6Cm@^|x2f3R z4(A#qbJ4-bYE#*THy+UMb?+t?8)RFhD(xo3K*^pSr5`a_S-f6ZrtD_8-{Xwr{hbpj zJyR{>H}BdYM5i8QmYR&FKA=X#YpX%YH?mxSJK8?>$GPAE$ErH6nXt*+Zfp*d-idYn z#V>ZnsCS58Tqq6qhpTP4;Hv|Q%TkMsHTPSpYl?j%Xew9KVnUiS5Fd-{a&q<jm+ck7 z8FV&F5vX{@2OMG;+(k{wDlysW4t%*UH~jZlT&^IB9^AG~ytqMZ?yoq>+BQc_dg@r) zNBv(NqBbel0N-;>e~RUaNkD5+jgpc|bk=|ju7q#sjO5hW_)NHG>wK6I=RqN}5oWBJ zZRG~XZLts9%zWm=EF|9h@|hFD%W>RJwq_HyMxz2t_MmC^_D#0G6%f(hHx$F>`ViNk zd^aL`v`*E%SqieONK$}5j4g&dlA@K@7LBWmc#1cZAf&`&XN)v-*J8F5h)2)Y&3n|? z5ib}Mn_owT77|n2G0#}QlUlUH7iP6&UlN#kfBRdVW4A`?ta_C^|M$jbuvAh;D%CKE zh>fLOQ^7XItRo_ektN48Vjx`)(x=Tvir4{x5(8Qa6Gu_G^Dw1w`1RL_&#F3pc)KhT zlZDAH<(yaaI_?>f<Fj*r@?yF88#_|6Yw<AsZut6IC5RWxS?oncdiQ6?CX-}8Y3w#D z{y<B%5hfPMc0dL~4N-mJl-~UZs#6$Wd1x#4m<(yq!;cjmi-l=0y0v~1g8&>H4Gsoi zBqpncg}bQ(o5s4z6+1n`O4Wmi({MmqJ;SK_o<*mll{pO74R~yX?iM|WyUBu|TLyU# z=5_!w14vUo2Q&g6#cQkgkS8RAaXdDJb#2$@E`kmQ%E>dU8s486@;}U?A-T#5zOefx zB>XWeOFY@mh~1N}2Nzip;EPwgL^7;V>hr-|MlbTmX@6oa!>ZXKWL(U3X-i^EzTv>) zXG)OsAcUk+iNk`Z&S+ZvJF*#~ZD|AxB2(qEwS@%Ke8aJ$WxRzYxghE!k^lfr6(&|5 zFwOA=5C1gd?dtz_@E=d`7`A<;kaw+sRm31@tESGav-#g@@}~pph{~u;RW{VAr#E%M zvudn)J@lPve4GEuqxU{ARV~N0zL}MLIQmEfi+^TOHq3gAh=y%DnFl|69s(Z1swNx@ z1~_v!FKp1jrlSxQi#%zZzxQjHqZi3;!R~*s{yg8LY70kcbpWc*tL+pGEox*IJn^#t z5N&CJPiOj!X2X>&h0*o(xyh5oP6dP`r+2b1>-Oh5i726M2D`uHy2nA^>x2H}d4uI* z{pDo&bPa#Vaa}0V8J5W6R0)457kkBF&6S}04WV)uP+;=SK=2LEa?n71&t}e5dg|zo zhlT%!nh88${j>RC&TN%EmgQ}ZcL<v@{Zev<Bfjg?VaFpO0V?yt=Fs@txsC<Wxf$@; zFRrpO8y>qJRkG-u6VRGtKMZvP=iuX5lP-e|gb{!8=-F+UWCZXDa#@NCjsE#bE-}I8 zPlmLYSlm$=#miEC0jzTvnei@8<kPm0<CgNWs9-2dyyC1fm6Ey@$K=&#PG762xIyBZ z9MHPHlPS^Gm<U9|Nhy$+4y28N)Sr&WzX6T@8+_u9T@X?Ju|k2hMW3NbTmSAGqG$q& z?+k12P=MklCOtYfr(}i7q?PdwsC5fr;H#!YAnJZq!|AG+MhHwuRCW?3eY)-9@<PzQ zXE5d0Np=f8PF{UhZNF9=L$7hQ6r^*#zn|+6g_Y-x6d$N-u&RsQVvG&Tg(IT|k_SS6 z4vEjy4=dKDfdU2A)ZL{O7~P96S`W}tF;naLrc3-1-~=(z(mROCDV)^CUBA%&WbujI zU^7e<1A;v%%CsP`%J(8;hd$3lF*Ru(iZbl~ZA_-pmkPe%#n?p{;!TUZUn{5`fmPir z!q{Amm!w4QfB#-%JCFI#LQnY{q|npLnX$fg_tV)?n9VTPaU!r4gv*1m2iq_=9LlXp zw^t(5eY{@x<wx5>H3%T>@gDUC({-2aa$Cs_ouE^PZFdQ5W^%HqG&~ECZpGB9+i7oJ zaWVi*s0d{U;HH0;^(d`J?lj$26DHAj-w9{H{$mF)GjewvA-9@8UZ7V)w7)A&Cs%Vt z+`LxaX9;|>ZuIhV3@@w@um@bQa<l#akeZt}n(xzrC)};FUjn2JqhoC5^nVP&S`o&R zTV?o7nyvo*l<D|_)VHGe>K{1h{51cUY{w<%k4Il*`zC~>ZOFEF_5X0TWuph86-6uK zCD@s~xX4{x0!EPFuQK*QkjDw<02JFF0m^c5#F9v_$g)~N*wW5qa1#-*{44bnE+q3; zIww+zBKo9{y6Wocnl#eO`440obnmt4=Y6=xw|-Xva^>X*Z86I#j5qrZT@pQKo@HHG zHEaNFct*A{gC?2=LaBZ5=Zh`i!-$49LP|THm%0RpeU?JGVcDNt#eCVLEvp^^wJSd( zZis|cI>($JRRqnu;Y+HNG{_(cTW<}`KbxhBguWT2!Q3c^mob=3NKhHYrdp=@__KYU z(pV!MC~i&~(||sz^Nk+dBh{*U+$w8WDSv~b2z+|br>^LA*Xu>!hvgmr4}>rnP$*w@ zeE*nOZhP8zJ1lFLo8_>ekXLPSPSY}lC7*B`cR?2UNe92%XeihwN&1%2QfF()rW>N6 z6gflL^cK?B4hHHm+5uPWm*nlL0CzN?Gf1bC9X#w+UzTm8i8T#41v<g?Q)Ov-lFY!0 zIL%eqmw&Z|F8NKBJz3PaY-zS@6Q?eqv0a|#&of@C&nR1!eBnFw2f&p5?ohtRkbFf` z|D;IWCf}E>SL6WUEiRQ*@L#}O5CaZ$X6#lrXNo&3yC<E_!I)3K>FHLF`$(|a8)USU z%*R;o=8m`nQxp;i$L@*ovwV;sOHpMektrqcO!|AMn~WXe4?Rf>R}-$2VFjsl2*#OB zWxH&#Tk&N-b^mSC`Q@fCsOmqk&TD8|0n>mF%a2-)V<|I9m{z<zM?^SHJG9A9%t37n zW+A8{kT_HWKwEmi3Vn$F^~_BVZLSV}YaC1e>{U~>Y2YgbtF=llMA*-wr^eVFE&9CJ zou>M1CDRk?3GF?7Q=ve@b;-B)$#^&_ZJzJe`g?Qd&m{-cWJdgNxW9)}YF_l3<f;1F zxN62|e#i6(Pd==P&r(pY8b1^eqXb0=PvqBIHm92Xtz23Avg5#t985c?;H<g@HOWVk zA7A`at1-q;151xAvB|r8W6apz{wGUM*lmwiiFy8l!Bmg12YgU$hRHurzqD$L<<T!= z=kMXZG2h5?20Zug0VrOQbjG9w`vTLLN+5_*XIsyVn&_U^XW953W50%dy>dkh{PR<v zoiK9Ma0>*NDRU5#&4Xde&0y|&`nnveZKPhat{i&G-aCu*xTZBRN?}BSf+uTBbE@Y& zdX(Y2Cu?zAls*gXTp6z5MHO=-Af6oj%5ibGa(Xbw1zK~YKH;XSBP2N_AHNmhRkQ!h z%cG{%Aha&}`(nC^xp+hmo%lBV?A8%9?K8PyY~gdU^iutoZgh<v%Tc=V?0l`fVxv0X zG%KNad<YY{YAkbv1CrJY9rw^YY0GLR#QaNGJ12<B6YT$-H3;E}5N;;<LGxtk`Vdjf z3AmG&e>tD+U_t)<%mQb?v;2`}oQ2Q%xIr9NuGp%*jo;QgcDGV<1NyD06`#A`5$qY4 z9MP0#kjAu```WrBeXQ?xhAi=FR$$KGN1(~uL#W>wp}3vxmLobICmndv0WZHpbgz## zmj=f&R=S3?mmC<o{#)H(>+#64sy0KDTH%HEFN>$TK=P8u#S}v#y4uHlbiGg7*RX?X zZR72l*E;x4nQn=-RTlH}wph?-{JyBaHr<bZ4(x_Rus6)QJAcPy$J7|6lEA<y+sLbM zeln3!tb${*n-XE#05o@Yf6tDrF>JA@gvvXV;gmXVxM8ASky(in^0%z*G5V06W=Xy| zZY#8F`vw`~iE_o6e+}~M*)s&`@}+(Z-86I1B5N;kM*U@cvGcNiz&#AcO1XhnGO+wN zTZ+DQPg>I2FBcbsk(MJlNa`*0zRD8bWysVGR8O~cA^PT*0!r1XkvwSFQ2d?W1#&m~ z_N`hk@?kx+ehKuk^$p+LAr*<Y;`ebnDQAreZN>V3jo{!M2b({q2xG4rLgK!sx?_UU z4BX0~tDEg~q*2QLnh$`M`lej2OmjTgjaa3#E=HpIBBt)eu8)(|@~n*yNh!k)V&d{I zqw;naJHOUg0zXjQKOP{wB_<4L2yy8X3_Q#yXlQ6C$O{@1ijqTI-4w%>f;|`_VlH;8 zW-_ohclTfPBj_)bzY*e=zR<q3wkwzs;On>2{CNWgV`fAzw@fQYnGFZs&v*u46MUx- z=Gan5q~*LbTFxnwp#)oY%^Gi{313#d^7ULb{{yADm4!lf@d;95PO`r4xZKYs`;c0z z^<rcVS$4E0mvfe@l&Y-0u3H(73X_Y9-#4)P*9j_e9cN|qz+$Imp)|(WTIXj&uXfxn zPCk)Q*?0ezRb4WeClfsPT+%L)5q}8@Js61cZ~V6Nw7GE;ZCVZtBNdDbH@mM+BLD*W zX3|8pZjX78TfS7R^B<JZ3t=JPF~M`q73mV07E%rmK6L)Wc|1?bl!O1xT#B0psdlh= zi6NQm^P^HkDapK)7S$@Vn*VDVU_B(^+<t_gBt_akL`_eAvAfBAtAN=j*{q3d&n$Ag zS<5mXoHUz~O?|4sgUxv1mBEX3W}uVvfquq<`jgpvTc4~+<Jg2vW~zV{ScTD6SL|1N zs_`4&OH@DaYJVnk*O9Yz8%`OCCH!4^ngSTU<WMTm2&b~(+b%Y+&EQe>C{!y=TrD-O zub%|-C`#93IwNKlNJicl#Yfw-*Rj4>1+_JAT|I(jCAT`gDoRpS_T#92W5as>Tk-W( zc-)eS|6&RYN7l3KVQ4gCqzra6W(+O@5oKuYyi8Cy8I!`;7L76b*==*zdGT^OqW8Py zagzFfj!VgQ`t>Jl#cQ8Oug_mu6|CjH#fLQQ#T?M+=0t_|&bX0Q9?;gCu3GHfcFUwC z{gB{+Cx8wu-G?6p+C^&NXJf~tQBO`C2L`=T@3(u69@}w>^c5BfgeNrtmXBd+Zr#YJ z@3lrPe_6st-?1oH{L-h1r=n8HzXHG~o6O#pZ)$tCw?uCk#cJ~j)%H}qTU6#Z0P)4- z3Erbrai-Q<Ss!*vvZ3K&UU+}l;npWB2KWxF38r!{fp`7k7i+$gbEMu}7V9*Mudn*t zW(&1|i3U<MVpL<>6iG(4+BjIP85$d~h&KDf$jCkolj%vgqflAF{`I>|Cv0^eCfysJ zoirvp88lmDnH|of_Og)lsl?KU7eayKv%KAniz2IU{ptiz`C#)9(nL_!jfY2o>lWG* z4~HgB+N$F(zxk`p>-4Qc>T_Uj{Nk&>k2hb?!QA1;^57=2`?r0sn$kv#%VZx5c?ZHv zX1L9WoJTGMU6CAiBU{Uc2KOTlKf}aH-g<%H?v8{LBr!PXFbPj00E!T$8e8&u4u11} zU{Xb+k2+x)6ra|B`gnvJ9l@i?aXI>>OR7(h1Wg0d?1o!~a}qPAR1>Kz-u1U5;pt># zxygo7kO%d|KoJuwRn`8{Z;>tUfJMB}u=j*E6&uWKwNI|h`B)>D!sM2=*W6NgUJ`}+ zls#BM)zI{%c73x9kt6HFA#xq>&yI{~JPXgq*SZ1PE-i~+6j-Lk`Lh9R+9$rnY*1RE z7=p^V!1W%ioK^g=($y*^ARW)jGR|-5)Zr@&U}Xn&rj*eYsR-QYpj!fe=pDj0L=(Fz z8?-~=uNUqI={yhD2ZPs=y5j3^&h2a4Vd8|uU{}5`#RwHjB4EzXiEcJb+hvwS(Yd{o zuKV#R*RrD`F`zUS7-u!V$wi?w3OJF9=(~Art}>r5UlZZw&E!x0DJuw^s1p`0(TO-5 zs~X^M1;5Fot7N*z#$VBmrZiQG<L)V6kTd=a5u@0t5&qMuMGsD8rc^*mPaLV3P=CNp zU4CK>mhTtEE*e$25tGcXUHh@D^pfH}k8a6dM^pCoQnm1#aw1tC`w8rPd&uEHy$w`Q zP$L2QXYC{rwkH{AFOuJh#d6n&8ct2-9x}1QCCG6{$EUPmhk>*aZK@g3v3)hmj%gU@ z@Hi->!HybfD7HE<7O}($=-Hq#Q^*ub28)RyGq$Om++5Pryi<d=#CMbc{6<9~B1{f$ zlt8B5v!^j}4|*YoaI@KeoF5cg!4H;3eD}*_DBhD~?^(+OK})a(Kc_x$X{{)ohh6FK zn*~D?WMu_atc}=K{<@;-cLl~7i)+C2Q2~c?Q3+F~s7$+^Mg*5dza5DqMp&2E##qi$ z#-PYkD^+!oMZDbC9fnzrZAMg_N;3R?BbF&y{?i&iV8^!4JeN>0{KpC+)Jcs{;hm6$ z-_ML{qXo0nLp@;YkXxyz)8dQdtD880Hm*>nYacAa9Fw)ImHGWO6>`@$nv6nkf35j$ zn2x>+#r0gfTr2<Djx&b@4wu}xx+*Cjb~E(!1H>VA`aB6@C=4gS!V$8!n_~MIsJbrV z{RXA;&+jj_JI7<_N^rE4jDrNa+_K@Z#2RWuae=km+=aWxt&DrqgHrrT8u~xW*>{_> zScj=xR`zkx2U1gJ)UuR!Yx*fr8OnFk<l_*-Vm@hH^;w35!Q8?aqWL@p7F393W2#w2 z#;HUIU8@7;9RBi5?8=C_wCw4BSY#s6?(t<S<m_2jlAKs<7JBG7M{$qddwj$=$1icF zrhQm>_)q6geEaUi)<?TASR};8xXV!~^f^KF0qh^$Ety{H%aKJ8Op*PL-CxKUgjE;B zSs$#8>lbCG{n@IGiNIycp0*c3oO~FGsfd_H+*KvDqjvffsDFnul>2#=CIrjDb)$5s z2r%6-YcHb&fMq@X2g+i=<zAh1E@`fuTMsIm1Mh&}0UbRGUm>iqn3TL$d!OT9qr@xb zXLcW(w7UNZK|+H+bCV9kmU?iJ(xYmIRzV|%8q)KdJC#rapp3`Bt`LB4YEhoTp`691 zR)dn%09U(!d55}NZ!n=x5w~7%dE6#nofwVh!9%aA&VYrDTL&e3ZDs;T6D?9G*ZfXk z5hKMyXNR{DWdfUFE(0&coS5Q9w60bH<kFyNNJJasKDS<L#rlyM3%4eb_Jt(=3?mzp zU9|vv^p-h+--7#=S6OCr)od1-NRht@S~J95V+90hm%MI}*xk;D#_ouE)^>W`%%@AE z4QkLe(uoE4?E2ljE<&toGr{{_&}IX^sEr&GjY`W>!itX#iM$uwN>`E!oj9AMi|aDD zur&qx%&XI!a+eC#2t_~S{B)=H_xraz3K4;n`0u~WQm%Ju$A@%IFm+zojsO}(V@Mc{ z#=N&i4t%2EAew~!Og(m<n~2l}Qn$>WOJk=Zs32HE%pYJ2mAGWL5(@VTSYZS!+#ya& zO4I??zvv*8&g<8IpolWvgS&#-zKM`yKJC62$jX25rUN%z@cjctP9eh1PMTWS^kJ}| zYtz6yWih0NC(g}7CDB5en?;3LG#1~{lwMMtA@BJrRzBDsL>MoRH}(%y*>5}3U+~xD z3%YmYM(9<K9t>*=ITE6OY5V@eEmSU(Ed*Ftlzs_P{R&mGLL&g6g&QH-X`QWR4z!ph z&2}c?<zjypYu|T6+SoxvDn(HYA28>~xc2R6@(OjCww(0BoC3m!e2KzB5W_=<9?c}g zmP&!P_=5MZAM%-y#GuOr7m9iNNMX>aknrq|+>m0;1hJ83HX}$@74k43KCkbtsh>A7 z7Ng2`V~f()SVhT~ogYdO%JUTuL$$HBbO2MzWBBQ6pEm;gHwp)SM!+m6={Hb!BIg@v z005I;oOJzUa+q}sfk-g!(K2pWl@KU;)YNQ@oEE?#<kuhh+VE&GPBbH;evjpDJG-a~ zeg1O=N)I*=UZx9L)BqxKSo(-;!otYNwyMMleMxf=OnHKr4Wpr0{^FY{0IHa%|3Vyd zPtX)D__R!Cwd#(de50k(&+4EtWo(fFqEseR7$3ThL7K(B)QaJw<|834!a6D(!9-1- z3{(6`tDd=KD*JtC%J~M0?idpxcor_S-`Ln1CYgwOs#Z%5%sBCvhW+FdL&|W*nhAc{ zE3FQrq;XHSX`!P@G)l8%#c-e|T70=Q1d3?uUHZH65n>REZ<O^ZvXRK<n+j_J$~hs? z6wjOhYxIiHr<~ts&}S7x+C67k<^omWWm&-=4NRaDG<vh2Nw9=+-EEZ?ux$)Fvn^1? zpP_=ppo8$B&WJYaaf8j6uMSc$shd(n9hq86aUHj(hR4ENYP8}PCY;#W_pgJiqC=`f z9H}og)Xaj)4uLXJZlyTT_EqBbQ*O3NK$y(%tuQoBOt3hDc=AvXWe7_wRHg(J5=Ju( z>p1sxEKDOJ!Gfg67TTzi5QkV9TnUBFCt^s8SZWY*FCZSAsu&-X?tu_hA~S}fNzeAy z1}?{UL46bs#S;UDr83CPO^VxP4}AowK*L~Y$(f)yL@j4f4_NTh%AChacf&@leqJni z4Jbzra*YXzTIOHyb{s^u<>!C~?AgU;Vl}b@G38}gb19`FVun{|nMMtinpf2_Xz0(D zOE)~r&8#D0!m@5w*^O3y6bV;RD>0sFUA(qHW{7R)bTs3g3=v!)A$?Np8P2Bi*6@W> zFV#6Xmt}3H6Zq{GyW~6&hnScIRTf%TQ7tQ?x2@rtueL}MtCN}9F#sK6#fJ#xdBh|F zQ4A5EQ#$lMej%BoPS_~aZSr&mMy!|r16Av>Km={tfD}b+fv=<PWK}OFp!c1<GdJeq z7DbT)Q2ok4*2D>$qNSI7F->Dvi}KwxF9bQ+q6HpSjMb@mxsVcCa&X8kECgn)Zfxii zTB0O9G7PK%8KgG|mDY$-*BK0R3({tLV_TWg4Bqsx-uG_8;=+;@F(_!Wv^~7ye#|0v zo|=8tVsY-w18uP=F%AcEhjLc>U9WQ3elcTEmmm5byPW{$f*p;$mAS0w!0uX^ZTBP> zJiLUsZ5@21OH5XXHYp>Dt6j61c!te(sEiFZB5zgYBGf1^hb>c3V?LsWcm+|CNMa$Y zOc1>T%$PMXzYtV=aFWS0XQJqO1ghJCIsB!KzBKJQR*l!qml!31{+~&V>v#E0xT2KF zt1Y{a-r_M?Y|^}ppLiecmtPaAA*l1x=vIBShrb^01=8X?9GjFpOCVh>&_svcpV7yU zNko~c%~lFYgzt~u+82Cp_n#J2Vns1vduTDr4Y2ks<g+-SgK~;9b+N`br!=p_(E0KR z1Z`vEEAHfjI~Xk}5}WiW_V}c*=1`}?y$;t%q@$ybYSbC64$^yAdK4l`EW4D%Fd3Pi zQ=-_Z;DpyCME{)Yy%F_hoB9_D#LDm!i<MUq;)?;~mEpCq))Q+Hi3s^%n8xRY)aDM# zV4%9>kQE8an8is%<-}5ixU1#8ih{HHx1|||Y-5Gk4N`43%D^*O?-cYVMe>MZU6hKw z>WgO>@f&iKR{K2S63^_Cpow}7U=`?=>vP`xc&2$iK^iLs4Bq~D4bSqy9b{qqTq&y* z*@aWP6<;wgq99=JOIoup+NtZ0n(tfzvDPvhF~kGjSC^HFYra#%ztSp$?~oO(fI@`( z^p}AIuFo+lU(xncaa6Ifg<2y}?T!iUQ+?*_o;BPBi&w@-o{jfR!p27-qbDk|@|$gt zg?NfA%;Ht11VBq%BLutWfKB&8D~Y1~J+CRnfg8(E%2}~`OIn;wnXU7PMyBS)Z;@JS zf8~%2N<GP}Q7X#9h##A#ISt{Q6hedvuF`?WP`mu`$aB-M=&r@pVJZfyieW<(OX1L1 z`xPO2fz10L5R>+qV763Iliy`c>AYA0m+k@z+q`K)-jG5FaX*Dj#wC_&%cd{dRGJtD zPC)U;z@fb$>$rUoB&&f4)d+2Z7GbsOgPDzM5k`|qFA&;54mDxO8a?q!ewja<D_5E> zQpW3&Lo)7Z0<}uf^!BePYN$|4E%T{d<)`!<KQkG7mYqMMB9}Fz@~qwxWF@1D%OO^+ zfY{+DV#@KcsiYMBaZ=uVve)H4*B+}S>K6*<+l%+<zYal<i_9H7%+xLRS#>CG3b}=* z@;S4#SWqw+4q(;PeIgI7u4ro!O0dpnAoaS7deFw!T2P`vCIvz`VohVRuB~ZN7X4r( zMZXRJ)KxrrZO(|K9tS(vv-J2^hJDV~SQ-;|r|r&<6PjjnOjjTN6BBPmb(kCyJ-{=w ztE`@!i&a@HNIgwPS`?2C$6qjZxfoLi2WACks<bRJD2VZvTH<q!Uyqb!ttus742%Wk zKTukQNor7Vtm&T++qE!$#lhr5A+>A6j#j9yuaJKJnWbFv-1<|JYai41YyZ?9ay4eN zTJZ6nT90I4CR8*@dIQZgF*QbbL<+}zp<fPS;$nPAnzq=*9!(*Wt!~Ed(at9F;X@g4 z!f9aQad`j9*Oo@a<_dr#AmBr>$?LplbvSW=x>@6S=XJHwZYq-zbKHz;asB2ol-V&j zNvEvWpg6#dNB%@LOmxdhyBPB=)X^yuCS6l13!h`lA82@={zHRTiXP?x-o$8*d!3Rx z<epI3mKdjQ*#r#xp@9WdDIYyJDn&F+-0N1uCPICb1T^ZDxZ}kDD;3muD=By-sFJc} z2WYicZX_I_(y0*6R-SU+4@^_DkKkemzY&OV)xm(|C(_cyCGu}Q0X`s~E;|w5VfvDY znE42LsaP~q)LsRoa<(A4%7YR2WUuoArOq?uV%n*%aPhMO^kK48V)eeI&E5eMrdy{Z zM{6I9--DS@NHcSdqepV+!gyBWV6k@mqvhWA*x7=(g%C?&pk`ruF)rz*sevj*hocu7 zbQy{)ovqkb^}Kmvb~XO<o!3ykX<=moz=rfF4hs_uGRz9dq9G4y0K#15Wn~d>3`R$| z;G36ORTn{i33?rRx-N`sr0^s9%kF1u+`XuTj_81;&V`FIMCRn!!*RR}u-L<!S#ah~ zSGmbk?x|g6?!oTdtlk~@t0o@S`F)ok!5i&KT>6wI!dv=1GNwD#3~tND&W8Q--$@Cm z?|7I@qjqx|+9`sqiDFROVS639o^eS7F$K=8R1X}5;ZG?8Txf&Ls=R1_M@vF{dzQT0 zz7Y2CUdz-k1w1WcnvZcJ9k)ESEtVmnr-P{}3BPl{Ib$d{r|PXtA!1O71;fBX4jFD5 zHS)Id+*?Kg);K1-3MnutF&cFs3!myy?nBMA;M09Mw6$oLsh}tenSEcrN=FABZF?I$ z!?itsQb%r~l8tZ4M;NNo+`%4S=ERB?7&_d);VT$w+d|(o3bAy*qHP~^Ao?$!;TnuX z1!N7e!;}P})Tn65mVhmUtQfSaJB2~+L@xCEvGC(s)OZ|U<0M(k#wBwEQ5h}}#$vHr zq`n19^rHz6+_6k#PD=iUr{*46Un6Z&=7u>(cNKV^Rd3V}K)Gytyy?jOF&N=-kKyK_ z!MTJ%idpx((;tQF5{49}2o^WY3k&}fz;OJT5Q$2VSWOHnE=1+x@Ka#y-Y+0W85}WO z`(W=Rn=&{2xhmwM%NCHEyrMRV{lIUP`c@*@@4ZsJo3)~64xHewg}vDyIyw4_?2kP8 z5xqdPb_{8bmr!F2eN?|=-4$Bj%7US3K`CG}nnD*fJZp`kCsEq)C!ZpPh|{ZnFwIHY zx;<`=ly-1zmtJ1uyOj{(l{Gi?Q#`O3Q(X4*M!JhnqB`W;CZj1V-XFr7B%W3D6C)&n zh_C20s-}crO!dt5yPp)WM5zCPx`|sgkSCuyCk?IafBI{L)5rqP6P5Q%=SKT8{^k23 z6M|9?LCISGVLKSR{9HiykWIy~b?gd!bzQ#JII{gFa4JCnESQue_G5wHGd-;-@U)7S z`g$NffRKPL!|>uF;HU))xz1w^op|ttxg6H&QJrG5CjnW((9o3o{m!%v5b!x_kQS4D zQ36)nJO@U2Xj%*($-zhH9>|-3_mz>^w;GW?90Od;AD~Y>BH|u-rN5B^<!4O1U7<yw z-Qg(RWjDJZ?r}XZQyc_^hyoq7dPe~h1<lwAogSka^94sscv<Ynr1khtVlgGa^80j> zD9v%20{uj7^^rDP_Q_FS?wk@q?6Zfg)uKh-eEmzi=wQrBkVPiBH1w~sQ+6-(cGWy| z@yH*6o%B6E@N3~uYQm@ELUo2WPJwaU<KP){F<zFFjyLC%R-Rz%9pr^^bZ#^(Qu}J{ zZ%g(v%Vad8B|p<}FlL5qI)BQi7&8>ce<cPj3o3PL#nlQ=#!4n2BOuYX&Z2xdPvZ%) z-auZMWD^C#VG7AU`z&N2QoX}BN~}0M3E}Ul(Y5XJYLlcdGt8v@L}<+-`TzL(4zMPd zZS5~1K<FX#4k1A3RXRusy-2SjRRp9<6A%J~D!qgB4$?b_O0S}TG${&F1gR>Dg6JRj z-shZq?{n|ZlZUi7Gi%;iQ`ed`vix_VVZ~uw?x$tu9~1B2^b4_xn5ZB+>LyDY<2uWq zAhBtiIkb+=<&BXuxpC3Xd_f)@S)!g1oZEuMg*U}5$dzYm&<uFcTy%F9Ocj<!(B{BA z#z)3MIm7O-rmL_3#fMP1X5d^WBjDqy$rCfwAhUjW<A4FiK<QhqTozb|eg07GO>RgX zd(~}jJ?HK2B9l5pb(6_PQ_rTNjkv6Yk24hh=8@rLd$*VctepnkS2_%q#h*W|@TQ@S z;y}-So$+0Ad~RaDZtNmSt!HaPDCkz&d?!3aX_NQXO@URres}B7bTPQ|KYgzA#?drL z=`R)i0bzG=77tQ95C8`7a1#ia7BU_8cvjf*tm4m$|NFtO<I$>%{r>>cHE$@cdUSs2 z|Nmfe<kmZvSvr^6=Kk-D$eBD!zL$2cxWWG~jJ8Vhs{WZA$+zV##Q%ZGk<;s3IMm{F zWnI-v`QI_z^m6X?s~(_L_mEWwde{H8>>Syv{GF?r-EtmPtG6Tn_jGJpNj%!^@AKfw z-sEBQzhblxC0Cs!<B^?m9-U9z{&#-N?{uyRHtYC(Ge2rsHTmB$PZXL%JYJ5)1iWlW zUilyO#dklE9LHGJo1@OvyS)}NovZEv3;&}~%>aOpld$oXN9XELy1{1Ws$*v@vQF>d z+<yE=?O)(SoKonfYjFjHcy!Vim~OLluCjD!Ap;o4ksMCn{w^JW#|z*j<ZoN`=y<VX zCUDfbd{n1}gcBjtHIaX_Q~+*|!^25vQ?%;QScz=8<Iy?xKgr1?Ljg3%bnU}$s~*0* zT=6(^<lNGAAgQ=?{$zx}6(?OA3gF)2$XRupuoAet>hXshbsDgeUm34Nr=>zR?pt?& z0*8E}(7EcMZSP>V>hXsh?loH2Q<6u0B{yj2w2-|36qni`a+aygrx<VjMNV2+OkAId z-qKgLfeI}x4gmaz_CMr6RthZUe*Hzx!-$S~ubL;ih;%ZEEG>Yr0VmiWa$2$Tb@Q3u z{vyXxtKxzA^~iYYOtqh20Kif4H#v!1T(r>Nz+dd3>`lrAzaG<FuoEqP%L0@DuoI4* z!c~t6-=Jx&2c7T!VJE9j`%kg8xN!9VP~pmdRA=pumwC*(?vZN!x9V^$02H_)rE7Wq z!Q|oC@#wsC>$JvQ*5e-VkLr+P0MO_kjM1NeyvQ5~PAzoWJw3{BYKrtf;YFt3#r+uc zU)xWbGEOZ7J1^azt$L)Yoe?r1GyY@%5dTTyUk^3G&K0tb)v?Z1q>(iMAwuGG3itPy zJpaPzb9Am+XJ>eH5=*2j0eD(mm_HDL|0IG-O@F0x)g)xqy#Yrk05EC^{DA-x{|%$f zAc>Q9RntKWnffOSyFU<6#ec(Sxsc;92q)Z!C4fu1&L0Rs_Ag2P5nEt>)q`pR_aRRS zv?AT&FO2K|zzB2G+spoi->pTa_y57*{q^?0W`}x@{nuN;nLij%>%TENf9%ebf9%d+ z0$j+<|894NvEwe$E<F`VYZ*{7n6=yd+wP15U5EVwywYD4QYaE&-}C9>)lz+5u42Cf zEfo%d?njKz53dbYJ1?o|w@pw=!5*%6%(PS(!l*E&*-wYB;ifM7?AS?JDz>Ca(o@S% z<y+(76Jtd&Cys(}!VvR<Aln9ko`IYG!-_{r2F@F<4B6Kt%M*}*QUJMQLIpzeoH4az zx_|MWyNidar?)wo4@Daq^mg_GtNDT9huWUC3EX`akRiSS^O(Tw)1@chTsHYeRxJhc zOHPcdp9zrnt{cIpEWA!Zvh}z9t$VKoy1qjS;okXN(|mqkrX!YY%akl!aX)!yLz>TZ zVipsSefqlR8;YokGUJ!nTiKvM|HB89tb};ULwtvVQc2tOSE+kbv%2K(LJLivUMRoL zC)}B02xoEg_Et90M>@?pyXi-1@VBKf?7!&WIiHv%S*<TbwR_>;IgB!6t-D3Ykvv6w zOlCc1EA+Ck_{nboF6$9<QkeXUJ5rQSmvW<6Lz~B8`02dw;ltKh@{F`Sg~pQ?;?K-j z{SWn&$)Ch}y(y+yxTh64P-j8hH=LysN|f}zVek=^s~wy>E-opM%G{=)=;Z>>t=P8M zG4WzL)~L5vE@b@hOCcK*o0v#msBYR!?l-^Eq0oR(&wwv%r_FatpDa%qa$Sxc4BqOf zkyo_wB5Zh@Mc$Ifl3?#vYcePS0FvM_fi6!!x$dj^)cw7yAJ&yP?IovG8ezI?)1`MI zExk|Oy{orQ^>t&7s7%iq|5K3f{i3s$r{Rk!$%gbZaR~RM{Ne}B8V-3@d^9txY~-C$ zi?6ifg;%cMGWc2}KEHO1bA68>aLlrk?|7U0rxpp(C9;{XW*D@r445)CEcc7~YdOR< zGWHNQ=Kc$wpCi3)=pW-f4KS4cyz_}=1#XXF-;z^fs(5IKJ<IpQ=QibfUS3W3OT0bj zvU0W*PQoadQN!Ow^T_O1hg>Fq#O|kG&(0zS$~5@5FNk8%ZzOawUoqhFC}kJb^ye+E zZ*Fam?7G__D!5tgs~@2>?VXsCad~LmIrOB-A;75l^}b!0#>s*<H}jhZUi>^N6}NWM zZ`KVTD!<P9&JASON8a!24dCg~kgUHt`H<!J*7M$K>Ux79!`(`GF?I%~3CBG^Zz-Gf zY3v=IgyYzTjm@+33TGKOt0i5JQwCI=NzAX5l3d_fPF7m2rR$m4bG&aK>tA<GwN*Py z(p?Ti7--+XjZWsv!QcYmNoHxb5X)~sw9~Vv-eQcMhacqMIH@YUl=+ko5!2WdD10sP zoeP6b<d}J-&i8i(Y!IKN?UH<2snFrbyBejb_CPW2HX<u&{g>gU*Jpy*sz)x2c<?x# zwx<h>rs(v4197X8x^pM<)d+wY(iU9-v7}oz2oF4cB;&L@diS_`s--$W?i#@I@!(}) zoJ^J71C6PuFLUJ#iu%X48rquiMdACqdLgW;sN3<vTy;Eyv7;#u&pvpQ&)R=Do3OQ` zWtk3rkbpK6p5ob*`YT!F=nJF`h=j3AvLr>AjgeJ&DgVwm;mg?Kj$2Hxd7jz2NRL+p z-hOm^Kc6hNKwrt<_-OF{*}}^=54OwjhAEQRn71uN1jda5<-R9dUMG5T`5gOZf@kG( z1#*4pgX*;2buF8dU!2p<C!7@Af^pP=ck&MPD{Ls<#g%irg$6bs{i+^;!oC$>e3F08 z_9Xkppy#iXO-Wy^Z+%JI-j&%VFI84>K?k~aXsjPJTRC%u@6{eeo;}rHdiNXP3q({v z1U1W$EJGKit~{&e=i{ijA=@A`mcr0izHw{pSKohz9siN(BFlF(-^q^m9Ni18O*fkq zprY)fV$z+2U-Jq|in`5?<c)s3W14w;iucLmCdgehKdKGR2`BaA<1UJ_IieMSkB(Sj zQ;ZQe486w%b*Nz|Wrw3|d}RfCd(9lmLD`hq?|KF451Kgld$2i@8}P&*qb^fxucJ>c zOOmD1caxs4>iDj+k;y!z+e#Tb-pM$9^t^@!aE#dIMn@1p-As=yxcD<^49yPCR8h-T z80+l>drn57u@gK12}X#-6|4SDNJ`Z<>GkTUCsES@obNJnhFSKcslfxkcsJ8u{{|YB z$<{Y)qSjTk$n%bREGiC4K8C##o!WcO9CkfWpcuP88LaQ}5&?mV=`rXF%Bj<PyYZTv zcKbK+E|CXC#@*XFfNMrjrzY{#Nsjqa9o9H$>cJvbd#)ME>UBTBV<pqV-i#cPKebZ# zGtx-F%XQ7@uf3lz$ua{g8{Up!Zt2S<!nhK=YQPg7S0+FavGURpLzGHzPR*)JF~@Zg z@aY$AAjLGJaB?mTi57wBPMrwG7k4TNuowfIPr+~VUTeB$dH5SB{PD((`?$hx5zEgU z=c&)<2s1H4lU6T1*CH44sR_w=VZ;%88@yWfTrcRQn~Ij-GaoSQ!@tP~i0Q(m2zY-b zgx?g(2&Rwl8t@ZWCwcxM`V4nJ)3whbJrDj}0X?cij#5hX5CL&}<*!PR06!wt>?t_L z9u2+g6VZT9RUfQ{Dc?;kfhS@$)0&^e#~``!HILmx)JAK;C?`|2oTD-&svy+@;BZp< zu$!sMmkO%KFjxt8n#7k48T}%V(fS0?Xc83bAIr0f#c-wL_xp^~xPU+refcBlnPbZV z+t_Z?7(l(Qe^J-fz@d5a6?E?@Dd&QqJgGnpocP||MI9tsD;$HMkB*LBnR;=9Xv*FI zr4+@fu4&^!1{?m47K1rWP99uC{nXY&F4<*03FfZ0BoZdoRPoo~nm(a7YDm9jp`TKU z>o<^>w=`b+ve7(;9>0oR-sGTk`{_ek6n&lNNzQDT^gm4`PcphBBM9=;`jcL%3-YZh zg%OT3X&Er_a1ErMZaP`#kxV32A8^BV0}m-B4howsi}^Bswk2svzWnG&ypk;yfB+Ui zGd^aFE))e#cQnm+rp3$n4HUVsICD15{JV}Q65e4y1P&eEBg^_p*_5&VUo`YevDM^V z7f<2pf%%;+f;CF5nP7*C#<%k`0YUtH(CN5U^ZHksHOr1ACaTQrz0pJiJoa{9kpQ0o z$l93OGmio7F`?c2h&lJRbo%}tC<jHSx);j*QcH0u?QAV}#B0C?*SA)m#J0R(=+rof z4lO#iRr|j6>9WV^C-<@Of`&WtWoupmB{MVQhq?hg25_XqD#ph!d(?F%)-FAB=Zoc> zvQ4CYT#MNAA)gH^lFZfLKr{^vCi{pR86gx!v7oXg5o`nyR{&WFE<e|p*CB1}mHn~2 zmqZ+S;28xXz8fX~g@m_7XEs3ZV@$oEi|}LR#gTf_SPIA7>-)Yh8vWPrI7#^AO5~UY z(36d7{Fgd<l=z0P^;~QDA%suFJ>SW*St4*;JPM2MC(X$#ErKWTqD*Q7CYc(zUv=(e z4Z8SQ61K0a41T~zq$QVg8vB=Vd)#2zWNS-#PJJ8=H^@Tofg4i~mvCOPTSQ+C@RO@m zQ4!L3B|h({&+Je<lbYI-#FP&gRfj?AG9NIBuBHH)X|unZ=KwTS2Y3uLskmh9!g)ke z?L1@+^7l2O{388>_$zb}X%G5$R|@%<S+HE#EaK3|S*<=|4#B4==`wa_LPzVBSUJ(~ zLErDsM9%~cN_%B9C=(em{mQnzuWuS+GiT-d&<nm=eWEB+H$G=+PE7wZ)66VX<pmp2 zTl$TT?FUlqsnhltlDa+o#j-djMExr3H_K$jF=;%%sXQc}9w<`aZQ_$#X+%^R|IzJ7 zNp0kJ84&MpGf*KCP^%MJEr>5o;ywZil>4wB(weQg2}_de&d2#|fV%-b0KyD<=NmIj zwJEz#zh9)ma~lbzCQ0p_aWry+K+5)H=1g7066R6`a`4-PN)WHZj-B=dK^~duBp~sD zvPdJ%Y<03gr@KYz+!;gy<EqR}9@MmqB>dDm73jqt4yS1P+qVs5!ITWRaU(fN-4Vi= zT;c6*pm$p+N)S|lR8P`T^si4CC5j?ZYR8=nyP*mtqE9Wy>NltC`rZ{JuUJehEAoD+ zE|}4cDOrDEW)3#w15?kI)l(@MXlg>(L1MZ20XX~WXbT!4UM08aD&=lujV`RuZk}dn ze=SRD-$P^p5gFyMn3iruzOlDY5<?pj|ER!HiJ|E%GO1ly<o&aU7|diMw6+P*M%2sQ zNqMwg0Tm?W92SxVJ=e`SwOqsWty8N)&)rOyA8MBXL<RsTk?=nQAhg(&&ZEA2?8)<t z<qxctT7GmQyT6jDtYsAb)AxSw@qB9Ng1!d4R!ggxbfyaXZqvOIZ%|Gt*f`{*8;qP{ zhD;ajTgh%YvNC8YMNEC^q9N^qzBMiv9G*&Iq%`b92(2eEmtSJ}c)@SAmDQpiL{Z=; zXEHccf`>4mdVYa+hLA_lT(A=u-riA`cO@VIhf<G=ye@kjg@=deufcB+u8Ajbo)MoS zi;l3q9MFS5bat|Ejy(eiKz3kuT0H7LK2O|<KTP50a!f=itno&=l99B&J^lRxwVqt* zp#&Oj?@%Dbx3#r_E>aOrakJHVu3BL@2|}}-(PB&aBrfxNda%fQgJ*bfqZg(9r%NnO zZM?I(v&8-myXZubHxET}P!2@7QUdXM>IDuXBMokYB?1Uvb><W`Pl)tWc%J8gt8Qs; zT@#<8$hsy$`;sWrVk%F+Z;xw4cHRDk?Z27=96=gFRBg&9ArJtOQAqarXauofs{IX2 zI#vW~(w~q0rv(&0Ohdq+OFm8lT@l{p-upS^9X(V8B+&`+Dv23lF<$%-LXfzw)lHyX zxx4#KoSzG%nYA@bC97V3ImceguFBI+0;V#bml}r`jK8uN<h%Rj7K6~F4^R=MM#=R? zf_<2Zew_8Ad7Cb==bJXo1Y;-rQJE;r``jb!ZR#j93zCKiJIh%M7Z`OW5aoji!lRg} z!{9GiU~Fz08$ARel}MGD5%=^9I6pCU#~Vh<ARGWcEgu!sYgRt{Lf^>E40SDsAfm4> z<2|Hsc6FlV${FBhW7+tWMG>DoKh*Gcwv>+5=PA17B1aPkEA2q=8NkSKfQ?uOmtX+e zi|~K*rO3g$zi%dV!?7T^fQ<5~gb8_$&zWuV^}i>cAKq^-TU_>lYAm09w3)bHY#&>R zXZ;4Z?!qm{-gCR(d6-mH=5RFR#9`Xyat2PtXb%q8292AS!4eH~$W5rZ+?G=%C<Qnu zX~b?t3b1u|o;?|@6b1{a(m2Aa9qxw3>%%9Jlcjn@Dx_-fwIUki=DT4-<b9N$l7c|Z zwfMSdN+XWD0}Q+q>XL|5r5GiqNAdPpL*`laOj{m4jhZng&WLzZUR1Tf{-C&%)$O!z zgG(XYbvi8s--(#<GKusrWwbKbGqn(!cDLU@<j{lzuW!i4a_k<ch#O&Ra}tQ}Goq4K z?DpdZW3#M~x}2&Iwe>>?0ANIzm<5{d?2U;9L7D42Xj<_^vZEP9+KF0~>2K6aT6Qo+ z{%ZW!$km-}WUXdP7e@T`Z1AO|(KD_ZMt6yM{SExRZ!+p67wO!ehp{mhj~nKF*Bp4( z2FZf69+^bb*igRz56e5IgZmt=P+F)Cze^(fqKBr70IxU}Gy_LyCZp&5QTcszeE4;b zcsb>%o#V*R+D22J1L}-D!{eeBLg-T9tBc<F<iKU0$ZL2MZ#K9-0)jB&lXyE$!VyHg zW`aF3V8FA2m&A6P1+*s_J#G~Zl;BWWaj#O>H*{n$W>>o7ro%APZmPc(Nbc^4iMpG@ z{TNbK_5(ui|H&qEeM*XBLswoU-AS|L^COdEsED$%zn7|S)YB+0QtrDg&Z-`oteAch z0Hmo@p~LRNgebrT%0)JFS9I6(Z~nS+6;+=uh%ElFm_dEK-0zZr+suQdsl=sbyF?15 z(^~$(H3Mkb{7n&Wvmi5=G4Nw!&_D)+MQdw|S;w^{Ftn<bK&{1Y*z30By=GV2efnu5 zhr_fny;q(XFKFLMOmK*@z0a65a|bv7U0DVFlr6^TwKjgOVY*>a_XwQts2V+f7Ifjs zF8&?FNA>Pb`D{PMw94^TtviFULxMkfJ@Sh8(3hAC!Q>ag=2A+@{4mmVx?d8-ZUl3w z&j<rQ9Ov1Pz7QUx1e3bekpwO=w}w##o?2)S-FyA{EGR-5?XyE>jmhwD$=0@-6fOYA zs0<~?IS^b$Ix<Y~H)b-I^%7wi?CtHk;~QSs?Uv(0s$<eJGMt8Jb7a2_KOe6Bvx1Ea zuiVSk|7vgD`%J3KiV`^z?5Xma#ULV_@9sgBsNfz!bR0$iP@=vL*Lt(GTUbE0s=%DK zuD(3+lzrBekR_5>4vLUgpV)oEp7XjF5^5w)Bh2~LVr%a?wI3eefNfs&=4IIY6&2sq zU#nX@>f5iiI6`C$(;1C~zLr%ve#{(Y0vYas=ko99A=EJjBmjQY_bf%KJMxb*7F^e> z+bVDLp*1)qLtdx%3o^ARB&fYF_WKm{elA&(<psZl7>kcyXruIl$(Z0pekbXxgaU!N zOQWRd!M0EzERoOiy_62QIYR~8y&gddQhN`Z5H{bcD+v)fw!W_)aErcY-Q?Px%SMld z!lJ|-O5EE)oL*JosqGDY=Gp2N7q6YCA4$5uX~R8T>!fjw2gMU0+haZNtu$vm6jwP? z2Its$W;eG>1kMfT^tS%k^q!%B#EmDHJ)kaH5*qqtLVQeg+_aT!<VPP6Zc$ATPk%_6 zGCHmNwh9yBAEmx9tM+Lb@A!MNik5?ycEyty3do;V^c0Y~euBE$cdBsxN!1S#Qz}QQ zK(?Jgc57dIh`O0*KoAYrV<ASD65T7JL948^<4R|Zj|$>kO1}Z6IHy^j55cj>KGYfQ zE3cl4R|L0v<K;Scdes8#OOkCFV}c*xTSWM?3j|Ci&R^w+KT}aEqg;&ip7-#&oZOfy zH$gqEaV+m4yHJb`U?kf7QhTS8`@XoqJanzgV~&qGeI{a7hvnIOtcVNcm!~Kjm(d-I ztPvj*p&fuf??HgjyIHl1y$xJR*XlnL-Mzd<rISBk8QcII!3bW==+Td)G#y5?!S=qW zaF}7RdkNeNciznD!}t^tD~6nBXU9%oCV~@x1J5r$!R@o`s_h0IU#D38*1&|}Sa{&_ z6AA(B$jBWTTE1S*NjZ>NO)a=+xMBi4WBBDGSAHFtF77tZ+a@=f2YyXT=Fw!oygxve z^Sx8(-JITam&2hS2ReEum4R2(D{h+2Egrw9%p1vqYA^Fx<K#yYzAD<bsVv(xz50G> zkilIpkDq~m6D$fN3BniFs}3kG%4!-N92z@%uBaDObIEWE)4^qB*@?Sts(it<@T~DS zP{uT(#-@2V@Z(@=?>8WP{mD;jI<I{LsOF5fx$Lv+I)0VKmWKStEhOX}(uVOY`i<+s zv%^N{?m|{qu``7iig%lfdXJsH1=PIo%440)DK#HBCtp?gDvAr)^COu(y#e!a-d-K) zFZ;y*Kvl67LAT&w&f+4?nZ(A{(Q5!e$-Ojw+`oGFe)&nbe#vzCmvu61E2ShiP5~-? zNo&o1kklQ4OILGRpL%;ZUCbR#+30smH5>J)n?$+e&q~Fs+6!RFa$8o9QOXZhKRhq} zPZ2&k9{JI-6fepeiE^t8?IGT~WYp96(=B#{poo^5^XbV^)h>e<!?MD2^aQAC*U5`v z#!5@QuMJj{DqjyBB5K820!^jNO$zO6E?G>M0>35~Yi?LlcsS-7+W%qbBQ8KtR2xNg z1I2U$XwD{t-Y=hHz#Tkr9^9wRbRVJhLh`doWS*}zd-{X7De#=##mocp!bHZAuogmR z3NPE5sPt-q1?+nPY84|e5KcR?dNB+J*91*u6{pPUXSV2|7@unCBUv6A6<QEz*oA;s zTws}&BBy9}x|=ja3s|QBx@N)mF`#bl6eG%bI=h)qQFv@@?JOt&plIZS8w}a4bPkqI zKTmNuy1M7knKGrIGs8c{kx}txU*u7PbgHL`UtcF|kMHesruB>DmznPkd(`GWuj$m4 zm}e^!TC^;31u_a4&HM;_9KKB>N?j*l79^Hu{E6yonfVurf14lh@bLb-`GH;85N7Wq z5^-wq=ir;&!0bQf2k>9!2LfY>CMzK8K~3+ASlUnC%#TE}a93_nMQuOb{Wg33Hy~cR z@pLz^>cICV$q2^VeRA%piwfm=J$HC*+0JA3nxF31?bE{eLPcpwo*gXj7KwG4nNQVj zkvQ4QUt^_zh`)OK_@3c+%djEDPt#NbA-4UGbUxM+l=yF%2Mz9XwHP?Y`UNZ<!ln4d z3e+4OzLQ0czRl*6jvL)+l}jr8Na}Z-ys|!Mb_9()A?F~Hhpjc_L)XU61_W$MbwcRn z9s|dfwCtNrR|^w_{>nslsR7`{%k%kX)I!Eu0;BeCr4o&}^$b*&ku1X<zpnOs2nEBB z8;Rt2gWkSmBjQ-IqMa_@m(hJ~XM!W3_F1h+UktgLGqLT7&kx~8jM33Vk2)Yq@40$U z@fRC1#G;#awKg!8uKo2y(8NDG@5BhFdAYTy&$hHj`hHptpXsG(@_0)j9MH@6&3V5P z9%g5CWLp0ks_eK*{wmfVr^r&_4mBi!)YbJp4%8Gv5qQmesK2ni{iS8~NeO0<(sRl2 z-mhNv4<+b{N)kw1nS8u&SsXNKD4X4r$gt~Ytj@t@;l7TIQg?XC*0mNj@Z@P=4rFw~ zntFwDjb$}}G?{nvXnvsCn+ZROy;ByN=r5kDE!X5`_%=!l+6*R-k+DTuoB+p02{tcx zL!pXS2JWv_xcv$gdgG;7HGJCCHdzMVba_wi7Lf1{tdM3fm3W$sh^I7JlC<Xq42E42 z21E`7-My+meJfQ%*&$DcahAC~N0NX7S}FoFoJCkSH)?D5Q$ZXjKe)}Kw8^zM7Qm9# z5#9ASkP_KCQ?@6LkzYCpY?@O#C)OTw-Z{wIn^X}`gW%6u(=*<4o1D13A&udex>w0O zLqMi#k`{pXdA?sop6jx5hLN45U2$Vb;<8_VGS-M(Tb0r0o`my4bkuo~4|FyY>j)-( zh?EkeNjbQGuo*PR^oW}D8uE+w_tI3=F%SN;^y<nia%rERrRSx62de4W4!gzC?{j~3 z6+NR%uBB9#|9B%y@YB3kQ)r^CeNB~Rkz?;S%JaP{PXqamNC8`o!B4v3)~{?y#>{Ro z8fLqC3b-QKdEcUcYFK`hB6Ja;Ix5wDkJ)@yP$Us5Og;9s@9Sxwztg8u0z%`o;%6L6 zXn_aA;R@h}S6jaUS6P@V_C7qf{rY{1FWPxM<vdAjBqO3%NYPBLOeuQA2El9RlGe{$ zhtJ?6k|wY@K|HPzZ?9B2n!DOCCwg{T$vu&*T%gGQ(R#Jf`m(&G7j|kouqIG$rKfe1 zX_i#BO7^@ka*5l57Y4%m$}1)Zf4?Q$8Z;X<TGazQi27w>pi?S6G!q@adHP{?V(YJx zY0}Dko^(Q~fCMI5;jwL7F-ic)4j^Gos40ttHL?e}WXdNLq5w2I*)Upt%S%-uRG7t5 z-eq7*Nr;$iudEl<>qQ&>qZF+hbs0?bY7%k1&S+PG^lvUIj2SC(_r>6PwT|i_TKjfP z^29ZvUhKnhFjAO+$T^uC5JPsC`#Ql9cMa<dkq%_s#;pn6Z<D$$D(HdeIQ8)qK9JqZ zAO{y=Fs}d?kUFk?&s<^|XX16T$P)1q3H^@rz+`)7yZua|;KdMfza~;EX^gUQr*u)* z$+(6D4Yy&jwC|^BV<Pj0?d%r<K%Sa#WRHVPDOtaH-~?k&g=P`h3E3@Y-s;S}>t#21 zFt@x!BD$#Oe4FEOxPu4{C5Uq3CflvhIO}%(@u1x^J7N?ws2>P+L`XuxrT6T-YR^dj zuIzHcBs6YsUWDSjWRTl+d!M$j&Rjv!hk$ozYvymBgUzKUMDT#PR4qCKCGf(eAPG_x zd^D+*qFdIlxCuf6Wc3hHOHWoKPY#h*h=?(}w5Zbn9x1&MDy=@Zegu;UBVvsUWFXKK z$%H3}@fpJDw>T*|xysB+%Q5DhF?PvrQuDG)H!-G43|90%Gkpi5xX~P0#BDs~&-l57 zEWNnqqxjoJBBGGUa|oT)RjK-$%;@2h`FKWbZ2{yngBiQBX4^2k_B*&T`Ownw{dOq% z+w(V7=q?~c11h5#-O6t$t{b-&-6;trt1}9vRCuvLZ_&U66}%<p9=%z3DmfeNAft23 zh^B|~dFh{aRbM1w_o*iD)6&~Y%OaOOWhoc$?`L8`v!{K=tmWelNDFBmLdV)|Q8=<d zG^1^wR9C5z5Hj7ks+Nz`Ht`X7-fuSoMwtEv5&}VjW#1*gM_eAAk05ThHZ1ry9xTLK zg5UE?QuG%2xj(&R5ML2ro%yGMiUiFo^(?$hKfUfI9U9Ca@ACZ?Z_3`OS?)stM*_($ zx9CW{iYHRSgC)q}fWEMk?V1D9jT=8C<4&$!-=rJXawzd#foPsFsxc@5LS=vr?nWQs zG5S%J7gXm`v(U>avPN{h1y#hEI4+dnLdIw!>ou_LRe$)jhSIg?A8r*W{!8;KHR^9F z1qBVOTkbpAH_$1U@Q_0*ZdqAXzG!_i_$l^&{A0E^I`9ZMm^ROPI2=a!GtUgD>P}kR z=FT9kARDVQMTtcM#7P($cS9oZE$}C8Qk7VEj{dzUcJH|@A8vKu5C37gLh6{&5JPq# zg4B!Xb^qW<DJyp-OZk0aR9*<od7sxbm11IiTZilFpGH~3OunHg5a@5<#0Yoz;Gv3% zZI&YWP^MntL2mkN(gRGF8M_=#O#fDx<dcYB`dTt$XS0w;0*ri|l1kiN?E|HDzpu|> z%GP7iY|1nTXtz_>z5Jv16zAXyGGTzYbVS*38ULd+SM*!0)*%u^o0mjS5dj;RHDv?_ z;ycA(5qO1)bk|OMWMEc?zLy)0FX9pb3KHr?f6j$M-Y~SDkAB6fM7A|&C!?j)4TrI3 z(u`cn(rI)ng<<s8_>b<)bQ3PO*I0udmHNRW?g^Vr+BA(DywH4YtK^Jkd65ydOkx^@ z^^Fe!VK4x|8uRb|4@&{XZ9@U;RuXM?Yc_?u)^?+Xy)2fSYm~1-myeylSL7RV;6V@m zX{!-$SJdaM$DfPj5JX0v3`5c+ld=-?Sgc9CfFW^IvP+y!&=bzU+)7tIuCTZ9yr2Lr z@TGjVX9-;zHK;8MLFuQzQ9Y6yInc_>)JhhemP@JQJRvg$eVD*WzU>&slC5&u%p2^$ zkR?pY5e+<)Q;J#VMPsn#)S6(t3~#b=e!fUfUM$v)p#<v4ZY-7qN7#uZrAfLJ(lZyn zV}CPTiYgCS{zRD;>Kd;Mq|qJ;P)TDG%7m0kv}x&Uxfo0a)X5Zeq62nFgrqAK$B%Tf zN+olbCP|G}>#3c&QbF`+>PQeGyC$~LWB@PC<i1LzK@8Nu@I>WELgtZDqlz%)u#u91 zlJ=t>GakC~he+Z`N;+l%>fCl4^8yZB-01ca>Ugb3?#j6Wh#3Q*gf@GRL#gK*B$0-@ z0!nH#DY(ik*oDL=WEuQsBt;emo*;;nD0%$gp{TThMRqfd$_Bn6KiJe#CE-E_H&}n1 z*ixrp6l^zW{?Larv?xZ*>{D-G8ALwyq^(;2Jvtr`-fp1FH)R#nAc=_@-4$5&aeIUU z?D7WNB83K7c896H^qpxXDq<dy1p?O4%pO-m1}KN#Fu9olQlD5k2RV+-Zk@)Ph9{Dd zzXwy2r~&qbmm0^f*WKA`$NLJ@BG>ZSoBRrK(d@yI7>shiK$&i;?%)J~c>;?AxL%s$ zp-3t%v}u;H);`~SqXz=A@wxEu4@%yQT!Wh$n&U3!>k!P}rnFQ>8>Dq;QL_2POFs(~ z&Ho`pO$uQg9nK|9+^(9y*1_w3lF(L#9(6mAPt?GcX|{D0vd)`Qwru5IDgB@bE7-6G zdjaig(uh1Gs}Lx)6c6<W&~*F&GDiwoD)KTFuGNZUNw1*Y*IR4~iJ;?@rQ(X|cN%`k z9u@kL=y5VVPTQLyjOO4;(k*eJF1vU;A(O^acOiNuyR)``byc#D<_ISf$jfrqGfu88 zbg3!{f#jhji(BVJKi&RO{DC*(Zkrpi-guF7tAanos^ln0sI}kP#Y7JN>`y1>KnOCm z{yb5D8|l3mk3(x!i>++Zld3H5l|VG9NU7x*A`pu_qM1EUS6sJhYu!x81_{>1<idVI zfS!Q~ZF)Y>=b`H#Nh~>{2f!^IDIvVyE^Fq_$|NRGoy}b&^HAYSDA5t(CR1rJ$n6ZM zD+Qbo(y=VkGQ<FbIpFk!+V}J&DW+Blt(()7zG=%fh1MAR)KvJP#F3*(RIKr%IW*I* zREBdhRKxdM?%ocD2#U(Vs%yFOQBz_mnhD##vfm}KyKU>!$(qW*go<h=QlFqD(QVn) zWgUOJ6Ga?Z_c;8<zb7~K)A=b>Kd9YSODm}#iu>eD=i{bPf5xun04uZ2mgL4E@e%bj zsE_^0uPbC#n!lFmx|!9Fx53D<k=dIf_nQ|d?wK20j|-5<(bUw{hOh`2dvn7zv8OJ# zA_bR=wi^v@xsht0)4lTxF&K-@pkY_uPNPn*>0!5+1se_ER{*J;zk#QvG6SjZqS9}r zjc?b^y7@NOM(RM>VWJzl4{4Ik<~1$)$-~-G8$<KdI=FTCM|`1(!uHwW$+27=Z4Q7v zQuXsq{_P%@xTF^R0qW1HYsT{tViBQ{<C>}JXGjA|8ukHT@U~>Ws8}LN-ofMU1X^`g zsB-gQpFRh<$;c*m3pWPm_ZM9SZZ$K4<9rNPXg||vYwz=1dfZ;?l4oY@mN@a>8o!Sh z9}P8lYFR@(t>)2}w0n)P?%zYBY&wcUwGbj3(h%j}Vx%B5h|xrhxIYcpGn0%;w`h)6 z8O$vobIDzIkA3qdSb%t*=0qf>>IS1CU4r9l=GVoL(~*LyR?eVjFjIeGr$#X1buhZ$ zNeAI+oQa><?->WBg+u&CE7K!0NY&bQT<3=gTy;cIPys|kok;T=r6fDlgpz9nr6XU2 zV77`gprkd@GNTeR&WS)+`Up&dBqyD8*}+-4q%(qVU`*J$FsX5DKQEm-CF$#-b?V1d z7=Q%7Pz>R8nU0y@j_K!v2=0>`K0A^=FS!Ykl$oV)NnOKm+}pnOV1XzQ%L7<pZk6FN z2vt2);ogK*)#G+T_#S87F4w9<HWM2O5e*t5O+~~kX5351aXt#UyeAps2ujl5z#8@W z_y!ekIw?3+jWjNYvO9;-8Rwa;7WFB$H$#GsSRjd*ff^sofw(?7;fIM!8g?qD&t29W zzopcbWr_DzqX6`LnDylh-W&J;h7tp##VgAKm<cphh9hlOTxy14(s&rW^N*o{3V#+x zAM*sEYrz|zmVS<cuZeRW?_Zty7(+aI?uB^s<Tn$xL|#W!Ro9F->w0>y<y9UK+>UPX zK;m!D$8)*`b9`uZy?CxLRSQg&Zw6Ie--rcXTEa9bMP4LYF%#nfv^Pou2!<u3xjZoS zy4kOyxAT3`U4(rb*+gJS=`?{v8{K)NNk^*%w3KZ_J*_6kBEE-MEv=8WY3$w-ZoZfV z(n%t@qaYGA8%W{Wc2=4neOZpTUU^z;&ez)D!1_xwtj^Hs#M^dC?QwEEeb-@$!ahZ& za8`c2s8f)xMBu4R7x{s3a2$KpH?||^3(*;Y4}UFg29&jXlTF1xmZTYG0Tg`H;aJ{2 z{t061x`r#;<PS}_=Fa$V6OG+cCyMvx_{r#-vTH`MbTh~7jsU6`%Yk@PuL7bDzvhzH z;RHa7do|Nu4(`b?r_sK$jF(&^xk3#$w=BOpQRYgVYxMuK_B*IC7DhWHhm{yLMQ*sl zunZH$R=Rm&P7@j2f1WTXm3z>PNAC*nfpij9YNl~kNm6m2Disxr8!+Xgai@x~I5bu- zWA+6&su#Bbqe(t;2g72(QTmGKdcB(gxWXm<;kz)G57&Nu@)MPO!Fq-c5R<F93Py&- z0a-SYc|=+qp+h`fypcIHEvApMtrIK|1BQ4Dn`#iBy*>rQ1iUi{6ITr-9s*SaFA{;l zynP7CE|+qfSqXbPPxkC*ua8~r9S>i|h3AvbzLK_OU9os!`;AP4^!vv1g)@0I%x8tH zuHc|?71fDhdAbwjI0jSG&+Q<)E{$k`ues$ppYk4%up0=73}r-Mj>XIz<=lQ2G;CKL z8gJ)qu<?qfJFQ)lY*o(GEF8{n*NEZ)gR$OwEvJg_J|843my2Whw1yuUg<GQQj(dyQ zUjg5h6RCuTEE12(KcBXxGi7Kt4+xPHfSQ#p@amp*P%N6;xzN6u#=}nRPz1+CG_K$D zeZq3@>4wIt)3H{K(FY~Y<hVr(=TUC@oj~Q!mA9Li-a@Xvkss2)KsS54ccf$^S7l1P z8A2;hYzAHzwec)2OgO~KACeQcIXUkI*2p;ZAAY-kv{E2qp*~S#Oh)h(sk@TalH|$Y z4)U-4*SAGK-Y};G>`%{lm~$1nf6Y_uPSw;g3PUC+Z*rhw2tb5!0&?J;S|YM0LdPQe zuO&#Sc7^1i$Xq*n%KPbC-O1WFhO@N$>KgzU?V0$Z)5Yg;>JAsIh*ZDc=(JUYwRs5g zIk~Qh$Y*x>$y?ldLvz`zXu@38;i=|@J^YTqUY*lzJu*giX0m2HyA}()IjKcBZol3p zqKqHP9=7Dmcw6f@HD_6lt@qIf71&3wLO=(NQWzAujP*G;q`|_j-+dw&NVuSe@W(ID zIg?luAQt}M)J(f;H$4!@M<j|;`K-9JP93l0DYHkU?aok(9F8Cv56N+w4G-Z+eNIL0 zj`u`ua`G3+U;;W^O%usg>$H$YK}1bu2V$M1$Q1h6<bH2ttNHVZmL!PkxZ=CgA+OWI z><xYiK*{<CmD0y_li-2k-SEF-b>U8<fN|lv{@S&~MeF(}Ru|4^T0{xwH9dd})s;5; zsb%xuvASTFWF=4wHBjnsG<mbp_ys%gq5(-D&_B!lK@jmxx*Y2hP!`l~TGA<n`9?l} zLo%V0cJZ@xe#P1j_LeH(+o&>rfnt9_)&Pb~<I*t!WzJyrpU)5P&vAI3Xw@xYY#b|0 zfNC^I5$}1CKBa{xydP4+;KlcB(JTN+hK3}tOis!_+fW!F-?U@Z&B?OW;go}zlMM2c z4XzM1tFk!M(WNT~rsw_G*<WP$JaO%Qjb#b1P67)Bs+xbw!Ly)Rx1r#x`Er-TWUFLE zzS8okkpA$<R<zrBf<t1if|N*0TQj@&1OBMj#v}|f@K|nrli?3ZZs+;ViP6T%<*;v= z&Sx^-Ql%^l{ikJwk}WQ<d%g76i_e-(z>S!dCg2V@XnFZG)rW%JzT6HFJ^JitL%{OL z0ncvJA#_?~E#~HVBIQii&N1h`s6qdXzNAkU#QSJ%omR9_@(qt0KQ2mGv;3ZI`7#8) z^&Zo{k*)B^jSA)J9I7YsqYMQhq$40cyq6~LB6wd@raDuU5*=UR7e8LS&x{WP=>43$ zOFr*JO+Gm&@l=}&O^cc2tEZXV0n0=ohK&Ke>H7ir_Q!@SNvVRseT~Q+v)-~5H;7)W zu{U6u7O1<wTi4TP=EFiCbfArugU1s{sFhOj=jp@omxkJQ%?Q%?#*NRs>6!w;H(VS% zlDlr}i^fHKdJj3^(De`@31fA^rb#|8c?pgK1Y~KYmGArh2GqFC*(+WL+ZYD%*Js~? zNJyxp-~uUm3pMM@va7nV2gc(Qrs)BJQ8d*B_cF~scda%r8~b_IuIPYPZsXlG*T>z5 zp_O?(ntY`-wrjTPyid)ofgFA&c$>(O#8{S?r1$Lpf@U~zLV!fb%jTf+n&*g=jQsrV zgXs@Lgj*~4q4s?UskH9QehXL(p(j0^B@Z)%qtirg>h_+FBf9Q;{J;l>j%8lSw@ZF6 z=?|_T2c_q-=rGbg2@pk^Dfb=)Hx0f#1I^7^t+}3C7Lw(>GXbf*BGp3uig4vUcIFV3 zlvWt6&ME$++gJQf!>*;xY#C1E`Bc5Ew1WTDCW@uJUg4(uELz0Sz)lu8yOUYsoxe~_ zV}k%i@Ff&&lcK*K2`97VH-Y=^SQ+>}WBIWc>Mdz%cdl)Cgg*S1xwA|$sHDI}RgmJ& zXxRKwj)<nG3>DyQ>w_>aof=Yu{V;`p1biKrl#l)5einbYKbxK#xg#e9TtlKs6>5aA zJ0R`&P6E()9+%;>f`XlCo0*O^lucOA5&d(&xK>PU$?BrA;9hBK(_qy09S?W!pHyeS zr=z>unQmypyrHe}y72iM$@&I&M#J}te+65x`A%%{__w$y&PssViHmXbk5(V~%3v9m z`!{Cn1L(azw|_f6j&9shIr#blFHKUL^MQ=aP;3Xyd;tfqX@KE`u9`Q?6O3FsF@aC@ z&*e!aY6yf(BW;V1|NOZ!11&ac6z11Qz*$QSk7xBUHGSL>@Obs`ft?V!IcUchCilZL z|JB64lL7H#{vdM(m8VTlisrs+g2&}V&iwEn#&MUvIZ!UHvVDa+p1j8WFqlKO?fwIY zS#ne}lwXu^ot&%wC!^uD5?S3xukd(w{FiD&CO+bMd-tYrDn!O5>cv;+zhK`FN;3DQ zLBg4L)WXj99QkG>qz5uFC@9d|#Q*97Y`}*P;G1HYO#MKa40ZsYGFXE!GWU~m^U)kW zh493&nkC%}-GBnlUGb!ipP8QsqmzNsfE$Ir1T@vO1v9_kI?I*ogb{(eBgz{~`Be4z z({;85pE=y@GOeq0<E$b<eGelkq@q36E&U7|v_a7U`MHR_7nUzP2G0{%4(0so0_k?9 z9TVyfmuWUYBX-;XCIXF$e|N!7U8|JQ?SxIDbif}ia5%kVfPZ(lb0Z}0=|kf;Ex!TU z9^H-XYY*Sy1v&4FC$Yx+sWEzACp=^GR&z=~*<C%vQo_)2hZ<>e%;}+#3hVH`rWf58 zZz{*t!9%4%whzSaYJcP~vACs?a+b<#6!GoT`X)Sk4{vN-_dQgdRdVIE_Jh9<DJhWw zjKDXBMBPW3kuwx2s>pj$b^_A(2&G2H1JnAy{_JVq`1~E@r$C|c+V%U+2JQZxgO3;` zFajD+$%1g|>Y%alK-oN=W@aIRbvW4P4a(b|z1uLV#5-EygR6;EeT)FT#U1y-IloGP zj#M*MCq*y1AuX<WCig9U376IHbj%rip#d)l124xJ*EcQ*G2`A_RZ?3R7iWDTH^a{` zt4vGEMu%ovNdktrWo$|YwE-cK{6Ui)6-Hl)gs^G2N9QVndU5C{NgTGY*r^8V8J zPTgKZ!ke7oHr3{ZFAZBtuwAk$Y~&#<D}sk|?C3K;YI&DNdPr7DN}HfYqZV+kg^L}f zqWvD!uD;5WFKV&T>FS<^r!@*bW%^tf8p%cbQ7zGdGR2^AG<)vBpn>8A;oSx8+biD; z0(P?wmc+mO6s5GK!;9W@BglY1t4b^=n;tX}SCVinejh{`4I+U@FpSYZAQ(`hQ~nk@ z+E@DQOFG*uxQw}Q>-8^7;Zt4><CEq2bd0tavk(eO+QG?jbM`z|HOT$YtH!5(hoUVN z-jTVh#vWIOM9=YPUX|el;E;b>A++^AdNQGdA;6s2`&Vr`B`%g1U4>&;lj<qK<|@Ip zBBi3S(kYUY3P#@14E%4;Ffj1=p>O?Dg!aBraxWDPyfPz|+}NLS<|+**gG#JtHW4*9 zn-B~mPtW)?ZK(RXM+^fsy>ttG}dDAZCcyn+N*L!KJF97jwc<(h$W_;<6_K?9# zuEsfD*`ukVSL~9U%&Ie?yEj_dt&Cw}H$F0cs|DX~NF*pgl^1ZcvTce=>OXcu>e{d* zenwfNcp*<~iv&GNqrh!TMC`AX%v|n$x(fbex~?ktYX7t9_H`Xm14>ra#~2YUM~?I` z*wK++IPMBr0QNp+lY{gIMn=yC=~;Dhh<>RAGQbcwF_iBt`*j|=c9Hu~K^*<ex?EjZ zuj0eFsl$GWVbR_ag|;J#YM;E0c$F^qM%iDasYKY>v^`hRq&Kf>`Le$jGK^{UR^lS- zrJg&aH*%sYEgsEi=jTA(ROA8@n3nX(XO^yASA6O9>Umps&UfmTx`*EVzk$ScnS(C= z+ucyuK+WLZ`vhnW^(QefoC}*VVAbM|!Ys4ZQ7QN%dv<T|%&0_}q=)JaKk(I7M^5Hz znIT7HFrebKVDO?G7h;_7t-N6PecfcD!1aLWO+QW6%)JM}w;Z~#70KixK1HW5nn3jn zEFY2rKI1udRbK1q-;rQOQIg~@E%P>BRtPWOWwl0tX0A%UD8w-g)-O|QJ&M7ZGrG;4 zE$@-m25{kAyGI2i+0Lq_{b@>oX+>(g%)u3Kh^Wy9BFoP+!vv)`lS%Qcc;Wj6F@DDB zZ(#2%?pAHTo1|}IF0;OCoFy3IuRkD3nacgjQ*%v)>wJ_%AmWCzLZCp#1Z+bAr6MdG zz7h7$RRO0<Hp<GLrh+6cO^S^rC0)eXm{h9b3OlUdUEgSGvV-N?h?;)58F+H&kRT_r ziGm*%v22GDTUJvmMQE^W{s3zXkKj{j?QLXZ9>v+&qv*?Dt~UwS$2*hn2XkUUXj|Kh zw3A;SZH3#l3E$oL`166(eC+4Bu(!pF-$Sxle|mik&$^-WllWGV^uF|_^h^9u16(cH z&FPhg>1A!Riu41@+c1=Dtk7EH*jB(1ulw<pvJxL|e6S;Q+s*KNV&|>|t-mCnyl<e* zDDg7>KE=PXA=^eSnt^tTiVs2+rbzVoXg@NizH2MOslwA>-<dmxJ&}6vei1-ynKCVB zXze2Q<1TK0%pusEHo;jhBVtpSka&=`1~S{WB@4E(TbX3UHQ;X1u1r!EET~;ou>^&i z1Df6fBpb~<e{zx!z*@!!{?c3#08q%;&u(^Uyv6;*_8(b%CUhQ7v)6SCnuKj}*!C~O z?p?eiHK0-YXOP22N2U)@=SxK`+}4g}3}KI8n-t+U(m+k=vAcZw<{BQM6AvGzCgiPS zFE5kcbp_v5e9JNAeRkd06aBr057LsCOe+5ym`@As5UyvMgcw2LrZd6>?8;DO^)lI~ z@b|=5RM3!tCBlVmvrucrjs82a_yT()T1*y;U6`@lCvHT&DB~skxjd%JnG^v=m3$F& zADCD{#iVPdN)YAhkWdFI3cBjBSSji6qo-S3h`iU$6!|r&F*FGHK?6Ksu>nlf2X==I zBY5V-LK5{!ZfZ8TL=fC$%(l3I3txlf`Y#)I(t!mRILnffx-amp_>@H?fEu6?A@jxq zKhBBF1SXoBey8CFtRLWMrd8%dch`WV?h>^)1vFg!Rhwwl^HD?su?2yo)oP10>I1XT zn~37{$U{p1(~EHH&HN8|M4xaGOkHl9GX6Y#_o~H#qr%*s<{cXqA1-3WdIep-KF3>f zJblY7Cddx-@DlmFzjFBY_GBQzU9E{4D$5^qvTft|YkbK+s&RMc%o?t|&|?G*zbok` z^|pJ1H<SUu$Oud9#9fX=Tw+j0b`#X!VzPG{DJ<=};!8=;2@M&1oaGIX4T?5!cv10e zI{XJY2#%7Ocd+m-BYJEQPUxhc&oIWT#n&JZgwKW8d5CT=#eN*S+pSCR8-P_bQc`P> zef~(;zrD#qNVU<hlXQpBOo6QEg^%}7tX`m)br?duC7bl@qxMtmw^@Wtb)kLJyX-9p z*!gI)uhjt_XN<>Z1QAn?quWNwYGkB0N5`rMi8POTE$L$4_(?1K6HMuQC_CFs-50-w z20j+s&rQdi=2b1fz{;8f<=Q0_Vj}tY6~^KBc8AP)sAQ00n>2NHb3`*L=oxB4JMqKL zN2+s{I=uny-Mx1dqWKw#`M=fTI^=SlJh>hURPZhAM|1O?VbcQ+qmPtLP{Lf-R|laY zl-2N=_;ry8P2--CpPUO$Yno$~`}p5Q`$~Fqu1CGQe<c+|IISc#Z}&bId~%38mjlkB zJEq6;@tna2O5Fk-C>qhn=7U`q8%!D2g4bx!?wWf97uX@oC=Hd&MZvqX%}Vzgv(7%y z^p`~n^rKdR)G3$7rv0r6GY}Tk8QBWus|PlKYmelJ7xEPY1gO%yVr=SphS04eIVWFL z%T%Puj=D=6OYUe1l)@^c@6uL=Y9hcsMRP{I@7g&$fKoGu>tz7Ik?2R7@=N|Tfq>CO z4SmUYbVnp+hgVRn&Lj`LC*5Zc*NYg8Cdn+zx;{~Lr(c3NtDyV*hCZs+!8Yo!Ojqnk zxB9@y+|pE2-XJw>oYTOy|A&C6Aw55hh`Xr4#8wvZBzw|phFf>3f>haIKvBOLS8*H1 z@SzhS(SgU0Dr!oG)8O+n$^5D~ni_nAskmCL@!e}64gt~8#2;!jreO;xwg>R{Yv6i6 zAzPvAbSAJ`LyoahSR6(l?NZDcQ8vm;WZLd|fa_%)@h8}dcSRXjPg!r2FSeqjClNVB zJYMwiMal*>0d+^O_WMMYn9GAohRiij0Qwv*J)Bpb*S~9p7{8)JR=Gx1$=Mj6=7K$; zrFztdD#AA=`$}gp7(2w@QB4QDI-Pfdym{)lhL>keN(7nWKrXs{v4MVxy0K?k=3&+y za^5*TeQITmH&r@b($5Fvs`Kpyy^N-xc8-!9Ykl!JhT6)87R1T!fUHrc1=#a8h|Y87 zmB7@a&~me~@G?VyKT5w7b4wqOJ>dbP3DzZJ$Mo9&3v~;M^d{1&qKOJ4jS|u$u+025 zG|J<PMhsWFB2a8N!#C>#(I%XGb!Vy7-82ZA<3$21w*j`Isr-$iJzla1+Ej_L9S;!O z`#owMEi}QT{jyL#`ZHj?0?H#Dd^!Ekk5;}K?)*@K@Dg#K*7$lG`V1WxLmdm%YAr%9 ziM=yTSC)TG;pjX4>&x55Ccecl@F{VA;e#fnK_X?D&5Yc`#x_yofC|u2aB30QuK+;M z0qRUNgu74>?P_8GB4P<w;5;h#L{Q0F%G$d^l)<C|o#kD?fxrO35CDWw6wrXJ5|hq@ z%F!llp@k$ERgfk_E;O|<vxL>TXWUB&B(0hPAn9QJmZ2bytDM|0RqJtc%);}&t>3un zF~A_Z1OS;1Im#T2N@}Y5eiG2W<tjoqE+`;lQRzsF(oS^G0o9`q45ZfOj-4j%+Ke72 zti&aa467-Z6}7r+?n!D}7E4;AvcEH?aH9aX^BjRegbW>!JU9#hKr|kQ;To!NskBJM z9kMJW*%(+{XkS&U^8ke32vXC69?5kANcuoP0DZbpKKdK%D!AzEC-lIC=>Z-_dU3Lf ztu>2k{{VngFznL1TTF}t<r)2NhofqXza@XqTE_xz87Ce*w>ZK-LGMp;_;tVsS^YPL z0)e$aJgW=_XBdjs@}O6G`vkwhr9;bmT@El{w4gqBhC~69n_8ea4gMfWT!@H{B@374 zP0_ITeE`5)!-Y&z(<TAINkOH%Y^5v$reA?ejR_SE<1p%Y18zZjk-%Fnc@8^3XBj|6 z?iMTBMygd<Li!vI`%s>>EG__&yt94GQh*nfsB;7)Us9ff>bVv85KaXgC_!vU9b_gY z=@oKfFiqnq!}h0c02jH_32sG{<S;;)B?!B3iZ-SJ40Bdom^d0SC0HGFSrVQ_tO-^D zT^2#7gKyLOkt&g&n!JCx5~&&ZtHsp0Ap;%8P>7B*)`WiVPy-+=0gVb@AQT{}6=$t0 zw!vHsOhIL4oUySJ$S972s#P{&%ds*^qlW6ufIqyF9lt|r3)>3lV!9f2iRbf`ABM`6 z3Ki;<M#N1h@4_NKCNhO3sA8+?6;=0ArAn0}_}%X`1SK7r{Wq8Cr2{!H{#t3BkOY%- zyTSY+1xHAp?u9Tm0#Q-cu#ctn8t1AR&J%c4+^|UGG0Vde%bVaqDU}^2usr}QAy&=H z3E;uwZLko(jsdKO($s5#Th2n2Iad%DE(C~{Cpx*QY5<R6w;n6`FmJ9vcq@X?Ga(N{ zqhb{z>`WjKAb=txu^bc$twsa3EEGe410XaW>QZD(DsB5rSD^b~DQ|B3C<N<449FkU zIq;nZvZmUyeMB-0Vo)_0p%L3yv`M1}99)Iwsv;3WLZLxM-r@T?!Js-n{=UGcB5Gv_ zf{}dT`kOzd^8G}hT(_)Z2~hcne@w0Ph)=zM8?t`(B9Kssh={~TVm{u6I~xEg)EniF zR@n-Eb2`Jv2Z@FN02tGv7{rUG3~@kdIS!m12V;VU8Uh4%eBukTpnJOGPw9-NH5Z+C z3B0F2>IK}?1Lq#HIH|(+xDC{Hv@5j-;~_7>aNt1`4m}-$pg5~OFaQI91Mq0}C8Smn zeg$FMz-`abKh_j~>i+;7j8+jo02SNH?fXzEK~Ofj3@n#J4o{3RF!LP0^V*D0$m3{$ zzc>D6E_$awkE#j*Q~~8!ANL6%(K%qWq_(s=eO;f^d4951Wr1-=om)l!0KnE1FLk_3 zR%8TE*VzFYWGdINPr-=8y~|8pD34m-s}H{2xD=bixn{wlL+9h1Y!Bmck1FqzfTysj zEJx26GqrW$KH7vq<?0OJ;1u!=uzwh5jOkoSN&4_U&EmZ`pv@N&@&-^S2HH143%G}% z`(|lFS$sMkaAI8AXn$+~1PuY#&}foD8#3)Vp9g(oM+YPH&ty;q1b|R%^Nw=-wQIg~ zs(F5*BuJ4WM2Qk4NQqLZI0T9r#HJ`5DL+>G)7*+nLwjzT@@DKfQb&;KTLTQyhk7eR z7&#b~#(Ge2ha>b^bV;R%2NnHUxU=4oNAO?Is5hwEGsq1%ms5-Z;Axd#qJ-o*$eDat z7(kgyn!V+gg6v88<i97d;T>q*Ao$IDjb-eOhRLcQMJtT-pydt&3^PU?=&dDa;AZSN zQb&;OO9b~Kwou;NhCIH8(wu3Zt1@+<*fu<K#wrM&6_;a^LZ9|epnHsi3|kG2{;oBj z7C=v@&O@We@8bTsXf{sY)c*hh!NOpgsZuSfj!e~Y9fBW~O^v{t!Qw~8=9%ZJySR;; zpij2_!3d8(K_g(rh_w`hLCr&^;=*rp*<_q<D<%-(8e{Npg;GdLy}mG`m!+gCQX658 zf<pnNMvzzN17_?K?XNJv3(hYRKD9K@9o5~<R}tAM`BX^MS-j-oFipUzR)hxzD-iG% z{jpHgJ~rPJbYUH5aQ)u1h$RGm9&-9XP~YI*Fi0t)aryTV_$2-Qzh`^@00kGJ;9${c zNQuD31VY7+Yfop&36Mdux5KMc1m&0%x0BIFnj`Z;?&Q6FMo@7`mq_B7A(IN6m<jU? zc7I_qHM;q!J&2_Ej*Wls?)4c2e9nfi&x0lQ#jVIRzb*1xFsMH)5P+viFw`ln;ek8> z?h0GM=&Q{T_@T%k+Z*H6s#0=X5JYT9^tJSSBAHjA<X}-DN~y~AE8~g@0@L!a13(9Z zUgPF50BrN_;QSxY*Zs%w{u6|JEd~>ehJ(nrCSDE~7M}Ujrx^$+fvI*%NMyoOG-YeD zzcg2$cQb7<Ux;py9&=1x!Vk!w1b$v3%HR(|BJ?Qq`ZB1%Lk*b&t$6fMB}%urNAduC zwT25NKsM_)^^c3cdQ&iEZ?>N_2b_cy0K}RkBy!?K(D=5{YUigHjO8BzNu=i^vGN7` zCY0d(AJ5nQ$MOCY=!tU5fI;CgDM~Oyq>=o;v%yfX13?H#<RRl1RFpfmq>58U%t!Ea z@*oI3KIJ<TREaD6f-iYg!mZ9TOgulV%T>q-WFy>AdCT<MXCMFyJP(tc1ONa45CQ-O zk8nSOr;q_r=*86~4(Y2Xu+yUjM!*aNB`biBj7n0B66^$jFYNK?iE_$;QRRNkr8qwa z^Y#A#ar}RWb9J(ntT=6gyD-R-{-k_@+l}I`7nN+367!UvQ=4isSi~zkzCW$Dqa}q> zlbYwo@~skrUNRHFambSXrG0`)gyN1ly4gz7oVI`+A8OK^AA|Y&{{XoDKg7?jQk%#W zYeH}`n2-}@it!T!V3`r*piYU8Lc3Va#~JyGkdaa052d){A23o7sJtVQJ_-QrnDAqz ztcD~6Te7^s&<Lhqu2Q?mL~CMlzLB6DC<+^2lLSg4c^Z3oz)Y|Z?ep&7{2$NP{m1eC zFT#Mm#Yn?7*m<`YaQOp3=gVM7Ou&$VVU<<w5TtjGD4f`hBp)R`#weUvjRYSoFd!fZ zWe|EqD;?nuA0S{1`D_aanc+YV=7edx=p4)S5U9MQe{7|pE6@Gy!loCvKjYR#h0$N9 zIPS<OHvF0~QdM1#J}(~b55fHXf82i`;}3|PJg5W>Cd^UE1_B~(vhPi=j2h67KoUTz zhk)K}`GE%j7nn>dG3vUQ1}FqX^m?u)L4XBSb4SbwIY7KpV@rX~mU?9xM?^e^@kb;W z3ZT2oy*3^(_=(fXfkE2s`s+<87GIL%=qP~DakrjvfLPZs#R^ZBf6MeZKL_*m{{V6P ze~#xH&e|7xX2D&VYhal;*Q#?xcY(eUZQL*)YP(Ku{6h2Ie>;8kd&c@jvv9?I3g|g& zVYxWhsBr}~gPd<WDNSpd0CauDE^OCWB6UD-OT0u;A~7D5(T4<ryd3`k?tH=zs^bvB JyBnW*|Jf_lag+c6 literal 0 HcmV?d00001 From 1d56dd89c56f28cd9b47559d712ac1509ba85737 Mon Sep 17 00:00:00 2001 From: bshevchenko <bohdan.shevchenko@transoftgroup.com> Date: Mon, 4 Feb 2019 13:49:01 +0200 Subject: [PATCH 0492/1295] MAGETWO-96705: Cart Price Rule with related Banner for specific Customer Segment is persisted under long-term cookie --- .../Test/Mftf/Section/AdminCartPriceRulesFormSection.xml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml index 9e83d3045b942..9ce53163054b5 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml @@ -26,6 +26,7 @@ <!-- Conditions sub-form --> <element name="conditionsHeader" type="button" selector="div[data-index='conditions']" timeout="30"/> + <element name="conditionsHeaderOpen" type="button" selector="div[data-index='conditions'] div[data-state-collapsible='open']" timeout="30"/> <element name="addCondition" type="button" selector="//*[@id='conditions__{{arg}}__children']//span" parameterized="true"/> <element name="ruleCondition" type="select" selector="rule[conditions][{{arg}}][new_child]" parameterized="true"/> <element name="ruleParameter" type="text" selector="//span[@class='rule-param']/a[contains(text(), '{{arg}}')]" parameterized="true"/> @@ -33,10 +34,6 @@ <element name="ruleValueInput" type="input" selector="rule[conditions][{{arg}}][value]" parameterized="true"/> <element name="chooseValue" type="button" selector="label[for='conditions__{{arg}}__value']" parameterized="true"/> <element name="categoryCheckbox" type="checkbox" selector="//span[contains(text(), '{{arg}}')]/parent::a/preceding-sibling::input[@type='checkbox']" parameterized="true"/> - - <!-- Conditions sub-form --> - <element name="conditionsHeader" type="button" selector="div[data-index='conditions']" timeout="30"/> - <element name="conditionsHeaderOpen" type="button" selector="div[data-index='conditions'] div[data-state-collapsible='open']" timeout="30"/> <element name="conditionsValue" type="input" selector=".rule-param-edit input"/> <element name="conditionsOperator" type="select" selector=".rule-param-edit select"/> From 1df83b16c701f3812a4aeaae73a56bfc4816f096 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Mon, 4 Feb 2019 13:54:59 +0200 Subject: [PATCH 0493/1295] MAGETWO-96457: [Magento Cloud] [QUANS] Tiered block displayed on configurable products without any tiered pricing --- ...PriceNotAvailableForProductOptionsWithoutTierPriceTest.xml | 4 ++-- .../ConfigurableProduct/Pricing/Render/TierPriceBox.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml index cf4117162def8..35b44660c45c0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml @@ -11,7 +11,7 @@ <test name="AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest"> <annotations> <features value="ConfigurableProduct"/> - <stories value="View configurable product details in storefront"/> + <stories value="View configurable product details on storefront"/> <title value="Check that 'trie price' block not available for simple product from options without 'trie price'"/> <description value="Check that 'trie price' block not available for simple product from options without 'trie price'"/> <severity value="MAJOR"/> @@ -89,6 +89,6 @@ <amOnPage url="{{StorefrontProductPage.url($$createConfigProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToSimpleProductPage"/> <selectOption selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" userInput="$$getConfigAttributeOption1.value$$" stepKey="selectOption"/> <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="class" stepKey="grabGrabPriceClass"/> - <assertNotContains actual="$grabGrabPriceClass" expected=".price-box .price-tier_price" expectedType="string" stepKey="assertNotEquals"/> + <assertNotContains actual="$grabGrabPriceClass" expected=".price-box .price-tier_price" expectedType="string" stepKey="assertNotEquals"/> </test> </tests> diff --git a/app/code/Magento/ConfigurableProduct/Pricing/Render/TierPriceBox.php b/app/code/Magento/ConfigurableProduct/Pricing/Render/TierPriceBox.php index 4106c2553cfba..816de36b16f96 100644 --- a/app/code/Magento/ConfigurableProduct/Pricing/Render/TierPriceBox.php +++ b/app/code/Magento/ConfigurableProduct/Pricing/Render/TierPriceBox.php @@ -34,8 +34,8 @@ private function isTierPriceApplicable(): bool { $product = $this->getSaleableItem(); foreach ($product->getTypeInstance()->getUsedProducts($product) as $simpleProduct) { - if ($simpleProduct->isSalable() && - !empty($simpleProduct->getPriceInfo()->getPrice(TierPrice::PRICE_CODE)->getTierPriceList()) + if ($simpleProduct->isSalable() + && !empty($simpleProduct->getPriceInfo()->getPrice(TierPrice::PRICE_CODE)->getTierPriceList()) ) { return true; } From 1db76cd270d159a80af101329a27108b15648565 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Mon, 4 Feb 2019 15:30:42 +0200 Subject: [PATCH 0494/1295] MAGETWO-96457: [Magento Cloud] [QUANS] Tiered block displayed on configurable products without any tiered pricing --- ...eForProductOptionsWithoutTierPriceTest.xml | 94 ------------------- ...eForProductOptionsWithoutTierPriceTest.xml | 43 +++++++++ 2 files changed, 43 insertions(+), 94 deletions(-) delete mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml deleted file mode 100644 index 35b44660c45c0..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml +++ /dev/null @@ -1,94 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest"> - <annotations> - <features value="ConfigurableProduct"/> - <stories value="View configurable product details on storefront"/> - <title value="Check that 'trie price' block not available for simple product from options without 'trie price'"/> - <description value="Check that 'trie price' block not available for simple product from options without 'trie price'"/> - <severity value="MAJOR"/> - <testCaseId value="MC-13789"/> - <useCaseId value="MAGETWO-96457"/> - <group value="catalog"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - - <!-- Create the category --> - <createData entity="ApiCategory" stepKey="createCategory"/> - <!-- Create the configurable product and add it to the category --> - <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <!-- Create an attribute with two options to be used in the first child product --> - <createData entity="productAttributeWithDropdownTwoOptions" stepKey="createConfigProductAttribute"/> - <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <!-- Add the attribute we just created to default attribute set --> - <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </createData> - <!-- Get the option of the attribute we created --> - <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - </getData> - <!-- Create a simple product and give it the attribute with option --> - <createData entity="SimpleOption" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="SimpleOption" stepKey="createConfigChildProduct2"> - <field key="sku">SimpleTwoOption</field> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <!-- Create the configurable product --> - <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - <!-- Add simple product to the configurable product --> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct1"/> - </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct2"/> - </createData> - </before> - <after> - <!--Delete created data--> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - - <actionGroup ref="logout" stepKey="logoutOfAdmin"/> - </after> - - <!--Go to storefront product page an check price box css--> - <amOnPage url="{{StorefrontProductPage.url($$createConfigProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToSimpleProductPage"/> - <selectOption selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" userInput="$$getConfigAttributeOption1.value$$" stepKey="selectOption"/> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="class" stepKey="grabGrabPriceClass"/> - <assertNotContains actual="$grabGrabPriceClass" expected=".price-box .price-tier_price" expectedType="string" stepKey="assertNotEquals"/> - </test> -</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml new file mode 100644 index 0000000000000..ac4ea194ce6e8 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="View configurable product details on storefront"/> + <title value="Check that 'trie price' block not available for simple product from options without 'trie price'"/> + <description value="Check that 'trie price' block not available for simple product from options without 'trie price'"/> + <severity value="MAJOR"/> + <testCaseId value="MC-13789"/> + <useCaseId value="MAGETWO-96457"/> + <group value="catalog"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--Create Configurable product--> + <actionGroup ref="AdminCreateApiConfigurableProductActionGroup" stepKey="createConfigurableProduct"/> + </before> + <after> + <!--Delete created data--> + <deleteData createDataKey="createConfigProductCreateConfigurableProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigChildProduct1CreateConfigurableProduct" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2CreateConfigurableProduct" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProductAttributeCreateConfigurableProduct" stepKey="deleteConfigProductAttribute"/> + + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + </after> + + <!--Go to storefront product page an check price box css--> + <amOnPage url="{{StorefrontProductPage.url($$createConfigProductCreateConfigurableProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToSimpleProductPage"/> + <selectOption selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" userInput="$$getConfigAttributeOption1CreateConfigurableProduct.value$$" stepKey="selectOption"/> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="class" stepKey="grabGrabPriceClass"/> + <assertNotContains actual="$grabGrabPriceClass" expected=".price-box .price-tier_price" expectedType="string" stepKey="assertNotEquals"/> + </test> +</tests> From 1181d0fba1d2061f8136bd777041559792867c3d Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Mon, 4 Feb 2019 16:29:20 +0200 Subject: [PATCH 0495/1295] MAGETWO-93221: Payment method title does not update in the admin sales order grid --- app/code/Magento/Payment/Helper/Data.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Payment/Helper/Data.php b/app/code/Magento/Payment/Helper/Data.php index 5fd23c195f0c4..0afdb3b82bd41 100644 --- a/app/code/Magento/Payment/Helper/Data.php +++ b/app/code/Magento/Payment/Helper/Data.php @@ -267,10 +267,13 @@ public function getPaymentMethodList($sorted = true, $asLabelValue = false, $wit $groupRelations = []; foreach ($this->getPaymentMethods() as $code => $data) { - if (isset($data['title'])) { - $methods[$code] = $data['title']; - } else { - $methods[$code] = $this->getMethodInstance($code)->getConfigData('title', $store); + if (!empty($data['active'])) { + $storedTitle = $this->getMethodInstance($code)->getConfigData('title', $store); + if (isset($storedTitle)) { + $methods[$code] = $storedTitle; + } elseif (isset($data['title'])) { + $methods[$code] = $data['title']; + } } if ($asLabelValue && $withGroups && isset($data['group'])) { $groupRelations[$code] = $data['group']; From f4bec9aec5a22af59021b937fc09555823d3598e Mon Sep 17 00:00:00 2001 From: amol 2jcommerce <amol@2jcommerce.in> Date: Mon, 4 Feb 2019 20:20:11 +0530 Subject: [PATCH 0496/1295] admin-store-view-label-not-alignment-2 --- .../web/css/source/module/main/actions-bar/_store-switcher.less | 1 + 1 file changed, 1 insertion(+) diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/actions-bar/_store-switcher.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/actions-bar/_store-switcher.less index 80bebb22a9043..8daa7d5808d06 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/actions-bar/_store-switcher.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/actions-bar/_store-switcher.less @@ -235,6 +235,7 @@ .store-view { &:not(.store-switcher) { float: left; + margin-top: 13px; } .store-switcher-label { From 0be60e95cdc9c33a6fb2c22c2e5b7696d1e25b06 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Mon, 4 Feb 2019 18:17:23 +0200 Subject: [PATCH 0497/1295] MAGETWO-82385: [CE 2.1.0 rc3] - Cancel an order [configurable product] #5313 --- ...CheckingProductQtyAfterOrderCancelTest.xml | 19 +++++++++---------- .../ActionGroup/AdminInvoiceActionGroup.xml | 12 ++++++++++++ .../ActionGroup/AdminOrderActionGroup.xml | 6 +++--- .../ActionGroup/AdminOrderGridActionGroup.xml | 4 ++++ 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckingProductQtyAfterOrderCancelTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckingProductQtyAfterOrderCancelTest.xml index 8d76e842070a8..c0d861d4190b8 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckingProductQtyAfterOrderCancelTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckingProductQtyAfterOrderCancelTest.xml @@ -98,19 +98,18 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> <!--Open order--> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> + <actionGroup ref="openOrderById" stepKey="openOrderById"> <argument name="orderId" value="{$grabOrderNumber}"/> </actionGroup> - <click selector="{{AdminDataGridTableSection.firstRow}}" stepKey="openOrderViewPage"/> - <waitForPageLoad stepKey="waitForOrderViewPageOpen"/> - <!--Create Invoice--> + <!--Start create invoice--> <actionGroup ref="StartCreateInvoiceFromOrderPage" stepKey="startCreateInvoice"/> - <fillField selector="{{AdminInvoiceItemsSection.itemQtyToInvoice('1')}}" userInput="1" stepKey="changeQtyToInvoice"/> - <waitForElementVisible selector="{{AdminInvoiceItemsSection.updateQtyEnabled}}" stepKey="waitForUpdateQtyEnabled"/> - <click selector="{{AdminInvoiceItemsSection.updateQty}}" stepKey="updateQty"/> - <waitForLoadingMaskToDisappear stepKey="waitForQtyToUpdate"/> - <waitForElementVisible selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="waitForSubmitInvoiceButton"/> + <!--Create partial invoice--> + <actionGroup ref="CreatePartialInvoice" stepKey="createPartialInvoice"> + <argument name="rowNumber" value="1"/> + <argument name="qtyToInvoice" value="1"/> + </actionGroup> + <!--Submit Invoice--> <actionGroup ref="SubmitInvoice" stepKey="submitInvoice"/> <!--Create Shipment--> <actionGroup ref="StartCreateShipmentFromOrderPage" stepKey="startCreateShipment"/> @@ -118,7 +117,7 @@ <actionGroup ref="SubmitShipment" stepKey="submitShipment"/> <!--Cancel order--> - <actionGroup ref="cancelCompleteOrder" stepKey="cancelOrder"/> + <actionGroup ref="cancelProcessingOrder" stepKey="cancelOrder"/> <!--Check quantities in "Items Ordered" table--> <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Invoiced 1" stepKey="seeInvoicedQuantity"/> <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Shipped 1" stepKey="seeShippedQuantity"/> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml index 1ce5c38256295..e63e279612fb1 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml @@ -45,6 +45,18 @@ <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Invoice" stepKey="seeNewInvoicePageTitle"/> </actionGroup> + <actionGroup name="CreatePartialInvoice"> + <arguments> + <argument name="rowNumber" type="string" defaultValue="1"/> + <argument name="qtyToInvoice" type="string" defaultValue="1"/> + </arguments> + <fillField selector="{{AdminInvoiceItemsSection.itemQtyToInvoice(rowNumber)}}" userInput="{{qtyToInvoice}}" stepKey="changeQtyToInvoice"/> + <waitForElementVisible selector="{{AdminInvoiceItemsSection.updateQtyEnabled}}" stepKey="waitForUpdateQtyEnabled"/> + <click selector="{{AdminInvoiceItemsSection.updateQty}}" stepKey="updateQty"/> + <waitForLoadingMaskToDisappear stepKey="waitForQtyToUpdate"/> + <waitForElementVisible selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="waitForSubmitInvoiceButton"/> + </actionGroup> + <actionGroup name="SubmitInvoice"> <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> <waitForElementVisible selector="{{AdminMessagesSection.successMessage}}" stepKey="waitForMessageAppears"/> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml index 6e0d7ee542f58..6b409b9f6a3da 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml @@ -18,10 +18,10 @@ <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Canceled" stepKey="seeOrderStatusCanceled"/> </actionGroup> - <!--Cancel order that is in complete status--> - <actionGroup name="cancelCompleteOrder" extends="cancelPendingOrder"> + <!--Cancel order that is in processing status--> + <actionGroup name="cancelProcessingOrder" extends="cancelPendingOrder"> <remove keyForRemoval="seeOrderStatusCanceled"/> - <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" after="seeCancelSuccessMessage" userInput="Complete" stepKey="seeOrderStatusComplete"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" after="seeCancelSuccessMessage" userInput="{{CONST.orderStatusComplete}}" stepKey="seeOrderStatusComplete"/> </actionGroup> <!--Navigate to create order page (New Order -> Create New Customer)--> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml index c5307fa7ba73a..d99551de24db0 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml @@ -23,4 +23,8 @@ <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToGridOrdersPage"/> <conditionalClick selector="{{AdminOrdersGridSection.clearFilters}}" dependentSelector="{{AdminOrdersGridSection.enabledFilters}}" visible="true" stepKey="clickOnButtonToRemoveFiltersIfPresent"/> </actionGroup> + <actionGroup name="openOrderById" extends="filterOrderGridById"> + <click selector="{{AdminDataGridTableSection.firstRow}}" after="clickOrderApplyFilters" stepKey="openOrderViewPage"/> + <waitForPageLoad after="openOrderViewPage" stepKey="waitForOrderViewPageOpened"/> + </actionGroup> </actionGroups> From 32065b1bac8496c7898ff5073e0b902b1b31514f Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Tue, 5 Feb 2019 09:46:35 +0200 Subject: [PATCH 0498/1295] MAGETWO-95452: Special price does not work when "default config" scope timezone does not match "website" scope timezone --- .../AdminConfigurationGeneralSectionPage.xml | 1 + .../Page/AdminConfigurationStoresPage.xml | 2 +- .../Mftf/Section/LocaleOptionsSection.xml | 15 ++++ .../Unit/Pricing/Price/SpecialPriceTest.php | 9 +-- .../Catalog/Model/Product/Type/Price.php | 9 ++- .../Catalog/Pricing/Price/SpecialPrice.php | 9 ++- .../StorefrontProductInfoMainSection.xml | 1 + ...ceForDifferentTimezonesForWebsitesTest.xml | 76 +++++++++++++++++++ .../Model/Product/Type/Grouped/PriceTest.php | 8 +- .../Store/Api/Data/WebsiteInterface.php | 5 ++ .../AdminSwitchStoreViewActionGroup.xml | 15 +++- .../Mftf/Section/AdminMainActionsSection.xml | 3 +- 12 files changed, 132 insertions(+), 21 deletions(-) create mode 100644 app/code/Magento/Backend/Test/Mftf/Section/LocaleOptionsSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml diff --git a/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationGeneralSectionPage.xml b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationGeneralSectionPage.xml index c0c4f4bd9d3a5..c92c025b6272b 100644 --- a/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationGeneralSectionPage.xml +++ b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationGeneralSectionPage.xml @@ -7,5 +7,6 @@ <pages xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <page name="AdminConfigurationGeneralSectionPage" url="admin/system_config/edit/section/general/{{group_anchor}}" parameterized="true" area="admin" module="Magento_Config"> <section name="AdminConfigurationGeneralSectionCountryOptionsGroupSection"/> + <section name="LocaleOptionsSection"/> </page> </pages> diff --git a/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage.xml b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage.xml index 05073acff3ca9..d1bf3c2cb2ed6 100644 --- a/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage.xml +++ b/app/code/Magento/Backend/Test/Mftf/Page/AdminConfigurationStoresPage.xml @@ -7,7 +7,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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="ConfigurationStoresPage" url="admin/system_config/edit/section/cms/" area="admin" module="Catalog"> <section name="WYSIWYGOptionsSection"/> </page> diff --git a/app/code/Magento/Backend/Test/Mftf/Section/LocaleOptionsSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/LocaleOptionsSection.xml new file mode 100644 index 0000000000000..f9cfe7105d9a1 --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Section/LocaleOptionsSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="LocaleOptionsSection"> + <element name="timezone" type="select" selector="#general_locale_timezone"/> + <element name="useDefault" type="checkbox" selector="#general_locale_timezone_inherit"/> + </section> +</sections> diff --git a/app/code/Magento/Bundle/Test/Unit/Pricing/Price/SpecialPriceTest.php b/app/code/Magento/Bundle/Test/Unit/Pricing/Price/SpecialPriceTest.php index f38dfc5538cf3..3e60e057fe62b 100644 --- a/app/code/Magento/Bundle/Test/Unit/Pricing/Price/SpecialPriceTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Pricing/Price/SpecialPriceTest.php @@ -6,6 +6,7 @@ namespace Magento\Bundle\Test\Unit\Pricing\Price; use \Magento\Bundle\Pricing\Price\SpecialPrice; +use Magento\Store\Api\Data\WebsiteInterface; class SpecialPriceTest extends \PHPUnit\Framework\TestCase { @@ -77,12 +78,6 @@ public function testGetValue($regularPrice, $specialPrice, $isScopeDateInInterva ->method('getSpecialPrice') ->will($this->returnValue($specialPrice)); - $store = $this->getMockBuilder(\Magento\Store\Model\Store::class) - ->disableOriginalConstructor() - ->getMock(); - $this->saleable->expects($this->once()) - ->method('getStore') - ->will($this->returnValue($store)); $this->saleable->expects($this->once()) ->method('getSpecialFromDate') ->will($this->returnValue($specialFromDate)); @@ -92,7 +87,7 @@ public function testGetValue($regularPrice, $specialPrice, $isScopeDateInInterva $this->localeDate->expects($this->once()) ->method('isScopeDateInInterval') - ->with($store, $specialFromDate, $specialToDate) + ->with(WebsiteInterface::ADMIN_CODE, $specialFromDate, $specialToDate) ->will($this->returnValue($isScopeDateInInterval)); $this->priceCurrencyMock->expects($this->never()) diff --git a/app/code/Magento/Catalog/Model/Product/Type/Price.php b/app/code/Magento/Catalog/Model/Product/Type/Price.php index f6caa299d66d7..a4ee944a9bff2 100644 --- a/app/code/Magento/Catalog/Model/Product/Type/Price.php +++ b/app/code/Magento/Catalog/Model/Product/Type/Price.php @@ -11,6 +11,7 @@ use Magento\Store\Model\Store; use Magento\Catalog\Api\Data\ProductTierPriceExtensionFactory; use Magento\Framework\App\ObjectManager; +use Magento\Store\Api\Data\WebsiteInterface; /** * Product type price model @@ -184,6 +185,8 @@ public function getFinalPrice($qty, $product) } /** + * Retrieve final price for child product. + * * @param Product $product * @param float $productQty * @param Product $childProduct @@ -428,6 +431,8 @@ public function setTierPrices($product, array $tierPrices = null) } /** + * Retrieve customer group id from product. + * * @param Product $product * @return int */ @@ -453,7 +458,7 @@ protected function _applySpecialPrice($product, $finalPrice) $product->getSpecialPrice(), $product->getSpecialFromDate(), $product->getSpecialToDate(), - $product->getStore() + WebsiteInterface::ADMIN_CODE ); } @@ -601,7 +606,7 @@ public function calculatePrice( $specialPrice, $specialPriceFrom, $specialPriceTo, - $sId + WebsiteInterface::ADMIN_CODE ); if ($rulePrice === false) { diff --git a/app/code/Magento/Catalog/Pricing/Price/SpecialPrice.php b/app/code/Magento/Catalog/Pricing/Price/SpecialPrice.php index b1bfc6ff4ad6f..77c48fdb1667e 100644 --- a/app/code/Magento/Catalog/Pricing/Price/SpecialPrice.php +++ b/app/code/Magento/Catalog/Pricing/Price/SpecialPrice.php @@ -11,6 +11,7 @@ use Magento\Framework\Pricing\Price\AbstractPrice; use Magento\Framework\Pricing\Price\BasePriceProviderInterface; use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Store\Api\Data\WebsiteInterface; /** * Special price model @@ -46,6 +47,8 @@ public function __construct( } /** + * Retrieve special price. + * * @return bool|float */ public function getValue() @@ -96,19 +99,19 @@ public function getSpecialToDate() } /** - * @return bool + * @inheritdoc */ public function isScopeDateInInterval() { return $this->localeDate->isScopeDateInInterval( - $this->product->getStore(), + WebsiteInterface::ADMIN_CODE, $this->getSpecialFromDate(), $this->getSpecialToDate() ); } /** - * @return bool + * @inheritdoc */ public function isPercentageDiscount() { diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml index 4eb11727849e9..ae0cb3f970108 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -14,6 +14,7 @@ <element name="productSku" type="text" selector=".product.attribute.sku>.value"/> <element name="productPrice" type="text" selector=".price"/> <element name="specialPrice" type="text" selector=".special-price"/> + <element name="specialPriceValue" type="text" selector=".special-price .price"/> <element name="qty" type="input" selector="#qty"/> <element name="productStockStatus" type="text" selector=".stock[title=Availability]>span"/> <element name="productDescription" type="text" selector="#description .value"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml new file mode 100644 index 0000000000000..c3dc8a0676278 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest"> + <annotations> + <features value="Catalog"/> + <title value="Check that special price displayed when 'default config' scope timezone does not match 'website' scope timezone"/> + <description value="Check that special price displayed when 'default config' scope timezone does not match 'website' scope timezone"/> + <stories value="Verify product special price"/> + <severity value="MAJOR"/> + <testCaseId value="MC-13788"/> + <useCaseId value="MAGETWO-95452"/> + <group value="Catalog"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <createData entity="SimpleProduct3" stepKey="createProduct"/> + + <!--Create customer--> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + + <!--Set timezone for default config--> + <amOnPage url="{{AdminConfigurationGeneralSectionPage.url('#general_locale-link')}}" stepKey="openLocaleSection"/> + <selectOption selector="{{LocaleOptionsSection.timezone}}" userInput="Central European Standard Time (Europe/Paris)" stepKey="setTimezone"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="saveConfig"/> + <!--Set timezone for Main Website--> + <actionGroup ref="AdminSwitchWebsiteActionGroup" stepKey="adminSwitchStoreViewActionGroup"> + <argument name="storeView" value="_defaultWebsite.name"/> + </actionGroup> + <uncheckOption selector="{{LocaleOptionsSection.useDefault}}" stepKey="uncheckUseDefault"/> + <selectOption selector="{{LocaleOptionsSection.timezone}}" userInput="Greenwich Mean Time (Africa/Abidjan)" stepKey="setTimezone1"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="saveConfig1"/> + </before> + <after> + <!--Delete create data--> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + + <!--Reset timezone--> + <amOnPage url="{{AdminConfigurationGeneralSectionPage.url('#general_locale-link')}}" stepKey="openLocaleSectionReset"/> + <selectOption selector="{{LocaleOptionsSection.timezone}}" userInput="{{_ENV.DEFAULT_TIMEZONE}}" stepKey="resetTimezone"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="saveConfigReset"/> + + <actionGroup ref="AdminSwitchWebsiteActionGroup" stepKey="AdminSwitchStoreViewActionGroup"> + <argument name="storeView" value="_defaultWebsite.name"/> + </actionGroup> + <checkOption selector="{{LocaleOptionsSection.useDefault}}" stepKey="checkUseDefault"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="saveConfigReset1"/> + + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Set special price to created product--> + <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="openAdminEditPage"/> + <actionGroup ref="AddSpecialPriceToProductActionGroup" stepKey="setSpecialPriceToCreatedProduct"> + <argument name="price" value="15"/> + </actionGroup> + <actionGroup ref="saveProductForm" stepKey="saveProductForm"/> + + <!--Login to storefront from customer and check price--> + <actionGroup ref="CustomerLoginOnStorefront" stepKey="logInFromCustomer"> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + + <!--Go to the product page and check special price--> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="amOnSimpleProductPage"/> + <see selector="{{StorefrontProductInfoMainSection.specialPriceValue}}" userInput='$15.00' stepKey="assertSpecialPrice"/> + </test> +</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Type/Grouped/PriceTest.php b/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Type/Grouped/PriceTest.php index f02849c244cb3..1023dcf552d2f 100644 --- a/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Type/Grouped/PriceTest.php +++ b/app/code/Magento/GroupedProduct/Test/Unit/Model/Product/Type/Grouped/PriceTest.php @@ -64,7 +64,7 @@ public function testGetFinalPrice( $expectedFinalPrice ) { $rawFinalPrice = 10; - $rawPriceCheckStep = 6; + $rawPriceCheckStep = 5; $this->productMock->expects( $this->any() @@ -155,7 +155,7 @@ public function getFinalPriceDataProvider() 'custom_option_null' => [ 'associatedProducts' => [], 'options' => [[], []], - 'expectedPriceCall' => 6, /* product call number to check final price formed correctly */ + 'expectedPriceCall' => 5, /* product call number to check final price formed correctly */ 'expectedFinalPrice' => 10, /* 10(product price) + 2(options count) * 5(qty) * 5(option price) */ ], 'custom_option_exist' => [ @@ -165,9 +165,9 @@ public function getFinalPriceDataProvider() ['associated_product_2', $optionMock], ['associated_product_3', $optionMock], ], - 'expectedPriceCall' => 16, /* product call number to check final price formed correctly */ + 'expectedPriceCall' => 15, /* product call number to check final price formed correctly */ 'expectedFinalPrice' => 35, /* 10(product price) + 2(options count) * 5(qty) * 5(option price) */ - ] + ], ]; } diff --git a/app/code/Magento/Store/Api/Data/WebsiteInterface.php b/app/code/Magento/Store/Api/Data/WebsiteInterface.php index 176e82f5905b5..fae9fa368d3d1 100644 --- a/app/code/Magento/Store/Api/Data/WebsiteInterface.php +++ b/app/code/Magento/Store/Api/Data/WebsiteInterface.php @@ -13,6 +13,11 @@ */ interface WebsiteInterface extends \Magento\Framework\Api\ExtensibleDataInterface { + /** + * Contains code of admin website + */ + const ADMIN_CODE = 'admin'; + /** * @return int */ diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml index fd86c7a0525c7..b96a42a66d706 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml @@ -7,16 +7,25 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminSwitchStoreViewActionGroup"> + <actionGroup name="AdminSwitchBaseActionGroup"> <arguments> <argument name="storeView" defaultValue="customStore.name"/> </arguments> <click selector="{{AdminMainActionsSection.storeViewDropdown}}" stepKey="clickStoreViewSwitchDropdown"/> - <waitForElementVisible selector="{{AdminMainActionsSection.storeViewByName('Default Store View')}}" stepKey="waitForStoreViewsAreVisible"/> - <click selector="{{AdminMainActionsSection.storeViewByName(storeView)}}" stepKey="clickStoreViewByName"/> <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitingForInformationModal"/> <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmStoreSwitch"/> <waitForPageLoad stepKey="waitForStoreViewSwitched"/> <see userInput="{{storeView}}" selector="{{AdminMainActionsSection.storeSwitcher}}" stepKey="seeNewStoreViewName"/> </actionGroup> + + <actionGroup name="AdminSwitchStoreViewActionGroup" extends="AdminSwitchBaseActionGroup"> + <waitForElementVisible selector="{{AdminMainActionsSection.storeViewByName(storeView)}}" after="clickStoreViewSwitchDropdown" stepKey="waitForStoreViewsAreVisible"/> + <click selector="{{AdminMainActionsSection.storeViewByName(storeView)}}" after="waitForStoreViewsAreVisible" stepKey="clickStoreViewByName"/> + </actionGroup> + + <actionGroup name="AdminSwitchWebsiteActionGroup" extends="AdminSwitchBaseActionGroup"> + <waitForElementVisible selector="{{AdminMainActionsSection.websiteByName(storeView)}}" after="clickStoreViewSwitchDropdown" stepKey="waitForStoreViewsAreVisible"/> + <click selector="{{AdminMainActionsSection.websiteByName(storeView)}}" after="waitForStoreViewsAreVisible" stepKey="clickStoreViewByName"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmStoreSwitch"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminMainActionsSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminMainActionsSection.xml index 14429c298b5e5..1a95d88d454e4 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminMainActionsSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminMainActionsSection.xml @@ -7,10 +7,11 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminMainActionsSection"> <element name="storeSwitcher" type="text" selector=".store-switcher"/> <element name="storeViewDropdown" type="button" selector="#store-change-button"/> <element name="storeViewByName" type="button" selector="//*[@class='store-switcher-store-view ']/a[contains(text(), '{{storeViewName}}')]" timeout="30" parameterized="true"/> + <element name="websiteByName" type="button" selector="//*[@class='store-switcher-website ']/a[contains(text(), '{{websiteName}}')]" timeout="30" parameterized="true"/> </section> </sections> From 50c7536500b06e77e35d2c55c0ca16f81ab09a0d Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Tue, 5 Feb 2019 10:11:27 +0200 Subject: [PATCH 0499/1295] MAGETWO-73978: API call with pageSize and currentPage > items should return error --- .../Magento/Catalog/Model/CategoryList.php | 9 +- .../Test/Unit/Model/CategoryListTest.php | 2 +- .../ResourceModel/AddressRepositoryTest.php | 79 +++++++++++++-- .../Magento/Framework/Data/Collection.php | 9 ++ .../Data/Test/Unit/CollectionTest.php | 96 ++++++++++++++++++- 5 files changed, 179 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/Catalog/Model/CategoryList.php b/app/code/Magento/Catalog/Model/CategoryList.php index 790ea6b921fbe..86692c7d6bc61 100644 --- a/app/code/Magento/Catalog/Model/CategoryList.php +++ b/app/code/Magento/Catalog/Model/CategoryList.php @@ -15,6 +15,9 @@ use Magento\Framework\Api\SearchCriteriaInterface; use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; +/** + * Class for getting category list. + */ class CategoryList implements CategoryListInterface { /** @@ -64,7 +67,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function getList(SearchCriteriaInterface $searchCriteria) { @@ -75,8 +78,8 @@ public function getList(SearchCriteriaInterface $searchCriteria) $this->collectionProcessor->process($searchCriteria, $collection); $items = []; - foreach ($collection->getAllIds() as $id) { - $items[] = $this->categoryRepository->get($id); + foreach ($collection->getItems() as $category) { + $items[] = $this->categoryRepository->get($category->getId()); } /** @var CategorySearchResultsInterface $searchResult */ diff --git a/app/code/Magento/Catalog/Test/Unit/Model/CategoryListTest.php b/app/code/Magento/Catalog/Test/Unit/Model/CategoryListTest.php index b8b76524099f4..0b6b56781a5df 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/CategoryListTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/CategoryListTest.php @@ -93,7 +93,7 @@ public function testGetList() $collection = $this->getMockBuilder(Collection::class)->disableOriginalConstructor()->getMock(); $collection->expects($this->once())->method('getSize')->willReturn($totalCount); - $collection->expects($this->once())->method('getAllIds')->willReturn([$categoryIdFirst, $categoryIdSecond]); + $collection->expects($this->once())->method('getItems')->willReturn([$categoryIdFirst, $categoryIdSecond]); $this->collectionProcessorMock->expects($this->once()) ->method('process') 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 2b74a58288600..37a36b1b3c42a 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/AddressRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/AddressRepositoryTest.php @@ -17,6 +17,8 @@ use Magento\Store\Api\WebsiteRepositoryInterface; /** + * Class with integration tests for AddressRepository. + * * @SuppressWarnings(PHPMD.TooManyMethods) * @SuppressWarnings(PHPMD.ExcessivePublicCount) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -39,6 +41,9 @@ class AddressRepositoryTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Framework\Api\DataObjectHelper */ private $dataObjectHelper; + /** + * @inheritdoc + */ protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); @@ -87,6 +92,9 @@ protected function setUp() $this->expectedAddresses = [$address, $address2]; } + /** + * @inheritdoc + */ protected function tearDown() { $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); @@ -96,6 +104,8 @@ protected function tearDown() } /** + * Test for save address changes. + * * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Customer/_files/customer_address.php * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php @@ -117,6 +127,8 @@ public function testSaveAddressChanges() } /** + * Test for method save address with new id. + * * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Customer/_files/customer_address.php * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php @@ -131,6 +143,8 @@ public function testSaveAddressesIdSetButNotAlreadyExisting() } /** + * Test for method get address by id. + * * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Customer/_files/customer_address.php * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php @@ -144,6 +158,8 @@ public function testGetAddressById() } /** + * Test for method get address by id with incorrect id. + * * @magentoDataFixture Magento/Customer/_files/customer.php * @expectedException \Magento\Framework\Exception\NoSuchEntityException * @expectedExceptionMessage No such entity with addressId = 12345 @@ -154,6 +170,8 @@ public function testGetAddressByIdBadAddressId() } /** + * Test for method save new address. + * * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Customer/_files/customer_address.php * @magentoAppIsolation enabled @@ -174,6 +192,8 @@ public function testSaveNewAddress() } /** + * Test for saving address with invalid address. + * * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Customer/_files/customer_address.php * @magentoAppIsolation enabled @@ -199,6 +219,8 @@ public function testSaveNewAddressWithAttributes() } /** + * Test for method saaveNewAddress with new attributes. + * * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Customer/_files/customer_address.php * @magentoAppIsolation enabled @@ -222,6 +244,11 @@ public function testSaveNewInvalidAddress() } } + /** + * Test for saving address without existing customer. + * + * @return void + */ public function testSaveAddressesCustomerIdNotExist() { $proposedAddress = $this->_createSecondAddress()->setCustomerId(4200); @@ -233,6 +260,11 @@ public function testSaveAddressesCustomerIdNotExist() } } + /** + * Test for saving addresses with invalid customer id. + * + * @return void + */ public function testSaveAddressesCustomerIdInvalid() { $proposedAddress = $this->_createSecondAddress()->setCustomerId('this_is_not_a_valid_id'); @@ -245,6 +277,8 @@ public function testSaveAddressesCustomerIdInvalid() } /** + * Test for deleteAddressById. + * * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Customer/_files/customer_address.php */ @@ -268,6 +302,8 @@ public function testDeleteAddress() } /** + * Test for delete method. + * * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Customer/_files/customer_address.php */ @@ -291,6 +327,8 @@ public function testDeleteAddressById() } /** + * Test delete address from customer with incorrect address id. + * * @magentoDataFixture Magento/Customer/_files/customer.php */ public function testDeleteAddressFromCustomerBadAddressId() @@ -304,10 +342,14 @@ public function testDeleteAddressFromCustomerBadAddressId() } /** + * Test for searching addressed. + * * @param \Magento\Framework\Api\Filter[] $filters - * @param \Magento\Framework\Api\Filter[] $filterGroup - * @param \Magento\Framework\Api\SortOrder[] $filterOrders + * @param \Magento\Framework\Api\Filter[]|null $filterGroup + * @param \Magento\Framework\Api\SortOrder[]|null $filterOrders * @param array $expectedResult array of expected results indexed by ID + * @param int $currentPage current page for search criteria + * @return void * * @dataProvider searchAddressDataProvider * @@ -315,8 +357,13 @@ public function testDeleteAddressFromCustomerBadAddressId() * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php * @magentoAppIsolation enabled */ - public function testSearchAddresses($filters, $filterGroup, $filterOrders, $expectedResult) - { + public function testSearchAddresses( + array $filters, + $filterGroup, + $filterOrders, + array $expectedResult, + int $currentPage + ) { /** @var \Magento\Framework\Api\SearchCriteriaBuilder $searchBuilder */ $searchBuilder = $this->objectManager->create(\Magento\Framework\Api\SearchCriteriaBuilder::class); foreach ($filters as $filter) { @@ -332,7 +379,7 @@ public function testSearchAddresses($filters, $filterGroup, $filterOrders, $expe } $searchBuilder->setPageSize(1); - $searchBuilder->setCurrentPage(2); + $searchBuilder->setCurrentPage($currentPage); $searchCriteria = $searchBuilder->create(); $searchResults = $this->repository->getList($searchCriteria); @@ -350,7 +397,12 @@ public function testSearchAddresses($filters, $filterGroup, $filterOrders, $expe $this->assertEquals($expectedResult[$expectedResultIndex]['firstname'], $items[0]->getFirstname()); } - public function searchAddressDataProvider() + /** + * Data provider for searchAddresses. + * + * @return array + */ + public function searchAddressDataProvider(): array { /** * @var \Magento\Framework\Api\FilterBuilder $filterBuilder @@ -365,23 +417,25 @@ public function searchAddressDataProvider() return [ 'Address with postcode 75477' => [ [$filterBuilder->setField('postcode')->setValue('75477')->create()], - null, + [], null, [ ['id' => 1, 'city' => 'CityM', 'postcode' => 75477, 'firstname' => 'John'], ], + 1, ], 'Address with city CityM' => [ [$filterBuilder->setField('city')->setValue('CityM')->create()], - null, + [], null, [ ['id' => 1, 'city' => 'CityM', 'postcode' => 75477, 'firstname' => 'John'], ], + 1, ], 'Addresses with firstname John sorted by firstname desc, city asc' => [ [$filterBuilder->setField('firstname')->setValue('John')->create()], - null, + [], [ $orderBuilder->setField('firstname')->setDirection(SortOrder::SORT_DESC)->create(), $orderBuilder->setField('city')->setDirection(SortOrder::SORT_ASC)->create(), @@ -390,6 +444,7 @@ public function searchAddressDataProvider() ['id' => 1, 'city' => 'CityM', 'postcode' => 75477, 'firstname' => 'John'], ['id' => 2, 'city' => 'CityX', 'postcode' => 47676, 'firstname' => 'John'], ], + 2, ], 'Addresses with postcode of either 75477 or 47676 sorted by city desc' => [ [], @@ -404,10 +459,11 @@ public function searchAddressDataProvider() ['id' => 2, 'city' => 'CityX', 'postcode' => 47676, 'firstname' => 'John'], ['id' => 1, 'city' => 'CityM', 'postcode' => 75477, 'firstname' => 'John'], ], + 2, ], 'Addresses with postcode greater than 0 sorted by firstname asc, postcode desc' => [ [$filterBuilder->setField('postcode')->setValue('0')->setConditionType('gt')->create()], - null, + [], [ $orderBuilder->setField('firstname')->setDirection(SortOrder::SORT_ASC)->create(), $orderBuilder->setField('postcode')->setDirection(SortOrder::SORT_ASC)->create(), @@ -416,11 +472,14 @@ public function searchAddressDataProvider() ['id' => 2, 'city' => 'CityX', 'postcode' => 47676, 'firstname' => 'John'], ['id' => 1, 'city' => 'CityM', 'postcode' => 75477, 'firstname' => 'John'], ], + 2, ], ]; } /** + * Test for save addresses with restricted countries. + * * @magentoDataFixture Magento/Customer/Fixtures/customer_sec_website.php */ public function testSaveAddressWithRestrictedCountries() diff --git a/lib/internal/Magento/Framework/Data/Collection.php b/lib/internal/Magento/Framework/Data/Collection.php index 099753ac1b56f..4e9132d49a2e2 100644 --- a/lib/internal/Magento/Framework/Data/Collection.php +++ b/lib/internal/Magento/Framework/Data/Collection.php @@ -7,6 +7,7 @@ use Magento\Framework\Data\Collection\EntityFactoryInterface; use Magento\Framework\Option\ArrayInterface; +use Magento\Framework\Exception\InputException; /** * Data collection @@ -234,12 +235,20 @@ protected function _setIsLoaded($flag = true) * Get current collection page * * @param int $displacement + * @throws \Magento\Framework\Exception\InputException * @return int */ public function getCurPage($displacement = 0) { if ($this->_curPage + $displacement < 1) { return 1; + } elseif ($this->_curPage > $this->getLastPageNumber() && $displacement === 0) { + throw new InputException( + __( + 'currentPage value %1 specified is greater than the %2 page(s) available.', + [$this->_curPage, $this->getLastPageNumber()] + ) + ); } elseif ($this->_curPage + $displacement > $this->getLastPageNumber()) { return $this->getLastPageNumber(); } else { diff --git a/lib/internal/Magento/Framework/Data/Test/Unit/CollectionTest.php b/lib/internal/Magento/Framework/Data/Test/Unit/CollectionTest.php index 2ecc67e1eb70e..c1f504fc72086 100644 --- a/lib/internal/Magento/Framework/Data/Test/Unit/CollectionTest.php +++ b/lib/internal/Magento/Framework/Data/Test/Unit/CollectionTest.php @@ -7,6 +7,9 @@ // @codingStandardsIgnoreFile +/** + * Class for Collection test. + */ class CollectionTest extends \PHPUnit\Framework\TestCase { /** @@ -14,6 +17,9 @@ class CollectionTest extends \PHPUnit\Framework\TestCase */ protected $_model; + /** + * @inheritdoc + */ protected function setUp() { $this->_model = new \Magento\Framework\Data\Collection( @@ -21,6 +27,11 @@ protected function setUp() ); } + /** + * Test for method removeAllItems. + * + * @return void + */ public function testRemoveAllItems() { $this->_model->addItem(new \Magento\Framework\DataObject()); @@ -32,6 +43,7 @@ public function testRemoveAllItems() /** * Test loadWithFilter() + * * @return void */ public function testLoadWithFilter() @@ -44,6 +56,8 @@ public function testLoadWithFilter() } /** + * Test for method etItemObjectClass. + * * @dataProvider setItemObjectClassDataProvider */ public function testSetItemObjectClass($class) @@ -53,6 +67,8 @@ public function testSetItemObjectClass($class) } /** + * Data provider. + * * @return array */ public function setItemObjectClassDataProvider() @@ -61,6 +77,8 @@ public function setItemObjectClassDataProvider() } /** + * Test for method setItemObjectClass with exception. + * * @expectedException \InvalidArgumentException * @expectedExceptionMessage Incorrect_ClassName does not extend \Magento\Framework\DataObject */ @@ -69,12 +87,22 @@ public function testSetItemObjectClassException() $this->_model->setItemObjectClass('Incorrect_ClassName'); } + /** + * Test for method addFilter. + * + * @return void + */ public function testAddFilter() { $this->_model->addFilter('field1', 'value'); $this->assertEquals('field1', $this->_model->getFilter('field1')->getData('field')); } + /** + * Test for method getFilters. + * + * @return void + */ public function testGetFilters() { $this->_model->addFilter('field1', 'value'); @@ -83,12 +111,22 @@ public function testGetFilters() $this->assertEquals('field2', $this->_model->getFilter(['field1', 'field2'])[1]->getData('field')); } + /** + * Test for method get non existion filters. + * + * @return void + */ public function testGetNonExistingFilters() { $this->assertEmpty($this->_model->getFilter([])); $this->assertEmpty($this->_model->getFilter('non_existing_filter')); } + /** + * Test for lag. + * + * @return void + */ public function testFlag() { $this->_model->setFlag('flag_name', 'flag_value'); @@ -97,12 +135,36 @@ public function testFlag() $this->assertNull($this->_model->getFlag('non_existing_flag')); } + /** + * Test for method getCurPage. + * + * @return void + */ public function testGetCurPage() { - $this->_model->setCurPage(10); + $this->_model->setCurPage(1); $this->assertEquals(1, $this->_model->getCurPage()); } + /** + * Test for getCurPage with exception. + * + * @expectedException \Magento\Framework\Exception\StateException + * @expectedExceptionMessage currentPage value 10 specified is greater than the 1 page(s) available. + * @return void + */ + public function testGetCurPageWithException() + { + $this->_model->setCurPage(10); + $this->expectException(\Magento\Framework\Exception\InputException::class); + $this->_model->getCurPage(); + } + + /** + * Test for method possibleFlowWithItem. + * + * @return void + */ public function testPossibleFlowWithItem() { $firstItemMock = $this->createPartialMock(\Magento\Framework\DataObject::class, ['getId', 'getData', 'toArray']); @@ -164,6 +226,11 @@ public function testPossibleFlowWithItem() $this->assertEquals([], $this->_model->getItems()); } + /** + * Test for method eachCallsMethodOnEachItemWithNoArgs. + * + * @return void + */ public function testEachCallsMethodOnEachItemWithNoArgs() { for ($i = 0; $i < 3; $i++) { @@ -173,7 +240,12 @@ public function testEachCallsMethodOnEachItemWithNoArgs() } $this->_model->each('testCallback'); } - + + /** + * Test for method eachCallsMethodOnEachItemWithArgs. + * + * @return void + */ public function testEachCallsMethodOnEachItemWithArgs() { for ($i = 0; $i < 3; $i++) { @@ -184,6 +256,11 @@ public function testEachCallsMethodOnEachItemWithArgs() $this->_model->each('testCallback', ['a', 'b', 'c']); } + /** + * Test for method callsClosureWithEachItemAndNoArgs. + * + * @return void + */ public function testCallsClosureWithEachItemAndNoArgs() { for ($i = 0; $i < 3; $i++) { @@ -196,6 +273,11 @@ public function testCallsClosureWithEachItemAndNoArgs() }); } + /** + * Test for method callsClosureWithEachItemAndArgs. + * + * @return void + */ public function testCallsClosureWithEachItemAndArgs() { for ($i = 0; $i < 3; $i++) { @@ -208,6 +290,11 @@ public function testCallsClosureWithEachItemAndArgs() }, ['a', 'b', 'c']); } + /** + * Test for method callsCallableArrayWithEachItemNoArgs. + * + * @return void + */ public function testCallsCallableArrayWithEachItemNoArgs() { $mockCallbackObject = $this->getMockBuilder('DummyEachCallbackInstance') @@ -226,6 +313,11 @@ public function testCallsCallableArrayWithEachItemNoArgs() $this->_model->each([$mockCallbackObject, 'testObjCallback']); } + /** + * Test for method callsCallableArrayWithEachItemAndArgs. + * + * @return void + */ public function testCallsCallableArrayWithEachItemAndArgs() { $mockCallbackObject = $this->getMockBuilder('DummyEachCallbackInstance') From bfa89ccfaabf68be04d03717eacd3c3849e08e62 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Tue, 5 Feb 2019 10:33:32 +0200 Subject: [PATCH 0500/1295] MAGETWO-96457: [Magento Cloud] [QUANS] Tiered block displayed on configurable products without any tiered pricing --- .../Sales/Block/Adminhtml/Items/Column/Name.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) 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 f4e2a50ac62c9..2175d83c67d07 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Items/Column/Name.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Items/Column/Name.php @@ -33,8 +33,13 @@ class Name extends \Magento\Sales\Block\Adminhtml\Items\Column\DefaultColumn * @return string * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function truncateString($value, $length = 80, $etc = '...', &$remainder = '', $breakWords = true) - { + 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, 'breakWords' => $breakWords] @@ -48,7 +53,7 @@ public function truncateString($value, $length = 80, $etc = '...', &$remainder = * @param string $value * @return array */ - public function getFormattedOption($value) + public function getFormattedOption(string $value): array { $remainder = ''; $this->truncateString($value, 55, '', $remainder); From 936b550de66d5ba9e815f438e0c9ca47e0739dc7 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Tue, 5 Feb 2019 10:35:16 +0200 Subject: [PATCH 0501/1295] MAGETWO-97680: Issue with Email template preview --- lib/internal/Magento/Framework/Filter/Template.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Filter/Template.php b/lib/internal/Magento/Framework/Filter/Template.php index 92bcdd2082509..52dee5f0f1a41 100644 --- a/lib/internal/Magento/Framework/Filter/Template.php +++ b/lib/internal/Magento/Framework/Filter/Template.php @@ -368,8 +368,8 @@ protected function getVariable($value, $default = '{no_value_defined}') } $last = $i; } elseif (isset($stackVars[$i - 1]['variable']) - && is_object($stackVars[$i - 1]['variable']) - && $stackVars[$i]['type'] == 'method' + && is_object($stackVars[$i - 1]['variable']) + && $stackVars[$i]['type'] == 'method' ) { // Calling object methods $object = $stackVars[$i - 1]['variable']; From 3cf4f3506ef3b0cc841943b0995a2dd3786884f3 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Tue, 5 Feb 2019 10:53:44 +0200 Subject: [PATCH 0502/1295] MAGETWO-96457: [Magento Cloud] [QUANS] Tiered block displayed on configurable products without any tiered pricing --- .../Test/Mftf/Section/StorefrontProductInfoMainSection.xml | 1 + ...PriceNotAvailableForProductOptionsWithoutTierPriceTest.xml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontProductInfoMainSection.xml index be0c2b05e48ba..4f2320666efbc 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontProductInfoMainSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -14,5 +14,6 @@ <element name="stockIndication" type="block" selector=".stock" /> <element name="productAttributeOptionsSelectButton" type="select" selector="#product-options-wrapper .super-attribute-select"/> <element name="optionByAttributeId" type="input" selector="#attribute{{var1}}" parameterized="true"/> + <element name="productPriceBox" type="block" selector=".price-box"/> </section> </sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml index ac4ea194ce6e8..5daf699294155 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml @@ -37,7 +37,7 @@ <!--Go to storefront product page an check price box css--> <amOnPage url="{{StorefrontProductPage.url($$createConfigProductCreateConfigurableProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToSimpleProductPage"/> <selectOption selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" userInput="$$getConfigAttributeOption1CreateConfigurableProduct.value$$" stepKey="selectOption"/> - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="class" stepKey="grabGrabPriceClass"/> - <assertNotContains actual="$grabGrabPriceClass" expected=".price-box .price-tier_price" expectedType="string" stepKey="assertNotEquals"/> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productPriceBox}}" userInput="class" stepKey="grabGrabPriceClass"/> + <assertContains actual="$grabGrabPriceClass" expected="price-box price-final_price" expectedType="string" stepKey="assertEquals"/> </test> </tests> From 0c0926643d32d6686fac67a19d2e0fb964df8dec Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Tue, 5 Feb 2019 11:23:24 +0200 Subject: [PATCH 0503/1295] MAGETWO-94439: Not possible to drag specified Related/Upsell/Cross-sell products to new positions in Windows 10 machine --- .../Ui/view/base/web/js/dynamic-rows/dnd.js | 89 +++++++++++++------ 1 file changed, 60 insertions(+), 29 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dnd.js b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dnd.js index dee9ba7acc172..583e97b7e9449 100644 --- a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dnd.js +++ b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dnd.js @@ -15,8 +15,7 @@ define([ ], function (ko, $, _, Element) { 'use strict'; - var transformProp, - isTouchDevice = typeof document.ontouchstart !== 'undefined'; + var transformProp; /** * Get element context @@ -110,11 +109,7 @@ define([ * @param {Object} data - element data */ initListeners: function (elem, data) { - if (isTouchDevice) { - $(elem).on('touchstart', this.mousedownHandler.bind(this, data, elem)); - } else { - $(elem).on('mousedown', this.mousedownHandler.bind(this, data, elem)); - } + $(elem).on('mousedown touchstart', this.mousedownHandler.bind(this, data, elem)); }, /** @@ -131,26 +126,20 @@ define([ $table = $(elem).parents('table').eq(0), $tableWrapper = $table.parent(); + this.disableScroll(); $(recordNode).addClass(this.draggableElementClass); $(originRecord).addClass(this.draggableElementClass); this.step = this.step === 'auto' ? originRecord.height() / 2 : this.step; drEl.originRow = originRecord; drEl.instance = recordNode = this.processingStyles(recordNode, elem); drEl.instanceCtx = this.getRecord(originRecord[0]); - drEl.eventMousedownY = isTouchDevice ? event.originalEvent.touches[0].pageY : event.pageY; + drEl.eventMousedownY = this.getPageY(event); drEl.minYpos = $table.offset().top - originRecord.offset().top + $table.children('thead').outerHeight(); drEl.maxYpos = drEl.minYpos + $table.children('tbody').outerHeight() - originRecord.outerHeight(); $tableWrapper.append(recordNode); - - if (isTouchDevice) { - this.body.bind('touchmove', this.mousemoveHandler); - this.body.bind('touchend', this.mouseupHandler); - } else { - this.body.bind('mousemove', this.mousemoveHandler); - this.body.bind('mouseup', this.mouseupHandler); - } - + this.body.bind('mousemove touchmove', this.mousemoveHandler); + this.body.bind('mouseup touchend', this.mouseupHandler); }, /** @@ -160,16 +149,13 @@ define([ */ mousemoveHandler: function (event) { var depEl = this.draggableElement, - pageY = isTouchDevice ? event.originalEvent.touches[0].pageY : event.pageY, + pageY = this.getPageY(event), positionY = pageY - depEl.eventMousedownY, processingPositionY = positionY + 'px', processingMaxYpos = depEl.maxYpos + 'px', processingMinYpos = depEl.minYpos + 'px', depElement = this.getDepElement(depEl.instance, positionY, depEl.originRow); - event.stopPropagation(); - event.preventDefault(); - if (depElement) { depEl.depElement ? depEl.depElement.elem.removeClass(depEl.depElement.className) : false; depEl.depElement = depElement; @@ -194,9 +180,10 @@ define([ mouseupHandler: function (event) { var depElementCtx, drEl = this.draggableElement, - pageY = isTouchDevice ? event.originalEvent.touches[0].pageY : event.pageY, + pageY = this.getPageY(event), positionY = pageY - drEl.eventMousedownY; + this.enableScroll(); drEl.depElement = this.getDepElement(drEl.instance, positionY, this.draggableElement.originRow); drEl.instance.remove(); @@ -212,13 +199,8 @@ define([ drEl.originRow.removeClass(this.draggableElementClass); - if (isTouchDevice) { - this.body.unbind('touchmove', this.mousemoveHandler); - this.body.unbind('touchend', this.mouseupHandler); - } else { - this.body.unbind('mousemove', this.mousemoveHandler); - this.body.unbind('mouseup', this.mouseupHandler); - } + this.body.unbind('mousemove touchmove', this.mousemoveHandler); + this.body.unbind('mouseup touchend', this.mouseupHandler); this.draggableElement = {}; }, @@ -402,6 +384,55 @@ define([ index = _.isFunction(ctx.$index) ? ctx.$index() : ctx.$index; return this.recordsCache()[index]; + }, + + /** + * Get correct page Y + * + * @param {Object} event - current event + * @returns {integer} + */ + getPageY: function (event) { + var pageY; + + if (event.type.indexOf('touch') >= 0) { + if (event.originalEvent.touches[0]) { + pageY = event.originalEvent.touches[0].pageY; + } else { + pageY = event.originalEvent.changedTouches[0].pageY; + } + } else { + pageY = event.pageY; + } + + return pageY; + }, + + /** + * Disable page scrolling + */ + disableScroll: function () { + document.body.addEventListener('touchmove', this.preventDefault, { + passive: false + }); + }, + + /** + * Enable page scrolling + */ + enableScroll: function () { + document.body.removeEventListener('touchmove', this.preventDefault, { + passive: false + }); + }, + + /** + * Prevent default function + * + * @param {Object} event - event object + */ + preventDefault: function (event) { + event.preventDefault(); } }); From 1a3806794361745f7678ad4d5ede83ecdaa688cb Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Tue, 5 Feb 2019 12:17:40 +0200 Subject: [PATCH 0504/1295] ENGCOM-4088: Static test fix. --- .../Magento/Ui/view/base/web/js/form/element/post-code.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js b/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js index b8bab2f72f38a..1b6dd9f1c57ec 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js @@ -33,11 +33,11 @@ define([ } option = options[value]; - + if (!option) { return; } - + if (option['is_zipcode_optional']) { this.error(false); this.validation = _.omit(this.validation, 'required-entry'); From 0cbf9d7674ea90a3f3648a818186bbbc3d27a2e2 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Tue, 5 Feb 2019 12:19:10 +0200 Subject: [PATCH 0505/1295] MAGETWO-88612: Exception when login as restricted admin with access only to CMS Block --- .../Ui/Component/Listing/DataProviderTest.php | 11 ++++++----- .../Magento/Cms/Ui/Component/DataProvider.php | 16 +++++++++++----- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/DataProviderTest.php b/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/DataProviderTest.php index 54e0e17ab7ad6..ec9cb86c6c9dc 100644 --- a/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/DataProviderTest.php +++ b/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/DataProviderTest.php @@ -118,11 +118,12 @@ public function testPrepareMetadata() 'config' => [ 'editorConfig' => [ 'enabled' => false - ] - ] - ] - ] - ] + ], + 'componentType' => \Magento\Ui\Component\Container::NAME, + ], + ], + ], + ], ]; $this->assertEquals( diff --git a/app/code/Magento/Cms/Ui/Component/DataProvider.php b/app/code/Magento/Cms/Ui/Component/DataProvider.php index 5fc9c5a896037..a0f68f8dde05a 100644 --- a/app/code/Magento/Cms/Ui/Component/DataProvider.php +++ b/app/code/Magento/Cms/Ui/Component/DataProvider.php @@ -13,6 +13,9 @@ use Magento\Framework\AuthorizationInterface; use Magento\Framework\View\Element\UiComponent\DataProvider\Reporting; +/** + * DataProvider for cms ui. + */ class DataProvider extends \Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider { /** @@ -67,6 +70,8 @@ public function __construct( } /** + * Get authorization info. + * * @deprecated 101.0.7 * @return AuthorizationInterface|mixed */ @@ -95,11 +100,12 @@ public function prepareMetadata() 'config' => [ 'editorConfig' => [ 'enabled' => false - ] - ] - ] - ] - ] + ], + 'componentType' => \Magento\Ui\Component\Container::NAME, + ], + ], + ], + ], ]; } From 83d0f823c99cf31a4926ed2d8abe2675cd97bff2 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Tue, 5 Feb 2019 12:34:36 +0200 Subject: [PATCH 0506/1295] MAGETWO-73978: API call with pageSize and currentPage > items should return error --- app/code/Magento/Catalog/Test/Unit/Model/CategoryListTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/CategoryListTest.php b/app/code/Magento/Catalog/Test/Unit/Model/CategoryListTest.php index 0b6b56781a5df..f78c0ad924954 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/CategoryListTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/CategoryListTest.php @@ -93,7 +93,7 @@ public function testGetList() $collection = $this->getMockBuilder(Collection::class)->disableOriginalConstructor()->getMock(); $collection->expects($this->once())->method('getSize')->willReturn($totalCount); - $collection->expects($this->once())->method('getItems')->willReturn([$categoryIdFirst, $categoryIdSecond]); + $collection->expects($this->once())->method('getItems')->willReturn([$categoryFirst, $categorySecond]); $this->collectionProcessorMock->expects($this->once()) ->method('process') From cac994a49620be0ab5cb90ad320de6f4285fe79b Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Tue, 5 Feb 2019 12:42:10 +0200 Subject: [PATCH 0507/1295] MAGETWO-96061: App:config:dump doesn't lock all the settings in Magento backend --- lib/internal/Magento/Framework/App/DeploymentConfig.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/internal/Magento/Framework/App/DeploymentConfig.php b/lib/internal/Magento/Framework/App/DeploymentConfig.php index 615c295675adc..f7acd801dbed6 100644 --- a/lib/internal/Magento/Framework/App/DeploymentConfig.php +++ b/lib/internal/Magento/Framework/App/DeploymentConfig.php @@ -70,6 +70,11 @@ public function get($key = null, $defaultValue = null) if ($key === null) { return $this->flatData; } + + if (isset($this->flatData[$key]) && $this->flatData[$key] === null) { + return ''; + } + return $this->flatData[$key] ?? $defaultValue; } From d7fb21ae94273dc5c5e0fd445cc9a3049883ded1 Mon Sep 17 00:00:00 2001 From: DmytroPaidych <dimonovp@gmail.com> Date: Tue, 5 Feb 2019 13:03:11 +0200 Subject: [PATCH 0508/1295] MAGETWO-97236: Delete Product Staging Update when the Product is used --- .../UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml index b43e0e05ad55d..f51b9321e6911 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml @@ -8,7 +8,7 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="AdminUrlRewriteEditPage" url="admin/url_rewrite/edit/id/{{url_rewrite_id}}/" area="admin" module="Magento_UrlRewrite"> + <page name="AdminUrlRewriteEditPage" url="admin/url_rewrite/edit/id/{{url_rewrite_id}}/" area="admin" module="Magento_UrlRewrite" parameterized="true"> <section name="AdminUrlRewriteEditSection"/> </page> </pages> From 7ef7fdeb39623fb5e9da437724993396817609df Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Tue, 5 Feb 2019 13:22:39 +0200 Subject: [PATCH 0509/1295] MAGETWO-97950: Minicart isn't updated for disabled products --- .../frontend/templates/cart/noItems.phtml | 7 ++++ .../view/frontend/web/js/empty-cart.js | 12 ++++++ ...efrontGuestCheckoutDisabledProductTest.xml | 41 ++++++++++++++++--- 3 files changed, 54 insertions(+), 6 deletions(-) create mode 100644 app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/noItems.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/noItems.phtml index 1c0c221a550cd..67ac4a9335565 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/noItems.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/noItems.phtml @@ -13,3 +13,10 @@ $block->escapeUrl($block->getContinueShoppingUrl())) ?></p> <?= $block->getChildHtml('shopping.cart.table.after') ?> </div> +<script type="text/x-magento-init"> +{ + "*": { + "Magento_Checkout/js/empty-cart": {} + } +} +</script> \ No newline at end of file diff --git a/app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js b/app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js new file mode 100644 index 0000000000000..27d38697afe39 --- /dev/null +++ b/app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js @@ -0,0 +1,12 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'Magento_Customer/js/customer-data' +], function (customerData) { + 'use strict'; + + customerData.reload(['cart'], false); +}); diff --git a/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml b/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml index a2f08353a4f3b..034c2bc6f051b 100644 --- a/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml +++ b/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml @@ -22,6 +22,9 @@ <createData entity="_defaultProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> </createData> + <createData entity="_defaultProduct" stepKey="createSimpleProduct2"> + <requiredEntity createDataKey="createCategory"/> + </createData> <!-- Create the configurable product based on the data in the /data folder --> <createData entity="BaseConfigurableProduct" stepKey="createConfigProduct"> <requiredEntity createDataKey="createCategory"/> @@ -70,7 +73,10 @@ </createData> </before> <after> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductListing"/> + <actionGroup ref="resetProductGridToDefaultView" stepKey="resetGridToDefaultKeywordSearch"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createSimpleProduct2" stepKey="deleteProduct2"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> @@ -79,13 +85,11 @@ </after> <!-- Step 1: Add simple product to shopping cart --> <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.name$$)}}" stepKey="amOnSimpleProductPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddSimpleProductToCart"> <argument name="product" value="$$createSimpleProduct$$"/> <argument name="productCount" value="1"/> </actionGroup> <amOnPage url="{{StorefrontProductPage.url($$createConfigProduct.custom_attributes[url_key]$$)}}" stepKey="goToConfigProductPage"/> - <waitForPageLoad stepKey="waitForStoreFrontLoad"/> <selectOption selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" userInput="$$getConfigAttributeOption1.value$$" stepKey="selectOption"/> <click selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="clickAddToCart" /> <waitForElement selector="{{StorefrontMessagesSection.messageProductAddedToCart($$createConfigProduct.name$$)}}" time="30" stepKey="assertMessage"/> @@ -94,8 +98,6 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!-- Find the first simple product that we just created using the product grid and go to its page--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> - <waitForPageLoad stepKey="waitForAdminProductGridLoad"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> <actionGroup ref="filterProductGridBySku" stepKey="findCreatedProduct"> <argument name="product" value="$$createConfigChildProduct1$$"/> </actionGroup> @@ -113,10 +115,37 @@ <argument name="status" value="Disable"/> </actionGroup> <closeTab stepKey="closeTab"/> - <!--Check cart--> + <!-- Check cart --> <reloadPage stepKey="reloadPage"/> - <waitForPageLoad stepKey="waitForCheckoutPageReload2"/> + <waitForPageLoad stepKey="waitForCheckoutPageReload"/> <click selector="{{StorefrontMiniCartSection.show}}" stepKey="clickMiniCart"/> <dontSeeElement selector="{{StorefrontMiniCartSection.quantity}}" stepKey="dontSeeCartItem"/> + <!-- Add simple product to shopping cart --> + <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct2.name$$)}}" stepKey="amOnSimpleProductPage2"/> + <actionGroup ref="StorefrontAddProductToCartQuantityActionGroup" stepKey="addToCart2"> + <argument name="productName" value="$$createSimpleProduct2$$"/> + <argument name="quantity" value="1"/> + </actionGroup> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckoutCartPage"/> + <!-- Disabled via admin panel --> + <openNewTab stepKey="openNewTab2"/> + <!-- Find the first simple product that we just created using the product grid and go to its page --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage2"/> + <actionGroup ref="filterProductGridBySku" stepKey="findCreatedProduct2"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProductPage2"/> + <waitForPageLoad stepKey="waitForProductPageLoad2"/> + <!-- Disabled simple product from grid --> + <actionGroup ref="ChangeStatusProductUsingProductGridActionGroup" stepKey="disabledProductFromGrid2"> + <argument name="product" value="$$createSimpleProduct2$$"/> + <argument name="status" value="Disable"/> + </actionGroup> + <closeTab stepKey="closeTab2"/> + <!--Check cart--> + <reloadPage stepKey="reloadPage2"/> + <waitForPageLoad stepKey="waitForCheckoutPageReload2"/> + <click selector="{{StorefrontMiniCartSection.show}}" stepKey="clickMiniCart2"/> + <dontSeeElement selector="{{StorefrontMiniCartSection.quantity}}" stepKey="dontSeeCartItem2"/> </test> </tests> From d6a61ac1fdca052a4722d1a04e8dfc81c16d89c1 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Tue, 5 Feb 2019 14:11:05 +0200 Subject: [PATCH 0510/1295] MAGETWO-93221: Payment method title does not update in the admin sales order grid --- .../Payment/Test/Unit/Helper/DataTest.php | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/app/code/Magento/Payment/Test/Unit/Helper/DataTest.php b/app/code/Magento/Payment/Test/Unit/Helper/DataTest.php index 3752e82fd1e5b..5f1c89e61f6c7 100644 --- a/app/code/Magento/Payment/Test/Unit/Helper/DataTest.php +++ b/app/code/Magento/Payment/Test/Unit/Helper/DataTest.php @@ -18,6 +18,9 @@ class DataTest extends \PHPUnit\Framework\TestCase /** @var \PHPUnit_Framework_MockObject_MockObject */ private $scopeConfig; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + private $paymentConfig; + /** @var \PHPUnit_Framework_MockObject_MockObject */ private $initialConfig; @@ -48,6 +51,7 @@ protected function setUp() $this->methodFactory = $arguments['paymentMethodFactory']; $this->appEmulation = $arguments['appEmulation']; + $this->paymentConfig = $arguments['paymentConfig']; $this->initialConfig = $arguments['initialConfig']; $this->helper = $objectManagerHelper->getObject($className, $arguments); @@ -253,6 +257,56 @@ public function testGetInfoBlockHtml() $this->assertEquals($blockHtml, $this->helper->getInfoBlockHtml($infoMock, $storeId)); } + /** + * @param bool $sorted + * @param bool $asLabelValue + * @param bool $withGroups + * @param string|null $configTitle + * @param array $paymentMethod + * @param array $expectedPaymentMethodList + * @return void + * + * @dataProvider paymentMethodListDataProvider + */ + public function testGetPaymentMethodList( + bool $sorted, + bool $asLabelValue, + bool $withGroups, + $configTitle, + array $paymentMethod, + array $expectedPaymentMethodList + ) { + $groups = ['group' => 'Group Title']; + + $this->initialConfig->method('getData') + ->with('default') + ->willReturn( + [ + Data::XML_PATH_PAYMENT_METHODS => [ + $paymentMethod['code'] => $paymentMethod['data'], + ] + ] + ); + + $this->scopeConfig->method('getValue') + ->with(sprintf('%s/%s/model', Data::XML_PATH_PAYMENT_METHODS, $paymentMethod['code'])) + ->willReturn(\Magento\Payment\Model\Method\AbstractMethod::class); + + $methodInstanceMock = $this->getMockBuilder(\Magento\Payment\Model\MethodInterface::class) + ->getMockForAbstractClass(); + $methodInstanceMock->method('getConfigData') + ->with('title', null) + ->willReturn($configTitle); + $this->methodFactory->method('create') + ->willReturn($methodInstanceMock); + + $this->paymentConfig->method('getGroups') + ->willReturn($groups); + + $paymentMethodList = $this->helper->getPaymentMethodList($sorted, $asLabelValue, $withGroups); + $this->assertEquals($expectedPaymentMethodList, $paymentMethodList); + } + /** * @return array */ @@ -269,4 +323,89 @@ public function getSortMethodsDataProvider() ] ]; } + + /** + * @return array + */ + public function paymentMethodListDataProvider() + { + return [ + 'Payment method with changed title' => + [ + true, + false, + false, + 'Config Payment Title', + [ + 'code' => 'payment_method', + 'data' => [ + 'active' => 1, + 'title' => 'Payment Title', + ], + ], + ['payment_method' => 'Config Payment Title'] + ], + 'Payment method with default title' => + [ + true, + false, + false, + null, + [ + 'code' => 'payment_method', + 'data' => [ + 'active' => 1, + 'title' => 'Payment Title', + ], + ], + ['payment_method' => 'Payment Title'], + ], + 'Payment method as value => label' => + [ + true, + true, + false, + null, + [ + 'code' => 'payment_method', + 'data' => [ + 'active' => 1, + 'title' => 'Payment Title', + ], + ], + [ + 'payment_method' => [ + 'value' => 'payment_method', + 'label' => 'Payment Title', + ], + ], + ], + 'Payment method with group' => + [ + true, + true, + true, + null, + [ + 'code' => 'payment_method', + 'data' => [ + 'active' => 1, + 'title' => 'Payment Title', + 'group' => 'group', + ], + ], + [ + 'group' => [ + 'label' => 'Group Title', + 'value' => [ + 'payment_method' => [ + 'value' => 'payment_method', + 'label' => 'Payment Title', + ], + ], + ], + ], + ], + ]; + } } From 078830a669c090b763e6e844b9116ba3ff19d141 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Tue, 5 Feb 2019 15:26:49 +0200 Subject: [PATCH 0511/1295] MAGETWO-94450: Rewards points earned from coupon code are not applied to guests that create accounts after checking out --- .../AdminCustomerMainActionsSection.xml | 3 +- .../Test/Mftf/Data/SalesRuleData.xml | 41 ++++++++++++++++++- .../Data/SalesRuleExtensionAttributeData.xml | 13 ++++++ .../sales-rule-extension-attribute-meta.xml | 12 ++++++ .../Test/Mftf/Metadata/sales_rule-meta.xml | 6 +-- 5 files changed, 68 insertions(+), 7 deletions(-) create mode 100644 app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleExtensionAttributeData.xml create mode 100644 app/code/Magento/SalesRule/Test/Mftf/Metadata/sales-rule-extension-attribute-meta.xml diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerMainActionsSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerMainActionsSection.xml index 0a77890033295..9553752539757 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerMainActionsSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerMainActionsSection.xml @@ -7,8 +7,9 @@ --> <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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCustomerMainActionsSection"> <element name="saveButton" type="button" selector="#save" timeout="30"/> + <element name="deleteButton" type="button" selector="div.page-actions button.delete" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml index 595aedc2b604f..25aaa6dd5a23d 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="ApiSalesRule" type="SalesRule"> <data key="name" unique="suffix">salesRule</data> <data key="description">Sales Rule Descritpion</data> @@ -141,7 +141,6 @@ <data key="uses_per_coupon">0</data> <data key="simple_free_shipping">0</data> </entity> - <entity name="SalesRuleSpecificCouponWithFixedDiscount" type="SalesRule"> <data key="name" unique="suffix">SimpleSalesRule</data> <data key="description">Sales Rule Description</data> @@ -168,6 +167,44 @@ <data key="uses_per_coupon">10</data> <data key="simple_free_shipping">1</data> </entity> + <entity name="CartPriceRuleWithRewardPointsOnly" type="SalesRule"> + <data key="name" unique="suffix">SalesRuleReward</data> + <data key="description">Sales Rule with Reward Point</data> + <array key="website_ids"> + <item>1</item> + </array> + <array key="customer_group_ids"> + <item>0</item> + <item>1</item> + <item>2</item> + <item>3</item> + </array> + <data key="uses_per_customer">10</data> + <data key="is_active">true</data> + <data key="stop_rules_processing">true</data> + <data key="is_advanced">true</data> + <data key="sort_order">0</data> + <data key="simple_action">by_percent</data> + <data key="discount_amount">0</data> + <data key="discount_qty">0</data> + <data key="discount_step">0</data> + <data key="apply_to_shipping">false</data> + <data key="times_used">0</data> + <data key="is_rss">true</data> + <data key="coupon_type">NO_COUPON</data> + <data key="use_auto_generation">false</data> + <data key="uses_per_coupon">10</data> + <data key="simple_free_shipping">0</data> + <requiredEntity type="sales-rule-extension-attribute">SalesRuleExtensionAttribute</requiredEntity> + </entity> + + <entity name="PriceRuleWithCondition" type="SalesRule"> + <data key="name" unique="suffix">SalesRule</data> + <data key="websites">Main Website</data> + <data key="customerGroups">'NOT LOGGED IN', 'General', 'Wholesale', 'Retailer'</data> + <data key="apply">Fixed amount discount for whole cart</data> + <data key="discountAmount">0</data> + </entity> <entity name="PriceRuleWithCondition" type="SalesRule"> <data key="name" unique="suffix">SalesRule</data> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleExtensionAttributeData.xml b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleExtensionAttributeData.xml new file mode 100644 index 0000000000000..43ff4d897c143 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleExtensionAttributeData.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="SalesRuleExtensionAttribute" type="sales-rule-extension-attribute"> + <data key="reward_points_delta">200</data> + </entity> +</entities> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales-rule-extension-attribute-meta.xml b/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales-rule-extension-attribute-meta.xml new file mode 100644 index 0000000000000..51c4ac24a7426 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales-rule-extension-attribute-meta.xml @@ -0,0 +1,12 @@ +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="SetSalesRuleExtensionAttribute" dataType="sales-rule-extension-attribute" type="create"> + <field key="reward_points_delta">integer</field> + </operation> +</operations> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-meta.xml b/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-meta.xml index 0d4c4356a20a7..38009c510d2be 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-meta.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Metadata/sales_rule-meta.xml @@ -6,7 +6,7 @@ */ --> <operations 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/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateSalesRule" dataType="SalesRule" type="create" auth="adminOauth" url="/V1/salesRules" method="POST"> <contentType>application/json</contentType> <object key="rule" dataType="SalesRule"> @@ -30,6 +30,7 @@ <field key="simple_free_shipping">string</field> <field key="stop_rules_processing">boolean</field> <field key="is_advanced">boolean</field> + <field key="extension_attributes">sales-rule-extension-attribute</field> <array key="store_labels"> <!-- specify object name as array value --> <value>SalesRuleStoreLabel</value> @@ -67,9 +68,6 @@ <field key="value">string</field> <field key="extension_attributes">empty_extension_attribute</field> </object> - <object dataType="ExtensionAttribute" key="extension_attributes"> - <field key="reward_points_delta">integer</field> - </object> </object> </operation> <operation name="DeleteSalesRule" dataType="SalesRule" type="delete" auth="adminOauth" url="/V1/salesRules/{rule_id}" method="DELETE"> From 532ca5d9cc11685ceed4fa1923662fa0e037e4b3 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Tue, 5 Feb 2019 16:14:00 +0200 Subject: [PATCH 0512/1295] MAGETWO-97950: Minicart isn't updated for disabled products --- .../Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml b/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml index 034c2bc6f051b..e5e7c3834bf7d 100644 --- a/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml +++ b/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml @@ -74,7 +74,7 @@ </before> <after> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductListing"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetGridToDefaultKeywordSearch"/> + <actionGroup ref="AdminResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createSimpleProduct2" stepKey="deleteProduct2"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> @@ -123,7 +123,7 @@ <!-- Add simple product to shopping cart --> <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct2.name$$)}}" stepKey="amOnSimpleProductPage2"/> <actionGroup ref="StorefrontAddProductToCartQuantityActionGroup" stepKey="addToCart2"> - <argument name="productName" value="$$createSimpleProduct2$$"/> + <argument name="productName" value="$$createSimpleProduct2.name$$"/> <argument name="quantity" value="1"/> </actionGroup> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckoutCartPage"/> From f62220b490438b6afa5283ae55d8902af090631e Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Tue, 5 Feb 2019 16:39:02 +0200 Subject: [PATCH 0513/1295] MAGETWO-82385: [CE 2.1.0 rc3] - Cancel an order [configurable product] #5313 --- .../Test/AdminCheckingProductQtyAfterOrderCancelTest.xml | 6 +++--- .../Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml | 4 ++-- .../Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml | 2 +- .../Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml | 2 +- .../Sales/Test/Mftf/Section/AdminInvoiceItemsSection.xml | 1 + 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckingProductQtyAfterOrderCancelTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckingProductQtyAfterOrderCancelTest.xml index c0d861d4190b8..af463c9042357 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckingProductQtyAfterOrderCancelTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckingProductQtyAfterOrderCancelTest.xml @@ -98,7 +98,7 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> <!--Open order--> - <actionGroup ref="openOrderById" stepKey="openOrderById"> + <actionGroup ref="OpenOrderById" stepKey="openOrderById"> <argument name="orderId" value="{$grabOrderNumber}"/> </actionGroup> @@ -106,7 +106,7 @@ <actionGroup ref="StartCreateInvoiceFromOrderPage" stepKey="startCreateInvoice"/> <!--Create partial invoice--> <actionGroup ref="CreatePartialInvoice" stepKey="createPartialInvoice"> - <argument name="rowNumber" value="1"/> + <argument name="productSku" value="$createConfigChildProduct.sku$"/> <argument name="qtyToInvoice" value="1"/> </actionGroup> <!--Submit Invoice--> @@ -117,7 +117,7 @@ <actionGroup ref="SubmitShipment" stepKey="submitShipment"/> <!--Cancel order--> - <actionGroup ref="cancelProcessingOrder" stepKey="cancelOrder"/> + <actionGroup ref="CancelProcessingOrder" stepKey="cancelOrder"/> <!--Check quantities in "Items Ordered" table--> <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Invoiced 1" stepKey="seeInvoicedQuantity"/> <see selector="{{AdminOrderItemsOrderedSection.itemQty('1')}}" userInput="Shipped 1" stepKey="seeShippedQuantity"/> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml index e63e279612fb1..ed63da75df9e1 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml @@ -47,10 +47,10 @@ <actionGroup name="CreatePartialInvoice"> <arguments> - <argument name="rowNumber" type="string" defaultValue="1"/> + <argument name="productSku" type="string"/> <argument name="qtyToInvoice" type="string" defaultValue="1"/> </arguments> - <fillField selector="{{AdminInvoiceItemsSection.itemQtyToInvoice(rowNumber)}}" userInput="{{qtyToInvoice}}" stepKey="changeQtyToInvoice"/> + <fillField selector="{{AdminInvoiceItemsSection.itemQtyToInvoiceBySku(productSku)}}" userInput="{{qtyToInvoice}}" stepKey="changeQtyToInvoice"/> <waitForElementVisible selector="{{AdminInvoiceItemsSection.updateQtyEnabled}}" stepKey="waitForUpdateQtyEnabled"/> <click selector="{{AdminInvoiceItemsSection.updateQty}}" stepKey="updateQty"/> <waitForLoadingMaskToDisappear stepKey="waitForQtyToUpdate"/> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml index 6b409b9f6a3da..730d3d6a5a185 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml @@ -19,7 +19,7 @@ </actionGroup> <!--Cancel order that is in processing status--> - <actionGroup name="cancelProcessingOrder" extends="cancelPendingOrder"> + <actionGroup name="CancelProcessingOrder" extends="cancelPendingOrder"> <remove keyForRemoval="seeOrderStatusCanceled"/> <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" after="seeCancelSuccessMessage" userInput="{{CONST.orderStatusComplete}}" stepKey="seeOrderStatusComplete"/> </actionGroup> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml index d99551de24db0..e5646e0a64621 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml @@ -23,7 +23,7 @@ <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToGridOrdersPage"/> <conditionalClick selector="{{AdminOrdersGridSection.clearFilters}}" dependentSelector="{{AdminOrdersGridSection.enabledFilters}}" visible="true" stepKey="clickOnButtonToRemoveFiltersIfPresent"/> </actionGroup> - <actionGroup name="openOrderById" extends="filterOrderGridById"> + <actionGroup name="OpenOrderById" extends="filterOrderGridById"> <click selector="{{AdminDataGridTableSection.firstRow}}" after="clickOrderApplyFilters" stepKey="openOrderViewPage"/> <waitForPageLoad after="openOrderViewPage" stepKey="waitForOrderViewPageOpened"/> </actionGroup> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceItemsSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceItemsSection.xml index 0ab46bfff5d25..93d1b32f58dc0 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceItemsSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceItemsSection.xml @@ -14,5 +14,6 @@ <element name="updateQty" type="button" selector=".order-invoice-tables tfoot button[data-ui-id='order-items-update-button']"/> <element name="updateQtyEnabled" type="button" selector=".order-invoice-tables tfoot button[data-ui-id='order-items-update-button'][class='action-default scalable update-button']"/> <element name="productColumn" type="text" selector="//*[contains(@class,'order-invoice-tables')]//td[@class = 'col-product']//div[contains(text(),'{{productName}}')]" parameterized="true"/> + <element name="itemQtyToInvoiceBySku" type="input" selector="//div[contains(@class,'product-sku-block') and contains(., '{{productSku}}')]/ancestor::tr//td[contains(@class,'col-qty-invoice')]//input" parameterized="true"/> </section> </sections> From 31232717292845d0cac6040d498db085c9d2752b Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Tue, 5 Feb 2019 16:50:00 +0200 Subject: [PATCH 0514/1295] MAGETWO-74043: [GITHUB] Magento 2.1 CE, Cannot change attribute set for bundled product #5999 --- .../Product/Form/Modifier/BundlePanel.php | 20 ++++++++++++++++--- .../Product/Form/Modifier/AdvancedPricing.php | 7 +++++-- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php index b265f6cb4c2b9..8526e9a9e5d5d 100644 --- a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php +++ b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php @@ -14,6 +14,7 @@ use Magento\Framework\UrlInterface; use Magento\Ui\Component\Container; use Magento\Ui\Component\Form; +use Magento\Ui\Component\Form\Fieldset; use Magento\Ui\Component\Modal; /** @@ -69,13 +70,26 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc + * * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function modifyMeta(array $meta) { $meta = $this->removeFixedTierPrice($meta); - $path = $this->arrayManager->findPath(static::CODE_BUNDLE_DATA, $meta, null, 'children'); + + $groupCode = static::CODE_BUNDLE_DATA; + $path = $this->arrayManager->findPath($groupCode, $meta, null, 'children'); + if (empty($path)) { + $meta[$groupCode]['children'] = []; + $meta[$groupCode]['arguments']['data']['config'] = [ + 'componentType' => Fieldset::NAME, + 'label' => __('Bundle Items'), + 'collapsible' => true + ]; + + $path = $this->arrayManager->findPath($groupCode, $meta, null, 'children'); + } $meta = $this->arrayManager->merge( $path, @@ -220,7 +234,7 @@ private function removeFixedTierPrice(array $meta) } /** - * {@inheritdoc} + * @inheritdoc */ public function modifyData(array $data) { diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php index 37b0b328a522b..e3da613cb1634 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php @@ -139,7 +139,8 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc + * * @since 101.0.0 */ public function modifyMeta(array $meta) @@ -158,7 +159,8 @@ public function modifyMeta(array $meta) } /** - * {@inheritdoc} + * @inheritdoc + * * @since 101.0.0 */ public function modifyData(array $data) @@ -381,6 +383,7 @@ private function addAdvancedPriceLink() ); $advancedPricingButton['arguments']['data']['config'] = [ + 'dataScope' => 'advanced_pricing_button', 'displayAsLink' => true, 'formElement' => Container::NAME, 'componentType' => Container::NAME, From 849b179ceda3b5fb01b15dc60001e5f51af2a45b Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 5 Feb 2019 17:27:49 +0200 Subject: [PATCH 0515/1295] MAGETWO-95734: MFTF tests fix --- .../Test/Mftf/ActionGroup/AdminEmailTemplateActionGroup.xml | 3 ++- .../Email/Test/Mftf/Section/AdminEmailTemplateEditSection.xml | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Email/Test/Mftf/ActionGroup/AdminEmailTemplateActionGroup.xml b/app/code/Magento/Email/Test/Mftf/ActionGroup/AdminEmailTemplateActionGroup.xml index 4019e0538dd06..b0899c6d5ce4d 100644 --- a/app/code/Magento/Email/Test/Mftf/ActionGroup/AdminEmailTemplateActionGroup.xml +++ b/app/code/Magento/Email/Test/Mftf/ActionGroup/AdminEmailTemplateActionGroup.xml @@ -35,7 +35,8 @@ <argument name="emailTemplate" defaultValue="EmailTemplate"/> </arguments> <seeInCurrentUrl url="email_template/edit/id" stepKey="seeCreatedTemplateUrl"/> - <click selector="{{AdminMainActionsSection.delete}}" stepKey="clickDeleteTemplateButton"/> + <!--Do not change it to use element from AdminMainActionsSection. Refer to AdminEmailTemplateEditSection comment --> + <click selector="{{AdminEmailTemplateEditSection.delete}}" stepKey="clickDeleteTemplateButton"/> <acceptPopup stepKey="acceptDeletingTemplatePopUp"/> <see userInput="You deleted the email template." stepKey="seeSuccessfulMessage"/> <click selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="clickResetFilterButton"/> diff --git a/app/code/Magento/Email/Test/Mftf/Section/AdminEmailTemplateEditSection.xml b/app/code/Magento/Email/Test/Mftf/Section/AdminEmailTemplateEditSection.xml index 9da02b64cb2e7..81c29b28a3fd6 100644 --- a/app/code/Magento/Email/Test/Mftf/Section/AdminEmailTemplateEditSection.xml +++ b/app/code/Magento/Email/Test/Mftf/Section/AdminEmailTemplateEditSection.xml @@ -14,5 +14,7 @@ <element name="templateNameField" type="input" selector="#template_code"/> <element name="templateSubjectField" type="input" selector="#template_subject"/> <element name="previewTemplateButton" type="button" selector="#preview"/> + <!--Do not add time to this element. It call alert when clicking, so adding time will brake test--> + <element name="delete" type="button" selector="#delete"/> </section> </sections> From 3eef2afc98729f364ea8b79d033543c87f53d15c Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko <iivashchenko@magento.com> Date: Tue, 5 Feb 2019 15:55:46 +0200 Subject: [PATCH 0516/1295] MAGETWO-97898: [Magento Cloud] Store switcher URL redirects to the same store --- .../Store/Model/StoreResolver/Website.php | 2 +- .../Store/Model/StoreResolver/WebsiteTest.php | 62 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 dev/tests/integration/testsuite/Magento/Store/Model/StoreResolver/WebsiteTest.php diff --git a/app/code/Magento/Store/Model/StoreResolver/Website.php b/app/code/Magento/Store/Model/StoreResolver/Website.php index 29f85716fea29..2a8b30a007ad9 100644 --- a/app/code/Magento/Store/Model/StoreResolver/Website.php +++ b/app/code/Magento/Store/Model/StoreResolver/Website.php @@ -45,7 +45,7 @@ public function getAllowedStoreIds($scopeCode) $stores = []; $website = $scopeCode ? $this->websiteRepository->get($scopeCode) : $this->websiteRepository->getDefault(); foreach ($this->storeRepository->getList() as $store) { - if ($store->isActive() && $store->getWebsiteId() == $website->getId()) { + if (!$scopeCode || ($store->isActive() && $store->getWebsiteId() === $website->getId())) { $stores[] = $store->getId(); } } diff --git a/dev/tests/integration/testsuite/Magento/Store/Model/StoreResolver/WebsiteTest.php b/dev/tests/integration/testsuite/Magento/Store/Model/StoreResolver/WebsiteTest.php new file mode 100644 index 0000000000000..cc10b91a031cc --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Store/Model/StoreResolver/WebsiteTest.php @@ -0,0 +1,62 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Store\Model\StoreResolver; + +use Magento\TestFramework\Helper\Bootstrap; + +class WebsiteTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var Website + */ + private $reader; + + protected function setUp() + { + $this->reader = Bootstrap::getObjectManager()->create(Website::class); + } + + /** + * Tests retrieving of stores id by passed scope. + * + * @param string|null $scopeCode website code + * @param int $storesCount + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @magentoDbIsolation disabled + * @magentoAppIsolation enabled + * @dataProvider scopeDataProvider + */ + public function testGetAllowedStoreIds($scopeCode, $storesCount) + { + $this->assertCount($storesCount, $this->reader->getAllowedStoreIds($scopeCode)); + } + + /** + * Provides scopes and corresponding count of resolved stores. + * + * @return array + */ + public function scopeDataProvider(): array + { + return [ + [null, 4], + ['test', 2] + ]; + } + + /** + * Tests retrieving of stores id by passing incorrect scope. + * + * @expectedException \Magento\Framework\Exception\NoSuchEntityException + * @expectedExceptionMessage The website with code not_exists that was requested wasn't found. + */ + public function testIncorrectScope() + { + $this->reader->getAllowedStoreIds('not_exists'); + } +} From d70186b5835f4839413c283a5f319618998b762e Mon Sep 17 00:00:00 2001 From: Serhiy Zhovnir <s.zhovnir@atwix.com> Date: Tue, 5 Feb 2019 21:14:09 +0200 Subject: [PATCH 0517/1295] #18698 Fix PHPUnit test --- .../Unit/Model/Order/Email/Sender/OrderSenderTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/OrderSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/OrderSenderTest.php index 46c44c03b1514..88053ea684ce8 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/OrderSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/OrderSenderTest.php @@ -64,7 +64,7 @@ public function testSend($configValue, $forceSyncMode, $emailSendingResult, $sen $this->orderMock->expects($this->once()) ->method('setSendEmail') - ->with(true); + ->with($emailSendingResult); $this->globalConfig->expects($this->once()) ->method('getValue') @@ -72,7 +72,7 @@ public function testSend($configValue, $forceSyncMode, $emailSendingResult, $sen ->willReturn($configValue); if (!$configValue || $forceSyncMode) { - $this->identityContainerMock->expects($this->once()) + $this->identityContainerMock->expects($this->exactly(2)) ->method('isEnabled') ->willReturn($emailSendingResult); @@ -118,7 +118,7 @@ public function testSend($configValue, $forceSyncMode, $emailSendingResult, $sen $this->orderMock->expects($this->once()) ->method('setEmailSent') - ->with(true); + ->with($emailSendingResult); $this->orderResourceMock->expects($this->once()) ->method('saveAttribute') @@ -210,7 +210,7 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte ->with('sales_email/general/async_sending') ->willReturn(false); - $this->identityContainerMock->expects($this->once()) + $this->identityContainerMock->expects($this->exactly(2)) ->method('isEnabled') ->willReturn(true); From afe6be3b04702e08a0ff30d38539f20194579558 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Wed, 6 Feb 2019 08:45:54 +0200 Subject: [PATCH 0518/1295] MAGETWO-95626: Log of actions in the Admin panel --- .../ActionGroup/AdminIndexerActionGroup.xml | 23 +++++++++++++++++++ .../Mftf/Page/AdminIndexManagementPage.xml | 13 +++++++++++ .../Section/AdminIndexManagementSection.xml | 16 +++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 app/code/Magento/Indexer/Test/Mftf/ActionGroup/AdminIndexerActionGroup.xml create mode 100644 app/code/Magento/Indexer/Test/Mftf/Page/AdminIndexManagementPage.xml create mode 100644 app/code/Magento/Indexer/Test/Mftf/Section/AdminIndexManagementSection.xml diff --git a/app/code/Magento/Indexer/Test/Mftf/ActionGroup/AdminIndexerActionGroup.xml b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/AdminIndexerActionGroup.xml new file mode 100644 index 0000000000000..7c9c8c58d44b6 --- /dev/null +++ b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/AdminIndexerActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="UpdateIndexerMode"> + <arguments> + <argument name="indexerName" type="string" defaultValue="catalogsearch_fulltext"/> + <argument name="indexerMode" type="string" defaultValue="Update on Save"/> + </arguments> + <amOnPage url="{{AdminIndexManagementPage.url}}" stepKey="amOnIndexManagementPage"/> + <click selector="{{AdminIndexManagementSection.indexerCheckbox(indexerName)}}" stepKey="selectIndexer"/> + <selectOption selector="{{AdminIndexManagementSection.massActionSelect}}" userInput="{{indexerMode}}" stepKey="selectIndexerMode"/> + <click selector="{{AdminIndexManagementSection.massActionSubmit}}" stepKey="submitIndexerForm"/> + <waitForPageLoad stepKey="waitForIndexerUpdate"/> + <see selector="{{AdminMessagesSection.success}}" userInput='1 indexer(s) are in "{{indexerMode}}" mode.' stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Indexer/Test/Mftf/Page/AdminIndexManagementPage.xml b/app/code/Magento/Indexer/Test/Mftf/Page/AdminIndexManagementPage.xml new file mode 100644 index 0000000000000..504608d0721fe --- /dev/null +++ b/app/code/Magento/Indexer/Test/Mftf/Page/AdminIndexManagementPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminIndexManagementPage" url="indexer/indexer/list" module="Magento_Indexer" area="admin"> + <section name="AdminIndexManagementSection"/> + </page> +</pages> diff --git a/app/code/Magento/Indexer/Test/Mftf/Section/AdminIndexManagementSection.xml b/app/code/Magento/Indexer/Test/Mftf/Section/AdminIndexManagementSection.xml new file mode 100644 index 0000000000000..f72bffd9cf96d --- /dev/null +++ b/app/code/Magento/Indexer/Test/Mftf/Section/AdminIndexManagementSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminIndexManagementSection"> + <element name="indexerCheckbox" type="checkbox" selector="input[value='{{indexerName}}']" parameterized="true"/> + <element name="massActionSelect" type="select" selector="#gridIndexer_massaction-select"/> + <element name="massActionSubmit" type="button" selector="#gridIndexer_massaction-form button"/> + </section> +</sections> From 02c7c52ae3da52ee0645eb3f7b8ec9c9c9859612 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Wed, 6 Feb 2019 10:51:01 +0200 Subject: [PATCH 0519/1295] MAGETWO-88612: Exception when login as restricted admin with access only to CMS Block --- ...inRestrictedUserOnlyAccessCmsBlockTest.xml | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 app/code/Magento/Cms/Test/Mftf/Test/AdminRestrictedUserOnlyAccessCmsBlockTest.xml diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminRestrictedUserOnlyAccessCmsBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminRestrictedUserOnlyAccessCmsBlockTest.xml new file mode 100644 index 0000000000000..0e285555f8698 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminRestrictedUserOnlyAccessCmsBlockTest.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminRestrictedUserOnlyAccessCmsBlockTest"> + <annotations> + <features value="Cms"/> + <stories value="Check access for restricted admin user"/> + <title value="Check: restricted admin with access only to CMS Block"/> + <description value="Check that the system shows information only in Blocks"/> + <severity value="MAJOR"/> + <testCaseId value="MC-13814"/> + <useCaseId value="MAGETWO-88612"/> + <group value="Cms"/> + </annotations> + <before> + <createData entity="restrictedWebUser" stepKey="createRestrictedAdmin"/> + <actionGroup ref="LoginToAdminActionGroup" stepKey="loginToBackend"/> + <actionGroup ref="AdminCreateUserRoleActionGroup" stepKey="createRestrictedAdminRole"> + <argument name="roleName" value="{{RoleTest.roleName}}"/> + <argument name="resourceAccess" value="Custom"/> + <argument name="resource" value="Magento_Cms::block"/> + </actionGroup> + <actionGroup ref="AdminAssignUserRoleActionGroup" stepKey="assignAdminRole"> + <argument name="user_restricted" value="$$createRestrictedAdmin$$"/> + <argument name="roleName" value="{{RoleTest.roleName}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logOut"/> + </before> + <after> + <actionGroup ref="LoginActionGroup" stepKey="loginAsAdminWithAllAccess"/> + <actionGroup ref="AdminDeleteUserRoleActionGroup" stepKey="deleteRestrictedRole"> + <argument name="roleName" value="{{RoleTest.roleName}}"/> + </actionGroup> + <actionGroup ref="AdminDeleteUserActionGroup" stepKey="deleteRestrictedUser"> + <argument name="user_restricted" value="$$createRestrictedAdmin$$"/> + </actionGroup> + <!--Log Out--> + <actionGroup ref="logout" stepKey="logOut"/> + </after> + + <!--login as restricted user--> + <amOnPage stepKey="amOnAdminLoginPage" url="{{AdminLoginPage.url}}"/> + <fillField selector="{{AdminLoginFormSection.username}}" userInput="$$createRestrictedAdmin.username$$" stepKey="fillUsername" /> + <fillField selector="{{AdminLoginFormSection.password}}" userInput="$$createRestrictedAdmin.password$$" stepKey="fillPassword"/> + <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickOnSignIn2" /> + <!--Verify that The system shows information included in "Blocks"--> + <see stepKey="seeBlocksPage" userInput="Blocks"/> + <seeInCurrentUrl url="{{AdminCmsBlockGridPage.url}}" stepKey="assertUrl"/> + <!--Log Out--> + <actionGroup ref="logout" stepKey="logOut"/> + </test> +</tests> From b8a810d98b52f45fbfddc05c97b2d3340f802d41 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Wed, 6 Feb 2019 10:55:57 +0200 Subject: [PATCH 0520/1295] MAGETWO-93221: Payment method title does not update in the admin sales order grid --- app/code/Magento/Payment/Helper/Data.php | 4 ++-- app/code/Magento/Payment/Test/Unit/Helper/DataTest.php | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Payment/Helper/Data.php b/app/code/Magento/Payment/Helper/Data.php index 0afdb3b82bd41..97dac29b4918b 100644 --- a/app/code/Magento/Payment/Helper/Data.php +++ b/app/code/Magento/Payment/Helper/Data.php @@ -269,9 +269,9 @@ public function getPaymentMethodList($sorted = true, $asLabelValue = false, $wit foreach ($this->getPaymentMethods() as $code => $data) { if (!empty($data['active'])) { $storedTitle = $this->getMethodInstance($code)->getConfigData('title', $store); - if (isset($storedTitle)) { + if (!empty($storedTitle)) { $methods[$code] = $storedTitle; - } elseif (isset($data['title'])) { + } elseif (!empty($data['title'])) { $methods[$code] = $data['title']; } } diff --git a/app/code/Magento/Payment/Test/Unit/Helper/DataTest.php b/app/code/Magento/Payment/Test/Unit/Helper/DataTest.php index 5f1c89e61f6c7..1df07f87a3054 100644 --- a/app/code/Magento/Payment/Test/Unit/Helper/DataTest.php +++ b/app/code/Magento/Payment/Test/Unit/Helper/DataTest.php @@ -284,7 +284,7 @@ public function testGetPaymentMethodList( [ Data::XML_PATH_PAYMENT_METHODS => [ $paymentMethod['code'] => $paymentMethod['data'], - ] + ], ] ); @@ -327,7 +327,7 @@ public function getSortMethodsDataProvider() /** * @return array */ - public function paymentMethodListDataProvider() + public function paymentMethodListDataProvider(): array { return [ 'Payment method with changed title' => @@ -343,7 +343,7 @@ public function paymentMethodListDataProvider() 'title' => 'Payment Title', ], ], - ['payment_method' => 'Config Payment Title'] + ['payment_method' => 'Config Payment Title'], ], 'Payment method with default title' => [ From 0453a65c782064523cc8fea97e3e44ef2f7706a8 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Wed, 6 Feb 2019 11:17:52 +0200 Subject: [PATCH 0521/1295] MAGETWO-98107: [FT] [MFTF] AdminCartRulesAppliedForProductInCartTest fails because of bad design --- .../Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml | 2 +- .../Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index 3ed09fdf55ed5..d64dfb928e651 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -16,7 +16,7 @@ <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductToggle"/> <waitForElementVisible selector="{{AdminProductGridActionSection.addTypeProduct(product.type_id)}}" stepKey="waitForAddProductDropdown" time="30"/> <click selector="{{AdminProductGridActionSection.addTypeProduct(product.type_id)}}" stepKey="clickAddProductType"/> - <waitForPageLoad stepKey="waitForCreateProductPageLoad"/> + <waitForPageLoad time="30" stepKey="waitForCreateProductPageLoad"/> <seeInCurrentUrl url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId, product.type_id)}}" stepKey="seeNewProductUrl"/> <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Product" stepKey="seeNewProductTitle"/> </actionGroup> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml index 7da6ca3fbe072..327a126b8dc78 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml @@ -56,7 +56,8 @@ </actionGroup> <!--Off dynamic price and set value--> - <click selector="{{AdminProductFormBundleSection.priceTypeSwitcher}}" stepKey="offDynamicPrice"/> + <scrollToTopOfPage stepKey="scrollToTopOfThePageToSeePriceTypeElement"/> + <click selector="{{AdminProductFormBundleSection.priceTypeSwitcher}}" stepKey="offDynamicPrice"/> <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="0" stepKey="setProductPrice"/> <!-- Add option, a "Radio Buttons" type option, with one product and set fixed price 200--> From 80b13234373cd2115cbbd504547b93b1334a7ed3 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Wed, 6 Feb 2019 12:09:19 +0200 Subject: [PATCH 0522/1295] MAGETWO-96061: App:config:dump doesn't lock all the settings in Magento backend --- .../Framework/App/DeploymentConfig.php | 2 +- .../App/Test/Unit/DeploymentConfigTest.php | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/App/DeploymentConfig.php b/lib/internal/Magento/Framework/App/DeploymentConfig.php index f7acd801dbed6..beb4f98ae76bd 100644 --- a/lib/internal/Magento/Framework/App/DeploymentConfig.php +++ b/lib/internal/Magento/Framework/App/DeploymentConfig.php @@ -71,7 +71,7 @@ public function get($key = null, $defaultValue = null) return $this->flatData; } - if (isset($this->flatData[$key]) && $this->flatData[$key] === null) { + if (array_key_exists($key, $this->flatData) && $this->flatData[$key] === null) { return ''; } diff --git a/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfigTest.php b/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfigTest.php index 80ab2302dc91c..503ed9dfdd064 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfigTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfigTest.php @@ -140,4 +140,43 @@ public function keyCollisionDataProvider() ] ]; } + + /** + * @param string $key + * @param string $expectedFlattenData + * @return void + * @dataProvider getDataProvider + */ + public function testGet(string $key, string $expectedFlattenData) + { + $flatData = [ + 'key1' => 'value', + 'key2' => null, + ]; + + $this->reader->expects($this->once())->method('load')->willReturn($flatData); + + $this->assertEquals($expectedFlattenData, $this->_deploymentConfig->get($key)); + } + + /** + * @return array + */ + public function getDataProvider(): array + { + return [ + [ + 'key' => 'key1', + 'expectedFlattenData' => 'value', + ], + [ + 'key' => 'key2', + 'expectedFlattenData' => '', + ], + [ + 'key' => 'key3', + 'expectedFlattenData' => '', + ], + ]; + } } From 7e403f65e4e29a96a8c6fc5f13a605e7396f282d Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Wed, 6 Feb 2019 12:34:33 +0200 Subject: [PATCH 0523/1295] MAGETWO-96061: App:config:dump doesn't lock all the settings in Magento backend --- .../Magento/Framework/App/Test/Unit/DeploymentConfigTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfigTest.php b/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfigTest.php index 503ed9dfdd064..c6c90c1fea541 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfigTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfigTest.php @@ -175,7 +175,7 @@ public function getDataProvider(): array ], [ 'key' => 'key3', - 'expectedFlattenData' => '', + 'expectedFlattenData' => null, ], ]; } From 1e0fa4d299a160260e9a7162e3f950277d16ecbd Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Wed, 6 Feb 2019 12:46:30 +0200 Subject: [PATCH 0524/1295] MAGETWO-97947: Invalid action behavior --- .../Block/Grid/Renderer/Actions.php | 3 +- .../Backend/Block/System/Design/Edit.php | 2 +- .../Backend/Block/Widget/Form/Container.php | 3 +- .../Adminhtml/System/Design/Delete.php | 5 + .../Captcha/Controller/Refresh/Index.php | 9 +- .../Unit/Controller/Refresh/IndexTest.php | 1 + .../Adminhtml/Category/Edit/DeleteButton.php | 3 +- .../Adminhtml/Product/Attribute/Set/Main.php | 2 +- .../Adminhtml/Category/CategoriesJson.php | 5 + .../Controller/Adminhtml/Category/Delete.php | 5 + .../Controller/Adminhtml/Category/Move.php | 15 +-- .../Product/Action/Attribute/Save.php | 6 ++ .../Adminhtml/Product/Attribute/Save.php | 1 + .../Adminhtml/Product/MassStatus.php | 6 ++ .../Adminhtml/Product/Set/Delete.php | 10 +- .../Controller/Product/Compare/Add.php | 10 +- .../Controller/Product/Compare/Clear.php | 47 +++++---- .../Controller/Product/Compare/Remove.php | 10 +- .../Adminhtml/Category/DeleteTest.php | 3 +- .../Adminhtml/Category/MoveTest.php | 3 +- .../Product/Action/Attribute/SaveTest.php | 2 + .../Adminhtml/Product/MassStatusTest.php | 4 + .../Unit/Controller/Adminhtml/ProductTest.php | 2 +- .../Unit/Cron/FrontendActionsFlushTest.php | 7 +- .../Block/Adminhtml/Edit/DeleteButton.php | 2 +- .../Adminhtml/Promo/Catalog/Delete.php | 5 + .../Block/Adminhtml/Edit/DeleteButtonTest.php | 2 +- .../Magento/Checkout/Controller/Cart/Add.php | 6 +- .../Checkout/Controller/Cart/CouponPost.php | 19 ++-- .../Checkout/Controller/Cart/Delete.php | 2 +- .../Controller/Cart/UpdateItemOptions.php | 5 + .../Checkout/Controller/Cart/UpdatePost.php | 2 +- .../Controller/Sidebar/RemoveItem.php | 2 +- .../Test/Unit/Controller/Cart/AddTest.php | 5 +- .../Unit/Controller/Cart/CouponPostTest.php | 84 +++++++--------- .../Controller/Sidebar/RemoveItemTest.php | 6 +- .../Controller/Adminhtml/Agreement/Delete.php | 6 ++ .../Adminhtml/Block/Edit/DeleteButton.php | 2 +- .../Adminhtml/Page/Edit/DeleteButton.php | 2 +- .../Cms/Controller/Adminhtml/Block/Delete.php | 5 + .../Controller/Adminhtml/Block/MassDelete.php | 6 +- .../Cms/Controller/Adminhtml/Page/Delete.php | 6 +- .../Controller/Adminhtml/Page/MassDelete.php | 6 +- .../Controller/Adminhtml/Page/MassDisable.php | 6 +- .../Controller/Adminhtml/Page/MassEnable.php | 6 +- .../Cms/Controller/Adminhtml/Page/Save.php | 4 +- .../Adminhtml/Wysiwyg/Images/DeleteFolder.php | 9 +- .../Adminhtml/Wysiwyg/Images/NewFolder.php | 10 +- .../Adminhtml/Wysiwyg/Images/Upload.php | 4 + .../Controller/Adminhtml/Block/DeleteTest.php | 4 +- .../Adminhtml/Block/MassDeleteTest.php | 6 +- .../Controller/Adminhtml/Page/DeleteTest.php | 4 +- .../Adminhtml/Page/MassDeleteTest.php | 6 +- .../Adminhtml/Page/MassDisableTest.php | 4 + .../Adminhtml/Page/MassEnableTest.php | 4 + .../Listing/Column/BlockActionsTest.php | 1 + .../Listing/Column/PageActionsTest.php | 1 + .../Component/Listing/Column/BlockActions.php | 4 +- .../Component/Listing/Column/PageActions.php | 4 +- .../Adminhtml/System/Config/Save.php | 6 ++ .../Adminhtml/System/Config/SaveTest.php | 1 + .../Product/Attribute/CreateOptions.php | 6 +- .../Adminhtml/System/Currency/SaveRates.php | 7 ++ .../Adminhtml/System/Currencysymbol/Save.php | 11 ++- .../System/Currencysymbol/SaveTest.php | 11 ++- .../Magento/Customer/Block/Adminhtml/Edit.php | 2 +- .../Adminhtml/Edit/InvalidateTokenButton.php | 3 +- .../Customer/Controller/Address/Delete.php | 13 ++- .../Customer/Controller/Address/Index.php | 2 +- .../Controller/Adminhtml/Group/Save.php | 6 ++ .../Controller/Adminhtml/Index/InlineEdit.php | 4 +- .../Adminhtml/Index/ResetPassword.php | 8 +- .../Unit/Controller/Address/DeleteTest.php | 8 +- .../Controller/Adminhtml/Group/SaveTest.php | 2 + .../Adminhtml/Index/InlineEditTest.php | 5 +- .../Adminhtml/Index/ResetPasswordTest.php | 7 +- .../Adminhtml/Email/Template/Delete.php | 5 + .../adminhtml/templates/template/edit.phtml | 8 +- .../Controller/Adminhtml/Import/Validate.php | 7 +- .../Adminhtml/Indexer/MassChangelog.php | 15 ++- .../Adminhtml/Indexer/MassOnTheFly.php | 7 ++ .../Adminhtml/Indexer/MassChangelogTest.php | 18 ++-- .../Adminhtml/Indexer/MassOnTheFlyTest.php | 10 +- .../Adminhtml/Integration/Delete.php | 6 ++ .../Controller/Adminhtml/Integration/Save.php | 7 ++ .../Adminhtml/Integration/DeleteTest.php | 1 + .../Adminhtml/Integration/SaveTest.php | 9 ++ .../integration/popup_container.phtml | 7 +- .../Controller/Adminhtml/Queue/Save.php | 15 ++- .../Adminhtml/Subscriber/MassDelete.php | 18 +++- .../Adminhtml/Subscriber/MassUnsubscribe.php | 6 ++ .../Controller/Adminhtml/Template/Delete.php | 6 +- .../Controller/Adminhtml/Template/Drop.php | 7 ++ .../adminhtml/templates/template/edit.phtml | 8 +- .../Transparent/RequestSecureToken.php | 2 +- .../Transparent/RequestSecureTokenTest.php | 6 ++ .../Product/Gallery/RetrieveImage.php | 3 + .../Product/Gallery/RetrieveImageTest.php | 9 +- .../Adminhtml/Report/AbstractReport.php | 10 +- .../Report/Statistics/RefreshRecent.php | 7 ++ .../Magento/Review/Block/Adminhtml/Edit.php | 3 +- .../Controller/Adminhtml/Product/Delete.php | 27 +++--- .../Adminhtml/Product/MassDelete.php | 5 + .../Adminhtml/Product/MassUpdateStatus.php | 5 + .../Adminhtml/Product/MassVisibleIn.php | 5 + .../Controller/Adminhtml/Rating/Delete.php | 15 ++- .../Review/Controller/Product/Post.php | 2 +- .../Test/Unit/Controller/Product/PostTest.php | 15 +-- .../Sales/Block/Adminhtml/Order/Create.php | 2 +- .../Block/Status/Grid/Column/Unassign.php | 11 ++- .../Controller/Adminhtml/Order/Cancel.php | 8 +- .../Adminhtml/Order/Create/Save.php | 7 +- .../Adminhtml/Order/Creditmemo/Save.php | 13 ++- .../Adminhtml/Order/Creditmemo/UpdateQty.php | 4 + .../Adminhtml/Order/Invoice/UpdateQty.php | 15 ++- .../Adminhtml/Order/Status/AssignPost.php | 8 +- .../Adminhtml/Order/Status/Save.php | 13 ++- .../Adminhtml/Order/Status/Unassign.php | 15 ++- .../Controller/Adminhtml/Order/CancelTest.php | 12 ++- .../Adminhtml/Order/Creditmemo/SaveTest.php | 3 +- .../Order/Creditmemo/UpdateQtyTest.php | 1 + .../Adminhtml/Order/Invoice/UpdateQtyTest.php | 17 ++-- .../Promo/Quote/Edit/DeleteButton.php | 2 +- .../Promo/Quote/CouponsMassDelete.php | 5 + .../Adminhtml/Promo/Quote/Delete.php | 17 +++- .../Promo/Quote/Edit/DeleteButtonTest.php | 2 +- .../Adminhtml/Synonyms/Edit/DeleteButton.php | 2 +- .../Controller/Adminhtml/Synonyms/Delete.php | 2 +- .../Controller/Adminhtml/Term/Delete.php | 8 +- .../Controller/Adminhtml/Term/MassDelete.php | 6 ++ .../Adminhtml/Synonyms/DeleteTest.php | 3 +- .../Adminhtml/Term/MassDeleteTest.php | 3 +- .../Listing/Column/SynonymActions.php | 3 +- .../Magento/Shipping/Block/Adminhtml/View.php | 2 +- .../Adminhtml/Order/Shipment/Save.php | 12 +-- .../Adminhtml/Order/Shipment/SaveTest.php | 4 +- .../Tax/Block/Adminhtml/Rate/Toolbar/Save.php | 2 +- .../Tax/Controller/Adminhtml/Rule/Delete.php | 14 ++- .../Adminhtml/System/Design/Theme/Edit.php | 2 +- .../Ui/Controller/Adminhtml/Bookmark/Save.php | 6 ++ .../view/base/web/js/grid/columns/actions.js | 14 ++- app/code/Magento/UrlRewrite/Block/Edit.php | 4 +- .../Adminhtml/Url/Rewrite/Delete.php | 14 ++- app/code/Magento/User/Block/Buttons.php | 4 +- app/code/Magento/User/Block/User/Edit.php | 2 +- .../Adminhtml/User/Role/SaveRole.php | 7 +- .../User/Controller/Adminhtml/User/Save.php | 3 + .../Adminhtml/Widget/BuildWidget.php | 11 ++- .../Controller/Adminhtml/Widget/Index.php | 8 +- .../Adminhtml/Widget/Instance/Save.php | 2 +- .../TestCase/AbstractBackendController.php | 31 +++++- .../TestCase/AbstractController.php | 36 +++++-- .../Controller/Adminhtml/IndexTest.php | 3 + .../Controller/Adminhtml/UrlRewriteTest.php | 3 + .../Controller/Adminhtml/CategoryTest.php | 14 ++- .../Product/Action/AttributeTest.php | 4 + .../Adminhtml/Product/AttributeTest.php | 94 +++++------------- .../Adminhtml/Product/Set/DeleteTest.php | 3 +- .../Adminhtml/Product/Set/SaveTest.php | 2 + .../Controller/Adminhtml/ProductTest.php | 8 +- .../Controller/Product/CompareTest.php | 20 +++- .../Controller/Cart/Index/CouponPostTest.php | 5 +- .../Magento/Checkout/Controller/CartTest.php | 8 +- .../Adminhtml/Wysiwyg/Images/UploadTest.php | 1 + .../Adminhtml/System/ConfigTest.php | 31 +++--- .../Controller/Adminhtml/ProductTest.php | 2 + .../Controller/CartTest.php | 5 +- .../System/Currency/SaveRatesTest.php | 4 + .../System/Currencysymbol/SaveTest.php | 9 +- .../Customer/Controller/AccountTest.php | 97 ++++++++++--------- .../Customer/Controller/AddressTest.php | 10 +- .../Controller/Adminhtml/GroupTest.php | 78 ++++++++------- .../Adminhtml/Index/MassAssignGroupTest.php | 19 ++-- .../Adminhtml/Index/MassDeleteTest.php | 10 +- .../Adminhtml/Index/ResetPasswordTest.php | 10 +- .../Controller/Adminhtml/IndexTest.php | 79 +++++---------- .../Controller/Adminhtml/IntegrationTest.php | 21 ++++ .../Adminhtml/NewsletterQueueTest.php | 9 ++ .../Adminhtml/NewsletterTemplateTest.php | 6 ++ .../Adminhtml/Order/Create/SaveTest.php | 1 + .../Controller/Adminhtml/Order/CreateTest.php | 8 ++ .../System/Design/Config/SaveTest.php | 6 ++ .../User/Controller/Adminhtml/UserTest.php | 7 ++ lib/web/mage/adminhtml/tools.js | 2 +- setup/performance-toolkit/benchmark.jmx | 12 ++- 185 files changed, 1163 insertions(+), 578 deletions(-) diff --git a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php index 0f80b0e94e347..e515fb3ccae6c 100644 --- a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php +++ b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php @@ -55,7 +55,8 @@ public function render(\Magento\Framework\DataObject $row) $encodedUrl = $this->_urlHelper->getEncodedUrl(); return sprintf( - '%s%s<a class="action-delete" href="%s" onClick="deleteConfirm(\'%s\', this.href); return false;">%s</a>', + '%s%s<a class="action-delete" href="%s" onclick="deleteConfirm(\'%s\', this.href, {data: {}});' . + ' return false;">%s</a>', $readDetailsHtml, $markAsReadHtml, $this->getUrl( diff --git a/app/code/Magento/Backend/Block/System/Design/Edit.php b/app/code/Magento/Backend/Block/System/Design/Edit.php index 4d6c26e4cfe4b..d2d3035f62e3d 100644 --- a/app/code/Magento/Backend/Block/System/Design/Edit.php +++ b/app/code/Magento/Backend/Block/System/Design/Edit.php @@ -66,7 +66,7 @@ protected function _prepareLayout() 'label' => __('Delete'), 'onclick' => 'deleteConfirm(\'' . __( 'Are you sure?' - ) . '\', \'' . $this->getDeleteUrl() . '\')', + ) . '\', \'' . $this->getDeleteUrl() . '\', {data: {}})', 'class' => 'delete' ] ); diff --git a/app/code/Magento/Backend/Block/Widget/Form/Container.php b/app/code/Magento/Backend/Block/Widget/Form/Container.php index 7c6e8ec5a9b73..d7bd785367347 100644 --- a/app/code/Magento/Backend/Block/Widget/Form/Container.php +++ b/app/code/Magento/Backend/Block/Widget/Form/Container.php @@ -57,6 +57,7 @@ class Container extends \Magento\Backend\Block\Widget\Container /** * @return void + * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) */ protected function _construct() { @@ -93,7 +94,7 @@ protected function _construct() 'class' => 'delete', 'onclick' => 'deleteConfirm(\'' . __( 'Are you sure you want to do this?' - ) . '\', \'' . $this->getDeleteUrl() . '\')' + ) . '\', \'' . $this->getDeleteUrl() . '\', {data: {}})' ] ); } 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 21f28188cf874..6ccd7efff4d36 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Design/Delete.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Design/Delete.php @@ -10,9 +10,14 @@ class Delete extends \Magento\Backend\Controller\Adminhtml\System\Design { /** * @return \Magento\Backend\Model\View\Result\Redirect + * @throws \Magento\Framework\Exception\NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new \Magento\Framework\Exception\NotFoundException(__('Page not found.')); + } + $id = $this->getRequest()->getParam('id'); if ($id) { $design = $this->_objectManager->create(\Magento\Framework\App\DesignInterface::class)->load($id); diff --git a/app/code/Magento/Captcha/Controller/Refresh/Index.php b/app/code/Magento/Captcha/Controller/Refresh/Index.php index e89a80646ed8e..3f831606570ca 100644 --- a/app/code/Magento/Captcha/Controller/Refresh/Index.php +++ b/app/code/Magento/Captcha/Controller/Refresh/Index.php @@ -40,10 +40,15 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc + * @throws \Magento\Framework\Exception\NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new \Magento\Framework\Exception\NotFoundException(__('Page not found.')); + } + $formId = $this->_request->getPost('formId'); if (null === $formId) { $params = []; @@ -51,7 +56,7 @@ public function execute() if ($content) { $params = $this->serializer->unserialize($content); } - $formId = isset($params['formId']) ? $params['formId'] : null; + $formId = $params['formId'] ?? null; } $captchaModel = $this->captchaHelper->getCaptcha($formId); $captchaModel->generate(); diff --git a/app/code/Magento/Captcha/Test/Unit/Controller/Refresh/IndexTest.php b/app/code/Magento/Captcha/Test/Unit/Controller/Refresh/IndexTest.php index 99ac2e2d8fccc..ee97c11a58315 100644 --- a/app/code/Magento/Captcha/Test/Unit/Controller/Refresh/IndexTest.php +++ b/app/code/Magento/Captcha/Test/Unit/Controller/Refresh/IndexTest.php @@ -95,6 +95,7 @@ public function testExecute($formId, $callsNumber) $blockMethods = ['setFormId', 'setIsAjax', 'toHtml']; $blockMock = $this->createPartialMock(\Magento\Captcha\Block\Captcha::class, $blockMethods); + $this->requestMock->expects($this->once())->method('isPost')->willReturn(true); $this->requestMock->expects($this->any())->method('getPost')->with('formId')->will($this->returnValue($formId)); $this->requestMock->expects($this->exactly($callsNumber))->method('getContent') ->will($this->returnValue(json_encode($content))); diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Category/Edit/DeleteButton.php b/app/code/Magento/Catalog/Block/Adminhtml/Category/Edit/DeleteButton.php index 20411a4c4d767..2eef1188e3910 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Category/Edit/DeleteButton.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Category/Edit/DeleteButton.php @@ -27,7 +27,8 @@ public function getButtonData() return [ 'id' => 'delete', 'label' => __('Delete'), - 'on_click' => "categoryDelete('" . $this->getDeleteUrl() . "')", + 'on_click' => "deleteConfirm('" .__('Are you sure you want to delete this category?') ."', '" + . $this->getDeleteUrl() . "', {data: {}})", 'class' => 'delete', 'sort_order' => 10 ]; diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Set/Main.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Set/Main.php index 1b188de40710f..2cd27f2785af2 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Set/Main.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Set/Main.php @@ -140,7 +140,7 @@ protected function _prepareLayout() ) . '\', \'' . $this->getUrl( 'catalog/*/delete', ['id' => $setId] - ) . '\')', + ) . '\', {data: {}})', 'class' => 'delete' ] ); diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/CategoriesJson.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/CategoriesJson.php index b8865f2de8d1e..3969d8193817b 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/CategoriesJson.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/CategoriesJson.php @@ -37,9 +37,14 @@ public function __construct( * Get tree node (Ajax version) * * @return \Magento\Framework\Controller\ResultInterface + * @throws \Magento\Framework\Exception\NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new \Magento\Framework\Exception\NotFoundException(__('Page not found.')); + } + if ($this->getRequest()->getParam('expand_all')) { $this->_objectManager->get(\Magento\Backend\Model\Auth\Session::class)->setIsTreeWasExpanded(true); } else { diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Delete.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Delete.php index 0a54475b15f9c..4f14fb58487c1 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Delete.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Delete.php @@ -29,9 +29,14 @@ public function __construct( * Delete category action * * @return \Magento\Backend\Model\View\Result\Redirect + * @throws \Magento\Framework\Exception\NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new \Magento\Framework\Exception\NotFoundException(__('Page not found.')); + } + /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Move.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Move.php index df2c80eda141c..02ddb162aff3a 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Move.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Move.php @@ -26,7 +26,7 @@ class Move extends \Magento\Catalog\Controller\Adminhtml\Category /** * @param \Magento\Backend\App\Action\Context $context * @param \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory - * @param \Magento\Framework\View\LayoutFactory $layoutFactory, + * @param \Magento\Framework\View\LayoutFactory $layoutFactory * @param \Psr\Log\LoggerInterface $logger */ public function __construct( @@ -45,16 +45,17 @@ public function __construct( * Move category action * * @return \Magento\Framework\Controller\Result\Raw + * @throws \Magento\Framework\Exception\NotFoundException */ public function execute() { - /** - * New parent category identifier - */ + if (!$this->getRequest()->isPost()) { + throw new \Magento\Framework\Exception\NotFoundException(__('Page not found.')); + } + + /** New parent category identifier */ $parentNodeId = $this->getRequest()->getPost('pid', false); - /** - * Category id after which we have put our category - */ + /** Category id after which we have put our category */ $prevNodeId = $this->getRequest()->getPost('aid', false); /** @var $block \Magento\Framework\View\Element\Messages */ diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php index 0fbf9054ef1bd..492288308a95c 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php @@ -7,6 +7,7 @@ namespace Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute; use Magento\Backend\App\Action; +use Magento\Framework\Exception\NotFoundException; /** * Class Save @@ -81,12 +82,17 @@ public function __construct( * Update product attributes * * @return \Magento\Backend\Model\View\Result\Redirect + * @throws NotFoundException * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + if (!$this->_validateProducts()) { return $this->resultRedirectFactory->create()->setPath('catalog/product/', ['_current' => true]); } diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php index e2aa7758ebf21..da17ad3bb80b2 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php @@ -118,6 +118,7 @@ public function __construct( /** * @inheritdoc + * @throws NotFoundException * * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassStatus.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassStatus.php index b6e7e31fb9efd..02b0025a7922a 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassStatus.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/MassStatus.php @@ -9,6 +9,7 @@ use Magento\Backend\App\Action; use Magento\Catalog\Controller\Adminhtml\Product; use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\Exception\NotFoundException; use Magento\Ui\Component\MassAction\Filter; use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; @@ -77,9 +78,14 @@ public function _validateMassStatus(array $productIds, $status) * Update product(s) status action * * @return \Magento\Backend\Model\View\Result\Redirect + * @throws NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + $collection = $this->filter->getCollection($this->collectionFactory->create()); $productIds = $collection->getAllIds(); $storeId = (int) $this->getRequest()->getParam('store', 0); diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Delete.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Delete.php index f2695311732f0..09d35c4f72de6 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Delete.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Delete.php @@ -6,6 +6,8 @@ */ namespace Magento\Catalog\Controller\Adminhtml\Product\Set; +use Magento\Framework\Exception\NotFoundException; + class Delete extends \Magento\Catalog\Controller\Adminhtml\Product\Set { /** @@ -29,10 +31,15 @@ public function __construct( /** * @return \Magento\Backend\Model\View\Result\Redirect + * @throws NotFoundException */ public function execute() { - $setId = $this->getRequest()->getParam('id'); + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + + $setId = (int)$this->getRequest()->getParam('id'); $resultRedirect = $this->resultRedirectFactory->create(); try { $this->attributeSetRepository->deleteById($setId); @@ -42,6 +49,7 @@ public function execute() $this->messageManager->addErrorMessage(__('We can\'t delete this set right now.')); $resultRedirect->setUrl($this->_redirect->getRedirectUrl($this->getUrl('*'))); } + return $resultRedirect; } } diff --git a/app/code/Magento/Catalog/Controller/Product/Compare/Add.php b/app/code/Magento/Catalog/Controller/Product/Compare/Add.php index eb9cc83125541..24dec0e709122 100644 --- a/app/code/Magento/Catalog/Controller/Product/Compare/Add.php +++ b/app/code/Magento/Catalog/Controller/Product/Compare/Add.php @@ -18,7 +18,7 @@ class Add extends \Magento\Catalog\Controller\Product\Compare public function execute() { $resultRedirect = $this->resultRedirectFactory->create(); - if (!$this->_formKeyValidator->validate($this->getRequest())) { + if (!$this->isActionAllowed()) { return $resultRedirect->setRefererUrl(); } @@ -51,4 +51,12 @@ public function execute() } return $resultRedirect->setRefererOrBaseUrl(); } + + /** + * @return bool + */ + private function isActionAllowed(): bool + { + return $this->getRequest()->isPost() && $this->_formKeyValidator->validate($this->getRequest()); + } } diff --git a/app/code/Magento/Catalog/Controller/Product/Compare/Clear.php b/app/code/Magento/Catalog/Controller/Product/Compare/Clear.php index 568fbf1d05677..ebbf90e0701ae 100644 --- a/app/code/Magento/Catalog/Controller/Product/Compare/Clear.php +++ b/app/code/Magento/Catalog/Controller/Product/Compare/Clear.php @@ -17,29 +17,42 @@ class Clear extends \Magento\Catalog\Controller\Product\Compare */ public function execute() { - /** @var \Magento\Catalog\Model\ResourceModel\Product\Compare\Item\Collection $items */ - $items = $this->_itemCollectionFactory->create(); + if ($this->isActionAllowed()) { + /** @var \Magento\Catalog\Model\ResourceModel\Product\Compare\Item\Collection $items */ + $items = $this->_itemCollectionFactory->create(); - if ($this->_customerSession->isLoggedIn()) { - $items->setCustomerId($this->_customerSession->getCustomerId()); - } elseif ($this->_customerId) { - $items->setCustomerId($this->_customerId); - } else { - $items->setVisitorId($this->_customerVisitor->getId()); - } + if ($this->_customerSession->isLoggedIn()) { + $items->setCustomerId($this->_customerSession->getCustomerId()); + } elseif ($this->_customerId) { + $items->setCustomerId($this->_customerId); + } else { + $items->setVisitorId($this->_customerVisitor->getId()); + } - try { - $items->clear(); - $this->messageManager->addSuccessMessage(__('You cleared the comparison list.')); - $this->_objectManager->get(\Magento\Catalog\Helper\Product\Compare::class)->calculate(); - } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addErrorMessage($e->getMessage()); - } catch (\Exception $e) { - $this->messageManager->addExceptionMessage($e, __('Something went wrong clearing the comparison list.')); + try { + $items->clear(); + $this->messageManager->addSuccessMessage(__('You cleared the comparison list.')); + $this->_objectManager->get(\Magento\Catalog\Helper\Product\Compare::class)->calculate(); + } catch (\Magento\Framework\Exception\LocalizedException $e) { + $this->messageManager->addErrorMessage($e->getMessage()); + } catch (\Exception $e) { + $this->messageManager->addExceptionMessage( + $e, + __('Something went wrong clearing the comparison list.') + ); + } } /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); return $resultRedirect->setRefererOrBaseUrl(); } + + /** + * @return bool + */ + private function isActionAllowed(): bool + { + return $this->getRequest()->isPost() && $this->_formKeyValidator->validate($this->getRequest()); + } } diff --git a/app/code/Magento/Catalog/Controller/Product/Compare/Remove.php b/app/code/Magento/Catalog/Controller/Product/Compare/Remove.php index 2acbe5ce4d582..36aa0ea1caf9d 100644 --- a/app/code/Magento/Catalog/Controller/Product/Compare/Remove.php +++ b/app/code/Magento/Catalog/Controller/Product/Compare/Remove.php @@ -18,7 +18,7 @@ class Remove extends \Magento\Catalog\Controller\Product\Compare public function execute() { $productId = (int)$this->getRequest()->getParam('product'); - if ($productId) { + if ($this->isActionAllowed() && $productId) { $storeId = $this->_storeManager->getStore()->getId(); try { $product = $this->productRepository->getById($productId, false, $storeId); @@ -61,4 +61,12 @@ public function execute() return $resultRedirect->setRefererOrBaseUrl(); } } + + /** + * @return bool + */ + private function isActionAllowed(): bool + { + return $this->getRequest()->isPost() && $this->_formKeyValidator->validate($this->getRequest()); + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/DeleteTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/DeleteTest.php index 196b4df5b47c0..2cae2c07cc85a 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/DeleteTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/DeleteTest.php @@ -42,8 +42,9 @@ protected function setUp() false, true, true, - ['getParam', 'getPost'] + ['getParam', 'getPost', 'isPost'] ); + $this->request->expects($this->any())->method('isPost')->willReturn(true); $auth = $this->createPartialMock(\Magento\Backend\Model\Auth::class, ['getAuthStorage']); $this->authStorage = $this->createPartialMock( \Magento\Backend\Model\Auth\StorageInterface::class, diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/MoveTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/MoveTest.php index d729d0ffbdccc..3d5150fcc9f7e 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/MoveTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/MoveTest.php @@ -83,9 +83,10 @@ private function fillContext() { $this->request = $this ->getMockBuilder(\Magento\Framework\App\RequestInterface::class) - ->setMethods(['getPost']) + ->setMethods(['getPost', 'isPost']) ->disableOriginalConstructor() ->getMockForAbstractClass(); + $this->request->expects($this->any())->method('isPost')->willReturn(true); $this->context->expects($this->once())->method('getRequest')->will($this->returnValue($this->request)); $this->messageManager = $this->createMock(ManagerInterface::class); $this->context->expects($this->once())->method('getMessageManager')->willReturn($this->messageManager); diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Action/Attribute/SaveTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Action/Attribute/SaveTest.php index de44af7f58afc..9dd0f6ef3ff71 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Action/Attribute/SaveTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Action/Attribute/SaveTest.php @@ -229,6 +229,8 @@ protected function prepareContext() $this->objectManager->expects($this->any())->method('get')->will($this->returnValueMap([ [\Magento\CatalogInventory\Api\StockConfigurationInterface::class, $this->stockConfig], ])); + + $this->request->expects($this->any())->method('isPost')->willReturn(true); } public function testExecuteThatProductIdsAreObtainedFromAttributeHelper() diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/MassStatusTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/MassStatusTest.php index d41de5f67503c..e2b9b06175b13 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/MassStatusTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/MassStatusTest.php @@ -50,6 +50,9 @@ class MassStatusTest extends \Magento\Catalog\Test\Unit\Controller\Adminhtml\Pro */ private $actionMock; + /** + * @inheritdoc + */ protected function setUp() { $this->priceProcessorMock = $this->getMockBuilder(Processor::class) @@ -111,6 +114,7 @@ protected function setUp() ]; /** @var \Magento\Backend\App\Action\Context $context */ $context = $this->initContext($additionalParams, [[Action::class, $this->actionMock]]); + $this->request->expects($this->any())->method('isPost')->willReturn(true); $this->action = new \Magento\Catalog\Controller\Adminhtml\Product\MassStatus( $context, diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/ProductTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/ProductTest.php index 5d08f80847da2..7c3b78d5cf05a 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/ProductTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/ProductTest.php @@ -67,7 +67,7 @@ protected function initContext(array $additionalParams = [], array $objectManage ->setMethods(['add', 'prepend'])->disableOriginalConstructor()->getMock(); $title->expects($this->any())->method('prepend')->withAnyParameters()->will($this->returnSelf()); $requestInterfaceMock = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class)->setMethods( - ['getParam', 'getPost', 'getFullActionName', 'getPostValue'] + ['getParam', 'getPost', 'getFullActionName', 'getPostValue', 'isPost'] )->disableOriginalConstructor()->getMock(); $responseInterfaceMock = $this->getMockBuilder(\Magento\Framework\App\ResponseInterface::class)->setMethods( diff --git a/app/code/Magento/Catalog/Test/Unit/Cron/FrontendActionsFlushTest.php b/app/code/Magento/Catalog/Test/Unit/Cron/FrontendActionsFlushTest.php index 29d9736e02442..46eac31af986e 100644 --- a/app/code/Magento/Catalog/Test/Unit/Cron/FrontendActionsFlushTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Cron/FrontendActionsFlushTest.php @@ -87,13 +87,14 @@ public function testExecute() 'recently_viewed_product' ]); + $time = time() - 1500; $connectionMock->expects($this->once()) ->method('quoteInto') - ->with('added_at < ?', time() - 1500) - ->willReturn(['added_at < ?', time() - 1500]); + ->with('added_at < ?', $time) + ->willReturn(['added_at < ?', $time]); $connectionMock->expects($this->once()) ->method('delete') - ->with('catalog_product_frontend_action', [['added_at < ?', time() - 1500]]); + ->with('catalog_product_frontend_action', [['added_at < ?', $time]]); $this->model->execute(); } diff --git a/app/code/Magento/CatalogRule/Block/Adminhtml/Edit/DeleteButton.php b/app/code/Magento/CatalogRule/Block/Adminhtml/Edit/DeleteButton.php index 6390822b58f4a..184cb6419294f 100644 --- a/app/code/Magento/CatalogRule/Block/Adminhtml/Edit/DeleteButton.php +++ b/app/code/Magento/CatalogRule/Block/Adminhtml/Edit/DeleteButton.php @@ -25,7 +25,7 @@ public function getButtonData() 'class' => 'delete', 'on_click' => 'deleteConfirm(\'' . __( 'Are you sure you want to do this?' - ) . '\', \'' . $this->urlBuilder->getUrl('*/*/delete', ['id' => $ruleId]) . '\')', + ) . '\', \'' . $this->urlBuilder->getUrl('*/*/delete', ['id' => $ruleId]) . '\', {data: {}})', 'sort_order' => 20, ]; } 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 3500506d8d6c5..be8a5a1556193 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Delete.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Delete.php @@ -12,9 +12,14 @@ class Delete extends \Magento\CatalogRule\Controller\Adminhtml\Promo\Catalog { /** * @return void + * @throws \Magento\Framework\Exception\NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new \Magento\Framework\Exception\NotFoundException(__('Page not found.')); + } + $id = $this->getRequest()->getParam('id'); if ($id) { try { diff --git a/app/code/Magento/CatalogRule/Test/Unit/Block/Adminhtml/Edit/DeleteButtonTest.php b/app/code/Magento/CatalogRule/Test/Unit/Block/Adminhtml/Edit/DeleteButtonTest.php index 6178d51644fde..9b80984628b12 100644 --- a/app/code/Magento/CatalogRule/Test/Unit/Block/Adminhtml/Edit/DeleteButtonTest.php +++ b/app/code/Magento/CatalogRule/Test/Unit/Block/Adminhtml/Edit/DeleteButtonTest.php @@ -58,7 +58,7 @@ public function testGetButtonData() 'class' => 'delete', 'on_click' => 'deleteConfirm(\'' . __( 'Are you sure you want to do this?' - ) . '\', \'' . $deleteUrl . '\')', + ) . '\', \'' . $deleteUrl . '\', {data: {}})', 'sort_order' => 20, ]; diff --git a/app/code/Magento/Checkout/Controller/Cart/Add.php b/app/code/Magento/Checkout/Controller/Cart/Add.php index 82085281c93d9..4aebde6b3b0c0 100644 --- a/app/code/Magento/Checkout/Controller/Cart/Add.php +++ b/app/code/Magento/Checkout/Controller/Cart/Add.php @@ -1,6 +1,5 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -75,10 +74,15 @@ protected function _initProduct() * Add product to shopping cart action * * @return \Magento\Framework\Controller\Result\Redirect + * @throws \Magento\Framework\Exception\NotFoundException * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new \Magento\Framework\Exception\NotFoundException(__('Page not found.')); + } + if (!$this->_formKeyValidator->validate($this->getRequest())) { $this->messageManager->addErrorMessage( __('Your session has expired') diff --git a/app/code/Magento/Checkout/Controller/Cart/CouponPost.php b/app/code/Magento/Checkout/Controller/Cart/CouponPost.php index 56215814d2cf6..71407b32f2f22 100644 --- a/app/code/Magento/Checkout/Controller/Cart/CouponPost.php +++ b/app/code/Magento/Checkout/Controller/Cart/CouponPost.php @@ -61,11 +61,16 @@ public function __construct( * Initialize coupon * * @return \Magento\Framework\Controller\Result\Redirect + * @throws \Magento\Framework\Exception\NotFoundException * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new \Magento\Framework\Exception\NotFoundException(__('Page not found.')); + } + $couponCode = $this->getRequest()->getParam('remove') == 1 ? '' : trim($this->getRequest()->getParam('coupon_code')); @@ -95,14 +100,14 @@ public function execute() if (!$itemsCount) { if ($isCodeLengthValid && $coupon->getId()) { $this->_checkoutSession->getQuote()->setCouponCode($couponCode)->save(); - $this->messageManager->addSuccess( + $this->messageManager->addSuccessMessage( __( 'You used coupon code "%1".', $escaper->escapeHtml($couponCode) ) ); } else { - $this->messageManager->addError( + $this->messageManager->addErrorMessage( __( 'The coupon code "%1" is not valid.', $escaper->escapeHtml($couponCode) @@ -111,14 +116,14 @@ public function execute() } } else { if ($isCodeLengthValid && $coupon->getId() && $couponCode == $cartQuote->getCouponCode()) { - $this->messageManager->addSuccess( + $this->messageManager->addSuccessMessage( __( 'You used coupon code "%1".', $escaper->escapeHtml($couponCode) ) ); } else { - $this->messageManager->addError( + $this->messageManager->addErrorMessage( __( 'The coupon code "%1" is not valid.', $escaper->escapeHtml($couponCode) @@ -127,12 +132,12 @@ public function execute() } } } else { - $this->messageManager->addSuccess(__('You canceled the coupon code.')); + $this->messageManager->addSuccessMessage(__('You canceled the coupon code.')); } } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError(__('We cannot apply the coupon code.')); + $this->messageManager->addErrorMessage(__('We cannot apply the coupon code.')); $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); } diff --git a/app/code/Magento/Checkout/Controller/Cart/Delete.php b/app/code/Magento/Checkout/Controller/Cart/Delete.php index c0371a6e504f6..98277072fdd99 100644 --- a/app/code/Magento/Checkout/Controller/Cart/Delete.php +++ b/app/code/Magento/Checkout/Controller/Cart/Delete.php @@ -15,7 +15,7 @@ class Delete extends \Magento\Checkout\Controller\Cart */ public function execute() { - if (!$this->_formKeyValidator->validate($this->getRequest())) { + if (!$this->getRequest()->isPost() || !$this->_formKeyValidator->validate($this->getRequest())) { return $this->resultRedirectFactory->create()->setPath('*/*/'); } diff --git a/app/code/Magento/Checkout/Controller/Cart/UpdateItemOptions.php b/app/code/Magento/Checkout/Controller/Cart/UpdateItemOptions.php index 118611263220b..a6eb61169363c 100644 --- a/app/code/Magento/Checkout/Controller/Cart/UpdateItemOptions.php +++ b/app/code/Magento/Checkout/Controller/Cart/UpdateItemOptions.php @@ -12,11 +12,16 @@ class UpdateItemOptions extends \Magento\Checkout\Controller\Cart * Update product configuration for a cart item * * @return \Magento\Framework\Controller\Result\Redirect + * @throws \Magento\Framework\Exception\NotFoundException * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new \Magento\Framework\Exception\NotFoundException(__('Page not found.')); + } + $id = (int)$this->getRequest()->getParam('id'); $params = $this->getRequest()->getParams(); diff --git a/app/code/Magento/Checkout/Controller/Cart/UpdatePost.php b/app/code/Magento/Checkout/Controller/Cart/UpdatePost.php index 4ebf39df7defc..fb29014127490 100644 --- a/app/code/Magento/Checkout/Controller/Cart/UpdatePost.php +++ b/app/code/Magento/Checkout/Controller/Cart/UpdatePost.php @@ -95,7 +95,7 @@ protected function _updateShoppingCart() */ public function execute() { - if (!$this->_formKeyValidator->validate($this->getRequest())) { + if (!$this->getRequest()->isPost() || !$this->_formKeyValidator->validate($this->getRequest())) { return $this->resultRedirectFactory->create()->setPath('*/*/'); } diff --git a/app/code/Magento/Checkout/Controller/Sidebar/RemoveItem.php b/app/code/Magento/Checkout/Controller/Sidebar/RemoveItem.php index c84aec336a589..a17fa44776555 100644 --- a/app/code/Magento/Checkout/Controller/Sidebar/RemoveItem.php +++ b/app/code/Magento/Checkout/Controller/Sidebar/RemoveItem.php @@ -58,7 +58,7 @@ public function __construct( */ public function execute() { - if (!$this->getFormKeyValidator()->validate($this->getRequest())) { + if (!$this->getRequest()->isPost() || !$this->getFormKeyValidator()->validate($this->getRequest())) { return $this->resultRedirectFactory->create()->setPath('*/cart/'); } $itemId = (int)$this->getRequest()->getParam('item_id'); diff --git a/app/code/Magento/Checkout/Test/Unit/Controller/Cart/AddTest.php b/app/code/Magento/Checkout/Test/Unit/Controller/Cart/AddTest.php index b06000662e093..f7721bbc58f18 100644 --- a/app/code/Magento/Checkout/Test/Unit/Controller/Cart/AddTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Controller/Cart/AddTest.php @@ -58,7 +58,10 @@ public function setUp() $this->resultRedirectFactory = $this->getMockBuilder(RedirectFactory::class) ->disableOriginalConstructor()->getMock(); $this->request = $this->getMockBuilder(RequestInterface::class) - ->disableOriginalConstructor()->getmock(); + ->disableOriginalConstructor() + ->setMethods(['isPost']) + ->getMockForAbstractClass(); + $this->request->expects($this->any())->method('isPost')->willReturn(true); $this->messageManager = $this->getMockBuilder(ManagerInterface::class) ->disableOriginalConstructor()->getMock(); diff --git a/app/code/Magento/Checkout/Test/Unit/Controller/Cart/CouponPostTest.php b/app/code/Magento/Checkout/Test/Unit/Controller/Cart/CouponPostTest.php index b8f46feab0a48..491f4c741e645 100644 --- a/app/code/Magento/Checkout/Test/Unit/Controller/Cart/CouponPostTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Controller/Cart/CouponPostTest.php @@ -85,6 +85,7 @@ class CouponPostTest extends \PHPUnit\Framework\TestCase protected function setUp() { $this->request = $this->createMock(\Magento\Framework\App\Request\Http::class); + $this->request->expects($this->any())->method('isPost')->willReturn(true); $this->response = $this->createMock(\Magento\Framework\App\Response\Http::class); $this->quote = $this->createPartialMock(\Magento\Quote\Model\Quote::class, [ 'setCouponCode', @@ -165,15 +166,12 @@ protected function setUp() public function testExecuteWithEmptyCoupon() { - $this->request->expects($this->at(0)) - ->method('getParam') - ->with('remove') - ->willReturn(0); - - $this->request->expects($this->at(1)) - ->method('getParam') - ->with('coupon_code') - ->willReturn(''); + $this->request->expects($this->any())->method('getParam')->willReturnMap( + [ + ['remove', null, 0], + ['coupon_code', null, ''], + ] + ); $this->cart->expects($this->once()) ->method('getQuote') @@ -184,15 +182,12 @@ public function testExecuteWithEmptyCoupon() public function testExecuteWithGoodCouponAndItems() { - $this->request->expects($this->at(0)) - ->method('getParam') - ->with('remove') - ->willReturn(0); - - $this->request->expects($this->at(1)) - ->method('getParam') - ->with('coupon_code') - ->willReturn('CODE'); + $this->request->expects($this->any())->method('getParam')->willReturnMap( + [ + ['remove', null, 0], + ['coupon_code', null, 'CODE'], + ] + ); $this->cart->expects($this->any()) ->method('getQuote') @@ -236,7 +231,7 @@ public function testExecuteWithGoodCouponAndItems() ->willReturn('CODE'); $this->messageManager->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->willReturnSelf(); $this->objectManagerMock->expects($this->once()) @@ -248,15 +243,12 @@ public function testExecuteWithGoodCouponAndItems() public function testExecuteWithGoodCouponAndNoItems() { - $this->request->expects($this->at(0)) - ->method('getParam') - ->with('remove') - ->willReturn(0); - - $this->request->expects($this->at(1)) - ->method('getParam') - ->with('coupon_code') - ->willReturn('CODE'); + $this->request->expects($this->any())->method('getParam')->willReturnMap( + [ + ['remove', null, 0], + ['coupon_code', null, 'CODE'], + ] + ); $this->cart->expects($this->any()) ->method('getQuote') @@ -290,7 +282,7 @@ public function testExecuteWithGoodCouponAndNoItems() ->willReturnSelf(); $this->messageManager->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->willReturnSelf(); $this->objectManagerMock->expects($this->once()) @@ -302,15 +294,12 @@ public function testExecuteWithGoodCouponAndNoItems() public function testExecuteWithBadCouponAndItems() { - $this->request->expects($this->at(0)) - ->method('getParam') - ->with('remove') - ->willReturn(0); - - $this->request->expects($this->at(1)) - ->method('getParam') - ->with('coupon_code') - ->willReturn(''); + $this->request->expects($this->any())->method('getParam')->willReturnMap( + [ + ['remove', null, 0], + ['coupon_code', null, ''], + ] + ); $this->cart->expects($this->any()) ->method('getQuote') @@ -344,7 +333,7 @@ public function testExecuteWithBadCouponAndItems() ->willReturnSelf(); $this->messageManager->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with('You canceled the coupon code.') ->willReturnSelf(); @@ -353,15 +342,12 @@ public function testExecuteWithBadCouponAndItems() public function testExecuteWithBadCouponAndNoItems() { - $this->request->expects($this->at(0)) - ->method('getParam') - ->with('remove') - ->willReturn(0); - - $this->request->expects($this->at(1)) - ->method('getParam') - ->with('coupon_code') - ->willReturn('CODE'); + $this->request->expects($this->any())->method('getParam')->willReturnMap( + [ + ['remove', null, 0], + ['coupon_code', null, 'CODE'], + ] + ); $this->cart->expects($this->any()) ->method('getQuote') @@ -386,7 +372,7 @@ public function testExecuteWithBadCouponAndNoItems() ->willReturn($coupon); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->willReturnSelf(); $this->objectManagerMock->expects($this->once()) diff --git a/app/code/Magento/Checkout/Test/Unit/Controller/Sidebar/RemoveItemTest.php b/app/code/Magento/Checkout/Test/Unit/Controller/Sidebar/RemoveItemTest.php index 7653a51b2f9b7..3f9a84c1b1763 100644 --- a/app/code/Magento/Checkout/Test/Unit/Controller/Sidebar/RemoveItemTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Controller/Sidebar/RemoveItemTest.php @@ -47,7 +47,11 @@ protected function setUp() $this->sidebarMock = $this->createMock(\Magento\Checkout\Model\Sidebar::class); $this->loggerMock = $this->createMock(\Psr\Log\LoggerInterface::class); $this->jsonHelperMock = $this->createMock(\Magento\Framework\Json\Helper\Data::class); - $this->requestMock = $this->createMock(\Magento\Framework\App\RequestInterface::class); + $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) + ->disableOriginalConstructor() + ->setMethods(['isPost']) + ->getMockForAbstractClass(); + $this->requestMock->expects($this->any())->method('isPost')->willReturn(true); $this->responseMock = $this->getMockForAbstractClass( \Magento\Framework\App\ResponseInterface::class, [], diff --git a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php index f7b178df99624..447689c95dfd0 100644 --- a/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php +++ b/app/code/Magento/CheckoutAgreements/Controller/Adminhtml/Agreement/Delete.php @@ -9,6 +9,7 @@ use Magento\CheckoutAgreements\Api\CheckoutAgreementsRepositoryInterface; use Magento\CheckoutAgreements\Controller\Adminhtml\Agreement; use Magento\Backend\App\Action\Context; +use Magento\Framework\Exception\NotFoundException; use Magento\Framework\Registry; use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\LocalizedException; @@ -36,9 +37,14 @@ public function __construct( } /** * @return void + * @throws NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + $id = (int)$this->getRequest()->getParam('id'); $agreement = $this->agreementRepository->get($id); if (!$agreement->getAgreementId()) { diff --git a/app/code/Magento/Cms/Block/Adminhtml/Block/Edit/DeleteButton.php b/app/code/Magento/Cms/Block/Adminhtml/Block/Edit/DeleteButton.php index a7410cac64d76..926886316f16a 100644 --- a/app/code/Magento/Cms/Block/Adminhtml/Block/Edit/DeleteButton.php +++ b/app/code/Magento/Cms/Block/Adminhtml/Block/Edit/DeleteButton.php @@ -24,7 +24,7 @@ public function getButtonData() 'class' => 'delete', 'on_click' => 'deleteConfirm(\'' . __( 'Are you sure you want to do this?' - ) . '\', \'' . $this->getDeleteUrl() . '\')', + ) . '\', \'' . $this->getDeleteUrl() . '\', {data: {}})', 'sort_order' => 20, ]; } diff --git a/app/code/Magento/Cms/Block/Adminhtml/Page/Edit/DeleteButton.php b/app/code/Magento/Cms/Block/Adminhtml/Page/Edit/DeleteButton.php index 1fc599e4c856a..b11c2fc4163b2 100644 --- a/app/code/Magento/Cms/Block/Adminhtml/Page/Edit/DeleteButton.php +++ b/app/code/Magento/Cms/Block/Adminhtml/Page/Edit/DeleteButton.php @@ -24,7 +24,7 @@ public function getButtonData() 'class' => 'delete', 'on_click' => 'deleteConfirm(\'' . __( 'Are you sure you want to do this?' - ) . '\', \'' . $this->getDeleteUrl() . '\')', + ) . '\', \'' . $this->getDeleteUrl() . '\', {data: {}})', 'sort_order' => 20, ]; } diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Block/Delete.php b/app/code/Magento/Cms/Controller/Adminhtml/Block/Delete.php index 3aaf40e7d0ab2..22672e57ee6ab 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Block/Delete.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Block/Delete.php @@ -12,9 +12,14 @@ class Delete extends \Magento\Cms\Controller\Adminhtml\Block * Delete action * * @return \Magento\Framework\Controller\ResultInterface + * @throws \Magento\Framework\Exception\NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new \Magento\Framework\Exception\NotFoundException(__('Page not found.')); + } + /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); // check if we know what should be deleted diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Block/MassDelete.php b/app/code/Magento/Cms/Controller/Adminhtml/Block/MassDelete.php index 92bc7ad71f590..ef0fa937dbd5c 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Block/MassDelete.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Block/MassDelete.php @@ -49,10 +49,14 @@ public function __construct(Context $context, Filter $filter, CollectionFactory * Execute action * * @return \Magento\Backend\Model\View\Result\Redirect - * @throws \Magento\Framework\Exception\LocalizedException|\Exception + * @throws \Magento\Framework\Exception\NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new \Magento\Framework\Exception\NotFoundException(__('Page not found.')); + } + $collection = $this->filter->getCollection($this->collectionFactory->create()); $collectionSize = $collection->getSize(); diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/Delete.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/Delete.php index 16c99e9857c33..78753aee66cef 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/Delete.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/Delete.php @@ -1,6 +1,5 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -19,9 +18,14 @@ class Delete extends \Magento\Backend\App\Action * Delete action * * @return \Magento\Backend\Model\View\Result\Redirect + * @throws \Magento\Framework\Exception\NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new \Magento\Framework\Exception\NotFoundException(__('Page not found.')); + } + // check if we know what should be deleted $id = $this->getRequest()->getParam('page_id'); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDelete.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDelete.php index a1d32aa97a382..6d8fda918689d 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDelete.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDelete.php @@ -48,10 +48,14 @@ public function __construct(Context $context, Filter $filter, CollectionFactory * Execute action * * @return \Magento\Backend\Model\View\Result\Redirect - * @throws \Magento\Framework\Exception\LocalizedException|\Exception + * @throws \Magento\Framework\Exception\NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new \Magento\Framework\Exception\NotFoundException(__('Page not found.')); + } + $collection = $this->filter->getCollection($this->collectionFactory->create()); $collectionSize = $collection->getSize(); diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDisable.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDisable.php index a85b8ecd5e5a1..b36cf087ed9d6 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDisable.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/MassDisable.php @@ -48,10 +48,14 @@ public function __construct(Context $context, Filter $filter, CollectionFactory * Execute action * * @return \Magento\Backend\Model\View\Result\Redirect - * @throws \Magento\Framework\Exception\LocalizedException|\Exception + * @throws \Magento\Framework\Exception\NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new \Magento\Framework\Exception\NotFoundException(__('Page not found.')); + } + $collection = $this->filter->getCollection($this->collectionFactory->create()); foreach ($collection as $item) { diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/MassEnable.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/MassEnable.php index 3f26769e4c9e9..5ab7ae246a76f 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/MassEnable.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/MassEnable.php @@ -48,10 +48,14 @@ public function __construct(Context $context, Filter $filter, CollectionFactory * Execute action * * @return \Magento\Backend\Model\View\Result\Redirect - * @throws \Magento\Framework\Exception\LocalizedException|\Exception + * @throws \Magento\Framework\Exception\NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new \Magento\Framework\Exception\NotFoundException(__('Page not found.')); + } + $collection = $this->filter->getCollection($this->collectionFactory->create()); foreach ($collection as $item) { diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php index 1364e61816796..42b5c8f8497ec 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php @@ -44,8 +44,8 @@ class Save extends \Magento\Backend\App\Action * @param Action\Context $context * @param PostDataProcessor $dataProcessor * @param DataPersistorInterface $dataPersistor - * @param \Magento\Cms\Model\PageFactory $pageFactory - * @param \Magento\Cms\Api\PageRepositoryInterface $pageRepository + * @param \Magento\Cms\Model\PageFactory|null $pageFactory + * @param \Magento\Cms\Api\PageRepositoryInterface|null $pageRepository */ public function __construct( Action\Context $context, diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolder.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolder.php index 81ae1affb5e00..7acdbd28444d6 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolder.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolder.php @@ -7,7 +7,6 @@ namespace Magento\Cms\Controller\Adminhtml\Wysiwyg\Images; use Magento\Framework\App\Filesystem\DirectoryList; -use Magento\Framework\Exception\NotFoundException; /** * Delete image folder. @@ -58,11 +57,11 @@ public function __construct( */ public function execute() { - if (!$this->getRequest()->isPost()) { - throw new NotFoundException(__('Page not found')); - } - try { + if (!$this->getRequest()->isPost()) { + throw new \Exception('Wrong request.'); + } + $path = $this->getStorage()->getCmsWysiwygImages()->getCurrentPath(); if (!$this->directoryResolver->validatePath($path, DirectoryList::MEDIA)) { throw new \Magento\Framework\Exception\LocalizedException( diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolder.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolder.php index 87576265e537a..b66762503b18a 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolder.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolder.php @@ -7,7 +7,6 @@ namespace Magento\Cms\Controller\Adminhtml\Wysiwyg\Images; use Magento\Framework\App\Filesystem\DirectoryList; -use Magento\Framework\Exception\NotFoundException; /** * Creates new folder. @@ -29,7 +28,6 @@ class NewFolder extends \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images * @param \Magento\Framework\Registry $coreRegistry * @param \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory * @param \Magento\Framework\App\Filesystem\DirectoryResolver|null $directoryResolver - * @throws \RuntimeException */ public function __construct( \Magento\Backend\App\Action\Context $context, @@ -51,11 +49,11 @@ public function __construct( */ public function execute() { - if (!$this->getRequest()->isPost()) { - throw new NotFoundException(__('Page not found')); - } - try { + if (!$this->getRequest()->isPost()) { + throw new \Exception('Wrong request.'); + } + $this->_initAction(); $name = $this->getRequest()->getPost('name'); $path = $this->getStorage()->getSession()->getCurrentPath(); diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/Upload.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/Upload.php index 5c9aa2243bc6d..b25ad695ba008 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/Upload.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/Upload.php @@ -50,6 +50,10 @@ public function __construct( public function execute() { try { + if (!$this->getRequest()->isPost()) { + throw new \Exception('Wrong request.'); + } + $this->_initAction(); $path = $this->getStorage()->getSession()->getCurrentPath(); if (!$this->directoryResolver->validatePath($path, DirectoryList::MEDIA)) { diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/DeleteTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/DeleteTest.php index 55e8382d9ca23..c11c7c3810832 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/DeleteTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/DeleteTest.php @@ -73,7 +73,7 @@ protected function setUp() false, true, true, - ['getParam'] + ['getParam', 'isPost'] ); $this->blockMock = $this->getMockBuilder(\Magento\Cms\Model\Block::class) @@ -110,6 +110,8 @@ protected function setUp() ->method('getResultRedirectFactory') ->willReturn($this->resultRedirectFactoryMock); + $this->requestMock->expects($this->any())->method('isPost')->willReturn(true); + $this->deleteController = $this->objectManager->getObject( \Magento\Cms\Controller\Adminhtml\Block\Delete::class, [ diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/MassDeleteTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/MassDeleteTest.php index 39a7d0d74e4d8..3088e3b62c364 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/MassDeleteTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/MassDeleteTest.php @@ -36,12 +36,16 @@ protected function setUp() $this->blockCollectionMock = $this->createMock(\Magento\Cms\Model\ResourceModel\Block\Collection::class); + $requestMock = $this->createMock(\Magento\Framework\App\Request\Http::class); + $requestMock->expects($this->any())->method('isPost')->willReturn(true); + $this->contextMock->expects($this->any())->method('getRequest')->willReturn($requestMock); + $this->massDeleteController = $this->objectManager->getObject( \Magento\Cms\Controller\Adminhtml\Block\MassDelete::class, [ 'context' => $this->contextMock, 'filter' => $this->filterMock, - 'collectionFactory' => $this->collectionFactoryMock + 'collectionFactory' => $this->collectionFactoryMock, ] ); } diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/DeleteTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/DeleteTest.php index 09b36bc41d405..48098242197ae 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/DeleteTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/DeleteTest.php @@ -56,7 +56,7 @@ protected function setUp() false, true, true, - ['getParam'] + ['getParam', 'isPost'] ); $this->pageMock = $this->getMockBuilder(\Magento\Cms\Model\Page::class) @@ -95,6 +95,8 @@ protected function setUp() ->method('getResultRedirectFactory') ->willReturn($this->resultRedirectFactoryMock); + $this->requestMock->expects($this->any())->method('isPost')->willReturn(true); + $this->deleteController = $this->objectManager->getObject( \Magento\Cms\Controller\Adminhtml\Page\Delete::class, [ diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassDeleteTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassDeleteTest.php index f51ab152ba2a4..c0f3a719091d7 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassDeleteTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassDeleteTest.php @@ -36,12 +36,16 @@ protected function setUp() $this->pageCollectionMock = $this->createMock(\Magento\Cms\Model\ResourceModel\Page\Collection::class); + $requestMock = $this->createMock(\Magento\Framework\App\Request\Http::class); + $requestMock->expects($this->any())->method('isPost')->willReturn(true); + $this->contextMock->expects($this->any())->method('getRequest')->willReturn($requestMock); + $this->massDeleteController = $this->objectManager->getObject( \Magento\Cms\Controller\Adminhtml\Page\MassDelete::class, [ 'context' => $this->contextMock, 'filter' => $this->filterMock, - 'collectionFactory' => $this->collectionFactoryMock + 'collectionFactory' => $this->collectionFactoryMock, ] ); } diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassDisableTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassDisableTest.php index 5b80dd1873d5c..64b47b5a08416 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassDisableTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassDisableTest.php @@ -35,6 +35,10 @@ protected function setUp() $this->pageCollectionMock = $this->createMock(\Magento\Cms\Model\ResourceModel\Page\Collection::class); + $requestMock = $this->createMock(\Magento\Framework\App\Request\Http::class); + $requestMock->expects($this->any())->method('isPost')->willReturn(true); + $this->contextMock->expects($this->any())->method('getRequest')->willReturn($requestMock); + $this->massDisableController = $this->objectManager->getObject( \Magento\Cms\Controller\Adminhtml\Page\MassDisable::class, [ diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassEnableTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassEnableTest.php index 16b3dfe4ee638..a63a81882dfe9 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassEnableTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/MassEnableTest.php @@ -35,6 +35,10 @@ protected function setUp() $this->pageCollectionMock = $this->createMock(\Magento\Cms\Model\ResourceModel\Page\Collection::class); + $requestMock = $this->createMock(\Magento\Framework\App\Request\Http::class); + $requestMock->expects($this->any())->method('isPost')->willReturn(true); + $this->contextMock->expects($this->any())->method('getRequest')->willReturn($requestMock); + $this->massEnableController = $this->objectManager->getObject( \Magento\Cms\Controller\Adminhtml\Page\MassEnable::class, [ diff --git a/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/BlockActionsTest.php b/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/BlockActionsTest.php index 3dcf6c4a3fce0..3095abef7bbe3 100644 --- a/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/BlockActionsTest.php +++ b/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/BlockActionsTest.php @@ -95,6 +95,7 @@ public function testPrepareDataSource() 'title' => __('Delete %1', $title), 'message' => __('Are you sure you want to delete a %1 record?', $title) ], + 'post' => true ] ], ] diff --git a/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/PageActionsTest.php b/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/PageActionsTest.php index b0cc1bf061a48..9b3165a2c5517 100644 --- a/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/PageActionsTest.php +++ b/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/PageActionsTest.php @@ -70,6 +70,7 @@ public function testPrepareItemsByPageId() 'title' => __('Delete %1', $title), 'message' => __('Are you sure you want to delete a %1 record?', $title) ], + 'post' => true ] ], ] diff --git a/app/code/Magento/Cms/Ui/Component/Listing/Column/BlockActions.php b/app/code/Magento/Cms/Ui/Component/Listing/Column/BlockActions.php index 60b9f34d29ae6..30b966c6a8610 100644 --- a/app/code/Magento/Cms/Ui/Component/Listing/Column/BlockActions.php +++ b/app/code/Magento/Cms/Ui/Component/Listing/Column/BlockActions.php @@ -87,7 +87,8 @@ public function prepareDataSource(array $dataSource) 'confirm' => [ 'title' => __('Delete %1', $title), 'message' => __('Are you sure you want to delete a %1 record?', $title) - ] + ], + 'post' => true, ] ]; } @@ -99,6 +100,7 @@ public function prepareDataSource(array $dataSource) /** * Get instance of escaper + * * @return Escaper * @deprecated 101.0.7 */ diff --git a/app/code/Magento/Cms/Ui/Component/Listing/Column/PageActions.php b/app/code/Magento/Cms/Ui/Component/Listing/Column/PageActions.php index ea6882e21c85f..f7c16e2065c47 100644 --- a/app/code/Magento/Cms/Ui/Component/Listing/Column/PageActions.php +++ b/app/code/Magento/Cms/Ui/Component/Listing/Column/PageActions.php @@ -89,7 +89,8 @@ public function prepareDataSource(array $dataSource) 'confirm' => [ 'title' => __('Delete %1', $title), 'message' => __('Are you sure you want to delete a %1 record?', $title) - ] + ], + 'post' => true, ]; } if (isset($item['identifier'])) { @@ -110,6 +111,7 @@ public function prepareDataSource(array $dataSource) /** * Get instance of escaper + * * @return Escaper * @deprecated 101.0.7 */ diff --git a/app/code/Magento/Config/Controller/Adminhtml/System/Config/Save.php b/app/code/Magento/Config/Controller/Adminhtml/System/Config/Save.php index 7f7d461ea090d..893a73654137e 100644 --- a/app/code/Magento/Config/Controller/Adminhtml/System/Config/Save.php +++ b/app/code/Magento/Config/Controller/Adminhtml/System/Config/Save.php @@ -6,6 +6,7 @@ namespace Magento\Config\Controller\Adminhtml\System\Config; use Magento\Config\Controller\Adminhtml\System\AbstractConfig; +use Magento\Framework\Exception\NotFoundException; /** * System Configuration Save Controller @@ -140,9 +141,14 @@ protected function _saveAdvanced() * Save configuration * * @return \Magento\Backend\Model\View\Result\Redirect + * @throws NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + try { // custom save logic $this->_saveSection(); diff --git a/app/code/Magento/Config/Test/Unit/Controller/Adminhtml/System/Config/SaveTest.php b/app/code/Magento/Config/Test/Unit/Controller/Adminhtml/System/Config/SaveTest.php index 069a1c20b2966..980d8355de555 100644 --- a/app/code/Magento/Config/Test/Unit/Controller/Adminhtml/System/Config/SaveTest.php +++ b/app/code/Magento/Config/Test/Unit/Controller/Adminhtml/System/Config/SaveTest.php @@ -69,6 +69,7 @@ class SaveTest extends \PHPUnit\Framework\TestCase protected function setUp() { $this->_requestMock = $this->createMock(\Magento\Framework\App\Request\Http::class); + $this->_requestMock->expects($this->any())->method('isPost')->willReturn(true); $this->_responseMock = $this->createMock(\Magento\Framework\App\Response\Http::class); $configStructureMock = $this->createMock(\Magento\Config\Model\Config\Structure::class); diff --git a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Attribute/CreateOptions.php b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Attribute/CreateOptions.php index 6f5f106a8bb24..45057a3591044 100644 --- a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Attribute/CreateOptions.php +++ b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Attribute/CreateOptions.php @@ -50,7 +50,11 @@ public function __construct( */ public function execute() { - $this->getResponse()->representJson($this->jsonHelper->jsonEncode($this->saveAttributeOptions())); + $result = []; + if ($this->getRequest()->isPost()) { + $result = $this->saveAttributeOptions(); + } + $this->getResponse()->representJson($this->jsonHelper->jsonEncode($result)); } /** diff --git a/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currency/SaveRates.php b/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currency/SaveRates.php index ae13c4d399e47..3ab1bfc086721 100644 --- a/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currency/SaveRates.php +++ b/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currency/SaveRates.php @@ -7,15 +7,22 @@ namespace Magento\CurrencySymbol\Controller\Adminhtml\System\Currency; +use Magento\Framework\Exception\NotFoundException; + class SaveRates extends \Magento\CurrencySymbol\Controller\Adminhtml\System\Currency { /** * Save rates action * * @return void + * @throws NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + $data = $this->getRequest()->getParam('rate'); if (is_array($data)) { try { diff --git a/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/Save.php b/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/Save.php index eee7961b02f4a..ad80833d8da5d 100644 --- a/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/Save.php +++ b/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/Save.php @@ -6,15 +6,22 @@ */ namespace Magento\CurrencySymbol\Controller\Adminhtml\System\Currencysymbol; +use Magento\Framework\Exception\NotFoundException; + class Save extends \Magento\CurrencySymbol\Controller\Adminhtml\System\Currencysymbol { /** * Save custom Currency symbol * * @return void + * @throws NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + $symbolsDataArray = $this->getRequest()->getParam('custom_currency_symbol', null); if (is_array($symbolsDataArray)) { foreach ($symbolsDataArray as &$symbolsData) { @@ -27,9 +34,9 @@ public function execute() try { $this->_objectManager->create(\Magento\CurrencySymbol\Model\System\Currencysymbol::class) ->setCurrencySymbolsData($symbolsDataArray); - $this->messageManager->addSuccess(__('You applied the custom currency symbols.')); + $this->messageManager->addSuccessMessage(__('You applied the custom currency symbols.')); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } $this->getResponse()->setRedirect($this->_redirect->getRedirectUrl($this->getUrl('*'))); diff --git a/app/code/Magento/CurrencySymbol/Test/Unit/Controller/Adminhtml/System/Currencysymbol/SaveTest.php b/app/code/Magento/CurrencySymbol/Test/Unit/Controller/Adminhtml/System/Currencysymbol/SaveTest.php index 0863104a2bf8d..455449a449103 100644 --- a/app/code/Magento/CurrencySymbol/Test/Unit/Controller/Adminhtml/System/Currencysymbol/SaveTest.php +++ b/app/code/Magento/CurrencySymbol/Test/Unit/Controller/Adminhtml/System/Currencysymbol/SaveTest.php @@ -57,11 +57,18 @@ class SaveTest extends \PHPUnit\Framework\TestCase */ protected $filterManagerMock; + /** + * @inheritdoc + */ protected function setUp() { $objectManager = new ObjectManager($this); - $this->requestMock = $this->createMock(\Magento\Framework\App\RequestInterface::class); + $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) + ->disableOriginalConstructor() + ->setMethods(['isPost']) + ->getMockForAbstractClass(); + $this->requestMock->expects($this->any())->method('isPost')->willReturn(true); $this->helperMock = $this->createMock(\Magento\Backend\Helper\Data::class); @@ -128,7 +135,7 @@ public function testExecute() ->willReturn($this->filterManagerMock); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('You applied the custom currency symbols.')); $this->action->execute(); diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit.php b/app/code/Magento/Customer/Block/Adminhtml/Edit.php index 973016baba29c..701e38bea6b58 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit.php @@ -122,7 +122,7 @@ protected function _construct() [ 'label' => __('Force Sign-In'), 'onclick' => 'deleteConfirm(\'' . $this->escapeJs($this->escapeHtml($deleteConfirmMsg)) . - '\', \'' . $url . '\')', + '\', \'' . $url . '\', {data: {}})', 'class' => 'invalidate-token' ], 10 diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/InvalidateTokenButton.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/InvalidateTokenButton.php index 180cb3d66ea35..506ba3fb9bfda 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/InvalidateTokenButton.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/InvalidateTokenButton.php @@ -25,7 +25,8 @@ public function getButtonData() $data = [ 'label' => __('Force Sign-In'), 'class' => 'invalidate-token', - 'on_click' => 'deleteConfirm("' . $deleteConfirmMsg . '", "' . $this->getInvalidateTokenUrl() . '")', + 'on_click' => 'deleteConfirm("' . $deleteConfirmMsg . '", "' . $this->getInvalidateTokenUrl() . + '", {data: {}})', 'sort_order' => 65, ]; } diff --git a/app/code/Magento/Customer/Controller/Address/Delete.php b/app/code/Magento/Customer/Controller/Address/Delete.php index ef92bd2ef533b..d287808b4056d 100644 --- a/app/code/Magento/Customer/Controller/Address/Delete.php +++ b/app/code/Magento/Customer/Controller/Address/Delete.php @@ -6,13 +6,20 @@ */ namespace Magento\Customer\Controller\Address; +use Magento\Framework\Exception\NotFoundException; + class Delete extends \Magento\Customer\Controller\Address { /** * @return \Magento\Framework\Controller\Result\Redirect + * @throws NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + $addressId = $this->getRequest()->getParam('id', false); if ($addressId && $this->_formKeyValidator->validate($this->getRequest())) { @@ -20,12 +27,12 @@ public function execute() $address = $this->_addressRepository->getById($addressId); if ($address->getCustomerId() === $this->_getSession()->getCustomerId()) { $this->_addressRepository->deleteById($addressId); - $this->messageManager->addSuccess(__('You deleted the address.')); + $this->messageManager->addSuccessMessage(__('You deleted the address.')); } else { - $this->messageManager->addError(__('We can\'t delete the address right now.')); + $this->messageManager->addErrorMessage(__('We can\'t delete the address right now.')); } } catch (\Exception $other) { - $this->messageManager->addException($other, __('We can\'t delete the address right now.')); + $this->messageManager->addExceptionMessage($other, __('We can\'t delete the address right now.')); } } return $this->resultRedirectFactory->create()->setPath('*/*/index'); diff --git a/app/code/Magento/Customer/Controller/Address/Index.php b/app/code/Magento/Customer/Controller/Address/Index.php index ad04c7bd5c71b..674d3bcf0e0d3 100644 --- a/app/code/Magento/Customer/Controller/Address/Index.php +++ b/app/code/Magento/Customer/Controller/Address/Index.php @@ -28,9 +28,9 @@ class Index extends \Magento\Customer\Controller\Address * @param \Magento\Customer\Api\Data\RegionInterfaceFactory $regionDataFactory * @param \Magento\Framework\Reflection\DataObjectProcessor $dataProcessor * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper - * @param CustomerRepositoryInterface $customerRepository * @param \Magento\Framework\Controller\Result\ForwardFactory $resultForwardFactory * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory + * @param CustomerRepositoryInterface $customerRepository * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php index 936d9cdbc1704..6b35397d9be13 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php @@ -9,6 +9,7 @@ use Magento\Customer\Api\Data\GroupInterfaceFactory; use Magento\Customer\Api\Data\GroupInterface; use Magento\Customer\Api\GroupRepositoryInterface; +use Magento\Framework\Exception\NotFoundException; class Save extends \Magento\Customer\Controller\Adminhtml\Group { @@ -66,9 +67,14 @@ protected function storeCustomerGroupDataToSession($customerGroupData) * Create or save customer group. * * @return \Magento\Backend\Model\View\Result\Redirect|\Magento\Backend\Model\View\Result\Forward + * @throws NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + $taxClass = (int)$this->getRequest()->getParam('tax_class'); /** @var \Magento\Customer\Api\Data\GroupInterface $customerGroup */ diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php index 6753a48d02d6a..a51c8d08de41f 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php @@ -106,8 +106,6 @@ private function getEmailNotification() * Inline edit action execute * * @return \Magento\Framework\Controller\Result\Json - * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function execute() { @@ -115,7 +113,7 @@ public function execute() $resultJson = $this->resultJsonFactory->create(); $postItems = $this->getRequest()->getParam('items', []); - if (!($this->getRequest()->getParam('isAjax') && count($postItems))) { + if (!($this->getRequest()->getParam('isAjax') && $this->getRequest()->isPost() && count($postItems))) { return $resultJson->setData([ 'messages' => [__('Please correct the data sent.')], 'error' => true, diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/ResetPassword.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/ResetPassword.php index 37c8ed5a252f8..38b868d6b56f0 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/ResetPassword.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/ResetPassword.php @@ -19,7 +19,7 @@ public function execute() { $resultRedirect = $this->resultRedirectFactory->create(); $customerId = (int)$this->getRequest()->getParam('customer_id', 0); - if (!$customerId) { + if (!$this->getRequest()->isPost() || !$customerId) { $resultRedirect->setPath('customer/index'); return $resultRedirect; } @@ -31,7 +31,9 @@ public function execute() \Magento\Customer\Model\AccountManagement::EMAIL_REMINDER, $customer->getWebsiteId() ); - $this->messageManager->addSuccess(__('The customer will receive an email with a link to reset password.')); + $this->messageManager->addSuccessMessage( + __('The customer will receive an email with a link to reset password.') + ); } catch (NoSuchEntityException $exception) { $resultRedirect->setPath('customer/index'); return $resultRedirect; @@ -44,7 +46,7 @@ public function execute() } catch (SecurityViolationException $exception) { $this->messageManager->addErrorMessage($exception->getMessage()); } catch (\Exception $exception) { - $this->messageManager->addException( + $this->messageManager->addExceptionMessage( $exception, __('Something went wrong while resetting customer password.') ); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php index 4064b8586257d..f28053a6611fc 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php @@ -78,7 +78,9 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); $this->request = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) + ->setMethods(['isPost']) ->getMockForAbstractClass(); + $this->request->expects($this->any())->method('isPost')->willReturn(true); $this->address = $this->getMockBuilder(\Magento\Customer\Api\Data\AddressInterface::class) ->getMockForAbstractClass(); $this->messageManager = $this->getMockBuilder(\Magento\Framework\Message\ManagerInterface::class) @@ -146,7 +148,7 @@ public function testExecute() ->method('deleteById') ->with($addressId); $this->messageManager->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('You deleted the address.')); $this->resultRedirect->expects($this->once()) ->method('setPath') @@ -183,11 +185,11 @@ public function testExecuteWithException() ->willReturn(34); $exception = new \Exception('Exception'); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with(__('We can\'t delete the address right now.')) ->willThrowException($exception); $this->messageManager->expects($this->once()) - ->method('addException') + ->method('addExceptionMessage') ->with($exception, __('We can\'t delete the address right now.')); $this->resultRedirect->expects($this->once()) ->method('setPath') diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php index 5f7064d5b124b..55967854f97ca 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php @@ -88,7 +88,9 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); $this->request = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) + ->setMethods(['isPost']) ->getMockForAbstractClass(); + $this->request->expects($this->any())->method('isPost')->willReturn(true); $this->resultRedirectFactory = $this->getMockBuilder(RedirectFactory::class) ->disableOriginalConstructor() ->getMock(); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php index 78d9dd7003522..1a9fd3b1c41ea 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php @@ -75,7 +75,10 @@ protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->request = $this->getMockForAbstractClass(\Magento\Framework\App\RequestInterface::class, [], '', false); + $this->request = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) + ->setMethods(['isPost']) + ->getMockForAbstractClass(); + $this->request->expects($this->any())->method('isPost')->willReturn(true); $this->messageManager = $this->getMockForAbstractClass( \Magento\Framework\Message\ManagerInterface::class, [], diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ResetPasswordTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ResetPasswordTest.php index 02d071ab394a5..b98f3e016768b 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ResetPasswordTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ResetPasswordTest.php @@ -97,6 +97,7 @@ protected function setUp() $this->_request = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) ->disableOriginalConstructor() ->getMock(); + $this->_request->expects($this->any())->method('isPost')->willReturn(true); $this->_response = $this->getMockBuilder( \Magento\Framework\App\Response\Http::class @@ -143,7 +144,7 @@ protected function setUp() $this->messageManager = $this->getMockBuilder( \Magento\Framework\Message\Manager::class )->disableOriginalConstructor()->setMethods( - ['addSuccess', 'addMessage', 'addException', 'addErrorMessage'] + ['addSuccessMessage', 'addMessage', 'addExceptionMessage', 'addErrorMessage'] )->getMock(); $this->resultRedirectFactoryMock = $this->getMockBuilder( @@ -443,7 +444,7 @@ public function testResetPasswordActionException() $this->messageManager->expects( $this->once() )->method( - 'addException' + 'addExceptionMessage' )->with( $this->equalTo($exception), $this->equalTo('Something went wrong while resetting customer password.') @@ -503,7 +504,7 @@ public function testResetPasswordActionSendEmail() $this->messageManager->expects( $this->once() )->method( - 'addSuccess' + 'addSuccessMessage' )->with( $this->equalTo('The customer will receive an email with a link to reset password.') ); diff --git a/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Delete.php b/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Delete.php index eedcf5009ccfa..a609a14f663c1 100644 --- a/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Delete.php +++ b/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Delete.php @@ -12,9 +12,14 @@ class Delete extends \Magento\Email\Controller\Adminhtml\Email\Template * Delete transactional email action * * @return void + * @throws \Magento\Framework\Exception\NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new \Magento\Framework\Exception\NotFoundException(__('Page not found.')); + } + $template = $this->_initTemplate('id'); if ($template->getId()) { try { diff --git a/app/code/Magento/Email/view/adminhtml/templates/template/edit.phtml b/app/code/Magento/Email/view/adminhtml/templates/template/edit.phtml index c1beb3ecb5a77..1bf0ea947605d 100644 --- a/app/code/Magento/Email/view/adminhtml/templates/template/edit.phtml +++ b/app/code/Magento/Email/view/adminhtml/templates/template/edit.phtml @@ -63,10 +63,11 @@ require([ "jquery", "tinymce", "Magento_Ui/js/modal/alert", + "mage/dataPost", "mage/mage", "Magento_Variable/variables", "prototype" -], function(jQuery, tinyMCE, alert){ +], function(jQuery, tinyMCE, alert, dataPost){ //<![CDATA[ jQuery('#email_template_edit_form').mage('form').mage('validation'); @@ -167,7 +168,10 @@ require([ deleteTemplate: function() { if(window.confirm("<?= $block->escapeJs($block->escapeHtml(__('Are you sure you want to delete this template?'))) ?>")) { - window.location.href = '<?= $block->escapeJs($block->escapeUrl($block->getDeleteUrl())) ?>'; + dataPost().postData({ + action: '<?= $block->escapeJs($block->escapeUrl($block->getDeleteUrl())) ?>', + data: {} + }); } }, diff --git a/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Validate.php b/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Validate.php index 6c99e59d1fe05..d51e75e2c753b 100644 --- a/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Validate.php +++ b/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Validate.php @@ -5,13 +5,10 @@ */ namespace Magento\ImportExport\Controller\Adminhtml\Import; +use Magento\Framework\Controller\ResultFactory; +use Magento\ImportExport\Block\Adminhtml\Import\Frame\Result; use Magento\ImportExport\Controller\Adminhtml\ImportResult as ImportResultController; use Magento\ImportExport\Model\Import; -use Magento\ImportExport\Block\Adminhtml\Import\Frame\Result; -use Magento\Framework\Controller\ResultFactory; -use Magento\Framework\App\Filesystem\DirectoryList; -use Magento\ImportExport\Model\Import\Adapter as ImportAdapter; -use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface; class Validate extends ImportResultController { diff --git a/app/code/Magento/Indexer/Controller/Adminhtml/Indexer/MassChangelog.php b/app/code/Magento/Indexer/Controller/Adminhtml/Indexer/MassChangelog.php index b4a4d9f06ae48..dab47d091416e 100644 --- a/app/code/Magento/Indexer/Controller/Adminhtml/Indexer/MassChangelog.php +++ b/app/code/Magento/Indexer/Controller/Adminhtml/Indexer/MassChangelog.php @@ -6,18 +6,25 @@ */ namespace Magento\Indexer\Controller\Adminhtml\Indexer; +use Magento\Framework\Exception\NotFoundException; + class MassChangelog extends \Magento\Indexer\Controller\Adminhtml\Indexer { /** * Turn mview on for the given indexers * * @return void + * @throws NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + $indexerIds = $this->getRequest()->getParam('indexer_ids'); if (!is_array($indexerIds)) { - $this->messageManager->addError(__('Please select indexers.')); + $this->messageManager->addErrorMessage(__('Please select indexers.')); } else { try { foreach ($indexerIds as $indexerId) { @@ -27,13 +34,13 @@ public function execute() )->get($indexerId); $model->setScheduled(true); } - $this->messageManager->addSuccess( + $this->messageManager->addSuccessMessage( __('%1 indexer(s) are in "Update by Schedule" mode.', count($indexerIds)) ); } 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 change indexer(s)' mode because of an error.") ); diff --git a/app/code/Magento/Indexer/Controller/Adminhtml/Indexer/MassOnTheFly.php b/app/code/Magento/Indexer/Controller/Adminhtml/Indexer/MassOnTheFly.php index 7ace4a64d3829..ac6bb046dba22 100644 --- a/app/code/Magento/Indexer/Controller/Adminhtml/Indexer/MassOnTheFly.php +++ b/app/code/Magento/Indexer/Controller/Adminhtml/Indexer/MassOnTheFly.php @@ -6,15 +6,22 @@ */ namespace Magento\Indexer\Controller\Adminhtml\Indexer; +use Magento\Framework\Exception\NotFoundException; + class MassOnTheFly extends \Magento\Indexer\Controller\Adminhtml\Indexer { /** * Turn mview off for the given indexers * * @return void + * @throws NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + $indexerIds = $this->getRequest()->getParam('indexer_ids'); if (!is_array($indexerIds)) { $this->messageManager->addError(__('Please select indexers.')); diff --git a/app/code/Magento/Indexer/Test/Unit/Controller/Adminhtml/Indexer/MassChangelogTest.php b/app/code/Magento/Indexer/Test/Unit/Controller/Adminhtml/Indexer/MassChangelogTest.php index df15a0a58949f..16b5f90cead58 100644 --- a/app/code/Magento/Indexer/Test/Unit/Controller/Adminhtml/Indexer/MassChangelogTest.php +++ b/app/code/Magento/Indexer/Test/Unit/Controller/Adminhtml/Indexer/MassChangelogTest.php @@ -134,12 +134,10 @@ protected function setUp() \Magento\Framework\TestFramework\Unit\Helper\ObjectManager::class, ['get'] ); - $this->request = $this->getMockForAbstractClass( - \Magento\Framework\App\RequestInterface::class, - ['getParam', 'getRequest'], - '', - false - ); + $this->request = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) + ->setMethods(['getParam', 'getRequest', 'isPost']) + ->getMockForAbstractClass(); + $this->request->expects($this->any())->method('isPost')->willReturn(true); $this->response->expects($this->any())->method("setRedirect")->willReturn(1); $this->page = $this->createMock(\Magento\Framework\View\Result\Page::class); @@ -147,7 +145,7 @@ protected function setUp() $this->title = $this->createMock(\Magento\Framework\View\Page\Title::class); $this->messageManager = $this->getMockForAbstractClass( \Magento\Framework\Message\ManagerInterface::class, - ['addError', 'addSuccess'], + ['addErrorMessage', 'addSuccessMessage'], '', false ); @@ -181,7 +179,7 @@ public function testExecute($indexerIds, $exception, $expectsExceptionValues) if (!is_array($indexerIds)) { $this->messageManager->expects($this->once()) - ->method('addError')->with(__('Please select indexers.')) + ->method('addErrorMessage')->with(__('Please select indexers.')) ->will($this->returnValue(1)); } else { $this->objectManager->expects($this->any()) @@ -210,10 +208,10 @@ public function testExecute($indexerIds, $exception, $expectsExceptionValues) if ($exception !== null) { $this->messageManager ->expects($this->exactly($expectsExceptionValues[2])) - ->method('addError') + ->method('addErrorMessage') ->with($exception->getMessage()); $this->messageManager->expects($this->exactly($expectsExceptionValues[1])) - ->method('addException') + ->method('addExceptionMessage') ->with($exception, "We couldn't change indexer(s)' mode because of an error."); } } diff --git a/app/code/Magento/Indexer/Test/Unit/Controller/Adminhtml/Indexer/MassOnTheFlyTest.php b/app/code/Magento/Indexer/Test/Unit/Controller/Adminhtml/Indexer/MassOnTheFlyTest.php index a0e8134522461..956e102a3cc44 100644 --- a/app/code/Magento/Indexer/Test/Unit/Controller/Adminhtml/Indexer/MassOnTheFlyTest.php +++ b/app/code/Magento/Indexer/Test/Unit/Controller/Adminhtml/Indexer/MassOnTheFlyTest.php @@ -137,12 +137,9 @@ protected function setUp() \Magento\Framework\TestFramework\Unit\Helper\ObjectManager::class, ['get'] ); - $this->request = $this->getMockForAbstractClass( - \Magento\Framework\App\RequestInterface::class, - ['getParam', 'getRequest'], - '', - false - ); + $this->request = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) + ->setMethods(['getParam', 'getRequest', 'isPost']) + ->getMockForAbstractClass(); $this->response->expects($this->any())->method("setRedirect")->willReturn(1); $this->page = $this->createMock(\Magento\Framework\View\Result\Page::class); @@ -181,6 +178,7 @@ public function testExecute($indexerIds, $exception, $expectsExceptionValues) $this->request->expects($this->any()) ->method('getParam')->with('indexer_ids') ->will($this->returnValue($indexerIds)); + $this->request->expects($this->any())->method('isPost')->willReturn(true); if (!is_array($indexerIds)) { $this->messageManager->expects($this->once()) diff --git a/app/code/Magento/Integration/Controller/Adminhtml/Integration/Delete.php b/app/code/Magento/Integration/Controller/Adminhtml/Integration/Delete.php index 36073af56327a..94f4c80239712 100644 --- a/app/code/Magento/Integration/Controller/Adminhtml/Integration/Delete.php +++ b/app/code/Magento/Integration/Controller/Adminhtml/Integration/Delete.php @@ -6,6 +6,7 @@ */ namespace Magento\Integration\Controller\Adminhtml\Integration; +use Magento\Framework\Exception\NotFoundException; use Magento\Integration\Block\Adminhtml\Integration\Edit\Tab\Info; use Magento\Framework\Exception\IntegrationException; use Magento\Framework\Controller\ResultFactory; @@ -16,9 +17,14 @@ class Delete extends \Magento\Integration\Controller\Adminhtml\Integration * Delete the integration. * * @return \Magento\Backend\Model\View\Result\Redirect + * @throws NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); $integrationId = (int)$this->getRequest()->getParam(self::PARAM_INTEGRATION_ID); diff --git a/app/code/Magento/Integration/Controller/Adminhtml/Integration/Save.php b/app/code/Magento/Integration/Controller/Adminhtml/Integration/Save.php index dfddd8b954bd3..7afdfbb4de8c3 100644 --- a/app/code/Magento/Integration/Controller/Adminhtml/Integration/Save.php +++ b/app/code/Magento/Integration/Controller/Adminhtml/Integration/Save.php @@ -5,6 +5,7 @@ */ namespace Magento\Integration\Controller\Adminhtml\Integration; +use Magento\Framework\Exception\NotFoundException; use Magento\Integration\Block\Adminhtml\Integration\Edit\Tab\Info; use Magento\Framework\Exception\IntegrationException; use Magento\Framework\Exception\LocalizedException; @@ -43,9 +44,15 @@ private function getSecurityCookie() * Save integration action. * * @return void + * @throws NotFoundException + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + /** @var array $integrationData */ $integrationData = []; try { diff --git a/app/code/Magento/Integration/Test/Unit/Controller/Adminhtml/Integration/DeleteTest.php b/app/code/Magento/Integration/Test/Unit/Controller/Adminhtml/Integration/DeleteTest.php index 8173c50083973..1b2167ef104d1 100644 --- a/app/code/Magento/Integration/Test/Unit/Controller/Adminhtml/Integration/DeleteTest.php +++ b/app/code/Magento/Integration/Test/Unit/Controller/Adminhtml/Integration/DeleteTest.php @@ -23,6 +23,7 @@ protected function setUp() { parent::setUp(); + $this->_requestMock->expects($this->any())->method('isPost')->willReturn(true); $this->integrationController = $this->_createIntegrationController('Delete'); $resultRedirect = $this->getMockBuilder(\Magento\Backend\Model\View\Result\Redirect::class) diff --git a/app/code/Magento/Integration/Test/Unit/Controller/Adminhtml/Integration/SaveTest.php b/app/code/Magento/Integration/Test/Unit/Controller/Adminhtml/Integration/SaveTest.php index ca614966194a2..7401b86d29d6b 100644 --- a/app/code/Magento/Integration/Test/Unit/Controller/Adminhtml/Integration/SaveTest.php +++ b/app/code/Magento/Integration/Test/Unit/Controller/Adminhtml/Integration/SaveTest.php @@ -17,6 +17,15 @@ class SaveTest extends \Magento\Integration\Test\Unit\Controller\Adminhtml\IntegrationTest { + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->_requestMock->expects($this->any())->method('isPost')->willReturn(true); + } + public function testSaveAction() { // Use real translate model diff --git a/app/code/Magento/Integration/view/adminhtml/templates/integration/popup_container.phtml b/app/code/Magento/Integration/view/adminhtml/templates/integration/popup_container.phtml index 8b7e787337e1a..95c9d15d72203 100644 --- a/app/code/Magento/Integration/view/adminhtml/templates/integration/popup_container.phtml +++ b/app/code/Magento/Integration/view/adminhtml/templates/integration/popup_container.phtml @@ -15,7 +15,8 @@ "jquery", 'Magento_Ui/js/modal/confirm', "jquery/ui", - "Magento_Integration/js/integration" + "Magento_Integration/js/integration", + 'mage/dataPost' ], function ($, Confirm) { window.integration = new Integration( @@ -37,7 +38,7 @@ content: "<?= /* @escapeNotVerified */ __("Are you sure you want to delete this integration? You can't undo this action.") ?>", actions: { confirm: function () { - window.location.href = $(e.target).data('url'); + $.mage.dataPost().postData({action: $(e.target).data('url'), data: {}}); } } }); @@ -47,4 +48,4 @@ }); </script> -<div id="integration-popup-container" style="display: none;"></div> \ No newline at end of file +<div id="integration-popup-container" style="display: none;"></div> diff --git a/app/code/Magento/Newsletter/Controller/Adminhtml/Queue/Save.php b/app/code/Magento/Newsletter/Controller/Adminhtml/Queue/Save.php index 7293b350fcd01..0f2192dc442db 100644 --- a/app/code/Magento/Newsletter/Controller/Adminhtml/Queue/Save.php +++ b/app/code/Magento/Newsletter/Controller/Adminhtml/Queue/Save.php @@ -9,18 +9,25 @@ namespace Magento\Newsletter\Controller\Adminhtml\Queue; +use Magento\Framework\Exception\NotFoundException; + class Save extends \Magento\Newsletter\Controller\Adminhtml\Queue { /** - * Save Newsletter queue + * Save newsletter queue. * - * @throws \Magento\Framework\Exception\LocalizedException * @return void + * @throws \Magento\Framework\Exception\LocalizedException + * @throws NotFoundException * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function execute() { try { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + /* @var $queue \Magento\Newsletter\Model\Queue */ $queue = $this->_objectManager->create(\Magento\Newsletter\Model\Queue::class); @@ -30,7 +37,9 @@ public function execute() $template = $this->_objectManager->create(\Magento\Newsletter\Model\Template::class)->load($templateId); if (!$template->getId() || $template->getIsSystem()) { - throw new \Magento\Framework\Exception\LocalizedException(__('Please correct the newsletter template and try again.')); + throw new \Magento\Framework\Exception\LocalizedException( + __('Please correct the newsletter template and try again.') + ); } $queue->setTemplateId( diff --git a/app/code/Magento/Newsletter/Controller/Adminhtml/Subscriber/MassDelete.php b/app/code/Magento/Newsletter/Controller/Adminhtml/Subscriber/MassDelete.php index 7f02e4ea13445..4794d86faa17a 100644 --- a/app/code/Magento/Newsletter/Controller/Adminhtml/Subscriber/MassDelete.php +++ b/app/code/Magento/Newsletter/Controller/Adminhtml/Subscriber/MassDelete.php @@ -6,11 +6,12 @@ */ namespace Magento\Newsletter\Controller\Adminhtml\Subscriber; -use Magento\Newsletter\Controller\Adminhtml\Subscriber; use Magento\Backend\App\Action\Context; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\Response\Http\FileFactory; +use Magento\Framework\Exception\NotFoundException; +use Magento\Newsletter\Controller\Adminhtml\Subscriber; use Magento\Newsletter\Model\SubscriberFactory; -use Magento\Framework\App\ObjectManager; class MassDelete extends Subscriber { @@ -36,12 +37,17 @@ public function __construct( * Delete one or more subscribers action * * @return void + * @throws NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found.')); + } + $subscribersIds = $this->getRequest()->getParam('subscriber'); if (!is_array($subscribersIds)) { - $this->messageManager->addError(__('Please select one or more subscribers.')); + $this->messageManager->addErrorMessage(__('Please select one or more subscribers.')); } else { try { foreach ($subscribersIds as $subscriberId) { @@ -50,9 +56,11 @@ public function execute() ); $subscriber->delete(); } - $this->messageManager->addSuccess(__('Total of %1 record(s) were deleted.', count($subscribersIds))); + $this->messageManager->addSuccessMessage( + __('Total of %1 record(s) were deleted.', count($subscribersIds)) + ); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } } diff --git a/app/code/Magento/Newsletter/Controller/Adminhtml/Subscriber/MassUnsubscribe.php b/app/code/Magento/Newsletter/Controller/Adminhtml/Subscriber/MassUnsubscribe.php index b61494f795905..3b3ea0d4c67a0 100644 --- a/app/code/Magento/Newsletter/Controller/Adminhtml/Subscriber/MassUnsubscribe.php +++ b/app/code/Magento/Newsletter/Controller/Adminhtml/Subscriber/MassUnsubscribe.php @@ -6,6 +6,7 @@ */ namespace Magento\Newsletter\Controller\Adminhtml\Subscriber; +use Magento\Framework\Exception\NotFoundException; use Magento\Newsletter\Controller\Adminhtml\Subscriber; use Magento\Backend\App\Action\Context; use Magento\Framework\App\Response\Http\FileFactory; @@ -37,9 +38,14 @@ public function __construct( * Unsubscribe one or more subscribers action * * @return void + * @throws NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found.')); + } + $subscribersIds = $this->getRequest()->getParam('subscriber'); if (!is_array($subscribersIds)) { $this->messageManager->addError(__('Please select one or more subscribers.')); diff --git a/app/code/Magento/Newsletter/Controller/Adminhtml/Template/Delete.php b/app/code/Magento/Newsletter/Controller/Adminhtml/Template/Delete.php index d327d44feceb8..ac47f7e217b36 100644 --- a/app/code/Magento/Newsletter/Controller/Adminhtml/Template/Delete.php +++ b/app/code/Magento/Newsletter/Controller/Adminhtml/Template/Delete.php @@ -1,6 +1,5 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -12,9 +11,14 @@ class Delete extends \Magento\Newsletter\Controller\Adminhtml\Template * Delete newsletter Template * * @return void + * @throws \Magento\Framework\Exception\NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new \Magento\Framework\Exception\NotFoundException(__('Page not found.')); + } + $template = $this->_objectManager->create( \Magento\Newsletter\Model\Template::class )->load( diff --git a/app/code/Magento/Newsletter/Controller/Adminhtml/Template/Drop.php b/app/code/Magento/Newsletter/Controller/Adminhtml/Template/Drop.php index 52d46065ad05b..54a5eb651d99b 100644 --- a/app/code/Magento/Newsletter/Controller/Adminhtml/Template/Drop.php +++ b/app/code/Magento/Newsletter/Controller/Adminhtml/Template/Drop.php @@ -6,15 +6,22 @@ */ namespace Magento\Newsletter\Controller\Adminhtml\Template; +use Magento\Framework\Exception\NotFoundException; + class Drop extends \Magento\Newsletter\Controller\Adminhtml\Template { /** * Drop Newsletter Template * * @return void + * @throws NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + $this->_view->loadLayout('newsletter_template_preview_popup'); $this->_view->renderLayout(); } diff --git a/app/code/Magento/Newsletter/view/adminhtml/templates/template/edit.phtml b/app/code/Magento/Newsletter/view/adminhtml/templates/template/edit.phtml index eeca4fabd348d..b2a1c8d8c7208 100644 --- a/app/code/Magento/Newsletter/view/adminhtml/templates/template/edit.phtml +++ b/app/code/Magento/Newsletter/view/adminhtml/templates/template/edit.phtml @@ -34,9 +34,10 @@ require([ 'tinymce', 'Magento_Ui/js/modal/prompt', 'Magento_Ui/js/modal/confirm', + 'mage/dataPost', 'mage/mage', 'prototype' -], function(jQuery, tinyMCE, prompt, confirm){ +], function(jQuery, tinyMCE, prompt, confirm, dataPost){ //<![CDATA[ jQuery('#newsletter_template_edit_form').mage('form').mage('validation'); @@ -203,7 +204,10 @@ require([ content: "<?= $block->escapeJs($block->escapeHtml(__('Are you sure you want to delete this template?'))) ?>", actions: { confirm: function() { - window.location.href = '<?= $block->escapeUrl($block->getDeleteUrl()) ?>'; + dataPost().postData({ + action: '<?= $block->escapeUrl($block->getDeleteUrl()) ?>', + data: {} + }); } } }); diff --git a/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php b/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php index 2efae34a96459..e9030fa5c8a84 100644 --- a/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php +++ b/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php @@ -82,7 +82,7 @@ public function execute() /** @var Quote $quote */ $quote = $this->sessionManager->getQuote(); - if (!$quote or !$quote instanceof Quote) { + if (!$quote || !$quote instanceof Quote || !$this->getRequest()->isPost()) { return $this->getErrorResponse(); } diff --git a/app/code/Magento/Paypal/Test/Unit/Controller/Transparent/RequestSecureTokenTest.php b/app/code/Magento/Paypal/Test/Unit/Controller/Transparent/RequestSecureTokenTest.php index 60451a9827097..2e7257e8c0a80 100644 --- a/app/code/Magento/Paypal/Test/Unit/Controller/Transparent/RequestSecureTokenTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Controller/Transparent/RequestSecureTokenTest.php @@ -6,6 +6,7 @@ namespace Magento\Paypal\Test\Unit\Controller\Transparent; use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Request\Http; use Magento\Framework\Controller\Result\JsonFactory; use Magento\Framework\Session\Generic; use Magento\Framework\Session\SessionManager; @@ -67,6 +68,11 @@ protected function setUp() $this->contextMock = $this->getMockBuilder(\Magento\Framework\App\Action\Context::class) ->disableOriginalConstructor() ->getMock(); + + $requestMock = $this->createMock(Http::class); + $requestMock->expects($this->any())->method('isPost')->willReturn(true); + $this->contextMock->expects($this->any())->method('getRequest')->willReturn($requestMock); + $this->resultJsonFactoryMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\JsonFactory::class) ->setMethods(['create']) ->disableOriginalConstructor() diff --git a/app/code/Magento/ProductVideo/Controller/Adminhtml/Product/Gallery/RetrieveImage.php b/app/code/Magento/ProductVideo/Controller/Adminhtml/Product/Gallery/RetrieveImage.php index 9950526182e3e..53705ee9f6e72 100644 --- a/app/code/Magento/ProductVideo/Controller/Adminhtml/Product/Gallery/RetrieveImage.php +++ b/app/code/Magento/ProductVideo/Controller/Adminhtml/Product/Gallery/RetrieveImage.php @@ -107,6 +107,9 @@ public function execute() { $baseTmpMediaPath = $this->mediaConfig->getBaseTmpMediaPath(); try { + if (!$this->getRequest()->isPost()) { + throw new LocalizedException(__('Invalid request type.')); + } $remoteFileUrl = $this->getRequest()->getParam('remote_image'); $this->validateRemoteFile($remoteFileUrl); $localFileName = Uploader::getCorrectFileName(basename($remoteFileUrl)); diff --git a/app/code/Magento/ProductVideo/Test/Unit/Controller/Adminhtml/Product/Gallery/RetrieveImageTest.php b/app/code/Magento/ProductVideo/Test/Unit/Controller/Adminhtml/Product/Gallery/RetrieveImageTest.php index 28c6db7d31379..cc38e55d26af2 100644 --- a/app/code/Magento/ProductVideo/Test/Unit/Controller/Adminhtml/Product/Gallery/RetrieveImageTest.php +++ b/app/code/Magento/ProductVideo/Test/Unit/Controller/Adminhtml/Product/Gallery/RetrieveImageTest.php @@ -6,6 +6,7 @@ namespace Magento\ProductVideo\Test\Unit\Controller\Adminhtml\Product\Gallery; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\App\Request\Http; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -101,14 +102,16 @@ protected function setUp() $this->adapterFactoryMock->expects($this->once())->method('create')->willReturn($this->abstractAdapter); $this->curlMock = $this->createMock(\Magento\Framework\HTTP\Adapter\Curl::class); $this->storageFileMock = $this->createMock(\Magento\MediaStorage\Model\ResourceModel\File\Storage\File::class); - $this->request = $this->createMock(\Magento\Framework\App\RequestInterface::class); + $this->request = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) + ->setMethods(['isPost']) + ->getMockForAbstractClass(); + $this->request->expects($this->any())->method('isPost')->willReturn(true); $this->fileDriverMock = $this->createMock(\Magento\Framework\Filesystem\DriverInterface::class); - $this->contextMock->expects($this->any())->method('getRequest')->will($this->returnValue($this->request)); $managerMock = $this->getMockBuilder(\Magento\Framework\ObjectManagerInterface::class) ->disableOriginalConstructor() ->setMethods(['get']) ->getMockForAbstractClass(); - $this->contextMock->expects($this->any())->method('getRequest')->will($this->returnValue($this->request)); + $this->contextMock->expects($this->any())->method('getRequest')->willReturn($this->request); $this->contextMock->expects($this->any())->method('getObjectManager')->willReturn($managerMock); $this->image = $objectManager->getObject( diff --git a/app/code/Magento/Reports/Controller/Adminhtml/Report/AbstractReport.php b/app/code/Magento/Reports/Controller/Adminhtml/Report/AbstractReport.php index 68f2722ca6dfb..b1f5628a0b7a3 100644 --- a/app/code/Magento/Reports/Controller/Adminhtml/Report/AbstractReport.php +++ b/app/code/Magento/Reports/Controller/Adminhtml/Report/AbstractReport.php @@ -147,15 +147,19 @@ protected function _showLastExecutionTime($flagCode, $refreshCode) } $refreshStatsLink = $this->getUrl('reports/report_statistics'); - $directRefreshLink = $this->getUrl('reports/report_statistics/refreshRecent', ['code' => $refreshCode]); + $directRefreshLink = $this->getUrl('reports/report_statistics/refreshRecent'); $this->messageManager->addNotice( __( 'Last updated: %1. To refresh last day\'s <a href="%2">statistics</a>, ' . - 'click <a href="%3">here</a>.', + 'click <a href="#2" data-post="%3">here</a>.', $updatedAt, $refreshStatsLink, - $directRefreshLink + str_replace( + '"', + '"', + json_encode(['action' => $directRefreshLink, 'data' => ['code' => $refreshCode]]) + ) ) ); return $this; diff --git a/app/code/Magento/Reports/Controller/Adminhtml/Report/Statistics/RefreshRecent.php b/app/code/Magento/Reports/Controller/Adminhtml/Report/Statistics/RefreshRecent.php index 1f0f6e8e40535..957b1160d0281 100644 --- a/app/code/Magento/Reports/Controller/Adminhtml/Report/Statistics/RefreshRecent.php +++ b/app/code/Magento/Reports/Controller/Adminhtml/Report/Statistics/RefreshRecent.php @@ -6,15 +6,22 @@ */ namespace Magento\Reports\Controller\Adminhtml\Report\Statistics; +use Magento\Framework\Exception\NotFoundException; + class RefreshRecent extends \Magento\Reports\Controller\Adminhtml\Report\Statistics { /** * Refresh statistics for last 25 hours * * @return void + * @throws NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found.')); + } + try { $collectionsNames = $this->_getCollectionNames(); /** @var \DateTime $currentDate */ diff --git a/app/code/Magento/Review/Block/Adminhtml/Edit.php b/app/code/Magento/Review/Block/Adminhtml/Edit.php index d6868eae6fcbc..f4d05224e8ea7 100644 --- a/app/code/Magento/Review/Block/Adminhtml/Edit.php +++ b/app/code/Magento/Review/Block/Adminhtml/Edit.php @@ -56,6 +56,7 @@ public function __construct( * * @return void * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) */ protected function _construct() { @@ -168,7 +169,7 @@ protected function _construct() ) . '\' ' . '\'' . $this->getUrl( '*/*/delete', [$this->_objectId => $this->getRequest()->getParam($this->_objectId), 'ret' => 'pending'] - ) . '\'' . ')' + ) . '\'' . ', {data: {}})' ); $this->_coreRegistry->register('ret', 'pending'); } diff --git a/app/code/Magento/Review/Controller/Adminhtml/Product/Delete.php b/app/code/Magento/Review/Controller/Adminhtml/Product/Delete.php index 75015d65e1a18..68f178911dc7c 100644 --- a/app/code/Magento/Review/Controller/Adminhtml/Product/Delete.php +++ b/app/code/Magento/Review/Controller/Adminhtml/Product/Delete.php @@ -18,20 +18,23 @@ public function execute() /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); $reviewId = $this->getRequest()->getParam('id', false); - try { - $this->reviewFactory->create()->setId($reviewId)->aggregate()->delete(); + if ($this->getRequest()->isPost()) { + try { + $this->reviewFactory->create()->setId($reviewId)->aggregate()->delete(); - $this->messageManager->addSuccess(__('The review has been deleted.')); - if ($this->getRequest()->getParam('ret') == 'pending') { - $resultRedirect->setPath('review/*/pending'); - } else { - $resultRedirect->setPath('review/*/'); + $this->messageManager->addSuccess(__('The review has been deleted.')); + if ($this->getRequest()->getParam('ret') == 'pending') { + $resultRedirect->setPath('review/*/pending'); + } else { + $resultRedirect->setPath('review/*/'); + } + + return $resultRedirect; + } catch (\Magento\Framework\Exception\LocalizedException $e) { + $this->messageManager->addError($e->getMessage()); + } catch (\Exception $e) { + $this->messageManager->addException($e, __('Something went wrong deleting this review.')); } - return $resultRedirect; - } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); - } catch (\Exception $e) { - $this->messageManager->addException($e, __('Something went wrong deleting this review.')); } return $resultRedirect->setPath('review/*/edit/', ['id' => $reviewId]); diff --git a/app/code/Magento/Review/Controller/Adminhtml/Product/MassDelete.php b/app/code/Magento/Review/Controller/Adminhtml/Product/MassDelete.php index c792540000233..954c393276c14 100644 --- a/app/code/Magento/Review/Controller/Adminhtml/Product/MassDelete.php +++ b/app/code/Magento/Review/Controller/Adminhtml/Product/MassDelete.php @@ -13,9 +13,14 @@ class MassDelete extends ProductController { /** * @return \Magento\Backend\Model\View\Result\Redirect + * @throws \Magento\Framework\Exception\NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new \Magento\Framework\Exception\NotFoundException(__('Page not found.')); + } + $reviewsIds = $this->getRequest()->getParam('reviews'); if (!is_array($reviewsIds)) { $this->messageManager->addError(__('Please select review(s).')); diff --git a/app/code/Magento/Review/Controller/Adminhtml/Product/MassUpdateStatus.php b/app/code/Magento/Review/Controller/Adminhtml/Product/MassUpdateStatus.php index 2769a35ba9a48..a5850a6896321 100644 --- a/app/code/Magento/Review/Controller/Adminhtml/Product/MassUpdateStatus.php +++ b/app/code/Magento/Review/Controller/Adminhtml/Product/MassUpdateStatus.php @@ -13,9 +13,14 @@ class MassUpdateStatus extends ProductController { /** * @return \Magento\Backend\Model\View\Result\Redirect + * @throws \Magento\Framework\Exception\NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new \Magento\Framework\Exception\NotFoundException(__('Page not found.')); + } + $reviewsIds = $this->getRequest()->getParam('reviews'); if (!is_array($reviewsIds)) { $this->messageManager->addError(__('Please select review(s).')); diff --git a/app/code/Magento/Review/Controller/Adminhtml/Product/MassVisibleIn.php b/app/code/Magento/Review/Controller/Adminhtml/Product/MassVisibleIn.php index eca37d3fe24da..759ec36b9e834 100644 --- a/app/code/Magento/Review/Controller/Adminhtml/Product/MassVisibleIn.php +++ b/app/code/Magento/Review/Controller/Adminhtml/Product/MassVisibleIn.php @@ -13,9 +13,14 @@ class MassVisibleIn extends ProductController { /** * @return \Magento\Backend\Model\View\Result\Redirect + * @throws \Magento\Framework\Exception\NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new \Magento\Framework\Exception\NotFoundException(__('Page not found.')); + } + $reviewsIds = $this->getRequest()->getParam('reviews'); if (!is_array($reviewsIds)) { $this->messageManager->addError(__('Please select review(s).')); diff --git a/app/code/Magento/Review/Controller/Adminhtml/Rating/Delete.php b/app/code/Magento/Review/Controller/Adminhtml/Rating/Delete.php index 5535c3de26e43..c5610d135222a 100644 --- a/app/code/Magento/Review/Controller/Adminhtml/Rating/Delete.php +++ b/app/code/Magento/Review/Controller/Adminhtml/Rating/Delete.php @@ -5,6 +5,7 @@ */ namespace Magento\Review\Controller\Adminhtml\Rating; +use Magento\Framework\Exception\NotFoundException; use Magento\Review\Controller\Adminhtml\Rating as RatingController; use Magento\Framework\Controller\ResultFactory; @@ -12,19 +13,25 @@ class Delete extends RatingController { /** * @return \Magento\Backend\Model\View\Result\Redirect + * @throws NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); - if ($this->getRequest()->getParam('id') > 0) { + $ratingId = (int)$this->getRequest()->getParam('id'); + if ($ratingId) { try { /** @var \Magento\Review\Model\Rating $model */ $model = $this->_objectManager->create(\Magento\Review\Model\Rating::class); - $model->load($this->getRequest()->getParam('id'))->delete(); - $this->messageManager->addSuccess(__('You deleted the rating.')); + $model->load($ratingId)->delete(); + $this->messageManager->addSuccessMessage(__('You deleted the rating.')); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $resultRedirect->setPath('review/rating/edit', ['id' => $this->getRequest()->getParam('id')]); return $resultRedirect; } diff --git a/app/code/Magento/Review/Controller/Product/Post.php b/app/code/Magento/Review/Controller/Product/Post.php index be18f8fe25bbe..67c38f25d7ce3 100644 --- a/app/code/Magento/Review/Controller/Product/Post.php +++ b/app/code/Magento/Review/Controller/Product/Post.php @@ -22,7 +22,7 @@ public function execute() { /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); - if (!$this->formKeyValidator->validate($this->getRequest())) { + if (!$this->getRequest()->isPost() || !$this->formKeyValidator->validate($this->getRequest())) { $resultRedirect->setUrl($this->_redirect->getRefererUrl()); return $resultRedirect; } diff --git a/app/code/Magento/Review/Test/Unit/Controller/Product/PostTest.php b/app/code/Magento/Review/Test/Unit/Controller/Product/PostTest.php index 1526e80f8190a..73e85a7cdc179 100644 --- a/app/code/Magento/Review/Test/Unit/Controller/Product/PostTest.php +++ b/app/code/Magento/Review/Test/Unit/Controller/Product/PostTest.php @@ -105,7 +105,8 @@ class PostTest extends \PHPUnit\Framework\TestCase protected function setUp() { $this->redirect = $this->createMock(\Magento\Framework\App\Response\RedirectInterface::class); - $this->request = $this->createPartialMock(\Magento\Framework\App\Request\Http::class, ['getParam']); + $this->request = $this->createPartialMock(\Magento\Framework\App\Request\Http::class, ['getParam', 'isPost']); + $this->request->expects($this->any())->method('isPost')->willReturn(true); $this->response = $this->createPartialMock(\Magento\Framework\App\Response\Http::class, ['setRedirect']); $this->formKeyValidator = $this->createPartialMock( \Magento\Framework\Data\Form\FormKey\Validator::class, @@ -215,12 +216,12 @@ public function testExecute() $this->reviewSession->expects($this->any())->method('getFormData') ->with(true) ->willReturn($reviewData); - $this->request->expects($this->at(0))->method('getParam') - ->with('category', false) - ->willReturn(false); - $this->request->expects($this->at(1))->method('getParam') - ->with('id') - ->willReturn(1); + $this->request->expects($this->any())->method('getParam')->willReturnMap( + [ + ['category', false, false], + ['id', null, 1], + ] + ); $product = $this->createPartialMock( \Magento\Catalog\Model\Product::class, ['__wakeup', 'isVisibleInCatalog', 'isVisibleInSiteVisibility', 'getId', 'getWebsiteIds'] diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create.php index 083055994a282..a26ddc2961765 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create.php @@ -81,7 +81,7 @@ protected function _construct() $this->buttonList->update( 'reset', 'onclick', - 'deleteConfirm(\'' . $confirm . '\', \'' . $this->getCancelUrl() . '\')' + 'deleteConfirm(\'' . $confirm . '\', \'' . $this->getCancelUrl() . '\', {data: {}})' ); } diff --git a/app/code/Magento/Sales/Block/Status/Grid/Column/Unassign.php b/app/code/Magento/Sales/Block/Status/Grid/Column/Unassign.php index b413951d9d4f3..9df515a6c03ba 100644 --- a/app/code/Magento/Sales/Block/Status/Grid/Column/Unassign.php +++ b/app/code/Magento/Sales/Block/Status/Grid/Column/Unassign.php @@ -36,9 +36,16 @@ public function decorateAction($value, $row, $column, $isExport) $cell = ''; $state = $row->getState(); if (!empty($state)) { - $url = $this->getUrl('*/*/unassign', ['status' => $row->getStatus(), 'state' => $row->getState()]); + $url = $this->getUrl('*/*/unassign'); $label = __('Unassign'); - $cell = '<a href="' . $url . '">' . $label . '</a>'; + $cell = '<a href="#" data-post="' + .$this->escapeHtmlAttr( + \json_encode([ + 'action' => $url, + 'data' => ['status' => $row->getStatus(), 'state' => $row->getState()] + ]) + ) + .'">' . $label . '</a>'; } return $cell; } diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Cancel.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Cancel.php index de41c3c737968..7e41c7417b38d 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Cancel.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Cancel.php @@ -24,18 +24,18 @@ public function execute() { $resultRedirect = $this->resultRedirectFactory->create(); if (!$this->isValidPostRequest()) { - $this->messageManager->addError(__('You have not canceled the item.')); + $this->messageManager->addErrorMessage(__('You have not canceled the item.')); return $resultRedirect->setPath('sales/*/'); } $order = $this->_initOrder(); if ($order) { try { $this->orderManagement->cancel($order->getEntityId()); - $this->messageManager->addSuccess(__('You canceled the order.')); + $this->messageManager->addSuccessMessage(__('You canceled the order.')); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError(__('You have not canceled the item.')); + $this->messageManager->addErrorMessage(__('You have not canceled the item.')); $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); } return $resultRedirect->setPath('sales/order/view', ['order_id' => $order->getId()]); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/Save.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/Save.php index 2d22c3343c2e7..efe5ed5b7332a 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/Save.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/Save.php @@ -6,7 +6,7 @@ namespace Magento\Sales\Controller\Adminhtml\Order\Create; -use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\Exception\NotFoundException; use Magento\Framework\Exception\PaymentException; class Save extends \Magento\Sales\Controller\Adminhtml\Order\Create @@ -15,6 +15,7 @@ class Save extends \Magento\Sales\Controller\Adminhtml\Order\Create * Saving quote and create order * * @return \Magento\Framework\Controller\ResultInterface + * @throws NotFoundException * * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ @@ -23,6 +24,10 @@ public function execute() $path = 'sales/*/'; $pathParams = []; + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + try { // check if the creation of a new customer is allowed if (!$this->_authorization->isAllowed('Magento_Customer::manage') diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/Save.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/Save.php index 826a2a2a8b6c1..f71f567467bff 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/Save.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/Save.php @@ -6,7 +6,7 @@ namespace Magento\Sales\Controller\Adminhtml\Order\Creditmemo; use Magento\Backend\App\Action; -use Magento\Sales\Model\Order; +use Magento\Framework\Exception\NotFoundException; use Magento\Sales\Model\Order\Email\Sender\CreditmemoSender; class Save extends \Magento\Backend\App\Action @@ -56,12 +56,17 @@ public function __construct( * We can save only new creditmemo. Existing creditmemos are not editable * * @return \Magento\Backend\Model\View\Result\Redirect|\Magento\Backend\Model\View\Result\Forward + * @throws NotFoundException * * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + $resultRedirect = $this->resultRedirectFactory->create(); $data = $this->getRequest()->getPost('creditmemo'); if (!empty($data['comment_text'])) { @@ -109,7 +114,7 @@ public function execute() $this->creditmemoSender->send($creditmemo); } - $this->messageManager->addSuccess(__('You created the credit memo.')); + $this->messageManager->addSuccessMessage(__('You created the credit memo.')); $this->_getSession()->getCommentText(true); $resultRedirect->setPath('sales/order/view', ['order_id' => $creditmemo->getOrderId()]); return $resultRedirect; @@ -119,11 +124,11 @@ public function execute() return $resultForward; } } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $this->_getSession()->setFormData($data); } catch (\Exception $e) { $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); - $this->messageManager->addError(__('We can\'t save the credit memo right now.')); + $this->messageManager->addErrorMessage(__('We can\'t save the credit memo right now.')); } $resultRedirect->setPath('sales/*/new', ['_current' => true]); return $resultRedirect; diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/UpdateQty.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/UpdateQty.php index bfd95666e7c48..e445f98c5aa54 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/UpdateQty.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/UpdateQty.php @@ -65,6 +65,10 @@ public function __construct( public function execute() { try { + if (!$this->getRequest()->isPost()) { + throw new \Magento\Framework\Exception\LocalizedException(__('Invalid request type.')); + } + $this->creditmemoLoader->setOrderId($this->getRequest()->getParam('order_id')); $this->creditmemoLoader->setCreditmemoId($this->getRequest()->getParam('creditmemo_id')); $this->creditmemoLoader->setCreditmemo($this->getRequest()->getParam('creditmemo')); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/UpdateQty.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/UpdateQty.php index cdb4114f70976..de853bcb6f591 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/UpdateQty.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/UpdateQty.php @@ -7,7 +7,6 @@ namespace Magento\Sales\Controller\Adminhtml\Order\Invoice; use Magento\Framework\Exception\LocalizedException; -use Magento\Backend\App\Action; use Magento\Framework\Controller\Result\JsonFactory; use Magento\Framework\View\Result\PageFactory; use Magento\Framework\Controller\Result\RawFactory; @@ -74,27 +73,27 @@ public function __construct( public function execute() { try { + if (!$this->getRequest()->isPost()) { + throw new LocalizedException(__('Invalid request type.')); + } + $orderId = $this->getRequest()->getParam('order_id'); $invoiceData = $this->getRequest()->getParam('invoice', []); $invoiceItems = isset($invoiceData['items']) ? $invoiceData['items'] : []; /** @var \Magento\Sales\Model\Order $order */ $order = $this->_objectManager->create(\Magento\Sales\Model\Order::class)->load($orderId); if (!$order->getId()) { - throw new \Magento\Framework\Exception\LocalizedException(__('The order no longer exists.')); + throw new LocalizedException(__('The order no longer exists.')); } if (!$order->canInvoice()) { - throw new \Magento\Framework\Exception\LocalizedException( - __('The order does not allow an invoice to be created.') - ); + throw new LocalizedException(__('The order does not allow an invoice to be created.')); } $invoice = $this->invoiceService->prepareInvoice($order, $invoiceItems); if (!$invoice->getTotalQty()) { - throw new \Magento\Framework\Exception\LocalizedException( - __('You can\'t create an invoice without products.') - ); + throw new LocalizedException(__('You can\'t create an invoice without products.')); } $this->registry->register('current_invoice', $invoice); // Save invoice comment text in current invoice object in order to display it in corresponding view diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/AssignPost.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/AssignPost.php index 89820b41a68da..3b98d206d5f66 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/AssignPost.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/AssignPost.php @@ -26,18 +26,18 @@ public function execute() if ($status && $status->getStatus()) { try { $status->assignState($state, $isDefault, $visibleOnFront); - $this->messageManager->addSuccess(__('You assigned the order status.')); + $this->messageManager->addSuccessMessage(__('You assigned the order status.')); return $resultRedirect->setPath('sales/*/'); } 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, __('Something went wrong while assigning the order status.') ); } } else { - $this->messageManager->addError(__('We can\'t find this order status.')); + $this->messageManager->addErrorMessage(__('We can\'t find this order status.')); } return $resultRedirect->setPath('sales/*/assign'); } diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Save.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Save.php index 849a7e2d0c817..06fa61c3263de 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Save.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Save.php @@ -12,6 +12,7 @@ class Save extends \Magento\Sales\Controller\Adminhtml\Order\Status * Save status form processing * * @return \Magento\Backend\Model\View\Result\Redirect + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function execute() { @@ -40,7 +41,9 @@ public function execute() $status = $this->_objectManager->create(\Magento\Sales\Model\Order\Status::class)->load($statusCode); // check if status exist if ($isNew && $status->getStatus()) { - $this->messageManager->addError(__('We found another order status with the same order status code.')); + $this->messageManager->addErrorMessage( + __('We found another order status with the same order status code.') + ); $this->_getSession()->setFormData($data); return $resultRedirect->setPath('sales/*/new'); } @@ -49,12 +52,12 @@ public function execute() try { $status->save(); - $this->messageManager->addSuccess(__('You saved the order status.')); + $this->messageManager->addSuccessMessage(__('You saved the order status.')); return $resultRedirect->setPath('sales/*/'); - } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + } catch (LocalizedException $e) { + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException( + $this->messageManager->addExceptionMessage( $e, __('We can\'t add the order status right now.') ); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Unassign.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Unassign.php index 04db430e1ffa4..44238725ef65c 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Unassign.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Status/Unassign.php @@ -6,29 +6,36 @@ */ namespace Magento\Sales\Controller\Adminhtml\Order\Status; +use Magento\Framework\Exception\NotFoundException; + class Unassign extends \Magento\Sales\Controller\Adminhtml\Order\Status { /** * @return \Magento\Backend\Model\View\Result\Redirect + * @throws NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + $state = $this->getRequest()->getParam('state'); $status = $this->_initStatus(); if ($status) { try { $status->unassignState($state); - $this->messageManager->addSuccess(__('You have unassigned the order status.')); + $this->messageManager->addSuccessMessage(__('You have unassigned the order status.')); } 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, __('Something went wrong while unassigning the order.') ); } } else { - $this->messageManager->addError(__('We can\'t find this order status.')); + $this->messageManager->addErrorMessage(__('We can\'t find this order status.')); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/CancelTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/CancelTest.php index 363f78e738f12..64c73f8f36c23 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/CancelTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/CancelTest.php @@ -74,10 +74,12 @@ protected function setUp() ['setRedirect', 'sendResponse'] ); $this->request = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) - ->disableOriginalConstructor()->getMock(); + ->disableOriginalConstructor() + ->getMock(); + $this->request->expects($this->any())->method('isPost')->willReturn(true); $this->messageManager = $this->createPartialMock( \Magento\Framework\Message\Manager::class, - ['addSuccess', 'addError'] + ['addSuccessMessage', 'addErrorMessage'] ); $this->orderRepositoryMock = $this->getMockBuilder(\Magento\Sales\Api\OrderRepositoryInterface::class) ->disableOriginalConstructor() @@ -101,8 +103,8 @@ protected function setUp() \Magento\Sales\Controller\Adminhtml\Order\Cancel::class, [ 'context' => $this->context, - 'request' => $this->request, - 'response' => $this->response, + //'request' => $this->request, + //'response' => $this->response, 'orderRepository' => $this->orderRepositoryMock ] ); @@ -117,7 +119,7 @@ public function testExecuteNotPost() ->method('isPost') ->willReturn(false); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('You have not canceled the item.'); $this->resultRedirect->expects($this->once()) ->method('setPath') diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/SaveTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/SaveTest.php index cc2bf929f8250..31e5318aba409 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/SaveTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/SaveTest.php @@ -77,6 +77,7 @@ protected function setUp() $this->_responseMock = $this->createMock(\Magento\Framework\App\Response\Http::class); $this->_responseMock->headersSentThrowsException = false; $this->_requestMock = $this->createMock(\Magento\Framework\App\Request\Http::class); + $this->_requestMock->expects($this->any())->method('isPost')->willReturn(true); $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $constructArguments = $objectManager->getConstructArguments(\Magento\Backend\Model\Session::class, ['storage' => new \Magento\Framework\Session\Storage()] @@ -230,7 +231,7 @@ public function testSaveActionWithNegativeCreditmemo() */ protected function _setSaveActionExpectationForMageCoreException($data, $errorMessage) { - $this->_messageManager->expects($this->once())->method('addError')->with($this->equalTo($errorMessage)); + $this->_messageManager->expects($this->once())->method('addErrorMessage')->with($this->equalTo($errorMessage)); $this->_sessionMock->expects($this->once())->method('setFormData')->with($this->equalTo($data)); } } diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/UpdateQtyTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/UpdateQtyTest.php index e2554eefb9b4e..47c6aea844aca 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/UpdateQtyTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Creditmemo/UpdateQtyTest.php @@ -115,6 +115,7 @@ protected function setUp() $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) ->disableOriginalConstructor() ->getMock(); + $this->requestMock->expects($this->any())->method('isPost')->willReturn(true); $this->responseMock = $this->getMockBuilder(\Magento\Framework\App\Response\Http::class) ->disableOriginalConstructor() ->getMock(); diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/UpdateQtyTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/UpdateQtyTest.php index 899e3defc19a8..90374a2597a02 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/UpdateQtyTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/UpdateQtyTest.php @@ -89,8 +89,8 @@ protected function setUp() ->getMock(); $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) ->disableOriginalConstructor() - ->setMethods([]) ->getMock(); + $this->requestMock->expects($this->any())->method('isPost')->willReturn(true); $this->responseMock = $this->getMockBuilder(\Magento\Framework\App\Response\Http::class) ->disableOriginalConstructor() ->getMock(); @@ -191,14 +191,13 @@ public function testExecute() $invoiceData = ['comment_text' => 'test']; $response = 'test data'; - $this->requestMock->expects($this->at(0)) - ->method('getParam') - ->with('order_id') - ->will($this->returnValue($orderId)); - $this->requestMock->expects($this->at(1)) - ->method('getParam') - ->with('invoice', []) - ->will($this->returnValue($invoiceData)); + $this->requestMock->expects($this->any())->method('getParam') + ->willReturnMap( + [ + ['order_id', null, $orderId], + ['invoice', [], $invoiceData], + ] + ); $invoiceMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Invoice::class) ->disableOriginalConstructor() diff --git a/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/DeleteButton.php b/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/DeleteButton.php index 0cb286056d825..bee7573c1fe2a 100644 --- a/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/DeleteButton.php +++ b/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/DeleteButton.php @@ -26,7 +26,7 @@ public function getButtonData() 'class' => 'delete', 'on_click' => 'deleteConfirm(\'' . __( 'Are you sure you want to delete this?' - ) . '\', \'' . $this->urlBuilder->getUrl('*/*/delete', ['id' => $ruleId]) . '\')', + ) . '\', \'' . $this->urlBuilder->getUrl('*/*/delete', ['id' => $ruleId]) . '\', {data: {}})', 'sort_order' => 20, ]; } diff --git a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/CouponsMassDelete.php b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/CouponsMassDelete.php index dcbc290f98579..a55d4c86cfef7 100644 --- a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/CouponsMassDelete.php +++ b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/CouponsMassDelete.php @@ -12,9 +12,14 @@ class CouponsMassDelete extends \Magento\SalesRule\Controller\Adminhtml\Promo\Qu * Coupons mass delete action * * @return void + * @throws \Magento\Framework\Exception\NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new \Magento\Framework\Exception\NotFoundException(__('Page not found.')); + } + $this->_initRule(); $rule = $this->_coreRegistry->registry(\Magento\SalesRule\Model\RegistryConstants::CURRENT_SALES_RULE); diff --git a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Delete.php b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Delete.php index 9adb62583985d..b505fd1d6a0fb 100644 --- a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Delete.php +++ b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/Delete.php @@ -6,28 +6,35 @@ */ namespace Magento\SalesRule\Controller\Adminhtml\Promo\Quote; +use Magento\Framework\Exception\NotFoundException; + class Delete extends \Magento\SalesRule\Controller\Adminhtml\Promo\Quote { /** * Delete promo quote action * * @return void + * @throws NotFoundException */ public function execute() { - $id = $this->getRequest()->getParam('id'); + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + + $id = (int)$this->getRequest()->getParam('id'); if ($id) { try { $model = $this->_objectManager->create(\Magento\SalesRule\Model\Rule::class); $model->load($id); $model->delete(); - $this->messageManager->addSuccess(__('You deleted the rule.')); + $this->messageManager->addSuccessMessage(__('You deleted the rule.')); $this->_redirect('sales_rule/*/'); return; } catch (\Magento\Framework\Exception\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 the rule right now. Please review the log and try again.') ); $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); @@ -35,7 +42,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('sales_rule/*/'); } } diff --git a/app/code/Magento/SalesRule/Test/Unit/Block/Adminhtml/Promo/Quote/Edit/DeleteButtonTest.php b/app/code/Magento/SalesRule/Test/Unit/Block/Adminhtml/Promo/Quote/Edit/DeleteButtonTest.php index fb01476ed6b34..a19de4ff2ef90 100644 --- a/app/code/Magento/SalesRule/Test/Unit/Block/Adminhtml/Promo/Quote/Edit/DeleteButtonTest.php +++ b/app/code/Magento/SalesRule/Test/Unit/Block/Adminhtml/Promo/Quote/Edit/DeleteButtonTest.php @@ -62,7 +62,7 @@ public function testGetButtonData() 'class' => 'delete', 'on_click' => 'deleteConfirm(\'' . __( 'Are you sure you want to delete this?' - ) . '\', \'' . $deleteUrl . '\')', + ) . '\', \'' . $deleteUrl . '\', {data: {}})', 'sort_order' => 20, ]; diff --git a/app/code/Magento/Search/Block/Adminhtml/Synonyms/Edit/DeleteButton.php b/app/code/Magento/Search/Block/Adminhtml/Synonyms/Edit/DeleteButton.php index a73edcce99760..6c54d80a61319 100644 --- a/app/code/Magento/Search/Block/Adminhtml/Synonyms/Edit/DeleteButton.php +++ b/app/code/Magento/Search/Block/Adminhtml/Synonyms/Edit/DeleteButton.php @@ -24,7 +24,7 @@ public function getButtonData() 'class' => 'delete', 'on_click' => 'deleteConfirm(\'' . __('Are you sure you want to delete this synonym group?') - . '\', \'' . $this->getDeleteUrl() . '\')', + . '\', \'' . $this->getDeleteUrl() . '\', {data: {}})', 'sort_order' => 20, ]; } diff --git a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Delete.php b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Delete.php index 9d8b612cefadf..e531e947a5ab5 100644 --- a/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Delete.php +++ b/app/code/Magento/Search/Controller/Adminhtml/Synonyms/Delete.php @@ -55,7 +55,7 @@ public function execute() $id = $this->getRequest()->getParam('group_id'); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); - if ($id) { + if ($this->getRequest()->isPost() && $id) { try { /** @var \Magento\Search\Model\SynonymGroup $synGroupModel */ $synGroupModel = $this->synGroupRepository->get($id); diff --git a/app/code/Magento/Search/Controller/Adminhtml/Term/Delete.php b/app/code/Magento/Search/Controller/Adminhtml/Term/Delete.php index c7adf32da0fb0..52ff4a0388634 100644 --- a/app/code/Magento/Search/Controller/Adminhtml/Term/Delete.php +++ b/app/code/Magento/Search/Controller/Adminhtml/Term/Delete.php @@ -5,6 +5,7 @@ */ namespace Magento\Search\Controller\Adminhtml\Term; +use Magento\Framework\Exception\NotFoundException; use Magento\Search\Controller\Adminhtml\Term as TermController; use Magento\Framework\Controller\ResultFactory; @@ -12,10 +13,15 @@ class Delete extends TermController { /** * @return \Magento\Backend\Model\View\Result\Redirect + * @throws NotFoundException */ public function execute() { - $id = $this->getRequest()->getParam('id'); + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + + $id = (int)$this->getRequest()->getParam('id'); /** @var \Magento\Backend\Model\View\Result\Redirect $redirectResult */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); if ($id) { diff --git a/app/code/Magento/Search/Controller/Adminhtml/Term/MassDelete.php b/app/code/Magento/Search/Controller/Adminhtml/Term/MassDelete.php index f6874078f2f64..449450eeb8fd7 100644 --- a/app/code/Magento/Search/Controller/Adminhtml/Term/MassDelete.php +++ b/app/code/Magento/Search/Controller/Adminhtml/Term/MassDelete.php @@ -5,6 +5,7 @@ */ namespace Magento\Search\Controller\Adminhtml\Term; +use Magento\Framework\Exception\NotFoundException; use Magento\Search\Controller\Adminhtml\Term as TermController; use Magento\Framework\Controller\ResultFactory; @@ -12,9 +13,14 @@ class MassDelete extends TermController { /** * @return \Magento\Backend\Model\View\Result\Redirect + * @throws NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + $searchIds = $this->getRequest()->getParam('search'); if (!is_array($searchIds)) { $this->messageManager->addErrorMessage(__('Please select searches.')); diff --git a/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Synonyms/DeleteTest.php b/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Synonyms/DeleteTest.php index 38c78b986faf4..164f82da02f6f 100644 --- a/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Synonyms/DeleteTest.php +++ b/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Synonyms/DeleteTest.php @@ -55,8 +55,9 @@ protected function setUp() false, true, true, - ['getParam'] + ['getParam', 'isPost'] ); + $this->requestMock->expects($this->any())->method('isPost')->willReturn(true); $this->objectManagerMock = $this->getMockBuilder(\Magento\Framework\ObjectManager\ObjectManager::class) ->disableOriginalConstructor() diff --git a/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Term/MassDeleteTest.php b/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Term/MassDeleteTest.php index 60cc958a6187c..a9a705b74a8bb 100644 --- a/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Term/MassDeleteTest.php +++ b/app/code/Magento/Search/Test/Unit/Controller/Adminhtml/Term/MassDeleteTest.php @@ -46,7 +46,7 @@ protected function setUp() { $this->request = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) ->disableOriginalConstructor() - ->setMethods([]) + ->setMethods(['isPost']) ->getMockForAbstractClass(); $this->objectManager = $this->getMockBuilder(\Magento\Framework\ObjectManagerInterface::class) ->disableOriginalConstructor() @@ -85,6 +85,7 @@ protected function setUp() $this->context->expects($this->any()) ->method('getResultFactory') ->willReturn($this->resultFactoryMock); + $this->request->expects($this->any())->method('isPost')->willReturn(true); $this->objectManagerHelper = new ObjectManagerHelper($this); $this->controller = $this->objectManagerHelper->getObject( diff --git a/app/code/Magento/Search/Ui/Component/Listing/Column/SynonymActions.php b/app/code/Magento/Search/Ui/Component/Listing/Column/SynonymActions.php index 8cc9b809ff888..5495b93a0828e 100644 --- a/app/code/Magento/Search/Ui/Component/Listing/Column/SynonymActions.php +++ b/app/code/Magento/Search/Ui/Component/Listing/Column/SynonymActions.php @@ -63,7 +63,8 @@ public function prepareDataSource(array $dataSource) 'confirm' => [ 'title' => __('Delete'), 'message' => __('Are you sure you want to delete synonym group with id: %1?', $item['group_id']) - ] + ], + 'post' => true, ]; $item[$name]['edit'] = [ 'href' => $this->urlBuilder->getUrl(self::SYNONYM_URL_PATH_EDIT, ['group_id' => $item['group_id']]), diff --git a/app/code/Magento/Shipping/Block/Adminhtml/View.php b/app/code/Magento/Shipping/Block/Adminhtml/View.php index 04df7f6e35e24..711dd2f46c1c6 100644 --- a/app/code/Magento/Shipping/Block/Adminhtml/View.php +++ b/app/code/Magento/Shipping/Block/Adminhtml/View.php @@ -58,7 +58,7 @@ protected function _construct() 'onclick', "deleteConfirm('" . __( 'Are you sure you want to send a Shipment email to customer?' - ) . "', '" . $this->getEmailUrl() . "')" + ) . "', '" . $this->getEmailUrl() . "', {data: {}})" ); } diff --git a/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save.php b/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save.php index 5eca94bce562b..5a81bdb3cf32f 100644 --- a/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save.php +++ b/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save.php @@ -6,7 +6,6 @@ */ namespace Magento\Shipping\Controller\Adminhtml\Order\Shipment; -use Magento\Backend\App\Action; use Magento\Sales\Model\Order\Shipment\Validation\QuantityValidator; /** @@ -97,7 +96,7 @@ public function execute() $formKeyIsValid = $this->_formKeyValidator->validate($this->getRequest()); $isPost = $this->getRequest()->isPost(); if (!$formKeyIsValid || !$isPost) { - $this->messageManager->addError(__('We can\'t save the shipment right now.')); + $this->messageManager->addErrorMessage(__('We can\'t save the shipment right now.')); return $resultRedirect->setPath('sales/order/index'); } @@ -108,7 +107,6 @@ public function execute() } $isNeedCreateLabel = isset($data['create_shipping_label']) && $data['create_shipping_label']; - $responseAjax = new \Magento\Framework\DataObject(); try { @@ -136,7 +134,7 @@ public function execute() ->validate($shipment, [QuantityValidator::class]); if ($validationResult->hasMessages()) { - $this->messageManager->addError( + $this->messageManager->addErrorMessage( __("Shipment Document Validation Error(s):\n" . implode("\n", $validationResult->getMessages())) ); $this->_redirect('*/*/new', ['order_id' => $this->getRequest()->getParam('order_id')]); @@ -160,7 +158,7 @@ public function execute() $shipmentCreatedMessage = __('The shipment has been created.'); $labelCreatedMessage = __('You created the shipping label.'); - $this->messageManager->addSuccess( + $this->messageManager->addSuccessMessage( $isNeedCreateLabel ? $shipmentCreatedMessage . ' ' . $labelCreatedMessage : $shipmentCreatedMessage ); $this->_objectManager->get(\Magento\Backend\Model\Session::class)->getCommentText(true); @@ -169,7 +167,7 @@ public function execute() $responseAjax->setError(true); $responseAjax->setMessage($e->getMessage()); } else { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $this->_redirect('*/*/new', ['order_id' => $this->getRequest()->getParam('order_id')]); } } catch (\Exception $e) { @@ -178,7 +176,7 @@ public function execute() $responseAjax->setError(true); $responseAjax->setMessage(__('An error occurred while creating shipping label.')); } else { - $this->messageManager->addError(__('Cannot save shipment.')); + $this->messageManager->addErrorMessage(__('Cannot save shipment.')); $this->_redirect('*/*/new', ['order_id' => $this->getRequest()->getParam('order_id')]); } } diff --git a/app/code/Magento/Shipping/Test/Unit/Controller/Adminhtml/Order/Shipment/SaveTest.php b/app/code/Magento/Shipping/Test/Unit/Controller/Adminhtml/Order/Shipment/SaveTest.php index 491a1e01f1720..730088c76eada 100644 --- a/app/code/Magento/Shipping/Test/Unit/Controller/Adminhtml/Order/Shipment/SaveTest.php +++ b/app/code/Magento/Shipping/Test/Unit/Controller/Adminhtml/Order/Shipment/SaveTest.php @@ -135,7 +135,7 @@ protected function setUp() $this->request = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) ->disableOriginalConstructor()->getMock(); $this->objectManager = $this->createPartialMock(\Magento\Framework\ObjectManager\ObjectManager::class, ['create', 'get']); - $this->messageManager = $this->createPartialMock(\Magento\Framework\Message\Manager::class, ['addSuccess', 'addError']); + $this->messageManager = $this->createPartialMock(\Magento\Framework\Message\Manager::class, ['addSuccessMessage', 'addErrorMessage']); $this->session = $this->createPartialMock(\Magento\Backend\Model\Session::class, ['setIsUrlNotice', 'getCommentText']); $this->actionFlag = $this->createPartialMock(\Magento\Framework\App\ActionFlag::class, ['get']); $this->helper = $this->createPartialMock(\Magento\Backend\Helper\Data::class, ['getUrl']); @@ -216,7 +216,7 @@ public function testExecute($formKeyIsValid, $isPost) if (!$formKeyIsValid || !$isPost) { $this->messageManager->expects($this->once()) - ->method('addError'); + ->method('addErrorMessage'); $this->resultRedirect->expects($this->once()) ->method('setPath') diff --git a/app/code/Magento/Tax/Block/Adminhtml/Rate/Toolbar/Save.php b/app/code/Magento/Tax/Block/Adminhtml/Rate/Toolbar/Save.php index 50e7437396b0c..7bb7d70f70229 100644 --- a/app/code/Magento/Tax/Block/Adminhtml/Rate/Toolbar/Save.php +++ b/app/code/Magento/Tax/Block/Adminhtml/Rate/Toolbar/Save.php @@ -126,7 +126,7 @@ protected function _prepareLayout() ) . '\', \'' . $this->getUrl( 'tax/*/delete', ['rate' => $rate] - ) . '\')', + ) . '\', {data: {}})', 'class' => 'delete' ] ); diff --git a/app/code/Magento/Tax/Controller/Adminhtml/Rule/Delete.php b/app/code/Magento/Tax/Controller/Adminhtml/Rule/Delete.php index 71b6d7bf39396..45ad5acb7f033 100644 --- a/app/code/Magento/Tax/Controller/Adminhtml/Rule/Delete.php +++ b/app/code/Magento/Tax/Controller/Adminhtml/Rule/Delete.php @@ -7,28 +7,34 @@ namespace Magento\Tax\Controller\Adminhtml\Rule; use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\Exception\NotFoundException; class Delete extends \Magento\Tax\Controller\Adminhtml\Rule { /** * @return \Magento\Backend\Model\View\Result\Redirect + * @throws NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); $ruleId = (int)$this->getRequest()->getParam('rule'); try { $this->ruleService->deleteById($ruleId); - $this->messageManager->addSuccess(__('The tax rule has been deleted.')); + $this->messageManager->addSuccessMessage(__('The tax rule has been deleted.')); return $resultRedirect->setPath('tax/*/'); } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { - $this->messageManager->addError(__('This rule no longer exists.')); + $this->messageManager->addErrorMessage(__('This rule no longer exists.')); return $resultRedirect->setPath('tax/*/'); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError(__('Something went wrong deleting this tax rule.')); + $this->messageManager->addErrorMessage(__('Something went wrong deleting this tax rule.')); } return $resultRedirect->setUrl($this->_redirect->getRedirectUrl($this->getUrl('*'))); diff --git a/app/code/Magento/Theme/Block/Adminhtml/System/Design/Theme/Edit.php b/app/code/Magento/Theme/Block/Adminhtml/System/Design/Theme/Edit.php index 749a9f8dafac5..0ebbc794b7acd 100644 --- a/app/code/Magento/Theme/Block/Adminhtml/System/Design/Theme/Edit.php +++ b/app/code/Magento/Theme/Block/Adminhtml/System/Design/Theme/Edit.php @@ -77,7 +77,7 @@ protected function _prepareLayout() if ($theme->hasChildThemes()) { $message = __('Are you sure you want to delete this theme?'); $onClick = sprintf( - "deleteConfirm('%s', '%s')", + "deleteConfirm('%s', '%s', {data: {}})", $message, $this->getUrl('adminhtml/*/delete', ['id' => $theme->getId()]) ); diff --git a/app/code/Magento/Ui/Controller/Adminhtml/Bookmark/Save.php b/app/code/Magento/Ui/Controller/Adminhtml/Bookmark/Save.php index 9e43f1d11bed0..f7a1396c5e8c9 100644 --- a/app/code/Magento/Ui/Controller/Adminhtml/Bookmark/Save.php +++ b/app/code/Magento/Ui/Controller/Adminhtml/Bookmark/Save.php @@ -7,6 +7,7 @@ use Magento\Authorization\Model\UserContextInterface; use Magento\Backend\App\Action\Context; +use Magento\Framework\Exception\NotFoundException; use Magento\Framework\Json\DecoderInterface; use Magento\Framework\View\Element\UiComponentFactory; use Magento\Ui\Api\BookmarkManagementInterface; @@ -86,11 +87,16 @@ public function __construct( * Action for AJAX request * * @return void + * @throws NotFoundException + * @throws \InvalidArgumentException */ public function execute() { $bookmark = $this->bookmarkFactory->create(); $jsonData = $this->_request->getParam('data'); + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found.')); + } if (!$jsonData) { throw new \InvalidArgumentException('Invalid parameter "data"'); } diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/actions.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/actions.js index be7a1a13fbd61..c75f7797cf0f3 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/actions.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/actions.js @@ -11,8 +11,9 @@ define([ 'mageUtils', 'uiRegistry', './column', - 'Magento_Ui/js/modal/confirm' -], function (_, utils, registry, Column, confirm) { + 'Magento_Ui/js/modal/confirm', + 'mage/dataPost' +], function (_, utils, registry, Column, confirm, dataPost) { 'use strict'; return Column.extend({ @@ -267,7 +268,14 @@ define([ * @param {Object} action - Action's data. */ defaultCallback: function (actionIndex, recordId, action) { - window.location.href = action.href; + if (action.post) { + dataPost().postData({ + action: action.href, + data: {} + }); + } else { + window.location.href = action.href; + } }, /** diff --git a/app/code/Magento/UrlRewrite/Block/Edit.php b/app/code/Magento/UrlRewrite/Block/Edit.php index baee8af893083..3c823fb56e1d7 100644 --- a/app/code/Magento/UrlRewrite/Block/Edit.php +++ b/app/code/Magento/UrlRewrite/Block/Edit.php @@ -65,7 +65,7 @@ public function __construct( */ protected function _prepareLayout() { - $this->setTemplate('edit.phtml'); + $this->setTemplate('Magento_UrlRewrite::edit.phtml'); $this->_addBackButton(); $this->_prepareLayoutFeatures(); @@ -173,7 +173,7 @@ protected function _addDeleteButton() ['id' => $this->getUrlRewrite()->getId()] ) ) - . ')', + . ', {data: {}})', 'class' => 'scalable delete', 'level' => -1 ] diff --git a/app/code/Magento/UrlRewrite/Controller/Adminhtml/Url/Rewrite/Delete.php b/app/code/Magento/UrlRewrite/Controller/Adminhtml/Url/Rewrite/Delete.php index f8f7b145e2806..8a558d70e84fa 100644 --- a/app/code/Magento/UrlRewrite/Controller/Adminhtml/Url/Rewrite/Delete.php +++ b/app/code/Magento/UrlRewrite/Controller/Adminhtml/Url/Rewrite/Delete.php @@ -6,21 +6,29 @@ */ namespace Magento\UrlRewrite\Controller\Adminhtml\Url\Rewrite; +use Magento\Framework\Exception\NotFoundException; + class Delete extends \Magento\UrlRewrite\Controller\Adminhtml\Url\Rewrite { /** * URL rewrite delete action * * @return void + * @throws NotFoundException */ public function execute() { - if ($this->_getUrlRewrite()->getId()) { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + + $id = (int)$this->_getUrlRewrite()->getId(); + if ($id) { try { $this->_getUrlRewrite()->delete(); - $this->messageManager->addSuccess(__('You deleted the URL rewrite.')); + $this->messageManager->addSuccessMessage(__('You deleted the URL rewrite.')); } catch (\Exception $e) { - $this->messageManager->addException($e, __('We can\'t delete URL Rewrite right now.')); + $this->messageManager->addExceptionMessage($e, __('We can\'t delete URL Rewrite right now.')); $this->_redirect('adminhtml/*/edit/', ['id' => $this->_getUrlRewrite()->getId()]); return; } diff --git a/app/code/Magento/User/Block/Buttons.php b/app/code/Magento/User/Block/Buttons.php index bb7375ae83277..60cdf3341d8a6 100644 --- a/app/code/Magento/User/Block/Buttons.php +++ b/app/code/Magento/User/Block/Buttons.php @@ -34,6 +34,7 @@ public function __construct( /** * @return $this + * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) */ protected function _prepareLayout() { @@ -64,7 +65,7 @@ protected function _prepareLayout() ) . '\', \'' . $this->getUrl( '*/*/delete', ['rid' => $this->getRequest()->getParam('rid')] - ) . '\')', + ) . '\', {data: {}})', 'class' => 'delete' ] ); @@ -110,6 +111,7 @@ public function getSaveButtonHtml() /** * @return string|void + * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) */ public function getDeleteButtonHtml() { diff --git a/app/code/Magento/User/Block/User/Edit.php b/app/code/Magento/User/Block/User/Edit.php index 6e036cf20fa25..7f435829df30f 100644 --- a/app/code/Magento/User/Block/User/Edit.php +++ b/app/code/Magento/User/Block/User/Edit.php @@ -73,7 +73,7 @@ protected function _construct() 'label' => __('Force Sign-In'), 'class' => 'invalidate-token', 'onclick' => "deleteConfirm('" . $this->escapeJs($this->escapeHtml($deleteConfirmMsg)) . - "', '" . $this->getInvalidateUrl() . "')", + "', '" . $this->getInvalidateUrl() . "', {data: {}})", ] ); } diff --git a/app/code/Magento/User/Controller/Adminhtml/User/Role/SaveRole.php b/app/code/Magento/User/Controller/Adminhtml/User/Role/SaveRole.php index 7aa0d2368f67b..9019f4b3c7009 100644 --- a/app/code/Magento/User/Controller/Adminhtml/User/Role/SaveRole.php +++ b/app/code/Magento/User/Controller/Adminhtml/User/Role/SaveRole.php @@ -9,6 +9,7 @@ use Magento\Authorization\Model\Acl\Role\Group as RoleGroup; use Magento\Authorization\Model\UserContextInterface; use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\Exception\NotFoundException; use Magento\Framework\Exception\State\UserLockedException; use Magento\Security\Model\SecurityCookie; use Magento\Framework\Exception\LocalizedException; @@ -68,9 +69,13 @@ private function getSecurityCookie() * Role form submit action to save or create new role * * @return \Magento\Backend\Model\View\Result\Redirect + * @throws NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); @@ -86,7 +91,7 @@ public function execute() $role = $this->_initRole('role_id'); if (!$role->getId() && $rid) { - $this->messageManager->addError(__('This role no longer exists.')); + $this->messageManager->addErrorMessage(__('This role no longer exists.')); return $resultRedirect->setPath('adminhtml/*/'); } diff --git a/app/code/Magento/User/Controller/Adminhtml/User/Save.php b/app/code/Magento/User/Controller/Adminhtml/User/Save.php index d7d1c8b0e22a6..ebba96904aff5 100644 --- a/app/code/Magento/User/Controller/Adminhtml/User/Save.php +++ b/app/code/Magento/User/Controller/Adminhtml/User/Save.php @@ -44,6 +44,9 @@ public function execute() { $userId = (int)$this->getRequest()->getParam('user_id'); $data = $this->getRequest()->getPostValue(); + if (array_key_exists('form_key', $data)) { + unset($data['form_key']); + } if (!$data) { $this->_redirect('adminhtml/*/'); return; diff --git a/app/code/Magento/Widget/Controller/Adminhtml/Widget/BuildWidget.php b/app/code/Magento/Widget/Controller/Adminhtml/Widget/BuildWidget.php index d9ef20aa90e47..33c4992429490 100644 --- a/app/code/Magento/Widget/Controller/Adminhtml/Widget/BuildWidget.php +++ b/app/code/Magento/Widget/Controller/Adminhtml/Widget/BuildWidget.php @@ -37,10 +37,13 @@ public function __construct( */ public function execute() { - $type = $this->getRequest()->getPost('widget_type'); - $params = $this->getRequest()->getPost('parameters', []); - $asIs = $this->getRequest()->getPost('as_is'); - $html = $this->_widget->getWidgetDeclaration($type, $params, $asIs); + $html = ''; + if ($this->getRequest()->isPost()) { + $type = $this->getRequest()->getPost('widget_type'); + $params = $this->getRequest()->getPost('parameters', []); + $asIs = $this->getRequest()->getPost('as_is'); + $html = $this->_widget->getWidgetDeclaration($type, $params, $asIs); + } $this->getResponse()->setBody($html); } } diff --git a/app/code/Magento/Widget/Controller/Adminhtml/Widget/Index.php b/app/code/Magento/Widget/Controller/Adminhtml/Widget/Index.php index 6909f6058074e..c914f93e257f4 100644 --- a/app/code/Magento/Widget/Controller/Adminhtml/Widget/Index.php +++ b/app/code/Magento/Widget/Controller/Adminhtml/Widget/Index.php @@ -6,6 +6,8 @@ */ namespace Magento\Widget\Controller\Adminhtml\Widget; +use Magento\Framework\Exception\NotFoundException; + class Index extends \Magento\Backend\App\Action { /** @@ -41,12 +43,16 @@ public function __construct( } /** - * Wisywyg widget plugin main page + * Wysiwyg widget plugin main page * * @return void + * @throws NotFoundException */ public function execute() { + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } // save extra params for widgets insertion form $skipped = $this->getRequest()->getParam('skip_widgets'); $skipped = $this->_widgetConfig->decodeWidgetsFromQuery($skipped); diff --git a/app/code/Magento/Widget/Controller/Adminhtml/Widget/Instance/Save.php b/app/code/Magento/Widget/Controller/Adminhtml/Widget/Instance/Save.php index 98275c3b906db..0b38c5d99ca24 100644 --- a/app/code/Magento/Widget/Controller/Adminhtml/Widget/Instance/Save.php +++ b/app/code/Magento/Widget/Controller/Adminhtml/Widget/Instance/Save.php @@ -16,7 +16,7 @@ class Save extends \Magento\Widget\Controller\Adminhtml\Widget\Instance public function execute() { $widgetInstance = $this->_initWidgetInstance(); - if (!$widgetInstance) { + if (!$this->getRequest()->isPost() || !$widgetInstance) { $this->_redirect('adminhtml/*/'); return; } diff --git a/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractBackendController.php b/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractBackendController.php index 0845ef640aa0c..efa7cda029994 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractBackendController.php +++ b/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractBackendController.php @@ -5,10 +5,12 @@ */ namespace Magento\TestFramework\TestCase; +use Magento\Framework\App\Request\Http as HttpRequest; + /** - * A parent class for backend controllers - contains directives for admin user creation and authentication + * A parent class for backend controllers - contains directives for admin user creation and authentication. + * * @SuppressWarnings(PHPMD.NumberOfChildren) - * @SuppressWarnings(PHPMD.numberOfChildren) */ abstract class AbstractBackendController extends \Magento\TestFramework\TestCase\AbstractController { @@ -36,6 +38,16 @@ abstract class AbstractBackendController extends \Magento\TestFramework\TestCase */ protected $uri = null; + /** + * @var string|null + */ + protected $httpMethod; + + /** + * @inheritDoc + * + * @throws \Magento\Framework\Exception\AuthenticationException + */ protected function setUp() { parent::setUp(); @@ -62,6 +74,9 @@ protected function _getAdminCredentials() ]; } + /** + * @inheritDoc + */ protected function tearDown() { $this->_auth->getAuthStorage()->destroy(['send_expire_cookie' => false]); @@ -86,21 +101,33 @@ public function assertSessionMessages( parent::assertSessionMessages($constraint, $messageType, $messageManagerClass); } + /** + * Test ACL configuration for action working. + */ public function testAclHasAccess() { if ($this->uri === null) { $this->markTestIncomplete('AclHasAccess test is not complete'); } + if ($this->httpMethod) { + $this->getRequest()->setMethod($this->httpMethod); + } $this->dispatch($this->uri); $this->assertNotSame(403, $this->getResponse()->getHttpResponseCode()); $this->assertNotSame(404, $this->getResponse()->getHttpResponseCode()); } + /** + * Test ACL actually denying access. + */ public function testAclNoAccess() { if ($this->resource === null || $this->uri === null) { $this->markTestIncomplete('Acl test is not complete'); } + if ($this->httpMethod) { + $this->getRequest()->setMethod($this->httpMethod); + } $this->_objectManager->get(\Magento\Framework\Acl\Builder::class) ->getAcl() ->deny(null, $this->resource); diff --git a/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractController.php b/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractController.php index 9920f90193f69..feb9eca0793a2 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractController.php +++ b/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractController.php @@ -9,9 +9,13 @@ */ namespace Magento\TestFramework\TestCase; +use Magento\Framework\Data\Form\FormKey; +use Magento\Framework\Message\MessageInterface; use Magento\Framework\Stdlib\CookieManagerInterface; use Magento\Framework\View\Element\Message\InterpretationStrategyInterface; use Magento\Theme\Controller\Result\MessagePlugin; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\App\Response\Http as HttpResponse; /** * @SuppressWarnings(PHPMD.NumberOfChildren) @@ -68,6 +72,9 @@ protected function setUp() $this->_objectManager->removeSharedInstance(\Magento\Framework\App\RequestInterface::class); } + /** + * @inheritDoc + */ protected function tearDown() { $this->_request = null; @@ -96,14 +103,23 @@ protected function assertPostConditions() */ public function dispatch($uri) { - $this->getRequest()->setRequestUri($uri); + /** @var HttpRequest $request */ + $request = $this->getRequest(); + $request->setRequestUri($uri); + if ($request->isPost() + && !array_key_exists('form_key', $request->getPost()) + ) { + /** @var FormKey $formKey */ + $formKey = $this->_objectManager->get(FormKey::class); + $request->setPostValue('form_key', $formKey->getFormKey()); + } $this->_getBootstrap()->runApp(); } /** * Request getter * - * @return \Magento\Framework\App\RequestInterface + * @return \Magento\Framework\App\RequestInterface|HttpRequest */ public function getRequest() { @@ -116,7 +132,7 @@ public function getRequest() /** * Response getter * - * @return \Magento\Framework\App\ResponseInterface + * @return \Magento\Framework\App\ResponseInterface|HttpResponse */ public function getResponse() { @@ -201,13 +217,21 @@ public function assertSessionMessages( $messageManagerClass = \Magento\Framework\Message\Manager::class ) { $this->_assertSessionErrors = false; - + /** @var MessageInterface[]|string[] $messageObjects */ $messages = $this->getMessages($messageType, $messageManagerClass); + /** @var string[] $messages */ + $messagesFiltered = array_map( + function ($message) { + /** @var MessageInterface|string $message */ + return ($message instanceof MessageInterface) ? $message->toString() : $message; + }, + $messages + ); $this->assertThat( - $messages, + $messagesFiltered, $constraint, - 'Session messages do not meet expectations ' . var_export($messages, true) + 'Session messages do not meet expectations ' . var_export($messagesFiltered, true) ); } diff --git a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/IndexTest.php b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/IndexTest.php index 219fde6e37075..d5a48b960811e 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/IndexTest.php @@ -5,6 +5,8 @@ */ namespace Magento\Backend\Controller\Adminhtml; +use Magento\Framework\App\Request\Http as HttpRequest; + /** * @magentoAppArea adminhtml * @magentoDbIsolation enabled @@ -45,6 +47,7 @@ public function testLoggedIndexAction() public function testGlobalSearchAction() { $this->getRequest()->setParam('isAjax', 'true'); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue('query', 'dummy'); $this->dispatch('backend/admin/index/globalSearch'); diff --git a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/UrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/UrlRewriteTest.php index 1185ae9727e98..0d48fc8b0f59c 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/UrlRewriteTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/UrlRewriteTest.php @@ -5,6 +5,8 @@ */ namespace Magento\Backend\Controller\Adminhtml; +use Magento\Framework\App\Request\Http as HttpRequest; + /** * @magentoAppArea adminhtml */ @@ -20,6 +22,7 @@ public function testSaveActionCmsPage() $page = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Cms\Model\Page::class); $page->load('page_design_blank', 'identifier'); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue( [ 'description' => 'Some URL rewrite description', diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php index a6cffda80e705..bdf2486ac2e38 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php @@ -8,6 +8,7 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\Store\Model\Store; use Magento\Catalog\Model\ResourceModel\Product; +use Magento\Framework\App\Request\Http as HttpRequest; /** * @magentoAppArea adminhtml @@ -51,6 +52,7 @@ public function testSaveAction($inputData, $defaultAttributes, $attributesSaved $store->load('fixturestore', 'code'); $storeId = $store->getId(); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue($inputData); $this->getRequest()->setParam('store', $storeId); $this->getRequest()->setParam('id', 2); @@ -99,6 +101,7 @@ public function testSaveAction($inputData, $defaultAttributes, $attributesSaved */ public function testSaveActionFromProductCreationPage($postData) { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue($postData); $this->dispatch('backend/catalog/category/save'); @@ -357,6 +360,7 @@ public function saveActionDataProvider() */ public function testSaveActionCategoryWithDangerRequest() { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue( [ 'general' => [ @@ -407,7 +411,8 @@ public function testMoveAction($parentId, $childId, $childUrlKey, $grandChildId, } $this->getRequest() ->setPostValue('id', $grandChildId) - ->setPostValue('pid', $parentId); + ->setPostValue('pid', $parentId) + ->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/catalog/category/move'); $jsonResponse = json_decode($this->getResponse()->getBody()); $this->assertNotNull($jsonResponse); @@ -444,6 +449,7 @@ public function testSaveCategoryWithProductPosition(array $postData) $this->getRequest()->setParam('store', $storeId); $this->getRequest()->setParam('id', 96377); $this->getRequest()->setPostValue($postData); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/catalog/category/save'); $newCategoryProductsCount = $this->getCategoryProductsCount(); $this->assertEquals( @@ -543,10 +549,8 @@ private function getCategoryProductsCount(): int { $oldCategoryProducts = $this->productResource->getConnection()->select()->from( $this->productResource->getTable('catalog_category_product'), - 'product_id' - ); - return count( - $this->productResource->getConnection()->fetchAll($oldCategoryProducts) + new \Zend_Db_Expr('COUNT(product_id)') ); + return $this->productResource->getConnection()->fetchOne($oldCategoryProducts); } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php index cea49d940cb62..3d7575729cd92 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php @@ -5,6 +5,8 @@ */ namespace Magento\Catalog\Controller\Adminhtml\Product\Action; +use Magento\Framework\App\Request\Http as HttpRequest; + /** * @magentoAppArea adminhtml */ @@ -23,6 +25,7 @@ public function testSaveActionRedirectsSuccessfully() /** @var $session \Magento\Backend\Model\Session */ $session = $objectManager->get(\Magento\Backend\Model\Session::class); $session->setProductIds([1]); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/catalog/product_action_attribute/save/store/0'); @@ -69,6 +72,7 @@ public function testSaveActionChangeVisibility($attributes) $session = $objectManager->get(\Magento\Backend\Model\Session::class); $session->setProductIds([$product->getId()]); $this->getRequest()->setParam('attributes', $attributes); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/catalog/product_action_attribute/save/store/0'); /** @var \Magento\Catalog\Model\Category $category */ diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php index 3f033352eb7c1..169b6fa52e307 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php @@ -6,11 +6,12 @@ namespace Magento\Catalog\Controller\Adminhtml\Product; use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Data\Form\FormKey; +use Magento\Framework\App\Request\Http as HttpRequest; /** * @magentoAppArea adminhtml * @magentoDbIsolation enabled + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class AttributeTest extends \Magento\TestFramework\TestCase\AbstractBackendController @@ -20,15 +21,15 @@ class AttributeTest extends \Magento\TestFramework\TestCase\AbstractBackendContr */ public function testWrongFrontendInput() { - /** @var FormKey $formKey */ - $formKey = $this->_objectManager->get(FormKey::class); - $postData = $this->_getAttributeData() + [ + $postData = array_merge( + $this->_getAttributeData(), + [ 'attribute_id' => 100500, 'frontend_input' => 'some_input', - 'form_key' => $formKey->getFormKey() - ]; + ] + ); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue($postData); - $this->getRequest()->setMethod('POST'); $this->dispatch('backend/catalog/product_attribute/save'); $this->assertEquals(302, $this->getResponse()->getHttpResponseCode()); $this->assertContains( @@ -48,16 +49,13 @@ public function testWrongFrontendInput() */ public function testWithPopup() { - /** @var FormKey $formKey */ - $formKey = $this->_objectManager->get(FormKey::class); $postData = $this->_getAttributeData() + [ 'attribute_id' => 5, 'popup' => 'true', 'new_attribute_set_name' => 'new_attribute_set', - 'form_key' => $formKey->getFormKey() ]; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue($postData); - $this->getRequest()->setMethod('POST'); $this->dispatch('backend/catalog/product_attribute/save'); $this->assertEquals(302, $this->getResponse()->getHttpResponseCode()); $this->assertContains( @@ -76,16 +74,9 @@ public function testWithPopup() */ public function testWithExceptionWhenSaveAttribute() { - /** @var FormKey $formKey */ - $formKey = $this->_objectManager->get(FormKey::class); - $postData = $this->_getAttributeData() + [ - 'attribute_id' => 0, - 'frontend_input' => 'boolean', - 'form_key' => $formKey->getFormKey() - ]; - - $this->getRequest()->setMethod('POST'); + $postData = $this->_getAttributeData() + ['attribute_id' => 0, 'frontend_input' => 'boolean']; $this->getRequest()->setPostValue($postData); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/catalog/product_attribute/save'); $this->assertEquals(302, $this->getResponse()->getHttpResponseCode()); $this->assertContains( @@ -102,15 +93,9 @@ public function testWithExceptionWhenSaveAttribute() */ public function testWrongAttributeId() { - /** @var FormKey $formKey */ - $formKey = $this->_objectManager->get(FormKey::class); - $postData = $this->_getAttributeData() + [ - 'attribute_id' => 100500, - 'form_key' => $formKey->getFormKey() - ]; - - $this->getRequest()->setMethod('POST'); + $postData = $this->_getAttributeData() + ['attribute_id' => 100500]; $this->getRequest()->setPostValue($postData); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/catalog/product_attribute/save'); $this->assertEquals(302, $this->getResponse()->getHttpResponseCode()); $this->assertContains( @@ -130,15 +115,12 @@ public function testWrongAttributeId() */ public function testAttributeWithoutId() { - /** @var FormKey $formKey */ - $formKey = $this->_objectManager->get(FormKey::class); $postData = $this->_getAttributeData() + [ 'attribute_code' => uniqid('attribute_'), 'set' => 4, 'frontend_input' => 'boolean', - 'form_key' => $formKey->getFormKey() ]; - $this->getRequest()->setMethod('POST'); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue($postData); $this->dispatch('backend/catalog/product_attribute/save'); $this->assertEquals(302, $this->getResponse()->getHttpResponseCode()); @@ -159,14 +141,9 @@ public function testAttributeWithoutId() */ public function testWrongAttributeCode() { - /** @var FormKey $formKey */ - $formKey = $this->_objectManager->get(FormKey::class); - $postData = $this->_getAttributeData() + [ - 'attribute_code' => '_()&&&?', - 'form_key' => $formKey->getFormKey() - ]; + $postData = $this->_getAttributeData() + ['attribute_code' => '_()&&&?']; $this->getRequest()->setPostValue($postData); - $this->getRequest()->setMethod('POST'); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/catalog/product_attribute/save'); $this->assertEquals(302, $this->getResponse()->getHttpResponseCode()); $this->assertContains( @@ -190,15 +167,9 @@ public function testWrongAttributeCode() */ public function testAttributeWithoutEntityTypeId() { - /** @var FormKey $formKey */ - $formKey = $this->_objectManager->get(FormKey::class); - $postData = $this->_getAttributeData() + [ - 'attribute_id' => '2', - 'new_attribute_set_name' => ' ', - 'form_key' => $formKey->getFormKey() - ]; + $postData = $this->_getAttributeData() + ['attribute_id' => '2', 'new_attribute_set_name' => ' ']; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue($postData); - $this->getRequest()->setMethod('POST'); $this->dispatch('backend/catalog/product_attribute/save'); $this->assertEquals(302, $this->getResponse()->getHttpResponseCode()); $this->assertContains( @@ -212,15 +183,9 @@ public function testAttributeWithoutEntityTypeId() */ public function testSaveActionApplyToDataSystemAttribute() { - /** @var FormKey $formKey */ - $formKey = $this->_objectManager->get(FormKey::class); - $postData = $this->_getAttributeData() + [ - 'attribute_id' => '2', - 'form_key' => $formKey->getFormKey() - ]; - + $postData = $this->_getAttributeData() + ['attribute_id' => '2']; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue($postData); - $this->getRequest()->setMethod('POST'); $this->dispatch('backend/catalog/product_attribute/save'); $model = $this->_objectManager->create(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class); $model->load($postData['attribute_id']); @@ -232,15 +197,9 @@ public function testSaveActionApplyToDataSystemAttribute() */ public function testSaveActionApplyToDataUserDefinedAttribute() { - /** @var FormKey $formKey */ - $formKey = $this->_objectManager->get(FormKey::class); - $postData = $this->_getAttributeData() + [ - 'attribute_id' => '1', - 'form_key' => $formKey->getFormKey() - ]; - + $postData = $this->_getAttributeData() + ['attribute_id' => '1']; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue($postData); - $this->getRequest()->setMethod('POST'); $this->dispatch('backend/catalog/product_attribute/save'); /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $model */ $model = $this->_objectManager->create(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class); @@ -255,6 +214,7 @@ public function testSaveActionApplyToData() { $postData = $this->_getAttributeData() + ['attribute_id' => '3']; unset($postData['apply_to']); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue($postData); $this->dispatch('backend/catalog/product_attribute/save'); $model = $this->_objectManager->create(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class); @@ -274,7 +234,7 @@ public function testSaveActionCleanAttributeLabelCache() $this->assertEquals('predefined string translation', $this->_translate('string to translate')); $string->saveTranslate('string to translate', 'new string translation'); $postData = $this->_getAttributeData() + ['attribute_id' => 1]; - $this->getRequest()->setMethod('POST'); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue($postData); $this->dispatch('backend/catalog/product_attribute/save'); $this->assertEquals('new string translation', $this->_translate('string to translate')); @@ -350,11 +310,8 @@ public function testLargeOptionsDataSet() $optionsData[] = http_build_query($optionRowData); } $attributeData['serialized_options'] = json_encode($optionsData); - /** @var FormKey $formKey */ - $formKey = $this->_objectManager->get(FormKey::class); - $this->getRequest()->setMethod('POST'); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue($attributeData); - $this->getRequest()->setParam('form_key', $formKey->getFormKey()); $this->dispatch('backend/catalog/product_attribute/save'); $entityTypeId = $this->_objectManager->create( \Magento\Eav\Model\Entity::class @@ -425,6 +382,7 @@ protected function _getAttributeData() 'default_value_textarea' => '0', 'is_required' => '1', 'frontend_class' => '', + 'frontend_input' => 'select', 'is_searchable' => '0', 'is_visible_in_advanced_search' => '0', 'is_comparable' => '0', diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/DeleteTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/DeleteTest.php index 7c5d4ea48a238..7e034b8b3cb7e 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/DeleteTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/DeleteTest.php @@ -6,6 +6,7 @@ namespace Magento\Catalog\Controller\Adminhtml\Product\Set; use Magento\Framework\Message\MessageInterface; +use Magento\Framework\App\Request\Http as HttpRequest; class DeleteTest extends \Magento\TestFramework\TestCase\AbstractBackendController { @@ -15,7 +16,7 @@ class DeleteTest extends \Magento\TestFramework\TestCase\AbstractBackendControll public function testDeleteById() { $attributeSet = $this->getAttributeSetByName('empty_attribute_set'); - $this->getRequest()->setParam('id', $attributeSet->getId()); + $this->getRequest()->setParam('id', $attributeSet->getId())->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/catalog/product_set/delete/'); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/SaveTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/SaveTest.php index 5b711b2ea7418..8ccd426424a29 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/SaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/SaveTest.php @@ -9,6 +9,7 @@ use Magento\Eav\Api\Data\AttributeSetInterface; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\App\Request\Http as HttpRequest; class SaveTest extends \Magento\TestFramework\TestCase\AbstractBackendController { @@ -20,6 +21,7 @@ public function testAlreadyExistsExceptionProcessingWhenGroupCodeIsDuplicated() $attributeSet = $this->getAttributeSetByName('attribute_set_test'); $this->assertNotEmpty($attributeSet, 'Attribute set with name "attribute_set_test" is missed'); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue('data', json_encode([ 'attribute_set_name' => 'attribute_set_test', 'groups' => [ diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php index 4761f13175d81..44577b2a228a0 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php @@ -11,6 +11,7 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Framework\Message\MessageInterface; +use Magento\Framework\App\Request\Http as HttpRequest; /** * @magentoAppArea adminhtml @@ -19,6 +20,7 @@ class ProductTest extends \Magento\TestFramework\TestCase\AbstractBackendControl { public function testSaveActionWithDangerRequest() { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue(['product' => ['entity_id' => 15]]); $this->dispatch('backend/catalog/product/save'); $this->assertSessionMessages( @@ -36,6 +38,7 @@ public function testSaveActionAndNew() $this->getRequest()->setPostValue(['back' => 'new']); $repository = $this->_objectManager->create(\Magento\Catalog\Model\ProductRepository::class); $product = $repository->get('simple'); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/catalog/product/save/id/' . $product->getEntityId()); $this->assertRedirect($this->stringStartsWith('http://localhost/index.php/backend/catalog/product/new/')); $this->assertSessionMessages( @@ -52,6 +55,7 @@ public function testSaveActionAndDuplicate() $this->getRequest()->setPostValue(['back' => 'duplicate']); $repository = $this->_objectManager->create(\Magento\Catalog\Model\ProductRepository::class); $product = $repository->get('simple'); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/catalog/product/save/id/' . $product->getEntityId()); $this->assertRedirect($this->stringStartsWith('http://localhost/index.php/backend/catalog/product/edit/')); $this->assertRedirect( @@ -161,11 +165,13 @@ public function testEditAction() public function testSaveActionWithAlreadyExistingUrlKey(array $postData) { $this->getRequest()->setPostValue($postData); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/catalog/product/save'); /** @var Manager $messageManager */ $messageManager = $this->_objectManager->get(Manager::class); $messages = $messageManager->getMessages(); $errors = $messages->getItemsByType('error'); + $this->assertNotEmpty($errors); $message = array_shift($errors); $this->assertSame('URL key for specified store already exists.', $message->getText()); $this->assertRedirect($this->stringContains('/backend/catalog/product/new')); @@ -233,7 +239,6 @@ public function saveActionWithAlreadyExistingUrlKeyDataProvider() 'thumbnail' => '/m/a//magento_image.jpg.tmp', 'swatch_image' => '/m/a//magento_image.jpg.tmp', ], - 'form_key' => Bootstrap::getObjectManager()->get(FormKey::class)->getFormKey(), ] ] ]; @@ -251,6 +256,7 @@ public function saveActionWithAlreadyExistingUrlKeyDataProvider() public function testSaveActionTierPrice(array $postData, array $tierPrice) { $postData['product'] = $this->getProductData($tierPrice); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue($postData); $this->dispatch('backend/catalog/product/save/id/' . $postData['id']); $this->assertSessionMessages( diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php index 7e27fd7ede8b4..f9b1d10cbb8ae 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php @@ -7,7 +7,9 @@ // @codingStandardsIgnoreFile namespace Magento\Catalog\Controller\Product; + use Magento\Framework\Message\MessageInterface; +use Magento\Framework\App\Request\Http as HttpRequest; /** * @magentoDataFixture Magento/Catalog/controllers/_files/products.php @@ -23,6 +25,9 @@ class CompareTest extends \Magento\TestFramework\TestCase\AbstractController */ protected $productRepository; + /** + * @inheritDoc + */ protected function setUp() { parent::setUp(); @@ -40,6 +45,7 @@ public function testAddAction() /** @var \Magento\Framework\Data\Form\FormKey $formKey */ $formKey = $objectManager->get(\Magento\Framework\Data\Form\FormKey::class); $product = $this->productRepository->get('simple_product_1'); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch( sprintf( 'catalog/product_compare/add/product/%s/form_key/%s?nocookie=1', @@ -49,7 +55,12 @@ public function testAddAction() ); $this->assertSessionMessages( - $this->equalTo(['You added product Simple Product 1 Name to the <a href="http://localhost/index.php/catalog/product_compare/">comparison list</a>.']), + $this->equalTo( + [ + 'You added product Simple Product 1 Name to the '. + '<a href="http://localhost/index.php/catalog/product_compare/">comparison list</a>.' + ] + ), MessageInterface::TYPE_SUCCESS ); @@ -73,6 +84,7 @@ public function testRemoveAction() { $this->_requireVisitorWithTwoProducts(); $product = $this->productRepository->get('simple_product_2'); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('catalog/product_compare/remove/product/' . $product->getEntityId()); $this->assertSessionMessages( @@ -89,6 +101,7 @@ public function testRemoveActionWithSession() { $this->_requireCustomerWithTwoProducts(); $product = $this->productRepository->get('simple_product_1'); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('catalog/product_compare/remove/product/' . $product->getEntityId()); $secondProduct = $this->productRepository->get('simple_product_2'); @@ -132,6 +145,7 @@ public function testClearAction() { $this->_requireVisitorWithTwoProducts(); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('catalog/product_compare/clear'); $this->assertSessionMessages( @@ -151,6 +165,7 @@ public function testRemoveActionProductNameXss() { $this->_prepareCompareListWithProductNameXss(); $product = $this->productRepository->get('product-with-xss'); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('catalog/product_compare/remove/product/' . $product->getEntityId() . '?nocookie=1'); $this->assertSessionMessages( @@ -307,7 +322,8 @@ protected function _assertCompareListEquals(array $expectedProductIds) // important $compareItems->setVisitorId( \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Customer\Model\Visitor::class)->getId() + \Magento\Customer\Model\Visitor::class + )->getId() ); $actualProductIds = []; foreach ($compareItems as $compareItem) { diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/Index/CouponPostTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/Index/CouponPostTest.php index 2036042d0463c..3e99c5cad3c39 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/Index/CouponPostTest.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/Index/CouponPostTest.php @@ -6,6 +6,8 @@ namespace Magento\Checkout\Controller\Cart\Index; +use Magento\Framework\App\Request\Http as HttpRequest; + /** * @magentoDbIsolation enabled */ @@ -26,13 +28,14 @@ public function testExecute() 'remove' => 0, 'coupon_code' => 'test' ]; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue($inputData); $this->dispatch( 'checkout/cart/couponPost/' ); $this->assertSessionMessages( - $this->equalTo(['The coupon code "test" is not valid.']), + $this->equalTo(['The coupon code "test" is not valid.']), \Magento\Framework\Message\MessageInterface::TYPE_ERROR ); } diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php index 068a9c3529c15..78e1a2377575f 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php @@ -22,6 +22,7 @@ use Magento\Customer\Model\Session as CustomerSession; use Magento\Sales\Model\ResourceModel\Order\Collection as OrderCollection; use Magento\Sales\Model\ResourceModel\Order\Item\Collection as OrderItemCollection; +use Magento\Framework\App\Request\Http as HttpRequest; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -239,6 +240,7 @@ public function testUpdatePostAction() 'update_cart_action' => 'update_qty', 'form_key' => $formKey->getFormKey(), ]; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue($postData); /** @var $customerSession \Magento\Customer\Model\Session */ $customerSession = $this->_objectManager->create(\Magento\Customer\Model\Session::class); @@ -286,7 +288,8 @@ private function getQuote($reservedOrderId) * Gets \Magento\Quote\Model\Quote\Item from \Magento\Quote\Model\Quote by product id * * @param \Magento\Quote\Model\Quote $quote - * @param $productId + * @param string|int $productId + * * @return \Magento\Quote\Model\Quote\Item|null */ private function _getQuoteItemIdByProductId($quote, $productId) @@ -321,6 +324,7 @@ public function testAddToCartSimpleProduct($area, $expectedPrice) 'isAjax' => 1 ]; \Magento\TestFramework\Helper\Bootstrap::getInstance()->loadArea($area); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue($postData); $quote = $this->_objectManager->create(\Magento\Checkout\Model\Cart::class); @@ -367,6 +371,7 @@ public function testMessageAtAddToCartWithRedirect() ]; \Magento\TestFramework\Helper\Bootstrap::getInstance()->loadArea('frontend'); $this->getRequest()->setPostValue($postData); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('checkout/cart/add'); @@ -402,6 +407,7 @@ public function testMessageAtAddToCartWithoutRedirect() ]; \Magento\TestFramework\Helper\Bootstrap::getInstance()->loadArea('frontend'); $this->getRequest()->setPostValue($postData); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('checkout/cart/add'); diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/UploadTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/UploadTest.php index 686ac58652025..6b1f8fc717c2d 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/UploadTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/UploadTest.php @@ -102,6 +102,7 @@ public function testExecuteWithLinkedMedia() $fullDirectoryPath = $this->filesystem->getDirectoryRead(DirectoryList::PUB) ->getAbsolutePath() . DIRECTORY_SEPARATOR . $directoryName; $wysiwygDir = $this->mediaDirectory->getAbsolutePath() . '/wysiwyg'; + $this->model->getRequest()->setMethod('POST'); $this->model->getRequest()->setParams(['type' => 'image/png']); $this->model->getStorage()->getSession()->setCurrentPath($wysiwygDir); $this->model->execute(); diff --git a/dev/tests/integration/testsuite/Magento/Config/Controller/Adminhtml/System/ConfigTest.php b/dev/tests/integration/testsuite/Magento/Config/Controller/Adminhtml/System/ConfigTest.php index 9ad99745d572f..5170a4b8a4dd6 100644 --- a/dev/tests/integration/testsuite/Magento/Config/Controller/Adminhtml/System/ConfigTest.php +++ b/dev/tests/integration/testsuite/Magento/Config/Controller/Adminhtml/System/ConfigTest.php @@ -9,6 +9,7 @@ namespace Magento\Config\Controller\Adminhtml\System; use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\App\Request\Http as HttpRequest; /** * @magentoAppArea adminhtml @@ -22,6 +23,8 @@ public function testEditAction() } /** + * Test redirect after changing base URL. + * * @magentoAppIsolation enabled * @magentoDbIsolation enabled */ @@ -31,20 +34,22 @@ public function testChangeBaseUrl() $newHost = 'm2test123.loc'; $request = $this->getRequest(); $request->setPostValue( - ['groups' => - ['unsecure' => - ['fields' => - ['base_url' => - ['value' => 'http://' . $newHost . '/'] + [ + 'groups' => + ['unsecure' => + ['fields' => + ['base_url' => + ['value' => 'http://' . $newHost . '/'] + ] ] - ] - ], - 'config_state' => - ['web_unsecure' => 1] + ], + 'config_state' => ['web_unsecure' => 1] ] )->setParam( 'section', 'web' + )->setMethod( + HttpRequest::METHOD_POST ); $this->dispatch('backend/admin/system_config/save'); @@ -62,14 +67,16 @@ public function testChangeBaseUrl() } /** - * Reset test framework default base url + * Reset test framework default base url. + * + * @param string $defaultHost */ protected function resetBaseUrl($defaultHost) { $baseUrlData = [ 'section' => 'web', - 'website' => NULL, - 'store' => NULL, + 'website' => null, + 'store' => null, 'groups' => [ 'unsecure' => [ 'fields' => [ diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php index 4254a6ce9c71d..b71507ae43f9f 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/ProductTest.php @@ -9,6 +9,7 @@ use Magento\Framework\Registry; use Magento\TestFramework\ObjectManager; use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\App\Request\Http as HttpRequest; /** * @magentoAppArea adminhtml @@ -23,6 +24,7 @@ public function testSaveActionAssociatedProductIds() { $associatedProductIds = ['3', '14', '15', '92']; $associatedProductIdsJSON = json_encode($associatedProductIds); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue( [ 'id' => 1, diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/CartTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/CartTest.php index f9776b2264ff3..0d93d3ad4f4ae 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/CartTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/CartTest.php @@ -9,6 +9,8 @@ */ namespace Magento\ConfigurableProduct\Controller; +use Magento\Framework\App\Request\Http as HttpRequest; + class CartTest extends \Magento\TestFramework\TestCase\AbstractController { /** @@ -85,13 +87,14 @@ public function testExecuteForConfigurableLastOption() 'remove' => 0, 'coupon_code' => 'test' ]; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue($inputData); $this->dispatch( 'checkout/cart/couponPost/' ); $this->assertSessionMessages( - $this->equalTo(['The coupon code "test" is not valid.']), + $this->equalTo(['The coupon code "test" is not valid.']), \Magento\Framework\Message\MessageInterface::TYPE_ERROR ); } diff --git a/dev/tests/integration/testsuite/Magento/CurrencySymbol/Controller/Adminhtml/System/Currency/SaveRatesTest.php b/dev/tests/integration/testsuite/Magento/CurrencySymbol/Controller/Adminhtml/System/Currency/SaveRatesTest.php index c9f2ffad67644..fefd1a7b250c3 100644 --- a/dev/tests/integration/testsuite/Magento/CurrencySymbol/Controller/Adminhtml/System/Currency/SaveRatesTest.php +++ b/dev/tests/integration/testsuite/Magento/CurrencySymbol/Controller/Adminhtml/System/Currency/SaveRatesTest.php @@ -5,6 +5,8 @@ */ namespace Magento\CurrencySymbol\Controller\Adminhtml\System\Currency; +use Magento\Framework\App\Request\Http as HttpRequest; + class SaveRatesTest extends \Magento\TestFramework\TestCase\AbstractBackendController { @@ -43,6 +45,7 @@ public function testSaveAction() $rate = 1.0000; $request = $this->getRequest(); + $request->setMethod(HttpRequest::METHOD_POST); $request->setPostValue( 'rate', [ @@ -75,6 +78,7 @@ public function testSaveWithWarningAction() $rate = '0'; $request = $this->getRequest(); + $request->setMethod(HttpRequest::METHOD_POST); $request->setPostValue( 'rate', [ diff --git a/dev/tests/integration/testsuite/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/SaveTest.php b/dev/tests/integration/testsuite/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/SaveTest.php index 5217c3576a51d..2929f137be89f 100644 --- a/dev/tests/integration/testsuite/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/SaveTest.php +++ b/dev/tests/integration/testsuite/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/SaveTest.php @@ -5,10 +5,16 @@ */ namespace Magento\CurrencySymbol\Controller\Adminhtml\System\Currencysymbol; +use Magento\Framework\App\Request\Http as HttpRequest; + class SaveTest extends \Magento\TestFramework\TestCase\AbstractBackendController { /** - * Test save action + * Test save action. + * + * @param string $currencyCode + * @param string $inputCurrencySymbol + * @param string $outputCurrencySymbol * * @magentoConfigFixture currency/options/allow USD * @magentoDbIsolation enabled @@ -31,6 +37,7 @@ public function testSaveAction($currencyCode, $inputCurrencySymbol, $outputCurre $currencyCode => $inputCurrencySymbol, ] ); + $request->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/admin/system_currencysymbol/save'); $this->assertRedirect(); diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php index 4fed6c84ab09d..c169272b133bc 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php @@ -27,6 +27,7 @@ use Magento\Framework\Stdlib\CookieManagerInterface; use Magento\Theme\Controller\Result\MessagePlugin; use Zend\Stdlib\Parameters; +use Magento\Framework\App\Request\Http as HttpRequest; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -111,10 +112,8 @@ public function testForgotPasswordEmailMessageWithSpecialCharacters() { $email = 'customer@example.com'; - $this->getRequest() - ->setPostValue([ - 'email' => $email, - ]); + $this->getRequest()->setPostValue(['email' => $email]); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('customer/account/forgotPasswordPost'); $this->assertRedirect($this->stringContains('customer/account/')); @@ -232,6 +231,7 @@ public function testConfirmActionAlreadyActive() public function testNoFormKeyCreatePostAction() { $this->fillRequestWithAccountData(); + $this->getRequest()->setPostValue('form_key', null); $this->dispatch('customer/account/createPost'); $this->assertNull($this->getCustomerByEmail('test1@email.com')); @@ -279,8 +279,7 @@ public function testWithConfirmCreatePostAction() */ public function testExistingEmailCreatePostAction() { - $this->fillRequestWithAccountDataAndFormKey(); - $this->getRequest()->setParam('email', 'customer@example.com'); + $this->fillRequestWithAccountDataAndFormKey('customer@example.com'); $this->dispatch('customer/account/createPost'); $this->assertRedirect($this->stringContains('customer/account/create/')); $this->assertSessionMessages( @@ -339,10 +338,8 @@ public function testForgotPasswordPostAction() { $email = 'customer@example.com'; - $this->getRequest() - ->setPostValue([ - 'email' => $email, - ]); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue(['email' => $email]); $this->dispatch('customer/account/forgotPasswordPost'); $this->assertRedirect($this->stringContains('customer/account/')); @@ -362,6 +359,7 @@ public function testForgotPasswordPostAction() */ public function testForgotPasswordPostWithBadEmailAction() { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest() ->setPostValue([ 'email' => 'bad@email', @@ -383,6 +381,7 @@ public function testResetPasswordPostNoTokenAction() $this->getRequest() ->setParam('id', 1) ->setParam('token', '8ed8677e6c79e68b94e61658bd756ea5') + ->setMethod('POST') ->setPostValue([ 'password' => 'new-password', 'password_confirmation' => 'new-password', @@ -515,18 +514,19 @@ public function testChangePasswordEditPostAction() $this->login(1); $this->getRequest() ->setMethod('POST') - ->setPostValue([ - 'form_key' => $this->_objectManager->get( - FormKey::class)->getFormKey(), - 'firstname' => 'John', - 'lastname' => 'Doe', - 'email' => 'johndoe@email.com', - 'change_password' => 1, - 'change_email' => 1, - 'current_password' => 'password', - 'password' => 'new-Password1', - 'password_confirmation' => 'new-Password1', - ]); + ->setPostValue( + [ + 'form_key' => $this->_objectManager->get(FormKey::class)->getFormKey(), + 'firstname' => 'John', + 'lastname' => 'Doe', + 'email' => 'johndoe@email.com', + 'change_password' => 1, + 'change_email' => 1, + 'current_password' => 'password', + 'password' => 'new-Password1', + 'password_confirmation' => 'new-Password1', + ] + ); $this->dispatch('customer/account/editPost'); @@ -550,14 +550,16 @@ public function testMissingDataEditPostAction() $this->login(1); $this->getRequest() ->setMethod('POST') - ->setPostValue([ - 'form_key' => $this->_objectManager->get(FormKey::class)->getFormKey(), - 'firstname' => 'John', - 'lastname' => 'Doe', - 'change_email' => 1, - 'current_password' => 'password', - 'email' => 'bad-email', - ]); + ->setPostValue( + [ + 'form_key' => $this->_objectManager->get(FormKey::class)->getFormKey(), + 'firstname' => 'John', + 'lastname' => 'Doe', + 'change_email' => 1, + 'current_password' => 'password', + 'email' => 'bad-email', + ] + ); $this->dispatch('customer/account/editPost'); @@ -576,17 +578,18 @@ public function testWrongPasswordEditPostAction() $this->login(1); $this->getRequest() ->setMethod('POST') - ->setPostValue([ - 'form_key' => $this->_objectManager->get( - FormKey::class)->getFormKey(), - 'firstname' => 'John', - 'lastname' => 'Doe', - 'email' => 'johndoe@email.com', - 'change_password' => 1, - 'current_password' => 'wrong-password', - 'password' => 'new-password', - 'password_confirmation' => 'new-password', - ]); + ->setPostValue( + [ + 'form_key' => $this->_objectManager->get(FormKey::class)->getFormKey(), + 'firstname' => 'John', + 'lastname' => 'Doe', + 'email' => 'johndoe@email.com', + 'change_password' => 1, + 'current_password' => 'wrong-password', + 'password' => 'new-password', + 'password_confirmation' => 'new-password', + ] + ); $this->dispatch('customer/account/editPost'); @@ -607,8 +610,7 @@ public function testWrongConfirmationEditPostAction() $this->getRequest() ->setMethod('POST') ->setPostValue([ - 'form_key' => $this->_objectManager->get( - FormKey::class)->getFormKey(), + 'form_key' => $this->_objectManager->get(FormKey::class)->getFormKey(), 'firstname' => 'John', 'lastname' => 'Doe', 'email' => 'johndoe@email.com', @@ -640,19 +642,18 @@ public function testWrongConfirmationEditPostAction() public function testLoginPostRedirect($redirectDashboard, string $redirectUrl) { if (isset($redirectDashboard)) { - $this->_objectManager->get(ScopeConfigInterface::class)->setValue('customer/startup/redirect_dashboard', $redirectDashboard); + $this->_objectManager->get(ScopeConfigInterface::class)->setValue( + 'customer/startup/redirect_dashboard', + $redirectDashboard + ); } - $this->_objectManager->get(Redirect::class)->setRedirectCookie('test'); - $configValue = $this->_objectManager->create(Value::class); $configValue->load('web/unsecure/base_url', 'path'); $baseUrl = $configValue->getValue() ?: 'http://localhost/'; - $request = $this->prepareRequest(); $app = $this->_objectManager->create(Http::class, ['_request' => $request]); $response = $app->launch(); - $this->assertResponseRedirect($response, $baseUrl . $redirectUrl); $this->assertTrue($this->_objectManager->get(Session::class)->isLoggedIn()); } diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AddressTest.php index ddf23e1b6ea98..484725346af64 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AddressTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AddressTest.php @@ -9,6 +9,7 @@ use Magento\Customer\Model\CustomerRegistry; use Magento\Framework\Data\Form\FormKey; use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\App\Request\Http as HttpRequest; class AddressTest extends \Magento\TestFramework\TestCase\AbstractController { @@ -18,6 +19,9 @@ class AddressTest extends \Magento\TestFramework\TestCase\AbstractController /** @var FormKey */ private $formKey; + /** + * @inheritDoc + */ protected function setUp() { parent::setUp(); @@ -165,7 +169,7 @@ public function testFailedFormPostAction() public function testDeleteAction() { $this->getRequest()->setParam('id', 1); - $this->getRequest()->setParam('form_key', $this->formKey->getFormKey()); + $this->getRequest()->setParam('form_key', $this->formKey->getFormKey())->setMethod(HttpRequest::METHOD_POST); // we are overwriting the address coming from the fixture $this->dispatch('customer/address/delete'); @@ -183,13 +187,13 @@ public function testDeleteAction() public function testWrongAddressDeleteAction() { $this->getRequest()->setParam('id', 555); - $this->getRequest()->setParam('form_key', $this->formKey->getFormKey()); + $this->getRequest()->setParam('form_key', $this->formKey->getFormKey())->setMethod(HttpRequest::METHOD_POST); // we are overwriting the address coming from the fixture $this->dispatch('customer/address/delete'); $this->assertRedirect($this->stringContains('customer/address/index')); $this->assertSessionMessages( - $this->equalTo(['We can\'t delete the address right now.']), + $this->equalTo(['We can't delete the address right now.']), \Magento\Framework\Message\MessageInterface::TYPE_ERROR ); } diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/GroupTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/GroupTest.php index 094cc46d42867..446f3cb6aa464 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/GroupTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/GroupTest.php @@ -7,7 +7,7 @@ use Magento\Framework\Message\MessageInterface; use Magento\TestFramework\Helper\Bootstrap; -use Magento\Framework\Data\Form\FormKey; +use Magento\Framework\App\Request\Http as HttpRequest; /** * @magentoAppArea adminhtml @@ -26,6 +26,9 @@ class GroupTest extends \Magento\TestFramework\TestCase\AbstractBackendControlle /** @var \Magento\Customer\Api\GroupRepositoryInterface */ private $groupRepository; + /** + * @inheritDoc + */ public function setUp() { parent::setUp(); @@ -34,12 +37,9 @@ public function setUp() $this->groupRepository = $objectManager->get(\Magento\Customer\Api\GroupRepositoryInterface::class); } - public function tearDown() - { - parent::tearDown(); - //$this->session->unsCustomerGroupData(); - } - + /** + * Test new group form. + */ public function testNewActionNoCustomerGroupDataInSession() { $this->dispatch('backend/customer/group/new'); @@ -50,6 +50,9 @@ public function testNewActionNoCustomerGroupDataInSession() $this->assertContains($expected, $responseBody); } + /** + * Test form filling with data in session. + */ public function testNewActionWithCustomerGroupDataInSession() { /** @var \Magento\Customer\Api\Data\GroupInterfaceFactory $customerGroupFactory */ @@ -77,36 +80,27 @@ public function testNewActionWithCustomerGroupDataInSession() } /** + * Test calling delete without an ID. + * * @magentoDataFixture Magento/Customer/_files/customer_group.php */ public function testDeleteActionNoGroupId() { - /** @var FormKey $formKey */ - $formKey = $this->_objectManager->get(FormKey::class); - - $this->getRequest()->setMethod('POST'); - $this->getRequest()->setParam('form_key', $formKey->getFormKey()); + $this->getRequest()->setMethod(\Magento\Framework\App\Request\Http::METHOD_POST); $this->dispatch('backend/customer/group/delete'); $this->assertRedirect($this->stringStartsWith(self::BASE_CONTROLLER_URL)); } /** + * Test deleting a group. + * * @magentoDataFixture Magento/Customer/_files/customer_group.php */ public function testDeleteActionExistingGroup() { $groupId = $this->findGroupIdWithCode(self::CUSTOMER_GROUP_CODE); - - /** @var FormKey $formKey */ - $formKey = $this->_objectManager->get(FormKey::class); - - $this->getRequest()->setMethod('POST'); - $this->getRequest()->setParams( - [ - 'id' => $groupId, - 'form_key' => $formKey->getFormKey() - ] - ); + $this->getRequest()->setParam('id', $groupId); + $this->getRequest()->setMethod(\Magento\Framework\App\Request\Http::METHOD_POST); $this->dispatch('backend/customer/group/delete'); /** @@ -120,20 +114,14 @@ public function testDeleteActionExistingGroup() } /** + * Tet deleting with wrong ID. + * * @magentoDataFixture Magento/Customer/_files/customer_group.php */ public function testDeleteActionNonExistingGroupId() { - /** @var FormKey $formKey */ - $formKey = $this->_objectManager->get(FormKey::class); - - $this->getRequest()->setMethod('POST'); - $this->getRequest()->setParams( - [ - 'id' => 10000, - 'form_key' => $formKey->getFormKey() - ] - ); + $this->getRequest()->setParam('id', 10000); + $this->getRequest()->setMethod(\Magento\Framework\App\Request\Http::METHOD_POST); $this->dispatch('backend/customer/group/delete'); /** @@ -147,6 +135,8 @@ public function testDeleteActionNonExistingGroupId() } /** + * Test saving a valid group. + * * @magentoDataFixture Magento/Customer/_files/customer_group.php */ public function testSaveActionExistingGroup() @@ -155,6 +145,7 @@ public function testSaveActionExistingGroup() $this->getRequest()->setParam('tax_class', self::TAX_CLASS_ID); $this->getRequest()->setParam('id', $groupId); $this->getRequest()->setParam('code', self::CUSTOMER_GROUP_CODE); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/group/save'); @@ -186,9 +177,13 @@ public function testSaveActionExistingGroup() ); } + /** + * Test saving an invalid group. + */ public function testSaveActionCreateNewGroupWithoutCode() { $this->getRequest()->setParam('tax_class', self::TAX_CLASS_ID); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/group/save'); @@ -198,19 +193,26 @@ public function testSaveActionCreateNewGroupWithoutCode() ); } + /** + * Test saving an empty group. + */ public function testSaveActionForwardNewCreateNewGroup() { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/group/save'); $responseBody = $this->getResponse()->getBody(); $this->assertRegExp('/<h1 class\="page-title">\s*New Customer Group\s*<\/h1>/', $responseBody); } /** + * Test saving an existing group. + * * @magentoDataFixture Magento/Customer/_files/customer_group.php */ public function testSaveActionForwardNewEditExistingGroup() { $groupId = $this->findGroupIdWithCode(self::CUSTOMER_GROUP_CODE); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setParam('id', $groupId); $this->dispatch('backend/customer/group/save'); @@ -218,10 +220,14 @@ public function testSaveActionForwardNewEditExistingGroup() $this->assertRegExp('/<h1 class\="page-title">\s*' . self::CUSTOMER_GROUP_CODE . '\s*<\/h1>/', $responseBody); } + /** + * Test using an invalid ID. + */ public function testSaveActionNonExistingGroupId() { $this->getRequest()->setParam('id', 10000); $this->getRequest()->setParam('tax_class', self::TAX_CLASS_ID); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/group/save'); @@ -236,6 +242,8 @@ public function testSaveActionNonExistingGroupId() } /** + * Test using existing code. + * * @magentoDataFixture Magento/Customer/_files/customer_group.php */ public function testSaveActionNewGroupWithExistingGroupCode() @@ -245,6 +253,7 @@ public function testSaveActionNewGroupWithExistingGroupCode() $this->getRequest()->setParam('tax_class', self::TAX_CLASS_ID); $this->getRequest()->setParam('code', self::CUSTOMER_GROUP_CODE); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/group/save'); @@ -257,6 +266,8 @@ public function testSaveActionNewGroupWithExistingGroupCode() } /** + * Test saving an invalid group. + * * @magentoDataFixture Magento/Customer/_files/customer_group.php */ public function testSaveActionNewGroupWithoutGroupCode() @@ -265,6 +276,7 @@ public function testSaveActionNewGroupWithoutGroupCode() $originalCode = $this->groupRepository->getById($groupId)->getCode(); $this->getRequest()->setParam('tax_class', self::TAX_CLASS_ID); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/group/save'); diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroupTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroupTest.php index 434e24b7d2771..3df07fbd4e1c0 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroupTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroupTest.php @@ -9,6 +9,7 @@ use Magento\Backend\Model\Session; use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Framework\App\Request\Http as HttpRequest; use Magento\Customer\Api\Data\CustomerInterface; use Magento\Framework\Message\MessageInterface; use Magento\TestFramework\Helper\Bootstrap; @@ -31,12 +32,18 @@ class MassAssignGroupTest extends AbstractBackendController */ protected $customerRepository; + /** + * @inheritDoc + */ protected function setUp() { parent::setUp(); $this->customerRepository = Bootstrap::getObjectManager()->get(CustomerRepositoryInterface::class); } + /** + * @inheritDoc + */ protected function tearDown() { /** @@ -73,8 +80,8 @@ public function testMassAssignGroupAction() 'form_key' => $formKey->getFormKey() ]; - $this->getRequest()->setParams($params); - $this->getRequest()->setMethod('POST'); + $this->getRequest()->setParams($params) + ->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/index/massAssignGroup'); $this->assertSessionMessages( self::equalTo(['A total of 1 record(s) were updated.']), @@ -111,8 +118,8 @@ public function testLargeGroupMassAssignGroupAction() 'form_key' => $formKey->getFormKey() ]; - $this->getRequest()->setParams($params); - $this->getRequest()->setMethod('POST'); + $this->getRequest()->setParams($params) + ->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/index/massAssignGroup'); $this->assertSessionMessages( self::equalTo(['A total of 5 record(s) were updated.']), @@ -141,9 +148,7 @@ public function testMassAssignGroupActionNoCustomerIds() 'namespace' => 'customer_listing', 'form_key' => $formKey->getFormKey() ]; - - $this->getRequest()->setParams($params); - $this->getRequest()->setMethod('POST'); + $this->getRequest()->setParams($params)->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/index/massAssignGroup'); $this->assertSessionMessages( $this->equalTo(['Please select item(s).']), diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassDeleteTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassDeleteTest.php index 96e993932cb18..dc192c3c8681d 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassDeleteTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/MassDeleteTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\Constraint\Constraint; use Magento\Framework\Message\MessageInterface; use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\App\Request\Http as HttpRequest; use Magento\TestFramework\TestCase\AbstractBackendController; /** @@ -32,12 +33,18 @@ class MassDeleteTest extends AbstractBackendController */ private $baseControllerUrl = 'http://localhost/index.php/backend/customer/index/index'; + /** + * @inheritDoc + */ protected function setUp() { parent::setUp(); $this->customerRepository = Bootstrap::getObjectManager()->get(CustomerRepositoryInterface::class); } + /** + * @inheritDoc + */ protected function tearDown() { /** @@ -110,8 +117,7 @@ private function massDeleteAssertions($ids, Constraint $constraint, $messageType 'form_key' => $formKey->getFormKey() ]; - $this->getRequest()->setParams($requestData); - $this->getRequest()->setMethod('POST'); + $this->getRequest()->setParams($requestData)->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/index/massDelete'); $this->assertSessionMessages( $constraint, diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ResetPasswordTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ResetPasswordTest.php index b5ca783d68cf2..eaaba639d49a8 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ResetPasswordTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ResetPasswordTest.php @@ -5,6 +5,8 @@ */ namespace Magento\Customer\Controller\Adminhtml\Index; +use Magento\Framework\App\Request\Http as HttpRequest; + /** * ResetPassword controller test. * @@ -32,7 +34,7 @@ public function testResetPasswordSuccess() $this->passwordResetRequestEventCreate( \Magento\Security\Model\PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST ); - $this->getRequest()->setPostValue(['customer_id' => '1']); + $this->getRequest()->setPostValue(['customer_id' => '1'])->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/index/resetPassword'); $this->assertSessionMessages( $this->equalTo(['The customer will receive an email with a link to reset password.']), @@ -55,7 +57,7 @@ public function testResetPasswordMinTimeError() $this->passwordResetRequestEventCreate( \Magento\Security\Model\PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST ); - $this->getRequest()->setPostValue(['customer_id' => '1']); + $this->getRequest()->setPostValue(['customer_id' => '1'])->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/index/resetPassword'); $this->assertSessionMessages( $this->equalTo(['The customer will receive an email with a link to reset password.']), @@ -78,7 +80,7 @@ public function testResetPasswordLimitError() $this->passwordResetRequestEventCreate( \Magento\Security\Model\PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST ); - $this->getRequest()->setPostValue(['customer_id' => '1']); + $this->getRequest()->setPostValue(['customer_id' => '1'])->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/index/resetPassword'); $this->assertSessionMessages( $this->equalTo(['The customer will receive an email with a link to reset password.']), @@ -103,7 +105,7 @@ public function testResetPasswordWithSecurityViolationException() $this->passwordResetRequestEventCreate( \Magento\Security\Model\PasswordResetRequestEvent::ADMIN_PASSWORD_RESET_REQUEST ); - $this->getRequest()->setPostValue(['customer_id' => '1']); + $this->getRequest()->setPostValue(['customer_id' => '1'])->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/index/resetPassword'); $this->assertSessionMessages( $this->equalTo(['The customer will receive an email with a link to reset password.']), diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php index 36df9cbf851bd..db16af5aa69a0 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php @@ -12,6 +12,7 @@ use Magento\Customer\Controller\RegistryConstants; use Magento\Customer\Model\EmailNotification; use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\App\Request\Http as HttpRequest; /** * @magentoAppArea adminhtml @@ -85,7 +86,7 @@ protected function tearDown() */ public function testSaveActionWithEmptyPostData() { - $this->getRequest()->setPostValue([]); + $this->getRequest()->setPostValue([])->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/index/save'); $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl)); } @@ -96,7 +97,7 @@ public function testSaveActionWithEmptyPostData() public function testSaveActionWithInvalidFormData() { $post = ['account' => ['middlename' => 'test middlename', 'group_id' => 1]]; - $this->getRequest()->setPostValue($post); + $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/index/save'); /** * Check that errors was generated and set to session @@ -105,49 +106,13 @@ public function testSaveActionWithInvalidFormData() $this->logicalNot($this->isEmpty()), \Magento\Framework\Message\MessageInterface::TYPE_ERROR ); + /** @var \Magento\Backend\Model\Session $session */ + $session = $this->objectManager->get(\Magento\Backend\Model\Session::class); /** * Check that customer data were set to session */ - $this->assertEquals( - $post, - $this->objectManager->get(\Magento\Backend\Model\Session::class)->getCustomerFormData() - ); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'new')); - } - - /** - * @magentoDbIsolation enabled - */ - public function testSaveActionWithInvalidCustomerAddressData() - { - $post = [ - 'customer' => [ - 'middlename' => 'test middlename', - 'group_id' => 1, - 'website_id' => 0, - 'firstname' => 'test firstname', - 'lastname' => 'test lastname', - 'email' => 'example@domain.com', - 'default_billing' => '_item1', - ], - 'address' => ['_item1' => []], - ]; - $this->getRequest()->setPostValue($post); - $this->dispatch('backend/customer/index/save'); - /** - * Check that errors was generated and set to session - */ - $this->assertSessionMessages( - $this->logicalNot($this->isEmpty()), - \Magento\Framework\Message\MessageInterface::TYPE_ERROR - ); - /** - * Check that customer data were set to session - */ - $this->assertEquals( - $post, - $this->objectManager->get(\Magento\Backend\Model\Session::class)->getCustomerFormData() - ); + $this->assertNotEmpty($session->getCustomerFormData()); + $this->assertArraySubset($post, $session->getCustomerFormData()); $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'new')); } @@ -181,7 +146,7 @@ public function testSaveActionWithValidCustomerDataAndValidAddressData() ], ], ]; - $this->getRequest()->setPostValue($post); + $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setParam('back', '1'); // Emulate setting customer data to session in editAction @@ -293,7 +258,7 @@ public function testSaveActionExistingCustomerAndExistingAddressData() ], 'subscription' => '', ]; - $this->getRequest()->setPostValue($post); + $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setParam('id', 1); $this->dispatch('backend/customer/index/save'); @@ -359,7 +324,7 @@ public function testSaveActionExistingCustomerUnsubscribeNewsletter() ], 'subscription' => '0' ]; - $this->getRequest()->setPostValue($post); + $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setParam('id', 1); $this->dispatch('backend/customer/index/save'); @@ -397,7 +362,7 @@ public function testSaveActionExistingCustomerChangeEmail() 'change_email_template', [ 'name' => 'CustomerSupport', - 'email' => 'support@example.com' + 'email' => 'support@example.com', ], $customerId, $newEmail @@ -420,7 +385,7 @@ public function testSaveActionExistingCustomerChangeEmail() 'default_billing' => 1, ] ]; - $this->getRequest()->setPostValue($post); + $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setParam('id', 1); $this->dispatch('backend/customer/index/save'); @@ -447,7 +412,7 @@ public function testInlineEditChangeEmail() 'change_email_template', [ 'name' => 'CustomerSupport', - 'email' => 'support@example.com' + 'email' => 'support@example.com', ], $customerId, $newEmail @@ -467,7 +432,7 @@ public function testInlineEditChangeEmail() ] ]; $this->getRequest()->setParam('ajax', true)->setParam('isAjax', true); - $this->getRequest()->setPostValue($post); + $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setParam('id', 1); $this->dispatch('backend/customer/index/inlineEdit'); @@ -493,7 +458,7 @@ public function testSaveActionCoreException() 'password' => 'password', ], ]; - $this->getRequest()->setPostValue($post); + $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/index/save'); /* * Check that error message is set @@ -502,7 +467,7 @@ public function testSaveActionCoreException() $this->equalTo(['A customer with the same email already exists in an associated website.']), \Magento\Framework\Message\MessageInterface::TYPE_ERROR ); - $this->assertEquals( + $this->assertArraySubset( $post, Bootstrap::getObjectManager()->get(\Magento\Backend\Model\Session::class)->getCustomerFormData() ); @@ -579,8 +544,7 @@ public function testNotExistingCustomerDeleteAction() { $this->getRequest()->setParam('id', 2); $this->getRequest()->setParam('form_key', $this->formKey->getFormKey()); - - $this->getRequest()->setMethod(\Zend\Http\Request::METHOD_POST); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/index/delete'); $this->assertRedirect($this->stringContains('customer/index')); @@ -657,7 +621,7 @@ public function testValidateCustomerWithAddressSuccess() /** * set customer data */ - $this->getRequest()->setParams($customerData); + $this->getRequest()->setParams($customerData)->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/index/validate'); $body = $this->getResponse()->getBody(); @@ -711,7 +675,7 @@ public function testValidateCustomerWithAddressFailure() /** * set customer data */ - $this->getRequest()->setPostValue($customerData); + $this->getRequest()->setPostValue($customerData)->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/index/validate'); $body = $this->getResponse()->getBody(); @@ -728,6 +692,7 @@ public function testValidateCustomerWithAddressFailure() public function testResetPasswordActionNoCustomerId() { // No customer ID in post, will just get redirected to base + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/index/resetPassword'); $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl)); } @@ -738,6 +703,7 @@ public function testResetPasswordActionNoCustomerId() public function testResetPasswordActionBadCustomerId() { // Bad customer ID in post, will just get redirected to base + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue(['customer_id' => '789']); $this->dispatch('backend/customer/index/resetPassword'); $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl)); @@ -749,6 +715,7 @@ public function testResetPasswordActionBadCustomerId() public function testResetPasswordActionSuccess() { $this->getRequest()->setPostValue(['customer_id' => '1']); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/index/resetPassword'); $this->assertSessionMessages( $this->equalTo(['The customer will receive an email with a link to reset password.']), @@ -789,7 +756,7 @@ protected function prepareEmailMock($occurrenceNumber, $templateId, $sender, $cu 'setTemplateIdentifier', 'setTemplateVars', 'setTemplateOptions', - 'getTransport' + 'getTransport', ] ) ->getMock(); diff --git a/dev/tests/integration/testsuite/Magento/Integration/Controller/Adminhtml/IntegrationTest.php b/dev/tests/integration/testsuite/Magento/Integration/Controller/Adminhtml/IntegrationTest.php index 8011873577dc8..4da0c12c6087a 100644 --- a/dev/tests/integration/testsuite/Magento/Integration/Controller/Adminhtml/IntegrationTest.php +++ b/dev/tests/integration/testsuite/Magento/Integration/Controller/Adminhtml/IntegrationTest.php @@ -7,6 +7,7 @@ namespace Magento\Integration\Controller\Adminhtml; use Magento\TestFramework\Bootstrap; +use Magento\Framework\App\Request\Http as HttpRequest; /** * \Magento\Integration\Controller\Adminhtml\Integration @@ -20,6 +21,9 @@ class IntegrationTest extends \Magento\TestFramework\TestCase\AbstractBackendCon /** @var \Magento\Integration\Model\Integration */ private $_integration; + /** + * @inheritDoc + */ protected function setUp() { parent::setUp(); @@ -29,6 +33,9 @@ protected function setUp() $this->_integration = $integration->load('Fixture Integration', 'name'); } + /** + * Test view page. + */ public function testIndexAction() { $this->dispatch('backend/admin/integration/index'); @@ -44,6 +51,9 @@ public function testIndexAction() ); } + /** + * Test creation form. + */ public function testNewAction() { $this->dispatch('backend/admin/integration/new'); @@ -61,6 +71,9 @@ public function testNewAction() ); } + /** + * Test update form. + */ public function testEditAction() { $integrationId = $this->_integration->getId(); @@ -88,12 +101,16 @@ public function testEditAction() ); } + /** + * Test saving. + */ public function testSaveActionUpdateIntegration() { $integrationId = $this->_integration->getId(); $integrationName = $this->_integration->getName(); $this->getRequest()->setParam('id', $integrationId); $url = 'http://magento.ll/endpoint_url'; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue( [ 'name' => $integrationName, @@ -111,10 +128,14 @@ public function testSaveActionUpdateIntegration() $this->assertRedirect($this->stringContains('backend/admin/integration/index/')); } + /** + * Test saving. + */ public function testSaveActionNewIntegration() { $url = 'http://magento.ll/endpoint_url'; $integrationName = md5(rand()); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue( [ 'name' => $integrationName, diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Adminhtml/NewsletterQueueTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Adminhtml/NewsletterQueueTest.php index 5c1a11756c1b1..7a0ac030d120b 100644 --- a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Adminhtml/NewsletterQueueTest.php +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Adminhtml/NewsletterQueueTest.php @@ -5,6 +5,8 @@ */ namespace Magento\Newsletter\Controller\Adminhtml; +use Magento\Framework\App\Request\Http as HttpRequest; + /** * @magentoAppArea adminhtml */ @@ -15,6 +17,9 @@ class NewsletterQueueTest extends \Magento\TestFramework\TestCase\AbstractBacken */ protected $_model; + /** + * @inheritDoc + */ protected function setUp() { parent::setUp(); @@ -23,6 +28,9 @@ protected function setUp() ); } + /** + * @inheritDoc + */ protected function tearDown() { /** @@ -47,6 +55,7 @@ public function testSaveActionQueueTemplateAndVerifySuccessMessage() 'subject' => 'test subject', 'text' => 'newsletter text', ]; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue($postForQueue); // Loading by code, since ID will vary. template_code is not actually used to load anywhere else. diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Adminhtml/NewsletterTemplateTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Adminhtml/NewsletterTemplateTest.php index 50e89d92e434c..4a5b190d789d7 100644 --- a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Adminhtml/NewsletterTemplateTest.php +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Adminhtml/NewsletterTemplateTest.php @@ -20,6 +20,9 @@ class NewsletterTemplateTest extends \Magento\TestFramework\TestCase\AbstractBac */ protected $model; + /** + * @inheritDoc + */ protected function setUp() { parent::setUp(); @@ -39,6 +42,9 @@ protected function setUp() ); } + /** + * @inheritDoc + */ protected function tearDown() { /** diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/SaveTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/SaveTest.php index da0f2be856c51..26b5f58760da8 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/SaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/SaveTest.php @@ -78,6 +78,7 @@ public function testExecuteWithPaymentOperation() 'email' => $email, ] ]; + $this->getRequest()->setMethod('POST'); $this->getRequest()->setPostValue(['order' => $data]); /** @var OrderService|MockObject $orderService */ diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php index 7110f39ee532c..f8e468ac42a32 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php @@ -43,6 +43,7 @@ protected function setUp() public function testLoadBlockAction() { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setParam('block', ','); $this->getRequest()->setParam('json', 1); $this->dispatch('backend/sales/order_create/loadBlock'); @@ -60,6 +61,7 @@ public function testLoadBlockActionData() )->addProducts( [$product->getId() => ['qty' => 1]] ); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setParam('block', 'data'); $this->getRequest()->setParam('json', 1); $this->dispatch('backend/sales/order_create/loadBlock'); @@ -135,6 +137,7 @@ public function testLoadBlockShippingMethod() */ public function testLoadBlockActions($block, $expected) { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setParam('block', $block); $this->getRequest()->setParam('json', 1); $this->dispatch('backend/sales/order_create/loadBlock'); @@ -142,6 +145,9 @@ public function testLoadBlockActions($block, $expected) $this->assertContains($expected, $html); } + /** + * @return array + */ public function loadBlockActionsDataProvider() { return [ @@ -166,6 +172,7 @@ public function testLoadBlockActionItems() )->addProducts( [$product->getId() => ['qty' => 1]] ); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setParam('block', 'items'); $this->getRequest()->setParam('json', 1); $this->dispatch('backend/sales/order_create/loadBlock'); @@ -308,6 +315,7 @@ public function testDeniedSaveAction() \Magento\TestFramework\Helper\Bootstrap::getInstance() ->loadArea('adminhtml'); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/sales/order_create/save'); $this->assertEquals('403', $this->getResponse()->getHttpResponseCode()); } diff --git a/dev/tests/integration/testsuite/Magento/Theme/Controller/Adminhtml/System/Design/Config/SaveTest.php b/dev/tests/integration/testsuite/Magento/Theme/Controller/Adminhtml/System/Design/Config/SaveTest.php index 1ea2b28986d8a..1ceb15a63508c 100644 --- a/dev/tests/integration/testsuite/Magento/Theme/Controller/Adminhtml/System/Design/Config/SaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Theme/Controller/Adminhtml/System/Design/Config/SaveTest.php @@ -8,6 +8,7 @@ use Magento\Framework\Data\Form\FormKey; use Magento\TestFramework\TestCase\AbstractBackendController; +use Magento\Framework\App\Request\Http; /** * Class SaveTest @covers \Magento\Theme\Controller\Adminhtml\Design\Config\Save @@ -24,6 +25,11 @@ class SaveTest extends AbstractBackendController */ protected $uri = 'backend/theme/design_config/save'; + /** + * @var string + */ + protected $httpMethod = Http::METHOD_POST; + /** * Test design configuration save valid values. * diff --git a/dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/UserTest.php b/dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/UserTest.php index cfed67b9ddbdb..e62b45862025f 100644 --- a/dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/UserTest.php +++ b/dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/UserTest.php @@ -5,6 +5,7 @@ */ namespace Magento\User\Controller\Adminhtml; +use Magento\Framework\App\Request\Http as HttpRequest; use Magento\TestFramework\Bootstrap; /** @@ -34,6 +35,7 @@ public function testIndexAction() */ public function testSaveActionNoData() { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/admin/user/save'); $this->assertRedirect($this->stringContains('backend/admin/user/index/')); } @@ -54,6 +56,7 @@ public function testSaveActionWrongId() $userId = $user->getId(); $this->assertNotEmpty($userId, 'Broken fixture'); $user->delete(); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue('user_id', $userId); $this->dispatch('backend/admin/user/save'); $this->assertSessionMessages( @@ -71,6 +74,7 @@ public function testSaveActionWrongId() public function testSaveActionMissingCurrentAdminPassword() { $fixture = uniqid(); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue( [ 'username' => $fixture, @@ -94,6 +98,7 @@ public function testSaveActionMissingCurrentAdminPassword() public function testSaveAction() { $fixture = uniqid(); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue( [ 'username' => $fixture, @@ -121,6 +126,7 @@ public function testSaveAction() */ public function testSaveActionDuplicateUser() { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue( [ 'username' => 'adminUser', @@ -149,6 +155,7 @@ public function testSaveActionDuplicateUser() */ public function testSaveActionPasswordChange($postData, $isPasswordCorrect) { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue($postData); $this->dispatch('backend/admin/user/save'); diff --git a/lib/web/mage/adminhtml/tools.js b/lib/web/mage/adminhtml/tools.js index f23a0193cb6b2..369e755f80cfb 100644 --- a/lib/web/mage/adminhtml/tools.js +++ b/lib/web/mage/adminhtml/tools.js @@ -354,7 +354,7 @@ var Fieldset = { }, saveState: function (url, parameters) { new Ajax.Request(url, { - method: 'get', + method: 'post', parameters: Object.toQueryString(parameters), loaderArea: false }); diff --git a/setup/performance-toolkit/benchmark.jmx b/setup/performance-toolkit/benchmark.jmx index 64ff3bc9cba60..73403e543e918 100644 --- a/setup/performance-toolkit/benchmark.jmx +++ b/setup/performance-toolkit/benchmark.jmx @@ -32789,7 +32789,15 @@ vars.put("new_parent_category_id", props.get("admin_category_ids_list").get(cate <hashTree/> <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Delete category" enabled="true"> <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> - <collectionProp name="Arguments.arguments"/> + <collectionProp name="Arguments.arguments"> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + </collectionProp> </elementProp> <stringProp name="HTTPSampler.domain"/> <stringProp name="HTTPSampler.port"/> @@ -32798,7 +32806,7 @@ vars.put("new_parent_category_id", props.get("admin_category_ids_list").get(cate <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> <stringProp name="HTTPSampler.contentEncoding"/> <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/category/delete/id/${admin_category_id}/</stringProp> - <stringProp name="HTTPSampler.method">GET</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> <boolProp name="HTTPSampler.follow_redirects">true</boolProp> <boolProp name="HTTPSampler.auto_redirects">false</boolProp> <boolProp name="HTTPSampler.use_keepalive">true</boolProp> From 9024f9b8012a8d7f9db6baea69dd3e859c14db37 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Wed, 6 Feb 2019 12:50:04 +0200 Subject: [PATCH 0525/1295] MAGETWO-98110: [FT] [MFTF] StorefrontPurchaseProductWithCustomOptionsTest fails because of bad design --- ...ntPurchaseProductWithCustomOptionsTest.xml | 85 +++++++++---------- 1 file changed, 41 insertions(+), 44 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml index 94cdf4e22b25c..823e000bb9c27 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml @@ -20,23 +20,28 @@ </annotations> <before> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <!--Create Simple Product with Custom Options--> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + <field key="price">17</field> + </createData> + <updateData createDataKey="createProduct" entity="productWithOptions" stepKey="updateProductWithOption"/> + <!-- Logout customer before in case of it logged in from previous test --> + <actionGroup ref="CustomerLogoutStorefrontActionGroup" stepKey="logoutCustomer"/> </before> <after> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <!-- Delete product and category --> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearOrderListingFilters"/> <actionGroup ref="logout" stepKey="logoutAdmin"/> + <!-- Logout customer --> + <actionGroup ref="CustomerLogoutStorefrontActionGroup" stepKey="logoutCustomer"/> </after> - <!--Create Simple Product with Custom Options--> - - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - <field key="price">17</field> - </createData> - <updateData createDataKey="createProduct" entity="productWithOptions" stepKey="updateProductWithOption"/> - - <!-- Login Customer Storeront --> + <!-- Login Customer Storefront --> <actionGroup ref="CustomerLoginOnStorefront" stepKey="loginCustomerOnStorefront"> <argument name="customer" value="$$createCustomer$$"/> @@ -44,7 +49,7 @@ <!-- Checking the correctness of displayed prices for user parameters --> - <amOnPage url="{{StorefrontHomePage.url}}$createProduct.custom_attributes[url_key]$.html" stepKey="amOnProduct3Page"/> + <amOnPage url="{{StorefrontHomePage.url}}$$createProduct.custom_attributes[url_key]$$.html" stepKey="amOnProduct3Page"/> <seeElement selector="{{StorefrontProductInfoMainSection.productAttributeOptionsPrice(ProductOptionField.title, ProductOptionField.price)}}" stepKey="checkFieldProductOption"/> <seeElement selector="{{StorefrontProductInfoMainSection.productAttributeOptionsPrice(ProductOptionArea.title, '1.7')}}" stepKey="checkAreaProductOption"/> @@ -81,7 +86,7 @@ <grabTextFrom selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="finalProductPrice"/> <actionGroup ref="StorefrontAddToCartCustomOptionsProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> - <argument name="productName" value="$createProduct.name$"/> + <argument name="productName" value="$$createProduct.name$$"/> </actionGroup> <!-- Checking the correctness of displayed custom options for user parameters on checkout --> @@ -95,20 +100,20 @@ <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskForCartItem"/> <waitForElement selector="{{CheckoutPaymentSection.cartItemsAreaActive}}" time="30" stepKey="waitForCartItemsAreaActive"/> - <see selector="{{CheckoutPaymentSection.cartItems}}" userInput="$createProduct.name$" stepKey="seeProductInCart"/> + <see selector="{{CheckoutPaymentSection.cartItems}}" userInput="$$createProduct.name$$" stepKey="seeProductInCart"/> - <conditionalClick selector="{{CheckoutPaymentSection.productOptionsByProductItemName($createProduct.name$)}}" dependentSelector="{{CheckoutPaymentSection.productOptionsActiveByProductItemName($createProduct.name$)}}" visible="false" stepKey="exposeProductOptions"/> + <conditionalClick selector="{{CheckoutPaymentSection.productOptionsByProductItemName($$createProduct.name$$)}}" dependentSelector="{{CheckoutPaymentSection.productOptionsActiveByProductItemName($$createProduct.name$$)}}" visible="false" stepKey="exposeProductOptions"/> - <see selector="{{CheckoutPaymentSection.productOptionsActiveByProductItemName($createProduct.name$)}}" userInput="{{ProductOptionField.title}}" stepKey="seeProductOptionFieldInput1"/> - <see selector="{{CheckoutPaymentSection.productOptionsActiveByProductItemName($createProduct.name$)}}" userInput="{{ProductOptionArea.title}}" stepKey="seeProductOptionAreaInput1"/> - <see selector="{{CheckoutPaymentSection.productOptionsActiveByProductItemName($createProduct.name$)}}" userInput="{{productWithOptions.file}}" stepKey="seeProductOptionFileInput1"/> - <see selector="{{CheckoutPaymentSection.productOptionsActiveByProductItemName($createProduct.name$)}}" userInput="{{ProductOptionValueDropdown1.title}}" stepKey="seeProductOptionValueDropdown1Input1"/> - <see selector="{{CheckoutPaymentSection.productOptionsActiveByProductItemName($createProduct.name$)}}" userInput="{{ProductOptionValueRadioButtons1.title}}" stepKey="seeProductOptionValueRadioButtons1Input1"/> - <see selector="{{CheckoutPaymentSection.productOptionsActiveByProductItemName($createProduct.name$)}}" userInput="{{ProductOptionValueCheckbox.title}}" stepKey="seeProductOptionValueCheckboxInput1" /> - <see selector="{{CheckoutPaymentSection.productOptionsActiveByProductItemName($createProduct.name$)}}" userInput="{{ProductOptionValueMultiSelect1.title}}" stepKey="seeproductAttributeOptionsMultiselect1Input1" /> - <see selector="{{CheckoutPaymentSection.productOptionsActiveByProductItemName($createProduct.name$)}}" userInput="Jan 1, $year" stepKey="seeProductOptionDateAndTimeInput" /> - <see selector="{{CheckoutPaymentSection.productOptionsActiveByProductItemName($createProduct.name$)}}" userInput="1/1/$shortYear, 1:00 AM" stepKey="seeProductOptionDataInput" /> - <see selector="{{CheckoutPaymentSection.productOptionsActiveByProductItemName($createProduct.name$)}}" userInput="1:00 AM" stepKey="seeProductOptionTimeInput" /> + <see selector="{{CheckoutPaymentSection.productOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="{{ProductOptionField.title}}" stepKey="seeProductOptionFieldInput1"/> + <see selector="{{CheckoutPaymentSection.productOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="{{ProductOptionArea.title}}" stepKey="seeProductOptionAreaInput1"/> + <see selector="{{CheckoutPaymentSection.productOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="{{productWithOptions.file}}" stepKey="seeProductOptionFileInput1"/> + <see selector="{{CheckoutPaymentSection.productOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="{{ProductOptionValueDropdown1.title}}" stepKey="seeProductOptionValueDropdown1Input1"/> + <see selector="{{CheckoutPaymentSection.productOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="{{ProductOptionValueRadioButtons1.title}}" stepKey="seeProductOptionValueRadioButtons1Input1"/> + <see selector="{{CheckoutPaymentSection.productOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="{{ProductOptionValueCheckbox.title}}" stepKey="seeProductOptionValueCheckboxInput1" /> + <see selector="{{CheckoutPaymentSection.productOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="{{ProductOptionValueMultiSelect1.title}}" stepKey="seeproductAttributeOptionsMultiselect1Input1" /> + <see selector="{{CheckoutPaymentSection.productOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="Jan 1, $year" stepKey="seeProductOptionDateAndTimeInput" /> + <see selector="{{CheckoutPaymentSection.productOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="1/1/$shortYear, 1:00 AM" stepKey="seeProductOptionDataInput" /> + <see selector="{{CheckoutPaymentSection.productOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="1:00 AM" stepKey="seeProductOptionTimeInput" /> <!--Select shipping method--> <actionGroup ref="CheckoutSelectFlatRateShippingMethodActionGroup" stepKey="selectFlatRateShippingMethod"/> <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/> @@ -166,25 +171,17 @@ <!-- Go to Customer Order Page and Checking the correctness of displayed custom options for user parameters on Order --> - <amOnPage url="{{StorefrontCustomerOrderViewPage.url({$grabOrderNumber})}}" stepKey="amOnProduct4Page"/> - - <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($createProduct.name$, ProductOptionField.title, ProductOptionField.title)}}" userInput="{{ProductOptionField.title}}" stepKey="seeStorefontOrderProductOptionField1" /> - <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($createProduct.name$, ProductOptionArea.title, ProductOptionArea.title)}}" userInput="{{ProductOptionArea.title}}" stepKey="seeStorefontOrderProductOptionArea1"/> - <see selector="{{StorefrontCustomerOrderSection.productCustomOptionsFile($createProduct.name$, ProductOptionFile.title, productWithOptions.file)}}" userInput="{{productWithOptions.file}}" stepKey="seeStorefontOrderProductOptionFile1"/> - <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($createProduct.name$, ProductOptionDropDown.title, ProductOptionValueDropdown1.title)}}" userInput="{{ProductOptionValueDropdown1.title}}" stepKey="seeStorefontOrderProductOptionValueDropdown11"/> - <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($createProduct.name$, ProductOptionRadiobutton.title, ProductOptionValueRadioButtons1.title)}}" userInput="{{ProductOptionValueRadioButtons1.title}}" stepKey="seeStorefontOrderProductOptionValueRadioButtons11"/> - <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($createProduct.name$, ProductOptionCheckbox.title, ProductOptionValueCheckbox.title)}}" userInput="{{ProductOptionValueCheckbox.title}}" stepKey="seeStorefontOrderProductOptionValueCheckbox1" /> - <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($createProduct.name$, ProductOptionMultiSelect.title, ProductOptionValueMultiSelect1.title)}}" userInput="{{ProductOptionValueMultiSelect1.title}}" stepKey="seeStorefontOrderproductAttributeOptionsMultiselect11" /> - <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($createProduct.name$, ProductOptionDate.title, 'Jan 1, $year')}}" userInput="Jan 1, $year" stepKey="seeStorefontOrderProductOptionDateAndTime1" /> - <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($createProduct.name$, ProductOptionDateTime.title, '1/1/$shortYear, 1:00 AM')}}" userInput="1/1/$shortYear, 1:00 AM" stepKey="seeStorefontOrderProductOptionData1" /> - <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($createProduct.name$, ProductOptionTime.title, '1:00 AM')}}" userInput="1:00 AM" stepKey="seeStorefontOrderProductOptionTime1" /> - - <!-- Logout customer --> - <actionGroup ref="CustomerLogoutStorefrontActionGroup" stepKey="logoutCustomer"/> - - <!-- Delete product and category --> - - <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <amOnPage url="{{StorefrontCustomerOrderViewPage.url({$grabOrderNumber})}}" stepKey="amOnOrderPage"/> + + <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($$createProduct.name$$, ProductOptionField.title, ProductOptionField.title)}}" userInput="{{ProductOptionField.title}}" stepKey="seeStorefontOrderProductOptionField1" /> + <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($$createProduct.name$$, ProductOptionArea.title, ProductOptionArea.title)}}" userInput="{{ProductOptionArea.title}}" stepKey="seeStorefontOrderProductOptionArea1"/> + <see selector="{{StorefrontCustomerOrderSection.productCustomOptionsFile($$createProduct.name$$, ProductOptionFile.title, productWithOptions.file)}}" userInput="{{productWithOptions.file}}" stepKey="seeStorefontOrderProductOptionFile1"/> + <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($$createProduct.name$$, ProductOptionDropDown.title, ProductOptionValueDropdown1.title)}}" userInput="{{ProductOptionValueDropdown1.title}}" stepKey="seeStorefontOrderProductOptionValueDropdown11"/> + <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($$createProduct.name$$, ProductOptionRadiobutton.title, ProductOptionValueRadioButtons1.title)}}" userInput="{{ProductOptionValueRadioButtons1.title}}" stepKey="seeStorefontOrderProductOptionValueRadioButtons11"/> + <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($$createProduct.name$$, ProductOptionCheckbox.title, ProductOptionValueCheckbox.title)}}" userInput="{{ProductOptionValueCheckbox.title}}" stepKey="seeStorefontOrderProductOptionValueCheckbox1" /> + <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($$createProduct.name$$, ProductOptionMultiSelect.title, ProductOptionValueMultiSelect1.title)}}" userInput="{{ProductOptionValueMultiSelect1.title}}" stepKey="seeStorefontOrderproductAttributeOptionsMultiselect11" /> + <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($$createProduct.name$$, ProductOptionDate.title, 'Jan 1, $year')}}" userInput="Jan 1, $year" stepKey="seeStorefontOrderProductOptionDateAndTime1" /> + <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($$createProduct.name$$, ProductOptionDateTime.title, '1/1/$shortYear, 1:00 AM')}}" userInput="1/1/$shortYear, 1:00 AM" stepKey="seeStorefontOrderProductOptionData1" /> + <see selector="{{StorefrontCustomerOrderSection.productCustomOptions($$createProduct.name$$, ProductOptionTime.title, '1:00 AM')}}" userInput="1:00 AM" stepKey="seeStorefontOrderProductOptionTime1" /> </test> </tests> From 03f4af3ec92c1487f1564f3967e729edf137518c Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Wed, 6 Feb 2019 14:05:59 +0200 Subject: [PATCH 0526/1295] MAGETWO-95182: Order placed by new customer before they registered is displayed in the admin under the name Guest --- ...ontCheckCustomerInfoCreatedByGuestTest.xml | 61 +++++++++++++++++++ .../AssignOrderToCustomerObserver.php | 11 ++++ .../AssignOrderToCustomerObserverTest.php | 33 ++++++++-- 3 files changed, 99 insertions(+), 6 deletions(-) create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCustomerInfoCreatedByGuestTest.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCustomerInfoCreatedByGuestTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCustomerInfoCreatedByGuestTest.xml new file mode 100644 index 0000000000000..adc308b490c33 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCustomerInfoCreatedByGuestTest.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCheckCustomerInfoCreatedByGuestTest"> + <annotations> + <features value="Checkout"/> + <stories value="Check customer information created by guest"/> + <title value="Check Customer Information Created By Guest"/> + <description value="Check customer information after placing the order as the guest who created an account"/> + <severity value="MAJOR"/> + <testCaseId value="MC-13839"/> + <useCaseId value="MAGETWO-95820"/> + <group value="checkout"/> + <group value="customer"/> + </annotations> + + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + + <after> + <actionGroup ref="CustomerLogoutStorefrontActionGroup" stepKey="customerLogoutFromStorefront" /> + <deleteData createDataKey="createProduct" stepKey="deleteProduct" /> + <deleteData createDataKey="createCategory" stepKey="deleteCategory" /> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + </after> + + <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="navigateToProductPage"/> + <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> + <argument name="productName" value="$$createProduct.name$$"/> + </actionGroup> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> + <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="CustomerAddressSimple"/> + </actionGroup> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="placeOrder"> + <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage" /> + </actionGroup> + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> + <click selector="{{CheckoutSuccessRegisterSection.createAccountButton}}" stepKey="clickCreateAccountButton"/> + <fillField selector="{{StorefrontCustomerCreateFormSection.passwordField}}" userInput="{{CustomerEntityOne.password}}" stepKey="typePassword"/> + <fillField selector="{{StorefrontCustomerCreateFormSection.confirmPasswordField}}" userInput="{{CustomerEntityOne.password}}" stepKey="typeConfirmationPassword"/> + <click selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}" stepKey="clickOnCreateAccount"/> + <see selector="{{StorefrontMessagesSection.successMessage}}" userInput="Thank you for registering" stepKey="verifyAccountCreated"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdmin"/> + <amOnPage url="{{AdminOrderDetailsPage.url('$grabOrderNumber')}}" stepKey="navigateToOrderPage"/> + <see userInput="{{CustomerEntityOne.firstname}}" selector="{{AdminOrderDetailsInformationSection.customerName}}" stepKey="seeCustomerName"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Observer/AssignOrderToCustomerObserver.php b/app/code/Magento/Sales/Observer/AssignOrderToCustomerObserver.php index 9857fa39fa51a..80e909941c5ce 100644 --- a/app/code/Magento/Sales/Observer/AssignOrderToCustomerObserver.php +++ b/app/code/Magento/Sales/Observer/AssignOrderToCustomerObserver.php @@ -57,6 +57,17 @@ public function execute(Observer $observer) $orderId = $delegateData['__sales_assign_order_id']; $order = $this->orderRepository->get($orderId); if (!$order->getCustomerId() && $customer->getId()) { + // Assign customer info to order after customer creation. + $order->setCustomerId($customer->getId()) + ->setCustomerIsGuest(0) + ->setCustomerEmail($customer->getEmail()) + ->setCustomerFirstname($customer->getFirstname()) + ->setCustomerLastname($customer->getLastname()) + ->setCustomerMiddlename($customer->getMiddlename()) + ->setCustomerPrefix($customer->getPrefix()) + ->setCustomerSuffix($customer->getSuffix()) + ->setCustomerGroupId($customer->getGroupId()); + $this->assignmentService->execute($order, $customer); } } diff --git a/app/code/Magento/Sales/Test/Unit/Observer/AssignOrderToCustomerObserverTest.php b/app/code/Magento/Sales/Test/Unit/Observer/AssignOrderToCustomerObserverTest.php index 18371274049e3..8890f01130c82 100644 --- a/app/code/Magento/Sales/Test/Unit/Observer/AssignOrderToCustomerObserverTest.php +++ b/app/code/Magento/Sales/Test/Unit/Observer/AssignOrderToCustomerObserverTest.php @@ -52,9 +52,10 @@ protected function setUp() * * @dataProvider getCustomerIds * @param null|int $customerId + * @param null|int $customerOrderId * @return void */ - public function testAssignOrderToCustomerAfterGuestOrder($customerId) + public function testAssignOrderToCustomerAfterGuestOrder($customerId, $customerOrderId) { $orderId = 1; /** @var Observer|PHPUnit_Framework_MockObject_MockObject $observerMock */ @@ -64,7 +65,12 @@ public function testAssignOrderToCustomerAfterGuestOrder($customerId) ->setMethods(['getData']) ->getMock(); /** @var CustomerInterface|PHPUnit_Framework_MockObject_MockObject $customerMock */ - $customerMock = $this->createMock(CustomerInterface::class); + $customerMock = $this->getMockBuilder(CustomerInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $customerMock->expects($this->any()) + ->method('getId') + ->willReturn($customerId); /** @var OrderInterface|PHPUnit_Framework_MockObject_MockObject $orderMock */ $orderMock = $this->getMockBuilder(OrderInterface::class) ->disableOriginalConstructor() @@ -75,13 +81,24 @@ public function testAssignOrderToCustomerAfterGuestOrder($customerId) ['delegate_data', null, ['__sales_assign_order_id' => $orderId]], ['customer_data_object', null, $customerMock] ]); - $orderMock->expects($this->once())->method('getCustomerId')->willReturn($customerId); + $orderMock->expects($this->any())->method('getCustomerId')->willReturn($customerOrderId); $this->orderRepositoryMock->expects($this->once())->method('get')->with($orderId) ->willReturn($orderMock); - if ($customerId) { + if (!$customerOrderId && $customerId) { + $orderMock->expects($this->once())->method('setCustomerId')->willReturn($orderMock); + $orderMock->expects($this->once())->method('setCustomerIsGuest')->willReturn($orderMock); + $orderMock->expects($this->once())->method('setCustomerEmail')->willReturn($orderMock); + $orderMock->expects($this->once())->method('setCustomerFirstname')->willReturn($orderMock); + $orderMock->expects($this->once())->method('setCustomerLastname')->willReturn($orderMock); + $orderMock->expects($this->once())->method('setCustomerMiddlename')->willReturn($orderMock); + $orderMock->expects($this->once())->method('setCustomerPrefix')->willReturn($orderMock); + $orderMock->expects($this->once())->method('setCustomerSuffix')->willReturn($orderMock); + $orderMock->expects($this->once())->method('setCustomerGroupId')->willReturn($orderMock); + $this->assignmentMock->expects($this->once())->method('execute')->with($orderMock, $customerMock); $this->sut->execute($observerMock); + return; } @@ -94,8 +111,12 @@ public function testAssignOrderToCustomerAfterGuestOrder($customerId) * * @return array */ - public function getCustomerIds() + public function getCustomerIds(): array { - return [[null, 1]]; + return [ + [null, null], + [1, null], + [1, 1], + ]; } } From 0002710bbee3658ed56ce2b1bd9b8710ad506c60 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Wed, 6 Feb 2019 14:51:44 +0200 Subject: [PATCH 0527/1295] MAGETWO-96061: App:config:dump doesn't lock all the settings in Magento backend --- .../Magento/Framework/App/Test/Unit/DeploymentConfigTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfigTest.php b/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfigTest.php index c6c90c1fea541..3508cfed0777b 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfigTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfigTest.php @@ -143,11 +143,11 @@ public function keyCollisionDataProvider() /** * @param string $key - * @param string $expectedFlattenData + * @param string|null $expectedFlattenData * @return void * @dataProvider getDataProvider */ - public function testGet(string $key, string $expectedFlattenData) + public function testGet(string $key, $expectedFlattenData) { $flatData = [ 'key1' => 'value', From c3ce11c09c442bb83cd007f43c392e4f95790e74 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Wed, 6 Feb 2019 15:05:08 +0200 Subject: [PATCH 0528/1295] MAGETWO-73978: API call with pageSize and currentPage > items should return error --- app/code/Magento/Catalog/Test/Unit/Model/CategoryListTest.php | 2 ++ .../Magento/Framework/Data/Test/Unit/CollectionTest.php | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/CategoryListTest.php b/app/code/Magento/Catalog/Test/Unit/Model/CategoryListTest.php index f78c0ad924954..ab0c0ea38d79c 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/CategoryListTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/CategoryListTest.php @@ -86,7 +86,9 @@ public function testGetList() $categoryIdSecond = 2; $categoryFirst = $this->getMockBuilder(Category::class)->disableOriginalConstructor()->getMock(); + $categoryFirst->expects($this->atLeastOnce())->method('getId')->willReturn($categoryIdFirst); $categorySecond = $this->getMockBuilder(Category::class)->disableOriginalConstructor()->getMock(); + $categorySecond->expects($this->atLeastOnce())->method('getId')->willReturn($categoryIdSecond); /** @var SearchCriteriaInterface|\PHPUnit_Framework_MockObject_MockObject $searchCriteria */ $searchCriteria = $this->createMock(SearchCriteriaInterface::class); diff --git a/lib/internal/Magento/Framework/Data/Test/Unit/CollectionTest.php b/lib/internal/Magento/Framework/Data/Test/Unit/CollectionTest.php index c1f504fc72086..0b6ddd6999d3e 100644 --- a/lib/internal/Magento/Framework/Data/Test/Unit/CollectionTest.php +++ b/lib/internal/Magento/Framework/Data/Test/Unit/CollectionTest.php @@ -149,14 +149,13 @@ public function testGetCurPage() /** * Test for getCurPage with exception. * - * @expectedException \Magento\Framework\Exception\StateException + * @expectedException \Magento\Framework\Exception\InputException * @expectedExceptionMessage currentPage value 10 specified is greater than the 1 page(s) available. * @return void */ public function testGetCurPageWithException() { $this->_model->setCurPage(10); - $this->expectException(\Magento\Framework\Exception\InputException::class); $this->_model->getCurPage(); } From 8923b6a1e939904f7299050e616258e255e558fa Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Wed, 6 Feb 2019 13:07:29 +0000 Subject: [PATCH 0529/1295] Fixed static test failures --- .../blank/Magento_Checkout/web/css/source/module/_minicart.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less index 27655417984d0..5cf1e9f59af39 100644 --- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less +++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less @@ -107,7 +107,7 @@ @_toggle-selector: ~'.action.showcart', @_options-selector: ~'.block-minicart', @_dropdown-list-width: 320px, - @_dropdown-list-position-right: 0px, + @_dropdown-list-position-right: 0, @_dropdown-list-pointer-position: right, @_dropdown-list-pointer-position-left-right: 26px, @_dropdown-list-z-index: 101, From 6b50cd7a3549d55d89297faf4f1b3303a855ab47 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Wed, 6 Feb 2019 15:26:08 +0200 Subject: [PATCH 0530/1295] MAGETWO-74043: [GITHUB] Magento 2.1 CE, Cannot change attribute set for bundled product #5999 --- .../Ui/DataProvider/Product/Form/Modifier/BundlePanel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php index 8526e9a9e5d5d..150247729f125 100644 --- a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php +++ b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php @@ -85,7 +85,7 @@ public function modifyMeta(array $meta) $meta[$groupCode]['arguments']['data']['config'] = [ 'componentType' => Fieldset::NAME, 'label' => __('Bundle Items'), - 'collapsible' => true + 'collapsible' => true, ]; $path = $this->arrayManager->findPath($groupCode, $meta, null, 'children'); From 8634afc57fe063d368ce5f1c4d98e97f5536903a Mon Sep 17 00:00:00 2001 From: Iurii Ivashchenko <iivashchenko@magento.com> Date: Wed, 6 Feb 2019 15:43:17 +0200 Subject: [PATCH 0531/1295] MAGETWO-97898: [Magento Cloud] Store switcher URL redirects to the same store --- app/code/Magento/Store/Model/StoreResolver/Website.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Store/Model/StoreResolver/Website.php b/app/code/Magento/Store/Model/StoreResolver/Website.php index 2a8b30a007ad9..d4bb990307f1e 100644 --- a/app/code/Magento/Store/Model/StoreResolver/Website.php +++ b/app/code/Magento/Store/Model/StoreResolver/Website.php @@ -45,8 +45,10 @@ public function getAllowedStoreIds($scopeCode) $stores = []; $website = $scopeCode ? $this->websiteRepository->get($scopeCode) : $this->websiteRepository->getDefault(); foreach ($this->storeRepository->getList() as $store) { - if (!$scopeCode || ($store->isActive() && $store->getWebsiteId() === $website->getId())) { - $stores[] = $store->getId(); + if ($store->isActive()) { + if (!$scopeCode || ($store->getWebsiteId() === $website->getId())) { + $stores[] = $store->getId(); + } } } return $stores; From 74161d77d677dff24a49b1c2a1b9704f60a4f2cb Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Wed, 6 Feb 2019 15:54:16 +0200 Subject: [PATCH 0532/1295] MAGETWO-88612: Exception when login as restricted admin with access only to CMS Block --- .../AdminRestrictedUserOnlyAccessCmsBlockTest.xml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminRestrictedUserOnlyAccessCmsBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminRestrictedUserOnlyAccessCmsBlockTest.xml index 0e285555f8698..d0ed330779676 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminRestrictedUserOnlyAccessCmsBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminRestrictedUserOnlyAccessCmsBlockTest.xml @@ -45,12 +45,13 @@ </after> <!--login as restricted user--> - <amOnPage stepKey="amOnAdminLoginPage" url="{{AdminLoginPage.url}}"/> - <fillField selector="{{AdminLoginFormSection.username}}" userInput="$$createRestrictedAdmin.username$$" stepKey="fillUsername" /> - <fillField selector="{{AdminLoginFormSection.password}}" userInput="$$createRestrictedAdmin.password$$" stepKey="fillPassword"/> - <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickOnSignIn2" /> + <actionGroup ref="AdminLoginAsAnyUser" stepKey="logAsNewUser"> + <argument name="login" value="$$createRestrictedAdmin.username$$"/> + <argument name="password" value="$$createRestrictedAdmin.password$$"/> + </actionGroup> + <!--Verify that The system shows information included in "Blocks"--> - <see stepKey="seeBlocksPage" userInput="Blocks"/> + <see userInput="Blocks" stepKey="seeBlocksPage"/> <seeInCurrentUrl url="{{AdminCmsBlockGridPage.url}}" stepKey="assertUrl"/> <!--Log Out--> <actionGroup ref="logout" stepKey="logOut"/> From 79ffb40885e28860527d91c37db343c41a93b122 Mon Sep 17 00:00:00 2001 From: Yevhen Miroshnychenko <ymiroshnychenko@magento.com> Date: Wed, 6 Feb 2019 11:25:17 -0600 Subject: [PATCH 0533/1295] MAGETWO-97313: [Backport 2.2.x] Mysql reconnect does not work --- .../Framework/DB/Adapter/Pdo/MysqlTest.php | 41 +---- .../Framework/DB/Statement/Pdo/Mysql.php | 41 +++-- .../DB/Test/Unit/DB/Statement/MysqlTest.php | 154 ++++++++++++++++++ 3 files changed, 192 insertions(+), 44 deletions(-) create mode 100644 lib/internal/Magento/Framework/DB/Test/Unit/DB/Statement/MysqlTest.php diff --git a/dev/tests/integration/testsuite/Magento/Framework/DB/Adapter/Pdo/MysqlTest.php b/dev/tests/integration/testsuite/Magento/Framework/DB/Adapter/Pdo/MysqlTest.php index cf3b9f05cbe0f..403c45dde71a3 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/DB/Adapter/Pdo/MysqlTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/DB/Adapter/Pdo/MysqlTest.php @@ -8,6 +8,7 @@ use Magento\Framework\App\ResourceConnection; use Magento\TestFramework\Helper\CacheCleaner; use Magento\Framework\DB\Ddl\Table; +use Magento\TestFramework\Helper\Bootstrap; class MysqlTest extends \PHPUnit\Framework\TestCase { @@ -19,7 +20,7 @@ class MysqlTest extends \PHPUnit\Framework\TestCase protected function setUp() { set_error_handler(null); - $this->resourceConnection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + $this->resourceConnection = Bootstrap::getObjectManager() ->get(ResourceConnection::class); CacheCleaner::cleanAll(); } @@ -40,7 +41,6 @@ public function testWaitTimeout() $this->markTestSkipped('This test is for \Magento\Framework\DB\Adapter\Pdo\Mysql'); } try { - $defaultWaitTimeout = $this->getWaitTimeout(); $minWaitTimeout = 1; $this->setWaitTimeout($minWaitTimeout); $this->assertEquals($minWaitTimeout, $this->getWaitTimeout(), 'Wait timeout was not changed'); @@ -49,17 +49,8 @@ public function testWaitTimeout() sleep($minWaitTimeout + 1); $result = $this->executeQuery('SELECT 1'); $this->assertInstanceOf(\Magento\Framework\DB\Statement\Pdo\Mysql::class, $result); - // Restore wait_timeout - $this->setWaitTimeout($defaultWaitTimeout); - $this->assertEquals( - $defaultWaitTimeout, - $this->getWaitTimeout(), - 'Default wait timeout was not restored' - ); - } catch (\Exception $e) { - // Reset connection on failure to restore global variables + } finally { $this->getDbAdapter()->closeConnection(); - throw $e; } } @@ -87,30 +78,14 @@ private function setWaitTimeout($waitTimeout) /** * Execute SQL query and return result statement instance * - * @param string $sql - * @return \Zend_Db_Statement_Interface - * @throws \Exception + * @param $sql + * @return void|\Zend_Db_Statement_Pdo + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Zend_Db_Adapter_Exception */ private function executeQuery($sql) { - /** - * Suppress PDO warnings to work around the bug https://bugs.php.net/bug.php?id=63812 - */ - $phpErrorReporting = error_reporting(); - /** @var $pdoConnection \PDO */ - $pdoConnection = $this->getDbAdapter()->getConnection(); - $pdoWarningsEnabled = $pdoConnection->getAttribute(\PDO::ATTR_ERRMODE) & \PDO::ERRMODE_WARNING; - if (!$pdoWarningsEnabled) { - error_reporting($phpErrorReporting & ~E_WARNING); - } - try { - $result = $this->getDbAdapter()->query($sql); - error_reporting($phpErrorReporting); - } catch (\Exception $e) { - error_reporting($phpErrorReporting); - throw $e; - } - return $result; + return $this->getDbAdapter()->query($sql); } /** diff --git a/lib/internal/Magento/Framework/DB/Statement/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Statement/Pdo/Mysql.php index 7b8314a76f32e..d24bc5fef6ef6 100644 --- a/lib/internal/Magento/Framework/DB/Statement/Pdo/Mysql.php +++ b/lib/internal/Magento/Framework/DB/Statement/Pdo/Mysql.php @@ -3,21 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +namespace Magento\Framework\DB\Statement\Pdo; + +use Magento\Framework\DB\Statement\Parameter; /** * Mysql DB Statement * * @author Magento Core Team <core@magentocommerce.com> */ -namespace Magento\Framework\DB\Statement\Pdo; - -use Magento\Framework\DB\Statement\Parameter; - class Mysql extends \Zend_Db_Statement_Pdo { + /** - * Executes statement with binding values to it. - * Allows transferring specific options to DB driver. + * Executes statement with binding values to it. Allows transferring specific options to DB driver. * * @param array $params Array of values to bind to parameter placeholders. * @return bool @@ -61,11 +60,9 @@ public function _executeWithBinding(array $params) $statement->bindParam($paramName, $bindValues[$name], $dataType, $length, $driverOptions); } - try { + return $this->tryExecute(function () use ($statement) { return $statement->execute(); - } catch (\PDOException $e) { - throw new \Zend_Db_Statement_Exception($e->getMessage(), (int)$e->getCode(), $e); - } + }); } /** @@ -90,7 +87,29 @@ public function _execute(array $params = null) if ($specialExecute) { return $this->_executeWithBinding($params); } else { - return parent::_execute($params); + return $this->tryExecute(function () use ($params) { + return $params !== null ? $this->_stmt->execute($params) : $this->_stmt->execute(); + }); + } + } + + /** + * Executes query and avoid warnings. + * + * @param callable $callback + * @return bool + * @throws \Zend_Db_Statement_Exception + */ + private function tryExecute($callback) + { + $previousLevel = error_reporting(\E_ERROR); // disable warnings for PDO bugs #63812, #74401 + try { + return $callback(); + } catch (\PDOException $e) { + $message = sprintf('%s, query was: %s', $e->getMessage(), $this->_stmt->queryString); + throw new \Zend_Db_Statement_Exception($message, (int)$e->getCode(), $e); + } finally { + error_reporting($previousLevel); } } } diff --git a/lib/internal/Magento/Framework/DB/Test/Unit/DB/Statement/MysqlTest.php b/lib/internal/Magento/Framework/DB/Test/Unit/DB/Statement/MysqlTest.php new file mode 100644 index 0000000000000..714dfe6bb1059 --- /dev/null +++ b/lib/internal/Magento/Framework/DB/Test/Unit/DB/Statement/MysqlTest.php @@ -0,0 +1,154 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\DB\Test\Unit\DB\Statement; + +use Magento\Framework\DB\Statement\Parameter; +use Magento\Framework\DB\Statement\Pdo\Mysql; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * @inheritdoc + */ +class MysqlTest extends TestCase +{ + /** + * @var \Zend_Db_Adapter_Abstract|MockObject + */ + private $adapterMock; + + /** + * @var \PDO|MockObject + */ + private $pdoMock; + + /** + * @var \Zend_Db_Profiler|MockObject + */ + private $zendDbProfilerMock; + + /** + * @var \PDOStatement|MockObject + */ + private $pdoStatementMock; + + /** + * @inheritdoc + */ + public function setUp() + { + $this->adapterMock = $this->getMockForAbstractClass( + \Zend_Db_Adapter_Abstract::class, + [], + '', + false, + true, + true, + ['getConnection', 'getProfiler'] + ); + $this->pdoMock = $this->createMock(\PDO::class); + $this->adapterMock->expects($this->once()) + ->method('getConnection') + ->willReturn($this->pdoMock); + $this->zendDbProfilerMock = $this->createMock(\Zend_Db_Profiler::class); + $this->adapterMock->expects($this->once()) + ->method('getProfiler') + ->willReturn($this->zendDbProfilerMock); + $this->pdoStatementMock = $this->createMock(\PDOStatement::class); + } + + public function testExecuteWithoutParams() + { + $query = 'SET @a=1;'; + $this->pdoMock->expects($this->once()) + ->method('prepare') + ->with($query) + ->willReturn($this->pdoStatementMock); + $this->pdoStatementMock->expects($this->once()) + ->method('execute'); + (new Mysql($this->adapterMock, $query))->_execute(); + } + + public function testExecuteWhenThrowPDOException() + { + $this->expectException(\Zend_Db_Statement_Exception::class); + $this->expectExceptionMessage('test message, query was:'); + $errorReporting = error_reporting(); + $query = 'SET @a=1;'; + $this->pdoMock->expects($this->once()) + ->method('prepare') + ->with($query) + ->willReturn($this->pdoStatementMock); + $this->pdoStatementMock->expects($this->once()) + ->method('execute') + ->willThrowException(new \PDOException('test message')); + + $this->assertEquals($errorReporting, error_reporting(), 'Error report level was\'t restored'); + + (new Mysql($this->adapterMock, $query))->_execute(); + } + + public function testExecuteWhenParamsAsPrimitives() + { + $params = [':param1' => 'value1', ':param2' => 'value2']; + $query = 'UPDATE `some_table1` SET `col1`=\'val1\' WHERE `param1`=\':param1\' AND `param2`=\':param2\';'; + $this->pdoMock->expects($this->once()) + ->method('prepare') + ->with($query) + ->willReturn($this->pdoStatementMock); + $this->pdoStatementMock->expects($this->never()) + ->method('bindParam'); + $this->pdoStatementMock->expects($this->once()) + ->method('execute') + ->with($params); + + (new Mysql($this->adapterMock, $query))->_execute($params); + } + + public function testExecuteWhenParamsAsParameterObject() + { + $param1 = $this->createMock(Parameter::class); + $param1Value = 'SomeValue'; + $param1DataType = 'dataType'; + $param1Length = '9'; + $param1DriverOptions = 'some driver options'; + $param1->expects($this->once()) + ->method('getIsBlob') + ->willReturn(false); + $param1->expects($this->once()) + ->method('getDataType') + ->willReturn($param1DataType); + $param1->expects($this->once()) + ->method('getLength') + ->willReturn($param1Length); + $param1->expects($this->once()) + ->method('getDriverOptions') + ->willReturn($param1DriverOptions); + $param1->expects($this->once()) + ->method('getValue') + ->willReturn($param1Value); + $params = [ + ':param1' => $param1, + ':param2' => 'value2', + ]; + $query = 'UPDATE `some_table1` SET `col1`=\'val1\' WHERE `param1`=\':param1\' AND `param2`=\':param2\';'; + $this->pdoMock->expects($this->once()) + ->method('prepare') + ->with($query) + ->willReturn($this->pdoStatementMock); + $this->pdoStatementMock->expects($this->exactly(2)) + ->method('bindParam') + ->withConsecutive( + [':param1', $param1Value, $param1DataType, $param1Length, $param1DriverOptions], + [':param2', 'value2', \PDO::PARAM_STR, null, null] + ); + $this->pdoStatementMock->expects($this->once()) + ->method('execute'); + + (new Mysql($this->adapterMock, $query))->_execute($params); + } +} From 2079268223cba612e46c6d35a676dc8de3c24ca7 Mon Sep 17 00:00:00 2001 From: slopukhov <lopukhov@adobe.com> Date: Mon, 8 Oct 2018 11:24:19 +0300 Subject: [PATCH 0534/1295] MAGETWO-97094: Enable caching of AJAX requests on Varnish/Fastly --- .../Swatches/Controller/Ajax/Media.php | 22 ++++++++++++++++--- .../view/frontend/web/js/swatch-renderer.js | 14 +++++++----- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Swatches/Controller/Ajax/Media.php b/app/code/Magento/Swatches/Controller/Ajax/Media.php index d26080635c456..efde67c93132a 100644 --- a/app/code/Magento/Swatches/Controller/Ajax/Media.php +++ b/app/code/Magento/Swatches/Controller/Ajax/Media.php @@ -24,18 +24,26 @@ class Media extends \Magento\Framework\App\Action\Action */ private $swatchHelper; + /** + * @var \Magento\PageCache\Model\Config + */ + protected $config; + /** * @param Context $context * @param \Magento\Catalog\Model\ProductFactory $productModelFactory * @param \Magento\Swatches\Helper\Data $swatchHelper + * @param \Magento\PageCache\Model\Config $config */ public function __construct( Context $context, \Magento\Catalog\Model\ProductFactory $productModelFactory, - \Magento\Swatches\Helper\Data $swatchHelper + \Magento\Swatches\Helper\Data $swatchHelper, + \Magento\PageCache\Model\Config $config ) { $this->productModelFactory = $productModelFactory; $this->swatchHelper = $swatchHelper; + $this->config = $config; parent::__construct($context); } @@ -49,14 +57,22 @@ public function __construct( public function execute() { $productMedia = []; + + /** @var \Magento\Framework\Controller\Result\Json $resultJson */ + $resultJson = $this->resultFactory->create(ResultFactory::TYPE_JSON); + + /** @var \Magento\Framework\App\ResponseInterface $response */ + $response = $this->getResponse(); + if ($productId = (int)$this->getRequest()->getParam('product_id')) { $productMedia = $this->swatchHelper->getProductMediaGallery( $this->productModelFactory->create()->load($productId) ); + $resultJson->setHeader('X-Magento-Tags', 'catalog_product_' . $productId); + + $response->setPublicHeaders($this->config->getTtl()); } - /** @var \Magento\Framework\Controller\Result\Json $resultJson */ - $resultJson = $this->resultFactory->create(ResultFactory::TYPE_JSON); $resultJson->setData($productMedia); return $resultJson; } 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 d2ccda412de77..3e28982ad44d3 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 @@ -1085,12 +1085,14 @@ define([ mediaCallData.isAjax = true; $widget._XhrKiller(); $widget._EnableProductMediaLoader($this); - $widget.xhr = $.get( - $widget.options.mediaCallback, - mediaCallData, - mediaSuccessCallback, - 'json' - ).done(function () { + $widget.xhr = $.ajax({ + url: $widget.options.mediaCallback, + cache: true, + type: 'GET', + dataType: 'json', + data: mediaCallData, + success: mediaSuccessCallback + }).done(function () { $widget._XhrKiller(); }); } From 31a2be6b88d25cf888bf650a0c257b74a7d781f4 Mon Sep 17 00:00:00 2001 From: slopukhov <lopukhov@adobe.com> Date: Tue, 9 Oct 2018 07:49:30 +0300 Subject: [PATCH 0535/1295] MAGETWO-97094: Enable caching of AJAX requests on Varnish/Fastly --- app/code/Magento/Swatches/Controller/Ajax/Media.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Swatches/Controller/Ajax/Media.php b/app/code/Magento/Swatches/Controller/Ajax/Media.php index efde67c93132a..2aaf158064947 100644 --- a/app/code/Magento/Swatches/Controller/Ajax/Media.php +++ b/app/code/Magento/Swatches/Controller/Ajax/Media.php @@ -65,10 +65,11 @@ public function execute() $response = $this->getResponse(); if ($productId = (int)$this->getRequest()->getParam('product_id')) { + $product = $this->productModelFactory->create()->load($productId); $productMedia = $this->swatchHelper->getProductMediaGallery( - $this->productModelFactory->create()->load($productId) + $product ); - $resultJson->setHeader('X-Magento-Tags', 'catalog_product_' . $productId); + $resultJson->setHeader('X-Magento-Tags', implode(',', $product->getIdentities())); $response->setPublicHeaders($this->config->getTtl()); } From 649517ddc9e799ecb888385e52630b986cbc5c40 Mon Sep 17 00:00:00 2001 From: slopukhov <lopukhov@adobe.com> Date: Wed, 10 Oct 2018 14:08:04 +0300 Subject: [PATCH 0536/1295] MAGETWO-97094: Enable caching of AJAX requests on Varnish/Fastly --- .../Swatches/Controller/Ajax/Media.php | 2 +- .../Test/Unit/Controller/Ajax/MediaTest.php | 22 ++++++++++++++++++- app/code/Magento/Swatches/composer.json | 1 + 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Swatches/Controller/Ajax/Media.php b/app/code/Magento/Swatches/Controller/Ajax/Media.php index 2aaf158064947..2d20b9a9a4b7e 100644 --- a/app/code/Magento/Swatches/Controller/Ajax/Media.php +++ b/app/code/Magento/Swatches/Controller/Ajax/Media.php @@ -12,7 +12,7 @@ /** * Class Media */ -class Media extends \Magento\Framework\App\Action\Action +class Media extends \Magento\Framework\App\Action\Action implements \Magento\Framework\App\Action\HttpGetActionInterface { /** * @var \Magento\Catalog\Model\Product Factory diff --git a/app/code/Magento/Swatches/Test/Unit/Controller/Ajax/MediaTest.php b/app/code/Magento/Swatches/Test/Unit/Controller/Ajax/MediaTest.php index 7a110c63da79e..5a11e2787bc69 100644 --- a/app/code/Magento/Swatches/Test/Unit/Controller/Ajax/MediaTest.php +++ b/app/code/Magento/Swatches/Test/Unit/Controller/Ajax/MediaTest.php @@ -20,6 +20,9 @@ class MediaTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Catalog\Model\ProductFactory|\PHPUnit_Framework_MockObject_MockObject */ private $productModelFactoryMock; + /** @var \Magento\PageCache\Model\Config|\PHPUnit_Framework_MockObject_MockObject */ + private $config; + /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject */ private $productMock; @@ -29,6 +32,9 @@ class MediaTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject */ private $requestMock; + /** @var \Magento\Framework\App\ResponseInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $responseMock; + /** @var \Magento\Framework\Controller\ResultFactory|\PHPUnit_Framework_MockObject_MockObject */ private $resultFactory; @@ -57,11 +63,20 @@ protected function setUp() \Magento\Catalog\Model\ProductFactory::class, ['create'] ); + $this->config = $this->createMock(\Magento\PageCache\Model\Config::class); + $this->config->method('getTtl')->willReturn(1); + $this->productMock = $this->createMock(\Magento\Catalog\Model\Product::class); $this->contextMock = $this->createMock(\Magento\Framework\App\Action\Context::class); $this->requestMock = $this->createMock(\Magento\Framework\App\RequestInterface::class); $this->contextMock->method('getRequest')->willReturn($this->requestMock); + $this->responseMock = $this->getMockBuilder(\Magento\Framework\App\ResponseInterface::class) + ->disableOriginalConstructor() + ->setMethods(['setPublicHeaders']) + ->getMockForAbstractClass(); + $this->responseMock->method('setPublicHeaders')->willReturnSelf(); + $this->contextMock->method('getResponse')->willReturn($this->responseMock); $this->resultFactory = $this->createPartialMock(\Magento\Framework\Controller\ResultFactory::class, ['create']); $this->contextMock->method('getResultFactory')->willReturn($this->resultFactory); @@ -73,7 +88,8 @@ protected function setUp() [ 'context' => $this->contextMock, 'swatchHelper' => $this->swatchHelperMock, - 'productModelFactory' => $this->productModelFactoryMock + 'productModelFactory' => $this->productModelFactoryMock, + 'config' => $this->config ] ); } @@ -86,6 +102,10 @@ public function testExecute() ->method('load') ->with(59) ->willReturn($this->productMock); + $this->productMock + ->expects($this->once()) + ->method('getIdentities') + ->willReturn(['tags']); $this->productModelFactoryMock ->expects($this->once()) diff --git a/app/code/Magento/Swatches/composer.json b/app/code/Magento/Swatches/composer.json index adce50b564a1d..f46f6720d30d8 100644 --- a/app/code/Magento/Swatches/composer.json +++ b/app/code/Magento/Swatches/composer.json @@ -12,6 +12,7 @@ "magento/module-media-storage": "100.2.*", "magento/module-config": "101.0.*", "magento/module-theme": "100.2.*", + "magento/module-page-cache": "100.2.*", "magento/framework": "101.0.*" }, "suggest": { From 098b45fa17b6e254aab1a1974e937887b24efe99 Mon Sep 17 00:00:00 2001 From: Oleksandr Miroshnichenko <omiroshnichenko@magento.com> Date: Wed, 6 Feb 2019 14:32:22 -0600 Subject: [PATCH 0537/1295] MAGETWO-98131: Rename LESS var names --- .../web/css/source/module/order/_order-comments.less | 2 +- .../web/css/source/module/_staging-preview.less | 2 +- .../setup/styles/less/components/tooltips/_tooltips.less | 2 +- .../backend/web/app/setup/styles/less/lib/_variables.less | 2 +- .../app/updater/styles/less/pages/_extension-manager.less | 2 +- .../adminhtml/Magento/backend/web/css/source/_tabs.less | 4 ++-- .../Magento/backend/web/css/source/_typography.less | 6 +++--- .../backend/web/css/source/components/_file-uploader.less | 2 +- .../Magento/backend/web/css/source/forms/_temp.less | 6 +++--- .../Magento/backend/web/css/source/variables/_colors.less | 6 +++--- .../backend/web/css/source/variables/_data-grid.less | 2 +- setup/view/styles/lib/variables/_colors.less | 2 +- 12 files changed, 19 insertions(+), 19 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_order-comments.less b/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_order-comments.less index 2f6aec0315e3b..5bcf4d4953cc6 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_order-comments.less +++ b/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_order-comments.less @@ -49,7 +49,7 @@ margin: 0 0 @order-create-sidebar__margin; .lib-typography( @_font-size: 1.9rem, - @_color: @color-brown-darkie, + @_color: @color-brown-darker, @_font-weight: @font-weight__semibold, @_line-height: @line-height__s, @_font-family: false, diff --git a/app/design/adminhtml/Magento/backend/Magento_Staging/web/css/source/module/_staging-preview.less b/app/design/adminhtml/Magento/backend/Magento_Staging/web/css/source/module/_staging-preview.less index a71731320c5ce..e69e459e2bedf 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Staging/web/css/source/module/_staging-preview.less +++ b/app/design/adminhtml/Magento/backend/Magento_Staging/web/css/source/module/_staging-preview.less @@ -16,7 +16,7 @@ @staging-preview-header__font-size: 1.3rem; @staging-preview-header-item__active__background-color: @color-brownie-almost; -@staging-preview-header-item-actions__border-color: @color-darkie-gray; +@staging-preview-header-item-actions__border-color: @color-darker-gray; @staging-preview-form-element__background-color: @color-very-dark-brownie; @staging-preview-form-element__border-color: @color-lighter-grayish-almost; diff --git a/app/design/adminhtml/Magento/backend/web/app/setup/styles/less/components/tooltips/_tooltips.less b/app/design/adminhtml/Magento/backend/web/app/setup/styles/less/components/tooltips/_tooltips.less index c535047e37682..c9ad0c6c60b66 100644 --- a/app/design/adminhtml/Magento/backend/web/app/setup/styles/less/components/tooltips/_tooltips.less +++ b/app/design/adminhtml/Magento/backend/web/app/setup/styles/less/components/tooltips/_tooltips.less @@ -10,7 +10,7 @@ @tooltip__background-color: @color-white; @tooltip__border-color: @color-gray68; @tooltip__border-radius: 0; -@tooltip__color: @color-brown-darkie; +@tooltip__color: @color-brown-darker; @tooltip__max-width: 31rem; @tooltip__opacity: .9; @tooltip__shadow-color: @color-gray80; diff --git a/app/design/adminhtml/Magento/backend/web/app/setup/styles/less/lib/_variables.less b/app/design/adminhtml/Magento/backend/web/app/setup/styles/less/lib/_variables.less index 39d7be029f81f..be1378638180f 100644 --- a/app/design/adminhtml/Magento/backend/web/app/setup/styles/less/lib/_variables.less +++ b/app/design/adminhtml/Magento/backend/web/app/setup/styles/less/lib/_variables.less @@ -28,7 +28,7 @@ @color-green-apple: #79a22e; @color-green-islamic: #090; @color-dark-brownie: #41362f; -@color-brown-darkie: #41362f; +@color-brown-darker: #41362f; @color-phoenix-down: #e04f00; @color-phoenix: #eb5202; @color-phoenix-almost-rise: #ef672f; diff --git a/app/design/adminhtml/Magento/backend/web/app/updater/styles/less/pages/_extension-manager.less b/app/design/adminhtml/Magento/backend/web/app/updater/styles/less/pages/_extension-manager.less index 911ef55f3f2e6..30500569c82a0 100644 --- a/app/design/adminhtml/Magento/backend/web/app/updater/styles/less/pages/_extension-manager.less +++ b/app/design/adminhtml/Magento/backend/web/app/updater/styles/less/pages/_extension-manager.less @@ -15,7 +15,7 @@ @extension-manager-title__background-color: @color-white-fog; @extension-manager-title__border-color: @color-gray89; -@extension-manager-title__color: @color-brown-darkie; +@extension-manager-title__color: @color-brown-darker; @extension-manager-button__border-color: @color-gray68; diff --git a/app/design/adminhtml/Magento/backend/web/css/source/_tabs.less b/app/design/adminhtml/Magento/backend/web/css/source/_tabs.less index 475d3914a5ff0..5658214a76986 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/_tabs.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/_tabs.less @@ -45,13 +45,13 @@ } .ui-tabs-anchor { - color: @color-brown-darkie; + color: @color-brown-darker; display: block; padding: 1.5rem 1.8rem 1.3rem; text-decoration: none; &:hover { // ToDo UI: should be deleted with old styles - color: @color-brown-darkie; + color: @color-brown-darker; text-decoration: none; } } diff --git a/app/design/adminhtml/Magento/backend/web/css/source/_typography.less b/app/design/adminhtml/Magento/backend/web/css/source/_typography.less index 54726d2d34bd9..1f7d7f879c4aa 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/_typography.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/_typography.less @@ -71,7 +71,7 @@ h1 { .lib-typography( @_font-size: 2.8rem, - @_color: @color-brown-darkie, + @_color: @color-brown-darker, @_font-weight: @font-weight__regular, @_line-height: @line-height__s, @_font-family: false, @@ -84,7 +84,7 @@ h2 { .lib-typography( @_font-size: 2rem, - @_color: @color-brown-darkie, + @_color: @color-brown-darker, @_font-weight: @font-weight__regular, @_line-height: @line-height__s, @_font-family: false, @@ -97,7 +97,7 @@ h3 { .lib-typography( @_font-size: 1.7rem, - @_color: @color-brown-darkie, + @_color: @color-brown-darker, @_font-weight: @font-weight__semibold, @_line-height: @line-height__s, @_font-family: false, diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_file-uploader.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_file-uploader.less index 141e5b604e2e2..7e086db5b7d26 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_file-uploader.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_file-uploader.less @@ -48,7 +48,7 @@ @data-grid-file-uploader-menu-button__width: 2rem; -@data-grid-file-uploader-upload-icon__color: @color-darkie-gray; +@data-grid-file-uploader-upload-icon__color: @color-darker-gray; @data-grid-file-uploader-upload-icon__hover__color: @color-very-dark-gray; @data-grid-file-uploader-upload-icon__line-height: 48px; diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less index 0bfa454adbf0d..3a443bd31c148 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less @@ -162,7 +162,7 @@ @_icon-font-line-height: 16px, @_icon-font-text-hide: true, @_icon-font-position: after, - @_icon-font-color: @color-brown-darkie + @_icon-font-color: @color-brown-darker ); span { @@ -175,7 +175,7 @@ z-index: 2; &:after { - color: darken(@color-brown-darkie, 20%); + color: darken(@color-brown-darker, 20%); } // @Todo ui - testing solution to show action hint without title attribute @@ -547,7 +547,7 @@ label.mage-error { } .admin__control-select-placeholder { - color: @color-darkie-gray; + color: @color-darker-gray; font-weight: @font-weight__bold; } } diff --git a/app/design/adminhtml/Magento/backend/web/css/source/variables/_colors.less b/app/design/adminhtml/Magento/backend/web/css/source/variables/_colors.less index b477384096b01..ad57d7b47113e 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/variables/_colors.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/variables/_colors.less @@ -8,7 +8,7 @@ // _____________________________________________ @color-brown-dark: #4a3f39; -@color-brown-darkie: #41362f; +@color-brown-darker: #41362f; @color-very-dark-gray-black: #303030; @color-very-dark-gray-black2: #35302c; @color-very-dark-grayish-orange: #373330; @@ -23,7 +23,7 @@ @color-brownie-vanilla: #736963; @color-dark-gray0: #7f7c7a; @color-dark-gray: #808080; -@color-darkie-gray: #8a837f; +@color-darker-gray: #8a837f; @color-gray65: #a6a6a6; @color-gray65-almost: #a79d95; @color-gray65-lighten: #aaa6a0; @@ -73,5 +73,5 @@ @primary__color: @color-phoenix; @success__color: @color-green-apple; -@text__color: @color-brown-darkie; +@text__color: @color-brown-darker; @border__color: @color-gray89; diff --git a/app/design/adminhtml/Magento/backend/web/css/source/variables/_data-grid.less b/app/design/adminhtml/Magento/backend/web/css/source/variables/_data-grid.less index 69393a62200cc..40831684adceb 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/variables/_data-grid.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/variables/_data-grid.less @@ -30,7 +30,7 @@ @data-grid-td__odd__update__active__background-color: darken(@data-grid-td__update__active__background-color, 10%); @data-grid-td__odd__update__upcoming__background-color: darken(@data-grid-td__update__upcoming__background-color, 10%); -@data-grid-th__border-color: @color-darkie-gray; +@data-grid-th__border-color: @color-darker-gray; @data-grid-th__border-style: solid; @data-grid-th__background-color: @color-brownie; @data-grid-th__color: @color-white; diff --git a/setup/view/styles/lib/variables/_colors.less b/setup/view/styles/lib/variables/_colors.less index 638490ac8673a..a72dc69ac7669 100644 --- a/setup/view/styles/lib/variables/_colors.less +++ b/setup/view/styles/lib/variables/_colors.less @@ -24,7 +24,7 @@ @color-green-apple: #79a22e; @color-green-islamic: #090; @color-dark-brownie: #41362f; -@color-brown-darkie: #41362f; +@color-brown-darker: #41362f; @color-phoenix-down: #e04f00; @color-phoenix: #eb5202; @color-phoenix-almost-rise: #ef672f; From d28377b52347d4e2a0dd86b45144cd0f69581f65 Mon Sep 17 00:00:00 2001 From: duhon <duhon@rambler.ru> Date: Wed, 6 Feb 2019 16:34:06 -0600 Subject: [PATCH 0538/1295] MAGETWO-97094: [BackPort][MC-5663] Enable caching of AJAX requests on Varnish/Fastly --- app/code/Magento/Swatches/Controller/Ajax/Media.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Swatches/Controller/Ajax/Media.php b/app/code/Magento/Swatches/Controller/Ajax/Media.php index 2d20b9a9a4b7e..2aaf158064947 100644 --- a/app/code/Magento/Swatches/Controller/Ajax/Media.php +++ b/app/code/Magento/Swatches/Controller/Ajax/Media.php @@ -12,7 +12,7 @@ /** * Class Media */ -class Media extends \Magento\Framework\App\Action\Action implements \Magento\Framework\App\Action\HttpGetActionInterface +class Media extends \Magento\Framework\App\Action\Action { /** * @var \Magento\Catalog\Model\Product Factory From 120d415bbcc59fef974aca226a100eac632b0b6b Mon Sep 17 00:00:00 2001 From: Oleksandr Miroshnichenko <omiroshnichenko@magento.com> Date: Wed, 6 Feb 2019 16:47:17 -0600 Subject: [PATCH 0539/1295] MAGETWO-98131: Rename LESS var names - fix tests --- .../web/css/source/module/_staging-preview.less | 4 ++-- .../adminhtml/Magento/backend/web/css/source/forms/_temp.less | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Staging/web/css/source/module/_staging-preview.less b/app/design/adminhtml/Magento/backend/Magento_Staging/web/css/source/module/_staging-preview.less index e69e459e2bedf..a1b22b0e97120 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Staging/web/css/source/module/_staging-preview.less +++ b/app/design/adminhtml/Magento/backend/Magento_Staging/web/css/source/module/_staging-preview.less @@ -1,5 +1,5 @@ // /** -// * Copyright © 2015 Magento. All rights reserved. +// * Copyright © Magento, Inc. All rights reserved. // * See COPYING.txt for license details. // */ @@ -366,7 +366,7 @@ // Generic data grid .admin__data-grid-outer-wrap { border-top: 1px solid @staging-preview-table-dark__border-color; - max-height: 400px; // ToDO: remove after JS adjustment implemented + max-height: 400px; // ToDO remove after JS adjustment implemented overflow-y: auto; padding: 15px @indent__s 0 0; } diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less index 3a443bd31c148..b3dd7daff6573 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less @@ -408,6 +408,9 @@ label.mage-error { width: 16px; z-index: 1; + /** + *@codingStandardsIgnoreStart + */ &:before { &:extend(.admin__control-checkbox + label:before); left: 0; @@ -434,6 +437,7 @@ label.mage-error { &:before { &:extend(.admin__control-checkbox:checked + label:before); } + //@codingStandardsIgnoreEnd } &._indeterminate { From 9f2c4fe98c3dcf4c1a366075fff1f17e9668a4ef Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Thu, 7 Feb 2019 09:52:34 +0200 Subject: [PATCH 0540/1295] MAGETWO-95626: Log of actions in the Admin panel --- .../Test/Mftf/ActionGroup/AdminIndexerActionGroup.xml | 7 +++---- .../Test/Mftf/Section/AdminIndexManagementSection.xml | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Indexer/Test/Mftf/ActionGroup/AdminIndexerActionGroup.xml b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/AdminIndexerActionGroup.xml index 7c9c8c58d44b6..38852e9d833e6 100644 --- a/app/code/Magento/Indexer/Test/Mftf/ActionGroup/AdminIndexerActionGroup.xml +++ b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/AdminIndexerActionGroup.xml @@ -10,14 +10,13 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="UpdateIndexerMode"> <arguments> - <argument name="indexerName" type="string" defaultValue="catalogsearch_fulltext"/> + <argument name="indexerId" type="string"/> <argument name="indexerMode" type="string" defaultValue="Update on Save"/> </arguments> <amOnPage url="{{AdminIndexManagementPage.url}}" stepKey="amOnIndexManagementPage"/> - <click selector="{{AdminIndexManagementSection.indexerCheckbox(indexerName)}}" stepKey="selectIndexer"/> + <checkOption selector="{{AdminIndexManagementSection.indexerCheckbox(indexerId)}}" stepKey="selectIndexer"/> <selectOption selector="{{AdminIndexManagementSection.massActionSelect}}" userInput="{{indexerMode}}" stepKey="selectIndexerMode"/> - <click selector="{{AdminIndexManagementSection.massActionSubmit}}" stepKey="submitIndexerForm"/> - <waitForPageLoad stepKey="waitForIndexerUpdate"/> + <click selector="{{AdminIndexManagementSection.massActionSubmit}}" timeout="30" stepKey="submitIndexerForm"/> <see selector="{{AdminMessagesSection.success}}" userInput='1 indexer(s) are in "{{indexerMode}}" mode.' stepKey="seeSuccessMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Indexer/Test/Mftf/Section/AdminIndexManagementSection.xml b/app/code/Magento/Indexer/Test/Mftf/Section/AdminIndexManagementSection.xml index f72bffd9cf96d..0e954f12c4df7 100644 --- a/app/code/Magento/Indexer/Test/Mftf/Section/AdminIndexManagementSection.xml +++ b/app/code/Magento/Indexer/Test/Mftf/Section/AdminIndexManagementSection.xml @@ -9,7 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminIndexManagementSection"> - <element name="indexerCheckbox" type="checkbox" selector="input[value='{{indexerName}}']" parameterized="true"/> + <element name="indexerCheckbox" type="checkbox" selector="#gridIndexer_table input[value='{{indexerId}}']" parameterized="true"/> <element name="massActionSelect" type="select" selector="#gridIndexer_massaction-select"/> <element name="massActionSubmit" type="button" selector="#gridIndexer_massaction-form button"/> </section> From de2e02f0ac3be68bcb63e7c0b1f46b20ee97dd8e Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 7 Feb 2019 11:01:53 +0200 Subject: [PATCH 0541/1295] MAGETWO-95734: MFTF tests fix --- .../Backend/Test/Mftf/Section/AdminMainActionsSection.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminMainActionsSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminMainActionsSection.xml index 4b72aaa69fa86..3a0737bcae4a1 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminMainActionsSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminMainActionsSection.xml @@ -13,6 +13,6 @@ <element name="delete" type="button" selector="#delete" timeout="30"/> <element name="add" type="button" selector="#add" timeout="30"/> <element name="back" type="button" selector="#back" timeout="30"/> - <element name="saveAndContinue" type="button" selector="button[id*=save_and_continue]" timeout="30"/> + <element name="saveAndContinue" type="button" selector="button[id*=save_and_]" timeout="30"/> </section> </sections> From 004de84450feaa72f125145c7c46cc730008fa80 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Thu, 7 Feb 2019 11:35:26 +0200 Subject: [PATCH 0542/1295] MAGETWO-95463: Order Sales Report includes canceled orders --- .../Block/Adminhtml/Sales/Sales/Grid.php | 68 ++++++++++++- .../GenerateOrderReportActionGroup.xml | 22 +++++ .../Test/Mftf/Page/OrdersReportPage.xml | 14 +++ .../Section/AdminOrderReportFilterSection.xml | 16 ++++ .../AdminOrderReportMainActionsSection.xml | 15 +++ .../Section/AdminOrderReportTableSection.xml | 15 +++ .../CancelOrdersInOrderSalesReportTest.xml | 95 +++++++++++++++++++ 7 files changed, 242 insertions(+), 3 deletions(-) create mode 100644 app/code/Magento/Reports/Test/Mftf/ActionGroup/GenerateOrderReportActionGroup.xml create mode 100644 app/code/Magento/Reports/Test/Mftf/Page/OrdersReportPage.xml create mode 100644 app/code/Magento/Reports/Test/Mftf/Section/AdminOrderReportFilterSection.xml create mode 100644 app/code/Magento/Reports/Test/Mftf/Section/AdminOrderReportMainActionsSection.xml create mode 100644 app/code/Magento/Reports/Test/Mftf/Section/AdminOrderReportTableSection.xml create mode 100644 app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml 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 1f90309721c23..9c80f6aa423b8 100644 --- a/app/code/Magento/Reports/Block/Adminhtml/Sales/Sales/Grid.php +++ b/app/code/Magento/Reports/Block/Adminhtml/Sales/Sales/Grid.php @@ -6,23 +6,58 @@ namespace Magento\Reports\Block\Adminhtml\Sales\Sales; +use Magento\Framework\DataObject; use Magento\Reports\Block\Adminhtml\Grid\Column\Renderer\Currency; +use Magento\Framework\App\ObjectManager; +use Magento\Sales\Model\Order\ConfigFactory; +use Magento\Sales\Model\Order; /** * Adminhtml sales report grid block * - * @author Magento Core Team <core@magentocommerce.com> * @SuppressWarnings(PHPMD.DepthOfInheritance) */ class Grid extends \Magento\Reports\Block\Adminhtml\Grid\AbstractGrid { /** - * GROUP BY criteria - * * @var string */ protected $_columnGroupBy = 'period'; + /** + * @var ConfigFactory + */ + private $configFactory; + + /** + * @param \Magento\Backend\Block\Template\Context $context + * @param \Magento\Backend\Helper\Data $backendHelper + * @param \Magento\Reports\Model\ResourceModel\Report\Collection\Factory $resourceFactory + * @param \Magento\Reports\Model\Grouped\CollectionFactory $collectionFactory + * @param \Magento\Reports\Helper\Data $reportsData + * @param array $data + * @param ConfigFactory|null $configFactory + */ + public function __construct( + \Magento\Backend\Block\Template\Context $context, + \Magento\Backend\Helper\Data $backendHelper, + \Magento\Reports\Model\ResourceModel\Report\Collection\Factory $resourceFactory, + \Magento\Reports\Model\Grouped\CollectionFactory $collectionFactory, + \Magento\Reports\Helper\Data $reportsData, + array $data = [], + ConfigFactory $configFactory = null + ) { + parent::__construct( + $context, + $backendHelper, + $resourceFactory, + $collectionFactory, + $reportsData, + $data + ); + $this->configFactory = $configFactory ?: ObjectManager::getInstance()->get(ConfigFactory::class); + } + /** * {@inheritdoc} * @codeCoverageIgnore @@ -328,4 +363,31 @@ protected function _prepareColumns() return parent::_prepareColumns(); } + + /** + * @inheritdoc + * + * Filter canceled statuses for orders. + * + * @return Grid + */ + protected function _prepareCollection() + { + /** @var DataObject $filterData */ + $filterData = $this->getData('filter_data'); + if (!$filterData->hasData('order_statuses')) { + $orderConfig = $this->configFactory->create(); + $statusValues = []; + $canceledStatuses = $orderConfig->getStateStatuses(Order::STATE_CANCELED); + $statusCodes = array_keys($orderConfig->getStatuses()); + foreach ($statusCodes as $code) { + if (!isset($canceledStatuses[$code])) { + $statusValues[] = $code; + } + } + $filterData->setData('order_statuses', $statusValues); + } + + return parent::_prepareCollection(); + } } diff --git a/app/code/Magento/Reports/Test/Mftf/ActionGroup/GenerateOrderReportActionGroup.xml b/app/code/Magento/Reports/Test/Mftf/ActionGroup/GenerateOrderReportActionGroup.xml new file mode 100644 index 0000000000000..79b9c21b0db6a --- /dev/null +++ b/app/code/Magento/Reports/Test/Mftf/ActionGroup/GenerateOrderReportActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="GenerateOrderReportActionGroup"> + <arguments> + <argument name="orderFromDate" type="string"/> + <argument name="orderToDate" type="string"/> + </arguments> + <click selector="{{AdminOrderReportMainActionsSection.here}}" stepKey="refreshStatistics" /> + <fillField selector="{{AdminOrderReportFilterSection.dateFrom}}" userInput="{{orderFromDate}}" stepKey="fillFromDate"/> + <fillField selector="{{AdminOrderReportFilterSection.dateTo}}" userInput="{{orderToDate}}" stepKey="fillToDate"/> + <selectOption selector="{{AdminOrderReportFilterSection.orderStatus}}" userInput="Any" stepKey="selectAnyOption" /> + <click selector="{{AdminOrderReportMainActionsSection.showReport}}" stepKey="showReport" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Reports/Test/Mftf/Page/OrdersReportPage.xml b/app/code/Magento/Reports/Test/Mftf/Page/OrdersReportPage.xml new file mode 100644 index 0000000000000..46509089b97ba --- /dev/null +++ b/app/code/Magento/Reports/Test/Mftf/Page/OrdersReportPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="OrdersReportPage" url="reports/report_sales/sales/" area="admin" module="Reports"> + <section name="OrderReportFilterSection"/> + <section name="OrderReportMainSection"/> + <section name="GeneratedReportSection" /> + </page> +</pages> diff --git a/app/code/Magento/Reports/Test/Mftf/Section/AdminOrderReportFilterSection.xml b/app/code/Magento/Reports/Test/Mftf/Section/AdminOrderReportFilterSection.xml new file mode 100644 index 0000000000000..33527e1262020 --- /dev/null +++ b/app/code/Magento/Reports/Test/Mftf/Section/AdminOrderReportFilterSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminOrderReportFilterSection"> + <element name="dateFrom" type="input" selector="#sales_report_from"/> + <element name="dateTo" type="input" selector="#sales_report_to"/> + <element name="orderStatus" type="select" selector="#sales_report_show_order_statuses"/> + </section> +</sections> diff --git a/app/code/Magento/Reports/Test/Mftf/Section/AdminOrderReportMainActionsSection.xml b/app/code/Magento/Reports/Test/Mftf/Section/AdminOrderReportMainActionsSection.xml new file mode 100644 index 0000000000000..e9a56f538fd9c --- /dev/null +++ b/app/code/Magento/Reports/Test/Mftf/Section/AdminOrderReportMainActionsSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminOrderReportMainActionsSection"> + <element name="showReport" type="button" selector="#filter_form_submit"/> + <element name="here" type="text" selector="//a[contains(text(), 'here')]"/> + </section> +</sections> diff --git a/app/code/Magento/Reports/Test/Mftf/Section/AdminOrderReportTableSection.xml b/app/code/Magento/Reports/Test/Mftf/Section/AdminOrderReportTableSection.xml new file mode 100644 index 0000000000000..e920d28bcf386 --- /dev/null +++ b/app/code/Magento/Reports/Test/Mftf/Section/AdminOrderReportTableSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminOrderReportTableSection"> + <element name="ordersCount" type="text" selector=".totals .col-orders.col-orders_count.col-number"/> + <element name="canceledOrders" type="text" selector=".totals .col-canceled.col-total_canceled_amount.a-right"/> + </section> +</sections> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml new file mode 100644 index 0000000000000..c229fb1cf0271 --- /dev/null +++ b/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="CancelOrdersInOrderSalesReportTest"> + <annotations> + <features value="Reports"/> + <stories value="Order Sales Report"/> + <group value="reports"/> + <title value="Canceled orders in order sales report"/> + <description value="Verify canceling of orders in order sales report"/> + <severity value="MAJOR"/> + <testCaseId value="MC-13838"/> + <useCaseId value="MAGETWO-95463"/> + </annotations> + <before> + <!-- create new product --> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!-- create new customer--> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <!--login to Admin--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Create new order--> + <comment userInput="Admin creates order" stepKey="adminCreateOrderComment"/> + <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="startToCreateNewOrder"> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToOrderWithUserDefinedQty"> + <argument name="product" value="$$createSimpleProduct$$"/> + <argument name="quantity" value="1"/> + </actionGroup> + <!-- Select shipping --> + <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRateShipping"/> + <!--Select payment--> + <actionGroup ref="SelectCheckMoneyPaymentMethod" stepKey="selectCheckMoneyPayment"/> + <!--Submit Order--> + <click selector="{{AdminOrderFormActionSection.submitOrder}}" stepKey="clickSubmitOrder"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You created the order." stepKey="seeSuccessMessage"/> + <!--Create order invoice--> + <comment userInput="Admin creates invoice for order" stepKey="adminCreateInvoiceComment" /> + <actionGroup ref="StartCreateInvoiceFromOrderPage" stepKey="createInvoice"/> + <actionGroup ref="SubmitInvoice" stepKey="submitInvoice"/> <!--Invoice created successfully--> + <!--Ship Order--> + <comment userInput="Admin creates shipment" stepKey="adminCreatesShipmentComment"/> + <actionGroup ref="StartCreateShipmentFromOrderPage" stepKey="createShipment"/> + <actionGroup ref="SubmitShipment" stepKey="submitShipment"/> + + <!--Create new order--> + <comment userInput="Admin creates order" stepKey="adminCreateOrderComment1"/> + <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="startToCreateNewOrder1"> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToOrderWithUserDefinedQty1"> + <argument name="product" value="$$createSimpleProduct$$"/> + <argument name="quantity" value="1"/> + </actionGroup> + <!-- Select shipping --> + <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRateShipping1"/> + <!--Select payment--> + <actionGroup ref="SelectCheckMoneyPaymentMethod" stepKey="selectCheckMoneyPayment1"/> + <!--Submit Order--> + <click selector="{{AdminOrderFormActionSection.submitOrder}}" stepKey="clickSubmitOrder1"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You created the order." stepKey="seeSuccessMessage1"/> + <!-- Cancel order --> + <actionGroup ref="cancelPendingOrder" stepKey="cancelOrder"/> + + <!-- Generate Order report --> + <amOnPage url="{{OrdersReportPage.url}}" stepKey="goToOrdersReportPage1"/> + <!-- Get date --> + <generateDate date="+0 day" format="m/d/Y" stepKey="generateEndDate"/> + <generateDate date="-1 day" format="m/d/Y" stepKey="generateStartDate"/> + <actionGroup ref="GenerateOrderReportActionGroup" stepKey="generateReportAfterCancelOrder"> + <argument name="orderFromDate" value="$generateStartDate"/> + <argument name="orderToDate" value="$generateEndDate"/> + </actionGroup> + <waitForElement selector="{{AdminOrderReportTableSection.ordersCount}}" stepKey="waitForOrdersCount"/> + <see selector="{{AdminOrderReportTableSection.canceledOrders}}" userInput="$0.00" stepKey="seeCanceledOrderPrice"/> + </test> +</tests> From 34db704431f84c495570d69cde5866caaeabefc7 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 7 Feb 2019 12:42:42 +0200 Subject: [PATCH 0543/1295] MAGETWO-95452: Special price does not work when "default config" scope timezone does not match "website" scope timezone --- .../AdminSwitchWebsiteActionGroup.xml | 22 ------------------- 1 file changed, 22 deletions(-) delete mode 100644 app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchWebsiteActionGroup.xml diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchWebsiteActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchWebsiteActionGroup.xml deleted file mode 100644 index 06622d3a53177..0000000000000 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchWebsiteActionGroup.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminSwitchWebsiteActionGroup"> - <arguments> - <argument name="website"/> - </arguments> - <scrollToTopOfPage stepKey="scrollToTopOfPage" /> - <click selector="{{AdminMainActionsSection.storeViewDropdown}}" stepKey="clickWebsiteSwitchDropdown"/> - <waitForElementVisible selector="{{AdminMainActionsSection.websiteByName('Main Website')}}" stepKey="waitForWebsiteAreVisible"/> - <click selector="{{AdminMainActionsSection.websiteByName(website.name)}}" stepKey="clickWebsiteByName"/> - <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitingForInformationModal"/> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmStoreSwitch"/> - <see userInput="{{website.name}}" selector="{{AdminMainActionsSection.storeSwitcher}}" stepKey="seeNewWebsiteName"/> - </actionGroup> -</actionGroups> From 3803660262f766bc71e54e593b94584d91418bd0 Mon Sep 17 00:00:00 2001 From: Cristiano Casciotti <teknoman84@gmail.com> Date: Thu, 7 Feb 2019 11:37:13 +0100 Subject: [PATCH 0544/1295] Added RewriteBase directive template in .htaccess file into pub/media folder --- pub/media/.htaccess | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pub/media/.htaccess b/pub/media/.htaccess index 28e65b490fbb8..d8793a891430a 100644 --- a/pub/media/.htaccess +++ b/pub/media/.htaccess @@ -23,6 +23,9 @@ SetHandler default-handler Options +FollowSymLinks RewriteEngine on + ## you can put here your pub/media folder path relative to web root + #RewriteBase /magento/pub/media/ + ############################################ ## never rewrite for existing files RewriteCond %{REQUEST_FILENAME} !-f From 8ab274c64b59b8d1e2dbb8a7dec57aa7a4b172be Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 7 Feb 2019 14:08:02 +0200 Subject: [PATCH 0545/1295] MAGETWO-95452: Special price does not work when "default config" scope timezone does not match "website" scope timezone --- ...ceForDifferentTimezonesForWebsitesTest.xml | 4 ++-- .../AdminSwitchStoreViewActionGroup.xml | 20 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml index c3dc8a0676278..b9308edbb387f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml @@ -32,7 +32,7 @@ <click selector="{{AdminMainActionsSection.save}}" stepKey="saveConfig"/> <!--Set timezone for Main Website--> <actionGroup ref="AdminSwitchWebsiteActionGroup" stepKey="adminSwitchStoreViewActionGroup"> - <argument name="storeView" value="_defaultWebsite.name"/> + <argument name="scopeName" value="_defaultWebsite.name"/> </actionGroup> <uncheckOption selector="{{LocaleOptionsSection.useDefault}}" stepKey="uncheckUseDefault"/> <selectOption selector="{{LocaleOptionsSection.timezone}}" userInput="Greenwich Mean Time (Africa/Abidjan)" stepKey="setTimezone1"/> @@ -49,7 +49,7 @@ <click selector="{{AdminMainActionsSection.save}}" stepKey="saveConfigReset"/> <actionGroup ref="AdminSwitchWebsiteActionGroup" stepKey="AdminSwitchStoreViewActionGroup"> - <argument name="storeView" value="_defaultWebsite.name"/> + <argument name="scopeName" value="_defaultWebsite.name"/> </actionGroup> <checkOption selector="{{LocaleOptionsSection.useDefault}}" stepKey="checkUseDefault"/> <click selector="{{AdminMainActionsSection.save}}" stepKey="saveConfigReset1"/> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml index b96a42a66d706..d540a0000655f 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml @@ -9,23 +9,23 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminSwitchBaseActionGroup"> <arguments> - <argument name="storeView" defaultValue="customStore.name"/> + <argument name="scopeName" defaultValue="customStore.name"/> </arguments> - <click selector="{{AdminMainActionsSection.storeViewDropdown}}" stepKey="clickStoreViewSwitchDropdown"/> + <scrollToTopOfPage stepKey="scrollToTop"/> + <click selector="{{AdminMainActionsSection.storeViewDropdown}}" stepKey="clickScopeSwitchDropdown"/> <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitingForInformationModal"/> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmStoreSwitch"/> - <waitForPageLoad stepKey="waitForStoreViewSwitched"/> - <see userInput="{{storeView}}" selector="{{AdminMainActionsSection.storeSwitcher}}" stepKey="seeNewStoreViewName"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmScopeSwitch"/> + <waitForPageLoad stepKey="waitForScopeSwitched"/> + <see userInput="{{scopeName}}" selector="{{AdminMainActionsSection.storeSwitcher}}" stepKey="seeNewScopeName"/> </actionGroup> <actionGroup name="AdminSwitchStoreViewActionGroup" extends="AdminSwitchBaseActionGroup"> - <waitForElementVisible selector="{{AdminMainActionsSection.storeViewByName(storeView)}}" after="clickStoreViewSwitchDropdown" stepKey="waitForStoreViewsAreVisible"/> - <click selector="{{AdminMainActionsSection.storeViewByName(storeView)}}" after="waitForStoreViewsAreVisible" stepKey="clickStoreViewByName"/> + <waitForElementVisible selector="{{AdminMainActionsSection.storeViewByName(scopeName)}}" after="clickScopeSwitchDropdown" stepKey="waitForStoreViewNameIsVisible"/> + <click selector="{{AdminMainActionsSection.storeViewByName(scopeName)}}" after="waitForStoreViewNameIsVisible" stepKey="clickStoreViewByName"/> </actionGroup> <actionGroup name="AdminSwitchWebsiteActionGroup" extends="AdminSwitchBaseActionGroup"> - <waitForElementVisible selector="{{AdminMainActionsSection.websiteByName(storeView)}}" after="clickStoreViewSwitchDropdown" stepKey="waitForStoreViewsAreVisible"/> - <click selector="{{AdminMainActionsSection.websiteByName(storeView)}}" after="waitForStoreViewsAreVisible" stepKey="clickStoreViewByName"/> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmStoreSwitch"/> + <waitForElementVisible selector="{{AdminMainActionsSection.websiteByName(scopeName)}}" after="clickScopeSwitchDropdown" stepKey="waitForWebsiteNameIsVisible"/> + <click selector="{{AdminMainActionsSection.websiteByName(scopeName)}}" after="waitForWebsiteNameIsVisible" stepKey="clickStoreViewByName"/> </actionGroup> </actionGroups> From 402d83d2225aba6fe2c719d853d550a53e4848ca Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Thu, 7 Feb 2019 14:13:50 +0200 Subject: [PATCH 0546/1295] MAGETWO-73432: Change EAV model --- .../AdminAddCustomerAddressActionGroup.xml | 26 +++++++++++++++++++ .../Test/Mftf/Page/AdminEditCustomerPage.xml | 1 + .../AdminCustomerAccountNewAddressSection.xml | 19 ++++++++++++++ app/code/Magento/Eav/Model/Entity/Type.php | 7 ++--- 4 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAddCustomerAddressActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountNewAddressSection.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAddCustomerAddressActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAddCustomerAddressActionGroup.xml new file mode 100644 index 0000000000000..f042c272cbfce --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAddCustomerAddressActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAddCustomerAddressActionGroup" > + <arguments> + <argument name="customerAddress" defaultValue="CustomerAddressSimple"/> + </arguments> + <click selector="{{AdminCustomerAccountAddressSection.addresses}}" stepKey="proceedToAddresses"/> + <click selector="{{AdminCustomerAccountAddressSection.addNewAddress}}" stepKey="addNewAddresses"/> + <waitForPageLoad time="60" stepKey="waitAddressFieldsLoaded" /> + <fillField userInput="{{customerAddress.street[0]}}" selector="{{AdminCustomerAccountNewAddressSection.street}}" stepKey="fillStreetAddress"/> + <fillField userInput="{{customerAddress.city}}" selector="{{AdminCustomerAccountNewAddressSection.city}}" stepKey="fillCity"/> + <selectOption userInput="{{US_Address_CA.country_id}}" selector="{{AdminCustomerAccountNewAddressSection.country}}" stepKey="selectCountry"/> + <selectOption userInput="{{US_Address_CA.state}}" selector="{{AdminCustomerAccountNewAddressSection.region}}" stepKey="selectState"/> + <fillField userInput="{{customerAddress.postcode}}" selector="{{AdminCustomerAccountNewAddressSection.zip}}" stepKey="fillZipCode"/> + <fillField userInput="{{customerAddress.telephone}}" selector="{{AdminCustomerAccountNewAddressSection.phone}}" stepKey="fillPhone"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="saveCustomer"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the customer." stepKey="CustomerIsSaved"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Page/AdminEditCustomerPage.xml b/app/code/Magento/Customer/Test/Mftf/Page/AdminEditCustomerPage.xml index 72d5d90bdc05f..b5a87009315b3 100644 --- a/app/code/Magento/Customer/Test/Mftf/Page/AdminEditCustomerPage.xml +++ b/app/code/Magento/Customer/Test/Mftf/Page/AdminEditCustomerPage.xml @@ -12,5 +12,6 @@ <section name="AdminCustomerMainActionsSection"/> <section name="AdminCustomerAccountAddressSection"/> <section name="AdminCustomerAccountEditAddressSection"/> + <section name="AdminAddCustomerAddressActionGroup"/> </page> </pages> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountNewAddressSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountNewAddressSection.xml new file mode 100644 index 0000000000000..4f1bf69c6e687 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountNewAddressSection.xml @@ -0,0 +1,19 @@ +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminCustomerAccountNewAddressSection"> + <element name="firstName" type="button" selector="input[name*='address'][name*='new'][name*='firstname']"/> + <element name="lastName" type="button" selector="input[name*='address'][name*='new'][name*='lastname']"/> + <element name="street" type="button" selector="input[name*='new'][name*='street']"/> + <element name="city" type="input" selector="input[name*='new'][name*='city']"/> + <element name="country" type="select" selector="select[name*='address'][name*='new'][name*='country']" timeout="10"/> + <element name="region" type="select" selector="select[name*='address'][name*='new'][name*='region_id']"/> + <element name="zip" type="input" selector="input[name*='new'][name*='postcode']"/> + <element name="phone" type="text" selector="input[name*='new'][name*='telephone']" /> + </section> +</sections> diff --git a/app/code/Magento/Eav/Model/Entity/Type.php b/app/code/Magento/Eav/Model/Entity/Type.php index aa298d7d547bf..5ee15287e8899 100644 --- a/app/code/Magento/Eav/Model/Entity/Type.php +++ b/app/code/Magento/Eav/Model/Entity/Type.php @@ -167,11 +167,8 @@ public function getAttributeCollection($setId = null) */ protected function _getAttributeCollection() { - $collection = $this->_attributeFactory->create()->getCollection(); - $objectsModel = $this->getAttributeModel(); - if ($objectsModel) { - $collection->setModel($objectsModel); - } + $collection = $this->_universalFactory->create($this->getEntityAttributeCollection()); + $collection->setItemObjectClass($this->getAttributeModel()); return $collection; } From 5d6dd2a41a49dac7a45db21dfa73b09ab6377dd8 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Thu, 7 Feb 2019 14:58:52 +0200 Subject: [PATCH 0547/1295] MAGETWO-97947: Invalid action behavior --- .../Adminhtml/Wysiwyg/Images/DeleteFolder.php | 9 +++--- .../Adminhtml/Wysiwyg/Images/NewFolder.php | 9 +++--- .../Controller/Adminhtml/Order/CancelTest.php | 2 -- .../Adminhtml/Widget/BuildWidget.php | 30 ++++++++++++++----- .../Controller/Adminhtml/GroupTest.php | 6 ++-- 5 files changed, 36 insertions(+), 20 deletions(-) diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolder.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolder.php index 7acdbd28444d6..81ae1affb5e00 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolder.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolder.php @@ -7,6 +7,7 @@ namespace Magento\Cms\Controller\Adminhtml\Wysiwyg\Images; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Exception\NotFoundException; /** * Delete image folder. @@ -57,11 +58,11 @@ public function __construct( */ public function execute() { - try { - if (!$this->getRequest()->isPost()) { - throw new \Exception('Wrong request.'); - } + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + try { $path = $this->getStorage()->getCmsWysiwygImages()->getCurrentPath(); if (!$this->directoryResolver->validatePath($path, DirectoryList::MEDIA)) { throw new \Magento\Framework\Exception\LocalizedException( diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolder.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolder.php index b66762503b18a..5171430e67371 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolder.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolder.php @@ -7,6 +7,7 @@ namespace Magento\Cms\Controller\Adminhtml\Wysiwyg\Images; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Exception\NotFoundException; /** * Creates new folder. @@ -49,11 +50,11 @@ public function __construct( */ public function execute() { - try { - if (!$this->getRequest()->isPost()) { - throw new \Exception('Wrong request.'); - } + if (!$this->getRequest()->isPost()) { + throw new NotFoundException(__('Page not found')); + } + try { $this->_initAction(); $name = $this->getRequest()->getPost('name'); $path = $this->getStorage()->getSession()->getCurrentPath(); diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/CancelTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/CancelTest.php index 64c73f8f36c23..d37b0f6121b37 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/CancelTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/CancelTest.php @@ -103,8 +103,6 @@ protected function setUp() \Magento\Sales\Controller\Adminhtml\Order\Cancel::class, [ 'context' => $this->context, - //'request' => $this->request, - //'response' => $this->response, 'orderRepository' => $this->orderRepositoryMock ] ); diff --git a/app/code/Magento/Widget/Controller/Adminhtml/Widget/BuildWidget.php b/app/code/Magento/Widget/Controller/Adminhtml/Widget/BuildWidget.php index 33c4992429490..b52df887b47e2 100644 --- a/app/code/Magento/Widget/Controller/Adminhtml/Widget/BuildWidget.php +++ b/app/code/Magento/Widget/Controller/Adminhtml/Widget/BuildWidget.php @@ -6,6 +6,8 @@ */ namespace Magento\Widget\Controller\Adminhtml\Widget; +use Magento\Framework\App\ObjectManager; + class BuildWidget extends \Magento\Backend\App\Action { /** @@ -18,15 +20,25 @@ class BuildWidget extends \Magento\Backend\App\Action */ protected $_widget; + /** + * @var \Magento\Framework\Serialize\SerializerInterface + */ + private $serializer; + /** * @param \Magento\Backend\App\Action\Context $context * @param \Magento\Widget\Model\Widget $widget + * @param \Magento\Framework\Serialize\SerializerInterface|null $serializer */ public function __construct( \Magento\Backend\App\Action\Context $context, - \Magento\Widget\Model\Widget $widget + \Magento\Widget\Model\Widget $widget, + \Magento\Framework\Serialize\SerializerInterface $serializer = null ) { $this->_widget = $widget; + $this->serializer = $serializer ?: ObjectManager::getInstance()->get( + \Magento\Framework\Serialize\SerializerInterface::class + ); parent::__construct($context); } @@ -37,13 +49,17 @@ public function __construct( */ public function execute() { - $html = ''; - if ($this->getRequest()->isPost()) { - $type = $this->getRequest()->getPost('widget_type'); - $params = $this->getRequest()->getPost('parameters', []); - $asIs = $this->getRequest()->getPost('as_is'); - $html = $this->_widget->getWidgetDeclaration($type, $params, $asIs); + if (!$this->getRequest()->isPost()) { + $this->getResponse()->representJson( + $this->serializer->serialize(['error' => true, 'message' => 'Invalid request']) + ); + return; } + + $type = $this->getRequest()->getPost('widget_type'); + $params = $this->getRequest()->getPost('parameters', []); + $asIs = $this->getRequest()->getPost('as_is'); + $html = $this->_widget->getWidgetDeclaration($type, $params, $asIs); $this->getResponse()->setBody($html); } } diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/GroupTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/GroupTest.php index 446f3cb6aa464..1cc421fd2973d 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/GroupTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/GroupTest.php @@ -86,7 +86,7 @@ public function testNewActionWithCustomerGroupDataInSession() */ public function testDeleteActionNoGroupId() { - $this->getRequest()->setMethod(\Magento\Framework\App\Request\Http::METHOD_POST); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/group/delete'); $this->assertRedirect($this->stringStartsWith(self::BASE_CONTROLLER_URL)); } @@ -100,7 +100,7 @@ public function testDeleteActionExistingGroup() { $groupId = $this->findGroupIdWithCode(self::CUSTOMER_GROUP_CODE); $this->getRequest()->setParam('id', $groupId); - $this->getRequest()->setMethod(\Magento\Framework\App\Request\Http::METHOD_POST); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/group/delete'); /** @@ -121,7 +121,7 @@ public function testDeleteActionExistingGroup() public function testDeleteActionNonExistingGroupId() { $this->getRequest()->setParam('id', 10000); - $this->getRequest()->setMethod(\Magento\Framework\App\Request\Http::METHOD_POST); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/customer/group/delete'); /** From 096f2a730036aa2fde214bda0cc66e2a75109b22 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Thu, 7 Feb 2019 15:37:34 +0200 Subject: [PATCH 0548/1295] MAGETWO-95626: Log of actions in the Admin panel --- .../Indexer/Test/Mftf/ActionGroup/AdminIndexerActionGroup.xml | 2 +- .../Indexer/Test/Mftf/Section/AdminIndexManagementSection.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Indexer/Test/Mftf/ActionGroup/AdminIndexerActionGroup.xml b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/AdminIndexerActionGroup.xml index 38852e9d833e6..5cf6656f9fb50 100644 --- a/app/code/Magento/Indexer/Test/Mftf/ActionGroup/AdminIndexerActionGroup.xml +++ b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/AdminIndexerActionGroup.xml @@ -16,7 +16,7 @@ <amOnPage url="{{AdminIndexManagementPage.url}}" stepKey="amOnIndexManagementPage"/> <checkOption selector="{{AdminIndexManagementSection.indexerCheckbox(indexerId)}}" stepKey="selectIndexer"/> <selectOption selector="{{AdminIndexManagementSection.massActionSelect}}" userInput="{{indexerMode}}" stepKey="selectIndexerMode"/> - <click selector="{{AdminIndexManagementSection.massActionSubmit}}" timeout="30" stepKey="submitIndexerForm"/> + <click selector="{{AdminIndexManagementSection.massActionSubmit}}" stepKey="submitIndexerForm"/> <see selector="{{AdminMessagesSection.success}}" userInput='1 indexer(s) are in "{{indexerMode}}" mode.' stepKey="seeSuccessMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Indexer/Test/Mftf/Section/AdminIndexManagementSection.xml b/app/code/Magento/Indexer/Test/Mftf/Section/AdminIndexManagementSection.xml index 0e954f12c4df7..07d93ff850221 100644 --- a/app/code/Magento/Indexer/Test/Mftf/Section/AdminIndexManagementSection.xml +++ b/app/code/Magento/Indexer/Test/Mftf/Section/AdminIndexManagementSection.xml @@ -11,6 +11,6 @@ <section name="AdminIndexManagementSection"> <element name="indexerCheckbox" type="checkbox" selector="#gridIndexer_table input[value='{{indexerId}}']" parameterized="true"/> <element name="massActionSelect" type="select" selector="#gridIndexer_massaction-select"/> - <element name="massActionSubmit" type="button" selector="#gridIndexer_massaction-form button"/> + <element name="massActionSubmit" type="button" selector="#gridIndexer_massaction-form button" timeout="30"/> </section> </sections> From 1ebfd8c918c1673d7d72ee6f7cad9ae1fd884584 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Thu, 7 Feb 2019 15:41:02 +0200 Subject: [PATCH 0549/1295] ENGCOM-4122: Static test fix. --- .../web/css/source/_module.less | 10 +++++----- .../web/css/source/_module.less | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_MultipleWishlist/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_MultipleWishlist/web/css/source/_module.less index 4ded1c5f349b6..7d86850c4e517 100644 --- a/app/design/frontend/Magento/blank/Magento_MultipleWishlist/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_MultipleWishlist/web/css/source/_module.less @@ -342,7 +342,7 @@ .product { &-item { &-checkbox { - left: 0px; + left: 0; position: absolute; top: 20px; } @@ -381,16 +381,16 @@ .media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) { .wishlist { &.window.popup { + .field { + .lib-form-field-type-revert(@_type: block); + } + bottom: auto; .lib-css(top, @desktop-popup-position-top); .lib-css(left, @desktop-popup-position-left); .lib-css(margin-left, @desktop-popup-margin-left); .lib-css(width, @desktop-popup-width); right: auto; - - .field { - .lib-form-field-type-revert(@_type: block); - } } } diff --git a/app/design/frontend/Magento/luma/Magento_MultipleWishlist/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_MultipleWishlist/web/css/source/_module.less index 9cd4d16d64214..917e85cdf94b5 100644 --- a/app/design/frontend/Magento/luma/Magento_MultipleWishlist/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_MultipleWishlist/web/css/source/_module.less @@ -435,7 +435,7 @@ .product { &-item { &-checkbox { - left: 0px; + left: 0; position: absolute; top: 20px; } From 69b16da0598cf159040915723bcb1c26f5aa697e Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Thu, 7 Feb 2019 16:34:04 +0200 Subject: [PATCH 0550/1295] MAGETWO-73432: Change EAV model --- .../Magento/Customer/Test/Mftf/Page/AdminEditCustomerPage.xml | 2 +- .../Test/Mftf/Section/AdminCustomerAccountAddressSection.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/Page/AdminEditCustomerPage.xml b/app/code/Magento/Customer/Test/Mftf/Page/AdminEditCustomerPage.xml index b5a87009315b3..7cd36c12c80bd 100644 --- a/app/code/Magento/Customer/Test/Mftf/Page/AdminEditCustomerPage.xml +++ b/app/code/Magento/Customer/Test/Mftf/Page/AdminEditCustomerPage.xml @@ -12,6 +12,6 @@ <section name="AdminCustomerMainActionsSection"/> <section name="AdminCustomerAccountAddressSection"/> <section name="AdminCustomerAccountEditAddressSection"/> - <section name="AdminAddCustomerAddressActionGroup"/> + <section name="AdminCustomerAccountNewAddressSection"/> </page> </pages> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountAddressSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountAddressSection.xml index db9619dde671f..334a5d81901ea 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountAddressSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountAddressSection.xml @@ -7,7 +7,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCustomerAccountAddressSection"> - <element name="addresses" type="button" selector="a#tab_address"/> + <element name="addresses" type="button" selector="a#tab_address" timeout="30"/> <element name="addNewAddress" type="button" selector=".address-list-actions button.scalable.add span"/> </section> </sections> From f6a6ead3e9f577463c8d3e5a826e989b4f2016fc Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Thu, 7 Feb 2019 17:25:41 +0200 Subject: [PATCH 0551/1295] ENGCOM-4133: Static test fix. --- .../web/css/source/module/checkout/_tooltip.less | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_tooltip.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_tooltip.less index e42d8eb7a8dd7..39b9a051e6592 100644 --- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_tooltip.less +++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_tooltip.less @@ -151,26 +151,28 @@ // // Tablet // _____________________________________________ + @media only screen and (max-width: @screen__m) { .field-tooltip .field-tooltip-content { + left: auto; right: -10px; top: 40px; - left: auto; } - .field-tooltip .field-tooltip-content::before, .field-tooltip .field-tooltip-content::after { + .field-tooltip .field-tooltip-content::before, + .field-tooltip .field-tooltip-content::after { border: 10px solid transparent; height: 0; - width: 0; + left: auto; margin-top: -21px; right: 10px; - left: auto; top: 0; + width: 0; } .field-tooltip .field-tooltip-content::before { - border-bottom-color: #666; + border-bottom-color: @color-gray40; } .field-tooltip .field-tooltip-content::after { - border-bottom-color: #f4f4f4; + border-bottom-color: @color-gray-light01; top: 1px; } -} \ No newline at end of file +} From ff63723c96156bf749e025419ef1fd640bd28f69 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 7 Feb 2019 17:41:28 +0200 Subject: [PATCH 0552/1295] MAGETWO-98107: [FT] [MFTF] AdminCartRulesAppliedForProductInCartTest fails because of bad design --- .../app/Magento/Analytics/Test/TestCase/NavigateMenuTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/functional/tests/app/Magento/Analytics/Test/TestCase/NavigateMenuTest.xml b/dev/tests/functional/tests/app/Magento/Analytics/Test/TestCase/NavigateMenuTest.xml index 4a1ccf3bcb044..76f60a51c0ece 100644 --- a/dev/tests/functional/tests/app/Magento/Analytics/Test/TestCase/NavigateMenuTest.xml +++ b/dev/tests/functional/tests/app/Magento/Analytics/Test/TestCase/NavigateMenuTest.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Backend\Test\TestCase\NavigateMenuTest" summary="Navigate to menu chapter"> <variation name="NavigateMenuTestBIEssentials" summary="Navigate through BI Essentials admin menu to Sign Up page" ticketId="MAGETWO-63700"> + <data name="issue" xsi:type="string">MAGETWO-97298: [2.2-develop] Magento\Backend\Test\TestCase\NavigateMenuTest fails on Jenkins</data> <data name="menuItem" xsi:type="string">Reports > BI Essentials</data> <data name="waitMenuItemNotVisible" xsi:type="boolean">false</data> <data name="businessIntelligenceLink" xsi:type="string">https://account.magento.com/onboarding/steps/view/step/gmv/</data> From fe02088e52243c48b0a11c86c53862be8bfc573e Mon Sep 17 00:00:00 2001 From: Jason Woods <devel@jasonwoods.me.uk> Date: Thu, 7 Feb 2019 20:16:21 +0000 Subject: [PATCH 0553/1295] Fix admin quote address being lost when removing all items, causing broken quote process until address is updated --- app/code/Magento/Checkout/etc/di.xml | 3 --- app/code/Magento/Checkout/etc/frontend/di.xml | 3 +++ .../Checkout/Plugin/Model/Quote/ResetQuoteAddressesTest.php | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Checkout/etc/di.xml b/app/code/Magento/Checkout/etc/di.xml index 71dfd12bb4779..4ebd594a28562 100644 --- a/app/code/Magento/Checkout/etc/di.xml +++ b/app/code/Magento/Checkout/etc/di.xml @@ -49,7 +49,4 @@ </argument> </arguments> </type> - <type name="Magento\Quote\Model\Quote"> - <plugin name="clear_addresses_after_product_delete" type="Magento\Checkout\Plugin\Model\Quote\ResetQuoteAddresses"/> - </type> </config> diff --git a/app/code/Magento/Checkout/etc/frontend/di.xml b/app/code/Magento/Checkout/etc/frontend/di.xml index d80f88786c87b..e203fe5077d26 100644 --- a/app/code/Magento/Checkout/etc/frontend/di.xml +++ b/app/code/Magento/Checkout/etc/frontend/di.xml @@ -95,4 +95,7 @@ </argument> </arguments> </type> + <type name="Magento\Quote\Model\Quote"> + <plugin name="clear_addresses_after_product_delete" type="Magento\Checkout\Plugin\Model\Quote\ResetQuoteAddresses"/> + </type> </config> diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Plugin/Model/Quote/ResetQuoteAddressesTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Plugin/Model/Quote/ResetQuoteAddressesTest.php index 43108dbca1f5e..85dede0d84c2d 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/Plugin/Model/Quote/ResetQuoteAddressesTest.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/Plugin/Model/Quote/ResetQuoteAddressesTest.php @@ -21,6 +21,7 @@ class ResetQuoteAddressesTest extends \PHPUnit\Framework\TestCase { /** * @magentoDataFixture Magento/Checkout/_files/quote_with_virtual_product_and_address.php + * @magentoAppArea frontend * * @return void */ From d89a38aa10ef8b01736ccfc020cafef923e577fb Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Thu, 7 Feb 2019 14:45:36 -0600 Subject: [PATCH 0554/1295] magento-engcom/magento2ce#2538: Skipped randomly failed functional test --- .../app/Magento/Analytics/Test/TestCase/NavigateMenuTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/functional/tests/app/Magento/Analytics/Test/TestCase/NavigateMenuTest.xml b/dev/tests/functional/tests/app/Magento/Analytics/Test/TestCase/NavigateMenuTest.xml index 4a1ccf3bcb044..76f60a51c0ece 100644 --- a/dev/tests/functional/tests/app/Magento/Analytics/Test/TestCase/NavigateMenuTest.xml +++ b/dev/tests/functional/tests/app/Magento/Analytics/Test/TestCase/NavigateMenuTest.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Backend\Test\TestCase\NavigateMenuTest" summary="Navigate to menu chapter"> <variation name="NavigateMenuTestBIEssentials" summary="Navigate through BI Essentials admin menu to Sign Up page" ticketId="MAGETWO-63700"> + <data name="issue" xsi:type="string">MAGETWO-97298: [2.2-develop] Magento\Backend\Test\TestCase\NavigateMenuTest fails on Jenkins</data> <data name="menuItem" xsi:type="string">Reports > BI Essentials</data> <data name="waitMenuItemNotVisible" xsi:type="boolean">false</data> <data name="businessIntelligenceLink" xsi:type="string">https://account.magento.com/onboarding/steps/view/step/gmv/</data> From c412fc8ba3e7a96d7b2787220f7370c940740772 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Fri, 8 Feb 2019 10:10:11 +0200 Subject: [PATCH 0555/1295] MAGETWO-95463: Order Sales Report includes canceled orders --- .../Mftf/ActionGroup/GenerateOrderReportActionGroup.xml | 6 +++--- .../{OrdersReportPage.xml => AdminOrdersReportPage.xml} | 8 ++++---- .../Mftf/Section/AdminOrderReportMainActionsSection.xml | 4 ++-- .../Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) rename app/code/Magento/Reports/Test/Mftf/Page/{OrdersReportPage.xml => AdminOrdersReportPage.xml} (52%) diff --git a/app/code/Magento/Reports/Test/Mftf/ActionGroup/GenerateOrderReportActionGroup.xml b/app/code/Magento/Reports/Test/Mftf/ActionGroup/GenerateOrderReportActionGroup.xml index 79b9c21b0db6a..8ddfd4092645f 100644 --- a/app/code/Magento/Reports/Test/Mftf/ActionGroup/GenerateOrderReportActionGroup.xml +++ b/app/code/Magento/Reports/Test/Mftf/ActionGroup/GenerateOrderReportActionGroup.xml @@ -13,10 +13,10 @@ <argument name="orderFromDate" type="string"/> <argument name="orderToDate" type="string"/> </arguments> - <click selector="{{AdminOrderReportMainActionsSection.here}}" stepKey="refreshStatistics" /> + <click selector="{{AdminOrderReportMainActionsSection.refreshStatistics}}" stepKey="refreshStatistics"/> <fillField selector="{{AdminOrderReportFilterSection.dateFrom}}" userInput="{{orderFromDate}}" stepKey="fillFromDate"/> <fillField selector="{{AdminOrderReportFilterSection.dateTo}}" userInput="{{orderToDate}}" stepKey="fillToDate"/> - <selectOption selector="{{AdminOrderReportFilterSection.orderStatus}}" userInput="Any" stepKey="selectAnyOption" /> - <click selector="{{AdminOrderReportMainActionsSection.showReport}}" stepKey="showReport" /> + <selectOption selector="{{AdminOrderReportFilterSection.orderStatus}}" userInput="Any" stepKey="selectAnyOption"/> + <click selector="{{AdminOrderReportMainActionsSection.showReport}}" stepKey="showReport"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Reports/Test/Mftf/Page/OrdersReportPage.xml b/app/code/Magento/Reports/Test/Mftf/Page/AdminOrdersReportPage.xml similarity index 52% rename from app/code/Magento/Reports/Test/Mftf/Page/OrdersReportPage.xml rename to app/code/Magento/Reports/Test/Mftf/Page/AdminOrdersReportPage.xml index 46509089b97ba..f0b51f6e39357 100644 --- a/app/code/Magento/Reports/Test/Mftf/Page/OrdersReportPage.xml +++ b/app/code/Magento/Reports/Test/Mftf/Page/AdminOrdersReportPage.xml @@ -6,9 +6,9 @@ */ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="OrdersReportPage" url="reports/report_sales/sales/" area="admin" module="Reports"> - <section name="OrderReportFilterSection"/> - <section name="OrderReportMainSection"/> - <section name="GeneratedReportSection" /> + <page name="AdminOrdersReportPage" url="reports/report_sales/sales/" area="admin" module="Reports"> + <section name="AdminOrderReportFilterSection"/> + <section name="AdminOrderReportMainActionsSection"/> + <section name="AdminOrderReportTableSection"/> </page> </pages> diff --git a/app/code/Magento/Reports/Test/Mftf/Section/AdminOrderReportMainActionsSection.xml b/app/code/Magento/Reports/Test/Mftf/Section/AdminOrderReportMainActionsSection.xml index e9a56f538fd9c..c4a96537740ee 100644 --- a/app/code/Magento/Reports/Test/Mftf/Section/AdminOrderReportMainActionsSection.xml +++ b/app/code/Magento/Reports/Test/Mftf/Section/AdminOrderReportMainActionsSection.xml @@ -9,7 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderReportMainActionsSection"> - <element name="showReport" type="button" selector="#filter_form_submit"/> - <element name="here" type="text" selector="//a[contains(text(), 'here')]"/> + <element name="showReport" type="button" time="30" selector="#filter_form_submit"/> + <element name="refreshStatistics" type="text" time="30" selector="//a[contains(text(), 'here')]"/> </section> </sections> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml index c229fb1cf0271..009e4b8e5f6f1 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml @@ -55,7 +55,7 @@ <!--Create order invoice--> <comment userInput="Admin creates invoice for order" stepKey="adminCreateInvoiceComment" /> <actionGroup ref="StartCreateInvoiceFromOrderPage" stepKey="createInvoice"/> - <actionGroup ref="SubmitInvoice" stepKey="submitInvoice"/> <!--Invoice created successfully--> + <actionGroup ref="SubmitInvoice" stepKey="submitInvoice"/> <!--Ship Order--> <comment userInput="Admin creates shipment" stepKey="adminCreatesShipmentComment"/> <actionGroup ref="StartCreateShipmentFromOrderPage" stepKey="createShipment"/> @@ -81,7 +81,7 @@ <actionGroup ref="cancelPendingOrder" stepKey="cancelOrder"/> <!-- Generate Order report --> - <amOnPage url="{{OrdersReportPage.url}}" stepKey="goToOrdersReportPage1"/> + <amOnPage url="{{AdminOrdersReportPage.url}}" stepKey="goToAdminOrdersReportPage"/> <!-- Get date --> <generateDate date="+0 day" format="m/d/Y" stepKey="generateEndDate"/> <generateDate date="-1 day" format="m/d/Y" stepKey="generateStartDate"/> From acad817a34f70172f56d5c2601d094e274c12492 Mon Sep 17 00:00:00 2001 From: serhii balko <serhii.balko@transoftgroup.com> Date: Fri, 8 Feb 2019 13:49:17 +0200 Subject: [PATCH 0556/1295] MAGETWO-97528: Custom Customer Attribute is not updating on one website --- .../Controller/Adminhtml/Index/Save.php | 38 ++++++++-- .../Controller/Adminhtml/Index/SaveTest.php | 75 +++++++++++-------- .../Controller/Adminhtml/IndexTest.php | 36 +++++++++ 3 files changed, 112 insertions(+), 37 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php index 3a03e9064a0a3..ffff916053dbd 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php @@ -283,11 +283,9 @@ protected function _extractCustomerAddressData(array & $extractedCustomerData) public function execute() { $returnToEdit = false; - $originalRequestData = $this->getRequest()->getPostValue(); - $customerId = $this->getCurrentCustomerId(); - if ($originalRequestData) { + if ($this->getRequest()->getPostValue()) { try { // optional fields might be set in request for future processing by observers in other modules $customerData = $this->_extractCustomerData(); @@ -375,7 +373,7 @@ public function execute() $messages = $exception->getMessage(); } $this->_addSessionErrorMessages($messages); - $this->_getSession()->setCustomerFormData($originalRequestData); + $this->_getSession()->setCustomerFormData($this->retrieveFormattedFormData()); $returnToEdit = true; } catch (\Magento\Framework\Exception\AbstractAggregateException $exception) { $errors = $exception->getErrors(); @@ -384,18 +382,19 @@ public function execute() $messages[] = $error->getMessage(); } $this->_addSessionErrorMessages($messages); - $this->_getSession()->setCustomerFormData($originalRequestData); + $this->_getSession()->setCustomerFormData($this->retrieveFormattedFormData()); $returnToEdit = true; } catch (LocalizedException $exception) { $this->_addSessionErrorMessages($exception->getMessage()); - $this->_getSession()->setCustomerFormData($originalRequestData); + $this->_getSession()->setCustomerFormData($this->retrieveFormattedFormData()); $returnToEdit = true; } catch (\Exception $exception) { $this->messageManager->addException($exception, __('Something went wrong while saving the customer.')); - $this->_getSession()->setCustomerFormData($originalRequestData); + $this->_getSession()->setCustomerFormData($this->retrieveFormattedFormData()); $returnToEdit = true; } } + $resultRedirect = $this->resultRedirectFactory->create(); if ($returnToEdit) { if ($customerId) { @@ -501,4 +500,29 @@ private function disableAddressValidation(CustomerInterface $customer) $addressModel->setShouldIgnoreValidation(true); } } + + /** + * Retrieve formatted form data + * + * @return array + */ + private function retrieveFormattedFormData(): array + { + $originalRequestData = $originalRequestData = $this->getRequest()->getPostValue(); + + /* Customer data filtration */ + if (isset($originalRequestData['customer'])) { + $customerData = $this->_extractData( + 'adminhtml_customer', + CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, + [], + 'customer' + ); + + $customerData = array_intersect_key($customerData, $originalRequestData['customer']); + $originalRequestData['customer'] = array_merge($originalRequestData['customer'], $customerData); + } + + return $originalRequestData; + } } diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php index e703e7499d731..09082a0a9de53 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php @@ -886,22 +886,24 @@ public function testExecuteWithNewCustomerAndValidationException() 'customer' => [ 'coolness' => false, 'disable_auto_group_change' => 'false', + 'dob' => '3/12/1996', ], 'subscription' => $subscription, ]; $extractedData = [ 'coolness' => false, 'disable_auto_group_change' => 'false', + 'dob' => '1996-03-12', ]; /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $customerFormMock */ $attributeMock = $this->getMockBuilder( \Magento\Customer\Api\Data\AttributeMetadataInterface::class )->disableOriginalConstructor()->getMock(); - $attributeMock->expects($this->once()) + $attributeMock->expects($this->exactly(2)) ->method('getAttributeCode') ->willReturn('coolness'); - $attributeMock->expects($this->once()) + $attributeMock->expects($this->exactly(2)) ->method('getFrontendInput') ->willReturn('int'); $attributes = [$attributeMock]; @@ -912,7 +914,7 @@ public function testExecuteWithNewCustomerAndValidationException() [null, null, $postValue], [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], ]); - $this->requestMock->expects($this->exactly(2)) + $this->requestMock->expects($this->any()) ->method('getPost') ->willReturnMap( [ @@ -925,12 +927,12 @@ public function testExecuteWithNewCustomerAndValidationException() $objectMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) ->disableOriginalConstructor() ->getMock(); - $objectMock->expects($this->once()) + $objectMock->expects($this->exactly(2)) ->method('getData') ->with('customer') ->willReturn($postValue['customer']); - $this->objectFactoryMock->expects($this->once()) + $this->objectFactoryMock->expects($this->exactly(2)) ->method('create') ->with(['data' => $postValue]) ->willReturn($objectMock); @@ -938,19 +940,19 @@ public function testExecuteWithNewCustomerAndValidationException() $customerFormMock = $this->getMockBuilder( \Magento\Customer\Model\Metadata\Form::class )->disableOriginalConstructor()->getMock(); - $customerFormMock->expects($this->once()) + $customerFormMock->expects($this->exactly(2)) ->method('extractData') ->with($this->requestMock, 'customer') ->willReturn($extractedData); - $customerFormMock->expects($this->once()) + $customerFormMock->expects($this->exactly(2)) ->method('compactData') ->with($extractedData) ->willReturn($extractedData); - $customerFormMock->expects($this->once()) + $customerFormMock->expects($this->exactly(2)) ->method('getAttributes') ->willReturn($attributes); - $this->formFactoryMock->expects($this->once()) + $this->formFactoryMock->expects($this->exactly(2)) ->method('create') ->with( CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, @@ -998,7 +1000,10 @@ public function testExecuteWithNewCustomerAndValidationException() $this->sessionMock->expects($this->once()) ->method('setCustomerFormData') - ->with($postValue); + ->with([ + 'customer' => $extractedData, + 'subscription' => $subscription, + ]); /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) @@ -1029,22 +1034,24 @@ public function testExecuteWithNewCustomerAndLocalizedException() 'customer' => [ 'coolness' => false, 'disable_auto_group_change' => 'false', + 'dob' => '3/12/1996', ], 'subscription' => $subscription, ]; $extractedData = [ 'coolness' => false, 'disable_auto_group_change' => 'false', + 'dob' => '1996-03-12', ]; /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $customerFormMock */ $attributeMock = $this->getMockBuilder( \Magento\Customer\Api\Data\AttributeMetadataInterface::class )->disableOriginalConstructor()->getMock(); - $attributeMock->expects($this->once()) + $attributeMock->expects($this->exactly(2)) ->method('getAttributeCode') ->willReturn('coolness'); - $attributeMock->expects($this->once()) + $attributeMock->expects($this->exactly(2)) ->method('getFrontendInput') ->willReturn('int'); $attributes = [$attributeMock]; @@ -1055,7 +1062,7 @@ public function testExecuteWithNewCustomerAndLocalizedException() [null, null, $postValue], [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], ]); - $this->requestMock->expects($this->exactly(2)) + $this->requestMock->expects($this->any()) ->method('getPost') ->willReturnMap( [ @@ -1068,12 +1075,12 @@ public function testExecuteWithNewCustomerAndLocalizedException() $objectMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) ->disableOriginalConstructor() ->getMock(); - $objectMock->expects($this->once()) + $objectMock->expects($this->exactly(2)) ->method('getData') ->with('customer') ->willReturn($postValue['customer']); - $this->objectFactoryMock->expects($this->once()) + $this->objectFactoryMock->expects($this->exactly(2)) ->method('create') ->with(['data' => $postValue]) ->willReturn($objectMock); @@ -1082,19 +1089,19 @@ public function testExecuteWithNewCustomerAndLocalizedException() $customerFormMock = $this->getMockBuilder( \Magento\Customer\Model\Metadata\Form::class )->disableOriginalConstructor()->getMock(); - $customerFormMock->expects($this->once()) + $customerFormMock->expects($this->exactly(2)) ->method('extractData') ->with($this->requestMock, 'customer') ->willReturn($extractedData); - $customerFormMock->expects($this->once()) + $customerFormMock->expects($this->exactly(2)) ->method('compactData') ->with($extractedData) ->willReturn($extractedData); - $customerFormMock->expects($this->once()) + $customerFormMock->expects($this->exactly(2)) ->method('getAttributes') ->willReturn($attributes); - $this->formFactoryMock->expects($this->once()) + $this->formFactoryMock->expects($this->exactly(2)) ->method('create') ->with( CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, @@ -1141,7 +1148,10 @@ public function testExecuteWithNewCustomerAndLocalizedException() $this->sessionMock->expects($this->once()) ->method('setCustomerFormData') - ->with($postValue); + ->with([ + 'customer' => $extractedData, + 'subscription' => $subscription, + ]); /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) @@ -1172,22 +1182,24 @@ public function testExecuteWithNewCustomerAndException() 'customer' => [ 'coolness' => false, 'disable_auto_group_change' => 'false', + 'dob' => '3/12/1996', ], 'subscription' => $subscription, ]; $extractedData = [ 'coolness' => false, 'disable_auto_group_change' => 'false', + 'dob' => '1996-03-12', ]; /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $customerFormMock */ $attributeMock = $this->getMockBuilder( \Magento\Customer\Api\Data\AttributeMetadataInterface::class )->disableOriginalConstructor()->getMock(); - $attributeMock->expects($this->once()) + $attributeMock->expects($this->exactly(2)) ->method('getAttributeCode') ->willReturn('coolness'); - $attributeMock->expects($this->once()) + $attributeMock->expects($this->exactly(2)) ->method('getFrontendInput') ->willReturn('int'); $attributes = [$attributeMock]; @@ -1198,7 +1210,7 @@ public function testExecuteWithNewCustomerAndException() [null, null, $postValue], [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], ]); - $this->requestMock->expects($this->exactly(2)) + $this->requestMock->expects($this->any()) ->method('getPost') ->willReturnMap( [ @@ -1211,12 +1223,12 @@ public function testExecuteWithNewCustomerAndException() $objectMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) ->disableOriginalConstructor() ->getMock(); - $objectMock->expects($this->once()) + $objectMock->expects($this->exactly(2)) ->method('getData') ->with('customer') ->willReturn($postValue['customer']); - $this->objectFactoryMock->expects($this->once()) + $this->objectFactoryMock->expects($this->exactly(2)) ->method('create') ->with(['data' => $postValue]) ->willReturn($objectMock); @@ -1224,19 +1236,19 @@ public function testExecuteWithNewCustomerAndException() $customerFormMock = $this->getMockBuilder( \Magento\Customer\Model\Metadata\Form::class )->disableOriginalConstructor()->getMock(); - $customerFormMock->expects($this->once()) + $customerFormMock->expects($this->exactly(2)) ->method('extractData') ->with($this->requestMock, 'customer') ->willReturn($extractedData); - $customerFormMock->expects($this->once()) + $customerFormMock->expects($this->exactly(2)) ->method('compactData') ->with($extractedData) ->willReturn($extractedData); - $customerFormMock->expects($this->once()) + $customerFormMock->expects($this->exactly(2)) ->method('getAttributes') ->willReturn($attributes); - $this->formFactoryMock->expects($this->once()) + $this->formFactoryMock->expects($this->exactly(2)) ->method('create') ->with( CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, @@ -1285,7 +1297,10 @@ public function testExecuteWithNewCustomerAndException() $this->sessionMock->expects($this->once()) ->method('setCustomerFormData') - ->with($postValue); + ->with([ + 'customer' => $extractedData, + 'subscription' => $subscription, + ]); /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php index 36df9cbf851bd..ccf9c45da8660 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php @@ -509,6 +509,42 @@ public function testSaveActionCoreException() $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'new/key/')); } + /** + * @magentoDataFixture Magento/Customer/_files/customer_sample.php + */ + public function testSaveActionCoreExceptionFormatFormData() + { + $post = [ + 'customer' => [ + 'website_id' => 1, + 'email' => 'customer@example.com', + 'dob' => '12/3/1996', + ], + ]; + $postFormatted = [ + 'customer' => [ + 'website_id' => 1, + 'email' => 'customer@example.com', + 'dob' => '1996-12-03', + ], + ]; + $this->getRequest()->setPostValue($post); + $this->dispatch('backend/customer/index/save'); + /* + * Check that error message is set + */ + $this->assertSessionMessages( + $this->equalTo(['A customer with the same email already exists in an associated website.']), + \Magento\Framework\Message\MessageInterface::TYPE_ERROR + ); + $this->assertEquals( + $postFormatted, + Bootstrap::getObjectManager()->get(\Magento\Backend\Model\Session::class)->getCustomerFormData(), + 'Customer form data should be formatted' + ); + $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'new/key/')); + } + /** * @magentoDataFixture Magento/Customer/_files/customer_sample.php */ From 373c3b374892cf353b9d17ca098370aba2bf7aa2 Mon Sep 17 00:00:00 2001 From: Kajal Solanki <kajal.solanki@krishtechnolabs.com> Date: Fri, 8 Feb 2019 17:27:30 +0530 Subject: [PATCH 0557/1295] Error icon issue alignment issue resolved --- .../Magento/backend/web/css/source/components/_messages.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less index 19d076bd20cc5..bc0797ab18f32 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less @@ -111,7 +111,7 @@ content: @alert-icon__error__content; font-size: @alert-icon__error__font-size; left: 2.2rem; - margin-top: 0.5rem; + margin-top: -1.5rem; } } From 07216dde117d445d34c0e3e306dbee74c2f18fb0 Mon Sep 17 00:00:00 2001 From: Kajal Solanki <kajal.solanki@krishtechnolabs.com> Date: Fri, 8 Feb 2019 17:39:48 +0530 Subject: [PATCH 0558/1295] Resolved vertically middle issue --- .../Magento/backend/web/css/source/components/_messages.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less index bc0797ab18f32..15cd295885892 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less @@ -111,7 +111,7 @@ content: @alert-icon__error__content; font-size: @alert-icon__error__font-size; left: 2.2rem; - margin-top: -1.5rem; + margin-top: -1.1rem; } } From 6e515402393b02e954841ecdb3dabc09e1907432 Mon Sep 17 00:00:00 2001 From: serhii balko <serhii.balko@transoftgroup.com> Date: Fri, 8 Feb 2019 15:32:26 +0200 Subject: [PATCH 0559/1295] MAGETWO-97528: Custom Customer Attribute is not updating on one website --- app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php index ffff916053dbd..5b61c0aaf5e2f 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php @@ -520,7 +520,7 @@ private function retrieveFormattedFormData(): array ); $customerData = array_intersect_key($customerData, $originalRequestData['customer']); - $originalRequestData['customer'] = array_merge($originalRequestData['customer'], $customerData); + $originalRequestData['customer'] = array_merge($originalRequestData['customer'], $customerData); } return $originalRequestData; From 42a7099e70eb1e369cc120654e79e8620d7db687 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Fri, 8 Feb 2019 15:33:54 +0200 Subject: [PATCH 0560/1295] MAGETWO-86215: Static blocks with same ID appear in place of correct block --- .../Magento/Cms/Model/ResourceModel/Block.php | 23 ++++--- .../AdminCreateNewCMSBlockActionGroup.xml | 21 +++++++ .../AdminDeleteCMSBlockActionGroup.xml | 21 +++++++ .../Section/AdminCmsBlockContentSection.xml | 3 + ...teStaticBlockOnDuplicateIdentifierTest.xml | 61 +++++++++++++++++++ 5 files changed, 122 insertions(+), 7 deletions(-) create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCreateNewCMSBlockActionGroup.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminDeleteCMSBlockActionGroup.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/Test/CheckCreateStaticBlockOnDuplicateIdentifierTest.xml diff --git a/app/code/Magento/Cms/Model/ResourceModel/Block.php b/app/code/Magento/Cms/Model/ResourceModel/Block.php index 9aab54b02bc14..30e817713755c 100644 --- a/app/code/Magento/Cms/Model/ResourceModel/Block.php +++ b/app/code/Magento/Cms/Model/ResourceModel/Block.php @@ -95,9 +95,11 @@ protected function _beforeSave(AbstractModel $object) } /** + * Get block id. + * * @param AbstractModel $object * @param mixed $value - * @param null $field + * @param string $field * @return bool|int|string * @throws LocalizedException * @throws \Exception @@ -183,10 +185,12 @@ public function getIsUniqueBlockToStores(AbstractModel $object) $entityMetadata = $this->metadataPool->getMetadata(BlockInterface::class); $linkField = $entityMetadata->getLinkField(); - if ($this->_storeManager->isSingleStoreMode()) { - $stores = [Store::DEFAULT_STORE_ID]; - } else { - $stores = (array)$object->getData('store_id'); + $stores = (array)$object->getData('store_id'); + $isDefaultStore = $this->_storeManager->isSingleStoreMode() + || array_search(Store::DEFAULT_STORE_ID, $stores) !== false; + + if (!$isDefaultStore) { + $stores[] = Store::DEFAULT_STORE_ID; } $select = $this->getConnection()->select() @@ -196,8 +200,11 @@ public function getIsUniqueBlockToStores(AbstractModel $object) 'cb.' . $linkField . ' = cbs.' . $linkField, [] ) - ->where('cb.identifier = ?', $object->getData('identifier')) - ->where('cbs.store_id IN (?)', $stores); + ->where('cb.identifier = ? ', $object->getData('identifier')); + + if (!$isDefaultStore) { + $select->where('cbs.store_id IN (?)', $stores); + } if ($object->getId()) { $select->where('cb.' . $entityMetadata->getIdentifierField() . ' <> ?', $object->getId()); @@ -236,6 +243,8 @@ public function lookupStoreIds($id) } /** + * Save an object. + * * @param AbstractModel $object * @return $this * @throws \Exception diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCreateNewCMSBlockActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCreateNewCMSBlockActionGroup.xml new file mode 100644 index 0000000000000..ce240f9f47e99 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCreateNewCMSBlockActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="FillCMSBlockInformation"> + <arguments> + <argument name="title" type="string" defaultValue="{{DefaultCmsBlock.title}}"/> + <argument name="identifier" type="string" defaultValue="{{DefaultCmsBlock.identifier}}"/> + <argument name="content" type="string" defaultValue="{{DefaultCmsBlock.content}}"/> + </arguments> + <fillField selector="{{AdminCmsBlockContentSection.title}}" userInput="{{title}}" stepKey="fillFieldTitle"/> + <fillField selector="{{AdminCmsBlockContentSection.identifier}}" userInput="{{identifier}}" stepKey="fillFieldIdentifier"/> + <selectOption selector="{{AdminCmsBlockContentSection.storeView}}" userInput="All Store View" stepKey="selectAllStoreView" /> + <fillField selector="{{AdminCmsBlockContentSection.content}}" userInput="{{content}}" stepKey="fillContentField"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminDeleteCMSBlockActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminDeleteCMSBlockActionGroup.xml new file mode 100644 index 0000000000000..b5ff9c3639c8b --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminDeleteCMSBlockActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteCMSBlockActionGroup"> + <arguments> + <argument name="cmsBlockTitle" type="string" defaultValue="{{DefaultCmsBlock.title}}"/> + </arguments> + <amOnPage url="{{AdminCmsBlockGridPage.url}}" stepKey="navigateToCMSPagesGrid"/> + <click selector="{{CmsPagesPageActionsSection.select(cmsBlockTitle)}}" stepKey="clickOnSelect"/> + <click selector="{{CmsPagesPageActionsSection.delete(cmsBlockTitle)}}" stepKey="clickOnDelete"/> + <waitForElementVisible selector="{{AdminConfirmationModalSection.message}}" stepKey="waitForConfirmModal"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirm"/> + <see userInput="You deleted the block." stepKey="VerifyBlockIsDeleted"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/AdminCmsBlockContentSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/AdminCmsBlockContentSection.xml index 9614f13f9e3d3..20e55c49ec235 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/AdminCmsBlockContentSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/AdminCmsBlockContentSection.xml @@ -11,5 +11,8 @@ <section name="AdminCmsBlockContentSection"> <element name="content" type="textarea" selector="#cms_block_form_content"/> <element name="insertWidgetButton" type="button" selector=".scalable.action-add-widget.plugin"/> + <element name="title" type="input" selector="input[name=title]"/> + <element name="identifier" type="input" selector="input[name=identifier]"/> + <element name="storeView" type="multiselect" selector="select[name=store_id]"/> </section> </sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/CheckCreateStaticBlockOnDuplicateIdentifierTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/CheckCreateStaticBlockOnDuplicateIdentifierTest.xml new file mode 100644 index 0000000000000..371e6529590e7 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/CheckCreateStaticBlockOnDuplicateIdentifierTest.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="CheckCreateStaticBlockOnDuplicateIdentifierTest"> + <annotations> + <features value="Cms"/> + <stories value="Create a CMS Block via the Admin"/> + <title value="Check static blocks: ID should be unique per Store View"/> + <description value="Check static blocks: ID should be unique per Store View"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-13912"/> + <group value="Cms"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createSecondWebsite"> + <argument name="newWebsiteName" value="{{SecondWebsite.name}}"/> + <argument name="websiteCode" value="{{SecondWebsite.code}}"/> + </actionGroup> + <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createSecondStoreGroup"> + <argument name="website" value="{{SecondWebsite.name}}"/> + <argument name="storeGroupName" value="{{SecondStoreGroupUnique.name}}"/> + <argument name="storeGroupCode" value="{{SecondStoreGroupUnique.code}}"/> + </actionGroup> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createSecondStoreView"> + <argument name="storeGroup" value="SecondStoreGroupUnique"/> + <argument name="customStore" value="SecondStoreUnique"/> + </actionGroup> + </before> + <after> + <actionGroup ref="DeleteCMSBlockActionGroup" stepKey="deleteCMSBlockActionGroup"/> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> + <argument name="websiteName" value="{{SecondWebsite.name}}"/> + </actionGroup> + </after> + <!--Go to Cms blocks page--> + <amOnPage url="{{AdminCmsBlockGridPage.url}}" stepKey="navigateToCMSBlocksGridPage"/> + <!--Click to create new block--> + <click selector="{{AdminMainActionsSection.add}}" stepKey="addNewBlock"/> + <actionGroup ref="FillCMSBlockInformation" stepKey="FillOutBlockContent"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveBlockButton"/> + <see seelctor="AdminMessagesSection.success" userInput="You saved the block." stepKey="verifyBlockIsSaved"/> + <!--Go to Cms blocks page--> + <amOnPage url="{{AdminCmsBlockGridPage.url}}" stepKey="navigateToCMSBlocksGridPage1"/> + <click selector="{{AdminMainActionsSection.add}}" stepKey="addNewBlock1"/> + <actionGroup ref="FillCMSBlockInformation" stepKey="FillOutBlockContent1"/> + <unselectOption selector="{{AdminCmsBlockContentSection.storeView}}" userInput="All Store Views" stepKey="unselectAllStoreViewOption" /> + <selectOption selector="{{AdminCmsBlockContentSection.storeView}}" parameterArray="[Default Store View, {{SecondStoreUnique.name}}]" stepKey="selectStoreViews" /> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveBlockButton1"/> + <waitForPageLoad stepKey="waitForErrorMessageAppear"/> + <!--Verify that corresponding message is displayed--> + <see selector="{{AdminMessagesSection.error}}" userInput="A block identifier with the same properties already exists in the selected store." stepKey="verifyErrorMessage"/> + </test> +</tests> From 4cb01ef1823358aa9444f348e51e4c65c5bb95b6 Mon Sep 17 00:00:00 2001 From: U1PR01 <nirav@krishtechnolabs.com> Date: Tue, 11 Dec 2018 14:29:57 +0530 Subject: [PATCH 0561/1295] Fixed issue of Lifetime update syntax error --- lib/internal/Magento/Framework/Cache/Backend/Database.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Cache/Backend/Database.php b/lib/internal/Magento/Framework/Cache/Backend/Database.php index 291078383014a..e43c043ee2682 100644 --- a/lib/internal/Magento/Framework/Cache/Backend/Database.php +++ b/lib/internal/Magento/Framework/Cache/Backend/Database.php @@ -432,7 +432,7 @@ public function touch($id, $extraLifetime) return $this->_getConnection()->update( $this->_getDataTable(), ['expire_time' => new \Zend_Db_Expr('expire_time+' . $extraLifetime)], - ['id=?' => $id, 'expire_time = 0 OR expire_time>' => time()] + ['id=?' => $id, 'expire_time = 0 OR expire_time>?' => time()] ); } else { return true; From 5ecc6b2c9f3e205fc41f86b8feecc77a8521fb0f Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Thu, 13 Dec 2018 14:36:16 +0200 Subject: [PATCH 0562/1295] Fix static test. --- lib/internal/Magento/Framework/Cache/Backend/Database.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/internal/Magento/Framework/Cache/Backend/Database.php b/lib/internal/Magento/Framework/Cache/Backend/Database.php index e43c043ee2682..231a8584cc8a5 100644 --- a/lib/internal/Magento/Framework/Cache/Backend/Database.php +++ b/lib/internal/Magento/Framework/Cache/Backend/Database.php @@ -27,11 +27,11 @@ * ) ENGINE=InnoDB DEFAULT CHARSET=utf8; */ -/** - * Database cache backend - */ namespace Magento\Framework\Cache\Backend; +/** + * Database cache backend. + */ class Database extends \Zend_Cache_Backend implements \Zend_Cache_Backend_ExtendedInterface { /** @@ -139,7 +139,7 @@ protected function _getTagsTable() * * Note : return value is always "string" (unserialization is done by the core not by the backend) * - * @param string $id Cache id + * @param string $id Cache id * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested * @return string|false cached datas */ From e0c8a8fb57ccb3ee954a1d4492c8d79060e779ec Mon Sep 17 00:00:00 2001 From: Timon de Groot <timon@marissen.net> Date: Fri, 8 Feb 2019 15:23:10 +0100 Subject: [PATCH 0563/1295] Backport MAGETWO-95819: Customer registration fields not translated --- .../Model/Metadata/AttributeMetadataCache.php | 26 +++++++++++---- .../Metadata/AttributeMetadataCacheTest.php | 33 ++++++++++++++++--- 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Customer/Model/Metadata/AttributeMetadataCache.php b/app/code/Magento/Customer/Model/Metadata/AttributeMetadataCache.php index 5a46fdb9defc4..71f0b393e4a5d 100644 --- a/app/code/Magento/Customer/Model/Metadata/AttributeMetadataCache.php +++ b/app/code/Magento/Customer/Model/Metadata/AttributeMetadataCache.php @@ -12,6 +12,7 @@ use Magento\Framework\App\Cache\StateInterface; use Magento\Framework\App\CacheInterface; use Magento\Framework\Serialize\SerializerInterface; +use Magento\Store\Model\StoreManagerInterface; /** * Cache for attribute metadata @@ -53,6 +54,11 @@ class AttributeMetadataCache */ private $serializer; + /** + * @var StoreManagerInterface + */ + private $storeManager; + /** * Constructor * @@ -60,17 +66,21 @@ class AttributeMetadataCache * @param StateInterface $state * @param SerializerInterface $serializer * @param AttributeMetadataHydrator $attributeMetadataHydrator + * @param StoreManagerInterface|null $storeManager */ public function __construct( CacheInterface $cache, StateInterface $state, SerializerInterface $serializer, - AttributeMetadataHydrator $attributeMetadataHydrator + AttributeMetadataHydrator $attributeMetadataHydrator, + StoreManagerInterface $storeManager = null ) { $this->cache = $cache; $this->state = $state; $this->serializer = $serializer; $this->attributeMetadataHydrator = $attributeMetadataHydrator; + $this->storeManager = $storeManager ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(StoreManagerInterface::class); } /** @@ -82,11 +92,12 @@ public function __construct( */ public function load($entityType, $suffix = '') { - if (isset($this->attributes[$entityType . $suffix])) { - return $this->attributes[$entityType . $suffix]; + $storeId = $this->storeManager->getStore()->getId(); + if (isset($this->attributes[$entityType . $suffix . $storeId])) { + return $this->attributes[$entityType . $suffix . $storeId]; } if ($this->isEnabled()) { - $cacheKey = self::ATTRIBUTE_METADATA_CACHE_PREFIX . $entityType . $suffix; + $cacheKey = self::ATTRIBUTE_METADATA_CACHE_PREFIX . $entityType . $suffix . $storeId; $serializedData = $this->cache->load($cacheKey); if ($serializedData) { $attributesData = $this->serializer->unserialize($serializedData); @@ -94,7 +105,7 @@ public function load($entityType, $suffix = '') foreach ($attributesData as $key => $attributeData) { $attributes[$key] = $this->attributeMetadataHydrator->hydrate($attributeData); } - $this->attributes[$entityType . $suffix] = $attributes; + $this->attributes[$entityType . $suffix . $storeId] = $attributes; return $attributes; } } @@ -111,9 +122,10 @@ public function load($entityType, $suffix = '') */ public function save($entityType, array $attributes, $suffix = '') { - $this->attributes[$entityType . $suffix] = $attributes; + $storeId = $this->storeManager->getStore()->getId(); + $this->attributes[$entityType . $suffix . $storeId] = $attributes; if ($this->isEnabled()) { - $cacheKey = self::ATTRIBUTE_METADATA_CACHE_PREFIX . $entityType . $suffix; + $cacheKey = self::ATTRIBUTE_METADATA_CACHE_PREFIX . $entityType . $suffix . $storeId; $attributesData = []; foreach ($attributes as $key => $attribute) { $attributesData[$key] = $this->attributeMetadataHydrator->extract($attribute); diff --git a/app/code/Magento/Customer/Test/Unit/Model/Metadata/AttributeMetadataCacheTest.php b/app/code/Magento/Customer/Test/Unit/Model/Metadata/AttributeMetadataCacheTest.php index 658472d13ab93..d93ed3c7b351a 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Metadata/AttributeMetadataCacheTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Metadata/AttributeMetadataCacheTest.php @@ -15,7 +15,14 @@ use Magento\Framework\App\CacheInterface; use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreManagerInterface; +/** + * Class AttributeMetadataCache Test + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class AttributeMetadataCacheTest extends \PHPUnit\Framework\TestCase { /** @@ -43,6 +50,16 @@ class AttributeMetadataCacheTest extends \PHPUnit\Framework\TestCase */ private $attributeMetadataCache; + /** + * @var StoreInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $storeMock; + + /** + * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $storeManagerMock; + protected function setUp() { $objectManager = new ObjectManager($this); @@ -50,13 +67,18 @@ protected function setUp() $this->stateMock = $this->createMock(StateInterface::class); $this->serializerMock = $this->createMock(SerializerInterface::class); $this->attributeMetadataHydratorMock = $this->createMock(AttributeMetadataHydrator::class); + $this->storeMock = $this->createMock(StoreInterface::class); + $this->storeManagerMock = $this->createMock(StoreManagerInterface::class); + $this->storeManagerMock->method('getStore')->willReturn($this->storeMock); + $this->storeMock->method('getId')->willReturn(1); $this->attributeMetadataCache = $objectManager->getObject( AttributeMetadataCache::class, [ 'cache' => $this->cacheMock, 'state' => $this->stateMock, 'serializer' => $this->serializerMock, - 'attributeMetadataHydrator' => $this->attributeMetadataHydratorMock + 'attributeMetadataHydrator' => $this->attributeMetadataHydratorMock, + 'storeManager' => $this->storeManagerMock ] ); } @@ -80,7 +102,8 @@ public function testLoadNoCache() { $entityType = 'EntityType'; $suffix = 'none'; - $cacheKey = AttributeMetadataCache::ATTRIBUTE_METADATA_CACHE_PREFIX . $entityType . $suffix; + $storeId = 1; + $cacheKey = AttributeMetadataCache::ATTRIBUTE_METADATA_CACHE_PREFIX . $entityType . $suffix . $storeId; $this->stateMock->expects($this->once()) ->method('isEnabled') ->with(Type::TYPE_IDENTIFIER) @@ -96,7 +119,8 @@ public function testLoad() { $entityType = 'EntityType'; $suffix = 'none'; - $cacheKey = AttributeMetadataCache::ATTRIBUTE_METADATA_CACHE_PREFIX . $entityType . $suffix; + $storeId = 1; + $cacheKey = AttributeMetadataCache::ATTRIBUTE_METADATA_CACHE_PREFIX . $entityType . $suffix . $storeId; $serializedString = 'serialized string'; $attributeMetadataOneData = [ 'attribute_code' => 'attribute_code', @@ -156,7 +180,8 @@ public function testSave() { $entityType = 'EntityType'; $suffix = 'none'; - $cacheKey = AttributeMetadataCache::ATTRIBUTE_METADATA_CACHE_PREFIX . $entityType . $suffix; + $storeId = 1; + $cacheKey = AttributeMetadataCache::ATTRIBUTE_METADATA_CACHE_PREFIX . $entityType . $suffix . $storeId; $serializedString = 'serialized string'; $attributeMetadataOneData = [ 'attribute_code' => 'attribute_code', From 9d858b3b8c9e30b7414d7c85fa271dc310feed0b Mon Sep 17 00:00:00 2001 From: NazarKlovanych <nazarn96@gmail.com> Date: Fri, 8 Feb 2019 16:26:29 +0200 Subject: [PATCH 0564/1295] fix-issue21073 --- .../Edit/Action/Attribute/Tab/Inventory.php | 29 +++++++++++++++++-- .../product/edit/action/inventory.phtml | 2 +- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Inventory.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Inventory.php index 4aebd521fe60d..d11a6d632657f 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Inventory.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Inventory.php @@ -5,6 +5,8 @@ */ namespace Magento\Catalog\Block\Adminhtml\Product\Edit\Action\Attribute\Tab; +use Magento\Customer\Api\Data\GroupInterface; + /** * Products mass update inventory tab * @@ -29,6 +31,11 @@ class Inventory extends \Magento\Backend\Block\Widget implements \Magento\Backen */ protected $disabledFields = []; + /** + * @var \Magento\Framework\Serialize\SerializerInterface + */ + private $serializer; + /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\CatalogInventory\Model\Source\Backorders $backorders @@ -39,10 +46,13 @@ public function __construct( \Magento\Backend\Block\Template\Context $context, \Magento\CatalogInventory\Model\Source\Backorders $backorders, \Magento\CatalogInventory\Api\StockConfigurationInterface $stockConfiguration, - array $data = [] + array $data = [], + \Magento\Framework\Serialize\SerializerInterface $serializer = null ) { $this->_backorders = $backorders; $this->stockConfiguration = $stockConfiguration; + $this->serializer = $serializer ?? \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Serialize\SerializerInterface::class); parent::__construct($context, $data); } @@ -74,7 +84,7 @@ public function getFieldSuffix() public function getStoreId() { $storeId = $this->getRequest()->getParam('store'); - return (int)$storeId; + return (int) $storeId; } /** @@ -88,6 +98,21 @@ public function getDefaultConfigValue($field) return $this->stockConfiguration->getDefaultConfigValue($field); } + /** + * Returns min_sale_qty configuration for the ALL Customer Group + * @return int + */ + public function getDefaultMinSaleQty() + { + $default = $this->stockConfiguration->getDefaultConfigValue('min_sale_qty'); + if (!is_numeric($default)) { + $default = $this->serializer->unserialize($default); + $default = isset($default[GroupInterface::CUST_GROUP_ALL]) ? $default[GroupInterface::CUST_GROUP_ALL] : 1; + } + + return (int) $default; + } + /** * Tab settings * diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/inventory.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/inventory.phtml index efc06d675c369..96e07ceb4d305 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/inventory.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/inventory.phtml @@ -132,7 +132,7 @@ <div class="field"> <input type="text" class="input-text validate-number" id="inventory_min_sale_qty" name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[min_sale_qty]" - value="<?= /* @escapeNotVerified */ $block->getDefaultConfigValue('min_sale_qty') * 1 ?>" + value="<?= /* @escapeNotVerified */ $block->getDefaultMinSaleQty() * 1 ?>" disabled="disabled"/> </div> <div class="field choice"> From 9d0f7691131080b51b8da3ef06fff015ff37e023 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 8 Feb 2019 17:27:51 +0200 Subject: [PATCH 0565/1295] MAGETWO-73534: [GITHUB] Url rewrite for product is broken after using massaction #8227 --- .../Page/AdminProductUpdateAttributesPage.xml | 14 ++++ .../Section/AdminProductFiltersSection.xml | 3 +- .../Mftf/Section/AdminProductSEOSection.xml | 1 + .../Section/AdminUpdateAttributesSection.xml | 17 +++++ .../ProductToWebsiteChangeObserver.php | 17 +++-- ...minUrlForProductRewrittenCorrectlyTest.xml | 72 +++++++++++++++++++ 6 files changed, 117 insertions(+), 7 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Page/AdminProductUpdateAttributesPage.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection.xml create mode 100644 app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductUpdateAttributesPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductUpdateAttributesPage.xml new file mode 100644 index 0000000000000..84996a3814571 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductUpdateAttributesPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminProductUpdateAttributesPage" url="catalog/product_action_attribute/edit/" area="admin" module="Magento_Catalog"> + <section name="AdminUpdateAttributesHeaderSection"/> + <section name="AdminUpdateAttributesWebsiteSection"/> + </page> +</pages> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFiltersSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFiltersSection.xml index 6844006e4e399..8e13f9c38f805 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFiltersSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFiltersSection.xml @@ -7,12 +7,13 @@ --> <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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductFiltersSection"> <element name="FiltersButton" type="button" selector="#container > div > div.admin__data-grid-header > div:nth-child(1) > div.data-grid-filters-actions-wrap > div > button"/> <element name="clearFiltersButton" type="button" selector="//div[@class='admin__data-grid-header']//button[@class='action-tertiary action-clear']" timeout="10"/> <element name="NameInput" type="input" selector="input[name=name]"/> <element name="SkuInput" type="input" selector="input[name=sku]"/> <element name="Apply" type="button" selector="button[data-action=grid-filter-apply]" timeout="30"/> + <element name="allCheckbox" type="checkbox" selector="div[data-role='grid-wrapper'] label[data-bind='attr: {for: ko.uid}']" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductSEOSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductSEOSection.xml index 1d49d05363612..90c3856933be9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductSEOSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductSEOSection.xml @@ -11,5 +11,6 @@ <section name="AdminProductSEOSection"> <element name="sectionHeader" type="button" selector="div[data-index='search-engine-optimization']" timeout="30"/> <element name="urlKeyInput" type="input" selector="input[name='product[url_key]']"/> + <element name="useDefaultUrl" type="checkbox" selector="input[name='use_default[url_key]']"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection.xml new file mode 100644 index 0000000000000..051fda092d151 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminUpdateAttributesSection.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminUpdateAttributesHeaderSection"> + <element name="saveButton" type="button" selector="button[data-ui-id='page-actions-toolbar-save-button']" timeout="30"/> + </section> + <section name="AdminUpdateAttributesWebsiteSection"> + <element name="website" type="button" selector="#attributes_update_tabs_websites"/> + <element name="addProductToWebsite" type="checkbox" selector="#add-products-to-website-content .website-checkbox"/> + </section> +</sections> diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/ProductToWebsiteChangeObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/ProductToWebsiteChangeObserver.php index fc2056e83ec70..94798753ca63f 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/ProductToWebsiteChangeObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/ProductToWebsiteChangeObserver.php @@ -14,6 +14,9 @@ use Magento\UrlRewrite\Model\UrlPersistInterface; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; +/** + * Observer to assign the products to website. + */ class ProductToWebsiteChangeObserver implements ObserverInterface { /** @@ -69,12 +72,14 @@ public function execute(\Magento\Framework\Event\Observer $observer) $this->request->getParam('store_id', Store::DEFAULT_STORE_ID) ); - $this->urlPersist->deleteByData([ - UrlRewrite::ENTITY_ID => $product->getId(), - UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, - ]); - if ($product->getVisibility() != Visibility::VISIBILITY_NOT_VISIBLE) { - $this->urlPersist->replace($this->productUrlRewriteGenerator->generate($product)); + if (!empty($this->productUrlRewriteGenerator->generate($product))) { + $this->urlPersist->deleteByData([ + UrlRewrite::ENTITY_ID => $product->getId(), + UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, + ]); + if ($product->getVisibility() != Visibility::VISIBILITY_NOT_VISIBLE) { + $this->urlPersist->replace($this->productUrlRewriteGenerator->generate($product)); + } } } } diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml new file mode 100644 index 0000000000000..558e19885e112 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUrlForProductRewrittenCorrectlyTest"> + <annotations> + <features value="CatalogUrlRewrite"/> + <stories value="Url rewrites for products"/> + <title value="Check that URL for product rewritten correctly"/> + <description value="Check that URL for product rewritten correctly"/> + <severity value="MAJOR"/> + <testCaseId value="MC-13913"/> + <useCaseId value="MAGETWO-73534"/> + <group value="catalog"/> + <group value="catalogUrlRewrite"/> + </annotations> + <before> + <!--Create product--> + <createData entity="_defaultCategory" stepKey="category"/> + <createData entity="ApiSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="category"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <!--Delete created data--> + <deleteData createDataKey="createProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="category" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + + <!--Open Created product--> + <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="openProductEditPage"/> + <!--Switch to Default Store view--> + <actionGroup ref="SwitchToTheNewStoreView" stepKey="selectDefaultStoreView"> + <argument name="storeViewName" value="_defaultStore"/> + </actionGroup> + + <!--Set use default url--> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSearchEngineOptimizationTab"/> + <waitForElementVisible selector="{{AdminProductSEOSection.useDefaultUrl}}" time="30" stepKey="waitForUseDefaultUrlCheckbox"/> + <uncheckOption selector="{{AdminProductSEOSection.useDefaultUrl}}" stepKey="uncheckUseDefaultUrlCheckbox"/> + <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="$$createProduct.custom_attributes[url_key]$$-updated" stepKey="changeUrlKey"/> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + + <!--Select product and go toUpdate Attribute page--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="openProductsGrid"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterGridBySku"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + <click selector="{{AdminProductFiltersSection.allCheckbox}}" stepKey="selectFilteredProduct"/> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Update attributes')}}" stepKey="clickBulkUpdateAttributes"/> + <waitForPageLoad stepKey="waitForUpdateAttributesPageLoad"/> + <seeInCurrentUrl url="{{AdminProductUpdateAttributesPage.url}}" stepKey="seeInUrlAttributeUpdatePage"/> + <click selector="{{AdminUpdateAttributesWebsiteSection.website}}" stepKey="openWebsitesTab"/> + <waitForAjaxLoad stepKey="waitForLoadWebSiteTab"/> + <click selector="{{AdminUpdateAttributesWebsiteSection.addProductToWebsite}}" stepKey="checkAddProductToWebsiteCheckbox"/> + <click selector="{{AdminUpdateAttributesHeaderSection.saveButton}}" stepKey="clickSave"/> + <see selector="{{AdminMessagesSection.success}}" userInput="A total of 1 record(s) were updated." stepKey="seeSaveSuccessMessage"/> + <!--Got to Store front product page and check url--> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$-updated)}}" stepKey="navigateToSimpleProductPage"/> + <seeInCurrentUrl url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$-updated)}}" stepKey="seeProductNewUrl"/> + <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="$$createProduct.sku$$" stepKey="seeCorrectSku"/> + </test> +</tests> From e66aee3c3f230101ae5ddca26e9b6086143fccfe Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@adobe.com> Date: Wed, 6 Feb 2019 12:30:50 -0600 Subject: [PATCH 0566/1295] MQE-1430: bug fix in dev/tests/functional/utils/command.php etc --- .../lib/Magento/Mtf/Util/Command/Cli.php | 41 ++++++++---- .../Mtf/Util/Command/File/Export/Reader.php | 38 ++++++++--- .../Command/File/Export/ReaderInterface.php | 2 +- .../lib/Magento/Mtf/Util/Command/File/Log.php | 38 +++++++---- .../Mtf/Util/Command/GeneratedCode.php | 39 +++++++++-- .../lib/Magento/Mtf/Util/Command/Locales.php | 42 +++++++++--- .../Magento/Mtf/Util/Command/PathChecker.php | 43 +++++++++--- .../lib/Magento/Mtf/Util/Command/Website.php | 40 ++++++++---- .../CurlTransport/BackendDecorator.php | 65 ++++++++++++++----- .../CurlTransport/WebapiDecorator.php | 32 ++++++++- dev/tests/functional/utils/authenticate.php | 29 +++++++++ dev/tests/functional/utils/command.php | 26 ++++---- .../utils/deleteMagentoGeneratedCode.php | 11 +++- dev/tests/functional/utils/export.php | 39 ++++++----- dev/tests/functional/utils/locales.php | 26 +++++--- dev/tests/functional/utils/log.php | 24 ++++--- dev/tests/functional/utils/pathChecker.php | 17 +++-- dev/tests/functional/utils/website.php | 39 ++++++----- 18 files changed, 435 insertions(+), 156 deletions(-) create mode 100644 dev/tests/functional/utils/authenticate.php diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php index 8fa22122cce89..f0abd280f3ebc 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php @@ -8,6 +8,7 @@ use Magento\Mtf\Util\Protocol\CurlInterface; use Magento\Mtf\Util\Protocol\CurlTransport; +use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * Perform bin/magento commands from command line for functional tests executions. @@ -17,7 +18,7 @@ class Cli /** * Url to command.php. */ - const URL = 'dev/tests/functional/utils/command.php'; + const URL = '/dev/tests/functional/utils/command.php'; /** * Curl transport protocol. @@ -26,12 +27,21 @@ class Cli */ private $transport; + /** + * Webapi handler. + * + * @var WebapiDecorator + */ + private $webapiHandler; + /** * @param CurlTransport $transport + * @param WebapiDecorator $webapiHandler */ - public function __construct(CurlTransport $transport) + public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler) { $this->transport = $transport; + $this->webapiHandler = $webapiHandler; } /** @@ -43,22 +53,31 @@ public function __construct(CurlTransport $transport) */ public function execute($command, $options = []) { - $curl = $this->transport; - $curl->write($this->prepareUrl($command, $options), [], CurlInterface::GET); - $curl->read(); - $curl->close(); + $this->transport->write( + rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, + $this->prepareParamArray($command, $options), + CurlInterface::POST, + [] + ); + $this->transport->read(); + $this->transport->close(); } /** - * Prepare url. + * Prepare parameter array. * * @param string $command * @param array $options [optional] - * @return string + * @return array */ - private function prepareUrl($command, $options = []) + private function prepareParamArray($command, $options = []) { - $command .= ' ' . implode(' ', $options); - return $_ENV['app_frontend_url'] . self::URL . '?command=' . urlencode($command); + if (!empty($options)) { + $command .= ' ' . implode(' ', $options); + } + return [ + 'token' => urlencode($this->webapiHandler->getWebapiToken()), + 'command' => urlencode($command) + ]; } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/Reader.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/Reader.php index 1c05fbaebf625..69df78a5cad64 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/Reader.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/Reader.php @@ -3,12 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Mtf\Util\Command\File\Export; use Magento\Mtf\ObjectManagerInterface; use Magento\Mtf\Util\Protocol\CurlTransport; use Magento\Mtf\Util\Protocol\CurlInterface; +use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * File reader for Magento export files. @@ -36,16 +36,29 @@ class Reader implements ReaderInterface */ private $transport; + /** + * Webapi handler. + * + * @var WebapiDecorator + */ + private $webapiHandler; + /** * @param ObjectManagerInterface $objectManager * @param CurlTransport $transport + * @param WebapiDecorator $webapiHandler * @param string $template */ - public function __construct(ObjectManagerInterface $objectManager, CurlTransport $transport, $template) - { + public function __construct( + ObjectManagerInterface $objectManager, + CurlTransport $transport, + WebapiDecorator $webapiHandler, + $template + ) { $this->objectManager = $objectManager; $this->template = $template; $this->transport = $transport; + $this->webapiHandler = $webapiHandler; } /** @@ -70,20 +83,27 @@ public function getData() */ private function getFiles() { - $this->transport->write($this->prepareUrl(), [], CurlInterface::GET); + $this->transport->write( + rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, + $this->prepareParamArray(), + CurlInterface::POST, + [] + ); $serializedFiles = $this->transport->read(); $this->transport->close(); - return unserialize($serializedFiles); } /** - * Prepare url. + * Prepare parameter array. * - * @return string + * @return array */ - private function prepareUrl() + private function prepareParamArray() { - return $_ENV['app_frontend_url'] . self::URL . '?template=' . urlencode($this->template); + return [ + 'token' => urlencode($this->webapiHandler->getWebapiToken()), + 'template' => urlencode($this->template) + ]; } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/ReaderInterface.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/ReaderInterface.php index 93f7cf1ce9764..3666e8643efa3 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/ReaderInterface.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/ReaderInterface.php @@ -14,7 +14,7 @@ interface ReaderInterface /** * Url to export.php. */ - const URL = 'dev/tests/functional/utils/export.php'; + const URL = '/dev/tests/functional/utils/export.php'; /** * Exporting files as Data object from Magento. diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Log.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Log.php index 8b41924fe0a90..820a5b0a82228 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Log.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Log.php @@ -7,6 +7,7 @@ namespace Magento\Mtf\Util\Command\File; use Magento\Mtf\Util\Protocol\CurlTransport; +use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * Get content of log file in var/log folder. @@ -16,7 +17,7 @@ class Log /** * Url to log.php. */ - const URL = 'dev/tests/functional/utils/log.php'; + const URL = '/dev/tests/functional/utils/log.php'; /** * Curl transport protocol. @@ -25,12 +26,21 @@ class Log */ private $transport; + /** + * Webapi handler. + * + * @var WebapiDecorator + */ + private $webapiHandler; + /** * @param CurlTransport $transport + * @param WebapiDecorator $webapiHandler */ - public function __construct(CurlTransport $transport) + public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler) { $this->transport = $transport; + $this->webapiHandler = $webapiHandler; } /** @@ -41,22 +51,28 @@ public function __construct(CurlTransport $transport) */ public function getFileContent($name) { - $curl = $this->transport; - $curl->write($this->prepareUrl($name), [], CurlTransport::GET); - $data = $curl->read(); - $curl->close(); - + $this->transport->write( + rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, + $this->prepareParamArray($name), + CurlInterface::POST, + [] + ); + $data = $this->transport->read(); + $this->transport->close(); return unserialize($data); } /** - * Prepare url. + * Prepare parameter array. * * @param string $name - * @return string + * @return array */ - private function prepareUrl($name) + private function prepareParamArray($name) { - return $_ENV['app_frontend_url'] . self::URL . '?name=' . urlencode($name); + return [ + 'token' => urlencode($this->webapiHandler->getWebapiToken()), + 'name' => urlencode($name) + ]; } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/GeneratedCode.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/GeneratedCode.php index dde3409ed1562..a9fefa25ffa24 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/GeneratedCode.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/GeneratedCode.php @@ -7,6 +7,7 @@ use Magento\Mtf\Util\Protocol\CurlInterface; use Magento\Mtf\Util\Protocol\CurlTransport; +use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * GeneratedCode removes generated code of Magento (like generated/code and generated/metadata). @@ -16,7 +17,7 @@ class GeneratedCode /** * Url to deleteMagentoGeneratedCode.php. */ - const URL = 'dev/tests/functional/utils/deleteMagentoGeneratedCode.php'; + const URL = '/dev/tests/functional/utils/deleteMagentoGeneratedCode.php'; /** * Curl transport protocol. @@ -25,12 +26,21 @@ class GeneratedCode */ private $transport; + /** + * Webapi handler. + * + * @var WebapiDecorator + */ + private $webapiHandler; + /** * @param CurlTransport $transport + * @param WebapiDecorator $webapiHandler */ - public function __construct(CurlTransport $transport) + public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler) { $this->transport = $transport; + $this->webapiHandler = $webapiHandler; } /** @@ -40,10 +50,25 @@ public function __construct(CurlTransport $transport) */ public function delete() { - $url = $_ENV['app_frontend_url'] . self::URL; - $curl = $this->transport; - $curl->write($url, [], CurlInterface::GET); - $curl->read(); - $curl->close(); + $this->transport->write( + rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, + $this->prepareParamArray(), + CurlInterface::POST, + [] + ); + $this->transport->read(); + $this->transport->close(); + } + + /** + * Prepare parameter array. + * + * @return array + */ + private function prepareParamArray() + { + return [ + 'token' => urlencode($this->webapiHandler->getWebapiToken()) + ]; } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Locales.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Locales.php index f669d91f2f2e5..a55d803f43087 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Locales.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Locales.php @@ -7,6 +7,7 @@ use Magento\Mtf\Util\Protocol\CurlInterface; use Magento\Mtf\Util\Protocol\CurlTransport; +use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * Returns array of locales depends on fetching type. @@ -26,7 +27,7 @@ class Locales /** * Url to locales.php. */ - const URL = 'dev/tests/functional/utils/locales.php'; + const URL = '/dev/tests/functional/utils/locales.php'; /** * Curl transport protocol. @@ -35,12 +36,21 @@ class Locales */ private $transport; + /** + * Webapi handler. + * + * @var WebapiDecorator + */ + private $webapiHandler; + /** * @param CurlTransport $transport Curl transport protocol + * @param WebapiDecorator $webapiHandler */ - public function __construct(CurlTransport $transport) + public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler) { $this->transport = $transport; + $this->webapiHandler = $webapiHandler; } /** @@ -51,12 +61,28 @@ public function __construct(CurlTransport $transport) */ public function getList($type = self::TYPE_ALL) { - $url = $_ENV['app_frontend_url'] . self::URL . '?type=' . $type; - $curl = $this->transport; - $curl->write($url, [], CurlInterface::GET); - $result = $curl->read(); - $curl->close(); - + $this->transport->write( + rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, + $this->prepareParamArray($type), + CurlInterface::POST, + [] + ); + $result = $this->transport->read(); + $this->transport->close(); return explode('|', $result); } + + /** + * Prepare parameter array. + * + * @param string $type + * @return array + */ + private function prepareParamArray($type) + { + return [ + 'token' => urlencode($this->webapiHandler->getWebapiToken()), + 'type' => urlencode($type) + ]; + } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/PathChecker.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/PathChecker.php index fd1f746a6f09c..4b12f6eec87aa 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/PathChecker.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/PathChecker.php @@ -7,6 +7,7 @@ use Magento\Mtf\Util\Protocol\CurlInterface; use Magento\Mtf\Util\Protocol\CurlTransport; +use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * PathChecker checks that path to file or directory exists. @@ -16,7 +17,7 @@ class PathChecker /** * Url to checkPath.php. */ - const URL = 'dev/tests/functional/utils/pathChecker.php'; + const URL = '/dev/tests/functional/utils/pathChecker.php'; /** * Curl transport protocol. @@ -26,11 +27,21 @@ class PathChecker private $transport; /** + * Webapi handler. + * + * @var WebapiDecorator + */ + private $webapiHandler; + + /** + * @constructor * @param CurlTransport $transport + * @param WebapiDecorator $webapiHandler */ - public function __construct(CurlTransport $transport) + public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler) { $this->transport = $transport; + $this->webapiHandler = $webapiHandler; } /** @@ -41,12 +52,28 @@ public function __construct(CurlTransport $transport) */ public function pathExists($path) { - $url = $_ENV['app_frontend_url'] . self::URL . '?path=' . urlencode($path); - $curl = $this->transport; - $curl->write($url, [], CurlInterface::GET); - $result = $curl->read(); - $curl->close(); - + $this->transport->write( + rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, + $this->prepareParamArray($path), + CurlInterface::POST, + [] + ); + $result = $this->transport->read(); + $this->transport->close(); return strpos($result, 'path exists: true') !== false; } + + /** + * Prepare parameter array. + * + * @param string $path + * @return array + */ + private function prepareParamArray($path) + { + return [ + 'token' => urlencode($this->webapiHandler->getWebapiToken()), + 'path' => urlencode($path) + ]; + } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Website.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Website.php index 7d73634c0360d..fec20bb2a8715 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Website.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Website.php @@ -3,11 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Mtf\Util\Command; use Magento\Mtf\Util\Protocol\CurlInterface; use Magento\Mtf\Util\Protocol\CurlTransport; +use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * Perform Website folder creation for functional tests executions. @@ -17,7 +17,7 @@ class Website /** * Url to website.php. */ - const URL = 'dev/tests/functional/utils/website.php'; + const URL = '/dev/tests/functional/utils/website.php'; /** * Curl transport protocol. @@ -26,13 +26,22 @@ class Website */ private $transport; + /** + * Webapi handler. + * + * @var WebapiDecorator + */ + private $webapiHandler; + /** * @constructor * @param CurlTransport $transport + * @param WebapiDecorator $webapiHandler */ - public function __construct(CurlTransport $transport) + public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler) { $this->transport = $transport; + $this->webapiHandler = $webapiHandler; } /** @@ -43,21 +52,28 @@ public function __construct(CurlTransport $transport) */ public function create($websiteCode) { - $curl = $this->transport; - $curl->addOption(CURLOPT_HEADER, 1); - $curl->write($this->prepareUrl($websiteCode), [], CurlInterface::GET); - $curl->read(); - $curl->close(); + $this->transport->addOption(CURLOPT_HEADER, 1); + $this->transport->write( + rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, + $this->prepareParamArray($websiteCode), + CurlInterface::POST, + [] + ); + $this->transport->read(); + $this->transport->close(); } /** - * Prepare url. + * Prepare parameter array. * * @param string $websiteCode - * @return string + * @return array */ - private function prepareUrl($websiteCode) + private function prepareParamArray($websiteCode) { - return $_ENV['app_frontend_url'] . self::URL . '?website_code=' . urlencode($websiteCode); + return [ + 'token' => urlencode($this->webapiHandler->getWebapiToken()), + 'website_code' => urlencode($websiteCode) + ]; } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php index ab333dc7c005a..1126cd29727fc 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php @@ -63,23 +63,54 @@ public function __construct(CurlTransport $transport, DataInterface $configurati */ protected function authorize() { - // Perform GET to backend url so form_key is set - $url = $_ENV['app_backend_url']; - $this->transport->write($url, [], CurlInterface::GET); - $this->read(); - - $url = $_ENV['app_backend_url'] . $this->configuration->get('application/0/backendLoginUrl/0/value'); - $data = [ - 'login[username]' => $this->configuration->get('application/0/backendLogin/0/value'), - 'login[password]' => $this->configuration->get('application/0/backendPassword/0/value'), - 'form_key' => $this->formKey, - ]; - $this->transport->write($url, $data, CurlInterface::POST); - $response = $this->read(); - if (strpos($response, 'login-form')) { - throw new \Exception( - 'Admin user cannot be logged in by curl handler!' - ); + // There are situations where magento application backend url could be slightly different from the environment + // variable we know. It could be intentionally (e.g. InstallTest) or unintentionally. We would still want tests + // to run in this case. + // When the original app_backend_url does not work, we will try 4 variants of the it. i.e. with and without + // url rewrite, http and https. + $urls = []; + $originalUrl = rtrim($_ENV['app_backend_url'], '/') . '/'; + $urls[] = $originalUrl; + if (strpos($originalUrl, '/index.php') !== false) { + $url2 = str_replace('/index.php', '', $originalUrl); + } else { + $url2 = $originalUrl . 'index.php/'; + } + $urls[] = $url2; + if (strpos($originalUrl, 'https') !== false) { + $urls[] = str_replace('https', 'http', $originalUrl); + } else { + $urls[] = str_replace('http', 'https', $url2); + } + + $isAuthorized = false; + foreach ($urls as $url) { + try { + // Perform GET to backend url so form_key is set + $this->transport->write($url, [], CurlInterface::GET); + $this->read(); + + $authUrl = $url . $this->configuration->get('application/0/backendLoginUrl/0/value'); + $data = [ + 'login[username]' => $this->configuration->get('application/0/backendLogin/0/value'), + 'login[password]' => $this->configuration->get('application/0/backendPassword/0/value'), + 'form_key' => $this->formKey, + ]; + + $this->transport->write($authUrl, $data, CurlInterface::POST); + $response = $this->read(); + if (strpos($response, 'login-form')) { + continue; + } + $isAuthorized = true; + $_ENV['app_backend_url'] = $url; + break; + } catch (\Exception $e) { + continue; + } + } + if ($isAuthorized == false) { + throw new \Exception('Admin user cannot be logged in by curl handler!'); } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/WebapiDecorator.php b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/WebapiDecorator.php index 3aa756904ab00..df5ab45a3f96d 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/WebapiDecorator.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/WebapiDecorator.php @@ -70,6 +70,13 @@ class WebapiDecorator implements CurlInterface */ protected $response; + /** + * Webapi token. + * + * @var string + */ + protected $webapiToken; + /** * @construct * @param ObjectManager $objectManager @@ -110,6 +117,9 @@ protected function init() $integration->persist(); $this->setConfiguration($integration); + $this->webapiToken = $integration->getToken(); + } else { + $this->webapiToken = $integrationToken; } } @@ -161,7 +171,13 @@ protected function setConfiguration(Integration $integration) */ protected function isValidIntegration() { - $this->write($_ENV['app_frontend_url'] . 'rest/V1/modules', [], CurlInterface::GET); + $url = rtrim($_ENV['app_frontend_url'], '/'); + if (strpos($url, 'index.php') === false) { + $url .= '/index.php/rest/V1/modules'; + } else { + $url .= '/rest/V1/modules'; + } + $this->write($url, [], CurlInterface::GET); $response = json_decode($this->read(), true); return (null !== $response) && !isset($response['message']); @@ -219,4 +235,18 @@ public function close() { $this->transport->close(); } + + /** + * Return webapiToken. + * + * @return string + */ + public function getWebapiToken() + { + // Request token if integration is no longer valid + if (!$this->isValidIntegration()) { + $this->init(); + } + return $this->webapiToken; + } } diff --git a/dev/tests/functional/utils/authenticate.php b/dev/tests/functional/utils/authenticate.php new file mode 100644 index 0000000000000..15851f6e8000a --- /dev/null +++ b/dev/tests/functional/utils/authenticate.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** + * Check if token passed in is a valid auth token. + * + * @param string $token + * @return bool + */ +function authenticate($token) +{ + require_once __DIR__ . '/../../../../app/bootstrap.php'; + + $magentoObjectManagerFactory = \Magento\Framework\App\Bootstrap::createObjectManagerFactory(BP, $_SERVER); + $magentoObjectManager = $magentoObjectManagerFactory->create($_SERVER); + $tokenModel = $magentoObjectManager->get(\Magento\Integration\Model\Oauth\Token::class); + + $tokenPassedIn = $token; + // Token returned will be null if the token we passed in is invalid + $tokenFromMagento = $tokenModel->loadByToken($tokenPassedIn)->getToken(); + if (!empty($tokenFromMagento) && ($tokenFromMagento == $tokenPassedIn)) { + return true; + } else { + return false; + } +} diff --git a/dev/tests/functional/utils/command.php b/dev/tests/functional/utils/command.php index 8eaf82475a4e4..e7b336464682d 100644 --- a/dev/tests/functional/utils/command.php +++ b/dev/tests/functional/utils/command.php @@ -3,21 +3,25 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - +include __DIR__ . '/authenticate.php'; require_once __DIR__ . '/../../../../app/bootstrap.php'; use Symfony\Component\Console\Input\StringInput; use Symfony\Component\Console\Output\NullOutput; -if (isset($_GET['command'])) { - $command = urldecode($_GET['command']); - $magentoObjectManagerFactory = \Magento\Framework\App\Bootstrap::createObjectManagerFactory(BP, $_SERVER); - $magentoObjectManager = $magentoObjectManagerFactory->create($_SERVER); - $cli = $magentoObjectManager->create(\Magento\Framework\Console\Cli::class); - $input = new StringInput($command); - $input->setInteractive(false); - $output = new NullOutput(); - $cli->doRun($input, $output); +if (!empty($_POST['token']) && !empty($_POST['command'])) { + if (authenticate(urldecode($_POST['token']))) { + $command = urldecode($_POST['command']); + $magentoObjectManagerFactory = \Magento\Framework\App\Bootstrap::createObjectManagerFactory(BP, $_SERVER); + $magentoObjectManager = $magentoObjectManagerFactory->create($_SERVER); + $cli = $magentoObjectManager->create(\Magento\Framework\Console\Cli::class); + $input = new StringInput($command); + $input->setInteractive(false); + $output = new NullOutput(); + $cli->doRun($input, $output); + } else { + echo "Command not unauthorized."; + } } else { - throw new \InvalidArgumentException("Command GET parameter is not set."); + echo "'token' or 'command' parameter is not set."; } diff --git a/dev/tests/functional/utils/deleteMagentoGeneratedCode.php b/dev/tests/functional/utils/deleteMagentoGeneratedCode.php index 99aa9af06e92a..17e3575c87686 100644 --- a/dev/tests/functional/utils/deleteMagentoGeneratedCode.php +++ b/dev/tests/functional/utils/deleteMagentoGeneratedCode.php @@ -3,5 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +include __DIR__ . '/authenticate.php'; -exec('rm -rf ../../../../generated/*'); +if (!empty($_POST['token']) && !empty($_POST['path'])) { + if (authenticate(urldecode($_POST['token']))) { + exec('rm -rf ../../../../generated/*'); + } else { + echo "Command not unauthorized."; + } +} else { + echo "'token' parameter is not set."; +} diff --git a/dev/tests/functional/utils/export.php b/dev/tests/functional/utils/export.php index 343dcc557c832..9357bfa459be0 100644 --- a/dev/tests/functional/utils/export.php +++ b/dev/tests/functional/utils/export.php @@ -3,25 +3,30 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +include __DIR__ . '/authenticate.php'; -if (!isset($_GET['template'])) { - throw new \InvalidArgumentException('Argument "template" must be set.'); -} +if (!empty($_POST['token']) && !empty($_POST['template'])) { + if (authenticate(urldecode($_POST['token']))) { + $varDir = '../../../../var/'; + $template = urldecode($_POST['template']); + $fileList = scandir($varDir, SCANDIR_SORT_NONE); + $files = []; -$varDir = '../../../../var/'; -$template = urldecode($_GET['template']); -$fileList = scandir($varDir, SCANDIR_SORT_NONE); -$files = []; + foreach ($fileList as $fileName) { + if (preg_match("`$template`", $fileName) === 1) { + $filePath = $varDir . $fileName; + $files[] = [ + 'content' => file_get_contents($filePath), + 'name' => $fileName, + 'date' => filectime($filePath), + ]; + } + } -foreach ($fileList as $fileName) { - if (preg_match("`$template`", $fileName) === 1) { - $filePath = $varDir . $fileName; - $files[] = [ - 'content' => file_get_contents($filePath), - 'name' => $fileName, - 'date' => filectime($filePath), - ]; + echo serialize($files); + } else { + echo "Command not unauthorized."; } +} else { + echo "'token' or 'template' parameter is not set."; } - -echo serialize($files); diff --git a/dev/tests/functional/utils/locales.php b/dev/tests/functional/utils/locales.php index 827b8b1b89448..a3b4ec05eed65 100644 --- a/dev/tests/functional/utils/locales.php +++ b/dev/tests/functional/utils/locales.php @@ -3,15 +3,23 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +include __DIR__ . '/authenticate.php'; -if (isset($_GET['type']) && $_GET['type'] == 'deployed') { - $themePath = isset($_GET['theme_path']) ? $_GET['theme_path'] : 'adminhtml/Magento/backend'; - $directory = __DIR__ . '/../../../../pub/static/' . $themePath; - $locales = array_diff(scandir($directory), ['..', '.']); +if (!empty($_POST['token'])) { + if (authenticate(urldecode($_POST['token']))) { + if ($_POST['type'] == 'deployed') { + $themePath = isset($_POST['theme_path']) ? $_POST['theme_path'] : 'adminhtml/Magento/backend'; + $directory = __DIR__ . '/../../../../pub/static/' . $themePath; + $locales = array_diff(scandir($directory), ['..', '.']); + } else { + require_once __DIR__ . DIRECTORY_SEPARATOR . 'bootstrap.php'; + $localeConfig = $magentoObjectManager->create(\Magento\Framework\Locale\Config::class); + $locales = $localeConfig->getAllowedLocales(); + } + echo implode('|', $locales); + } else { + echo "Command not unauthorized."; + } } else { - require_once __DIR__ . DIRECTORY_SEPARATOR . 'bootstrap.php'; - $localeConfig = $magentoObjectManager->create(\Magento\Framework\Locale\Config::class); - $locales = $localeConfig->getAllowedLocales(); + echo "'token' parameter is not set."; } - -echo implode('|', $locales); diff --git a/dev/tests/functional/utils/log.php b/dev/tests/functional/utils/log.php index 8f07d72e2a569..0d6f1fa2ac5cf 100644 --- a/dev/tests/functional/utils/log.php +++ b/dev/tests/functional/utils/log.php @@ -3,15 +3,19 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +include __DIR__ . '/authenticate.php'; -if (!isset($_GET['name'])) { - throw new \InvalidArgumentException( - 'The name of log file is required for getting logs.' - ); -} -$name = urldecode($_GET['name']); -if (preg_match('/\.\.(\\\|\/)/', $name)) { - throw new \InvalidArgumentException('Invalid log file name'); -} +if (!empty($_POST['token']) && !empty($_POST['name'])) { + if (authenticate(urldecode($_POST['token']))) { + $name = urldecode($_POST['name']); + if (preg_match('/\.\.(\\\|\/)/', $name)) { + throw new \InvalidArgumentException('Invalid log file name'); + } -echo serialize(file_get_contents('../../../../var/log' .'/' .$name)); + echo serialize(file_get_contents('../../../../var/log' . '/' . $name)); + } else { + echo "Command not unauthorized."; + } +} else { + echo "'token' or 'name' parameter is not set."; +} diff --git a/dev/tests/functional/utils/pathChecker.php b/dev/tests/functional/utils/pathChecker.php index 11f8229bce56f..b5a2ddb405bde 100644 --- a/dev/tests/functional/utils/pathChecker.php +++ b/dev/tests/functional/utils/pathChecker.php @@ -3,15 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +include __DIR__ . '/authenticate.php'; -if (isset($_GET['path'])) { - $path = urldecode($_GET['path']); +if (!empty($_POST['token']) && !empty($_POST['path'])) { + if (authenticate(urldecode($_POST['token']))) { + $path = urldecode($_POST['path']); - if (file_exists('../../../../' . $path)) { - echo 'path exists: true'; + if (file_exists('../../../../' . $path)) { + echo 'path exists: true'; + } else { + echo 'path exists: false'; + } } else { - echo 'path exists: false'; + echo "Command not unauthorized."; } } else { - throw new \InvalidArgumentException("GET parameter 'path' is not set."); + echo "'token' or 'path' parameter is not set."; } diff --git a/dev/tests/functional/utils/website.php b/dev/tests/functional/utils/website.php index 625f5c6b483f8..ab8e3742f55ae 100644 --- a/dev/tests/functional/utils/website.php +++ b/dev/tests/functional/utils/website.php @@ -3,30 +3,35 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +include __DIR__ . '/authenticate.php'; -if (!isset($_GET['website_code'])) { - throw new \Exception("website_code GET parameter is not set."); -} - -$websiteCode = urldecode($_GET['website_code']); -$rootDir = '../../../../'; -$websiteDir = $rootDir . 'websites/' . $websiteCode . '/'; -$contents = file_get_contents($rootDir . 'index.php'); +if (!empty($_POST['token']) && !empty($_POST['website_code'])) { + if (authenticate(urldecode($_POST['token']))) { + $websiteCode = urldecode($_POST['website_code']); + $rootDir = '../../../../'; + $websiteDir = $rootDir . 'websites/' . $websiteCode . '/'; + $contents = file_get_contents($rootDir . 'index.php'); -$websiteParam = <<<EOD + $websiteParam = <<<EOD \$params = \$_SERVER; \$params[\Magento\Store\Model\StoreManager::PARAM_RUN_CODE] = '$websiteCode'; \$params[\Magento\Store\Model\StoreManager::PARAM_RUN_TYPE] = 'website'; EOD; -$pattern = '`(try {.*?)(\/app\/bootstrap.*?}\n)(.*?)\$_SERVER`mis'; -$replacement = "$1/../..$2\n$websiteParam$3\$params"; + $pattern = '`(try {.*?)(\/app\/bootstrap.*?}\n)(.*?)\$_SERVER`mis'; + $replacement = "$1/../..$2\n$websiteParam$3\$params"; -$contents = preg_replace($pattern, $replacement, $contents); + $contents = preg_replace($pattern, $replacement, $contents); -$old = umask(0); -mkdir($websiteDir, 0760, true); -umask($old); + $old = umask(0); + mkdir($websiteDir, 0760, true); + umask($old); -copy($rootDir . '.htaccess', $websiteDir . '.htaccess'); -file_put_contents($websiteDir . 'index.php', $contents); + copy($rootDir . '.htaccess', $websiteDir . '.htaccess'); + file_put_contents($websiteDir . 'index.php', $contents); + } else { + echo "Command not unauthorized."; + } +} else { + echo "'token' or 'website_code' parameter is not set."; +} From 1c54ed1289cfb11a4e64bc4e2612f5a557f83ae3 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Fri, 8 Feb 2019 13:45:17 -0600 Subject: [PATCH 0567/1295] magento-engcom/magento2ce#2545: Skipped randomly failed MFTF test --- ...rontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml index d60130c545f10..68dbe628e9817 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml @@ -16,6 +16,9 @@ <severity value="CRITICAL"/> <testCaseId value="MAGETWO-77831"/> <group value="product"/> + <skip> + <issueId value="MAGETWO-98182"/> + </skip> </annotations> <before> From 786b2fa832fcc75d5343a2f6f5bffb6aee8f4a79 Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Fri, 8 Feb 2019 21:52:18 +0200 Subject: [PATCH 0568/1295] MAGETWO-97425: Country of Manufacture displays empty under More Information tab --- .../Catalog/Block/Product/View/Attributes.php | 8 +++++++- .../Unit/Block/Product/View/AttributesTest.php | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Block/Product/View/Attributes.php b/app/code/Magento/Catalog/Block/Product/View/Attributes.php index b353e477a056c..8494b690bad9f 100644 --- a/app/code/Magento/Catalog/Block/Product/View/Attributes.php +++ b/app/code/Magento/Catalog/Block/Product/View/Attributes.php @@ -16,6 +16,8 @@ use Magento\Framework\Pricing\PriceCurrencyInterface; /** + * Attributes attributes block + * * @api * @since 100.0.2 */ @@ -56,6 +58,8 @@ public function __construct( } /** + * Returns a Product. + * * @return Product */ public function getProduct() @@ -67,6 +71,8 @@ public function getProduct() } /** + * Additional data. + * * $excludeAttr is optional array of attribute codes to * exclude them from additional data array * @@ -89,7 +95,7 @@ public function getAdditionalData(array $excludeAttr = []) $value = $this->priceCurrency->convertAndFormat($value); } - if (is_string($value) && strlen($value)) { + if (is_string($value) && strlen(trim($value))) { $data[$attribute->getAttributeCode()] = [ 'label' => __($attribute->getStoreLabel()), 'value' => $value, diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/View/AttributesTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/View/AttributesTest.php index 4602a0d99f6f1..a42b167bb432a 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Product/View/AttributesTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/View/AttributesTest.php @@ -138,6 +138,22 @@ public function testGetAttributeNoValue() $this->assertTrue(empty($attributes['phrase'])); } + /** + * Test getAttribute whitespaces. + * + * @return void + */ + public function testGetAttributeWhitespacesValue() + { + $this->phrase = ' '; + $this->frontendAttribute + ->expects($this->any()) + ->method('getValue') + ->willReturn($this->phrase); + $attributes = $this->attributesBlock->getAdditionalData(); + $this->assertTrue(empty($attributes['phrase'])); + } + /** * @return void */ From dd258a5f8eac1ea8c44fc5d0531003c24e51053e Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@adobe.com> Date: Fri, 8 Feb 2019 14:08:04 -0600 Subject: [PATCH 0569/1295] MQE-1430: bug fix in dev/tests/functional/utils/command.php etc --- .../Mtf/Util/Protocol/CurlTransport/BackendDecorator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php index 1126cd29727fc..44ddb16a18741 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php @@ -99,7 +99,7 @@ protected function authorize() $this->transport->write($authUrl, $data, CurlInterface::POST); $response = $this->read(); - if (strpos($response, 'login-form')) { + if (strpos($response, 'login-form') !== false) { continue; } $isAuthorized = true; From 99106e1e1a744fb4f3657783bbd6d6687c347f04 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Sun, 10 Feb 2019 21:46:20 +0200 Subject: [PATCH 0570/1295] Improving the uploaded images styling view --- .../Cms/view/adminhtml/templates/browser/content/files.phtml | 2 +- .../backend/web/css/source/components/_file-insertion.less | 1 + .../Magento/backend/web/css/source/components/_popups.less | 5 +++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Cms/view/adminhtml/templates/browser/content/files.phtml b/app/code/Magento/Cms/view/adminhtml/templates/browser/content/files.phtml index a4c570f9d65a1..f19450cb09c66 100644 --- a/app/code/Magento/Cms/view/adminhtml/templates/browser/content/files.phtml +++ b/app/code/Magento/Cms/view/adminhtml/templates/browser/content/files.phtml @@ -15,7 +15,7 @@ $_height = $block->getImagesHeight(); <?php if ($block->getFilesCount() > 0): ?> <?php foreach ($block->getFiles() as $file): ?> <div data-row="file" class="filecnt" id="<?= $block->escapeHtmlAttr($block->getFileId($file)) ?>"> - <p class="nm" style="height:<?= $block->escapeHtmlAttr($_height) ?>px;width:<?= $block->escapeHtmlAttr($_width) ?>px;"> + <p class="nm" style="height:<?= $block->escapeHtmlAttr($_height) ?>px;"> <?php if ($block->getFileThumbUrl($file)):?> <img src="<?= $block->escapeHtmlAttr($block->getFileThumbUrl($file)) ?>" alt="<?= $block->escapeHtmlAttr($block->getFileName($file)) ?>"/> <?php endif; ?> diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_file-insertion.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_file-insertion.less index 773b70ca5f09d..0fe3a3e8b2ec7 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_file-insertion.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_file-insertion.less @@ -42,6 +42,7 @@ margin: 0 @indent__xs 15px 0; overflow: hidden; padding: 3px; + text-overflow: ellipsis; width: 100px; &.selected { diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_popups.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_popups.less index 7bd10ad491f0c..334e0a4a396bc 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_popups.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_popups.less @@ -264,7 +264,7 @@ } } - #contents-uploader { + .contents-uploader { margin: 0 0 @indent__base; } @@ -298,6 +298,7 @@ margin: 0 @indent__xs 15px 0; overflow: hidden; padding: 3px; + text-overflow: ellipsis; width: 100px; &.selected { @@ -309,7 +310,7 @@ } } - #contents-uploader { + .contents-uploader { &:extend(.abs-clearfix all); } From cb67c971714308c0f8a0ce2831185c31da6c3887 Mon Sep 17 00:00:00 2001 From: Govind Sharma <govindpokhrelsharma@cedcoss.com> Date: Thu, 10 Jan 2019 13:08:39 +0530 Subject: [PATCH 0571/1295] Fixed issue #20157 Updated css file --- .../web/css/source/_module.less | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less index f785dd74d900e..69be357980cf0 100644 --- a/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less @@ -18,6 +18,20 @@ // _____________________________________________ & when (@media-common = true) { + + .search{ + .fieldset{ + .control{ + .addon{ + input{ + flex-basis: auto; + width: 100%; + } + } + } + } + } + .block-search { margin-bottom: 0; From 665dbefec928193cfcb0c4de9702fd78de6a5c62 Mon Sep 17 00:00:00 2001 From: Govind Sharma <govindpokhrelsharma@cedcoss.com> Date: Fri, 11 Jan 2019 10:25:27 +0530 Subject: [PATCH 0572/1295] Fixed Indentation --- .../luma/Magento_CatalogSearch/web/css/source/_module.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less index 69be357980cf0..c56975581f043 100644 --- a/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less @@ -24,8 +24,8 @@ .control{ .addon{ input{ - flex-basis: auto; - width: 100%; + flex-basis: auto; + width: 100%; } } } From 246a344c9a13011477d57f57dcdc5a24ee225bbf Mon Sep 17 00:00:00 2001 From: Govind Sharma <govindpokhrelsharma@cedcoss.com> Date: Fri, 11 Jan 2019 10:33:45 +0530 Subject: [PATCH 0573/1295] Updated Indentation --- .../web/css/source/_module.less | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less index c56975581f043..824d04b1dedff 100644 --- a/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less @@ -19,18 +19,18 @@ & when (@media-common = true) { - .search{ - .fieldset{ - .control{ - .addon{ - input{ - flex-basis: auto; - width: 100%; - } - } - } - } - } + .search { + .fieldset { + .control { + .addon { + input { + flex-basis: auto; + width: 100%; + } + } + } + } + } .block-search { margin-bottom: 0; From 09c0f0195d4149046ad538b0a25518316d7642af Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Tue, 29 Jan 2019 14:10:52 +0200 Subject: [PATCH 0574/1295] ENGCOM-3991: Static test fix. --- .../web/css/source/_module.less | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less index 824d04b1dedff..0f91f857a715c 100644 --- a/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less @@ -20,17 +20,17 @@ & when (@media-common = true) { .search { - .fieldset { - .control { - .addon { - input { - flex-basis: auto; - width: 100%; - } - } - } - } - } + .fieldset { + .control { + .addon { + input { + flex-basis: auto; + width: 100%; + } + } + } + } + } .block-search { margin-bottom: 0; From b24df575092f94e94688cbb93f2efe0e49b7ba57 Mon Sep 17 00:00:00 2001 From: Abrar pathan <abrarkhan@krishtechnolabs.com> Date: Wed, 30 Jan 2019 14:21:26 +0530 Subject: [PATCH 0575/1295] fixed-20790 --- .../web/css/source/_module.less | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/app/design/frontend/Magento/luma/Magento_Wishlist/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Wishlist/web/css/source/_module.less index 48db03d791657..d242788c95879 100644 --- a/app/design/frontend/Magento/luma/Magento_Wishlist/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Wishlist/web/css/source/_module.less @@ -164,6 +164,30 @@ } } } + .products-grid.wishlist { + .product-item-actions { + .action { + &.edit, + &.delete { + .lib-icon-font( + @icon-edit, + @_icon-font-size: 18px, + @_icon-font-line-height: 20px, + @_icon-font-text-hide: true, + @_icon-font-color: @minicart-icons-color, + @_icon-font-color-hover: @primary__color, + @_icon-font-color-active: @minicart-icons-color + ); + } + + &.delete { + .lib-icon-font-symbol( + @_icon-font-content: @icon-trash + ); + } + } + } + } } // @@ -211,15 +235,7 @@ &:last-child { margin-right: 0; } - - &.edit { - float: left; - } - - &.delete { - float: right; - } - + &.edit, &.delete { margin-top: 7px; From 2fa3f8f3268283f50efc850cf4273f334c274909 Mon Sep 17 00:00:00 2001 From: mageprince <mail.mageprince@gmail.com> Date: Fri, 25 Jan 2019 12:49:39 +0530 Subject: [PATCH 0576/1295] Add NOT FIND_IN_SET sql consition --- lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php index 3d06e27542f07..3689aeef81e0b 100644 --- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php +++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php @@ -2854,6 +2854,7 @@ public function endSetup() * - array("gteq" => $greaterOrEqualValue) * - array("lteq" => $lessOrEqualValue) * - array("finset" => $valueInSet) + * - array("nfinset" => $valueNotInSet) * - array("regexp" => $regularExpression) * - array("seq" => $stringValue) * - array("sneq" => $stringValue) @@ -2883,6 +2884,7 @@ public function prepareSqlCondition($fieldName, $condition) 'gteq' => "{{fieldName}} >= ?", 'lteq' => "{{fieldName}} <= ?", 'finset' => "FIND_IN_SET(?, {{fieldName}})", + 'nfinset' => "NOT FIND_IN_SET(?, {{fieldName}})", 'regexp' => "{{fieldName}} REGEXP ?", 'from' => "{{fieldName}} >= ?", 'to' => "{{fieldName}} <= ?", From 35b532f379d47eef2343ddbd2a96bfd1a18339fc Mon Sep 17 00:00:00 2001 From: serhii balko <serhii.balko@transoftgroup.com> Date: Mon, 11 Feb 2019 08:18:51 +0200 Subject: [PATCH 0577/1295] MAGETWO-97528: Custom Customer Attribute is not updating on one website --- app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php index 5b61c0aaf5e2f..561039990f705 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php @@ -508,7 +508,7 @@ private function disableAddressValidation(CustomerInterface $customer) */ private function retrieveFormattedFormData(): array { - $originalRequestData = $originalRequestData = $this->getRequest()->getPostValue(); + $originalRequestData = $this->getRequest()->getPostValue(); /* Customer data filtration */ if (isset($originalRequestData['customer'])) { From 05848e5e999e25aa75583fdfbee960f708a8e8dc Mon Sep 17 00:00:00 2001 From: Rajneesh Gupta <rajneeshgupta@cedcommerce.com> Date: Thu, 17 Jan 2019 11:46:39 -0800 Subject: [PATCH 0578/1295] issue fixed #20382 issue fixed #20382 --- .../Checkout/view/frontend/web/template/minicart/content.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/template/minicart/content.html b/app/code/Magento/Checkout/view/frontend/web/template/minicart/content.html index 2daca51a2f5da..fb128a891aea2 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/minicart/content.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/minicart/content.html @@ -97,7 +97,7 @@ </div> </div> - <div id="minicart-widgets" class="minicart-widgets"> + <div id="minicart-widgets" class="minicart-widgets" if="getRegion('promotion').length"> <each args="getRegion('promotion')" render=""/> </div> </div> From d234115d69b0d9bb1eaf72eef4f99d9c12d01c3b Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Mon, 11 Feb 2019 10:12:55 +0200 Subject: [PATCH 0579/1295] MAGETWO-94450: Rewards points earned from coupon code are not applied to guests that create accounts after checking out --- .../Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml index 2200f1eb45d7d..e85eea62d473e 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml @@ -206,14 +206,6 @@ <data key="discountAmount">0</data> </entity> - <entity name="PriceRuleWithCondition" type="SalesRule"> - <data key="name" unique="suffix">SalesRule</data> - <data key="websites">Main Website</data> - <data key="customerGroups">'NOT LOGGED IN', 'General', 'Wholesale', 'Retailer'</data> - <data key="apply">Fixed amount discount for whole cart</data> - <data key="discountAmount">0</data> - </entity> - <entity name="SalesRuleNoCouponWithFixedDiscount" extends="ApiCartRule"> <data key="simple_action">by_fixed</data> </entity> From 7bf6f187c4718ccde9570240f6dd34b8832542af Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Mon, 11 Feb 2019 10:57:49 +0200 Subject: [PATCH 0580/1295] MAGETWO-97947: Invalid action behavior --- .../Adminhtml/Index/ResetPassword.php | 2 +- .../Adminhtml/Index/ResetPasswordTest.php | 1 - .../Customer/view/frontend/web/js/address.js | 17 +++++++++++------ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/ResetPassword.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/ResetPassword.php index 38b868d6b56f0..eab18520e69a7 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/ResetPassword.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/ResetPassword.php @@ -19,7 +19,7 @@ public function execute() { $resultRedirect = $this->resultRedirectFactory->create(); $customerId = (int)$this->getRequest()->getParam('customer_id', 0); - if (!$this->getRequest()->isPost() || !$customerId) { + if (!$customerId) { $resultRedirect->setPath('customer/index'); return $resultRedirect; } diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ResetPasswordTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ResetPasswordTest.php index b98f3e016768b..35ac522499b63 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ResetPasswordTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ResetPasswordTest.php @@ -97,7 +97,6 @@ protected function setUp() $this->_request = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) ->disableOriginalConstructor() ->getMock(); - $this->_request->expects($this->any())->method('isPost')->willReturn(true); $this->_response = $this->getMockBuilder( \Magento\Framework\App\Response\Http::class diff --git a/app/code/Magento/Customer/view/frontend/web/js/address.js b/app/code/Magento/Customer/view/frontend/web/js/address.js index c6d05b51bdf0b..4984aecb6124e 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/address.js +++ b/app/code/Magento/Customer/view/frontend/web/js/address.js @@ -6,9 +6,10 @@ define([ 'jquery', 'Magento_Ui/js/modal/confirm', + 'mage/dataPost', 'jquery/ui', 'mage/translate' -], function ($, confirm) { +], function ($, confirm, dataPost) { 'use strict'; $.widget('mage.address', { @@ -53,7 +54,8 @@ define([ * @return {Boolean} */ _deleteAddress: function (e) { - var self = this; + var self = this, + addressId; confirm({ content: this.options.deleteConfirmMessage, @@ -62,12 +64,15 @@ define([ /** @inheritdoc */ confirm: function () { if (typeof $(e.target).parent().data('address') !== 'undefined') { - window.location = self.options.deleteUrlPrefix + $(e.target).parent().data('address') + - '/form_key/' + $.mage.cookies.get('form_key'); + addressId = $(e.target).parent().data('address'); } else { - window.location = self.options.deleteUrlPrefix + $(e.target).data('address') + - '/form_key/' + $.mage.cookies.get('form_key'); + addressId = $(e.target).data('address'); } + + dataPost().postData({ + action: self.options.deleteUrlPrefix + addressId, + data: { form_key: $.mage.cookies.get('form_key') } + }); } } }); From 8b4759fe28a1c2a8afc75f1ebde4010a4b2ec6ac Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Mon, 11 Feb 2019 11:27:34 +0200 Subject: [PATCH 0581/1295] MAGETWO-95182: Order placed by new customer before they registered is displayed in the admin under the name Guest --- .../StorefrontCheckCustomerInfoCreatedByGuestTest.xml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCustomerInfoCreatedByGuestTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCustomerInfoCreatedByGuestTest.xml index adc308b490c33..32cc254543bca 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCustomerInfoCreatedByGuestTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCustomerInfoCreatedByGuestTest.xml @@ -11,14 +11,15 @@ <test name="StorefrontCheckCustomerInfoCreatedByGuestTest"> <annotations> <features value="Checkout"/> - <stories value="Check customer information created by guest"/> - <title value="Check Customer Information Created By Guest"/> + <stories value="Check order customer information created by guest"/> + <title value="Check Order Customer Information Created By Guest"/> <description value="Check customer information after placing the order as the guest who created an account"/> <severity value="MAJOR"/> <testCaseId value="MC-13839"/> - <useCaseId value="MAGETWO-95820"/> + <useCaseId value="MAGETWO-95182"/> <group value="checkout"/> <group value="customer"/> + <group value="sales"/> </annotations> <before> @@ -29,10 +30,10 @@ </before> <after> - <actionGroup ref="CustomerLogoutStorefrontActionGroup" stepKey="customerLogoutFromStorefront" /> <deleteData createDataKey="createProduct" stepKey="deleteProduct" /> <deleteData createDataKey="createCategory" stepKey="deleteCategory" /> <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <actionGroup ref="CustomerLogoutStorefrontActionGroup" stepKey="customerLogoutFromStorefront" /> </after> <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="navigateToProductPage"/> @@ -44,6 +45,7 @@ <argument name="customerVar" value="CustomerEntityOne"/> <argument name="customerAddressVar" value="CustomerAddressSimple"/> </actionGroup> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectPaymentMethod"/> <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="placeOrder"> <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage"/> <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage" /> From 16f7465bef4cc8129e2fe4fa7ec8d686c9965243 Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Mon, 11 Feb 2019 12:12:16 +0200 Subject: [PATCH 0582/1295] MAGETWO-97425: Country of Manufacture displays empty under More Information tab --- .../Block/Product/View/AttributesTest.php | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/View/AttributesTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/View/AttributesTest.php index a42b167bb432a..66a62b444b4af 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Product/View/AttributesTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/View/AttributesTest.php @@ -125,33 +125,28 @@ protected function setUp() } /** + * Get attribute with no value phrase + * + * @param string $phrase * @return void + * @dataProvider noValue */ - public function testGetAttributeNoValue() + public function testGetAttributeNoValue(string $phrase) { - $this->phrase = ''; - $this->frontendAttribute - ->expects($this->any()) - ->method('getValue') - ->willReturn($this->phrase); + $this->frontendAttribute->method('getValue') + ->willReturn($phrase); $attributes = $this->attributesBlock->getAdditionalData(); - $this->assertTrue(empty($attributes['phrase'])); + $this->assertArrayNotHasKey('phrase', $attributes); } /** - * Test getAttribute whitespaces. + * No value data provider * - * @return void + * @return array */ - public function testGetAttributeWhitespacesValue() + public function noValue() { - $this->phrase = ' '; - $this->frontendAttribute - ->expects($this->any()) - ->method('getValue') - ->willReturn($this->phrase); - $attributes = $this->attributesBlock->getAdditionalData(); - $this->assertTrue(empty($attributes['phrase'])); + return [[' '], ['']]; } /** From 572693882f7a39768bfe31fbaa206fc696072ada Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Mon, 11 Feb 2019 12:38:06 +0200 Subject: [PATCH 0583/1295] MAGETWO-97425: Country of Manufacture displays empty under More Information tab --- .../Catalog/Test/Unit/Block/Product/View/AttributesTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/View/AttributesTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/View/AttributesTest.php index 66a62b444b4af..69133c1429be5 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Product/View/AttributesTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/View/AttributesTest.php @@ -144,7 +144,7 @@ public function testGetAttributeNoValue(string $phrase) * * @return array */ - public function noValue() + public function noValue(): array { return [[' '], ['']]; } From d56f551f500e98e8b17d72e726378e9300904add Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Mon, 11 Feb 2019 13:01:11 +0200 Subject: [PATCH 0584/1295] MAGETWO-98182: [FT] [MFTF] StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest fails because of bad design --- ...ctCustomOptionsDifferentStoreViewsTest.xml | 93 ++++++++++--------- .../Mftf/ActionGroup/CheckoutActionGroup.xml | 2 +- .../AdminCreateStoreViewActionGroup.xml | 4 +- .../StorefrontSwitchStoreViewActionGroup.xml | 2 +- 4 files changed, 53 insertions(+), 48 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml index 68dbe628e9817..5a3ac4ddd8495 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest"> <annotations> <features value="Purchase a product with Custom Options on different Store Views"/> @@ -16,9 +16,6 @@ <severity value="CRITICAL"/> <testCaseId value="MAGETWO-77831"/> <group value="product"/> - <skip> - <issueId value="MAGETWO-98182"/> - </skip> </annotations> <before> @@ -65,18 +62,24 @@ <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView2"> <argument name="customStore" value="customStoreFR"/> </actionGroup> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearWebsitesGridFilters"/> + + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearOrdersGridFilters"/> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearProductsGridFilters"/> </after> <!-- Open Product Grid, Filter product and open --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> - <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="filterGroupedProductOptions"> <argument name="product" value="_defaultProduct"/> </actionGroup> - <click selector="{{AdminProductGridSection.productGridXRowYColumnButton('1', '2')}}" stepKey="openProductForEdit"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad2"/> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProductPage"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> <!-- Update Product with Option Value DropDown 1--> @@ -100,15 +103,12 @@ <!-- Switcher to Store FR--> - <scrollToTopOfPage stepKey="scrollToTopOfPage1"/> - - <click selector="{{AdminProductFormActionSection.changeStoreButton}}" stepKey="clickStoreSwitcher"/> - <click selector="{{AdminProductFormActionSection.selectStoreView(customStoreFR.name)}}" stepKey="clickStoreView"/> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="acceptMessage"/> + <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="switchToStoreFR"> + <argument name="scopeName" value="customStoreFR.name"/> + </actionGroup> <!-- Open tab Customizable Options --> - <waitForPageLoad time="10" stepKey="waitForPageLoad4"/> <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" visible="true" stepKey="clickIfContentTabCloses3"/> <!-- Update Option Customizable Options and Option Value 1--> @@ -128,16 +128,13 @@ <!-- Login Customer Storefront --> - <amOnPage url="{{StorefrontCustomerSignInPage.url}}" stepKey="amOnSignInPage"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad6"/> - <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> <!-- Go to Product Page --> <amOnPage url="{{StorefrontHomePage.url}}$$createProduct.custom_attributes[url_key]$$.html" stepKey="amOnProduct1Page"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad7"/> <seeElement selector="{{StorefrontProductInfoMainSection.productOptionDropDownTitle('Custom Options 1')}}" stepKey="seeProductOptionDropDownTitle"/> <seeElement selector="{{StorefrontProductInfoMainSection.productOptionDropDownOptionTitle('Custom Options 1', 'option1')}}" stepKey="seeproductOptionDropDownOptionTitle1"/> @@ -156,10 +153,7 @@ </actionGroup> <!-- Checking the correctness of displayed custom options for user parameters on checkout --> - - <click selector="{{StorefrontMiniCartSection.show}}" stepKey="clickCart"/> - <click selector="{{StorefrontMiniCartSection.goToCheckout}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="s33"/> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart" /> <conditionalClick selector="{{CheckoutPaymentSection.cartItemsArea}}" dependentSelector="{{CheckoutPaymentSection.cartItemsArea}}" visible="true" stepKey="exposeMiniCart"/> @@ -177,20 +171,28 @@ <conditionalClick selector="{{CheckoutPaymentSection.productOptionsByProductItemPrice('150')}}" dependentSelector="{{CheckoutPaymentSection.productOptionsActiveByProductItemPrice('150')}}" visible="false" stepKey="exposeProductOptions1"/> <see selector="{{CheckoutPaymentSection.productOptionsActiveByProductItemPrice('150')}}" userInput="option2" stepKey="seeProductOptionValueDropdown1Input2"/> - <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad8"/> <!-- Place Order --> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyOrder1"/> - <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> + <!--Select shipping method--> + <actionGroup ref="CheckoutSelectFlatRateShippingMethodActionGroup" stepKey="selectFlatRateShippingMethod"/> + <waitForElementVisible selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/> + <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskAfterClickNext"/> + <!--Select payment method--> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectPaymentMethod"/> + <!-- Place Order --> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="customerPlaceOrder"> + <argument name="orderNumberMessage" value="CONST.successCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> + </actionGroup> <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> <!-- Open Order --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad9"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearOrdersGridFilters"/> <fillField selector="{{OrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="fillOrderNum"/> <click selector="{{OrdersGridSection.submitSearch}}" stepKey="submitSearch"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask4"/> @@ -205,14 +207,12 @@ <!-- Switch to FR Store View Storefront --> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnProduct4Page"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad11"/> - <click selector="{{StorefrontHeaderSection.storeViewSwitcher}}" stepKey="clickStoreViewSwitcher1"/> - <waitForElementVisible selector="{{StorefrontHeaderSection.storeViewDropdown}}" stepKey="waitForStoreViewDropdown1"/> - <click selector="{{StorefrontHeaderSection.storeViewOption(customStoreFR.code)}}" stepKey="selectStoreView1"/> - <waitForPageLoad stepKey="waitForPageLoad12"/> + + <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchStore"> + <argument name="storeView" value="customStoreFR"/> + </actionGroup> <amOnPage url="{{StorefrontHomePage.url}}$$createProduct.custom_attributes[url_key]$$.html" stepKey="amOnProduct2Page"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad13"/> <seeElement selector="{{StorefrontProductInfoMainSection.productOptionDropDownTitle('FR Custom Options 1')}}" stepKey="seeProductFrOptionDropDownTitle"/> <seeElement selector="{{StorefrontProductInfoMainSection.productOptionDropDownOptionTitle('FR Custom Options 1', 'FR option1')}}" stepKey="productFrOptionDropDownOptionTitle1"/> @@ -232,9 +232,7 @@ <!-- Checking the correctness of displayed custom options for user parameters on checkout --> - <click selector="{{StorefrontMiniCartSection.show}}" stepKey="clickCart1"/> - <click selector="{{StorefrontMiniCartSection.goToCheckout}}" stepKey="goToCheckout1"/> - <waitForPageLoad stepKey="s34"/> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart2" /> <conditionalClick selector="{{CheckoutPaymentSection.cartItemsArea}}" dependentSelector="{{CheckoutPaymentSection.cartItemsArea}}" visible="true" stepKey="exposeMiniCart1"/> @@ -252,18 +250,26 @@ <conditionalClick selector="{{CheckoutPaymentSection.productOptionsByProductItemPrice('150')}}" dependentSelector="{{CheckoutPaymentSection.productOptionsActiveByProductItemPrice('150')}}" visible="false" stepKey="exposeProductOptions3"/> <see selector="{{CheckoutPaymentSection.productOptionsActiveByProductItemPrice('150')}}" userInput="FR option2" stepKey="seeProductFrOptionValueDropdown1Input3"/> - <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext1"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad14"/> <!-- Place Order --> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyOrder2"/> - <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder1"/> + <!--Select shipping method--> + <actionGroup ref="CheckoutSelectFlatRateShippingMethodActionGroup" stepKey="selectFlatRateShippingMethod2"/> + <waitForElementVisible selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton2"/> + <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext2"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskAfterClickNext2"/> + + <!--Select payment method--> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectPaymentMethod2"/> + <!-- Place Order --> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="customerPlaceOrder2"> + <argument name="orderNumberMessage" value="CONST.successCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> + </actionGroup> <!-- Open Product Grid, Filter product and open --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage1"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad15"/> <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="filterGroupedProductOptions1"> <argument name="product" value="_defaultProduct"/> @@ -297,7 +303,6 @@ <!--Go to Product Page--> <amOnPage url="{{StorefrontHomePage.url}}$$createProduct.custom_attributes[url_key]$$.html" stepKey="amOnProduct2Page2"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad20"/> <seeElement selector="{{StorefrontProductInfoMainSection.productOptionDropDownTitle('Custom Options 1')}}" stepKey="seeProductOptionDropDownTitle1"/> <seeElement selector="{{StorefrontProductInfoMainSection.productOptionDropDownOptionTitle('Custom Options 1', 'option1')}}" stepKey="seeProductOptionDropDownOptionTitle3"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml index f79d59028c468..e3dbb3870896d 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml @@ -37,7 +37,7 @@ <argument name="orderNumberMessage"/> <argument name="emailYouMessage"/> </arguments> - <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/> + <waitForElementVisible selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/> <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> <see selector="{{CheckoutSuccessMainSection.success}}" userInput="{{orderNumberMessage}}" stepKey="seeOrderNumber"/> <see selector="{{CheckoutSuccessMainSection.success}}" userInput="{{emailYouMessage}}" stepKey="seeEmailYou"/> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml index f1c6f4d87e0d6..2f541feacd9c2 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml @@ -24,8 +24,8 @@ <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForModal" /> <see selector="{{AdminConfirmationModalSection.title}}" userInput="Warning message" stepKey="seeWarning" /> <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="dismissModal" /> - <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" stepKey="waitForPageReolad"/> - <see userInput="You saved the store view." stepKey="seeSavedMessage" /> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForPageReolad"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the store view." stepKey="seeSavedMessage" /> </actionGroup> <actionGroup name="AdminCreateStoreViewUseStringArgumentsActionGroup" extends="AdminCreateStoreViewActionGroup"> <arguments> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/StorefrontSwitchStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/StorefrontSwitchStoreViewActionGroup.xml index e6ebd229e4683..c62ed55c7df49 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/StorefrontSwitchStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/StorefrontSwitchStoreViewActionGroup.xml @@ -13,7 +13,7 @@ </arguments> <click selector="{{StorefrontHeaderSection.storeViewSwitcher}}" stepKey="clickStoreViewSwitcher"/> <waitForElementVisible selector="{{StorefrontHeaderSection.storeViewDropdown}}" stepKey="waitForStoreViewDropdown"/> - <click selector="{{StorefrontHeaderSection.storeViewOption(storeView.name)}}" stepKey="clickSelectStoreView"/> + <click selector="{{StorefrontHeaderSection.storeViewOption(storeView.code)}}" stepKey="clickSelectStoreView"/> <waitForPageLoad stepKey="waitForPageLoad"/> </actionGroup> </actionGroups> From 495c7a4574ff1b7a8e8c66657ed37f632b83094c Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Mon, 11 Feb 2019 13:24:53 +0200 Subject: [PATCH 0585/1295] MAGETWO-97947: Invalid action behavior --- app/code/Magento/Customer/view/frontend/web/js/address.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/view/frontend/web/js/address.js b/app/code/Magento/Customer/view/frontend/web/js/address.js index 4984aecb6124e..561afca5d5655 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/address.js +++ b/app/code/Magento/Customer/view/frontend/web/js/address.js @@ -71,7 +71,7 @@ define([ dataPost().postData({ action: self.options.deleteUrlPrefix + addressId, - data: { form_key: $.mage.cookies.get('form_key') } + data: {form_key: $.mage.cookies.get('form_key')} }); } } From a3535662fafb1d4355e7db851a46c60d21f08ae3 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <d.cheshun@atwix.com> Date: Mon, 11 Feb 2019 13:36:46 +0200 Subject: [PATCH 0586/1295] Fix minor code styling issues --- .../web/css/source/_module.less | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/app/design/frontend/Magento/luma/Magento_Wishlist/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Wishlist/web/css/source/_module.less index d242788c95879..96b8c3442f44a 100644 --- a/app/design/frontend/Magento/luma/Magento_Wishlist/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Wishlist/web/css/source/_module.less @@ -164,25 +164,26 @@ } } } + .products-grid.wishlist { .product-item-actions { .action { &.edit, &.delete { .lib-icon-font( - @icon-edit, - @_icon-font-size: 18px, - @_icon-font-line-height: 20px, - @_icon-font-text-hide: true, - @_icon-font-color: @minicart-icons-color, - @_icon-font-color-hover: @primary__color, - @_icon-font-color-active: @minicart-icons-color + @icon-edit, + @_icon-font-size: 18px, + @_icon-font-line-height: 20px, + @_icon-font-text-hide: true, + @_icon-font-color: @minicart-icons-color, + @_icon-font-color-hover: @primary__color, + @_icon-font-color-active: @minicart-icons-color ); } &.delete { .lib-icon-font-symbol( - @_icon-font-content: @icon-trash + @_icon-font-content: @icon-trash ); } } From adc050d34fd58ea809d963d3aa0dfc49ce9bcb01 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Mon, 11 Feb 2019 13:36:57 +0200 Subject: [PATCH 0587/1295] MAGETWO-96489: Anchor categories are not showing products after enabling subcategories --- app/code/Magento/Catalog/Model/Category.php | 20 +++++-- .../StorefrontCategoryActionGroup.xml | 6 +++ .../Section/StorefrontCategoryMainSection.xml | 1 + ...vailableAfterEnablingSubCategoriesTest.xml | 54 +++++++++++++++++++ .../Catalog/Test/Unit/Model/CategoryTest.php | 24 ++++++--- 5 files changed, 92 insertions(+), 13 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductAvailableAfterEnablingSubCategoriesTest.xml diff --git a/app/code/Magento/Catalog/Model/Category.php b/app/code/Magento/Catalog/Model/Category.php index 00b093b2918f1..0bf4ac49acf1d 100644 --- a/app/code/Magento/Catalog/Model/Category.php +++ b/app/code/Magento/Catalog/Model/Category.php @@ -1130,10 +1130,15 @@ public function reindex() } } $productIndexer = $this->indexerRegistry->get(Indexer\Category\Product::INDEXER_ID); - if (!$productIndexer->isScheduled() - && (!empty($this->getAffectedProductIds()) || $this->dataHasChangedFor('is_anchor')) - ) { - $productIndexer->reindexList($this->getPathIds()); + + if (!empty($this->getAffectedProductIds()) + || $this->dataHasChangedFor('is_anchor') + || $this->dataHasChangedFor('is_active')) { + if (!$productIndexer->isScheduled()) { + $productIndexer->reindexList($this->getPathIds()); + } else { + $productIndexer->invalidate(); + } } } @@ -1167,6 +1172,11 @@ public function getIdentities() if ($this->hasDataChanges() || $this->isDeleted() || $this->dataHasChangedFor(self::KEY_INCLUDE_IN_MENU)) { $identities[] = Product::CACHE_PRODUCT_CATEGORY_TAG . '_' . $this->getId(); + if ($this->dataHasChangedFor('is_anchor') || $this->dataHasChangedFor('is_active')) { + foreach ($this->getPathIds() as $id) { + $identities[] = Product::CACHE_PRODUCT_CATEGORY_TAG . '_' . $id; + } + } } if ($this->isObjectNew()) { @@ -1174,7 +1184,7 @@ public function getIdentities() } } - return $identities; + return array_unique($identities); } /** diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml index 2551bd61580ed..e46bfc7ee8b02 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml @@ -19,6 +19,12 @@ <see userInput="{{category.name}}" selector="{{StorefrontCategoryMainSection.categoryTitle}}" stepKey="assertCategoryName"/> <see userInput="{{productCount}}" selector="{{StorefrontCategoryMainSection.productCount}} span" stepKey="assertProductCount"/> </actionGroup> + <!--Check category is empty--> + <actionGroup name="StorefrontCheckEmptyCategoryActionGroup" extends="StorefrontCheckCategoryActionGroup"> + <remove keyForRemoval="assertProductCount"/> + <amOnPage url="{{StorefrontCategoryPage.url(category.name)}}" before="checkUrl" stepKey="goToCategoryStorefront"/> + <see selector="{{StorefrontCategoryMainSection.categoryEmptyMessage}}" userInput="We can't find products matching the selection." stepKey="seeCategoryEmpty"/> + </actionGroup> <!-- Check simple product on the category page --> <actionGroup name="StorefrontCheckCategorySimpleProduct"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml index 1f1a4ce9133e7..3c769f9dbc0ce 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml @@ -22,5 +22,6 @@ <element name="categoryPageProductImagePlaceholderSmall" type="text" selector=".products-grid img[src*='placeholder/small_image.jpg']"/> <element name="categoryPageProductImage" type="text" selector=".products-grid img[src*='/{{var1}}']" parameterized="true"/> <element name="categoryPageProductName" type="text" selector=".products.list.items.product-items li:nth-of-type({{line}}) .product-item-link" timeout="30" parameterized="true"/> + <element name="categoryEmptyMessage" type="text" selector=".column.main .message.info.empty"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductAvailableAfterEnablingSubCategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductAvailableAfterEnablingSubCategoriesTest.xml new file mode 100644 index 0000000000000..fe7858313c848 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductAvailableAfterEnablingSubCategoriesTest.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontProductAvailableAfterEnablingSubCategoriesTest"> + <annotations> + <features value="Catalog"/> + <stories value="Show category products on storefront"/> + <title value="Check that parent categories are showing products after enabling subcategories"/> + <description value="Check that parent categories are showing products after enabling subcategories"/> + <severity value="MAJOR"/> + <testCaseId value="MC-13914"/> + <useCaseId value="MAGETWO-96489"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SubCategoryWithParent" stepKey="createSubCategory"> + <requiredEntity createDataKey="createCategory"/> + <field key="is_active">false</field> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createSubCategory"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!--Check anchor category is empty--> + <actionGroup ref="StorefrontCheckEmptyCategoryActionGroup" stepKey="checkEmptyAnchorCategory"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="0"/> + </actionGroup> + <!--Enable subcategory--> + <actionGroup ref="AdminNavigateToCategoryInTree" stepKey="openCreatedSubCategory"> + <argument name="category" value="$$createSubCategory$$"/> + </actionGroup> + <click selector="{{AdminCategoryBasicFieldSection.enableCategoryLabel}}" stepKey="enableCategory"/> + <actionGroup ref="saveCategoryForm" stepKey="saveCategory"/> + <!--Check created product in anchor category on storefront--> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="goToCategoryStorefront"/> + <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="seeCreatedProduct"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Unit/Model/CategoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/CategoryTest.php index a53b87dcf1567..c84753ad4adcb 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/CategoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/CategoryTest.php @@ -348,13 +348,15 @@ public function testReindexFlatEnabled($flatScheduled, $productScheduled, $expec public function reindexFlatDisabledTestDataProvider() { return [ - [false, null, null, null, 0], - [true, null, null, null, 0], - [false, [], null, null, 0], - [false, ["1", "2"], null, null, 1], - [false, null, 1, null, 1], - [false, ["1", "2"], 0, 1, 1], - [false, null, 1, 1, 0], + [false, null, null, null, null, null, 0], + [true, null, null, null, null, null, 0], + [false, [], null, null, null, null, 0], + [false, ["1", "2"], null, null, null, null, 1], + [false, null, 1, null, null, null, 1], + [false, ["1", "2"], 0, 1, null, null, 1], + [false, null, 1, 1, null, null, 0], + [false, ["1", "2"], null, null, 0, 1, 1], + [false, ["1", "2"], null, null, 1, 0, 1], ]; } @@ -363,6 +365,8 @@ public function reindexFlatDisabledTestDataProvider() * @param array $affectedIds * @param int|string $isAnchorOrig * @param int|string $isAnchor + * @param mixed $isActiveOrig + * @param mixed $isActive, * @param int $expectedProductReindexCall * * @dataProvider reindexFlatDisabledTestDataProvider @@ -372,12 +376,16 @@ public function testReindexFlatDisabled( $affectedIds, $isAnchorOrig, $isAnchor, + $isActiveOrig, + $isActive, $expectedProductReindexCall ) { $this->category->setAffectedProductIds($affectedIds); $this->category->setData('is_anchor', $isAnchor); $this->category->setOrigData('is_anchor', $isAnchorOrig); $this->category->setAffectedProductIds($affectedIds); + $this->category->setData('is_active', $isActive); + $this->category->setOrigData('is_active', $isActiveOrig); $pathIds = ['path/1/2', 'path/2/3']; $this->category->setData('path_ids', $pathIds); @@ -387,7 +395,7 @@ public function testReindexFlatDisabled( ->method('isFlatEnabled') ->will($this->returnValue(false)); - $this->productIndexer->expects($this->exactly(1)) + $this->productIndexer->expects($this->any()) ->method('isScheduled') ->willReturn($productScheduled); $this->productIndexer->expects($this->exactly($expectedProductReindexCall)) From 24f571c5da75e93db47a0997d6820029c70fba04 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Mon, 11 Feb 2019 14:12:40 +0200 Subject: [PATCH 0588/1295] MAGETWO-97947: Invalid action behavior --- app/code/Magento/Customer/view/frontend/web/js/address.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/view/frontend/web/js/address.js b/app/code/Magento/Customer/view/frontend/web/js/address.js index 561afca5d5655..9bee3a458415e 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/address.js +++ b/app/code/Magento/Customer/view/frontend/web/js/address.js @@ -71,7 +71,9 @@ define([ dataPost().postData({ action: self.options.deleteUrlPrefix + addressId, - data: {form_key: $.mage.cookies.get('form_key')} + data: { + form_key: $.mage.cookies.get('form_key') + } }); } } From 11399999465917b5098be761c9a4b7b703f4baa2 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Mon, 11 Feb 2019 14:48:16 +0200 Subject: [PATCH 0589/1295] MAGETWO-97947: Invalid action behavior --- app/code/Magento/Customer/view/frontend/web/js/address.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/view/frontend/web/js/address.js b/app/code/Magento/Customer/view/frontend/web/js/address.js index 9bee3a458415e..5499327c2e27d 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/address.js +++ b/app/code/Magento/Customer/view/frontend/web/js/address.js @@ -72,7 +72,7 @@ define([ dataPost().postData({ action: self.options.deleteUrlPrefix + addressId, data: { - form_key: $.mage.cookies.get('form_key') + 'form_key': $.mage.cookies.get('form_key') } }); } From ba2ad3863e53d4f05bbc6f669b61f82715268a81 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Mon, 11 Feb 2019 15:16:46 +0200 Subject: [PATCH 0590/1295] MAGETWO-96489: Anchor categories are not showing products after enabling subcategories --- app/code/Magento/Catalog/Model/Category.php | 31 +++++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Category.php b/app/code/Magento/Catalog/Model/Category.php index 0bf4ac49acf1d..aa99918753e81 100644 --- a/app/code/Magento/Catalog/Model/Category.php +++ b/app/code/Magento/Catalog/Model/Category.php @@ -1170,15 +1170,8 @@ public function getIdentities() $identities[] = self::CACHE_TAG . '_' . $this->getId(); } - if ($this->hasDataChanges() || $this->isDeleted() || $this->dataHasChangedFor(self::KEY_INCLUDE_IN_MENU)) { - $identities[] = Product::CACHE_PRODUCT_CATEGORY_TAG . '_' . $this->getId(); - if ($this->dataHasChangedFor('is_anchor') || $this->dataHasChangedFor('is_active')) { - foreach ($this->getPathIds() as $id) { - $identities[] = Product::CACHE_PRODUCT_CATEGORY_TAG . '_' . $id; - } - } - } - + $identities = $this->getCategoryRelationIdentities($identities); + if ($this->isObjectNew()) { $identities[] = self::CACHE_TAG; } @@ -1470,5 +1463,25 @@ public function setExtensionAttributes(\Magento\Catalog\Api\Data\CategoryExtensi return $this->_setExtensionAttributes($extensionAttributes); } + /** + * Return category relation identities. + * + * @param array $identities + * @return array + */ + private function getCategoryRelationIdentities(array $identities): array + { + if ($this->hasDataChanges() || $this->isDeleted() || $this->dataHasChangedFor(self::KEY_INCLUDE_IN_MENU)) { + $identities[] = Product::CACHE_PRODUCT_CATEGORY_TAG . '_' . $this->getId(); + if ($this->dataHasChangedFor('is_anchor') || $this->dataHasChangedFor('is_active')) { + foreach ($this->getPathIds() as $id) { + $identities[] = Product::CACHE_PRODUCT_CATEGORY_TAG . '_' . $id; + } + } + } + + return $identities; + } + //@codeCoverageIgnoreEnd } From f461c3dcbd63f8cdf0fe18ab4de7c89ece0dde71 Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Mon, 11 Feb 2019 15:29:41 +0200 Subject: [PATCH 0591/1295] MAGETWO-97425: Country of Manufacture displays empty under More Information tab --- .../Catalog/Test/Unit/Block/Product/View/AttributesTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/View/AttributesTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/View/AttributesTest.php index 69133c1429be5..2310b1f8b871c 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Product/View/AttributesTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/View/AttributesTest.php @@ -129,7 +129,7 @@ protected function setUp() * * @param string $phrase * @return void - * @dataProvider noValue + * @dataProvider noValueProvider */ public function testGetAttributeNoValue(string $phrase) { @@ -144,7 +144,7 @@ public function testGetAttributeNoValue(string $phrase) * * @return array */ - public function noValue(): array + public function noValueProvider(): array { return [[' '], ['']]; } From 62a5a11363849f7f94e7f60b5545816a75fb67bd Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Mon, 11 Feb 2019 16:19:36 +0200 Subject: [PATCH 0592/1295] MAGETWO-98185: Revert MAGETWO-73613 from 2.2.8 --- .../Mftf/Data/ProductStockOptionsData.xml | 25 -------- .../Metadata/product_stock_options-meta.xml | 21 ------- .../Customer/Wishlist/Item/Column/Cart.php | 26 -------- .../StorefrontCustomerWishlistActionGroup.xml | 22 ------- ...orefrontCustomerWishlistProductSection.xml | 2 - ...StorefrontCheckAmountLimitWishlistTest.xml | 60 ------------------- .../frontend/templates/item/column/cart.phtml | 3 +- .../view/frontend/web/js/add-to-wishlist.js | 57 +++++++++--------- 8 files changed, 28 insertions(+), 188 deletions(-) delete mode 100644 app/code/Magento/CatalogInventory/Test/Mftf/Data/ProductStockOptionsData.xml delete mode 100644 app/code/Magento/CatalogInventory/Test/Mftf/Metadata/product_stock_options-meta.xml delete mode 100644 app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontCheckAmountLimitWishlistTest.xml diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Data/ProductStockOptionsData.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Data/ProductStockOptionsData.xml deleted file mode 100644 index 4ff43f4177401..0000000000000 --- a/app/code/Magento/CatalogInventory/Test/Mftf/Data/ProductStockOptionsData.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> - <!-- Change Maximum Qty Allowed in Shopping Cart config --> - <entity name="ProductStockOptions" type="catalog_inventory_product_stock_options"> - <requiredEntity type="max_qty_to_cart">MaxQtyAllowInCartChange</requiredEntity> - </entity> - <entity name="MaxQtyAllowInCartChange" type="max_qty_to_cart"> - <data key="value">0</data> - </entity> - <!-- Maximum Qty Allowed in Shopping Cart to default config --> - <entity name="DefaultProductStockOptions" type="catalog_inventory_product_stock_options"> - <requiredEntity type="max_qty_to_cart">MaxQtyAllowInCartDefault</requiredEntity> - </entity> - <entity name="MaxQtyAllowInCartDefault" type="max_qty_to_cart"> - <data key="value">10000</data> - </entity> -</entities> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Metadata/product_stock_options-meta.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Metadata/product_stock_options-meta.xml deleted file mode 100644 index 71b1ebd9806ca..0000000000000 --- a/app/code/Magento/CatalogInventory/Test/Mftf/Metadata/product_stock_options-meta.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> - <operation name="CatalogInventoryProductStockSetup" dataType="catalog_inventory_product_stock_options" type="create" auth="adminFormKey" url="/admin/system_config/save/section/cataloginventory/" successRegex="/messages-message-success/" method="POST"> - <object key="groups" dataType="catalog_inventory_product_stock_options"> - <object key="item_options" dataType="catalog_inventory_product_stock_options"> - <object key="fields" dataType="catalog_inventory_product_stock_options"> - <object key="max_sale_qty" dataType="max_qty_to_cart"> - <field key="value">string</field> - </object> - </object> - </object> - </object> - </operation> -</operations> diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Cart.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Cart.php index 1ba31b26df46e..b043a8d4b684c 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Cart.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Cart.php @@ -6,8 +6,6 @@ namespace Magento\Wishlist\Block\Customer\Wishlist\Item\Column; -use Magento\Catalog\Controller\Adminhtml\Product\Initialization\StockDataFilter; - /** * Wishlist block customer item cart column * @@ -37,28 +35,4 @@ public function getProductItem() { return $this->getItem()->getProduct(); } - - /** - * Get min and max qty for wishlist form. - * - * @return array - */ - public function getMinMaxQty(): array - { - $stockItem = $this->stockRegistry->getStockItem( - $this->getItem()->getProduct()->getId(), - $this->getItem()->getProduct()->getStore()->getWebsiteId() - ); - - $params = []; - - $params['minAllowed'] = (float)$stockItem->getMinSaleQty(); - if ($stockItem->getMaxSaleQty()) { - $params['maxAllowed'] = (float)$stockItem->getMaxSaleQty(); - } else { - $params['maxAllowed'] = (float)StockDataFilter::MAX_QTY_VALUE; - } - - return $params; - } } diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml index 920b387441ada..87a77526e0af5 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml @@ -87,26 +87,4 @@ <click selector="{{StorefrontCustomerWishlistProductSection.productUpdateWishList}}" stepKey="submitUpdateWishlist"/> <see selector="{{StorefrontMessagesSection.success}}" userInput="{{product.name}} has been updated in your Wish List." stepKey="successMessage"/> </actionGroup> - - <actionGroup name="StorefrontCustomerEditProductInWishlistMakeQuantityValidationError"> - <arguments> - <argument name="product"/> - <argument name="description" type="string"/> - <argument name="quantity" type="string"/> - <argument name="errorNum" type="string"/> - </arguments> - <scrollToTopOfPage stepKey="scrollToTop1"/> - <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.productInfoByName(product.name)}}" stepKey="mouseOverOnProduct" /> - <fillField selector="{{StorefrontCustomerWishlistProductSection.productDescription(product.name)}}" userInput="{{description}}" stepKey="fillDescription"/> - <fillField selector="{{StorefrontCustomerWishlistProductSection.productQuantity(product.name)}}" userInput="{{quantity}}" stepKey="fillQuantity"/> - <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.productAddAllToCart}}" stepKey="mouseOver1"/> - <click selector="{{StorefrontCustomerWishlistProductSection.productUpdateWishList}}" stepKey="clickAddToWishlistButton"/> - <waitForElement selector="{{StorefrontCustomerWishlistProductSection.productQtyError(product.name)}}" stepKey="waitForErrorMessage"/> - <scrollToTopOfPage stepKey="scrollToTop2"/> - - <!--Check error message--> - <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.productInfoByName(product.name)}}" stepKey="wishlistMoveMouseOverProduct" /> - <see selector="{{StorefrontCustomerWishlistProductSection.productQtyError(product.name)}}" userInput="The maximum you may purchase is {{errorNum}}." stepKey="checkQtyError"/> - <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.productAddAllToCart}}" stepKey="mouseOver2"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml index ea9c4748f67b1..88d68a460da74 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml @@ -17,9 +17,7 @@ <element name="productImageByImageName" type="text" selector="//main//li//a//img[contains(@src, '{{var1}}')]" parameterized="true"/> <element name="productDescription" type="input" selector="//a[contains(text(), '{{productName}}')]/ancestor::div[@class='product-item-info']//textarea[@class='product-item-comment']" parameterized="true"/> <element name="productQuantity" type="input" selector="//a[contains(text(), '{{productName}}')]/ancestor::div[@class='product-item-info']//input[@class='input-text qty']" parameterized="true"/> - <element name="productEditButtonByName" type="button" selector="//li[.//a[contains(text(), '{{var1}}')]]//span[contains(text(), 'Edit')]" parameterized="true"/> <element name="productUpdateWishList" type="button" selector=".column.main .actions-toolbar .action.update" timeout="30"/> <element name="productAddAllToCart" type="button" selector=".column.main .actions-toolbar .action.tocart" timeout="30"/> - <element name="productQtyError" type="text" selector="//li[.//a[contains(text(), '{{var1}}')]]//div[@class='mage-error']" parameterized="true" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontCheckAmountLimitWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontCheckAmountLimitWishlistTest.xml deleted file mode 100644 index 3bc924a7e60fa..0000000000000 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontCheckAmountLimitWishlistTest.xml +++ /dev/null @@ -1,60 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontCheckAmountLimitWishlistTest"> - <annotations> - <stories value="MAGETWO-73613: My Wishlist - quantity input box issue"/> - <title value="Check amount limit for Wishlist"/> - <description value="Check amount limit for Wishlist with different config settings"/> - <features value="Wishlist"/> - <severity value="AVERAGE"/> - <testCaseId value="MAGETWO-96606"/> - <group value="wishlist"/> - </annotations> - <before> - <createData entity="SimpleSubCategory" stepKey="createCategory"/> - <createData entity="SimpleProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <createData entity="Simple_US_Customer" stepKey="createCustomer"/> - </before> - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <actionGroup ref="CustomerLogoutStorefrontActionGroup" stepKey="logoutCustomerAccount"/> - <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <createData entity="DefaultProductStockOptions" stepKey="changeToDefaultQtyAllowAmount"/> - </after> - <!--Login as Customer--> - <actionGroup ref="CustomerLoginOnStorefront" stepKey="loginToStorefrontAccount"> - <argument name="customer" value="$$createCustomer$$"/> - </actionGroup> - <!--Go to category page--> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="goToCreateCategoryPage"/> - <!--Add created product to Wish List--> - <actionGroup ref="StorefrontCustomerAddCategoryProductToWishlistActionGroup" stepKey="addSimpleProduct1ToWishlist"> - <argument name="productVar" value="$$createProduct$$"/> - </actionGroup> - <actionGroup ref="StorefrontCustomerEditProductInWishlistMakeQuantityValidationError" stepKey="checkWishListError1"> - <argument name="product" value="$$createProduct$$"/> - <argument name="description" value="It`s my dream"/> - <argument name="quantity" value="1234567890"/> - <argument name="errorNum" value="10000"/> - </actionGroup> - <createData entity="ProductStockOptions" stepKey="changeDefaultQtyAllowAmount"/> - <!--Go to wishlist page--> - <amOnPage url="{{StorefrontCustomerWishlistPage.url}}" stepKey="amOnWishlist" /> - <actionGroup ref="StorefrontCustomerEditProductInWishlistMakeQuantityValidationError" stepKey="checkWishListError2"> - <argument name="product" value="$$createProduct$$"/> - <argument name="description" value="It`s my dream"/> - <argument name="quantity" value="1234567890"/> - <argument name="errorNum" value="99999999"/> - </actionGroup> - </test> -</tests> diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml index 49c35331b7868..e2f22c111bdd2 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml @@ -11,7 +11,6 @@ /** @var \Magento\Wishlist\Model\Item $item */ $item = $block->getItem(); $product = $item->getProduct(); -$allowedQty = $block->getMinMaxQty(); ?> <?php foreach ($block->getChildNames() as $childName): ?> <?= /* @noEscape */ $block->getLayout()->renderElement($childName, false) ?> @@ -22,7 +21,7 @@ $allowedQty = $block->getMinMaxQty(); <div class="field qty"> <label class="label" for="qty[<?= $block->escapeHtmlAttr($item->getId()) ?>]"><span><?= $block->escapeHtml(__('Qty')) ?></span></label> <div class="control"> - <input type="number" data-role="qty" id="qty[<?= $block->escapeHtmlAttr($item->getId()) ?>]" class="input-text qty" data-validate="{'required-number':true,'validate-greater-than-zero':true, 'validate-item-quantity':{'minAllowed':<?= /* @noEscape */ $allowedQty['minAllowed'] ?>,'maxAllowed':<?= /* @noEscape */ $allowedQty['maxAllowed'] ?>}}" + <input type="number" data-role="qty" id="qty[<?= $block->escapeHtmlAttr($item->getId()) ?>]" class="input-text qty" data-validate="{'required-number':true 'validate-greater-than-zero':true}" name="qty[<?= $block->escapeHtmlAttr($item->getId()) ?>]" value="<?= /* @noEscape */ (int)($block->getAddToCartQty($item) * 1) ?>"> </div> </div> diff --git a/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js b/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js index cab130f7c2104..7ce934317263b 100644 --- a/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js +++ b/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js @@ -63,6 +63,12 @@ define([ isFileUploaded = false, self = this; + if (event.handleObj.selector == this.options.qtyInfo) { //eslint-disable-line eqeqeq + this._updateAddToWishlistButton({}); + event.stopPropagation(); + + return; + } $(event.handleObj.selector).each(function (index, element) { if ($(element).is('input[type=text]') || $(element).is('input[type=email]') || @@ -83,7 +89,9 @@ define([ } }); - this.bindFormSubmit(isFileUploaded); + if (isFileUploaded) { + this.bindFormSubmit(); + } this._updateAddToWishlistButton(dataToAdd); event.stopPropagation(); }, @@ -187,45 +195,34 @@ define([ /** * Bind form submit. - * - * @param {Boolean} isFileUploaded */ - bindFormSubmit: function (isFileUploaded) { + bindFormSubmit: function () { var self = this; $('[data-action="add-to-wishlist"]').on('click', function (event) { var element, params, form, action; - if (!$($(self.options.qtyInfo).closest('form')).valid()) { - event.stopPropagation(); - event.preventDefault(); - - return; - } - - if (isFileUploaded) { - - element = $('input[type=file]' + self.options.customOptionsInfo); - params = $(event.currentTarget).data('post'); - form = $(element).closest('form'); - action = params.action; + event.stopPropagation(); + event.preventDefault(); - if (params.data.id) { - $('<input>', { - type: 'hidden', - name: 'id', - value: params.data.id - }).appendTo(form); - } + element = $('input[type=file]' + self.options.customOptionsInfo); + params = $(event.currentTarget).data('post'); + form = $(element).closest('form'); + action = params.action; - if (params.data.uenc) { - action += 'uenc/' + params.data.uenc; - } + if (params.data.id) { + $('<input>', { + type: 'hidden', + name: 'id', + value: params.data.id + }).appendTo(form); + } - $(form).attr('action', action).submit(); - event.stopPropagation(); - event.preventDefault(); + if (params.data.uenc) { + action += 'uenc/' + params.data.uenc; } + + $(form).attr('action', action).submit(); }); } }); From b4f47f947d6e6b50518553dd9c22e4e722fb86b9 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Mon, 11 Feb 2019 17:22:36 +0200 Subject: [PATCH 0593/1295] ENGCOM-4000: Integration test fix. --- .../Magento/Rule/Model/Condition/Product/AbstractProduct.php | 2 +- .../Magento/CatalogWidget/Block/Product/ProductListTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php b/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php index b7dd1d6c55f4e..bedac8c1108e5 100644 --- a/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php +++ b/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php @@ -713,7 +713,7 @@ protected function _getAttributeSetId($productId) public function getOperatorForValidate() { $operator = $this->getOperator(); - if (in_array($this->getInputType(), ['category'])) { + if ('category' === $this->getInputType()) { if ($operator == '==') { $operator = '{}'; } elseif ($operator == '!=') { diff --git a/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php b/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php index 08e9ebbd1f9f0..4251e01f4299a 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php @@ -83,7 +83,7 @@ public function testCreateCollectionWithMultipleSkuCondition() { $encodedConditions = '^[`1`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Combine`,' . '`aggregator`:`all`,`value`:`1`,`new_child`:``^],`1--1`:^[`type`:`Magento||CatalogWidget||Model||Rule|' . - '|Condition||Product`,`attribute`:`sku`,`operator`:`==`,`value`:`simple1, simple2`^]^]'; + '|Condition||Product`,`attribute`:`sku`,`operator`:`()`,`value`:`simple1, simple2`^]^]'; $this->block->setData('conditions_encoded', $encodedConditions); $this->performAssertions(2); } From a93901cbb4cc1a70794c64359a373550dd2ae6e9 Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@adobe.com> Date: Mon, 11 Feb 2019 10:20:30 -0600 Subject: [PATCH 0594/1295] MQE-1430: bug fix in dev/tests/functional/utils/command.php etc --- .../Mtf/Util/Protocol/CurlTransport/BackendDecorator.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php index 44ddb16a18741..2fc26a975e159 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php @@ -71,6 +71,8 @@ protected function authorize() $urls = []; $originalUrl = rtrim($_ENV['app_backend_url'], '/') . '/'; $urls[] = $originalUrl; + // It could be the case that the page needs a refresh, so we will try the original one twice + $urls[] = $originalUrl; if (strpos($originalUrl, '/index.php') !== false) { $url2 = str_replace('/index.php', '', $originalUrl); } else { From bd1e5380e64634a2275d224c2c08e8d1dcd974e7 Mon Sep 17 00:00:00 2001 From: Janak <janak@krishtechnolabs.com> Date: Sat, 15 Dec 2018 12:51:01 +0530 Subject: [PATCH 0595/1295] Fixed store switcher not work multistore setup with different product urls issue --- .../UrlRewrite/Model/StoreSwitcher/RewriteUrl.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php b/app/code/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php index 2b6f9e87e3de2..1ccdb92c03dff 100644 --- a/app/code/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php +++ b/app/code/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php @@ -66,13 +66,19 @@ public function switch(StoreInterface $fromStore, StoreInterface $targetStore, s ]); if ($oldRewrite) { // look for url rewrite match on the target store - $currentRewrite = $this->urlFinder->findOneByData([ - UrlRewrite::REQUEST_PATH => $urlPath, + $currentRewrites = $this->urlFinder->findAllByData([ + UrlRewrite::TARGET_PATH => $oldRewrite->getTargetPath(), UrlRewrite::STORE_ID => $targetStore->getId(), ]); - if (null === $currentRewrite) { + + if (null === $currentRewrites || empty($currentRewrites)) { /** @var \Magento\Framework\App\Response\Http $response */ $targetUrl = $targetStore->getBaseUrl(); + } else { + foreach($currentRewrites as $rewrite) { + $targetUrl = $targetStore->getBaseUrl() . $rewrite->getRequestPath(); + break; + } } } From a8861035b34da209b00d4a37a7704208853c0d0c Mon Sep 17 00:00:00 2001 From: Janak <janak@krishtechnolabs.com> Date: Sat, 15 Dec 2018 15:02:18 +0530 Subject: [PATCH 0596/1295] Fixed code as per given instrunction in codacy --- .../UrlRewrite/Model/StoreSwitcher/RewriteUrl.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php b/app/code/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php index 1ccdb92c03dff..cf80b91c81a24 100644 --- a/app/code/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php +++ b/app/code/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php @@ -70,15 +70,14 @@ public function switch(StoreInterface $fromStore, StoreInterface $targetStore, s UrlRewrite::TARGET_PATH => $oldRewrite->getTargetPath(), UrlRewrite::STORE_ID => $targetStore->getId(), ]); - if (null === $currentRewrites || empty($currentRewrites)) { /** @var \Magento\Framework\App\Response\Http $response */ $targetUrl = $targetStore->getBaseUrl(); - } else { - foreach($currentRewrites as $rewrite) { - $targetUrl = $targetStore->getBaseUrl() . $rewrite->getRequestPath(); - break; - } + return $targetUrl; + } + foreach ($currentRewrites as $rewrite) { + $targetUrl = $targetStore->getBaseUrl() . $rewrite->getRequestPath(); + break; } } From 78df524058bfeb23368cb865047e185aec80345b Mon Sep 17 00:00:00 2001 From: Janak <janak@krishtechnolabs.com> Date: Sat, 22 Dec 2018 11:24:30 +0530 Subject: [PATCH 0597/1295] Code updated as per reviever instrunctions --- app/code/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php b/app/code/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php index cf80b91c81a24..43eafef3b127b 100644 --- a/app/code/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php +++ b/app/code/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php @@ -77,7 +77,6 @@ public function switch(StoreInterface $fromStore, StoreInterface $targetStore, s } foreach ($currentRewrites as $rewrite) { $targetUrl = $targetStore->getBaseUrl() . $rewrite->getRequestPath(); - break; } } From 7ebc03ffcd0a4ace268488112e101971cc2ad661 Mon Sep 17 00:00:00 2001 From: Janak <janak@krishtechnolabs.com> Date: Fri, 25 Jan 2019 12:00:33 +0530 Subject: [PATCH 0598/1295] Fixed Travis CI build failed --- .../UrlRewrite/Model/StoreSwitcher/RewriteUrl.php | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php b/app/code/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php index 43eafef3b127b..a6cb4081965dd 100644 --- a/app/code/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php +++ b/app/code/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php @@ -65,21 +65,16 @@ public function switch(StoreInterface $fromStore, StoreInterface $targetStore, s UrlRewrite::STORE_ID => $oldStoreId, ]); if ($oldRewrite) { + $targetUrl = $targetStore->getBaseUrl(); // look for url rewrite match on the target store - $currentRewrites = $this->urlFinder->findAllByData([ + $currentRewrite = $this->urlFinder->findOneByData([ UrlRewrite::TARGET_PATH => $oldRewrite->getTargetPath(), UrlRewrite::STORE_ID => $targetStore->getId(), ]); - if (null === $currentRewrites || empty($currentRewrites)) { - /** @var \Magento\Framework\App\Response\Http $response */ - $targetUrl = $targetStore->getBaseUrl(); - return $targetUrl; - } - foreach ($currentRewrites as $rewrite) { - $targetUrl = $targetStore->getBaseUrl() . $rewrite->getRequestPath(); + if ($currentRewrite) { + $targetUrl .= $currentRewrite->getRequestPath(); } } - return $targetUrl; } } From 7e4fe31d4b3ae19c8a462598b77b4bdf4fa4c36f Mon Sep 17 00:00:00 2001 From: Janak <janak@krishtechnolabs.com> Date: Mon, 28 Jan 2019 15:25:02 +0530 Subject: [PATCH 0599/1295] Fixed travis failed issue --- .../Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php index d23389718e2c2..c3c8a6536fee9 100644 --- a/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php @@ -90,7 +90,8 @@ public function testSwitchToExistingPage() $storeRepository = $this->objectManager->create(\Magento\Store\Api\StoreRepositoryInterface::class); $toStore = $storeRepository->get($toStoreCode); - $redirectUrl = $expectedUrl = "http://localhost/page-c"; + $redirectUrl = "http://localhost/index.php/page-c/"; + $expectedUrl = "http://localhost/index.php/page-c-on-2nd-store"; $this->assertEquals($expectedUrl, $this->storeSwitcher->switch($fromStore, $toStore, $redirectUrl)); } From bbabad9c2ecb7fb7bf0152e601a95f282adaea91 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 12 Feb 2019 10:45:43 +0200 Subject: [PATCH 0600/1295] MAGETWO-98182: [FT] [MFTF] StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest fails because of bad design --- ...roductCustomOptionsDifferentStoreViewsTest.xml | 15 ++++++--------- .../Test/Mftf/ActionGroup/CheckoutActionGroup.xml | 2 +- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml index 5a3ac4ddd8495..e7054839ef4d0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml @@ -64,8 +64,7 @@ </actionGroup> <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearWebsitesGridFilters"/> - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearOrdersGridFilters"/> + <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearOrdersGridFilter"/> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearProductsGridFilters"/> @@ -191,11 +190,9 @@ <!-- Open Order --> - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearOrdersGridFilters"/> - <fillField selector="{{OrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="fillOrderNum"/> - <click selector="{{OrdersGridSection.submitSearch}}" stepKey="submitSearch"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask4"/> + <actionGroup ref="filterOrderGridById" stepKey="openOrdersGrid"> + <argument name="orderId" value="{$grabOrderNumber}"/> + </actionGroup> <click selector="{{OrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> <waitForPageLoad time="30" stepKey="waitForPageLoad10"/> @@ -212,7 +209,7 @@ <argument name="storeView" value="customStoreFR"/> </actionGroup> - <amOnPage url="{{StorefrontHomePage.url}}$$createProduct.custom_attributes[url_key]$$.html" stepKey="amOnProduct2Page"/> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="amOnProduct2Page"/> <seeElement selector="{{StorefrontProductInfoMainSection.productOptionDropDownTitle('FR Custom Options 1')}}" stepKey="seeProductFrOptionDropDownTitle"/> <seeElement selector="{{StorefrontProductInfoMainSection.productOptionDropDownOptionTitle('FR Custom Options 1', 'FR option1')}}" stepKey="productFrOptionDropDownOptionTitle1"/> @@ -302,7 +299,7 @@ <!--Go to Product Page--> - <amOnPage url="{{StorefrontHomePage.url}}$$createProduct.custom_attributes[url_key]$$.html" stepKey="amOnProduct2Page2"/> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="amOnProduct2Page2"/> <seeElement selector="{{StorefrontProductInfoMainSection.productOptionDropDownTitle('Custom Options 1')}}" stepKey="seeProductOptionDropDownTitle1"/> <seeElement selector="{{StorefrontProductInfoMainSection.productOptionDropDownOptionTitle('Custom Options 1', 'option1')}}" stepKey="seeProductOptionDropDownOptionTitle3"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml index e3dbb3870896d..d20616b4384d1 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml @@ -7,7 +7,7 @@ --> <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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Checkout select Check/Money Order payment --> <actionGroup name="CheckoutSelectCheckMoneyOrderPaymentActionGroup"> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> From c1afe36da67f59029dcb566431214509a544b3f7 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Tue, 12 Feb 2019 12:33:49 +0200 Subject: [PATCH 0601/1295] MAGETWO-86215: Static blocks with same ID appear in place of correct block --- .../Magento/Cms/Model/ResourceModel/Block.php | 2 +- .../ActionGroup/AdminCmsBlockActionGroup.xml | 46 +++++++++++++++++++ .../AdminCreateNewCMSBlockActionGroup.xml | 21 --------- .../AdminDeleteCMSBlockActionGroup.xml | 21 --------- .../Test/Mftf/Page/AdminCmsBlockNewPage.xml | 14 ++++++ ...teStaticBlockOnDuplicateIdentifierTest.xml | 33 ++++++------- 6 files changed, 74 insertions(+), 63 deletions(-) create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml delete mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCreateNewCMSBlockActionGroup.xml delete mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminDeleteCMSBlockActionGroup.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/Page/AdminCmsBlockNewPage.xml diff --git a/app/code/Magento/Cms/Model/ResourceModel/Block.php b/app/code/Magento/Cms/Model/ResourceModel/Block.php index 30e817713755c..6eae76f9790c9 100644 --- a/app/code/Magento/Cms/Model/ResourceModel/Block.php +++ b/app/code/Magento/Cms/Model/ResourceModel/Block.php @@ -200,7 +200,7 @@ public function getIsUniqueBlockToStores(AbstractModel $object) 'cb.' . $linkField . ' = cbs.' . $linkField, [] ) - ->where('cb.identifier = ? ', $object->getData('identifier')); + ->where('cb.identifier = ? ', $object->getData('identifier')); if (!$isDefaultStore) { $select->where('cbs.store_id IN (?)', $stores); diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml new file mode 100644 index 0000000000000..0ee5b3280a7c3 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="FillCmsBlockForm"> + <arguments> + <argument name="title" type="string" defaultValue="{{DefaultCmsBlock.title}}"/> + <argument name="identifier" type="string" defaultValue="{{DefaultCmsBlock.identifier}}"/> + <argument name="store" type="string" defaultValue="All Store View"/> + <argument name="content" type="string" defaultValue="{{DefaultCmsBlock.content}}"/> + </arguments> + <fillField selector="{{AdminCmsBlockContentSection.title}}" userInput="{{title}}" stepKey="fillFieldTitle"/> + <fillField selector="{{AdminCmsBlockContentSection.identifier}}" userInput="{{identifier}}" stepKey="fillFieldIdentifier"/> + <selectOption selector="{{AdminCmsBlockContentSection.storeView}}" userInput="{{store}}" stepKey="selectStore" /> + <fillField selector="{{AdminCmsBlockContentSection.content}}" userInput="{{content}}" stepKey="fillContentField"/> + </actionGroup> + <actionGroup name="DeleteCmsBlockActionGroup"> + <arguments> + <argument name="cmsBlockTitle" type="string" defaultValue="{{DefaultCmsBlock.title}}"/> + </arguments> + <amOnPage url="{{AdminCmsBlockGridPage.url}}" stepKey="navigateToCmsBlockListingPage"/> + <click selector="{{CmsPagesPageActionsSection.select(cmsBlockTitle)}}" stepKey="clickOnSelect"/> + <click selector="{{CmsPagesPageActionsSection.delete(cmsBlockTitle)}}" stepKey="clickOnDelete"/> + <waitForElementVisible selector="{{AdminConfirmationModalSection.message}}" stepKey="waitForConfirmModal"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirm"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the block." stepKey="verifyBlockIsDeleted"/> + </actionGroup> + <actionGroup name="NavigateToCreateCmsBlockActionGroup"> + <amOnPage url="{{AdminCmsBlockNewPage.url}}" stepKey="navigateToCreateCmsBlockPage"/> + </actionGroup> + <actionGroup name="SaveCmsBlockActionGroup"> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveBlockButton"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the block." stepKey="verifyMessage"/> + </actionGroup> + <actionGroup name="SaveCmsBlockWithErrorActionGroup" extends="SaveCmsBlockActionGroup"> + <arguments> + <argument name="errorMessage" type="string" defaultValue="A block identifier with the same properties already exists in the selected store."/> + </arguments> + <see selector="{{AdminMessagesSection.error}}" userInput="{{errorMessage}}" stepKey="verifyMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCreateNewCMSBlockActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCreateNewCMSBlockActionGroup.xml deleted file mode 100644 index ce240f9f47e99..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCreateNewCMSBlockActionGroup.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="FillCMSBlockInformation"> - <arguments> - <argument name="title" type="string" defaultValue="{{DefaultCmsBlock.title}}"/> - <argument name="identifier" type="string" defaultValue="{{DefaultCmsBlock.identifier}}"/> - <argument name="content" type="string" defaultValue="{{DefaultCmsBlock.content}}"/> - </arguments> - <fillField selector="{{AdminCmsBlockContentSection.title}}" userInput="{{title}}" stepKey="fillFieldTitle"/> - <fillField selector="{{AdminCmsBlockContentSection.identifier}}" userInput="{{identifier}}" stepKey="fillFieldIdentifier"/> - <selectOption selector="{{AdminCmsBlockContentSection.storeView}}" userInput="All Store View" stepKey="selectAllStoreView" /> - <fillField selector="{{AdminCmsBlockContentSection.content}}" userInput="{{content}}" stepKey="fillContentField"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminDeleteCMSBlockActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminDeleteCMSBlockActionGroup.xml deleted file mode 100644 index b5ff9c3639c8b..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminDeleteCMSBlockActionGroup.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="DeleteCMSBlockActionGroup"> - <arguments> - <argument name="cmsBlockTitle" type="string" defaultValue="{{DefaultCmsBlock.title}}"/> - </arguments> - <amOnPage url="{{AdminCmsBlockGridPage.url}}" stepKey="navigateToCMSPagesGrid"/> - <click selector="{{CmsPagesPageActionsSection.select(cmsBlockTitle)}}" stepKey="clickOnSelect"/> - <click selector="{{CmsPagesPageActionsSection.delete(cmsBlockTitle)}}" stepKey="clickOnDelete"/> - <waitForElementVisible selector="{{AdminConfirmationModalSection.message}}" stepKey="waitForConfirmModal"/> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirm"/> - <see userInput="You deleted the block." stepKey="VerifyBlockIsDeleted"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsBlockNewPage.xml b/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsBlockNewPage.xml new file mode 100644 index 0000000000000..2868d832ad762 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsBlockNewPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminCmsBlockNewPage" url="/cms/block/new/" area="admin" module="Magento_Cms"> + <section name="AdminCmsBlockContentSection"/> + </page> +</pages> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/CheckCreateStaticBlockOnDuplicateIdentifierTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/CheckCreateStaticBlockOnDuplicateIdentifierTest.xml index 371e6529590e7..f7ed9776c066c 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/CheckCreateStaticBlockOnDuplicateIdentifierTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/CheckCreateStaticBlockOnDuplicateIdentifierTest.xml @@ -11,12 +11,13 @@ <test name="CheckCreateStaticBlockOnDuplicateIdentifierTest"> <annotations> <features value="Cms"/> - <stories value="Create a CMS Block via the Admin"/> + <stories value="Create CMS Block"/> <title value="Check static blocks: ID should be unique per Store View"/> <description value="Check static blocks: ID should be unique per Store View"/> <severity value="CRITICAL"/> <testCaseId value="MC-13912"/> - <group value="Cms"/> + <useCaseId value="MAGETWO-86215"/> + <group value="cms"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> @@ -35,27 +36,19 @@ </actionGroup> </before> <after> - <actionGroup ref="DeleteCMSBlockActionGroup" stepKey="deleteCMSBlockActionGroup"/> + <actionGroup ref="DeleteCmsBlockActionGroup" stepKey="deleteCMSBlockActionGroup"/> <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> <argument name="websiteName" value="{{SecondWebsite.name}}"/> </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> </after> - <!--Go to Cms blocks page--> - <amOnPage url="{{AdminCmsBlockGridPage.url}}" stepKey="navigateToCMSBlocksGridPage"/> - <!--Click to create new block--> - <click selector="{{AdminMainActionsSection.add}}" stepKey="addNewBlock"/> - <actionGroup ref="FillCMSBlockInformation" stepKey="FillOutBlockContent"/> - <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveBlockButton"/> - <see seelctor="AdminMessagesSection.success" userInput="You saved the block." stepKey="verifyBlockIsSaved"/> - <!--Go to Cms blocks page--> - <amOnPage url="{{AdminCmsBlockGridPage.url}}" stepKey="navigateToCMSBlocksGridPage1"/> - <click selector="{{AdminMainActionsSection.add}}" stepKey="addNewBlock1"/> - <actionGroup ref="FillCMSBlockInformation" stepKey="FillOutBlockContent1"/> - <unselectOption selector="{{AdminCmsBlockContentSection.storeView}}" userInput="All Store Views" stepKey="unselectAllStoreViewOption" /> - <selectOption selector="{{AdminCmsBlockContentSection.storeView}}" parameterArray="[Default Store View, {{SecondStoreUnique.name}}]" stepKey="selectStoreViews" /> - <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveBlockButton1"/> - <waitForPageLoad stepKey="waitForErrorMessageAppear"/> - <!--Verify that corresponding message is displayed--> - <see selector="{{AdminMessagesSection.error}}" userInput="A block identifier with the same properties already exists in the selected store." stepKey="verifyErrorMessage"/> + <actionGroup ref="NavigateToCreateCmsBlockActionGroup" stepKey="NavigateToCreateCmsBlock"/> + <actionGroup ref="FillCmsBlockForm" stepKey="fillCmsBlockForm"/> + <actionGroup ref="SaveCmsBlockActionGroup" stepKey="saveCmsBlock"/> + <actionGroup ref="NavigateToCreateCmsBlockActionGroup" stepKey="NavigateToCreateCmsBlock1"/> + <actionGroup ref="FillCmsBlockForm" stepKey="fillCmsBlockForm1"> + <argument name="store" parameterArray="[{{_defaultStore.name}},{{SecondStoreUnique.name}}]"/> + </actionGroup> + <actionGroup ref="SaveCmsBlockWithErrorActionGroup" stepKey="saveCmsBlock1"/> </test> </tests> From 73c4bca05b737f3962687dcb3c9a888d1a3110e5 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Tue, 12 Feb 2019 14:33:24 +0200 Subject: [PATCH 0602/1295] MAGETWO-90192: Prefix in checkout not starting with blank value --- .../Block/Checkout/LayoutProcessor.php | 38 ++++++++----------- .../Mftf/Section/CheckoutShippingSection.xml | 3 ++ .../Magento/Customer/Block/Widget/Name.php | 7 +++- .../CustomerNameAddressOptionsConfigData.xml | 38 +++++++++++++++++++ ...tomer_name_address_options_config-meta.xml | 25 ++++++++++++ 5 files changed, 88 insertions(+), 23 deletions(-) create mode 100644 app/code/Magento/Customer/Test/Mftf/Data/CustomerNameAddressOptionsConfigData.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Metadata/customer_name_address_options_config-meta.xml diff --git a/app/code/Magento/Checkout/Block/Checkout/LayoutProcessor.php b/app/code/Magento/Checkout/Block/Checkout/LayoutProcessor.php index f47e514948d69..c5d4d68b06225 100644 --- a/app/code/Magento/Checkout/Block/Checkout/LayoutProcessor.php +++ b/app/code/Magento/Checkout/Block/Checkout/LayoutProcessor.php @@ -6,8 +6,11 @@ namespace Magento\Checkout\Block\Checkout; use Magento\Checkout\Helper\Data; +use Magento\Customer\Model\AttributeMetadataDataProvider; +use Magento\Customer\Model\Options; use Magento\Framework\App\ObjectManager; use Magento\Store\Api\StoreResolverInterface; +use Magento\Ui\Component\Form\AttributeMapper; /** * Class LayoutProcessor @@ -15,12 +18,12 @@ class LayoutProcessor implements \Magento\Checkout\Block\Checkout\LayoutProcessorInterface { /** - * @var \Magento\Customer\Model\AttributeMetadataDataProvider + * @var AttributeMetadataDataProvider */ private $attributeMetadataDataProvider; /** - * @var \Magento\Ui\Component\Form\AttributeMapper + * @var AttributeMapper */ protected $attributeMapper; @@ -30,7 +33,7 @@ class LayoutProcessor implements \Magento\Checkout\Block\Checkout\LayoutProcesso protected $merger; /** - * @var \Magento\Customer\Model\Options + * @var Options */ private $options; @@ -50,30 +53,21 @@ class LayoutProcessor implements \Magento\Checkout\Block\Checkout\LayoutProcesso private $shippingConfig; /** - * @param \Magento\Customer\Model\AttributeMetadataDataProvider $attributeMetadataDataProvider - * @param \Magento\Ui\Component\Form\AttributeMapper $attributeMapper + * @param AttributeMetadataDataProvider $attributeMetadataDataProvider + * @param AttributeMapper $attributeMapper * @param AttributeMerger $merger + * @param Options|null $options */ public function __construct( - \Magento\Customer\Model\AttributeMetadataDataProvider $attributeMetadataDataProvider, - \Magento\Ui\Component\Form\AttributeMapper $attributeMapper, - AttributeMerger $merger + AttributeMetadataDataProvider $attributeMetadataDataProvider, + AttributeMapper $attributeMapper, + AttributeMerger $merger, + Options $options = null ) { $this->attributeMetadataDataProvider = $attributeMetadataDataProvider; $this->attributeMapper = $attributeMapper; $this->merger = $merger; - } - - /** - * @deprecated 100.0.11 - * @return \Magento\Customer\Model\Options - */ - private function getOptions() - { - if (!is_object($this->options)) { - $this->options = ObjectManager::getInstance()->get(\Magento\Customer\Model\Options::class); - } - return $this->options; + $this->options = $options ?? ObjectManager::getInstance()->get(Options::class); } /** @@ -143,8 +137,8 @@ private function convertElementsToSelect($elements, $attributesToConvert) public function process($jsLayout) { $attributesToConvert = [ - 'prefix' => [$this->getOptions(), 'getNamePrefixOptions'], - 'suffix' => [$this->getOptions(), 'getNameSuffixOptions'], + 'prefix' => [$this->options, 'getNamePrefixOptions'], + 'suffix' => [$this->options, 'getNameSuffixOptions'], ]; $elements = $this->getAddressAttributes(); diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml index 76c9e9f674e6a..66cd480051905 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml @@ -33,5 +33,8 @@ <element name="addCountry" type="select" selector="#shipping-new-address-form select[name='country_id']"/> <element name="addSaveButton" type="button" selector=".action.primary.action-save-address"/> <element name="editActiveAddress" type="button" selector="//div[@class='shipping-address-item selected-item']//span[text()='Edit']" timeout="30"/> + <element name="namePrefix" type="input" selector="select[name=prefix]"/> + <element name="nameSuffix" type="selector" selector="[name='suffix']"/> + <element name="nameSuffixOption" type="text" selector="select[name='suffix'] option[value='{{arg}}']" parameterized="true" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Customer/Block/Widget/Name.php b/app/code/Magento/Customer/Block/Widget/Name.php index 35f3bbefb8f00..4411f40fe41f3 100644 --- a/app/code/Magento/Customer/Block/Widget/Name.php +++ b/app/code/Magento/Customer/Block/Widget/Name.php @@ -245,10 +245,13 @@ public function getStoreLabel($attributeCode) */ public function getAttributeValidationClass($attributeCode) { - return $this->_addressHelper->getAttributeValidationClass($attributeCode); + $attributeMetadata = $this->_getAttribute($attributeCode); + return $attributeMetadata ? $attributeMetadata->getFrontendClass() : ''; } /** + * Check if attribute is required + * * @param string $attributeCode * @return bool */ @@ -259,6 +262,8 @@ private function _isAttributeRequired($attributeCode) } /** + * Check if attribute is visible + * * @param string $attributeCode * @return bool */ diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerNameAddressOptionsConfigData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerNameAddressOptionsConfigData.xml new file mode 100644 index 0000000000000..1331f288286e5 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerNameAddressOptionsConfigData.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="CustomerPrefixOptions" type="customer_name_address_options"> + <requiredEntity type="prefix_options">PrefixOptions</requiredEntity> + </entity> + <entity name="PrefixOptions" type="prefix_options"> + <data key="value">Mr;Mrs;Ms;Dr</data> + </entity> + + <entity name="DefaultCustomerPrefixOptions" type="customer_name_address_options"> + <requiredEntity type="prefix_options">DefaultPrefixOptions</requiredEntity> + </entity> + <entity name="DefaultPrefixOptions" type="prefix_options"> + <data key="value"></data> + </entity> + + <entity name="CustomerSuffixOptions" type="customer_name_address_options"> + <requiredEntity type="suffix_options">SuffixOptions</requiredEntity> + </entity> + <entity name="SuffixOptions" type="suffix_options"> + <data key="value">Jr;Sr</data> + </entity> + + <entity name="DefaultCustomerSuffixOptions" type="customer_name_address_options"> + <requiredEntity type="suffix_options">DefaultSuffixOptions</requiredEntity> + </entity> + <entity name="DefaultSuffixOptions" type="suffix_options"> + <data key="value"></data> + </entity> +</entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Metadata/customer_name_address_options_config-meta.xml b/app/code/Magento/Customer/Test/Mftf/Metadata/customer_name_address_options_config-meta.xml new file mode 100644 index 0000000000000..07175a09fe3e1 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Metadata/customer_name_address_options_config-meta.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="CustomerConfigNameAddressOptions" dataType="customer_name_address_options" type="create" auth="adminFormKey" url="/admin/system_config/save/section/customer/" method="POST" successRegex="/messages-message-success/"> + <object key="groups" dataType="customer_name_address_options"> + <object key="address" dataType="customer_name_address_options"> + <object key="fields" dataType="customer_name_address_options"> + <object key="prefix_options" dataType="prefix_options"> + <field key="value">string</field> + </object> + <object key="suffix_options" dataType="suffix_options"> + <field key="value">string</field> + </object> + </object> + </object> + </object> + </operation> +</operations> From 5d3744335b52453ff7245c5629e0a6c357dca945 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Tue, 12 Feb 2019 14:53:15 +0200 Subject: [PATCH 0603/1295] MAGETWO-76424: Additional blank option in country dropdown --- .../Page/StorefrontCustomerAddressNewPage.xml | 13 +++++ ...StorefrontCustomerAddNewAddressSection.xml | 13 +++++ .../ResourceModel/Country/Collection.php | 3 +- .../Mftf/Data/CountryOptionsConfigData.xml | 16 ++++++ .../Metadata/country_options_config-meta.xml | 34 ++++++++++++ ...untryDropdownWithOneAllowedCountryTest.xml | 54 +++++++++++++++++++ 6 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerAddressNewPage.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAddNewAddressSection.xml create mode 100644 app/code/Magento/Directory/Test/Mftf/Data/CountryOptionsConfigData.xml create mode 100644 app/code/Magento/Directory/Test/Mftf/Metadata/country_options_config-meta.xml create mode 100644 app/code/Magento/Directory/Test/Mftf/Test/StorefrontCheckingCountryDropdownWithOneAllowedCountryTest.xml diff --git a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerAddressNewPage.xml b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerAddressNewPage.xml new file mode 100644 index 0000000000000..35134157bd373 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerAddressNewPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="StorefrontCustomerAddressNewPage" url="/customer/address/new" area="storefront" module="Magento_Customer"> + <section name="StorefrontCustomerAddNewAddressSection"/> + </page> +</pages> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAddNewAddressSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAddNewAddressSection.xml new file mode 100644 index 0000000000000..218c5e6c2f39f --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAddNewAddressSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCustomerAddNewAddressSection"> + <element name="country" type="select" selector=".form-address-edit select#country" /> + <element name="countryEmptyOption" type="select" selector=".form-address-edit select#country option[value='']"/> + </section> +</sections> diff --git a/app/code/Magento/Directory/Model/ResourceModel/Country/Collection.php b/app/code/Magento/Directory/Model/ResourceModel/Country/Collection.php index da5ad64f8b239..4394e360df2d4 100644 --- a/app/code/Magento/Directory/Model/ResourceModel/Country/Collection.php +++ b/app/code/Magento/Directory/Model/ResourceModel/Country/Collection.php @@ -207,6 +207,7 @@ public function getItemById($countryId) /** * Add filter by country code to collection. + * * $countryCode can be either array of country codes or string representing one country code. * $iso can be either array containing 'iso2', 'iso3' values or string with containing one of that values directly. * The collection will contain countries where at least one of contry $iso fields matches $countryCode. @@ -299,7 +300,7 @@ public function toOptionArray($emptyLabel = ' ') } $options[] = $option; } - if ($emptyLabel !== false && count($options) > 0) { + if ($emptyLabel !== false && count($options) > 1) { array_unshift($options, ['value' => '', 'label' => $emptyLabel]); } diff --git a/app/code/Magento/Directory/Test/Mftf/Data/CountryOptionsConfigData.xml b/app/code/Magento/Directory/Test/Mftf/Data/CountryOptionsConfigData.xml new file mode 100644 index 0000000000000..ceb21f5986487 --- /dev/null +++ b/app/code/Magento/Directory/Test/Mftf/Data/CountryOptionsConfigData.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="DefaultAllowCountriesConfig" type="allow_countries_config_default"> + <data key="value">0</data> + </entity> + <entity name="SetAllowCountriesConfigUS" type="allow_countries_config"> + <data key="value">US</data> + </entity> +</entities> diff --git a/app/code/Magento/Directory/Test/Mftf/Metadata/country_options_config-meta.xml b/app/code/Magento/Directory/Test/Mftf/Metadata/country_options_config-meta.xml new file mode 100644 index 0000000000000..63193dd206d73 --- /dev/null +++ b/app/code/Magento/Directory/Test/Mftf/Metadata/country_options_config-meta.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="AllowCountriesConfigDefault" dataType="allow_countries_config_default" type="create" auth="adminFormKey" url="/admin/system_config/save/section/general/" method="POST" successRegex="/messages-message-success/"> + <object key="groups" dataType="allow_countries_config_default"> + <object key="country" dataType="allow_countries_config_default"> + <object key="fields" dataType="allow_countries_config_default"> + <object key="allow" dataType="allow_countries_config_default"> + <object key="inherit" dataType="allow_countries_config_default"> + <field key="value">integer</field> + </object> + </object> + </object> + </object> + </object> + </operation> + <operation name="AllowCountriesConfig" dataType="allow_countries_config" type="create" auth="adminFormKey" url="/admin/system_config/save/section/general/" method="POST" successRegex="/messages-message-success/"> + <object key="groups" dataType="allow_countries_config"> + <object key="country" dataType="allow_countries_config"> + <object key="fields" dataType="allow_countries_config"> + <object key="allow" dataType="allow_countries_config"> + <field key="value">string</field> + </object> + </object> + </object> + </object> + </operation> +</operations> diff --git a/app/code/Magento/Directory/Test/Mftf/Test/StorefrontCheckingCountryDropdownWithOneAllowedCountryTest.xml b/app/code/Magento/Directory/Test/Mftf/Test/StorefrontCheckingCountryDropdownWithOneAllowedCountryTest.xml new file mode 100644 index 0000000000000..10cb0fb53541e --- /dev/null +++ b/app/code/Magento/Directory/Test/Mftf/Test/StorefrontCheckingCountryDropdownWithOneAllowedCountryTest.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCheckingCountryDropdownWithOneAllowedCountryTest"> + <annotations> + <features value="Directory"/> + <stories value="Country dropdown"/> + <title value="Checking country dropdown with one allowed country"/> + <description value="Checking country dropdown with one allowed country"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14810"/> + <useCaseId value="MAGETWO-76424"/> + <group value="directory"/> + <group value="customer"/> + </annotations> + <before> + <!--Create customer--> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <!--Set "Allow Countries" config to US only--> + <createData entity="SetAllowCountriesConfigUS" stepKey="setAllowCountriesConfigToUS"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + <!--Login as Customer--> + <actionGroup ref="CustomerLoginOnStorefront" stepKey="customerLogin"> + <argument name="customer" value="$createCustomer$" /> + </actionGroup> + </before> + <after> + <!--Delete Customer--> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <!--Set "Allow Countries" config to default--> + <createData entity="DefaultAllowCountriesConfig" stepKey="setAllowCountriesConfigToDefault"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + <!--Logout--> + <actionGroup ref="CustomerLogoutStorefrontActionGroup" stepKey="logoutFromStorefront"/> + </after> + + <!--Go to "Add New Address" page--> + <amOnPage url="{{StorefrontCustomerAddressNewPage.url}}" stepKey="goToAddNewAddressPage"/> + <!--Click on Country dropdown--> + <click selector="{{StorefrontCustomerAddNewAddressSection.country}}" stepKey="clickOnCountryDropdown"/> + <!--Check dropdown options--> + <see selector="{{StorefrontCustomerAddNewAddressSection.country}}" userInput="United States" stepKey="seeUSInCountryDropdown"/> + <dontSee selector="{{StorefrontCustomerAddNewAddressSection.country}}" userInput="Brazil" stepKey="dontSeeBrazilInCountryDropdown"/> + <dontSeeElement selector="{{StorefrontCustomerAddNewAddressSection.countryEmptyOption}}" stepKey="dontSeeEmptyOptionInCountryDropdown"/> + </test> +</tests> From 08142ff4aa1efb22ce10056fd7afc08ff2d63c18 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Tue, 12 Feb 2019 15:27:19 +0200 Subject: [PATCH 0604/1295] MAGETWO-97242: Stabilize AdminAbleToShipPartiallyInvoicedItemsTest --- ...inAbleToShipPartiallyInvoicedItemsTest.xml | 3 - .../order/creditmemo/create/items.phtml | 67 +++++++++++-------- .../order/invoice/create/items.phtml | 53 ++++++++------- 3 files changed, 67 insertions(+), 56 deletions(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml index cb761dc358abb..d196783744c46 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml @@ -14,9 +14,6 @@ <severity value="CRITICAL"/> <testCaseId value="MAGETWO-95524"/> <group value="sales"/> - <skip> - <issueId value="MAGETWO-97664"/> - </skip> </annotations> <before> <createData entity="_defaultCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/items.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/items.phtml index a73740c249b67..dc7d8fbebd46a 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/items.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/items.phtml @@ -140,67 +140,76 @@ </section> <script> -require(['jquery', 'prototype'], function(jQuery){ +require(['jquery'], function(jQuery){ //<![CDATA[ -var submitButtons = $$('.submit-button'); -var updateButtons = $$('.update-button,.update-totals-button'); -var fields = $$('.qty-input,.order-subtotal-table input[type="text"]'); +var submitButtons = jQuery('.submit-button'); +var updateButtons = jQuery('.update-button,.update-totals-button'); +var fields = jQuery('.qty-input,.order-subtotal-table input[type="text"]'); -updateButtons.each(function (elem) {elem.disabled=true;elem.addClassName('disabled');}); +function enableButtons(buttons) { + buttons.removeClass('disabled').prop('disabled',false); +}; -for(var i=0;i<fields.length;i++){ - fields[i].observe('change', checkButtonsRelation) - fields[i].baseValue = fields[i].value; -} +function disableButtons(buttons) { + buttons.addClass('disabled').prop('disabled',true); +}; + +disableButtons(updateButtons); + +fields.on('change', checkButtonsRelation); +fields.each(function (i,elem) { + elem.baseValue = elem.value; +}); function checkButtonsRelation() { var hasChanges = false; - fields.each(function (elem) { + fields.each(function (i,elem) { if (elem.baseValue != elem.value) { hasChanges = true; } }.bind(this)); if (hasChanges) { - submitButtons.each(function (elem) {elem.disabled=true;elem.addClassName('disabled');}); - updateButtons.each(function (elem) {elem.disabled=false;elem.removeClassName('disabled');}); + disableButtons(submitButtons); + enableButtons(updateButtons); } else { - submitButtons.each(function (elem) {elem.disabled=false;elem.removeClassName('disabled');}); - updateButtons.each(function (elem) {elem.disabled=true;elem.addClassName('disabled');}); + enableButtons(submitButtons); + disableButtons(updateButtons); } } submitCreditMemo = function() { - if ($('creditmemo_do_offline')) $('creditmemo_do_offline').value=0; + var creditMemoOffline = jQuery('#creditmemo_do_offline'); + if (creditMemoOffline.length) { + creditMemoOffline.prop('value',0); + } // Temporary solution will be replaced after refactoring order functionality jQuery('#edit_form').triggerHandler('save'); } submitCreditMemoOffline = function() { - if ($('creditmemo_do_offline')) $('creditmemo_do_offline').value=1; + var creditMemoOffline = jQuery('#creditmemo_do_offline'); + if (creditMemoOffline.length) { + creditMemoOffline.prop('value', 1); + } // Temporary solution will be replaced after refactoring order functionality jQuery('#edit_form').triggerHandler('save'); } -var sendEmailCheckbox = $('send_email'); - -if (sendEmailCheckbox) { - var notifyCustomerCheckbox = $('notify_customer'); - var creditmemoCommentText = $('creditmemo_comment_text'); - Event.observe(sendEmailCheckbox, 'change', bindSendEmail); +var sendEmailCheckbox = jQuery('#send_email'); +if (sendEmailCheckbox.length) { + var notifyCustomerCheckbox = jQuery('#notify_customer'); + sendEmailCheckbox.on('change', bindSendEmail); bindSendEmail(); } -function bindSendEmail() -{ - if (sendEmailCheckbox.checked == true) { - notifyCustomerCheckbox.disabled = false; - //creditmemoCommentText.disabled = false; +function bindSendEmail() { + if (sendEmailCheckbox.prop('checked') == true) { + notifyCustomerCheckbox.prop('disabled',false); } else { - notifyCustomerCheckbox.disabled = true; - //creditmemoCommentText.disabled = true; + notifyCustomerCheckbox.prop('disabled',true); } } diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/invoice/create/items.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/invoice/create/items.phtml index 4a77c3b166de9..8efdca2fce8e2 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/invoice/create/items.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/invoice/create/items.phtml @@ -134,56 +134,61 @@ </section> <script> -require(['jquery', 'prototype'], function(jQuery){ +require(['jquery'], function(jQuery){ //<![CDATA[ -var submitButtons = $$('.submit-button'); -var updateButtons = $$('.update-button'); +var submitButtons = jQuery('.submit-button'); +var updateButtons = jQuery('.update-button'); var enableSubmitButtons = <?= (int) !$block->getDisableSubmitButton() ?>; -var fields = $$('.qty-input'); +var fields = jQuery('.qty-input'); -updateButtons.each(function (elem) {elem.disabled=true;elem.addClassName('disabled');}); +function enableButtons(buttons) { + buttons.removeClass('disabled').prop('disabled',false); +}; -for(var i=0;i<fields.length;i++){ - jQuery(fields[i]).on('keyup', checkButtonsRelation); - fields[i].baseValue = fields[i].value; -} +function disableButtons(buttons) { + buttons.addClass('disabled').prop('disabled',true); +}; + +disableButtons(updateButtons); + +fields.on('keyup', checkButtonsRelation); +fields.each(function (i,elem) { + elem.baseValue = elem.value; +}); function checkButtonsRelation() { var hasChanges = false; - fields.each(function (elem) { + fields.each(function (i,elem) { if (elem.baseValue != elem.value) { hasChanges = true; } }.bind(this)); if (hasChanges) { - submitButtons.each(function (elem) {elem.disabled=true;elem.addClassName('disabled');}); - updateButtons.each(function (elem) {elem.disabled=false;elem.removeClassName('disabled');}); + disableButtons(submitButtons); + enableButtons(updateButtons); } else { if (enableSubmitButtons) { - submitButtons.each(function (elem) {elem.disabled=false;elem.removeClassName('disabled');}); + enableButtons(submitButtons); } - updateButtons.each(function (elem) {elem.disabled=true;elem.addClassName('disabled');}); + disableButtons(updateButtons); } } -var sendEmailCheckbox = $('send_email'); -if (sendEmailCheckbox) { - var notifyCustomerCheckbox = $('notify_customer'); - var invoiceCommentText = $('invoice_comment_text'); - Event.observe(sendEmailCheckbox, 'change', bindSendEmail); +var sendEmailCheckbox = jQuery('#send_email'); +if (sendEmailCheckbox.length) { + var notifyCustomerCheckbox = jQuery('#notify_customer'); + sendEmailCheckbox.on('change', bindSendEmail); bindSendEmail(); } function bindSendEmail() { - if (sendEmailCheckbox.checked == true) { - notifyCustomerCheckbox.disabled = false; - //invoiceCommentText.disabled = false; + if (sendEmailCheckbox.prop('checked') == true) { + notifyCustomerCheckbox.prop('disabled',false); } else { - notifyCustomerCheckbox.disabled = true; - //invoiceCommentText.disabled = true; + notifyCustomerCheckbox.prop('disabled',true); } } From 115b1bcbd38495e269ada16e0515b883104a7f7f Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 12 Feb 2019 15:38:52 +0200 Subject: [PATCH 0605/1295] MAGETWO-96636: MFTF test fix --- ...efrontBraintreeFillCardDataActionGroup.xml | 32 +++++++++++++++++++ .../Test/Mftf/Data/BraintreeData.xml | 3 +- ...ntBraintreePaymentConfigurationSection.xml | 24 ++++++++++++++ .../Data/ProductConfigurableAttributeData.xml | 2 +- .../AdminCreditMemoActionGroup.xml | 27 ++++++++++++++++ .../Section/AdminCreditMemoTotalSection.xml | 4 ++- .../AdminOrderDetailsMainActionsSection.xml | 2 +- .../Section/AdminOrderViewTabsSection.xml | 1 + 8 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 app/code/Magento/Braintree/Test/Mftf/ActionGroup/StorefrontBraintreeFillCardDataActionGroup.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/StorefrontBraintreePaymentConfigurationSection.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreditMemoActionGroup.xml diff --git a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/StorefrontBraintreeFillCardDataActionGroup.xml b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/StorefrontBraintreeFillCardDataActionGroup.xml new file mode 100644 index 0000000000000..561d660a1bb75 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/StorefrontBraintreeFillCardDataActionGroup.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontBraintreeFillCardDataActionGroup"> + <arguments> + <argument name="cardData" defaultValue="BraintreeCard"/> + </arguments> + <switchToIFrame selector="{{StorefrontBraintreePaymentConfigurationSection.cardFrame}}" stepKey="switchToIframe"/> + <waitForElementVisible selector="{{StorefrontBraintreePaymentConfigurationSection.cardCode}}" stepKey="waitCardCodeElement"/> + <fillField selector="{{StorefrontBraintreePaymentConfigurationSection.cardCode}}" userInput="{{cardData.card_number}}" stepKey="setCardCode"/> + <switchToIFrame stepKey="switchBack"/> + <switchToIFrame selector="{{StorefrontBraintreePaymentConfigurationSection.monthFrame}}" stepKey="switchToIframe1"/> + <waitForElementVisible selector="{{StorefrontBraintreePaymentConfigurationSection.month}}" stepKey="waitMonthElement"/> + <fillField selector="{{StorefrontBraintreePaymentConfigurationSection.month}}" userInput="{{cardData.exp_month}}" stepKey="setMonth"/> + <switchToIFrame stepKey="switchBack1"/> + <switchToIFrame selector="{{StorefrontBraintreePaymentConfigurationSection.yearFrame}}" stepKey="switchToIframe2"/> + <waitForElementVisible selector="{{StorefrontBraintreePaymentConfigurationSection.year}}" stepKey="waitYearElement"/> + <fillField selector="{{StorefrontBraintreePaymentConfigurationSection.year}}" userInput="{{cardData.exp_year}}" stepKey="setYear"/> + <switchToIFrame stepKey="switchBack2"/> + <switchToIFrame selector="{{StorefrontBraintreePaymentConfigurationSection.codeFrame}}" stepKey="switchToIframe3"/> + <waitForElementVisible selector="{{StorefrontBraintreePaymentConfigurationSection.verificationNumber}}" stepKey="waitVerificationNumber"/> + <fillField selector="{{StorefrontBraintreePaymentConfigurationSection.verificationNumber}}" userInput="{{cardData.cvv}}" stepKey="setVerificationNumber"/> + <switchToIFrame stepKey="SwitchBackToWindow"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml b/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml index 0c57147ca950e..4e8526a2b0e2b 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml @@ -7,7 +7,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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="SampleBraintreeConfig" type="braintree_config_state"> <requiredEntity type="title">SampleTitle</requiredEntity> <requiredEntity type="payment_action">SamplePaymentAction</requiredEntity> @@ -113,5 +113,4 @@ <data key="exp_year">20</data> <data key="cvv">113</data> </entity> - </entities> diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontBraintreePaymentConfigurationSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontBraintreePaymentConfigurationSection.xml new file mode 100644 index 0000000000000..d4cc3fe0f113d --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontBraintreePaymentConfigurationSection.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontBraintreePaymentConfigurationSection"> + <element name="creditCard" type="radio" selector="#braintree"/> + <element name="paymentMethodContainer" type="block" selector=".payment-method-braintree"/> + <element name="paymentMethod" type="radio" selector="//div[@class='payment-group']//input[contains(@id, 'braintree_cc_vault_')]"/> + <element name="cardFrame" type="iframe" selector="braintree-hosted-field-number"/> + <element name="monthFrame" type="iframe" selector="braintree-hosted-field-expirationMonth"/> + <element name="yearFrame" type="iframe" selector="braintree-hosted-field-expirationYear"/> + <element name="codeFrame" type="iframe" selector="braintree-hosted-field-cvv"/> + <element name="cardCode" type="input" selector="#credit-card-number"/> + <element name="month" type="input" selector="#expiration-month"/> + <element name="year" type="input" selector="#expiration-year"/> + <element name="verificationNumber" type="input" selector="#cvv"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductConfigurableAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductConfigurableAttributeData.xml index 0446a9db14f1a..8d588a192ed90 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductConfigurableAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductConfigurableAttributeData.xml @@ -7,7 +7,7 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/dataProfileSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="colorProductAttribute" type="product_attribute"> <data key="default_label" unique="suffix">Color</data> <data key="attribute_quantity">1</data> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreditMemoActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreditMemoActionGroup.xml new file mode 100644 index 0000000000000..7197afb4a20df --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreditMemoActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StartCreateCreditMemoFromOrderPage"> + <click selector="{{AdminOrderDetailsMainActionsSection.creditMemo}}" stepKey="clickCreditMemoButton"/> + <conditionalClick selector="{{AdminConfirmationModalSection.ok}}" dependentSelector="{{AdminConfirmationModalSection.ok}}" + visible="true" stepKey="acceptModal"/> + <seeInCurrentUrl url="{{AdminCreditMemoNewPage.url}}" stepKey="seeNewCreditMemoUrl"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Memo" stepKey="seeNewCreditMemoPageTitle"/> + </actionGroup> + + <actionGroup name="SubmitCreditMemo"> + <waitForElementNotVisible selector="{{AdminCreditMemoTotalSection.submitRefundOfflineDisabled}}" stepKey="waitButtonEnabled"/> + <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickSubmitCreditMemo"/> + <waitForElementVisible selector="{{AdminMessagesSection.successMessage}}" stepKey="waitForMessageAppears"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="You created the credit memo." stepKey="seeCreditMemoCreateSuccess"/> + <grabFromCurrentUrl regex="~/order_id/(\d+)/~" stepKey="grabOrderId"/> + <seeInCurrentUrl url="{{AdminOrderDetailsPage.url('$grabOrderId')}}" stepKey="seeViewOrderPageCreditMemo"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoTotalSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoTotalSection.xml index 648f519455b56..73ea29836e242 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoTotalSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoTotalSection.xml @@ -13,6 +13,8 @@ <element name="refundShipping" type="input" selector=".order-subtotal-table tbody input[name='creditmemo[shipping_amount]']"/> <element name="updateTotals" type="button" selector=".update-totals-button" timeout="30"/> <element name="submitRefundOffline" type="button" selector=".order-totals-actions button[title='Refund Offline']" timeout="30"/> - <element name="submitRefundOfflineEnabled" type="button" selector=".order-totals-actions button[data-ui-id='order-items-submit-button'][class='action-default scalable save submit-button primary']" timeout="30"/> + <element name="submitRefundOfflineEnabled" type="button" selector=".order-totals-actions button[data-ui-id='order-items-submit-button'].action-default.scalable.save.submit-button.primary']" timeout="30"/> + <element name="adjustmentRefund" type="input" selector=".order-subtotal-table tbody input[name='creditmemo[adjustment_positive]']"/> + <element name="submitRefundOfflineDisabled" type="button" selector="//*[@class='order-totals-actions']//button[@title='Refund Offline' and contains(@class, 'disabled')]" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsMainActionsSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsMainActionsSection.xml index eac238584b030..3794def0ac77a 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsMainActionsSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsMainActionsSection.xml @@ -7,7 +7,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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderDetailsMainActionsSection"> <element name="back" type="button" selector="#back" timeout="30"/> <element name="cancel" type="button" selector="#order-view-cancel-button" timeout="30"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderViewTabsSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderViewTabsSection.xml index 78d0a45bce460..0037c482fcd72 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderViewTabsSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderViewTabsSection.xml @@ -12,5 +12,6 @@ <element name="invoices" type="button" selector="#sales_order_view_tabs_order_invoices"/> <element name="creditMemos" type="button" selector="#sales_order_view_tabs_order_creditmemos"/> <element name="shipments" type="button" selector="#sales_order_view_tabs_order_shipments"/> + <element name="commentsHistory" type="button" selector="#sales_order_view_tabs_order_history" timeout="30"/> </section> </sections> From 99e83429832c29904823ff06819bd39e685b973e Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Tue, 12 Feb 2019 16:15:49 +0200 Subject: [PATCH 0606/1295] MAGETWO-86215: Static blocks with same ID appear in place of correct block --- app/code/Magento/Cms/Model/ResourceModel/Block.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Cms/Model/ResourceModel/Block.php b/app/code/Magento/Cms/Model/ResourceModel/Block.php index 6eae76f9790c9..9b4bc5ec3ea11 100644 --- a/app/code/Magento/Cms/Model/ResourceModel/Block.php +++ b/app/code/Magento/Cms/Model/ResourceModel/Block.php @@ -200,7 +200,7 @@ public function getIsUniqueBlockToStores(AbstractModel $object) 'cb.' . $linkField . ' = cbs.' . $linkField, [] ) - ->where('cb.identifier = ? ', $object->getData('identifier')); + ->where('cb.identifier = ?', $object->getData('identifier')); if (!$isDefaultStore) { $select->where('cbs.store_id IN (?)', $stores); From 3c54afad8d0aa02ce4d6e2e1b64fdd19a627c635 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Tue, 12 Feb 2019 17:11:35 +0200 Subject: [PATCH 0607/1295] MAGETWO-86215: Static blocks with same ID appear in place of correct block --- .../Mftf/ActionGroup/AdminCmsBlockActionGroup.xml | 11 ++++++----- ...heckCreateStaticBlockOnDuplicateIdentifierTest.xml | 10 +++++----- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml index 0ee5b3280a7c3..da7a3ea0c16c2 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml @@ -11,21 +11,21 @@ <arguments> <argument name="title" type="string" defaultValue="{{DefaultCmsBlock.title}}"/> <argument name="identifier" type="string" defaultValue="{{DefaultCmsBlock.identifier}}"/> - <argument name="store" type="string" defaultValue="All Store View"/> + <argument name="store" type="string" defaultValue="[All Store View]"/> <argument name="content" type="string" defaultValue="{{DefaultCmsBlock.content}}"/> </arguments> <fillField selector="{{AdminCmsBlockContentSection.title}}" userInput="{{title}}" stepKey="fillFieldTitle"/> <fillField selector="{{AdminCmsBlockContentSection.identifier}}" userInput="{{identifier}}" stepKey="fillFieldIdentifier"/> - <selectOption selector="{{AdminCmsBlockContentSection.storeView}}" userInput="{{store}}" stepKey="selectStore" /> + <selectOption selector="{{AdminCmsBlockContentSection.storeView}}" parameterArray="{{store}}" stepKey="selectStore" /> <fillField selector="{{AdminCmsBlockContentSection.content}}" userInput="{{content}}" stepKey="fillContentField"/> </actionGroup> <actionGroup name="DeleteCmsBlockActionGroup"> <arguments> - <argument name="cmsBlockTitle" type="string" defaultValue="{{DefaultCmsBlock.title}}"/> + <argument name="cmsBlockIdentifier" type="string" defaultValue="{{DefaultCmsBlock.identifier}}"/> </arguments> <amOnPage url="{{AdminCmsBlockGridPage.url}}" stepKey="navigateToCmsBlockListingPage"/> - <click selector="{{CmsPagesPageActionsSection.select(cmsBlockTitle)}}" stepKey="clickOnSelect"/> - <click selector="{{CmsPagesPageActionsSection.delete(cmsBlockTitle)}}" stepKey="clickOnDelete"/> + <click selector="{{CmsPagesPageActionsSection.select(cmsBlockIdentifier)}}" stepKey="clickOnSelect"/> + <click selector="{{CmsPagesPageActionsSection.delete(cmsBlockIdentifier)}}" stepKey="clickOnDelete"/> <waitForElementVisible selector="{{AdminConfirmationModalSection.message}}" stepKey="waitForConfirmModal"/> <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirm"/> <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the block." stepKey="verifyBlockIsDeleted"/> @@ -34,6 +34,7 @@ <amOnPage url="{{AdminCmsBlockNewPage.url}}" stepKey="navigateToCreateCmsBlockPage"/> </actionGroup> <actionGroup name="SaveCmsBlockActionGroup"> + <scrollToTopOfPage stepKey="scrollToTopOfPage"/> <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveBlockButton"/> <see selector="{{AdminMessagesSection.success}}" userInput="You saved the block." stepKey="verifyMessage"/> </actionGroup> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/CheckCreateStaticBlockOnDuplicateIdentifierTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/CheckCreateStaticBlockOnDuplicateIdentifierTest.xml index f7ed9776c066c..e2e510f85cf0a 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/CheckCreateStaticBlockOnDuplicateIdentifierTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/CheckCreateStaticBlockOnDuplicateIdentifierTest.xml @@ -42,13 +42,13 @@ </actionGroup> <actionGroup ref="logout" stepKey="logout"/> </after> - <actionGroup ref="NavigateToCreateCmsBlockActionGroup" stepKey="NavigateToCreateCmsBlock"/> + <actionGroup ref="NavigateToCreateCmsBlockActionGroup" stepKey="navigateToCreateCmsBlock"/> <actionGroup ref="FillCmsBlockForm" stepKey="fillCmsBlockForm"/> <actionGroup ref="SaveCmsBlockActionGroup" stepKey="saveCmsBlock"/> - <actionGroup ref="NavigateToCreateCmsBlockActionGroup" stepKey="NavigateToCreateCmsBlock1"/> - <actionGroup ref="FillCmsBlockForm" stepKey="fillCmsBlockForm1"> - <argument name="store" parameterArray="[{{_defaultStore.name}},{{SecondStoreUnique.name}}]"/> + <actionGroup ref="NavigateToCreateCmsBlockActionGroup" stepKey="navigateToCreateCmsBlock1"/> + <actionGroup ref="FillCmsBlockForm" stepKey="fillDuplicateCmsBlockForm"> + <argument name="store" value="[{{_defaultStore.name}},{{SecondStoreUnique.name}}]"/> </actionGroup> - <actionGroup ref="SaveCmsBlockWithErrorActionGroup" stepKey="saveCmsBlock1"/> + <actionGroup ref="SaveCmsBlockWithErrorActionGroup" stepKey="assertErrorMessageOnSave"/> </test> </tests> From ecc08cbc02d71ca733bcbb88d545730b5ec3713c Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Tue, 12 Feb 2019 17:59:03 +0200 Subject: [PATCH 0608/1295] MAGETWO-86215: Static blocks with same ID appear in place of correct block --- .../Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml | 6 +++--- .../CheckCreateStaticBlockOnDuplicateIdentifierTest.xml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml index da7a3ea0c16c2..62cc5f06fedcc 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml @@ -21,11 +21,11 @@ </actionGroup> <actionGroup name="DeleteCmsBlockActionGroup"> <arguments> - <argument name="cmsBlockIdentifier" type="string" defaultValue="{{DefaultCmsBlock.identifier}}"/> + <argument name="identifier" type="string" defaultValue="{{DefaultCmsBlock.identifier}}"/> </arguments> <amOnPage url="{{AdminCmsBlockGridPage.url}}" stepKey="navigateToCmsBlockListingPage"/> - <click selector="{{CmsPagesPageActionsSection.select(cmsBlockIdentifier)}}" stepKey="clickOnSelect"/> - <click selector="{{CmsPagesPageActionsSection.delete(cmsBlockIdentifier)}}" stepKey="clickOnDelete"/> + <click selector="{{CmsPagesPageActionsSection.select(identifier)}}" stepKey="clickOnSelect"/> + <click selector="{{CmsPagesPageActionsSection.delete(identifier)}}" stepKey="clickOnDelete"/> <waitForElementVisible selector="{{AdminConfirmationModalSection.message}}" stepKey="waitForConfirmModal"/> <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirm"/> <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the block." stepKey="verifyBlockIsDeleted"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/CheckCreateStaticBlockOnDuplicateIdentifierTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/CheckCreateStaticBlockOnDuplicateIdentifierTest.xml index e2e510f85cf0a..b79997578e675 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/CheckCreateStaticBlockOnDuplicateIdentifierTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/CheckCreateStaticBlockOnDuplicateIdentifierTest.xml @@ -45,7 +45,7 @@ <actionGroup ref="NavigateToCreateCmsBlockActionGroup" stepKey="navigateToCreateCmsBlock"/> <actionGroup ref="FillCmsBlockForm" stepKey="fillCmsBlockForm"/> <actionGroup ref="SaveCmsBlockActionGroup" stepKey="saveCmsBlock"/> - <actionGroup ref="NavigateToCreateCmsBlockActionGroup" stepKey="navigateToCreateCmsBlock1"/> + <actionGroup ref="NavigateToCreateCmsBlockActionGroup" stepKey="navigateToCreateDuplicateCmsBlock"/> <actionGroup ref="FillCmsBlockForm" stepKey="fillDuplicateCmsBlockForm"> <argument name="store" value="[{{_defaultStore.name}},{{SecondStoreUnique.name}}]"/> </actionGroup> From 2fc33b4c1a3f39cfe83ec2e7d441dc9329caaef5 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Wed, 6 Feb 2019 17:46:57 +0200 Subject: [PATCH 0609/1295] Covering the successfully adding a valid coupon to cart by an integration test --- .../Controller/Cart/Index/CouponPostTest.php | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/Index/CouponPostTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/Index/CouponPostTest.php index 2036042d0463c..1b387ae378e32 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/Index/CouponPostTest.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/Index/CouponPostTest.php @@ -36,4 +36,35 @@ public function testExecute() \Magento\Framework\Message\MessageInterface::TYPE_ERROR ); } + + /** + * Testing by adding a valid coupon to cart + * + * @magentoDataFixture Magento/Checkout/_files/quote_with_virtual_product_and_address.php + * @magentoDataFixture Magento/Usps/Fixtures/cart_rule_coupon_free_shipping.php + * @return void + */ + public function testAddingValidCoupon(): void + { + /** @var $session \Magento\Checkout\Model\Session */ + $session = $this->_objectManager->create(\Magento\Checkout\Model\Session::class); + $quote = $session->getQuote(); + $quote->setData('trigger_recollect', 1)->setTotalsCollectedFlag(true); + + $couponCode = 'IMPHBR852R61'; + $inputData = [ + 'remove' => 0, + 'coupon_code' => $couponCode + ]; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($inputData); + $this->dispatch( + 'checkout/cart/couponPost/' + ); + + $this->assertSessionMessages( + $this->equalTo(['You used coupon code "' . $couponCode . '".']), + \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + ); + } } From 53f344d0390b170366736b93ce096354e072a8ac Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Thu, 7 Feb 2019 19:56:52 +0200 Subject: [PATCH 0610/1295] Covering the Send to friend by integration tests --- .../SendFriend/Controller/SendmailTest.php | 144 ++++++++++++++++++ .../_files/disable_allow_guest_config.php | 24 +++ 2 files changed, 168 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php create mode 100644 dev/tests/integration/testsuite/Magento/SendFriend/_files/disable_allow_guest_config.php diff --git a/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php b/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php new file mode 100644 index 0000000000000..6c34592a1a386 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php @@ -0,0 +1,144 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SendFriend\Controller; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Customer\Model\Session; +use Magento\Framework\Data\Form\FormKey; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\Request; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Class SendmailTest + */ +class SendmailTest extends AbstractController +{ + /** + * Share the product to friend as logged in customer + * + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/SendFriend/_files/disable_allow_guest_config.php + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Catalog/_files/products.php + */ + public function testSendActionAsLoggedIn() + { + $product = $this->getProduct(); + $this->login(1); + $this->prepareRequestData(); + + $this->dispatch('sendfriend/product/sendmail/id/' . $product->getId()); + $this->assertSessionMessages( + $this->equalTo(['The link to a friend was sent.']), + MessageInterface::TYPE_SUCCESS + ); + } + + /** + * Share the product to friend as guest customer + * + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoConfigFixture default_store sendfriend/email/enabled 1 + * @magentoConfigFixture default_store sendfriend/email/allow_guest 1 + * @magentoDataFixture Magento/Catalog/_files/products.php + */ + public function testSendActionAsGuest() + { + $product = $this->getProduct(); + $this->prepareRequestData(); + + $this->dispatch('sendfriend/product/sendmail/id/' . $product->getId()); + $this->assertSessionMessages( + $this->equalTo(['The link to a friend was sent.']), + MessageInterface::TYPE_SUCCESS + ); + } + + /** + * Share the product to friend as guest customer with invalid post data + * + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoConfigFixture default_store sendfriend/email/enabled 1 + * @magentoConfigFixture default_store sendfriend/email/allow_guest 1 + * @magentoDataFixture Magento/Catalog/_files/products.php + */ + public function testSendActionAsGuestWithInvalidData() + { + $product = $this->getProduct(); + $this->prepareRequestData(true); + + $this->dispatch('sendfriend/product/sendmail/id/' . $product->getId()); + $this->assertSessionMessages( + $this->equalTo(['Invalid Sender Email']), + MessageInterface::TYPE_ERROR + ); + } + + /** + * @return ProductInterface + */ + private function getProduct() + { + return $this->_objectManager->get(ProductRepositoryInterface::class)->get('custom-design-simple-product'); + } + + /** + * Login the user + * + * @param string $customerId Customer to mark as logged in for the session + * @return void + */ + protected function login($customerId) + { + /** @var Session $session */ + $session = Bootstrap::getObjectManager() + ->get(Session::class); + $session->loginById($customerId); + } + + /** + * @param bool $invalidData + * @return void + */ + private function prepareRequestData($invalidData = false) + { + /** @var FormKey $formKey */ + $formKey = $this->_objectManager->get(FormKey::class); + $post = [ + 'sender' => [ + 'name' => 'Test', + 'email' => 'test@example.com', + 'message' => 'Message', + ], + 'recipients' => [ + 'name' => [ + 'Recipient 1', + 'Recipient 2' + ], + 'email' => [ + 'r1@example.com', + 'r2@example.com' + ] + ], + 'form_key' => $formKey->getFormKey(), + ]; + if ($invalidData) { + unset($post['sender']['email']); + } + + $this->getRequest()->setMethod(Request::METHOD_POST); + $this->getRequest()->setPostValue($post); + } +} + diff --git a/dev/tests/integration/testsuite/Magento/SendFriend/_files/disable_allow_guest_config.php b/dev/tests/integration/testsuite/Magento/SendFriend/_files/disable_allow_guest_config.php new file mode 100644 index 0000000000000..202a396132485 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SendFriend/_files/disable_allow_guest_config.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\App\Config\Value; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var Value $config */ +$config = Bootstrap::getObjectManager()->create(Value::class); +$config->setPath('sendfriend/email/enabled'); +$config->setScope('default'); +$config->setScopeId(0); +$config->setValue(1); +$config->save(); + +/** @var Value $config */ +$config = Bootstrap::getObjectManager()->create(Value::class); +$config->setPath('sendfriend/email/allow_guest'); +$config->setScope('default'); +$config->setScopeId(0); +$config->setValue(0); +$config->save(); From 8b98fb991f2d58f0780e9c522dd38f48acb7ae94 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Fri, 8 Feb 2019 13:08:41 +0200 Subject: [PATCH 0611/1295] Fix static test. --- .../testsuite/Magento/SendFriend/Controller/SendmailTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php b/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php index 6c34592a1a386..a075398e9cdb7 100644 --- a/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php +++ b/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php @@ -141,4 +141,3 @@ private function prepareRequestData($invalidData = false) $this->getRequest()->setPostValue($post); } } - From ee8868922bd027510c707a092d2c0f0d04019b71 Mon Sep 17 00:00:00 2001 From: Pratik Oza <magepratik@gmail.com> Date: Sun, 3 Feb 2019 12:26:41 +0530 Subject: [PATCH 0612/1295] Magento backend catalog cost without currency symbol --- .../view/adminhtml/ui_component/product_listing.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml index 65090fa3ac461..578281f44c4cf 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml @@ -190,6 +190,13 @@ <label translate="true">Websites</label> </settings> </column> + <column name="cost" class="Magento\Catalog\Ui\Component\Listing\Columns\Price" sortOrder="120"> + <settings> + <addField>true</addField> + <filter>textRange</filter> + <label translate="true">Cost</label> + </settings> + </column> <actionsColumn name="actions" class="Magento\Catalog\Ui\Component\Listing\Columns\ProductActions" sortOrder="200"> <settings> <indexField>entity_id</indexField> From 1a31aeedf706a3e48eb7a9d695a89ad384260626 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Tue, 12 Feb 2019 20:11:39 +0200 Subject: [PATCH 0613/1295] Small code adjustments --- .../Checkout/Controller/Cart/Index/CouponPostTest.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/Index/CouponPostTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/Index/CouponPostTest.php index 1b387ae378e32..661593b65adca 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/Index/CouponPostTest.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/Index/CouponPostTest.php @@ -6,6 +6,8 @@ namespace Magento\Checkout\Controller\Cart\Index; +use Magento\Framework\App\Request\Http as HttpRequest; + /** * @magentoDbIsolation enabled */ @@ -44,7 +46,7 @@ public function testExecute() * @magentoDataFixture Magento/Usps/Fixtures/cart_rule_coupon_free_shipping.php * @return void */ - public function testAddingValidCoupon(): void + public function testAddingValidCoupon() { /** @var $session \Magento\Checkout\Model\Session */ $session = $this->_objectManager->create(\Magento\Checkout\Model\Session::class); @@ -63,7 +65,7 @@ public function testAddingValidCoupon(): void ); $this->assertSessionMessages( - $this->equalTo(['You used coupon code "' . $couponCode . '".']), + $this->equalTo(['You used coupon code "' . $couponCode . '".']), \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS ); } From 9612b4221fac3d1cbac3ca1dac338e92fb92dc3c Mon Sep 17 00:00:00 2001 From: Dharmendra <dharmendra@wagento.com> Date: Tue, 5 Feb 2019 14:51:41 +0530 Subject: [PATCH 0614/1295] Solve custom option dropdown issue --- .../Ui/DataProvider/Product/Form/Modifier/CustomOptions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CustomOptions.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CustomOptions.php index e557c8a377681..cf65c2ff2b206 100755 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CustomOptions.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CustomOptions.php @@ -11,6 +11,7 @@ use Magento\Catalog\Model\Config\Source\Product\Options\Price as ProductOptionsPrice; use Magento\Framework\UrlInterface; use Magento\Framework\Stdlib\ArrayManager; +use Magento\Ui\Component\Form\Element\Hidden; use Magento\Ui\Component\Modal; use Magento\Ui\Component\Container; use Magento\Ui\Component\DynamicRows; @@ -867,10 +868,9 @@ protected function getPositionFieldConfig($sortOrder) 'data' => [ 'config' => [ 'componentType' => Field::NAME, - 'formElement' => Input::NAME, + 'formElement' => Hidden::NAME, 'dataScope' => static::FIELD_SORT_ORDER_NAME, 'dataType' => Number::NAME, - 'visible' => false, 'sortOrder' => $sortOrder, ], ], From b82bf3928a9ed34c944f85a152014c1e63bbffcc Mon Sep 17 00:00:00 2001 From: Dominic <d.fernando@ism-apac.com> Date: Mon, 4 Feb 2019 19:28:47 +0530 Subject: [PATCH 0615/1295] Fixed redirection issue in Admin Sontent Schedule --- .../Backend/Controller/Adminhtml/System/Design/Save.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 0228b48f7f11e..01498d939b310 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Design/Save.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Design/Save.php @@ -54,10 +54,10 @@ public function execute() } catch (\Exception $e) { $this->messageManager->addErrorMessage($e->getMessage()); $this->_objectManager->get(\Magento\Backend\Model\Session::class)->setDesignData($data); - return $resultRedirect->setPath('adminhtml/*/', ['id' => $design->getId()]); + return $resultRedirect->setPath('*/*/edit', ['id' => $design->getId()]); } } - return $resultRedirect->setPath('adminhtml/*/'); + return $resultRedirect->setPath('*/*/'); } } From c59b2313757b63892a83723280566668ae24e54d Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Tue, 5 Feb 2019 15:34:31 +0200 Subject: [PATCH 0616/1295] ENGCOM-4110: Static test fix. --- .../Backend/Controller/Adminhtml/System/Design/Save.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) 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 01498d939b310..25cfb61d658c3 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Design/Save.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Design/Save.php @@ -6,7 +6,12 @@ */ namespace Magento\Backend\Controller\Adminhtml\System\Design; -class Save extends \Magento\Backend\Controller\Adminhtml\System\Design +use Magento\Framework\App\Action\HttpPostActionInterface; + +/** + * Save design action. + */ +class Save extends \Magento\Backend\Controller\Adminhtml\System\Design implements HttpPostActionInterface { /** * Filtering posted data. Converting localized data if needed @@ -26,6 +31,8 @@ protected function _filterPostData($data) } /** + * Save design action. + * * @return \Magento\Backend\Model\View\Result\Redirect */ public function execute() From 46f3169043144e4dbbb78988c92417e4997ec603 Mon Sep 17 00:00:00 2001 From: Arvinda kumar <arvindakumar@cedcommerce.com> Date: Mon, 4 Feb 2019 15:12:24 +0530 Subject: [PATCH 0617/1295] Extra space from left in top message section (Notification section) Extra space from left in top message section (Notification section) --- .../Magento_AdminNotification/web/css/source/_module.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_AdminNotification/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_AdminNotification/web/css/source/_module.less index c1b684aef354f..afd91ed3dbde6 100644 --- a/app/design/adminhtml/Magento/backend/Magento_AdminNotification/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_AdminNotification/web/css/source/_module.less @@ -83,7 +83,7 @@ .message-system-short-wrapper { overflow: hidden; - padding: 0 1.5rem 0 @indent__l; + padding: 0 1.5rem 0 1rem; } .message-system-collapsible { From 2220707dd64518b64fe87a4b56fb2972fed9ac8e Mon Sep 17 00:00:00 2001 From: Govind Sharma <govindpokhrelsharma@cedcoss.com> Date: Sun, 3 Feb 2019 13:36:48 +0530 Subject: [PATCH 0618/1295] Update _temp.less --- .../adminhtml/Magento/backend/web/css/source/forms/_temp.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less index 0bfa454adbf0d..cad7a38dc1ef5 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less @@ -253,7 +253,7 @@ label.mage-error { .captcha-reload { float: right; - vertical-align: middle; + margin-top: 15px; } } } From ce53cee6adf5a58978333e42e9c4bc8309e0c0cb Mon Sep 17 00:00:00 2001 From: amol 2jcommerce <amol@2jcommerce.in> Date: Wed, 30 Jan 2019 20:15:09 +0530 Subject: [PATCH 0619/1295] Orders-and-Returns-layout-not-proper --- .../Magento/blank/Magento_Customer/web/css/source/_module.less | 3 ++- .../Magento/luma/Magento_Customer/web/css/source/_module.less | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less index fea9e8e497e40..0ebd722429480 100644 --- a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less @@ -451,7 +451,8 @@ .form.password.reset, .form.send.confirmation, .form.password.forget, - .form.create.account { + .form.create.account, + .form.form-orders-search { min-width: 600px; width: 50%; } diff --git a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less index 5bba92ed20523..460f8e50d59a9 100755 --- a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less @@ -402,7 +402,8 @@ .form.password.reset, .form.send.confirmation, .form.password.forget, - .form.create.account { + .form.create.account, + .form.form-orders-search { min-width: 600px; width: 50%; } From 11cafc3ce9b662ab0ea871c36761f59565343a37 Mon Sep 17 00:00:00 2001 From: hiren <hiren@wagento.com> Date: Sun, 3 Feb 2019 13:39:37 +0530 Subject: [PATCH 0620/1295] Solved swagger response of product attribute option is_default --- .../Directory/Model/ResourceModel/Country/Collection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Directory/Model/ResourceModel/Country/Collection.php b/app/code/Magento/Directory/Model/ResourceModel/Country/Collection.php index da5ad64f8b239..02fed61c44863 100644 --- a/app/code/Magento/Directory/Model/ResourceModel/Country/Collection.php +++ b/app/code/Magento/Directory/Model/ResourceModel/Country/Collection.php @@ -328,7 +328,7 @@ private function addDefaultCountryToOptions(array &$options) foreach ($options as $key => $option) { if (isset($defaultCountry[$option['value']])) { - $options[$key]['is_default'] = $defaultCountry[$option['value']]; + $options[$key]['is_default'] = !empty($defaultCountry[$option['value']]); } } } From 5482702cbe8579dca95a3e60788de614f9c4c5fc Mon Sep 17 00:00:00 2001 From: Arvinda kumar <arvindakumar@cedcommerce.com> Date: Fri, 1 Feb 2019 12:35:10 +0530 Subject: [PATCH 0621/1295] Email to a Friend form not full responsive and remove link not position on correct place Email to a Friend form not full responsive and remove link not position on correct place --- .../web/css/source/_module.less | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/app/design/frontend/Magento/luma/Magento_SendFriend/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_SendFriend/web/css/source/_module.less index baf5468b18485..75ce71dea1e78 100644 --- a/app/design/frontend/Magento/luma/Magento_SendFriend/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_SendFriend/web/css/source/_module.less @@ -44,3 +44,24 @@ } } } +.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { + .form.send.friend { + .fieldset { + .field { + .control { + width: 100%; + } + } + padding-bottom: 10px; + } + .action { + &.remove { + right: 0; + margin-left: 0; + text-align: right; + width: 65px; + top: 100%; + } + } + } +} From bb12b351d2209dc680987fd8835e474fd7858be3 Mon Sep 17 00:00:00 2001 From: Arvinda kumar <arvindakumar@cedcommerce.com> Date: Mon, 4 Feb 2019 14:46:05 +0530 Subject: [PATCH 0622/1295] _module.less updated _module.less updated --- .../luma/Magento_SendFriend/web/css/source/_module.less | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/design/frontend/Magento/luma/Magento_SendFriend/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_SendFriend/web/css/source/_module.less index 75ce71dea1e78..d8f198c57b30c 100644 --- a/app/design/frontend/Magento/luma/Magento_SendFriend/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_SendFriend/web/css/source/_module.less @@ -47,19 +47,17 @@ .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { .form.send.friend { .fieldset { + padding-bottom: @indent__xs; .field { .control { width: 100%; } } - padding-bottom: 10px; } .action { &.remove { right: 0; margin-left: 0; - text-align: right; - width: 65px; top: 100%; } } From 89309ce2c23a454c4f281bfda40551d55bb5c3bc Mon Sep 17 00:00:00 2001 From: Arvinda kumar <arvindakumar@cedcommerce.com> Date: Mon, 4 Feb 2019 14:53:01 +0530 Subject: [PATCH 0623/1295] _module.less updated _module.less updated --- .../web/css/source/_module.less | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/app/design/frontend/Magento/luma/Magento_SendFriend/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_SendFriend/web/css/source/_module.less index d8f198c57b30c..77cef1e9a15f8 100644 --- a/app/design/frontend/Magento/luma/Magento_SendFriend/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_SendFriend/web/css/source/_module.less @@ -10,6 +10,13 @@ & when (@media-common = true) { .form.send.friend { &:extend(.abs-add-fields all); + .fieldset { + .field { + .control { + width: 100%; + } + } + } } .product-social-links .action.mailto.friend { @@ -21,6 +28,7 @@ } } + // // Desktop // _____________________________________________ @@ -47,12 +55,7 @@ .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { .form.send.friend { .fieldset { - padding-bottom: @indent__xs; - .field { - .control { - width: 100%; - } - } + padding-bottom: @indent__xs; } .action { &.remove { From fd220482e5eccf050c080944fc3bd4564eed32b2 Mon Sep 17 00:00:00 2001 From: Arvinda kumar <arvindakumar@cedcommerce.com> Date: Mon, 4 Feb 2019 15:23:33 +0530 Subject: [PATCH 0624/1295] _module.less updated _module.less updated --- .../luma/Magento_SendFriend/web/css/source/_module.less | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/luma/Magento_SendFriend/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_SendFriend/web/css/source/_module.less index 77cef1e9a15f8..0c475b2c59aee 100644 --- a/app/design/frontend/Magento/luma/Magento_SendFriend/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_SendFriend/web/css/source/_module.less @@ -10,6 +10,7 @@ & when (@media-common = true) { .form.send.friend { &:extend(.abs-add-fields all); + .fieldset { .field { .control { @@ -28,7 +29,6 @@ } } - // // Desktop // _____________________________________________ @@ -57,6 +57,7 @@ .fieldset { padding-bottom: @indent__xs; } + .action { &.remove { right: 0; From 8932e147a31ee5ac01fe0a673cfac805c1cb9c20 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Tue, 5 Feb 2019 13:56:58 +0200 Subject: [PATCH 0625/1295] ENGCOM-4115: Static test fix. --- .../luma/Magento_SendFriend/web/css/source/_module.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/design/frontend/Magento/luma/Magento_SendFriend/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_SendFriend/web/css/source/_module.less index 0c475b2c59aee..3435736a54a6a 100644 --- a/app/design/frontend/Magento/luma/Magento_SendFriend/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_SendFriend/web/css/source/_module.less @@ -60,9 +60,9 @@ .action { &.remove { - right: 0; margin-left: 0; - top: 100%; + right: 0; + top: 100%; } } } From 7e7494e3ee76ff3101eaa9369aeccde611e7f6dc Mon Sep 17 00:00:00 2001 From: Ilan Parmentier <ilan.parmentier@artbambou.com> Date: Wed, 30 Jan 2019 13:43:47 +0100 Subject: [PATCH 0626/1295] Adjust table for grouped products I think there is a mistake here in declaring a tbody tag for every iteration ? --- .../view/frontend/templates/product/view/type/grouped.phtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/GroupedProduct/view/frontend/templates/product/view/type/grouped.phtml b/app/code/Magento/GroupedProduct/view/frontend/templates/product/view/type/grouped.phtml index 900d4a1bd5bbc..0be71f20a3822 100644 --- a/app/code/Magento/GroupedProduct/view/frontend/templates/product/view/type/grouped.phtml +++ b/app/code/Magento/GroupedProduct/view/frontend/templates/product/view/type/grouped.phtml @@ -33,8 +33,8 @@ </thead> <?php if ($_hasAssociatedProducts): ?> - <?php foreach ($_associatedProducts as $_item): ?> <tbody> + <?php foreach ($_associatedProducts as $_item): ?> <tr> <td data-th="<?= $block->escapeHtml(__('Product Name')) ?>" class="col item"> <strong class="product-item-name"><?= $block->escapeHtml($_item->getName()) ?></strong> @@ -80,8 +80,8 @@ </td> </tr> <?php endif; ?> - </tbody> <?php endforeach; ?> + </tbody> <?php else: ?> <tbody> <tr> From 65ba55d689d08f8258c86f7b64381f9697a1f87f Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Thu, 7 Feb 2019 12:28:58 +0200 Subject: [PATCH 0627/1295] ENGCOM-4061: MTF test fix. --- .../GroupedProduct/Test/Block/Catalog/Product/View.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Block/Catalog/Product/View.php b/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Block/Catalog/Product/View.php index 5627a9d887bc7..c47df8c5463e5 100644 --- a/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Block/Catalog/Product/View.php +++ b/dev/tests/functional/tests/app/Magento/GroupedProduct/Test/Block/Catalog/Product/View.php @@ -27,14 +27,15 @@ class View extends ParentView * * @var string */ - protected $formatTierPrice = "//tbody[%row-number%]//ul[contains(@class,'tier')]//*[@class='item'][%line-number%]"; + protected $formatTierPrice = + "//tr[@class='row-tier-price'][%row-number%]//ul[contains(@class,'tier')]//*[@class='item'][%line-number%]"; /** * This member holds the class name of the special price block. * * @var string */ - protected $formatSpecialPrice = '//tbody[%row-number%]//*[contains(@class,"price-box")]'; + protected $formatSpecialPrice = '//tbody//tr[%row-number%]//*[contains(@class,"price-box")]'; /** * Get grouped product block From 41742d06877a02e2fb27817fb023dde111b6d326 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Tue, 12 Feb 2019 21:23:32 +0200 Subject: [PATCH 0628/1295] MAGETWO-86215: Static blocks with same ID appear in place of correct block --- .../Test/CheckCreateStaticBlockOnDuplicateIdentifierTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Cms/Test/Mftf/Test/CheckCreateStaticBlockOnDuplicateIdentifierTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/CheckCreateStaticBlockOnDuplicateIdentifierTest.xml index b79997578e675..ac1b68269740f 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/CheckCreateStaticBlockOnDuplicateIdentifierTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/CheckCreateStaticBlockOnDuplicateIdentifierTest.xml @@ -18,6 +18,7 @@ <testCaseId value="MC-13912"/> <useCaseId value="MAGETWO-86215"/> <group value="cms"/> + <group value="WYSIWYGDisabled"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> From bfa4e1ff8f4173e75cb0c45fc7cbb0f644f6c96f Mon Sep 17 00:00:00 2001 From: Pieter Hoste <hoste.pieter@gmail.com> Date: Wed, 23 Jan 2019 20:12:12 +0100 Subject: [PATCH 0629/1295] Fixes incorrect country code being used for Greek VAT numbers, should be 'EL' instead of 'GR'. --- app/code/Magento/Customer/Model/Vat.php | 29 +++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Customer/Model/Vat.php b/app/code/Magento/Customer/Model/Vat.php index f608a6cf4c11c..123a9eef4b75a 100644 --- a/app/code/Magento/Customer/Model/Vat.php +++ b/app/code/Magento/Customer/Model/Vat.php @@ -179,18 +179,21 @@ public function checkVatNumber($countryCode, $vatNumber, $requesterCountryCode = return $gatewayResponse; } + $countryCodeForVatNumber = $this->getCountryCodeForVatNumber($countryCode); + $requesterCountryCodeForVatNumber = $this->getCountryCodeForVatNumber($requesterCountryCode); + try { $soapClient = $this->createVatNumberValidationSoapClient(); $requestParams = []; - $requestParams['countryCode'] = $countryCode; + $requestParams['countryCode'] = $countryCodeForVatNumber; $vatNumberSanitized = $this->isCountryInEU($countryCode) - ? str_replace([' ', '-', $countryCode], ['', '', ''], $vatNumber) + ? str_replace([' ', '-', $countryCodeForVatNumber], ['', '', ''], $vatNumber) : str_replace([' ', '-'], ['', ''], $vatNumber); $requestParams['vatNumber'] = $vatNumberSanitized; - $requestParams['requesterCountryCode'] = $requesterCountryCode; + $requestParams['requesterCountryCode'] = $requesterCountryCodeForVatNumber; $reqVatNumSanitized = $this->isCountryInEU($requesterCountryCode) - ? str_replace([' ', '-', $requesterCountryCode], ['', '', ''], $requesterVatNumber) + ? str_replace([' ', '-', $requesterCountryCodeForVatNumber], ['', '', ''], $requesterVatNumber) : str_replace([' ', '-'], ['', ''], $requesterVatNumber); $requestParams['requesterVatNumber'] = $reqVatNumSanitized; // Send request to service @@ -301,4 +304,22 @@ public function isCountryInEU($countryCode, $storeId = null) ); return in_array($countryCode, $euCountries); } + + /** + * Returns the country code to use in the VAT number which is not always the same as the normal country code + * + * @param string $countryCode + * @return string + */ + private function getCountryCodeForVatNumber(string $countryCode): string + { + // Greece uses a different code for VAT numbers then its country code + // See: http://ec.europa.eu/taxation_customs/vies/faq.html#item_11 + // And https://en.wikipedia.org/wiki/VAT_identification_number: + // "The full identifier starts with an ISO 3166-1 alpha-2 (2 letters) country code + // (except for Greece, which uses the ISO 639-1 language code EL for the Greek language, + // instead of its ISO 3166-1 alpha-2 country code GR)" + + return $countryCode === 'GR' ? 'EL' : $countryCode; + } } From a16427dbf793d530491c3969f77e085a1208d3ce Mon Sep 17 00:00:00 2001 From: amol 2jcommerce <amol@2jcommerce.in> Date: Wed, 13 Feb 2019 10:00:50 +0530 Subject: [PATCH 0630/1295] Fixes-for-account-my-recent-reviews-alignment-2.2 --- .../Magento/luma/Magento_Review/web/css/source/_module.less | 1 + 1 file changed, 1 insertion(+) diff --git a/app/design/frontend/Magento/luma/Magento_Review/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Review/web/css/source/_module.less index da78406f92212..816afe1c07aeb 100644 --- a/app/design/frontend/Magento/luma/Magento_Review/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Review/web/css/source/_module.less @@ -361,6 +361,7 @@ .label { .lib-css(font-weight, @font-weight__semibold); .lib-css(margin-right, @indent__s); + vertical-align: middle; } } } From e9102ecb206fd19b73493b0f7e1e00dbc92d661f Mon Sep 17 00:00:00 2001 From: amol 2jcommerce <amol@2jcommerce.in> Date: Wed, 13 Feb 2019 10:34:24 +0530 Subject: [PATCH 0631/1295] fixes-customer-information-wishlist-configurable-product-alignment-2.2 --- .../product/composite/fieldset/configurable.phtml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/composite/fieldset/configurable.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/composite/fieldset/configurable.phtml index a8712cdc183de..190ecccbfdb76 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/composite/fieldset/configurable.phtml +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/composite/fieldset/configurable.phtml @@ -17,9 +17,9 @@ <legend class="legend admin__legend"> <span><?= /* @escapeNotVerified */ __('Associated Products') ?></span> </legend> - <div class="product-options"> - <div class="field admin__field _required required"> - <?php foreach ($_attributes as $_attribute): ?> + <div class="product-options fieldset admin__fieldset"> + <?php foreach ($_attributes as $_attribute): ?> + <div class="field admin__field _required required"> <label class="label admin__field-label"><?php /* @escapeNotVerified */ echo $_attribute->getProductAttribute() ->getStoreLabel($_product->getStoreId()); @@ -34,8 +34,8 @@ <option><?= /* @escapeNotVerified */ __('Choose an Option...') ?></option> </select> </div> - <?php endforeach; ?> - </div> + </div> + <?php endforeach; ?> </div> </fieldset> <script> From 8a28cca64af335735acb299598a6b1a0330a86d0 Mon Sep 17 00:00:00 2001 From: amol 2jcommerce <amol@2jcommerce.in> Date: Wed, 13 Feb 2019 10:44:11 +0530 Subject: [PATCH 0632/1295] Fixed-Widget-left-navigation-block-2.2 --- .../Magento_Backend/web/css/source/module/main/_page-nav.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_page-nav.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_page-nav.less index 7b60a8d871702..36c1ecbca9d99 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_page-nav.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_page-nav.less @@ -23,7 +23,7 @@ @admin__page-nav-item__hover__background-color: darken(@admin__page-nav__background-color, 5%); @admin__page-nav-link__color: @color-very-dark-gray-black; -@admin__page-nav-link__padding: @indent__base 0rem @indent__base @indent__s; +@admin__page-nav-link__padding: @indent__base 4rem @indent__base @indent__s; @admin__page-nav-link__hover__color: @color-very-dark-gray-black; @admin__page-nav-link__changed__color: @color-very-dark-gray; @@ -203,7 +203,7 @@ font-weight: @font-weight__heavier; line-height: @line-height__s; margin: 0 0 -1px; - padding: @admin__page-nav-link__padding; + padding: 2rem 0rem 2rem 1rem; transition: @admin__page-nav-transition; word-wrap: break-word; } From 30e61f499761ba83ace058d230e91807dbd45803 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Wed, 13 Feb 2019 08:38:22 +0200 Subject: [PATCH 0633/1295] MAGETWO-94421: Default addresses not selected when checking out from cart --- .../Customer/Test/Mftf/Data/CustomerData.xml | 10 +++++ .../ActionGroup/AdminOrderActionGroup.xml | 45 +++++++++++++++++++ .../AdminOrderFormShippingAddressSection.xml | 23 ++++++++++ 3 files changed, 78 insertions(+) create mode 100644 app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormShippingAddressSection.xml diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml index a8b8cece39fad..7082a799ed748 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml @@ -44,6 +44,16 @@ <data key="website_id">0</data> <requiredEntity type="address">US_Address_TX</requiredEntity> </entity> + <entity name="Simple_Customer_Without_Address" type="customer"> + <data key="group_id">1</data> + <data key="email" unique="prefix">John.Doe@example.com</data> + <data key="firstname">John</data> + <data key="lastname">Doe</data> + <data key="fullname">John Doe</data> + <data key="password">pwdTest123!</data> + <data key="store_id">0</data> + <data key="website_id">0</data> + </entity> <entity name="Simple_US_Customer_For_Update" type="customer"> <var key="id" entityKey="id" entityType="customer"/> <data key="firstname">Jane</data> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml index bb3bfdc03d340..2f7bd98572f8b 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml @@ -174,6 +174,51 @@ <fillField selector="{{AdminOrderFormBillingAddressSection.postalCode}}" userInput="{{address.postcode}}" stepKey="fillPostalCode"/> <fillField selector="{{AdminOrderFormBillingAddressSection.phone}}" userInput="{{address.telephone}}" stepKey="fillPhone"/> </actionGroup> + <!--Check customer billing address fields--> + <actionGroup name="checkOrderCustomerBillingInformation"> + <arguments> + <argument name="customer"/> + <argument name="address"/> + </arguments> + <seeInField selector="{{AdminOrderFormBillingAddressSection.firstName}}" userInput="{{customer.firstname}}" stepKey="checkFirstName"/> + <seeInField selector="{{AdminOrderFormBillingAddressSection.lastName}}" userInput="{{customer.lastname}}" stepKey="checkLastName"/> + <seeInField selector="{{AdminOrderFormBillingAddressSection.streetLine1}}" userInput="{{address.street[0]}}" stepKey="checkStreet"/> + <seeInField selector="{{AdminOrderFormBillingAddressSection.city}}" userInput="{{address.city}}" stepKey="checkCity"/> + <seeInField selector="{{AdminOrderFormBillingAddressSection.country}}" userInput="{{address.country_id}}" stepKey="checkCountry"/> + <seeInField selector="{{AdminOrderFormBillingAddressSection.state}}" userInput="{{address.state}}" stepKey="checkState"/> + <seeInField selector="{{AdminOrderFormBillingAddressSection.postalCode}}" userInput="{{address.postcode}}" stepKey="checkPostCode"/> + <seeInField selector="{{AdminOrderFormBillingAddressSection.phone}}" userInput="{{address.telephone}}" stepKey="checkTelephone"/> + </actionGroup> + <!--Fill customer shipping address--> + <actionGroup name="fillOrderCustomerShippingInformation"> + <arguments> + <argument name="customer"/> + <argument name="address"/> + </arguments> + <fillField selector="{{AdminOrderFormShippingAddressSection.firstName}}" userInput="{{customer.firstname}}" stepKey="fillFirstName"/> + <fillField selector="{{AdminOrderFormShippingAddressSection.lastName}}" userInput="{{customer.lastname}}" stepKey="fillLastName"/> + <fillField selector="{{AdminOrderFormShippingAddressSection.streetLine1}}" userInput="{{address.street[0]}}" stepKey="fillStreetLine1"/> + <fillField selector="{{AdminOrderFormShippingAddressSection.city}}" userInput="{{address.city}}" stepKey="fillCity"/> + <selectOption selector="{{AdminOrderFormShippingAddressSection.country}}" userInput="{{address.country_id}}" stepKey="fillCountry"/> + <selectOption selector="{{AdminOrderFormShippingAddressSection.state}}" userInput="{{address.state}}" stepKey="fillState"/> + <fillField selector="{{AdminOrderFormShippingAddressSection.postalCode}}" userInput="{{address.postcode}}" stepKey="fillPostalCode"/> + <fillField selector="{{AdminOrderFormShippingAddressSection.phone}}" userInput="{{address.telephone}}" stepKey="fillPhone"/> + </actionGroup> + <!--Check customer shipping address fields--> + <actionGroup name="checkOrderCustomerShippingInformation"> + <arguments> + <argument name="customer"/> + <argument name="address"/> + </arguments> + <seeInField selector="{{AdminOrderFormShippingAddressSection.firstName}}" userInput="{{customer.firstname}}" stepKey="checkFirstName"/> + <seeInField selector="{{AdminOrderFormShippingAddressSection.lastName}}" userInput="{{customer.lastname}}" stepKey="checkLastName"/> + <seeInField selector="{{AdminOrderFormShippingAddressSection.streetLine1}}" userInput="{{address.street[0]}}" stepKey="checkStreet"/> + <seeInField selector="{{AdminOrderFormShippingAddressSection.city}}" userInput="{{address.city}}" stepKey="checkCity"/> + <seeInField selector="{{AdminOrderFormShippingAddressSection.country}}" userInput="{{address.country_id}}" stepKey="checkCountry"/> + <seeInField selector="{{AdminOrderFormShippingAddressSection.state}}" userInput="{{address.state}}" stepKey="checkState"/> + <seeInField selector="{{AdminOrderFormShippingAddressSection.postalCode}}" userInput="{{address.postcode}}" stepKey="checkPostCode"/> + <seeInField selector="{{AdminOrderFormShippingAddressSection.phone}}" userInput="{{address.telephone}}" stepKey="checkTelephone"/> + </actionGroup> <!--Select flat rate shipping method--> <actionGroup name="orderSelectFlatRateShipping"> <click selector="{{AdminOrderFormPaymentSection.header}}" stepKey="unfocus"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormShippingAddressSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormShippingAddressSection.xml new file mode 100644 index 0000000000000..aae90589a390f --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormShippingAddressSection.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminOrderFormShippingAddressSection"> + <element name="firstName" type="input" selector="#order-shipping_address_firstname" timeout="30"/> + <element name="lastName" type="input" selector="#order-shipping_address_lastname" timeout="30"/> + <element name="streetLine1" type="input" selector="#order-shipping_address_street0" timeout="30"/> + <element name="city" type="input" selector="#order-shipping_address_city" timeout="30"/> + <element name="country" type="select" selector="#order-shipping_address_country_id" timeout="30"/> + <element name="state" type="select" selector="#order-shipping_address_region_id" timeout="30"/> + <element name="postalCode" type="input" selector="#order-shipping_address_postcode" timeout="30"/> + <element name="phone" type="input" selector="#order-shipping_address_telephone" timeout="30"/> + <element name="saveAddress" type="checkbox" selector="#order-shipping_address_save_in_address_book"/> + <element name="sameAsBilling" type="checkbox" selector="#order-shipping_same_as_billing"/> + </section> +</sections> From ef8e4f5a7d779278010a5d0b261759c75889ed53 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Wed, 13 Feb 2019 08:46:01 +0200 Subject: [PATCH 0634/1295] MAGETWO-94421: Default addresses not selected when checking out from cart --- app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml b/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml index e67a2c3b961d9..ca4239bb060ca 100644 --- a/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml +++ b/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml @@ -7,12 +7,13 @@ --> <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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminOrderCreatePage" url="sales/order_create/index" area="admin" module="Magento_Sales"> <section name="AdminOrderFormSelectWebsiteSection"/> <section name="AdminOrderFormActionSection"/> <section name="AdminOrderFormAccountSection"/> <section name="AdminOrderFormBillingAddressSection"/> + <section name="AdminOrderFormShippingAddressSection"/> <section name="AdminOrderFormPaymentSection"/> <section name="AdminOrderFormItemsSection"/> <section name="AdminOrderFormTotalSection"/> From 118d4d587aaa83944aa487ad95ba8fd41dc794de Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Wed, 13 Feb 2019 09:16:32 +0200 Subject: [PATCH 0635/1295] MAGETWO-73432: Change EAV model --- .../ActionGroup/AdminAddCustomerAddressActionGroup.xml | 9 ++++----- .../Mftf/Section/AdminCustomerAccountAddressSection.xml | 4 ++-- .../Section/AdminCustomerAccountNewAddressSection.xml | 6 +++--- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAddCustomerAddressActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAddCustomerAddressActionGroup.xml index f042c272cbfce..1ffc258e78a43 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAddCustomerAddressActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAddCustomerAddressActionGroup.xml @@ -7,20 +7,19 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminAddCustomerAddressActionGroup" > + <actionGroup name="AdminAddCustomerAddressWithRegionTypeSelectActionGroup" > <arguments> <argument name="customerAddress" defaultValue="CustomerAddressSimple"/> </arguments> <click selector="{{AdminCustomerAccountAddressSection.addresses}}" stepKey="proceedToAddresses"/> <click selector="{{AdminCustomerAccountAddressSection.addNewAddress}}" stepKey="addNewAddresses"/> - <waitForPageLoad time="60" stepKey="waitAddressFieldsLoaded" /> <fillField userInput="{{customerAddress.street[0]}}" selector="{{AdminCustomerAccountNewAddressSection.street}}" stepKey="fillStreetAddress"/> <fillField userInput="{{customerAddress.city}}" selector="{{AdminCustomerAccountNewAddressSection.city}}" stepKey="fillCity"/> - <selectOption userInput="{{US_Address_CA.country_id}}" selector="{{AdminCustomerAccountNewAddressSection.country}}" stepKey="selectCountry"/> - <selectOption userInput="{{US_Address_CA.state}}" selector="{{AdminCustomerAccountNewAddressSection.region}}" stepKey="selectState"/> + <selectOption userInput="{{customerAddress.country_id}}" selector="{{AdminCustomerAccountNewAddressSection.country}}" stepKey="selectCountry"/> + <selectOption userInput="{{customerAddress.state}}" selector="{{AdminCustomerAccountNewAddressSection.regionId}}" stepKey="selectState"/> <fillField userInput="{{customerAddress.postcode}}" selector="{{AdminCustomerAccountNewAddressSection.zip}}" stepKey="fillZipCode"/> <fillField userInput="{{customerAddress.telephone}}" selector="{{AdminCustomerAccountNewAddressSection.phone}}" stepKey="fillPhone"/> <click selector="{{AdminMainActionsSection.save}}" stepKey="saveCustomer"/> - <see selector="{{AdminMessagesSection.success}}" userInput="You saved the customer." stepKey="CustomerIsSaved"/> + <see userInput="You saved the customer." selector="{{AdminMessagesSection.success}}" stepKey="customerIsSaved"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountAddressSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountAddressSection.xml index 334a5d81901ea..70042e2a71467 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountAddressSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountAddressSection.xml @@ -7,7 +7,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCustomerAccountAddressSection"> - <element name="addresses" type="button" selector="a#tab_address" timeout="30"/> - <element name="addNewAddress" type="button" selector=".address-list-actions button.scalable.add span"/> + <element name="addresses" type="button" selector="#tab_address" timeout="30"/> + <element name="addNewAddress" type="button" selector=".address-list-actions button.scalable.add span" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountNewAddressSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountNewAddressSection.xml index 4f1bf69c6e687..8445343c9b9c0 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountNewAddressSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountNewAddressSection.xml @@ -12,8 +12,8 @@ <element name="street" type="button" selector="input[name*='new'][name*='street']"/> <element name="city" type="input" selector="input[name*='new'][name*='city']"/> <element name="country" type="select" selector="select[name*='address'][name*='new'][name*='country']" timeout="10"/> - <element name="region" type="select" selector="select[name*='address'][name*='new'][name*='region_id']"/> - <element name="zip" type="input" selector="input[name*='new'][name*='postcode']"/> - <element name="phone" type="text" selector="input[name*='new'][name*='telephone']" /> + <element name="regionId" type="select" selector="select[name*='address'][name*='new'][name*='region_id']"/> + <element name="zip" type="input" selector="input[name*='address'][name*='new'][name*='postcode']"/> + <element name="phone" type="text" selector="input[name*='address'][name*='new'][name*='telephone']" /> </section> </sections> From e6fa7e1d601905cb10fbb996386eb27f7de5292d Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Wed, 13 Feb 2019 09:37:31 +0200 Subject: [PATCH 0636/1295] MAGETWO-86215: Static blocks with same ID appear in place of correct block --- .../Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml index 62cc5f06fedcc..d9dc9ec10670c 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml @@ -24,6 +24,7 @@ <argument name="identifier" type="string" defaultValue="{{DefaultCmsBlock.identifier}}"/> </arguments> <amOnPage url="{{AdminCmsBlockGridPage.url}}" stepKey="navigateToCmsBlockListingPage"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFilters"/> <click selector="{{CmsPagesPageActionsSection.select(identifier)}}" stepKey="clickOnSelect"/> <click selector="{{CmsPagesPageActionsSection.delete(identifier)}}" stepKey="clickOnDelete"/> <waitForElementVisible selector="{{AdminConfirmationModalSection.message}}" stepKey="waitForConfirmModal"/> From da56acdd8fc5d3d952ca7d0d6b552a9a22efa286 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Wed, 13 Feb 2019 11:10:22 +0200 Subject: [PATCH 0637/1295] MAGETWO-86215: Static blocks with same ID appear in place of correct block --- .../Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml index d9dc9ec10670c..36eb3e3c351e9 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml @@ -24,12 +24,16 @@ <argument name="identifier" type="string" defaultValue="{{DefaultCmsBlock.identifier}}"/> </arguments> <amOnPage url="{{AdminCmsBlockGridPage.url}}" stepKey="navigateToCmsBlockListingPage"/> - <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFilters"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFiltersBeforeDelete"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openCmsBlockFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('identifier')}}" userInput="{{identifier}}" stepKey="fillFilter"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFilters"/> <click selector="{{CmsPagesPageActionsSection.select(identifier)}}" stepKey="clickOnSelect"/> <click selector="{{CmsPagesPageActionsSection.delete(identifier)}}" stepKey="clickOnDelete"/> <waitForElementVisible selector="{{AdminConfirmationModalSection.message}}" stepKey="waitForConfirmModal"/> <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirm"/> <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the block." stepKey="verifyBlockIsDeleted"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFiltersAfterDelete"/> </actionGroup> <actionGroup name="NavigateToCreateCmsBlockActionGroup"> <amOnPage url="{{AdminCmsBlockNewPage.url}}" stepKey="navigateToCreateCmsBlockPage"/> From 934b6e2cfa898d628c88e09fc88406e36b716e78 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Wed, 13 Feb 2019 12:20:47 +0200 Subject: [PATCH 0638/1295] MAGETWO-96636: MFTF test fix --- .../StorefrontBraintreeFillCardDataActionGroup.xml | 9 +++++++-- .../Braintree/Test/Mftf/Page/CheckoutPage.xml | 14 ++++++++++++++ ...refrontBraintreePaymentConfigurationSection.xml | 6 ++---- 3 files changed, 23 insertions(+), 6 deletions(-) create mode 100644 app/code/Magento/Braintree/Test/Mftf/Page/CheckoutPage.xml diff --git a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/StorefrontBraintreeFillCardDataActionGroup.xml b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/StorefrontBraintreeFillCardDataActionGroup.xml index 561d660a1bb75..93f239b5c38a1 100644 --- a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/StorefrontBraintreeFillCardDataActionGroup.xml +++ b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/StorefrontBraintreeFillCardDataActionGroup.xml @@ -7,13 +7,18 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontBraintreeSelectPaymentMethodActionGroup"> + <click selector="{{StorefrontBraintreePaymentConfigurationSection.creditCardBraintreePaymentMethod}}" stepKey="selectBraintreePaymentMethod"/> + </actionGroup> + <actionGroup name="StorefrontBraintreeFillCardDataActionGroup"> <arguments> <argument name="cardData" defaultValue="BraintreeCard"/> </arguments> + <scrollTo selector="{{StorefrontBraintreePaymentConfigurationSection.creditCardBraintreePaymentMethod}}" stepKey="scrollToCreditCardSection"/> <switchToIFrame selector="{{StorefrontBraintreePaymentConfigurationSection.cardFrame}}" stepKey="switchToIframe"/> - <waitForElementVisible selector="{{StorefrontBraintreePaymentConfigurationSection.cardCode}}" stepKey="waitCardCodeElement"/> - <fillField selector="{{StorefrontBraintreePaymentConfigurationSection.cardCode}}" userInput="{{cardData.card_number}}" stepKey="setCardCode"/> + <waitForElementVisible selector="{{StorefrontBraintreePaymentConfigurationSection.cardNumber}}" stepKey="waitCardNumberElement"/> + <fillField selector="{{StorefrontBraintreePaymentConfigurationSection.cardNumber}}" userInput="{{cardData.card_number}}" stepKey="setCardNumber"/> <switchToIFrame stepKey="switchBack"/> <switchToIFrame selector="{{StorefrontBraintreePaymentConfigurationSection.monthFrame}}" stepKey="switchToIframe1"/> <waitForElementVisible selector="{{StorefrontBraintreePaymentConfigurationSection.month}}" stepKey="waitMonthElement"/> diff --git a/app/code/Magento/Braintree/Test/Mftf/Page/CheckoutPage.xml b/app/code/Magento/Braintree/Test/Mftf/Page/CheckoutPage.xml new file mode 100644 index 0000000000000..bc23a1e1fe3f7 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Page/CheckoutPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="CheckoutPage" url="/checkout" area="storefront" module="Magento_Checkout"> + <section name="StorefrontBraintreePaymentConfigurationSection"/> + </page> +</pages> diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontBraintreePaymentConfigurationSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontBraintreePaymentConfigurationSection.xml index d4cc3fe0f113d..f72eed9179764 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontBraintreePaymentConfigurationSection.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontBraintreePaymentConfigurationSection.xml @@ -9,14 +9,12 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontBraintreePaymentConfigurationSection"> - <element name="creditCard" type="radio" selector="#braintree"/> - <element name="paymentMethodContainer" type="block" selector=".payment-method-braintree"/> - <element name="paymentMethod" type="radio" selector="//div[@class='payment-group']//input[contains(@id, 'braintree_cc_vault_')]"/> + <element name="creditCardBraintreePaymentMethod" type="radio" selector="#braintree" timeout="30"/> <element name="cardFrame" type="iframe" selector="braintree-hosted-field-number"/> <element name="monthFrame" type="iframe" selector="braintree-hosted-field-expirationMonth"/> <element name="yearFrame" type="iframe" selector="braintree-hosted-field-expirationYear"/> <element name="codeFrame" type="iframe" selector="braintree-hosted-field-cvv"/> - <element name="cardCode" type="input" selector="#credit-card-number"/> + <element name="cardNumber" type="input" selector="#credit-card-number"/> <element name="month" type="input" selector="#expiration-month"/> <element name="year" type="input" selector="#expiration-year"/> <element name="verificationNumber" type="input" selector="#cvv"/> From 9ace1ea71b185544340c29a809e0c44159952852 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Wed, 13 Feb 2019 12:22:26 +0200 Subject: [PATCH 0639/1295] MAGETWO-86215: Static blocks with same ID appear in place of correct block --- .../Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml index 36eb3e3c351e9..597df165f61d1 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCmsBlockActionGroup.xml @@ -21,15 +21,15 @@ </actionGroup> <actionGroup name="DeleteCmsBlockActionGroup"> <arguments> - <argument name="identifier" type="string" defaultValue="{{DefaultCmsBlock.identifier}}"/> + <argument name="cmsBlockIdentifier" type="string" defaultValue="{{DefaultCmsBlock.identifier}}"/> </arguments> <amOnPage url="{{AdminCmsBlockGridPage.url}}" stepKey="navigateToCmsBlockListingPage"/> <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFiltersBeforeDelete"/> <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openCmsBlockFilters"/> - <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('identifier')}}" userInput="{{identifier}}" stepKey="fillFilter"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('identifier')}}" userInput="{{cmsBlockIdentifier}}" stepKey="fillFilter"/> <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFilters"/> - <click selector="{{CmsPagesPageActionsSection.select(identifier)}}" stepKey="clickOnSelect"/> - <click selector="{{CmsPagesPageActionsSection.delete(identifier)}}" stepKey="clickOnDelete"/> + <click selector="{{CmsPagesPageActionsSection.select(cmsBlockIdentifier)}}" stepKey="clickOnSelect"/> + <click selector="{{CmsPagesPageActionsSection.delete(cmsBlockIdentifier)}}" stepKey="clickOnDelete"/> <waitForElementVisible selector="{{AdminConfirmationModalSection.message}}" stepKey="waitForConfirmModal"/> <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirm"/> <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the block." stepKey="verifyBlockIsDeleted"/> From 8787ab7561d2c04179c6326d6c7a8e8b28e423c5 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Wed, 13 Feb 2019 12:25:27 +0200 Subject: [PATCH 0640/1295] MAGETWO-73534: [GITHUB] Url rewrite for product is broken after using massaction #8227 --- .../Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml index 558e19885e112..d52395342c092 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml @@ -34,7 +34,6 @@ <actionGroup ref="logout" stepKey="logout"/> </after> - <!--Open Created product--> <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="openProductEditPage"/> <!--Switch to Default Store view--> From 8c5e0d81e4f77665af4439c69a64de870fc03b3d Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Wed, 13 Feb 2019 13:26:00 +0200 Subject: [PATCH 0641/1295] MAGETWO-96375: Checkout Free Shipping Recalculation after Coupon Code Added --- .../Mftf/ActionGroup/CheckoutActionGroup.xml | 15 ++- .../Model/Carrier/Freeshipping.php | 5 +- ...gRecalculationAfterCouponCodeAddedTest.xml | 104 ++++++++++++++++++ .../ApplyCartRuleOnStorefrontActionGroup.xml | 29 +++-- .../Section/StorefrontDiscountSection.xml | 7 +- .../Mftf/Data/StoreShippingMethodsData.xml | 37 +++++++ .../Metadata/store_shipping_methods-meta.xml | 51 +++++++++ .../Section/StorefrontMessagesSection.xml | 1 + 8 files changed, 234 insertions(+), 15 deletions(-) create mode 100644 app/code/Magento/Sales/Test/Mftf/Test/StorefrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml create mode 100644 app/code/Magento/Store/Test/Mftf/Data/StoreShippingMethodsData.xml create mode 100644 app/code/Magento/Store/Test/Mftf/Metadata/store_shipping_methods-meta.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml index f79d59028c468..bb0b0a5edf2d3 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml @@ -7,7 +7,7 @@ --> <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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Checkout select Check/Money Order payment --> <actionGroup name="CheckoutSelectCheckMoneyOrderPaymentActionGroup"> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> @@ -106,4 +106,17 @@ <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="waitForLoadSuccessPage"/> </actionGroup> + + <actionGroup name="CheckoutSelectShippingMethodActionGroup"> + <arguments> + <!-- First available shipping method will be selected if value is not passed for shippingMethod --> + <argument name="shippingMethod" defaultValue="" type="string"/> + </arguments> + <conditionalClick selector="{{CheckoutShippingMethodsSection.checkShippingMethodByName(shippingMethod)}}" dependentSelector="{{CheckoutShippingMethodsSection.checkShippingMethodByName(shippingMethod)}}" visible="true" stepKey="selectShippingMethod"/> + <waitForLoadingMaskToDisappear stepKey="waitForNextButton"/> + <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="clickNext"/> + <waitForLoadingMaskToDisappear stepKey="waitForPaymentMethod"/> + <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" stepKey="waitForPaymentSectionLoaded"/> + <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/OfflineShipping/Model/Carrier/Freeshipping.php b/app/code/Magento/OfflineShipping/Model/Carrier/Freeshipping.php index 2373b5285ed00..0fc56c7136327 100644 --- a/app/code/Magento/OfflineShipping/Model/Carrier/Freeshipping.php +++ b/app/code/Magento/OfflineShipping/Model/Carrier/Freeshipping.php @@ -80,9 +80,8 @@ public function collectRates(RateRequest $request) $this->_updateFreeMethodQuote($request); - if ($request->getFreeShipping() || $request->getBaseSubtotalInclTax() >= $this->getConfigData( - 'free_shipping_subtotal' - ) + if ($request->getFreeShipping() + || ($request->getPackageValueWithDiscount() >= $this->getConfigData('free_shipping_subtotal')) ) { /** @var \Magento\Quote\Model\Quote\Address\RateResult\Method $method */ $method = $this->_rateMethodFactory->create(); diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml new file mode 100644 index 0000000000000..0407502b1a2d7 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml @@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontFreeShippingRecalculationAfterCouponCodeAddedTest"> + <annotations> + <features value="Sales"/> + <title value="Checkout Free Shipping Recalculation after Coupon Code Applied"/> + <stories value="Checkout Free Shipping Recalculation after Coupon Code Applied"/> + <description value="User should be able to do checkout free shipping recalculation after adding coupon code"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-96537"/> + <useCaseId value="MC-14807"/> + <group value="sales"/> + <group value="salesRule"/> + </annotations> + + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createSimpleProduct"> + <field key="price">90</field> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="Simple_US_Customer" stepKey="createSimpleUsCustomer"/> + + <!-- Enable Free Shipping and set minimum order amount --> + <createData entity="FreeShippingMethodsSettingConfig" stepKey="freeShippingMethodSettingConfig"/> + <createData entity="MinimumOrderAmount90" stepKey="setMinimumOrderAmount"/> + + <!-- Create Cart Price Rule --> + <createData entity="SalesRuleSpecificCouponWithPercentDiscount" stepKey="createCartPriceRule"> + <field key="simple_free_shipping">0</field> + </createData> + <createData entity="SimpleSalesRuleCoupon" stepKey="createCouponForCartPriceRule"> + <requiredEntity createDataKey="createCartPriceRule"/> + </createData> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="CustomerLoginOnStorefront" stepKey="loginToStorefront"> + <argument name="customer" value="$$createSimpleUsCustomer$$"/> + </actionGroup> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createSimpleUsCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCartPriceRule" stepKey="deleteCartPriceRule"/> + <createData entity="DefaultShippingMethodsConfig" stepKey="setDefaultShippingMethodsConfig"/> + <createData entity="DefaultMinimumOrderAmount" stepKey="setDefaultMinimumOrderAmount"/> + <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="CustomerLogoutStorefrontActionGroup" stepKey="logoutFromStorefront"/> + </after> + + <!-- Add product to Shopping Cart and apply coupon --> + <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.name$$)}}" stepKey="navigateToProductPage"/> + <actionGroup ref="ApplyCartRuleOnStorefrontActionGroup" stepKey="applyCartRule"> + <argument name="product" value="$$createSimpleProduct$$"/> + <argument name="couponCode" value="$$createCouponForCartPriceRule.code$$"/> + </actionGroup> + + <!-- Proceed to Checkout and make sure Free Shipping method isn't displaying --> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> + <dontSee selector="{{CheckoutShippingMethodsSection.shippingMethodRowByName('Free')}}" stepKey="dontSeeFreeShipping"/> + + <!-- Back to Shopping Cart page and cancel coupon--> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToShoppingCartPage"/> + <actionGroup ref="StorefrontCancelCouponActionGroup" stepKey="cancelCoupon"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + + <!-- Proceed to Checkout, select Free Shipping method and apply coupon --> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart1"/> + <actionGroup ref="CheckoutSelectShippingMethodActionGroup" stepKey="selectFreeShipping"> + <argument name="shippingMethod" value="Free"/> + </actionGroup> + <actionGroup ref="StorefrontApplyCouponOnCheckoutActionGroup" stepKey="applyCouponOnCheckout"> + <argument name="couponCode" value="$$createCouponForCartPriceRule.code$$"/> + <argument name="successMessage" value="Your coupon was successfully applied."/> + </actionGroup> + + <!-- Try to Place Order --> + <waitForElementVisible selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="waitForPlaceOrderButton"/> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.errorMessage}}" stepKey="waitForError"/> + <see selector="{{StorefrontMessagesSection.errorMessage}}" userInput="The shipping method is missing. Select the shipping method and try again." stepKey="seeShippingMethodError"/> + + <!-- Go back to Shipping step and select Shipping method --> + <amOnPage url="{{CheckoutPage.url}}/#shipping" stepKey="navigateToShippingStep"/> + <actionGroup ref="CheckoutSelectShippingMethodActionGroup" stepKey="selectFlatRateShipping"> + <argument name="shippingMethod" value="Flat Rate"/> + </actionGroup> + + <!-- Select Payment method and Place order--> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectPaymentMethod"/> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="placeOrder"> + <argument name="orderNumberMessage" value="CONST.successCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml index 0ea7ec06ca869..a63fcec9f92bb 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml @@ -28,11 +28,12 @@ <arguments> <argument name="couponCode" type="string"/> </arguments> - <waitForElement selector="{{AdminCartPriceRuleDiscountSection.discountTab}}" time="30" stepKey="waitForCouponHeader" /> - <conditionalClick selector="{{AdminCartPriceRuleDiscountSection.discountTab}}" dependentSelector="{{AdminCartPriceRuleDiscountSection.discountBlockActive}}" visible="false" stepKey="clickCouponHeader" /> - <waitForElementVisible selector="{{AdminCartPriceRuleDiscountSection.couponInput}}" stepKey="waitForCouponField" /> - <fillField userInput="{{couponCode}}" selector="{{AdminCartPriceRuleDiscountSection.couponInput}}" stepKey="fillCouponField"/> - <click selector="{{AdminCartPriceRuleDiscountSection.applyCodeBtn}}" stepKey="clickApplyButton"/> + <waitForElement selector="{{StorefrontDiscountSection.discountTab}}" time="30" stepKey="waitForCouponHeader" /> + <conditionalClick selector="{{StorefrontDiscountSection.discountTab}}" dependentSelector="{{StorefrontDiscountSection.discountBlockActive}}" visible="false" stepKey="clickCouponHeader" /> + <waitForElementVisible selector="{{StorefrontDiscountSection.couponInput}}" stepKey="waitForCouponField" /> + <fillField userInput="{{couponCode}}" selector="{{StorefrontDiscountSection.couponInput}}" stepKey="fillCouponField"/> + <click selector="{{StorefrontDiscountSection.applyCodeBtn}}" stepKey="clickApplyButton"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.successMessage}}" stepKey="waitForSuccessMessage" /> <see userInput='You used coupon code "{{couponCode}}".' selector="{{StorefrontMessagesSection.successMessage}}" stepKey="seeSuccessMessage"/> </actionGroup> @@ -46,10 +47,20 @@ <!-- Cancel Sales Rule Coupon applied to the cart --> <actionGroup name="StorefrontCancelCouponActionGroup"> - <waitForElement selector="{{AdminCartPriceRuleDiscountSection.discountTab}}" time="30" stepKey="waitForCouponHeader" /> - <conditionalClick selector="{{AdminCartPriceRuleDiscountSection.discountTab}}" dependentSelector="{{AdminCartPriceRuleDiscountSection.discountBlockActive}}" visible="false" stepKey="clickCouponHeader" /> - <waitForElementVisible selector="{{AdminCartPriceRuleDiscountSection.couponInput}}" stepKey="waitForCouponField" /> - <click selector="{{AdminCartPriceRuleDiscountSection.cancelButton}}" stepKey="clickCancelButton"/> + <waitForElement selector="{{StorefrontDiscountSection.discountTab}}" time="30" stepKey="waitForCouponHeader" /> + <conditionalClick selector="{{StorefrontDiscountSection.discountTab}}" dependentSelector="{{AdminCartPriceRuleDiscountSection.discountBlockActive}}" visible="false" stepKey="clickCouponHeader" /> + <waitForElementVisible selector="{{StorefrontDiscountSection.couponInput}}" stepKey="waitForCouponField" /> + <click selector="{{StorefrontDiscountSection.cancelCoupon}}" stepKey="clickCancelButton"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.successMessage}}" stepKey="waitForSuccessMessage"/> <see userInput="You canceled the coupon code." selector="{{StorefrontMessagesSection.successMessage}}" stepKey="seeSuccessMessage"/> </actionGroup> + + <actionGroup name="StorefrontApplyCouponOnCheckoutActionGroup" extends="StorefrontApplyCouponActionGroup"> + <arguments> + <argument name="successMessage" type="string"/> + </arguments> + <waitForElementVisible selector="{{StorefrontDiscountSection.discountInput}}" stepKey="waitForCouponField"/> + <fillField userInput="{{couponCode}}" selector="{{StorefrontDiscountSection.discountInput}}" stepKey="fillCouponField"/> + <see userInput='{{successMessage}}' selector="{{StorefrontMessagesSection.successMessage}}" stepKey="seeSuccessMessage"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/StorefrontDiscountSection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/StorefrontDiscountSection.xml index 2ae50489b6d12..359e2eecb6b7e 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Section/StorefrontDiscountSection.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/StorefrontDiscountSection.xml @@ -6,10 +6,13 @@ */ --> <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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontDiscountSection"> - <element name="discountTab" type="button" selector="#block-discount"/> + <element name="discountTab" type="button" selector="#block-discount-heading"/> <element name="couponInput" type="input" selector="#coupon_code"/> <element name="applyCodeBtn" type="button" selector="//span[text()='Apply Discount']"/> + <element name="cancelCoupon" type="button" selector="//button[@value='Cancel Coupon']"/> + <element name="discountInput" type="input" selector="#discount-code"/> + <element name="discountBlockActive" type="text" selector=".block.discount.active"/> </section> </sections> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreShippingMethodsData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreShippingMethodsData.xml new file mode 100644 index 0000000000000..6b29baeb9eea3 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreShippingMethodsData.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="FreeShippingMethodsSettingConfig" type="free_shipping_config_state"> + <requiredEntity type="active">Active</requiredEntity> + </entity> + <entity name="Active" type="active"> + <data key="value">1</data> + </entity> + + <entity name="MinimumOrderAmount90" type="minimum_order_amount"> + <requiredEntity type="free_shipping_subtotal">Price</requiredEntity> + </entity> + <entity name="Price" type="free_shipping_subtotal"> + <data key="value">90</data> + </entity> + + <entity name="DefaultMinimumOrderAmount" type="minimum_order_amount"> + <requiredEntity type="free_shipping_subtotal">DefaultPrice</requiredEntity> + </entity> + <entity name="DefaultPrice" type="free_shipping_subtotal"> + <data key="value">0</data> + </entity> + + <entity name="DefaultShippingMethodsConfig" type="free_shipping_config_state"> + <requiredEntity type="active">DefaultFreeShipping</requiredEntity> + </entity> + <entity name="DefaultFreeShipping" type="active"> + <data key="value">0</data> + </entity> +</entities> diff --git a/app/code/Magento/Store/Test/Mftf/Metadata/store_shipping_methods-meta.xml b/app/code/Magento/Store/Test/Mftf/Metadata/store_shipping_methods-meta.xml new file mode 100644 index 0000000000000..46132973e52c8 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/Metadata/store_shipping_methods-meta.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="EnableFreeShippingConfigState" dataType="free_shipping_config_state" type="create" auth="adminFormKey" + url="/admin/system_config/save/section/carriers/" method="POST"> + <object key="groups" dataType="free_shipping_config_state"> + <object key="freeshipping" dataType="free_shipping_config_state"> + <object key="fields" dataType="free_shipping_config_state"> + <object key="active" dataType="active"> + <field key="value">string</field> + </object> + </object> + </object> + </object> + </operation> + + <operation name="DisableFreeShippingConfigState" dataType="disable_free_shipping_config_state" type="create" auth="adminFormKey" + url="/admin/system_config/save/section/carriers/" method="POST"> + <object key="groups" dataType="disable_free_shipping_config_state"> + <object key="freeshipping" dataType="disable_free_shipping_config_state"> + <object key="fields" dataType="disable_free_shipping_config_state"> + <object key="active" dataType="disable_free_shipping_config_state"> + <object key="inherit" dataType="disableFreeShipping"> + <field key="value">integer</field> + </object> + </object> + </object> + </object> + </object> + </operation> + + <operation name="MinimumOrderAmount" dataType="minimum_order_amount" type="create" auth="adminFormKey" + url="/admin/system_config/save/section/carriers/" method="POST"> + <object key="groups" dataType="minimum_order_amount"> + <object key="freeshipping" dataType="minimum_order_amount"> + <object key="fields" dataType="minimum_order_amount"> + <object key="free_shipping_subtotal" dataType="free_shipping_subtotal"> + <field key="value">string</field> + </object> + </object> + </object> + </object> + </operation> +</operations> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/StorefrontMessagesSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/StorefrontMessagesSection.xml index b07f2d356b9ea..2834c367f136c 100644 --- a/app/code/Magento/Ui/Test/Mftf/Section/StorefrontMessagesSection.xml +++ b/app/code/Magento/Ui/Test/Mftf/Section/StorefrontMessagesSection.xml @@ -10,5 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontMessagesSection"> <element name="successMessage" type="text" selector=".message-success"/> + <element name="errorMessage" type="text" selector=".message-error"/> </section> </sections> From e5afdb2956192c024c556e0c7e29bdfb72bcf1bd Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Wed, 13 Feb 2019 13:10:03 +0000 Subject: [PATCH 0642/1295] magento/magento2#21160: Adjusting ported pull request to 2.2 version --- .../Backend/Controller/Adminhtml/System/Design/Save.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 25cfb61d658c3..a2a53f3f787e3 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/System/Design/Save.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/System/Design/Save.php @@ -6,12 +6,10 @@ */ namespace Magento\Backend\Controller\Adminhtml\System\Design; -use Magento\Framework\App\Action\HttpPostActionInterface; - /** * Save design action. */ -class Save extends \Magento\Backend\Controller\Adminhtml\System\Design implements HttpPostActionInterface +class Save extends \Magento\Backend\Controller\Adminhtml\System\Design { /** * Filtering posted data. Converting localized data if needed From daa8996bbda2e7ca938a7e3a3e8afc40502cd98f Mon Sep 17 00:00:00 2001 From: gwharton <30697781+gwharton@users.noreply.github.com> Date: Wed, 13 Feb 2019 13:42:27 +0000 Subject: [PATCH 0643/1295] Changed setFromByStore to setFromByScope in deprecation comments. --- .../Magento/Framework/Mail/Template/TransportBuilderByStore.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php index baac867fd6ce8..bdb7619d7fe07 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php @@ -13,7 +13,7 @@ * * @deprecated The ability to set From address based on store is now available * in the \Magento\Framework\Mail\Template\TransportBuilder class - * @see \Magento\Framework\Mail\Template\TransportBuilder::setFromByStore + * @see \Magento\Framework\Mail\Template\TransportBuilder::setFromByScope */ class TransportBuilderByStore { From ec33ab8c279a017da6413260ce174055df2cd314 Mon Sep 17 00:00:00 2001 From: "w.perera" <w.perera@ism-apac.com> Date: Fri, 8 Feb 2019 12:51:57 +0530 Subject: [PATCH 0644/1295] added min=0 to qty field product detail page --- .../Catalog/view/frontend/templates/product/view/addtocart.phtml | 1 + .../view/frontend/templates/cart/item/configure/updatecart.phtml | 1 + 2 files changed, 2 insertions(+) diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/addtocart.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/addtocart.phtml index 9c18a18ff5837..baed8c1ce04de 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/addtocart.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/addtocart.phtml @@ -20,6 +20,7 @@ <input type="number" name="qty" id="qty" + min="0" value="<?= /* @escapeNotVerified */ $block->getProductDefaultQty() * 1 ?>" title="<?= /* @escapeNotVerified */ __('Qty') ?>" class="input-text qty" diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/item/configure/updatecart.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/item/configure/updatecart.phtml index c1db2f7775ca8..bfb7ddc55cda6 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/item/configure/updatecart.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/item/configure/updatecart.phtml @@ -20,6 +20,7 @@ <input type="number" name="qty" id="qty" + min="0" value="" title="<?= /* @escapeNotVerified */ __('Qty') ?>" class="input-text qty" From b86a3940282b558ecb0363b990b6ff3dc41a61d0 Mon Sep 17 00:00:00 2001 From: Amit Vishvakarma <amitvishwakarma@cedcoss.com> Date: Thu, 24 Jan 2019 11:26:24 +0530 Subject: [PATCH 0645/1295] Fixed issue related to Meta Keywords/Description Fixed issue related to Meta Keywords/Description --- .../Ui/DataProvider/Product/Form/Modifier/General.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php index e598773ac368d..71cfc3ef1853f 100755 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php @@ -361,8 +361,10 @@ protected function customizeNameListeners(array $meta) 'allowImport' => !$this->locator->getProduct()->getId(), ]; - if (!in_array($listener, $textListeners)) { - $importsConfig['elementTmpl'] = 'ui/form/element/input'; + if (in_array($listener, $textListeners)) { + $importsConfig['cols'] = 15; + $importsConfig['rows'] = 2; + $importsConfig['elementTmpl'] = 'ui/form/element/textarea'; } $meta = $this->arrayManager->merge($listenerPath . static::META_CONFIG_PATH, $meta, $importsConfig); From 8deb8f4d810646eeefde5bc94046f45c3be8e8db Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Wed, 13 Feb 2019 11:56:39 -0600 Subject: [PATCH 0646/1295] magento-engcom/magento2ce#2565: Fixed static test failures --- .../web/css/source/forms/_controls.less | 31 ++++++------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less index 44a086ac61392..9a5f9af5bfd68 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less @@ -107,19 +107,6 @@ } } -// ToDo UI: add month and date styles -// .admin__control-select-month { -// width: 140px; -// } - -// .admin__control-select-year { -// width: 103px; -// } - -// .admin__control-cvn { -// width: 3em; -// } - option:empty { display: none; } @@ -151,22 +138,24 @@ option:empty { .admin__control-file-label { &:before { &:extend(.abs-form-control-pattern); - - content:''; - left: 0; - position: absolute; - top: 0; - width: 100%; - z-index: 0; - .admin__control-file:active + &, .admin__control-file:focus + & { + /** + * @codingStandardsIgnoreStart + */ &:extend(.abs-form-control-pattern:focus); } .admin__control-file[disabled] + & { &:extend(.abs-form-control-pattern[disabled]); } + + content: ''; + left: 0; + position: absolute; + top: 0; + width: 100%; + z-index: 0; } } From 85cdc9d41bb027aca3ce5b4412f67e8af634c141 Mon Sep 17 00:00:00 2001 From: milindsingh <milind7@live.com> Date: Sat, 29 Dec 2018 00:08:12 +0530 Subject: [PATCH 0647/1295] Issue fix #20010 Wrong price amount in opengraph Issue fix #20010 Wrong price amount in opengraph --- .../frontend/templates/product/view/opengraph/general.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/general.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/general.phtml index a2b91a5eeb99f..40f86c7e68d6c 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/general.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/general.phtml @@ -14,7 +14,7 @@ <meta property="og:image" content="<?= $block->escapeUrl($block->getImage($block->getProduct(), 'product_base_image')->getImageUrl()) ?>" /> <meta property="og:description" content="<?= $block->escapeHtmlAttr($block->stripTags($block->getProduct()->getShortDescription())) ?>" /> <meta property="og:url" content="<?= $block->escapeUrl($block->getProduct()->getProductUrl()) ?>" /> -<?php if ($priceAmount = $block->getProduct()->getFinalPrice()):?> +<?php if ($priceAmount = $block->getProduct()->getPriceInfo()->getPrice(\Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE)->getAmount()):?> <meta property="product:price:amount" content="<?= /* @escapeNotVerified */ $priceAmount ?>"/> <?= $block->getChildHtml('meta.currency') ?> <?php endif;?> From 7bbaf78331d5883f68f6d217a6122069cc0ff4cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Fri, 19 Oct 2018 19:29:10 +0300 Subject: [PATCH 0648/1295] text showing feature added feature to show text when icon is set to false --- lib/web/css/source/lib/_icons.less | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/lib/web/css/source/lib/_icons.less b/lib/web/css/source/lib/_icons.less index d113935e2b1cd..08e4798426343 100644 --- a/lib/web/css/source/lib/_icons.less +++ b/lib/web/css/source/lib/_icons.less @@ -25,9 +25,12 @@ @_icon-font-text-hide: @icon-font__text-hide, @_icon-font-display: @icon-font__display ) when (@_icon-font-position = before) { - ._lib-icon-text-hide(@_icon-font-text-hide); .lib-css(display, @_icon-font-display); - text-decoration: none; + text-decoration: none; + + & when not (@_icon-font-content = false) { + ._lib-icon-text-hide(@_icon-font-text-hide); + } &:before { ._lib-icon-font( @@ -68,10 +71,13 @@ @_icon-font-text-hide: @icon-font__text-hide, @_icon-font-display: @icon-font__display ) when (@_icon-font-position = after) { - ._lib-icon-text-hide(@_icon-font-text-hide); .lib-css(display, @_icon-font-display); text-decoration: none; - + + & when not (@_icon-font-content = false) { + ._lib-icon-text-hide(@_icon-font-text-hide); + } + &:after { ._lib-icon-font( @_icon-font-content, @@ -151,8 +157,11 @@ @_icon-image-text-hide: @icon__text-hide ) when (@_icon-image-position = before) { display: inline-block; - ._lib-icon-text-hide(@_icon-image-text-hide); - + + & when not (@_icon-image = false) { + ._lib-icon-text-hide(@_icon-font-text-hide); + } + &:before { ._lib-icon-image( @_icon-image, @@ -179,7 +188,10 @@ @_icon-image-text-hide: @icon__text-hide ) when (@_icon-image-position = after) { display: inline-block; - ._lib-icon-text-hide(@_icon-image-text-hide); + + & when not (@_icon-image = false) { + ._lib-icon-text-hide(@_icon-font-text-hide); + } &:after { ._lib-icon-image( From 5aab17eb8e76eb25d2e56f014eac6087e215ec43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Tue, 23 Oct 2018 20:05:05 +0300 Subject: [PATCH 0649/1295] Update _icons.less fix typo --- lib/web/css/source/lib/_icons.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/source/lib/_icons.less b/lib/web/css/source/lib/_icons.less index 08e4798426343..abb8b43368f13 100644 --- a/lib/web/css/source/lib/_icons.less +++ b/lib/web/css/source/lib/_icons.less @@ -159,7 +159,7 @@ display: inline-block; & when not (@_icon-image = false) { - ._lib-icon-text-hide(@_icon-font-text-hide); + ._lib-icon-text-hide(@_icon-image-text-hide); } &:before { From f40d13914fae5a44b056d2cd1ef8dd7964020b78 Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Fri, 8 Feb 2019 18:17:34 -0500 Subject: [PATCH 0650/1295] Add alt text to saved payment method for accessibility Fixes #21089 --- .../Magento/Payment/Model/CcConfigProvider.php | 5 +++-- .../Test/Unit/Model/CcConfigProviderTest.php | 14 +++++++++++--- .../view/frontend/web/template/payment/form.html | 3 ++- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Payment/Model/CcConfigProvider.php b/app/code/Magento/Payment/Model/CcConfigProvider.php index 15bdd0072a51a..ba3721e6cfc2b 100644 --- a/app/code/Magento/Payment/Model/CcConfigProvider.php +++ b/app/code/Magento/Payment/Model/CcConfigProvider.php @@ -69,7 +69,7 @@ public function getIcons() } $types = $this->ccConfig->getCcAvailableTypes(); - foreach (array_keys($types) as $code) { + foreach ($types as $code => $label) { if (!array_key_exists($code, $this->icons)) { $asset = $this->ccConfig->createAsset('Magento_Payment::images/cc/' . strtolower($code) . '.png'); $placeholder = $this->assetSource->findSource($asset); @@ -78,7 +78,8 @@ public function getIcons() $this->icons[$code] = [ 'url' => $asset->getUrl(), 'width' => $width, - 'height' => $height + 'height' => $height, + 'title' => __($label), ]; } } diff --git a/app/code/Magento/Payment/Test/Unit/Model/CcConfigProviderTest.php b/app/code/Magento/Payment/Test/Unit/Model/CcConfigProviderTest.php index a8856166995fc..ff6aea44645cf 100644 --- a/app/code/Magento/Payment/Test/Unit/Model/CcConfigProviderTest.php +++ b/app/code/Magento/Payment/Test/Unit/Model/CcConfigProviderTest.php @@ -42,12 +42,14 @@ public function testGetConfig() 'vi' => [ 'url' => 'http://cc.card/vi.png', 'width' => getimagesize($imagesDirectoryPath . 'vi.png')[0], - 'height' => getimagesize($imagesDirectoryPath . 'vi.png')[1] + 'height' => getimagesize($imagesDirectoryPath . 'vi.png')[1], + 'title' => __('Visa'), ], 'ae' => [ 'url' => 'http://cc.card/ae.png', 'width' => getimagesize($imagesDirectoryPath . 'ae.png')[0], - 'height' => getimagesize($imagesDirectoryPath . 'ae.png')[1] + 'height' => getimagesize($imagesDirectoryPath . 'ae.png')[1], + 'title' => __('American Express'), ] ] ] @@ -56,11 +58,13 @@ public function testGetConfig() $ccAvailableTypesMock = [ 'vi' => [ + 'title' => 'Visa', 'fileId' => 'Magento_Payment::images/cc/vi.png', 'path' => $imagesDirectoryPath . 'vi.png', 'url' => 'http://cc.card/vi.png' ], 'ae' => [ + 'title' => 'American Express', 'fileId' => 'Magento_Payment::images/cc/ae.png', 'path' => $imagesDirectoryPath . 'ae.png', 'url' => 'http://cc.card/ae.png' @@ -68,7 +72,11 @@ public function testGetConfig() ]; $assetMock = $this->createMock(\Magento\Framework\View\Asset\File::class); - $this->ccConfigMock->expects($this->once())->method('getCcAvailableTypes')->willReturn($ccAvailableTypesMock); + $this->ccConfigMock->expects($this->once())->method('getCcAvailableTypes') + ->willReturn(array_combine( + array_keys($ccAvailableTypesMock), + array_column($ccAvailableTypesMock, 'title') + )); $this->ccConfigMock->expects($this->atLeastOnce()) ->method('createAsset') diff --git a/app/code/Magento/Vault/view/frontend/web/template/payment/form.html b/app/code/Magento/Vault/view/frontend/web/template/payment/form.html index 0ef330cd3014e..49cc488060120 100644 --- a/app/code/Magento/Vault/view/frontend/web/template/payment/form.html +++ b/app/code/Magento/Vault/view/frontend/web/template/payment/form.html @@ -19,7 +19,8 @@ <img data-bind="attr: { 'src': getIcons(getCardType()).url, 'width': getIcons(getCardType()).width, - 'height': getIcons(getCardType()).height + 'height': getIcons(getCardType()).height, + 'alt': getIcons(getCardType()).title }" class="payment-icon"> <span translate="'ending'"></span> <span text="getMaskedCard()"></span> From 4a0dc53116e94291e4a466241853383ea1eb9b77 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Tue, 12 Feb 2019 10:44:55 +0200 Subject: [PATCH 0651/1295] Fix static test. --- app/code/Magento/Payment/Model/CcConfigProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Payment/Model/CcConfigProvider.php b/app/code/Magento/Payment/Model/CcConfigProvider.php index ba3721e6cfc2b..497ce93c30c71 100644 --- a/app/code/Magento/Payment/Model/CcConfigProvider.php +++ b/app/code/Magento/Payment/Model/CcConfigProvider.php @@ -44,7 +44,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function getConfig() { From 79d5de06262da335e0762ad6f0e47e85c0199fe1 Mon Sep 17 00:00:00 2001 From: Sunil Patel <patelsunil42@gmail.com> Date: Wed, 6 Feb 2019 12:08:57 +0530 Subject: [PATCH 0652/1295] disable add to cart until page load --- .../view/frontend/templates/product/view/addtocart.phtml | 2 +- .../Magento/Catalog/view/frontend/web/js/validate-product.js | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/addtocart.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/addtocart.phtml index 9c18a18ff5837..fc578c7652419 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/addtocart.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/addtocart.phtml @@ -32,7 +32,7 @@ <button type="submit" title="<?= /* @escapeNotVerified */ $buttonTitle ?>" class="action primary tocart" - id="product-addtocart-button"> + id="product-addtocart-button" disabled> <span><?= /* @escapeNotVerified */ $buttonTitle ?></span> </button> <?= $block->getChildHtml('', true) ?> diff --git a/app/code/Magento/Catalog/view/frontend/web/js/validate-product.js b/app/code/Magento/Catalog/view/frontend/web/js/validate-product.js index c0637cb672dc6..ab848aa442f81 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/validate-product.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/validate-product.js @@ -13,7 +13,8 @@ define([ $.widget('mage.productValidate', { options: { bindSubmit: false, - radioCheckboxClosest: '.nested' + radioCheckboxClosest: '.nested', + addToCartButtonSelector: '.action.tocart' }, /** @@ -41,6 +42,7 @@ define([ return false; } }); + $(this.options.addToCartButtonSelector).attr('disabled',false); } }); From 410dc5101e1e6e78f9976af13f07966a1bbc8d1f Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Mon, 11 Feb 2019 15:14:09 +0200 Subject: [PATCH 0653/1295] ENGCOM-4178: Static test fix. --- .../Magento/Catalog/view/frontend/web/js/validate-product.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/view/frontend/web/js/validate-product.js b/app/code/Magento/Catalog/view/frontend/web/js/validate-product.js index ab848aa442f81..755e777a01f77 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/validate-product.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/validate-product.js @@ -42,7 +42,7 @@ define([ return false; } }); - $(this.options.addToCartButtonSelector).attr('disabled',false); + $(this.options.addToCartButtonSelector).attr('disabled', false); } }); From 474cc79c77289c596466f8ab95422b94edc61786 Mon Sep 17 00:00:00 2001 From: Govind Sharma <govindpokhrelsharma@cedcoss.com> Date: Tue, 8 Jan 2019 16:07:42 +0530 Subject: [PATCH 0654/1295] Fixed issue #19891 Fixed issue #19891 added check of attribute type_id --- .../Catalog/Controller/Adminhtml/Product/Attribute/Validate.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php index 3a81b4633b2ff..b4bb7fd05f740 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php @@ -100,7 +100,7 @@ public function execute() $attributeCode ); - if ($attribute->getId() && !$attributeId || $attributeCode === 'product_type') { + if ($attribute->getId() && !$attributeId || $attributeCode === 'product_type' || $attributeCode === 'type_id') { $message = strlen($this->getRequest()->getParam('attribute_code')) ? __('An attribute with this code already exists.') : __('An attribute with the same code (%1) already exists.', $attributeCode); From 180db3301dedcc50a1098c3b97ca715dd6f978e1 Mon Sep 17 00:00:00 2001 From: amol 2jcommerce <amol@2jcommerce.in> Date: Thu, 14 Feb 2019 10:51:02 +0530 Subject: [PATCH 0655/1295] focus-not-proper-on-configurable-product-swatches-2.2 --- .../Magento/Swatches/view/frontend/web/css/swatches.css | 8 ++++++++ .../luma/Magento_Catalog/web/css/source/_module.less | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/app/code/Magento/Swatches/view/frontend/web/css/swatches.css b/app/code/Magento/Swatches/view/frontend/web/css/swatches.css index 67b9fffcf1282..fb1dcff2ecfb0 100644 --- a/app/code/Magento/Swatches/view/frontend/web/css/swatches.css +++ b/app/code/Magento/Swatches/view/frontend/web/css/swatches.css @@ -31,6 +31,10 @@ margin-top: 10px; } +.swatch-attribute-options:focus { + box-shadow: none; +} + .swatch-option { /*width: 30px;*/ padding: 1px 2px; @@ -47,6 +51,10 @@ text-overflow: ellipsis; } +.swatch-option:focus { + box-shadow: 0 0 3px 1px #68a8e0; +} + .swatch-option.text { background: #F0F0F0; color: #686868; diff --git a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less index 228c6947c938b..365b7736b7e14 100644 --- a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less @@ -294,6 +294,11 @@ } .product-options-wrapper { + .fieldset { + &:focus { + box-shadow: none; + } + } .fieldset-product-options-inner { .legend { .lib-css(font-weight, @font-weight__semibold); From b4568442e60ba5ecbb5d325e5d72d55d8e4f3e95 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Thu, 14 Feb 2019 08:01:03 +0200 Subject: [PATCH 0656/1295] MAGETWO-95762: Resource model changes --- .../Model/ResourceModel/AbstractResource.php | 12 +++++-- .../Catalog/Model/ResourceModel/Product.php | 23 ++++++++---- .../Mftf/Section/AdminProductFormSection.xml | 1 + .../Eav/Model/Entity/AbstractEntity.php | 28 ++++++++++----- .../Attribute/UniqueValidationInterface.php | 35 +++++++++++++++++++ .../Entity/Attribute/UniqueValidator.php | 33 +++++++++++++++++ app/code/Magento/Eav/etc/di.xml | 1 + 7 files changed, 115 insertions(+), 18 deletions(-) create mode 100644 app/code/Magento/Eav/Model/Entity/Attribute/UniqueValidationInterface.php create mode 100644 app/code/Magento/Eav/Model/Entity/Attribute/UniqueValidator.php diff --git a/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php b/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php index 12009e62fd27e..7f50c7edbb049 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php @@ -9,6 +9,10 @@ namespace Magento\Catalog\Model\ResourceModel; use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; +use Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend; +use Magento\Eav\Model\Entity\Attribute\Frontend\AbstractFrontend; +use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource; +use Magento\Eav\Model\Entity\Attribute\UniqueValidationInterface; /** * Catalog entity abstract model @@ -39,16 +43,18 @@ abstract class AbstractResource extends \Magento\Eav\Model\Entity\AbstractEntity * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\Catalog\Model\Factory $modelFactory * @param array $data + * @param UniqueValidationInterface|null $uniqueValidator */ public function __construct( \Magento\Eav\Model\Entity\Context $context, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Catalog\Model\Factory $modelFactory, - $data = [] + $data = [], + UniqueValidationInterface $uniqueValidator = null ) { $this->_storeManager = $storeManager; $this->_modelFactory = $modelFactory; - parent::__construct($context, $data); + parent::__construct($context, $data, $uniqueValidator); } /** @@ -88,7 +94,7 @@ protected function _isApplicableAttribute($object, $attribute) /** * Check whether attribute instance (attribute, backend, frontend or source) has method and applicable * - * @param AbstractAttribute|\Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend|\Magento\Eav\Model\Entity\Attribute\Frontend\AbstractFrontend|\Magento\Eav\Model\Entity\Attribute\Source\AbstractSource $instance + * @param AbstractAttribute|AbstractBackend|AbstractFrontend|AbstractSource $instance * @param string $method * @param array $args array of arguments * @return boolean diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product.php b/app/code/Magento/Catalog/Model/ResourceModel/Product.php index b4b78996f762f..6998431d19a8f 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product.php @@ -8,6 +8,7 @@ use Magento\Catalog\Model\ResourceModel\Product\Website\Link as ProductWebsiteLink; use Magento\Framework\App\ObjectManager; use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer; +use Magento\Eav\Model\Entity\Attribute\UniqueValidationInterface; /** * Product entity resource model @@ -101,6 +102,7 @@ class Product extends AbstractResource * @param \Magento\Catalog\Model\Product\Attribute\DefaultAttributes $defaultAttributes * @param array $data * @param TableMaintainer|null $tableMaintainer + * @param UniqueValidationInterface|null $uniqueValidator * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -115,7 +117,8 @@ public function __construct( \Magento\Eav\Model\Entity\TypeFactory $typeFactory, \Magento\Catalog\Model\Product\Attribute\DefaultAttributes $defaultAttributes, $data = [], - TableMaintainer $tableMaintainer = null + TableMaintainer $tableMaintainer = null, + UniqueValidationInterface $uniqueValidator = null ) { $this->_categoryCollectionFactory = $categoryCollectionFactory; $this->_catalogCategory = $catalogCategory; @@ -127,7 +130,8 @@ public function __construct( $context, $storeManager, $modelFactory, - $data + $data, + $uniqueValidator ); $this->connectionName = 'catalog'; $this->tableMaintainer = $tableMaintainer ?: ObjectManager::getInstance()->get(TableMaintainer::class); @@ -289,7 +293,7 @@ protected function _afterSave(\Magento\Framework\DataObject $product) } /** - * {@inheritdoc} + * @inheritdoc */ public function delete($object) { @@ -575,7 +579,7 @@ public function countAll() } /** - * {@inheritdoc} + * @inheritdoc */ public function validate($object) { @@ -615,7 +619,7 @@ public function load($object, $entityId, $attributes = []) } /** - * {@inheritdoc} + * @inheritdoc * @SuppressWarnings(PHPMD.UnusedLocalVariable) * @since 101.0.0 */ @@ -657,6 +661,8 @@ public function save(\Magento\Framework\Model\AbstractModel $object) } /** + * Retrieve entity manager object + * * @return \Magento\Framework\EntityManager\EntityManager */ private function getEntityManager() @@ -669,6 +675,8 @@ private function getEntityManager() } /** + * Retrieve ProductWebsiteLink object + * * @deprecated 101.1.0 * @return ProductWebsiteLink */ @@ -678,6 +686,8 @@ private function getProductWebsiteLink() } /** + * Retrieve CategoryLink object + * * @deprecated 101.1.0 * @return \Magento\Catalog\Model\ResourceModel\Product\CategoryLink */ @@ -692,9 +702,10 @@ private function getProductCategoryLink() /** * Extends parent method to be appropriate for product. + * * Store id is required to correctly identify attribute value we are working with. * - * {@inheritdoc} + * @inheritdoc * @since 101.1.0 */ protected function getAttributeRow($entity, $object, $attribute) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml index 7a97a75556769..ed5a0480325dd 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml @@ -40,6 +40,7 @@ <element name="attributeSetDropDown" type="select" selector="div[data-index='attribute_set_id'] .action-select.admin__action-multiselect"/> <element name="requiredNameIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=name]>.admin__field-label span'), ':after').getPropertyValue('content');"/> <element name="requiredSkuIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=sku]>.admin__field-label span'), ':after').getPropertyValue('content');"/> + <element name="customSelectField" type="select" selector="select[name='product[{{attributeCode}}]']" parameterized="true"/> </section> <section name="ProductInWebsitesSection"> <element name="sectionHeader" type="button" selector="div[data-index='websites']" timeout="30"/> diff --git a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php index 40b884aec529f..7f01cf268d06b 100644 --- a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php +++ b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php @@ -12,6 +12,7 @@ use Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend; use Magento\Eav\Model\Entity\Attribute\Frontend\AbstractFrontend; use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource; +use Magento\Eav\Model\Entity\Attribute\UniqueValidationInterface; use Magento\Framework\App\Config\Element; use Magento\Framework\DataObject; use Magento\Framework\DB\Adapter\DuplicateException; @@ -217,12 +218,21 @@ abstract class AbstractEntity extends AbstractResource implements EntityInterfac */ protected $objectRelationProcessor; + /** + * @var UniqueValidationInterface + */ + private $uniqueValidator; + /** * @param Context $context * @param array $data + * @param UniqueValidationInterface|null $uniqueValidator */ - public function __construct(Context $context, $data = []) - { + public function __construct( + Context $context, + $data = [], + UniqueValidationInterface $uniqueValidator = null + ) { $this->_eavConfig = $context->getEavConfig(); $this->_resource = $context->getResource(); $this->_attrSetEntity = $context->getAttributeSetEntity(); @@ -231,6 +241,8 @@ public function __construct(Context $context, $data = []) $this->_universalFactory = $context->getUniversalFactory(); $this->transactionManager = $context->getTransactionManager(); $this->objectRelationProcessor = $context->getObjectRelationProcessor(); + $this->uniqueValidator = $uniqueValidator ?: + ObjectManager::getInstance()->get(UniqueValidationInterface::class); parent::__construct(); $properties = get_object_vars($this); foreach ($data as $key => $value) { @@ -499,6 +511,7 @@ public function addAttributeByScope(AbstractAttribute $attribute, $entity = null /** * Get attributes by scope * + * @param string $suffix * @return array */ private function getAttributesByScope($suffix) @@ -969,12 +982,8 @@ public function checkAttributeUniqueValue(AbstractAttribute $attribute, $object) $data = $connection->fetchCol($select, $bind); - $objectId = $object->getData($entityIdField); - if ($objectId) { - if (isset($data[0])) { - return $data[0] == $objectId; - } - return true; + if ($object->getData($entityIdField)) { + return $this->uniqueValidator->validate($attribute, $object, $this, $entityIdField, $data); } return !count($data); @@ -1984,7 +1993,8 @@ public function afterDelete(DataObject $object) /** * Load attributes for object - * if the object will not pass all attributes for this entity type will be loaded + * + * If the object will not pass all attributes for this entity type will be loaded * * @param array $attributes * @param AbstractEntity|null $object diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/UniqueValidationInterface.php b/app/code/Magento/Eav/Model/Entity/Attribute/UniqueValidationInterface.php new file mode 100644 index 0000000000000..f4912f25ae1de --- /dev/null +++ b/app/code/Magento/Eav/Model/Entity/Attribute/UniqueValidationInterface.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Eav\Model\Entity\Attribute; + +use Magento\Framework\DataObject; +use Magento\Eav\Model\Entity\AbstractEntity; + +/** + * Interface for unique attribute validator. + */ +interface UniqueValidationInterface +{ + /** + * Validate if attribute value is unique. + * + * @param AbstractAttribute $attribute + * @param DataObject $object + * @param AbstractEntity $entity + * @param string $entityLinkField + * @param array $entityIds + * @return bool + */ + public function validate( + AbstractAttribute $attribute, + DataObject $object, + AbstractEntity $entity, + string $entityLinkField, + array $entityIds + ); +} diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/UniqueValidator.php b/app/code/Magento/Eav/Model/Entity/Attribute/UniqueValidator.php new file mode 100644 index 0000000000000..da0d0e07f303a --- /dev/null +++ b/app/code/Magento/Eav/Model/Entity/Attribute/UniqueValidator.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Eav\Model\Entity\Attribute; + +use Magento\Framework\DataObject; +use Magento\Eav\Model\Entity\AbstractEntity; + +/** + * Class for validate unique attribute value. + */ +class UniqueValidator implements UniqueValidationInterface +{ + /** + * @inheritdoc + */ + public function validate( + AbstractAttribute $attribute, + DataObject $object, + AbstractEntity $entity, + string $entityLinkField, + array $entityIds + ) { + if (isset($entityIds[0])) { + return $entityIds[0] == $object->getData($entityLinkField); + } + return true; + } +} diff --git a/app/code/Magento/Eav/etc/di.xml b/app/code/Magento/Eav/etc/di.xml index c8afd10aa3eee..92c1ef11b9c1f 100644 --- a/app/code/Magento/Eav/etc/di.xml +++ b/app/code/Magento/Eav/etc/di.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <preference for="Magento\Eav\Model\Entity\Setup\PropertyMapperInterface" type="Magento\Eav\Model\Entity\Setup\PropertyMapper\Composite" /> <preference for="Magento\Eav\Model\Entity\AttributeLoaderInterface" type="Magento\Eav\Model\Entity\AttributeLoader" /> + <preference for="Magento\Eav\Model\Entity\Attribute\UniqueValidationInterface" type="Magento\Eav\Model\Entity\Attribute\UniqueValidator" /> <preference for="Magento\Eav\Api\Data\AttributeInterface" type="Magento\Eav\Model\Entity\Attribute" /> <preference for="Magento\Eav\Api\AttributeRepositoryInterface" type="Magento\Eav\Model\AttributeRepository" /> <preference for="Magento\Eav\Api\Data\AttributeGroupInterface" type="Magento\Eav\Model\Entity\Attribute\Group" /> From 3ade1b4279dd8ea8c65f327d8434ebd7492db46e Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Thu, 14 Feb 2019 09:53:26 +0200 Subject: [PATCH 0657/1295] MC-14842: Can't upgrade magento instance from 2.2.7 to 2.2.8 --- .../Magento/ConfigurableProduct/Setup/UpgradeData.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Setup/UpgradeData.php b/app/code/Magento/ConfigurableProduct/Setup/UpgradeData.php index 1ff78a632c3bb..2f53bc2292664 100644 --- a/app/code/Magento/ConfigurableProduct/Setup/UpgradeData.php +++ b/app/code/Magento/ConfigurableProduct/Setup/UpgradeData.php @@ -109,8 +109,9 @@ private function updateRelatedProductTypes(string $attributeId, array $relatedPr */ private function upgradeQuoteItemPrice(ModuleDataSetupInterface $setup) { - $connection = $setup->getConnection(); - $quoteItemTable = $setup->getTable('quote_item'); + $connection = $setup->getConnection('checkout'); + $quoteItemTable = $connection->getTableName('quote_item'); + $select = $connection->select(); $select->joinLeft( ['qi2' => $quoteItemTable], @@ -121,10 +122,10 @@ private function upgradeQuoteItemPrice(ModuleDataSetupInterface $setup) . ' AND qi1.parent_item_id IS NOT NULL' . ' AND qi2.product_type = "' . Configurable::TYPE_CODE . '"' ); - $updateQuoteItem = $setup->getConnection()->updateFromSelect( + $updateQuoteItem = $connection->updateFromSelect( $select, ['qi1' => $quoteItemTable] ); - $setup->getConnection()->query($updateQuoteItem); + $connection->query($updateQuoteItem); } } From 6596ca7676ebe478d04a5c8ee898263d52fa49f7 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Thu, 14 Feb 2019 10:08:01 +0200 Subject: [PATCH 0658/1295] MAGETWO-98245: Can't remove gift wrapping from shopping cart --- app/code/Magento/Checkout/Controller/Cart/UpdatePost.php | 3 +-- .../testsuite/Magento/Checkout/Controller/CartTest.php | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/code/Magento/Checkout/Controller/Cart/UpdatePost.php b/app/code/Magento/Checkout/Controller/Cart/UpdatePost.php index fb29014127490..90335f8fe164f 100644 --- a/app/code/Magento/Checkout/Controller/Cart/UpdatePost.php +++ b/app/code/Magento/Checkout/Controller/Cart/UpdatePost.php @@ -1,6 +1,5 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -95,7 +94,7 @@ protected function _updateShoppingCart() */ public function execute() { - if (!$this->getRequest()->isPost() || !$this->_formKeyValidator->validate($this->getRequest())) { + if (!$this->_formKeyValidator->validate($this->getRequest())) { return $this->resultRedirectFactory->create()->setPath('*/*/'); } diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php index 78e1a2377575f..d2e93f7c94ff4 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php @@ -240,7 +240,6 @@ public function testUpdatePostAction() 'update_cart_action' => 'update_qty', 'form_key' => $formKey->getFormKey(), ]; - $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue($postData); /** @var $customerSession \Magento\Customer\Model\Session */ $customerSession = $this->_objectManager->create(\Magento\Customer\Model\Session::class); From f6a5f2e61436396659952e11d0bc42452f4d6ee6 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Thu, 14 Feb 2019 12:31:37 +0200 Subject: [PATCH 0659/1295] MAGETWO-76424: Additional blank option in country dropdown --- .../Test/Mftf/Data/CountryOptionsConfigData.xml | 6 +++++- .../Metadata/country_options_config-meta.xml | 16 +++------------- ...StorefrontCustomerAddressEditFormSection.xml} | 2 +- ...gCountryDropdownWithOneAllowedCountryTest.xml | 8 ++++---- 4 files changed, 13 insertions(+), 19 deletions(-) rename app/code/Magento/{Directory => Backend}/Test/Mftf/Data/CountryOptionsConfigData.xml (72%) rename app/code/Magento/{Directory => Backend}/Test/Mftf/Metadata/country_options_config-meta.xml (64%) rename app/code/Magento/Customer/Test/Mftf/Section/{StorefrontCustomerAddNewAddressSection.xml => StorefrontCustomerAddressEditFormSection.xml} (89%) diff --git a/app/code/Magento/Directory/Test/Mftf/Data/CountryOptionsConfigData.xml b/app/code/Magento/Backend/Test/Mftf/Data/CountryOptionsConfigData.xml similarity index 72% rename from app/code/Magento/Directory/Test/Mftf/Data/CountryOptionsConfigData.xml rename to app/code/Magento/Backend/Test/Mftf/Data/CountryOptionsConfigData.xml index ceb21f5986487..593d3831b643f 100644 --- a/app/code/Magento/Directory/Test/Mftf/Data/CountryOptionsConfigData.xml +++ b/app/code/Magento/Backend/Test/Mftf/Data/CountryOptionsConfigData.xml @@ -7,9 +7,13 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> - <entity name="DefaultAllowCountriesConfig" type="allow_countries_config_default"> + <entity name="DefaultAllowCountriesConfig" type="allow_countries_config"> + <requiredEntity type="allow_countries_config_default">DefaultAllowCountries</requiredEntity> + </entity> + <entity name="DefaultAllowCountries" type="allow_countries_config_default"> <data key="value">0</data> </entity> + <entity name="SetAllowCountriesConfigUS" type="allow_countries_config"> <data key="value">US</data> </entity> diff --git a/app/code/Magento/Directory/Test/Mftf/Metadata/country_options_config-meta.xml b/app/code/Magento/Backend/Test/Mftf/Metadata/country_options_config-meta.xml similarity index 64% rename from app/code/Magento/Directory/Test/Mftf/Metadata/country_options_config-meta.xml rename to app/code/Magento/Backend/Test/Mftf/Metadata/country_options_config-meta.xml index 63193dd206d73..c54e99ff65d06 100644 --- a/app/code/Magento/Directory/Test/Mftf/Metadata/country_options_config-meta.xml +++ b/app/code/Magento/Backend/Test/Mftf/Metadata/country_options_config-meta.xml @@ -7,25 +7,15 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> - <operation name="AllowCountriesConfigDefault" dataType="allow_countries_config_default" type="create" auth="adminFormKey" url="/admin/system_config/save/section/general/" method="POST" successRegex="/messages-message-success/"> - <object key="groups" dataType="allow_countries_config_default"> - <object key="country" dataType="allow_countries_config_default"> - <object key="fields" dataType="allow_countries_config_default"> - <object key="allow" dataType="allow_countries_config_default"> - <object key="inherit" dataType="allow_countries_config_default"> - <field key="value">integer</field> - </object> - </object> - </object> - </object> - </object> - </operation> <operation name="AllowCountriesConfig" dataType="allow_countries_config" type="create" auth="adminFormKey" url="/admin/system_config/save/section/general/" method="POST" successRegex="/messages-message-success/"> <object key="groups" dataType="allow_countries_config"> <object key="country" dataType="allow_countries_config"> <object key="fields" dataType="allow_countries_config"> <object key="allow" dataType="allow_countries_config"> <field key="value">string</field> + <object key="inherit" dataType="allow_countries_config_default"> + <field key="value">integer</field> + </object> </object> </object> </object> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAddNewAddressSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAddressEditFormSection.xml similarity index 89% rename from app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAddNewAddressSection.xml rename to app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAddressEditFormSection.xml index 218c5e6c2f39f..2af00532301ed 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAddNewAddressSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAddressEditFormSection.xml @@ -6,7 +6,7 @@ */ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="StorefrontCustomerAddNewAddressSection"> + <section name="StorefrontCustomerAddressEditFormSection"> <element name="country" type="select" selector=".form-address-edit select#country" /> <element name="countryEmptyOption" type="select" selector=".form-address-edit select#country option[value='']"/> </section> diff --git a/app/code/Magento/Directory/Test/Mftf/Test/StorefrontCheckingCountryDropdownWithOneAllowedCountryTest.xml b/app/code/Magento/Directory/Test/Mftf/Test/StorefrontCheckingCountryDropdownWithOneAllowedCountryTest.xml index 10cb0fb53541e..90133737c6a1d 100644 --- a/app/code/Magento/Directory/Test/Mftf/Test/StorefrontCheckingCountryDropdownWithOneAllowedCountryTest.xml +++ b/app/code/Magento/Directory/Test/Mftf/Test/StorefrontCheckingCountryDropdownWithOneAllowedCountryTest.xml @@ -45,10 +45,10 @@ <!--Go to "Add New Address" page--> <amOnPage url="{{StorefrontCustomerAddressNewPage.url}}" stepKey="goToAddNewAddressPage"/> <!--Click on Country dropdown--> - <click selector="{{StorefrontCustomerAddNewAddressSection.country}}" stepKey="clickOnCountryDropdown"/> + <click selector="{{StorefrontCustomerAddressEditFormSection.country}}" stepKey="clickOnCountryDropdown"/> <!--Check dropdown options--> - <see selector="{{StorefrontCustomerAddNewAddressSection.country}}" userInput="United States" stepKey="seeUSInCountryDropdown"/> - <dontSee selector="{{StorefrontCustomerAddNewAddressSection.country}}" userInput="Brazil" stepKey="dontSeeBrazilInCountryDropdown"/> - <dontSeeElement selector="{{StorefrontCustomerAddNewAddressSection.countryEmptyOption}}" stepKey="dontSeeEmptyOptionInCountryDropdown"/> + <see selector="{{StorefrontCustomerAddressEditFormSection.country}}" userInput="United States" stepKey="seeUSInCountryDropdown"/> + <dontSee selector="{{StorefrontCustomerAddressEditFormSection.country}}" userInput="Brazil" stepKey="dontSeeBrazilInCountryDropdown"/> + <dontSeeElement selector="{{StorefrontCustomerAddressEditFormSection.countryEmptyOption}}" stepKey="dontSeeEmptyOptionInCountryDropdown"/> </test> </tests> From 1eb1f7251f8ada6be0fdf85fb6ad58dc8d2e01e4 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Thu, 14 Feb 2019 12:48:49 +0200 Subject: [PATCH 0660/1295] MC-14842: Can't upgrade magento instance from 2.2.7 to 2.2.8 --- app/code/Magento/ConfigurableProduct/Setup/UpgradeData.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Setup/UpgradeData.php b/app/code/Magento/ConfigurableProduct/Setup/UpgradeData.php index 2f53bc2292664..97d94ac8772ed 100644 --- a/app/code/Magento/ConfigurableProduct/Setup/UpgradeData.php +++ b/app/code/Magento/ConfigurableProduct/Setup/UpgradeData.php @@ -109,8 +109,9 @@ private function updateRelatedProductTypes(string $attributeId, array $relatedPr */ private function upgradeQuoteItemPrice(ModuleDataSetupInterface $setup) { - $connection = $setup->getConnection('checkout'); - $quoteItemTable = $connection->getTableName('quote_item'); + $connectionName = 'checkout'; + $connection = $setup->getConnection($connectionName); + $quoteItemTable = $setup->getTable('quote_item', $connectionName); $select = $connection->select(); $select->joinLeft( From 67ef38c5e05a9d0b49996abdf98da7df1abea1ad Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Sat, 9 Feb 2019 16:25:39 +0200 Subject: [PATCH 0661/1295] Improves the UX by moving the customer to the Dashboard's Recent Orders --- .../Magento/Sales/view/frontend/templates/reorder/sidebar.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/view/frontend/templates/reorder/sidebar.phtml b/app/code/Magento/Sales/view/frontend/templates/reorder/sidebar.phtml index 9b3633fde60b4..a2ab3d02b13ea 100644 --- a/app/code/Magento/Sales/view/frontend/templates/reorder/sidebar.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/reorder/sidebar.phtml @@ -57,7 +57,7 @@ </button> </div> <div class="secondary"> - <a class="action view" href="<?= /* @escapeNotVerified */ $block->getUrl('customer/account') ?>"> + <a class="action view" href="<?= /* @escapeNotVerified */ $block->getUrl('customer/account') ?>#my-orders-table"> <span><?= /* @escapeNotVerified */ __('View All') ?></span> </a> </div> From c3f6d09a9a0afb5d0a4e4bac5fc9e6e292ff7bbf Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Thu, 14 Feb 2019 13:12:36 +0200 Subject: [PATCH 0662/1295] MAGETWO-95422: Layered Navigation shows options not available in Catalog --- .../AdminProductAttributeActionGroup.xml | 34 +++++++++++++++++ .../Test/Mftf/Data/FrontendLabelData.xml | 1 + .../Mftf/Section/AdminProductFormSection.xml | 1 + ...inCreateConfigurableProductActionGroup.xml | 37 +++++++++++++++++++ 4 files changed, 73 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml index 1c6f115c2cfce..e82633f1321f0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml @@ -74,4 +74,38 @@ <click selector="{{AdminDataGridTableSection.row('1')}}" stepKey="clickOnAttributeRow"/> <waitForPageLoad stepKey="waitForAttributePageLoad" /> </actionGroup> + <actionGroup name="StartCreateProductAttribute"> + <arguments> + <argument name="attributeCode" type="string"/> + <argument name="attributeType" type="string" defaultValue="select"/> + </arguments> + <amOnPage url="{{ProductAttributePage.url}}" stepKey="goToNewProductAttributePage"/> + <fillField selector="{{AttributePropertiesSection.defaultLabel}}" userInput="{{attributeCode}}" stepKey="fillDefaultLabel"/> + <selectOption selector="{{AttributePropertiesSection.inputType}}" userInput="{{attributeType}}" stepKey="selectInputType"/> + <waitForElementVisible selector="{{AdminNewAttributePanelSection.addOption}}" stepKey="waitForElementVisible"/> + </actionGroup> + <actionGroup name="AddOptionToProductAttribute"> + <arguments> + <argument name="optionName" type="string"/> + <argument name="optionNumber" type="string"/> + </arguments> + <click selector="{{AdminNewAttributePanelSection.addOption}}" stepKey="clickAddOption"/> + <waitForElementVisible selector="{{AdminNewAttributePanelSection.optionAdminValue('optionNumber')}}" time="30" stepKey="waitForOptionRow"/> + <fillField selector="{{AdminNewAttributePanelSection.optionAdminValue('optionNumber')}}" userInput="{{optionName}}" stepKey="fillAdminLabel"/> + </actionGroup> + <actionGroup name="SetScopeToProductAttribute"> + <arguments> + <argument name="scope" type="string" defaultValue="1"/> + </arguments> + <click selector="{{AttributePropertiesSection.AdvancedProperties}}" stepKey="expandAdvancedProperties"/> + <selectOption selector="{{AttributePropertiesSection.scope}}" userInput="{{scope}}" stepKey="selectGlobalScope"/> + </actionGroup> + <actionGroup name="SetUseInLayeredNavigationToProductAttribute"> + <arguments> + <argument name="useInLayeredNavigation" type="string" defaultValue="1"/> + </arguments> + <scrollToTopOfPage stepKey="scrollToTop"/> + <click selector="{{StorefrontPropertiesSection.storefrontPropertiesTab}}" stepKey="goToStorefrontProperties"/> + <selectOption selector="{{AttributePropertiesSection.useInLayeredNavigation}}" userInput="{{useInLayeredNavigation}}" stepKey="selectUseInLayeredNavigation"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml index 7b01b6b4e5189..ec8a355523d63 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/FrontendLabelData.xml @@ -16,6 +16,7 @@ <entity name="ProductAttributeFrontendLabelThree" type="FrontendLabel"> <data key="store_id">0</data> <data key="label" unique="suffix">attributeThree</data> + <data key="default_label" unique="suffix">attributeThree</data> </entity> <entity name="ColorAttributeFrontandLabel" type="FrontendLabel"> <data key="store_id">0</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml index 7a97a75556769..b01daff9b4f0f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml @@ -180,6 +180,7 @@ <element name="applySinglePriceToAllSkus" type="radio" selector=".admin__field-label[for='apply-single-price-radio']"/> <element name="singlePrice" type="input" selector="#apply-single-price-input"/> <element name="attributeByName" type="input" selector="//label[text()='{{var}}']/preceding-sibling::input" parameterized="true"/> + <element name="checkboxByName" type="input" selector="//div[text()='{{var}}']//ancestor::tr//input" parameterized="true"/> </section> <section name="AdminNewAttributePanel"> <element name="saveAttribute" type="button" selector="#save" timeout="30"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateConfigurableProductActionGroup.xml index e16ee43978a1e..6c47a24315c9a 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateConfigurableProductActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateConfigurableProductActionGroup.xml @@ -90,4 +90,41 @@ <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="waitForNextPageOpened2"/> <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="generateProducts"/> </actionGroup> + + <actionGroup name="AdminCreateConfigurableProductTwoAttributesWithOptionsActionGroup" extends="AdminCreateConfigurableProductActionGroup"> + <arguments> + <argument name="attributeOption" type="string"/> + <argument name="attributeOption1" type="string"/> + <argument name="configurableAttributeCode1" type="string"/> + <argument name="attribute1Option" type="string"/> + <argument name="attribute1Option1" type="string"/> + <argument name="attribute1Option2" type="string"/> + </arguments> + + <remove keyForRemoval="startEditAttrSet"/> + <remove keyForRemoval="searchForAttrSet"/> + <remove keyForRemoval="waitForLoad"/> + <remove keyForRemoval="selectAttrSetProd"/> + <remove keyForRemoval="saveEditedProductForProduct"/> + <remove keyForRemoval="clickClearFilters"/> + <remove keyForRemoval="clickFiltersExpand"/> + <remove keyForRemoval="fillFilter"/> + <remove keyForRemoval="clickSearch"/> + <remove keyForRemoval="clickAttributeColorCheckbox"/> + <remove keyForRemoval="clickOnSelectAllSecond"/> + + <fillField userInput="{{configurationsPrice}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> + <fillField userInput="{{configurationsQty}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> + + <!--Select attributes --> + <click selector="{{AdminCreateProductConfigurationsPanel.checkboxByName(configurableAttributeCode)}}" after="openConfigurationPanel" stepKey="selectAttribute"/> + <click selector="{{AdminCreateProductConfigurationsPanel.checkboxByName(configurableAttributeCode1)}}" after="selectAttribute" stepKey="selectAttribute1"/> + + <!--Select options--> + <click selector="{{AdminCreateProductConfigurationsPanel.attributeByName(attributeOption)}}" after="clickNextButton" stepKey="selectAttributeOption"/> + <click selector="{{AdminCreateProductConfigurationsPanel.attributeByName(attributeOption1)}}" after="selectAttributeOption" stepKey="selectAttributeOption1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.attributeByName(attribute1Option)}}" after="selectAttributeOption1" stepKey="selectAttribute1Option"/> + <click selector="{{AdminCreateProductConfigurationsPanel.attributeByName(attribute1Option1)}}" after="selectAttribute1Option" stepKey="selectAttribute1Option1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.attributeByName(attribute1Option2)}}" after="selectAttribute1Option1" stepKey="selectAttribute1Option2"/> + </actionGroup> </actionGroups> From b94cbd86babdc607d8f59cd018fbdb31a6455b28 Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Thu, 14 Feb 2019 14:24:15 +0200 Subject: [PATCH 0663/1295] MAGETWO-90953: Incorrect page caching with enabled maintenance mode when accessing from the exclude IP list. --- .../Observer/SwitchPageCacheOnMaintenance.php | 108 ++++++++++ .../PageCacheState.php | 74 +++++++ .../SwitchPageCacheOnMaintenanceTest.php | 161 ++++++++++++++ app/code/Magento/PageCache/etc/events.xml | 3 + .../PageCacheStateTest.php | 69 ++++++ .../Magento/Framework/App/MaintenanceMode.php | 14 +- .../App/Test/Unit/MaintenanceModeTest.php | 199 +++++++++++++----- 7 files changed, 569 insertions(+), 59 deletions(-) create mode 100644 app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance.php create mode 100644 app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php create mode 100644 app/code/Magento/PageCache/Test/Unit/Observer/SwitchPageCacheOnMaintenanceTest.php create mode 100644 dev/tests/integration/testsuite/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheStateTest.php diff --git a/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance.php b/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance.php new file mode 100644 index 0000000000000..7a1cc8934c017 --- /dev/null +++ b/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance.php @@ -0,0 +1,108 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\PageCache\Observer; + +use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Event\Observer; +use Magento\Framework\App\Cache\Manager; +use Magento\PageCache\Model\Cache\Type as PageCacheType; +use Magento\PageCache\Observer\SwitchPageCacheOnMaintenance\PageCacheState; + +/** + * Switch Page Cache on maintenance. + */ +class SwitchPageCacheOnMaintenance implements ObserverInterface +{ + /** + * @var Manager + */ + private $cacheManager; + + /** + * @var PageCacheState + */ + private $pageCacheStateStorage; + + /** + * @param Manager $cacheManager + * @param PageCacheState $pageCacheStateStorage + */ + public function __construct(Manager $cacheManager, PageCacheState $pageCacheStateStorage) + { + $this->cacheManager = $cacheManager; + $this->pageCacheStateStorage = $pageCacheStateStorage; + } + + /** + * Switches Full Page Cache. + * + * Depending on enabling or disabling Maintenance Mode it turns off or restores Full Page Cache state. + * + * @param Observer $observer + * @return void + */ + public function execute(Observer $observer) + { + if ($observer->getData('isOn')) { + $this->pageCacheStateStorage->save($this->isFullPageCacheEnabled()); + $this->turnOffFullPageCache(); + } else { + $this->restoreFullPageCacheState(); + } + } + + /** + * Turns off Full Page Cache. + * + * @return void + */ + private function turnOffFullPageCache() + { + if (!$this->isFullPageCacheEnabled()) { + return; + } + + $this->cacheManager->clean([PageCacheType::TYPE_IDENTIFIER]); + $this->cacheManager->setEnabled([PageCacheType::TYPE_IDENTIFIER], false); + } + + /** + * Full Page Cache state. + * + * @return bool + */ + private function isFullPageCacheEnabled(): bool + { + $cacheStatus = $this->cacheManager->getStatus(); + + if (!array_key_exists(PageCacheType::TYPE_IDENTIFIER, $cacheStatus)) { + return false; + } + + return (bool)$cacheStatus[PageCacheType::TYPE_IDENTIFIER]; + } + + /** + * Restores Full Page Cache state. + * + * Returns FPC to previous state that was before maintenance mode turning on. + * + * @return void + */ + private function restoreFullPageCacheState() + { + $storedPageCacheState = $this->pageCacheStateStorage->isEnabled(); + $this->pageCacheStateStorage->flush(); + + if ($storedPageCacheState) { + $this->cacheManager->setEnabled([PageCacheType::TYPE_IDENTIFIER], true); + } + } +} diff --git a/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php b/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php new file mode 100644 index 0000000000000..4180885fcbc54 --- /dev/null +++ b/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php @@ -0,0 +1,74 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\PageCache\Observer\SwitchPageCacheOnMaintenance; + +use Magento\Framework\Filesystem; +use Magento\Framework\App\Filesystem\DirectoryList; + +/** + * Page Cache state. + */ +class PageCacheState +{ + /** + * Full Page Cache Off state file name. + */ + const PAGE_CACHE_STATE_FILENAME = '.maintenance.fpc.state'; + + /** + * @var Filesystem\Directory\WriteInterface + */ + private $flagDir; + + /** + * @param Filesystem $fileSystem + */ + public function __construct(Filesystem $fileSystem) + { + $this->flagDir = $fileSystem->getDirectoryWrite(DirectoryList::VAR_DIR); + } + + /** + * Saves Full Page Cache state. + * + * Saves FPC state across requests. + * + * @param bool $state + * @return void + */ + public function save(bool $state) + { + $this->flagDir->writeFile(self::PAGE_CACHE_STATE_FILENAME, (string)$state); + } + + /** + * Returns stored Full Page Cache state. + * + * @return bool + */ + public function isEnabled(): bool + { + if (!$this->flagDir->isExist(self::PAGE_CACHE_STATE_FILENAME)) { + return false; + } + + return (bool)$this->flagDir->readFile(self::PAGE_CACHE_STATE_FILENAME); + } + + /** + * Flushes Page Cache state storage. + * + * @return void + */ + public function flush() + { + $this->flagDir->delete(self::PAGE_CACHE_STATE_FILENAME); + } +} diff --git a/app/code/Magento/PageCache/Test/Unit/Observer/SwitchPageCacheOnMaintenanceTest.php b/app/code/Magento/PageCache/Test/Unit/Observer/SwitchPageCacheOnMaintenanceTest.php new file mode 100644 index 0000000000000..8c4661cddd44c --- /dev/null +++ b/app/code/Magento/PageCache/Test/Unit/Observer/SwitchPageCacheOnMaintenanceTest.php @@ -0,0 +1,161 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\PageCache\Test\Unit\Observer; + +use PHPUnit\Framework\TestCase; +use Magento\PageCache\Observer\SwitchPageCacheOnMaintenance; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\App\Cache\Manager; +use Magento\Framework\Event\Observer; +use Magento\PageCache\Model\Cache\Type as PageCacheType; +use Magento\PageCache\Observer\SwitchPageCacheOnMaintenance\PageCacheState; + +/** + * SwitchPageCacheOnMaintenance observer test. + */ +class SwitchPageCacheOnMaintenanceTest extends TestCase +{ + /** + * @var SwitchPageCacheOnMaintenance + */ + private $model; + + /** + * @var Manager|\PHPUnit_Framework_MockObject_MockObject + */ + private $cacheManager; + + /** + * @var PageCacheState|\PHPUnit_Framework_MockObject_MockObject + */ + private $pageCacheStateStorage; + + /** + * @var Observer|\PHPUnit_Framework_MockObject_MockObject + */ + private $observer; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = new ObjectManager($this); + $this->cacheManager = $this->createMock(Manager::class); + $this->pageCacheStateStorage = $this->createMock(PageCacheState::class); + $this->observer = $this->createMock(Observer::class); + + $this->model = $objectManager->getObject(SwitchPageCacheOnMaintenance::class, [ + 'cacheManager' => $this->cacheManager, + 'pageCacheStateStorage' => $this->pageCacheStateStorage, + ]); + } + + /** + * Tests execute when setting maintenance mode to on. + * + * @param array $cacheStatus + * @param bool $cacheState + * @param int $flushCacheCalls + * @return void + * @dataProvider enablingPageCacheStateProvider + */ + public function testExecuteWhileMaintenanceEnabling(array $cacheStatus, bool $cacheState, int $flushCacheCalls) + { + $this->observer->method('getData') + ->with('isOn') + ->willReturn(true); + $this->cacheManager->method('getStatus') + ->willReturn($cacheStatus); + + // Page Cache state will be stored. + $this->pageCacheStateStorage->expects($this->once()) + ->method('save') + ->with($cacheState); + + // Page Cache will be cleaned and disabled + $this->cacheManager->expects($this->exactly($flushCacheCalls)) + ->method('clean') + ->with([PageCacheType::TYPE_IDENTIFIER]); + $this->cacheManager->expects($this->exactly($flushCacheCalls)) + ->method('setEnabled') + ->with([PageCacheType::TYPE_IDENTIFIER], false); + + $this->model->execute($this->observer); + } + + /** + * Tests execute when setting Maintenance Mode to off. + * + * @param bool $storedCacheState + * @param int $enableCacheCalls + * @return void + * @dataProvider disablingPageCacheStateProvider + */ + public function testExecuteWhileMaintenanceDisabling(bool $storedCacheState, int $enableCacheCalls) + { + $this->observer->method('getData') + ->with('isOn') + ->willReturn(false); + + $this->pageCacheStateStorage->method('isEnabled') + ->willReturn($storedCacheState); + + // Nullify Page Cache state. + $this->pageCacheStateStorage->expects($this->once()) + ->method('flush'); + + // Page Cache will be enabled. + $this->cacheManager->expects($this->exactly($enableCacheCalls)) + ->method('setEnabled') + ->with([PageCacheType::TYPE_IDENTIFIER]); + + $this->model->execute($this->observer); + } + + /** + * Page Cache state data provider. + * + * @return array + */ + public function enablingPageCacheStateProvider(): array + { + return [ + 'page_cache_is_enable' => [ + 'cache_status' => [PageCacheType::TYPE_IDENTIFIER => 1], + 'cache_state' => true, + 'flush_cache_calls' => 1, + ], + 'page_cache_is_missing_in_system' => [ + 'cache_status' => [], + 'cache_state' => false, + 'flush_cache_calls' => 0, + ], + 'page_cache_is_disable' => [ + 'cache_status' => [PageCacheType::TYPE_IDENTIFIER => 0], + 'cache_state' => false, + 'flush_cache_calls' => 0, + ], + ]; + } + + /** + * Page Cache state data provider. + * + * @return array + */ + public function disablingPageCacheStateProvider(): array + { + return [ + ['stored_cache_state' => true, 'enable_cache_calls' => 1], + ['stored_cache_state' => false, 'enable_cache_calls' => 0], + ]; + } +} diff --git a/app/code/Magento/PageCache/etc/events.xml b/app/code/Magento/PageCache/etc/events.xml index 7584f5f36d69c..3f0a2532ae60a 100644 --- a/app/code/Magento/PageCache/etc/events.xml +++ b/app/code/Magento/PageCache/etc/events.xml @@ -57,4 +57,7 @@ <event name="customer_logout"> <observer name="FlushFormKey" instance="Magento\PageCache\Observer\FlushFormKey"/> </event> + <event name="maintenance_mode_changed"> + <observer name="page_cache_switcher_for_maintenance" instance="Magento\PageCache\Observer\SwitchPageCacheOnMaintenance"/> + </event> </config> diff --git a/dev/tests/integration/testsuite/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheStateTest.php b/dev/tests/integration/testsuite/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheStateTest.php new file mode 100644 index 0000000000000..456e6df3a7421 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheStateTest.php @@ -0,0 +1,69 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\PageCache\Observer\SwitchPageCacheOnMaintenance; + +use PHPUnit\Framework\TestCase; +use Magento\TestFramework\Helper\Bootstrap; + +/** + * Page Cache state test. + */ +class PageCacheStateTest extends TestCase +{ + /** + * @var PageCacheState + */ + private $pageCacheStateStorage; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->pageCacheStateStorage = $objectManager->get(PageCacheState::class); + } + + /** + * Tests save state. + * + * @param bool $state + * @return void + * @dataProvider saveStateProvider + */ + public function testSave(bool $state) + { + $this->pageCacheStateStorage->save($state); + $this->assertEquals($state, $this->pageCacheStateStorage->isEnabled()); + } + + /** + * Tests flush state. + * + * @return void + */ + public function testFlush() + { + $this->pageCacheStateStorage->save(true); + $this->assertTrue($this->pageCacheStateStorage->isEnabled()); + $this->pageCacheStateStorage->flush(); + $this->assertFalse($this->pageCacheStateStorage->isEnabled()); + } + + /** + * Save state provider. + * + * @return array + */ + public function saveStateProvider(): array + { + return [[true], [false]]; + } +} diff --git a/lib/internal/Magento/Framework/App/MaintenanceMode.php b/lib/internal/Magento/Framework/App/MaintenanceMode.php index 4e4328cb72aef..225375c7df463 100644 --- a/lib/internal/Magento/Framework/App/MaintenanceMode.php +++ b/lib/internal/Magento/Framework/App/MaintenanceMode.php @@ -7,6 +7,7 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem; +use Magento\Framework\Event\Manager; /** * Application Maintenance Mode @@ -39,13 +40,18 @@ class MaintenanceMode protected $flagDir; /** - * Constructor - * + * @var Manager + */ + private $eventManager; + + /** * @param \Magento\Framework\Filesystem $filesystem + * @param Manager|null $eventManager */ - public function __construct(Filesystem $filesystem) + public function __construct(Filesystem $filesystem, Manager $eventManager = null) { $this->flagDir = $filesystem->getDirectoryWrite(self::FLAG_DIR); + $this->eventManager = $eventManager ?: ObjectManager::getInstance()->get(Manager::class); } /** @@ -73,6 +79,8 @@ public function isOn($remoteAddr = '') */ public function set($isOn) { + $this->eventManager->dispatch('maintenance_mode_changed', ['isOn' => $isOn]); + if ($isOn) { return $this->flagDir->touch(self::FLAG_FILENAME); } diff --git a/lib/internal/Magento/Framework/App/Test/Unit/MaintenanceModeTest.php b/lib/internal/Magento/Framework/App/Test/Unit/MaintenanceModeTest.php index 5d1c22a38af4d..09bcbd760c87a 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/MaintenanceModeTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/MaintenanceModeTest.php @@ -6,9 +6,17 @@ namespace Magento\Framework\App\Test\Unit; -use \Magento\Framework\App\MaintenanceMode; +use Magento\Framework\App\MaintenanceMode; +use Magento\Framework\Event\Manager; +use Magento\Framework\Filesystem\Directory\WriteInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\Filesystem; +use PHPUnit\Framework\TestCase; -class MaintenanceModeTest extends \PHPUnit\Framework\TestCase +/** + * MaintenanceMode Test + */ +class MaintenanceModeTest extends TestCase { /** * @var MaintenanceMode @@ -16,141 +24,213 @@ class MaintenanceModeTest extends \PHPUnit\Framework\TestCase protected $model; /** - * @var \Magento\Framework\Filesystem\Directory\WriteInterface | \PHPUnit_Framework_MockObject_MockObject + * @var WriteInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $flagDir; + /** + * @var Manager|\PHPUnit_Framework_MockObject_MockObject + */ + private $eventManager; + + /** + * @inheritdoc + */ protected function setup() { - $this->flagDir = $this->getMockForAbstractClass(\Magento\Framework\Filesystem\Directory\WriteInterface::class); - $filesystem = $this->createMock(\Magento\Framework\Filesystem::class); - $filesystem->expects($this->any()) - ->method('getDirectoryWrite') - ->will($this->returnValue($this->flagDir)); + $this->flagDir = $this->getMockForAbstractClass(WriteInterface::class); + $filesystem = $this->createMock(Filesystem::class); + $filesystem->method('getDirectoryWrite') + ->willReturn($this->flagDir); + $this->eventManager = $this->createMock(Manager::class); - $this->model = new MaintenanceMode($filesystem); + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject(MaintenanceMode::class, [ + 'filesystem' => $filesystem, + 'eventManager' => $this->eventManager, + ]); } + /** + * Is On initial test + * + * @return void + */ public function testIsOnInitial() { - $this->flagDir->expects($this->once())->method('isExist') + $this->flagDir->expects($this->once()) + ->method('isExist') ->with(MaintenanceMode::FLAG_FILENAME) - ->will($this->returnValue(false)); + ->willReturn(false); $this->assertFalse($this->model->isOn()); } + /** + * Is On without ip test + * + * @return void + */ public function testisOnWithoutIP() { $mapisExist = [ [MaintenanceMode::FLAG_FILENAME, true], [MaintenanceMode::IP_FILENAME, false], ]; - $this->flagDir->expects($this->exactly(2))->method('isExist') - ->will(($this->returnValueMap($mapisExist))); + $this->flagDir->expects($this->exactly(2)) + ->method('isExist') + ->willReturnMap($mapisExist); $this->assertTrue($this->model->isOn()); } + /** + * Is On with IP test + * + * @return void + */ public function testisOnWithIP() { $mapisExist = [ [MaintenanceMode::FLAG_FILENAME, true], [MaintenanceMode::IP_FILENAME, true], ]; - $this->flagDir->expects($this->exactly(2))->method('isExist') - ->will(($this->returnValueMap($mapisExist))); + $this->flagDir->expects($this->exactly(2)) + ->method('isExist') + ->willReturnMap($mapisExist); $this->assertFalse($this->model->isOn()); } + /** + * Is On with IP but no Maintenance files test + * + * @return void + */ public function testisOnWithIPNoMaintenance() { - $this->flagDir->expects($this->once())->method('isExist') + $this->flagDir->expects($this->once()) + ->method('isExist') ->with(MaintenanceMode::FLAG_FILENAME) ->willReturn(false); $this->assertFalse($this->model->isOn()); } + /** + * Maintenance Mode On test + * + * Tests common scenario with Full Page Cache is set to On + * + * @return void + */ public function testMaintenanceModeOn() { - $this->flagDir->expects($this->at(0))->method('isExist')->with(MaintenanceMode::FLAG_FILENAME) - ->will($this->returnValue(false)); - $this->flagDir->expects($this->at(1))->method('touch')->will($this->returnValue(true)); - $this->flagDir->expects($this->at(2))->method('isExist')->with(MaintenanceMode::FLAG_FILENAME) - ->will($this->returnValue(true)); - $this->flagDir->expects($this->at(3))->method('isExist')->with(MaintenanceMode::IP_FILENAME) - ->will($this->returnValue(false)); + $this->eventManager->expects($this->once()) + ->method('dispatch') + ->with('maintenance_mode_changed', ['isOn' => true]); - $this->assertFalse($this->model->isOn()); - $this->assertTrue($this->model->set(true)); - $this->assertTrue($this->model->isOn()); + $this->flagDir->expects($this->once()) + ->method('touch') + ->with(MaintenanceMode::FLAG_FILENAME); + + $this->model->set(true); } + /** + * Maintenance Mode Off test + * + * Tests common scenario when before Maintenance Mode Full Page Cache was setted to on + * + * @return void + */ public function testMaintenanceModeOff() { - $this->flagDir->expects($this->at(0))->method('isExist')->with(MaintenanceMode::FLAG_FILENAME) - ->will($this->returnValue(true)); - $this->flagDir->expects($this->at(1))->method('delete')->with(MaintenanceMode::FLAG_FILENAME) - ->will($this->returnValue(false)); - $this->flagDir->expects($this->at(2))->method('isExist')->with(MaintenanceMode::FLAG_FILENAME) - ->will($this->returnValue(false)); - - $this->assertFalse($this->model->set(false)); - $this->assertFalse($this->model->isOn()); + $this->eventManager->expects($this->once()) + ->method('dispatch') + ->with('maintenance_mode_changed', ['isOn' => false]); + + $this->flagDir->method('isExist') + ->with(MaintenanceMode::FLAG_FILENAME) + ->willReturn(true); + + $this->flagDir->expects($this->once()) + ->method('delete') + ->with(MaintenanceMode::FLAG_FILENAME); + + $this->model->set(false); } + /** + * Set empty addresses test + * + * @return void + */ public function testSetAddresses() { $mapisExist = [ [MaintenanceMode::FLAG_FILENAME, true], [MaintenanceMode::IP_FILENAME, true], ]; - $this->flagDir->expects($this->any())->method('isExist')->will($this->returnValueMap($mapisExist)); - $this->flagDir->expects($this->any())->method('writeFile') + $this->flagDir->method('isExist') + ->willReturnMap($mapisExist); + $this->flagDir->method('writeFile') ->with(MaintenanceMode::IP_FILENAME) - ->will($this->returnValue(true)); + ->willReturn(true); - $this->flagDir->expects($this->any())->method('readFile') + $this->flagDir->method('readFile') ->with(MaintenanceMode::IP_FILENAME) - ->will($this->returnValue('')); + ->willReturn(''); $this->model->setAddresses(''); $this->assertEquals([''], $this->model->getAddressInfo()); } + /** + * Set single address test + * + * @return void + */ public function testSetSingleAddresses() { $mapisExist = [ [MaintenanceMode::FLAG_FILENAME, true], [MaintenanceMode::IP_FILENAME, true], ]; - $this->flagDir->expects($this->any())->method('isExist')->will($this->returnValueMap($mapisExist)); - $this->flagDir->expects($this->any())->method('delete')->will($this->returnValueMap($mapisExist)); + $this->flagDir->method('isExist') + ->willReturnMap($mapisExist); + $this->flagDir->method('delete') + ->willReturnMap($mapisExist); - $this->flagDir->expects($this->any())->method('writeFile') - ->will($this->returnValue(10)); + $this->flagDir->method('writeFile') + ->willReturn(10); - $this->flagDir->expects($this->any())->method('readFile') + $this->flagDir->method('readFile') ->with(MaintenanceMode::IP_FILENAME) - ->will($this->returnValue('address1')); + ->willReturn('address1'); $this->model->setAddresses('address1'); $this->assertEquals(['address1'], $this->model->getAddressInfo()); } + /** + * Is On when multiple addresses test was setted + * + * @return void + */ public function testOnSetMultipleAddresses() { $mapisExist = [ [MaintenanceMode::FLAG_FILENAME, true], [MaintenanceMode::IP_FILENAME, true], ]; - $this->flagDir->expects($this->any())->method('isExist')->will($this->returnValueMap($mapisExist)); - $this->flagDir->expects($this->any())->method('delete')->will($this->returnValueMap($mapisExist)); + $this->flagDir->method('isExist') + ->willReturnMap($mapisExist); + $this->flagDir->method('delete') + ->willReturnMap($mapisExist); - $this->flagDir->expects($this->any())->method('writeFile') - ->will($this->returnValue(10)); + $this->flagDir->method('writeFile') + ->willReturn(10); - $this->flagDir->expects($this->any())->method('readFile') + $this->flagDir->method('readFile') ->with(MaintenanceMode::IP_FILENAME) - ->will($this->returnValue('address1,10.50.60.123')); + ->willReturn('address1,10.50.60.123'); $expectedArray = ['address1', '10.50.60.123']; $this->model->setAddresses('address1,10.50.60.123'); @@ -159,18 +239,25 @@ public function testOnSetMultipleAddresses() $this->assertTrue($this->model->isOn('address3')); } + /** + * Is Off when multiple addresses test was setted + * + * @return void + */ public function testOffSetMultipleAddresses() { $mapisExist = [ [MaintenanceMode::FLAG_FILENAME, false], [MaintenanceMode::IP_FILENAME, true], ]; - $this->flagDir->expects($this->any())->method('isExist')->will($this->returnValueMap($mapisExist)); - $this->flagDir->expects($this->any())->method('delete')->will($this->returnValueMap($mapisExist)); + $this->flagDir->method('isExist') + ->willReturnMap($mapisExist); + $this->flagDir->method('delete') + ->willReturnMap($mapisExist); - $this->flagDir->expects($this->any())->method('readFile') + $this->flagDir->method('readFile') ->with(MaintenanceMode::IP_FILENAME) - ->will($this->returnValue('address1,10.50.60.123')); + ->willReturn('address1,10.50.60.123'); $expectedArray = ['address1', '10.50.60.123']; $this->model->setAddresses('address1,10.50.60.123'); From af81dcee6846e82cd0855971b490c69a923deacc Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Thu, 14 Feb 2019 14:42:21 +0200 Subject: [PATCH 0664/1295] ENGCOM-4000: MFTF test fix. --- .../Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml | 2 ++ app/code/Magento/Widget/Test/Mftf/Data/WidgetsData.xml | 1 + .../Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml | 2 ++ 3 files changed, 5 insertions(+) diff --git a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml index dfb33b197d74f..0acbc33fcb06b 100644 --- a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml +++ b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml @@ -32,6 +32,8 @@ <click selector="{{AdminNewWidgetSection.addNewCondition}}" stepKey="clickAddNewCondition"/> <selectOption selector="{{AdminNewWidgetSection.selectCondition}}" userInput="{{widget.condition}}" stepKey="selectCondition"/> <waitForElement selector="{{AdminNewWidgetSection.ruleParameter}}" stepKey="waitRuleParameter"/> + <click selector="{{AdminNewWidgetSection.ruleParameterEdit}}" stepKey="clickRuleParameterEdit"/> + <selectOption selector="{{AdminNewWidgetSection.ruleParameterEditSelect}}" userInput="{{widget.ruleIsOneOf}}" stepKey="selectRuleParameterEdit"/> <click selector="{{AdminNewWidgetSection.ruleParameter}}" stepKey="clickRuleParameter"/> <click selector="{{AdminNewWidgetSection.openChooser}}" stepKey="clickChooser"/> <waitForPageLoad stepKey="waitForAjaxLoad"/> diff --git a/app/code/Magento/Widget/Test/Mftf/Data/WidgetsData.xml b/app/code/Magento/Widget/Test/Mftf/Data/WidgetsData.xml index d7db8fe50cb7f..ce56a6d92dd97 100644 --- a/app/code/Magento/Widget/Test/Mftf/Data/WidgetsData.xml +++ b/app/code/Magento/Widget/Test/Mftf/Data/WidgetsData.xml @@ -18,6 +18,7 @@ <data key="condition">SKU</data> <data key="display_on">All Pages</data> <data key="container">Main Content Area</data> + <data key="ruleIsOneOf">is one of</data> </entity> <entity name="DynamicBlocksRotatorWidget" type="widget"> <data key="type">Banner Rotator</data> diff --git a/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml b/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml index 37ad425114d5c..389d7bd1ab0e1 100644 --- a/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml +++ b/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml @@ -20,6 +20,8 @@ <element name="widgetOptions" type="select" selector="#widget_instace_tabs_properties_section"/> <element name="addNewCondition" type="select" selector=".rule-param.rule-param-new-child"/> <element name="selectCondition" type="input" selector="#conditions__1__new_child"/> + <element name="ruleParameterEdit" type="select" selector="#conditions__1__children>li:nth-child(1)>span:nth-child(3)>a"/> + <element name="ruleParameterEditSelect" type="select" selector="#conditions__1__children>li:nth-child(1)>span:nth-child(3)>span>select"/> <element name="ruleParameter" type="select" selector="#conditions__1__children>li:nth-child(1)>span:nth-child(4)>a"/> <element name="setRuleParameter" type="input" selector="#conditions__1--1__value"/> <element name="applyParameter" type="button" selector=".rule-param-apply"/> From 5af1479bf4d3e87a8e794cadc78a1bc1a3c2eb35 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Thu, 14 Feb 2019 14:58:01 +0200 Subject: [PATCH 0665/1295] ENGCOM-4247: Static test fix. --- .../Magento_Backend/web/css/source/module/main/_page-nav.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_page-nav.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_page-nav.less index 36c1ecbca9d99..070ee6347508f 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_page-nav.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_page-nav.less @@ -203,7 +203,7 @@ font-weight: @font-weight__heavier; line-height: @line-height__s; margin: 0 0 -1px; - padding: 2rem 0rem 2rem 1rem; + padding: 2rem 0 2rem 1rem; transition: @admin__page-nav-transition; word-wrap: break-word; } From de8d46fac2925323f84d47b1711440c7e12dedb0 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Thu, 14 Feb 2019 15:07:57 +0200 Subject: [PATCH 0666/1295] MAGETWO-94147: Swatch Attribute is not displayed in the Widget CMS --- ...minCategoryProductAttributeActionGroup.xml | 8 +- .../Block/Product/ProductsList.php | 80 ++++++++++++++- .../layout/catalog_widget_product_list.xml | 14 +++ .../product/widget/content/grid.phtml | 99 ++++++++++--------- .../CreateNewPageWithWidgetActionGroup.xml | 49 +++++++++ .../DeletePageByUrlKeyActionGroup.xml | 10 +- .../Section/CmsNewPagePageActionsSection.xml | 1 + .../Section/AdminNewAttributePanelSection.xml | 9 +- ...figurationsWithVisualSwatchActionGroup.xml | 11 ++- ...SwatchAttributesDisplayInWidgetCMSTest.xml | 71 +++++++++++++ .../layout/catalog_widget_product_list.xml | 14 +++ .../view/frontend/layout/cms_page_view.xml | 12 +++ 12 files changed, 324 insertions(+), 54 deletions(-) create mode 100644 app/code/Magento/CatalogWidget/view/frontend/layout/catalog_widget_product_list.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithWidgetActionGroup.xml create mode 100644 app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml create mode 100644 app/code/Magento/Swatches/view/frontend/layout/catalog_widget_product_list.xml create mode 100644 app/code/Magento/Swatches/view/frontend/layout/cms_page_view.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryProductAttributeActionGroup.xml index 5c71342264212..21e41732369e9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryProductAttributeActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryProductAttributeActionGroup.xml @@ -7,15 +7,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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!--Action to delete product attribute--> <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"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFiltersBeforeDelete"/> <fillField selector="{{AdminProductAttributeGridSection.gridFilterFrontEndLabel}}" userInput="{{productAttribute.default_label}}" stepKey="fillAttributeDefaultLabelInput"/> <click selector="{{AdminProductAttributeGridSection.search}}" stepKey="searchForAttribute"/> @@ -23,7 +22,8 @@ <waitForPageLoad time="30" stepKey="waitForPageLoad"/> <click selector="{{AdminProductAttributeEditSection.deleteAttribute}}" stepKey="deleteProductAttribute"/> <waitForElementVisible selector="{{AdminConfirmationModalSection.message}}" stepKey="waitingForWarningModal"/> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmStoreDelete"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDelete"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFiltersAfterDelete"/> </actionGroup> <actionGroup name="navigateToProductAttribute"> diff --git a/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php b/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php index cb462ada0fc91..f5a69418c5938 100644 --- a/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php +++ b/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php @@ -11,6 +11,10 @@ use Magento\Framework\Pricing\PriceCurrencyInterface; use Magento\Framework\Serialize\Serializer\Json; use Magento\Widget\Block\BlockInterface; +use Magento\Framework\Url\EncoderInterface; +use Magento\Framework\View\LayoutFactory; +use Magento\Catalog\Model\Product; +use Magento\Framework\App\ActionInterface; /** * Catalog Products List widget block @@ -94,6 +98,21 @@ class ProductsList extends \Magento\Catalog\Block\Product\AbstractProduct implem */ private $json; + /** + * @var LayoutFactory + */ + private $layoutFactory; + + /** + * @var \Magento\Framework\Url\EncoderInterface|null + */ + private $urlEncoder; + + /** + * @var \Magento\Framework\View\Element\RendererList + */ + private $rendererListBlock; + /** * @param \Magento\Catalog\Block\Product\Context $context * @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory @@ -104,6 +123,10 @@ class ProductsList extends \Magento\Catalog\Block\Product\AbstractProduct implem * @param \Magento\Widget\Helper\Conditions $conditionsHelper * @param array $data * @param Json|null $json + * @param LayoutFactory|null $layoutFactory + * @param \Magento\Framework\Url\EncoderInterface|null $urlEncoder + * + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\Catalog\Block\Product\Context $context, @@ -114,7 +137,9 @@ public function __construct( \Magento\CatalogWidget\Model\Rule $rule, \Magento\Widget\Helper\Conditions $conditionsHelper, array $data = [], - Json $json = null + Json $json = null, + LayoutFactory $layoutFactory = null, + EncoderInterface $urlEncoder = null ) { $this->productCollectionFactory = $productCollectionFactory; $this->catalogProductVisibility = $catalogProductVisibility; @@ -123,6 +148,8 @@ public function __construct( $this->rule = $rule; $this->conditionsHelper = $conditionsHelper; $this->json = $json ?: ObjectManager::getInstance()->get(Json::class); + $this->layoutFactory = $layoutFactory ?: ObjectManager::getInstance()->get(LayoutFactory::class); + $this->urlEncoder = $urlEncoder ?: ObjectManager::getInstance()->get(EncoderInterface::class); parent::__construct( $context, $data @@ -210,6 +237,39 @@ public function getProductPriceHtml( return $price; } + /** + * @inheritdoc + */ + protected function getDetailsRendererList() + { + /** @var $layout \Magento\Framework\View\LayoutInterface */ + $layout = $this->layoutFactory->create(['cacheable' => false]); + $layout->getUpdate()->addHandle('catalog_widget_product_list')->load(); + $layout->generateXml(); + $layout->generateElements(); + + return $layout->getBlock('category.product.type.widget.details.renderers'); + } + + /** + * Get post parameters. + * + * @param Product $product + * @return array + */ + public function getAddToCartPostParams(Product $product) + { + $url = $this->getAddToCartUrl($product); + + return [ + 'action' => $url, + 'data' => [ + 'product' => $product->getEntityId(), + ActionInterface::PARAM_NAME_URL_ENCODED => $this->urlEncoder->encode($url), + ] + ]; + } + /** * {@inheritdoc} */ @@ -400,6 +460,24 @@ private function getPriceCurrency() } /** + * @inheritdoc + */ + public function getAddToCartUrl($product, $additional = []) + { + $requestingPageUrl = $this->getRequest()->getParam('requesting_page_url'); + + if (!empty($requestingPageUrl)) { + $additional['useUencPlaceholder'] = true; + $url = parent::getAddToCartUrl($product, $additional); + return str_replace('%25uenc%25', $this->urlEncoder->encode($requestingPageUrl), $url); + } + + return parent::getAddToCartUrl($product, $additional); + } + + /** + * Get widget block name + * * @return string */ private function getWidgetPagerBlockName() diff --git a/app/code/Magento/CatalogWidget/view/frontend/layout/catalog_widget_product_list.xml b/app/code/Magento/CatalogWidget/view/frontend/layout/catalog_widget_product_list.xml new file mode 100644 index 0000000000000..4fe7af7f34683 --- /dev/null +++ b/app/code/Magento/CatalogWidget/view/frontend/layout/catalog_widget_product_list.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> + <body> + <block class="Magento\Framework\View\Element\RendererList" name="category.product.type.widget.details.renderers"> + <block class="Magento\Framework\View\Element\Template" name="category.product.type.details.renderers.default" as="default"/> + </block> + </body> +</page> diff --git a/app/code/Magento/CatalogWidget/view/frontend/templates/product/widget/content/grid.phtml b/app/code/Magento/CatalogWidget/view/frontend/templates/product/widget/content/grid.phtml index 574cbe1107e88..0217838b7fd8b 100644 --- a/app/code/Magento/CatalogWidget/view/frontend/templates/product/widget/content/grid.phtml +++ b/app/code/Magento/CatalogWidget/view/frontend/templates/product/widget/content/grid.phtml @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +use Magento\Framework\App\Action\Action; // @codingStandardsIgnoreFile @@ -48,57 +49,67 @@ <?= $block->escapeHtml($_item->getName()) ?> </a> </strong> - <?php - echo $block->getProductPriceHtml($_item, $type); - ?> - <?php if ($templateType): ?> <?= $block->getReviewsSummaryHtml($_item, $templateType) ?> <?php endif; ?> + <?= $block->getProductPriceHtml($_item, $type) ?> + + <?= $block->getProductDetailsHtml($_item) ?> + <?php if ($showWishlist || $showCompare || $showCart): ?> - <div class="product-item-actions"> - <?php if ($showCart): ?> - <div class="actions-primary"> - <?php if ($_item->isSaleable()): ?> - <?php if ($_item->getTypeInstance()->hasRequiredOptions($_item)): ?> - <button class="action tocart primary" data-mage-init='{"redirectUrl":{"url":"<?= $block->escapeUrl($block->getAddToCartUrl($_item)) ?>"}}' type="button" title="<?= $block->escapeHtmlAttr(__('Add to Cart')) ?>"> - <span><?= $block->escapeHtml(__('Add to Cart')) ?></span> - </button> - <?php else: ?> - <?php - $postDataHelper = $this->helper('Magento\Framework\Data\Helper\PostHelper'); - $postData = $postDataHelper->getPostData($block->getAddToCartUrl($_item), ['product' => $_item->getEntityId()]) - ?> - <button class="action tocart primary" data-post='<?= /* @noEscape */ $postData ?>' type="button" title="<?= $block->escapeHtmlAttr(__('Add to Cart')) ?>"> - <span><?= $block->escapeHtml(__('Add to Cart')) ?></span> - </button> + <div class="product-item-inner"> + <div class="product-item-actions"> + <?php if ($showCart): ?> + <div class="actions-primary"> + <?php if ($_item->isSaleable()): ?> + <?php $postParams = $block->getAddToCartPostParams($_item); ?> + <form data-role="tocart-form" + data-product-sku="<?= $block->escapeHtml($_item->getSku()) ?>" + action="<?= /* @NoEscape */ + $postParams['action'] ?>" method="post"> + <input type="hidden" name="product" + value="<?= /* @escapeNotVerified */ + $postParams['data']['product'] ?>"> + <input type="hidden" name="<?= /* @escapeNotVerified */ + Action::PARAM_NAME_URL_ENCODED ?>" + value="<?= /* @escapeNotVerified */ + $postParams['data'][Action::PARAM_NAME_URL_ENCODED] ?>"> + <?= $block->getBlockHtml('formkey') ?> + <button type="submit" + title="<?= $block->escapeHtml(__('Add to Cart')) ?>" + class="action tocart primary"> + <span><?= /* @escapeNotVerified */ + __('Add to Cart') ?></span> + </button> + </form> + <?php else: ?> + + <?php if ($_item->getIsSalable()): ?> + <div class="stock available"><span><?= $block->escapeHtml(__('In stock')) ?></span></div> + <?php else: ?> + <div class="stock unavailable"><span><?= $block->escapeHtml(__('Out of stock')) ?></span></div> + <?php endif; ?> + <?php endif; ?> + </div> + <?php endif; ?> + <?php if ($showWishlist || $showCompare): ?> + <div class="actions-secondary" data-role="add-to-links"> + <?php if ($this->helper('Magento\Wishlist\Helper\Data')->isAllow() && $showWishlist): ?> + <a href="#" + data-post='<?= /* @noEscape */ $block->getAddToWishlistParams($_item) ?>' class="action towishlist" data-action="add-to-wishlist" title="<?= $block->escapeHtmlAttr(__('Add to Wish List')) ?>"> + <span><?= $block->escapeHtml(__('Add to Wish List')) ?></span> + </a> <?php endif; ?> - <?php else: ?> - <?php if ($_item->getIsSalable()): ?> - <div class="stock available"><span><?= $block->escapeHtml(__('In stock')) ?></span></div> - <?php else: ?> - <div class="stock unavailable"><span><?= $block->escapeHtml(__('Out of stock')) ?></span></div> + <?php if ($block->getAddToCompareUrl() && $showCompare): ?> + <?php $compareHelper = $this->helper('Magento\Catalog\Helper\Product\Compare');?> + <a href="#" class="action tocompare" data-post='<?= /* @noEscape */ $compareHelper->getPostDataParams($_item) ?>' title="<?= $block->escapeHtmlAttr(__('Add to Compare')) ?>"> + <span><?= $block->escapeHtml(__('Add to Compare')) ?></span> + </a> <?php endif; ?> - <?php endif; ?> - </div> - <?php endif; ?> - <?php if ($showWishlist || $showCompare): ?> - <div class="actions-secondary" data-role="add-to-links"> - <?php if ($this->helper('Magento\Wishlist\Helper\Data')->isAllow() && $showWishlist): ?> - <a href="#" - data-post='<?= /* @noEscape */ $block->getAddToWishlistParams($_item) ?>' class="action towishlist" data-action="add-to-wishlist" title="<?= $block->escapeHtmlAttr(__('Add to Wish List')) ?>"> - <span><?= $block->escapeHtml(__('Add to Wish List')) ?></span> - </a> - <?php endif; ?> - <?php if ($block->getAddToCompareUrl() && $showCompare): ?> - <?php $compareHelper = $this->helper('Magento\Catalog\Helper\Product\Compare');?> - <a href="#" class="action tocompare" data-post='<?= /* @noEscape */ $compareHelper->getPostDataParams($_item) ?>' title="<?= $block->escapeHtmlAttr(__('Add to Compare')) ?>"> - <span><?= $block->escapeHtml(__('Add to Compare')) ?></span> - </a> - <?php endif; ?> - </div> - <?php endif; ?> + </div> + <?php endif; ?> + </div> </div> <?php endif; ?> </div> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithWidgetActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithWidgetActionGroup.xml new file mode 100644 index 0000000000000..6a6bb9edaa43a --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithWidgetActionGroup.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateNewPageWithWidgetWithCategoryCondition"> + <arguments> + <argument name="cmsPage" defaultValue="_defaultCmsPage"/> + <argument name="categoryId" type="string"/> + <argument name="conditionOperator" type="string" defaultValue="is"/> + <argument name="widgetType" type="string" defaultValue="Catalog Products List"/> + </arguments> + <amOnPage url="{{CmsNewPagePage.url}}" stepKey="amOnCMSNewPage"/> + <fillField selector="{{CmsNewPagePageBasicFieldsSection.pageTitle}}" userInput="{{cmsPage.title}}" stepKey="fillFieldTitle"/> + + <click selector="{{CmsNewPagePageSeoSection.header}}" stepKey="clickExpandSearchEngineOptimisation"/> + <fillField selector="{{CmsNewPagePageSeoSection.urlKey}}" userInput="{{cmsPage.identifier}}" stepKey="fillFieldUrlKey"/> + + <click selector="{{CmsNewPagePageContentSection.header}}" stepKey="clickExpandContent"/> + <click selector="{{CmsNewPagePageActionsSection.insertWidgetButton}}" stepKey="clickInsertWidgetButton"/> + <waitForElementVisible selector="{{AdminNewWidgetSection.widgetTypeDropDown}}" time="10" stepKey="waitForInsertWidgetFrame"/> + + <selectOption selector="{{AdminNewWidgetSection.widgetTypeDropDown}}" userInput="{{widgetType}}" stepKey="selectCatalogProductListOption"/> + <waitForElementVisible selector="{{AdminNewWidgetSection.addNewCondition}}" stepKey="waitForConditionsElementBecomeAvailable"/> + + <click selector="{{AdminNewWidgetSection.addNewCondition}}" stepKey="clickToAddCondition"/> + <waitForElementVisible selector="{{AdminNewWidgetSection.selectCondition}}" stepKey="waitForSelectBoxOpened"/> + + <selectOption selector="{{AdminNewWidgetSection.selectCondition}}" userInput="Category" stepKey="selectCategoryCondition"/> + <waitForElementVisible selector="{{AdminNewWidgetSection.ruleParameter}}" stepKey="seeConditionsAdded"/> + + <click selector="{{AdminNewWidgetSection.conditionOperator}}" stepKey="clickToConditionIs"/> + <selectOption selector="{{AdminNewWidgetSection.conditionOperatorSelect('1')}}" userInput="{{conditionOperator}}" stepKey="selectOperator"/> + + <click selector="{{AdminNewWidgetSection.ruleParameter}}" stepKey="clickAddConditionItem"/> + <waitForElementVisible selector="{{AdminNewWidgetSection.setRuleParameter}}" stepKey="waitForConditionFieldOpened"/> + + <fillField selector="{{AdminNewWidgetSection.setRuleParameter}}" userInput="{{categoryId}}" stepKey="setCategoryId"/> + <click selector="{{AdminNewWidgetSection.insertWidget}}" stepKey="clickInsertWidget"/> + + <waitForElementVisible selector="{{AdminMainActionsSection.save}}" stepKey="waitForInsertWidgetSaved"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveButton"/> + <see userInput="You saved the page." stepKey="seeSavedPageMsgOnForm"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml index 05e61ac86e166..3e034199ac1a2 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml @@ -6,18 +6,22 @@ */ --> <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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="DeletePageByUrlKeyActionGroup"> <arguments> - <argument name="urlKey" type="string"/> + <argument name="urlKey" type="string" defaultValue="{{_defaultCmsPage.identifier}}"/> </arguments> <amOnPage url="{{CmsPagesPage.url}}" stepKey="amOnCMSPagesIndexPage"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFiltersBeforeDelete"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openCmsPageFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('identifier')}}" userInput="{{urlKey}}" stepKey="fillFilter"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFilters"/> <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"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFiltersAfterDelete"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageActionsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageActionsSection.xml index f60fced7d05d4..da9efa0dab62a 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageActionsSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageActionsSection.xml @@ -11,5 +11,6 @@ <section name="CmsNewPagePageActionsSection"> <element name="savePage" type="button" selector="#save" timeout="30"/> <element name="saveAndContinueEdit" type="button" selector="#save_and_continue" timeout="10"/> + <element name="insertWidgetButton" type="button" selector=".scalable.action-add-widget.plugin"/> </section> </sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminNewAttributePanelSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminNewAttributePanelSection.xml index 429d535952f76..7a722959c9996 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminNewAttributePanelSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminNewAttributePanelSection.xml @@ -7,7 +7,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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminNewAttributePanelSection"> <element name="container" type="text" selector="#create_new_attribute"/> <element name="saveAttribute" type="button" selector="#save"/> @@ -21,5 +21,12 @@ <element name="deleteOption" type="button" selector="#delete_button_option_{{row}}" parameterized="true"/> <element name="attributeSelect" type="select" selector="product[{{var}}]" parameterized="true"/> <element name="attributeName" type="select" selector="//option[text()='{{var}}']" parameterized="true"/> + <element name="useInSearch" type="select" selector="#is_searchable"/> + <element name="visibleInAdvancedSearch" type="select" selector="#is_visible_in_advanced_search"/> + <element name="comparableOnStorefront" type="select" selector="#is_comparable"/> + <element name="useInLayeredNavigation" type="select" selector="#is_filterable"/> + <element name="visibleOnCatalogPagesOnStorefront" type="select" selector="#is_visible_on_front"/> + <element name="useInProductListing" type="select" selector="#used_in_product_listing"/> + <element name="storefrontPropertiesTab" type="button" selector="#front_fieldset-wrapper"/> </section> </sections> diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/CreateConfigurationsWithVisualSwatchActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/CreateConfigurationsWithVisualSwatchActionGroup.xml index 4eb4ca0b1327b..4d6896086f91b 100644 --- a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/CreateConfigurationsWithVisualSwatchActionGroup.xml +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/CreateConfigurationsWithVisualSwatchActionGroup.xml @@ -7,7 +7,7 @@ --> <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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="CreateConfigurationsWithVisualSwatch"> <arguments> <argument name="attribute" defaultValue="VisualSwatchAttribute"/> @@ -59,4 +59,13 @@ <click selector="{{AdminChooseAffectedAttributeSetSection.confirm}}" stepKey="clickOnConfirmInPopup"/> <seeElement selector="{{AdminMessagesSection.success}}" stepKey="seeSaveProductMessage"/> </actionGroup> + <actionGroup name="CreateConfigurationsWithVisualSwatchVisibleOnStorefront" extends="CreateConfigurationsWithVisualSwatch"> + <click selector="{{AdminNewAttributePanelSection.storefrontPropertiesTab}}" after="fillDefaultStoreLabel2" stepKey="expandStorefrontPropertiesTab"/> + <selectOption selector="{{AdminNewAttributePanelSection.useInSearch}}" userInput="1" after="expandStorefrontPropertiesTab" stepKey="enableUseInSearch"/> + <selectOption selector="{{AdminNewAttributePanelSection.visibleInAdvancedSearch}}" userInput="1" after="enableUseInSearch" stepKey="enableVisibleInAdvancedSearch"/> + <selectOption selector="{{AdminNewAttributePanelSection.comparableOnStorefront}}" userInput="1" after="enableVisibleInAdvancedSearch" stepKey="enableComparableOnStorefront"/> + <selectOption selector="{{AdminNewAttributePanelSection.useInLayeredNavigation}}" userInput="1" after="enableComparableOnStorefront" stepKey="enableUseInLayeredNavigation"/> + <selectOption selector="{{AdminNewAttributePanelSection.visibleOnCatalogPagesOnStorefront}}" userInput="1" after="enableUseInLayeredNavigation" stepKey="enableVisibleOnCatalogPagesOnStorefront"/> + <selectOption selector="{{AdminNewAttributePanelSection.useInProductListing}}" userInput="1" after="enableVisibleOnCatalogPagesOnStorefront" stepKey="enableUseInProductListing"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml new file mode 100644 index 0000000000000..167d7ac007eef --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontSwatchAttributesDisplayInWidgetCMSTest"> + <annotations> + <features value="Swatches"/> + <stories value="Create/configure swatches product attribute"/> + <title value="Swatch Attribute is not displayed in the Widget CMS"/> + <description value="Swatch Attribute is not displayed in the Widget CMS"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14844"/> + <useCaseId value="MAGETWO-94147"/> + <group value="configurableProduct"/> + <group value="swatches"/> + <group value="cms"/> + <group value="WYSIWYGDisabled"/> + </annotations> + + <before> + <!--create category--> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <!--Login--> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdmin"/> + <!--Create a configurable swatch product via the UI --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> + <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="CreateConfigurationsWithVisualSwatchVisibleOnStorefront" stepKey="addSwatchToProduct"/> + <!--Create CMS page--> + <actionGroup ref="CreateNewPageWithWidgetWithCategoryCondition" stepKey="createCMSPageWithWidget"> + <argument name="categoryId" value="$$createCategory.id$$"/> + <argument name="conditionOperator" value="contains"/> + </actionGroup> + </before> + + <after> + <!--delete created category--> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <!--delete created configurable product--> + <actionGroup ref="DeleteAllProductsOnProductsGridPageFilteredByName" stepKey="deleteAllCreatedProducts"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearGridFilter"/> + <!--delete created attribute product--> + <actionGroup ref="DeleteProductAttribute" stepKey="deleteAttribute"> + <argument name="productAttribute" value="VisualSwatchAttribute"/> + </actionGroup> + <!--delete created page product--> + <actionGroup ref="DeletePageByUrlKeyActionGroup" stepKey="deletePage"/> + <!--logout--> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + </after> + <!--Open Storefront page for the new created page--> + <amOnPage url="{{StorefrontHomePage.url}}{{_defaultCmsPage.identifier}}" stepKey="gotToCreatedCmsPage"/> + <seeElement selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel(VisualSwatchOption1.default_label)}}" stepKey="assertAddedWidgetS"/> + <seeElement selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel(VisualSwatchOption2.default_label)}}" stepKey="assertAddedWidgetM"/> + </test> +</tests> diff --git a/app/code/Magento/Swatches/view/frontend/layout/catalog_widget_product_list.xml b/app/code/Magento/Swatches/view/frontend/layout/catalog_widget_product_list.xml new file mode 100644 index 0000000000000..fce81408cd14d --- /dev/null +++ b/app/code/Magento/Swatches/view/frontend/layout/catalog_widget_product_list.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> + <body> + <referenceBlock name="category.product.type.widget.details.renderers"> + <block class="Magento\Swatches\Block\Product\Renderer\Listing\Configurable" name="category.product.type.details.renderers.configurable" as="configurable" template="Magento_Swatches::product/listing/renderer.phtml" ifconfig="catalog/frontend/show_swatches_in_product_list"/> + </referenceBlock> + </body> +</page> diff --git a/app/code/Magento/Swatches/view/frontend/layout/cms_page_view.xml b/app/code/Magento/Swatches/view/frontend/layout/cms_page_view.xml new file mode 100644 index 0000000000000..2ef1e2fb2e803 --- /dev/null +++ b/app/code/Magento/Swatches/view/frontend/layout/cms_page_view.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> + <head> + <css src="Magento_Swatches::css/swatches.css"/> + </head> +</page> From be92e7b79ebe046ccd01abed961229d3bdf22e3a Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Thu, 14 Feb 2019 13:48:48 +0000 Subject: [PATCH 0667/1295] magento/magento2#21162: Fixed static tests --- .../Magento/backend/web/css/source/forms/_temp.less | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less index cad7a38dc1ef5..8fb105691adf3 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less @@ -407,16 +407,20 @@ label.mage-error { padding: 0; width: 16px; z-index: 1; - + &:before { + // @codingStandardsIgnoreStart &:extend(.admin__control-checkbox + label:before); + // @codingStandardsIgnoreEnd left: 0; position: absolute; top: 0; } &:after { + // @codingStandardsIgnoreStart &:extend(.action-multicheck-wrap .action-multicheck-toggle:after); + // @codingStandardsIgnoreEnd top: 40% !important; } } @@ -424,7 +428,9 @@ label.mage-error { &:focus { + label { &:after { + // @codingStandardsIgnoreStart &:extend(.action-multicheck-wrap .action-multicheck-toggle._active:after); + // @codingStandardsIgnoreEnd } } } @@ -432,7 +438,9 @@ label.mage-error { &._checked { + label { &:before { + // @codingStandardsIgnoreStart &:extend(.admin__control-checkbox:checked + label:before); + // @codingStandardsIgnoreEnd } } From bce16b405034a90ceeb0b99bfa0727e7fe6053e0 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Thu, 14 Feb 2019 13:49:58 +0000 Subject: [PATCH 0668/1295] magento/magento2#21162: Fixed static tests --- .../adminhtml/Magento/backend/web/css/source/forms/_temp.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less index 8fb105691adf3..05d29ad0872ed 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less @@ -407,7 +407,7 @@ label.mage-error { padding: 0; width: 16px; z-index: 1; - + &:before { // @codingStandardsIgnoreStart &:extend(.admin__control-checkbox + label:before); From b141ccb3bce6ff40bd0f1701f626a7831d398382 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Thu, 14 Feb 2019 16:17:53 +0200 Subject: [PATCH 0669/1295] MAGETWO-73524: WebAPI: product "has_options" flag not updated when options added via API --- .../Model/Product/Option/SaveHandler.php | 6 ++ .../UpdateProductCustomOptionsAttributes.php | 58 +++++++++++++++++ .../Magento/Catalog/etc/webapi_rest/di.xml | 3 + .../Magento/Catalog/etc/webapi_soap/di.xml | 3 + .../Api/ProductCustomOptionRepositoryTest.php | 64 +++++++++---------- 5 files changed, 100 insertions(+), 34 deletions(-) create mode 100644 app/code/Magento/Catalog/Plugin/Model/Product/Option/UpdateProductCustomOptionsAttributes.php diff --git a/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php b/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php index c4a2d60414a7b..d15e8ef5efa55 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php @@ -28,6 +28,8 @@ public function __construct( } /** + * Perform action on relation/extension attribute. + * * @param object $entity * @param array $arguments * @return \Magento\Catalog\Api\Data\ProductInterface|object @@ -35,6 +37,10 @@ public function __construct( */ public function execute($entity, $arguments = []) { + if ($entity->getOptionsSaved()) { + return $entity; + } + $options = $entity->getOptions(); $optionIds = []; diff --git a/app/code/Magento/Catalog/Plugin/Model/Product/Option/UpdateProductCustomOptionsAttributes.php b/app/code/Magento/Catalog/Plugin/Model/Product/Option/UpdateProductCustomOptionsAttributes.php new file mode 100644 index 0000000000000..2d25bd695763e --- /dev/null +++ b/app/code/Magento/Catalog/Plugin/Model/Product/Option/UpdateProductCustomOptionsAttributes.php @@ -0,0 +1,58 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Plugin\Model\Product\Option; + +/** + * Plugin for updating product 'has_options' and 'required_options' attributes. + */ +class UpdateProductCustomOptionsAttributes +{ + /** + * @var \Magento\Catalog\Api\ProductRepositoryInterface + */ + private $productRepository; + + /** + * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository + */ + public function __construct(\Magento\Catalog\Api\ProductRepositoryInterface $productRepository) + { + $this->productRepository = $productRepository; + } + + /** + * Update product 'has_options' and 'required_options' attributes after option save. + * + * @param \Magento\Catalog\Api\ProductCustomOptionRepositoryInterface $subject + * @param \Magento\Catalog\Api\Data\ProductCustomOptionInterface $option + * + * @return \Magento\Catalog\Api\Data\ProductCustomOptionInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterSave( + \Magento\Catalog\Api\ProductCustomOptionRepositoryInterface $subject, + \Magento\Catalog\Api\Data\ProductCustomOptionInterface $option + ) { + $product = $this->productRepository->get($option->getProductSku()); + if (!$product->getHasOptions() + || ($option->getIsRequire() + && !$product->getRequiredOptions()) + ) { + $product->setCanSaveCustomOptions(true); + $product->setOptionsSaved(true); + $currentOptions = array_filter($product->getOptions(), function ($iOption) use ($option) { + return $option->getOptionId() != $iOption->getOptionId(); + }); + $currentOptions[] = $option; + $product->setOptions($currentOptions); + $product->save(); + } + + return $option; + } +} diff --git a/app/code/Magento/Catalog/etc/webapi_rest/di.xml b/app/code/Magento/Catalog/etc/webapi_rest/di.xml index 49c5eff91ee49..7c86c1f2e3929 100644 --- a/app/code/Magento/Catalog/etc/webapi_rest/di.xml +++ b/app/code/Magento/Catalog/etc/webapi_rest/di.xml @@ -20,4 +20,7 @@ <plugin name="get_catalog_category_product_index_table_name" type="Magento\Catalog\Model\Indexer\Category\Product\Plugin\TableResolver"/> <plugin name="get_catalog_product_price_index_table_name" type="Magento\Catalog\Model\Indexer\Product\Price\Plugin\TableResolver"/> </type> + <type name="Magento\Catalog\Api\ProductCustomOptionRepositoryInterface"> + <plugin name="updateProductCustomOptionsAttributes" type="Magento\Catalog\Plugin\Model\Product\Option\UpdateProductCustomOptionsAttributes"/> + </type> </config> diff --git a/app/code/Magento/Catalog/etc/webapi_soap/di.xml b/app/code/Magento/Catalog/etc/webapi_soap/di.xml index 2a5d60222e9f8..44cdd473bf74e 100644 --- a/app/code/Magento/Catalog/etc/webapi_soap/di.xml +++ b/app/code/Magento/Catalog/etc/webapi_soap/di.xml @@ -19,4 +19,7 @@ <plugin name="get_catalog_category_product_index_table_name" type="Magento\Catalog\Model\Indexer\Category\Product\Plugin\TableResolver"/> <plugin name="get_catalog_product_price_index_table_name" type="Magento\Catalog\Model\Indexer\Product\Price\Plugin\TableResolver"/> </type> + <type name="Magento\Catalog\Api\ProductCustomOptionRepositoryInterface"> + <plugin name="updateProductCustomOptionsAttributes" type="Magento\Catalog\Plugin\Model\Product\Option\UpdateProductCustomOptionsAttributes"/> + </type> </config> diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php index 1927d967a181c..5cffcea0fa832 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php @@ -5,32 +5,38 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - namespace Magento\Catalog\Api; +use Magento\Catalog\Model\ProductFactory; use Magento\Catalog\Model\ProductRepository; +use Magento\Framework\ObjectManagerInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\WebapiAbstract; class ProductCustomOptionRepositoryTest extends WebapiAbstract { /** - * @var \Magento\Framework\ObjectManagerInterface + * @var ObjectManagerInterface */ protected $objectManager; const SERVICE_NAME = 'catalogProductCustomOptionRepositoryV1'; /** - * @var \Magento\Catalog\Model\ProductFactory + * @var ProductFactory */ protected $productFactory; + /** + * @var ProductRepository + */ + private $productRepository; + protected function setUp() { - $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->productFactory = $this->objectManager->get(\Magento\Catalog\Model\ProductFactory::class); + $this->objectManager = Bootstrap::getObjectManager(); + $this->productFactory = $this->objectManager->get(ProductFactory::class); + $this->productRepository = $this->objectManager->create(ProductRepository::class); } /** @@ -40,12 +46,8 @@ protected function setUp() public function testRemove() { $sku = 'simple'; - /** @var ProductRepository $productRepository */ - $productRepository = $this->objectManager->create( - \Magento\Catalog\Model\ProductRepository::class - ); - /** @var \Magento\Catalog\Model\Product $product */ - $product = $productRepository->get($sku, false, null, true); + /** @var \Magento\Catalog\Model\Product $product */ + $product = $this->productRepository->get($sku, false, null, true); $customOptions = $product->getOptions(); $optionId = array_pop($customOptions)->getId(); $serviceInfo = [ @@ -60,8 +62,8 @@ public function testRemove() ], ]; $this->assertTrue($this->_webApiCall($serviceInfo, ['sku' => $sku, 'optionId' => $optionId])); - /** @var \Magento\Catalog\Model\Product $product */ - $product = $productRepository->get($sku, false, null, true); + /** @var \Magento\Catalog\Model\Product $product */ + $product = $this->productRepository->get($sku, false, null, true); $this->assertNull($product->getOptionById($optionId)); $this->assertEquals(9, count($product->getOptions())); } @@ -164,6 +166,7 @@ public function testSave($optionData) ]; $result = $this->_webApiCall($serviceInfo, ['option' => $optionDataPost]); + $product = $this->productRepository->get($productSku); unset($result['product_sku']); unset($result['option_id']); if (!empty($result['values'])) { @@ -172,6 +175,10 @@ public function testSave($optionData) } } $this->assertEquals($optionData, $result); + $this->assertTrue($product->getHasOptions() == 1); + if ($optionDataPost['is_require']) { + $this->assertTrue($product->getRequiredOptions() == 1); + } } public function optionDataProvider() @@ -182,7 +189,7 @@ public function optionDataProvider() $fixtureOptions[$item['type']] = [ 'optionData' => $item, ]; - }; + } return $fixtureOptions; } @@ -191,6 +198,7 @@ public function optionDataProvider() * @magentoApiDataFixture Magento/Catalog/_files/product_without_options.php * @magentoAppIsolation enabled * @dataProvider optionNegativeDataProvider + * @param array $optionData */ public function testAddNegative($optionData) { @@ -244,12 +252,7 @@ public function optionNegativeDataProvider() public function testUpdate() { $productSku = 'simple'; - /** @var ProductRepository $productRepository */ - $productRepository = $this->objectManager->create( - \Magento\Catalog\Model\ProductRepository::class - ); - - $options = $productRepository->get($productSku, true)->getOptions(); + $options = $this->productRepository->get($productSku, true)->getOptions(); $option = array_shift($options); $optionId = $option->getOptionId(); $optionDataPost = [ @@ -292,11 +295,10 @@ public function testUpdate() } /** - * @param string $optionType - * * @magentoApiDataFixture Magento/Catalog/_files/product_with_options.php * @magentoAppIsolation enabled * @dataProvider validOptionDataProvider + * @param string $optionType * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ public function testUpdateOptionAddingNewValue($optionType) @@ -310,14 +312,10 @@ public function testUpdateOptionAddingNewValue($optionType) 'sort_order' => 100, ]; - /** @var ProductRepository $productRepository */ - $productRepository = $this->objectManager->create( - \Magento\Catalog\Model\ProductRepository::class - ); - /** @var \Magento\Catalog\Model\Product $product */ - $product = $productRepository->get('simple', false, null, true); + /** @var \Magento\Catalog\Model\Product $product */ + $product = $this->productRepository->get('simple', false, null, true); - /**@var $option \Magento\Catalog\Model\Product\Option */ + /** @var \Magento\Catalog\Model\Product\Option $option */ foreach ($product->getOptions() as $option) { if ($option->getType() == $optionType) { $fixtureOption = $option; @@ -361,7 +359,7 @@ public function testUpdateOptionAddingNewValue($optionType) $data['option_id'] = $fixtureOption->getId(); $valueObject = $this->_webApiCall( $serviceInfo, - [ 'option_id' => $fixtureOption->getId(), 'option' => $data] + ['option_id' => $fixtureOption->getId(), 'option' => $data] ); } else { $valueObject = $this->_webApiCall($serviceInfo, ['option' => $data]); @@ -397,9 +395,7 @@ public function testUpdateNegative($optionData, $message, $exceptionCode) { $this->_markTestAsRestOnly(); $productSku = 'simple'; - /** @var ProductRepository $productRepository */ - $productRepository = $this->objectManager->create(ProductRepository::class); - $options = $productRepository->get($productSku, true)->getOptions(); + $options = $this->productRepository->get($productSku, true)->getOptions(); $option = array_shift($options); $optionId = $option->getOptionId(); From d16f704fe32ee28e6ef989e5733b284eede1a9ac Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Thu, 14 Feb 2019 16:34:14 +0200 Subject: [PATCH 0670/1295] MAGETWO-94147: Swatch Attribute is not displayed in the Widget CMS --- .../Magento/CatalogWidget/Block/Product/ProductsList.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php b/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php index f5a69418c5938..6a89bf23c3c7d 100644 --- a/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php +++ b/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php @@ -108,11 +108,6 @@ class ProductsList extends \Magento\Catalog\Block\Product\AbstractProduct implem */ private $urlEncoder; - /** - * @var \Magento\Framework\View\Element\RendererList - */ - private $rendererListBlock; - /** * @param \Magento\Catalog\Block\Product\Context $context * @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory From 079d5d5f99f1cd6db5d03fb1adba3e685fd3b8f9 Mon Sep 17 00:00:00 2001 From: Pratik Oza <magepratik@gmail.com> Date: Thu, 14 Feb 2019 20:12:09 +0530 Subject: [PATCH 0671/1295] Fixed product advanced pricing design issue --- app/code/Magento/Ui/view/base/web/templates/form/field.html | 4 ++-- .../Magento/backend/web/css/source/forms/_fields.less | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) 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 ed84e158819a2..6a095b4da14ed 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,8 +8,8 @@ visible="visible" css="$data.additionalClasses" attr="'data-index': index"> - <div class="admin__field-label"> - <label if="$data.label" visible="$data.labelVisible" attr="for: uid"> + <div class="admin__field-label" visible="$data.labelVisible"> + <label if="$data.label" attr="for: uid"> <span translate="label" attr="'data-config-scope': $data.scopeLabel" /> </label> </div> 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 41aff7796090d..5879c397e6bee 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 @@ -527,7 +527,6 @@ & > .admin__field-label { #mix-grid .column(@field-label-grid__column, @field-grid__columns); - background: @color-white; cursor: pointer; left: 0; position: absolute; From 7d8ee689643bbee0445379aa64e242c7b0592050 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Thu, 14 Feb 2019 16:56:58 +0200 Subject: [PATCH 0672/1295] MAGETWO-94147: Swatch Attribute is not displayed in the Widget CMS --- app/code/Magento/CatalogWidget/Block/Product/ProductsList.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php b/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php index 6a89bf23c3c7d..3c8599133588b 100644 --- a/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php +++ b/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php @@ -173,6 +173,7 @@ protected function _construct() * Get key pieces for caching block content * * @return array + * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) */ public function getCacheKeyInfo() { @@ -278,6 +279,7 @@ protected function _beforeToHtml() * Prepare and return product collection * * @return \Magento\Catalog\Model\ResourceModel\Product\Collection + * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) */ public function createCollection() { From 69972709ad74896c4d3d5ce28020e07571aeb8fa Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 14 Feb 2019 17:51:06 +0200 Subject: [PATCH 0673/1295] MAGETWO-96636: MFTF test fix --- .../Test/Mftf/ActionGroup/AdminCreditMemoActionGroup.xml | 3 ++- .../Test/Mftf/Section/AdminCreditMemoItemsSection.xml | 2 +- .../Test/Mftf/Section/AdminCreditMemoTotalSection.xml | 6 ++---- .../Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml | 2 +- .../Test/Mftf/Test/AdminCheckingCreditMemoTotalsTest.xml | 9 ++------- 5 files changed, 8 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreditMemoActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreditMemoActionGroup.xml index 7197afb4a20df..de1ed79db81ad 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreditMemoActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreditMemoActionGroup.xml @@ -12,12 +12,13 @@ <click selector="{{AdminOrderDetailsMainActionsSection.creditMemo}}" stepKey="clickCreditMemoButton"/> <conditionalClick selector="{{AdminConfirmationModalSection.ok}}" dependentSelector="{{AdminConfirmationModalSection.ok}}" visible="true" stepKey="acceptModal"/> + <waitForPageLoad time="30" stepKey="waitPageLoaded"/> <seeInCurrentUrl url="{{AdminCreditMemoNewPage.url}}" stepKey="seeNewCreditMemoUrl"/> <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Memo" stepKey="seeNewCreditMemoPageTitle"/> </actionGroup> <actionGroup name="SubmitCreditMemo"> - <waitForElementNotVisible selector="{{AdminCreditMemoTotalSection.submitRefundOfflineDisabled}}" stepKey="waitButtonEnabled"/> + <waitForElementVisible selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="waitButtonEnabled"/> <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickSubmitCreditMemo"/> <waitForElementVisible selector="{{AdminMessagesSection.successMessage}}" stepKey="waitForMessageAppears"/> <see selector="{{AdminMessagesSection.successMessage}}" userInput="You created the credit memo." stepKey="seeCreditMemoCreateSuccess"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoItemsSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoItemsSection.xml index 496fae2b548a7..15d4df39416f2 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoItemsSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoItemsSection.xml @@ -10,6 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCreditMemoItemsSection"> <element name="itemQtyToRefund" type="input" selector=".order-creditmemo-tables tbody:nth-of-type({{row}}) .col-refund .qty-input" parameterized="true"/> - <element name="updateQty" type="button" selector=".order-creditmemo-tables tfoot button[data-ui-id='order-items-update-button']" timeout="30"/> + <element name="updateQty" type="button" selector=".order-creditmemo-tables tfoot button[data-ui-id='order-items-update-button']:not([class~='disabled'])" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoTotalSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoTotalSection.xml index 73ea29836e242..e087873fe62c2 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoTotalSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoTotalSection.xml @@ -11,10 +11,8 @@ <section name="AdminCreditMemoTotalSection"> <element name="total" type="text" selector="//table[contains(@class,'order-subtotal-table')]/tbody/tr/td[contains(text(), '{{total}}')]/following-sibling::td/span/span[contains(@class, 'price')]" parameterized="true"/> <element name="refundShipping" type="input" selector=".order-subtotal-table tbody input[name='creditmemo[shipping_amount]']"/> - <element name="updateTotals" type="button" selector=".update-totals-button" timeout="30"/> - <element name="submitRefundOffline" type="button" selector=".order-totals-actions button[title='Refund Offline']" timeout="30"/> - <element name="submitRefundOfflineEnabled" type="button" selector=".order-totals-actions button[data-ui-id='order-items-submit-button'].action-default.scalable.save.submit-button.primary']" timeout="30"/> + <element name="updateTotals" type="button" selector="button[class~='update-totals-button']:not([class~='disabled'])" timeout="30"/> + <element name="submitRefundOffline" type="button" selector=".order-totals-actions button[title='Refund Offline']:not([class~='disabled'])" timeout="30"/> <element name="adjustmentRefund" type="input" selector=".order-subtotal-table tbody input[name='creditmemo[adjustment_positive]']"/> - <element name="submitRefundOfflineDisabled" type="button" selector="//*[@class='order-totals-actions']//button[@title='Refund Offline' and contains(@class, 'disabled')]" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml index cb761dc358abb..1abb23ebddba0 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml @@ -119,7 +119,7 @@ <!--Submit refund--> <scrollTo selector="{{AdminCreditMemoItemsSection.itemQtyToRefund('1')}}" stepKey="scrollToItemsToRefund"/> <seeInField selector="{{AdminCreditMemoItemsSection.itemQtyToRefund('1')}}" userInput="5" after="scrollToItemsToRefund" stepKey="checkQtyOfItemsToRefund"/> - <waitForElementVisible selector="{{AdminCreditMemoTotalSection.submitRefundOfflineEnabled}}" stepKey="waitForSubmitRefundOfflineEnabled"/> + <waitForElementVisible selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="waitForSubmitRefundOfflineEnabled"/> <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="submitRefundOffline"/> <!--Verify Credit Memo created successfully--> <waitForElementVisible diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoTotalsTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoTotalsTest.xml index b73c66d49d690..e9fff90bdd632 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoTotalsTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoTotalsTest.xml @@ -91,15 +91,10 @@ <click selector="{{AdminOrderInvoiceViewMainActionsSection.creditMemo}}" stepKey="clickCreditMemo"/> <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Memo" stepKey="seeNewMemoPageTitle"/> <fillField selector="{{AdminCreditMemoTotalSection.refundShipping}}" userInput="0" stepKey="setRefundShipping"/> + <waitForElementVisible selector="{{AdminCreditMemoTotalSection.updateTotals}}" time="30" stepKey="waitUpdateTotalsButtonEnabled"/> <click selector="{{AdminCreditMemoTotalSection.updateTotals}}" stepKey="clickUpdateTotals"/> <waitForLoadingMaskToDisappear stepKey="waitForUpdateTotals"/> - <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickRefundOffline"/> - <waitForElementVisible - selector="{{AdminMessagesSection.success}}" - time="10" - stepKey="waitForMessage"/> - <see selector="{{AdminMessagesSection.success}}" userInput="You created the credit memo." - stepKey="seeCreatedCreditMemoSuccessMessage"/> + <actionGroup ref="SubmitCreditMemo" stepKey="submitCreditMemo"/> <!--Go to Credit Memo tab--> <click selector="{{AdminOrderViewTabsSection.creditMemos}}" stepKey="clickCreditMemosTab"/> From 9d670715c7880f970418fd139af42410df3339ae Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Thu, 14 Feb 2019 10:31:28 -0600 Subject: [PATCH 0674/1295] magento-engcom/magento2ce#2574: Fixed static test failures --- .../backend/web/css/source/forms/_temp.less | 24 ++++++++---- .../luma/web/css/source/_sections.less | 39 ++++++++++--------- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less index 05d29ad0872ed..b37266c0de600 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_temp.less @@ -409,18 +409,22 @@ label.mage-error { z-index: 1; &:before { - // @codingStandardsIgnoreStart + /** + * @codingStandardsIgnoreStart + */ &:extend(.admin__control-checkbox + label:before); - // @codingStandardsIgnoreEnd + // @codingStandardsIgnoreEnd left: 0; position: absolute; top: 0; } &:after { - // @codingStandardsIgnoreStart + /** + * @codingStandardsIgnoreStart + */ &:extend(.action-multicheck-wrap .action-multicheck-toggle:after); - // @codingStandardsIgnoreEnd + // @codingStandardsIgnoreEnd top: 40% !important; } } @@ -428,9 +432,11 @@ label.mage-error { &:focus { + label { &:after { - // @codingStandardsIgnoreStart + /** + * @codingStandardsIgnoreStart + */ &:extend(.action-multicheck-wrap .action-multicheck-toggle._active:after); - // @codingStandardsIgnoreEnd + // @codingStandardsIgnoreEnd } } } @@ -438,9 +444,11 @@ label.mage-error { &._checked { + label { &:before { - // @codingStandardsIgnoreStart + /** + * @codingStandardsIgnoreStart + */ &:extend(.admin__control-checkbox:checked + label:before); - // @codingStandardsIgnoreEnd + // @codingStandardsIgnoreEnd } } diff --git a/app/design/frontend/Magento/luma/web/css/source/_sections.less b/app/design/frontend/Magento/luma/web/css/source/_sections.less index 38b936c83cb95..95769c4f4b6ba 100644 --- a/app/design/frontend/Magento/luma/web/css/source/_sections.less +++ b/app/design/frontend/Magento/luma/web/css/source/_sections.less @@ -19,16 +19,16 @@ a { position: relative; .lib-icon-font( - @_icon-font-content: @icon-down, - @_icon-font-size: @font-size__base, - @_icon-font-line-height: @icon-font__line-height, - @_icon-font-color: @icon-font__color, - @_icon-font-color-hover: @icon-font__color-hover, - @_icon-font-color-active: @icon-font__color-active, - @_icon-font-margin: @icon-font__margin, - @_icon-font-vertical-align: @icon-font__vertical-align, - @_icon-font-position: after, - @_icon-font-display: false + @_icon-font-content: @icon-down, + @_icon-font-size: @font-size__base, + @_icon-font-line-height: @icon-font__line-height, + @_icon-font-color: @icon-font__color, + @_icon-font-color-hover: @icon-font__color-hover, + @_icon-font-color-active: @icon-font__color-active, + @_icon-font-margin: @icon-font__margin, + @_icon-font-vertical-align: @icon-font__vertical-align, + @_icon-font-position: after, + @_icon-font-display: false ); &:after { @@ -77,14 +77,15 @@ } .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { - .product.data.items{ - .item.title { - >.switch{ - padding: 1px 15px 1px; - } - } - >.item.content{ - padding: 10px 15px 30px; + .product.data.items { + .item.title { + > .switch { + padding: 1px 15px 1px; + } + } + + > .item.content { + padding: 10px 15px 30px; + } } - } } From ad4fe51a75384197e39dab8171969147a7a83a3a Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Thu, 14 Feb 2019 11:18:41 -0600 Subject: [PATCH 0675/1295] MQE-1436: Remove composer.json from dev/tests/acceptance - removed composer/lock and robofile --- dev/tests/acceptance/RoboFile.php | 171 -- dev/tests/acceptance/composer.json | 31 - dev/tests/acceptance/composer.lock | 3316 ---------------------------- 3 files changed, 3518 deletions(-) delete mode 100644 dev/tests/acceptance/RoboFile.php delete mode 100755 dev/tests/acceptance/composer.json delete mode 100644 dev/tests/acceptance/composer.lock diff --git a/dev/tests/acceptance/RoboFile.php b/dev/tests/acceptance/RoboFile.php deleted file mode 100644 index f36150ad254b5..0000000000000 --- a/dev/tests/acceptance/RoboFile.php +++ /dev/null @@ -1,171 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -use Symfony\Component\Yaml\Yaml; - -/** This is project's console commands configuration for Robo task runner. - * - * @codingStandardsIgnoreStart - * @see http://robo.li/ - */ -class RoboFile extends \Robo\Tasks -{ - use Robo\Task\Base\loadShortcuts; - - /** - * Duplicate the Example configuration files for the Project. - * Build the Codeception project. - * - * @return void - */ - function buildProject() - { - passthru($this->getBaseCmd("build:project")); - } - - /** - * Generate all Tests in PHP OR Generate set of tests via passing array of tests - * - * @param array $tests - * @param array $opts - * @return void - */ - function generateTests(array $tests, $opts = [ - 'config' => null, - 'force' => false, - 'nodes' => null, - 'lines' => null, - 'tests' => null - ]) - { - $baseCmd = $this->getBaseCmd("generate:tests"); - - $mftfArgNames = ['config', 'nodes', 'lines', 'tests']; - // append arguments to the end of the command - foreach ($opts as $argName => $argValue) { - if (in_array($argName, $mftfArgNames) && $argValue !== null) { - $baseCmd .= " --$argName $argValue"; - } - } - - // use a separate conditional for the force flag (casting bool to string in php is hard) - if ($opts['force']) { - $baseCmd .= ' --force'; - } - - $this->taskExec($baseCmd)->args($tests)->run(); - } - - /** - * Generate a suite based on name(s) passed in as args. - * - * @param array $args - * @throws Exception - * @return void - */ - function generateSuite(array $args) - { - if (empty($args)) { - throw new Exception("Please provide suite name(s) after generate:suite command"); - } - $baseCmd = $this->getBaseCmd("generate:suite"); - $this->taskExec($baseCmd)->args($args)->run(); - } - - /** - * Run all Tests with the specified @group tag'. - * - * @param array $args - * @return void - */ - function group(array $args) - { - $args = array_merge($args, ['-k']); - $baseCmd = $this->getBaseCmd("run:group"); - $this->taskExec($baseCmd)->args($args)->run(); - } - - /** - * Generate the HTML for the Allure report based on the Test XML output - Allure v1.4.X - * - * @return \Robo\Result - */ - function allure1Generate() - { - return $this->_exec('allure generate tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-results'. DIRECTORY_SEPARATOR .' -o tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-report'. DIRECTORY_SEPARATOR .''); - } - - /** - * Generate the HTML for the Allure report based on the Test XML output - Allure v2.3.X - * - * @return \Robo\Result - */ - function allure2Generate() - { - return $this->_exec('allure generate tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-results'. DIRECTORY_SEPARATOR .' --output tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-report'. DIRECTORY_SEPARATOR .' --clean'); - } - - /** - * Open the HTML Allure report - Allure v1.4.X - * - * @return void - */ - function allure1Open() - { - $this->_exec('allure report open --report-dir tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-report'. DIRECTORY_SEPARATOR .''); - } - - /** - * Open the HTML Allure report - Allure v2.3.X - * - * @return void - */ - function allure2Open() - { - $this->_exec('allure open --port 0 tests'. DIRECTORY_SEPARATOR .'_output'. DIRECTORY_SEPARATOR .'allure-report'. DIRECTORY_SEPARATOR .''); - } - - /** - * Generate and open the HTML Allure report - Allure v1.4.X - * - * @return void - */ - function allure1Report() - { - $result1 = $this->allure1Generate(); - - if ($result1->wasSuccessful()) { - $this->allure1Open(); - } - } - - /** - * Generate and open the HTML Allure report - Allure v2.3.X - * - * @return void - */ - function allure2Report() - { - $result1 = $this->allure2Generate(); - - if ($result1->wasSuccessful()) { - $this->allure2Open(); - } - } - - /** - * Private function for returning the formatted command for the passthru to mftf bin execution. - * - * @param string $command - * @return string - */ - private function getBaseCmd($command) - { - $this->writeln("\033[01;31m Use of robo will be deprecated with next major release, please use <root>/vendor/bin/mftf $command \033[0m"); - chdir(__DIR__); - return realpath('../../../vendor/bin/mftf') . " $command"; - } -} diff --git a/dev/tests/acceptance/composer.json b/dev/tests/acceptance/composer.json deleted file mode 100755 index a0c3ad37f47a3..0000000000000 --- a/dev/tests/acceptance/composer.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "description": "Magento 2 (Open Source) Functional Tests", - "type": "project", - "version": "1.0.0-dev", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "config": { - "sort-packages": true - }, - "require": { - "php": "~7.0.13|~7.1.0", - "codeception/codeception": "~2.3.4", - "consolidation/robo": "^1.0.0", - "vlucas/phpdotenv": "^2.4", - "doctrine/instantiator": "<=1.0.5", - "myclabs/deep-copy": "<=1.7.0", - "symfony/filesystem": "<=3.4.12", - "symfony/finder": "<=3.4.12", - "symfony/process": "<=3.4.12", - "symfony/yaml": "<=3.4.12" - }, - "autoload": { - "psr-4": { - "Magento\\": "tests/functional/Magento" - }, - "files": ["tests/_bootstrap.php"] - }, - "prefer-stable": true -} diff --git a/dev/tests/acceptance/composer.lock b/dev/tests/acceptance/composer.lock deleted file mode 100644 index 1592e385dd5c0..0000000000000 --- a/dev/tests/acceptance/composer.lock +++ /dev/null @@ -1,3316 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "87a504ae6c961babfa9113e81f9345fc", - "packages": [ - { - "name": "behat/gherkin", - "version": "v4.4.5", - "source": { - "type": "git", - "url": "https://github.com/Behat/Gherkin.git", - "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/5c14cff4f955b17d20d088dec1bde61c0539ec74", - "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74", - "shasum": "" - }, - "require": { - "php": ">=5.3.1" - }, - "require-dev": { - "phpunit/phpunit": "~4.5|~5", - "symfony/phpunit-bridge": "~2.7|~3", - "symfony/yaml": "~2.3|~3" - }, - "suggest": { - "symfony/yaml": "If you want to parse features, represented in YAML files" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.4-dev" - } - }, - "autoload": { - "psr-0": { - "Behat\\Gherkin": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - } - ], - "description": "Gherkin DSL parser for PHP 5.3", - "homepage": "http://behat.org/", - "keywords": [ - "BDD", - "Behat", - "Cucumber", - "DSL", - "gherkin", - "parser" - ], - "time": "2016-10-30T11:50:56+00:00" - }, - { - "name": "codeception/codeception", - "version": "2.3.9", - "source": { - "type": "git", - "url": "https://github.com/Codeception/Codeception.git", - "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/Codeception/zipball/104f46fa0bde339f1bcc3a375aac21eb36e65a1e", - "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e", - "shasum": "" - }, - "require": { - "behat/gherkin": "~4.4.0", - "codeception/stub": "^1.0", - "ext-json": "*", - "ext-mbstring": "*", - "facebook/webdriver": ">=1.1.3 <2.0", - "guzzlehttp/guzzle": ">=4.1.4 <7.0", - "guzzlehttp/psr7": "~1.0", - "php": ">=5.4.0 <8.0", - "phpunit/php-code-coverage": ">=2.2.4 <6.0", - "phpunit/phpunit": ">=4.8.28 <5.0.0 || >=5.6.3 <7.0", - "sebastian/comparator": ">1.1 <3.0", - "sebastian/diff": ">=1.4 <3.0", - "symfony/browser-kit": ">=2.7 <5.0", - "symfony/console": ">=2.7 <5.0", - "symfony/css-selector": ">=2.7 <5.0", - "symfony/dom-crawler": ">=2.7 <5.0", - "symfony/event-dispatcher": ">=2.7 <5.0", - "symfony/finder": ">=2.7 <5.0", - "symfony/yaml": ">=2.7 <5.0" - }, - "require-dev": { - "codeception/specify": "~0.3", - "facebook/graph-sdk": "~5.3", - "flow/jsonpath": "~0.2", - "monolog/monolog": "~1.8", - "pda/pheanstalk": "~3.0", - "php-amqplib/php-amqplib": "~2.4", - "predis/predis": "^1.0", - "squizlabs/php_codesniffer": "~2.0", - "symfony/process": ">=2.7 <5.0", - "vlucas/phpdotenv": "^2.4.0" - }, - "suggest": { - "aws/aws-sdk-php": "For using AWS Auth in REST module and Queue module", - "codeception/phpbuiltinserver": "Start and stop PHP built-in web server for your tests", - "codeception/specify": "BDD-style code blocks", - "codeception/verify": "BDD-style assertions", - "flow/jsonpath": "For using JSONPath in REST module", - "league/factory-muffin": "For DataFactory module", - "league/factory-muffin-faker": "For Faker support in DataFactory module", - "phpseclib/phpseclib": "for SFTP option in FTP Module", - "stecman/symfony-console-completion": "For BASH autocompletion", - "symfony/phpunit-bridge": "For phpunit-bridge support" - }, - "bin": [ - "codecept" - ], - "type": "library", - "extra": { - "branch-alias": [] - }, - "autoload": { - "psr-4": { - "Codeception\\": "src\\Codeception", - "Codeception\\Extension\\": "ext" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Bodnarchuk", - "email": "davert@mail.ua", - "homepage": "http://codegyre.com" - } - ], - "description": "BDD-style testing framework", - "homepage": "http://codeception.com/", - "keywords": [ - "BDD", - "TDD", - "acceptance testing", - "functional testing", - "unit testing" - ], - "time": "2018-02-26T23:29:41+00:00" - }, - { - "name": "codeception/stub", - "version": "1.0.4", - "source": { - "type": "git", - "url": "https://github.com/Codeception/Stub.git", - "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Codeception/Stub/zipball/681b62348837a5ef07d10d8a226f5bc358cc8805", - "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805", - "shasum": "" - }, - "require": { - "phpunit/phpunit-mock-objects": ">2.3 <7.0" - }, - "require-dev": { - "phpunit/phpunit": ">=4.8 <8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Codeception\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", - "time": "2018-05-17T09:31:08+00:00" - }, - { - "name": "consolidation/annotated-command", - "version": "2.8.4", - "source": { - "type": "git", - "url": "https://github.com/consolidation/annotated-command.git", - "reference": "651541a0b68318a2a202bda558a676e5ad92223c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/651541a0b68318a2a202bda558a676e5ad92223c", - "reference": "651541a0b68318a2a202bda558a676e5ad92223c", - "shasum": "" - }, - "require": { - "consolidation/output-formatters": "^3.1.12", - "php": ">=5.4.0", - "psr/log": "^1", - "symfony/console": "^2.8|^3|^4", - "symfony/event-dispatcher": "^2.5|^3|^4", - "symfony/finder": "^2.5|^3|^4" - }, - "require-dev": { - "g1a/composer-test-scenarios": "^2", - "phpunit/phpunit": "^6", - "satooshi/php-coveralls": "^2", - "squizlabs/php_codesniffer": "^2.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "Consolidation\\AnnotatedCommand\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Greg Anderson", - "email": "greg.1.anderson@greenknowe.org" - } - ], - "description": "Initialize Symfony Console commands from annotated command class methods.", - "time": "2018-05-25T18:04:25+00:00" - }, - { - "name": "consolidation/config", - "version": "1.0.11", - "source": { - "type": "git", - "url": "https://github.com/consolidation/config.git", - "reference": "ede41d946078e97e7a9513aadc3352f1c26817af" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consolidation/config/zipball/ede41d946078e97e7a9513aadc3352f1c26817af", - "reference": "ede41d946078e97e7a9513aadc3352f1c26817af", - "shasum": "" - }, - "require": { - "dflydev/dot-access-data": "^1.1.0", - "grasmash/expander": "^1", - "php": ">=5.4.0" - }, - "require-dev": { - "g1a/composer-test-scenarios": "^1", - "phpunit/phpunit": "^4", - "satooshi/php-coveralls": "^1.0", - "squizlabs/php_codesniffer": "2.*", - "symfony/console": "^2.5|^3|^4", - "symfony/yaml": "^2.8.11|^3|^4" - }, - "suggest": { - "symfony/yaml": "Required to use Consolidation\\Config\\Loader\\YamlConfigLoader" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Consolidation\\Config\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Greg Anderson", - "email": "greg.1.anderson@greenknowe.org" - } - ], - "description": "Provide configuration services for a commandline tool.", - "time": "2018-05-27T01:17:02+00:00" - }, - { - "name": "consolidation/log", - "version": "1.0.6", - "source": { - "type": "git", - "url": "https://github.com/consolidation/log.git", - "reference": "dfd8189a771fe047bf3cd669111b2de5f1c79395" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consolidation/log/zipball/dfd8189a771fe047bf3cd669111b2de5f1c79395", - "reference": "dfd8189a771fe047bf3cd669111b2de5f1c79395", - "shasum": "" - }, - "require": { - "php": ">=5.5.0", - "psr/log": "~1.0", - "symfony/console": "^2.8|^3|^4" - }, - "require-dev": { - "g1a/composer-test-scenarios": "^1", - "phpunit/phpunit": "4.*", - "satooshi/php-coveralls": "^2", - "squizlabs/php_codesniffer": "2.*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Consolidation\\Log\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Greg Anderson", - "email": "greg.1.anderson@greenknowe.org" - } - ], - "description": "Improved Psr-3 / Psr\\Log logger based on Symfony Console components.", - "time": "2018-05-25T18:14:39+00:00" - }, - { - "name": "consolidation/output-formatters", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/consolidation/output-formatters.git", - "reference": "d78ef59aea19d3e2e5a23f90a055155ee78a0ad5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/d78ef59aea19d3e2e5a23f90a055155ee78a0ad5", - "reference": "d78ef59aea19d3e2e5a23f90a055155ee78a0ad5", - "shasum": "" - }, - "require": { - "php": ">=5.4.0", - "symfony/console": "^2.8|^3|^4", - "symfony/finder": "^2.5|^3|^4" - }, - "require-dev": { - "g1a/composer-test-scenarios": "^2", - "phpunit/phpunit": "^5.7.27", - "satooshi/php-coveralls": "^2", - "squizlabs/php_codesniffer": "^2.7", - "symfony/console": "3.2.3", - "symfony/var-dumper": "^2.8|^3|^4", - "victorjonsson/markdowndocs": "^1.3" - }, - "suggest": { - "symfony/var-dumper": "For using the var_dump formatter" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Consolidation\\OutputFormatters\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Greg Anderson", - "email": "greg.1.anderson@greenknowe.org" - } - ], - "description": "Format text by applying transformations provided by plug-in formatters.", - "time": "2018-05-25T18:02:34+00:00" - }, - { - "name": "consolidation/robo", - "version": "1.3.0", - "source": { - "type": "git", - "url": "https://github.com/consolidation/Robo.git", - "reference": "ac563abfadf7cb7314b4e152f2b5033a6c255f6f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/consolidation/Robo/zipball/ac563abfadf7cb7314b4e152f2b5033a6c255f6f", - "reference": "ac563abfadf7cb7314b4e152f2b5033a6c255f6f", - "shasum": "" - }, - "require": { - "consolidation/annotated-command": "^2.8.2", - "consolidation/config": "^1.0.10", - "consolidation/log": "~1", - "consolidation/output-formatters": "^3.1.13", - "grasmash/yaml-expander": "^1.3", - "league/container": "^2.2", - "php": ">=5.5.0", - "symfony/console": "^2.8|^3|^4", - "symfony/event-dispatcher": "^2.5|^3|^4", - "symfony/filesystem": "^2.5|^3|^4", - "symfony/finder": "^2.5|^3|^4", - "symfony/process": "^2.5|^3|^4" - }, - "replace": { - "codegyre/robo": "< 1.0" - }, - "require-dev": { - "codeception/aspect-mock": "^1|^2.1.1", - "codeception/base": "^2.3.7", - "codeception/verify": "^0.3.2", - "g1a/composer-test-scenarios": "^2", - "goaop/framework": "~2.1.2", - "goaop/parser-reflection": "^1.1.0", - "natxet/cssmin": "3.0.4", - "nikic/php-parser": "^3.1.5", - "patchwork/jsqueeze": "~2", - "pear/archive_tar": "^1.4.2", - "phpunit/php-code-coverage": "~2|~4", - "satooshi/php-coveralls": "^2", - "squizlabs/php_codesniffer": "^2.8" - }, - "suggest": { - "henrikbjorn/lurker": "For monitoring filesystem changes in taskWatch", - "natxet/CssMin": "For minifying CSS files in taskMinify", - "patchwork/jsqueeze": "For minifying JS files in taskMinify", - "pear/archive_tar": "Allows tar archives to be created and extracted in taskPack and taskExtract, respectively." - }, - "bin": [ - "robo" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev", - "dev-state": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Robo\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Davert", - "email": "davert.php@resend.cc" - } - ], - "description": "Modern task runner", - "time": "2018-05-27T01:42:53+00:00" - }, - { - "name": "container-interop/container-interop", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/container-interop/container-interop.git", - "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8", - "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8", - "shasum": "" - }, - "require": { - "psr/container": "^1.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Interop\\Container\\": "src/Interop/Container/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Promoting the interoperability of container objects (DIC, SL, etc.)", - "homepage": "https://github.com/container-interop/container-interop", - "time": "2017-02-14T19:40:03+00:00" - }, - { - "name": "dflydev/dot-access-data", - "version": "v1.1.0", - "source": { - "type": "git", - "url": "https://github.com/dflydev/dflydev-dot-access-data.git", - "reference": "3fbd874921ab2c041e899d044585a2ab9795df8a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/3fbd874921ab2c041e899d044585a2ab9795df8a", - "reference": "3fbd874921ab2c041e899d044585a2ab9795df8a", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "Dflydev\\DotAccessData": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dragonfly Development Inc.", - "email": "info@dflydev.com", - "homepage": "http://dflydev.com" - }, - { - "name": "Beau Simensen", - "email": "beau@dflydev.com", - "homepage": "http://beausimensen.com" - }, - { - "name": "Carlos Frutos", - "email": "carlos@kiwing.it", - "homepage": "https://github.com/cfrutos" - } - ], - "description": "Given a deep data structure, access data by dot notation.", - "homepage": "https://github.com/dflydev/dflydev-dot-access-data", - "keywords": [ - "access", - "data", - "dot", - "notation" - ], - "time": "2017-01-20T21:14:22+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "1.0.5", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", - "shasum": "" - }, - "require": { - "php": ">=5.3,<8.0-DEV" - }, - "require-dev": { - "athletic/athletic": "~0.1.8", - "ext-pdo": "*", - "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://github.com/doctrine/instantiator", - "keywords": [ - "constructor", - "instantiate" - ], - "time": "2015-06-14T21:17:01+00:00" - }, - { - "name": "facebook/webdriver", - "version": "1.6.0", - "source": { - "type": "git", - "url": "https://github.com/facebook/php-webdriver.git", - "reference": "bd8c740097eb9f2fc3735250fc1912bc811a954e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/facebook/php-webdriver/zipball/bd8c740097eb9f2fc3735250fc1912bc811a954e", - "reference": "bd8c740097eb9f2fc3735250fc1912bc811a954e", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-json": "*", - "ext-mbstring": "*", - "ext-zip": "*", - "php": "^5.6 || ~7.0", - "symfony/process": "^2.8 || ^3.1 || ^4.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.0", - "jakub-onderka/php-parallel-lint": "^0.9.2", - "php-coveralls/php-coveralls": "^2.0", - "php-mock/php-mock-phpunit": "^1.1", - "phpunit/phpunit": "^5.7", - "sebastian/environment": "^1.3.4 || ^2.0 || ^3.0", - "squizlabs/php_codesniffer": "^2.6", - "symfony/var-dumper": "^3.3 || ^4.0" - }, - "suggest": { - "ext-SimpleXML": "For Firefox profile creation" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-community": "1.5-dev" - } - }, - "autoload": { - "psr-4": { - "Facebook\\WebDriver\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "description": "A PHP client for Selenium WebDriver", - "homepage": "https://github.com/facebook/php-webdriver", - "keywords": [ - "facebook", - "php", - "selenium", - "webdriver" - ], - "time": "2018-05-16T17:37:13+00:00" - }, - { - "name": "grasmash/expander", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/grasmash/expander.git", - "reference": "95d6037344a4be1dd5f8e0b0b2571a28c397578f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/grasmash/expander/zipball/95d6037344a4be1dd5f8e0b0b2571a28c397578f", - "reference": "95d6037344a4be1dd5f8e0b0b2571a28c397578f", - "shasum": "" - }, - "require": { - "dflydev/dot-access-data": "^1.1.0", - "php": ">=5.4" - }, - "require-dev": { - "greg-1-anderson/composer-test-scenarios": "^1", - "phpunit/phpunit": "^4|^5.5.4", - "satooshi/php-coveralls": "^1.0.2|dev-master", - "squizlabs/php_codesniffer": "^2.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Grasmash\\Expander\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthew Grasmick" - } - ], - "description": "Expands internal property references in PHP arrays file.", - "time": "2017-12-21T22:14:55+00:00" - }, - { - "name": "grasmash/yaml-expander", - "version": "1.4.0", - "source": { - "type": "git", - "url": "https://github.com/grasmash/yaml-expander.git", - "reference": "3f0f6001ae707a24f4d9733958d77d92bf9693b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/grasmash/yaml-expander/zipball/3f0f6001ae707a24f4d9733958d77d92bf9693b1", - "reference": "3f0f6001ae707a24f4d9733958d77d92bf9693b1", - "shasum": "" - }, - "require": { - "dflydev/dot-access-data": "^1.1.0", - "php": ">=5.4", - "symfony/yaml": "^2.8.11|^3|^4" - }, - "require-dev": { - "greg-1-anderson/composer-test-scenarios": "^1", - "phpunit/phpunit": "^4.8|^5.5.4", - "satooshi/php-coveralls": "^1.0.2|dev-master", - "squizlabs/php_codesniffer": "^2.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Grasmash\\YamlExpander\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthew Grasmick" - } - ], - "description": "Expands internal property references in a yaml file.", - "time": "2017-12-16T16:06:03+00:00" - }, - { - "name": "guzzlehttp/guzzle", - "version": "6.3.3", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", - "shasum": "" - }, - "require": { - "guzzlehttp/promises": "^1.0", - "guzzlehttp/psr7": "^1.4", - "php": ">=5.5" - }, - "require-dev": { - "ext-curl": "*", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", - "psr/log": "^1.0" - }, - "suggest": { - "psr/log": "Required for using the Log middleware" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.3-dev" - } - }, - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "GuzzleHttp\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Guzzle is a PHP HTTP client library", - "homepage": "http://guzzlephp.org/", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "rest", - "web service" - ], - "time": "2018-04-22T15:46:56+00:00" - }, - { - "name": "guzzlehttp/promises", - "version": "v1.3.1", - "source": { - "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", - "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", - "shasum": "" - }, - "require": { - "php": ">=5.5.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Guzzle promises library", - "keywords": [ - "promise" - ], - "time": "2016-12-20T10:07:11+00:00" - }, - { - "name": "guzzlehttp/psr7", - "version": "1.4.2", - "source": { - "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c", - "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c", - "shasum": "" - }, - "require": { - "php": ">=5.4.0", - "psr/http-message": "~1.0" - }, - "provide": { - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Tobias Schultze", - "homepage": "https://github.com/Tobion" - } - ], - "description": "PSR-7 message implementation that also provides common utility methods", - "keywords": [ - "http", - "message", - "request", - "response", - "stream", - "uri", - "url" - ], - "time": "2017-03-20T17:10:46+00:00" - }, - { - "name": "league/container", - "version": "2.4.1", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/container.git", - "reference": "43f35abd03a12977a60ffd7095efd6a7808488c0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/container/zipball/43f35abd03a12977a60ffd7095efd6a7808488c0", - "reference": "43f35abd03a12977a60ffd7095efd6a7808488c0", - "shasum": "" - }, - "require": { - "container-interop/container-interop": "^1.2", - "php": "^5.4.0 || ^7.0" - }, - "provide": { - "container-interop/container-interop-implementation": "^1.2", - "psr/container-implementation": "^1.0" - }, - "replace": { - "orno/di": "~2.0" - }, - "require-dev": { - "phpunit/phpunit": "4.*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev", - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "League\\Container\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Phil Bennett", - "email": "philipobenito@gmail.com", - "homepage": "http://www.philipobenito.com", - "role": "Developer" - } - ], - "description": "A fast and intuitive dependency injection container.", - "homepage": "https://github.com/thephpleague/container", - "keywords": [ - "container", - "dependency", - "di", - "injection", - "league", - "provider", - "service" - ], - "time": "2017-05-10T09:20:27+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.7.0", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^4.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, - "files": [ - "src/DeepCopy/deep_copy.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "time": "2017-10-19T19:58:43+00:00" - }, - { - "name": "phar-io/manifest", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0", - "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "phar-io/version": "^1.0.1", - "php": "^5.6 || ^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2017-03-05T18:14:27+00:00" - }, - { - "name": "phar-io/version", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "time": "2017-03-05T17:38:23+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "shasum": "" - }, - "require": { - "php": ">=5.5" - }, - "require-dev": { - "phpunit/phpunit": "^4.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "time": "2017-09-11T18:02:19+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "4.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08", - "shasum": "" - }, - "require": { - "php": "^7.0", - "phpdocumentor/reflection-common": "^1.0.0", - "phpdocumentor/type-resolver": "^0.4.0", - "webmozart/assert": "^1.0" - }, - "require-dev": { - "doctrine/instantiator": "~1.0.5", - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^6.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-11-30T07:14:17+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "0.4.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", - "shasum": "" - }, - "require": { - "php": "^5.5 || ^7.0", - "phpdocumentor/reflection-common": "^1.0" - }, - "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "time": "2017-07-14T14:27:02+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "1.7.6", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/33a7e3c4fda54e912ff6338c48823bd5c0f0b712", - "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", - "sebastian/comparator": "^1.1|^2.0|^3.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0" - }, - "require-dev": { - "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.7.x-dev" - } - }, - "autoload": { - "psr-0": { - "Prophecy\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "time": "2018-04-18T13:57:24+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "5.3.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "c89677919c5dd6d3b3852f230a663118762218ac" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c89677919c5dd6d3b3852f230a663118762218ac", - "reference": "c89677919c5dd6d3b3852f230a663118762218ac", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-xmlwriter": "*", - "php": "^7.0", - "phpunit/php-file-iterator": "^1.4.2", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^2.0.1", - "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.0", - "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "suggest": { - "ext-xdebug": "^2.5.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.3.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "time": "2018-04-06T15:36:58+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "1.4.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "time": "2017-11-27T13:52:08+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "time": "2015-06-21T13:50:34+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "1.0.9", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "time": "2017-02-26T11:10:40+00:00" - }, - { - "name": "phpunit/php-token-stream", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "791198a2c6254db10131eecfe8c06670700904db" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db", - "reference": "791198a2c6254db10131eecfe8c06670700904db", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.2.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", - "keywords": [ - "tokenizer" - ], - "time": "2017-11-27T05:48:46+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "6.5.9", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "093ca5508174cd8ab8efe44fd1dde447adfdec8f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/093ca5508174cd8ab8efe44fd1dde447adfdec8f", - "reference": "093ca5508174cd8ab8efe44fd1dde447adfdec8f", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "myclabs/deep-copy": "^1.6.1", - "phar-io/manifest": "^1.0.1", - "phar-io/version": "^1.0", - "php": "^7.0", - "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^5.3", - "phpunit/php-file-iterator": "^1.4.3", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^1.0.9", - "phpunit/phpunit-mock-objects": "^5.0.5", - "sebastian/comparator": "^2.1", - "sebastian/diff": "^2.0", - "sebastian/environment": "^3.1", - "sebastian/exporter": "^3.1", - "sebastian/global-state": "^2.0", - "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^1.0", - "sebastian/version": "^2.0.1" - }, - "conflict": { - "phpdocumentor/reflection-docblock": "3.0.2", - "phpunit/dbunit": "<3.0" - }, - "require-dev": { - "ext-pdo": "*" - }, - "suggest": { - "ext-xdebug": "*", - "phpunit/php-invoker": "^1.1" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.5.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "time": "2018-07-03T06:40:40+00:00" - }, - { - "name": "phpunit/phpunit-mock-objects", - "version": "5.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "6f9a3c8bf34188a2b53ce2ae7a126089c53e0a9f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/6f9a3c8bf34188a2b53ce2ae7a126089c53e0a9f", - "reference": "6f9a3c8bf34188a2b53ce2ae7a126089c53e0a9f", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.5", - "php": "^7.0", - "phpunit/php-text-template": "^1.2.1", - "sebastian/exporter": "^3.1" - }, - "conflict": { - "phpunit/phpunit": "<6.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.5" - }, - "suggest": { - "ext-soap": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" - ], - "time": "2018-07-13T03:27:23+00:00" - }, - { - "name": "psr/container", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "time": "2017-02-14T16:28:37+00:00" - }, - { - "name": "psr/http-message", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", - "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" - ], - "time": "2016-08-06T14:39:51+00:00" - }, - { - "name": "psr/log", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", - "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "time": "2016-10-10T12:19:37+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04T06:30:41+00:00" - }, - { - "name": "sebastian/comparator", - "version": "2.1.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9", - "shasum": "" - }, - "require": { - "php": "^7.0", - "sebastian/diff": "^2.0 || ^3.0", - "sebastian/exporter": "^3.1" - }, - "require-dev": { - "phpunit/phpunit": "^6.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "time": "2018-02-01T13:46:46+00:00" - }, - { - "name": "sebastian/diff", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff" - ], - "time": "2017-08-03T08:09:46+00:00" - }, - { - "name": "sebastian/environment", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "time": "2017-07-01T08:51:00+00:00" - }, - { - "name": "sebastian/exporter", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", - "shasum": "" - }, - "require": { - "php": "^7.0", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "time": "2017-04-03T13:19:02+00:00" - }, - { - "name": "sebastian/global-state", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "time": "2017-04-27T15:39:26+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "shasum": "" - }, - "require": { - "php": "^7.0", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-08-03T12:35:26+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "773f97c67f28de00d397be301821b06708fca0be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", - "reference": "773f97c67f28de00d397be301821b06708fca0be", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "time": "2017-03-29T09:07:27+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2017-03-03T06:23:57+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "shasum": "" - }, - "require": { - "php": ">=5.6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2015-07-28T20:34:47+00:00" - }, - { - "name": "sebastian/version", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "time": "2016-10-03T07:35:21+00:00" - }, - { - "name": "symfony/browser-kit", - "version": "v3.4.14", - "source": { - "type": "git", - "url": "https://github.com/symfony/browser-kit.git", - "reference": "f6668d1a6182d5a8dec65a1c863a4c1d963816c0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/f6668d1a6182d5a8dec65a1c863a4c1d963816c0", - "reference": "f6668d1a6182d5a8dec65a1c863a4c1d963816c0", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/dom-crawler": "~2.8|~3.0|~4.0" - }, - "require-dev": { - "symfony/css-selector": "~2.8|~3.0|~4.0", - "symfony/process": "~2.8|~3.0|~4.0" - }, - "suggest": { - "symfony/process": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\BrowserKit\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony BrowserKit Component", - "homepage": "https://symfony.com", - "time": "2018-07-26T09:06:28+00:00" - }, - { - "name": "symfony/console", - "version": "v3.4.14", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "6b217594552b9323bcdcfc14f8a0ce126e84cd73" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/6b217594552b9323bcdcfc14f8a0ce126e84cd73", - "reference": "6b217594552b9323bcdcfc14f8a0ce126e84cd73", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/debug": "~2.8|~3.0|~4.0", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/dependency-injection": "<3.4", - "symfony/process": "<3.3" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.3|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~2.8|~3.0|~4.0", - "symfony/lock": "~3.4|~4.0", - "symfony/process": "~3.3|~4.0" - }, - "suggest": { - "psr/log-implementation": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Console Component", - "homepage": "https://symfony.com", - "time": "2018-07-26T11:19:56+00:00" - }, - { - "name": "symfony/css-selector", - "version": "v3.4.14", - "source": { - "type": "git", - "url": "https://github.com/symfony/css-selector.git", - "reference": "edda5a6155000ff8c3a3f85ee5c421af93cca416" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/edda5a6155000ff8c3a3f85ee5c421af93cca416", - "reference": "edda5a6155000ff8c3a3f85ee5c421af93cca416", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\CssSelector\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jean-François Simon", - "email": "jeanfrancois.simon@sensiolabs.com" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony CssSelector Component", - "homepage": "https://symfony.com", - "time": "2018-07-26T09:06:28+00:00" - }, - { - "name": "symfony/debug", - "version": "v3.4.14", - "source": { - "type": "git", - "url": "https://github.com/symfony/debug.git", - "reference": "d5a058ff6ecad26b30c1ba452241306ea34c65cc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/d5a058ff6ecad26b30c1ba452241306ea34c65cc", - "reference": "d5a058ff6ecad26b30c1ba452241306ea34c65cc", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "psr/log": "~1.0" - }, - "conflict": { - "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" - }, - "require-dev": { - "symfony/http-kernel": "~2.8|~3.0|~4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Debug\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Debug Component", - "homepage": "https://symfony.com", - "time": "2018-07-26T11:19:56+00:00" - }, - { - "name": "symfony/dom-crawler", - "version": "v3.4.14", - "source": { - "type": "git", - "url": "https://github.com/symfony/dom-crawler.git", - "reference": "452bfc854b60134438e3824b159b0d24a5892331" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/452bfc854b60134438e3824b159b0d24a5892331", - "reference": "452bfc854b60134438e3824b159b0d24a5892331", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0" - }, - "require-dev": { - "symfony/css-selector": "~2.8|~3.0|~4.0" - }, - "suggest": { - "symfony/css-selector": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\DomCrawler\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony DomCrawler Component", - "homepage": "https://symfony.com", - "time": "2018-07-26T10:03:52+00:00" - }, - { - "name": "symfony/event-dispatcher", - "version": "v3.4.14", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "b2e1f19280c09a42dc64c0b72b80fe44dd6e88fb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b2e1f19280c09a42dc64c0b72b80fe44dd6e88fb", - "reference": "b2e1f19280c09a42dc64c0b72b80fe44dd6e88fb", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8" - }, - "conflict": { - "symfony/dependency-injection": "<3.3" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~2.8|~3.0|~4.0", - "symfony/dependency-injection": "~3.3|~4.0", - "symfony/expression-language": "~2.8|~3.0|~4.0", - "symfony/stopwatch": "~2.8|~3.0|~4.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony EventDispatcher Component", - "homepage": "https://symfony.com", - "time": "2018-07-26T09:06:28+00:00" - }, - { - "name": "symfony/filesystem", - "version": "v3.4.12", - "source": { - "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "8a721a5f2553c6c3482b1c5b22ed60fe94dd63ed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/8a721a5f2553c6c3482b1c5b22ed60fe94dd63ed", - "reference": "8a721a5f2553c6c3482b1c5b22ed60fe94dd63ed", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-ctype": "~1.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Filesystem Component", - "homepage": "https://symfony.com", - "time": "2018-06-21T11:10:19+00:00" - }, - { - "name": "symfony/finder", - "version": "v3.4.12", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "3a8c3de91d2b2c68cd2d665cf9d00f7ef9eaa394" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/3a8c3de91d2b2c68cd2d665cf9d00f7ef9eaa394", - "reference": "3a8c3de91d2b2c68cd2d665cf9d00f7ef9eaa394", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Finder Component", - "homepage": "https://symfony.com", - "time": "2018-06-19T20:52:10+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.8.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/7cc359f1b7b80fc25ed7796be7d96adc9b354bae", - "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - }, - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "time": "2018-04-30T19:57:29+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.8.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "3296adf6a6454a050679cde90f95350ad604b171" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/3296adf6a6454a050679cde90f95350ad604b171", - "reference": "3296adf6a6454a050679cde90f95350ad604b171", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "time": "2018-04-26T10:06:28+00:00" - }, - { - "name": "symfony/process", - "version": "v3.4.12", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "acc5a37c706ace827962851b69705b24e71ca17c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/acc5a37c706ace827962851b69705b24e71ca17c", - "reference": "acc5a37c706ace827962851b69705b24e71ca17c", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Process Component", - "homepage": "https://symfony.com", - "time": "2018-05-30T04:24:30+00:00" - }, - { - "name": "symfony/yaml", - "version": "v3.4.12", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "c5010cc1692ce1fa328b1fb666961eb3d4a85bb0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/c5010cc1692ce1fa328b1fb666961eb3d4a85bb0", - "reference": "c5010cc1692ce1fa328b1fb666961eb3d4a85bb0", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-ctype": "~1.8" - }, - "conflict": { - "symfony/console": "<3.4" - }, - "require-dev": { - "symfony/console": "~3.4|~4.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Yaml Component", - "homepage": "https://symfony.com", - "time": "2018-05-03T23:18:14+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b", - "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "time": "2017-04-07T12:08:54+00:00" - }, - { - "name": "vlucas/phpdotenv", - "version": "v2.5.1", - "source": { - "type": "git", - "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "8abb4f9aa89ddea9d52112c65bbe8d0125e2fa8e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/8abb4f9aa89ddea9d52112c65bbe8d0125e2fa8e", - "reference": "8abb4f9aa89ddea9d52112c65bbe8d0125e2fa8e", - "shasum": "" - }, - "require": { - "php": ">=5.3.9" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.5-dev" - } - }, - "autoload": { - "psr-4": { - "Dotenv\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Vance Lucas", - "email": "vance@vancelucas.com", - "homepage": "http://www.vancelucas.com" - } - ], - "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", - "keywords": [ - "dotenv", - "env", - "environment" - ], - "time": "2018-07-29T20:33:41+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.3.0", - "source": { - "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "time": "2018-01-29T19:49:41+00:00" - } - ], - "packages-dev": [], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": true, - "prefer-lowest": false, - "platform": { - "php": "~7.0.13|~7.1.0" - }, - "platform-dev": [] -} From 544d99c4a30f0e89604fd2ef78dc3c6f1edb96c7 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Sat, 9 Feb 2019 15:56:46 +0200 Subject: [PATCH 0676/1295] Fixing compare block product removing action from sidebar --- .../luma/Magento_Catalog/web/css/source/_module.less | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less index 6aa0e50f3c393..4bf41343919d0 100644 --- a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less @@ -534,6 +534,15 @@ } } + .block-compare { + .action { + &.delete { + &:extend(.abs-remove-button-for-blocks all); + right: initial; + } + } + } + .action.tocart { border-radius: 0; } From d77bc6acaccaecddc4bda3415d268ed21ef94981 Mon Sep 17 00:00:00 2001 From: gauravagarwal1001 <37572719+gauravagarwal1001@users.noreply.github.com> Date: Tue, 12 Feb 2019 14:38:45 +0530 Subject: [PATCH 0677/1295] Updated Account.php --- .../Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php index b61a5cf83734b..dc93e8ba8417c 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php @@ -199,7 +199,7 @@ private function extractValuesFromAttributes(array $attributes, int $storeId): a if (isset($defaultValue) && !isset($formValues[$code])) { $formValues[$code] = $defaultValue; } - if ($code === 'group_id' && empty($defaultValue)) { + if ($code === 'group_id' && empty($formValues[$code])) { $formValues[$code] = $this->getDefaultCustomerGroup($storeId); } } From 16f096cac91bbcae3d2dae2d18a4d0f8095a337e Mon Sep 17 00:00:00 2001 From: Nainesh <nainesh@2jcommerce.in> Date: Thu, 7 Feb 2019 16:11:21 +0530 Subject: [PATCH 0678/1295] quantity-not-center-align-on-review-order --- .../Magento_Multishipping/web/css/source/_module.less | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/design/frontend/Magento/luma/Magento_Multishipping/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Multishipping/web/css/source/_module.less index 9761f36b96344..7fe2f2c58e655 100644 --- a/app/design/frontend/Magento/luma/Magento_Multishipping/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Multishipping/web/css/source/_module.less @@ -345,6 +345,15 @@ .data.table { &:extend(.abs-checkout-order-review all); + &.table-order-review { + > tbody { + > tr { + > td.col.qty { + text-align: center; + } + } + } + } } } From d893e08e7fc2db2032d559e4f2a97ef36368cd64 Mon Sep 17 00:00:00 2001 From: Nainesh <nainesh@2jcommerce.in> Date: Thu, 7 Feb 2019 17:12:25 +0530 Subject: [PATCH 0679/1295] quantity-not-center-align-on-review-order --- .../Magento_Multishipping/web/css/source/_module.less | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/design/frontend/Magento/luma/Magento_Multishipping/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Multishipping/web/css/source/_module.less index 7fe2f2c58e655..7977fb16c524c 100644 --- a/app/design/frontend/Magento/luma/Magento_Multishipping/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Multishipping/web/css/source/_module.less @@ -348,8 +348,15 @@ &.table-order-review { > tbody { > tr { - > td.col.qty { - text-align: center; + > td { + &.col { + &.subtotal { + border-bottom: none; + } + &.qty { + text-align: center; + } + } } } } From 512430af08837ba1ef8d8d4a598a98db90b47636 Mon Sep 17 00:00:00 2001 From: Arvinda kumar <arvindakumar@cedcommerce.com> Date: Wed, 6 Feb 2019 14:03:31 +0530 Subject: [PATCH 0680/1295] issue fixed #20919 Email label and email field not aligned from left for reorder of guest user issue fixed #20919 Email label and email field not aligned from left for reorder of guest user --- .../css/source/module/order/_order-account.less | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_order-account.less b/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_order-account.less index e14bcbcddd47f..5494cf46c6f9d 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_order-account.less +++ b/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_order-account.less @@ -27,3 +27,18 @@ width: 50%; } } +.page-create-order { + .order-details { + &:not(.order-details-existing-customer) { + .order-account-information { + .field-email { + margin-left: -30px; + } + + .field-group_id { + margin-right: 30px; + } + } + } + } +} From c1057381bcde28433edfafe62d339405facd7139 Mon Sep 17 00:00:00 2001 From: Arvinda kumar <arvindakumar@cedcommerce.com> Date: Wed, 6 Feb 2019 09:53:28 -0800 Subject: [PATCH 0681/1295] _order-account.less updated _order-account.less updated --- .../web/css/source/module/order/_order-account.less | 1 + 1 file changed, 1 insertion(+) diff --git a/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_order-account.less b/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_order-account.less index 5494cf46c6f9d..f66e94940c55d 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_order-account.less +++ b/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_order-account.less @@ -27,6 +27,7 @@ width: 50%; } } + .page-create-order { .order-details { &:not(.order-details-existing-customer) { From 573072bb73aca09240a55933eb4e232e0df62d52 Mon Sep 17 00:00:00 2001 From: Parag Chavare <parag@2jcommerce.in> Date: Mon, 4 Feb 2019 16:55:00 +0530 Subject: [PATCH 0682/1295] bundle-product-table-data-grouped-alignment :: Bundle product table data grouped alignment in mobile view --- .../web/css/source/_module.less | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/design/frontend/Magento/luma/Magento_GroupedProduct/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_GroupedProduct/web/css/source/_module.less index 088372808aa6a..ef323eab94e68 100644 --- a/app/design/frontend/Magento/luma/Magento_GroupedProduct/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_GroupedProduct/web/css/source/_module.less @@ -133,10 +133,19 @@ } .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { + .product-add-form{ .table-wrapper.grouped { - .lib-css(margin-left, -@layout__width-xs-indent); - .lib-css(margin-right, -@layout__width-xs-indent); + .lib-css(margin-left, -@layout__width-xs-indent); + .lib-css(margin-right, -@layout__width-xs-indent); + .table.data.grouped{ + tr{ + td{ + padding: 5px 10px 5px 15px; + } + } + } } + } } // From 30d4e283b4555b35cb63cfd34ca3895ae0f7d43a Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Tue, 5 Feb 2019 15:41:33 +0200 Subject: [PATCH 0683/1295] ENGCOM-4111: Static test fixed. --- .../web/css/source/_module.less | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/app/design/frontend/Magento/luma/Magento_GroupedProduct/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_GroupedProduct/web/css/source/_module.less index ef323eab94e68..fe49d6679a613 100644 --- a/app/design/frontend/Magento/luma/Magento_GroupedProduct/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_GroupedProduct/web/css/source/_module.less @@ -133,19 +133,19 @@ } .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { - .product-add-form{ - .table-wrapper.grouped { - .lib-css(margin-left, -@layout__width-xs-indent); - .lib-css(margin-right, -@layout__width-xs-indent); - .table.data.grouped{ - tr{ - td{ - padding: 5px 10px 5px 15px; - } + .product-add-form { + .table-wrapper.grouped { + .lib-css(margin-left, -@layout__width-xs-indent); + .lib-css(margin-right, -@layout__width-xs-indent); + .table.data.grouped { + tr { + td { + padding: 5px 10px 5px 15px; + } + } + } } - } } - } } // From 4b07a5c8806a22758c46d17d4493de2c3c362044 Mon Sep 17 00:00:00 2001 From: priti <priti@2jcommerce.in> Date: Tue, 22 Jan 2019 16:59:10 +0530 Subject: [PATCH 0684/1295] view-order-price-subtotal-alignment-not-proper-mobile --- .../Magento/luma/Magento_Sales/web/css/source/_module.less | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_module.less index 1e4a92fa0701f..c8bdc56621f7b 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 @@ -503,6 +503,11 @@ } } } + .order-items.table-wrapper { + .col.price, .col.qty, .col.subtotal, .col.msrp { + text-align: left; + } + } } .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { From ef4cdb62bc1044e2f2fcb402465f7f8b5b92f52a Mon Sep 17 00:00:00 2001 From: priti <priti@2jcommerce.in> Date: Tue, 22 Jan 2019 19:50:13 +0530 Subject: [PATCH 0685/1295] view-order-price-subtotal-alignment-not-proper-mobile --- .../Magento/blank/Magento_Sales/web/css/source/_module.less | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/design/frontend/Magento/blank/Magento_Sales/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Sales/web/css/source/_module.less index 1ea1e2c483d0b..a85820ab441e9 100644 --- a/app/design/frontend/Magento/blank/Magento_Sales/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Sales/web/css/source/_module.less @@ -294,6 +294,11 @@ } } } + .order-items.table-wrapper { + .col.price, .col.qty, .col.subtotal, .col.msrp { + text-align: left; + } + } } .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { From e956712022174e1eaf46b152b25b60e98515e674 Mon Sep 17 00:00:00 2001 From: priti <priti@2jcommerce.in> Date: Thu, 31 Jan 2019 12:31:40 +0530 Subject: [PATCH 0686/1295] view-order-price-subtotal-alignment-not-proper-mobile --- .../Magento/luma/Magento_Sales/web/css/source/_module.less | 5 ----- 1 file changed, 5 deletions(-) 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 c8bdc56621f7b..1e4a92fa0701f 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 @@ -503,11 +503,6 @@ } } } - .order-items.table-wrapper { - .col.price, .col.qty, .col.subtotal, .col.msrp { - text-align: left; - } - } } .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { From 597c4af3d5ea9b47200108adac46909d95a3b808 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Mon, 4 Feb 2019 12:38:59 +0200 Subject: [PATCH 0687/1295] ENGCOM-4069: Static test fix. --- .../Magento/blank/Magento_Sales/web/css/source/_module.less | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/blank/Magento_Sales/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Sales/web/css/source/_module.less index a85820ab441e9..44eb06ac5ce19 100644 --- a/app/design/frontend/Magento/blank/Magento_Sales/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Sales/web/css/source/_module.less @@ -295,7 +295,10 @@ } } .order-items.table-wrapper { - .col.price, .col.qty, .col.subtotal, .col.msrp { + .col.price, + .col.qty, + .col.subtotal, + .col.msrp { text-align: left; } } From 77c780d5ae54a8ec982648d6104cff6da50e35a6 Mon Sep 17 00:00:00 2001 From: Shikha Mishra <shikhamishra@cedcoss.com> Date: Sun, 3 Feb 2019 10:37:39 +0530 Subject: [PATCH 0688/1295] Updated Options.php --- app/code/Magento/Customer/Model/Options.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Model/Options.php b/app/code/Magento/Customer/Model/Options.php index 7747e309d82a6..19cac4a7bc326 100644 --- a/app/code/Magento/Customer/Model/Options.php +++ b/app/code/Magento/Customer/Model/Options.php @@ -91,7 +91,7 @@ private function prepareNamePrefixSuffixOptions($options, $isOptional = false) return false; } $result = []; - $options = explode(';', $options); + $options = array_filter(explode(';', $options)); foreach ($options as $value) { $value = $this->escaper->escapeHtml(trim($value)); $result[$value] = $value; From 5eeecac645d53a3c543247a1d6e6c26752ee5012 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Fri, 8 Feb 2019 15:39:40 +0200 Subject: [PATCH 0689/1295] ENGCOM-4146: Static test fix. --- app/code/Magento/Customer/Model/Options.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Customer/Model/Options.php b/app/code/Magento/Customer/Model/Options.php index 19cac4a7bc326..71e70f8e14208 100644 --- a/app/code/Magento/Customer/Model/Options.php +++ b/app/code/Magento/Customer/Model/Options.php @@ -8,7 +8,11 @@ use Magento\Config\Model\Config\Source\Nooptreq as NooptreqSource; use Magento\Customer\Helper\Address as AddressHelper; use Magento\Framework\Escaper; +use Magento\Store\Api\Data\StoreInterface; +/** + * Customer Options. + */ class Options { /** @@ -38,7 +42,7 @@ public function __construct( /** * Retrieve name prefix dropdown options * - * @param null $store + * @param null|string|bool|int|StoreInterface $store * @return array|bool */ public function getNamePrefixOptions($store = null) @@ -52,7 +56,7 @@ public function getNamePrefixOptions($store = null) /** * Retrieve name suffix dropdown options * - * @param null $store + * @param null|string|bool|int|StoreInterface $store * @return array|bool */ public function getNameSuffixOptions($store = null) @@ -64,7 +68,9 @@ public function getNameSuffixOptions($store = null) } /** - * @param $options + * Unserialize and clear name prefix or suffix options. + * + * @param string $options * @param bool $isOptional * @return array|bool * @@ -78,6 +84,7 @@ protected function _prepareNamePrefixSuffixOptions($options, $isOptional = false /** * Unserialize and clear name prefix or suffix options + * * If field is optional, add an empty first option. * * @param string $options From a47ea3820a0c3886b2a73ca8feb76a1ea3a1d1b8 Mon Sep 17 00:00:00 2001 From: "Leandro F. L" <lfluvisotto@gmail.com> Date: Sun, 27 Jan 2019 21:46:01 -0200 Subject: [PATCH 0690/1295] It is recommended to use the &&, || operators, instead of and, or to prevent confusion. --- .../Adminhtml/Product/Attribute/Validate.php | 2 +- .../Controller/Transparent/RequestSecureToken.php | 2 +- .../Security/Model/SecurityChecker/Quantity.php | 2 +- app/code/Magento/SendFriend/Model/SendFriend.php | 12 ++++++------ app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php | 2 +- .../testsuite/Magento/Test/Integrity/ClassesTest.php | 2 +- lib/internal/Magento/Framework/Filter/Template.php | 2 +- lib/internal/Magento/Framework/Message/Manager.php | 2 +- .../Setup/Model/ConfigOptionsList/Session.php | 2 +- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php index 3a81b4633b2ff..40a2ce692e404 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php @@ -158,7 +158,7 @@ private function isUniqueAdminValues(array $optionsValues, array $deletedOptions { $adminValues = []; foreach ($optionsValues as $optionKey => $values) { - if (!(isset($deletedOptions[$optionKey]) and $deletedOptions[$optionKey] === '1')) { + if (!(isset($deletedOptions[$optionKey]) && $deletedOptions[$optionKey] === '1')) { $adminValues[] = reset($values); } } diff --git a/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php b/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php index 2efae34a96459..a6d55c91b6105 100644 --- a/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php +++ b/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php @@ -82,7 +82,7 @@ public function execute() /** @var Quote $quote */ $quote = $this->sessionManager->getQuote(); - if (!$quote or !$quote instanceof Quote) { + if (!$quote || !$quote instanceof Quote) { return $this->getErrorResponse(); } diff --git a/app/code/Magento/Security/Model/SecurityChecker/Quantity.php b/app/code/Magento/Security/Model/SecurityChecker/Quantity.php index 7f1d85abeb74b..eb6ba6cef16bc 100644 --- a/app/code/Magento/Security/Model/SecurityChecker/Quantity.php +++ b/app/code/Magento/Security/Model/SecurityChecker/Quantity.php @@ -53,7 +53,7 @@ public function check($securityEventType, $accountReference = null, $longIp = nu { $isEnabled = $this->securityConfig->getPasswordResetProtectionType() != ResetMethod::OPTION_NONE; $allowedAttemptsNumber = $this->securityConfig->getMaxNumberPasswordResetRequests(); - if ($isEnabled and $allowedAttemptsNumber) { + if ($isEnabled && $allowedAttemptsNumber) { $collection = $this->prepareCollection($securityEventType, $accountReference, $longIp); if ($collection->count() >= $allowedAttemptsNumber) { throw new SecurityViolationException( diff --git a/app/code/Magento/SendFriend/Model/SendFriend.php b/app/code/Magento/SendFriend/Model/SendFriend.php index c69d6342b4892..79bac0e680952 100644 --- a/app/code/Magento/SendFriend/Model/SendFriend.php +++ b/app/code/Magento/SendFriend/Model/SendFriend.php @@ -236,7 +236,7 @@ public function validate() } $email = $this->getSender()->getEmail(); - if (empty($email) or !\Zend_Validate::is($email, \Magento\Framework\Validator\EmailAddress::class)) { + if (empty($email) || !\Zend_Validate::is($email, \Magento\Framework\Validator\EmailAddress::class)) { $errors[] = __('Invalid Sender Email'); } @@ -281,13 +281,13 @@ public function setRecipients($recipients) // validate array if (!is_array( $recipients - ) or !isset( + ) || !isset( $recipients['email'] - ) or !isset( + ) || !isset( $recipients['name'] - ) or !is_array( + ) || !is_array( $recipients['email'] - ) or !is_array( + ) || !is_array( $recipients['name'] ) ) { @@ -487,7 +487,7 @@ protected function _sentCountByCookies($increment = false) $oldTimes = explode(',', $oldTimes); foreach ($oldTimes as $oldTime) { $periodTime = $time - $this->_sendfriendData->getPeriod(); - if (is_numeric($oldTime) and $oldTime >= $periodTime) { + if (is_numeric($oldTime) && $oldTime >= $periodTime) { $newTimes[] = $oldTime; } } diff --git a/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php b/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php index 82e2048283a9b..94ea0417be69f 100755 --- a/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php +++ b/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php @@ -264,7 +264,7 @@ protected function processExtraTaxables(Address\Total $total, array $itemsByType { $extraTaxableDetails = []; foreach ($itemsByType as $itemType => $itemTaxDetails) { - if ($itemType != self::ITEM_TYPE_PRODUCT and $itemType != self::ITEM_TYPE_SHIPPING) { + if ($itemType != self::ITEM_TYPE_PRODUCT && $itemType != self::ITEM_TYPE_SHIPPING) { foreach ($itemTaxDetails as $itemCode => $itemTaxDetail) { /** @var \Magento\Tax\Api\Data\TaxDetailsInterface $taxDetails */ $taxDetails = $itemTaxDetail[self::KEY_ITEM]; diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php index 5c1e342e1bc81..4b31b80f1d7b3 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php @@ -191,7 +191,7 @@ private function assertClassesExist($classes, $path) foreach ($classes as $class) { $class = trim($class, '\\'); try { - if (strrchr($class, '\\') === false and !Classes::isVirtual($class)) { + if (strrchr($class, '\\') === false && !Classes::isVirtual($class)) { $badUsages[] = $class; continue; } else { diff --git a/lib/internal/Magento/Framework/Filter/Template.php b/lib/internal/Magento/Framework/Filter/Template.php index 40799bfe0a6b5..a2e772853efcb 100644 --- a/lib/internal/Magento/Framework/Filter/Template.php +++ b/lib/internal/Magento/Framework/Filter/Template.php @@ -227,7 +227,7 @@ public function templateDirective($construction) { // Processing of {template config_path=... [...]} statement $templateParameters = $this->getParameters($construction[2]); - if (!isset($templateParameters['config_path']) or !$this->getTemplateProcessor()) { + if (!isset($templateParameters['config_path']) || !$this->getTemplateProcessor()) { // Not specified template or not set include processor $replacedValue = '{Error in template processing}'; } else { diff --git a/lib/internal/Magento/Framework/Message/Manager.php b/lib/internal/Magento/Framework/Message/Manager.php index f31892a938fb1..75b351515839c 100644 --- a/lib/internal/Magento/Framework/Message/Manager.php +++ b/lib/internal/Magento/Framework/Message/Manager.php @@ -226,7 +226,7 @@ public function addUniqueMessages(array $messages, $group = null) $items = $this->getMessages(false, $group)->getItems(); foreach ($messages as $message) { - if ($message instanceof MessageInterface and !in_array($message, $items, false)) { + if ($message instanceof MessageInterface && !in_array($message, $items, false)) { $this->addMessage($message, $group); } } diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Session.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Session.php index 3b3fbf33a02e2..1c64d8994630d 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Session.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Session.php @@ -301,7 +301,7 @@ public function validate(array $options, DeploymentConfig $deploymentConfig) if (isset($options[self::INPUT_KEY_SESSION_REDIS_LOG_LEVEL])) { $level = $options[self::INPUT_KEY_SESSION_REDIS_LOG_LEVEL]; - if (($level < 0) or ($level > 7)) { + if (($level < 0) || ($level > 7)) { $errors[] = "Invalid Redis log level '{$level}'. Valid range is 0-7, inclusive."; } } From 30273c63f3ed6127bb58cb6d64b8b7ca7aee6c03 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Mon, 28 Jan 2019 16:50:22 +0200 Subject: [PATCH 0691/1295] ENGCOM-3980: Static test fix. --- .../Paypal/Controller/Transparent/RequestSecureToken.php | 2 ++ .../Magento/Security/Model/SecurityChecker/Quantity.php | 2 +- app/code/Magento/SendFriend/Model/SendFriend.php | 3 +++ app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php | 1 + lib/internal/Magento/Framework/Message/Manager.php | 2 ++ setup/src/Magento/Setup/Model/ConfigOptionsList/Session.php | 6 +++--- 6 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php b/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php index a6d55c91b6105..3738a479816b3 100644 --- a/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php +++ b/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php @@ -106,6 +106,8 @@ public function execute() } /** + * Get error response. + * * @return Json */ private function getErrorResponse() diff --git a/app/code/Magento/Security/Model/SecurityChecker/Quantity.php b/app/code/Magento/Security/Model/SecurityChecker/Quantity.php index eb6ba6cef16bc..cbf41980c51d3 100644 --- a/app/code/Magento/Security/Model/SecurityChecker/Quantity.php +++ b/app/code/Magento/Security/Model/SecurityChecker/Quantity.php @@ -47,7 +47,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function check($securityEventType, $accountReference = null, $longIp = null) { diff --git a/app/code/Magento/SendFriend/Model/SendFriend.php b/app/code/Magento/SendFriend/Model/SendFriend.php index 79bac0e680952..38525a9f83a12 100644 --- a/app/code/Magento/SendFriend/Model/SendFriend.php +++ b/app/code/Magento/SendFriend/Model/SendFriend.php @@ -16,6 +16,7 @@ * @method \Magento\SendFriend\Model\SendFriend setTime(int $value) * * @author Magento Core Team <core@magentocommerce.com> + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * * @api @@ -162,6 +163,8 @@ protected function _construct() } /** + * Send email. + * * @return $this * @throws CoreException */ diff --git a/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php b/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php index 94ea0417be69f..865b656918cc1 100755 --- a/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php +++ b/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php @@ -407,6 +407,7 @@ protected function enhanceTotalData( /** * Process model configuration array. + * * This method can be used for changing totals collect sort order * * @param array $config diff --git a/lib/internal/Magento/Framework/Message/Manager.php b/lib/internal/Magento/Framework/Message/Manager.php index 75b351515839c..4e73b6112f9d8 100644 --- a/lib/internal/Magento/Framework/Message/Manager.php +++ b/lib/internal/Magento/Framework/Message/Manager.php @@ -11,6 +11,8 @@ /** * Message manager model + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Manager implements ManagerInterface diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Session.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Session.php index 1c64d8994630d..b5fbfd795363f 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Session.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Session.php @@ -124,7 +124,7 @@ class Session implements ConfigOptionsListInterface ]; /** - * {@inheritdoc} + * @inheritdoc * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function getOptions() @@ -250,7 +250,7 @@ public function getOptions() } /** - * {@inheritdoc} + * @inheritdoc */ public function createConfig(array $options, DeploymentConfig $deploymentConfig) { @@ -281,7 +281,7 @@ public function createConfig(array $options, DeploymentConfig $deploymentConfig) } /** - * {@inheritdoc} + * @inheritdoc */ public function validate(array $options, DeploymentConfig $deploymentConfig) { From da3a606adcdce1e1f55e2e74e106cf3f67a650db Mon Sep 17 00:00:00 2001 From: Abrar pathan <abrarkhan@krishtechnolabs.com> Date: Fri, 8 Feb 2019 17:55:10 +0530 Subject: [PATCH 0692/1295] fixed-issue-21070 --- .../Magento_Sales/web/css/source/_module.less | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_module.less index 1e4a92fa0701f..815517670438a 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 @@ -555,13 +555,13 @@ margin: 0 @tab-control__margin-right 0 0; a { - padding: @tab-control__padding-top @tab-control__padding-right; + padding: @tab-control__padding-top @indent__base; } strong { border-bottom: 0; margin-bottom: -1px; - padding: @tab-control__padding-top @tab-control__padding-right @tab-control__padding-bottom + 1 @tab-control__padding-left; + padding: @tab-control__padding-top @indent__base @tab-control__padding-bottom + 1 @indent__base; } } } @@ -687,3 +687,23 @@ } } } + +.media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__l) { + + .order-links { + .item { + margin: 0 @tab-control__margin-right 0 0; + + a { + padding: @tab-control__padding-top @tab-control__padding-right; + } + + strong { + border-bottom: 0; + margin-bottom: -1px; + padding: @tab-control__padding-top @tab-control__padding-right @tab-control__padding-bottom + 1 @tab-control__padding-left; + } + } + } + +} \ No newline at end of file From 6acd8a4c1265c3f6da08b9d7722a77d83def245f Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <d.cheshun@atwix.com> Date: Sun, 10 Feb 2019 22:59:38 +0200 Subject: [PATCH 0693/1295] Update _module.less Code refactoring, remove the redundant rules --- .../Magento/luma/Magento_Sales/web/css/source/_module.less | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) 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 815517670438a..314f0d0ee6298 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 @@ -689,7 +689,6 @@ } .media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__l) { - .order-links { .item { margin: 0 @tab-control__margin-right 0 0; @@ -699,11 +698,8 @@ } strong { - border-bottom: 0; - margin-bottom: -1px; padding: @tab-control__padding-top @tab-control__padding-right @tab-control__padding-bottom + 1 @tab-control__padding-left; } } } - -} \ No newline at end of file +} From caac0c28ad050f70531bd061935e56c9b93788f9 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Fri, 15 Feb 2019 10:54:38 +0200 Subject: [PATCH 0694/1295] MAGETWO-97684: State is always required in backend in customer address form --- .../Magento/Customer/Block/Adminhtml/Edit/Renderer/Region.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Renderer/Region.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Renderer/Region.php index 9a025211c9b0a..0aeed1562c51e 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Renderer/Region.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Renderer/Region.php @@ -48,7 +48,7 @@ public function render(\Magento\Framework\Data\Form\Element\AbstractElement $ele $regionId = $element->getForm()->getElement('region_id')->getValue(); - $html = '<div class="field field-state required admin__field _required">'; + $html = '<div class="field field-state admin__field">'; $element->setClass('input-text admin__control-text'); $element->setRequired(true); $html .= $element->getLabelHtml() . '<div class="control admin__field-control">'; From 361145d37b299f085a34944049b380ba173e5701 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Fri, 15 Feb 2019 10:28:30 +0200 Subject: [PATCH 0695/1295] MC-14840: Not clickable link --- .../Magento_Rma/web/css/source/_module.less | 2 ++ .../Magento_Rma/web/css/source/module/_rma.less | 15 +++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 app/design/adminhtml/Magento/backend/Magento_Rma/web/css/source/module/_rma.less diff --git a/app/design/adminhtml/Magento/backend/Magento_Rma/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Rma/web/css/source/_module.less index c405707ee7bbe..2ae33e0269ac4 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Rma/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Rma/web/css/source/_module.less @@ -3,6 +3,8 @@ // * See COPYING.txt for license details. // */ +@import 'module/_rma.less'; + .media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) { .rma-request-details, .rma-wrapper .order-shipping-address { diff --git a/app/design/adminhtml/Magento/backend/Magento_Rma/web/css/source/module/_rma.less b/app/design/adminhtml/Magento/backend/Magento_Rma/web/css/source/module/_rma.less new file mode 100644 index 0000000000000..4b3ee21e04ff3 --- /dev/null +++ b/app/design/adminhtml/Magento/backend/Magento_Rma/web/css/source/module/_rma.less @@ -0,0 +1,15 @@ +// /** +// * Copyright © Magento, Inc. All rights reserved. +// * See COPYING.txt for license details. +// */ + +// +// Layout +// --------------------------------------------- + +.media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) { + .rma-wrapper .order-shipping-method { + float: right; + #mix-grid .width(6,12); + } +} From 46572afd1a9f142958207718311dad47b667d606 Mon Sep 17 00:00:00 2001 From: Vasilii <v.burlacu@atwix.com> Date: Wed, 23 Jan 2019 11:38:32 +0200 Subject: [PATCH 0696/1295] magento/magento2#18347 - Element 'css', attribute 'as': The attribute 'as' is not allowed. (CSS preloading) - Added as attribute to linkType with 3 possible options: style, script and font --- lib/internal/Magento/Framework/View/Layout/etc/head.xsd | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/internal/Magento/Framework/View/Layout/etc/head.xsd b/lib/internal/Magento/Framework/View/Layout/etc/head.xsd index a913507ae17b3..15762dc2f0ae6 100644 --- a/lib/internal/Magento/Framework/View/Layout/etc/head.xsd +++ b/lib/internal/Magento/Framework/View/Layout/etc/head.xsd @@ -20,6 +20,15 @@ <xs:attribute name="type" type="xs:string"/> <xs:attribute name="order" type="xs:integer"/> <xs:attribute name="src_type" type="xs:string"/> + <xs:attribute name="as"> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:enumeration value="font" /> + <xs:enumeration value="script" /> + <xs:enumeration value="style" /> + </xs:restriction> + </xs:simpleType> + </xs:attribute> </xs:complexType> <xs:complexType name="metaType"> From f0670b6675875620a0a92c57e96d943de3eb6e1b Mon Sep 17 00:00:00 2001 From: Shikha Mishra <shikhamishra@cedcoss.com> Date: Thu, 27 Dec 2018 22:12:21 +0530 Subject: [PATCH 0697/1295] Updated Download.php --- app/code/Magento/Downloadable/Helper/Download.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Downloadable/Helper/Download.php b/app/code/Magento/Downloadable/Helper/Download.php index 910eaa42f0e84..50f8d757d5537 100644 --- a/app/code/Magento/Downloadable/Helper/Download.php +++ b/app/code/Magento/Downloadable/Helper/Download.php @@ -200,6 +200,9 @@ public function getContentType() return $this->_downloadableFile->getFileType($this->_resourceFile); } } elseif ($this->_linkType == self::LINK_TYPE_URL) { + if(is_array($this->_handle->stat($this->_resourceFile)['type'])){ + return end($this->_handle->stat($this->_resourceFile)['type']); + }else return $this->_handle->stat($this->_resourceFile)['type']; } return $this->_contentType; From 04f6cfedfd6ec39fcbe0d8ae8ae298c2c62f734d Mon Sep 17 00:00:00 2001 From: Shikha Mishra <shikhamishra@cedcoss.com> Date: Thu, 27 Dec 2018 22:17:42 +0530 Subject: [PATCH 0698/1295] Updated Http.php --- lib/internal/Magento/Framework/Filesystem/Driver/Http.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/internal/Magento/Framework/Filesystem/Driver/Http.php b/lib/internal/Magento/Framework/Filesystem/Driver/Http.php index 5c7fdb0630186..c4350c970f9e6 100644 --- a/lib/internal/Magento/Framework/Filesystem/Driver/Http.php +++ b/lib/internal/Magento/Framework/Filesystem/Driver/Http.php @@ -39,6 +39,11 @@ public function isExists($path) if (strpos($status, '302 Found') !== false && isset($headers[1])) { $status = $headers[1]; } + + /* Handling 301 redirection */ + if (strpos($status, '301 Moved Permanently') !== false && isset($headers[1])) { + $status = $headers[1]; + } if (strpos($status, '200 OK') === false) { $result = false; From a89e6ed21e7044fa29d75866d7041af9b209fb7c Mon Sep 17 00:00:00 2001 From: Milind Singh <milind7@live.com> Date: Sat, 29 Dec 2018 15:00:45 +0530 Subject: [PATCH 0699/1295] Update Download.php --- app/code/Magento/Downloadable/Helper/Download.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Downloadable/Helper/Download.php b/app/code/Magento/Downloadable/Helper/Download.php index 50f8d757d5537..0da899dcbce84 100644 --- a/app/code/Magento/Downloadable/Helper/Download.php +++ b/app/code/Magento/Downloadable/Helper/Download.php @@ -200,10 +200,11 @@ public function getContentType() return $this->_downloadableFile->getFileType($this->_resourceFile); } } elseif ($this->_linkType == self::LINK_TYPE_URL) { - if(is_array($this->_handle->stat($this->_resourceFile)['type'])){ + if (is_array($this->_handle->stat($this->_resourceFile)['type'])) { return end($this->_handle->stat($this->_resourceFile)['type']); - }else - return $this->_handle->stat($this->_resourceFile)['type']; + } else { + return $this->_handle->stat($this->_resourceFile)['type']; + } } return $this->_contentType; } From 7058adb9fba8fb58c6d62ee7f4e1f27f2a7f58fc Mon Sep 17 00:00:00 2001 From: Chris Snedaker <df2002@gmail.com> Date: Mon, 31 Dec 2018 04:45:25 -0500 Subject: [PATCH 0700/1295] Updated to reduce overall complexity of function --- .../Magento/Downloadable/Helper/Download.php | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Downloadable/Helper/Download.php b/app/code/Magento/Downloadable/Helper/Download.php index 0da899dcbce84..adb3378f23e46 100644 --- a/app/code/Magento/Downloadable/Helper/Download.php +++ b/app/code/Magento/Downloadable/Helper/Download.php @@ -185,26 +185,23 @@ public function getFileSize() * * @return string */ - public function getContentType() + public function getContentType() { $this->_getHandle(); - if ($this->_linkType == self::LINK_TYPE_FILE) { - if (function_exists( - 'mime_content_type' - ) && ($contentType = mime_content_type( - $this->_workingDirectory->getAbsolutePath($this->_resourceFile) - )) + if ($this->_linkType === self::LINK_TYPE_FILE) { + if (function_exists('mime_content_type') + && ($contentType = mime_content_type( + $this->_workingDirectory->getAbsolutePath($this->_resourceFile) + )) ) { return $contentType; - } else { - return $this->_downloadableFile->getFileType($this->_resourceFile); - } - } elseif ($this->_linkType == self::LINK_TYPE_URL) { - if (is_array($this->_handle->stat($this->_resourceFile)['type'])) { - return end($this->_handle->stat($this->_resourceFile)['type']); - } else { - return $this->_handle->stat($this->_resourceFile)['type']; } + return $this->_downloadableFile->getFileType($this->_resourceFile); + } + if ($this->_linkType === self::LINK_TYPE_URL) { + return (is_array($this->_handle->stat($this->_resourceFile)['type']) + ? end($this->_handle->stat($this->_resourceFile)['type']) + : $this->_handle->stat($this->_resourceFile)['type']); } return $this->_contentType; } From dac6687ed0b2eba02bd3e67913b2929afc940c9c Mon Sep 17 00:00:00 2001 From: Chris Snedaker <df2002@gmail.com> Date: Mon, 31 Dec 2018 04:51:05 -0500 Subject: [PATCH 0701/1295] Merged logic and reduced overall complexity of function Note: if() constructions that return boolean values based on conditions, can be simplified to return the condition instead. --- .../Framework/Filesystem/Driver/Http.php | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/lib/internal/Magento/Framework/Filesystem/Driver/Http.php b/lib/internal/Magento/Framework/Filesystem/Driver/Http.php index c4350c970f9e6..55935f2f149ce 100644 --- a/lib/internal/Magento/Framework/Filesystem/Driver/Http.php +++ b/lib/internal/Magento/Framework/Filesystem/Driver/Http.php @@ -27,31 +27,18 @@ class Http extends File * * @param string $path * @return bool - * @throws FileSystemException */ public function isExists($path) { $headers = array_change_key_case(get_headers($this->getScheme() . $path, 1), CASE_LOWER); - $status = $headers[0]; - /* Handling 302 redirection */ - if (strpos($status, '302 Found') !== false && isset($headers[1])) { - $status = $headers[1]; - } - - /* Handling 301 redirection */ - if (strpos($status, '301 Moved Permanently') !== false && isset($headers[1])) { + /* Handling 301 or 302 redirection */ + if (isset($headers[1]) && preg_match('/^30[12]/', $status)) { $status = $headers[1]; } - if (strpos($status, '200 OK') === false) { - $result = false; - } else { - $result = true; - } - - return $result; + return !(strpos($status, '200 OK') === false); } /** From fc90509ec4cac3785de7f2cc537476a87e89e611 Mon Sep 17 00:00:00 2001 From: Shikha Mishra <shikhamishra@cedcoss.com> Date: Wed, 2 Jan 2019 21:23:35 +0530 Subject: [PATCH 0702/1295] Updated Http.php --- lib/internal/Magento/Framework/Filesystem/Driver/Http.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Filesystem/Driver/Http.php b/lib/internal/Magento/Framework/Filesystem/Driver/Http.php index 55935f2f149ce..46ad773cff201 100644 --- a/lib/internal/Magento/Framework/Filesystem/Driver/Http.php +++ b/lib/internal/Magento/Framework/Filesystem/Driver/Http.php @@ -34,7 +34,7 @@ public function isExists($path) $status = $headers[0]; /* Handling 301 or 302 redirection */ - if (isset($headers[1]) && preg_match('/^30[12]/', $status)) { + if (isset($headers[1]) && preg_match('/30[12]/', $status)) { $status = $headers[1]; } From 71a5f5d302088d638cec73dd7835358beba84f15 Mon Sep 17 00:00:00 2001 From: Shikha Mishra <shikhamishra@cedcoss.com> Date: Tue, 22 Jan 2019 00:23:36 +0530 Subject: [PATCH 0703/1295] updated Download.php --- app/code/Magento/Downloadable/Helper/Download.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Downloadable/Helper/Download.php b/app/code/Magento/Downloadable/Helper/Download.php index adb3378f23e46..58ae9a1050c9d 100644 --- a/app/code/Magento/Downloadable/Helper/Download.php +++ b/app/code/Magento/Downloadable/Helper/Download.php @@ -255,10 +255,19 @@ public function setResource($resourceFile, $linkType = self::LINK_TYPE_FILE) ); } } + + /** + * check header + */ $this->_resourceFile = $resourceFile; + $headers = array_change_key_case(get_headers($this->_resourceFile, 1), CASE_LOWER); + if(isset($headers['location'])){ + $this->_resourceFile = is_array($headers['location']) ? current($headers['location']): + $headers['location']; + } + $this->_linkType = $linkType; - return $this; } From 0fc730bb0c3545b3ff241d097efb74ad160c8e6d Mon Sep 17 00:00:00 2001 From: Prince Patel <mail.mageprince@gmail.com> Date: Wed, 23 Jan 2019 12:25:44 +0530 Subject: [PATCH 0704/1295] Update Download.php --- app/code/Magento/Downloadable/Helper/Download.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Downloadable/Helper/Download.php b/app/code/Magento/Downloadable/Helper/Download.php index 58ae9a1050c9d..349a22c383eb5 100644 --- a/app/code/Magento/Downloadable/Helper/Download.php +++ b/app/code/Magento/Downloadable/Helper/Download.php @@ -185,7 +185,7 @@ public function getFileSize() * * @return string */ - public function getContentType() + public function getContentType() { $this->_getHandle(); if ($this->_linkType === self::LINK_TYPE_FILE) { From be3a5456339f66f98d2aaa42eebafbe9fcfa389f Mon Sep 17 00:00:00 2001 From: Shikha Mishra <shikhamishra@cedcoss.com> Date: Mon, 28 Jan 2019 23:22:46 +0530 Subject: [PATCH 0705/1295] updated Download.php --- .../Magento/Downloadable/Helper/Download.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Downloadable/Helper/Download.php b/app/code/Magento/Downloadable/Helper/Download.php index 349a22c383eb5..4832773f13d49 100644 --- a/app/code/Magento/Downloadable/Helper/Download.php +++ b/app/code/Magento/Downloadable/Helper/Download.php @@ -256,15 +256,17 @@ public function setResource($resourceFile, $linkType = self::LINK_TYPE_FILE) } } - /** - * check header - */ - $this->_resourceFile = $resourceFile; - $headers = array_change_key_case(get_headers($this->_resourceFile, 1), CASE_LOWER); - if(isset($headers['location'])){ - $this->_resourceFile = is_array($headers['location']) ? current($headers['location']): - $headers['location']; + + /** + * check header for urls + */ + if ($this->_linkType === self::LINK_TYPE_URL) { + $headers = array_change_key_case(get_headers($this->_resourceFile, 1), CASE_LOWER); + if(isset($headers['location'])){ + $this->_resourceFile = is_array($headers['location']) ? current($headers['location']): + $headers['location']; + } } $this->_linkType = $linkType; From e5d04fa7171ad496e6d2836f56e6eff4d8a2e44d Mon Sep 17 00:00:00 2001 From: Shikha Mishra <shikhamishra@cedcoss.com> Date: Mon, 28 Jan 2019 23:46:54 +0530 Subject: [PATCH 0706/1295] Updated Download.php --- app/code/Magento/Downloadable/Helper/Download.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Downloadable/Helper/Download.php b/app/code/Magento/Downloadable/Helper/Download.php index 4832773f13d49..9b91c18a33cbd 100644 --- a/app/code/Magento/Downloadable/Helper/Download.php +++ b/app/code/Magento/Downloadable/Helper/Download.php @@ -261,7 +261,7 @@ public function setResource($resourceFile, $linkType = self::LINK_TYPE_FILE) /** * check header for urls */ - if ($this->_linkType === self::LINK_TYPE_URL) { + if ($linkType === self::LINK_TYPE_URL) { $headers = array_change_key_case(get_headers($this->_resourceFile, 1), CASE_LOWER); if(isset($headers['location'])){ $this->_resourceFile = is_array($headers['location']) ? current($headers['location']): From 9817c50262fcdd45246bf59475795689e7cbca30 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Mon, 11 Feb 2019 12:36:21 +0200 Subject: [PATCH 0707/1295] Fix static test. --- app/code/Magento/Downloadable/Helper/Download.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Downloadable/Helper/Download.php b/app/code/Magento/Downloadable/Helper/Download.php index 9b91c18a33cbd..e98d400794dd1 100644 --- a/app/code/Magento/Downloadable/Helper/Download.php +++ b/app/code/Magento/Downloadable/Helper/Download.php @@ -15,6 +15,7 @@ /** * Downloadable Products Download Helper * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class Download extends \Magento\Framework\App\Helper\AbstractHelper { @@ -263,9 +264,9 @@ public function setResource($resourceFile, $linkType = self::LINK_TYPE_FILE) */ if ($linkType === self::LINK_TYPE_URL) { $headers = array_change_key_case(get_headers($this->_resourceFile, 1), CASE_LOWER); - if(isset($headers['location'])){ - $this->_resourceFile = is_array($headers['location']) ? current($headers['location']): - $headers['location']; + if (isset($headers['location'])) { + $this->_resourceFile = is_array($headers['location']) ? current($headers['location']) + : $headers['location']; } } From cbf2c011a88210984dc77dda7a5ddd5a55ee72f7 Mon Sep 17 00:00:00 2001 From: Emipro <git@emiprotechnologies.com> Date: Fri, 7 Dec 2018 14:33:48 +0530 Subject: [PATCH 0708/1295] Fixed Custom option price calculation is wrong with multi currency when option price type is percentage. --- app/code/Magento/Catalog/Block/Product/View/Options.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Product/View/Options.php b/app/code/Magento/Catalog/Block/Product/View/Options.php index 0720c018f6a9b..1f8041d115f89 100644 --- a/app/code/Magento/Catalog/Block/Product/View/Options.php +++ b/app/code/Magento/Catalog/Block/Product/View/Options.php @@ -160,7 +160,7 @@ public function hasOptions() */ protected function _getPriceConfiguration($option) { - $optionPrice = $this->pricingHelper->currency($option->getPrice(true), false, false); + $optionPrice = $option->getPrice(true); $data = [ 'prices' => [ 'oldPrice' => [ @@ -195,7 +195,7 @@ protected function _getPriceConfiguration($option) ], ], 'type' => $option->getPriceType(), - 'name' => $option->getTitle() + 'name' => $option->getTitle(), ]; return $data; } @@ -231,7 +231,7 @@ public function getJsonConfig() //pass the return array encapsulated in an object for the other modules to be able to alter it eg: weee $this->_eventManager->dispatch('catalog_product_option_price_configuration_after', ['configObj' => $configObj]); - $config=$configObj->getConfig(); + $config = $configObj->getConfig(); return $this->_jsonEncoder->encode($config); } From ee8402fd25974d64c98a8da28fcbf042251b4da6 Mon Sep 17 00:00:00 2001 From: Emipro <git@emiprotechnologies.com> Date: Fri, 7 Dec 2018 15:50:55 +0530 Subject: [PATCH 0709/1295] solved fixed price calculation --- app/code/Magento/Catalog/Block/Product/View/Options.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Block/Product/View/Options.php b/app/code/Magento/Catalog/Block/Product/View/Options.php index 1f8041d115f89..ece517d033f87 100644 --- a/app/code/Magento/Catalog/Block/Product/View/Options.php +++ b/app/code/Magento/Catalog/Block/Product/View/Options.php @@ -20,6 +20,11 @@ */ class Options extends \Magento\Framework\View\Element\Template { + /** + * Option type percent + */ + protected static $typePercent = 'percent'; + /** * @var Product */ @@ -160,7 +165,8 @@ public function hasOptions() */ protected function _getPriceConfiguration($option) { - $optionPrice = $option->getPrice(true); + $option->getPriceType() == self::$typePercent ? $optionPrice = $option->getPrice(true) : + $optionPrice = $this->pricingHelper->currency($option->getPrice(true), false, false); $data = [ 'prices' => [ 'oldPrice' => [ From 5c073d9de2b11eb16548cc79f49ebf4b19f2ae71 Mon Sep 17 00:00:00 2001 From: Emipro <git@emiprotechnologies.com> Date: Sat, 9 Feb 2019 12:08:11 +0530 Subject: [PATCH 0710/1295] Update Code for Price Calculation --- .../Magento/Catalog/Block/Product/View/Options.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Product/View/Options.php b/app/code/Magento/Catalog/Block/Product/View/Options.php index ece517d033f87..10f73b6012f66 100644 --- a/app/code/Magento/Catalog/Block/Product/View/Options.php +++ b/app/code/Magento/Catalog/Block/Product/View/Options.php @@ -12,6 +12,7 @@ namespace Magento\Catalog\Block\Product\View; use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Option\Value; /** * @api @@ -20,11 +21,6 @@ */ class Options extends \Magento\Framework\View\Element\Template { - /** - * Option type percent - */ - protected static $typePercent = 'percent'; - /** * @var Product */ @@ -165,8 +161,10 @@ public function hasOptions() */ protected function _getPriceConfiguration($option) { - $option->getPriceType() == self::$typePercent ? $optionPrice = $option->getPrice(true) : - $optionPrice = $this->pricingHelper->currency($option->getPrice(true), false, false); + $optionPrice = $option->getPrice(true); + if($option->getPriceType() != Value::TYPE_PERCENT) { + $optionPrice = $this->pricingHelper->currency($optionPrice, false, false); + } $data = [ 'prices' => [ 'oldPrice' => [ From 040c20e30db0ba6d83675bfc9c9b1e47fbc48da5 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Tue, 12 Feb 2019 16:30:29 +0200 Subject: [PATCH 0711/1295] ENGCOM-4217: Static test fix. --- .../Magento/Catalog/Block/Product/View/Options.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Product/View/Options.php b/app/code/Magento/Catalog/Block/Product/View/Options.php index 10f73b6012f66..c457b20cd0904 100644 --- a/app/code/Magento/Catalog/Block/Product/View/Options.php +++ b/app/code/Magento/Catalog/Block/Product/View/Options.php @@ -4,17 +4,15 @@ * See COPYING.txt for license details. */ -/** - * Product options block - * - * @author Magento Core Team <core@magentocommerce.com> - */ namespace Magento\Catalog\Block\Product\View; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Option\Value; /** + * Product options block + * + * @author Magento Core Team <core@magentocommerce.com> * @api * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @since 100.0.2 @@ -122,6 +120,8 @@ public function setProduct(Product $product = null) } /** + * Get group of option. + * * @param string $type * @return string */ @@ -143,6 +143,8 @@ public function getOptions() } /** + * Check if block has options. + * * @return bool */ public function hasOptions() @@ -162,7 +164,7 @@ public function hasOptions() protected function _getPriceConfiguration($option) { $optionPrice = $option->getPrice(true); - if($option->getPriceType() != Value::TYPE_PERCENT) { + if ($option->getPriceType() !== Value::TYPE_PERCENT) { $optionPrice = $this->pricingHelper->currency($optionPrice, false, false); } $data = [ From 15ae23c01e8c6ed88c15a3802baa25276061a085 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Fri, 15 Feb 2019 14:25:41 +0200 Subject: [PATCH 0712/1295] MC-14897: "Street Address" label is absent and field doesn't have required icon --- .../Magento/Ui/view/base/web/templates/group/group.html | 9 +++++---- .../Magento/backend/web/css/source/forms/_fields.less | 1 - 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/templates/group/group.html b/app/code/Magento/Ui/view/base/web/templates/group/group.html index e30ac7a377542..9da9e69b772eb 100644 --- a/app/code/Magento/Ui/view/base/web/templates/group/group.html +++ b/app/code/Magento/Ui/view/base/web/templates/group/group.html @@ -8,10 +8,11 @@ visible="visible" css="_required: required" attr="'data-index': index"> - <legend class="admin__field-label" if="showLabel"> - <span translate="label" attr="'data-config-scope': $data.scopeLabel"/> - </legend> - + <div class="admin__field-label"> + <legend if="showLabel"> + <span translate="label" attr="'data-config-scope': $data.scopeLabel"/> + </legend> + </div> <div class="admin__field-control" css="$data.additionalClasses"> <each args="elems"> <if args="visible()" if="!$data.additionalForGroup"> 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 4eadca34d99aa..4018117277e72 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 @@ -207,7 +207,6 @@ &:before { .appearing__off(); - content: '.'; margin-left: -7px; overflow: hidden; } From b138428c2b64c1755bd6dcb8d1c4e6a8376e857d Mon Sep 17 00:00:00 2001 From: Suneet <suneet64@gmail.com> Date: Sun, 3 Feb 2019 14:06:17 +0530 Subject: [PATCH 0713/1295] Fixed issue if there are multiple skus in catalog rule condition combination. --- .../Magento/Rule/Model/Condition/Product/AbstractProduct.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php b/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php index c02bbd64e7ca3..8baa843f8ade8 100644 --- a/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php +++ b/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php @@ -520,6 +520,10 @@ public function loadArray($arr) ) ? $this->_localeFormat->getNumber( $arr['is_value_parsed'] ) : false; + } elseif (!empty($arr['operator']) && $arr['operator'] == '()') { + if (isset($arr['value'])) { + $arr['value'] = preg_replace('/\s*,\s*/', ',', $arr['value']); + } } return parent::loadArray($arr); From 1750a5883aedee5200d54eaf816bc23ceb40d10f Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Fri, 8 Feb 2019 15:58:56 +0200 Subject: [PATCH 0714/1295] ENGCOM-4149: Static test fix. --- .../Magento/Rule/Model/Condition/Product/AbstractProduct.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php b/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php index 8baa843f8ade8..e5d613f7c9558 100644 --- a/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php +++ b/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php @@ -95,8 +95,8 @@ abstract class AbstractProduct extends \Magento\Rule\Model\Condition\AbstractCon * @param \Magento\Catalog\Model\ResourceModel\Product $productResource * @param \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\Collection $attrSetCollection * @param \Magento\Framework\Locale\FormatInterface $localeFormat - * @param ProductCategoryList|null $categoryList * @param array $data + * @param ProductCategoryList|null $categoryList * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -710,6 +710,7 @@ protected function _getAttributeSetId($productId) /** * Correct '==' and '!=' operators + * * Categories can't be equal because product is included categories selected by administrator and in their parents * * @return string From 69cf6cebcd8ad8d51dc3d88fe89b6f3375bf311c Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Fri, 15 Feb 2019 14:29:58 +0100 Subject: [PATCH 0715/1295] Removed unnecessary line-break in DocBlock --- app/code/Magento/Customer/Model/Options.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Customer/Model/Options.php b/app/code/Magento/Customer/Model/Options.php index 71e70f8e14208..f8761b4888a32 100644 --- a/app/code/Magento/Customer/Model/Options.php +++ b/app/code/Magento/Customer/Model/Options.php @@ -84,7 +84,6 @@ protected function _prepareNamePrefixSuffixOptions($options, $isOptional = false /** * Unserialize and clear name prefix or suffix options - * * If field is optional, add an empty first option. * * @param string $options From 16715de4e59421d4dd1165717ff384b7c026691f Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Fri, 15 Feb 2019 16:08:26 +0200 Subject: [PATCH 0716/1295] MAGETWO-97242: Stabilize AdminAbleToShipPartiallyInvoicedItemsTest --- .../templates/order/creditmemo/create/items.phtml | 14 +++++++------- .../templates/order/invoice/create/items.phtml | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/items.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/items.phtml index dc7d8fbebd46a..b0b52b199978a 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/items.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/items.phtml @@ -148,23 +148,23 @@ var updateButtons = jQuery('.update-button,.update-totals-button'); var fields = jQuery('.qty-input,.order-subtotal-table input[type="text"]'); function enableButtons(buttons) { - buttons.removeClass('disabled').prop('disabled',false); + buttons.removeClass('disabled').prop('disabled', false); }; function disableButtons(buttons) { - buttons.addClass('disabled').prop('disabled',true); + buttons.addClass('disabled').prop('disabled', true); }; disableButtons(updateButtons); fields.on('change', checkButtonsRelation); -fields.each(function (i,elem) { +fields.each(function (i, elem) { elem.baseValue = elem.value; }); function checkButtonsRelation() { var hasChanges = false; - fields.each(function (i,elem) { + fields.each(function (i, elem) { if (elem.baseValue != elem.value) { hasChanges = true; } @@ -182,7 +182,7 @@ function checkButtonsRelation() { submitCreditMemo = function() { var creditMemoOffline = jQuery('#creditmemo_do_offline'); if (creditMemoOffline.length) { - creditMemoOffline.prop('value',0); + creditMemoOffline.prop('value', 0); } // Temporary solution will be replaced after refactoring order functionality jQuery('#edit_form').triggerHandler('save'); @@ -206,10 +206,10 @@ if (sendEmailCheckbox.length) { function bindSendEmail() { if (sendEmailCheckbox.prop('checked') == true) { - notifyCustomerCheckbox.prop('disabled',false); + notifyCustomerCheckbox.prop('disabled', false); } else { - notifyCustomerCheckbox.prop('disabled',true); + notifyCustomerCheckbox.prop('disabled', true); } } diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/invoice/create/items.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/invoice/create/items.phtml index 8efdca2fce8e2..0d873645bce86 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/invoice/create/items.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/invoice/create/items.phtml @@ -143,23 +143,23 @@ var enableSubmitButtons = <?= (int) !$block->getDisableSubmitButton() ?>; var fields = jQuery('.qty-input'); function enableButtons(buttons) { - buttons.removeClass('disabled').prop('disabled',false); + buttons.removeClass('disabled').prop('disabled', false); }; function disableButtons(buttons) { - buttons.addClass('disabled').prop('disabled',true); + buttons.addClass('disabled').prop('disabled', true); }; disableButtons(updateButtons); fields.on('keyup', checkButtonsRelation); -fields.each(function (i,elem) { +fields.each(function (i, elem) { elem.baseValue = elem.value; }); function checkButtonsRelation() { var hasChanges = false; - fields.each(function (i,elem) { + fields.each(function (i, elem) { if (elem.baseValue != elem.value) { hasChanges = true; } @@ -185,10 +185,10 @@ if (sendEmailCheckbox.length) { function bindSendEmail() { if (sendEmailCheckbox.prop('checked') == true) { - notifyCustomerCheckbox.prop('disabled',false); + notifyCustomerCheckbox.prop('disabled', false); } else { - notifyCustomerCheckbox.prop('disabled',true); + notifyCustomerCheckbox.prop('disabled', true); } } From 4d9e6c8f946594af042c9d8eec8e82ecc63aa307 Mon Sep 17 00:00:00 2001 From: hiren pandya <infinitehdp@gmail.com> Date: Sat, 13 Oct 2018 11:52:27 +0530 Subject: [PATCH 0717/1295] Special price date from issue resolve --- .../Controller/Adminhtml/Product/Initialization/Helper.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php index 7153f9fd0f3f9..17b08373a0efa 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php @@ -463,6 +463,7 @@ private function fillProductOptions(Product $product, array $productOptions) private function convertSpecialFromDateStringToObject($productData) { if (isset($productData['special_from_date']) && $productData['special_from_date'] != '') { + $productData['special_from_date'] = $this->dateFilter->filter($productData['special_from_date']); $productData['special_from_date'] = new \DateTime($productData['special_from_date']); } From 523567461e74dd4580fe8e4e59d71aeae8191042 Mon Sep 17 00:00:00 2001 From: hiren pandya <hiren.pandya@krishtechnolabs.com> Date: Sat, 13 Oct 2018 13:00:58 +0530 Subject: [PATCH 0718/1295] Revert "Special price date from issue resolve" This reverts commit 4e382cc735a883c9dcd8eaeeedc185d7faa06a7f. --- .../Controller/Adminhtml/Product/Initialization/Helper.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php index 17b08373a0efa..7153f9fd0f3f9 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php @@ -463,7 +463,6 @@ private function fillProductOptions(Product $product, array $productOptions) private function convertSpecialFromDateStringToObject($productData) { if (isset($productData['special_from_date']) && $productData['special_from_date'] != '') { - $productData['special_from_date'] = $this->dateFilter->filter($productData['special_from_date']); $productData['special_from_date'] = new \DateTime($productData['special_from_date']); } From ad5bf08df72a5115b2777ebf55b2fc8530c88d3e Mon Sep 17 00:00:00 2001 From: hiren pandya <hiren.pandya@krishtechnolabs.com> Date: Sat, 13 Oct 2018 13:03:07 +0530 Subject: [PATCH 0719/1295] Special price date from issue resolve --- .../Controller/Adminhtml/Product/Initialization/Helper.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php index 7153f9fd0f3f9..67eb10564333f 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php @@ -463,6 +463,7 @@ private function fillProductOptions(Product $product, array $productOptions) private function convertSpecialFromDateStringToObject($productData) { if (isset($productData['special_from_date']) && $productData['special_from_date'] != '') { + $productData['special_from_date'] = $this->dateFilter->filter($productData['special_from_date']); $productData['special_from_date'] = new \DateTime($productData['special_from_date']); } From f92ea567c8a380f9db40e03ea03ca7ab49e610fe Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Fri, 15 Feb 2019 17:33:57 +0200 Subject: [PATCH 0720/1295] MC-14895: Order status closed when paid Paypal Express payment method with Payment Action = Order --- .../Model/ResourceModel/Order/Handler/State.php | 7 +++++-- .../Model/ResourceModel/Order/Handler/StateTest.php | 12 ++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) 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 f18118447f95f..820ef71a7ea79 100644 --- a/app/code/Magento/Sales/Model/ResourceModel/Order/Handler/State.php +++ b/app/code/Magento/Sales/Model/ResourceModel/Order/Handler/State.php @@ -9,7 +9,7 @@ use Magento\Sales\Model\Order; /** - * Class to check order State. + * Class to check and adjust order state/status. */ class State { @@ -31,7 +31,10 @@ public function check(Order $order) } if (!$order->isCanceled() && !$order->canUnhold() && !$order->canInvoice()) { - if (in_array($currentState, [Order::STATE_PROCESSING, Order::STATE_COMPLETE]) && !$order->canCreditmemo()) { + if (in_array($currentState, [Order::STATE_PROCESSING, Order::STATE_COMPLETE]) + && !$order->canCreditmemo() + && !$order->canShip() + ) { $order->setState(Order::STATE_CLOSED) ->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_CLOSED)); } elseif ($currentState === Order::STATE_PROCESSING && !$order->canShip()) { diff --git a/app/code/Magento/Sales/Test/Unit/Model/ResourceModel/Order/Handler/StateTest.php b/app/code/Magento/Sales/Test/Unit/Model/ResourceModel/Order/Handler/StateTest.php index 48f4a282a2be2..65313c584454b 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/ResourceModel/Order/Handler/StateTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/ResourceModel/Order/Handler/StateTest.php @@ -118,13 +118,13 @@ public function stateCheckDataProvider(): array { return [ 'processing - !canCreditmemo!canShip -> closed' => - [false, 1, false, 0, Order::STATE_PROCESSING, Order::STATE_CLOSED], + [false, 1, false, 1, Order::STATE_PROCESSING, Order::STATE_CLOSED], 'complete - !canCreditmemo,!canShip -> closed' => - [false, 1, false, 0, Order::STATE_COMPLETE, Order::STATE_CLOSED], - 'processing - !canCreditmemo,canShip -> closed' => - [false, 1, true, 0, Order::STATE_PROCESSING, Order::STATE_CLOSED], - 'complete - !canCreditmemo,canShip -> closed' => - [false, 1, true, 0, Order::STATE_COMPLETE, Order::STATE_CLOSED], + [false, 1, false, 1, Order::STATE_COMPLETE, Order::STATE_CLOSED], + 'processing - !canCreditmemo,canShip -> processing' => + [false, 1, true, 2, Order::STATE_PROCESSING, Order::STATE_PROCESSING], + 'complete - !canCreditmemo,canShip -> complete' => + [false, 1, true, 1, Order::STATE_COMPLETE, Order::STATE_COMPLETE], 'processing - canCreditmemo,!canShip -> complete' => [true, 1, false, 1, Order::STATE_PROCESSING, Order::STATE_COMPLETE], 'complete - canCreditmemo,!canShip -> complete' => From 0d95f7eef9c182fa96325285a00e915a08b30676 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Fri, 15 Feb 2019 15:14:21 -0600 Subject: [PATCH 0721/1295] MAGETWO-96001: Update FedEx Shipping Dates behavior in Tracking Popup --- .../DataProviders/Tracking/ChangeTitle.php | 33 +++++++++++ .../Block/Tracking/PopupDeliveryDate.php | 55 +++++++++++++++++++ app/code/Magento/Fedex/etc/di.xml | 6 ++ .../Tracking/DeliveryDateTitle.php | 26 +++++++++ .../layout/shipping_tracking_popup.xml | 6 +- .../frontend/templates/tracking/details.phtml | 2 +- 6 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Fedex/Plugin/Block/DataProviders/Tracking/ChangeTitle.php create mode 100644 app/code/Magento/Fedex/Plugin/Block/Tracking/PopupDeliveryDate.php create mode 100644 app/code/Magento/Shipping/Block/DataProviders/Tracking/DeliveryDateTitle.php diff --git a/app/code/Magento/Fedex/Plugin/Block/DataProviders/Tracking/ChangeTitle.php b/app/code/Magento/Fedex/Plugin/Block/DataProviders/Tracking/ChangeTitle.php new file mode 100644 index 0000000000000..bb0d22bd76e6a --- /dev/null +++ b/app/code/Magento/Fedex/Plugin/Block/DataProviders/Tracking/ChangeTitle.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Fedex\Plugin\Block\DataProviders\Tracking; + +use Magento\Fedex\Model\Carrier; +use Magento\Shipping\Model\Tracking\Result\Status; +use Magento\Shipping\Block\DataProviders\Tracking\DeliveryDateTitle as Subject; + +/** + * Plugin to change delivery date title with FedEx customized value + */ +class ChangeTitle +{ + /** + * @param Subject $subject + * @param string $result + * @param Status $trackingStatus + * @return string + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterGetTitle(Subject $subject, string $result, Status $trackingStatus): string + { + if ($trackingStatus->getCarrier() === Carrier::CODE) { + $result = __('Expected Delivery:'); + } + return $result; + } +} diff --git a/app/code/Magento/Fedex/Plugin/Block/Tracking/PopupDeliveryDate.php b/app/code/Magento/Fedex/Plugin/Block/Tracking/PopupDeliveryDate.php new file mode 100644 index 0000000000000..942d21dfa5e47 --- /dev/null +++ b/app/code/Magento/Fedex/Plugin/Block/Tracking/PopupDeliveryDate.php @@ -0,0 +1,55 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Fedex\Plugin\Block\Tracking; + +use Magento\Shipping\Block\Tracking\Popup; +use Magento\Fedex\Model\Carrier; +use Magento\Shipping\Model\Tracking\Result\Status; + +/** + * Plugin to update delivery date value in case if Fedex used + */ +class PopupDeliveryDate +{ + /** + * Show only date for expected delivery in case if Fedex is a carrier + * + * @param Popup $subject + * @param string $result + * @param string $date + * @param string $time + * @return string + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterFormatDeliveryDateTime(Popup $subject, string $result, string $date, string $time): string + { + if ($this->getCarrier($subject) === Carrier::CODE) { + $result = $subject->formatDeliveryDate($date); + } + return $result; + } + + /** + * Retrieve carrier name from tracking info + * + * @param Popup $subject + * @return string + */ + private function getCarrier(Popup $subject): string + { + foreach ($subject->getTrackingInfo() as $trackingData) { + foreach ($trackingData as $trackingInfo) { + if ($trackingInfo instanceof Status) { + $carrier = $trackingInfo->getCarrier(); + return $carrier; + } + } + } + return ''; + } +} diff --git a/app/code/Magento/Fedex/etc/di.xml b/app/code/Magento/Fedex/etc/di.xml index f17f8f2afe663..c542b1f04d1eb 100644 --- a/app/code/Magento/Fedex/etc/di.xml +++ b/app/code/Magento/Fedex/etc/di.xml @@ -22,4 +22,10 @@ </argument> </arguments> </type> + <type name="Magento\Shipping\Block\DataProviders\Tracking\DeliveryDateTitle"> + <plugin name="update_delivery_date_title" type="Magento\Fedex\Plugin\Block\DataProviders\Tracking\ChangeTitle"/> + </type> + <type name="Magento\Shipping\Block\Tracking\Popup"> + <plugin name="update_delivery_date_value" type="Magento\Fedex\Plugin\Block\Tracking\PopupDeliveryDate"/> + </type> </config> diff --git a/app/code/Magento/Shipping/Block/DataProviders/Tracking/DeliveryDateTitle.php b/app/code/Magento/Shipping/Block/DataProviders/Tracking/DeliveryDateTitle.php new file mode 100644 index 0000000000000..e4c31629b9db7 --- /dev/null +++ b/app/code/Magento/Shipping/Block/DataProviders/Tracking/DeliveryDateTitle.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Shipping\Block\DataProviders\Tracking; + +use Magento\Framework\View\Element\Block\ArgumentInterface; +use Magento\Shipping\Model\Tracking\Result\Status; + +/** + * Extension point to provide ability to change tracking details titles + */ +class DeliveryDateTitle implements ArgumentInterface +{ + /** + * @param Status $trackingStatus + * @return string + */ + public function getTitle(Status $trackingStatus): string + { + return $trackingStatus->getCarrier() ? __('Delivered on:') : ''; + } +} diff --git a/app/code/Magento/Shipping/view/frontend/layout/shipping_tracking_popup.xml b/app/code/Magento/Shipping/view/frontend/layout/shipping_tracking_popup.xml index 1f5b0ae4630ad..67d03da2599bf 100644 --- a/app/code/Magento/Shipping/view/frontend/layout/shipping_tracking_popup.xml +++ b/app/code/Magento/Shipping/view/frontend/layout/shipping_tracking_popup.xml @@ -8,7 +8,11 @@ <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="empty" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceContainer name="content"> - <block class="Magento\Shipping\Block\Tracking\Popup" name="shipping.tracking.popup" template="Magento_Shipping::tracking/popup.phtml" cacheable="false" /> + <block class="Magento\Shipping\Block\Tracking\Popup" name="shipping.tracking.popup" template="Magento_Shipping::tracking/popup.phtml" cacheable="false"> + <arguments> + <argument name="delivery_date_title" xsi:type="object">Magento\Shipping\Block\DataProviders\Tracking\DeliveryDateTitle</argument> + </arguments> + </block> </referenceContainer> </body> </page> diff --git a/app/code/Magento/Shipping/view/frontend/templates/tracking/details.phtml b/app/code/Magento/Shipping/view/frontend/templates/tracking/details.phtml index 9253b47f82f5d..e8584d8f6ad51 100644 --- a/app/code/Magento/Shipping/view/frontend/templates/tracking/details.phtml +++ b/app/code/Magento/Shipping/view/frontend/templates/tracking/details.phtml @@ -77,7 +77,7 @@ $number = is_object($track) ? $track->getTracking() : $track['number']; <?php if ($track->getDeliverydate()): ?> <tr> - <th class="col label" scope="row"><?= $block->escapeHtml(__('Delivered on:')) ?></th> + <th class="col label" scope="row"><?= $block->escapeHtml($parentBlock->getDeliveryDateTitle()->getTitle($track)) ?></th> <td class="col value"> <?= /* @noEscape */ $parentBlock->formatDeliveryDateTime($track->getDeliverydate(), $track->getDeliverytime()) ?> </td> From 4b7b06b0b254753acba2cd7f3a017f1429252e44 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Fri, 15 Feb 2019 16:43:45 -0600 Subject: [PATCH 0722/1295] MAGETWO-96001: Update FedEx Shipping Dates behavior in Tracking Popup --- .../Plugin/Block/DataProviders/Tracking/ChangeTitle.php | 7 +++---- .../Block/DataProviders/Tracking/DeliveryDateTitle.php | 5 ++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Fedex/Plugin/Block/DataProviders/Tracking/ChangeTitle.php b/app/code/Magento/Fedex/Plugin/Block/DataProviders/Tracking/ChangeTitle.php index bb0d22bd76e6a..d148b91aa3e7a 100644 --- a/app/code/Magento/Fedex/Plugin/Block/DataProviders/Tracking/ChangeTitle.php +++ b/app/code/Magento/Fedex/Plugin/Block/DataProviders/Tracking/ChangeTitle.php @@ -3,7 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -declare(strict_types=1); namespace Magento\Fedex\Plugin\Block\DataProviders\Tracking; @@ -18,12 +17,12 @@ class ChangeTitle { /** * @param Subject $subject - * @param string $result + * @param \Magento\Framework\Phrase|string $result * @param Status $trackingStatus - * @return string + * @return \Magento\Framework\Phrase|string * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function afterGetTitle(Subject $subject, string $result, Status $trackingStatus): string + public function afterGetTitle(Subject $subject, $result, Status $trackingStatus): string { if ($trackingStatus->getCarrier() === Carrier::CODE) { $result = __('Expected Delivery:'); diff --git a/app/code/Magento/Shipping/Block/DataProviders/Tracking/DeliveryDateTitle.php b/app/code/Magento/Shipping/Block/DataProviders/Tracking/DeliveryDateTitle.php index e4c31629b9db7..dc66c4f0bd018 100644 --- a/app/code/Magento/Shipping/Block/DataProviders/Tracking/DeliveryDateTitle.php +++ b/app/code/Magento/Shipping/Block/DataProviders/Tracking/DeliveryDateTitle.php @@ -3,7 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -declare(strict_types=1); namespace Magento\Shipping\Block\DataProviders\Tracking; @@ -17,9 +16,9 @@ class DeliveryDateTitle implements ArgumentInterface { /** * @param Status $trackingStatus - * @return string + * @return \Magento\Framework\Phrase|string */ - public function getTitle(Status $trackingStatus): string + public function getTitle(Status $trackingStatus) { return $trackingStatus->getCarrier() ? __('Delivered on:') : ''; } From 864f7fbf5eb89033846da2166fa0177dbcc71e14 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Sat, 16 Feb 2019 01:00:17 +0200 Subject: [PATCH 0723/1295] MAGETWO-98182: [FT] [MFTF] StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest fails because of bad design --- .../Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml index d540a0000655f..244992cafa2a3 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml @@ -16,6 +16,7 @@ <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitingForInformationModal"/> <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmScopeSwitch"/> <waitForPageLoad stepKey="waitForScopeSwitched"/> + <scrollTo selector="{{AdminMainActionsSection.storeViewDropdown}}" stepKey="scrollToStoreSwitcher"/> <see userInput="{{scopeName}}" selector="{{AdminMainActionsSection.storeSwitcher}}" stepKey="seeNewScopeName"/> </actionGroup> From e84fbf2d2b19a2daf33c9974535007f3da7e746f Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Sat, 16 Feb 2019 06:34:56 +0200 Subject: [PATCH 0724/1295] MAGETWO-98182: [FT] [MFTF] StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest fails because of bad design --- .../Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml index 244992cafa2a3..1c56c3cc44220 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml @@ -16,7 +16,7 @@ <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitingForInformationModal"/> <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmScopeSwitch"/> <waitForPageLoad stepKey="waitForScopeSwitched"/> - <scrollTo selector="{{AdminMainActionsSection.storeViewDropdown}}" stepKey="scrollToStoreSwitcher"/> + <scrollToTopOfPage stepKey="scrollToStoreSwitcher"/> <see userInput="{{scopeName}}" selector="{{AdminMainActionsSection.storeSwitcher}}" stepKey="seeNewScopeName"/> </actionGroup> From b0208801bbf228a5bea108824207d4ff11cb0e32 Mon Sep 17 00:00:00 2001 From: Yannis Livasov <melaxon@users.noreply.github.com> Date: Mon, 31 Dec 2018 18:25:27 +0300 Subject: [PATCH 0725/1295] hardcoded table name hardcoded table name --- app/code/Magento/Directory/Model/ResourceModel/Currency.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Directory/Model/ResourceModel/Currency.php b/app/code/Magento/Directory/Model/ResourceModel/Currency.php index ffbcce11cb4f6..5339b0c9eb5bd 100644 --- a/app/code/Magento/Directory/Model/ResourceModel/Currency.php +++ b/app/code/Magento/Directory/Model/ResourceModel/Currency.php @@ -216,7 +216,7 @@ protected function _getRatesByCode($code, $toCurrencies = null) $connection = $this->getConnection(); $bind = [':currency_from' => $code]; $select = $connection->select()->from( - $this->getTable('directory_currency_rate'), + $this->_currencyRateTable, ['currency_to', 'rate'] )->where( 'currency_from = :currency_from' From 7ef43a1fbde1263c4aba103e3b390b9ffbb525c4 Mon Sep 17 00:00:00 2001 From: Aki Ojalehto <aki@ojalehto.eu> Date: Sun, 21 Jan 2018 21:49:31 +0200 Subject: [PATCH 0726/1295] Refactor getFrontName --- lib/internal/Magento/Framework/View/Context.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/internal/Magento/Framework/View/Context.php b/lib/internal/Magento/Framework/View/Context.php index 0c3932ffe4bd7..c3f1c3e691c84 100644 --- a/lib/internal/Magento/Framework/View/Context.php +++ b/lib/internal/Magento/Framework/View/Context.php @@ -332,15 +332,11 @@ public function getModuleName() } /** - * Retrieve the module name - * - * @return string - * - * @todo alias of getModuleName + * @see getModuleName */ public function getFrontName() { - return $this->getRequest()->getModuleName(); + return $this->getModuleName(); } /** From 1a05beb053d2f36f9df194834d655d68787629d4 Mon Sep 17 00:00:00 2001 From: amol 2jcommerce <amol@2jcommerce.in> Date: Mon, 18 Feb 2019 10:38:57 +0530 Subject: [PATCH 0727/1295] update branch 2.2-develop-PR-port-20519 --- .../Magento/luma/Magento_Bundle/web/css/source/_module.less | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/design/frontend/Magento/luma/Magento_Bundle/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Bundle/web/css/source/_module.less index 99c8aa1ad2bae..262e2d68d1ea3 100644 --- a/app/design/frontend/Magento/luma/Magento_Bundle/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Bundle/web/css/source/_module.less @@ -61,6 +61,10 @@ margin-top: 4px; } + input[type="checkbox"] { + margin-top: 2px; + } + .label { &:extend(.abs-add-clearfix all); display: block; From a12d7af12c33180657f038c007521c7cf55e07f6 Mon Sep 17 00:00:00 2001 From: amol 2jcommerce <amol@2jcommerce.in> Date: Mon, 18 Feb 2019 11:19:36 +0530 Subject: [PATCH 0728/1295] branch updated 2.2-develop-PR-port-20466 --- .../Magento_Checkout/web/css/source/module/_cart.less | 8 ++++++++ 1 file changed, 8 insertions(+) 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 67057967451d8..e764093668a07 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 @@ -422,6 +422,14 @@ &:extend(.abs-sidebar-totals-mobile all); } } + .order-items.table-wrapper { + .col.price, + .col.qty, + .col.subtotal, + .col.msrp { + text-align: left; + } + } } .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { From 494937ead0371dc842e59abaf4841bb96d32feae Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Mon, 18 Feb 2019 08:24:55 +0200 Subject: [PATCH 0729/1295] MC-14897: "Street Address" label is absent and field doesn't have required icon --- app/code/Magento/Ui/view/base/web/templates/group/group.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/templates/group/group.html b/app/code/Magento/Ui/view/base/web/templates/group/group.html index 9da9e69b772eb..6d6e61b805d62 100644 --- a/app/code/Magento/Ui/view/base/web/templates/group/group.html +++ b/app/code/Magento/Ui/view/base/web/templates/group/group.html @@ -8,8 +8,8 @@ visible="visible" css="_required: required" attr="'data-index': index"> - <div class="admin__field-label"> - <legend if="showLabel"> + <div if="showLabel" class="admin__field-label"> + <legend> <span translate="label" attr="'data-config-scope': $data.scopeLabel"/> </legend> </div> From 17d416a1d0fda6d3d2cc8730198d354a56ce4923 Mon Sep 17 00:00:00 2001 From: amol 2jcommerce <amol@2jcommerce.in> Date: Mon, 18 Feb 2019 12:31:48 +0530 Subject: [PATCH 0730/1295] updated branch fixes-customer-information-wishlist-configurable-product-alignment-2.2 --- .../Test/Block/Adminhtml/Product/Composite/Configure.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Block/Adminhtml/Product/Composite/Configure.xml b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Block/Adminhtml/Product/Composite/Configure.xml index a66753c2adf23..9614f0d1bf7b5 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Block/Adminhtml/Product/Composite/Configure.xml +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Block/Adminhtml/Product/Composite/Configure.xml @@ -9,7 +9,7 @@ <fields> <qty /> <attribute> - <selector>//div[@class="product-options"]//label[.="%s"]//following-sibling::*//select</selector> + <selector>//div[contains(@class, "product-options")]//div//label[.="%s"]//following-sibling::*//select</selector> <strategy>xpath</strategy> <input>select</input> </attribute> From be57683357f8a6819a575f051d0ea96bcdfc1548 Mon Sep 17 00:00:00 2001 From: Stjepan Udovicic <stjepan.udovicic@inchoo.net> Date: Mon, 18 Feb 2019 08:07:09 +0100 Subject: [PATCH 0731/1295] Backport ENGCOM-3616: Static test fix --- .../Indexer/Price/CustomOptionPriceModifier.php | 10 ++++++++++ .../Product/Indexer/Price/DefaultPrice.php | 14 ++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CustomOptionPriceModifier.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CustomOptionPriceModifier.php index 646cd0d4c1a4c..ce0b7a4111d2c 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CustomOptionPriceModifier.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/CustomOptionPriceModifier.php @@ -127,6 +127,8 @@ public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = } /** + * Check if custom options exist. + * * @param IndexTableStructure $priceTable * @return bool * @throws \Exception @@ -154,6 +156,8 @@ private function checkIfCustomOptionsExist(IndexTableStructure $priceTable): boo } /** + * Get connection. + * * @return \Magento\Framework\DB\Adapter\AdapterInterface */ private function getConnection() @@ -373,6 +377,8 @@ private function getSelectAggregated(string $sourceTable): Select } /** + * Get select for update. + * * @param string $sourceTable * @return \Magento\Framework\DB\Select */ @@ -402,6 +408,8 @@ private function getSelectForUpdate(string $sourceTable): Select } /** + * Get table name. + * * @param string $tableName * @return string */ @@ -411,6 +419,8 @@ private function getTable(string $tableName): string } /** + * Is price scope global. + * * @return bool */ private function isPriceGlobal(): bool 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 8fdeaf00b804f..aab4577de3770 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 @@ -10,6 +10,7 @@ /** * Default Product Type Price Indexer Resource model + * * For correctly work need define product type id * * @api @@ -208,6 +209,8 @@ public function reindexEntity($entityIds) } /** + * Reindex prices. + * * @param null|int|array $entityIds * @return \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice */ @@ -802,6 +805,8 @@ public function getIdxTable($table = null) } /** + * Check if product exists. + * * @return bool */ protected function hasEntity() @@ -823,6 +828,8 @@ protected function hasEntity() } /** + * Get total tier price expression. + * * @param \Zend_Db_Expr $priceExpression * @return \Zend_Db_Expr */ @@ -862,6 +869,13 @@ private function getTotalTierPriceExpression(\Zend_Db_Expr $priceExpression) ); } + /** + * Get tier price expression for table. + * + * @param string $tableAlias + * @param \Zend_Db_Expr $priceExpression + * @return \Zend_Db_Expr + */ private function getTierPriceExpressionForTable($tableAlias, \Zend_Db_Expr $priceExpression) { return $this->getConnection()->getCheckSql( From 9f1a322c239f940457b30ceadf137628651e04ad Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Mon, 18 Feb 2019 09:28:45 +0100 Subject: [PATCH 0732/1295] Changed indent to meet the standard --- .../web/css/source/_module.less | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/app/design/frontend/Magento/luma/Magento_Newsletter/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Newsletter/web/css/source/_module.less index aab895392e6dd..d7ee1319c9a43 100644 --- a/app/design/frontend/Magento/luma/Magento_Newsletter/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Newsletter/web/css/source/_module.less @@ -87,18 +87,18 @@ // _____________________________________________ .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { - .block{ - &.newsletter { - input { - font-size: 12px; - padding-left: 30px; - } - - .field { - .control:before{ - font-size: 13px; + .block { + &.newsletter { + input { + font-size: 12px; + padding-left: 30px; + } + + .field { + .control:before { + font-size: 13px; + } + } } - } } - } } From 139833bcd2d776d2af8ba043e01844355834d327 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Mon, 18 Feb 2019 10:44:33 +0200 Subject: [PATCH 0733/1295] MAGETWO-98044: Customer Group Regression issues from MAGETWO-96886 --- .../Magento/Backend/Model/Session/Quote.php | 3 +- .../Test/Unit/Model/Session/QuoteTest.php | 5 +- .../Adminhtml/Order/Create/Form/Account.php | 27 +----- .../Order/Create/Form/AccountTest.php | 83 ++++++++++++++----- 4 files changed, 70 insertions(+), 48 deletions(-) diff --git a/app/code/Magento/Backend/Model/Session/Quote.php b/app/code/Magento/Backend/Model/Session/Quote.php index 11edaa26f443f..e32f1bc57596e 100644 --- a/app/code/Magento/Backend/Model/Session/Quote.php +++ b/app/code/Magento/Backend/Model/Session/Quote.php @@ -149,7 +149,8 @@ public function getQuote() $this->_quote = $this->quoteFactory->create(); if ($this->getStoreId()) { if (!$this->getQuoteId()) { - $this->_quote->setCustomerGroupId($this->groupManagement->getDefaultGroup()->getId()); + $customerGroupId = $this->groupManagement->getDefaultGroup($this->getStoreId())->getId(); + $this->_quote->setCustomerGroupId($customerGroupId); $this->_quote->setIsActive(false); $this->_quote->setStoreId($this->getStoreId()); diff --git a/app/code/Magento/Backend/Test/Unit/Model/Session/QuoteTest.php b/app/code/Magento/Backend/Test/Unit/Model/Session/QuoteTest.php index 869d4ba3f45b1..d159225089afc 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/Session/QuoteTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/Session/QuoteTest.php @@ -267,7 +267,10 @@ public function testGetQuoteWithoutQuoteId() $cartInterfaceMock->expects($this->atLeastOnce())->method('getId')->willReturn($quoteId); $defaultGroup = $this->getMockBuilder(\Magento\Customer\Api\Data\GroupInterface::class)->getMock(); $defaultGroup->expects($this->any())->method('getId')->will($this->returnValue($customerGroupId)); - $this->groupManagementMock->expects($this->any())->method('getDefaultGroup')->willReturn($defaultGroup); + $this->groupManagementMock + ->method('getDefaultGroup') + ->with($storeId) + ->willReturn($defaultGroup); $dataCustomerMock = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) ->disableOriginalConstructor() diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php index dc93e8ba8417c..11f3faa9d042e 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Account.php @@ -11,7 +11,6 @@ use Magento\Framework\Api\ExtensibleDataObjectConverter; use Magento\Framework\Data\Form\Element\AbstractElement; use Magento\Framework\Pricing\PriceCurrencyInterface; -use Magento\Store\Model\ScopeInterface; /** * Create order account form @@ -135,8 +134,9 @@ protected function _prepareForm() $this->_addAttributesToForm($attributes, $fieldset); $this->_form->addFieldNameSuffix('order[account]'); - $storeId = (int)$this->_sessionQuote->getStoreId(); - $this->_form->setValues($this->extractValuesFromAttributes($attributes, $storeId)); + + $formValues = $this->extractValuesFromAttributes($attributes); + $this->_form->setValues($formValues); return $this; } @@ -188,10 +188,9 @@ public function getFormValues() * Extract the form values from attributes. * * @param array $attributes - * @param int $storeId * @return array */ - private function extractValuesFromAttributes(array $attributes, int $storeId): array + private function extractValuesFromAttributes(array $attributes): array { $formValues = $this->getFormValues(); foreach ($attributes as $code => $attribute) { @@ -199,26 +198,8 @@ private function extractValuesFromAttributes(array $attributes, int $storeId): a if (isset($defaultValue) && !isset($formValues[$code])) { $formValues[$code] = $defaultValue; } - if ($code === 'group_id' && empty($formValues[$code])) { - $formValues[$code] = $this->getDefaultCustomerGroup($storeId); - } } return $formValues; } - - /** - * Gets default customer group. - * - * @param int $storeId - * @return string|null - */ - private function getDefaultCustomerGroup(int $storeId) - { - return $this->_scopeConfig->getValue( - 'customer/create_account/default_group', - ScopeInterface::SCOPE_STORE, - $storeId - ); - } } diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php index ce3f3a3e1fc8e..cc051c11aabf8 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php @@ -5,7 +5,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - declare(strict_types=1); namespace Magento\Sales\Block\Adminhtml\Order\Create\Form; @@ -23,7 +22,10 @@ use PHPUnit\Framework\MockObject\MockObject; /** + * Class for test Account + * * @magentoAppArea adminhtml + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class AccountTest extends \PHPUnit\Framework\TestCase { @@ -43,23 +45,33 @@ class AccountTest extends \PHPUnit\Framework\TestCase private $session; /** - * @magentoDataFixture Magento/Sales/_files/quote.php + * @inheritdoc */ protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); - $quote = $this->objectManager->create(Quote::class)->load(1); + parent::setUp(); + } + + /** + * Test for get form with existing customer + * + * @magentoDataFixture Magento/Customer/_files/customer.php + */ + public function testGetFormWithCustomer() + { + $customerGroup = 2; + $quote = $this->objectManager->create(Quote::class); $this->session = $this->getMockBuilder(SessionQuote::class) ->disableOriginalConstructor() - ->setMethods(['getCustomerId', 'getStore', 'getStoreId', 'getQuote', 'getQuoteId']) + ->setMethods(['getCustomerId','getQuote']) ->getMock(); - $this->session->method('getCustomerId') - ->willReturn(1); $this->session->method('getQuote') ->willReturn($quote); - $this->session->method('getQuoteId') - ->willReturn($quote->getId()); + $this->session->method('getCustomerId') + ->willReturn(1); + /** @var LayoutInterface $layout */ $layout = $this->objectManager->get(LayoutInterface::class); $this->accountBlock = $layout->createBlock( @@ -67,18 +79,20 @@ protected function setUp() 'address_block' . rand(), ['sessionQuote' => $this->session] ); - parent::setUp(); - } - /** - * @magentoDataFixture Magento/Customer/_files/customer.php - */ - public function testGetForm() - { + $fixtureCustomerId = 1; + /** @var \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository */ + $customerRepository = $this->objectManager->get(\Magento\Customer\Api\CustomerRepositoryInterface::class); + /** @var \Magento\Customer\Api\Data\CustomerInterface $customer */ + $customer = $customerRepository->getById($fixtureCustomerId); + $customer->setGroupId($customerGroup); + $customerRepository->save($customer); + $expectedFields = ['group_id', 'email']; $form = $this->accountBlock->getForm(); self::assertEquals(1, $form->getElements()->count(), "Form has invalid number of fieldsets"); $fieldset = $form->getElements()[0]; + $content = $form->toHtml(); self::assertEquals(count($expectedFields), $fieldset->getElements()->count()); @@ -88,22 +102,45 @@ public function testGetForm() sprintf('Unexpected field "%s" in form.', $element->getId()) ); } + + self::assertContains( + '<option value="'.$customerGroup.'" selected="selected">Wholesale</option>', + $content, + 'The Customer Group specified for the chosen customer should be selected.' + ); + + self::assertContains( + 'value="'.$customer->getEmail().'"', + $content, + 'The Customer Email specified for the chosen customer should be input ' + ); } /** * Tests a case when user defined custom attribute has default value. * - * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoConfigFixture current_store customer/create_account/default_group 3 + * @magentoDataFixture Magento/Store/_files/core_second_third_fixturestore.php + * @magentoConfigFixture current_store customer/create_account/default_group 2 + * @magentoConfigFixture secondstore_store customer/create_account/default_group 3 */ public function testGetFormWithUserDefinedAttribute() { + /** @var \Magento\Store\Model\StoreManagerInterface $storeManager */ + $storeManager = Bootstrap::getObjectManager()->get(\Magento\Store\Model\StoreManagerInterface::class); + $secondStore = $storeManager->getStore('secondstore'); + + $quoteSession = $this->objectManager->get(SessionQuote::class); + $quoteSession->setStoreId($secondStore->getId()); + $formFactory = $this->getFormFactoryMock(); $this->objectManager->addSharedInstance($formFactory, FormFactory::class); /** @var LayoutInterface $layout */ $layout = $this->objectManager->get(LayoutInterface::class); - $accountBlock = $layout->createBlock(Account::class, 'address_block' . rand()); + $accountBlock = $layout->createBlock( + Account::class, + 'address_block' . rand() + ); $form = $accountBlock->getForm(); $form->setUseContainer(true); @@ -116,7 +153,7 @@ public function testGetFormWithUserDefinedAttribute() ); self::assertContains( - '<option value="3" selected="selected">Customer Group 1</option>', + '<option value="3" selected="selected">Retailer</option>', $content, 'The Customer Group specified for the chosen store should be selected.' ); @@ -162,13 +199,13 @@ private function createCustomerGroupAttribute(): AttributeMetadataInterface { /** @var Option $option1 */ $option1 = $this->objectManager->create(Option::class); - $option1->setValue(3); - $option1->setLabel('Customer Group 1'); + $option1->setValue(2); + $option1->setLabel('Wholesale'); /** @var Option $option2 */ $option2 = $this->objectManager->create(Option::class); - $option2->setValue(4); - $option2->setLabel('Customer Group 2'); + $option2->setValue(3); + $option2->setLabel('Retailer'); /** @var AttributeMetadataInterfaceFactory $attributeMetadataFactory */ $attributeMetadataFactory = $this->objectManager->create(AttributeMetadataInterfaceFactory::class); From 67b3c9cab030bbaf1f0fbb1d5d0d6c228453edaf Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Mon, 18 Feb 2019 12:02:51 +0000 Subject: [PATCH 0734/1295] magento/magento2#21273: Fixed functional tests --- .../Controller/Adminhtml/Product/Initialization/Helper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php index 67eb10564333f..5e9579290535c 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php @@ -463,7 +463,7 @@ private function fillProductOptions(Product $product, array $productOptions) private function convertSpecialFromDateStringToObject($productData) { if (isset($productData['special_from_date']) && $productData['special_from_date'] != '') { - $productData['special_from_date'] = $this->dateFilter->filter($productData['special_from_date']); + $productData['special_from_date'] = $this->getDateTimeFilter()->filter($productData['special_from_date']); $productData['special_from_date'] = new \DateTime($productData['special_from_date']); } From 7b8ec8085d29f84fadac67a4e17753a66f4e6ddf Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Mon, 18 Feb 2019 14:09:19 +0200 Subject: [PATCH 0735/1295] Fix functional tests. --- app/code/Magento/Ui/view/base/web/js/form/element/country.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/country.js b/app/code/Magento/Ui/view/base/web/js/form/element/country.js index f64a80bf535ec..c75301018e190 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/country.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/country.js @@ -49,7 +49,7 @@ define([ if (!this.value()) { defaultCountry = _.filter(result, function (item) { - return item['is_default'] && item['is_default'].includes(value); + return item['is_default'] && _.contains(item['is_default'], value); }); if (defaultCountry.length) { From ac3787a77391f075081061c9f8d88f63939b49c1 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Mon, 18 Feb 2019 15:04:23 +0000 Subject: [PATCH 0736/1295] magento-engcom/magento2ce#2594: Fixed unit test --- .../Product/Initialization/Helper.php | 9 ++++++++- .../Product/Initialization/HelperTest.php | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php index 5e9579290535c..f11d16755ef0d 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php @@ -19,6 +19,8 @@ use Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper\AttributeFilter; /** + * Product helper + * * @api * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @since 100.0.2 @@ -104,7 +106,7 @@ class Helper * @param \Magento\Backend\Helper\Js $jsHelper * @param \Magento\Framework\Stdlib\DateTime\Filter\Date $dateFilter * @param CustomOptionFactory|null $customOptionFactory - * @param ProductLinkFactory |null $productLinkFactory + * @param ProductLinkFactory|null $productLinkFactory * @param ProductRepositoryInterface|null $productRepository * @param LinkTypeProvider|null $linkTypeProvider * @param AttributeFilter|null $attributeFilter @@ -365,6 +367,8 @@ private function overwriteValue($optionId, $option, $overwriteOptions) } /** + * Get link resolver instance + * * @return LinkResolver * @deprecated 101.0.0 */ @@ -377,6 +381,8 @@ private function getLinkResolver() } /** + * Get DateTimeFilter instance + * * @return \Magento\Framework\Stdlib\DateTime\Filter\DateTime * @deprecated 101.0.0 */ @@ -391,6 +397,7 @@ private function getDateTimeFilter() /** * Remove ids of non selected websites from $websiteIds array and return filtered data + * * $websiteIds parameter expects array with website ids as keys and 1 (selected) or 0 (non selected) as values * Only one id (default website ID) will be set to $websiteIds array when the single store mode is turned on * diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php index aed87f918ebb8..c889c58e3df3a 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php @@ -95,6 +95,14 @@ class HelperTest extends \PHPUnit\Framework\TestCase */ protected $attributeFilterMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $dateTimeFilterMock; + + /** + * @inheritdoc + */ protected function setUp() { $this->objectManager = new ObjectManager($this); @@ -167,6 +175,11 @@ protected function setUp() $resolverProperty = $helperReflection->getProperty('linkResolver'); $resolverProperty->setAccessible(true); $resolverProperty->setValue($this->helper, $this->linkResolverMock); + + $this->dateTimeFilterMock = $this->createMock(\Magento\Framework\Stdlib\DateTime\Filter\DateTime::class); + $dateTimeFilterProperty = $helperReflection->getProperty('dateTimeFilter'); + $dateTimeFilterProperty->setAccessible(true); + $dateTimeFilterProperty->setValue($this->helper, $this->dateTimeFilterMock); } /** @@ -208,6 +221,12 @@ public function testInitialize( if (!empty($tierPrice)) { $productData = array_merge($productData, ['tier_price' => $tierPrice]); } + + $this->dateTimeFilterMock->expects($this->once()) + ->method('filter') + ->with($specialFromDate) + ->willReturn($specialFromDate); + $attributeNonDate = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) ->disableOriginalConstructor() ->getMock(); From b16221b1b76dd18bb1fbebbe4b26ed394e4e3657 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Mon, 18 Feb 2019 20:54:51 +0200 Subject: [PATCH 0737/1295] MAGETWO-94190: [Magento Cloud] Error when saving Bundle product --- .../Bundle/Model/Product/SaveHandler.php | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Bundle/Model/Product/SaveHandler.php b/app/code/Magento/Bundle/Model/Product/SaveHandler.php index 09fce9222e63d..dbd07f188f90b 100644 --- a/app/code/Magento/Bundle/Model/Product/SaveHandler.php +++ b/app/code/Magento/Bundle/Model/Product/SaveHandler.php @@ -7,6 +7,7 @@ use Magento\Bundle\Api\ProductOptionRepositoryInterface as OptionRepository; use Magento\Bundle\Api\ProductLinkManagementInterface; +use Magento\Catalog\Api\Data\ProductInterface; use Magento\Framework\App\ObjectManager; use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\EntityManager\Operation\ExtensionInterface; @@ -49,12 +50,11 @@ public function __construct( } /** + * Perform action on Bundle product relation/extension attribute. + * * @param object $entity * @param array $arguments - * @return \Magento\Catalog\Api\Data\ProductInterface|object - * @throws \Magento\Framework\Exception\NoSuchEntityException - * @throws \Magento\Framework\Exception\InputException - * @throws \Magento\Framework\Exception\CouldNotSaveException + * @return ProductInterface|object * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function execute($entity, $arguments = []) @@ -78,7 +78,7 @@ public function execute($entity, $arguments = []) $options = $bundleProductOptions ?: []; if (!$entity->getCopyFromView()) { - $this->processRemovedOptions($entity->getSku(), $existingOptionsIds, $optionIds); + $this->processRemovedOptions($entity, $existingOptionsIds, $optionIds); $newOptionsIds = array_diff($optionIds, $existingOptionsIds); $this->saveOptions($entity, $options, $newOptionsIds); @@ -92,10 +92,10 @@ public function execute($entity, $arguments = []) } /** + * Remove option product links. + * * @param string $entitySku * @param \Magento\Bundle\Api\Data\OptionInterface $option - * @throws \Magento\Framework\Exception\NoSuchEntityException - * @throws \Magento\Framework\Exception\InputException * @return void */ protected function removeOptionLinks($entitySku, $option) @@ -152,21 +152,21 @@ private function getOptionIds(array $options) } /** - * Removes old options that no longer exists + * Removes old options that no longer exists. * - * @param string $entitySku + * @param ProductInterface $entity * @param array $existingOptionsIds * @param array $optionIds - * @throws \Magento\Framework\Exception\NoSuchEntityException - * @throws \Magento\Framework\Exception\InputException - * @throws \Magento\Framework\Exception\CouldNotSaveException * @return void */ - private function processRemovedOptions($entitySku, array $existingOptionsIds, array $optionIds) + private function processRemovedOptions(ProductInterface $entity, array $existingOptionsIds, array $optionIds) { + $metadata = $this->metadataPool->getMetadata(ProductInterface::class); + $parentId = $entity->getData($metadata->getLinkField()); foreach (array_diff($existingOptionsIds, $optionIds) as $optionId) { - $option = $this->optionRepository->get($entitySku, $optionId); - $this->removeOptionLinks($entitySku, $option); + $option = $this->optionRepository->get($entity->getSku(), $optionId); + $option->setParentId($parentId); + $this->removeOptionLinks($entity->getSku(), $option); $this->optionRepository->delete($option); } } From 9dccad1672b750908268b2577104e94a8857886a Mon Sep 17 00:00:00 2001 From: mageprince <mail.mageprince@gmail.com> Date: Tue, 19 Feb 2019 11:03:37 +0530 Subject: [PATCH 0738/1295] Minimum Qty Allowed in Shopping Cart not working on related product --- app/code/Magento/Checkout/Model/Cart.php | 40 +++++++++++++++--------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Checkout/Model/Cart.php b/app/code/Magento/Checkout/Model/Cart.php index c0ba9616754bb..0eb59fc70d92f 100644 --- a/app/code/Magento/Checkout/Model/Cart.php +++ b/app/code/Magento/Checkout/Model/Cart.php @@ -15,6 +15,7 @@ * Shopping cart model * * @api + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @deprecated 100.1.0 Use \Magento\Quote\Model\Quote instead */ @@ -354,22 +355,10 @@ protected function _getProductRequest($requestInfo) public function addProduct($productInfo, $requestInfo = null) { $product = $this->_getProduct($productInfo); - $request = $this->_getProductRequest($requestInfo); $productId = $product->getId(); if ($productId) { - $stockItem = $this->stockRegistry->getStockItem($productId, $product->getStore()->getWebsiteId()); - $minimumQty = $stockItem->getMinSaleQty(); - //If product quantity is not specified in request and there is set minimal qty for it - if ($minimumQty - && $minimumQty > 0 - && !$request->getQty() - ) { - $request->setQty($minimumQty); - } - } - - if ($productId) { + $request = $this->getQtyRequest($product, $requestInfo); try { $result = $this->getQuote()->addProduct($product, $request); } catch (\Magento\Framework\Exception\LocalizedException $e) { @@ -425,8 +414,9 @@ public function addProductsByIds($productIds) } $product = $this->_getProduct($productId); if ($product->getId() && $product->isVisibleInCatalog()) { + $request = $this->getQtyRequest($product); try { - $this->getQuote()->addProduct($product); + $this->getQuote()->addProduct($product, $request); } catch (\Exception $e) { $allAdded = false; } @@ -747,4 +737,26 @@ private function getRequestInfoFilter() } return $this->requestInfoFilter; } + + /** + * Get request quantity + * + * @param Product $product + * @param \Magento\Framework\DataObject|int|array $request + * @return int|DataObject + */ + private function getQtyRequest($product, $request = 0) + { + $request = $this->_getProductRequest($request); + $stockItem = $this->stockRegistry->getStockItem($product->getId(), $product->getStore()->getWebsiteId()); + $minimumQty = $stockItem->getMinSaleQty(); + //If product quantity is not specified in request and there is set minimal qty for it + if ($minimumQty + && $minimumQty > 0 + && !$request->getQty() + ) { + $request->setQty($minimumQty); + } + return $request; + } } From 1e4055d7e734be1cf69312a40147a68c5183e1bc Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Tue, 19 Feb 2019 08:28:26 +0200 Subject: [PATCH 0739/1295] Fixing the state of dropdown icon --- .../templates/grid/filters/elements/ui-select-optgroup.html | 2 +- .../base/web/templates/grid/filters/elements/ui-select.html | 3 ++- .../backend/web/css/source/actions/_actions-multiselect.less | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/filters/elements/ui-select-optgroup.html b/app/code/Magento/Ui/view/base/web/templates/grid/filters/elements/ui-select-optgroup.html index 56244422a6b43..1ad0e7505ec9d 100644 --- a/app/code/Magento/Ui/view/base/web/templates/grid/filters/elements/ui-select-optgroup.html +++ b/app/code/Magento/Ui/view/base/web/templates/grid/filters/elements/ui-select-optgroup.html @@ -19,7 +19,7 @@ css: { _selected: $parent.root.isSelected(option.value), _hover: $parent.root.isHovered(option, $element), - _expended: $parent.root.getLevelVisibility($data), + _expended: $parent.root.getLevelVisibility($data) || $data.visible, _unclickable: $parent.root.isLabelDecoration($data), _last: $parent.root.addLastElement($data), '_with-checkbox': $parent.root.showCheckbox diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/filters/elements/ui-select.html b/app/code/Magento/Ui/view/base/web/templates/grid/filters/elements/ui-select.html index a96a4163caf7e..0411063c11512 100644 --- a/app/code/Magento/Ui/view/base/web/templates/grid/filters/elements/ui-select.html +++ b/app/code/Magento/Ui/view/base/web/templates/grid/filters/elements/ui-select.html @@ -125,12 +125,13 @@ css: { _selected: $parent.isSelected(option.value), _hover: $parent.isHovered(option, $element), - _expended: $parent.getLevelVisibility($data), + _expended: $parent.getLevelVisibility($data) && $parent.showLevels($data), _unclickable: $parent.isLabelDecoration($data), _last: $parent.addLastElement($data), '_with-checkbox': $parent.showCheckbox }, click: function(data, event){ + $parent.showLevels($data); $parent.toggleOptionSelected($data, $index(), event); }, clickBubble: false diff --git a/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-multiselect.less b/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-multiselect.less index 213dd3d0f8e25..fc56ec1b6570a 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-multiselect.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-multiselect.less @@ -330,7 +330,7 @@ border-top: @action-multiselect-tree-lines; height: 1px; top: @action-multiselect-menu-item__padding + @action-multiselect-tree-arrow__size/2; - width: @action-multiselect-tree-menu-item__margin-left + @action-multiselect-menu-item__padding; + width: @action-multiselect-tree-menu-item__margin-left; } // Vertical dotted line From 8da71964e8c0c937458f6499863821ad10438f75 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Tue, 19 Feb 2019 10:42:11 +0200 Subject: [PATCH 0740/1295] MAGETWO-97332: Configurable product is displayed as In Stock in product grid when it's set to out of stock --- ...uantityAndStockStatusFieldToCollection.php | 32 ++++++++ .../CatalogInventory/etc/adminhtml/di.xml | 1 + .../Product/QuantityAndStockStatusTest.php | 77 +++++++++++++++++++ ...nd_stock_status_attribute_used_in_grid.php | 18 +++++ ...status_attribute_used_in_grid_rollback.php | 18 +++++ 5 files changed, 146 insertions(+) create mode 100644 app/code/Magento/CatalogInventory/Ui/DataProvider/Product/AddQuantityAndStockStatusFieldToCollection.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/QuantityAndStockStatusTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/quantity_and_stock_status_attribute_used_in_grid.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/quantity_and_stock_status_attribute_used_in_grid_rollback.php diff --git a/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/AddQuantityAndStockStatusFieldToCollection.php b/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/AddQuantityAndStockStatusFieldToCollection.php new file mode 100644 index 0000000000000..d66a783c6720d --- /dev/null +++ b/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/AddQuantityAndStockStatusFieldToCollection.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogInventory\Ui\DataProvider\Product; + +use Magento\Framework\Data\Collection; +use Magento\Ui\DataProvider\AddFieldToCollectionInterface; + +/** + * Add quantity_and_stock_status field to collection + */ +class AddQuantityAndStockStatusFieldToCollection implements AddFieldToCollectionInterface +{ + /** + * @inheritdoc + */ + public function addField(Collection $collection, $field, $alias = null) + { + $collection->joinField( + 'quantity_and_stock_status', + 'cataloginventory_stock_item', + 'is_in_stock', + 'product_id=entity_id', + '{{table}}.stock_id=1', + 'left' + ); + } +} diff --git a/app/code/Magento/CatalogInventory/etc/adminhtml/di.xml b/app/code/Magento/CatalogInventory/etc/adminhtml/di.xml index 803a6dae492a0..601f7ef61973b 100644 --- a/app/code/Magento/CatalogInventory/etc/adminhtml/di.xml +++ b/app/code/Magento/CatalogInventory/etc/adminhtml/di.xml @@ -23,6 +23,7 @@ <arguments> <argument name="addFieldStrategies" xsi:type="array"> <item name="qty" xsi:type="object">Magento\CatalogInventory\Ui\DataProvider\Product\AddQuantityFieldToCollection</item> + <item name="quantity_and_stock_status" xsi:type="object">Magento\CatalogInventory\Ui\DataProvider\Product\AddQuantityAndStockStatusFieldToCollection</item> </argument> <argument name="addFilterStrategies" xsi:type="array"> <item name="qty" xsi:type="object">Magento\CatalogInventory\Ui\DataProvider\Product\AddQuantityFilterToCollection</item> diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/QuantityAndStockStatusTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/QuantityAndStockStatusTest.php new file mode 100644 index 0000000000000..00fc5d3a46ec4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/QuantityAndStockStatusTest.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Ui\DataProvider\Product; + +use Magento\CatalogInventory\Model\Stock\StockItemRepository; +use Magento\CatalogInventory\Ui\DataProvider\Product\AddQuantityAndStockStatusFieldToCollection; +use PHPUnit\Framework\TestCase; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\CatalogInventory\Api\StockRegistryInterface; + +/** + * Quantity and stock status test + */ +class QuantityAndStockStatusTest extends TestCase +{ + /** + * @var string + */ + private static $quantityAndStockStatus = 'quantity_and_stock_status'; + + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + } + + /** + * Test product stock status in the products grid column + * + * @magentoDataFixture Magento/Catalog/_files/quantity_and_stock_status_attribute_used_in_grid.php + * @magentoDataFixture Magento/Catalog/_files/products.php + */ + public function testProductStockStatus() + { + /** @var StockItemRepository $stockItemRepository */ + $stockItemRepository = $this->objectManager->create(StockItemRepository::class); + + /** @var StockRegistryInterface $stockRegistry */ + $stockRegistry = $this->objectManager->create(StockRegistryInterface::class); + + $stockItem = $stockRegistry->getStockItemBySku('simple'); + $stockItem->setIsInStock(false); + $stockItemRepository->save($stockItem); + $savedStockStatus = (int)$stockItem->getIsInStock(); + + $dataProvider = $this->objectManager->create( + ProductDataProvider::class, + [ + 'name' => 'product_listing_data_source', + 'primaryFieldName' => 'entity_id', + 'requestFieldName' => 'id', + 'addFieldStrategies' => [ + 'quantity_and_stock_status' => + $this->objectManager->get(AddQuantityAndStockStatusFieldToCollection::class) + ] + ] + ); + + $dataProvider->addField(self::$quantityAndStockStatus); + $data = $dataProvider->getData(); + $dataProviderStockStatus = $data['items'][0][self::$quantityAndStockStatus]; + + $this->assertEquals($dataProviderStockStatus, $savedStockStatus); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/quantity_and_stock_status_attribute_used_in_grid.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/quantity_and_stock_status_attribute_used_in_grid.php new file mode 100644 index 0000000000000..1870eaba566d8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/quantity_and_stock_status_attribute_used_in_grid.php @@ -0,0 +1,18 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$eavSetupFactory = $objectManager->create(\Magento\Eav\Setup\EavSetupFactory::class); +/** @var \Magento\Eav\Setup\EavSetup $eavSetup */ +$eavSetup = $eavSetupFactory->create(); +$eavSetup->updateAttribute( + \Magento\Catalog\Model\Product::ENTITY, + 'quantity_and_stock_status', + [ + 'is_used_in_grid' => 1, + ] +); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/quantity_and_stock_status_attribute_used_in_grid_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/quantity_and_stock_status_attribute_used_in_grid_rollback.php new file mode 100644 index 0000000000000..fba12f19fdca8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/quantity_and_stock_status_attribute_used_in_grid_rollback.php @@ -0,0 +1,18 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$eavSetupFactory = $objectManager->create(\Magento\Eav\Setup\EavSetupFactory::class); +/** @var \Magento\Eav\Setup\EavSetup $eavSetup */ +$eavSetup = $eavSetupFactory->create(); +$eavSetup->updateAttribute( + \Magento\Catalog\Model\Product::ENTITY, + 'quantity_and_stock_status', + [ + 'is_used_in_grid' => 0, + ] +); From 9746716226a11b4f19a6e1351c4428df2dcc3742 Mon Sep 17 00:00:00 2001 From: Nazarn96 <nazarn96@gmail.com> Date: Tue, 19 Feb 2019 11:38:45 +0200 Subject: [PATCH 0741/1295] Backport-issue-195196 --- .../Test/Mftf/Test/StorefrontGuestCheckoutTest.xml | 2 +- app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml | 1 + app/code/Magento/Quote/Model/QuoteManagement.php | 8 ++++++++ .../Magento/Quote/Test/Unit/Model/QuoteManagementTest.php | 2 +- .../testsuite/Magento/Quote/Model/QuoteTest.php | 3 +++ 5 files changed, 14 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml index dff957457da95..3d72582d83082 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml @@ -69,7 +69,7 @@ <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnSearch"/> <click selector="{{OrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> <see selector="{{OrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="seeAdminOrderStatus"/> - <see selector="{{OrderDetailsInformationSection.accountInformation}}" userInput="Guest" stepKey="seeAdminOrderGuest"/> + <see selector="{{OrderDetailsInformationSection.accountInformation}}" userInput="{{CustomerEntityOne.fullname}}" stepKey="seeAdminOrderGuest"/> <see selector="{{OrderDetailsInformationSection.accountInformation}}" userInput="{{CustomerEntityOne.email}}" stepKey="seeAdminOrderEmail"/> <see selector="{{OrderDetailsInformationSection.billingAddress}}" userInput="{{CustomerAddressSimple.street[0]}}" stepKey="seeAdminOrderBillingAddress"/> <see selector="{{OrderDetailsInformationSection.shippingAddress}}" userInput="{{CustomerAddressSimple.street[0]}}" stepKey="seeAdminOrderShippingAddress"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml index 74da84c01b861..6c7fdb9134685 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml @@ -21,6 +21,7 @@ <data key="firstname">John</data> <data key="lastname">Doe</data> <data key="middlename">S</data> + <data key="fullname">John Doe</data>s <data key="password">pwdTest123!</data> <data key="prefix">Mr</data> <data key="suffix">Sr</data> diff --git a/app/code/Magento/Quote/Model/QuoteManagement.php b/app/code/Magento/Quote/Model/QuoteManagement.php index e2ee8bbad01b9..bf1caa2434638 100644 --- a/app/code/Magento/Quote/Model/QuoteManagement.php +++ b/app/code/Magento/Quote/Model/QuoteManagement.php @@ -25,6 +25,7 @@ /** * Class QuoteManagement * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.TooManyFields) */ @@ -355,6 +356,13 @@ public function placeOrder($cartId, PaymentInterface $paymentMethod = null) if ($quote->getCheckoutMethod() === self::METHOD_GUEST) { $quote->setCustomerId(null); $quote->setCustomerEmail($quote->getBillingAddress()->getEmail()); + if ($quote->getCustomerFirstname() === null && $quote->getCustomerLastname() === null) { + $quote->setCustomerFirstname($quote->getBillingAddress()->getFirstname()); + $quote->setCustomerLastname($quote->getBillingAddress()->getLastname()); + if ($quote->getBillingAddress()->getMiddlename() === null) { + $quote->setCustomerMiddlename($quote->getBillingAddress()->getMiddlename()); + } + } $quote->setCustomerIsGuest(true); $quote->setCustomerGroupId(\Magento\Customer\Api\Data\GroupInterface::NOT_LOGGED_IN_ID); } diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php index 145a18fb34ca3..cdfd0df8f9927 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php @@ -642,7 +642,7 @@ public function testPlaceOrderIfCustomerIsGuest() $addressMock = $this->createPartialMock(\Magento\Quote\Model\Quote\Address::class, ['getEmail']); $addressMock->expects($this->once())->method('getEmail')->willReturn($email); - $this->quoteMock->expects($this->once())->method('getBillingAddress')->with()->willReturn($addressMock); + $this->quoteMock->expects($this->any())->method('getBillingAddress')->with()->willReturn($addressMock); $this->quoteMock->expects($this->once())->method('setCustomerIsGuest')->with(true)->willReturnSelf(); $this->quoteMock->expects($this->once()) diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php index 802358c2c83c7..3a516befc37ff 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php @@ -131,6 +131,9 @@ public function testUpdateCustomerData() $this->assertContains($item, $actual); } $this->assertEquals('test@example.com', $quote->getCustomerEmail()); + $this->assertEquals('Joe', $quote->getCustomerFirstname()); + $this->assertEquals('Dou', $quote->getCustomerLastname()); + $this->assertEquals('Ivan', $quote->getCustomerMiddlename()); } /** From ea6fb2b439b4c3f5528fd526a140e07aeb4e2a6a Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Tue, 19 Feb 2019 12:38:03 +0200 Subject: [PATCH 0742/1295] MC-14939: Uneven displayed fields in Advanced Pricing --- app/code/Magento/Ui/view/base/web/templates/form/field.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ed84e158819a2..d71fdb1801058 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,7 +8,7 @@ visible="visible" css="$data.additionalClasses" attr="'data-index': index"> - <div class="admin__field-label"> + <div class="admin__field-label" if="$data.label" visible="$data.labelVisible"> <label if="$data.label" visible="$data.labelVisible" attr="for: uid"> <span translate="label" attr="'data-config-scope': $data.scopeLabel" /> </label> From 640409541ea1a84498817e8e1e289a1182997961 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Tue, 19 Feb 2019 13:11:05 +0200 Subject: [PATCH 0743/1295] MC-14939: Uneven displayed fields in Advanced Pricing --- .../Magento/backend/web/css/source/forms/_fields.less | 5 ++++- 1 file changed, 4 insertions(+), 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 4eadca34d99aa..decfaa805654e 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 @@ -679,10 +679,13 @@ margin: 0; opacity: 1; position: static; - text-align: left; } } + .admin__field-label { + text-align: left; + } + &:nth-child(n + 2) { &:not(.admin__field-option):not(.admin__field-group-show-label):not(.admin__field-date) { > .admin__field-label[class] { From 5d42fa8d04ba2fe7ad45ad5a2a6e79f6d0cf0ebe Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Tue, 19 Feb 2019 13:27:13 +0200 Subject: [PATCH 0744/1295] Fix functional test. --- .../Mftf/Section/AdminOrderFormConfigureProductSection.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormConfigureProductSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormConfigureProductSection.xml index 83d417f6f8555..efffa48103b93 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormConfigureProductSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormConfigureProductSection.xml @@ -9,8 +9,8 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderFormConfigureProductSection"> - <element name="optionSelect" type="select" selector="//div[@class='product-options']/div/div/select[../../label[text() = '{{option}}']]" parameterized="true"/> + <element name="optionSelect" type="select" selector="//div[contains(@class, 'product-options')]//div//label[.='{{option}}']//following-sibling::*//select" parameterized="true"/> <element name="quantity" type="input" selector="#product_composite_configure_input_qty"/> <element name="ok" type="button" selector=".modal-header .page-actions button[data-role='action']" timeout="30"/> </section> -</sections> \ No newline at end of file +</sections> From ae764ff45d7318112ad473cb359af8f2bd2dcc03 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Tue, 19 Feb 2019 14:29:58 +0200 Subject: [PATCH 0745/1295] MC-14894: The *Recently Viewed Products* widget not displayed when more one product it was viewed --- .../Model/Product/ProductFrontendAction/Synchronizer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/ProductFrontendAction/Synchronizer.php b/app/code/Magento/Catalog/Model/Product/ProductFrontendAction/Synchronizer.php index 3c8d49cbdd744..5d2490e01dc3a 100644 --- a/app/code/Magento/Catalog/Model/Product/ProductFrontendAction/Synchronizer.php +++ b/app/code/Magento/Catalog/Model/Product/ProductFrontendAction/Synchronizer.php @@ -138,8 +138,8 @@ private function getProductIdsByActions(array $actions) $productIds = []; foreach ($actions as $action) { - if (isset($action['product_id']) && is_int($action['product_id'])) { - $productIds[] = $action['product_id']; + if (isset($action['product_id']) && (int)$action['product_id']) { + $productIds[] = (int)$action['product_id']; } } From e9f6a560a49d19cea76e23d14f1a86372be16207 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Tue, 19 Feb 2019 16:35:42 +0200 Subject: [PATCH 0746/1295] MC-14985: Custom options fields have double required icons --- .../web/css/source/_module.less | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module.less index ffbbaeb084162..20342442048fd 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module.less @@ -29,6 +29,35 @@ &:extend(.abs-control-qty all); } } + + .admin__field { + &.required, + &._required { + & > .admin__field-label { + &:after { + color: @validation__color; + content: '*'; + display: inline-block; + font-size: @font-size__l; + font-weight: @font-weight__heavier; + line-height: 1; + margin-left: 10px; + margin-top: .2rem; + position: absolute; + z-index: 1; + } + & .price-notice { + &:after { + display: none; + } + + & span:after { + display: none + } + } + } + } + } } // From 7a2b275c22324e9b2684698167372dc10a196dbe Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Tue, 19 Feb 2019 16:59:11 +0200 Subject: [PATCH 0747/1295] MAGETWO-95904: Header Minicart, Shopping cart page and Checkout page show incorrect product name --- .../Magento/Checkout/Block/Cart/Sidebar.php | 5 +- .../Magento/Checkout/CustomerData/Cart.php | 3 +- .../StorefrontMiniCartActionGroup.xml | 2 +- .../CheckoutShippingGuestInfoSection.xml | 4 +- ...tOnCheckoutPageDifferentStoreViewsTest.xml | 87 +++++++++++++++++++ .../Test/Unit/Block/Cart/SidebarTest.php | 14 ++- .../Test/Unit/CustomerData/CartTest.php | 13 ++- .../view/frontend/web/js/view/minicart.js | 7 +- .../Block/Adminhtml/Edit/Tab/View/Cart.php | 2 +- app/code/Magento/Quote/Model/Quote.php | 6 +- .../Quote/Plugin/UpdateQuoteItemStore.php | 73 ++++++++++++++++ app/code/Magento/Quote/etc/frontend/di.xml | 3 + 12 files changed, 204 insertions(+), 15 deletions(-) create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml create mode 100644 app/code/Magento/Quote/Plugin/UpdateQuoteItemStore.php diff --git a/app/code/Magento/Checkout/Block/Cart/Sidebar.php b/app/code/Magento/Checkout/Block/Cart/Sidebar.php index 5c237eecf0a9f..7453d3c62e862 100644 --- a/app/code/Magento/Checkout/Block/Cart/Sidebar.php +++ b/app/code/Magento/Checkout/Block/Cart/Sidebar.php @@ -67,7 +67,7 @@ public function __construct( } /** - * Returns minicart config + * Returns minicart config. * * @return array */ @@ -82,7 +82,8 @@ public function getConfig() 'baseUrl' => $this->getBaseUrl(), 'minicartMaxItemsVisible' => $this->getMiniCartMaxItemsCount(), 'websiteId' => $this->_storeManager->getStore()->getWebsiteId(), - 'maxItemsToDisplay' => $this->getMaxItemsToDisplay() + 'maxItemsToDisplay' => $this->getMaxItemsToDisplay(), + 'storeId' => $this->_storeManager->getStore()->getId(), ]; } diff --git a/app/code/Magento/Checkout/CustomerData/Cart.php b/app/code/Magento/Checkout/CustomerData/Cart.php index 01e91d75c02d9..9154e9c99478e 100644 --- a/app/code/Magento/Checkout/CustomerData/Cart.php +++ b/app/code/Magento/Checkout/CustomerData/Cart.php @@ -98,7 +98,8 @@ public function getSectionData() 'items' => $this->getRecentItems(), 'extra_actions' => $this->layout->createBlock(\Magento\Catalog\Block\ShortcutButtons::class)->toHtml(), 'isGuestCheckoutAllowed' => $this->isGuestCheckoutAllowed(), - 'website_id' => $this->getQuote()->getStore()->getWebsiteId() + 'website_id' => $this->getQuote()->getStore()->getWebsiteId(), + 'storeId' => $this->getQuote()->getStore()->getStoreId(), ]; } diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontMiniCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontMiniCartActionGroup.xml index 7a5c5e1d15872..3390dc588b69b 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontMiniCartActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontMiniCartActionGroup.xml @@ -15,7 +15,7 @@ </actionGroup> <actionGroup name="assertOneProductNameInMiniCart"> <arguments> - <argument name="productName"/> + <argument name="productName" type="string"/> </arguments> <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForViewAndEditCartVisible"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingGuestInfoSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingGuestInfoSection.xml index 8e043f85a0b95..0a243d54cedef 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingGuestInfoSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingGuestInfoSection.xml @@ -7,7 +7,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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CheckoutShippingGuestInfoSection"> <element name="email" type="input" selector="#customer-email"/> <element name="firstName" type="input" selector="input[name=firstname]"/> @@ -17,5 +17,7 @@ <element name="region" type="select" selector="select[name=region_id]"/> <element name="postcode" type="input" selector="input[name=postcode]"/> <element name="telephone" type="input" selector="input[name=telephone]"/> + <element name="itemInCart" type="button" selector="div.items-in-cart div.title"/> + <element name="productName" type="text" selector="div.items-in-cart strong.product-item-name"/> </section> </sections> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml new file mode 100644 index 0000000000000..a810aa7107ff1 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml @@ -0,0 +1,87 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest"> + <annotations> + <title value="Checking Product name with custom store views"/> + <description value="Checking Product name in Minicart and on Checkout page with custom store views"/> + <stories value="Verify product name with custom store view"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14944"/> + <features value="Checkout"/> + <group value="checkout"/> + </annotations> + <before> + <!--Login as Admin--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--Create a product--> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!-- Create store view --> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"> + <argument name="customStore" value="customStore"/> + </actionGroup> + </before> + <after> + <!--Delete product--> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <!--Delete store view--> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"> + <argument name="customStore" value="customStore"/> + </actionGroup> + <!--Logout from admin--> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Go to created product page--> + <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="goToEditPage"/> + + <!--Switch to second store view and change the product name--> + <actionGroup ref="SwitchToTheNewStoreView" stepKey="switchToCustomStoreView"> + <argument name="storeViewName" value="customStore"/> + </actionGroup> + <click selector="{{AdminProductFormSection.productNameUseDefault}}" stepKey="uncheckUseDefault"/> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="$$createProduct.name$$-new" stepKey="fillProductName"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> + + <!--Add product to cart--> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="amOnProductPage"/> + <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> + <argument name="productName" value="$createProduct.name$$"/> + </actionGroup> + + <!--Check simple product in minicart--> + <actionGroup ref="assertOneProductNameInMiniCart" stepKey="assertProductNameInMiniCart1"> + <argument name="productName" value="$$createProduct.name$$"/> + </actionGroup> + + <!--Switch to second store view--> + <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchStoreView"> + <argument name="storeView" value="customStore"/> + </actionGroup> + + <!--Check simple product in minicart--> + <actionGroup ref="assertOneProductNameInMiniCart" stepKey="assertProductNameInMiniCart2"> + <argument name="productName" value="$$createProduct.name$$-new"/> + </actionGroup> + + <!--Go to Shopping Cart--> + <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToCheckoutFromMinicart"/> + <seeElement selector="{{CheckoutCartProductSection.productLinkByName($$createProduct.name$$-new)}}" stepKey="assertProductName"/> + + <!--Proceed to checkout--> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutPage"/> + <conditionalClick selector="{{CheckoutShippingGuestInfoSection.itemInCart}}" dependentSelector="{{CheckoutShippingGuestInfoSection.itemInCart}}" visible="true" stepKey="clickItemsInCart"/> + <see selector="{{CheckoutShippingGuestInfoSection.productName}}" userInput="$$createProduct.name$$-new" stepKey="seeProductNameAtCheckout"/> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Unit/Block/Cart/SidebarTest.php b/app/code/Magento/Checkout/Test/Unit/Block/Cart/SidebarTest.php index 88751b899d7c9..015d8ccbe928f 100644 --- a/app/code/Magento/Checkout/Test/Unit/Block/Cart/SidebarTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Block/Cart/SidebarTest.php @@ -6,6 +6,8 @@ namespace Magento\Checkout\Test\Unit\Block\Cart; /** + * Unit tests for Magento\Checkout\Block\Cart\Sidebar. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class SidebarTest extends \PHPUnit\Framework\TestCase @@ -123,6 +125,11 @@ public function testGetTotalsHtml() $this->assertEquals($totalsHtml, $this->model->getTotalsHtml()); } + /** + * Unit test for getConfig method. + * + * @return void + */ public function testGetConfig() { $websiteId = 100; @@ -144,14 +151,15 @@ public function testGetConfig() 'baseUrl' => $baseUrl, 'minicartMaxItemsVisible' => 3, 'websiteId' => 100, - 'maxItemsToDisplay' => 8 + 'maxItemsToDisplay' => 8, + 'storeId' => null, ]; $valueMap = [ ['checkout/cart', [], $shoppingCartUrl], ['checkout', [], $checkoutUrl], ['checkout/sidebar/updateItemQty', ['_secure' => false], $updateItemQtyUrl], - ['checkout/sidebar/removeItem', ['_secure' => false], $removeItemUrl] + ['checkout/sidebar/removeItem', ['_secure' => false], $removeItemUrl], ]; $this->requestMock->expects($this->any()) @@ -161,7 +169,7 @@ public function testGetConfig() $this->urlBuilderMock->expects($this->exactly(4)) ->method('getUrl') ->willReturnMap($valueMap); - $this->storeManagerMock->expects($this->exactly(2))->method('getStore')->willReturn($storeMock); + $this->storeManagerMock->expects($this->atLeastOnce())->method('getStore')->willReturn($storeMock); $storeMock->expects($this->once())->method('getBaseUrl')->willReturn($baseUrl); $this->imageHelper->expects($this->once())->method('getFrame')->willReturn(false); diff --git a/app/code/Magento/Checkout/Test/Unit/CustomerData/CartTest.php b/app/code/Magento/Checkout/Test/Unit/CustomerData/CartTest.php index 75e181cbabd08..9f718f00b4b9d 100644 --- a/app/code/Magento/Checkout/Test/Unit/CustomerData/CartTest.php +++ b/app/code/Magento/Checkout/Test/Unit/CustomerData/CartTest.php @@ -7,6 +7,8 @@ namespace Magento\Checkout\Test\Unit\CustomerData; /** + * Unit tests for Magento\Checkout\CustomerData\Cart. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CartTest extends \PHPUnit\Framework\TestCase @@ -78,6 +80,11 @@ public function testIsGuestCheckoutAllowed() $this->assertTrue($this->model->isGuestCheckoutAllowed()); } + /** + * Unit test for getSectionData method. + * + * @return void + */ public function testGetSectionData() { $summaryQty = 100; @@ -113,7 +120,7 @@ public function testGetSectionData() $storeMock = $this->createPartialMock(\Magento\Store\Model\System\Store::class, ['getWebsiteId']); $storeMock->expects($this->once())->method('getWebsiteId')->willReturn($websiteId); - $quoteMock->expects($this->once())->method('getStore')->willReturn($storeMock); + $quoteMock->expects($this->atLeastOnce())->method('getStore')->willReturn($storeMock); $productMock = $this->createPartialMock( \Magento\Catalog\Model\Product::class, @@ -162,6 +169,7 @@ public function testGetSectionData() 'isGuestCheckoutAllowed' => 1, 'website_id' => $websiteId, 'subtotalAmount' => 200, + 'storeId' => null, ]; $this->assertEquals($expectedResult, $this->model->getSectionData()); } @@ -199,7 +207,7 @@ public function testGetSectionDataWithCompositeProduct() $storeMock = $this->createPartialMock(\Magento\Store\Model\System\Store::class, ['getWebsiteId']); $storeMock->expects($this->once())->method('getWebsiteId')->willReturn($websiteId); - $quoteMock->expects($this->once())->method('getStore')->willReturn($storeMock); + $quoteMock->expects($this->atLeastOnce())->method('getStore')->willReturn($storeMock); $this->checkoutCartMock->expects($this->once())->method('getSummaryQty')->willReturn($summaryQty); $this->checkoutHelperMock->expects($this->once()) @@ -265,6 +273,7 @@ public function testGetSectionDataWithCompositeProduct() 'isGuestCheckoutAllowed' => 1, 'website_id' => $websiteId, 'subtotalAmount' => 200, + 'storeId' => null, ]; $this->assertEquals($expectedResult, $this->model->getSectionData()); } diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js b/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js index a2f8c8c56ff33..5e29fa209a641 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js @@ -81,6 +81,7 @@ define([ maxItemsToDisplay: window.checkout.maxItemsToDisplay, cart: {}, + // jscs:disable requireCamelCaseOrUpperCaseIdentifiers /** * @override */ @@ -101,12 +102,16 @@ define([ self.isLoading(true); }); - if (cartData()['website_id'] !== window.checkout.websiteId) { + if (cartData().website_id !== window.checkout.websiteId || + cartData().store_id !== window.checkout.storeId + ) { customerData.reload(['cart'], false); } return this._super(); }, + //jscs:enable requireCamelCaseOrUpperCaseIdentifiers + isLoading: ko.observable(false), initSidebar: initSidebar, diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/View/Cart.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/View/Cart.php index 3f2c7cda7608d..d0973d3baf383 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/View/Cart.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/View/Cart.php @@ -94,7 +94,7 @@ protected function _prepareCollection() $quote = $this->getQuote(); if ($quote) { - $collection = $quote->getItemsCollection(false); + $collection = $quote->getItemsCollection(true); } else { $collection = $this->_dataCollectionFactory->create(); } diff --git a/app/code/Magento/Quote/Model/Quote.php b/app/code/Magento/Quote/Model/Quote.php index a3f5d1aaa6a6a..6ea70e63fdbf6 100644 --- a/app/code/Magento/Quote/Model/Quote.php +++ b/app/code/Magento/Quote/Model/Quote.php @@ -1354,7 +1354,7 @@ public function addShippingAddress(\Magento\Quote\Api\Data\AddressInterface $add } /** - * Retrieve quote items collection + * Retrieve quote items collection. * * @param bool $useCache * @return \Magento\Eav\Model\Entity\Collection\AbstractCollection @@ -1362,10 +1362,10 @@ public function addShippingAddress(\Magento\Quote\Api\Data\AddressInterface $add */ public function getItemsCollection($useCache = true) { - if ($this->hasItemsCollection()) { + if ($this->hasItemsCollection() && $useCache) { return $this->getData('items_collection'); } - if (null === $this->_items) { + if (null === $this->_items || !$useCache) { $this->_items = $this->_quoteItemCollectionFactory->create(); $this->extensionAttributesJoinProcessor->process($this->_items); $this->_items->setQuote($this); diff --git a/app/code/Magento/Quote/Plugin/UpdateQuoteItemStore.php b/app/code/Magento/Quote/Plugin/UpdateQuoteItemStore.php new file mode 100644 index 0000000000000..4d3e2dff70f9b --- /dev/null +++ b/app/code/Magento/Quote/Plugin/UpdateQuoteItemStore.php @@ -0,0 +1,73 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Quote\Plugin; + +use Magento\Checkout\Model\Session; +use Magento\Quote\Model\QuoteRepository; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreSwitcherInterface; + +/** + * Updates quote items store id. + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) + */ +class UpdateQuoteItemStore +{ + /** + * @var QuoteRepository + */ + private $quoteRepository; + + /** + * @var Session + */ + private $checkoutSession; + + /** + * @param QuoteRepository $quoteRepository + * @param Session $checkoutSession + */ + public function __construct( + QuoteRepository $quoteRepository, + Session $checkoutSession + ) { + $this->quoteRepository = $quoteRepository; + $this->checkoutSession = $checkoutSession; + } + + /** + * Update store id in active quote after store view switching. + * + * @param StoreSwitcherInterface $subject + * @param string $result + * @param StoreInterface $fromStore store where we came from + * @param StoreInterface $targetStore store where to go to + * @param string $redirectUrl original url requested for redirect after switching + * @return string url to be redirected after switching + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterSwitch( + StoreSwitcherInterface $subject, + string $result, + StoreInterface $fromStore, + StoreInterface $targetStore, + string $redirectUrl + ): string { + $quote = $this->checkoutSession->getQuote(); + if ($quote->getIsActive()) { + $quote->setStoreId( + $targetStore->getId() + ); + $quote->getItemsCollection(false); + $this->quoteRepository->save($quote); + } + + return $result; + } +} diff --git a/app/code/Magento/Quote/etc/frontend/di.xml b/app/code/Magento/Quote/etc/frontend/di.xml index 25acd6763ba56..91f4cfbf60aba 100644 --- a/app/code/Magento/Quote/etc/frontend/di.xml +++ b/app/code/Magento/Quote/etc/frontend/di.xml @@ -12,6 +12,9 @@ <argument name="checkoutSession" xsi:type="object">Magento\Checkout\Model\Session\Proxy</argument> </arguments> </type> + <type name="Magento\Store\Model\StoreSwitcherInterface"> + <plugin name="update_quote_item_store_after_switch_store_view" type="Magento\Quote\Plugin\UpdateQuoteItemStore"/> + </type> <type name="Magento\Store\Api\StoreCookieManagerInterface"> <plugin name="update_quote_store_after_switch_store_view" type="Magento\Quote\Plugin\UpdateQuoteStore"/> </type> From b2bc1c4710c7818a583572132307819f2360b254 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Tue, 19 Feb 2019 17:02:12 +0200 Subject: [PATCH 0748/1295] MC-14945: Order details view is not displayed when order created with Signifyd --- app/code/Magento/Sales/Model/Order/Status/History.php | 2 +- .../Test/Unit/Model/Order/Status/HistoryTest.php | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Model/Order/Status/History.php b/app/code/Magento/Sales/Model/Order/Status/History.php index 37aef2a5a29aa..d563f9f15b94a 100644 --- a/app/code/Magento/Sales/Model/Order/Status/History.php +++ b/app/code/Magento/Sales/Model/Order/Status/History.php @@ -142,7 +142,7 @@ public function getOrder() */ public function getStatusLabel() { - if ($this->getOrder()) { + if ($this->getOrder() && $this->getStatus() !== null) { return $this->getOrder()->getConfig()->getStatusLabel($this->getStatus()); } return null; diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Status/HistoryTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Status/HistoryTest.php index e7187219d56b6..866fef378a1e9 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Status/HistoryTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Status/HistoryTest.php @@ -83,6 +83,17 @@ public function testGetStatusLabel() $this->assertEquals($status, $this->model->getStatusLabel()); } + /** + * @return void + */ + public function testGetStatusLabelWithNullStatus() + { + $this->model->setOrder($this->order); + $this->model->setStatus(null); + + $this->assertNull($this->model->getStatusLabel()); + } + public function testGetStoreFromStoreManager() { $resultStore = 1; From 47aee60795ad6bd41f32d64adeb1b1b4f076fa15 Mon Sep 17 00:00:00 2001 From: Volodymyr Vygovskyi <vovsky@atwix.com> Date: Sat, 1 Dec 2018 13:31:49 +0200 Subject: [PATCH 0749/1295] ISSUE-5021 fixed guest checkout for custom shipping carrier with underscores (cherry picked from commit 2360efd5e6854488c5e23a985249a5ab68221acf) --- .../Checkout/Model/GuestPaymentInformationManagement.php | 5 ++--- .../Unit/Model/GuestPaymentInformationManagementTest.php | 7 ++++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php b/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php index d1894c98e7bce..935531683f885 100644 --- a/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php +++ b/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php @@ -189,9 +189,8 @@ private function limitShippingCarrier(Quote $quote) { $shippingAddress = $quote->getShippingAddress(); if ($shippingAddress && $shippingAddress->getShippingMethod()) { - $shippingDataArray = explode('_', $shippingAddress->getShippingMethod()); - $shippingCarrier = array_shift($shippingDataArray); - $shippingAddress->setLimitCarrier($shippingCarrier); + $shippingRate = $shippingAddress->getShippingRateByCode($shippingAddress->getShippingMethod()); + $shippingAddress->setLimitCarrier($shippingRate->getCarrier()); } } } diff --git a/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php b/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php index 2d313d2f50052..7a731c1c07039 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php @@ -280,9 +280,11 @@ private function getMockForAssignBillingAddress($cartId, $billingAddressMock) $billingAddressId = 1; $quote = $this->createMock(Quote::class); $quoteBillingAddress = $this->createMock(Address::class); + $shippingRate = $this->createPartialMock(\Magento\Quote\Model\Quote\Address\Rate::class, []); + $shippingRate->setCarrier('flatrate'); $quoteShippingAddress = $this->createPartialMock( Address::class, - ['setLimitCarrier', 'getShippingMethod'] + ['setLimitCarrier', 'getShippingMethod', 'getShippingRateByCode'] ); $this->cartRepositoryMock->method('getActive') ->with($cartId) @@ -302,6 +304,9 @@ private function getMockForAssignBillingAddress($cartId, $billingAddressMock) $quote->expects($this->once()) ->method('setBillingAddress') ->with($billingAddressMock); + $quoteShippingAddress->expects($this->any()) + ->method('getShippingRateByCode') + ->willReturn($shippingRate); $quote->expects($this->once()) ->method('setDataChanges') ->willReturnSelf(); From 8e19a732fd33619dac7c3390d9695739eac117cf Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Mon, 18 Feb 2019 11:03:27 +0200 Subject: [PATCH 0750/1295] Static test fix. (cherry picked from commit 1238e4d5ca209b1344bd68ed479ce0c8d19efb7c) --- .../Model/GuestPaymentInformationManagement.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php b/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php index 935531683f885..507411a19f965 100644 --- a/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php +++ b/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php @@ -13,6 +13,8 @@ use Magento\Quote\Model\Quote; /** + * Guest payment information management model. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class GuestPaymentInformationManagement implements \Magento\Checkout\Api\GuestPaymentInformationManagementInterface @@ -65,7 +67,7 @@ class GuestPaymentInformationManagement implements \Magento\Checkout\Api\GuestPa * @param \Magento\Checkout\Api\PaymentInformationManagementInterface $paymentInformationManagement * @param \Magento\Quote\Model\QuoteIdMaskFactory $quoteIdMaskFactory * @param CartRepositoryInterface $cartRepository - * @param ResourceConnection|null + * @param ResourceConnection $connectionPool * @codeCoverageIgnore */ public function __construct( @@ -87,7 +89,7 @@ public function __construct( } /** - * {@inheritDoc} + * @inheritdoc */ public function savePaymentInformationAndPlaceOrder( $cartId, @@ -128,7 +130,7 @@ public function savePaymentInformationAndPlaceOrder( } /** - * {@inheritDoc} + * @inheritdoc */ public function savePaymentInformation( $cartId, @@ -155,7 +157,7 @@ public function savePaymentInformation( } /** - * {@inheritDoc} + * @inheritdoc */ public function getPaymentInformation($cartId) { From 0c5f4a16d48e0e510db3cdd111e7b426bf43c510 Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets <vzaets@magento.com> Date: Tue, 19 Feb 2019 15:27:47 -0600 Subject: [PATCH 0751/1295] Add ignore tag for static test --- .../web/css/source/module/order/_order-account.less | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_order-account.less b/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_order-account.less index f66e94940c55d..7958378be2c5b 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_order-account.less +++ b/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_order-account.less @@ -35,10 +35,11 @@ .field-email { margin-left: -30px; } - + //@codingStandardsIgnoreStart .field-group_id { margin-right: 30px; } + //@codingStandardsIgnoreEnd } } } From eaf914bfdf964b434f8a575e9141a566a0d96a72 Mon Sep 17 00:00:00 2001 From: Yogesh Suhagiya <yksuhagiya@gmail.com> Date: Wed, 13 Feb 2019 11:03:55 +0530 Subject: [PATCH 0752/1295] Added translation for comment tag --- app/code/Magento/Msrp/etc/adminhtml/system.xml | 2 +- app/code/Magento/Msrp/i18n/en_US.csv | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Msrp/etc/adminhtml/system.xml b/app/code/Magento/Msrp/etc/adminhtml/system.xml index 8ce0ea67343f8..c20d753a2e794 100644 --- a/app/code/Magento/Msrp/etc/adminhtml/system.xml +++ b/app/code/Magento/Msrp/etc/adminhtml/system.xml @@ -10,7 +10,7 @@ <section id="sales"> <group id="msrp" translate="label" type="text" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Minimum Advertised Price</label> - <field id="enabled" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="enabled" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Enable MAP</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment> diff --git a/app/code/Magento/Msrp/i18n/en_US.csv b/app/code/Magento/Msrp/i18n/en_US.csv index d647f8527ec15..d47d72b2bdc9a 100644 --- a/app/code/Magento/Msrp/i18n/en_US.csv +++ b/app/code/Magento/Msrp/i18n/en_US.csv @@ -13,6 +13,7 @@ Price,Price "Add to Cart","Add to Cart" "Minimum Advertised Price","Minimum Advertised Price" "Enable MAP","Enable MAP" +"<strong style=""color:red"">Warning!</strong> Enabling MAP by default will hide all product prices on Storefront.","<strong style=""color:red"">Warning!</strong> Enabling MAP by default will hide all product prices on Storefront." "Display Actual Price","Display Actual Price" "Default Popup Text Message","Default Popup Text Message" "Default ""What's This"" Text Message","Default ""What's This"" Text Message" From d78ccf6cec8e3f2dee6d47afe49545967fe3a2b3 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Wed, 20 Feb 2019 10:08:30 +0200 Subject: [PATCH 0753/1295] MAGETWO-95904: Header Minicart, Shopping cart page and Checkout page show incorrect product name --- app/code/Magento/Checkout/Block/Cart/Sidebar.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Checkout/Block/Cart/Sidebar.php b/app/code/Magento/Checkout/Block/Cart/Sidebar.php index 7453d3c62e862..5e3234e9f4cc8 100644 --- a/app/code/Magento/Checkout/Block/Cart/Sidebar.php +++ b/app/code/Magento/Checkout/Block/Cart/Sidebar.php @@ -133,6 +133,7 @@ public function getShoppingCartUrl() * * @return string * @codeCoverageIgnore + * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) */ public function getUpdateItemQtyUrl() { @@ -144,6 +145,7 @@ public function getUpdateItemQtyUrl() * * @return string * @codeCoverageIgnore + * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) */ public function getRemoveItemUrl() { From d2ce214c950da94afd7958df79b2f2b1c5009996 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Wed, 20 Feb 2019 11:05:05 +0200 Subject: [PATCH 0754/1295] MC-14894: The *Recently Viewed Products* widget not displayed when more one product it was viewed --- .../SynchronizerTest.php | 138 +++++++++++------- 1 file changed, 84 insertions(+), 54 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/ProductFrontendAction/SynchronizerTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/ProductFrontendAction/SynchronizerTest.php index fce4a02622d9e..5d5447f09827f 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/ProductFrontendAction/SynchronizerTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/ProductFrontendAction/SynchronizerTest.php @@ -78,27 +78,19 @@ protected function setUp() ); } - public function testFilterProductActions() + /** + * @dataProvider filterProductActionsDataProvider + * + * @param $productsData + * @return void + */ + public function testFilterProductActions($productsData, $correct) { - $productsData = [ - 1 => [ - 'added_at' => 12, - 'product_id' => 1, - ], - 2 => [ - 'added_at' => 13, - 'product_id' => 2, - ], - 3 => [ - 'added_at' => 14, - 'product_id' => 3, - ] - ]; $frontendConfiguration = $this->createMock(\Magento\Catalog\Model\FrontendStorageConfigurationInterface::class); $frontendConfiguration->expects($this->once()) ->method('get') ->willReturn([ - 'lifetime' => 2 + 'lifetime' => 2, ]); $this->frontendStorageConfigurationPoolMock->expects($this->once()) ->method('get') @@ -110,7 +102,6 @@ public function testFilterProductActions() $action2 = $this->getMockBuilder(ProductFrontendActionInterface::class) ->getMockForAbstractClass(); - $frontendAction = $this->createMock(ProductFrontendActionInterface::class); $collection = $this->getMockBuilder(Collection::class) ->disableOriginalConstructor() ->getMock(); @@ -126,47 +117,86 @@ public function testFilterProductActions() $collection->expects($this->once()) ->method('addFilterByUserIdentities') ->with(1, 34); - $collection->expects($this->any()) - ->method('addFieldToFilter') - ->withConsecutive(['type_id'], ['product_id']); - $iterator = new \IteratorIterator(new \ArrayIterator([$frontendAction])); - $collection->expects($this->once()) - ->method('getIterator') - ->willReturn($iterator); - $this->entityManagerMock->expects($this->once()) - ->method('delete') - ->with($frontendAction); - $this->productFrontendActionFactoryMock->expects($this->exactly(2)) - ->method('create') - ->withConsecutive( - [ + if ($correct) { + $frontendAction = $this->createMock(ProductFrontendActionInterface::class); + $iterator = new \IteratorIterator(new \ArrayIterator([$frontendAction])); + $collection->expects($this->any()) + ->method('addFieldToFilter') + ->withConsecutive(['type_id'], ['product_id']); + $collection->expects($this->once()) + ->method('getIterator') + ->willReturn($iterator); + $this->entityManagerMock->expects($this->once()) + ->method('delete') + ->with($frontendAction); + $this->entityManagerMock->expects($this->exactly(2)) + ->method('save') + ->withConsecutive([$action1], [$action2]); + $this->productFrontendActionFactoryMock->expects($this->exactly(2)) + ->method('create') + ->withConsecutive( [ - 'data' => [ - 'visitor_id' => null, - 'customer_id' => 1, - 'added_at' => 12, - 'product_id' => 1, - 'type_id' => 'recently_compared_product' - ] - ] - ], - [ + [ + 'data' => [ + 'visitor_id' => null, + 'customer_id' => 1, + 'added_at' => 12, + 'product_id' => 1, + 'type_id' => 'recently_compared_product', + ], + ], + ], [ - 'data' => [ - 'visitor_id' => null, - 'customer_id' => 1, - 'added_at' => 13, - 'product_id' => 2, - 'type_id' => 'recently_compared_product' - ] + [ + 'data' => [ + 'visitor_id' => null, + 'customer_id' => 1, + 'added_at' => 13, + 'product_id' => 2, + 'type_id' => 'recently_compared_product', + ], + ], ] - ] - ) - ->willReturnOnConsecutiveCalls($action1, $action2); - $this->entityManagerMock->expects($this->exactly(2)) - ->method('save') - ->withConsecutive([$action1], [$action2]); + ) + ->willReturnOnConsecutiveCalls($action1, $action2); + } + $this->model->syncActions($productsData, 'recently_compared_product'); } + + /** + * @return array + */ + public function filterProductActionsDataProvider(): array + { + return [ + [ + 'productsData' => [ + 1 => [ + 'added_at' => 12, + 'product_id' => 1, + ], + 2 => [ + 'added_at' => 13, + 'product_id' => 2, + ], + 3 => [ + 'added_at' => 14, + 'product_id' => 3, + ], + ], + 'correct' => true, + ], + [ + 'productsData' => [ + 1 => [ + 'added_at' => 12, + 'product_id' => 'test', + ], + ], + 'correct' => false, + ], + ]; + } } From 4387ab32b0e125133697e17a7856ee186f658c85 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Wed, 20 Feb 2019 11:58:17 +0200 Subject: [PATCH 0755/1295] MC-14894: The *Recently Viewed Products* widget not displayed when more one product it was viewed --- .../Product/ProductFrontendAction/SynchronizerTest.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/ProductFrontendAction/SynchronizerTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/ProductFrontendAction/SynchronizerTest.php index 5d5447f09827f..2fd787e216118 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/ProductFrontendAction/SynchronizerTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/ProductFrontendAction/SynchronizerTest.php @@ -81,10 +81,11 @@ protected function setUp() /** * @dataProvider filterProductActionsDataProvider * - * @param $productsData + * @param array $productsData + * @param bool $correct * @return void */ - public function testFilterProductActions($productsData, $correct) + public function testFilterProductActions(array $productsData, bool $correct) { $frontendConfiguration = $this->createMock(\Magento\Catalog\Model\FrontendStorageConfigurationInterface::class); $frontendConfiguration->expects($this->once()) @@ -160,6 +161,11 @@ public function testFilterProductActions($productsData, $correct) ] ) ->willReturnOnConsecutiveCalls($action1, $action2); + } else { + $this->entityManagerMock->expects($this->never()) + ->method('delete'); + $this->entityManagerMock->expects($this->never()) + ->method('save'); } $this->model->syncActions($productsData, 'recently_compared_product'); From 39b96e9a4962aeff34052e7807f773ac19ac4836 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Wed, 20 Feb 2019 12:13:56 +0200 Subject: [PATCH 0756/1295] MAGETWO-94834: Cannot translate specific admin sections with inline translation --- app/code/Magento/Ui/view/base/web/js/grid/search/search.js | 2 +- .../base/web/templates/grid/controls/bookmarks/bookmarks.html | 2 +- .../view/base/web/templates/grid/controls/bookmarks/view.html | 2 +- .../Magento/Ui/view/base/web/templates/grid/search/search.html | 3 ++- app/code/Magento/Ui/view/base/web/templates/grid/submenu.html | 2 +- .../Ui/view/base/web/templates/grid/tree-massactions.html | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/search/search.js b/app/code/Magento/Ui/view/base/web/js/grid/search/search.js index fa445a2577adb..be825a391cf07 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/search/search.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/search/search.js @@ -18,7 +18,7 @@ define([ return Element.extend({ defaults: { template: 'ui/grid/search/search', - placeholder: $t('Search by keyword'), + placeholder: 'Search by keyword', label: $t('Keyword'), value: '', previews: [], diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/bookmarks.html b/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/bookmarks.html index 3ef64fd4b5371..36a3232c3e61a 100644 --- a/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/bookmarks.html +++ b/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/bookmarks.html @@ -6,7 +6,7 @@ --> <div class="admin__action-dropdown-wrap admin__data-grid-action-bookmarks" collapsible> <button class="admin__action-dropdown" type="button" toggleCollapsible> - <span class="admin__action-dropdown-text" text="activeView.label"/> + <span class="admin__action-dropdown-text" translate="activeView.label"/> </button> <ul class="admin__action-dropdown-menu"> <repeat args="foreach: viewsArray, item: '$view'"> diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/view.html b/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/view.html index b52669e2cd28d..521ce9fc806ac 100644 --- a/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/view.html +++ b/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/view.html @@ -30,7 +30,7 @@ </div> <div class="action-dropdown-menu-item"> - <a href="" class="action-dropdown-menu-link" text="$view().label" click="applyView.bind($data, $view().index)" closeCollapsible/> + <a href="" class="action-dropdown-menu-link" translate="$view().label" click="applyView.bind($data, $view().index)" closeCollapsible/> <div class="action-dropdown-menu-item-actions" if="$view().editable"> <button class="action-edit" type="button" attr="title: $t('Edit bookmark')" click="editView.bind($data, $view().index)"> diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/search/search.html b/app/code/Magento/Ui/view/base/web/templates/grid/search/search.html index 39d996e05c3a6..13b82a93eca25 100644 --- a/app/code/Magento/Ui/view/base/web/templates/grid/search/search.html +++ b/app/code/Magento/Ui/view/base/web/templates/grid/search/search.html @@ -10,9 +10,10 @@ </label> <input class="admin__control-text data-grid-search-control" type="text" data-bind=" + i18n: placeholder, attr: { id: index, - placeholder: placeholder + placeholder: $t(placeholder) }, textInput: inputValue, keyboard: { diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/submenu.html b/app/code/Magento/Ui/view/base/web/templates/grid/submenu.html index c5d87a4b16c4e..610d78e00b81d 100644 --- a/app/code/Magento/Ui/view/base/web/templates/grid/submenu.html +++ b/app/code/Magento/Ui/view/base/web/templates/grid/submenu.html @@ -6,7 +6,7 @@ --> <ul class="action-submenu" each="data: action.actions, as: 'action'" css="_active: action.visible"> <li css="_visible: $data.visible"> - <span class="action-menu-item" text="label" click="$parent.applyAction.bind($parent, type)"/> + <span class="action-menu-item" translate="label" click="$parent.applyAction.bind($parent, type)"/> <render args="name: $parent.submenuTemplate, data: $parent" if="$data.actions"/> </li> </ul> diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/tree-massactions.html b/app/code/Magento/Ui/view/base/web/templates/grid/tree-massactions.html index 1aeb48b7c7698..d11d4aa243737 100644 --- a/app/code/Magento/Ui/view/base/web/templates/grid/tree-massactions.html +++ b/app/code/Magento/Ui/view/base/web/templates/grid/tree-massactions.html @@ -11,7 +11,7 @@ <div class="action-menu-items"> <ul class="action-menu" each="data: actions, as: 'action'" css="_active: opened"> <li css="_visible: $data.visible, _parent: $data.actions"> - <span class="action-menu-item" text="label" click="$parent.applyAction.bind($parent, type)"/> + <span class="action-menu-item" translate="label" click="$parent.applyAction.bind($parent, type)"/> <render args="name: $parent.submenuTemplate, data: $parent" if="$data.actions"/> </li> </ul> From f5df1a8e4e4a97ee5ce6b7f1fdd0e5f90a41c679 Mon Sep 17 00:00:00 2001 From: Jaimin Sutariya <jaimin.sutariya@krishtechnolabs.com> Date: Sun, 27 Jan 2019 14:06:01 +0530 Subject: [PATCH 0757/1295] Removed direct use of SessionManager class, used SessionManagerInterface instead --- .../Customer/CustomerData/Plugin/SessionChecker.php | 8 ++++---- app/code/Magento/Customer/etc/frontend/di.xml | 4 ++-- .../Controller/Transparent/RequestSecureToken.php | 11 +++++++---- lib/internal/Magento/Framework/View/Context.php | 7 +++++-- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Customer/CustomerData/Plugin/SessionChecker.php b/app/code/Magento/Customer/CustomerData/Plugin/SessionChecker.php index aa73e275ee0ca..6497b039c7790 100644 --- a/app/code/Magento/Customer/CustomerData/Plugin/SessionChecker.php +++ b/app/code/Magento/Customer/CustomerData/Plugin/SessionChecker.php @@ -5,7 +5,7 @@ */ namespace Magento\Customer\CustomerData\Plugin; -use Magento\Framework\Session\SessionManager; +use Magento\Framework\Session\SessionManagerInterface; use Magento\Framework\Stdlib\Cookie\CookieMetadataFactory; use Magento\Framework\Stdlib\Cookie\PhpCookieManager; @@ -36,10 +36,10 @@ public function __construct( /** * Delete frontend session cookie if customer session is expired * - * @param SessionManager $sessionManager + * @param SessionManagerInterface $sessionManager * @return void */ - public function beforeStart(SessionManager $sessionManager) + public function beforeStart(SessionManagerInterface $sessionManager) { if (!$this->cookieManager->getCookie($sessionManager->getName()) && $this->cookieManager->getCookie('mage-cache-sessid') @@ -49,4 +49,4 @@ public function beforeStart(SessionManager $sessionManager) $this->cookieManager->deleteCookie('mage-cache-sessid', $metadata); } } -} +} \ No newline at end of file diff --git a/app/code/Magento/Customer/etc/frontend/di.xml b/app/code/Magento/Customer/etc/frontend/di.xml index 4a45c4ad48d19..c31742519e581 100644 --- a/app/code/Magento/Customer/etc/frontend/di.xml +++ b/app/code/Magento/Customer/etc/frontend/di.xml @@ -57,7 +57,7 @@ <type name="Magento\Checkout\Block\Cart\Sidebar"> <plugin name="customer_cart" type="Magento\Customer\Model\Cart\ConfigPlugin" /> </type> - <type name="Magento\Framework\Session\SessionManager"> + <type name="Magento\Framework\Session\SessionManagerInterface"> <plugin name="session_checker" type="Magento\Customer\CustomerData\Plugin\SessionChecker" /> </type> <type name="Magento\Authorization\Model\CompositeUserContext"> @@ -77,4 +77,4 @@ </argument> </arguments> </type> -</config> +</config> \ No newline at end of file diff --git a/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php b/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php index 3738a479816b3..68e5e888364b2 100644 --- a/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php +++ b/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php @@ -11,6 +11,7 @@ use Magento\Framework\Controller\ResultInterface; use Magento\Framework\Session\Generic; use Magento\Framework\Session\SessionManager; +use Magento\Framework\Session\SessionManagerInterface; use Magento\Paypal\Model\Payflow\Service\Request\SecureToken; use Magento\Paypal\Model\Payflow\Transparent; use Magento\Quote\Model\Quote; @@ -39,7 +40,7 @@ class RequestSecureToken extends \Magento\Framework\App\Action\Action private $secureTokenService; /** - * @var SessionManager + * @var SessionManager|SessionManagerInterface */ private $sessionManager; @@ -55,6 +56,7 @@ class RequestSecureToken extends \Magento\Framework\App\Action\Action * @param SecureToken $secureTokenService * @param SessionManager $sessionManager * @param Transparent $transparent + * @param SessionManagerInterface|null $sessionManagerInterface */ public function __construct( Context $context, @@ -62,12 +64,13 @@ public function __construct( Generic $sessionTransparent, SecureToken $secureTokenService, SessionManager $sessionManager, - Transparent $transparent + Transparent $transparent, + SessionManagerInterface $sessionInterface = null ) { $this->resultJsonFactory = $resultJsonFactory; $this->sessionTransparent = $sessionTransparent; $this->secureTokenService = $secureTokenService; - $this->sessionManager = $sessionManager; + $this->sessionManager = $sessionInterface ?: $sessionManager; $this->transparent = $transparent; parent::__construct($context); } @@ -120,4 +123,4 @@ private function getErrorResponse() ] ); } -} +} \ No newline at end of file diff --git a/lib/internal/Magento/Framework/View/Context.php b/lib/internal/Magento/Framework/View/Context.php index c3f1c3e691c84..c3876b8ca74de 100644 --- a/lib/internal/Magento/Framework/View/Context.php +++ b/lib/internal/Magento/Framework/View/Context.php @@ -14,6 +14,7 @@ use Magento\Framework\Event\ManagerInterface; use Psr\Log\LoggerInterface as Logger; use Magento\Framework\Session\SessionManager; +use Magento\Framework\Session\SessionManagerInterface; use Magento\Framework\TranslateInterface; use Magento\Framework\UrlInterface; use Magento\Framework\View\ConfigInterface as ViewConfig; @@ -144,6 +145,7 @@ class Context * @param Logger $logger * @param AppState $appState * @param LayoutInterface $layout + * @param SessionManagerInterface|null $sessionManager * * @todo reduce parameter number * @@ -163,7 +165,8 @@ public function __construct( CacheState $cacheState, Logger $logger, AppState $appState, - LayoutInterface $layout + LayoutInterface $layout, + SessionManagerInterface $sessionManager = null ) { $this->request = $request; $this->eventManager = $eventManager; @@ -171,7 +174,7 @@ public function __construct( $this->translator = $translator; $this->cache = $cache; $this->design = $design; - $this->session = $session; + $this->session = $sessionManager ?: $session; $this->scopeConfig = $scopeConfig; $this->frontController = $frontController; $this->viewConfig = $viewConfig; From 737a79eb5aae8b7b3bb7065707f7e643aa750ede Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Sat, 16 Feb 2019 17:56:34 +0000 Subject: [PATCH 0758/1295] magento/magento2#19359: Fixed static tests --- .../Customer/CustomerData/Plugin/SessionChecker.php | 7 ++++++- .../Paypal/Controller/Transparent/RequestSecureToken.php | 4 ++-- lib/internal/Magento/Framework/View/Context.php | 2 ++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Customer/CustomerData/Plugin/SessionChecker.php b/app/code/Magento/Customer/CustomerData/Plugin/SessionChecker.php index 6497b039c7790..f82a4d15ae8bf 100644 --- a/app/code/Magento/Customer/CustomerData/Plugin/SessionChecker.php +++ b/app/code/Magento/Customer/CustomerData/Plugin/SessionChecker.php @@ -9,6 +9,9 @@ use Magento\Framework\Stdlib\Cookie\CookieMetadataFactory; use Magento\Framework\Stdlib\Cookie\PhpCookieManager; +/** + * Class SessionChecker + */ class SessionChecker { /** @@ -38,6 +41,8 @@ public function __construct( * * @param SessionManagerInterface $sessionManager * @return void + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Stdlib\Cookie\FailureToSendException */ public function beforeStart(SessionManagerInterface $sessionManager) { @@ -49,4 +54,4 @@ public function beforeStart(SessionManagerInterface $sessionManager) $this->cookieManager->deleteCookie('mage-cache-sessid', $metadata); } } -} \ No newline at end of file +} diff --git a/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php b/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php index 68e5e888364b2..497e32157de05 100644 --- a/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php +++ b/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php @@ -56,7 +56,7 @@ class RequestSecureToken extends \Magento\Framework\App\Action\Action * @param SecureToken $secureTokenService * @param SessionManager $sessionManager * @param Transparent $transparent - * @param SessionManagerInterface|null $sessionManagerInterface + * @param SessionManagerInterface|null $sessionInterface */ public function __construct( Context $context, @@ -123,4 +123,4 @@ private function getErrorResponse() ] ); } -} \ No newline at end of file +} diff --git a/lib/internal/Magento/Framework/View/Context.php b/lib/internal/Magento/Framework/View/Context.php index c3876b8ca74de..508d63d158bd7 100644 --- a/lib/internal/Magento/Framework/View/Context.php +++ b/lib/internal/Magento/Framework/View/Context.php @@ -335,6 +335,8 @@ public function getModuleName() } /** + * Get Front Name + * * @see getModuleName */ public function getFrontName() From e53106c9e2a46cf83c95a0ce01347d31a1715fac Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Wed, 20 Feb 2019 13:11:28 +0200 Subject: [PATCH 0759/1295] MAGETWO-94147: Swatch Attribute is not displayed in the Widget CMS --- ...minCategoryProductAttributeActionGroup.xml | 7 +-- .../Block/Product/ProductsList.php | 25 +++++++--- ...up.xml => CreateNewCmsPageActionGroup.xml} | 50 ++++++++++--------- .../DeletePageByUrlKeyActionGroup.xml | 10 ++-- .../Cms/Test/Mftf/Page/StorefrontCmsPage.xml | 13 +++++ .../Section/CmsNewPagePageActionsSection.xml | 2 +- ...SwatchAttributesDisplayInWidgetCMSTest.xml | 7 ++- 7 files changed, 71 insertions(+), 43 deletions(-) rename app/code/Magento/Cms/Test/Mftf/ActionGroup/{CreateNewPageWithWidgetActionGroup.xml => CreateNewCmsPageActionGroup.xml} (58%) create mode 100644 app/code/Magento/Cms/Test/Mftf/Page/StorefrontCmsPage.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryProductAttributeActionGroup.xml index 21e41732369e9..11b5aabf0cee9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryProductAttributeActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryProductAttributeActionGroup.xml @@ -14,16 +14,17 @@ <argument name="productAttribute"/> </arguments> <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributesGridPage"/> + <waitForPageLoad time="30" stepKey="waitForProductAttributesGridPageLoad"/> <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFiltersBeforeDelete"/> <fillField selector="{{AdminProductAttributeGridSection.gridFilterFrontEndLabel}}" userInput="{{productAttribute.default_label}}" stepKey="fillAttributeDefaultLabelInput"/> - <click selector="{{AdminProductAttributeGridSection.search}}" stepKey="searchForAttribute"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="applyFilters"/> <click selector="{{AdminProductAttributeGridSection.firstRow}}" stepKey="clickFirstRow"/> <waitForPageLoad time="30" stepKey="waitForPageLoad"/> - <click selector="{{AdminProductAttributeEditSection.deleteAttribute}}" stepKey="deleteProductAttribute"/> + <click selector="{{AdminMainActionsSection.delete}}" stepKey="deleteProductAttribute"/> <waitForElementVisible selector="{{AdminConfirmationModalSection.message}}" stepKey="waitingForWarningModal"/> <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDelete"/> - <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFiltersAfterDelete"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the product attribute." stepKey="seeSuccessMessage"/> </actionGroup> <actionGroup name="navigateToProductAttribute"> diff --git a/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php b/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php index 3c8599133588b..41f5123f3b772 100644 --- a/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php +++ b/app/code/Magento/CatalogWidget/Block/Product/ProductsList.php @@ -104,10 +104,15 @@ class ProductsList extends \Magento\Catalog\Block\Product\AbstractProduct implem private $layoutFactory; /** - * @var \Magento\Framework\Url\EncoderInterface|null + * @var \Magento\Framework\Url\EncoderInterface */ private $urlEncoder; + /** + * @var \Magento\Framework\View\Element\RendererList + */ + private $rendererListBlock; + /** * @param \Magento\Catalog\Block\Product\Context $context * @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory @@ -238,13 +243,17 @@ public function getProductPriceHtml( */ protected function getDetailsRendererList() { - /** @var $layout \Magento\Framework\View\LayoutInterface */ - $layout = $this->layoutFactory->create(['cacheable' => false]); - $layout->getUpdate()->addHandle('catalog_widget_product_list')->load(); - $layout->generateXml(); - $layout->generateElements(); + if (empty($this->rendererListBlock)) { + /** @var $layout \Magento\Framework\View\LayoutInterface */ + $layout = $this->layoutFactory->create(['cacheable' => false]); + $layout->getUpdate()->addHandle('catalog_widget_product_list')->load(); + $layout->generateXml(); + $layout->generateElements(); + + $this->rendererListBlock = $layout->getBlock('category.product.type.widget.details.renderers'); + } - return $layout->getBlock('category.product.type.widget.details.renderers'); + return $this->rendererListBlock; } /** @@ -253,7 +262,7 @@ protected function getDetailsRendererList() * @param Product $product * @return array */ - public function getAddToCartPostParams(Product $product) + public function getAddToCartPostParams(Product $product): array { $url = $this->getAddToCartUrl($product); diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithWidgetActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewCmsPageActionGroup.xml similarity index 58% rename from app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithWidgetActionGroup.xml rename to app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewCmsPageActionGroup.xml index 6a6bb9edaa43a..667dc79d2d6b4 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithWidgetActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewCmsPageActionGroup.xml @@ -7,43 +7,45 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="CreateNewPageWithWidgetWithCategoryCondition"> + <actionGroup name="CreateNewCmsPageActionGroup"> <arguments> <argument name="cmsPage" defaultValue="_defaultCmsPage"/> - <argument name="categoryId" type="string"/> - <argument name="conditionOperator" type="string" defaultValue="is"/> - <argument name="widgetType" type="string" defaultValue="Catalog Products List"/> </arguments> <amOnPage url="{{CmsNewPagePage.url}}" stepKey="amOnCMSNewPage"/> <fillField selector="{{CmsNewPagePageBasicFieldsSection.pageTitle}}" userInput="{{cmsPage.title}}" stepKey="fillFieldTitle"/> - <click selector="{{CmsNewPagePageSeoSection.header}}" stepKey="clickExpandSearchEngineOptimisation"/> <fillField selector="{{CmsNewPagePageSeoSection.urlKey}}" userInput="{{cmsPage.identifier}}" stepKey="fillFieldUrlKey"/> + <scrollToTopOfPage stepKey="scrollToTopOfPage"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveButton"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the page." stepKey="seeSaveSuccessMessage"/> + </actionGroup> - <click selector="{{CmsNewPagePageContentSection.header}}" stepKey="clickExpandContent"/> - <click selector="{{CmsNewPagePageActionsSection.insertWidgetButton}}" stepKey="clickInsertWidgetButton"/> - <waitForElementVisible selector="{{AdminNewWidgetSection.widgetTypeDropDown}}" time="10" stepKey="waitForInsertWidgetFrame"/> - - <selectOption selector="{{AdminNewWidgetSection.widgetTypeDropDown}}" userInput="{{widgetType}}" stepKey="selectCatalogProductListOption"/> - <waitForElementVisible selector="{{AdminNewWidgetSection.addNewCondition}}" stepKey="waitForConditionsElementBecomeAvailable"/> + <actionGroup name="CreateNewPageWithWidgetWithCategoryCondition" extends="CreateNewCmsPageActionGroup"> + <arguments> + <argument name="categoryId" type="string"/> + <argument name="conditionOperator" type="string" defaultValue="is"/> + <argument name="widgetType" type="string" defaultValue="Catalog Products List"/> + </arguments> + <click selector="{{CmsNewPagePageContentSection.header}}" after="fillFieldUrlKey" stepKey="clickExpandContent"/> + <click selector="{{CmsNewPagePageActionsSection.insertWidgetButton}}" after="clickExpandContent" stepKey="clickInsertWidgetButton"/> - <click selector="{{AdminNewWidgetSection.addNewCondition}}" stepKey="clickToAddCondition"/> - <waitForElementVisible selector="{{AdminNewWidgetSection.selectCondition}}" stepKey="waitForSelectBoxOpened"/> + <selectOption selector="{{AdminNewWidgetSection.widgetTypeDropDown}}" userInput="{{widgetType}}" after="clickInsertWidgetButton" stepKey="selectCatalogProductListOption"/> + <waitForElementVisible selector="{{AdminNewWidgetSection.addNewCondition}}" after="selectCatalogProductListOption" stepKey="waitForConditionsElementBecomeAvailable"/> - <selectOption selector="{{AdminNewWidgetSection.selectCondition}}" userInput="Category" stepKey="selectCategoryCondition"/> - <waitForElementVisible selector="{{AdminNewWidgetSection.ruleParameter}}" stepKey="seeConditionsAdded"/> + <click selector="{{AdminNewWidgetSection.addNewCondition}}" after="waitForConditionsElementBecomeAvailable" stepKey="clickToAddCondition"/> + <waitForElementVisible selector="{{AdminNewWidgetSection.selectCondition}}" after="clickToAddCondition" stepKey="waitForSelectBoxOpened"/> - <click selector="{{AdminNewWidgetSection.conditionOperator}}" stepKey="clickToConditionIs"/> - <selectOption selector="{{AdminNewWidgetSection.conditionOperatorSelect('1')}}" userInput="{{conditionOperator}}" stepKey="selectOperator"/> + <selectOption selector="{{AdminNewWidgetSection.selectCondition}}" userInput="Category" after="waitForSelectBoxOpened" stepKey="selectCategoryCondition"/> + <waitForElementVisible selector="{{AdminNewWidgetSection.ruleParameter}}" after="selectCategoryCondition" stepKey="seeConditionsAdded"/> - <click selector="{{AdminNewWidgetSection.ruleParameter}}" stepKey="clickAddConditionItem"/> - <waitForElementVisible selector="{{AdminNewWidgetSection.setRuleParameter}}" stepKey="waitForConditionFieldOpened"/> + <click selector="{{AdminNewWidgetSection.conditionOperator}}" after="seeConditionsAdded" stepKey="clickToConditionIs"/> + <selectOption selector="{{AdminNewWidgetSection.conditionOperatorSelect('1')}}" after="clickToConditionIs" userInput="{{conditionOperator}}" stepKey="selectOperator"/> - <fillField selector="{{AdminNewWidgetSection.setRuleParameter}}" userInput="{{categoryId}}" stepKey="setCategoryId"/> - <click selector="{{AdminNewWidgetSection.insertWidget}}" stepKey="clickInsertWidget"/> + <click selector="{{AdminNewWidgetSection.ruleParameter}}" after="selectOperator" stepKey="clickAddConditionItem"/> + <waitForElementVisible selector="{{AdminNewWidgetSection.setRuleParameter}}" after="clickAddConditionItem" stepKey="waitForConditionFieldOpened"/> - <waitForElementVisible selector="{{AdminMainActionsSection.save}}" stepKey="waitForInsertWidgetSaved"/> - <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveButton"/> - <see userInput="You saved the page." stepKey="seeSavedPageMsgOnForm"/> + <fillField selector="{{AdminNewWidgetSection.setRuleParameter}}" userInput="{{categoryId}}" after="waitForConditionFieldOpened" stepKey="setCategoryId"/> + <click selector="{{AdminNewWidgetSection.insertWidget}}" after="setCategoryId" stepKey="clickInsertWidget"/> + <waitForElementVisible selector="{{AdminMainActionsSection.save}}" after="clickInsertWidget" stepKey="waitForInsertWidgetSaved"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml index 3e034199ac1a2..fd20954dc7a9e 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeletePageByUrlKeyActionGroup.xml @@ -9,19 +9,19 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="DeletePageByUrlKeyActionGroup"> <arguments> - <argument name="urlKey" type="string" defaultValue="{{_defaultCmsPage.identifier}}"/> + <argument name="urlKey" type="string"/> </arguments> <amOnPage url="{{CmsPagesPage.url}}" stepKey="amOnCMSPagesIndexPage"/> + <waitForPageLoad time="30" stepKey="waitForCmsPageListingLoaded"/> <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFiltersBeforeDelete"/> <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openCmsPageFilters"/> <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('identifier')}}" userInput="{{urlKey}}" stepKey="fillFilter"/> <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFilters"/> <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"/> + <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForOkButtonToBeVisible"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="clickOkButton"/> + <see selector="{{AdminMessagesSection.success}}" userInput="The page has been deleted." stepKey="seeSuccessMessage"/> <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFiltersAfterDelete"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/Page/StorefrontCmsPage.xml b/app/code/Magento/Cms/Test/Mftf/Page/StorefrontCmsPage.xml new file mode 100644 index 0000000000000..b2de3a225f8ce --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Page/StorefrontCmsPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="StorefrontCmsPage" url="/{{urlKey}}" area="storefront" module="Magento_Cms" parameterized="true"> + </page> +</pages> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageActionsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageActionsSection.xml index da9efa0dab62a..740650d6fdfa9 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageActionsSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageActionsSection.xml @@ -11,6 +11,6 @@ <section name="CmsNewPagePageActionsSection"> <element name="savePage" type="button" selector="#save" timeout="30"/> <element name="saveAndContinueEdit" type="button" selector="#save_and_continue" timeout="10"/> - <element name="insertWidgetButton" type="button" selector=".scalable.action-add-widget.plugin"/> + <element name="insertWidgetButton" type="button" selector=".scalable.action-add-widget.plugin" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml index 167d7ac007eef..cd376e9e0124d 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml @@ -58,13 +58,16 @@ <actionGroup ref="DeleteProductAttribute" stepKey="deleteAttribute"> <argument name="productAttribute" value="VisualSwatchAttribute"/> </actionGroup> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearGridFilter1"/> <!--delete created page product--> - <actionGroup ref="DeletePageByUrlKeyActionGroup" stepKey="deletePage"/> + <actionGroup ref="DeletePageByUrlKeyActionGroup" stepKey="deletePage"> + <argument name="urlKey" value="{{_defaultCmsPage.identifier}}"/> + </actionGroup> <!--logout--> <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> <!--Open Storefront page for the new created page--> - <amOnPage url="{{StorefrontHomePage.url}}{{_defaultCmsPage.identifier}}" stepKey="gotToCreatedCmsPage"/> + <amOnPage url="{{StorefrontCmsPage.url(_defaultCmsPage.identifier)}}" stepKey="goToCreatedCmsPage"/> <seeElement selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel(VisualSwatchOption1.default_label)}}" stepKey="assertAddedWidgetS"/> <seeElement selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel(VisualSwatchOption2.default_label)}}" stepKey="assertAddedWidgetM"/> </test> From 0cc461c81004423f28c21abc6bbfef37baa69bce Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Wed, 20 Feb 2019 13:46:42 +0200 Subject: [PATCH 0760/1295] MC-14946: When create Permanent Update to specific store view - field Price not disabled but checkbox "Use Default Value" is checked --- .../web/js/components/price-configurable.js | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/price-configurable.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/price-configurable.js index 6bbab77a3a0ab..b2ef35546eea8 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/price-configurable.js +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/price-configurable.js @@ -11,9 +11,6 @@ define([ return Abstract.extend({ defaults: { - listens: { - isConfigurable: 'handlePriceValue' - }, imports: { isConfigurable: '!ns = ${ $.ns }, index = configurable-matrix:isEmpty' }, @@ -22,12 +19,15 @@ define([ } }, - /** - * Invokes initialize method of parent class, - * contains initialization logic - */ + /** @inheritdoc */ initialize: function () { this._super(); + // resolve initial disable state + this.handlePriceValue(this.isConfigurable); + // add listener to track "configurable" type + this.setListeners({ + isConfigurable: 'handlePriceValue' + }); return this; }, @@ -50,11 +50,10 @@ define([ * @param {String} isConfigurable */ handlePriceValue: function (isConfigurable) { + this.disabled(!!this.isUseDefault() || isConfigurable); + if (isConfigurable) { - this.disable(); this.clear(); - } else { - this.enable(); } } }); From 0a828ca9c0526e0a951a3946ea6c1f10a0bc3638 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Wed, 20 Feb 2019 15:58:36 +0200 Subject: [PATCH 0761/1295] MC-14985: Custom options fields have double required icons --- .../fieldset/options/type/text.phtml | 6 +++-- .../web/css/source/_module.less | 27 +++++-------------- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/text.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/text.phtml index 11fba22ea8139..1580cec60b3fa 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/text.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/text.phtml @@ -11,8 +11,10 @@ <?php $_option = $block->getOption(); ?> <div class="field admin__field<?php if ($_option->getIsRequire()) echo ' required _required' ?>"> <label class="admin__field-label label"> - <?= $block->escapeHtml($_option->getTitle()) ?> - <?= /* @escapeNotVerified */ $block->getFormattedPrice() ?> + <span> + <?= $block->escapeHtml($_option->getTitle()) ?> + <?= /* @escapeNotVerified */ $block->getFormattedPrice() ?> + </span> </label> <div class="control admin__field-control"> <?php if ($_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_FIELD): ?> diff --git a/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module.less index 20342442048fd..eda29bb171a33 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module.less @@ -33,27 +33,12 @@ .admin__field { &.required, &._required { - & > .admin__field-label { - &:after { - color: @validation__color; - content: '*'; - display: inline-block; - font-size: @font-size__l; - font-weight: @font-weight__heavier; - line-height: 1; - margin-left: 10px; - margin-top: .2rem; - position: absolute; - z-index: 1; - } - & .price-notice { - &:after { - display: none; - } - - & span:after { - display: none - } + & > .admin__field-label > span{ + + width: 100%; + + & span:after { + display: none; } } } From c753b2a081ccba854124002069ec65c198883ba3 Mon Sep 17 00:00:00 2001 From: Serhii Dzhepa <sdzhepa@adobe.com> Date: Wed, 20 Feb 2019 10:55:31 -0600 Subject: [PATCH 0762/1295] Fixed format for @codingStandardsIgnoreStart --- .../web/css/source/module/order/_order-account.less | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_order-account.less b/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_order-account.less index 7958378be2c5b..5c30a00c9a0d7 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_order-account.less +++ b/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_order-account.less @@ -35,11 +35,13 @@ .field-email { margin-left: -30px; } - //@codingStandardsIgnoreStart + /** + * @codingStandardsIgnoreStart + */ .field-group_id { margin-right: 30px; } - //@codingStandardsIgnoreEnd + // @codingStandardsIgnoreEnd } } } From 7affb7d6dad35e4d24385518f6d90ae14f91329b Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 20 Feb 2019 12:24:06 -0600 Subject: [PATCH 0763/1295] MAGETWO-96001: Update FedEx Shipping Dates behavior in Tracking Popup --- .../Fedex/Plugin/Block/DataProviders/Tracking/ChangeTitle.php | 4 +++- .../Block/DataProviders/Tracking/DeliveryDateTitle.php | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Fedex/Plugin/Block/DataProviders/Tracking/ChangeTitle.php b/app/code/Magento/Fedex/Plugin/Block/DataProviders/Tracking/ChangeTitle.php index d148b91aa3e7a..b271251c4d214 100644 --- a/app/code/Magento/Fedex/Plugin/Block/DataProviders/Tracking/ChangeTitle.php +++ b/app/code/Magento/Fedex/Plugin/Block/DataProviders/Tracking/ChangeTitle.php @@ -16,13 +16,15 @@ class ChangeTitle { /** + * Update title in case if Fedex used as carrier + * * @param Subject $subject * @param \Magento\Framework\Phrase|string $result * @param Status $trackingStatus * @return \Magento\Framework\Phrase|string * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function afterGetTitle(Subject $subject, $result, Status $trackingStatus): string + public function afterGetTitle(Subject $subject, $result, Status $trackingStatus) { if ($trackingStatus->getCarrier() === Carrier::CODE) { $result = __('Expected Delivery:'); diff --git a/app/code/Magento/Shipping/Block/DataProviders/Tracking/DeliveryDateTitle.php b/app/code/Magento/Shipping/Block/DataProviders/Tracking/DeliveryDateTitle.php index dc66c4f0bd018..661068d42c35d 100644 --- a/app/code/Magento/Shipping/Block/DataProviders/Tracking/DeliveryDateTitle.php +++ b/app/code/Magento/Shipping/Block/DataProviders/Tracking/DeliveryDateTitle.php @@ -15,6 +15,8 @@ class DeliveryDateTitle implements ArgumentInterface { /** + * Return title if carrier is defined + * * @param Status $trackingStatus * @return \Magento\Framework\Phrase|string */ From 8bcfa65db7adea4a5819ea9ed40ca79452769f1c Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 20 Feb 2019 13:15:39 -0600 Subject: [PATCH 0764/1295] MAGETWO-96001: Update FedEx Shipping Dates behavior in Tracking Popup --- .../Magento/Fedex/Plugin/Block/Tracking/PopupDeliveryDate.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Fedex/Plugin/Block/Tracking/PopupDeliveryDate.php b/app/code/Magento/Fedex/Plugin/Block/Tracking/PopupDeliveryDate.php index 942d21dfa5e47..e1597707f9d02 100644 --- a/app/code/Magento/Fedex/Plugin/Block/Tracking/PopupDeliveryDate.php +++ b/app/code/Magento/Fedex/Plugin/Block/Tracking/PopupDeliveryDate.php @@ -3,7 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -declare(strict_types=1); namespace Magento\Fedex\Plugin\Block\Tracking; @@ -26,7 +25,7 @@ class PopupDeliveryDate * @return string * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function afterFormatDeliveryDateTime(Popup $subject, string $result, string $date, string $time): string + public function afterFormatDeliveryDateTime(Popup $subject, $result, $date, $time) { if ($this->getCarrier($subject) === Carrier::CODE) { $result = $subject->formatDeliveryDate($date); From a0dade4d8bc15b651d8b1ea0e7e4e3580fb1d3ae Mon Sep 17 00:00:00 2001 From: vtymchynskyi <vtymchynskyi@magento.com> Date: Tue, 19 Feb 2019 15:01:01 -0600 Subject: [PATCH 0765/1295] MAGETWO-98129: Investigation: Transaction MD5 hash field is removed by Authorize.net - added support of SHA-512 hash --- .../Magento/Authorizenet/Model/Directpost.php | 9 +- .../Authorizenet/Model/Directpost/Request.php | 116 ++++++++++++++++-- .../Model/Directpost/Response.php | 72 +++++++++-- .../Unit/Model/Directpost/RequestTest.php | 80 ++++++++++++ .../Unit/Model/Directpost/ResponseTest.php | 78 +++++------- .../Authorizenet/etc/adminhtml/system.xml | 4 + app/code/Magento/Authorizenet/etc/config.xml | 1 + 7 files changed, 290 insertions(+), 70 deletions(-) create mode 100644 app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/RequestTest.php diff --git a/app/code/Magento/Authorizenet/Model/Directpost.php b/app/code/Magento/Authorizenet/Model/Directpost.php index 3057eaabde44f..6c5eeb1710170 100644 --- a/app/code/Magento/Authorizenet/Model/Directpost.php +++ b/app/code/Magento/Authorizenet/Model/Directpost.php @@ -543,15 +543,16 @@ public function setResponseData(array $postData) public function validateResponse() { $response = $this->getResponse(); - //md5 check - if (!$this->getConfigData('trans_md5') - || !$this->getConfigData('login') - || !$response->isValidHash($this->getConfigData('trans_md5'), $this->getConfigData('login')) + $hashConfigKey = !empty($response->getData('x_SHA2_Hash')) ? 'signature_key' : 'trans_md5'; + + //hash check + if (!$response->isValidHash($this->getConfigData($hashConfigKey), $this->getConfigData('login')) ) { throw new \Magento\Framework\Exception\LocalizedException( __('The transaction was declined because the response hash validation failed.') ); } + return true; } diff --git a/app/code/Magento/Authorizenet/Model/Directpost/Request.php b/app/code/Magento/Authorizenet/Model/Directpost/Request.php index fc78d836b6080..dd35fd71c5a6d 100644 --- a/app/code/Magento/Authorizenet/Model/Directpost/Request.php +++ b/app/code/Magento/Authorizenet/Model/Directpost/Request.php @@ -7,6 +7,7 @@ namespace Magento\Authorizenet\Model\Directpost; use Magento\Authorizenet\Model\Request as AuthorizenetRequest; +use Magento\Framework\Intl\DateTimeFactory; /** * Authorize.net request model for DirectPost model @@ -18,9 +19,33 @@ class Request extends AuthorizenetRequest */ protected $_transKey = null; + /** + * Hexadecimal signature key. + * + * @var string + */ + private $signatureKey = ''; + + /** + * @var DateTimeFactory + */ + private $dateTimeFactory; + + /** + * @param DateTimeFactory $dateTimeFactory + * @param array $data + */ + public function __construct( + DateTimeFactory $dateTimeFactory, + array $data = [] + ) { + $this->dateTimeFactory = $dateTimeFactory; + parent::__construct($data); + } + /** * Return merchant transaction key. - * Needed to generate sign. + * Needed to generate MD5 sign. * * @return string */ @@ -31,7 +56,7 @@ protected function _getTransactionKey() /** * Set merchant transaction key. - * Needed to generate sign. + * Needed to generate MD5 sign. * * @param string $transKey * @return $this @@ -43,7 +68,7 @@ protected function _setTransactionKey($transKey) } /** - * Generates the fingerprint for request. + * Generates the MD5 fingerprint for request. * * @param string $merchantApiLoginId * @param string $merchantTransactionKey @@ -63,7 +88,7 @@ public function generateRequestSign( ) { return hash_hmac( "md5", - $merchantApiLoginId . "^" . $fpSequence . "^" . $fpTimestamp . "^" . $amount . "^" . $currencyCode, + $merchantApiLoginId . '^' . $fpSequence . '^' . $fpTimestamp . '^' . $amount . '^' . $currencyCode, $merchantTransactionKey ); } @@ -85,6 +110,7 @@ public function setConstantData(\Magento\Authorizenet\Model\Directpost $paymentM ->setXRelayUrl($paymentMethod->getRelayUrl()); $this->_setTransactionKey($paymentMethod->getConfigData('trans_key')); + $this->setSignatureKey($paymentMethod->getConfigData('signature_key')); return $this; } @@ -168,17 +194,81 @@ public function setDataFromOrder( */ public function signRequestData() { - $fpTimestamp = time(); - $hash = $this->generateRequestSign( - $this->getXLogin(), - $this->_getTransactionKey(), - $this->getXAmount(), - $this->getXCurrencyCode(), - $this->getXFpSequence(), - $fpTimestamp - ); + $fpDate = $this->dateTimeFactory->create('now', new \DateTimeZone('UTC')); + $fpTimestamp = $fpDate->getTimestamp(); + + if (!empty($this->getSignatureKey())) { + $hash = $this->generateSha2RequestSign( + $this->getXLogin(), + $this->getSignatureKey(), + $this->getXAmount(), + $this->getXCurrencyCode(), + $this->getXFpSequence(), + $fpTimestamp + ); + } else { + $hash = $this->generateRequestSign( + $this->getXLogin(), + $this->_getTransactionKey(), + $this->getXAmount(), + $this->getXCurrencyCode(), + $this->getXFpSequence(), + $fpTimestamp + ); + } + $this->setXFpTimestamp($fpTimestamp); $this->setXFpHash($hash); + return $this; } + + /** + * Generates the SHA2 fingerprint for request. + * + * @param string $merchantApiLoginId + * @param string $merchantSignatureKey + * @param string $amount + * @param string $currencyCode + * @param string $fpSequence An invoice number or random number. + * @param string $fpTimestamp + * @return string The fingerprint. + */ + private function generateSha2RequestSign( + $merchantApiLoginId, + $merchantSignatureKey, + $amount, + $currencyCode, + $fpSequence, + $fpTimestamp + ): string { + $message = $merchantApiLoginId . '^' . $fpSequence . '^' . $fpTimestamp . '^' . $amount . '^' . $currencyCode; + + return strtoupper(hash_hmac('sha512', $message, pack('H*', $merchantSignatureKey))); + } + + /** + * Return merchant hexadecimal signature key. + * + * Needed to generate SHA2 sign. + * + * @return string + */ + private function getSignatureKey(): string + { + return $this->signatureKey; + } + + /** + * Set merchant hexadecimal signature key. + * + * Needed to generate SHA2 sign. + * + * @param string $signatureKey + * @return void + */ + private function setSignatureKey(string $signatureKey) + { + $this->signatureKey = $signatureKey; + } } diff --git a/app/code/Magento/Authorizenet/Model/Directpost/Response.php b/app/code/Magento/Authorizenet/Model/Directpost/Response.php index dc62c1e990dc3..55e31f1526610 100644 --- a/app/code/Magento/Authorizenet/Model/Directpost/Response.php +++ b/app/code/Magento/Authorizenet/Model/Directpost/Response.php @@ -24,25 +24,31 @@ class Response extends AuthorizenetResponse */ public function generateHash($merchantMd5, $merchantApiLogin, $amount, $transactionId) { - if (!$amount) { - $amount = '0.00'; - } - return strtoupper(md5($merchantMd5 . $merchantApiLogin . $transactionId . $amount)); } /** * Return if is valid order id. * - * @param string $merchantMd5 + * @param string $storedHash * @param string $merchantApiLogin * @return bool */ - public function isValidHash($merchantMd5, $merchantApiLogin) + public function isValidHash($storedHash, $merchantApiLogin) { - $hash = $this->generateHash($merchantMd5, $merchantApiLogin, $this->getXAmount(), $this->getXTransId()); + if (empty($this->getData('x_amount'))) { + $this->setData('x_amount', '0.00'); + } - return Security::compareStrings($hash, $this->getData('x_MD5_Hash')); + if (!empty($this->getData('x_SHA2_Hash'))) { + $hash = $this->generateSha2Hash($storedHash); + return Security::compareStrings($hash, $this->getData('x_SHA2_Hash')); + } elseif (!empty($this->getData('x_MD5_Hash'))) { + $hash = $this->generateHash($storedHash, $merchantApiLogin, $this->getXAmount(), $this->getXTransId()); + return Security::compareStrings($hash, $this->getData('x_MD5_Hash')); + } + + return false; } /** @@ -54,4 +60,54 @@ public function isApproved() { return $this->getXResponseCode() == \Magento\Authorizenet\Model\Directpost::RESPONSE_CODE_APPROVED; } + + /** + * Generates an SHA2 hash to compare against AuthNet's. + * + * @param string $signatureKey + * @return string + * @see https://support.authorize.net/s/article/MD5-Hash-End-of-Life-Signature-Key-Replacement + */ + private function generateSha2Hash(string $signatureKey): string + { + $hashFields = [ + 'x_trans_id', + 'x_test_request', + 'x_response_code', + 'x_auth_code', + 'x_cvv2_resp_code', + 'x_cavv_response', + 'x_avs_code', + 'x_method', + 'x_account_number', + 'x_amount', + 'x_company', + 'x_first_name', + 'x_last_name', + 'x_address', + 'x_city', + 'x_state', + 'x_zip', + 'x_country', + 'x_phone', + 'x_fax', + 'x_email', + 'x_ship_to_company', + 'x_ship_to_first_name', + 'x_ship_to_last_name', + 'x_ship_to_address', + 'x_ship_to_city', + 'x_ship_to_state', + 'x_ship_to_zip', + 'x_ship_to_country', + 'x_invoice_num', + ]; + + $message = '^'; + foreach ($hashFields as $field) { + $message .= ($this->getData($field) ?? '') . '^'; + } + + return strtoupper(hash_hmac('sha512', $message, pack('H*', $signatureKey))); + } } diff --git a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/RequestTest.php b/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/RequestTest.php new file mode 100644 index 0000000000000..d3caa1597e64b --- /dev/null +++ b/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/RequestTest.php @@ -0,0 +1,80 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Authorizenet\Test\Unit\Model\Directpost; + +use Magento\Authorizenet\Model\Directpost\Request; +use Magento\Framework\Intl\DateTimeFactory; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +class RequestTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var DateTimeFactory|MockObject + */ + private $dateTimeFactory; + + /** + * @var Request + */ + private $requestModel; + + protected function setUp() + { + $this->dateTimeFactory = $this->getMockBuilder(DateTimeFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $dateTime = new \DateTime('2016-07-05 00:00:00', new \DateTimeZone('UTC')); + $this->dateTimeFactory->method('create') + ->willReturn($dateTime); + + $this->requestModel = new Request($this->dateTimeFactory); + } + + /** + * @param string $signatureKey + * @param string $expectedHash + * @dataProvider signRequestDataProvider + */ + public function testSignRequestData(string $signatureKey, string $expectedHash) + { + /** @var \Magento\Authorizenet\Model\Directpost $paymentMethod */ + $paymentMethod = $this->createMock(\Magento\Authorizenet\Model\Directpost::class); + $paymentMethod->method('getConfigData') + ->willReturnMap( + [ + ['test', null, true], + ['login', null, 'login'], + ['trans_key', null, 'trans_key'], + ['signature_key', null, $signatureKey], + ] + ); + + $this->requestModel->setConstantData($paymentMethod); + $this->requestModel->signRequestData(); + $signHash = $this->requestModel->getXFpHash(); + + $this->assertEquals($expectedHash, $signHash); + } + + /** + * @return array + */ + public function signRequestDataProvider() + { + return [ + [ + 'signatureKey' => '3EAFCE5697C1B4B9748385C1FCD29D86F3B9B41C7EED85A3A01DFF65' . + '70C8C29373C2A153355C3313CDF4AF723C0036DBF244A0821713A910024EE85547CEF37F', + 'expectedHash' => '719ED94DF5CF3510CB5531E8115462C8F12CBCC8E917BD809E8D40B4FF06' . + '1E14953554403DD9813CCCE0F31B184EB4DEF558E9C0747505A0C25420372DB00BE1' + ], + [ + 'signatureKey' => '', + 'expectedHash' => '3656211f2c41d1e4c083606f326c0460' + ], + ]; + } +} diff --git a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/ResponseTest.php b/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/ResponseTest.php index b4274e87401ca..0b616f32f9fe5 100644 --- a/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/ResponseTest.php +++ b/app/code/Magento/Authorizenet/Test/Unit/Model/Directpost/ResponseTest.php @@ -13,53 +13,16 @@ class ResponseTest extends \PHPUnit\Framework\TestCase /** * @var \Magento\Authorizenet\Model\Directpost\Response */ - protected $responseModel; + private $responseModel; protected function setUp() { $objectManager = new ObjectManager($this); - $this->responseModel = $objectManager->getObject(\Magento\Authorizenet\Model\Directpost\Response::class); - } - - /** - * @param string $merchantMd5 - * @param string $merchantApiLogin - * @param float|null $amount - * @param float|string $amountTestFunc - * @param string $transactionId - * @dataProvider generateHashDataProvider - */ - public function testGenerateHash($merchantMd5, $merchantApiLogin, $amount, $amountTestFunc, $transactionId) - { - $this->assertEquals( - $this->generateHash($merchantMd5, $merchantApiLogin, $amountTestFunc, $transactionId), - $this->responseModel->generateHash($merchantMd5, $merchantApiLogin, $amount, $transactionId) + $this->responseModel = $objectManager->getObject( + \Magento\Authorizenet\Model\Directpost\Response::class ); } - /** - * @return array - */ - public function generateHashDataProvider() - { - return [ - [ - 'merchantMd5' => 'FCD7F001E9274FDEFB14BFF91C799306', - 'merchantApiLogin' => 'Magento', - 'amount' => null, - 'amountTestFunc' => '0.00', - 'transactionId' => '1' - ], - [ - 'merchantMd5' => '8AEF4E508261A287C3E2F544720FCA3A', - 'merchantApiLogin' => 'Magento2', - 'amount' => 100.50, - 'amountTestFunc' => 100.50, - 'transactionId' => '2' - ] - ]; - } - /** * @param $merchantMd5 * @param $merchantApiLogin @@ -74,7 +37,8 @@ protected function generateHash($merchantMd5, $merchantApiLogin, $amount, $trans } /** - * @param string $merchantMd5 + * @param string $storedHash + * @param string $hashKey * @param string $merchantApiLogin * @param float|null $amount * @param string $transactionId @@ -82,12 +46,21 @@ protected function generateHash($merchantMd5, $merchantApiLogin, $amount, $trans * @param bool $expectedValue * @dataProvider isValidHashDataProvider */ - public function testIsValidHash($merchantMd5, $merchantApiLogin, $amount, $transactionId, $hash, $expectedValue) - { + public function testIsValidHash( + string $storedHash, + string $hashKey, + string $merchantApiLogin, + $amount, + string $transactionId, + string $hash, + bool $expectedValue + ) { $this->responseModel->setXAmount($amount); $this->responseModel->setXTransId($transactionId); - $this->responseModel->setData('x_MD5_Hash', $hash); - $this->assertEquals($expectedValue, $this->responseModel->isValidHash($merchantMd5, $merchantApiLogin)); + $this->responseModel->setData($hashKey, $hash); + $result = $this->responseModel->isValidHash($storedHash, $merchantApiLogin); + + $this->assertEquals($expectedValue, $result); } /** @@ -95,9 +68,14 @@ public function testIsValidHash($merchantMd5, $merchantApiLogin, $amount, $trans */ public function isValidHashDataProvider() { + $signatureKey = '3EAFCE5697C1B4B9748385C1FCD29D86F3B9B41C7EED85A3A01DFF6570C8C' . + '29373C2A153355C3313CDF4AF723C0036DBF244A0821713A910024EE85547CEF37F'; + $expectedSha2Hash = '368D48E0CD1274BF41C059138DA69985594021A4AD5B4C5526AE88C8F' . + '7C5769B13C5E1E4358900F3E51076FB69D14B0A797904C22E8A11A52AA49CDE5FBB703C'; return [ [ 'merchantMd5' => 'FCD7F001E9274FDEFB14BFF91C799306', + 'hashKey' => 'x_MD5_Hash', 'merchantApiLogin' => 'Magento', 'amount' => null, 'transactionId' => '1', @@ -106,11 +84,21 @@ public function isValidHashDataProvider() ], [ 'merchantMd5' => '8AEF4E508261A287C3E2F544720FCA3A', + 'hashKey' => 'x_MD5_Hash', 'merchantApiLogin' => 'Magento2', 'amount' => 100.50, 'transactionId' => '2', 'hash' => '1F24A4EC9A169B2B2A072A5F168E16DC', 'expectedValue' => false + ], + [ + 'signatureKey' => $signatureKey, + 'hashKey' => 'x_SHA2_Hash', + 'merchantApiLogin' => 'Magento2', + 'amount' => 100.50, + 'transactionId' => '2', + 'hash' => $expectedSha2Hash, + 'expectedValue' => true ] ]; } diff --git a/app/code/Magento/Authorizenet/etc/adminhtml/system.xml b/app/code/Magento/Authorizenet/etc/adminhtml/system.xml index 1319fa102d0d8..d6a935020a6dc 100644 --- a/app/code/Magento/Authorizenet/etc/adminhtml/system.xml +++ b/app/code/Magento/Authorizenet/etc/adminhtml/system.xml @@ -29,6 +29,10 @@ <label>Transaction Key</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> </field> + <field id="signature_key" translate="label" type="obscure" sortOrder="55" showInDefault="1" showInWebsite="1" showInStore="0"> + <label>Signature Key</label> + <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> + </field> <field id="trans_md5" translate="label" type="obscure" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Merchant MD5</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> diff --git a/app/code/Magento/Authorizenet/etc/config.xml b/app/code/Magento/Authorizenet/etc/config.xml index 3a192646b6f7e..a8c747f208377 100644 --- a/app/code/Magento/Authorizenet/etc/config.xml +++ b/app/code/Magento/Authorizenet/etc/config.xml @@ -22,6 +22,7 @@ <title>Credit Card Direct Post (Authorize.net) + 0 USD 1 From 9a88b705c26626e5a9b705e99c713ef58a401d1c Mon Sep 17 00:00:00 2001 From: Stas Kozar Date: Thu, 21 Feb 2019 10:43:53 +0200 Subject: [PATCH 0766/1295] MAGETWO-94147: Swatch Attribute is not displayed in the Widget CMS --- .../Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml index cd376e9e0124d..f5bb9372266e0 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml @@ -68,6 +68,7 @@ + From 927408d63aa9612cf0c9a9932a9cf5c40f9a4ce8 Mon Sep 17 00:00:00 2001 From: nmalevanec Date: Thu, 21 Feb 2019 13:04:57 +0200 Subject: [PATCH 0767/1295] Fix static tests. --- .../Magento/luma/Magento_Bundle/web/css/source/_module.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/luma/Magento_Bundle/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Bundle/web/css/source/_module.less index 262e2d68d1ea3..544414e447bed 100644 --- a/app/design/frontend/Magento/luma/Magento_Bundle/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Bundle/web/css/source/_module.less @@ -61,7 +61,7 @@ margin-top: 4px; } - input[type="checkbox"] { + input[type='checkbox'] { margin-top: 2px; } From d7cf4b76d12bebefad1278c655e71df3e8056b7c Mon Sep 17 00:00:00 2001 From: serhii balko Date: Thu, 21 Feb 2019 13:18:35 +0200 Subject: [PATCH 0768/1295] MAGETWO-96870: Tax applied on child product is based on Tax Class of parent product --- .../Sales/Total/Quote/CommonTaxCollector.php | 44 +++++++++ .../Total/Quote/CommonTaxCollectorTest.php | 94 +++++++++++++++++++ .../Magento/ConfigurableProduct/composer.json | 3 +- .../Magento/ConfigurableProduct/etc/di.xml | 3 + 4 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/ConfigurableProduct/Plugin/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php create mode 100644 app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/Tax/Model/Sales/Total/Quote/CommonTaxCollectorTest.php diff --git a/app/code/Magento/ConfigurableProduct/Plugin/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php b/app/code/Magento/ConfigurableProduct/Plugin/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php new file mode 100644 index 0000000000000..8bdde2aeb0cff --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Plugin/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php @@ -0,0 +1,44 @@ +getProduct()->getTypeId() === Configurable::TYPE_CODE && $item->getHasChildren()) { + $childItem = $item->getChildren()[0]; + $result->getTaxClassKey()->setValue($childItem->getProduct()->getTaxClassId()); + } + + return $result; + } +} diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/Tax/Model/Sales/Total/Quote/CommonTaxCollectorTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/Tax/Model/Sales/Total/Quote/CommonTaxCollectorTest.php new file mode 100644 index 0000000000000..1a5c6c0003bfa --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/Tax/Model/Sales/Total/Quote/CommonTaxCollectorTest.php @@ -0,0 +1,94 @@ +objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->commonTaxCollectorPlugin = $this->objectManager->getObject(CommonTaxCollectorPlugin::class); + } + + /** + * Test to apply Tax Class Id from child item for configurable product + */ + public function testAfterMapItem() + { + $childTaxClassId = 10; + + /** @var Product|MockObject $childProductMock */ + $childProductMock = $this->createPartialMock( + Product::class, + ['getTaxClassId'] + ); + $childProductMock->method('getTaxClassId')->willReturn($childTaxClassId); + /* @var AbstractItem|MockObject $quoteItemMock */ + $childQuoteItemMock = $this->createMock( + AbstractItem::class + ); + $childQuoteItemMock->method('getProduct')->willReturn($childProductMock); + + /** @var Product|MockObject $productMock */ + $productMock = $this->createPartialMock( + Product::class, + ['getTypeId'] + ); + $productMock->method('getTypeId')->willReturn(Configurable::TYPE_CODE); + /* @var AbstractItem|MockObject $quoteItemMock */ + $quoteItemMock = $this->createPartialMock( + AbstractItem::class, + ['getProduct', 'getHasChildren', 'getChildren', 'getQuote', 'getAddress', 'getOptionByCode'] + ); + $quoteItemMock->method('getProduct')->willReturn($productMock); + $quoteItemMock->method('getHasChildren')->willReturn(true); + $quoteItemMock->method('getChildren')->willReturn([$childQuoteItemMock]); + + /* @var TaxClassKeyInterface|MockObject $taxClassObjectMock */ + $taxClassObjectMock = $this->createMock(TaxClassKeyInterface::class); + $taxClassObjectMock->expects($this->once())->method('setValue')->with($childTaxClassId); + + /* @var QuoteDetailsItemInterface|MockObject $quoteDetailsItemMock */ + $quoteDetailsItemMock = $this->createMock(QuoteDetailsItemInterface::class); + $quoteDetailsItemMock->method('getTaxClassKey')->willReturn($taxClassObjectMock); + + $this->commonTaxCollectorPlugin->afterMapItem( + $this->createMock(CommonTaxCollector::class), + $quoteDetailsItemMock, + $this->createMock(QuoteDetailsItemInterfaceFactory::class), + $quoteItemMock + ); + } +} diff --git a/app/code/Magento/ConfigurableProduct/composer.json b/app/code/Magento/ConfigurableProduct/composer.json index 74e611af2cbbc..c33dc148cddb1 100644 --- a/app/code/Magento/ConfigurableProduct/composer.json +++ b/app/code/Magento/ConfigurableProduct/composer.json @@ -22,7 +22,8 @@ "magento/module-sales-rule": "101.0.*", "magento/module-product-video": "100.2.*", "magento/module-configurable-sample-data": "Sample Data version:100.2.*", - "magento/module-product-links-sample-data": "Sample Data version:100.2.*" + "magento/module-product-links-sample-data": "Sample Data version:100.2.*", + "magento/module-tax": "100.2.*" }, "type": "magento2-module", "version": "100.2.7", diff --git a/app/code/Magento/ConfigurableProduct/etc/di.xml b/app/code/Magento/ConfigurableProduct/etc/di.xml index e6e0da721e150..a390cb375befc 100644 --- a/app/code/Magento/ConfigurableProduct/etc/di.xml +++ b/app/code/Magento/ConfigurableProduct/etc/di.xml @@ -245,4 +245,7 @@ + + + From a1cbb1b7a2b7b3f89c98e100785be5160794d238 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov Date: Thu, 21 Feb 2019 14:31:05 +0200 Subject: [PATCH 0769/1295] MC-15012: Uneven displayed fields Status and Attributes --- .../Ui/DataProvider/Product/Form/Modifier/TierPrice.php | 2 +- .../DataProvider/Product/Form/Modifier/ConfigurablePanel.php | 1 + app/code/Magento/Ui/view/base/web/templates/form/field.html | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php index 1a9b9f205d701..9111bf544d52a 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php @@ -115,7 +115,7 @@ private function getUpdatedTierPriceStructure(array $priceMeta) 'dataType' => Price::NAME, 'component' => 'Magento_Ui/js/form/components/group', 'label' => __('Price'), - 'enableLabel' => true, + 'showLabel' => false, 'dataScope' => '', 'additionalClasses' => 'control-grouped', 'sortOrder' => isset($priceMeta['arguments']['data']['config']['sortOrder']) diff --git a/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/ConfigurablePanel.php b/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/ConfigurablePanel.php index 9fd225e8acaab..e1774cfb91750 100644 --- a/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/ConfigurablePanel.php +++ b/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/ConfigurablePanel.php @@ -591,6 +591,7 @@ protected function getColumn( 'formElement' => Container::NAME, 'component' => 'Magento_Ui/js/form/components/group', 'label' => $label, + 'showLabel' => false, 'dataScope' => '', ]; $container['children'] = [ 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 ed84e158819a2..376f165279f5d 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,8 +8,8 @@ visible="visible" css="$data.additionalClasses" attr="'data-index': index"> -
-
diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/file.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/file.phtml index 4ad7a95c91980..dd40cc68ac1ed 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/file.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/file.phtml @@ -64,8 +64,10 @@ require(['prototype'], function(){
diff --git a/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module.less index eda29bb171a33..4cb6bdc59f722 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Catalog/web/css/source/_module.less @@ -33,8 +33,7 @@ .admin__field { &.required, &._required { - & > .admin__field-label > span{ - + & > .admin__field-label > span { width: 100%; & span:after { From 52d1c806618ce6d654cac6153260ed14081b8133 Mon Sep 17 00:00:00 2001 From: Viktor Petryk Date: Thu, 21 Feb 2019 16:48:01 +0200 Subject: [PATCH 0771/1295] MAGETWO-73985: Simple product with MAP assigned to configurable should displays the same way as products with special price --- .../Block/Product/View/Type/Configurable.php | 9 ++ .../Model/Product/Type/Configurable.php | 3 +- .../Product/View/Type/ConfigurableTest.php | 15 +- .../view/frontend/web/js/configurable.js | 7 + app/code/Magento/Msrp/Helper/Data.php | 23 ++- app/code/Magento/Msrp/composer.json | 1 + .../base/templates/product/price/msrp.phtml | 103 +++++++------ .../Magento/Msrp/view/base/web/js/msrp.js | 137 +++++++++++++++--- .../Msrp/view/frontend/templates/popup.phtml | 7 +- .../view/frontend/web/js/swatch-renderer.js | 9 +- .../Magento_Msrp/web/css/source/_module.less | 4 + .../Magento_Msrp/web/css/source/_module.less | 4 + 12 files changed, 246 insertions(+), 76 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Block/Product/View/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Block/Product/View/Type/Configurable.php index a80a15b59c2ce..efade5cd2c605 100644 --- a/app/code/Magento/ConfigurableProduct/Block/Product/View/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Block/Product/View/Type/Configurable.php @@ -15,6 +15,8 @@ use Magento\Framework\Pricing\PriceCurrencyInterface; /** + * Configurable product view type. + * * @api * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @api @@ -277,6 +279,8 @@ protected function getOptionImages() } /** + * Collect price options. + * * @return array */ protected function getOptionPrices() @@ -315,6 +319,11 @@ protected function getOptionPrices() ), ], 'tierPrices' => $tierPrices, + 'msrpPrice' => [ + 'amount' => $this->localeFormat->getNumber( + $product->getMsrp() + ), + ], ]; } return $prices; diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php index f98075f2294cc..46f10608bc95e 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php @@ -24,6 +24,7 @@ * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @api * @since 100.0.2 */ @@ -1385,7 +1386,7 @@ function ($item) { */ private function getUsedProductsCacheKey($keyParts) { - return md5(implode('_', $keyParts)); + return sha1(implode('_', $keyParts)); } /** diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php index b45306d670bff..20b0905b7707b 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php @@ -347,15 +347,15 @@ public function testGetJsonConfig() } /** - * Retrieve array with expected parameters for method getJsonConfig() + * Retrieve array with expected parameters for method getJsonConfig(). * - * @param $productId - * @param $amount - * @param $priceQty - * @param $percentage + * @param int $productId + * @param float $amount + * @param int $priceQty + * @param int $percentage * @return array */ - private function getExpectedArray($productId, $amount, $priceQty, $percentage) + private function getExpectedArray(int $productId, float $amount, int $priceQty, int $percentage): array { $expectedArray = [ 'attributes' => [], @@ -379,6 +379,9 @@ private function getExpectedArray($productId, $amount, $priceQty, $percentage) 'percentage' => $percentage, ], ], + 'msrpPrice' => [ + 'amount' => null, + ], ], ], 'priceFormat' => [], diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js index 1df84d27a5c30..c343ef25d7dfb 100644 --- a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js +++ b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js @@ -609,6 +609,13 @@ define([ } else { $(this.options.slyOldPriceSelector).hide(); } + + $(document).trigger('updateMsrpPriceBlock', + [ + optionId, + this.options.spConfig.optionPrices, + ] + ); }, /** diff --git a/app/code/Magento/Msrp/Helper/Data.php b/app/code/Magento/Msrp/Helper/Data.php index b4ec34ebee19c..e8f8ca17a8b64 100644 --- a/app/code/Magento/Msrp/Helper/Data.php +++ b/app/code/Magento/Msrp/Helper/Data.php @@ -11,6 +11,8 @@ use Magento\Store\Model\StoreManagerInterface; use Magento\Catalog\Model\Product; use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\GroupedProduct\Model\Product\Type\Grouped; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; /** * Msrp data helper @@ -70,8 +72,7 @@ public function __construct( } /** - * Check if can apply Minimum Advertise price to product - * in specific visibility + * Check if can apply MAP to product in specific visibility. * * @param int|Product $product * @param int|null $visibility Check displaying price in concrete place (by default generally) @@ -135,6 +136,8 @@ public function isShowPriceOnGesture($product) } /** + * Check if should show MAP price before order confirmation. + * * @param int|Product $product * @return bool */ @@ -144,6 +147,8 @@ public function isShowBeforeOrderConfirm($product) } /** + * Check if any MAP price is larger than "As low as" value. + * * @param int|Product $product * @return bool|float */ @@ -155,10 +160,18 @@ public function isMinimalPriceLessMsrp($product) $msrp = $product->getMsrp(); $price = $product->getPriceInfo()->getPrice(\Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE); if ($msrp === null) { - if ($product->getTypeId() !== \Magento\GroupedProduct\Model\Product\Type\Grouped::TYPE_CODE) { - return false; - } else { + if ($product->getTypeId() === Grouped::TYPE_CODE) { $msrp = $product->getTypeInstance()->getChildrenMsrp($product); + } elseif ($product->getTypeId() === Configurable::TYPE_CODE) { + $prices = []; + foreach ($product->getTypeInstance()->getUsedProducts($product) as $item) { + if ($item->getMsrp() !== null) { + $prices[] = $item->getMsrp(); + } + } + $msrp = $prices ? max($prices) : 0; + } else { + return false; } } if ($msrp) { diff --git a/app/code/Magento/Msrp/composer.json b/app/code/Magento/Msrp/composer.json index 625bec5d6c9d2..3fef9b17d4bd9 100644 --- a/app/code/Magento/Msrp/composer.json +++ b/app/code/Magento/Msrp/composer.json @@ -8,6 +8,7 @@ "magento/module-downloadable": "100.2.*", "magento/module-eav": "101.0.*", "magento/module-grouped-product": "100.2.*", + "magento/module-configurable-product": "100.2.*", "magento/module-tax": "100.2.*", "magento/framework": "101.0.*" }, diff --git a/app/code/Magento/Msrp/view/base/templates/product/price/msrp.phtml b/app/code/Magento/Msrp/view/base/templates/product/price/msrp.phtml index 869d81563645a..d7c6132d747be 100644 --- a/app/code/Magento/Msrp/view/base/templates/product/price/msrp.phtml +++ b/app/code/Magento/Msrp/view/base/templates/product/price/msrp.phtml @@ -20,61 +20,80 @@ $priceType = $block->getPrice(); /** @var $product \Magento\Catalog\Model\Product */ $product = $block->getSaleableItem(); $productId = $product->getId(); +$amount = 0; + +if ($product->getMsrp()) { + $amount = $product->getMsrp(); +} elseif ($product->getTypeId() === \Magento\GroupedProduct\Model\Product\Type\Grouped::TYPE_CODE) { + $amount = $product->getTypeInstance()->getChildrenMsrp($product); +} elseif ($product->getTypeId() === \Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE) { + foreach ($product->getTypeInstance()->getUsedProducts($product) as $item) { + if ($item->getMsrp() !== null) { + $prices[] = $item->getMsrp(); + } + } + $amount = $prices ? max($prices) : 0; +} + $msrpPrice = $block->renderAmount( - $priceType->getCustomAmount($product->getMsrp() ?: $product->getTypeInstance()->getChildrenMsrp($product)), + $priceType->getCustomAmount($amount), [ 'price_id' => $block->getPriceId() ? $block->getPriceId() : 'old-price-' . $productId, 'include_container' => false, - 'skip_adjustments' => true + 'skip_adjustments' => true, ] ); $priceElementIdPrefix = $block->getPriceElementIdPrefix() ? $block->getPriceElementIdPrefix() : 'product-price-'; - -$addToCartUrl = ''; -if ($product->isSaleable()) { - /** @var Magento\Catalog\Block\Product\AbstractProduct $addToCartUrlGenerator */ - $addToCartUrlGenerator = $block->getLayout()->getBlockSingleton('Magento\Catalog\Block\Product\AbstractProduct'); - $addToCartUrl = $addToCartUrlGenerator->getAddToCartUrl( - $product, - ['_query' => [ - \Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED => - $this->helper('Magento\Framework\Url\Helper\Data')->getEncodedUrl( - $addToCartUrlGenerator->getAddToCartUrl($product) - ), - ]] - ); -} ?> -getMsrp()): ?> + + + isShowPriceOnGesture()): ?> getIdSuffix(); - $popupId = 'msrp-popup-' . $productId . $block->getRandomString(20); - $data = ['addToCart' => [ - 'origin'=> 'msrp', - 'popupId' => '#' . $popupId, - 'productName' => $block->escapeJs($block->escapeHtml($product->getName())), - 'productId' => $productId, - 'productIdInput' => 'input[type="hidden"][name="product"]', - 'realPrice' => $block->getRealPriceHtml(), - 'isSaleable' => $product->isSaleable(), - 'msrpPrice' => $msrpPrice, - 'priceElementId' => $priceElementId, - 'closeButtonId' => '#map-popup-close', - 'addToCartUrl' => $addToCartUrl, - 'paymentButtons' => '[data-label=or]' - ]]; - if ($block->getRequest()->getFullActionName() === 'catalog_product_view') { - $data['addToCart']['addToCartButton'] = '#product_addtocart_form [type=submit]'; - } else { - $data['addToCart']['addToCartButton'] = sprintf( - 'form:has(input[type="hidden"][name="product"][value="%s"]) button[type="submit"]', - (int) $productId - ); - } + + $addToCartUrl = ''; + if ($product->isSaleable()) { + /** @var Magento\Catalog\Block\Product\AbstractProduct $addToCartUrlGenerator */ + $addToCartUrlGenerator = $block->getLayout()->getBlockSingleton('Magento\Catalog\Block\Product\AbstractProduct'); + $addToCartUrl = $addToCartUrlGenerator->getAddToCartUrl( + $product, + ['_query' => [ + \Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED => + $this->helper('Magento\Framework\Url\Helper\Data')->getEncodedUrl( + $addToCartUrlGenerator->getAddToCartUrl($product) + ), + ]] + ); + } + + $priceElementId = $priceElementIdPrefix . $productId . $block->getIdSuffix(); + $popupId = 'msrp-popup-' . $productId . $block->getRandomString(20); + $data = ['addToCart' => [ + 'origin' => 'msrp', + 'popupId' => '#' . $popupId, + 'productName' => $block->escapeJs($block->escapeHtml($product->getName())), + 'productId' => $productId, + 'productIdInput' => 'input[type="hidden"][name="product"]', + 'realPrice' => $block->getRealPriceHtml(), + 'isSaleable' => $product->isSaleable(), + 'msrpPrice' => $msrpPrice, + 'priceElementId' => $priceElementId, + 'closeButtonId' => '#map-popup-close', + 'addToCartUrl' => $addToCartUrl, + 'paymentButtons' => '[data-label=or]' + ]]; + if ($block->getRequest()->getFullActionName() === 'catalog_product_view') { + $data['addToCart']['addToCartButton'] = '#product_addtocart_form [type=submit]'; + } else { + $data['addToCart']['addToCartButton'] = sprintf( + 'form:has(input[type="hidden"][name="product"][value="%s"]) button[type="submit"]', + (int) $productId . ',' . + sprintf('.block.widget .price-box[data-product-id=%s]+.product-item-actions button.tocart', + (int) $productId)); + } ?>
-
- + +
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 3e28982ad44d3..40201238e8e6c 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 @@ -493,7 +493,7 @@ define([ return ''; } - $.each(config.options, function () { + $.each(config.options, function (index) { var id, type, value, @@ -521,6 +521,7 @@ define([ label = this.label ? this.label : ''; attr = ' id="' + controlId + '-item-' + id + '"' + + ' index="' + index + '"' + ' aria-checked="false"' + ' aria-describedby="' + controlId + '"' + ' tabindex="0"' + @@ -746,6 +747,12 @@ define([ $widget._UpdatePrice(); } + $(document).trigger('updateMsrpPriceBlock', + [ + parseInt($this.attr('index'), 10) + 1, + $widget.options.jsonConfig.optionPrices + ]); + $widget._loadMedia(eventName); $input.trigger('change'); }, diff --git a/app/design/frontend/Magento/blank/Magento_Msrp/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Msrp/web/css/source/_module.less index f0dd8a957e9b5..6e2069c6e88ef 100644 --- a/app/design/frontend/Magento/blank/Magento_Msrp/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Msrp/web/css/source/_module.less @@ -55,6 +55,10 @@ } } + .map-fallback-price { + display: none; + } + .map-old-price { text-decoration: none; diff --git a/app/design/frontend/Magento/luma/Magento_Msrp/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Msrp/web/css/source/_module.less index 112184b45fe86..475361c56afc8 100644 --- a/app/design/frontend/Magento/luma/Magento_Msrp/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Msrp/web/css/source/_module.less @@ -74,6 +74,10 @@ } } + .map-fallback-price { + display: none; + } + .map-old-price, .product-item .map-old-price, .product-info-price .map-show-info { From 990c249c874c5192ee0688042d93014d4908c061 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun Date: Fri, 22 Feb 2019 11:29:52 +0200 Subject: [PATCH 0772/1295] MAGETWO-90192: Prefix in checkout not starting with blank value --- .../Checkout/Test/Mftf/Section/CheckoutShippingSection.xml | 5 +++-- app/code/Magento/Customer/Block/Widget/Name.php | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml index 66cd480051905..b735abc665022 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml @@ -33,8 +33,9 @@ - + + - + diff --git a/app/code/Magento/Customer/Block/Widget/Name.php b/app/code/Magento/Customer/Block/Widget/Name.php index 4411f40fe41f3..2576545601c73 100644 --- a/app/code/Magento/Customer/Block/Widget/Name.php +++ b/app/code/Magento/Customer/Block/Widget/Name.php @@ -246,6 +246,7 @@ public function getStoreLabel($attributeCode) public function getAttributeValidationClass($attributeCode) { $attributeMetadata = $this->_getAttribute($attributeCode); + return $attributeMetadata ? $attributeMetadata->getFrontendClass() : ''; } From c1d0a23162bb18cd943fe02d7386c740246aea0c Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun Date: Fri, 22 Feb 2019 15:23:25 +0200 Subject: [PATCH 0773/1295] MAGETWO-95762: Resource model changes --- .../Catalog/Model/ResourceModel/AbstractResource.php | 2 +- app/code/Magento/Catalog/Model/ResourceModel/Product.php | 6 +++--- .../Catalog/Test/Mftf/Section/AdminProductFormSection.xml | 2 +- .../Model/Entity/Attribute/UniqueValidationInterface.php | 2 +- .../Magento/Eav/Model/Entity/Attribute/UniqueValidator.php | 3 ++- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php b/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php index 7f50c7edbb049..6614b10ef609e 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php @@ -101,7 +101,7 @@ protected function _isApplicableAttribute($object, $attribute) */ protected function _isCallableAttributeInstance($instance, $method, $args) { - if ($instance instanceof \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend + if ($instance instanceof AbstractBackend && ($method == 'beforeSave' || $method == 'afterSave') ) { $attributeCode = $instance->getAttribute()->getAttributeCode(); diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product.php b/app/code/Magento/Catalog/Model/ResourceModel/Product.php index 6998431d19a8f..e438e2f54113d 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product.php @@ -661,7 +661,7 @@ public function save(\Magento\Framework\Model\AbstractModel $object) } /** - * Retrieve entity manager object + * Retrieve entity manager object. * * @return \Magento\Framework\EntityManager\EntityManager */ @@ -675,7 +675,7 @@ private function getEntityManager() } /** - * Retrieve ProductWebsiteLink object + * Retrieve ProductWebsiteLink object. * * @deprecated 101.1.0 * @return ProductWebsiteLink @@ -686,7 +686,7 @@ private function getProductWebsiteLink() } /** - * Retrieve CategoryLink object + * Retrieve CategoryLink object. * * @deprecated 101.1.0 * @return \Magento\Catalog\Model\ResourceModel\Product\CategoryLink diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml index ed5a0480325dd..5e7fe16727f65 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml @@ -40,7 +40,7 @@ - +
diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/UniqueValidationInterface.php b/app/code/Magento/Eav/Model/Entity/Attribute/UniqueValidationInterface.php index f4912f25ae1de..50a6ff9329fc9 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/UniqueValidationInterface.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/UniqueValidationInterface.php @@ -31,5 +31,5 @@ public function validate( AbstractEntity $entity, string $entityLinkField, array $entityIds - ); + ): bool; } diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/UniqueValidator.php b/app/code/Magento/Eav/Model/Entity/Attribute/UniqueValidator.php index da0d0e07f303a..9aa501daf1584 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/UniqueValidator.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/UniqueValidator.php @@ -24,10 +24,11 @@ public function validate( AbstractEntity $entity, string $entityLinkField, array $entityIds - ) { + ): bool { if (isset($entityIds[0])) { return $entityIds[0] == $object->getData($entityLinkField); } + return true; } } From 4fe746122005744398f848393e78aa11dc3254f8 Mon Sep 17 00:00:00 2001 From: Viktor Sevch Date: Fri, 22 Feb 2019 15:45:54 +0200 Subject: [PATCH 0774/1295] MAGETWO-95422: Layered Navigation shows options not available in Catalog --- .../AdminProductAttributeActionGroup.xml | 2 +- .../Mftf/Page/AdminProductAttributeNewPage.xml | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeNewPage.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml index e82633f1321f0..f043b82f2d44f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml @@ -79,7 +79,7 @@ - + diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeNewPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeNewPage.xml new file mode 100644 index 0000000000000..9116e9ae7446f --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductAttributeNewPage.xml @@ -0,0 +1,17 @@ + + + + +
+
+
+
+
+ + From a3a70c585c3e4725944e80ab8d5f9b7a98563e81 Mon Sep 17 00:00:00 2001 From: Viktor Petryk Date: Fri, 22 Feb 2019 16:31:59 +0200 Subject: [PATCH 0775/1295] MAGETWO-73985: Simple product with MAP assigned to configurable should displays the same way as products with special price --- .../ConfigurableProduct/view/frontend/web/js/configurable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js index c343ef25d7dfb..e732960421541 100644 --- a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js +++ b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js @@ -613,7 +613,7 @@ define([ $(document).trigger('updateMsrpPriceBlock', [ optionId, - this.options.spConfig.optionPrices, + this.options.spConfig.optionPrices ] ); }, From b60001e94c0dadfba2beaca493376ffc08e2659e Mon Sep 17 00:00:00 2001 From: Stas Kozar Date: Fri, 22 Feb 2019 17:02:55 +0200 Subject: [PATCH 0776/1295] MAGETWO-95616: Changing Attribute Set may lead to exception "Attempt to load value of nonexistent EAV attribute" --- .../Eav/Model/ResourceModel/ReadHandler.php | 77 ++++- .../Model/ResourceModel/ReadHandlerTest.php | 273 ++++++++++++------ .../Framework/DB/Sql/UnionExpression.php | 20 +- .../DB/Test/Unit/Sql/UnionExpressionTest.php | 44 ++- 4 files changed, 307 insertions(+), 107 deletions(-) diff --git a/app/code/Magento/Eav/Model/ResourceModel/ReadHandler.php b/app/code/Magento/Eav/Model/ResourceModel/ReadHandler.php index cd2fe7477ca60..18bb62b3f7d93 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/ReadHandler.php +++ b/app/code/Magento/Eav/Model/ResourceModel/ReadHandler.php @@ -5,13 +5,19 @@ */ namespace Magento\Eav\Model\ResourceModel; +use Magento\Eav\Model\Config; use Magento\Framework\DataObject; +use Magento\Framework\DB\Select; +use Magento\Framework\DB\Sql\UnionExpression; use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\EntityManager\Operation\AttributeInterface; use Magento\Framework\Model\Entity\ScopeInterface; use Magento\Framework\Model\Entity\ScopeResolver; use Psr\Log\LoggerInterface; +/** + * EAV read handler. + */ class ReadHandler implements AttributeInterface { /** @@ -30,23 +36,21 @@ class ReadHandler implements AttributeInterface private $logger; /** - * @var \Magento\Eav\Model\Config + * @var Config */ private $config; /** - * ReadHandler constructor. - * * @param MetadataPool $metadataPool * @param ScopeResolver $scopeResolver * @param LoggerInterface $logger - * @param \Magento\Eav\Model\Config $config + * @param Config $config */ public function __construct( MetadataPool $metadataPool, ScopeResolver $scopeResolver, LoggerInterface $logger, - \Magento\Eav\Model\Config $config + Config $config ) { $this->metadataPool = $metadataPool; $this->scopeResolver = $scopeResolver; @@ -86,6 +90,8 @@ private function getEntityAttributes(string $entityType, DataObject $entity): ar } /** + * Get context variables. + * * @param ScopeInterface $scope * @return array */ @@ -99,6 +105,8 @@ protected function getContextVariables(ScopeInterface $scope) } /** + * Execute read handler. + * * @param string $entityType * @param array $entityData * @param array $arguments @@ -129,33 +137,40 @@ public function execute($entityType, $entityData, $arguments = []) } } if (count($attributeTables)) { - $attributeTables = array_keys($attributeTables); - foreach ($attributeTables as $attributeTable) { + $identifiers = null; + foreach ($attributeTables as $attributeTable => $attributeIds) { $select = $connection->select() ->from( ['t' => $attributeTable], ['value' => 't.value', 'attribute_id' => 't.attribute_id'] ) - ->where($metadata->getLinkField() . ' = ?', $entityData[$metadata->getLinkField()]); + ->where($metadata->getLinkField() . ' = ?', $entityData[$metadata->getLinkField()]) + ->where('attribute_id IN (?)', $attributeIds); + $attributeIdentifiers = []; foreach ($context as $scope) { //TODO: if (in table exists context field) $select->where( - $metadata->getEntityConnection()->quoteIdentifier($scope->getIdentifier()) . ' IN (?)', + $connection->quoteIdentifier($scope->getIdentifier()) . ' IN (?)', $this->getContextVariables($scope) - )->order('t.' . $scope->getIdentifier() . ' DESC'); + ); + $attributeIdentifiers[] = $scope->getIdentifier(); } + $attributeIdentifiers = array_unique($attributeIdentifiers); + $identifiers = array_intersect($identifiers ?? $attributeIdentifiers, $attributeIdentifiers); $selects[] = $select; } - $unionSelect = new \Magento\Framework\DB\Sql\UnionExpression( - $selects, - \Magento\Framework\DB\Select::SQL_UNION_ALL - ); - foreach ($connection->fetchAll($unionSelect) as $attributeValue) { + $this->applyIdentifierForSelects($selects, $identifiers); + $unionSelect = new UnionExpression($selects, Select::SQL_UNION_ALL, '( %s )'); + $orderedUnionSelect = $connection->select(); + $orderedUnionSelect->from(['u' => $unionSelect]); + $this->applyIdentifierForUnion($orderedUnionSelect, $identifiers); + $attributes = $connection->fetchAll($orderedUnionSelect); + foreach ($attributes as $attributeValue) { if (isset($attributesMap[$attributeValue['attribute_id']])) { $entityData[$attributesMap[$attributeValue['attribute_id']]] = $attributeValue['value']; } else { $this->logger->warning( - "Attempt to load value of nonexistent EAV attribute '{$attributeValue['attribute_id']}' + "Attempt to load value of nonexistent EAV attribute '{$attributeValue['attribute_id']}' for entity type '$entityType'." ); } @@ -163,4 +178,34 @@ public function execute($entityType, $entityData, $arguments = []) } return $entityData; } + + /** + * Apply identifiers column on select array. + * + * @param Select[] $selects + * @param array $identifiers + * @return void + */ + private function applyIdentifierForSelects(array $selects, array $identifiers) + { + foreach ($selects as $select) { + foreach ($identifiers as $identifier) { + $select->columns($identifier, 't'); + } + } + } + + /** + * Apply identifiers order on union select. + * + * @param Select $unionSelect + * @param array $identifiers + * @return void + */ + private function applyIdentifierForUnion(Select $unionSelect, array $identifiers) + { + foreach ($identifiers as $identifier) { + $unionSelect->order($identifier); + } + } } diff --git a/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/ReadHandlerTest.php b/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/ReadHandlerTest.php index 82e9033496b78..5f74579e32a12 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/ReadHandlerTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/ReadHandlerTest.php @@ -5,19 +5,30 @@ */ namespace Magento\Eav\Test\Unit\Model\ResourceModel; +use Magento\Eav\Model\Config; use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; +use Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend; +use Magento\Eav\Model\ResourceModel\ReadHandler; +use Magento\Framework\DataObject; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Select; +use Magento\Framework\DB\Sql\UnionExpression; use Magento\Framework\EntityManager\EntityMetadataInterface; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Framework\Model\Entity\ScopeResolver; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\Model\Entity\ScopeInterface; +use Psr\Log\LoggerInterface; class ReadHandlerTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Eav\Model\Config|\PHPUnit_Framework_MockObject_MockObject + * @var Config|\PHPUnit_Framework_MockObject_MockObject */ private $configMock; /** - * @var \Magento\Framework\EntityManager\MetadataPool|\PHPUnit_Framework_MockObject_MockObject + * @var MetadataPool|\PHPUnit_Framework_MockObject_MockObject */ private $metadataPoolMock; @@ -27,112 +38,206 @@ class ReadHandlerTest extends \PHPUnit\Framework\TestCase private $metadataMock; /** - * @var \Magento\Eav\Model\ResourceModel\ReadHandler + * @var ScopeResolver|\PHPUnit_Framework_MockObject_MockObject */ - private $readHandler; + private $scopeResolverMock; /** - * @var \Magento\Framework\Model\Entity\ScopeResolver|\PHPUnit_Framework_MockObject_MockObject + * @var LoggerInterface|\PHPUnit_Framework_MockObject_MockObject */ - private $scopeResolverMock; + private $loggerMock; + + /** + * @var ScopeInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $scopeMock; + + /** + * @var AdapterInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $connectionMock; + + /** + * @var AbstractAttribute|\PHPUnit_Framework_MockObject_MockObject + */ + private $attributeMock; + + /** + * @var ObjectManager + */ + private $objectManager; + /** + * @var ReadHandler + */ + private $readHandler; + + /** + * @inheritdoc + */ protected function setUp() { - $objectManager = new ObjectManager($this); - $args = $objectManager->getConstructArguments(\Magento\Eav\Model\ResourceModel\ReadHandler::class); - $this->metadataPoolMock = $args['metadataPool']; - $this->metadataMock = $this->getMockBuilder(EntityMetadataInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->metadataPoolMock->expects($this->any()) - ->method('getMetadata') - ->willReturn($this->metadataMock); - $this->configMock = $args['config']; - $this->scopeResolverMock = $args['scopeResolver']; - $this->scopeResolverMock->method('getEntityContext') - ->willReturn([]); + $this->objectManager = new ObjectManager($this); - $this->readHandler = $objectManager->getObject(\Magento\Eav\Model\ResourceModel\ReadHandler::class, $args); + $this->metadataPoolMock = $this->createMock(MetadataPool::class); + $this->scopeResolverMock = $this->createMock(ScopeResolver::class); + $this->loggerMock = $this->createMock(LoggerInterface::class); + $this->configMock = $this->createMock(Config::class); + $this->metadataMock = $this->createMock(EntityMetadataInterface::class); + $this->scopeMock = $this->createMock(ScopeInterface::class); + $this->connectionMock = $this->createMock(AdapterInterface::class); + $this->attributeMock = $this->createMock(AbstractAttribute::class); + + $this->readHandler = $this->objectManager->getObject(ReadHandler::class, + [ + 'metadataPool' => $this->metadataPoolMock, + 'scopeResolver' => $this->scopeResolverMock, + 'logger' => $this->loggerMock, + 'config' => $this->configMock, + ] + ); } /** - * @param string $eavEntityType - * @param int $callNum - * @param array $expected - * @param bool $isStatic - * @dataProvider executeDataProvider + * @return void */ - public function testExecute($eavEntityType, $callNum, array $expected, $isStatic = true) + public function testExecuteNew() { + $eavEntityType = 'env-entity-type'; $entityData = ['linkField' => 'theLinkField']; - $this->metadataMock->method('getEavEntityType') - ->willReturn($eavEntityType); - $connectionMock = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $selectMock = $this->getMockBuilder(\Magento\Framework\DB\Select::class) - ->disableOriginalConstructor() - ->getMock(); - $selectMock->method('from') + $attributeId = '1'; + $attributeCode = 'attribute_code'; + $identifier = 'store_id'; + $expectedEntityData = [ + 'linkField' => 'theLinkField', + $attributeCode => $attributeId, + ]; + $entityDataObject = $this->objectManager->getObject(DataObject::class, ['data' => $entityData]); + $abstractBackendMock = $this->createMock(AbstractBackend::class); + $selectMock = $this->createMock(Select::class); + $orderedUnionSelectMock = $this->createMock(Select::class); + $fallbackScopeMock = $this->createMock(ScopeInterface::class); + + $this->metadataPoolMock->expects($this->exactly(2)) + ->method('getMetadata') + ->willReturn($this->metadataMock); + $this->metadataMock->expects($this->exactly(2))->method('getEavEntityType')->willReturn($eavEntityType); + $this->scopeResolverMock->expects($this->once()) + ->method('getEntityContext') + ->with($eavEntityType, $entityData) + ->willReturn([$this->scopeMock]); + $this->metadataMock->expects($this->once())->method('getEntityConnection')->willReturn($this->connectionMock); + $this->configMock->expects($this->once()) + ->method('getEntityAttributes') + ->with($eavEntityType, $entityDataObject) + ->willReturn([$this->attributeMock]); + $this->attributeMock->expects($this->once())->method('isStatic')->willReturn(false); + $this->attributeMock->expects($this->once())->method('getBackend')->willReturn($abstractBackendMock); + $abstractBackendMock->expects($this->once())->method('getTable')->willReturn('some_table'); + $this->attributeMock->expects($this->exactly(2))->method('getAttributeId')->willReturn($attributeId); + $this->attributeMock->expects($this->once())->method('getAttributeCode')->willReturn($attributeCode); + $this->connectionMock->expects($this->at(0))->method('select')->willReturn($selectMock); + $selectMock->expects($this->at(0)) + ->method('from') + ->with( + [ + 't' => 'some_table' + ], + ['value' => 't.value', 'attribute_id' => 't.attribute_id'] + ) + ->willReturnSelf(); + $this->metadataMock->expects($this->exactly(2))->method('getLinkField')->willReturn('linkField'); + $selectMock->expects($this->at(1)) + ->method('where') + ->with('linkField = ?', 'theLinkField') ->willReturnSelf(); - $selectMock->method('where') + $selectMock->expects($this->at(2)) + ->method('where') + ->with('attribute_id IN (?)', [$attributeId]) ->willReturnSelf(); - $connectionMock->method('select') - ->willReturn($selectMock); - $connectionMock->method('fetchAll') - ->willReturn( + $this->scopeMock->expects($this->exactly(2))->method('getIdentifier')->willReturn($identifier); + $this->connectionMock->expects($this->at(1)) + ->method('quoteIdentifier') + ->with($identifier) + ->willReturn("`$identifier`"); + $selectMock->expects($this->at(3)) + ->method('where') + ->with( + "`$identifier` IN (?)", [ - [ - 'attribute_id' => 'attributeId', - 'value' => 'attributeValue', - ] + 0 => '1', + 1 => 0, ] - ); - $this->metadataMock->method('getEntityConnection') - ->willReturn($connectionMock); - $this->metadataMock->method('getLinkField') - ->willReturn('linkField'); - - $attributeMock = $this->getMockBuilder(AbstractAttribute::class) - ->disableOriginalConstructor() - ->getMock(); - $attributeMock->method('isStatic') - ->willReturn($isStatic); - $backendMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend::class) - ->disableOriginalConstructor() - ->getMock(); - $backendMock->method('getTable') - ->willReturn('backendTable'); - $attributeMock->method('getBackend') - ->willReturn($backendMock); - $attributeMock->method('getAttributeId') - ->willReturn('attributeId'); - $attributeMock->method('getAttributeCode') - ->willReturn('attributeCode'); - $this->configMock->expects($this->exactly($callNum)) + )->willReturnSelf(); + $this->scopeMock->expects($this->once())->method('getValue')->willReturn('1'); + $this->scopeMock->expects($this->exactly(2))->method('getFallback')->willReturn($fallbackScopeMock); + $fallbackScopeMock->expects($this->once())->method('getValue')->willReturn(0); + $fallbackScopeMock->expects($this->once())->method('getFallback')->willReturn(null); + $selectMock->expects($this->at(4))->method('columns')->with($identifier, 't')->willReturnSelf(); + $this->connectionMock->expects($this->at(2))->method('select')->willReturn($orderedUnionSelectMock); + + $unionSelect = $this->objectManager->getObject(UnionExpression::class, + [ + 'parts' => [$selectMock], + 'type' => 'UNION ALL', + 'pattern' => '( %s )', + ] + ); + $orderedUnionSelectMock->expects($this->once())->method('from')->with(['u' => $unionSelect])->willReturnSelf(); + $orderedUnionSelectMock->expects($this->once())->method('order')->with($identifier)->willReturnSelf(); + $this->connectionMock->expects($this->at(3)) + ->method('fetchAll') + ->with($orderedUnionSelectMock) + ->willReturn([ + [ + 'attribute_id' => '1', + 'value' => '1', + 'store_id' => '1', + ] + ]); + + $this->assertEquals($expectedEntityData, $this->readHandler->execute($eavEntityType, $entityData)); + } + + /** + * @return void + */ + public function testExecuteWithStaticAttributeAttributes() + { + $eavEntityType = 'env-entity-type'; + $entityData = ['linkField' => 'theLinkField']; + $entityDataObject = $this->objectManager->getObject(DataObject::class, ['data' => $entityData]); + $this->attributeMock = $this->createMock(AbstractAttribute::class); + + $this->metadataPoolMock->expects($this->exactly(2)) + ->method('getMetadata') + ->willReturn($this->metadataMock); + $this->metadataMock->expects($this->exactly(2))->method('getEavEntityType')->willReturn($eavEntityType); + $this->scopeResolverMock->expects($this->once()) + ->method('getEntityContext') + ->with($eavEntityType, $entityData) + ->willReturn([$this->scopeMock]); + $this->metadataMock->expects($this->once())->method('getEntityConnection')->willReturn($this->connectionMock); + $this->configMock->expects($this->once()) ->method('getEntityAttributes') - ->willReturn([$attributeMock]); - $this->assertEquals($expected, $this->readHandler->execute('entity_type', $entityData)); + ->with($eavEntityType, $entityDataObject) + ->willReturn([$this->attributeMock]); + $this->attributeMock->expects($this->once())->method('isStatic')->willReturn(true); + + $this->assertEquals($entityData, $this->readHandler->execute($eavEntityType, $entityData)); } /** - * @return array + * @return void */ - public function executeDataProvider() + public function testExecuteWithoutAttribute() { - return [ - 'null entity type' => [null, 0, ['linkField' => 'theLinkField']], - 'static attribute' => ['env-entity-type', 1, ['linkField' => 'theLinkField']], - 'non-static attribute' => [ - 'env-entity-type', - 1, - [ - 'linkField' => 'theLinkField', - 'attributeCode' => 'attributeValue' - ], - false - ], - ]; + $entityData = ['linkField' => 'theLinkField']; + + $this->metadataPoolMock->expects($this->once())->method('getMetadata')->willReturn($this->metadataMock); + $this->metadataMock->expects($this->once())->method('getEavEntityType')->willReturn(null); + + $this->assertEquals($entityData, $this->readHandler->execute('env-entity-type', $entityData)); } /** diff --git a/lib/internal/Magento/Framework/DB/Sql/UnionExpression.php b/lib/internal/Magento/Framework/DB/Sql/UnionExpression.php index 3ce78177d875f..9957c6ffb8272 100644 --- a/lib/internal/Magento/Framework/DB/Sql/UnionExpression.php +++ b/lib/internal/Magento/Framework/DB/Sql/UnionExpression.php @@ -22,18 +22,25 @@ class UnionExpression extends Expression */ protected $type; + /** + * @var string + */ + private $pattern; + /** * @param Select[] $parts - * @param string $type + * @param string $type (optional) + * @param string $pattern (optional) */ - public function __construct(array $parts, $type = Select::SQL_UNION) + public function __construct(array $parts, string $type = Select::SQL_UNION, string $pattern = '') { $this->parts = $parts; $this->type = $type; + $this->pattern = $pattern; } /** - * @return string + * @inheritdoc */ public function __toString() { @@ -45,6 +52,11 @@ public function __toString() $parts[] = $part; } } - return implode($parts, $this->type); + $sql = implode($parts, $this->type); + if ($this->pattern) { + return sprintf($this->pattern, $sql); + } + + return $sql; } } diff --git a/lib/internal/Magento/Framework/DB/Test/Unit/Sql/UnionExpressionTest.php b/lib/internal/Magento/Framework/DB/Test/Unit/Sql/UnionExpressionTest.php index 1a3a91ea6bd54..a5aca9707081b 100644 --- a/lib/internal/Magento/Framework/DB/Test/Unit/Sql/UnionExpressionTest.php +++ b/lib/internal/Magento/Framework/DB/Test/Unit/Sql/UnionExpressionTest.php @@ -6,11 +6,22 @@ namespace Magento\Framework\DB\Test\Unit\Sql; use Magento\Framework\DB\Select; +use Magento\Framework\DB\Sql\UnionExpression; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; class UnionExpressionTest extends \PHPUnit\Framework\TestCase { - public function testToString() + /** + * @param string $type + * @param string $pattern + * @param string $expectedSql + * @return void + * @dataProvider toStringDataProvider + */ + public function testToString(string $type, string $pattern, string $expectedSql) { + $objectManager = new ObjectManager($this); + $sqlMock = $this->getMockBuilder(\Magento\Framework\DB\Select::class) ->disableOriginalConstructor() ->getMock(); @@ -21,7 +32,34 @@ public function testToString() $sqlMock, '(test_column)' ]; - $model = new \Magento\Framework\DB\Sql\UnionExpression($parts); - $this->assertEquals('(test_assemble)' . Select::SQL_UNION . '(test_column)', $model->__toString()); + $model = $objectManager->getObject( + UnionExpression::class, + [ + 'parts' => $parts, + 'type' => $type, + 'pattern' => $pattern, + ] + ); + + $this->assertEquals($expectedSql, $model->__toString()); + } + + /** + * @return array + */ + public function toStringDataProvider(): array + { + return [ + [ + 'type' => Select::SQL_UNION, + 'pattern' => '', + 'expectedSql' => "(test_assemble)" . Select::SQL_UNION . "(test_column)", + ], + [ + 'type' => Select::SQL_UNION, + 'pattern' => 'test_with_pattern %s', + 'expectedSql' => "test_with_pattern (test_assemble)" . Select::SQL_UNION . "(test_column)", + ], + ]; } } From fefed3ea9fb2750dbf19658d5ced83e018233a7d Mon Sep 17 00:00:00 2001 From: Andrii Meysar Date: Fri, 22 Feb 2019 17:44:50 +0200 Subject: [PATCH 0777/1295] MC-15079: Exception is thrown when cart product is removed from shared catalog --- .../Quote/Model/ResourceModel/Quote/Item/Collection.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php b/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php index 309c89e3702f5..959604592c848 100644 --- a/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php +++ b/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php @@ -325,10 +325,10 @@ private function getOptionProductIds( /** * Check is valid product. * - * @param ProductInterface $product + * @param ProductInterface|null $product * @return bool */ - private function isValidProduct(ProductInterface $product): bool + private function isValidProduct(ProductInterface $product = null): bool { $result = ($product && (int)$product->getStatus() !== ProductStatus::STATUS_DISABLED); From f90f7827b7a08b8d7bf761879eb7b494bf54b9c6 Mon Sep 17 00:00:00 2001 From: Viktor Petryk Date: Sun, 24 Feb 2019 16:46:35 +0200 Subject: [PATCH 0778/1295] MAGETWO-73985: Simple product with MAP assigned to configurable should displays the same way as products with special price --- .../Catalog/Test/Mftf/Data/ProductData.xml | 9 ++++ ...reateApiConfigurableProductActionGroup.xml | 38 ++++++++++++++++ .../Msrp/Test/Mftf/Data/MsrpConfigData.xml | 23 ++++++++++ .../Test/Mftf/Metadata/msrp_config-meta.xml | 21 +++++++++ ...ingConfigurableProductPriceWithMapTest.xml | 45 +++++++++++++++++++ 5 files changed, 136 insertions(+) create mode 100644 app/code/Magento/Msrp/Test/Mftf/Data/MsrpConfigData.xml create mode 100644 app/code/Magento/Msrp/Test/Mftf/Metadata/msrp_config-meta.xml create mode 100644 app/code/Magento/Msrp/Test/Mftf/Test/StorefrontCheckingConfigurableProductPriceWithMapTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index c8c24d1f41e19..fbf54f50797de 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -299,4 +299,13 @@ 100 EavStock100 + + 50 + + + 60 + + + 70 + diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml index 161441736f940..ddf06a4d84bbb 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml @@ -82,4 +82,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/Msrp/Test/Mftf/Data/MsrpConfigData.xml b/app/code/Magento/Msrp/Test/Mftf/Data/MsrpConfigData.xml new file mode 100644 index 0000000000000..731169aa40041 --- /dev/null +++ b/app/code/Magento/Msrp/Test/Mftf/Data/MsrpConfigData.xml @@ -0,0 +1,23 @@ + + + + + EnableMAP + + + 1 + + + + DisableMAP + + + 0 + + diff --git a/app/code/Magento/Msrp/Test/Mftf/Metadata/msrp_config-meta.xml b/app/code/Magento/Msrp/Test/Mftf/Metadata/msrp_config-meta.xml new file mode 100644 index 0000000000000..f911d7072fb9a --- /dev/null +++ b/app/code/Magento/Msrp/Test/Mftf/Metadata/msrp_config-meta.xml @@ -0,0 +1,21 @@ + + + + + + + + + string + + + + + + diff --git a/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontCheckingConfigurableProductPriceWithMapTest.xml b/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontCheckingConfigurableProductPriceWithMapTest.xml new file mode 100644 index 0000000000000..034c0c8378e44 --- /dev/null +++ b/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontCheckingConfigurableProductPriceWithMapTest.xml @@ -0,0 +1,45 @@ + + + + + + + + + <description value="Check that simple products with MAP assigned to configurable product displayed correctly"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-15014"/> + <useCaseId value="MAGETWO-73985"/> + <group value="msrp"/> + <group value="configurableProduct"/> + </annotations> + <before> + <!--Enable MAP in configuration--> + <createData entity="EnableMAPConfig" stepKey="enableMapConfig"/> + <!--Create configurable product--> + <actionGroup ref="AdminCreateApiConfigurableProductWithThreeChildActionGroup" stepKey="createConfigProduct"/> + <!--Login--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <!--Disable MAP in configuration--> + <createData entity="DisableMAPConfig" stepKey="disableMAPConfig"/> + <!--Delete entities--> + <deleteData createDataKey="createConfigProductCreateConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigChildProduct1CreateConfigProduct" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2CreateConfigProduct" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigChildProduct3CreateConfigProduct" stepKey="deleteConfigChildProduct3"/> + <deleteData createDataKey="createConfigProductAttributeCreateConfigProduct" stepKey="deleteConfigProductAttribute"/> + <!--Logout--> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + </after> + + + </test> +</tests> From 49e26db2a7ab099258cdd9307ad572f5b8075333 Mon Sep 17 00:00:00 2001 From: Graham Wharton <graham@gwharton.me.uk> Date: Sun, 24 Feb 2019 16:30:14 +0000 Subject: [PATCH 0779/1295] Updated Carrier Test to be broadly inline with 2.3-develop. There are differences in the DHL API version that is used between 2.2 and 2.3 branches, therefore there had to be some modifications to the tests. Unfortunately it means it is difficult to readacross directly to 2.3 tests. --- .../Dhl/Test/Unit/Model/CarrierTest.php | 403 +++++++------ .../_files/apregion_shipment_request.xml | 85 +++ .../Dhl/Test/Unit/Model/_files/countries.xml | 58 +- .../Model/_files/dhl_quote_request_data.php | 48 ++ .../Unit/Model/_files/dhl_quote_response.xml | 530 ++++++++++++++++++ .../Model/_files/dhl_quote_response_rates.php | 31 + .../euregion_dutiable_shipment_request.xml | 89 +++ .../_files/euregion_shipment_request.xml | 85 +++ 8 files changed, 1119 insertions(+), 210 deletions(-) create mode 100644 app/code/Magento/Dhl/Test/Unit/Model/_files/apregion_shipment_request.xml create mode 100644 app/code/Magento/Dhl/Test/Unit/Model/_files/dhl_quote_request_data.php create mode 100644 app/code/Magento/Dhl/Test/Unit/Model/_files/dhl_quote_response.xml create mode 100644 app/code/Magento/Dhl/Test/Unit/Model/_files/dhl_quote_response_rates.php create mode 100644 app/code/Magento/Dhl/Test/Unit/Model/_files/euregion_dutiable_shipment_request.xml create mode 100644 app/code/Magento/Dhl/Test/Unit/Model/_files/euregion_shipment_request.xml diff --git a/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php b/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php index 949311bb65d05..8f90bac0b7f28 100644 --- a/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php +++ b/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Dhl\Test\Unit\Model; use Magento\Dhl\Model\Carrier; @@ -32,7 +33,6 @@ use Magento\Store\Model\Website; use PHPUnit_Framework_MockObject_MockObject as MockObject; use Psr\Log\LoggerInterface; -use Magento\Store\Model\ScopeInterface; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -79,11 +79,6 @@ class CarrierTest extends \PHPUnit\Framework\TestCase */ private $xmlValidator; - /** - * @var Request|MockObject - */ - private $request; - /** * @var LoggerInterface|MockObject */ @@ -96,35 +91,8 @@ protected function setUp() { $this->objectManager = new ObjectManager($this); - $this->request = $this->getMockBuilder(Request::class) - ->disableOriginalConstructor() - ->setMethods( - [ - 'getPackages', - 'getOrigCountryId', - 'setPackages', - 'setPackageWeight', - 'setPackageValue', - 'setValueWithDiscount', - 'setPackageCustomsValue', - 'setFreeMethodWeight', - 'getPackageWeight', - 'getFreeMethodWeight', - 'getOrderShipment', - ] - ) - ->getMock(); - $this->scope = $this->getMockForAbstractClass(ScopeConfigInterface::class); - $xmlElFactory = $this->getXmlFactory(); - $rateFactory = $this->getRateFactory(); - $rateMethodFactory = $this->getRateMethodFactory(); - $httpClientFactory = $this->getHttpClientFactory(); - $configReader = $this->getConfigReader(); - $readFactory = $this->getReadFactory(); - $storeManager = $this->getStoreManager(); - $this->error = $this->getMockBuilder(Error::class) ->setMethods(['setCarrier', 'setCarrierTitle', 'setErrorMessage']) ->getMock(); @@ -135,8 +103,6 @@ protected function setUp() $this->errorFactory->method('create') ->willReturn($this->error); - $carrierHelper = $this->getCarrierHelper(); - $this->xmlValidator = $this->getMockBuilder(XmlValidator::class) ->disableOriginalConstructor() ->getMock(); @@ -149,17 +115,17 @@ protected function setUp() 'scopeConfig' => $this->scope, 'xmlSecurity' => new Security(), 'logger' => $this->logger, - 'xmlElFactory' => $xmlElFactory, - 'rateFactory' => $rateFactory, + 'xmlElFactory' => $this->getXmlFactory(), + 'rateFactory' => $this->getRateFactory(), 'rateErrorFactory' => $this->errorFactory, - 'rateMethodFactory' => $rateMethodFactory, - 'httpClientFactory' => $httpClientFactory, - 'readFactory' => $readFactory, - 'storeManager' => $storeManager, - 'configReader' => $configReader, - 'carrierHelper' => $carrierHelper, + 'rateMethodFactory' => $this->getRateMethodFactory(), + 'httpClientFactory' => $this->getHttpClientFactory(), + 'readFactory' => $this->getReadFactory(), + 'storeManager' => $this->getStoreManager(), + 'configReader' => $this->getConfigReader(), + 'carrierHelper' => $this->getCarrierHelper(), 'data' => ['id' => 'dhl', 'store' => '1'], - 'xmlValidator' => $this->xmlValidator, + 'xmlValidator' => $this->xmlValidator ] ); } @@ -176,14 +142,14 @@ public function scopeConfigGetValue($path) 'carriers/dhl/shipment_days' => 'Mon,Tue,Wed,Thu,Fri,Sat', 'carriers/dhl/intl_shipment_days' => 'Mon,Tue,Wed,Thu,Fri,Sat', 'carriers/dhl/allowed_methods' => 'IE', - 'carriers/dhl/international_searvice' => 'IE', + 'carriers/dhl/international_service' => 'IE', 'carriers/dhl/gateway_url' => 'https://xmlpi-ea.dhl.com/XMLShippingServlet', 'carriers/dhl/id' => 'some ID', 'carriers/dhl/password' => 'some password', 'carriers/dhl/content_type' => 'N', 'carriers/dhl/nondoc_methods' => '1,3,4,8,P,Q,E,F,H,J,M,V,Y', 'carriers/dhl/showmethod' => 1, - 'carriers/dhl/title' => 'dhl Title', + 'carriers/dhl/title' => 'DHL Title', 'carriers/dhl/specificerrmsg' => 'dhl error message', 'carriers/dhl/unit_of_measure' => 'K', 'carriers/dhl/size' => '1', @@ -191,11 +157,16 @@ public function scopeConfigGetValue($path) 'carriers/dhl/width' => '1.6', 'carriers/dhl/depth' => '1.6', 'carriers/dhl/debug' => 1, - 'shipping/origin/country_id' => 'GB', + 'shipping/origin/country_id' => 'GB' ]; return isset($pathMap[$path]) ? $pathMap[$path] : null; } + /** + * Prepare shipping label content test + * + * @throws \ReflectionException + */ public function testPrepareShippingLabelContent() { $xml = simplexml_load_file( @@ -207,6 +178,8 @@ public function testPrepareShippingLabelContent() } /** + * Prepare shipping label content exception test + * * @dataProvider prepareShippingLabelContentExceptionDataProvider * @expectedException \Magento\Framework\Exception\LocalizedException * @expectedExceptionMessage Unable to retrieve shipping label @@ -217,6 +190,8 @@ public function testPrepareShippingLabelContentException(\SimpleXMLElement $xml) } /** + * Prepare shipping label content exception data provider + * * @return array */ public function prepareShippingLabelContentExceptionDataProvider() @@ -236,8 +211,11 @@ public function prepareShippingLabelContentExceptionDataProvider() } /** + * Invoke prepare shipping label content + * * @param \SimpleXMLElement $xml * @return \Magento\Framework\DataObject + * @throws \ReflectionException */ protected function _invokePrepareShippingLabelContent(\SimpleXMLElement $xml) { @@ -247,8 +225,14 @@ protected function _invokePrepareShippingLabelContent(\SimpleXMLElement $xml) return $method->invoke($model, $xml); } + /** + * Tests that valid rates are returned when sending a quotes request. + */ public function testCollectRates() { + $requestData = require __DIR__ . '/_files/dhl_quote_request_data.php'; + $responseXml = file_get_contents(__DIR__ . '/_files/dhl_quote_response.xml'); + $this->scope->method('getValue') ->willReturnCallback([$this, 'scopeConfigGetValue']); @@ -256,29 +240,39 @@ public function testCollectRates() ->willReturn(true); $this->httpResponse->method('getBody') - ->willReturn(file_get_contents(__DIR__ . '/_files/success_dhl_response_rates.xml')); + ->willReturn($responseXml); - /** @var RateRequest $request */ - $request = $this->objectManager->getObject( - RateRequest::class, - require __DIR__ . '/_files/rates_request_data_dhl.php' - ); + $request = $this->objectManager->getObject(RateRequest::class, $requestData); $reflectionClass = new \ReflectionObject($this->httpClient); $rawPostData = $reflectionClass->getProperty('raw_post_data'); $rawPostData->setAccessible(true); - $this->logger->expects(self::once()) + $this->logger->expects($this->once()) ->method('debug') - ->with(self::stringContains('<SiteID>****</SiteID><Password>****</Password>')); + ->with($this->stringContains('<SiteID>****</SiteID><Password>****</Password>')); + + $expectedRates = require __DIR__ . '/_files/dhl_quote_response_rates.php'; + $actualRates = $this->model->collectRates($request)->getAllRates(); + + self::assertEquals(count($expectedRates), count($actualRates)); - self::assertNotEmpty($this->model->collectRates($request)->getAllRates()); - self::assertContains('<Weight>18.223</Weight>', $rawPostData->getValue($this->httpClient)); - self::assertContains('<Height>0.630</Height>', $rawPostData->getValue($this->httpClient)); - self::assertContains('<Width>0.630</Width>', $rawPostData->getValue($this->httpClient)); - self::assertContains('<Depth>0.630</Depth>', $rawPostData->getValue($this->httpClient)); + foreach ($actualRates as $i => $actualRate) { + $actualRate = $actualRate->getData(); + unset($actualRate['method_title']); + self::assertEquals($expectedRates[$i], $actualRate); + } + + $requestXml = $rawPostData->getValue($this->httpClient); + self::assertContains('<Weight>18.223</Weight>', $requestXml); + self::assertContains('<Height>0.630</Height>', $requestXml); + self::assertContains('<Width>0.630</Width>', $requestXml); + self::assertContains('<Depth>0.630</Depth>', $requestXml); } + /** + * Tests that an error is returned when attempting to collect rates for an inactive shipping method. + */ public function testCollectRatesErrorMessage() { $this->scope->method('getValue') @@ -296,26 +290,79 @@ public function testCollectRatesErrorMessage() $this->assertSame($this->error, $this->model->collectRates($request)); } - public function testCollectRatesFail() - { - $this->scope->expects($this->once())->method('isSetFlag')->willReturn(true); + /** + * Test request to shipment sends valid xml values. + * + * @dataProvider requestToShipmentDataProvider + * @param string $origCountryId + * @param string $expectedRegionCode + * @param string $destCountryId + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \ReflectionException + */ + public function testRequestToShipment( + string $origCountryId, + string $expectedRegionCode, + string $destCountryId, + bool $dutiable + ) { + $scopeConfigValueMap = [ + ['carriers/dhl/account', 'store', null, '1234567890'], + ['carriers/dhl/gateway_url', 'store', null, 'https://xmlpi-ea.dhl.com/XMLShippingServlet'], + ['carriers/dhl/id', 'store', null, 'some ID'], + ['carriers/dhl/password', 'store', null, 'some password'], + ['carriers/dhl/content_type', 'store', null, 'N'], + ['carriers/dhl/nondoc_methods', 'store', null, '1,3,4,8,P,Q,E,F,H,J,M,V,Y'], + ['shipping/origin/country_id', 'store', null, $origCountryId], + ]; - $request = new RateRequest(); - $request->setPackageWeight(1); + $this->scope->method('getValue') + ->willReturnMap($scopeConfigValueMap); - $this->assertFalse(false, $this->model->collectRates($request)); + $this->httpResponse->method('getBody') + ->willReturn(utf8_encode(file_get_contents(__DIR__ . '/_files/response_shipping_label.xml'))); + + $request = $this->getRequest($origCountryId, $destCountryId); + + $this->logger->method('debug') + ->with($this->stringContains('<SiteID>****</SiteID><Password>****</Password>')); + + $result = $this->model->requestToShipment($request); + + $reflectionClass = new \ReflectionObject($this->httpClient); + $rawPostData = $reflectionClass->getProperty('raw_post_data'); + $rawPostData->setAccessible(true); + + $this->assertNotNull($result); + $requestXml = $rawPostData->getValue($this->httpClient); + $requestElement = new Element($requestXml); + + $this->assertXmlStringEqualsXmlString( + $this->getExpectedRequestXml($origCountryId, $destCountryId, $expectedRegionCode, $dutiable)->asXML(), + $requestElement->asXML() + ); } /** - * Test request to shipment sends valid xml values. + * Prepare and retrieve request object + * + * @param string $origCountryId + * @param string $destCountryId + * @return Request|MockObject */ - public function testRequestToShipment() + private function getRequest(string $origCountryId, string $destCountryId) { - $this->scope->method('getValue') - ->willReturnCallback([$this, 'scopeConfigGetValue']); + $order = $this->getMockBuilder(Order::class) + ->disableOriginalConstructor() + ->getMock(); + $order->method('getSubtotal') + ->willReturn('10.00'); - $this->httpResponse->method('getBody') - ->willReturn(utf8_encode(file_get_contents(__DIR__ . '/_files/response_shipping_label.xml'))); + $shipment = $this->getMockBuilder(Order\Shipment::class) + ->disableOriginalConstructor() + ->getMock(); + $shipment->method('getOrder') + ->willReturn($order); $packages = [ 'package' => [ @@ -334,130 +381,108 @@ public function testRequestToShipment() 'name' => 'item_name', ], ], - ] + ], ]; - $order = $this->getMockBuilder(Order::class) - ->disableOriginalConstructor() - ->getMock(); - $order->method('getSubtotal') - ->willReturn('10.00'); + $methods = [ + 'getPackages' => $packages, + 'getOrigCountryId' => $origCountryId, + 'getDestCountryId' => $destCountryId, + 'getShipperAddressCountryCode' => $origCountryId, + 'getRecipientAddressCountryCode' => $destCountryId, + 'setPackages' => null, + 'setPackageWeight' => null, + 'setPackageValue' => null, + 'setValueWithDiscount' => null, + 'setPackageCustomsValue' => null, + 'setFreeMethodWeight' => null, + 'getPackageWeight' => '0.454000000001', + 'getFreeMethodWeight' => '0.454000000001', + 'getOrderShipment' => $shipment, + ]; - $shipment = $this->getMockBuilder(Order\Shipment::class) + /** @var Request|MockObject $request */ + $request = $this->getMockBuilder(Request::class) ->disableOriginalConstructor() + ->setMethods(array_keys($methods)) ->getMock(); - $shipment->method('getOrder') - ->willReturn($order); - $this->request->method('getPackages') - ->willReturn($packages); - $this->request->method('getOrigCountryId') - ->willReturn('GB'); - $this->request->method('setPackages') - ->willReturnSelf(); - $this->request->method('setPackageWeight') - ->willReturnSelf(); - $this->request->method('setPackageValue') - ->willReturnSelf(); - $this->request->method('setValueWithDiscount') - ->willReturnSelf(); - $this->request->method('setPackageCustomsValue') - ->willReturnSelf(); - $this->request->method('setFreeMethodWeight') - ->willReturnSelf(); - $this->request->method('getPackageWeight') - ->willReturn('0.454000000001'); - $this->request->method('getFreeMethodWeight') - ->willReturn('0.454000000001'); - $this->request->method('getOrderShipment') - ->willReturn($shipment); + foreach ($methods as $method => $return) { + $return ? $request->method($method)->willReturn($return) : $request->method($method)->willReturnSelf(); + } - $this->logger->method('debug') - ->with(self::stringContains('<SiteID>****</SiteID><Password>****</Password>')); + return $request; + } - $result = $this->model->requestToShipment($this->request); + /** + * Prepare and retrieve expected request xml element + * + * @param string $origCountryId + * @param string $destCountryId + * @return Element + */ + private function getExpectedRequestXml( + string $origCountryId, + string $destCountryId, + string $regionCode, + bool $dutiable + ) { + $requestXmlPath = $regionCode === "EU" + ? ($dutiable ? '/_files/euregion_dutiable_shipment_request.xml' : '/_files/euregion_shipment_request.xml') + : '/_files/apregion_shipment_request.xml'; - $reflectionClass = new \ReflectionObject($this->httpClient); - $rawPostData = $reflectionClass->getProperty('raw_post_data'); - $rawPostData->setAccessible(true); + $expectedRequestElement = new Element(file_get_contents(__DIR__ . $requestXmlPath)); - $this->assertNotNull($result); - $this->assertContains('<Weight>0.454</Weight>', $rawPostData->getValue($this->httpClient)); + $expectedRequestElement->Consignee->CountryCode = $destCountryId; + $expectedRequestElement->Consignee->CountryName = $this->getCountryName($destCountryId); + + $expectedRequestElement->Shipper->CountryCode = $origCountryId; + $expectedRequestElement->Shipper->CountryName = $this->getCountryName($origCountryId); + + return $expectedRequestElement; } /** - * Test that shipping label request for origin country from AP region doesn't contain restricted fields. + * Get Country Name by Country Code + * + * @param string $countryCode + * @return string */ - public function testShippingLabelRequestForAsiaPacificRegion() + private function getCountryName($countryCode) { - $this->scope->method('getValue') - ->willReturnMap( - [ - ['shipping/origin/country_id', ScopeInterface::SCOPE_STORE, null, 'SG'], - ['carriers/dhl/gateway_url', ScopeInterface::SCOPE_STORE, null, 'https://xmlpi-ea.dhl.com'], - ] - ); - - $this->httpResponse->method('getBody') - ->willReturn(utf8_encode(file_get_contents(__DIR__ . '/_files/response_shipping_label.xml'))); + $countryNames = [ + 'US' => 'United States Of America', + 'SG' => 'Singapore', + 'GB' => 'United Kingdom', + 'DE' => 'Germany', + ]; + return $countryNames[$countryCode]; + } - $packages = [ - 'package' => [ - 'params' => [ - 'width' => '1', - 'length' => '1', - 'height' => '1', - 'dimension_units' => 'INCH', - 'weight_units' => 'POUND', - 'weight' => '0.45', - 'customs_value' => '10.00', - 'container' => Carrier::DHL_CONTENT_TYPE_NON_DOC, - ], - 'items' => [ - 'item1' => [ - 'name' => 'item_name', - ], - ], + /** + * Data provider to testRequestToShipment + * + * @return array + */ + public function requestToShipmentDataProvider() + { + return [ + [ + 'GB', 'EU', 'US', 1 + ], + [ + 'SG', 'AP', 'US', 0 + ], + [ + 'DE', 'EU', 'DE', 0 ] ]; - - $this->request->method('getPackages')->willReturn($packages); - $this->request->method('getOrigCountryId')->willReturn('SG'); - $this->request->method('setPackages')->willReturnSelf(); - $this->request->method('setPackageWeight')->willReturnSelf(); - $this->request->method('setPackageValue')->willReturnSelf(); - $this->request->method('setValueWithDiscount')->willReturnSelf(); - $this->request->method('setPackageCustomsValue')->willReturnSelf(); - - $result = $this->model->requestToShipment($this->request); - - $reflectionClass = new \ReflectionObject($this->httpClient); - $rawPostData = $reflectionClass->getProperty('raw_post_data'); - $rawPostData->setAccessible(true); - - $this->assertNotNull($result); - $requestXml = $rawPostData->getValue($this->httpClient); - - $this->assertNotContains( - 'NewShipper', - $requestXml, - 'NewShipper is restricted field for AP region' - ); - $this->assertNotContains( - 'Division', - $requestXml, - 'Division is restricted field for AP region' - ); - $this->assertNotContains( - 'RegisteredAccount', - $requestXml, - 'RegisteredAccount is restricted field for AP region' - ); } /** - * @dataProvider dhlProductsDataProvider + * Get DHL products test * + * @dataProvider dhlProductsDataProvider * @param string $docType * @param array $products */ @@ -467,6 +492,8 @@ public function testGetDhlProducts(string $docType, array $products) } /** + * DHL products data provider + * * @return array */ public function dhlProductsDataProvider() : array @@ -576,19 +603,25 @@ private function getRateMethodFactory(): MockObject ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $rateMethod = $this->getMockBuilder(Method::class) - ->disableOriginalConstructor() - ->setMethods(['setPrice']) - ->getMock(); - $rateMethod->method('setPrice') - ->willReturnSelf(); + $rateMethodFactory->method('create') - ->willReturn($rateMethod); + ->willReturnCallback(function () { + $rateMethod = $this->getMockBuilder(Method::class) + ->disableOriginalConstructor() + ->setMethods(['setPrice']) + ->getMock(); + $rateMethod->method('setPrice') + ->willReturnSelf(); + + return $rateMethod; + }); return $rateMethodFactory; } /** + * Get config reader + * * @return MockObject */ private function getConfigReader(): MockObject @@ -603,6 +636,8 @@ private function getConfigReader(): MockObject } /** + * Get read factory + * * @return MockObject */ private function getReadFactory(): MockObject @@ -621,6 +656,8 @@ private function getReadFactory(): MockObject } /** + * Get store manager + * * @return MockObject */ private function getStoreManager(): MockObject @@ -642,6 +679,8 @@ private function getStoreManager(): MockObject } /** + * Get carrier helper + * * @return CarrierHelper */ private function getCarrierHelper(): CarrierHelper @@ -660,6 +699,8 @@ private function getCarrierHelper(): CarrierHelper } /** + * Get HTTP client factory + * * @return MockObject */ private function getHttpClientFactory(): MockObject diff --git a/app/code/Magento/Dhl/Test/Unit/Model/_files/apregion_shipment_request.xml b/app/code/Magento/Dhl/Test/Unit/Model/_files/apregion_shipment_request.xml new file mode 100644 index 0000000000000..91f8372cd8dbb --- /dev/null +++ b/app/code/Magento/Dhl/Test/Unit/Model/_files/apregion_shipment_request.xml @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<req:ShipmentValidateRequestAP xmlns:req="http://www.dhl.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.dhl.com ship-val-req_AP.xsd"> + <Request xmlns=""> + <ServiceHeader> + <SiteID>some ID</SiteID> + <Password>some password</Password> + </ServiceHeader> + </Request> + <LanguageCode xmlns="">EN</LanguageCode> + <PiecesEnabled xmlns="">Y</PiecesEnabled> + <Billing xmlns=""> + <ShipperAccountNumber>1234567890</ShipperAccountNumber> + <ShippingPaymentType>S</ShippingPaymentType> + <BillingAccountNumber>1234567890</BillingAccountNumber> + <DutyPaymentType>S</DutyPaymentType> + <DutyAccountNumber>1234567890</DutyAccountNumber> + </Billing> + <Consignee xmlns=""> + <CompanyName/> + <AddressLine/> + <City/> + <PostalCode/> + <CountryCode/> + <CountryName/> + <Contact> + <PersonName/> + <PhoneNumber/> + </Contact> + </Consignee> + <Commodity xmlns=""> + <CommodityCode>1</CommodityCode> + </Commodity> + <Dutiable xmlns=""> + <DeclaredValue>10.00</DeclaredValue> + <DeclaredCurrency>USD</DeclaredCurrency> + </Dutiable> + <Reference xmlns=""> + <ReferenceID>shipment reference</ReferenceID> + <ReferenceType>St</ReferenceType> + </Reference> + <ShipmentDetails xmlns=""> + <NumberOfPieces>1</NumberOfPieces> + <CurrencyCode>USD</CurrencyCode> + <Pieces xmlns=""> + <Piece xmlns=""> + <PieceID>1</PieceID> + <PackageType>CP</PackageType> + <Weight>0.5</Weight> + <Depth>3</Depth> + <Width>3</Width> + <Height>3</Height> + <PieceContents>item_name</PieceContents> + </Piece> + </Pieces> + <PackageType>CP</PackageType> + <Weight>0.454</Weight> + <DimensionUnit>C</DimensionUnit> + <WeightUnit>K</WeightUnit> + <GlobalProductCode/> + <LocalProductCode/> + <DoorTo>DD</DoorTo> + <Date/> + <Contents>DHL Parcel TEST</Contents> + </ShipmentDetails> + <Shipper xmlns=""> + <ShipperID>1234567890</ShipperID> + <CompanyName/> + <AddressLine/> + <City/> + <PostalCode/> + <CountryCode/> + <CountryName/> + <Contact xmlns=""> + <PersonName/> + <PhoneNumber/> + </Contact> + </Shipper> + <LabelImageFormat xmlns="">PDF</LabelImageFormat> +</req:ShipmentValidateRequestAP> diff --git a/app/code/Magento/Dhl/Test/Unit/Model/_files/countries.xml b/app/code/Magento/Dhl/Test/Unit/Model/_files/countries.xml index 3f28111f229d1..792465ce45942 100644 --- a/app/code/Magento/Dhl/Test/Unit/Model/_files/countries.xml +++ b/app/code/Magento/Dhl/Test/Unit/Model/_files/countries.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="UTF-8"?> +<?xml version="1.0"?> <!-- /** * Copyright © Magento, Inc. All rights reserved. @@ -83,7 +83,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Austria</name> <domestic>1</domestic> </AT> @@ -132,7 +132,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Belgium</name> <domestic>1</domestic> </BE> @@ -146,7 +146,7 @@ <currency>BGN</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Bulgaria</name> <domestic>1</domestic> </BG> @@ -257,7 +257,7 @@ <currency>CHF</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Switzerland</name> </CH> <CI> @@ -331,7 +331,7 @@ <currency>CZK</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Czech Republic, The</name> <domestic>1</domestic> </CZ> @@ -339,7 +339,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Germany</name> <domestic>1</domestic> </DE> @@ -353,7 +353,7 @@ <currency>DKK</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Denmark</name> <domestic>1</domestic> </DK> @@ -389,7 +389,7 @@ <currency>EEK</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Estonia</name> <domestic>1</domestic> </EE> @@ -410,7 +410,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Spain</name> <domestic>1</domestic> </ES> @@ -424,7 +424,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Finland</name> <domestic>1</domestic> </FI> @@ -457,7 +457,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>France</name> <domestic>1</domestic> </FR> @@ -471,7 +471,7 @@ <currency>GBP</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>United Kingdom</name> <domestic>1</domestic> </GB> @@ -549,7 +549,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Greece</name> <domestic>1</domestic> </GR> @@ -612,7 +612,7 @@ <currency>HUF</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Hungary</name> <domestic>1</domestic> </HU> @@ -633,7 +633,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Ireland, Republic Of</name> <domestic>1</domestic> </IE> @@ -668,14 +668,14 @@ <currency>ISK</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Iceland</name> </IS> <IT> <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Italy</name> <domestic>1</domestic> </IT> @@ -834,7 +834,7 @@ <currency>LTL</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Lithuania</name> <domestic>1</domestic> </LT> @@ -842,7 +842,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Luxembourg</name> <domestic>1</domestic> </LU> @@ -850,7 +850,7 @@ <currency>LVL</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Latvia</name> <domestic>1</domestic> </LV> @@ -1039,7 +1039,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Netherlands, The</name> <domestic>1</domestic> </NL> @@ -1047,7 +1047,7 @@ <currency>NOK</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Norway</name> </NO> <NP> @@ -1127,7 +1127,7 @@ <currency>PLN</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Poland</name> <domestic>1</domestic> </PL> @@ -1142,7 +1142,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Portugal</name> <domestic>1</domestic> </PT> @@ -1177,7 +1177,7 @@ <currency>RON</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Romania</name> <domestic>1</domestic> </RO> @@ -1231,7 +1231,7 @@ <currency>SEK</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Sweden</name> <domestic>1</domestic> </SE> @@ -1246,7 +1246,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Slovenia</name> <domestic>1</domestic> </SI> @@ -1254,7 +1254,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Slovakia</name> <domestic>1</domestic> </SK> diff --git a/app/code/Magento/Dhl/Test/Unit/Model/_files/dhl_quote_request_data.php b/app/code/Magento/Dhl/Test/Unit/Model/_files/dhl_quote_request_data.php new file mode 100644 index 0000000000000..f762b99735233 --- /dev/null +++ b/app/code/Magento/Dhl/Test/Unit/Model/_files/dhl_quote_request_data.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +return [ + 'data' => [ + 'dest_country_id' => 'DE', + 'dest_region_id' => '82', + 'dest_region_code' => 'BER', + 'dest_street' => 'Turmstraße 17', + 'dest_city' => 'Berlin', + 'dest_postcode' => '10559', + 'dest_postal' => '10559', + 'package_value' => '5', + 'package_value_with_discount' => '5', + 'package_weight' => '8.2657', + 'package_qty' => '1', + 'package_physical_value' => '5', + 'free_method_weight' => '5', + 'store_id' => '1', + 'website_id' => '1', + 'free_shipping' => '0', + 'limit_carrier' => null, + 'base_subtotal_incl_tax' => '5', + 'orig_country_id' => 'US', + 'orig_region_id' => '12', + 'orig_city' => 'Fremont', + 'orig_postcode' => '94538', + 'dhl_id' => 'MAGEN_8501', + 'dhl_password' => 'QR2GO1U74X', + 'dhl_account' => '799909537', + 'dhl_shipping_intl_key' => '54233F2B2C4E5C4B4C5E5A59565530554B405641475D5659', + 'girth' => null, + 'height' => null, + 'length' => null, + 'width' => null, + 'weight' => 1, + 'dhl_shipment_type' => 'P', + 'dhl_duitable' => 0, + 'dhl_duty_payment_type' => 'R', + 'dhl_content_desc' => 'Big Box', + 'limit_method' => 'IE', + 'ship_date' => '2014-01-09', + 'action' => 'RateEstimate', + 'all_items' => [], + ] +]; diff --git a/app/code/Magento/Dhl/Test/Unit/Model/_files/dhl_quote_response.xml b/app/code/Magento/Dhl/Test/Unit/Model/_files/dhl_quote_response.xml new file mode 100644 index 0000000000000..7421707966411 --- /dev/null +++ b/app/code/Magento/Dhl/Test/Unit/Model/_files/dhl_quote_response.xml @@ -0,0 +1,530 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<res:DCTResponse xmlns:res="http://www.dhl.com" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.dhl.com DCT-Response_global-2.0.xsd"> + <GetQuoteResponse> + <Response> + <ServiceHeader> + <MessageTime>2014-01-09T12:13:29.498+00:00</MessageTime> + <SiteID>EvgeniyUSA</SiteID> + </ServiceHeader> + </Response> + <BkgDetails> + <QtdShp> + <OriginServiceArea> + <FacilityCode>NUQ</FacilityCode> + <ServiceAreaCode>NUQ</ServiceAreaCode> + </OriginServiceArea> + <DestinationServiceArea> + <FacilityCode>BER</FacilityCode> + <ServiceAreaCode>BER</ServiceAreaCode> + </DestinationServiceArea> + <GlobalProductCode>E</GlobalProductCode> + <LocalProductCode>E</LocalProductCode> + <ProductShortName>EXPRESS 9:00</ProductShortName> + <LocalProductName>EXPRESS 9:00 NONDOC</LocalProductName> + <NetworkTypeCode>TD</NetworkTypeCode> + <POfferedCustAgreement>N</POfferedCustAgreement> + <TransInd>Y</TransInd> + <PickupDate>2014-01-09</PickupDate> + <PickupCutoffTime>PT16H15M</PickupCutoffTime> + <BookingTime>PT15H15M</BookingTime> + <CurrencyCode>USD</CurrencyCode> + <ExchangeRate>1.000000</ExchangeRate> + <WeightCharge>42.060</WeightCharge> + <WeightChargeTax>0.000</WeightChargeTax> + <TotalTransitDays>2</TotalTransitDays> + <PickupPostalLocAddDays>0</PickupPostalLocAddDays> + <DeliveryPostalLocAddDays>0</DeliveryPostalLocAddDays> + <DeliveryDate> + <DlvyDateTime>2014-01-13 11:59:00</DlvyDateTime> + <DeliveryDateTimeOffset>+00:00</DeliveryDateTimeOffset> + </DeliveryDate> + <DeliveryTime>PT9H</DeliveryTime> + <DimensionalWeight>2.205</DimensionalWeight> + <WeightUnit>LB</WeightUnit> + <PickupDayOfWeekNum>4</PickupDayOfWeekNum> + <DestinationDayOfWeekNum>1</DestinationDayOfWeekNum> + <QtdShpExChrg> + <SpecialServiceType>FF</SpecialServiceType> + <LocalServiceType>FF</LocalServiceType> + <GlobalServiceName>FUEL SURCHARGE</GlobalServiceName> + <LocalServiceTypeName>FUEL SURCHARGE</LocalServiceTypeName> + <ChargeCodeType>SCH</ChargeCodeType> + <CurrencyCode>USD</CurrencyCode> + <ChargeValue>3.790</ChargeValue> + <QtdSExtrChrgInAdCur> + <ChargeValue>3.790</ChargeValue> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>BILLC</CurrencyRoleTypeCode> + </QtdSExtrChrgInAdCur> + <QtdSExtrChrgInAdCur> + <ChargeValue>3.790</ChargeValue> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>PULCL</CurrencyRoleTypeCode> + </QtdSExtrChrgInAdCur> + <QtdSExtrChrgInAdCur> + <ChargeValue>3.790</ChargeValue> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>BASEC</CurrencyRoleTypeCode> + </QtdSExtrChrgInAdCur> + </QtdShpExChrg> + <PricingDate>2014-01-09</PricingDate> + <ShippingCharge>45.850</ShippingCharge> + <TotalTaxAmount>0.000</TotalTaxAmount> + <QtdSInAdCur> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>BILLC</CurrencyRoleTypeCode> + <WeightCharge>42.060</WeightCharge> + <TotalAmount>45.850</TotalAmount> + <TotalTaxAmount>0.000</TotalTaxAmount> + <WeightChargeTax>0.000</WeightChargeTax> + </QtdSInAdCur> + <QtdSInAdCur> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>PULCL</CurrencyRoleTypeCode> + <WeightCharge>42.060</WeightCharge> + <TotalAmount>45.850</TotalAmount> + <TotalTaxAmount>0.000</TotalTaxAmount> + <WeightChargeTax>0.000</WeightChargeTax> + </QtdSInAdCur> + <QtdSInAdCur> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>BASEC</CurrencyRoleTypeCode> + <WeightCharge>42.060</WeightCharge> + <TotalAmount>45.850</TotalAmount> + <TotalTaxAmount>0.000</TotalTaxAmount> + <WeightChargeTax>0.000</WeightChargeTax> + </QtdSInAdCur> + <PickupWindowEarliestTime>09:00:00</PickupWindowEarliestTime> + <PickupWindowLatestTime>17:00:00</PickupWindowLatestTime> + <BookingCutoffOffset>PT1H</BookingCutoffOffset> + </QtdShp> + <QtdShp> + <OriginServiceArea> + <FacilityCode>NUQ</FacilityCode> + <ServiceAreaCode>NUQ</ServiceAreaCode> + </OriginServiceArea> + <DestinationServiceArea> + <FacilityCode>BER</FacilityCode> + <ServiceAreaCode>BER</ServiceAreaCode> + </DestinationServiceArea> + <GlobalProductCode>Q</GlobalProductCode> + <LocalProductCode>Q</LocalProductCode> + <ProductShortName>MEDICAL EXPRESS</ProductShortName> + <LocalProductName>MEDICAL EXPRESS</LocalProductName> + <NetworkTypeCode>TD</NetworkTypeCode> + <POfferedCustAgreement>Y</POfferedCustAgreement> + <TransInd>N</TransInd> + <PickupDate>2014-01-09</PickupDate> + <PickupCutoffTime>PT16H15M</PickupCutoffTime> + <BookingTime>PT15H15M</BookingTime> + <CurrencyCode>USD</CurrencyCode> + <ExchangeRate>1.000000</ExchangeRate> + <WeightCharge>32.350</WeightCharge> + <WeightChargeTax>0.000</WeightChargeTax> + <TotalTransitDays>2</TotalTransitDays> + <PickupPostalLocAddDays>0</PickupPostalLocAddDays> + <DeliveryPostalLocAddDays>0</DeliveryPostalLocAddDays> + <DeliveryDate> + <DlvyDateTime>2014-01-13 11:59:00</DlvyDateTime> + <DeliveryDateTimeOffset>+00:00</DeliveryDateTimeOffset> + </DeliveryDate> + <DeliveryTime>PT9H</DeliveryTime> + <DimensionalWeight>2.205</DimensionalWeight> + <WeightUnit>LB</WeightUnit> + <PickupDayOfWeekNum>4</PickupDayOfWeekNum> + <DestinationDayOfWeekNum>1</DestinationDayOfWeekNum> + <QtdShpExChrg> + <SpecialServiceType>FF</SpecialServiceType> + <LocalServiceType>FF</LocalServiceType> + <GlobalServiceName>FUEL SURCHARGE</GlobalServiceName> + <LocalServiceTypeName>FUEL SURCHARGE</LocalServiceTypeName> + <ChargeCodeType>SCH</ChargeCodeType> + <CurrencyCode>USD</CurrencyCode> + <ChargeValue>2.910</ChargeValue> + <QtdSExtrChrgInAdCur> + <ChargeValue>2.910</ChargeValue> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>BILLC</CurrencyRoleTypeCode> + </QtdSExtrChrgInAdCur> + <QtdSExtrChrgInAdCur> + <ChargeValue>2.910</ChargeValue> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>PULCL</CurrencyRoleTypeCode> + </QtdSExtrChrgInAdCur> + <QtdSExtrChrgInAdCur> + <ChargeValue>2.910</ChargeValue> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>BASEC</CurrencyRoleTypeCode> + </QtdSExtrChrgInAdCur> + </QtdShpExChrg> + <PricingDate>2014-01-09</PricingDate> + <ShippingCharge>35.260</ShippingCharge> + <TotalTaxAmount>0.000</TotalTaxAmount> + <QtdSInAdCur> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>BILLC</CurrencyRoleTypeCode> + <WeightCharge>32.350</WeightCharge> + <TotalAmount>35.260</TotalAmount> + <TotalTaxAmount>0.000</TotalTaxAmount> + <WeightChargeTax>0.000</WeightChargeTax> + </QtdSInAdCur> + <QtdSInAdCur> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>PULCL</CurrencyRoleTypeCode> + <WeightCharge>32.350</WeightCharge> + <TotalAmount>35.260</TotalAmount> + <TotalTaxAmount>0.000</TotalTaxAmount> + <WeightChargeTax>0.000</WeightChargeTax> + </QtdSInAdCur> + <QtdSInAdCur> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>BASEC</CurrencyRoleTypeCode> + <WeightCharge>32.350</WeightCharge> + <TotalAmount>35.260</TotalAmount> + <TotalTaxAmount>0.000</TotalTaxAmount> + <WeightChargeTax>0.000</WeightChargeTax> + </QtdSInAdCur> + <PickupWindowEarliestTime>09:00:00</PickupWindowEarliestTime> + <PickupWindowLatestTime>17:00:00</PickupWindowLatestTime> + <BookingCutoffOffset>PT1H</BookingCutoffOffset> + </QtdShp> + <QtdShp> + <OriginServiceArea> + <FacilityCode>NUQ</FacilityCode> + <ServiceAreaCode>NUQ</ServiceAreaCode> + </OriginServiceArea> + <DestinationServiceArea> + <FacilityCode>BER</FacilityCode> + <ServiceAreaCode>BER</ServiceAreaCode> + </DestinationServiceArea> + <GlobalProductCode>Y</GlobalProductCode> + <LocalProductCode>Y</LocalProductCode> + <ProductShortName>EXPRESS 12:00</ProductShortName> + <LocalProductName>EXPRESS 12:00 NONDOC</LocalProductName> + <NetworkTypeCode>TD</NetworkTypeCode> + <POfferedCustAgreement>N</POfferedCustAgreement> + <TransInd>Y</TransInd> + <PickupDate>2014-01-09</PickupDate> + <PickupCutoffTime>PT16H15M</PickupCutoffTime> + <BookingTime>PT15H15M</BookingTime> + <CurrencyCode>USD</CurrencyCode> + <ExchangeRate>1.000000</ExchangeRate> + <WeightCharge>34.290</WeightCharge> + <WeightChargeTax>0.000</WeightChargeTax> + <TotalTransitDays>2</TotalTransitDays> + <PickupPostalLocAddDays>0</PickupPostalLocAddDays> + <DeliveryPostalLocAddDays>0</DeliveryPostalLocAddDays> + <DeliveryDate> + <DlvyDateTime>2014-01-13 11:59:00</DlvyDateTime> + <DeliveryDateTimeOffset>+00:00</DeliveryDateTimeOffset> + </DeliveryDate> + <DeliveryTime>PT12H</DeliveryTime> + <DimensionalWeight>2.205</DimensionalWeight> + <WeightUnit>LB</WeightUnit> + <PickupDayOfWeekNum>4</PickupDayOfWeekNum> + <DestinationDayOfWeekNum>1</DestinationDayOfWeekNum> + <QtdShpExChrg> + <SpecialServiceType>FF</SpecialServiceType> + <LocalServiceType>FF</LocalServiceType> + <GlobalServiceName>FUEL SURCHARGE</GlobalServiceName> + <LocalServiceTypeName>FUEL SURCHARGE</LocalServiceTypeName> + <ChargeCodeType>SCH</ChargeCodeType> + <CurrencyCode>USD</CurrencyCode> + <ChargeValue>3.090</ChargeValue> + <QtdSExtrChrgInAdCur> + <ChargeValue>3.090</ChargeValue> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>BILLC</CurrencyRoleTypeCode> + </QtdSExtrChrgInAdCur> + <QtdSExtrChrgInAdCur> + <ChargeValue>3.090</ChargeValue> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>PULCL</CurrencyRoleTypeCode> + </QtdSExtrChrgInAdCur> + <QtdSExtrChrgInAdCur> + <ChargeValue>3.090</ChargeValue> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>BASEC</CurrencyRoleTypeCode> + </QtdSExtrChrgInAdCur> + </QtdShpExChrg> + <PricingDate>2014-01-09</PricingDate> + <ShippingCharge>37.380</ShippingCharge> + <TotalTaxAmount>0.000</TotalTaxAmount> + <QtdSInAdCur> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>BILLC</CurrencyRoleTypeCode> + <WeightCharge>34.290</WeightCharge> + <TotalAmount>37.380</TotalAmount> + <TotalTaxAmount>0.000</TotalTaxAmount> + <WeightChargeTax>0.000</WeightChargeTax> + </QtdSInAdCur> + <QtdSInAdCur> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>PULCL</CurrencyRoleTypeCode> + <WeightCharge>34.290</WeightCharge> + <TotalAmount>37.380</TotalAmount> + <TotalTaxAmount>0.000</TotalTaxAmount> + <WeightChargeTax>0.000</WeightChargeTax> + </QtdSInAdCur> + <QtdSInAdCur> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>BASEC</CurrencyRoleTypeCode> + <WeightCharge>34.290</WeightCharge> + <TotalAmount>37.380</TotalAmount> + <TotalTaxAmount>0.000</TotalTaxAmount> + <WeightChargeTax>0.000</WeightChargeTax> + </QtdSInAdCur> + <PickupWindowEarliestTime>09:00:00</PickupWindowEarliestTime> + <PickupWindowLatestTime>17:00:00</PickupWindowLatestTime> + <BookingCutoffOffset>PT1H</BookingCutoffOffset> + </QtdShp> + <QtdShp> + <OriginServiceArea> + <FacilityCode>NUQ</FacilityCode> + <ServiceAreaCode>NUQ</ServiceAreaCode> + </OriginServiceArea> + <DestinationServiceArea> + <FacilityCode>BER</FacilityCode> + <ServiceAreaCode>BER</ServiceAreaCode> + </DestinationServiceArea> + <GlobalProductCode>3</GlobalProductCode> + <LocalProductCode>3</LocalProductCode> + <ProductShortName>B2C</ProductShortName> + <LocalProductName>EXPRESS WORLDWIDE (B2C)</LocalProductName> + <NetworkTypeCode>TD</NetworkTypeCode> + <POfferedCustAgreement>Y</POfferedCustAgreement> + <TransInd>N</TransInd> + <PickupDate>2014-01-09</PickupDate> + <PickupCutoffTime>PT16H15M</PickupCutoffTime> + <BookingTime>PT15H15M</BookingTime> + <ExchangeRate>1.000000</ExchangeRate> + <WeightCharge>0</WeightCharge> + <WeightChargeTax>0.000</WeightChargeTax> + <TotalTransitDays>2</TotalTransitDays> + <PickupPostalLocAddDays>0</PickupPostalLocAddDays> + <DeliveryPostalLocAddDays>0</DeliveryPostalLocAddDays> + <DeliveryDate> + <DlvyDateTime>2014-01-13 11:59:00</DlvyDateTime> + <DeliveryDateTimeOffset>+00:00</DeliveryDateTimeOffset> + </DeliveryDate> + <DeliveryTime>PT23H59M</DeliveryTime> + <DimensionalWeight>2.205</DimensionalWeight> + <WeightUnit>LB</WeightUnit> + <PickupDayOfWeekNum>4</PickupDayOfWeekNum> + <DestinationDayOfWeekNum>1</DestinationDayOfWeekNum> + <PricingDate>2014-01-09</PricingDate> + <ShippingCharge>0.000</ShippingCharge> + <TotalTaxAmount>0.000</TotalTaxAmount> + <QtdSInAdCur> + <CurrencyRoleTypeCode>BILLC</CurrencyRoleTypeCode> + <WeightCharge>0</WeightCharge> + <TotalAmount>0.000</TotalAmount> + <TotalTaxAmount>0.000</TotalTaxAmount> + <WeightChargeTax>0.000</WeightChargeTax> + </QtdSInAdCur> + <QtdSInAdCur> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>PULCL</CurrencyRoleTypeCode> + <WeightCharge>0</WeightCharge> + <TotalAmount>0.000</TotalAmount> + <TotalTaxAmount>0.000</TotalTaxAmount> + <WeightChargeTax>0.000</WeightChargeTax> + </QtdSInAdCur> + <QtdSInAdCur> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>BASEC</CurrencyRoleTypeCode> + <WeightCharge>0</WeightCharge> + <TotalAmount>0.000</TotalAmount> + <TotalTaxAmount>0.000</TotalTaxAmount> + <WeightChargeTax>0.000</WeightChargeTax> + </QtdSInAdCur> + <PickupWindowEarliestTime>09:00:00</PickupWindowEarliestTime> + <PickupWindowLatestTime>17:00:00</PickupWindowLatestTime> + <BookingCutoffOffset>PT1H</BookingCutoffOffset> + </QtdShp> + <QtdShp> + <OriginServiceArea> + <FacilityCode>NUQ</FacilityCode> + <ServiceAreaCode>NUQ</ServiceAreaCode> + </OriginServiceArea> + <DestinationServiceArea> + <FacilityCode>BER</FacilityCode> + <ServiceAreaCode>BER</ServiceAreaCode> + </DestinationServiceArea> + <GlobalProductCode>P</GlobalProductCode> + <LocalProductCode>P</LocalProductCode> + <ProductShortName>EXPRESS WORLDWIDE</ProductShortName> + <LocalProductName>EXPRESS WORLDWIDE NONDOC</LocalProductName> + <NetworkTypeCode>TD</NetworkTypeCode> + <POfferedCustAgreement>N</POfferedCustAgreement> + <TransInd>Y</TransInd> + <PickupDate>2014-01-09</PickupDate> + <PickupCutoffTime>PT16H15M</PickupCutoffTime> + <BookingTime>PT15H15M</BookingTime> + <CurrencyCode>USD</CurrencyCode> + <ExchangeRate>1.000000</ExchangeRate> + <WeightCharge>32.350</WeightCharge> + <WeightChargeTax>0.000</WeightChargeTax> + <TotalTransitDays>2</TotalTransitDays> + <PickupPostalLocAddDays>0</PickupPostalLocAddDays> + <DeliveryPostalLocAddDays>0</DeliveryPostalLocAddDays> + <DeliveryDate> + <DlvyDateTime>2014-01-13 11:59:00</DlvyDateTime> + <DeliveryDateTimeOffset>+00:00</DeliveryDateTimeOffset> + </DeliveryDate> + <DeliveryTime>PT23H59M</DeliveryTime> + <DimensionalWeight>2.205</DimensionalWeight> + <WeightUnit>LB</WeightUnit> + <PickupDayOfWeekNum>4</PickupDayOfWeekNum> + <DestinationDayOfWeekNum>1</DestinationDayOfWeekNum> + <QtdShpExChrg> + <SpecialServiceType>FF</SpecialServiceType> + <LocalServiceType>FF</LocalServiceType> + <GlobalServiceName>FUEL SURCHARGE</GlobalServiceName> + <LocalServiceTypeName>FUEL SURCHARGE</LocalServiceTypeName> + <ChargeCodeType>SCH</ChargeCodeType> + <CurrencyCode>USD</CurrencyCode> + <ChargeValue>2.910</ChargeValue> + <QtdSExtrChrgInAdCur> + <ChargeValue>2.910</ChargeValue> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>BILLC</CurrencyRoleTypeCode> + </QtdSExtrChrgInAdCur> + <QtdSExtrChrgInAdCur> + <ChargeValue>2.910</ChargeValue> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>PULCL</CurrencyRoleTypeCode> + </QtdSExtrChrgInAdCur> + <QtdSExtrChrgInAdCur> + <ChargeValue>2.910</ChargeValue> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>BASEC</CurrencyRoleTypeCode> + </QtdSExtrChrgInAdCur> + </QtdShpExChrg> + <PricingDate>2014-01-09</PricingDate> + <ShippingCharge>35.260</ShippingCharge> + <TotalTaxAmount>0.000</TotalTaxAmount> + <QtdSInAdCur> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>BILLC</CurrencyRoleTypeCode> + <WeightCharge>32.350</WeightCharge> + <TotalAmount>35.260</TotalAmount> + <TotalTaxAmount>0.000</TotalTaxAmount> + <WeightChargeTax>0.000</WeightChargeTax> + </QtdSInAdCur> + <QtdSInAdCur> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>PULCL</CurrencyRoleTypeCode> + <WeightCharge>32.350</WeightCharge> + <TotalAmount>35.260</TotalAmount> + <TotalTaxAmount>0.000</TotalTaxAmount> + <WeightChargeTax>0.000</WeightChargeTax> + </QtdSInAdCur> + <QtdSInAdCur> + <CurrencyCode>USD</CurrencyCode> + <CurrencyRoleTypeCode>BASEC</CurrencyRoleTypeCode> + <WeightCharge>32.350</WeightCharge> + <TotalAmount>35.260</TotalAmount> + <TotalTaxAmount>0.000</TotalTaxAmount> + <WeightChargeTax>0.000</WeightChargeTax> + </QtdSInAdCur> + <PickupWindowEarliestTime>09:00:00</PickupWindowEarliestTime> + <PickupWindowLatestTime>17:00:00</PickupWindowLatestTime> + <BookingCutoffOffset>PT1H</BookingCutoffOffset> + </QtdShp> + </BkgDetails> + <Srvs> + <Srv> + <GlobalProductCode>E</GlobalProductCode> + <MrkSrv> + <LocalProductCode>E</LocalProductCode> + <ProductShortName>EXPRESS 9:00</ProductShortName> + <LocalProductName>EXPRESS 9:00 NONDOC</LocalProductName> + <NetworkTypeCode>TD</NetworkTypeCode> + <POfferedCustAgreement>N</POfferedCustAgreement> + <TransInd>Y</TransInd> + </MrkSrv> + <MrkSrv> + <LocalServiceType>FF</LocalServiceType> + <GlobalServiceName>FUEL SURCHARGE</GlobalServiceName> + <LocalServiceTypeName>FUEL SURCHARGE</LocalServiceTypeName> + <ChargeCodeType>SCH</ChargeCodeType> + <MrkSrvInd>N</MrkSrvInd> + </MrkSrv> + </Srv> + <Srv> + <GlobalProductCode>Q</GlobalProductCode> + <MrkSrv> + <LocalProductCode>Q</LocalProductCode> + <ProductShortName>MEDICAL EXPRESS</ProductShortName> + <LocalProductName>MEDICAL EXPRESS</LocalProductName> + <NetworkTypeCode>TD</NetworkTypeCode> + <POfferedCustAgreement>Y</POfferedCustAgreement> + <TransInd>N</TransInd> + </MrkSrv> + <MrkSrv> + <LocalServiceType>FF</LocalServiceType> + <GlobalServiceName>FUEL SURCHARGE</GlobalServiceName> + <LocalServiceTypeName>FUEL SURCHARGE</LocalServiceTypeName> + <ChargeCodeType>SCH</ChargeCodeType> + <MrkSrvInd>N</MrkSrvInd> + </MrkSrv> + </Srv> + <Srv> + <GlobalProductCode>Y</GlobalProductCode> + <MrkSrv> + <LocalProductCode>Y</LocalProductCode> + <ProductShortName>EXPRESS 12:00</ProductShortName> + <LocalProductName>EXPRESS 12:00 NONDOC</LocalProductName> + <NetworkTypeCode>TD</NetworkTypeCode> + <POfferedCustAgreement>N</POfferedCustAgreement> + <TransInd>Y</TransInd> + </MrkSrv> + <MrkSrv> + <LocalServiceType>FF</LocalServiceType> + <GlobalServiceName>FUEL SURCHARGE</GlobalServiceName> + <LocalServiceTypeName>FUEL SURCHARGE</LocalServiceTypeName> + <ChargeCodeType>SCH</ChargeCodeType> + <MrkSrvInd>N</MrkSrvInd> + </MrkSrv> + </Srv> + <Srv> + <GlobalProductCode>3</GlobalProductCode> + <MrkSrv> + <LocalProductCode>3</LocalProductCode> + <ProductShortName>B2C</ProductShortName> + <LocalProductName>EXPRESS WORLDWIDE (B2C)</LocalProductName> + <NetworkTypeCode>TD</NetworkTypeCode> + <POfferedCustAgreement>Y</POfferedCustAgreement> + <TransInd>N</TransInd> + </MrkSrv> + </Srv> + <Srv> + <GlobalProductCode>P</GlobalProductCode> + <MrkSrv> + <LocalProductCode>P</LocalProductCode> + <ProductShortName>EXPRESS WORLDWIDE</ProductShortName> + <LocalProductName>EXPRESS WORLDWIDE NONDOC</LocalProductName> + <NetworkTypeCode>TD</NetworkTypeCode> + <POfferedCustAgreement>N</POfferedCustAgreement> + <TransInd>Y</TransInd> + </MrkSrv> + <MrkSrv> + <LocalServiceType>FF</LocalServiceType> + <GlobalServiceName>FUEL SURCHARGE</GlobalServiceName> + <LocalServiceTypeName>FUEL SURCHARGE</LocalServiceTypeName> + <ChargeCodeType>SCH</ChargeCodeType> + <MrkSrvInd>N</MrkSrvInd> + </MrkSrv> + </Srv> + </Srvs> + </GetQuoteResponse> +</res:DCTResponse> diff --git a/app/code/Magento/Dhl/Test/Unit/Model/_files/dhl_quote_response_rates.php b/app/code/Magento/Dhl/Test/Unit/Model/_files/dhl_quote_response_rates.php new file mode 100644 index 0000000000000..ddd7b2e4f97c5 --- /dev/null +++ b/app/code/Magento/Dhl/Test/Unit/Model/_files/dhl_quote_response_rates.php @@ -0,0 +1,31 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +return [ + [ + 'carrier' => 'dhl', + 'carrier_title' => 'DHL Title', + 'cost' => 45.85, + 'method' => 'E' + ], + [ + 'carrier' => 'dhl', + 'carrier_title' => 'DHL Title', + 'cost' => 35.26, + 'method' => 'Q' + ], + [ + 'carrier' => 'dhl', + 'carrier_title' => 'DHL Title', + 'cost' => 37.38, + 'method' => 'Y' + ], + [ + 'carrier' => 'dhl', + 'carrier_title' => 'DHL Title', + 'cost' => 35.26, + 'method' => 'P' + ] +]; diff --git a/app/code/Magento/Dhl/Test/Unit/Model/_files/euregion_dutiable_shipment_request.xml b/app/code/Magento/Dhl/Test/Unit/Model/_files/euregion_dutiable_shipment_request.xml new file mode 100644 index 0000000000000..50a8c1b7a02a9 --- /dev/null +++ b/app/code/Magento/Dhl/Test/Unit/Model/_files/euregion_dutiable_shipment_request.xml @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<req:ShipmentValidateRequestEU xmlns:req="http://www.dhl.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.dhl.com ship-val-req_EU.xsd"> + <Request xmlns=""> + <ServiceHeader> + <SiteID>some ID</SiteID> + <Password>some password</Password> + </ServiceHeader> + </Request> + <NewShipper xmlns="">N</NewShipper> + <LanguageCode xmlns="">EN</LanguageCode> + <PiecesEnabled xmlns="">Y</PiecesEnabled> + <Billing xmlns=""> + <ShipperAccountNumber>1234567890</ShipperAccountNumber> + <ShippingPaymentType>S</ShippingPaymentType> + <BillingAccountNumber>1234567890</BillingAccountNumber> + <DutyPaymentType>S</DutyPaymentType> + <DutyAccountNumber>1234567890</DutyAccountNumber> + </Billing> + <Consignee xmlns=""> + <CompanyName/> + <AddressLine/> + <City/> + <Division/> + <PostalCode/> + <CountryCode/> + <CountryName/> + <Contact> + <PersonName/> + <PhoneNumber/> + </Contact> + </Consignee> + <Commodity xmlns=""> + <CommodityCode>1</CommodityCode> + </Commodity> + <Dutiable> + <DeclaredValue>10.00</DeclaredValue> + <DeclaredCurrency>USD</DeclaredCurrency> + </Dutiable> + <Reference xmlns=""> + <ReferenceID>shipment reference</ReferenceID> + <ReferenceType>St</ReferenceType> + </Reference> + <ShipmentDetails xmlns=""> + <NumberOfPieces>1</NumberOfPieces> + <CurrencyCode>USD</CurrencyCode> + <Pieces xmlns=""> + <Piece xmlns=""> + <PieceID>1</PieceID> + <PackageType>CP</PackageType> + <Weight>0.5</Weight> + <Depth>3</Depth> + <Width>3</Width> + <Height>3</Height> + <PieceContents>item_name</PieceContents> + </Piece> + </Pieces> + <PackageType>CP</PackageType> + <Weight>0.454</Weight> + <DimensionUnit>C</DimensionUnit> + <WeightUnit>K</WeightUnit> + <GlobalProductCode/> + <LocalProductCode/> + <DoorTo>DD</DoorTo> + <Date/> + <Contents>DHL Parcel TEST</Contents> + </ShipmentDetails> + <Shipper xmlns=""> + <ShipperID>1234567890</ShipperID> + <CompanyName/> + <RegisteredAccount>1234567890</RegisteredAccount> + <AddressLine/> + <City/> + <Division/> + <PostalCode/> + <CountryCode/> + <CountryName/> + <Contact xmlns=""> + <PersonName/> + <PhoneNumber/> + </Contact> + </Shipper> + <LabelImageFormat xmlns="">PDF</LabelImageFormat> +</req:ShipmentValidateRequestEU> \ No newline at end of file diff --git a/app/code/Magento/Dhl/Test/Unit/Model/_files/euregion_shipment_request.xml b/app/code/Magento/Dhl/Test/Unit/Model/_files/euregion_shipment_request.xml new file mode 100644 index 0000000000000..6dccea89f8f6a --- /dev/null +++ b/app/code/Magento/Dhl/Test/Unit/Model/_files/euregion_shipment_request.xml @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<req:ShipmentValidateRequestEU xmlns:req="http://www.dhl.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.dhl.com ship-val-req_EU.xsd"> + <Request xmlns=""> + <ServiceHeader> + <SiteID>some ID</SiteID> + <Password>some password</Password> + </ServiceHeader> + </Request> + <NewShipper xmlns="">N</NewShipper> + <LanguageCode xmlns="">EN</LanguageCode> + <PiecesEnabled xmlns="">Y</PiecesEnabled> + <Billing xmlns=""> + <ShipperAccountNumber>1234567890</ShipperAccountNumber> + <ShippingPaymentType>S</ShippingPaymentType> + <BillingAccountNumber>1234567890</BillingAccountNumber> + <DutyPaymentType>S</DutyPaymentType> + <DutyAccountNumber>1234567890</DutyAccountNumber> + </Billing> + <Consignee xmlns=""> + <CompanyName/> + <AddressLine/> + <City/> + <Division/> + <PostalCode/> + <CountryCode/> + <CountryName/> + <Contact> + <PersonName/> + <PhoneNumber/> + </Contact> + </Consignee> + <Commodity xmlns=""> + <CommodityCode>1</CommodityCode> + </Commodity> + <Reference xmlns=""> + <ReferenceID>shipment reference</ReferenceID> + <ReferenceType>St</ReferenceType> + </Reference> + <ShipmentDetails xmlns=""> + <NumberOfPieces>1</NumberOfPieces> + <CurrencyCode>USD</CurrencyCode> + <Pieces xmlns=""> + <Piece xmlns=""> + <PieceID>1</PieceID> + <PackageType>CP</PackageType> + <Weight>0.5</Weight> + <Depth>3</Depth> + <Width>3</Width> + <Height>3</Height> + <PieceContents>item_name</PieceContents> + </Piece> + </Pieces> + <PackageType>CP</PackageType> + <Weight>0.454</Weight> + <DimensionUnit>C</DimensionUnit> + <WeightUnit>K</WeightUnit> + <GlobalProductCode/> + <LocalProductCode/> + <DoorTo>DD</DoorTo> + <Date/> + <Contents>DHL Parcel TEST</Contents> + </ShipmentDetails> + <Shipper xmlns=""> + <ShipperID>1234567890</ShipperID> + <CompanyName/> + <RegisteredAccount>1234567890</RegisteredAccount> + <AddressLine/> + <City/> + <Division/> + <PostalCode/> + <CountryCode/> + <CountryName/> + <Contact xmlns=""> + <PersonName/> + <PhoneNumber/> + </Contact> + </Shipper> + <LabelImageFormat xmlns="">PDF</LabelImageFormat> +</req:ShipmentValidateRequestEU> \ No newline at end of file From a497c95712e1682904d62b80525efd3bce380fef Mon Sep 17 00:00:00 2001 From: gwharton <30697781+gwharton@users.noreply.github.com> Date: Sun, 24 Feb 2019 17:31:45 +0000 Subject: [PATCH 0780/1295] Reduced excessive variable lengths --- app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php b/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php index 8f90bac0b7f28..681bd089b556b 100644 --- a/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php +++ b/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php @@ -431,15 +431,15 @@ private function getExpectedRequestXml( ? ($dutiable ? '/_files/euregion_dutiable_shipment_request.xml' : '/_files/euregion_shipment_request.xml') : '/_files/apregion_shipment_request.xml'; - $expectedRequestElement = new Element(file_get_contents(__DIR__ . $requestXmlPath)); + $requestElement = new Element(file_get_contents(__DIR__ . $requestXmlPath)); - $expectedRequestElement->Consignee->CountryCode = $destCountryId; - $expectedRequestElement->Consignee->CountryName = $this->getCountryName($destCountryId); + $requestElement->Consignee->CountryCode = $destCountryId; + $requestElement->Consignee->CountryName = $this->getCountryName($destCountryId); - $expectedRequestElement->Shipper->CountryCode = $origCountryId; - $expectedRequestElement->Shipper->CountryName = $this->getCountryName($origCountryId); + $requestElement->Shipper->CountryCode = $origCountryId; + $requestElement->Shipper->CountryName = $this->getCountryName($origCountryId); - return $expectedRequestElement; + return $requestElement; } /** From 5557b9d06c169fbd8b38f7c598e51c8c49e94739 Mon Sep 17 00:00:00 2001 From: gwharton <30697781+gwharton@users.noreply.github.com> Date: Sun, 24 Feb 2019 17:34:22 +0000 Subject: [PATCH 0781/1295] Reduced incorrect indentation on function declaration --- app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php b/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php index 681bd089b556b..41e545412aea1 100644 --- a/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php +++ b/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php @@ -422,10 +422,10 @@ private function getRequest(string $origCountryId, string $destCountryId) * @return Element */ private function getExpectedRequestXml( - string $origCountryId, - string $destCountryId, - string $regionCode, - bool $dutiable + string $origCountryId, + string $destCountryId, + string $regionCode, + bool $dutiable ) { $requestXmlPath = $regionCode === "EU" ? ($dutiable ? '/_files/euregion_dutiable_shipment_request.xml' : '/_files/euregion_shipment_request.xml') From abc3e7c17b11cfc99edc06495a5da47c0c82d9f0 Mon Sep 17 00:00:00 2001 From: Alexandre Jardin <info@ajardin.fr> Date: Tue, 5 Feb 2019 15:11:29 +0100 Subject: [PATCH 0782/1295] Make the module list more deterministic --- .../Framework/Module/ModuleList/Loader.php | 36 +++++++++++-- .../Test/Unit/ModuleList/LoaderTest.php | 51 +++++++++++++++++++ 2 files changed, 82 insertions(+), 5 deletions(-) diff --git a/lib/internal/Magento/Framework/Module/ModuleList/Loader.php b/lib/internal/Magento/Framework/Module/ModuleList/Loader.php index bdfb77762b41c..80958f65e110e 100644 --- a/lib/internal/Magento/Framework/Module/ModuleList/Loader.php +++ b/lib/internal/Magento/Framework/Module/ModuleList/Loader.php @@ -126,16 +126,21 @@ private function getModuleConfigs() * * @param array $origList * @return array - * @SuppressWarnings(PHPMD.UnusedLocalVariable) + * @throws \Exception */ - private function sortBySequence($origList) + private function sortBySequence(array $origList): array { ksort($origList); + $modules = $this->prearrangeModules($origList); + $expanded = []; - foreach ($origList as $moduleName => $value) { + foreach ($modules as $moduleName => $value) { + $sequence = $this->expandSequence($origList, $moduleName); + asort($sequence); + $expanded[] = [ 'name' => $moduleName, - 'sequence' => $this->expandSequence($origList, $moduleName), + 'sequence' => $sequence, ]; } @@ -143,7 +148,7 @@ private function sortBySequence($origList) $total = count($expanded); for ($i = 0; $i < $total - 1; $i++) { for ($j = $i; $j < $total; $j++) { - if (in_array($expanded[$j]['name'], $expanded[$i]['sequence'])) { + if (in_array($expanded[$j]['name'], $expanded[$i]['sequence'], true)) { $temp = $expanded[$i]; $expanded[$i] = $expanded[$j]; $expanded[$j] = $temp; @@ -159,6 +164,27 @@ private function sortBySequence($origList) return $result; } + /** + * Prearrange all modules by putting those from Magento before the others + * + * @param array $modules + * @return array + */ + private function prearrangeModules(array $modules): array + { + $breakdown = ['magento' => [], 'others' => []]; + + foreach ($modules as $moduleName => $moduleDetails) { + if (strpos($moduleName, 'Magento_') !== false) { + $breakdown['magento'][$moduleName] = $moduleDetails; + } else { + $breakdown['others'][$moduleName] = $moduleDetails; + } + } + + return array_merge($breakdown['magento'], $breakdown['others']); + } + /** * Accumulate information about all transitive "sequence" references * diff --git a/lib/internal/Magento/Framework/Module/Test/Unit/ModuleList/LoaderTest.php b/lib/internal/Magento/Framework/Module/Test/Unit/ModuleList/LoaderTest.php index fe613450fd485..a62bb5fa70f97 100644 --- a/lib/internal/Magento/Framework/Module/Test/Unit/ModuleList/LoaderTest.php +++ b/lib/internal/Magento/Framework/Module/Test/Unit/ModuleList/LoaderTest.php @@ -160,4 +160,55 @@ public function testLoadCircular() ])); $this->loader->load(); } + + /** + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function testLoadPrearranged(): void + { + $fixtures = [ + 'Foo_Bar' => ['name' => 'Foo_Bar', 'sequence' => ['Magento_Store']], + 'Magento_Directory' => ['name' => 'Magento_Directory', 'sequence' => ['Magento_Store']], + 'Magento_Store' => ['name' => 'Magento_Store', 'sequence' => []], + 'Magento_Theme' => ['name' => 'Magento_Theme', 'sequence' => ['Magento_Store', 'Magento_Directory']], + 'Test_HelloWorld' => ['name' => 'Test_HelloWorld', 'sequence' => ['Magento_Theme']] + ]; + + $index = 0; + foreach ($fixtures as $name => $fixture) { + $this->converter->expects($this->at($index++))->method('convert')->willReturn([$name => $fixture]); + } + + $this->registry->expects($this->once()) + ->method('getPaths') + ->willReturn([ + '/path/to/Foo_Bar', + '/path/to/Magento_Directory', + '/path/to/Magento_Store', + '/path/to/Magento_Theme', + '/path/to/Test_HelloWorld' + ]); + + $this->driver->expects($this->exactly(5)) + ->method('fileGetContents') + ->will($this->returnValueMap([ + ['/path/to/Foo_Bar/etc/module.xml', null, null, self::$sampleXml], + ['/path/to/Magento_Directory/etc/module.xml', null, null, self::$sampleXml], + ['/path/to/Magento_Store/etc/module.xml', null, null, self::$sampleXml], + ['/path/to/Magento_Theme/etc/module.xml', null, null, self::$sampleXml], + ['/path/to/Test_HelloWorld/etc/module.xml', null, null, self::$sampleXml], + ])); + + // Load the full module list information + $result = $this->loader->load(); + + $this->assertSame( + ['Magento_Store', 'Magento_Directory', 'Magento_Theme', 'Foo_Bar', 'Test_HelloWorld'], + array_keys($result) + ); + + foreach ($fixtures as $name => $fixture) { + $this->assertSame($fixture, $result[$name]); + } + } } From 4b2c1b47998775c114b443ae3dae78d532727598 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Wed, 20 Feb 2019 13:30:27 +0200 Subject: [PATCH 0783/1295] Fix static tests. --- lib/internal/Magento/Framework/Module/ModuleList/Loader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Module/ModuleList/Loader.php b/lib/internal/Magento/Framework/Module/ModuleList/Loader.php index 80958f65e110e..72421f793f131 100644 --- a/lib/internal/Magento/Framework/Module/ModuleList/Loader.php +++ b/lib/internal/Magento/Framework/Module/ModuleList/Loader.php @@ -134,7 +134,7 @@ private function sortBySequence(array $origList): array $modules = $this->prearrangeModules($origList); $expanded = []; - foreach ($modules as $moduleName => $value) { + foreach (array_keys($modules) as $moduleName) { $sequence = $this->expandSequence($origList, $moduleName); asort($sequence); From 730987f7405376c7d9cf8964ef61f74a09c2c019 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Mon, 25 Feb 2019 08:30:09 +0200 Subject: [PATCH 0784/1295] Adjusting the Unit Test --- .../Framework/Module/Test/Unit/ModuleList/LoaderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Module/Test/Unit/ModuleList/LoaderTest.php b/lib/internal/Magento/Framework/Module/Test/Unit/ModuleList/LoaderTest.php index a62bb5fa70f97..827a0df8e4930 100644 --- a/lib/internal/Magento/Framework/Module/Test/Unit/ModuleList/LoaderTest.php +++ b/lib/internal/Magento/Framework/Module/Test/Unit/ModuleList/LoaderTest.php @@ -164,7 +164,7 @@ public function testLoadCircular() /** * @throws \Magento\Framework\Exception\LocalizedException */ - public function testLoadPrearranged(): void + public function testLoadPrearranged() { $fixtures = [ 'Foo_Bar' => ['name' => 'Foo_Bar', 'sequence' => ['Magento_Store']], From b42950aedf52033f1199c2c570a4ac2866649102 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Mon, 25 Feb 2019 10:10:59 +0200 Subject: [PATCH 0785/1295] MAGETWO-95616: Changing Attribute Set may lead to exception "Attempt to load value of nonexistent EAV attribute" --- .../Unit/Model/ResourceModel/ReadHandlerTest.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/ReadHandlerTest.php b/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/ReadHandlerTest.php index 5f74579e32a12..10a6d99caa5f7 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/ReadHandlerTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/ReadHandlerTest.php @@ -88,7 +88,8 @@ protected function setUp() $this->connectionMock = $this->createMock(AdapterInterface::class); $this->attributeMock = $this->createMock(AbstractAttribute::class); - $this->readHandler = $this->objectManager->getObject(ReadHandler::class, + $this->readHandler = $this->objectManager->getObject( + ReadHandler::class, [ 'metadataPool' => $this->metadataPoolMock, 'scopeResolver' => $this->scopeResolverMock, @@ -143,9 +144,11 @@ public function testExecuteNew() [ 't' => 'some_table' ], - ['value' => 't.value', 'attribute_id' => 't.attribute_id'] - ) - ->willReturnSelf(); + [ + 'value' => 't.value', + 'attribute_id' => 't.attribute_id', + ] + )->willReturnSelf(); $this->metadataMock->expects($this->exactly(2))->method('getLinkField')->willReturn('linkField'); $selectMock->expects($this->at(1)) ->method('where') @@ -176,7 +179,8 @@ public function testExecuteNew() $selectMock->expects($this->at(4))->method('columns')->with($identifier, 't')->willReturnSelf(); $this->connectionMock->expects($this->at(2))->method('select')->willReturn($orderedUnionSelectMock); - $unionSelect = $this->objectManager->getObject(UnionExpression::class, + $unionSelect = $this->objectManager->getObject( + UnionExpression::class, [ 'parts' => [$selectMock], 'type' => 'UNION ALL', From e9f03335d3732d79531d43e9ce274db2ab9f9939 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Mon, 25 Feb 2019 10:54:46 +0200 Subject: [PATCH 0786/1295] MC-15073: Left Side Back End Menu Design broken --- .../Magento_Backend/web/css/source/module/_menu.less | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less index 42c9b289afdb7..0573054d5b96f 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less @@ -271,17 +271,9 @@ &._show { > .submenu { - display: block; - float: left; - left: 100%; - max-width: 1640px; - min-height: 98.65%; - min-width: 100%; - overflow-x: scroll; - position: absolute; transform: translateX(0); visibility: visible; - z-index: 698; + z-index: @submenu__z-index; } } } From 8bda0fded8562be036a17f099bec3b6e86de5c09 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Mon, 25 Feb 2019 11:49:42 +0200 Subject: [PATCH 0787/1295] MAGETWO-95616: Changing Attribute Set may lead to exception "Attempt to load value of nonexistent EAV attribute" --- .../Test/Unit/Model/ResourceModel/ReadHandlerTest.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/ReadHandlerTest.php b/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/ReadHandlerTest.php index 10a6d99caa5f7..66977a34a5adc 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/ReadHandlerTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/ReadHandlerTest.php @@ -20,6 +20,11 @@ use Magento\Framework\Model\Entity\ScopeInterface; use Psr\Log\LoggerInterface; +/** + * Test for Magento\Eav\Model\ResourceModel\ReadHandler class. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class ReadHandlerTest extends \PHPUnit\Framework\TestCase { /** @@ -102,7 +107,7 @@ protected function setUp() /** * @return void */ - public function testExecuteNew() + public function testExecute() { $eavEntityType = 'env-entity-type'; $entityData = ['linkField' => 'theLinkField']; @@ -119,9 +124,7 @@ public function testExecuteNew() $orderedUnionSelectMock = $this->createMock(Select::class); $fallbackScopeMock = $this->createMock(ScopeInterface::class); - $this->metadataPoolMock->expects($this->exactly(2)) - ->method('getMetadata') - ->willReturn($this->metadataMock); + $this->metadataPoolMock->expects($this->exactly(2))->method('getMetadata')->willReturn($this->metadataMock); $this->metadataMock->expects($this->exactly(2))->method('getEavEntityType')->willReturn($eavEntityType); $this->scopeResolverMock->expects($this->once()) ->method('getEntityContext') From d40ea45a5827fd4da763d20be6cde0d3b279809f Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <svizev.igor@gmail.com> Date: Wed, 10 Oct 2018 10:39:59 +0300 Subject: [PATCH 0788/1295] Checkout - Fix JS error Cannot read property 'quoteData' of undefined --- app/code/Magento/Checkout/view/frontend/web/js/model/quote.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/quote.js b/app/code/Magento/Checkout/view/frontend/web/js/model/quote.js index 2510d1aced3d3..3486a92736617 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/quote.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/quote.js @@ -7,7 +7,8 @@ */ define([ 'ko', - 'underscore' + 'underscore', + 'domReady!' ], function (ko, _) { 'use strict'; From dcc772199cb4c0faa0a8cc44dbea9f911dfab468 Mon Sep 17 00:00:00 2001 From: Anton Evers <evers@adobe.com> Date: Wed, 2 Jan 2019 14:02:29 +0100 Subject: [PATCH 0789/1295] Make it possible to generate sales PDF's using the API --- .../base/templates/info/pdf/default.phtml | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 app/code/Magento/Payment/view/base/templates/info/pdf/default.phtml diff --git a/app/code/Magento/Payment/view/base/templates/info/pdf/default.phtml b/app/code/Magento/Payment/view/base/templates/info/pdf/default.phtml new file mode 100644 index 0000000000000..7acac62f65d38 --- /dev/null +++ b/app/code/Magento/Payment/view/base/templates/info/pdf/default.phtml @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +// @codingStandardsIgnoreFile +/** + * @see \Magento\Payment\Block\Info + * @var \Magento\Payment\Block\Info $block + */ +?> +<?= $block->escapeHtml($block->getMethod()->getTitle()) ?>{{pdf_row_separator}} + +<?php if ($specificInfo = $block->getSpecificInformation()):?> + <?php foreach ($specificInfo as $label => $value):?> + <?= $block->escapeHtml($label) ?>: + <?= $block->escapeHtml(implode(' ', $block->getValueAsArray($value))) ?> + {{pdf_row_separator}} + <?php endforeach; ?> +<?php endif;?> + +<?= $block->escapeHtml(implode('{{pdf_row_separator}}', $block->getChildPdfAsArray())) ?> From 21386b3ee4c7d3d1c06af3d7e44b3b675c8c8288 Mon Sep 17 00:00:00 2001 From: Anton Evers <evers@adobe.com> Date: Thu, 3 Jan 2019 13:54:57 +0100 Subject: [PATCH 0790/1295] Make it possible to generate sales PDF's using the API --- .../base/templates/info/pdf/checkmo.phtml | 26 +++++++++++++++++++ .../templates/info/pdf/purchaseorder.phtml | 11 ++++++++ 2 files changed, 37 insertions(+) create mode 100644 app/code/Magento/OfflinePayments/view/base/templates/info/pdf/checkmo.phtml create mode 100644 app/code/Magento/OfflinePayments/view/base/templates/info/pdf/purchaseorder.phtml diff --git a/app/code/Magento/OfflinePayments/view/base/templates/info/pdf/checkmo.phtml b/app/code/Magento/OfflinePayments/view/base/templates/info/pdf/checkmo.phtml new file mode 100644 index 0000000000000..4d63577319d5b --- /dev/null +++ b/app/code/Magento/OfflinePayments/view/base/templates/info/pdf/checkmo.phtml @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +// @codingStandardsIgnoreFile +/** + * @var $block \Magento\OfflinePayments\Block\Info\Checkmo + */ +?> +<?= $block->escapeHtml($block->getMethod()->getTitle()) ?> + {{pdf_row_separator}} +<?php if ($block->getInfo()->getAdditionalInformation()): ?> + {{pdf_row_separator}} + <?php if ($block->getPayableTo()): ?> + <?= $block->escapeHtml(__('Make Check payable to: %1', $block->getPayableTo())) ?> + {{pdf_row_separator}} + <?php endif; ?> + <?php if ($block->getMailingAddress()): ?> + <?= $block->escapeHtml(__('Send Check to:')) ?> + {{pdf_row_separator}} + <?= /* @noEscape */ nl2br($block->escapeHtml($block->getMailingAddress())) ?> + {{pdf_row_separator}} + <?php endif; ?> +<?php endif; ?> diff --git a/app/code/Magento/OfflinePayments/view/base/templates/info/pdf/purchaseorder.phtml b/app/code/Magento/OfflinePayments/view/base/templates/info/pdf/purchaseorder.phtml new file mode 100644 index 0000000000000..4a6ea1c00b21c --- /dev/null +++ b/app/code/Magento/OfflinePayments/view/base/templates/info/pdf/purchaseorder.phtml @@ -0,0 +1,11 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +/** + * @var $block \Magento\OfflinePayments\Block\Info\Purchaseorder + */ +?> +<?= $block->escapeHtml(__('Purchase Order Number: %1', $block->getInfo()->getPoNumber())) ?> + {{pdf_row_separator}} From cd9287867dbe0843614ef6ac9cf75da203505d82 Mon Sep 17 00:00:00 2001 From: LisovyiEvhenii <lisovyievhenii@gmail.com> Date: Thu, 24 Jan 2019 15:34:04 +0200 Subject: [PATCH 0791/1295] #13982: Customer Login Block sets the title for the page when rendered. Move setPageTitle from Block to layout configs --- app/code/Magento/Customer/Block/Form/Login.php | 9 --------- .../view/frontend/layout/customer_account_login.xml | 5 +++++ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Customer/Block/Form/Login.php b/app/code/Magento/Customer/Block/Form/Login.php index 7b265ae1f0f32..d3d3306a49b44 100644 --- a/app/code/Magento/Customer/Block/Form/Login.php +++ b/app/code/Magento/Customer/Block/Form/Login.php @@ -47,15 +47,6 @@ public function __construct( $this->_customerSession = $customerSession; } - /** - * @return $this - */ - protected function _prepareLayout() - { - $this->pageConfig->getTitle()->set(__('Customer Login')); - return parent::_prepareLayout(); - } - /** * Retrieve form posting url * diff --git a/app/code/Magento/Customer/view/frontend/layout/customer_account_login.xml b/app/code/Magento/Customer/view/frontend/layout/customer_account_login.xml index d49dae6dee58f..00f9c4ed84d24 100644 --- a/app/code/Magento/Customer/view/frontend/layout/customer_account_login.xml +++ b/app/code/Magento/Customer/view/frontend/layout/customer_account_login.xml @@ -7,6 +7,11 @@ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> + <referenceBlock name="page.main.title"> + <action method="setPageTitle"> + <argument name="title" translate="true" xsi:type="string">Customer Login</argument> + </action> + </referenceBlock> <referenceContainer name="content"> <!-- customer.form.login.extra --> <container name="customer.login.container" label="Customer Login Container" htmlTag="div" htmlClass="login-container"> From f1de3406cd8cfb3454baef8a72ed2d2cc0d4f10e Mon Sep 17 00:00:00 2001 From: LisovyiEvhenii <lisovyievhenii@gmail.com> Date: Fri, 25 Jan 2019 10:32:37 +0200 Subject: [PATCH 0792/1295] #13982: fix page title to be displayed both in head's <title> and page's title --- .../view/frontend/layout/customer_account_login.xml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Customer/view/frontend/layout/customer_account_login.xml b/app/code/Magento/Customer/view/frontend/layout/customer_account_login.xml index 00f9c4ed84d24..3518df736c4ac 100644 --- a/app/code/Magento/Customer/view/frontend/layout/customer_account_login.xml +++ b/app/code/Magento/Customer/view/frontend/layout/customer_account_login.xml @@ -6,12 +6,10 @@ */ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> + <head> + <title>Customer Login + - - - Customer Login - - From 2e58ab6009c68ea1e4e0095897517c68273995f9 Mon Sep 17 00:00:00 2001 From: Vinai Kopp Date: Sun, 3 Feb 2019 17:46:26 +0530 Subject: [PATCH 0793/1295] magento/magento2#20773: Make autoloader PSR-4 compliant --- .../Code/Generator/AutoloaderTest.php | 85 +++++++++++++++++++ .../Framework/Code/Generator/Autoloader.php | 70 +++++++++++++-- 2 files changed, 146 insertions(+), 9 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Code/Generator/AutoloaderTest.php diff --git a/dev/tests/integration/testsuite/Magento/Framework/Code/Generator/AutoloaderTest.php b/dev/tests/integration/testsuite/Magento/Framework/Code/Generator/AutoloaderTest.php new file mode 100644 index 0000000000000..0e1b51b3ae273 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Code/Generator/AutoloaderTest.php @@ -0,0 +1,85 @@ +createMock(LoggerInterface::class); + $this->getTestFrameworkObjectManager()->addSharedInstance($loggerTestDouble, MagentoMonologLogger::class); + } + + /** + * @after + */ + public function removeLoggerTestDouble(): void + { + $this->getTestFrameworkObjectManager()->removeSharedInstance(MagentoMonologLogger::class); + } + + /** + * @param \RuntimeException $testException + * @return Generator|MockObject + */ + private function createExceptionThrowingGeneratorTestDouble(\RuntimeException $testException) + { + /** @var Generator|MockObject $generatorStub */ + $generatorStub = $this->createMock(Generator::class); + $generatorStub->method('generateClass')->willThrowException($testException); + + return $generatorStub; + } + + public function testLogsExceptionDuringGeneration(): void + { + $exceptionMessage = 'Test exception thrown during generation'; + $testException = new \RuntimeException($exceptionMessage); + + $loggerMock = ObjectManager::getInstance()->get(LoggerInterface::class); + $loggerMock->expects($this->once())->method('debug')->with($exceptionMessage, ['exception' => $testException]); + + $autoloader = new Autoloader($this->createExceptionThrowingGeneratorTestDouble($testException)); + $this->assertNull($autoloader->load(NonExistingClassName::class)); + } + + public function testFiltersDuplicateExceptionMessages(): void + { + $exceptionMessage = 'Test exception thrown during generation'; + $testException = new \RuntimeException($exceptionMessage); + + $loggerMock = ObjectManager::getInstance()->get(LoggerInterface::class); + $loggerMock->expects($this->once())->method('debug')->with($exceptionMessage, ['exception' => $testException]); + + $autoloader = new Autoloader($this->createExceptionThrowingGeneratorTestDouble($testException)); + $autoloader->load(OneNonExistingClassName::class); + $autoloader->load(AnotherNonExistingClassName::class); + } +} diff --git a/lib/internal/Magento/Framework/Code/Generator/Autoloader.php b/lib/internal/Magento/Framework/Code/Generator/Autoloader.php index c214008393609..1afa97729b158 100644 --- a/lib/internal/Magento/Framework/Code/Generator/Autoloader.php +++ b/lib/internal/Magento/Framework/Code/Generator/Autoloader.php @@ -3,37 +3,89 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Framework\Code\Generator; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Code\Generator; +use Psr\Log\LoggerInterface; class Autoloader { /** - * @var \Magento\Framework\Code\Generator + * @var Generator */ protected $_generator; /** - * @param \Magento\Framework\Code\Generator $generator + * Enables guarding against spamming the debug log with duplicate messages, as + * the generation exception will be thrown multiple times within a single request. + * + * @var string + */ + private $lastGenerationErrorMessage; + + /** + * @param Generator $generator */ - public function __construct( - \Magento\Framework\Code\Generator $generator - ) { + public function __construct(Generator $generator) + { $this->_generator = $generator; } /** * Load specified class name and generate it if necessary * + * According to PSR-4 section 2.4 an autoloader MUST NOT throw an exception and SHOULD NOT return a value. + * + * @see https://www.php-fig.org/psr/psr-4/ + * * @param string $className - * @return bool True if class was loaded + * @return void */ public function load($className) { - if (!class_exists($className)) { - return Generator::GENERATION_ERROR != $this->_generator->generateClass($className); + if (! class_exists($className)) { + try { + $this->_generator->generateClass($className); + } catch (\Exception $exception) { + $this->tryToLogExceptionMessageIfNotDuplicate($exception); + } + } + } + + /** + * @param \Exception $exception + */ + private function tryToLogExceptionMessageIfNotDuplicate(\Exception $exception): void + { + if ($this->lastGenerationErrorMessage !== $exception->getMessage()) { + $this->lastGenerationErrorMessage = $exception->getMessage(); + $this->tryToLogException($exception); + } + } + + /** + * Try to capture the exception message. + * + * The Autoloader is instantiated before the ObjectManager, so the LoggerInterface can not be injected. + * The Logger is instantiated in the try/catch block because ObjectManager might still not be initialized. + * In that case the exception message can not be captured. + * + * The debug level is used for logging in case class generation fails for a common class, but a custom + * autoloader is used later in the stack. A more severe log level would fill the logs with messages on production. + * The exception message now can be accessed in developer mode if debug logging is enabled. + * + * @param \Exception $exception + * @return void + */ + private function tryToLogException(\Exception $exception): void + { + try { + $logger = ObjectManager::getInstance()->get(LoggerInterface::class); + $logger->debug($exception->getMessage(), ['exception' => $exception]); + } catch (\Exception $ignoreThisException) { + // Do not take an action here, since the original exception might have been caused by logger } - return true; } } From 3de1647e51215ba933f007cab64e9483f2487c61 Mon Sep 17 00:00:00 2001 From: nmalevanec Date: Fri, 22 Feb 2019 11:55:31 +0200 Subject: [PATCH 0794/1295] Fix static tests. --- lib/internal/Magento/Framework/Code/Generator/Autoloader.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/internal/Magento/Framework/Code/Generator/Autoloader.php b/lib/internal/Magento/Framework/Code/Generator/Autoloader.php index 1afa97729b158..35c138147e9d3 100644 --- a/lib/internal/Magento/Framework/Code/Generator/Autoloader.php +++ b/lib/internal/Magento/Framework/Code/Generator/Autoloader.php @@ -10,6 +10,9 @@ use Magento\Framework\Code\Generator; use Psr\Log\LoggerInterface; +/** + * Class loader and generator. + */ class Autoloader { /** @@ -55,6 +58,8 @@ public function load($className) } /** + * Log exception. + * * @param \Exception $exception */ private function tryToLogExceptionMessageIfNotDuplicate(\Exception $exception): void From b635e090921616459f254b63ae7de80f51a9e228 Mon Sep 17 00:00:00 2001 From: Nazarn96 Date: Mon, 25 Feb 2019 13:59:50 +0200 Subject: [PATCH 0795/1295] Backport-issue-195196 --- app/code/Magento/Eav/Model/Attribute/Data/File.php | 2 +- .../Magento/Ui/view/base/web/js/form/element/file-uploader.js | 3 ++- app/code/Magento/Variable/view/adminhtml/web/variables.js | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Eav/Model/Attribute/Data/File.php b/app/code/Magento/Eav/Model/Attribute/Data/File.php index d6476eefc59b1..1ee66915453c3 100644 --- a/app/code/Magento/Eav/Model/Attribute/Data/File.php +++ b/app/code/Magento/Eav/Model/Attribute/Data/File.php @@ -146,7 +146,7 @@ protected function _validateByRules($value) return $this->_fileValidator->getMessages(); } - if (!empty($value['tmp_name']) && !is_uploaded_file($value['tmp_name'])) { + if (!empty($value['tmp_name']) && !file_exists($value['tmp_name'])) { return [__('"%1" is not a valid file.', $label)]; } diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js index 4b178622c28cf..1fbde3601cc18 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js @@ -14,7 +14,8 @@ define([ 'Magento_Ui/js/lib/validation/validator', 'Magento_Ui/js/form/element/abstract', 'mage/translate', - 'jquery/file-uploader' + 'jquery/file-uploader', + 'mage/adminhtml/tools' ], function ($, _, utils, uiAlert, validator, Element, $t) { 'use strict'; diff --git a/app/code/Magento/Variable/view/adminhtml/web/variables.js b/app/code/Magento/Variable/view/adminhtml/web/variables.js index d519053b5265a..e91f172f59fe7 100644 --- a/app/code/Magento/Variable/view/adminhtml/web/variables.js +++ b/app/code/Magento/Variable/view/adminhtml/web/variables.js @@ -9,7 +9,8 @@ define([ 'mage/translate', 'Magento_Ui/js/modal/modal', 'jquery/ui', - 'prototype' + 'prototype', + 'mage/adminhtml/tools' ], function (jQuery, $t) { 'use strict'; From ae2ce0676522428e0d5aa8275e12609db9192868 Mon Sep 17 00:00:00 2001 From: Andrii Meysar Date: Mon, 25 Feb 2019 14:42:58 +0200 Subject: [PATCH 0796/1295] MC-15079: Exception is thrown when cart product is removed from shared catalog --- .../Quote/Item/CollectionTest.php | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Quote/Model/ResourceModel/Quote/Item/CollectionTest.php diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/ResourceModel/Quote/Item/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/ResourceModel/Quote/Item/CollectionTest.php new file mode 100644 index 0000000000000..60ba5deda4529 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/ResourceModel/Quote/Item/CollectionTest.php @@ -0,0 +1,96 @@ +objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + } + + /** + * Covers case when during quote item collection load product exists in db but not accessible. + * + * @magentoDataFixture Magento/Sales/_files/quote.php + * @return void + */ + public function testLoadCollectionWithNotAccessibleProduct() + { + /** @var Quote $quote */ + $quote = $this->objectManager->create(Quote::class); + $quote->load('test01', 'reserved_order_id'); + + $this->assertCount(1, $quote->getItemsCollection()); + + $product = $this->productRepository->get('simple'); + /** @var ProductCollection $productCollection */ + $productCollection = $this->objectManager->create(ProductCollection::class); + $this->setPropertyValue($productCollection, '_isCollectionLoaded', true); + /** @var ProductCollectionFactory|\PHPUnit_Framework_MockObject_MockObject $productCollectionFactoryMock */ + $productCollectionFactoryMock = $this->createMock(ProductCollectionFactory::class); + $productCollectionFactoryMock->expects($this->any())->method('create')->willReturn($productCollection); + + /** @var QuoteItemCollection $quoteItemCollection */ + $quoteItemCollection = $this->objectManager->create( + QuoteItemCollection::class, + [ + 'productCollectionFactory' => $productCollectionFactoryMock, + ] + ); + + $quoteItemCollection->setQuote($quote); + $this->assertCount(1, $quoteItemCollection); + $item = $quoteItemCollection->getItemByColumnValue('product_id', $product->getId()); + + $this->assertNotNull($item); + $this->assertTrue($item->isDeleted()); + } + + /** + * Set object non-public property value. + * + * @param object $object + * @param string $propertyName + * @param mixed $value + * @return void + */ + private function setPropertyValue($object, string $propertyName, $value) + { + $reflectionClass = new \ReflectionClass($object); + if ($reflectionClass->hasProperty($propertyName)) { + $reflectionProperty = $reflectionClass->getProperty($propertyName); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($object, $value); + } + } +} From 102d5972b4c69f5efb6e7ee5100120d06716ecb4 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov Date: Mon, 25 Feb 2019 14:44:29 +0200 Subject: [PATCH 0797/1295] MAGETWO-94421: Default addresses not selected when checking out from cart --- .../Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml index 4af8128d7ac14..eabe51776894e 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml @@ -181,7 +181,7 @@ - + @@ -196,7 +196,7 @@ - + @@ -211,7 +211,7 @@ - + From f83d29760883a1fa57c809b340a2b9ad6e3401de Mon Sep 17 00:00:00 2001 From: Viktor Petryk Date: Mon, 25 Feb 2019 14:48:57 +0200 Subject: [PATCH 0798/1295] MAGETWO-73985: Simple product with MAP assigned to configurable should displays the same way as products with special price --- .../ActionGroup/AdminProductActionGroup.xml | 22 ++++++++++++ .../StorefrontProductActionGroup.xml | 30 ++++++++++++++++ ...AdminProductFormAdvancedPricingSection.xml | 13 +++++++ .../StorefrontProductInfoMainSection.xml | 15 ++++++++ ...ingConfigurableProductPriceWithMapTest.xml | 36 +++++++++++++++++++ 5 files changed, 116 insertions(+) create mode 100644 app/code/Magento/Msrp/Test/Mftf/ActionGroup/AdminProductActionGroup.xml create mode 100644 app/code/Magento/Msrp/Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml create mode 100644 app/code/Magento/Msrp/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml create mode 100644 app/code/Magento/Msrp/Test/Mftf/Section/StorefrontProductInfoMainSection.xml diff --git a/app/code/Magento/Msrp/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Msrp/Test/Mftf/ActionGroup/AdminProductActionGroup.xml new file mode 100644 index 0000000000000..d6672619dbdb2 --- /dev/null +++ b/app/code/Magento/Msrp/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/Msrp/Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml b/app/code/Magento/Msrp/Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml new file mode 100644 index 0000000000000..f3408ef81c575 --- /dev/null +++ b/app/code/Magento/Msrp/Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + ${{msrp}} + grabMsrp + + + + + + + + + + + diff --git a/app/code/Magento/Msrp/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml b/app/code/Magento/Msrp/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml new file mode 100644 index 0000000000000..0819d5cf35f9d --- /dev/null +++ b/app/code/Magento/Msrp/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml @@ -0,0 +1,13 @@ + + + +
+ +
+
diff --git a/app/code/Magento/Msrp/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/Msrp/Test/Mftf/Section/StorefrontProductInfoMainSection.xml new file mode 100644 index 0000000000000..7aba2d8d6e211 --- /dev/null +++ b/app/code/Magento/Msrp/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -0,0 +1,15 @@ + + + +
+ + + +
+
diff --git a/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontCheckingConfigurableProductPriceWithMapTest.xml b/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontCheckingConfigurableProductPriceWithMapTest.xml index 034c0c8378e44..127769b3dbe2d 100644 --- a/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontCheckingConfigurableProductPriceWithMapTest.xml +++ b/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontCheckingConfigurableProductPriceWithMapTest.xml @@ -40,6 +40,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
From b18b8a531bc477eb8c6f1cc64fc47f4c5bb3090a Mon Sep 17 00:00:00 2001 From: Viktor Petryk Date: Mon, 25 Feb 2019 14:51:43 +0200 Subject: [PATCH 0799/1295] MAGETWO-73985: Simple product with MAP assigned to configurable should displays the same way as products with special price --- .../StorefrontCheckingConfigurableProductPriceWithMapTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontCheckingConfigurableProductPriceWithMapTest.xml b/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontCheckingConfigurableProductPriceWithMapTest.xml index 127769b3dbe2d..2b6ebaadc3c8e 100644 --- a/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontCheckingConfigurableProductPriceWithMapTest.xml +++ b/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontCheckingConfigurableProductPriceWithMapTest.xml @@ -10,7 +10,7 @@ - + <description value="Check that simple products with MAP assigned to configurable product displayed correctly"/> <severity value="CRITICAL"/> From 4369bba58d160670363fb3c67a9f36f5d287e429 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Mon, 25 Feb 2019 15:35:48 +0200 Subject: [PATCH 0800/1295] MAGETWO-96895: [Magento Cloud] Import doesn't work with skip error entries --- .../Model/Import/Product.php | 112 ++++++--- .../Test/Unit/Model/Import/ProductTest.php | 219 +++++++++--------- .../Magento/ImportExport/Model/Import.php | 7 - .../Model/Import/ProductTest.php | 7 +- .../Adminhtml/Import/ValidateTest.php | 9 +- 5 files changed, 201 insertions(+), 153 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 3578123e94dc1..f1d1868621fff 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -3,12 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\CatalogImportExport\Model\Import; use Magento\Catalog\Model\Config as CatalogConfig; use Magento\Catalog\Model\Product\Visibility; use Magento\CatalogImportExport\Model\Import\Product\MediaGalleryProcessor; use Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface as ValidatorInterface; +use Magento\CatalogImportExport\Model\StockItemImporterInterface; use Magento\CatalogInventory\Api\Data\StockItemInterface; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Exception\LocalizedException; @@ -638,6 +640,13 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity */ private $_logger; + /** + * "Duplicate multiselect values" error array key + * + * @var string + */ + private static $errorDuplicateMultiselectValues = 'duplicatedMultiselectValues'; + /** * {@inheritdoc} */ @@ -767,7 +776,6 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity * @param CatalogConfig $catalogConfig * @param MediaGalleryProcessor $mediaProcessor * @param ProductRepositoryInterface|null $productRepository - * @throws \Magento\Framework\Exception\LocalizedException * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -857,11 +865,8 @@ public function __construct( $string, $errorAggregator ); - $this->_optionEntity = isset( - $data['option_entity'] - ) ? $data['option_entity'] : $optionFactory->create( - ['data' => ['product_entity' => $this]] - ); + $this->_optionEntity = $data['option_entity'] + ?? $optionFactory->create(['data' => ['product_entity' => $this]]); $this->_initAttributeSets() ->_initTypeModels() @@ -869,6 +874,9 @@ public function __construct( $this->validator->init($this); $this->productRepository = $productRepository ?? \Magento\Framework\App\ObjectManager::getInstance() ->get(ProductRepositoryInterface::class); + + $errorMessageText = 'Value for multiselect attribute %s contains duplicated values'; + $this->_messageTemplates[self::$errorDuplicateMultiselectValues] = $errorMessageText; } /** @@ -884,7 +892,7 @@ public function isAttributeValid($attrCode, array $attrParams, array $rowData, $ { if (!$this->validator->isAttributeValid($attrCode, $attrParams, $rowData)) { foreach ($this->validator->getMessages() as $message) { - $this->addRowError($message, $rowNum, $attrCode); + $this->skipRow($rowNum, $message, ProcessingError::ERROR_LEVEL_NOT_CRITICAL, $attrCode); } return false; } @@ -1592,7 +1600,7 @@ protected function _saveProducts() if (!empty($rowData[self::URL_KEY])) { // If url_key column and its value were in the CSV file $rowData[self::URL_KEY] = $urlKey; - } else if ($this->isNeedToChangeUrlKey($rowData)) { + } elseif ($this->isNeedToChangeUrlKey($rowData)) { // If url_key column was empty or even not declared in the CSV file but by the rules it is need to // be setteed. In case when url_key is generating from name column we have to ensure that the bunch // of products will pass for the event with url_key column. @@ -1604,7 +1612,9 @@ protected function _saveProducts() if (null === $rowSku) { $this->getErrorAggregator()->addRowToSkip($rowNum); continue; - } elseif (self::SCOPE_STORE == $rowScope) { + } + + if (self::SCOPE_STORE == $rowScope) { // set necessary data from SCOPE_DEFAULT row $rowData[self::COL_TYPE] = $this->skuProcessor->getNewSku($rowSku)['type_id']; $rowData['attribute_set_id'] = $this->skuProcessor->getNewSku($rowSku)['attr_set_id']; @@ -1740,13 +1750,7 @@ protected function _saveProducts() $uploadedImages[$columnImage] = $uploadedFile; } else { unset($rowData[$column]); - $this->addRowError( - ValidatorInterface::ERROR_MEDIA_URL_NOT_ACCESSIBLE, - $rowNum, - null, - null, - ProcessingError::ERROR_LEVEL_NOT_CRITICAL - ); + $this->skipRow($rowNum, ValidatorInterface::ERROR_MEDIA_URL_NOT_ACCESSIBLE); } } else { $uploadedFile = $uploadedImages[$columnImage]; @@ -2380,32 +2384,35 @@ public function validateRow(array $rowData, $rowNum) // BEHAVIOR_DELETE and BEHAVIOR_REPLACE use specific validation logic if (Import::BEHAVIOR_REPLACE == $this->getBehavior()) { if (self::SCOPE_DEFAULT == $rowScope && !$this->isSkuExist($sku)) { - $this->addRowError(ValidatorInterface::ERROR_SKU_NOT_FOUND_FOR_DELETE, $rowNum); + $this->skipRow($rowNum, ValidatorInterface::ERROR_SKU_NOT_FOUND_FOR_DELETE); return false; } } if (Import::BEHAVIOR_DELETE == $this->getBehavior()) { if (self::SCOPE_DEFAULT == $rowScope && !$this->isSkuExist($sku)) { - $this->addRowError(ValidatorInterface::ERROR_SKU_NOT_FOUND_FOR_DELETE, $rowNum); + $this->skipRow($rowNum, ValidatorInterface::ERROR_SKU_NOT_FOUND_FOR_DELETE); return false; } return true; } + // if product doesn't exist, need to throw critical error else all errors should be not critical. + $errorLevel = $this->getValidationErrorLevel($sku); + if (!$this->validator->isValid($rowData)) { foreach ($this->validator->getMessages() as $message) { - $this->addRowError($message, $rowNum, $this->validator->getInvalidAttribute()); + $this->skipRow($rowNum, $message, $errorLevel, $this->validator->getInvalidAttribute()); } } if (null === $sku) { - $this->addRowError(ValidatorInterface::ERROR_SKU_IS_EMPTY, $rowNum); + $this->skipRow($rowNum, ValidatorInterface::ERROR_SKU_IS_EMPTY, $errorLevel); } elseif (false === $sku) { - $this->addRowError(ValidatorInterface::ERROR_ROW_IS_ORPHAN, $rowNum); + $this->skipRow($rowNum, ValidatorInterface::ERROR_ROW_IS_ORPHAN, $errorLevel); } elseif (self::SCOPE_STORE == $rowScope && !$this->storeResolver->getStoreCodeToId($rowData[self::COL_STORE]) ) { - $this->addRowError(ValidatorInterface::ERROR_INVALID_STORE, $rowNum); + $this->skipRow($rowNum, ValidatorInterface::ERROR_INVALID_STORE, $errorLevel); } // SKU is specified, row is SCOPE_DEFAULT, new product block begins @@ -2420,19 +2427,15 @@ public function validateRow(array $rowData, $rowNum) $this->prepareNewSkuData($sku) ); } else { - $this->addRowError(ValidatorInterface::ERROR_TYPE_UNSUPPORTED, $rowNum); + $this->skipRow($rowNum, ValidatorInterface::ERROR_TYPE_UNSUPPORTED, $errorLevel); } } else { // validate new product type and attribute set - if (!isset($rowData[self::COL_TYPE]) || !isset($this->_productTypeModels[$rowData[self::COL_TYPE]])) { - $this->addRowError(ValidatorInterface::ERROR_INVALID_TYPE, $rowNum); - } elseif (!isset( - $rowData[self::COL_ATTR_SET] - ) || !isset( - $this->_attrSetNameToId[$rowData[self::COL_ATTR_SET]] - ) + if (!isset($rowData[self::COL_TYPE], $this->_productTypeModels[$rowData[self::COL_TYPE]])) { + $this->skipRow($rowNum, ValidatorInterface::ERROR_INVALID_TYPE, $errorLevel); + } elseif (!isset($rowData[self::COL_ATTR_SET], $this->_attrSetNameToId[$rowData[self::COL_ATTR_SET]]) ) { - $this->addRowError(ValidatorInterface::ERROR_INVALID_ATTR_SET, $rowNum); + $this->skipRow($rowNum, ValidatorInterface::ERROR_INVALID_ATTR_SET, $errorLevel); } elseif (is_null($this->skuProcessor->getNewSku($sku))) { $this->skuProcessor->addNewSku( $sku, @@ -2488,8 +2491,11 @@ public function validateRow(array $rowData, $rowNum) ValidatorInterface::ERROR_DUPLICATE_URL_KEY, $rowNum, $rowData[self::COL_NAME], - $message - ); + $message, + $errorLevel + ) + ->getErrorAggregator() + ->addRowToSkip($rowNum); } } } @@ -2499,9 +2505,10 @@ public function validateRow(array $rowData, $rowNum) $newFromTimestamp = strtotime($this->dateTime->formatDate($rowData[self::COL_NEW_FROM_DATE], false)); $newToTimestamp = strtotime($this->dateTime->formatDate($rowData[self::COL_NEW_TO_DATE], false)); if ($newFromTimestamp > $newToTimestamp) { - $this->addRowError( - ValidatorInterface::ERROR_NEW_TO_DATE, + $this->skipRow( $rowNum, + ValidatorInterface::ERROR_NEW_TO_DATE, + $errorLevel, $rowData[self::COL_NEW_TO_DATE] ); } @@ -3029,4 +3036,39 @@ private function retrieveProductBySku(string $sku) return $product; } + + /** + * Add row as skipped + * + * @param int $rowNum + * @param string $errorCode Error code or simply column name + * @param string $errorLevel error level + * @param string|null $colName optional column name + * @return $this + */ + private function skipRow( + int $rowNum, + string $errorCode, + string $errorLevel = ProcessingError::ERROR_LEVEL_NOT_CRITICAL, + $colName = null + ): self { + $this->addRowError($errorCode, $rowNum, $colName, null, $errorLevel); + $this->getErrorAggregator() + ->addRowToSkip($rowNum); + + return $this; + } + + /** + * Returns errorLevel for validation + * + * @param string $sku + * @return string + */ + private function getValidationErrorLevel(string $sku): string + { + return (!$this->isSkuExist($sku) && Import::BEHAVIOR_REPLACE !== $this->getBehavior()) + ? ProcessingError::ERROR_LEVEL_CRITICAL + : ProcessingError::ERROR_LEVEL_NOT_CRITICAL; + } } diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php index 1cd19852f393c..3f72fcc39f548 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php @@ -3,10 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\CatalogImportExport\Test\Unit\Model\Import; +use Magento\CatalogImportExport\Model\Import\Product; +use Magento\CatalogImportExport\Model\Import\Product\ImageTypeProcessor; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\ImportExport\Model\Import; +use PHPUnit_Framework_MockObject_MockObject as MockObject; /** * Class ProductTest @@ -25,126 +29,126 @@ class ProductTest extends \Magento\ImportExport\Test\Unit\Model\Import\AbstractI const ENTITY_ID = 13; - /** @var \Magento\Framework\DB\Adapter\AdapterInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Framework\DB\Adapter\AdapterInterface| MockObject */ protected $_connection; - /** @var \Magento\Framework\Json\Helper\Data|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Framework\Json\Helper\Data| MockObject */ protected $jsonHelper; - /** @var \Magento\ImportExport\Model\ResourceModel\Import\Data|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\ImportExport\Model\ResourceModel\Import\Data| MockObject */ protected $_dataSourceModel; - /** @var \Magento\Framework\App\ResourceConnection|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Framework\App\ResourceConnection| MockObject */ protected $resource; - /** @var \Magento\ImportExport\Model\ResourceModel\Helper|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\ImportExport\Model\ResourceModel\Helper| MockObject */ protected $_resourceHelper; - /** @var \Magento\Framework\Stdlib\StringUtils|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Framework\Stdlib\StringUtils|MockObject */ protected $string; - /** @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Framework\Event\ManagerInterface|MockObject */ protected $_eventManager; - /** @var \Magento\CatalogInventory\Api\StockRegistryInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\CatalogInventory\Api\StockRegistryInterface|MockObject */ protected $stockRegistry; - /** @var \Magento\CatalogImportExport\Model\Import\Product\OptionFactory|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\CatalogImportExport\Model\Import\Product\OptionFactory|MockObject */ protected $optionFactory; - /** @var \Magento\CatalogInventory\Api\StockConfigurationInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\CatalogInventory\Api\StockConfigurationInterface|MockObject */ protected $stockConfiguration; - /** @var \Magento\CatalogInventory\Model\Spi\StockStateProviderInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\CatalogInventory\Model\Spi\StockStateProviderInterface|MockObject */ protected $stockStateProvider; - /** @var \Magento\CatalogImportExport\Model\Import\Product\Option|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\CatalogImportExport\Model\Import\Product\Option|MockObject */ protected $optionEntity; - /** @var \Magento\Framework\Stdlib\DateTime|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Framework\Stdlib\DateTime|MockObject */ protected $dateTime; /** @var array */ protected $data; - /** @var \Magento\ImportExport\Helper\Data|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\ImportExport\Helper\Data|MockObject */ protected $importExportData; - /** @var \Magento\ImportExport\Model\ResourceModel\Import\Data|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\ImportExport\Model\ResourceModel\Import\Data|MockObject */ protected $importData; - /** @var \Magento\Eav\Model\Config|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Eav\Model\Config|MockObject */ protected $config; - /** @var \Magento\ImportExport\Model\ResourceModel\Helper|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\ImportExport\Model\ResourceModel\Helper|MockObject */ protected $resourceHelper; - /** @var \Magento\Catalog\Helper\Data|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Catalog\Helper\Data|MockObject */ protected $_catalogData; - /** @var \Magento\ImportExport\Model\Import\Config|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\ImportExport\Model\Import\Config|MockObject */ protected $_importConfig; - /** @var \PHPUnit_Framework_MockObject_MockObject */ + /** @var MockObject */ protected $_resourceFactory; // @codingStandardsIgnoreStart - /** @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory|MockObject */ protected $_setColFactory; - /** @var \Magento\CatalogImportExport\Model\Import\Product\Type\Factory|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\CatalogImportExport\Model\Import\Product\Type\Factory|MockObject */ protected $_productTypeFactory; - /** @var \Magento\Catalog\Model\ResourceModel\Product\LinkFactory|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Catalog\Model\ResourceModel\Product\LinkFactory|MockObject */ protected $_linkFactory; - /** @var \Magento\CatalogImportExport\Model\Import\Proxy\ProductFactory|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\CatalogImportExport\Model\Import\Proxy\ProductFactory|MockObject */ protected $_proxyProdFactory; - /** @var \Magento\CatalogImportExport\Model\Import\UploaderFactory|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\CatalogImportExport\Model\Import\UploaderFactory|MockObject */ protected $_uploaderFactory; - /** @var \Magento\Framework\Filesystem|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Framework\Filesystem|MockObject */ protected $_filesystem; - /** @var \Magento\Framework\Filesystem\Directory\WriteInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Framework\Filesystem\Directory\WriteInterface|MockObject */ protected $_mediaDirectory; - /** @var \Magento\CatalogInventory\Model\ResourceModel\Stock\ItemFactory|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\CatalogInventory\Model\ResourceModel\Stock\ItemFactory|MockObject */ protected $_stockResItemFac; - /** @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface|MockObject */ protected $_localeDate; - /** @var \Magento\Framework\Indexer\IndexerRegistry|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Framework\Indexer\IndexerRegistry|MockObject */ protected $indexerRegistry; - /** @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Psr\Log\LoggerInterface|MockObject */ protected $_logger; - /** @var \Magento\CatalogImportExport\Model\Import\Product\StoreResolver|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\CatalogImportExport\Model\Import\Product\StoreResolver|MockObject */ protected $storeResolver; - /** @var \Magento\CatalogImportExport\Model\Import\Product\SkuProcessor|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\CatalogImportExport\Model\Import\Product\SkuProcessor|MockObject */ protected $skuProcessor; - /** @var \Magento\CatalogImportExport\Model\Import\Product\CategoryProcessor|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\CatalogImportExport\Model\Import\Product\CategoryProcessor|MockObject */ protected $categoryProcessor; - /** @var \Magento\CatalogImportExport\Model\Import\Product\Validator|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\CatalogImportExport\Model\Import\Product\Validator|MockObject */ protected $validator; - /** @var \Magento\Framework\Model\ResourceModel\Db\ObjectRelationProcessor|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Framework\Model\ResourceModel\Db\ObjectRelationProcessor|MockObject */ protected $objectRelationProcessor; - /** @var \Magento\Framework\Model\ResourceModel\Db\TransactionManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Framework\Model\ResourceModel\Db\TransactionManagerInterface|MockObject */ protected $transactionManager; - /** @var \Magento\CatalogImportExport\Model\Import\Product\TaxClassProcessor|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\CatalogImportExport\Model\Import\Product\TaxClassProcessor|MockObject */ // @codingStandardsIgnoreEnd protected $taxClassProcessor; - /** @var \Magento\CatalogImportExport\Model\Import\Product */ + /** @var Product */ protected $importProduct; /** @@ -152,10 +156,10 @@ class ProductTest extends \Magento\ImportExport\Test\Unit\Model\Import\AbstractI */ protected $errorAggregator; - /** @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject*/ + /** @var \Magento\Framework\App\Config\ScopeConfigInterface|MockObject */ protected $scopeConfig; - /** @var \Magento\Catalog\Model\Product\Url|\PHPUnit_Framework_MockObject_MockObject*/ + /** @var \Magento\Catalog\Model\Product\Url|MockObject */ protected $productUrl; /** @@ -334,7 +338,7 @@ protected function setUp() $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->importProduct = $objectManager->getObject( - \Magento\CatalogImportExport\Model\Import\Product::class, + Product::class, [ 'jsonHelper' => $this->jsonHelper, 'importExportData' => $this->importExportData, @@ -375,7 +379,7 @@ protected function setUp() 'data' => $this->data ] ); - $reflection = new \ReflectionClass(\Magento\CatalogImportExport\Model\Import\Product::class); + $reflection = new \ReflectionClass(Product::class); $reflectionProperty = $reflection->getProperty('metadataPool'); $reflectionProperty->setAccessible(true); $reflectionProperty->setValue($this->importProduct, $metadataPoolMock); @@ -584,7 +588,7 @@ public function testGetMultipleValueSeparatorFromParameters() public function testDeleteProductsForReplacement() { - $importProduct = $this->getMockBuilder(\Magento\CatalogImportExport\Model\Import\Product::class) + $importProduct = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() ->setMethods([ 'setParameters', '_deleteProducts' @@ -650,7 +654,7 @@ public function testValidateRowIsAlreadyValidated() */ public function testValidateRow($rowScope, $oldSku, $expectedResult, $behaviour = Import::BEHAVIOR_DELETE) { - $importProduct = $this->getMockBuilder(\Magento\CatalogImportExport\Model\Import\Product::class) + $importProduct = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() ->setMethods(['getBehavior', 'getRowScope', 'getErrorAggregator']) ->getMock(); @@ -662,7 +666,7 @@ public function testValidateRow($rowScope, $oldSku, $expectedResult, $behaviour ->method('getErrorAggregator') ->willReturn($this->getErrorAggregatorObject()); $importProduct->expects($this->once())->method('getRowScope')->willReturn($rowScope); - $skuKey = \Magento\CatalogImportExport\Model\Import\Product::COL_SKU; + $skuKey = Product::COL_SKU; $rowData = [ $skuKey => 'sku', ]; @@ -674,18 +678,22 @@ public function testValidateRow($rowScope, $oldSku, $expectedResult, $behaviour public function testValidateRowDeleteBehaviourAddRowErrorCall() { - $importProduct = $this->getMockBuilder(\Magento\CatalogImportExport\Model\Import\Product::class) + $importProduct = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() - ->setMethods(['getBehavior', 'getRowScope', 'addRowError']) + ->setMethods(['getBehavior', 'getRowScope', 'addRowError', 'getErrorAggregator']) ->getMock(); $importProduct->expects($this->exactly(2))->method('getBehavior') ->willReturn(\Magento\ImportExport\Model\Import::BEHAVIOR_DELETE); $importProduct->expects($this->once())->method('getRowScope') - ->willReturn(\Magento\CatalogImportExport\Model\Import\Product::SCOPE_DEFAULT); + ->willReturn(Product::SCOPE_DEFAULT); $importProduct->expects($this->once())->method('addRowError'); + $importProduct->method('getErrorAggregator') + ->willReturn( + $this->getErrorAggregatorObject(['addRowToSkip']) + ); $rowData = [ - \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => 'sku', + Product::COL_SKU => 'sku', ]; $importProduct->validateRow($rowData, 0); @@ -696,7 +704,7 @@ public function testValidateRowValidatorCheck() $messages = ['validator message']; $this->validator->expects($this->once())->method('getMessages')->willReturn($messages); $rowData = [ - \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => 'sku', + Product::COL_SKU => 'sku', ]; $rowNum = 0; $this->importProduct->validateRow($rowData, $rowNum); @@ -798,7 +806,7 @@ public function getStoreIdByCodeDataProvider() return [ [ '$storeCode' => null, - '$expectedResult' => \Magento\CatalogImportExport\Model\Import\Product::SCOPE_DEFAULT, + '$expectedResult' => Product::SCOPE_DEFAULT, ], [ '$storeCode' => 'value', @@ -813,17 +821,17 @@ public function getStoreIdByCodeDataProvider() public function testValidateRowCheckSpecifiedSku($sku, $expectedError) { $importProduct = $this->createModelMockWithErrorAggregator( - [ 'addRowError', 'getOptionEntity', 'getRowScope'], + ['addRowError', 'getOptionEntity', 'getRowScope'], ['isRowInvalid' => true] ); $rowNum = 0; $rowData = [ - \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => $sku, - \Magento\CatalogImportExport\Model\Import\Product::COL_STORE => '', + Product::COL_SKU => $sku, + Product::COL_STORE => '', ]; - $this->storeResolver->expects($this->any())->method('getStoreCodeToId')->willReturn(null); + $this->storeResolver->method('getStoreCodeToId')->willReturn(null); $this->setPropertyValue($importProduct, 'storeResolver', $this->storeResolver); $this->setPropertyValue($importProduct, 'skuProcessor', $this->skuProcessor); @@ -832,7 +840,7 @@ public function testValidateRowCheckSpecifiedSku($sku, $expectedError) $importProduct ->expects($this->once()) ->method('getRowScope') - ->willReturn(\Magento\CatalogImportExport\Model\Import\Product::SCOPE_STORE); + ->willReturn(Product::SCOPE_STORE); $importProduct->expects($this->at(1))->method('addRowError')->with($expectedError, $rowNum)->willReturn(null); $importProduct->validateRow($rowData, $rowNum); @@ -846,7 +854,7 @@ public function testValidateRowProcessEntityIncrement() $errorAggregator->method('isRowInvalid')->willReturn(true); $this->setPropertyValue($this->importProduct, '_processedEntitiesCount', $count); $this->setPropertyValue($this->importProduct, 'errorAggregator', $errorAggregator); - $rowData = [\Magento\CatalogImportExport\Model\Import\Product::COL_SKU => false]; + $rowData = [Product::COL_SKU => false]; //suppress validator $this->_setValidatorMockInImportProduct($this->importProduct); $this->importProduct->validateRow($rowData, $rowNum); @@ -856,14 +864,14 @@ public function testValidateRowProcessEntityIncrement() public function testValidateRowValidateExistingProductTypeAddNewSku() { $importProduct = $this->createModelMockWithErrorAggregator( - [ 'addRowError', 'getOptionEntity'], + ['addRowError', 'getOptionEntity'], ['isRowInvalid' => true] ); $sku = 'sku'; $rowNum = 0; $rowData = [ - \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => $sku, + Product::COL_SKU => $sku, ]; $oldSku = [ $sku => [ @@ -886,7 +894,7 @@ public function testValidateRowValidateExistingProductTypeAddNewSku() $this->setPropertyValue($importProduct, '_oldSku', $oldSku); $expectedData = [ - 'entity_id' => $oldSku[$sku]['entity_id'], //entity_id_val + 'entity_id' => $oldSku[$sku]['entity_id'], //entity_id_val 'type_id' => $oldSku[$sku]['type_id'],// type_id_val 'attr_set_id' => $oldSku[$sku]['attr_set_id'], //attr_set_id_val 'attr_set_code' => $_attrSetIdToName[$oldSku[$sku]['attr_set_id']],//attr_set_id_val @@ -904,7 +912,7 @@ public function testValidateRowValidateExistingProductTypeAddErrorRowCall() $sku = 'sku'; $rowNum = 0; $rowData = [ - \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => $sku, + Product::COL_SKU => $sku, ]; $oldSku = [ $sku => [ @@ -929,6 +937,11 @@ public function testValidateRowValidateExistingProductTypeAddErrorRowCall() /** * @dataProvider validateRowValidateNewProductTypeAddRowErrorCallDataProvider + * @param string $colType + * @param string $productTypeModelsColType + * @param string $colAttrSet + * @param string $attrSetNameToIdColAttrSet + * @param string $error */ public function testValidateRowValidateNewProductTypeAddRowErrorCall( $colType, @@ -940,15 +953,15 @@ public function testValidateRowValidateNewProductTypeAddRowErrorCall( $sku = 'sku'; $rowNum = 0; $rowData = [ - \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => $sku, - \Magento\CatalogImportExport\Model\Import\Product::COL_TYPE => $colType, - \Magento\CatalogImportExport\Model\Import\Product::COL_ATTR_SET => $colAttrSet, + Product::COL_SKU => $sku, + Product::COL_TYPE => $colType, + Product::COL_ATTR_SET => $colAttrSet, ]; $_attrSetNameToId = [ - $rowData[\Magento\CatalogImportExport\Model\Import\Product::COL_ATTR_SET] => $attrSetNameToIdColAttrSet, + $rowData[Product::COL_ATTR_SET] => $attrSetNameToIdColAttrSet, ]; $_productTypeModels = [ - $rowData[\Magento\CatalogImportExport\Model\Import\Product::COL_TYPE] => $productTypeModelsColType, + $rowData[Product::COL_TYPE] => $productTypeModelsColType, ]; $oldSku = [ $sku => null, @@ -976,29 +989,25 @@ public function testValidateRowValidateNewProductTypeGetNewSkuCall() $sku = 'sku'; $rowNum = 0; $rowData = [ - \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => $sku, - \Magento\CatalogImportExport\Model\Import\Product::COL_TYPE => 'value', - \Magento\CatalogImportExport\Model\Import\Product::COL_ATTR_SET => 'value', + Product::COL_SKU => $sku, + Product::COL_TYPE => 'value', + Product::COL_ATTR_SET => 'value', ]; $_productTypeModels = [ - $rowData[\Magento\CatalogImportExport\Model\Import\Product::COL_TYPE] => 'value', + $rowData[Product::COL_TYPE] => 'value', ]; $oldSku = [ $sku => null, ]; $_attrSetNameToId = [ - $rowData[\Magento\CatalogImportExport\Model\Import\Product::COL_ATTR_SET] => 'attr_set_code_val' + $rowData[Product::COL_ATTR_SET] => 'attr_set_code_val', ]; $expectedData = [ 'entity_id' => null, - 'type_id' => $rowData[\Magento\CatalogImportExport\Model\Import\Product::COL_TYPE],//value + 'type_id' => $rowData[Product::COL_TYPE], //attr_set_id_val - 'attr_set_id' => $_attrSetNameToId[ - $rowData[ - \Magento\CatalogImportExport\Model\Import\Product::COL_ATTR_SET - ] - ], - 'attr_set_code' => $rowData[\Magento\CatalogImportExport\Model\Import\Product::COL_ATTR_SET],//value + 'attr_set_id' => $_attrSetNameToId[$rowData[Product::COL_ATTR_SET]], + 'attr_set_code' => $rowData[Product::COL_ATTR_SET], 'row_id' => null ]; $importProduct = $this->createModelMockWithErrorAggregator( @@ -1034,8 +1043,8 @@ public function testValidateRowSetAttributeSetCodeIntoRowData() $sku = 'sku'; $rowNum = 0; $rowData = [ - \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => $sku, - \Magento\CatalogImportExport\Model\Import\Product::COL_ATTR_SET => 'col_attr_set_val', + Product::COL_SKU => $sku, + Product::COL_ATTR_SET => 'col_attr_set_val', ]; $expectedAttrSetCode = 'new_attr_set_code'; $newSku = [ @@ -1043,8 +1052,8 @@ public function testValidateRowSetAttributeSetCodeIntoRowData() 'type_id' => 'new_type_id_val', ]; $expectedRowData = [ - \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => $sku, - \Magento\CatalogImportExport\Model\Import\Product::COL_ATTR_SET => $newSku['attr_set_code'], + Product::COL_SKU => $sku, + Product::COL_ATTR_SET => $newSku['attr_set_code'], ]; $oldSku = [ $sku => [ @@ -1078,8 +1087,8 @@ public function testValidateValidateOptionEntity() $sku = 'sku'; $rowNum = 0; $rowData = [ - \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => $sku, - \Magento\CatalogImportExport\Model\Import\Product::COL_ATTR_SET => 'col_attr_set_val', + Product::COL_SKU => $sku, + Product::COL_ATTR_SET => 'col_attr_set_val', ]; $oldSku = [ $sku => [ @@ -1336,7 +1345,7 @@ public function validateRowDataProvider() { return [ [ - '$rowScope' => \Magento\CatalogImportExport\Model\Import\Product::SCOPE_DEFAULT, + '$rowScope' => Product::SCOPE_DEFAULT, '$oldSku' => null, '$expectedResult' => false, ], @@ -1351,12 +1360,12 @@ public function validateRowDataProvider() '$expectedResult' => true, ], [ - '$rowScope' => \Magento\CatalogImportExport\Model\Import\Product::SCOPE_DEFAULT, + '$rowScope' => Product::SCOPE_DEFAULT, '$oldSku' => true, '$expectedResult' => true, ], [ - '$rowScope' => \Magento\CatalogImportExport\Model\Import\Product::SCOPE_DEFAULT, + '$rowScope' => Product::SCOPE_DEFAULT, '$oldSku' => null, '$expectedResult' => false, '$behaviour' => Import::BEHAVIOR_REPLACE @@ -1377,7 +1386,7 @@ public function isAttributeValidAssertAttrValidDataProvider() '$rowData' => [ 'code' => str_repeat( 'a', - \Magento\CatalogImportExport\Model\Import\Product::DB_MAX_VARCHAR_LENGTH - 1 + Product::DB_MAX_VARCHAR_LENGTH - 1 ), ], ], @@ -1430,7 +1439,7 @@ public function isAttributeValidAssertAttrValidDataProvider() '$rowData' => [ 'code' => str_repeat( 'a', - \Magento\CatalogImportExport\Model\Import\Product::DB_MAX_TEXT_LENGTH - 1 + Product::DB_MAX_TEXT_LENGTH - 1 ), ], ], @@ -1450,7 +1459,7 @@ public function isAttributeValidAssertAttrInvalidDataProvider() '$rowData' => [ 'code' => str_repeat( 'a', - \Magento\CatalogImportExport\Model\Import\Product::DB_MAX_VARCHAR_LENGTH + 1 + Product::DB_MAX_VARCHAR_LENGTH + 1 ), ], ], @@ -1503,7 +1512,7 @@ public function isAttributeValidAssertAttrInvalidDataProvider() '$rowData' => [ 'code' => str_repeat( 'a', - \Magento\CatalogImportExport\Model\Import\Product::DB_MAX_TEXT_LENGTH + 1 + Product::DB_MAX_TEXT_LENGTH + 1 ), ], ], @@ -1515,8 +1524,8 @@ public function isAttributeValidAssertAttrInvalidDataProvider() */ public function getRowScopeDataProvider() { - $colSku = \Magento\CatalogImportExport\Model\Import\Product::COL_SKU; - $colStore = \Magento\CatalogImportExport\Model\Import\Product::COL_STORE; + $colSku = Product::COL_SKU; + $colStore = Product::COL_STORE; return [ [ @@ -1524,21 +1533,21 @@ public function getRowScopeDataProvider() $colSku => null, $colStore => 'store', ], - '$expectedResult' => \Magento\CatalogImportExport\Model\Import\Product::SCOPE_STORE + '$expectedResult' => Product::SCOPE_STORE, ], [ '$rowData' => [ $colSku => 'sku', $colStore => null, ], - '$expectedResult' => \Magento\CatalogImportExport\Model\Import\Product::SCOPE_DEFAULT + '$expectedResult' => Product::SCOPE_DEFAULT, ], [ '$rowData' => [ $colSku => 'sku', $colStore => 'store', ], - '$expectedResult' => \Magento\CatalogImportExport\Model\Import\Product::SCOPE_STORE + '$expectedResult' => Product::SCOPE_STORE, ], ]; } @@ -1615,9 +1624,9 @@ protected function overrideMethod(&$object, $methodName, array $parameters = []) * * @see _rewriteGetOptionEntityInImportProduct() * @see _setValidatorMockInImportProduct() - * @param \Magento\CatalogImportExport\Model\Import\Product + * @param Product * Param should go with rewritten getOptionEntity method. - * @return \Magento\CatalogImportExport\Model\Import\Product\Option|\PHPUnit_Framework_MockObject_MockObject + * @return \Magento\CatalogImportExport\Model\Import\Product\Option|MockObject */ private function _suppressValidateRowOptionValidatorInvalidRows($importProduct) { @@ -1633,8 +1642,8 @@ private function _suppressValidateRowOptionValidatorInvalidRows($importProduct) * Used in group of validateRow method's tests. * Set validator mock in importProduct, return true for isValid method. * - * @param \Magento\CatalogImportExport\Model\Import\Product - * @return \Magento\CatalogImportExport\Model\Import\Product\Validator|\PHPUnit_Framework_MockObject_MockObject + * @param Product + * @return \Magento\CatalogImportExport\Model\Import\Product\Validator|MockObject */ private function _setValidatorMockInImportProduct($importProduct) { @@ -1648,9 +1657,9 @@ private function _setValidatorMockInImportProduct($importProduct) * Used in group of validateRow method's tests. * Make getOptionEntity return option mock. * - * @param \Magento\CatalogImportExport\Model\Import\Product + * @param Product * Param should go with rewritten getOptionEntity method. - * @return \Magento\CatalogImportExport\Model\Import\Product\Option|\PHPUnit_Framework_MockObject_MockObject + * @return \Magento\CatalogImportExport\Model\Import\Product\Option|MockObject */ private function _rewriteGetOptionEntityInImportProduct($importProduct) { @@ -1665,12 +1674,12 @@ private function _rewriteGetOptionEntityInImportProduct($importProduct) /** * @param array $methods * @param array $errorAggregatorMethods - * @return \PHPUnit_Framework_MockObject_MockObject + * @return MockObject */ protected function createModelMockWithErrorAggregator(array $methods = [], array $errorAggregatorMethods = []) { $methods[] = 'getErrorAggregator'; - $importProduct = $this->getMockBuilder(\Magento\CatalogImportExport\Model\Import\Product::class) + $importProduct = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() ->setMethods($methods) ->getMock(); diff --git a/app/code/Magento/ImportExport/Model/Import.php b/app/code/Magento/ImportExport/Model/Import.php index 6ff2ca8c4d0eb..2d8b84cd035ff 100644 --- a/app/code/Magento/ImportExport/Model/Import.php +++ b/app/code/Magento/ImportExport/Model/Import.php @@ -626,13 +626,6 @@ public function validateSource(\Magento\ImportExport\Model\Import\AbstractSource $errorsCount = $errorAggregator->getErrorsCount(); $result = !$errorsCount; - $validationStrategy = $this->getData(self::FIELD_NAME_VALIDATION_STRATEGY); - if ($errorsCount - && $validationStrategy === ProcessingErrorAggregatorInterface::VALIDATION_STRATEGY_SKIP_ERRORS - ) { - $this->messageManager->addWarningMessage(__('Skipped errors: %1', $errorsCount)); - $result = true; - } if ($result) { $this->addLogComment(__('Import data validation is complete.')); diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index 9cc7c452e646f..6e361dfb05de0 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -1388,10 +1388,15 @@ public function testValidateUrlKeys($importFile, $expectedErrors) )->setSource( $source )->validateData(); + $urlKeyErrors = $errors->getErrorsByCode([RowValidatorInterface::ERROR_DUPLICATE_URL_KEY]); $this->assertEquals( $expectedErrors[RowValidatorInterface::ERROR_DUPLICATE_URL_KEY], - count($errors->getErrorsByCode([RowValidatorInterface::ERROR_DUPLICATE_URL_KEY])) + count($urlKeyErrors) ); + + foreach ($urlKeyErrors as $error) { + $this->assertEquals('critical', $error->getErrorLevel()); + } } /** diff --git a/dev/tests/integration/testsuite/Magento/ImportExport/Controller/Adminhtml/Import/ValidateTest.php b/dev/tests/integration/testsuite/Magento/ImportExport/Controller/Adminhtml/Import/ValidateTest.php index aab94361f752c..674335d361ffb 100644 --- a/dev/tests/integration/testsuite/Magento/ImportExport/Controller/Adminhtml/Import/ValidateTest.php +++ b/dev/tests/integration/testsuite/Magento/ImportExport/Controller/Adminhtml/Import/ValidateTest.php @@ -3,9 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\ImportExport\Controller\Adminhtml\Import; use Magento\Framework\Filesystem\DirectoryList; +use Magento\Framework\HTTP\Adapter\FileTransferFactory; use Magento\ImportExport\Model\Import; use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface; @@ -61,10 +63,7 @@ public function testValidationReturn(string $fileName, string $mimeType, string $this->_objectManager->configure( [ - 'preferences' => [ - \Magento\Framework\HTTP\Adapter\FileTransferFactory::class => - \Magento\ImportExport\Controller\Adminhtml\Import\HttpFactoryMock::class - ] + 'preferences' => [FileTransferFactory::class => HttpFactoryMock::class] ] ); @@ -81,7 +80,7 @@ public function testValidationReturn(string $fileName, string $mimeType, string /** * @return array */ - public function validationDataProvider() + public function validationDataProvider(): array { return [ [ From 3e428206b80ff0cec3687def7bbeff23a801d8eb Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Mon, 25 Feb 2019 16:09:32 +0200 Subject: [PATCH 0801/1295] MC-15079: Exception is thrown when cart product is removed from shared catalog --- .../Quote/Model/ResourceModel/Quote/Item/CollectionTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/ResourceModel/Quote/Item/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/ResourceModel/Quote/Item/CollectionTest.php index 60ba5deda4529..7bfdb096673cc 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/ResourceModel/Quote/Item/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/ResourceModel/Quote/Item/CollectionTest.php @@ -11,6 +11,7 @@ use Magento\Catalog\Model\ResourceModel\Product\Collection as ProductCollection; use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory; use Magento\Framework\ObjectManagerInterface; +use Magento\Quote\Model\Quote; use Magento\Quote\Model\ResourceModel\Quote\Item\Collection as QuoteItemCollection; use Magento\TestFramework\Helper\Bootstrap; From a58911f6e0d5e78765b90688097e10deb8bb0231 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Mon, 25 Feb 2019 17:20:56 +0200 Subject: [PATCH 0802/1295] MAGETWO-96895: [Magento Cloud] Import doesn't work with skip error entries --- .../Magento/CatalogImportExport/Model/Import/Product.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index f1d1868621fff..9cb6d4bd2390d 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -875,7 +875,7 @@ public function __construct( $this->productRepository = $productRepository ?? \Magento\Framework\App\ObjectManager::getInstance() ->get(ProductRepositoryInterface::class); - $errorMessageText = 'Value for multiselect attribute %s contains duplicated values'; + $errorMessageText = __('Value for multiselect attribute %s contains duplicated values'); $this->_messageTemplates[self::$errorDuplicateMultiselectValues] = $errorMessageText; } @@ -3062,10 +3062,10 @@ private function skipRow( /** * Returns errorLevel for validation * - * @param string $sku + * @param string|bool|null $sku * @return string */ - private function getValidationErrorLevel(string $sku): string + private function getValidationErrorLevel($sku): string { return (!$this->isSkuExist($sku) && Import::BEHAVIOR_REPLACE !== $this->getBehavior()) ? ProcessingError::ERROR_LEVEL_CRITICAL From 0673cb94424d049ea827b1298dedd7c3c1daa3e8 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Mon, 25 Feb 2019 18:24:21 +0200 Subject: [PATCH 0803/1295] MAGETWO-73524: WebAPI: product "has_options" flag not updated when options added via API --- .../Option/UpdateProductCustomOptionsAttributes.php | 5 +++-- .../Catalog/Api/ProductCustomOptionRepositoryTest.php | 11 +++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Catalog/Plugin/Model/Product/Option/UpdateProductCustomOptionsAttributes.php b/app/code/Magento/Catalog/Plugin/Model/Product/Option/UpdateProductCustomOptionsAttributes.php index 2d25bd695763e..ada96d4fd48f4 100644 --- a/app/code/Magento/Catalog/Plugin/Model/Product/Option/UpdateProductCustomOptionsAttributes.php +++ b/app/code/Magento/Catalog/Plugin/Model/Product/Option/UpdateProductCustomOptionsAttributes.php @@ -45,8 +45,9 @@ public function afterSave( ) { $product->setCanSaveCustomOptions(true); $product->setOptionsSaved(true); - $currentOptions = array_filter($product->getOptions(), function ($iOption) use ($option) { - return $option->getOptionId() != $iOption->getOptionId(); + $optionId = $option->getOptionId(); + $currentOptions = array_filter($product->getOptions(), function ($optionItem) use ($optionId) { + return $optionId != $optionItem->getOptionId(); }); $currentOptions[] = $option; $product->setOptions($currentOptions); diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php index 5cffcea0fa832..e99a892b184ec 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductCustomOptionRepositoryTest.php @@ -32,6 +32,9 @@ class ProductCustomOptionRepositoryTest extends WebapiAbstract */ private $productRepository; + /** + * @inheritdoc + */ protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); @@ -175,9 +178,9 @@ public function testSave($optionData) } } $this->assertEquals($optionData, $result); - $this->assertTrue($product->getHasOptions() == 1); + $this->assertEquals(1, $product->getHasOptions()); if ($optionDataPost['is_require']) { - $this->assertTrue($product->getRequiredOptions() == 1); + $this->assertEquals(1, $product->getRequiredOptions()); } } @@ -200,7 +203,7 @@ public function optionDataProvider() * @dataProvider optionNegativeDataProvider * @param array $optionData */ - public function testAddNegative($optionData) + public function testAddNegative(array $optionData) { $productSku = 'simple'; $optionDataPost = $optionData; @@ -301,7 +304,7 @@ public function testUpdate() * @param string $optionType * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ - public function testUpdateOptionAddingNewValue($optionType) + public function testUpdateOptionAddingNewValue(string $optionType) { $fixtureOption = null; $valueData = [ From bb6cf0e62024cf3e2ad74107f2856b9e5462df60 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Tue, 26 Feb 2019 10:15:02 +0200 Subject: [PATCH 0804/1295] MAGETWO-95616: Changing Attribute Set may lead to exception "Attempt to load value of nonexistent EAV attribute" --- .../Eav/Test/Unit/Model/ResourceModel/ReadHandlerTest.php | 2 +- lib/internal/Magento/Framework/DB/Sql/UnionExpression.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/ReadHandlerTest.php b/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/ReadHandlerTest.php index 66977a34a5adc..9020f22c497b6 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/ReadHandlerTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/ResourceModel/ReadHandlerTest.php @@ -145,7 +145,7 @@ public function testExecute() ->method('from') ->with( [ - 't' => 'some_table' + 't' => 'some_table', ], [ 'value' => 't.value', diff --git a/lib/internal/Magento/Framework/DB/Sql/UnionExpression.php b/lib/internal/Magento/Framework/DB/Sql/UnionExpression.php index 9957c6ffb8272..6961426fcc7f1 100644 --- a/lib/internal/Magento/Framework/DB/Sql/UnionExpression.php +++ b/lib/internal/Magento/Framework/DB/Sql/UnionExpression.php @@ -32,7 +32,7 @@ class UnionExpression extends Expression * @param string $type (optional) * @param string $pattern (optional) */ - public function __construct(array $parts, string $type = Select::SQL_UNION, string $pattern = '') + public function __construct(array $parts, $type = Select::SQL_UNION, $pattern = '') { $this->parts = $parts; $this->type = $type; From 6c09eca826e4ad3b4215748ae52054383b6163da Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Tue, 26 Feb 2019 12:37:48 +0200 Subject: [PATCH 0805/1295] MAGETWO-95904: Header Minicart, Shopping cart page and Checkout page show incorrect product name --- .../Magento/Checkout/Block/Cart/Sidebar.php | 2 + .../CheckoutShippingGuestInfoSection.xml | 2 - ...tOnCheckoutPageDifferentStoreViewsTest.xml | 19 ++- .../view/frontend/web/js/view/minicart.js | 4 +- .../Quote/Plugin/UpdateQuoteItemStore.php | 4 +- .../Unit/Plugin/UpdateQuoteItemStoreTest.php | 143 ++++++++++++++++++ 6 files changed, 159 insertions(+), 15 deletions(-) create mode 100644 app/code/Magento/Quote/Test/Unit/Plugin/UpdateQuoteItemStoreTest.php diff --git a/app/code/Magento/Checkout/Block/Cart/Sidebar.php b/app/code/Magento/Checkout/Block/Cart/Sidebar.php index 7453d3c62e862..5e3234e9f4cc8 100644 --- a/app/code/Magento/Checkout/Block/Cart/Sidebar.php +++ b/app/code/Magento/Checkout/Block/Cart/Sidebar.php @@ -133,6 +133,7 @@ public function getShoppingCartUrl() * * @return string * @codeCoverageIgnore + * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) */ public function getUpdateItemQtyUrl() { @@ -144,6 +145,7 @@ public function getUpdateItemQtyUrl() * * @return string * @codeCoverageIgnore + * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) */ public function getRemoveItemUrl() { diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingGuestInfoSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingGuestInfoSection.xml index 0a243d54cedef..115eb92c48268 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingGuestInfoSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingGuestInfoSection.xml @@ -17,7 +17,5 @@ <element name="region" type="select" selector="select[name=region_id]"/> <element name="postcode" type="input" selector="input[name=postcode]"/> <element name="telephone" type="input" selector="input[name=telephone]"/> - <element name="itemInCart" type="button" selector="div.items-in-cart div.title"/> - <element name="productName" type="text" selector="div.items-in-cart strong.product-item-name"/> </section> </sections> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml index a810aa7107ff1..4622a6bd1ebf6 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml @@ -10,22 +10,23 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest"> <annotations> + <features value="Checkout"/> <title value="Checking Product name with custom store views"/> <description value="Checking Product name in Minicart and on Checkout page with custom store views"/> - <stories value="Verify product name with custom store view"/> + <stories value="Checkout via Guest Checkout"/> <severity value="MAJOR"/> <testCaseId value="MC-14944"/> - <features value="Checkout"/> + <useCaseId value="MAGETWO-95904"/> <group value="checkout"/> </annotations> <before> - <!--Login as Admin--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!--Create a product--> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <createData entity="SimpleProduct" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> </createData> + <!--Login as Admin--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!-- Create store view --> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"> <argument name="customStore" value="customStore"/> @@ -39,6 +40,7 @@ <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"> <argument name="customStore" value="customStore"/> </actionGroup> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearGridFilter"/> <!--Logout from admin--> <actionGroup ref="logout" stepKey="logout"/> </after> @@ -55,7 +57,7 @@ <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> <!--Add product to cart--> - <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="amOnProductPage"/> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.sku$$)}}" stepKey="amOnProductPage"/> <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> <argument name="productName" value="$createProduct.name$$"/> </actionGroup> @@ -76,12 +78,13 @@ </actionGroup> <!--Go to Shopping Cart--> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToCheckoutFromMinicart"/> + <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartFromMinicart"/> <seeElement selector="{{CheckoutCartProductSection.productLinkByName($$createProduct.name$$-new)}}" stepKey="assertProductName"/> <!--Proceed to checkout--> <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutPage"/> - <conditionalClick selector="{{CheckoutShippingGuestInfoSection.itemInCart}}" dependentSelector="{{CheckoutShippingGuestInfoSection.itemInCart}}" visible="true" stepKey="clickItemsInCart"/> - <see selector="{{CheckoutShippingGuestInfoSection.productName}}" userInput="$$createProduct.name$$-new" stepKey="seeProductNameAtCheckout"/> + <conditionalClick selector="{{CheckoutOrderSummarySection.miniCartTab}}" dependentSelector="{{CheckoutOrderSummarySection.miniCartTab}}" visible="true" stepKey="clickItemsInCart"/> + <waitForElementVisible selector="{{CheckoutOrderSummarySection.productItemName}}" stepKey="waitForProduct"/> + <see selector="{{CheckoutOrderSummarySection.productItemName}}" userInput="$$createProduct.name$$-new" stepKey="seeProductNameAtCheckout"/> </test> </tests> diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js b/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js index 5e29fa209a641..da1b1a41e2fa9 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js @@ -102,8 +102,8 @@ define([ self.isLoading(true); }); - if (cartData().website_id !== window.checkout.websiteId || - cartData().store_id !== window.checkout.storeId + if (cartData().website_id !== window.checkout.websiteId + || cartData().store_id !== window.checkout.storeId ) { customerData.reload(['cart'], false); } diff --git a/app/code/Magento/Quote/Plugin/UpdateQuoteItemStore.php b/app/code/Magento/Quote/Plugin/UpdateQuoteItemStore.php index 4d3e2dff70f9b..0ac589b11b53b 100644 --- a/app/code/Magento/Quote/Plugin/UpdateQuoteItemStore.php +++ b/app/code/Magento/Quote/Plugin/UpdateQuoteItemStore.php @@ -61,9 +61,7 @@ public function afterSwitch( ): string { $quote = $this->checkoutSession->getQuote(); if ($quote->getIsActive()) { - $quote->setStoreId( - $targetStore->getId() - ); + $quote->setStoreId($targetStore->getId()); $quote->getItemsCollection(false); $this->quoteRepository->save($quote); } diff --git a/app/code/Magento/Quote/Test/Unit/Plugin/UpdateQuoteItemStoreTest.php b/app/code/Magento/Quote/Test/Unit/Plugin/UpdateQuoteItemStoreTest.php new file mode 100644 index 0000000000000..f6146c824aabc --- /dev/null +++ b/app/code/Magento/Quote/Test/Unit/Plugin/UpdateQuoteItemStoreTest.php @@ -0,0 +1,143 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Quote\Test\Unit\Plugin; + +use Magento\Quote\Model\ResourceModel\Quote\Item; +use Magento\Store\Model\StoreSwitcherInterface; +use Magento\Quote\Plugin\UpdateQuoteItemStore; +use Magento\Quote\Model\QuoteRepository; +use Magento\Checkout\Model\Session; +use Magento\Store\Model\Store; +use Magento\Quote\Model\Quote; + +/** + * Unit test for Magento\Quote\Plugin\UpdateQuoteItemStore. + */ +class UpdateQuoteItemStoreTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var UpdateQuoteItemStore + */ + private $model; + + /** + * @var StoreSwitcherInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $subjectMock; + + /** + * @var QuoteRepository|\PHPUnit_Framework_MockObject_MockObject + */ + private $quoteRepositoryMock; + + /** + * @var Quote|\PHPUnit_Framework_MockObject_MockObject + */ + private $quoteMock; + + /** + * @var Session|\PHPUnit_Framework_MockObject_MockObject + */ + private $checkoutSessionMock; + + /** + * @var Store|\PHPUnit_Framework_MockObject_MockObject + */ + private $storeMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->checkoutSessionMock = $this->createPartialMock( + Session::class, + ['getQuote'] + ); + $this->quoteMock = $this->createPartialMock( + Quote::class, + ['getIsActive', 'setStoreId', 'getItemsCollection'] + ); + $this->storeMock = $this->createPartialMock( + Store::class, + ['getId'] + ); + $this->quoteRepositoryMock = $this->createPartialMock( + QuoteRepository::class, + ['save'] + ); + $this->subjectMock = $this->createMock(StoreSwitcherInterface::class); + + $this->checkoutSessionMock->expects($this->once())->method('getQuote')->willReturn($this->quoteMock); + + $this->model = $objectManager->getObject( + UpdateQuoteItemStore::class, + [ + 'quoteRepository' => $this->quoteRepositoryMock, + 'checkoutSession' => $this->checkoutSessionMock, + ] + ); + } + + /** + * Unit test for afterSwitch method with active quote. + * + * @return void + */ + public function testWithActiveQuote() + { + $storeId = 1; + $this->quoteMock->expects($this->once())->method('getIsActive')->willReturn(true); + $this->storeMock->expects($this->once())->method('getId')->willReturn($storeId); + $this->quoteMock->expects($this->once())->method('setStoreId')->with($storeId)->willReturnSelf(); + $quoteItem = $this->createMock(Item::class); + $this->quoteMock->expects($this->once())->method('getItemsCollection')->willReturnSelf($quoteItem); + + $this->model->afterSwitch( + $this->subjectMock, + 'magento2.loc', + $this->storeMock, + $this->storeMock, + 'magento2.loc' + ); + } + + /** + * Unit test for afterSwitch method without active quote. + * + * @dataProvider getIsActive + * @param bool|null $isActive + * @return void + */ + public function testWithoutActiveQuote($isActive) + { + $this->quoteMock->expects($this->once())->method('getIsActive')->willReturn($isActive); + $this->quoteRepositoryMock->expects($this->never())->method('save'); + + $this->model->afterSwitch( + $this->subjectMock, + 'magento2.loc', + $this->storeMock, + $this->storeMock, + 'magento2.loc' + ); + } + + /** + * Data provider for method testWithoutActiveQuote. + * @return array + */ + public function getIsActive() + { + return [ + [false], + [null], + ]; + } +} From 985a34a6b74f9afd7fd6e6eda1f11631a03d3765 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Tue, 26 Feb 2019 13:06:01 +0200 Subject: [PATCH 0806/1295] MAGETWO-76424: Additional blank option in country dropdown --- .../Test/Mftf/Page/StorefrontCustomerAddressNewPage.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerAddressNewPage.xml b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerAddressNewPage.xml index 35134157bd373..8fbc7b10fdd95 100644 --- a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerAddressNewPage.xml +++ b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerAddressNewPage.xml @@ -8,6 +8,6 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="StorefrontCustomerAddressNewPage" url="/customer/address/new" area="storefront" module="Magento_Customer"> - <section name="StorefrontCustomerAddNewAddressSection"/> + <section name="StorefrontCustomerAddressEditFormSection"/> </page> </pages> From 835cdbeb44c27fa67b1d933dceee33ad4261f0a2 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Tue, 26 Feb 2019 14:13:15 +0200 Subject: [PATCH 0807/1295] MAGETWO-95904: Header Minicart, Shopping cart page and Checkout page show incorrect product name --- .../Magento/Checkout/view/frontend/web/js/view/minicart.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js b/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js index da1b1a41e2fa9..5e29fa209a641 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/minicart.js @@ -102,8 +102,8 @@ define([ self.isLoading(true); }); - if (cartData().website_id !== window.checkout.websiteId - || cartData().store_id !== window.checkout.storeId + if (cartData().website_id !== window.checkout.websiteId || + cartData().store_id !== window.checkout.storeId ) { customerData.reload(['cart'], false); } From 3a68a8de9749280fcc48f10daa85238217fb0bf0 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Tue, 26 Feb 2019 15:23:04 +0200 Subject: [PATCH 0808/1295] MAGETWO-94421: Default addresses not selected when checking out from cart --- .../Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml index eabe51776894e..479b631db0b71 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml @@ -296,4 +296,10 @@ <waitForElementVisible selector="{{AdminOrderFormPaymentSection.paymentBlock}}" stepKey="waitForPaymentOptions"/> <conditionalClick selector="{{AdminOrderFormPaymentSection.checkMoneyOption}}" dependentSelector="{{AdminOrderFormPaymentSection.checkMoneyOption}}" visible="true" stepKey="checkCheckMoneyOption"/> </actionGroup> + <!--Submit order--> + <actionGroup name="SubmitOrderActionGroup"> + <click selector="{{AdminOrderFormActionSection.submitOrder}}" stepKey="clickSubmitOrder"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You created the order." stepKey="seeOrderSuccessMessage"/> + </actionGroup> </actionGroups> From 806fb3736273279d042ba3f88878eb07e54c9b16 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Tue, 26 Feb 2019 16:56:51 +0200 Subject: [PATCH 0809/1295] MAGETWO-97975: Shipping tax rounding issue --- .../Magento/SalesRule/Model/Validator.php | 22 +-- .../Test/Unit/Model/ValidatorTest.php | 148 ++++++++++-------- .../ui_component/sales_rule_form.xml | 2 +- .../web/js/form/element/apply_to_shipping.js | 37 +++++ .../TestCase/CreateSalesRuleEntityTest.xml | 1 - 5 files changed, 131 insertions(+), 79 deletions(-) create mode 100644 app/code/Magento/SalesRule/view/adminhtml/web/js/form/element/apply_to_shipping.js diff --git a/app/code/Magento/SalesRule/Model/Validator.php b/app/code/Magento/SalesRule/Model/Validator.php index 201df99aa5187..64e170580a449 100644 --- a/app/code/Magento/SalesRule/Model/Validator.php +++ b/app/code/Magento/SalesRule/Model/Validator.php @@ -184,6 +184,8 @@ protected function _getRules(Address $address = null) } /** + * Address id getter. + * * @param Address $address * @return string */ @@ -329,21 +331,7 @@ public function processShippingAmount(Address $address) $baseDiscountAmount = $rule->getDiscountAmount(); break; case \Magento\SalesRule\Model\Rule::CART_FIXED_ACTION: - $cartRules = $address->getCartFixedRules(); - if (!isset($cartRules[$rule->getId()])) { - $cartRules[$rule->getId()] = $rule->getDiscountAmount(); - } - if ($cartRules[$rule->getId()] > 0) { - $quoteAmount = $this->priceCurrency->convert($cartRules[$rule->getId()], $quote->getStore()); - $discountAmount = min($shippingAmount - $address->getShippingDiscountAmount(), $quoteAmount); - $baseDiscountAmount = min( - $baseShippingAmount - $address->getBaseShippingDiscountAmount(), - $cartRules[$rule->getId()] - ); - $cartRules[$rule->getId()] -= $baseDiscountAmount; - } - - $address->setCartFixedRules($cartRules); + // Shouldn't be proceed according to MAGETWO-96403 break; } @@ -521,6 +509,8 @@ public function sortItemsByPriority($items, Address $address = null) } /** + * Rule total items getter. + * * @param int $key * @return array * @throws \Magento\Framework\Exception\LocalizedException @@ -535,6 +525,8 @@ public function getRuleItemTotalsInfo($key) } /** + * Decrease rule items count. + * * @param int $key * @return $this */ diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/ValidatorTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/ValidatorTest.php index 42448565791c5..5a4ce40c12288 100644 --- a/app/code/Magento/SalesRule/Test/Unit/Model/ValidatorTest.php +++ b/app/code/Magento/SalesRule/Test/Unit/Model/ValidatorTest.php @@ -5,6 +5,13 @@ */ namespace Magento\SalesRule\Test\Unit\Model; +use Magento\Framework\Pricing\PriceCurrencyInterface; +use Magento\Quote\Model\Quote; +use Magento\SalesRule\Model\Rule; +use Magento\SalesRule\Model\Validator; +use Magento\Store\Model\Store; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + /** * Class ValidatorTest * @@SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -17,50 +24,55 @@ class ValidatorTest extends \PHPUnit\Framework\TestCase protected $helper; /** - * @var \Magento\SalesRule\Model\Validator + * @var Validator */ protected $model; /** - * @var \Magento\Quote\Model\Quote\Item|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Quote\Model\Quote\Item|MockObject */ protected $item; /** - * @var \Magento\Quote\Model\Quote\Address|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Quote\Model\Quote\Address|MockObject */ protected $addressMock; /** - * @var \Magento\SalesRule\Model\RulesApplier|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\SalesRule\Model\RulesApplier|MockObject */ protected $rulesApplier; /** - * @var \Magento\SalesRule\Model\Validator\Pool|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\SalesRule\Model\Validator\Pool|MockObject */ protected $validators; /** - * @var \Magento\SalesRule\Model\Utility|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\SalesRule\Model\Utility|MockObject */ protected $utility; /** - * @var \Magento\SalesRule\Model\ResourceModel\Rule\Collection|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\SalesRule\Model\ResourceModel\Rule\Collection|MockObject */ protected $ruleCollection; /** - * @var \Magento\Catalog\Helper\Data|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Catalog\Helper\Data|MockObject */ protected $catalogData; /** - * @var \Magento\Framework\Message\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Message\ManagerInterface|MockObject */ protected $messageManager; + /** + * @var PriceCurrencyInterface|MockObject + */ + private $priceCurrency; + protected function setUp() { $this->helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -74,6 +86,7 @@ protected function setUp() ->setMethods( [ 'getShippingAmountForDiscount', + 'getBaseShippingAmountForDiscount', 'getQuote', 'getCustomAttributesCodes', 'setCartFixedRules' @@ -81,7 +94,7 @@ protected function setUp() ) ->getMock(); - /** @var \Magento\Quote\Model\Quote\Item\AbstractItem|\PHPUnit_Framework_MockObject_MockObject $item */ + /** @var \Magento\Quote\Model\Quote\Item\AbstractItem|MockObject $item */ $this->item = $this->createPartialMock( \Magento\Quote\Model\Quote\Item::class, ['__wakeup', 'getAddress', 'getParentItemId'] @@ -100,10 +113,13 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); $ruleCollectionFactoryMock = $this->prepareRuleCollectionMock($this->ruleCollection); + $this->priceCurrency = $this->getMockBuilder(PriceCurrencyInterface::class) + ->disableOriginalConstructor() + ->getMock(); - /** @var \Magento\SalesRule\Model\Validator|\PHPUnit_Framework_MockObject_MockObject $validator */ + /** @var Validator|MockObject $validator */ $this->model = $this->helper->getObject( - \Magento\SalesRule\Model\Validator::class, + Validator::class, [ 'context' => $context, 'registry' => $registry, @@ -112,7 +128,8 @@ protected function setUp() 'utility' => $this->utility, 'rulesApplier' => $this->rulesApplier, 'validators' => $this->validators, - 'messageManager' => $this->messageManager + 'messageManager' => $this->messageManager, + 'priceCurrency' => $this->priceCurrency ] ); $this->model->setWebsiteId(1); @@ -131,7 +148,7 @@ protected function setUp() } /** - * @return \Magento\Quote\Model\Quote\Item|\PHPUnit_Framework_MockObject_MockObject + * @return \Magento\Quote\Model\Quote\Item|MockObject */ protected function getQuoteItemMock() { @@ -145,8 +162,8 @@ protected function getQuoteItemMock() $itemSimple = $this->createPartialMock(\Magento\Quote\Model\Quote\Item::class, ['getAddress', '__wakeup']); $itemSimple->expects($this->any())->method('getAddress')->will($this->returnValue($this->addressMock)); - /** @var $quote \Magento\Quote\Model\Quote */ - $quote = $this->createPartialMock(\Magento\Quote\Model\Quote::class, ['getStoreId', '__wakeup']); + /** @var $quote Quote */ + $quote = $this->createPartialMock(Quote::class, ['getStoreId', '__wakeup']); $quote->expects($this->any())->method('getStoreId')->will($this->returnValue(1)); $itemData = include $fixturePath . 'quote_item_downloadable.php'; @@ -168,7 +185,7 @@ public function testCanApplyRules() $this->model->getCouponCode() ); $item = $this->getQuoteItemMock(); - $rule = $this->createMock(\Magento\SalesRule\Model\Rule::class); + $rule = $this->createMock(Rule::class); $actionsCollection = $this->createPartialMock(\Magento\Rule\Model\Action\Collection::class, ['validate']); $actionsCollection->expects($this->any()) ->method('validate') @@ -278,7 +295,7 @@ public function testApplyRulesThatAppliedRuleIdsAreCollected() public function testInit() { $this->assertInstanceOf( - \Magento\SalesRule\Model\Validator::class, + Validator::class, $this->model->init( $this->model->getWebsiteId(), $this->model->getCustomerGroupId(), @@ -314,7 +331,7 @@ public function testCanApplyDiscount() public function testInitTotalsCanApplyDiscount() { $rule = $this->createPartialMock( - \Magento\SalesRule\Model\Rule::class, + Rule::class, ['getSimpleAction', 'getActions', 'getId'] ); $item1 = $this->getMockForAbstractClass( @@ -337,7 +354,7 @@ public function testInitTotalsCanApplyDiscount() $rule->expects($this->any()) ->method('getSimpleAction') - ->willReturn(\Magento\SalesRule\Model\Rule::CART_FIXED_ACTION); + ->willReturn(Rule::CART_FIXED_ACTION); $iterator = new \ArrayIterator([$rule]); $this->ruleCollection->expects($this->once())->method('getIterator')->willReturn($iterator); $validator = $this->getMockBuilder(\Magento\Framework\Validator\AbstractValidator::class) @@ -392,7 +409,7 @@ public function testInitTotalsNoItems() /** * @param $ruleCollection - * @return \PHPUnit_Framework_MockObject_MockObject + * @return MockObject */ protected function prepareRuleCollectionMock($ruleCollection) { @@ -427,14 +444,14 @@ public function testProcessShippingAmountNoRules() $this->model->getCouponCode() ); $this->assertInstanceOf( - \Magento\SalesRule\Model\Validator::class, + Validator::class, $this->model->processShippingAmount($this->setupAddressMock()) ); } public function testProcessShippingAmountProcessDisabled() { - $ruleMock = $this->getMockBuilder(\Magento\SalesRule\Model\Rule::class) + $ruleMock = $this->getMockBuilder(Rule::class) ->disableOriginalConstructor() ->setMethods([]) ->getMock(); @@ -448,51 +465,54 @@ public function testProcessShippingAmountProcessDisabled() $this->model->getCouponCode() ); $this->assertInstanceOf( - \Magento\SalesRule\Model\Validator::class, + Validator::class, $this->model->processShippingAmount($this->setupAddressMock()) ); } /** + * Tests shipping amounts according to rule simple action. + * * @param string $action + * @param int $ruleDiscount + * @param int $shippingDiscount * @dataProvider dataProviderActions */ - public function testProcessShippingAmountActions($action) + public function testProcessShippingAmountActions($action, $ruleDiscount, $shippingDiscount): void { - $discountAmount = 50; + $shippingAmount = 5; - $ruleMock = $this->getMockBuilder(\Magento\SalesRule\Model\Rule::class) + $ruleMock = $this->getMockBuilder(Rule::class) ->disableOriginalConstructor() ->setMethods(['getApplyToShipping', 'getSimpleAction', 'getDiscountAmount']) ->getMock(); - $ruleMock->expects($this->any()) - ->method('getApplyToShipping') + $ruleMock->method('getApplyToShipping') ->willReturn(true); - $ruleMock->expects($this->any()) - ->method('getDiscountAmount') - ->willReturn($discountAmount); - $ruleMock->expects($this->any()) - ->method('getSimpleAction') + $ruleMock->method('getDiscountAmount') + ->willReturn($ruleDiscount); + $ruleMock->method('getSimpleAction') ->willReturn($action); $iterator = new \ArrayIterator([$ruleMock]); - $this->ruleCollection->expects($this->any()) - ->method('getIterator') + $this->ruleCollection->method('getIterator') ->willReturn($iterator); - $this->utility->expects($this->any()) - ->method('canProcessRule') + $this->utility->method('canProcessRule') ->willReturn(true); + $this->priceCurrency->method('convert') + ->willReturn($ruleDiscount); + $this->model->init( $this->model->getWebsiteId(), $this->model->getCustomerGroupId(), $this->model->getCouponCode() ); - $this->assertInstanceOf( - \Magento\SalesRule\Model\Validator::class, - $this->model->processShippingAmount($this->setupAddressMock(5)) - ); + + $addressMock = $this->setupAddressMock($shippingAmount); + + self::assertInstanceOf(Validator::class, $this->model->processShippingAmount($addressMock)); + self::assertEquals($shippingDiscount, $addressMock->getShippingDiscountAmount()); } /** @@ -501,44 +521,48 @@ public function testProcessShippingAmountActions($action) public static function dataProviderActions() { return [ - [\Magento\SalesRule\Model\Rule::TO_PERCENT_ACTION], - [\Magento\SalesRule\Model\Rule::BY_PERCENT_ACTION], - [\Magento\SalesRule\Model\Rule::TO_FIXED_ACTION], - [\Magento\SalesRule\Model\Rule::BY_FIXED_ACTION], - [\Magento\SalesRule\Model\Rule::CART_FIXED_ACTION], + [Rule::TO_PERCENT_ACTION, 50, 2.5], + [Rule::BY_PERCENT_ACTION, 50, 2.5], + [Rule::TO_FIXED_ACTION, 5, 0], + [Rule::BY_FIXED_ACTION, 5, 5], + [Rule::CART_FIXED_ACTION, 5, 0], ]; } /** * @param null|int $shippingAmount - * @return \PHPUnit_Framework_MockObject_MockObject + * @return MockObject */ protected function setupAddressMock($shippingAmount = null) { - $storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) + $storeMock = $this->getMockBuilder(Store::class) ->disableOriginalConstructor() ->setMethods([]) ->getMock(); - $quoteMock = $this->getMockBuilder(\Magento\Quote\Model\Quote::class) + + $quoteMock = $this->getMockBuilder(Quote::class) ->disableOriginalConstructor() ->setMethods(['setAppliedRuleIds', 'getStore']) ->getMock(); - $quoteMock->expects($this->any()) - ->method('getStore') + + $quoteMock->method('getStore') ->willReturn($storeMock); - $quoteMock->expects($this->any()) - ->method('setAppliedRuleIds') + + $quoteMock->method('setAppliedRuleIds') ->willReturnSelf(); - $this->addressMock->expects($this->any()) - ->method('getShippingAmountForDiscount') + $this->addressMock->method('getShippingAmountForDiscount') ->willReturn($shippingAmount); - $this->addressMock->expects($this->any()) - ->method('getQuote') + + $this->addressMock->method('getBaseShippingAmountForDiscount') + ->willReturn($shippingAmount); + + $this->addressMock->method('getQuote') ->willReturn($quoteMock); - $this->addressMock->expects($this->any()) - ->method('getCustomAttributesCodes') + + $this->addressMock->method('getCustomAttributesCodes') ->willReturn([]); + return $this->addressMock; } @@ -546,7 +570,7 @@ public function testReset() { $this->utility->expects($this->once()) ->method('resetRoundingDeltas'); - $quoteMock = $this->getMockBuilder(\Magento\Quote\Model\Quote::class) + $quoteMock = $this->getMockBuilder(Quote::class) ->disableOriginalConstructor() ->getMock(); $addressMock = $this->getMockBuilder(\Magento\Quote\Model\Quote\Address::class) @@ -560,6 +584,6 @@ public function testReset() $this->model->getCustomerGroupId(), $this->model->getCouponCode() ); - $this->assertInstanceOf(\Magento\SalesRule\Model\Validator::class, $this->model->reset($addressMock)); + $this->assertInstanceOf(Validator::class, $this->model->reset($addressMock)); } } diff --git a/app/code/Magento/SalesRule/view/adminhtml/ui_component/sales_rule_form.xml b/app/code/Magento/SalesRule/view/adminhtml/ui_component/sales_rule_form.xml index 9b579f47759a6..570eb0bf151f0 100644 --- a/app/code/Magento/SalesRule/view/adminhtml/ui_component/sales_rule_form.xml +++ b/app/code/Magento/SalesRule/view/adminhtml/ui_component/sales_rule_form.xml @@ -452,7 +452,7 @@ <dataScope>discount_step</dataScope> </settings> </field> - <field name="apply_to_shipping" component="Magento_Ui/js/form/element/single-checkbox-toggle-notice" formElement="checkbox"> + <field name="apply_to_shipping" component="Magento_SalesRule/js/form/element/apply_to_shipping" formElement="checkbox"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="source" xsi:type="string">sales_rule</item> diff --git a/app/code/Magento/SalesRule/view/adminhtml/web/js/form/element/apply_to_shipping.js b/app/code/Magento/SalesRule/view/adminhtml/web/js/form/element/apply_to_shipping.js new file mode 100644 index 0000000000000..dfb3f909345b3 --- /dev/null +++ b/app/code/Magento/SalesRule/view/adminhtml/web/js/form/element/apply_to_shipping.js @@ -0,0 +1,37 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'Magento_Ui/js/form/element/single-checkbox-toggle-notice' +], function (Checkbox) { + 'use strict'; + + return Checkbox.extend({ + defaults: { + imports: { + toggleDisabled: '${ $.parentName }.simple_action:value' + } + }, + + /** + * Toggle element disabled state according to simple action value. + * + * @param {String} action + */ + toggleDisabled: function (action) { + switch (action) { + case 'cart_fixed': + this.disabled(true); + break; + default: + this.disabled(false); + } + + if (this.disabled()) { + this.checked(false); + } + } + }); +}); diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml index 586ad2acee203..4995c1feb048e 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml @@ -75,7 +75,6 @@ <data name="salesRule/data/coupon_code" xsi:type="string">Lorem ipsum dolor sit amet, consectetur adipiscing elit - %isolation%</data> <data name="salesRule/data/simple_action" xsi:type="string">Fixed amount discount for whole cart</data> <data name="salesRule/data/discount_amount" xsi:type="string">60</data> - <data name="salesRule/data/apply_to_shipping" xsi:type="string">No</data> <data name="salesRule/data/simple_free_shipping" xsi:type="string">No</data> <data name="salesRule/data/store_labels/0" xsi:type="string">Coupon code+Fixed amount discount for whole cart</data> <data name="productForSalesRule1/dataset" xsi:type="string">simple_for_salesrule_1</data> From dd8202e943c112b716b80c99a2596b16545b9a00 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Tue, 26 Feb 2019 17:00:34 +0200 Subject: [PATCH 0810/1295] MAGETWO-95904: Header Minicart, Shopping cart page and Checkout page show incorrect product name --- ...ProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml index 4622a6bd1ebf6..9fee0302345d5 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml @@ -57,7 +57,7 @@ <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> <!--Add product to cart--> - <amOnPage url="{{StorefrontProductPage.url($$createProduct.sku$$)}}" stepKey="amOnProductPage"/> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="amOnProductPage"/> <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> <argument name="productName" value="$createProduct.name$$"/> </actionGroup> From 97b8f5f60c4e970988ddbf3737740cdfae3054f8 Mon Sep 17 00:00:00 2001 From: Nazarn96 <nazarn96@gmail.com> Date: Wed, 27 Feb 2019 10:58:14 +0200 Subject: [PATCH 0811/1295] Removing unused legacy price code --- lib/web/mage/adminhtml/tools.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/web/mage/adminhtml/tools.js b/lib/web/mage/adminhtml/tools.js index f23a0193cb6b2..ed4bab7102ae5 100644 --- a/lib/web/mage/adminhtml/tools.js +++ b/lib/web/mage/adminhtml/tools.js @@ -89,12 +89,6 @@ function checkByProductPriceType(elem) { } -Event.observe(window, 'load', function () { - if ($('price_default') && $('price_default').checked) { - $('price').disabled = 'disabled'; - } -}); - function toggleSeveralValueElements(checkbox, containers, excludedElements, checked) { 'use strict'; From ca2d3bd42541d2b61616f81a69f004a1a07aa483 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Wed, 27 Feb 2019 13:00:17 +0200 Subject: [PATCH 0812/1295] MAGETWO-92495: [Magento Cloud] - Wrong color image when filtering by color filter --- .../Product/Renderer/Listing/Configurable.php | 36 +++++-- app/code/Magento/Swatches/Helper/Data.php | 13 +-- .../Renderer/Listing/ConfigurableTest.php | 100 +++++++++++++++++- .../Swatches/Test/Unit/Helper/DataTest.php | 2 + 4 files changed, 134 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/Swatches/Block/Product/Renderer/Listing/Configurable.php b/app/code/Magento/Swatches/Block/Product/Renderer/Listing/Configurable.php index 85633475205ed..3df47ee749587 100644 --- a/app/code/Magento/Swatches/Block/Product/Renderer/Listing/Configurable.php +++ b/app/code/Magento/Swatches/Block/Product/Renderer/Listing/Configurable.php @@ -8,6 +8,8 @@ use Magento\Catalog\Block\Product\Context; use Magento\Catalog\Helper\Product as CatalogProduct; use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Layer\Resolver; +use Magento\Catalog\Model\Layer\Category as CategoryLayer; use Magento\ConfigurableProduct\Helper\Data; use Magento\ConfigurableProduct\Model\ConfigurableAttributeData; use Magento\Customer\Helper\Session\CurrentCustomer; @@ -39,6 +41,12 @@ class Configurable extends \Magento\Swatches\Block\Product\Renderer\Configurable private $variationPrices; /** + * @var \Magento\Catalog\Model\Layer\Resolver + */ + private $layerResolver; + + /** + * @SuppressWarnings(PHPMD.ExcessiveParameterList) * @param Context $context * @param ArrayUtils $arrayUtils * @param EncoderInterface $jsonEncoder @@ -49,11 +57,11 @@ class Configurable extends \Magento\Swatches\Block\Product\Renderer\Configurable * @param ConfigurableAttributeData $configurableAttributeData * @param SwatchData $swatchHelper * @param Media $swatchMediaHelper - * @param array $data other data - * @param SwatchAttributesProvider $swatchAttributesProvider + * @param array $data + * @param SwatchAttributesProvider|null $swatchAttributesProvider * @param \Magento\Framework\Locale\Format|null $localeFormat * @param \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Variations\Prices|null $variationPrices - * @SuppressWarnings(PHPMD.ExcessiveParameterList) + * @param Resolver $layerResolver */ public function __construct( Context $context, @@ -69,7 +77,8 @@ public function __construct( array $data = [], SwatchAttributesProvider $swatchAttributesProvider = null, \Magento\Framework\Locale\Format $localeFormat = null, - \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Variations\Prices $variationPrices = null + \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Variations\Prices $variationPrices = null, + Resolver $layerResolver = null ) { parent::__construct( $context, @@ -91,10 +100,11 @@ public function __construct( $this->variationPrices = $variationPrices ?: ObjectManager::getInstance()->get( \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Variations\Prices::class ); + $this->layerResolver = $layerResolver ?: ObjectManager::getInstance()->get(Resolver::class); } /** - * @return string + * @inheritdoc */ protected function getRendererTemplate() { @@ -120,7 +130,7 @@ protected function _toHtml() } /** - * @return array + * @inheritdoc */ protected function getSwatchAttributesData() { @@ -182,6 +192,7 @@ protected function getOptionImages() * Add images to result json config in case of Layered Navigation is used * * @return array + * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) * @since 100.2.0 */ protected function _getAdditionalConfig() @@ -246,4 +257,17 @@ private function getLayeredAttributesIfExists(Product $configurableProduct, arra return $layeredAttributes; } + + /** + * @inheritdoc + */ + public function getCacheKeyInfo() + { + $cacheKeyInfo = parent::getCacheKeyInfo(); + /** @var CategoryLayer $catalogLayer */ + $catalogLayer = $this->layerResolver->get(); + $cacheKeyInfo[] = $catalogLayer->getStateKey(); + + return $cacheKeyInfo; + } } diff --git a/app/code/Magento/Swatches/Helper/Data.php b/app/code/Magento/Swatches/Helper/Data.php index ae35f5203dd73..2c00224083bfc 100644 --- a/app/code/Magento/Swatches/Helper/Data.php +++ b/app/code/Magento/Swatches/Helper/Data.php @@ -249,18 +249,15 @@ public function loadVariationByFallback(Product $parentProduct, array $attribute $this->addFilterByParent($productCollection, $parentId); $configurableAttributes = $this->getAttributesFromConfigurable($parentProduct); - $allAttributesArray = []; + + $resultAttributesToFilter = []; foreach ($configurableAttributes as $attribute) { - if (!empty($attribute['default_value'])) { - $allAttributesArray[$attribute['attribute_code']] = $attribute['default_value']; + $attributeCode = $attribute->getData('attribute_code'); + if (array_key_exists($attributeCode, $attributes)) { + $resultAttributesToFilter[$attributeCode] = $attributes[$attributeCode]; } } - $resultAttributesToFilter = array_merge( - $attributes, - array_diff_key($allAttributesArray, $attributes) - ); - $this->addFilterByAttributes($productCollection, $resultAttributesToFilter); $variationProduct = $productCollection->getFirstItem(); diff --git a/app/code/Magento/Swatches/Test/Unit/Block/Product/Renderer/Listing/ConfigurableTest.php b/app/code/Magento/Swatches/Test/Unit/Block/Product/Renderer/Listing/ConfigurableTest.php index 6b72422d05014..adcdcad25e337 100644 --- a/app/code/Magento/Swatches/Test/Unit/Block/Product/Renderer/Listing/ConfigurableTest.php +++ b/app/code/Magento/Swatches/Test/Unit/Block/Product/Renderer/Listing/ConfigurableTest.php @@ -61,6 +61,18 @@ class ConfigurableTest extends \PHPUnit\Framework\TestCase /** @var \PHPUnit_Framework_MockObject_MockObject */ private $variationPricesMock; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + private $layerResolverMock; + + /** @var ObjectManagerHelper */ + private $objectManagerHelper; + + /** @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $storeManagerMock; + + /** @var \Magento\Customer\Model\Session|\PHPUnit_Framework_MockObject_MockObject */ + private $customerSessionMock; + public function setUp() { $this->arrayUtils = $this->createMock(\Magento\Framework\Stdlib\ArrayUtils::class); @@ -82,9 +94,35 @@ public function setUp() $this->variationPricesMock = $this->createMock( \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Variations\Prices::class ); + $this->layerResolverMock = $this->createMock(\Magento\Catalog\Model\Layer\Resolver::class); + + $this->storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) + ->setMethods(['getStore']) + ->getMockForAbstractClass(); + $this->customerSessionMock = $this->getMockBuilder(\Magento\Customer\Model\Session::class) + ->disableOriginalConstructor() + ->setMethods(['getCustomerGroupId']) + ->getMock(); + + $this->objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->prepareObjectManager([ + [ + \Magento\Framework\Locale\Format::class, + $this->createMock(\Magento\Framework\Locale\Format::class), + ], + [ + \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Variations\Prices::class, + $this->createMock( + \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Variations\Prices::class + ), + ], + [ + \Magento\Customer\Model\Session::class, + $this->customerSessionMock, + ], + ]); - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->configurable = $objectManagerHelper->getObject( + $this->configurable = $this->objectManagerHelper->getObject( \Magento\Swatches\Block\Product\Renderer\Listing\Configurable::class, [ 'scopeConfig' => $this->scopeConfig, @@ -100,9 +138,15 @@ public function setUp() 'priceCurrency' => $this->priceCurrency, 'configurableAttributeData' => $this->configurableAttributeData, 'data' => [], - 'variationPrices' => $this->variationPricesMock + 'variationPrices' => $this->variationPricesMock, + 'layerResolver' => $this->layerResolverMock, ] ); + $this->objectManagerHelper->setBackwardCompatibleProperty( + $this->configurable, + '_storeManager', + $this->storeManagerMock + ); } /** @@ -235,4 +279,54 @@ public function testGetPricesJson() $this->jsonEncoder->expects($this->once())->method('encode')->with($expectedPrices); $this->configurable->getPricesJson(); } + + /** + * @param $map + */ + private function prepareObjectManager($map) + { + $objectManagerMock = $this->getMockBuilder(\Magento\Framework\ObjectManagerInterface::class) + ->setMethods(['getInstance']) + ->getMockForAbstractClass(); + $objectManagerMock->expects($this->any())->method('getInstance')->willReturnSelf(); + $objectManagerMock->expects($this->any()) + ->method('get') + ->will($this->returnValueMap($map)); + $reflectionClass = new \ReflectionClass(\Magento\Framework\App\ObjectManager::class); + $reflectionProperty = $reflectionClass->getProperty('_instance'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($objectManagerMock); + } + + /** + * Test receiving cache key info. + */ + public function testGetCacheKeyInfo() + { + $expected = [ + 0 => 'BLOCK_TPL', + 1 => 'default', + 2 => NULL, + 'base_url' => NULL, + 'template' => NULL, + 3 => 'USD', + 4 => NULL, + 5 => 'STORE_1_CAT_25_CUSTGROUP_0_color_53', + ]; + $stateKey = 'STORE_1_CAT_25_CUSTGROUP_0_color_53'; + + $storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class)->getMockForAbstractClass(); + $storeMock->expects($this->any())->method('getCode')->willReturn('default'); + $this->storeManagerMock->expects($this->any())->method('getStore')->willReturn($storeMock); + $currency = $this->getMockBuilder(\Magento\Directory\Model\Currency::class) + ->disableOriginalConstructor() + ->getMock(); + $this->priceCurrency->expects($this->once())->method('getCurrency')->willReturn($currency); + $currency->expects($this->once())->method('getCode')->willReturn('USD'); + $layer = $this->createMock(\Magento\Catalog\Model\Layer::class); + $this->layerResolverMock->expects($this->once())->method('get')->willReturn($layer); + $layer->expects($this->once())->method('getStateKey')->willReturn($stateKey); + + $this->assertEquals($this->configurable->getCacheKeyInfo(), $expected); + } } diff --git a/app/code/Magento/Swatches/Test/Unit/Helper/DataTest.php b/app/code/Magento/Swatches/Test/Unit/Helper/DataTest.php index 5732018f7615f..58f9ea821f0b4 100644 --- a/app/code/Magento/Swatches/Test/Unit/Helper/DataTest.php +++ b/app/code/Magento/Swatches/Test/Unit/Helper/DataTest.php @@ -278,6 +278,8 @@ public function testLoadVariationByFallback($product) $metadataMock = $this->createMock(\Magento\Framework\EntityManager\EntityMetadataInterface::class); $this->metaDataPoolMock->expects($this->once())->method('getMetadata')->willReturn($metadataMock); $metadataMock->expects($this->once())->method('getLinkField')->willReturn('id'); + $this->productMock->expects($this->once())->method('getTypeInstance')->willReturn($this->configurableMock); + $this->attributeMock->method('getData')->with('attribute_code')->willReturn('color'); $this->getSwatchAttributes($product); From e5145576095fe77d839e138f68fbf9e4f49c21fc Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Wed, 27 Feb 2019 14:28:00 +0200 Subject: [PATCH 0813/1295] MAGETWO-92495: [Magento Cloud] - Wrong color image when filtering by color filter --- .../Block/Product/Renderer/Listing/ConfigurableTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Swatches/Test/Unit/Block/Product/Renderer/Listing/ConfigurableTest.php b/app/code/Magento/Swatches/Test/Unit/Block/Product/Renderer/Listing/ConfigurableTest.php index adcdcad25e337..fce0f6609ad73 100644 --- a/app/code/Magento/Swatches/Test/Unit/Block/Product/Renderer/Listing/ConfigurableTest.php +++ b/app/code/Magento/Swatches/Test/Unit/Block/Product/Renderer/Listing/ConfigurableTest.php @@ -306,11 +306,11 @@ public function testGetCacheKeyInfo() $expected = [ 0 => 'BLOCK_TPL', 1 => 'default', - 2 => NULL, - 'base_url' => NULL, - 'template' => NULL, + 2 => null, + 'base_url' => null, + 'template' => null, 3 => 'USD', - 4 => NULL, + 4 => null, 5 => 'STORE_1_CAT_25_CUSTGROUP_0_color_53', ]; $stateKey = 'STORE_1_CAT_25_CUSTGROUP_0_color_53'; From abcf202ef6bc9c3d9b3276215126e7404b5326e7 Mon Sep 17 00:00:00 2001 From: Nazarn96 <nazarn96@gmail.com> Date: Wed, 27 Feb 2019 15:40:40 +0200 Subject: [PATCH 0814/1295] fix wrong check CustomerMiddleName --- app/code/Magento/Quote/Model/QuoteManagement.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Quote/Model/QuoteManagement.php b/app/code/Magento/Quote/Model/QuoteManagement.php index bf1caa2434638..e21ae7fa1af37 100644 --- a/app/code/Magento/Quote/Model/QuoteManagement.php +++ b/app/code/Magento/Quote/Model/QuoteManagement.php @@ -359,7 +359,7 @@ public function placeOrder($cartId, PaymentInterface $paymentMethod = null) if ($quote->getCustomerFirstname() === null && $quote->getCustomerLastname() === null) { $quote->setCustomerFirstname($quote->getBillingAddress()->getFirstname()); $quote->setCustomerLastname($quote->getBillingAddress()->getLastname()); - if ($quote->getBillingAddress()->getMiddlename() === null) { + if ($quote->getCustomerMiddlename() === null) { $quote->setCustomerMiddlename($quote->getBillingAddress()->getMiddlename()); } } From 7d9d9e116c3b4dff0e737ed304ff74b6803d704b Mon Sep 17 00:00:00 2001 From: serhii balko <serhii.balko@transoftgroup.com> Date: Wed, 27 Feb 2019 15:57:41 +0200 Subject: [PATCH 0815/1295] MAGETWO-98365: [Magento Cloud] Recalculation of Tier prices after customer logs in --- .../Model/Product/Type/Configurable/Price.php | 19 +-- .../Product/Type/Configurable/PriceTest.php | 109 +++++++++++++++--- 2 files changed, 107 insertions(+), 21 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Price.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Price.php index bee334596e990..f2bf3116af9e4 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Price.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Price.php @@ -7,14 +7,15 @@ */ namespace Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Catalog\Model\Product; + +/** + * Class Price for configurable product + */ class Price extends \Magento\Catalog\Model\Product\Type\Price { /** - * Get product final price - * - * @param float $qty - * @param \Magento\Catalog\Model\Product $product - * @return float + * @inheritdoc */ public function getFinalPrice($qty, $product) { @@ -22,7 +23,10 @@ public function getFinalPrice($qty, $product) return $product->getCalculatedFinalPrice(); } if ($product->getCustomOption('simple_product') && $product->getCustomOption('simple_product')->getProduct()) { - $finalPrice = parent::getFinalPrice($qty, $product->getCustomOption('simple_product')->getProduct()); + /** @var Product $simpleProduct */ + $simpleProduct = $product->getCustomOption('simple_product')->getProduct(); + $simpleProduct->setCustomerGroupId($product->getCustomerGroupId()); + $finalPrice = parent::getFinalPrice($qty, $simpleProduct); } else { $priceInfo = $product->getPriceInfo(); $finalPrice = $priceInfo->getPrice('final_price')->getAmount()->getValue(); @@ -35,7 +39,7 @@ public function getFinalPrice($qty, $product) } /** - * {@inheritdoc} + * @inheritdoc */ public function getPrice($product) { @@ -48,6 +52,7 @@ public function getPrice($product) } } } + return 0; } } diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/Configurable/PriceTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/Configurable/PriceTest.php index 64b9b3776442a..0fc650a4113c6 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/Configurable/PriceTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/Configurable/PriceTest.php @@ -6,22 +6,47 @@ namespace Magento\ConfigurableProduct\Test\Unit\Model\Product\Type\Configurable; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Configuration\Item\Option; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable\Price as ConfigurablePrice; +use Magento\Framework\Event\ManagerInterface; +use Magento\Framework\Pricing\Amount\AmountInterface; +use Magento\Framework\Pricing\Price\PriceInterface; +use Magento\Framework\Pricing\PriceInfo\Base as PriceInfoBase; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use PHPUnit_Framework_MockObject_MockObject as MockObject; class PriceTest extends \PHPUnit\Framework\TestCase { - /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Price */ + /** + * @var ObjectManagerHelper + */ + protected $objectManagerHelper; + + /** + * @var ConfigurablePrice + */ protected $model; - /** @var ObjectManagerHelper */ - protected $objectManagerHelper; + /** + * @var ManagerInterface|MockObject + */ + private $eventManagerMock; + /** + * @inheritdoc + */ protected function setUp() { $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->eventManagerMock = $this->createPartialMock( + ManagerInterface::class, + ['dispatch'] + ); $this->model = $this->objectManagerHelper->getObject( - \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Price::class + ConfigurablePrice::class, + ['eventManager' => $this->eventManagerMock] ); } @@ -29,29 +54,29 @@ public function testGetFinalPrice() { $finalPrice = 10; $qty = 1; - $configurableProduct = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) - ->disableOriginalConstructor() - ->setMethods(['getCustomOption', 'getPriceInfo', 'setFinalPrice', '__wakeUp']) - ->getMock(); - $customOption = $this->getMockBuilder(\Magento\Catalog\Model\Product\Configuration\Item\Option::class) + + /** @var Product|MockObject $configurableProduct */ + $configurableProduct = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() - ->setMethods(['getProduct']) + ->setMethods(['getCustomOption', 'getPriceInfo', 'setFinalPrice']) ->getMock(); - $priceInfo = $this->getMockBuilder(\Magento\Framework\Pricing\PriceInfo\Base::class) + /** @var PriceInfoBase|MockObject $priceInfo */ + $priceInfo = $this->getMockBuilder(PriceInfoBase::class) ->disableOriginalConstructor() ->setMethods(['getPrice']) ->getMock(); - $price = $this->getMockBuilder(\Magento\Framework\Pricing\Price\PriceInterface::class) + /** @var PriceInterface|MockObject $price */ + $price = $this->getMockBuilder(PriceInterface::class) ->disableOriginalConstructor() ->getMock(); - $amount = $this->getMockBuilder(\Magento\Framework\Pricing\Amount\AmountInterface::class) + /** @var AmountInterface|MockObject $amount */ + $amount = $this->getMockBuilder(AmountInterface::class) ->disableOriginalConstructor() ->getMock(); $configurableProduct->expects($this->any()) ->method('getCustomOption') ->willReturnMap([['simple_product', false], ['option_ids', false]]); - $customOption->expects($this->never())->method('getProduct'); $configurableProduct->expects($this->once())->method('getPriceInfo')->willReturn($priceInfo); $priceInfo->expects($this->once())->method('getPrice')->with('final_price')->willReturn($price); $price->expects($this->once())->method('getAmount')->willReturn($amount); @@ -60,4 +85,60 @@ public function testGetFinalPrice() $this->assertEquals($finalPrice, $this->model->getFinalPrice($qty, $configurableProduct)); } + + public function testGetFinalPriceWithSimpleProduct() + { + $finalPrice = 10; + $qty = 1; + $customerGroupId = 1; + + /** @var Product|MockObject $configurableProduct */ + $configurableProduct = $this->createPartialMock( + Product::class, + ['getCustomOption', 'setFinalPrice', 'getCustomerGroupId'] + ); + /** @var Option|MockObject $customOption */ + $customOption = $this->createPartialMock( + Option::class, + ['getProduct'] + ); + /** @var Product|MockObject $simpleProduct */ + $simpleProduct = $this->createPartialMock( + Product::class, + ['setCustomerGroupId', 'setFinalPrice', 'getPrice', 'getTierPrice', 'getData', 'getCustomOption'] + ); + + $configurableProduct->method('getCustomOption') + ->willReturnMap([ + ['simple_product', $customOption], + ['option_ids', false] + ]); + $configurableProduct->method('getCustomerGroupId')->willReturn($customerGroupId); + $configurableProduct->expects($this->atLeastOnce()) + ->method('setFinalPrice') + ->with($finalPrice) + ->willReturnSelf(); + $customOption->method('getProduct')->willReturn($simpleProduct); + $simpleProduct->expects($this->atLeastOnce()) + ->method('setCustomerGroupId') + ->with($customerGroupId) + ->willReturnSelf(); + $simpleProduct->method('getPrice')->willReturn($finalPrice); + $simpleProduct->method('getTierPrice')->with($qty)->willReturn($finalPrice); + $simpleProduct->expects($this->atLeastOnce()) + ->method('setFinalPrice') + ->with($finalPrice) + ->willReturnSelf(); + $simpleProduct->method('getData')->with('final_price')->willReturn($finalPrice); + $simpleProduct->method('getCustomOption')->with('option_ids')->willReturn(false); + $this->eventManagerMock->expects($this->once()) + ->method('dispatch') + ->with('catalog_product_get_final_price', ['product' => $simpleProduct, 'qty' => $qty]); + + $this->assertEquals( + $finalPrice, + $this->model->getFinalPrice($qty, $configurableProduct), + 'The final price calculation is wrong' + ); + } } From 4147a12914f150f4ec35ac150a1f8d470e053584 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Wed, 27 Feb 2019 16:22:14 +0200 Subject: [PATCH 0816/1295] MAGETWO-70467: CMS Block status is not staged --- .../Cms/Test/Mftf/Data/CmsBlockData.xml | 6 ++++++ .../Cms/Test/Mftf/Metadata/cms_block-meta.xml | 2 +- .../AdminCreateWidgetActionGroup.xml | 18 ++++++++++++++++++ .../Widget/Test/Mftf/Data/WidgetsData.xml | 10 ++++++++++ .../Test/Mftf/Page/AdminNewWidgetPage.xml | 1 + .../Mftf/Section/AdminNewWidgetSection.xml | 1 + .../AdminSelectWidgetBlockGridSection.xml | 16 ++++++++++++++++ 7 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Widget/Test/Mftf/Section/AdminSelectWidgetBlockGridSection.xml diff --git a/app/code/Magento/Cms/Test/Mftf/Data/CmsBlockData.xml b/app/code/Magento/Cms/Test/Mftf/Data/CmsBlockData.xml index c8f71253dc6bd..79da00f26ecb9 100644 --- a/app/code/Magento/Cms/Test/Mftf/Data/CmsBlockData.xml +++ b/app/code/Magento/Cms/Test/Mftf/Data/CmsBlockData.xml @@ -14,4 +14,10 @@ <data key="content">Here is a block test. Yeah!</data> <data key="active">true</data> </entity> + <entity name="Sales25offBlock" type="cms_block"> + <data key="title" unique="suffix">Sales25off</data> + <data key="identifier" unique="suffix">Sales25off</data> + <data key="content">sales25off everything!</data> + <data key="active">false</data> + </entity> </entities> diff --git a/app/code/Magento/Cms/Test/Mftf/Metadata/cms_block-meta.xml b/app/code/Magento/Cms/Test/Mftf/Metadata/cms_block-meta.xml index bab2be6a36155..60a33c132a6c1 100644 --- a/app/code/Magento/Cms/Test/Mftf/Metadata/cms_block-meta.xml +++ b/app/code/Magento/Cms/Test/Mftf/Metadata/cms_block-meta.xml @@ -13,7 +13,7 @@ <field key="title">string</field> <field key="identifier">string</field> <field key="content">string</field> - <field key="active">true</field> + <field key="active">string</field> </object> </operation> diff --git a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml index dfb33b197d74f..cc1e64cd3609c 100644 --- a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml +++ b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml @@ -63,4 +63,22 @@ <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDelete"/> <see selector="{{AdminMessagesSection.successMessage}}" userInput="The widget instance has been deleted" stepKey="seeSuccess"/> </actionGroup> + + <actionGroup name="AdminCreateWidgetWithBlockActionGroup" extends="AdminCreateWidgetActionGroup"> + <arguments> + <argument name="blockTitle" type="string"/> + </arguments> + <waitForElement selector="{{AdminNewWidgetSection.widgetSelectBlock}}" time="60" after="clickWidgetOptions" stepKey="waitForSelectBlock"/> + <click selector="{{AdminNewWidgetSection.widgetSelectBlock}}" stepKey="openSelectBlock"/> + <waitForPageLoad stepKey="waitForLoadBlocks"/> + <waitForElementVisible selector="{{AdminSelectWidgetBlockGridSection.sectionTitle}}" stepKey="waitSectionHeaderIsLoaded"/> + <selectOption selector="{{AdminSelectWidgetBlockGridSection.blockStatusFilter}}" userInput="Disabled" stepKey="chooseDisabledStatus"/> + <fillField selector="{{AdminSelectWidgetBlockGridSection.blockTitleFilter}}" userInput="{{blockTitle}}" stepKey="fillBlockTitle"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="searchBlock"/> + <click selector="{{AdminDataGridTableSection.row('1')}}" stepKey="clickSearchedBlock"/> + <waitForPageLoad stepKey="wait"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="saveWidget"/> + <waitForPageLoad stepKey="waitForSaving"/> + <see selector="{{AdminMessagesSection.success}}" userInput="The widget instance has been saved." stepKey="seeSuccessMessage"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Widget/Test/Mftf/Data/WidgetsData.xml b/app/code/Magento/Widget/Test/Mftf/Data/WidgetsData.xml index d7db8fe50cb7f..c883324853b79 100644 --- a/app/code/Magento/Widget/Test/Mftf/Data/WidgetsData.xml +++ b/app/code/Magento/Widget/Test/Mftf/Data/WidgetsData.xml @@ -32,4 +32,14 @@ <data key="display_mode">salesrule</data> <data key="restrict_type">header</data> </entity> + <entity name="WidgetWithBlock" type="widget"> + <data key="type">CMS Static Block</data> + <data key="design_theme">Magento Luma</data> + <data key="name" unique="suffix">testName</data> + <array key="store_ids"> + <item>All Store Views</item> + </array> + <data key="display_on">All Pages</data> + <data key="container">Page Top</data> + </entity> </entities> diff --git a/app/code/Magento/Widget/Test/Mftf/Page/AdminNewWidgetPage.xml b/app/code/Magento/Widget/Test/Mftf/Page/AdminNewWidgetPage.xml index d495a36f68d0a..da77738c7d013 100644 --- a/app/code/Magento/Widget/Test/Mftf/Page/AdminNewWidgetPage.xml +++ b/app/code/Magento/Widget/Test/Mftf/Page/AdminNewWidgetPage.xml @@ -10,5 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminNewWidgetPage" url="admin/widget_instance/new/" area="admin" module="Magento_Widget"> <section name="AdminNewWidgetSection"/> + <section name="AdminSelectWidgetBlockGridSection"/> </page> </pages> diff --git a/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml b/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml index 37ad425114d5c..3aaf1bcaa6a0f 100644 --- a/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml +++ b/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml @@ -33,5 +33,6 @@ <element name="conditionOperatorSelect" type="select" selector="#conditions__1--{{arg1}}__operator" parameterized="true"/> <element name="displayMode" type="select" selector="select[id*='display_mode']"/> <element name="restrictTypes" type="select" selector="select[id*='types']"/> + <element name="widgetSelectBlock" type="button" selector=".action-default.scalable.btn-chooser"/> </section> </sections> diff --git a/app/code/Magento/Widget/Test/Mftf/Section/AdminSelectWidgetBlockGridSection.xml b/app/code/Magento/Widget/Test/Mftf/Section/AdminSelectWidgetBlockGridSection.xml new file mode 100644 index 0000000000000..d46d6edf8026b --- /dev/null +++ b/app/code/Magento/Widget/Test/Mftf/Section/AdminSelectWidgetBlockGridSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminSelectWidgetBlockGridSection"> + <element name="blockTitleFilter" type="input" selector=".modal-content input[name='chooser_title']"/> + <element name="blockStatusFilter" type="select" selector=".modal-content select[name='chooser_is_active']"/> + <element name="sectionTitle" type="button" selector="//*[@class='modal-header']//h1[contains(text(), 'Select Block')]"/> + </section> +</sections> From 6147df8f07634378d5232deee846443b17222137 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Thu, 28 Feb 2019 10:30:34 +0200 Subject: [PATCH 0817/1295] MAGETWO-97975: Shipping tax rounding issue --- app/code/Magento/SalesRule/Test/Unit/Model/ValidatorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/ValidatorTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/ValidatorTest.php index 5a4ce40c12288..a4b45813a10bc 100644 --- a/app/code/Magento/SalesRule/Test/Unit/Model/ValidatorTest.php +++ b/app/code/Magento/SalesRule/Test/Unit/Model/ValidatorTest.php @@ -478,7 +478,7 @@ public function testProcessShippingAmountProcessDisabled() * @param int $shippingDiscount * @dataProvider dataProviderActions */ - public function testProcessShippingAmountActions($action, $ruleDiscount, $shippingDiscount): void + public function testProcessShippingAmountActions($action, $ruleDiscount, $shippingDiscount) { $shippingAmount = 5; From 7b13eb301da64c36fab09680d9d293329690459c Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Thu, 28 Feb 2019 13:15:02 +0200 Subject: [PATCH 0818/1295] MC-15075: Revert Date changes --- .../Magento/Framework/Data/Form/Element/Date.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Date.php b/lib/internal/Magento/Framework/Data/Form/Element/Date.php index e762a641bfdcc..73d50cde5fd09 100644 --- a/lib/internal/Magento/Framework/Data/Form/Element/Date.php +++ b/lib/internal/Magento/Framework/Data/Form/Element/Date.php @@ -82,13 +82,14 @@ public function setValue($value) $this->_value = $value; return $this; } + if (preg_match('/^[0-9]+$/', $value)) { + $this->_value = (new \DateTime())->setTimestamp($this->_toTimestamp($value)); + + return $this; + } + try { - if (preg_match('/^[0-9]+$/', $value)) { - $this->_value = (new \DateTime())->setTimestamp($this->_toTimestamp($value)); - } else { - $this->_value = new \DateTime($value); - $this->_value->setTimezone(new \DateTimeZone($this->localeDate->getConfigTimezone())); - } + $this->_value = new \DateTime($value, new \DateTimeZone($this->localeDate->getConfigTimezone())); } catch (\Exception $e) { $this->_value = ''; } From 77391fca341d90c63a1d04e66bc7c4c434bd6a71 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Thu, 28 Feb 2019 13:37:07 +0200 Subject: [PATCH 0819/1295] MAGETWO-92495: [Magento Cloud] - Wrong color image when filtering by color filter --- .../Unit/Block/Product/Renderer/Listing/ConfigurableTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Swatches/Test/Unit/Block/Product/Renderer/Listing/ConfigurableTest.php b/app/code/Magento/Swatches/Test/Unit/Block/Product/Renderer/Listing/ConfigurableTest.php index fce0f6609ad73..a876a4dcc85c1 100644 --- a/app/code/Magento/Swatches/Test/Unit/Block/Product/Renderer/Listing/ConfigurableTest.php +++ b/app/code/Magento/Swatches/Test/Unit/Block/Product/Renderer/Listing/ConfigurableTest.php @@ -73,6 +73,9 @@ class ConfigurableTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Customer\Model\Session|\PHPUnit_Framework_MockObject_MockObject */ private $customerSessionMock; + /** + * @inheritdoc + */ public function setUp() { $this->arrayUtils = $this->createMock(\Magento\Framework\Stdlib\ArrayUtils::class); From d49a58f3604a39937bc9da4ad9dfdabe08082b7f Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Thu, 28 Feb 2019 13:40:47 +0200 Subject: [PATCH 0820/1295] MAGETWO-97523: Quick Order does not show a correct link for configurable variations --- ...mple_products_not_visible_individually.php | 32 +++++++++++++++++++ ...ucts_not_visible_individually_rollback.php | 27 ++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/simple_products_not_visible_individually.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/simple_products_not_visible_individually_rollback.php diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_products_not_visible_individually.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_products_not_visible_individually.php new file mode 100644 index 0000000000000..dfb8ce4d4741f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_products_not_visible_individually.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +/** @var $product \Magento\Catalog\Model\Product */ +$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); +$product->isObjectNew(true); +$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setId(67) + ->setAttributeSetId(4) + ->setName('Simple Product Not visible 1') + ->setSku('simple_not_visible_1') + ->setTaxClassId('none') + ->setDescription('description') + ->setShortDescription('short description') + ->setOptionsContainer('container1') + ->setMsrpDisplayActualPriceType(\Magento\Msrp\Model\Product\Attribute\Source\Type::TYPE_IN_CART) + ->setPrice(10) + ->setWeight(1) + ->setMetaTitle('meta title product Not visible') + ->setMetaKeyword('meta keyword') + ->setMetaDescription('meta description') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_NOT_VISIBLE) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setWebsiteIds([1]) + ->setCategoryIds([300]) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 30, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setSpecialPrice('5.99') + ->save(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_products_not_visible_individually_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_products_not_visible_individually_rollback.php new file mode 100644 index 0000000000000..bbdcfd7acf081 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_products_not_visible_individually_rollback.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Exception\NoSuchEntityException; + +\Magento\TestFramework\Helper\Bootstrap::getInstance()->getInstance()->reinitialize(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); +try { + $product = $productRepository->get('simple_not_visible_1', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); From e53d1f76f41ef94795b4a1d0219f0f84712f4bb1 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Thu, 28 Feb 2019 16:16:17 +0200 Subject: [PATCH 0821/1295] MAGETWO-73985: Simple product with MAP assigned to configurable should displays the same way as products with special price --- .../Catalog/Test/Mftf/Data/ProductData.xml | 9 ------- ...reateApiConfigurableProductActionGroup.xml | 27 ++++++++++++++----- .../ActionGroup/AdminProductActionGroup.xml | 4 +-- .../StorefrontProductActionGroup.xml | 4 +-- ...AdminProductFormAdvancedPricingSection.xml | 2 +- ...ingConfigurableProductPriceWithMapTest.xml | 10 +++---- 6 files changed, 31 insertions(+), 25 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index fbf54f50797de..c8c24d1f41e19 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -299,13 +299,4 @@ <data key="quantity">100</data> <requiredEntity type="product_extension_attribute">EavStock100</requiredEntity> </entity> - <entity name="ApiSimpleWithPrice50" type="product2" extends="ApiSimpleOne"> - <data key="price">50</data> - </entity> - <entity name="ApiSimpleWithPrice60" type="product2" extends="ApiSimpleOne"> - <data key="price">60</data> - </entity> - <entity name="ApiSimpleWithPrice70" type="product2" extends="ApiSimpleOne"> - <data key="price">70</data> - </entity> </entities> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml index ddf06a4d84bbb..cdb147e99ad58 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml @@ -86,6 +86,10 @@ <!--Create the configurable product with three child products--> <actionGroup name="AdminCreateApiConfigurableProductWithThreeChildActionGroup" extends="AdminCreateApiConfigurableProductActionGroup"> <remove keyForRemoval="createConfigProductOption"/> + <remove keyForRemoval="createConfigChildProduct1"/> + <remove keyForRemoval="createConfigChildProduct2"/> + <remove keyForRemoval="createConfigProductAddChild1"/> + <remove keyForRemoval="createConfigProductAddChild2"/> <createData entity="ProductAttributeOption3" after="createConfigProductAttributeOption2" stepKey="createConfigProductAttributeOption3"> <requiredEntity createDataKey="createConfigProductAttribute"/> @@ -94,20 +98,23 @@ <requiredEntity createDataKey="createConfigProductAttribute"/> </getData> - <createData entity="ApiSimpleWithPrice50" stepKey="createConfigChildProduct1"> + <createData entity="ApiSimpleOne" stepKey="createChildProduct1"> + <field key="price">50</field> <requiredEntity createDataKey="createConfigProductAttribute"/> <requiredEntity createDataKey="getConfigAttributeOption1"/> </createData> - <createData entity="ApiSimpleWithPrice60" stepKey="createConfigChildProduct2"> + <createData entity="ApiSimpleOne" stepKey="createChildProduct2"> + <field key="price">60</field> <requiredEntity createDataKey="createConfigProductAttribute"/> <requiredEntity createDataKey="getConfigAttributeOption2"/> </createData> - <createData entity="ApiSimpleWithPrice70" after="createConfigChildProduct2" stepKey="createConfigChildProduct3"> + <createData entity="ApiSimpleOne" stepKey="createChildProduct3"> + <field key="price">70</field> <requiredEntity createDataKey="createConfigProductAttribute"/> <requiredEntity createDataKey="getConfigAttributeOption3"/> </createData> - <createData entity="ConfigurableProductThreeOptions" after="createConfigChildProduct3" stepKey="createConfigProductOptions"> + <createData entity="ConfigurableProductThreeOptions" stepKey="createConfigProductOptions"> <requiredEntity createDataKey="createConfigProduct"/> <requiredEntity createDataKey="createConfigProductAttribute"/> <requiredEntity createDataKey="getConfigAttributeOption1"/> @@ -115,9 +122,17 @@ <requiredEntity createDataKey="getConfigAttributeOption3"/> </createData> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild3"> + <createData entity="ConfigurableProductAddChild" stepKey="createProductAddChild1"> <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct3"/> + <requiredEntity createDataKey="createChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createChildProduct2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createProductAddChild3"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createChildProduct3"/> </createData> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Msrp/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Msrp/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index d6672619dbdb2..a53a7e34924ed 100644 --- a/app/code/Magento/Msrp/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Msrp/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -9,10 +9,10 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminProductSetMsrp"> <arguments> - <argument name="productId" type="string"/> + <argument name="product"/> <argument name="msrp" type="string" defaultValue="100"/> </arguments> - <amOnPage url="{{AdminProductEditPage.url(productId)}}" stepKey="goToProductEditPage"/> + <amOnPage url="{{AdminProductEditPage.url(product.id)}}" stepKey="goToProductEditPage"/> <waitForPageLoad stepKey="waitForProductEditPageLoad"/> <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickAdvancedPricingLink"/> <waitForElement selector="{{AdminProductFormAdvancedPricingSection.msrp}}" stepKey="waitForMsrpElement"/> diff --git a/app/code/Magento/Msrp/Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml b/app/code/Magento/Msrp/Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml index f3408ef81c575..b3742574c8235 100644 --- a/app/code/Magento/Msrp/Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml +++ b/app/code/Magento/Msrp/Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml @@ -12,7 +12,7 @@ <arguments> <argument name="msrp" type="string"/> </arguments> - <waitForElement selector="{{StorefrontProductInfoMainSection.msrp}}" stepKey="waitForMsrp"/> + <waitForElementVisible selector="{{StorefrontProductInfoMainSection.msrp}}" stepKey="waitForMsrp"/> <grabTextFrom selector="{{StorefrontProductInfoMainSection.msrp}}" stepKey="grabMsrp"/> <assertEquals stepKey="assertMsrp"> <expectedResult type="string">${{msrp}}</expectedResult> @@ -23,7 +23,7 @@ <!--Check product price when MSRP is not set or is less than price--> <actionGroup name="AssertMsrpFallbackOfProduct" extends="AssertMsrpOfProduct"> <remove keyForRemoval="seeClickForPriceLink"/> - <waitForElement selector="{{StorefrontProductInfoMainSection.msrpFallback}}" stepKey="waitForMsrp"/> + <waitForElementVisible selector="{{StorefrontProductInfoMainSection.msrpFallback}}" stepKey="waitForMsrp"/> <grabTextFrom selector="{{StorefrontProductInfoMainSection.msrpFallback}}" stepKey="grabMsrp"/> <dontSeeElement selector="{{StorefrontProductInfoMainSection.clickForPriceLink}}" after="assertMsrp" stepKey="dontSeeClickForPriceLink"/> </actionGroup> diff --git a/app/code/Magento/Msrp/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml b/app/code/Magento/Msrp/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml index 0819d5cf35f9d..08c0cb8d48309 100644 --- a/app/code/Magento/Msrp/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml +++ b/app/code/Magento/Msrp/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml @@ -8,6 +8,6 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductFormAdvancedPricingSection"> - <element name="msrp" type="input" selector="//input[@name='product[msrp]']" timeout="5"/> + <element name="msrp" type="input" selector="input[name='product[msrp]']" timeout="5"/> </section> </sections> diff --git a/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontCheckingConfigurableProductPriceWithMapTest.xml b/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontCheckingConfigurableProductPriceWithMapTest.xml index 2b6ebaadc3c8e..1a2d4ad8e06e4 100644 --- a/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontCheckingConfigurableProductPriceWithMapTest.xml +++ b/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontCheckingConfigurableProductPriceWithMapTest.xml @@ -32,9 +32,9 @@ <createData entity="DisableMAPConfig" stepKey="disableMAPConfig"/> <!--Delete entities--> <deleteData createDataKey="createConfigProductCreateConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="createConfigChildProduct1CreateConfigProduct" stepKey="deleteConfigChildProduct1"/> - <deleteData createDataKey="createConfigChildProduct2CreateConfigProduct" stepKey="deleteConfigChildProduct2"/> - <deleteData createDataKey="createConfigChildProduct3CreateConfigProduct" stepKey="deleteConfigChildProduct3"/> + <deleteData createDataKey="createChildProduct1CreateConfigProduct" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createChildProduct2CreateConfigProduct" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createChildProduct3CreateConfigProduct" stepKey="deleteConfigChildProduct3"/> <deleteData createDataKey="createConfigProductAttributeCreateConfigProduct" stepKey="deleteConfigProductAttribute"/> <!--Logout--> <actionGroup ref="logout" stepKey="logoutFromAdmin"/> @@ -42,14 +42,14 @@ <!--Set MSRP for Child product 1--> <actionGroup ref="AdminProductSetMsrp" stepKey="setMsrpForChildProduct1"> - <argument name="productId" value="$createConfigChildProduct1CreateConfigProduct.id$"/> + <argument name="product" value="$createChildProduct1CreateConfigProduct$"/> <argument name="msrp" value="45"/> </actionGroup> <actionGroup ref="saveProductForm" stepKey="saveChildProduct1"/> <!--Set MSRP for Child product 2--> <actionGroup ref="AdminProductSetMsrp" stepKey="setMsrpForChildProduct2"> - <argument name="productId" value="$createConfigChildProduct2CreateConfigProduct.id$"/> + <argument name="product" value="$createChildProduct2CreateConfigProduct$"/> <argument name="msrp" value="66"/> </actionGroup> <actionGroup ref="saveProductForm" stepKey="saveChildProduct2"/> From 5a736409eef6c7841f4a6fc8f3a7a8a8affd2a32 Mon Sep 17 00:00:00 2001 From: "Leandro F. L" <lfluvisotto@gmail.com> Date: Thu, 28 Feb 2019 16:20:05 +0100 Subject: [PATCH 0822/1295] Fix: Cart is emptied when enter is pressed after changing product quantity --- .../Magento/Checkout/view/frontend/web/js/shopping-cart.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/shopping-cart.js b/app/code/Magento/Checkout/view/frontend/web/js/shopping-cart.js index 399321bd2f67d..b09a003613970 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/shopping-cart.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/shopping-cart.js @@ -14,7 +14,11 @@ define([ _create: function () { var items, i; - $(this.options.emptyCartButton).on('click', $.proxy(function () { + $(this.options.emptyCartButton).on('click', $.proxy(function (event) { + if (event.detail == 0) { + return; + } + $(this.options.emptyCartButton).attr('name', 'update_cart_action_temp'); $(this.options.updateCartActionContainer) .attr('name', 'update_cart_action').attr('value', 'empty_cart'); From 6228b6f57929ec2f2ec39481910d297f04252a1c Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Thu, 28 Feb 2019 17:44:48 +0200 Subject: [PATCH 0823/1295] MAGETWO-73985: Simple product with MAP assigned to configurable should displays the same way as products with special price --- .../Msrp/view/base/templates/product/price/msrp.phtml | 5 ++--- app/code/Magento/Msrp/view/frontend/templates/popup.phtml | 7 ++++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Msrp/view/base/templates/product/price/msrp.phtml b/app/code/Magento/Msrp/view/base/templates/product/price/msrp.phtml index d7c6132d747be..ee282ebb82eb9 100644 --- a/app/code/Magento/Msrp/view/base/templates/product/price/msrp.phtml +++ b/app/code/Magento/Msrp/view/base/templates/product/price/msrp.phtml @@ -90,9 +90,8 @@ $priceElementIdPrefix = $block->getPriceElementIdPrefix() ? $block->getPriceElem } else { $data['addToCart']['addToCartButton'] = sprintf( 'form:has(input[type="hidden"][name="product"][value="%s"]) button[type="submit"]', - (int) $productId . ',' . - sprintf('.block.widget .price-box[data-product-id=%s]+.product-item-actions button.tocart', - (int) $productId)); + (int) $productId + ); } ?> <span id="<?= /* @escapeNotVerified */ $block->getPriceId() ? $block->getPriceId() : $priceElementId ?>" style="display:none"></span> diff --git a/app/code/Magento/Msrp/view/frontend/templates/popup.phtml b/app/code/Magento/Msrp/view/frontend/templates/popup.phtml index e0b3dd77dedcb..7ccd606b8f113 100644 --- a/app/code/Magento/Msrp/view/frontend/templates/popup.phtml +++ b/app/code/Magento/Msrp/view/frontend/templates/popup.phtml @@ -30,11 +30,12 @@ <span id="map-popup-price" class="actual-price"></span> </div> </div> - <form action="" method="POST" class="map-form-addtocart"> - <input type="hidden" name="product" class="product_id" value="" /> + <form action="" method="POST" id="product_addtocart_form_from_popup" class="map-form-addtocart"> + <input type="hidden" name="product" class="product_id" value="" id="map-popup-product-id"/> <button type="button" title="<?= $block->escapeHtml(__('Add to Cart')) ?>" - class="action tocart primary"> + class="action tocart primary" + id="map-popup-button"> <span><?= /* @escapeNotVerified */ __('Add to Cart') ?></span> </button> <div class="additional-addtocart-box"> From 8493e59539df034b78b3424c37c3c87c927c724a Mon Sep 17 00:00:00 2001 From: Shikha Mishra <shikhamishra@cedcoss.com> Date: Tue, 19 Feb 2019 20:00:48 +0530 Subject: [PATCH 0824/1295] Fixed #15059 Cannot reorder from the first try --- app/code/Magento/Sales/Model/AdminOrder/Create.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Sales/Model/AdminOrder/Create.php b/app/code/Magento/Sales/Model/AdminOrder/Create.php index 12d2a5396ddc4..313cd1eadec8d 100644 --- a/app/code/Magento/Sales/Model/AdminOrder/Create.php +++ b/app/code/Magento/Sales/Model/AdminOrder/Create.php @@ -583,6 +583,7 @@ public function initFromOrder(\Magento\Sales\Model\Order $order) } $quote->getShippingAddress()->unsCachedItemsAll(); + $quote->getBillingAddress()->unsCachedItemsAll(); $quote->setTotalsCollectedFlag(false); $this->quoteRepository->save($quote); From 65e9346166084c859b0e1d58e8448aabc9f4bc49 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Thu, 21 Feb 2019 10:49:29 +0200 Subject: [PATCH 0825/1295] Fix static tests. --- app/code/Magento/Sales/Model/AdminOrder/Create.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Sales/Model/AdminOrder/Create.php b/app/code/Magento/Sales/Model/AdminOrder/Create.php index 313cd1eadec8d..99393f77397c1 100644 --- a/app/code/Magento/Sales/Model/AdminOrder/Create.php +++ b/app/code/Magento/Sales/Model/AdminOrder/Create.php @@ -24,6 +24,7 @@ * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @since 100.0.2 */ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\Model\Cart\CartInterface From eaad776629a933b944663ef35c4bac08143d0592 Mon Sep 17 00:00:00 2001 From: Jitheesh <jitheeshvo@gmail.com> Date: Fri, 22 Feb 2019 10:01:45 +0530 Subject: [PATCH 0826/1295] As low as displays incorrect pricing on category page, tax appears to be added twice #21383 - excluded tax from min price --- .../Catalog/Pricing/Price/MinimalTierPriceCalculator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Pricing/Price/MinimalTierPriceCalculator.php b/app/code/Magento/Catalog/Pricing/Price/MinimalTierPriceCalculator.php index 387ef9416ef68..af81e67e86fce 100644 --- a/app/code/Magento/Catalog/Pricing/Price/MinimalTierPriceCalculator.php +++ b/app/code/Magento/Catalog/Pricing/Price/MinimalTierPriceCalculator.php @@ -58,6 +58,6 @@ public function getAmount(SaleableInterface $saleableItem) return $value === null ? null - : $this->calculator->getAmount($value, $saleableItem); + : $this->calculator->getAmount($value, $saleableItem, 'tax'); } } From aa7fee51fb66d75800c7c454da013b75d565ebe4 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Tue, 26 Feb 2019 11:57:48 +0200 Subject: [PATCH 0827/1295] Fix static tests. --- .../Pricing/Price/MinimalTierPriceCalculator.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Pricing/Price/MinimalTierPriceCalculator.php b/app/code/Magento/Catalog/Pricing/Price/MinimalTierPriceCalculator.php index af81e67e86fce..a5e573caa381e 100644 --- a/app/code/Magento/Catalog/Pricing/Price/MinimalTierPriceCalculator.php +++ b/app/code/Magento/Catalog/Pricing/Price/MinimalTierPriceCalculator.php @@ -29,8 +29,10 @@ public function __construct(CalculatorInterface $calculator) } /** - * Get raw value of "as low as" as a minimal among tier prices - * {@inheritdoc} + * Get raw value of "as low as" as a minimal among tier prices{@inheritdoc} + * + * @param SaleableInterface $saleableItem + * @return float|null */ public function getValue(SaleableInterface $saleableItem) { @@ -49,8 +51,10 @@ public function getValue(SaleableInterface $saleableItem) } /** - * Return calculated amount object that keeps "as low as" value - * {@inheritdoc} + * Return calculated amount object that keeps "as low as" value{@inheritdoc} + * + * @param SaleableInterface $saleableItem + * @return AmountInterface|null */ public function getAmount(SaleableInterface $saleableItem) { From 7bb5dd47e92e4d20da612e62bae7822de48d1bee Mon Sep 17 00:00:00 2001 From: Wojtek Naruniec <wojtek@mediotype.com> Date: Mon, 18 Feb 2019 13:16:20 +0100 Subject: [PATCH 0828/1295] Skip cart validation if empty cart action is executed --- .../view/frontend/web/js/action/update-shopping-cart.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js b/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js index ce1527b3d72d6..9d79e34b10f17 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js @@ -35,6 +35,11 @@ define([ return true; } + var action = this.element.find('#update_cart_action_container').val(); + if (action === 'empty_cart') { + return true; + } + if (this.isValid()) { event.preventDefault(); this.validateItems(this.options.validationURL, this.element.serialize()); From 847115efd5068b40a4d274702d6086f0d6549adc Mon Sep 17 00:00:00 2001 From: Wojtek Naruniec <wojtek@mediotype.com> Date: Fri, 22 Feb 2019 10:17:42 +0100 Subject: [PATCH 0829/1295] Move element id to component parameters, fix JS coding style --- .../Checkout/view/frontend/templates/cart/form.phtml | 3 ++- .../view/frontend/web/js/action/update-shopping-cart.js | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml index 1005c11e44d95..84ab9b13d8f3a 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml @@ -14,7 +14,8 @@ method="post" id="form-validate" data-mage-init='{"Magento_Checkout/js/action/update-shopping-cart": - {"validationURL" : "/checkout/cart/updateItemQty"} + {"validationURL" : "/checkout/cart/updateItemQty", + "updateCartActionContainer": "#update_cart_action_container"} }' class="form form-cart"> <?= $block->getBlockHtml('formkey') ?> diff --git a/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js b/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js index 9d79e34b10f17..7686dcad6e050 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js @@ -14,7 +14,8 @@ define([ $.widget('mage.updateShoppingCart', { options: { validationURL: '', - eventName: 'updateCartItemQty' + eventName: 'updateCartItemQty', + updateCartActionContainer: '' }, /** @inheritdoc */ @@ -31,11 +32,12 @@ define([ * @return {Boolean} */ onSubmit: function (event) { + var action = this.element.find(this.options.updateCartActionContainer).val(); + if (!this.options.validationURL) { return true; } - var action = this.element.find('#update_cart_action_container').val(); if (action === 'empty_cart') { return true; } From 42b03bb7cd92320bb6b689b82101ac4eb91b1b93 Mon Sep 17 00:00:00 2001 From: Oleksandr Kravchuk <swnsma@gmail.com> Date: Fri, 22 Feb 2019 13:46:04 +0200 Subject: [PATCH 0830/1295] Simplify. --- .../view/frontend/web/js/action/update-shopping-cart.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js b/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js index 7686dcad6e050..1920bc4d7ac41 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/action/update-shopping-cart.js @@ -34,11 +34,7 @@ define([ onSubmit: function (event) { var action = this.element.find(this.options.updateCartActionContainer).val(); - if (!this.options.validationURL) { - return true; - } - - if (action === 'empty_cart') { + if (!this.options.validationURL || action === 'empty_cart') { return true; } From 13c6edd690905c704cb69ff245d5ce7faff0aefe Mon Sep 17 00:00:00 2001 From: Ilan Parmentier <ilan.parmentier@artbambou.com> Date: Thu, 31 Jan 2019 14:44:29 +0100 Subject: [PATCH 0831/1295] Update details.phtml Good afternoon, I just encountered a problem with product's tabs in product info. I realized when description or other custom tabs have links, when you click on it, nothing happen. It caused by the script mage/tabs.js in details.phtml. The current link to trigger the tab is declared with data-role="switch" instead of a data-role="trigger". https://github.com/magento/magento2/blob/4f232511ceba6f1f7bf6f73b3b5609bd087f8c74/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml#L24-L37 When not even the trigger and the header is declared respectively by data-role="heading" and data-role="trigger", the script based it search on the current collapsible panel declared by data-role="collapsible". https://github.com/magento/magento2/blob/4f232511ceba6f1f7bf6f73b3b5609bd087f8c74/lib/web/mage/tabs.js#L99-L124 You can simply try by adding a link in the product description. Tell me if I am wrong. Cheers, Ilan PARMENTIER --- .../Catalog/view/frontend/templates/product/view/details.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml index 038bea86e7d4e..bbbc950e6712e 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml @@ -25,7 +25,7 @@ data-role="collapsible" id="tab-label-<?= /* @escapeNotVerified */ $alias ?>"> <a class="data switch" tabindex="-1" - data-toggle="switch" + data-toggle="trigger" href="#<?= /* @escapeNotVerified */ $alias ?>" id="tab-label-<?= /* @escapeNotVerified */ $alias ?>-title"> <?= /* @escapeNotVerified */ $label ?> From e5676cfb72590fff072c8dfff931740587ff2213 Mon Sep 17 00:00:00 2001 From: Ilan Parmentier <ilan.parmentier@artbambou.com> Date: Mon, 18 Feb 2019 15:37:52 +0100 Subject: [PATCH 0832/1295] Misconfigured aria-labelledby for product tabs Good afternoon, I find out attribute aria-labelledby is misconfigured. It must be declared for the content of the tab and not the title, in particular, for a parent tag. Ilan PARMENTIER Source : https://www.w3.org/TR/wai-aria-practices/examples/tabs/tabs-1/tabs.html#sc1_label --- .../view/frontend/templates/product/view/details.phtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml index 038bea86e7d4e..19a058006b18c 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml @@ -21,7 +21,6 @@ $label = $block->getChildData($alias, 'title'); ?> <div class="data item title" - aria-labelledby="tab-label-<?= /* @escapeNotVerified */ $alias ?>-title" data-role="collapsible" id="tab-label-<?= /* @escapeNotVerified */ $alias ?>"> <a class="data switch" tabindex="-1" @@ -31,7 +30,8 @@ <?= /* @escapeNotVerified */ $label ?> </a> </div> - <div class="data item content" id="<?= /* @escapeNotVerified */ $alias ?>" data-role="content"> + <div class="data item content" + aria-labelledby="tab-label-<?= /* @escapeNotVerified */ $alias ?>-title" id="<?= /* @escapeNotVerified */ $alias ?>" data-role="content"> <?= /* @escapeNotVerified */ $html ?> </div> <?php endforeach;?> From d935998b6f70c9f5943b817511b0b8576366cb55 Mon Sep 17 00:00:00 2001 From: tuyennn <thinghost76@gmail.com> Date: Fri, 1 Mar 2019 15:56:29 +0700 Subject: [PATCH 0833/1295] issues-21521: Fix Broken Tax Rate Search Filter Admin grid --- app/code/Magento/Tax/etc/di.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Tax/etc/di.xml b/app/code/Magento/Tax/etc/di.xml index 096f8359fadd3..3b46b0f9e258c 100644 --- a/app/code/Magento/Tax/etc/di.xml +++ b/app/code/Magento/Tax/etc/di.xml @@ -143,6 +143,7 @@ <arguments> <argument name="fieldMapping" xsi:type="array"> <item name="id" xsi:type="string">tax_calculation_rule_id</item> + <item name="code" xsi:type="string">main_table.code</item> <item name="tax_rate_ids" xsi:type="string">tax_calculation_rate_id</item> <item name="customer_tax_class_ids" xsi:type="string">cd.customer_tax_class_id</item> <item name="product_tax_class_ids" xsi:type="string">cd.product_tax_class_id</item> @@ -154,6 +155,7 @@ <arguments> <argument name="fieldMapping" xsi:type="array"> <item name="id" xsi:type="string">tax_calculation_rule_id</item> + <item name="code" xsi:type="string">main_table.code</item> <item name="tax_rate_ids" xsi:type="string">tax_calculation_rate_id</item> <item name="customer_tax_class_ids" xsi:type="string">cd.customer_tax_class_id</item> <item name="product_tax_class_ids" xsi:type="string">cd.product_tax_class_id</item> From 342afb26a16ea02b5a09d8e6a1de077a7ea09004 Mon Sep 17 00:00:00 2001 From: Dominic <d.fernando@ism-apac.com> Date: Sun, 10 Feb 2019 11:12:20 +0530 Subject: [PATCH 0834/1295] fixed-pagination-issue-admin-review-grid --- lib/web/mage/adminhtml/grid.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/web/mage/adminhtml/grid.js b/lib/web/mage/adminhtml/grid.js index c9af869d79161..b16b2c9defce0 100644 --- a/lib/web/mage/adminhtml/grid.js +++ b/lib/web/mage/adminhtml/grid.js @@ -531,12 +531,19 @@ define([ /** * @param {Object} event */ - inputPage: function (event) { + inputPage: function (event, lastId) { var element = Event.element(event), - keyCode = event.keyCode || event.which; + keyCode = event.keyCode || event.which, + enteredValue = parseInt(element.value), + lastId = parseInt(lastId); + if (keyCode == Event.KEY_RETURN) { //eslint-disable-line eqeqeq - this.setPage(element.value); + if (enteredValue > lastId) { + this.setPage(lastId); + } else { + this.setPage(element.value); + } } /*if(keyCode>47 && keyCode<58){ From 36ebc6542767c48c6f939e1fe1fe0f0faf2949f2 Mon Sep 17 00:00:00 2001 From: Dominic <d.fernando@ism-apac.com> Date: Sun, 10 Feb 2019 11:27:50 +0530 Subject: [PATCH 0835/1295] fixed-pagination-issue-admin-review-grid --- lib/web/mage/adminhtml/grid.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/web/mage/adminhtml/grid.js b/lib/web/mage/adminhtml/grid.js index b16b2c9defce0..b7e696ae8f4bd 100644 --- a/lib/web/mage/adminhtml/grid.js +++ b/lib/web/mage/adminhtml/grid.js @@ -530,6 +530,7 @@ define([ /** * @param {Object} event + * @param int lastId */ inputPage: function (event, lastId) { var element = Event.element(event), From 5392ee7a7d7d10dd61d5c712d9a23304830445e9 Mon Sep 17 00:00:00 2001 From: Dominic <d.fernando@ism-apac.com> Date: Sun, 10 Feb 2019 11:31:49 +0530 Subject: [PATCH 0836/1295] fixed-pagination-issue-admin-review-grid-clean-code --- lib/web/mage/adminhtml/grid.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/web/mage/adminhtml/grid.js b/lib/web/mage/adminhtml/grid.js index b7e696ae8f4bd..e27dd9c2a0d26 100644 --- a/lib/web/mage/adminhtml/grid.js +++ b/lib/web/mage/adminhtml/grid.js @@ -538,12 +538,11 @@ define([ enteredValue = parseInt(element.value), lastId = parseInt(lastId); - if (keyCode == Event.KEY_RETURN) { //eslint-disable-line eqeqeq if (enteredValue > lastId) { this.setPage(lastId); } else { - this.setPage(element.value); + this.setPage(enteredValue); } } From 73b57d441f2a5494098c112482fe9316d802fb99 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Mon, 25 Feb 2019 16:00:48 +0200 Subject: [PATCH 0837/1295] Fix static tests. --- lib/web/mage/adminhtml/grid.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/web/mage/adminhtml/grid.js b/lib/web/mage/adminhtml/grid.js index e27dd9c2a0d26..5f7a709f04fea 100644 --- a/lib/web/mage/adminhtml/grid.js +++ b/lib/web/mage/adminhtml/grid.js @@ -530,17 +530,17 @@ define([ /** * @param {Object} event - * @param int lastId + * @param {*} lastId */ inputPage: function (event, lastId) { var element = Event.element(event), keyCode = event.keyCode || event.which, - enteredValue = parseInt(element.value), - lastId = parseInt(lastId); + enteredValue = parseInt(element.value, 10), + pageId = parseInt(lastId, 10); if (keyCode == Event.KEY_RETURN) { //eslint-disable-line eqeqeq - if (enteredValue > lastId) { - this.setPage(lastId); + if (enteredValue > pageId) { + this.setPage(pageId); } else { this.setPage(enteredValue); } From d504aa1ca7f42156d0ca3f08a4f3905b40df8066 Mon Sep 17 00:00:00 2001 From: mageprince <mail.mageprince@gmail.com> Date: Fri, 1 Mar 2019 14:57:52 +0530 Subject: [PATCH 0838/1295] 2.2-develop-backport-21401 --- app/code/Magento/Checkout/Controller/Cart/Addgroup.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Checkout/Controller/Cart/Addgroup.php b/app/code/Magento/Checkout/Controller/Cart/Addgroup.php index 8654bdbde5893..1876d3ca37d94 100644 --- a/app/code/Magento/Checkout/Controller/Cart/Addgroup.php +++ b/app/code/Magento/Checkout/Controller/Cart/Addgroup.php @@ -41,6 +41,8 @@ public function execute() } } $this->cart->save(); + } else { + $this->messageManager->addErrorMessage(__('Please select at least one product to add to cart')); } return $this->_goBack(); } From 73cca3972b4caa7ed560e7213b2a96b23f5fd05e Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Fri, 1 Mar 2019 12:26:41 +0200 Subject: [PATCH 0839/1295] MC-15187: Restricted Countries on Default Websites affect Other Sites --- .../Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php | 5 +++++ .../Sales/Block/Adminhtml/Order/Create/Form/Address.php | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php index d15c218a60b47..c1b67e6dcf88a 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php @@ -96,6 +96,11 @@ protected function _prepareLayout() public function getForm() { if ($this->_form === null) { + $storeId = $this->getCreateOrderModel() + ->getSession() + ->getStoreId(); + $this->_storeManager->setCurrentStore($storeId); + $this->_form = $this->_formFactory->create(); $this->_prepareForm(); } diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Address.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Address.php index fe1682e2de830..6625f438f9515 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Address.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Address.php @@ -217,11 +217,6 @@ public function getAddressCollectionJson() */ protected function _prepareForm() { - $storeId = $this->getCreateOrderModel() - ->getSession() - ->getStoreId(); - $this->_storeManager->setCurrentStore($storeId); - $fieldset = $this->_form->addFieldset('main', ['no_container' => true]); $addressForm = $this->_customerFormFactory->create('customer_address', 'adminhtml_customer_address'); From cb6f9864804f96fd27db699dbf3be98eb9120d5f Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Fri, 1 Mar 2019 15:13:28 +0200 Subject: [PATCH 0840/1295] MAGETWO-98488: New customer created from admin for non default store view receives Welcome Email from default store view --- .../Model/Plugin/CustomerPlugin.php | 19 +++---------- .../Unit/Model/Plugin/CustomerPluginTest.php | 27 +------------------ 2 files changed, 5 insertions(+), 41 deletions(-) diff --git a/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php b/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php index 792dcf2fbe689..58b51009c205a 100644 --- a/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php +++ b/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php @@ -6,13 +6,12 @@ namespace Magento\Newsletter\Model\Plugin; use Magento\Customer\Api\CustomerRepositoryInterface as CustomerRepository; -use Magento\Customer\Api\Data\CustomerExtensionInterface; use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Newsletter\Model\SubscriberFactory; use Magento\Framework\Api\ExtensionAttributesFactory; -use Magento\Framework\App\ObjectManager; use Magento\Newsletter\Model\ResourceModel\Subscriber; -use Magento\Newsletter\Model\SubscriberFactory; -use Magento\Store\Model\StoreManagerInterface; +use Magento\Customer\Api\Data\CustomerExtensionInterface; +use Magento\Framework\App\ObjectManager; class CustomerPlugin { @@ -38,30 +37,22 @@ class CustomerPlugin */ private $customerSubscriptionStatus = []; - /** - * @var StoreManagerInterface - */ - private $storeManager; - /** * Initialize dependencies. * * @param SubscriberFactory $subscriberFactory * @param ExtensionAttributesFactory|null $extensionFactory * @param Subscriber|null $subscriberResource - * @param StoreManagerInterface|null $storeManager */ public function __construct( SubscriberFactory $subscriberFactory, ExtensionAttributesFactory $extensionFactory = null, - Subscriber $subscriberResource = null, - StoreManagerInterface $storeManager = null + Subscriber $subscriberResource = null ) { $this->subscriberFactory = $subscriberFactory; $this->extensionFactory = $extensionFactory ?: ObjectManager::getInstance()->get(ExtensionAttributesFactory::class); $this->subscriberResource = $subscriberResource ?: ObjectManager::getInstance()->get(Subscriber::class); - $this->storeManager = $storeManager ?: ObjectManager::getInstance()->get(StoreManagerInterface::class); } /** @@ -158,8 +149,6 @@ public function afterDelete(CustomerRepository $subject, $result, CustomerInterf public function afterGetById(CustomerRepository $subject, CustomerInterface $customer) { $extensionAttributes = $customer->getExtensionAttributes(); - $storeId = $this->storeManager->getStore()->getId(); - $customer->setStoreId($storeId); if ($extensionAttributes === null) { /** @var CustomerExtensionInterface $extensionAttributes */ $extensionAttributes = $this->extensionFactory->create(CustomerInterface::class); diff --git a/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php b/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php index 0bc79244bdf1c..39a9c2a0d95d2 100644 --- a/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php +++ b/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php @@ -10,8 +10,6 @@ use Magento\Customer\Api\Data\CustomerExtensionInterface; use Magento\Framework\Api\ExtensionAttributesFactory; use Magento\Newsletter\Model\ResourceModel\Subscriber; -use Magento\Store\Model\Store; -use Magento\Store\Model\StoreManagerInterface; class CustomerPluginTest extends \PHPUnit\Framework\TestCase { @@ -55,11 +53,6 @@ class CustomerPluginTest extends \PHPUnit\Framework\TestCase */ private $customerMock; - /** - * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $storeManagerMock; - protected function setUp() { $this->subscriberFactory = $this->getMockBuilder(\Magento\Newsletter\Model\SubscriberFactory::class) @@ -94,8 +87,6 @@ protected function setUp() ->setMethods(["getExtensionAttributes"]) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->storeManagerMock = $this->createMock(StoreManagerInterface::class); - $this->subscriberFactory->expects($this->any())->method('create')->willReturn($this->subscriber); $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->plugin = $this->objectManager->getObject( @@ -103,8 +94,7 @@ protected function setUp() [ 'subscriberFactory' => $this->subscriberFactory, 'extensionFactory' => $this->extensionFactoryMock, - 'subscriberResource' => $this->subscriberResourceMock, - 'storeManager' => $this->storeManagerMock, + 'subscriberResource' => $this->subscriberResourceMock ] ); } @@ -208,7 +198,6 @@ public function testAfterGetByIdCreatesExtensionAttributesIfItIsNotSet( ) { $subject = $this->createMock(\Magento\Customer\Api\CustomerRepositoryInterface::class); $subscriber = [$subscriberStatusKey => $subscriberStatusValue]; - $this->prepareStoreData(); $this->extensionFactoryMock->expects($this->any()) ->method('create') ->willReturn($this->customerExtensionMock); @@ -234,7 +223,6 @@ public function testAfterGetByIdSetsIsSubscribedFlagIfItIsNotSet() { $subject = $this->createMock(\Magento\Customer\Api\CustomerRepositoryInterface::class); $subscriber = ['subscriber_id' => 1, 'subscriber_status' => 1]; - $this->prepareStoreData(); $this->customerMock->expects($this->any()) ->method('getExtensionAttributes') ->willReturn($this->customerExtensionMock); @@ -267,17 +255,4 @@ public function afterGetByIdDataProvider() [null, null, false] ]; } - - /** - * Prepare store information - * - * @return void - */ - private function prepareStoreData() - { - $storeId = 1; - $storeMock = $this->createMock(Store::class); - $storeMock->expects($this->any())->method('getId')->willReturn($storeId); - $this->storeManagerMock->expects($this->any())->method('getStore')->willReturn($storeMock); - } } From e9e306a6b57f738db813835e459209dcdf1e9381 Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Fri, 1 Mar 2019 16:19:08 +0200 Subject: [PATCH 0841/1295] MAGETWO-97271: Configurable product can be added as a variation to another Configurable product in the admin --- .../Unit/Ui/Component/ColumnFactoryTest.php | 156 ++++++++++++++++++ .../Catalog/Ui/Component/ColumnFactory.php | 6 +- .../Modifier/Data/AssociatedProductsTest.php | 64 ++++++- 3 files changed, 224 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php new file mode 100644 index 0000000000000..c4a7aa4037bec --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php @@ -0,0 +1,156 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Test\Unit\Ui\Component; + +use PHPUnit\Framework\TestCase; +use Magento\Catalog\Ui\Component\ColumnFactory; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Framework\View\Element\UiComponentFactory; +use Magento\Ui\Component\Listing\Columns\ColumnInterface; +use Magento\Ui\Component\Filters\FilterModifier; + +/** + * ColumnFactory test. + */ +class ColumnFactoryTest extends TestCase +{ + /** + * @var ColumnFactory + */ + private $columnFactory; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var ProductAttributeInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $attribute; + + /** + * @var ContextInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $context; + + /** + * @var UiComponentFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $uiComponentFactory; + + /** + * @var ColumnInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $column; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = new ObjectManager($this); + + $this->attribute = $this->getMockBuilder(ProductAttributeInterface::class) + ->setMethods(['usesSource']) + ->getMockForAbstractClass(); + $this->context = $this->createMock(ContextInterface::class); + $this->uiComponentFactory = $this->createMock(UiComponentFactory::class); + $this->column = $this->getMockForAbstractClass(ColumnInterface::class); + $this->uiComponentFactory->method('create') + ->willReturn($this->column); + + $this->columnFactory = $this->objectManager->getObject(ColumnFactory::class, [ + 'componentFactory' => $this->uiComponentFactory + ]); + } + + /** + * Tests the create method will return correct object. + * + * @return void + */ + public function testCreatedObject() + { + $this->context->method('getRequestParam') + ->with(FilterModifier::FILTER_MODIFIER, []) + ->willReturn([]); + + $object = $this->columnFactory->create($this->attribute, $this->context); + $this->assertEquals( + $this->column, + $object, + 'Object must be the same which the ui component factory creates.' + ); + } + + /** + * Tests create method with not filterable in grid attribute. + * + * @param array $filterModifiers + * @param null|string $filter + * + * @return void + * @dataProvider filterModifiersProvider + */ + public function testCreateWithNotFilterableInGridAttribute(array $filterModifiers, $filter) + { + $componentFactoryArgument = [ + 'data' => [ + 'config' => [ + 'label' => __(null), + 'dataType' => 'text', + 'add_field' => true, + 'visible' => null, + 'filter' => $filter, + 'component' => 'Magento_Ui/js/grid/columns/column', + ], + ], + 'context' => $this->context, + ]; + + $this->context->method('getRequestParam') + ->with(FilterModifier::FILTER_MODIFIER, []) + ->willReturn($filterModifiers); + $this->attribute->method('getIsFilterableInGrid') + ->willReturn(false); + $this->attribute->method('getAttributeCode') + ->willReturn('color'); + + $this->uiComponentFactory->expects($this->once()) + ->method('create') + ->with($this->anything(), $this->anything(), $componentFactoryArgument); + + $this->columnFactory->create($this->attribute, $this->context); + } + + /** + * Filter modifiers data provider. + * + * @return array + */ + public function filterModifiersProvider(): array + { + return [ + 'without' => [ + 'filter_modifiers' => [], + 'filter' => null, + ], + 'with' => [ + 'filter_modifiers' => [ + 'color' => [ + 'condition_type' => 'notnull', + ], + ], + 'filter' => 'text', + ], + ]; + } +} diff --git a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php index cbc67fee8a5a3..40687e37e1538 100644 --- a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php +++ b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php @@ -5,6 +5,8 @@ */ namespace Magento\Catalog\Ui\Component; +use Magento\Ui\Component\Filters\FilterModifier; + /** * @api * @since 100.0.2 @@ -54,13 +56,15 @@ public function __construct(\Magento\Framework\View\Element\UiComponentFactory $ */ public function create($attribute, $context, array $config = []) { + $filterModifiers = $context->getRequestParam(FilterModifier::FILTER_MODIFIER, []); + $columnName = $attribute->getAttributeCode(); $config = array_merge([ 'label' => __($attribute->getDefaultFrontendLabel()), 'dataType' => $this->getDataType($attribute), 'add_field' => true, 'visible' => $attribute->getIsVisibleInGrid(), - 'filter' => ($attribute->getIsFilterableInGrid()) + 'filter' => ($attribute->getIsFilterableInGrid() || array_key_exists($columnName, $filterModifiers)) ? $this->getFilterType($attribute->getFrontendInput()) : null, ], $config); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/Data/AssociatedProductsTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/Data/AssociatedProductsTest.php index 234f0aae6a3ea..63858e91b64f2 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/Data/AssociatedProductsTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/Data/AssociatedProductsTest.php @@ -5,7 +5,19 @@ */ namespace Magento\ConfigurableProduct\Ui\DataProvider\Product\Form\Modifier\Data; -class AssociatedProductsTest extends \PHPUnit\Framework\TestCase +use Magento\Framework\View\Element\UiComponentFactory; +use Magento\Ui\Component\Filters\FilterModifier; +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\ConfigurableProduct\Ui\DataProvider\Product\Form\Modifier\ConfigurablePanel; +use Magento\Framework\App\RequestInterface; +use PHPUnit\Framework\TestCase; + +/** + * AssociatedProductsTest + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class AssociatedProductsTest extends TestCase { /** * @var \Magento\Framework\ObjectManagerInterface $objectManager @@ -17,6 +29,9 @@ class AssociatedProductsTest extends \PHPUnit\Framework\TestCase */ private $registry; + /** + * @inheritdoc + */ public function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); @@ -64,6 +79,53 @@ public function testGetProductMatrix($interfaceLocale) } } + /** + * Tests configurable product won't appear in product listing. + * + * Tests configurable product won't appear in configurable associated product listing if its options attribute + * is not filterable in grid. + * + * @return void + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @magentoAppArea adminhtml + */ + public function testAddManuallyConfigurationsWithNotFilterableInGridAttribute() + { + /** @var RequestInterface $request */ + $request = $this->objectManager->get(RequestInterface::class); + $request->setParams([ + FilterModifier::FILTER_MODIFIER => [ + 'test_configurable' => [ + 'condition_type' => 'notnull', + ], + ], + 'attributes_codes' => [ + 'test_configurable' + ], + ]); + $context = $this->objectManager->create(ContextInterface::class, ['request' => $request]); + /** @var UiComponentFactory $uiComponentFactory */ + $uiComponentFactory = $this->objectManager->get(UiComponentFactory::class); + $uiComponent = $uiComponentFactory->create( + ConfigurablePanel::ASSOCIATED_PRODUCT_LISTING, + null, + ['context' => $context] + ); + + foreach ($uiComponent->getChildComponents() as $childUiComponent) { + $childUiComponent->prepare(); + } + + $dataSource = $uiComponent->getDataSourceData(); + $skus = array_column($dataSource['data']['items'], 'sku'); + + $this->assertNotContains( + 'configurable', + $skus, + 'Only products with specified attribute should be in product list' + ); + } + /** * @return array */ From 05300a421a4afe3f033a0200275b367e02ad95dd Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Fri, 1 Mar 2019 16:54:47 +0200 Subject: [PATCH 0842/1295] MAGETWO-96062: Wrong special price displayed in product search results --- .../Model/Product/Type/FrontSpecialPrice.php | 128 ++++++++++++++++++ .../ActionGroup/AdminProductActionGroup.xml | 1 + .../Mftf/Data/CatalogStorefrontConfigData.xml | 22 +++ ...AdminProductFormAdvancedPricingSection.xml | 1 + app/code/Magento/Catalog/etc/frontend/di.xml | 1 + .../AdminSwitchStoreViewActionGroup.xml | 7 +- .../Mftf/Section/AdminMainActionsSection.xml | 3 +- 7 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Catalog/Model/Product/Type/FrontSpecialPrice.php diff --git a/app/code/Magento/Catalog/Model/Product/Type/FrontSpecialPrice.php b/app/code/Magento/Catalog/Model/Product/Type/FrontSpecialPrice.php new file mode 100644 index 0000000000000..f6893a41113e6 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Type/FrontSpecialPrice.php @@ -0,0 +1,128 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\Type; + +use Magento\Store\Model\Store; +use Magento\Catalog\Model\ResourceModel\Product\Price\SpecialPrice; +use Magento\Catalog\Api\Data\SpecialPriceInterface; +use Magento\Store\Api\Data\WebsiteInterface; + +/** + * Product special price model. + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class FrontSpecialPrice extends Price +{ + /** + * @var SpecialPrice + */ + private $specialPrice; + + /** + * @param \Magento\CatalogRule\Model\ResourceModel\RuleFactory $ruleFactory + * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate + * @param \Magento\Customer\Model\Session $customerSession + * @param \Magento\Framework\Event\ManagerInterface $eventManager + * @param \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency + * @param \Magento\Customer\Api\GroupManagementInterface $groupManagement + * @param \Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory $tierPriceFactory + * @param \Magento\Framework\App\Config\ScopeConfigInterface $config + * @param SpecialPrice $specialPrice + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + */ + public function __construct( + \Magento\CatalogRule\Model\ResourceModel\RuleFactory $ruleFactory, + \Magento\Store\Model\StoreManagerInterface $storeManager, + \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, + \Magento\Customer\Model\Session $customerSession, + \Magento\Framework\Event\ManagerInterface $eventManager, + \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency, + \Magento\Customer\Api\GroupManagementInterface $groupManagement, + \Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory $tierPriceFactory, + \Magento\Framework\App\Config\ScopeConfigInterface $config, + SpecialPrice $specialPrice + ) { + $this->specialPrice = $specialPrice; + parent::__construct( + $ruleFactory, + $storeManager, + $localeDate, + $customerSession, + $eventManager, + $priceCurrency, + $groupManagement, + $tierPriceFactory, + $config + ); + } + + /** + * @inheritdoc + */ + protected function _applySpecialPrice($product, $finalPrice) + { + if (!$product->getSpecialPrice()) { + return $finalPrice; + } + + $specialPrices = $this->getSpecialPrices($product); + $specialPrice = !(empty($specialPrices)) ? min($specialPrices) : $product->getSpecialPrice(); + + $specialPrice = $this->calculateSpecialPrice( + $finalPrice, + $specialPrice, + $product->getSpecialFromDate(), + $product->getSpecialToDate(), + WebsiteInterface::ADMIN_CODE + ); + $product->setData('special_price', $specialPrice); + + return $specialPrice; + } + + /** + * Get special prices. + * + * @param mixed $product + * @return array + */ + private function getSpecialPrices($product): array + { + $allSpecialPrices = $this->specialPrice->get([$product->getSku()]); + $specialPrices = []; + foreach ($allSpecialPrices as $price) { + if ($this->isSuitableSpecialPrice($product, $price)) { + $specialPrices[] = $price['value']; + } + } + + return $specialPrices; + } + + /** + * Price is suitable from default and current store + start and end date are equal. + * + * @param mixed $product + * @param array $price + * @return bool + */ + private function isSuitableSpecialPrice($product, array $price): bool + { + $priceStoreId = $price[Store::STORE_ID]; + if (($priceStoreId == Store::DEFAULT_STORE_ID || $product->getStoreId() == $priceStoreId) + && $price[SpecialPriceInterface::PRICE_FROM] == $product->getSpecialFromDate() + && $price[SpecialPriceInterface::PRICE_TO] == $product->getSpecialToDate()) { + return true; + } + + return false; + } +} diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index d64dfb928e651..88764fef3c86d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -86,6 +86,7 @@ <scrollToTopOfPage stepKey="initScrollToTopOfThePage"/> <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickAdvancedPricingLink"/> <waitForElementVisible selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="waitSpecialPrice"/> + <conditionalClick selector="{{AdminProductFormAdvancedPricingSection.useDefaultPrice}}" dependentSelector="{{AdminProductFormAdvancedPricingSection.useDefaultPrice}}" visible="true" stepKey="checkUseDefault"/> <fillField userInput="{{price}}" selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="fillSpecialPrice"/> <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDone"/> <waitForElementNotVisible selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="waitForCloseModalWindow"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml index 850190f0fd24c..d36877025e970 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml @@ -46,4 +46,26 @@ <entity name="DefaultFlatCatalogProduct" type="flat_catalog_product"> <data key="value">0</data> </entity> + + <entity name="UseFlatCatalogCategoryAndProduct" type="catalog_storefront_config"> + <requiredEntity type="flat_catalog_product">UseFlatCatalogProduct</requiredEntity> + <requiredEntity type="flat_catalog_category">UseFlatCatalogCategory</requiredEntity> + </entity> + + <entity name="UseFlatCatalogProduct" type="flat_catalog_product"> + <data key="value">1</data> + </entity> + + <entity name="UseFlatCatalogCategory" type="flat_catalog_category"> + <data key="value">1</data> + </entity> + + <entity name="DefaultFlatCatalogCategoryAndProduct" type="catalog_storefront_config"> + <requiredEntity type="flat_catalog_product">DefaultFlatCatalogProduct</requiredEntity> + <requiredEntity type="flat_catalog_category">DefaultFlatCatalogCategory</requiredEntity> + </entity> + + <entity name="DefaultFlatCatalogCategory" type="flat_catalog_category"> + <data key="value">0</data> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml index ef66a41e27d06..9714c1f6eb483 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml @@ -21,5 +21,6 @@ <element name="productTierPricePercentageError" type="text" selector="div[data-index='percentage_value'] label.admin__field-error" /> <element name="specialPrice" type="input" selector="input[name='product[special_price]']"/> <element name="doneButton" type="button" selector=".product_form_product_form_advanced_pricing_modal button.action-primary" timeout="5"/> + <element name="useDefaultPrice" type="checkbox" selector="input[name='use_default[special_price]']"/> </section> </sections> diff --git a/app/code/Magento/Catalog/etc/frontend/di.xml b/app/code/Magento/Catalog/etc/frontend/di.xml index 95391b656380f..4203af383b366 100644 --- a/app/code/Magento/Catalog/etc/frontend/di.xml +++ b/app/code/Magento/Catalog/etc/frontend/di.xml @@ -98,4 +98,5 @@ <type name="Magento\Framework\App\Action\AbstractAction"> <plugin name="catalog_app_action_dispatch_controller_context_plugin" type="Magento\Catalog\Plugin\Framework\App\Action\ContextPlugin" /> </type> + <preference for="Magento\Catalog\Model\Product\Type\Price" type="Magento\Catalog\Model\Product\Type\FrontSpecialPrice" /> </config> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml index 1c56c3cc44220..34a0e5a10b81d 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml @@ -9,7 +9,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminSwitchBaseActionGroup"> <arguments> - <argument name="scopeName" defaultValue="customStore.name"/> + <argument name="scopeName" type="string" defaultValue="customStore.name"/> </arguments> <scrollToTopOfPage stepKey="scrollToTop"/> <click selector="{{AdminMainActionsSection.storeViewDropdown}}" stepKey="clickScopeSwitchDropdown"/> @@ -29,4 +29,9 @@ <waitForElementVisible selector="{{AdminMainActionsSection.websiteByName(scopeName)}}" after="clickScopeSwitchDropdown" stepKey="waitForWebsiteNameIsVisible"/> <click selector="{{AdminMainActionsSection.websiteByName(scopeName)}}" after="waitForWebsiteNameIsVisible" stepKey="clickStoreViewByName"/> </actionGroup> + + <actionGroup name="AdminSwitchToAllStoreViewActionGroup" extends="AdminSwitchBaseActionGroup"> + <click selector="{{AdminMainActionsSection.allStoreViews}}" after="clickScopeSwitchDropdown" stepKey="clickStoreViewByName"/> + <see selector="{{AdminMainActionsSection.storeSwitcher}}" userInput="All Store Views" after="clickStoreViewByName" stepKey="seeNewStoreViewName"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminMainActionsSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminMainActionsSection.xml index 1a95d88d454e4..7088ba0e1d103 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminMainActionsSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminMainActionsSection.xml @@ -11,7 +11,8 @@ <section name="AdminMainActionsSection"> <element name="storeSwitcher" type="text" selector=".store-switcher"/> <element name="storeViewDropdown" type="button" selector="#store-change-button"/> - <element name="storeViewByName" type="button" selector="//*[@class='store-switcher-store-view ']/a[contains(text(), '{{storeViewName}}')]" timeout="30" parameterized="true"/> + <element name="storeViewByName" type="button" selector="//*[@class='store-switcher-store-view ']/*[contains(text(), '{{storeViewName}}')]" timeout="30" parameterized="true"/> <element name="websiteByName" type="button" selector="//*[@class='store-switcher-website ']/a[contains(text(), '{{websiteName}}')]" timeout="30" parameterized="true"/> + <element name="allStoreViews" type="button" selector=".store-switcher .store-switcher-all" timeout="30"/> </section> </sections> From 9f0ef596df5e7e97d4057e4a84eaf18f08cbb5e0 Mon Sep 17 00:00:00 2001 From: Maksym Novik <m.novik@ism-ukraine.com> Date: Fri, 1 Mar 2019 17:02:10 +0200 Subject: [PATCH 0843/1295] Static tests: forbid 'or' instead of '||' #21062. Added Squiz.Operators.ValidLogicalOperators rule --- dev/tests/static/framework/Magento/ruleset.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/static/framework/Magento/ruleset.xml b/dev/tests/static/framework/Magento/ruleset.xml index 56a5a9e55c30e..fac0842214d92 100644 --- a/dev/tests/static/framework/Magento/ruleset.xml +++ b/dev/tests/static/framework/Magento/ruleset.xml @@ -25,5 +25,6 @@ <rule ref="Squiz.Commenting.DocCommentAlignment"/> <rule ref="Squiz.Functions.GlobalFunction"/> + <rule ref="Squiz.Operators.ValidLogicalOperators"/> <rule ref="Squiz.WhiteSpace.LogicalOperatorSpacing"/> </ruleset> From 020ab5e1541d26050e5b0c4b88e4e99fc69746be Mon Sep 17 00:00:00 2001 From: David Alger <davidmalger@gmail.com> Date: Fri, 1 Mar 2019 15:43:20 -0600 Subject: [PATCH 0844/1295] Fixed curl adapter to properly set http version as passed in arguments to prevent failure when requesting from http2 capable endpoint --- lib/internal/Magento/Framework/HTTP/Adapter/Curl.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/internal/Magento/Framework/HTTP/Adapter/Curl.php b/lib/internal/Magento/Framework/HTTP/Adapter/Curl.php index db466ca30e7eb..5c2fbb855f161 100644 --- a/lib/internal/Magento/Framework/HTTP/Adapter/Curl.php +++ b/lib/internal/Magento/Framework/HTTP/Adapter/Curl.php @@ -183,6 +183,12 @@ public function write($method, $url, $http_ver = '1.1', $headers = [], $body = ' curl_setopt($this->_getResource(), CURLOPT_CUSTOMREQUEST, 'GET'); } + if ($http_ver === \Zend_Http_Client::HTTP_1) { + curl_setopt($this->_getResource(), CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + } elseif ($http_ver === \Zend_Http_Client::HTTP_0) { + curl_setopt($this->_getResource(), CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); + } + if (is_array($headers)) { curl_setopt($this->_getResource(), CURLOPT_HTTPHEADER, $headers); } From 1b8d7686548aa64e31f58b8496aea56e54d9e2a1 Mon Sep 17 00:00:00 2001 From: vtymchynskyi <vtymchynskyi@magento.com> Date: Fri, 1 Mar 2019 16:51:44 -0600 Subject: [PATCH 0845/1295] MAGETWO-98431: Paypal securetoken/securetokenId can be compromised - added token based validation --- .../Transparent/RequestSecureToken.php | 16 ++- .../Transparent/RequestSecureTokenTest.php | 116 ++++++++++++------ 2 files changed, 92 insertions(+), 40 deletions(-) diff --git a/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php b/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php index 497e32157de05..09b1e9faccf2f 100644 --- a/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php +++ b/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php @@ -6,9 +6,11 @@ namespace Magento\Paypal\Controller\Transparent; use Magento\Framework\App\Action\Context; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Controller\Result\Json; use Magento\Framework\Controller\Result\JsonFactory; use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\Data\Form\FormKey\Validator; use Magento\Framework\Session\Generic; use Magento\Framework\Session\SessionManager; use Magento\Framework\Session\SessionManagerInterface; @@ -49,6 +51,11 @@ class RequestSecureToken extends \Magento\Framework\App\Action\Action */ private $transparent; + /** + * @var Validator + */ + private $formKeyValidator; + /** * @param Context $context * @param JsonFactory $resultJsonFactory @@ -57,6 +64,7 @@ class RequestSecureToken extends \Magento\Framework\App\Action\Action * @param SessionManager $sessionManager * @param Transparent $transparent * @param SessionManagerInterface|null $sessionInterface + * @param Validator $formKeyValidator */ public function __construct( Context $context, @@ -65,13 +73,17 @@ public function __construct( SecureToken $secureTokenService, SessionManager $sessionManager, Transparent $transparent, - SessionManagerInterface $sessionInterface = null + SessionManagerInterface $sessionInterface = null, + Validator $formKeyValidator = null + ) { $this->resultJsonFactory = $resultJsonFactory; $this->sessionTransparent = $sessionTransparent; $this->secureTokenService = $secureTokenService; $this->sessionManager = $sessionInterface ?: $sessionManager; $this->transparent = $transparent; + $this->formKeyValidator = $formKeyValidator ?: ObjectManager::getInstance()->get(Validator::class); + parent::__construct($context); } @@ -85,7 +97,7 @@ public function execute() /** @var Quote $quote */ $quote = $this->sessionManager->getQuote(); - if (!$quote || !$quote instanceof Quote) { + if (!$quote || !$quote instanceof Quote || !$this->formKeyValidator->validate($this->getRequest())) { return $this->getErrorResponse(); } diff --git a/app/code/Magento/Paypal/Test/Unit/Controller/Transparent/RequestSecureTokenTest.php b/app/code/Magento/Paypal/Test/Unit/Controller/Transparent/RequestSecureTokenTest.php index 60451a9827097..4b8cb98936463 100644 --- a/app/code/Magento/Paypal/Test/Unit/Controller/Transparent/RequestSecureTokenTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Controller/Transparent/RequestSecureTokenTest.php @@ -6,13 +6,15 @@ namespace Magento\Paypal\Test\Unit\Controller\Transparent; use Magento\Framework\App\Action\Context; +use Magento\Framework\Controller\Result\Json; use Magento\Framework\Controller\Result\JsonFactory; +use Magento\Framework\Data\Form\FormKey\Validator; use Magento\Framework\Session\Generic; use Magento\Framework\Session\SessionManager; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Paypal\Controller\Transparent\RequestSecureToken; use Magento\Paypal\Model\Payflow\Service\Request\SecureToken; use Magento\Paypal\Model\Payflow\Transparent; +use Magento\Quote\Model\Quote; /** * Class RequestSecureTokenTest @@ -56,6 +58,11 @@ class RequestSecureTokenTest extends \PHPUnit\Framework\TestCase */ protected $sessionManagerMock; + /** + * @var Validator|\PHPUnit_Framework_MockObject_MockObject + */ + private $formKeyValidator; + /** * Set up * @@ -64,9 +71,13 @@ class RequestSecureTokenTest extends \PHPUnit\Framework\TestCase protected function setUp() { + $request = $this->createMock(\Magento\Framework\App\RequestInterface::class); $this->contextMock = $this->getMockBuilder(\Magento\Framework\App\Action\Context::class) ->disableOriginalConstructor() ->getMock(); + $this->contextMock->method('getRequest') + ->willReturn($request); + $this->resultJsonFactoryMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\JsonFactory::class) ->setMethods(['create']) ->disableOriginalConstructor() @@ -90,13 +101,19 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); + $this->formKeyValidator = $this->getMockBuilder(Validator::class) + ->disableOriginalConstructor() + ->getMock(); + $this->controller = new \Magento\Paypal\Controller\Transparent\RequestSecureToken( $this->contextMock, $this->resultJsonFactoryMock, $this->sessionTransparentMock, $this->secureTokenServiceMock, $this->sessionManagerMock, - $this->transparentMock + $this->transparentMock, + null, + $this->formKeyValidator ); } @@ -113,16 +130,15 @@ public function testExecuteSuccess() 'error' => false ]; - $quoteMock = $this->getMockBuilder(\Magento\Quote\Model\Quote::class) + $quoteMock = $this->getMockBuilder(Quote::class) ->disableOriginalConstructor() ->getMock(); $tokenMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) ->disableOriginalConstructor() ->getMock(); - $jsonMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Json::class) - ->disableOriginalConstructor() - ->getMock(); + $this->formKeyValidator->method('validate') + ->willReturn(true); $this->sessionManagerMock->expects($this->atLeastOnce()) ->method('getQuote') ->willReturn($quoteMock); @@ -147,15 +163,9 @@ public function testExecuteSuccess() ['securetoken', null, $secureToken] ] ); - $this->resultJsonFactoryMock->expects($this->once()) - ->method('create') - ->willReturn($jsonMock); - $jsonMock->expects($this->once()) - ->method('setData') - ->with($resultExpectation) - ->willReturnSelf(); + $jsonResult = $this->getJsonResult($resultExpectation); - $this->assertEquals($jsonMock, $this->controller->execute()); + $this->assertEquals($jsonResult, $this->controller->execute()); } public function testExecuteTokenRequestException() @@ -167,13 +177,11 @@ public function testExecuteTokenRequestException() 'error_messages' => __('Your payment has been declined. Please try again.') ]; - $quoteMock = $this->getMockBuilder(\Magento\Quote\Model\Quote::class) - ->disableOriginalConstructor() - ->getMock(); - $jsonMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Json::class) + $quoteMock = $this->getMockBuilder(Quote::class) ->disableOriginalConstructor() ->getMock(); - + $this->formKeyValidator->method('validate') + ->willReturn(true); $this->sessionManagerMock->expects($this->atLeastOnce()) ->method('getQuote') ->willReturn($quoteMock); @@ -187,18 +195,21 @@ public function testExecuteTokenRequestException() ->method('requestToken') ->with($quoteMock) ->willThrowException(new \Exception()); - $this->resultJsonFactoryMock->expects($this->once()) - ->method('create') - ->willReturn($jsonMock); - $jsonMock->expects($this->once()) - ->method('setData') - ->with($resultExpectation) - ->willReturnSelf(); - $this->assertEquals($jsonMock, $this->controller->execute()); + $jsonResult = $this->getJsonResult($resultExpectation); + + $this->assertEquals($jsonResult, $this->controller->execute()); } - public function testExecuteEmptyQuoteError() + /** + * Tests error generation. + * + * @param Quote|null $quote + * @param bool $isValidToken + * @return void + * @dataProvider executeErrorDataProvider + */ + public function testExecuteError($quote, bool $isValidToken): void { $resultExpectation = [ 'success' => false, @@ -206,22 +217,51 @@ public function testExecuteEmptyQuoteError() 'error_messages' => __('Your payment has been declined. Please try again.') ]; - $quoteMock = null; - $jsonMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Json::class) + $this->sessionManagerMock->expects($this->atLeastOnce()) + ->method('getQuote') + ->willReturn($quote); + $this->formKeyValidator->method('validate') + ->willReturn($isValidToken); + + $jsonResult = $this->getJsonResult($resultExpectation); + + $this->assertEquals($jsonResult, $this->controller->execute()); + } + + /** + * @return array + */ + public function executeErrorDataProvider() + { + $quote = $this->getMockBuilder(Quote::class) ->disableOriginalConstructor() ->getMock(); - $this->sessionManagerMock->expects($this->atLeastOnce()) - ->method('getQuote') - ->willReturn($quoteMock); - $this->resultJsonFactoryMock->expects($this->once()) - ->method('create') - ->willReturn($jsonMock); + return [ + 'empty quote' => [null, true], + 'invalid CSRF token' => [$quote, false] + ]; + } + + /** + * Returns json result. + * + * @param array $result + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getJsonResult(array $result): \PHPUnit_Framework_MockObject_MockObject + { + $jsonMock = $this->getMockBuilder(Json::class) + ->disableOriginalConstructor() + ->getMock(); $jsonMock->expects($this->once()) ->method('setData') - ->with($resultExpectation) + ->with($result) ->willReturnSelf(); + $this->resultJsonFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($jsonMock); - $this->assertEquals($jsonMock, $this->controller->execute()); + return $jsonMock; } } From c6f87fa59eb2ddf96ddd6ea884415a01d4baec03 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Mon, 4 Mar 2019 09:56:49 +0200 Subject: [PATCH 0846/1295] MAGETWO-96062: Wrong special price displayed in product search results --- .../Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml index 730d3d6a5a185..5e3d25b47a6e7 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml @@ -92,6 +92,7 @@ <argument name="product" defaultValue="_defaultProduct"/> <argument name="quantity" type="string" defaultValue="1"/> </arguments> + <waitForElementVisible selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="waitForAddProductVisible"/> <click selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="clickAddProducts"/> <fillField selector="{{AdminOrderFormItemsSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillSkuFilter"/> <click selector="{{AdminOrderFormItemsSection.search}}" stepKey="clickSearch"/> From 42d7504d1def753cf8bedfdac9b1b4a7d165aab8 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Mon, 4 Mar 2019 10:11:49 +0200 Subject: [PATCH 0847/1295] MAGETWO-94190: [Magento Cloud] Error when saving Bundle product --- .../Unit/Model/Product/SaveHandlerTest.php | 275 ++++++++++++++++++ 1 file changed, 275 insertions(+) create mode 100644 app/code/Magento/Bundle/Test/Unit/Model/Product/SaveHandlerTest.php diff --git a/app/code/Magento/Bundle/Test/Unit/Model/Product/SaveHandlerTest.php b/app/code/Magento/Bundle/Test/Unit/Model/Product/SaveHandlerTest.php new file mode 100644 index 0000000000000..94e5b72b10e3c --- /dev/null +++ b/app/code/Magento/Bundle/Test/Unit/Model/Product/SaveHandlerTest.php @@ -0,0 +1,275 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Bundle\Test\Unit\Model\Product; + +use Magento\Bundle\Api\Data\LinkInterface; +use Magento\Bundle\Api\Data\OptionInterface; +use Magento\Bundle\Api\ProductLinkManagementInterface; +use Magento\Bundle\Api\ProductOptionRepositoryInterface; +use Magento\Bundle\Model\Product\SaveHandler; +use Magento\Catalog\Api\Data\ProductExtensionInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Framework\EntityManager\EntityMetadataInterface; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +class SaveHandlerTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + /** + * @var ProductInterface|MockObject + */ + private $productMock; + + /** + * @var ProductExtensionInterface|MockObject + */ + private $productExtensionMock; + + /** + * @var OptionInterface|MockObject + */ + private $optionMock; + + /** + * @var ProductOptionRepositoryInterface|MockObject + */ + private $optionRepositoryMock; + + /** + * @var ProductLinkManagementInterface|MockObject + */ + private $productLinkManagementMock; + + /** + * @var LinkInterface|MockObject + */ + private $linkMock; + + /** + * @var MetadataPool|MockObject + */ + private $metadataPoolMock; + + /** + * @var EntityMetadataInterface|MockObject + */ + private $metadataMock; + + /** + * @var SaveHandler + */ + private $saveHandler; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = new ObjectManager($this); + + $this->productMock = $this->getMockBuilder(ProductInterface::class) + ->setMethods( + [ + 'getExtensionAttributes', + 'getCopyFromView', + 'getData', + 'getTypeId', + 'getSku', + ] + ) + ->getMockForAbstractClass(); + $this->productExtensionMock = $this->getMockBuilder(ProductExtensionInterface::class) + ->setMethods(['getBundleProductOptions']) + ->getMockForAbstractClass(); + $this->optionMock = $this->getMockBuilder(OptionInterface::class) + ->setMethods( + [ + 'setParentId', + 'getId', + 'getOptionId', + ] + ) + ->getMockForAbstractClass(); + $this->optionRepositoryMock = $this->createMock(ProductOptionRepositoryInterface::class); + $this->productLinkManagementMock = $this->createMock(ProductLinkManagementInterface::class); + $this->linkMock = $this->createMock(LinkInterface::class); + $this->metadataPoolMock = $this->createMock(MetadataPool::class); + $this->metadataMock = $this->createMock(EntityMetadataInterface::class); + $this->metadataPoolMock->expects($this->any()) + ->method('getMetadata') + ->willReturn($this->metadataMock); + + $this->saveHandler = $this->objectManager->getObject( + SaveHandler::class, + [ + 'optionRepository' => $this->optionRepositoryMock, + 'productLinkManagement' => $this->productLinkManagementMock, + 'metadataPool' => $this->metadataPoolMock, + ] + ); + } + + /** + * @return void + */ + public function testExecuteWithInvalidProductType() + { + $productType = 'simple'; + + $this->productMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->productExtensionMock); + $this->productExtensionMock->expects($this->once()) + ->method('getBundleProductOptions') + ->willReturn([]); + $this->productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn($productType); + + $entity = $this->saveHandler->execute($this->productMock); + $this->assertSame($this->productMock, $entity); + } + + /** + * @return void + */ + public function testExecuteWithoutExistingOption() + { + $productType = 'bundle'; + $productSku = 'product-sku'; + $optionId = ''; + + $this->productMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->productExtensionMock); + $this->productExtensionMock->expects($this->once()) + ->method('getBundleProductOptions') + ->willReturn([$this->optionMock]); + + $this->productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn($productType); + + $this->productMock->expects($this->once()) + ->method('getSku') + ->willReturn($productSku); + $this->optionRepositoryMock->expects($this->once()) + ->method('getList') + ->with($productSku) + ->willReturn([]); + + $this->optionMock->expects($this->any()) + ->method('getOptionId') + ->willReturn($optionId); + + $this->productMock->expects($this->once()) + ->method('getCopyFromView') + ->willReturn(false); + + $this->optionMock->expects($this->never())->method('setOptionId'); + $this->optionRepositoryMock->expects($this->once()) + ->method('save') + ->with($this->productMock, $this->optionMock) + ->willReturn($optionId); + + $this->saveHandler->execute($this->productMock); + } + + /** + * @return void + */ + public function testExecuteWithExistingOption() + { + $productType = 'bundle'; + $productSku = 'product-sku'; + $productLinkSku = 'product-link-sku'; + $linkField = 'entity_id'; + $parentId = 1; + $existingOptionId = 1; + $optionId = 2; + + /** @var OptionInterface|MockObject $existingOptionMock */ + $existingOptionMock = $this->getMockBuilder(OptionInterface::class) + ->setMethods(['getOptionId']) + ->getMockForAbstractClass(); + + $this->productMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->productExtensionMock); + $this->productExtensionMock->expects($this->once()) + ->method('getBundleProductOptions') + ->willReturn([$this->optionMock]); + $this->productMock->expects($this->once()) + ->method('getTypeId') + ->willReturn($productType); + + $this->productMock->expects($this->exactly(3)) + ->method('getSku') + ->willReturn($productSku); + $this->optionRepositoryMock->expects($this->once()) + ->method('getList') + ->with($productSku) + ->willReturn([$existingOptionMock]); + + $existingOptionMock->expects($this->any()) + ->method('getOptionId') + ->willReturn($existingOptionId); + $this->optionMock->expects($this->any()) + ->method('getOptionId') + ->willReturn($optionId); + + $this->productMock->expects($this->once()) + ->method('getCopyFromView') + ->willReturn(false); + $this->metadataMock->expects($this->once()) + ->method('getLinkField') + ->willReturn($linkField); + $this->productMock->expects($this->once()) + ->method('getData') + ->with($linkField) + ->willReturn($parentId); + + $this->optionRepositoryMock->expects($this->once()) + ->method('get') + ->with($productSku, $existingOptionId) + ->willReturn($this->optionMock); + $this->optionMock->expects($this->once()) + ->method('setParentId') + ->with($parentId) + ->willReturnSelf(); + $this->optionMock->expects($this->once()) + ->method('getProductLinks') + ->willReturn([$this->linkMock]); + $this->linkMock->expects($this->once()) + ->method('getSku') + ->willReturn($productLinkSku); + + $this->optionMock->expects($this->any()) + ->method('getId') + ->willReturn($existingOptionId); + $this->productLinkManagementMock->expects($this->once()) + ->method('removeChild') + ->with($productSku, $existingOptionId, $productLinkSku) + ->willReturn(true); + $this->optionRepositoryMock->expects($this->once()) + ->method('delete') + ->with($this->optionMock) + ->willReturn(true); + + $this->optionRepositoryMock->expects($this->once()) + ->method('save') + ->with($this->productMock, $this->optionMock) + ->willReturn($optionId); + + $this->saveHandler->execute($this->productMock); + } +} From 79429edd3f660bac9c26e09afab5abefe6c8a178 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Mon, 4 Mar 2019 10:31:10 +0200 Subject: [PATCH 0848/1295] Fix functional tests. --- app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml index c4b1c14d09890..078d210d9cb61 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml @@ -21,7 +21,7 @@ <data key="firstname">John</data> <data key="lastname">Doe</data> <data key="middlename">S</data> - <data key="fullname">John Doe</data>s + <data key="fullname">John Doe</data> <data key="password">pwdTest123!</data> <data key="prefix">Mr</data> <data key="suffix">Sr</data> From f3b9e7096d3bd27245ea43b6642f6a6a0117aeb8 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Mon, 4 Mar 2019 11:36:39 +0200 Subject: [PATCH 0849/1295] MAGETWO-96062: Wrong special price displayed in product search results --- .../Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml index 34a0e5a10b81d..49715cf2c28e9 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml @@ -9,7 +9,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminSwitchBaseActionGroup"> <arguments> - <argument name="scopeName" type="string" defaultValue="customStore.name"/> + <argument name="scopeName" defaultValue="customStore.name"/> </arguments> <scrollToTopOfPage stepKey="scrollToTop"/> <click selector="{{AdminMainActionsSection.storeViewDropdown}}" stepKey="clickScopeSwitchDropdown"/> From 96c83295a26dd07e1819e310d2d31fef25fadec9 Mon Sep 17 00:00:00 2001 From: Vivek Kumar <vivekkumar@cedcommerce.com> Date: Thu, 17 Jan 2019 17:53:04 +0530 Subject: [PATCH 0850/1295] Issue Fixed: #8086: Multiline admin field is broken --- app/code/Magento/Config/Model/Config.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/code/Magento/Config/Model/Config.php b/app/code/Magento/Config/Model/Config.php index b1074e92cc949..bec44e9d55757 100644 --- a/app/code/Magento/Config/Model/Config.php +++ b/app/code/Magento/Config/Model/Config.php @@ -424,6 +424,11 @@ protected function _processGroup( if (!isset($fieldData['value'])) { $fieldData['value'] = null; } + + if ($field->getType() == 'multiline' && is_array($fieldData['value'])) { + $fieldData['value'] = trim(implode(PHP_EOL, $fieldData['value'])); + } + $data = [ 'field' => $fieldId, 'groups' => $groups, From 4a477ce5d1ca2b4bca8f31eb57069b610df1b0a3 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Mon, 4 Mar 2019 13:23:29 +0200 Subject: [PATCH 0851/1295] MAGETWO-70467: CMS Block status is not staged --- .../Magento/Cms/Test/Mftf/Page/AdminCmsBlockEditPage.xml | 2 +- .../Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsBlockEditPage.xml b/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsBlockEditPage.xml index e3584427f4767..328dc156a38fb 100644 --- a/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsBlockEditPage.xml +++ b/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsBlockEditPage.xml @@ -8,7 +8,7 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="AdminCmsBlockEditPage" url="/cms/block/edit/id/{{var1}}" area="admin" module="Magento_Cms" parameterized="true"> + <page name="AdminCmsBlockEditPage" url="/cms/block/edit/block_id/{{blockId}}/" area="admin" module="Magento_Cms" parameterized="true"> <section name="AdminCmsBlockContentSection" /> <section name="AdminMediaGallerySection" /> </page> diff --git a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml index 9ae0340570e1d..b3013e8af7e54 100644 --- a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml +++ b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml @@ -78,7 +78,6 @@ <click selector="{{AdminDataGridTableSection.row('1')}}" stepKey="clickSearchedBlock"/> <waitForPageLoad stepKey="wait"/> <click selector="{{AdminMainActionsSection.save}}" stepKey="saveWidget"/> - <waitForPageLoad stepKey="waitForSaving"/> <see selector="{{AdminMessagesSection.success}}" userInput="The widget instance has been saved." stepKey="seeSuccessMessage"/> </actionGroup> @@ -87,11 +86,11 @@ <argument name="product"/> </arguments> <selectOption selector="{{AdminNewWidgetSection.selectTemplate}}" userInput="{{widget.template}}" after="waitForPageLoad" stepKey="setTemplate"/> - <waitForAjaxLoad after="setTemplate" stepKey="waitForPageLoad2"/> + <waitForAjaxLoad stepKey="waitForPageLoad2"/> <click selector="{{AdminNewWidgetSection.selectProduct}}" after="clickWidgetOptions" stepKey="clickSelectProduct"/> - <fillField selector="{{AdminNewWidgetSelectProductPopupSection.filterBySku}}" userInput="{{product.sku}}" after="clickSelectProduct" stepKey="fillProductNameInFilter"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" after="fillProductNameInFilter" stepKey="applyFilter"/> - <click selector="{{AdminNewWidgetSelectProductPopupSection.firstRow}}" after="applyFilter" stepKey="selectProduct"/> + <fillField selector="{{AdminNewWidgetSelectProductPopupSection.filterBySku}}" userInput="{{product.sku}}" stepKey="fillProductNameInFilter"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="applyFilter"/> + <click selector="{{AdminNewWidgetSelectProductPopupSection.firstRow}}" stepKey="selectProduct"/> <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveWidget"/> <see selector="{{AdminMessagesSection.successMessage}}" userInput="The widget instance has been saved" stepKey="seeSuccess"/> </actionGroup> From 09d34fc4f5369b7ddea1590c9124e8d7ab8924e6 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Mon, 4 Mar 2019 13:37:51 +0200 Subject: [PATCH 0852/1295] MAGETWO-96062: Wrong special price displayed in product search results --- app/code/Magento/Store/Test/Mftf/Data/StoreData.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml index 5877ed383ae16..d008225d1c84b 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml @@ -102,4 +102,9 @@ <data key="store_type">group</data> <requiredEntity type="storeGroup">customStoreGroup</requiredEntity> </entity> + <entity name="AllStoreView" type="store"> + <data key="name">All Store Views</data> + <data key="code">allstoreviews</data> + <data key="is_active">1</data> + </entity> </entities> From a0f5f96d43fe7b11719ffee342251cd854669231 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Mon, 4 Mar 2019 16:25:05 +0200 Subject: [PATCH 0853/1295] MAGETWO-98527: Config:set command failed for path bigger than 3 depth --- app/code/Magento/Config/Model/Config.php | 25 +++--- .../Config/Test/Unit/Model/ConfigTest.php | 76 ++++++++++++++----- 2 files changed, 71 insertions(+), 30 deletions(-) diff --git a/app/code/Magento/Config/Model/Config.php b/app/code/Magento/Config/Model/Config.php index b1074e92cc949..6bdf9af5074f6 100644 --- a/app/code/Magento/Config/Model/Config.php +++ b/app/code/Magento/Config/Model/Config.php @@ -520,24 +520,29 @@ public function setDataByPath($path, $value) if ($path === '') { throw new \UnexpectedValueException('Path must not be empty'); } + $pathParts = explode('/', $path); $keyDepth = count($pathParts); - if ($keyDepth !== 3) { + if ($keyDepth < 3) { throw new \UnexpectedValueException( - "Allowed depth of configuration is 3 (<section>/<group>/<field>). Your configuration depth is " - . $keyDepth . " for path '$path'" + 'Minimal depth of configuration is 3. Your configuration depth is ' . $keyDepth ); } + + $section = array_shift($pathParts); $data = [ - 'section' => $pathParts[0], - 'groups' => [ - $pathParts[1] => [ - 'fields' => [ - $pathParts[2] => ['value' => $value], - ], - ], + 'fields' => [ + array_pop($pathParts) => ['value' => $value], ], ]; + while ($pathParts) { + $data = [ + 'groups' => [ + array_pop($pathParts) => $data, + ], + ]; + } + $data['section'] = $section; $this->addData($data); } diff --git a/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php b/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php index bb772f51c0dac..a731be96af963 100644 --- a/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/ConfigTest.php @@ -355,22 +355,60 @@ public function testSaveToCheckScopeDataSet() $this->model->save(); } - public function testSetDataByPath() + /** + * @param string $path + * @param string $value + * @param string $section + * @param array $groups + * @return void + * @dataProvider setDataByPathDataProvider + */ + public function testSetDataByPath(string $path, string $value, string $section, array $groups) { - $value = 'value'; - $path = '<section>/<group>/<field>'; $this->model->setDataByPath($path, $value); - $expected = [ - 'section' => '<section>', - 'groups' => [ - '<group>' => [ - 'fields' => [ - '<field>' => ['value' => $value], + $this->assertEquals($section, $this->model->getData('section')); + $this->assertEquals($groups, $this->model->getData('groups')); + } + + /** + * @return array + */ + public function setDataByPathDataProvider(): array + { + return [ + 'depth 3' => [ + 'a/b/c', + 'value1', + 'a', + [ + 'b' => [ + 'fields' => [ + 'c' => ['value' => 'value1'], + ], + ], + ], + ], + 'depth 5' => [ + 'a/b/c/d/e', + 'value1', + 'a', + [ + 'b' => [ + 'groups' => [ + 'c' => [ + 'groups' => [ + 'd' => [ + 'fields' => [ + 'e' => ['value' => 'value1'], + ], + ], + ], + ], + ], ], ], ], ]; - $this->assertSame($expected, $this->model->getData()); } /** @@ -384,14 +422,14 @@ public function testSetDataByPathEmpty() /** * @param string $path - * @param string $expectedException - * + * @return void * @dataProvider setDataByPathWrongDepthDataProvider */ - public function testSetDataByPathWrongDepth($path, $expectedException) + public function testSetDataByPathWrongDepth(string $path) { - $expectedException = 'Allowed depth of configuration is 3 (<section>/<group>/<field>). ' . $expectedException; - $this->expectException('\UnexpectedValueException'); + $currentDepth = count(explode('/', $path)); + $expectedException = 'Minimal depth of configuration is 3. Your configuration depth is ' . $currentDepth; + $this->expectException(\UnexpectedValueException::class); $this->expectExceptionMessage($expectedException); $value = 'value'; $this->model->setDataByPath($path, $value); @@ -400,13 +438,11 @@ public function testSetDataByPathWrongDepth($path, $expectedException) /** * @return array */ - public function setDataByPathWrongDepthDataProvider() + public function setDataByPathWrongDepthDataProvider(): array { return [ - 'depth 2' => ['section/group', "Your configuration depth is 2 for path 'section/group'"], - 'depth 1' => ['section', "Your configuration depth is 1 for path 'section'"], - 'depth 4' => ['section/group/field/sub-field', "Your configuration depth is 4 for path" - . " 'section/group/field/sub-field'", ], + 'depth 2' => ['section/group'], + 'depth 1' => ['section'], ]; } } From d8110c23e4f66ce819b4be90b12a1e4eb190b178 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Mon, 4 Mar 2019 16:50:27 +0200 Subject: [PATCH 0854/1295] ENGCOM-4000: Crash fix. --- .../Model/Condition/Product/AbstractProduct.php | 8 -------- .../Block/Product/ProductListTest.php | 15 --------------- 2 files changed, 23 deletions(-) diff --git a/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php b/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php index bedac8c1108e5..a428794b41a0f 100644 --- a/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php +++ b/app/code/Magento/Rule/Model/Condition/Product/AbstractProduct.php @@ -137,7 +137,6 @@ public function getDefaultOperatorInputByType() */ $this->_defaultOperatorInputByType['category'] = ['==', '!=', '{}', '!{}', '()', '!()']; $this->_arrayInputTypes[] = 'category'; - $this->_defaultOperatorInputByType['sku'] = ['==', '!=', '{}', '!{}', '()', '!()']; } return $this->_defaultOperatorInputByType; } @@ -383,9 +382,6 @@ public function getInputType() if ($this->getAttributeObject()->getAttributeCode() == 'category_ids') { return 'category'; } - if ($this->getAttributeObject()->getAttributeCode() == 'sku') { - return 'sku'; - } switch ($this->getAttributeObject()->getFrontendInput()) { case 'select': return 'select'; @@ -610,10 +606,6 @@ public function getBindArgumentValue() $this->getValueParsed() )->__toString() ); - } elseif ($this->getAttribute() === 'sku') { - $value = $this->getData('value'); - $value = preg_split('#\s*[,;]\s*#', $value, null, PREG_SPLIT_NO_EMPTY); - $this->setValueParsed($value); } return parent::getBindArgumentValue(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php b/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php index 4251e01f4299a..5774f1cf76ae9 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php @@ -73,21 +73,6 @@ public function testCreateCollection() $this->performAssertions(2); } - /** - * Test product list widget can process condition with multiple product sku. - * - * @magentoDbIsolation disabled - * @magentoDataFixture Magento/Catalog/_files/multiple_products.php - */ - public function testCreateCollectionWithMultipleSkuCondition() - { - $encodedConditions = '^[`1`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Combine`,' . - '`aggregator`:`all`,`value`:`1`,`new_child`:``^],`1--1`:^[`type`:`Magento||CatalogWidget||Model||Rule|' . - '|Condition||Product`,`attribute`:`sku`,`operator`:`()`,`value`:`simple1, simple2`^]^]'; - $this->block->setData('conditions_encoded', $encodedConditions); - $this->performAssertions(2); - } - /** * Test product list widget can process condition with dropdown type of attribute * From 41e694f19ab1c66019abad1910bb9fe8fb0d9a3a Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Mon, 4 Mar 2019 17:14:45 +0200 Subject: [PATCH 0855/1295] MAGETWO-94190: [Magento Cloud] Error when saving Bundle product --- .../Bundle/Test/Unit/Model/Product/SaveHandlerTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/Bundle/Test/Unit/Model/Product/SaveHandlerTest.php b/app/code/Magento/Bundle/Test/Unit/Model/Product/SaveHandlerTest.php index 94e5b72b10e3c..a5b9fb4e1b268 100644 --- a/app/code/Magento/Bundle/Test/Unit/Model/Product/SaveHandlerTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Model/Product/SaveHandlerTest.php @@ -18,12 +18,16 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use PHPUnit_Framework_MockObject_MockObject as MockObject; +/** + * Unit tests for \Magento\Bundle\Model\Product\SaveHandler class. + */ class SaveHandlerTest extends \PHPUnit\Framework\TestCase { /** * @var ObjectManager */ private $objectManager; + /** * @var ProductInterface|MockObject */ From 134e96d9b3362f17e631c9cdb7be7c2f29989dd7 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Mon, 4 Mar 2019 13:29:10 -0600 Subject: [PATCH 0856/1295] MAGETWO-98431: Paypal securetoken/securetokenId can be compromised - added token based validation --- .../Paypal/Controller/Transparent/RequestSecureToken.php | 1 - .../Test/Unit/Controller/Transparent/RequestSecureTokenTest.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php b/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php index 09b1e9faccf2f..43ea8c0ac0127 100644 --- a/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php +++ b/app/code/Magento/Paypal/Controller/Transparent/RequestSecureToken.php @@ -75,7 +75,6 @@ public function __construct( Transparent $transparent, SessionManagerInterface $sessionInterface = null, Validator $formKeyValidator = null - ) { $this->resultJsonFactory = $resultJsonFactory; $this->sessionTransparent = $sessionTransparent; diff --git a/app/code/Magento/Paypal/Test/Unit/Controller/Transparent/RequestSecureTokenTest.php b/app/code/Magento/Paypal/Test/Unit/Controller/Transparent/RequestSecureTokenTest.php index 4b8cb98936463..dc90d49a05706 100644 --- a/app/code/Magento/Paypal/Test/Unit/Controller/Transparent/RequestSecureTokenTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Controller/Transparent/RequestSecureTokenTest.php @@ -209,7 +209,7 @@ public function testExecuteTokenRequestException() * @return void * @dataProvider executeErrorDataProvider */ - public function testExecuteError($quote, bool $isValidToken): void + public function testExecuteError($quote, bool $isValidToken) { $resultExpectation = [ 'success' => false, From e0e5c402a22cb9e4f49776cfb3ad524f53c3b1fc Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Mon, 4 Mar 2019 14:26:22 -0600 Subject: [PATCH 0857/1295] MAGETWO-96137: Transfer Cart Line Items and Transfer Shipping Options do not work for PayPal - Sets shipping method on order review step according to shipping method selected on PayPal page --- app/code/Magento/Paypal/Model/Express/Checkout.php | 1 + .../Magento/Paypal/Model/Express/CheckoutTest.php | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Paypal/Model/Express/Checkout.php b/app/code/Magento/Paypal/Model/Express/Checkout.php index f0b86588f1cfa..69e821910d84c 100644 --- a/app/code/Magento/Paypal/Model/Express/Checkout.php +++ b/app/code/Magento/Paypal/Model/Express/Checkout.php @@ -1070,6 +1070,7 @@ protected static function cmpShippingOptions(DataObject $option1, DataObject $op */ protected function _matchShippingMethodCode(Address $address, $selectedCode): string { + $address->collectShippingRates(); $options = $this->_prepareShippingOptions($address, false); foreach ($options as $option) { if ($selectedCode === $option['code'] // the proper case as outlined in documentation diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php index eb80da6d21b19..ee05e402bc689 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php +++ b/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php @@ -72,7 +72,7 @@ protected function setUp() $this->api = $this->getMockBuilder(Nvp::class) ->disableOriginalConstructor() - ->setMethods(['call', 'getExportedShippingAddress', 'getExportedBillingAddress']) + ->setMethods(['call', 'getExportedShippingAddress', 'getExportedBillingAddress', 'getShippingRateCode']) ->getMock(); $this->api->expects($this->any()) @@ -303,6 +303,7 @@ public function testReturnFromPaypal() public function testReturnFromPaypalButton() { $quote = $this->getFixtureQuote(); + $quote->getShippingAddress()->setShippingMethod(''); $this->prepareCheckoutModel($quote); $quote->getPayment()->setAdditionalInformation(Checkout::PAYMENT_INFO_BUTTON, 1); @@ -318,6 +319,8 @@ public function testReturnFromPaypalButton() $this->assertEquals($exportedShippingData['telephone'], $shippingAddress->getTelephone()); $this->assertEquals($exportedShippingData['email'], $shippingAddress->getEmail()); + $this->assertEquals('flatrate_flatrate', $shippingAddress->getShippingMethod()); + $this->assertEquals([$exportedShippingData['street']], $billingAddress->getStreet()); $this->assertEquals($exportedShippingData['firstname'], $billingAddress->getFirstname()); $this->assertEquals($exportedShippingData['city'], $billingAddress->getCity()); @@ -512,6 +515,9 @@ private function prepareCheckoutModel(Quote $quote, $prefix = '') $this->api->method('getExportedShippingAddress') ->will($this->returnValue($exportedShippingAddress)); + $this->api->method('getShippingRateCode') + ->willReturn('flatrate_flatrate Flat Rate - Fixed'); + $this->paypalInfo->method('importToPayment') ->with($this->api, $quote->getPayment()); } From 4c8571e436006d16698596684d1c5ae29e779209 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Mon, 4 Mar 2019 14:28:51 -0600 Subject: [PATCH 0858/1295] ENGCOM-4389: Adjust module naming convention to be compatible with 2.3.1 --- .../static/testsuite/Magento/Test/Integrity/ComposerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php index 6fc84486c626b..2f1ab7a75bc83 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php @@ -452,7 +452,7 @@ private function convertModuleToPackageName($moduleName) { list($vendor, $name) = explode('_', $moduleName, 2); $package = 'module'; - foreach (preg_split('/([A-Z][a-z\d]+)/', $name, -1, PREG_SPLIT_DELIM_CAPTURE) as $chunk) { + foreach (preg_split('/([A-Z\d][a-z]*)/', $name, -1, PREG_SPLIT_DELIM_CAPTURE) as $chunk) { $package .= $chunk ? "-{$chunk}" : ''; } return strtolower("{$vendor}/{$package}"); From 080743fba05fdcc4cfb98fd9bcdc15e39d7e02d9 Mon Sep 17 00:00:00 2001 From: Shikha Mishra <shikhamishra@cedcoss.com> Date: Mon, 25 Feb 2019 22:38:49 +0530 Subject: [PATCH 0859/1295] Fixed #21425 Date design change show not correctly value in backend --- .../Magento/Backend/Block/System/Design/Edit/Tab/General.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Backend/Block/System/Design/Edit/Tab/General.php b/app/code/Magento/Backend/Block/System/Design/Edit/Tab/General.php index 5a09e1f17f617..3fe187e6e7694 100644 --- a/app/code/Magento/Backend/Block/System/Design/Edit/Tab/General.php +++ b/app/code/Magento/Backend/Block/System/Design/Edit/Tab/General.php @@ -90,7 +90,7 @@ protected function _prepareForm() ] ); - $dateFormat = $this->_localeDate->getDateFormat(\IntlDateFormatter::SHORT); + $dateFormat = $this->_localeDate->getDateFormatWithLongYear(); $fieldset->addField( 'date_from', 'date', From 2b41017794d74966ed2df9932d7d1d33518475f1 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Thu, 28 Feb 2019 10:36:58 +0200 Subject: [PATCH 0860/1295] Fix static tests. --- .../Magento/Backend/Block/System/Design/Edit/Tab/General.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Backend/Block/System/Design/Edit/Tab/General.php b/app/code/Magento/Backend/Block/System/Design/Edit/Tab/General.php index 3fe187e6e7694..6004d08b4b738 100644 --- a/app/code/Magento/Backend/Block/System/Design/Edit/Tab/General.php +++ b/app/code/Magento/Backend/Block/System/Design/Edit/Tab/General.php @@ -6,6 +6,9 @@ namespace Magento\Backend\Block\System\Design\Edit\Tab; +/** + * General system tab block. + */ class General extends \Magento\Backend\Block\Widget\Form\Generic { /** From b169fe3f94c7107bb9f88380b530d00a812f765a Mon Sep 17 00:00:00 2001 From: Jitheesh <jitheeshvo@gmail.com> Date: Wed, 20 Feb 2019 16:14:06 +0530 Subject: [PATCH 0861/1295] Checkout Page Cancel button is not working #21327 - hide cancel button if billing address is not available on current quote --- .../Checkout/view/frontend/web/js/view/billing-address.js | 6 ++++++ .../view/frontend/web/template/billing-address.html | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js b/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js index 6f9a1a46826da..121a94a14852f 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js @@ -201,6 +201,12 @@ function ( this.isAddressDetailsVisible(true); } }, + /** + * Manage cancel button visibility + */ + canUseCancelBillingAddress: ko.computed(function () { + return quote.billingAddress() || lastSelectedBillingAddress; + }), /** * Restore billing address diff --git a/app/code/Magento/Checkout/view/frontend/web/template/billing-address.html b/app/code/Magento/Checkout/view/frontend/web/template/billing-address.html index 5f735fbb4daa9..63edb5057b933 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/billing-address.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/billing-address.html @@ -22,7 +22,7 @@ <button class="action action-update" type="button" data-bind="click: updateAddress"> <span data-bind="i18n: 'Update'"></span> </button> - <button class="action action-cancel" type="button" data-bind="click: cancelAddressEdit"> + <button class="action action-cancel" type="button" data-bind="click: cancelAddressEdit, visible: canUseCancelBillingAddress()"> <span data-bind="i18n: 'Cancel'"></span> </button> </div> From b2d04ca9882e660cf60de599b1446620f5f5ae91 Mon Sep 17 00:00:00 2001 From: Jitheesh <jitheeshvo@gmail.com> Date: Thu, 21 Feb 2019 10:28:32 +0530 Subject: [PATCH 0862/1295] Checkout Page Cancel button is not working #21327 - CR fix --- .../Checkout/view/frontend/web/js/view/billing-address.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js b/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js index 121a94a14852f..4ea6fb5e5df75 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js @@ -201,6 +201,7 @@ function ( this.isAddressDetailsVisible(true); } }, + /** * Manage cancel button visibility */ From b4af1319de272df127c3a4c0945e871f258f91ab Mon Sep 17 00:00:00 2001 From: Jitheesh <jitheeshvo@gmail.com> Date: Fri, 22 Feb 2019 11:21:44 +0530 Subject: [PATCH 0863/1295] Checkout Page Cancel button is not working - Code style issues fixed --- .../Checkout/view/frontend/web/js/view/billing-address.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js b/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js index 4ea6fb5e5df75..d68b0682eb511 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js @@ -201,7 +201,7 @@ function ( this.isAddressDetailsVisible(true); } }, - + /** * Manage cancel button visibility */ From ce1c1a2e8f2342efb287934066e70587fe16c30d Mon Sep 17 00:00:00 2001 From: Nick de Kleijn <nick.dekleijn@gmail.com> Date: Tue, 15 Jan 2019 16:39:08 +0100 Subject: [PATCH 0864/1295] #20310 change product_price_value based on tax setting --- .../Magento/Tax/Plugin/Checkout/CustomerData/Cart.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/code/Magento/Tax/Plugin/Checkout/CustomerData/Cart.php b/app/code/Magento/Tax/Plugin/Checkout/CustomerData/Cart.php index 87f65ef311ac2..c1b495d4ecf98 100644 --- a/app/code/Magento/Tax/Plugin/Checkout/CustomerData/Cart.php +++ b/app/code/Magento/Tax/Plugin/Checkout/CustomerData/Cart.php @@ -68,6 +68,14 @@ public function afterGetSectionData(\Magento\Checkout\CustomerData\Cart $subject $this->itemPriceRenderer->setItem($item); $this->itemPriceRenderer->setTemplate('checkout/cart/item/price/sidebar.phtml'); $result['items'][$key]['product_price']=$this->itemPriceRenderer->toHtml(); + if($this->itemPriceRenderer->displayPriceExclTax()) { + $result['items'][$key]['product_price_value'] = $item->getCalculationPrice(); + } elseif ($this->itemPriceRenderer->displayPriceInclTax()) { + $result['items'][$key]['product_price_value'] = $item->getPriceInclTax(); + } elseif ($this->itemPriceRenderer->displayBothPrices()) { + $result['items'][$key]['product_price_value']['incl_tax'] = $item->getPriceInclTax(); + $result['items'][$key]['product_price_value']['excl_tax'] = $item->getCalculationPrice(); + } } } } From c821730fa110d93f92a87feb3e1bc8253c6ba820 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Tue, 26 Feb 2019 14:23:30 +0200 Subject: [PATCH 0865/1295] Fix static and functional tests. --- .../Magento/Tax/Plugin/Checkout/CustomerData/Cart.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Tax/Plugin/Checkout/CustomerData/Cart.php b/app/code/Magento/Tax/Plugin/Checkout/CustomerData/Cart.php index c1b495d4ecf98..208833733ae3f 100644 --- a/app/code/Magento/Tax/Plugin/Checkout/CustomerData/Cart.php +++ b/app/code/Magento/Tax/Plugin/Checkout/CustomerData/Cart.php @@ -6,6 +6,10 @@ namespace Magento\Tax\Plugin\Checkout\CustomerData; +/** + * Process quote items price, considering tax configuration. + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) + */ class Cart { /** @@ -68,11 +72,13 @@ public function afterGetSectionData(\Magento\Checkout\CustomerData\Cart $subject $this->itemPriceRenderer->setItem($item); $this->itemPriceRenderer->setTemplate('checkout/cart/item/price/sidebar.phtml'); $result['items'][$key]['product_price']=$this->itemPriceRenderer->toHtml(); - if($this->itemPriceRenderer->displayPriceExclTax()) { + if ($this->itemPriceRenderer->displayPriceExclTax()) { $result['items'][$key]['product_price_value'] = $item->getCalculationPrice(); } elseif ($this->itemPriceRenderer->displayPriceInclTax()) { $result['items'][$key]['product_price_value'] = $item->getPriceInclTax(); } elseif ($this->itemPriceRenderer->displayBothPrices()) { + //unset product price value in case price already has been set as scalar value. + unset($result['items'][$key]['product_price_value']); $result['items'][$key]['product_price_value']['incl_tax'] = $item->getPriceInclTax(); $result['items'][$key]['product_price_value']['excl_tax'] = $item->getCalculationPrice(); } From eea46883e0941bec6bc71350338160dbe68877da Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Tue, 5 Mar 2019 10:55:53 +0200 Subject: [PATCH 0866/1295] MAGETWO-94190: [Magento Cloud] Error when saving Bundle product --- .../Magento/Bundle/Test/Unit/Model/Product/SaveHandlerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Bundle/Test/Unit/Model/Product/SaveHandlerTest.php b/app/code/Magento/Bundle/Test/Unit/Model/Product/SaveHandlerTest.php index a5b9fb4e1b268..41e6c2c160e0a 100644 --- a/app/code/Magento/Bundle/Test/Unit/Model/Product/SaveHandlerTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Model/Product/SaveHandlerTest.php @@ -150,7 +150,7 @@ public function testExecuteWithoutExistingOption() { $productType = 'bundle'; $productSku = 'product-sku'; - $optionId = ''; + $optionId = null; $this->productMock->expects($this->once()) ->method('getExtensionAttributes') From aca373840ad9db849233f9cce1968ba57132c2cf Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 5 Mar 2019 11:23:57 +0200 Subject: [PATCH 0867/1295] MAGETWO-98527: Config:set command failed for path bigger than 3 depth --- .../Console/Command/ConfigSetCommandTest.php | 21 +++++++++++++++++++ .../Magento/Config/_files/system.xml | 20 ++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Config/_files/system.xml diff --git a/dev/tests/integration/testsuite/Magento/Config/Console/Command/ConfigSetCommandTest.php b/dev/tests/integration/testsuite/Magento/Config/Console/Command/ConfigSetCommandTest.php index a1998c6c89536..782d8dadcc1e8 100644 --- a/dev/tests/integration/testsuite/Magento/Config/Console/Command/ConfigSetCommandTest.php +++ b/dev/tests/integration/testsuite/Magento/Config/Console/Command/ConfigSetCommandTest.php @@ -6,6 +6,8 @@ namespace Magento\Config\Console\Command; use Magento\Config\Model\Config\Backend\Admin\Custom; +use Magento\Config\Model\Config\Structure\Converter; +use Magento\Config\Model\Config\Structure\Data as StructureData; use Magento\Directory\Model\Currency; use Magento\Framework\App\Config\ConfigPathResolver; use Magento\Framework\App\Config\ScopeConfigInterface; @@ -90,6 +92,8 @@ protected function setUp() { Bootstrap::getInstance()->reinitialize(); $this->objectManager = Bootstrap::getObjectManager(); + $this->extendSystemStructure(); + $this->scopeConfig = $this->objectManager->get(ScopeConfigInterface::class); $this->reader = $this->objectManager->get(FileReader::class); $this->filesystem = $this->objectManager->get(Filesystem::class); @@ -122,6 +126,21 @@ protected function tearDown() $this->appConfig->reinit(); } + /** + * Add test system structure to main system structure + * + * @return void + */ + private function extendSystemStructure() + { + $document = new \DOMDocument(); + $document->load(__DIR__ . '/../../_files/system.xml'); + $converter = $this->objectManager->get(Converter::class); + $systemConfig = $converter->convert($document); + $structureData = $this->objectManager->get(StructureData::class); + $structureData->merge($systemConfig); + } + /** * @return array */ @@ -190,6 +209,8 @@ public function runLockDataProvider() ['general/region/display_all', '1'], ['general/region/state_required', 'BR,FR', ScopeInterface::SCOPE_WEBSITE, 'base'], ['admin/security/use_form_key', '0'], + ['general/group/subgroup/field', 'default_value'], + ['general/group/subgroup/field', 'website_value', ScopeInterface::SCOPE_WEBSITE, 'base'], ]; } diff --git a/dev/tests/integration/testsuite/Magento/Config/_files/system.xml b/dev/tests/integration/testsuite/Magento/Config/_files/system.xml new file mode 100644 index 0000000000000..f0063a3c0bf7f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Config/_files/system.xml @@ -0,0 +1,20 @@ +<?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:module:Magento_Config:etc/system_file.xsd"> + <system> + <section id="general"> + <group id="group"> + <group id="subgroup"> + <field id="field" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0"> + <label>Label</label> + </field> + </group> + </group> + </section> + </system> +</config> From 2c956f661caf1a10b1b6e6afea185fd473313fd3 Mon Sep 17 00:00:00 2001 From: Cristiano Casciotti <teknoman84@gmail.com> Date: Tue, 5 Mar 2019 12:02:18 +0100 Subject: [PATCH 0868/1295] Fix for issue #21510 --- app/code/Magento/Indexer/Model/Indexer.php | 2 +- .../Magento/Indexer/Test/Unit/Model/IndexerTest.php | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Indexer/Model/Indexer.php b/app/code/Magento/Indexer/Model/Indexer.php index 4e9b3dbdb8866..afeff3f38d63a 100644 --- a/app/code/Magento/Indexer/Model/Indexer.php +++ b/app/code/Magento/Indexer/Model/Indexer.php @@ -361,7 +361,7 @@ public function getLatestUpdated() return $this->getView()->getUpdated(); } } - return $this->getState()->getUpdated(); + return $this->getState()->getUpdated() ?: ''; } /** diff --git a/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php b/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php index 6b7cc12218990..ae0d721a549bd 100644 --- a/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php +++ b/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php @@ -164,7 +164,12 @@ public function testGetLatestUpdated($getViewIsEnabled, $getViewGetUpdated, $get } } } else { - $this->assertEquals($getStateGetUpdated, $this->model->getLatestUpdated()); + $actualGetLatestUpdated = $this->model->getLatestUpdated(); + $this->assertEquals($getStateGetUpdated, $actualGetLatestUpdated); + + if ($getStateGetUpdated === null) { + $this->assertNotNull($actualGetLatestUpdated); + } } } @@ -182,7 +187,8 @@ public function getLatestUpdatedDataProvider() [true, '', '06-Jan-1944'], [true, '06-Jan-1944', ''], [true, '', ''], - [true, '06-Jan-1944', '05-Jan-1944'] + [true, '06-Jan-1944', '05-Jan-1944'], + [false, null, null], ]; } From 082f4268630601f764ec469e3988cc5b4bef9535 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Tue, 5 Mar 2019 13:45:53 +0200 Subject: [PATCH 0869/1295] MAGETWO-96062: Wrong special price displayed in product search results --- .../Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml | 2 +- app/code/Magento/Store/Test/Mftf/Data/StoreData.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml index 49715cf2c28e9..3ca45a5a378fd 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml @@ -32,6 +32,6 @@ <actionGroup name="AdminSwitchToAllStoreViewActionGroup" extends="AdminSwitchBaseActionGroup"> <click selector="{{AdminMainActionsSection.allStoreViews}}" after="clickScopeSwitchDropdown" stepKey="clickStoreViewByName"/> - <see selector="{{AdminMainActionsSection.storeSwitcher}}" userInput="All Store Views" after="clickStoreViewByName" stepKey="seeNewStoreViewName"/> + <see selector="{{AdminMainActionsSection.storeSwitcher}}" userInput="{{scopeName}}" after="clickStoreViewByName" stepKey="seeNewStoreViewName"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml index d008225d1c84b..61146f186923e 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml @@ -102,7 +102,7 @@ <data key="store_type">group</data> <requiredEntity type="storeGroup">customStoreGroup</requiredEntity> </entity> - <entity name="AllStoreView" type="store"> + <entity name="AllStoreViews" type="store"> <data key="name">All Store Views</data> <data key="code">allstoreviews</data> <data key="is_active">1</data> From d5ed9e89b41c6d407869d4cf00f8293fb5f6e41f Mon Sep 17 00:00:00 2001 From: Cristiano Casciotti <teknoman84@gmail.com> Date: Tue, 5 Mar 2019 14:27:58 +0100 Subject: [PATCH 0870/1295] Changed variable name for codacy quality review --- app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php b/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php index ae0d721a549bd..ca2da9585f934 100644 --- a/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php +++ b/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php @@ -164,11 +164,11 @@ public function testGetLatestUpdated($getViewIsEnabled, $getViewGetUpdated, $get } } } else { - $actualGetLatestUpdated = $this->model->getLatestUpdated(); - $this->assertEquals($getStateGetUpdated, $actualGetLatestUpdated); + $getLatestUpdated = $this->model->getLatestUpdated(); + $this->assertEquals($getStateGetUpdated, $getLatestUpdated); if ($getStateGetUpdated === null) { - $this->assertNotNull($actualGetLatestUpdated); + $this->assertNotNull($getLatestUpdated); } } } From ca72609de5bdd2ff1d74eb5957a8acca125eadba Mon Sep 17 00:00:00 2001 From: NazarKlovanych <nazarn96@gmail.com> Date: Fri, 8 Feb 2019 16:26:58 +0200 Subject: [PATCH 0871/1295] [backport]fix wrong check CustomerMiddleName --- .../Edit/Action/Attribute/Tab/Inventory.php | 39 +++++-------------- .../product/edit/action/inventory.phtml | 9 ++++- 2 files changed, 17 insertions(+), 31 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Inventory.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Inventory.php index d11a6d632657f..edeb7f94c3ae5 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Inventory.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Inventory.php @@ -5,8 +5,6 @@ */ namespace Magento\Catalog\Block\Adminhtml\Product\Edit\Action\Attribute\Tab; -use Magento\Customer\Api\Data\GroupInterface; - /** * Products mass update inventory tab * @@ -31,11 +29,6 @@ class Inventory extends \Magento\Backend\Block\Widget implements \Magento\Backen */ protected $disabledFields = []; - /** - * @var \Magento\Framework\Serialize\SerializerInterface - */ - private $serializer; - /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\CatalogInventory\Model\Source\Backorders $backorders @@ -46,13 +39,10 @@ public function __construct( \Magento\Backend\Block\Template\Context $context, \Magento\CatalogInventory\Model\Source\Backorders $backorders, \Magento\CatalogInventory\Api\StockConfigurationInterface $stockConfiguration, - array $data = [], - \Magento\Framework\Serialize\SerializerInterface $serializer = null + array $data = [] ) { $this->_backorders = $backorders; $this->stockConfiguration = $stockConfiguration; - $this->serializer = $serializer ?? \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\Serialize\SerializerInterface::class); parent::__construct($context, $data); } @@ -80,11 +70,11 @@ public function getFieldSuffix() * Retrieve current store id * * @return int + * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) */ public function getStoreId() { - $storeId = $this->getRequest()->getParam('store'); - return (int) $storeId; + return (int)$this->getRequest()->getParam('store'); } /** @@ -98,21 +88,6 @@ public function getDefaultConfigValue($field) return $this->stockConfiguration->getDefaultConfigValue($field); } - /** - * Returns min_sale_qty configuration for the ALL Customer Group - * @return int - */ - public function getDefaultMinSaleQty() - { - $default = $this->stockConfiguration->getDefaultConfigValue('min_sale_qty'); - if (!is_numeric($default)) { - $default = $this->serializer->unserialize($default); - $default = isset($default[GroupInterface::CUST_GROUP_ALL]) ? $default[GroupInterface::CUST_GROUP_ALL] : 1; - } - - return (int) $default; - } - /** * Tab settings * @@ -124,6 +99,8 @@ public function getTabLabel() } /** + * Return Tab title. + * * @return \Magento\Framework\Phrase */ public function getTabTitle() @@ -132,7 +109,7 @@ public function getTabTitle() } /** - * @return bool + * @inheritdoc */ public function canShowTab() { @@ -140,7 +117,7 @@ public function canShowTab() } /** - * @return bool + * @inheritdoc */ public function isHidden() { @@ -148,6 +125,8 @@ public function isHidden() } /** + * Get availability status. + * * @param string $fieldName * @return bool * @SuppressWarnings(PHPMD.UnusedFormalParameter) diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/inventory.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/inventory.phtml index 96e07ceb4d305..64c8ba7dcf49f 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/inventory.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/inventory.phtml @@ -30,6 +30,13 @@ }); </script> +<?php +$defaultMinSaleQty = $block->getDefaultConfigValue('min_sale_qty'); +if (!is_numeric($defaultMinSaleQty)) { + $defaultMinSaleQty = json_decode($defaultMinSaleQty, true); + $defaultMinSaleQty = (float) $defaultMinSaleQty[\Magento\Customer\Api\Data\GroupInterface::CUST_GROUP_ALL] ?? 1; +} +?> <div class="fieldset-wrapper form-inline advanced-inventory-edit"> <div class="fieldset-wrapper-title"> <strong class="title"> @@ -132,7 +139,7 @@ <div class="field"> <input type="text" class="input-text validate-number" id="inventory_min_sale_qty" name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[min_sale_qty]" - value="<?= /* @escapeNotVerified */ $block->getDefaultMinSaleQty() * 1 ?>" + value="<?= /* @escapeNotVerified */ $defaultMinSaleQty ?>" disabled="disabled"/> </div> <div class="field choice"> From ab2de1505b5b98d746c3623269e19fdb283676b5 Mon Sep 17 00:00:00 2001 From: Prakash <prakash@2jcommerce.in> Date: Fri, 8 Feb 2019 19:34:23 +0530 Subject: [PATCH 0872/1295] Fixes for product tabbing issue --- lib/web/mage/tabs.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/web/mage/tabs.js b/lib/web/mage/tabs.js index b441477ab8d8a..e4d196fcbbca8 100644 --- a/lib/web/mage/tabs.js +++ b/lib/web/mage/tabs.js @@ -71,6 +71,9 @@ define([ anchorId = anchor.replace('#', ''); if (anchor && isValid) { + if(anchorId == 'review-form'){ + anchorId = anchorId.replace('-form', 's'); + } $.each(self.contents, function (i) { if ($(this).attr('id') === anchorId) { self.collapsibles.not(self.collapsibles.eq(i)).collapsible('forceDeactivate'); From c6c6ead1aaaa5e45b992aff7d564a44c2276e083 Mon Sep 17 00:00:00 2001 From: Prakash <prakash@2jcommerce.in> Date: Wed, 13 Feb 2019 11:59:25 +0530 Subject: [PATCH 0873/1295] Fixes for tabbing issue on product --- lib/web/mage/tabs.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/web/mage/tabs.js b/lib/web/mage/tabs.js index e4d196fcbbca8..ee3119e90d3a8 100644 --- a/lib/web/mage/tabs.js +++ b/lib/web/mage/tabs.js @@ -70,12 +70,9 @@ define([ isValid = $.mage.isValidSelector(anchor), anchorId = anchor.replace('#', ''); - if (anchor && isValid) { - if(anchorId == 'review-form'){ - anchorId = anchorId.replace('-form', 's'); - } + if (anchor && isValid) { $.each(self.contents, function (i) { - if ($(this).attr('id') === anchorId) { + if ($(this).attr('id') === anchorId || $(this).find('#' + anchorId).length) { self.collapsibles.not(self.collapsibles.eq(i)).collapsible('forceDeactivate'); return false; From 905085d16039d44c5a1159d2fc68a68062291f37 Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets <vzaets@magento.com> Date: Wed, 13 Feb 2019 17:58:30 +0200 Subject: [PATCH 0874/1295] Remove spaces --- lib/web/mage/tabs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/mage/tabs.js b/lib/web/mage/tabs.js index ee3119e90d3a8..65c452d33bf12 100644 --- a/lib/web/mage/tabs.js +++ b/lib/web/mage/tabs.js @@ -70,7 +70,7 @@ define([ isValid = $.mage.isValidSelector(anchor), anchorId = anchor.replace('#', ''); - if (anchor && isValid) { + if (anchor && isValid) { $.each(self.contents, function (i) { if ($(this).attr('id') === anchorId || $(this).find('#' + anchorId).length) { self.collapsibles.not(self.collapsibles.eq(i)).collapsible('forceDeactivate'); From 887433247a7d0da1237c0d7f60fe5bee1dfa4324 Mon Sep 17 00:00:00 2001 From: milindsingh <milind7@live.com> Date: Tue, 8 Jan 2019 22:29:19 +0530 Subject: [PATCH 0875/1295] Issue fixed #20128 : Date range returns same start and end date --- .../Magento/Reports/Model/ResourceModel/Order/Collection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php index fd9adbe734101..da7ab97a1b211 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php @@ -445,7 +445,7 @@ public function getDateRange($range, $customStart, $customEnd, $returnObjects = break; case 'custom': - $dateStart = $customStart ? $customStart : $dateEnd; + $dateStart = $customStart ? $customStart : $dateStart; $dateEnd = $customEnd ? $customEnd : $dateEnd; break; From 1c45357d60ef44975e12bad55fa7f2ef9107cad9 Mon Sep 17 00:00:00 2001 From: LisovyiEvhenii <lisovyievhenii@gmail.com> Date: Fri, 25 Jan 2019 13:14:09 +0200 Subject: [PATCH 0876/1295] #14882: product_types.xml doesn't allow numbers in modelInstance. Fix module and models name pattern for product_types_base.xsd, product_options.xsd, import.xsd, export.xsd. Adjust unit tests, related to these files --- .../Config/_files/invalidProductOptionsXmlArray.php | 8 ++++---- .../Config/_files/invalidProductTypesXmlArray.php | 8 ++++---- .../Config/_files/valid_product_types_merged.xml | 9 +++++++++ app/code/Magento/Catalog/etc/product_options.xsd | 4 ++-- app/code/Magento/Catalog/etc/product_types_base.xsd | 4 ++-- .../Export/Config/_files/invalidExportXmlArray.php | 10 +++++----- .../Config/_files/invalidImportMergedXmlArray.php | 10 +++++----- .../Import/Config/_files/invalidImportXmlArray.php | 12 ++++++------ app/code/Magento/ImportExport/etc/export.xsd | 4 ++-- app/code/Magento/ImportExport/etc/import.xsd | 4 ++-- 10 files changed, 41 insertions(+), 32 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductOptions/Config/_files/invalidProductOptionsXmlArray.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductOptions/Config/_files/invalidProductOptionsXmlArray.php index 034b04b6a757d..31f3ec07bf063 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductOptions/Config/_files/invalidProductOptionsXmlArray.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductOptions/Config/_files/invalidProductOptionsXmlArray.php @@ -29,12 +29,12 @@ ], ], 'renderer_attribute_with_invalid_value' => [ - '<?xml version="1.0"?><config><option name="name_one" renderer="true12"><inputType name="name_one"/>' . + '<?xml version="1.0"?><config><option name="name_one" renderer="123true"><inputType name="name_one"/>' . '</option></config>', [ - "Element 'option', attribute 'renderer': [facet 'pattern'] The value 'true12' is not accepted by the " . - "pattern '[a-zA-Z_\\\\]+'.\nLine: 1\n", - "Element 'option', attribute 'renderer': 'true12' is not a valid value of the atomic" . + "Element 'option', attribute 'renderer': [facet 'pattern'] The value '123true' is not accepted by the " . + "pattern '[a-zA-Z_\\\\]+[a-zA-Z0-9_\\\\]+'.\nLine: 1\n", + "Element 'option', attribute 'renderer': '123true' is not a valid value of the atomic" . " type 'modelName'.\nLine: 1\n" ], ], diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductTypes/Config/_files/invalidProductTypesXmlArray.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductTypes/Config/_files/invalidProductTypesXmlArray.php index e1847bea53fcb..1850ff476a809 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductTypes/Config/_files/invalidProductTypesXmlArray.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductTypes/Config/_files/invalidProductTypesXmlArray.php @@ -23,7 +23,7 @@ '<?xml version="1.0"?><config><type name="some_name" modelInstance="123" /></config>', [ "Element 'type', attribute 'modelInstance': [facet 'pattern'] The value '123' is not accepted by the" . - " pattern '[a-zA-Z_\\\\]+'.\nLine: 1\n", + " pattern '[a-zA-Z_\\\\]+[a-zA-Z0-9_\\\\]+'.\nLine: 1\n", "Element 'type', attribute 'modelInstance': '123' is not a valid value of the atomic type" . " 'modelName'.\nLine: 1\n" ], @@ -57,7 +57,7 @@ '<?xml version="1.0"?><config><type name="some_name"><priceModel instance="123123" /></type></config>', [ "Element 'priceModel', attribute 'instance': [facet 'pattern'] The value '123123' is not accepted " . - "by the pattern '[a-zA-Z_\\\\]+'.\nLine: 1\n", + "by the pattern '[a-zA-Z_\\\\]+[a-zA-Z0-9_\\\\]+'.\nLine: 1\n", "Element 'priceModel', attribute 'instance': '123123' is not a valid value of the atomic type" . " 'modelName'.\nLine: 1\n" ], @@ -66,7 +66,7 @@ '<?xml version="1.0"?><config><type name="some_name"><indexerModel instance="123" /></type></config>', [ "Element 'indexerModel', attribute 'instance': [facet 'pattern'] The value '123' is not accepted by " . - "the pattern '[a-zA-Z_\\\\]+'.\nLine: 1\n", + "the pattern '[a-zA-Z_\\\\]+[a-zA-Z0-9_\\\\]+'.\nLine: 1\n", "Element 'indexerModel', attribute 'instance': '123' is not a valid value of the atomic type" . " 'modelName'.\nLine: 1\n" ], @@ -83,7 +83,7 @@ '<?xml version="1.0"?><config><type name="some_name"><stockIndexerModel instance="1234"/></type></config>', [ "Element 'stockIndexerModel', attribute 'instance': [facet 'pattern'] The value '1234' is not " . - "accepted by the pattern '[a-zA-Z_\\\\]+'.\nLine: 1\n", + "accepted by the pattern '[a-zA-Z_\\\\]+[a-zA-Z0-9_\\\\]+'.\nLine: 1\n", "Element 'stockIndexerModel', attribute 'instance': '1234' is not a valid value of the atomic " . "type 'modelName'.\nLine: 1\n" ], diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductTypes/Config/_files/valid_product_types_merged.xml b/app/code/Magento/Catalog/Test/Unit/Model/ProductTypes/Config/_files/valid_product_types_merged.xml index 7edbc399a9476..701338774baa5 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductTypes/Config/_files/valid_product_types_merged.xml +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductTypes/Config/_files/valid_product_types_merged.xml @@ -15,6 +15,14 @@ <stockIndexerModel instance="instance_name"/> </type> <type label="some_label" name="some_name2" modelInstance="model_name"> + <allowedSelectionTypes> + <type name="some_name" /> + </allowedSelectionTypes> + <priceModel instance="instance_name_with_digits_123" /> + <indexerModel instance="instance_name_with_digits_123" /> + <stockIndexerModel instance="instance_name_with_digits_123"/> + </type> + <type label="some_label" name="some_name3" modelInstance="model_name"> <allowedSelectionTypes> <type name="some_name" /> </allowedSelectionTypes> @@ -25,5 +33,6 @@ <composableTypes> <type name="some_name"/> <type name="some_name2"/> + <type name="some_name3"/> </composableTypes> </config> diff --git a/app/code/Magento/Catalog/etc/product_options.xsd b/app/code/Magento/Catalog/etc/product_options.xsd index 3bc24a9099262..e57eb4e715918 100644 --- a/app/code/Magento/Catalog/etc/product_options.xsd +++ b/app/code/Magento/Catalog/etc/product_options.xsd @@ -61,11 +61,11 @@ <xs:simpleType name="modelName"> <xs:annotation> <xs:documentation> - Model name can contain only [a-zA-Z_\\]. + Model name can contain only [a-zA-Z_\\]+[a-zA-Z0-9_\\]+. </xs:documentation> </xs:annotation> <xs:restriction base="xs:string"> - <xs:pattern value="[a-zA-Z_\\]+" /> + <xs:pattern value="[a-zA-Z_\\]+[a-zA-Z0-9_\\]+" /> </xs:restriction> </xs:simpleType> </xs:schema> diff --git a/app/code/Magento/Catalog/etc/product_types_base.xsd b/app/code/Magento/Catalog/etc/product_types_base.xsd index 6cc35fd7bee37..fdefc95c277b1 100644 --- a/app/code/Magento/Catalog/etc/product_types_base.xsd +++ b/app/code/Magento/Catalog/etc/product_types_base.xsd @@ -92,11 +92,11 @@ <xs:simpleType name="modelName"> <xs:annotation> <xs:documentation> - Model name can contain only [a-zA-Z_\\]. + Model name can contain only [a-zA-Z_\\]+[a-zA-Z0-9_\\]+. </xs:documentation> </xs:annotation> <xs:restriction base="xs:string"> - <xs:pattern value="[a-zA-Z_\\]+" /> + <xs:pattern value="[a-zA-Z_\\]+[a-zA-Z0-9_\\]+" /> </xs:restriction> </xs:simpleType> </xs:schema> diff --git a/app/code/Magento/ImportExport/Test/Unit/Model/Export/Config/_files/invalidExportXmlArray.php b/app/code/Magento/ImportExport/Test/Unit/Model/Export/Config/_files/invalidExportXmlArray.php index 288a99770974a..251ccae6d7a8a 100644 --- a/app/code/Magento/ImportExport/Test/Unit/Model/Export/Config/_files/invalidExportXmlArray.php +++ b/app/code/Magento/ImportExport/Test/Unit/Model/Export/Config/_files/invalidExportXmlArray.php @@ -22,15 +22,15 @@ 'attributes_with_type_modelName_and_invalid_value' => [ '<?xml version="1.0"?><config><entity name="Name/one" model="model_one" ' . 'entityAttributeFilterType="model_one"/><entityType entity="Name/one" name="name_one" model="1"/>' - . ' <fileFormat name="name_one" model="model1"/></config>', + . ' <fileFormat name="name_one" model="1model"/></config>', [ "Element 'entityType', attribute 'model': [facet 'pattern'] The value '1' is not accepted by the " . - "pattern '[A-Za-z_\\\\]+'.\nLine: 1\n", + "pattern '[a-zA-Z_\\\\]+[a-zA-Z0-9_\\\\]+'.\nLine: 1\n", "Element 'entityType', attribute 'model': '1' is not a valid value of the atomic type" . " 'modelName'.\nLine: 1\n", - "Element 'fileFormat', attribute 'model': [facet 'pattern'] The value 'model1' is not " . - "accepted by the pattern '[A-Za-z_\\\\]+'.\nLine: 1\n", - "Element 'fileFormat', attribute 'model': 'model1' is not a valid " . + "Element 'fileFormat', attribute 'model': [facet 'pattern'] The value '1model' is not " . + "accepted by the pattern '[a-zA-Z_\\\\]+[a-zA-Z0-9_\\\\]+'.\nLine: 1\n", + "Element 'fileFormat', attribute 'model': '1model' is not a valid " . "value of the atomic type 'modelName'.\nLine: 1\n" ], ], diff --git a/app/code/Magento/ImportExport/Test/Unit/Model/Import/Config/_files/invalidImportMergedXmlArray.php b/app/code/Magento/ImportExport/Test/Unit/Model/Import/Config/_files/invalidImportMergedXmlArray.php index 357b35e8a313c..e51d46536abc0 100644 --- a/app/code/Magento/ImportExport/Test/Unit/Model/Import/Config/_files/invalidImportMergedXmlArray.php +++ b/app/code/Magento/ImportExport/Test/Unit/Model/Import/Config/_files/invalidImportMergedXmlArray.php @@ -26,12 +26,12 @@ ["Element 'entity', attribute 'notallowed': The attribute 'notallowed' is not allowed.\nLine: 1\n"], ], 'entity_model_with_invalid_value' => [ - '<?xml version="1.0"?><config><entity name="test_name" label="test_label" model="afwer34" ' . + '<?xml version="1.0"?><config><entity name="test_name" label="test_label" model="34afwer" ' . 'behaviorModel="test" /></config>', [ - "Element 'entity', attribute 'model': [facet 'pattern'] The value 'afwer34' is not " . - "accepted by the pattern '[A-Za-z_\\\\]+'.\nLine: 1\n", - "Element 'entity', attribute 'model': 'afwer34' is not a valid value of the atomic type" . + "Element 'entity', attribute 'model': [facet 'pattern'] The value '34afwer' is not " . + "accepted by the pattern '[a-zA-Z_\\\\]+[a-zA-Z0-9_\\\\]+'.\nLine: 1\n", + "Element 'entity', attribute 'model': '34afwer' is not a valid value of the atomic type" . " 'modelName'.\nLine: 1\n" ], ], @@ -40,7 +40,7 @@ '</config>', [ "Element 'entity', attribute 'behaviorModel': [facet 'pattern'] The value '666' is not accepted by " . - "the pattern '[A-Za-z_\\\\]+'.\nLine: 1\n", + "the pattern '[a-zA-Z_\\\\]+[a-zA-Z0-9_\\\\]+'.\nLine: 1\n", "Element 'entity', attribute 'behaviorModel': '666' is not a valid value of the atomic type" . " 'modelName'.\nLine: 1\n" ], diff --git a/app/code/Magento/ImportExport/Test/Unit/Model/Import/Config/_files/invalidImportXmlArray.php b/app/code/Magento/ImportExport/Test/Unit/Model/Import/Config/_files/invalidImportXmlArray.php index c913b53e8b531..4b9d8e1c32f6d 100644 --- a/app/code/Magento/ImportExport/Test/Unit/Model/Import/Config/_files/invalidImportXmlArray.php +++ b/app/code/Magento/ImportExport/Test/Unit/Model/Import/Config/_files/invalidImportXmlArray.php @@ -19,7 +19,7 @@ '<?xml version="1.0"?><config><entity name="some_name" model="12345"/></config>', [ "Element 'entity', attribute 'model': [facet 'pattern'] The value '12345' is not accepted by " . - "the pattern '[A-Za-z_\\\\]+'.\nLine: 1\n", + "the pattern '[a-zA-Z_\\\\]+[a-zA-Z0-9_\\\\]+'.\nLine: 1\n", "Element 'entity', attribute 'model': '12345' is not a valid value of the atomic type" . " 'modelName'.\nLine: 1\n" ], @@ -28,7 +28,7 @@ '<?xml version="1.0"?><config><entity name="some_name" behaviorModel="=--09"/></config>', [ "Element 'entity', attribute 'behaviorModel': [facet 'pattern'] The value '=--09' is not " . - "accepted by the pattern '[A-Za-z_\\\\]+'.\nLine: 1\n", + "accepted by the pattern '[a-zA-Z_\\\\]+[a-zA-Z0-9_\\\\]+'.\nLine: 1\n", "Element 'entity', attribute 'behaviorModel': '=--09' is not a valid value of the atomic type" . " 'modelName'.\nLine: 1\n" ], @@ -46,11 +46,11 @@ ["Element 'entityType': The attribute 'model' is required but missing.\nLine: 1\n"], ], 'entitytype_with_invalid_model_attribute_value' => [ - '<?xml version="1.0"?><config><entityType entity="entity_name" name="some_name" model="test1"/></config>', + '<?xml version="1.0"?><config><entityType entity="entity_name" name="some_name" model="1test"/></config>', [ - "Element 'entityType', attribute 'model': [facet 'pattern'] The value 'test1' is not " . - "accepted by the pattern '[A-Za-z_\\\\]+'.\nLine: 1\n", - "Element 'entityType', attribute 'model': 'test1' is not a valid value of the atomic type" . + "Element 'entityType', attribute 'model': [facet 'pattern'] The value '1test' is not " . + "accepted by the pattern '[a-zA-Z_\\\\]+[a-zA-Z0-9_\\\\]+'.\nLine: 1\n", + "Element 'entityType', attribute 'model': '1test' is not a valid value of the atomic type" . " 'modelName'.\nLine: 1\n" ], ], diff --git a/app/code/Magento/ImportExport/etc/export.xsd b/app/code/Magento/ImportExport/etc/export.xsd index 65728a9be5c62..b196ae0cb6f11 100644 --- a/app/code/Magento/ImportExport/etc/export.xsd +++ b/app/code/Magento/ImportExport/etc/export.xsd @@ -71,11 +71,11 @@ <xs:simpleType name="modelName"> <xs:annotation> <xs:documentation> - Model name can contain only [A-Za-z_\\]. + Model name can contain only [a-zA-Z_\\]+[a-zA-Z0-9_\\]+. </xs:documentation> </xs:annotation> <xs:restriction base="xs:string"> - <xs:pattern value="[A-Za-z_\\]+" /> + <xs:pattern value="[a-zA-Z_\\]+[a-zA-Z0-9_\\]+" /> </xs:restriction> </xs:simpleType> </xs:schema> diff --git a/app/code/Magento/ImportExport/etc/import.xsd b/app/code/Magento/ImportExport/etc/import.xsd index aefa6541d7e13..42bd365878eb2 100644 --- a/app/code/Magento/ImportExport/etc/import.xsd +++ b/app/code/Magento/ImportExport/etc/import.xsd @@ -61,11 +61,11 @@ <xs:simpleType name="modelName"> <xs:annotation> <xs:documentation> - Model name can contain only [A-Za-z_\\]. + Model name can contain only [a-zA-Z_\\]+[a-zA-Z0-9_\\]+. </xs:documentation> </xs:annotation> <xs:restriction base="xs:string"> - <xs:pattern value="[A-Za-z_\\]+" /> + <xs:pattern value="[a-zA-Z_\\]+[a-zA-Z0-9_\\]+" /> </xs:restriction> </xs:simpleType> </xs:schema> From ab8d312286fa448f408727ea6fff9e790424cb34 Mon Sep 17 00:00:00 2001 From: LisovyiEvhenii <lisovyievhenii@gmail.com> Date: Tue, 29 Jan 2019 15:50:21 +0200 Subject: [PATCH 0877/1295] #14882: improve regular expression --- .../Config/_files/invalidProductOptionsXmlArray.php | 2 +- .../Config/_files/invalidProductTypesXmlArray.php | 8 ++++---- app/code/Magento/Catalog/etc/product_options.xsd | 4 ++-- app/code/Magento/Catalog/etc/product_types_base.xsd | 4 ++-- .../Model/Export/Config/_files/invalidExportXmlArray.php | 4 ++-- .../Import/Config/_files/invalidImportMergedXmlArray.php | 4 ++-- .../Model/Import/Config/_files/invalidImportXmlArray.php | 6 +++--- app/code/Magento/ImportExport/etc/export.xsd | 4 ++-- app/code/Magento/ImportExport/etc/import.xsd | 4 ++-- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductOptions/Config/_files/invalidProductOptionsXmlArray.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductOptions/Config/_files/invalidProductOptionsXmlArray.php index 31f3ec07bf063..cfb54c3aefd0f 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductOptions/Config/_files/invalidProductOptionsXmlArray.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductOptions/Config/_files/invalidProductOptionsXmlArray.php @@ -33,7 +33,7 @@ '</option></config>', [ "Element 'option', attribute 'renderer': [facet 'pattern'] The value '123true' is not accepted by the " . - "pattern '[a-zA-Z_\\\\]+[a-zA-Z0-9_\\\\]+'.\nLine: 1\n", + "pattern '([\\\\]?[a-zA-Z_][a-zA-Z0-9_]*)+'.\nLine: 1\n", "Element 'option', attribute 'renderer': '123true' is not a valid value of the atomic" . " type 'modelName'.\nLine: 1\n" ], diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductTypes/Config/_files/invalidProductTypesXmlArray.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductTypes/Config/_files/invalidProductTypesXmlArray.php index 1850ff476a809..868252da8190c 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductTypes/Config/_files/invalidProductTypesXmlArray.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductTypes/Config/_files/invalidProductTypesXmlArray.php @@ -23,7 +23,7 @@ '<?xml version="1.0"?><config><type name="some_name" modelInstance="123" /></config>', [ "Element 'type', attribute 'modelInstance': [facet 'pattern'] The value '123' is not accepted by the" . - " pattern '[a-zA-Z_\\\\]+[a-zA-Z0-9_\\\\]+'.\nLine: 1\n", + " pattern '([\\\\]?[a-zA-Z_][a-zA-Z0-9_]*)+'.\nLine: 1\n", "Element 'type', attribute 'modelInstance': '123' is not a valid value of the atomic type" . " 'modelName'.\nLine: 1\n" ], @@ -57,7 +57,7 @@ '<?xml version="1.0"?><config><type name="some_name"><priceModel instance="123123" /></type></config>', [ "Element 'priceModel', attribute 'instance': [facet 'pattern'] The value '123123' is not accepted " . - "by the pattern '[a-zA-Z_\\\\]+[a-zA-Z0-9_\\\\]+'.\nLine: 1\n", + "by the pattern '([\\\\]?[a-zA-Z_][a-zA-Z0-9_]*)+'.\nLine: 1\n", "Element 'priceModel', attribute 'instance': '123123' is not a valid value of the atomic type" . " 'modelName'.\nLine: 1\n" ], @@ -66,7 +66,7 @@ '<?xml version="1.0"?><config><type name="some_name"><indexerModel instance="123" /></type></config>', [ "Element 'indexerModel', attribute 'instance': [facet 'pattern'] The value '123' is not accepted by " . - "the pattern '[a-zA-Z_\\\\]+[a-zA-Z0-9_\\\\]+'.\nLine: 1\n", + "the pattern '([\\\\]?[a-zA-Z_][a-zA-Z0-9_]*)+'.\nLine: 1\n", "Element 'indexerModel', attribute 'instance': '123' is not a valid value of the atomic type" . " 'modelName'.\nLine: 1\n" ], @@ -83,7 +83,7 @@ '<?xml version="1.0"?><config><type name="some_name"><stockIndexerModel instance="1234"/></type></config>', [ "Element 'stockIndexerModel', attribute 'instance': [facet 'pattern'] The value '1234' is not " . - "accepted by the pattern '[a-zA-Z_\\\\]+[a-zA-Z0-9_\\\\]+'.\nLine: 1\n", + "accepted by the pattern '([\\\\]?[a-zA-Z_][a-zA-Z0-9_]*)+'.\nLine: 1\n", "Element 'stockIndexerModel', attribute 'instance': '1234' is not a valid value of the atomic " . "type 'modelName'.\nLine: 1\n" ], diff --git a/app/code/Magento/Catalog/etc/product_options.xsd b/app/code/Magento/Catalog/etc/product_options.xsd index e57eb4e715918..734c8f378d5d7 100644 --- a/app/code/Magento/Catalog/etc/product_options.xsd +++ b/app/code/Magento/Catalog/etc/product_options.xsd @@ -61,11 +61,11 @@ <xs:simpleType name="modelName"> <xs:annotation> <xs:documentation> - Model name can contain only [a-zA-Z_\\]+[a-zA-Z0-9_\\]+. + Model name can contain only ([\\]?[a-zA-Z_][a-zA-Z0-9_]*)+. </xs:documentation> </xs:annotation> <xs:restriction base="xs:string"> - <xs:pattern value="[a-zA-Z_\\]+[a-zA-Z0-9_\\]+" /> + <xs:pattern value="([\\]?[a-zA-Z_][a-zA-Z0-9_]*)+" /> </xs:restriction> </xs:simpleType> </xs:schema> diff --git a/app/code/Magento/Catalog/etc/product_types_base.xsd b/app/code/Magento/Catalog/etc/product_types_base.xsd index fdefc95c277b1..dec952bcf492e 100644 --- a/app/code/Magento/Catalog/etc/product_types_base.xsd +++ b/app/code/Magento/Catalog/etc/product_types_base.xsd @@ -92,11 +92,11 @@ <xs:simpleType name="modelName"> <xs:annotation> <xs:documentation> - Model name can contain only [a-zA-Z_\\]+[a-zA-Z0-9_\\]+. + Model name can contain only ([\\]?[a-zA-Z_][a-zA-Z0-9_]*)+. </xs:documentation> </xs:annotation> <xs:restriction base="xs:string"> - <xs:pattern value="[a-zA-Z_\\]+[a-zA-Z0-9_\\]+" /> + <xs:pattern value="([\\]?[a-zA-Z_][a-zA-Z0-9_]*)+" /> </xs:restriction> </xs:simpleType> </xs:schema> diff --git a/app/code/Magento/ImportExport/Test/Unit/Model/Export/Config/_files/invalidExportXmlArray.php b/app/code/Magento/ImportExport/Test/Unit/Model/Export/Config/_files/invalidExportXmlArray.php index 251ccae6d7a8a..179f3f3cadab0 100644 --- a/app/code/Magento/ImportExport/Test/Unit/Model/Export/Config/_files/invalidExportXmlArray.php +++ b/app/code/Magento/ImportExport/Test/Unit/Model/Export/Config/_files/invalidExportXmlArray.php @@ -25,11 +25,11 @@ . ' <fileFormat name="name_one" model="1model"/></config>', [ "Element 'entityType', attribute 'model': [facet 'pattern'] The value '1' is not accepted by the " . - "pattern '[a-zA-Z_\\\\]+[a-zA-Z0-9_\\\\]+'.\nLine: 1\n", + "pattern '([\\\\]?[a-zA-Z_][a-zA-Z0-9_]*)+'.\nLine: 1\n", "Element 'entityType', attribute 'model': '1' is not a valid value of the atomic type" . " 'modelName'.\nLine: 1\n", "Element 'fileFormat', attribute 'model': [facet 'pattern'] The value '1model' is not " . - "accepted by the pattern '[a-zA-Z_\\\\]+[a-zA-Z0-9_\\\\]+'.\nLine: 1\n", + "accepted by the pattern '([\\\\]?[a-zA-Z_][a-zA-Z0-9_]*)+'.\nLine: 1\n", "Element 'fileFormat', attribute 'model': '1model' is not a valid " . "value of the atomic type 'modelName'.\nLine: 1\n" ], diff --git a/app/code/Magento/ImportExport/Test/Unit/Model/Import/Config/_files/invalidImportMergedXmlArray.php b/app/code/Magento/ImportExport/Test/Unit/Model/Import/Config/_files/invalidImportMergedXmlArray.php index e51d46536abc0..409c1af9cb38a 100644 --- a/app/code/Magento/ImportExport/Test/Unit/Model/Import/Config/_files/invalidImportMergedXmlArray.php +++ b/app/code/Magento/ImportExport/Test/Unit/Model/Import/Config/_files/invalidImportMergedXmlArray.php @@ -30,7 +30,7 @@ 'behaviorModel="test" /></config>', [ "Element 'entity', attribute 'model': [facet 'pattern'] The value '34afwer' is not " . - "accepted by the pattern '[a-zA-Z_\\\\]+[a-zA-Z0-9_\\\\]+'.\nLine: 1\n", + "accepted by the pattern '([\\\\]?[a-zA-Z_][a-zA-Z0-9_]*)+'.\nLine: 1\n", "Element 'entity', attribute 'model': '34afwer' is not a valid value of the atomic type" . " 'modelName'.\nLine: 1\n" ], @@ -40,7 +40,7 @@ '</config>', [ "Element 'entity', attribute 'behaviorModel': [facet 'pattern'] The value '666' is not accepted by " . - "the pattern '[a-zA-Z_\\\\]+[a-zA-Z0-9_\\\\]+'.\nLine: 1\n", + "the pattern '([\\\\]?[a-zA-Z_][a-zA-Z0-9_]*)+'.\nLine: 1\n", "Element 'entity', attribute 'behaviorModel': '666' is not a valid value of the atomic type" . " 'modelName'.\nLine: 1\n" ], diff --git a/app/code/Magento/ImportExport/Test/Unit/Model/Import/Config/_files/invalidImportXmlArray.php b/app/code/Magento/ImportExport/Test/Unit/Model/Import/Config/_files/invalidImportXmlArray.php index 4b9d8e1c32f6d..c7b06a8731f02 100644 --- a/app/code/Magento/ImportExport/Test/Unit/Model/Import/Config/_files/invalidImportXmlArray.php +++ b/app/code/Magento/ImportExport/Test/Unit/Model/Import/Config/_files/invalidImportXmlArray.php @@ -19,7 +19,7 @@ '<?xml version="1.0"?><config><entity name="some_name" model="12345"/></config>', [ "Element 'entity', attribute 'model': [facet 'pattern'] The value '12345' is not accepted by " . - "the pattern '[a-zA-Z_\\\\]+[a-zA-Z0-9_\\\\]+'.\nLine: 1\n", + "the pattern '([\\\\]?[a-zA-Z_][a-zA-Z0-9_]*)+'.\nLine: 1\n", "Element 'entity', attribute 'model': '12345' is not a valid value of the atomic type" . " 'modelName'.\nLine: 1\n" ], @@ -28,7 +28,7 @@ '<?xml version="1.0"?><config><entity name="some_name" behaviorModel="=--09"/></config>', [ "Element 'entity', attribute 'behaviorModel': [facet 'pattern'] The value '=--09' is not " . - "accepted by the pattern '[a-zA-Z_\\\\]+[a-zA-Z0-9_\\\\]+'.\nLine: 1\n", + "accepted by the pattern '([\\\\]?[a-zA-Z_][a-zA-Z0-9_]*)+'.\nLine: 1\n", "Element 'entity', attribute 'behaviorModel': '=--09' is not a valid value of the atomic type" . " 'modelName'.\nLine: 1\n" ], @@ -49,7 +49,7 @@ '<?xml version="1.0"?><config><entityType entity="entity_name" name="some_name" model="1test"/></config>', [ "Element 'entityType', attribute 'model': [facet 'pattern'] The value '1test' is not " . - "accepted by the pattern '[a-zA-Z_\\\\]+[a-zA-Z0-9_\\\\]+'.\nLine: 1\n", + "accepted by the pattern '([\\\\]?[a-zA-Z_][a-zA-Z0-9_]*)+'.\nLine: 1\n", "Element 'entityType', attribute 'model': '1test' is not a valid value of the atomic type" . " 'modelName'.\nLine: 1\n" ], diff --git a/app/code/Magento/ImportExport/etc/export.xsd b/app/code/Magento/ImportExport/etc/export.xsd index b196ae0cb6f11..f62dbc891ef0f 100644 --- a/app/code/Magento/ImportExport/etc/export.xsd +++ b/app/code/Magento/ImportExport/etc/export.xsd @@ -71,11 +71,11 @@ <xs:simpleType name="modelName"> <xs:annotation> <xs:documentation> - Model name can contain only [a-zA-Z_\\]+[a-zA-Z0-9_\\]+. + Model name can contain only ([\\]?[a-zA-Z_][a-zA-Z0-9_]*)+. </xs:documentation> </xs:annotation> <xs:restriction base="xs:string"> - <xs:pattern value="[a-zA-Z_\\]+[a-zA-Z0-9_\\]+" /> + <xs:pattern value="([\\]?[a-zA-Z_][a-zA-Z0-9_]*)+" /> </xs:restriction> </xs:simpleType> </xs:schema> diff --git a/app/code/Magento/ImportExport/etc/import.xsd b/app/code/Magento/ImportExport/etc/import.xsd index 42bd365878eb2..e73038ebc0710 100644 --- a/app/code/Magento/ImportExport/etc/import.xsd +++ b/app/code/Magento/ImportExport/etc/import.xsd @@ -61,11 +61,11 @@ <xs:simpleType name="modelName"> <xs:annotation> <xs:documentation> - Model name can contain only [a-zA-Z_\\]+[a-zA-Z0-9_\\]+. + Model name can contain only ([\\]?[a-zA-Z_][a-zA-Z0-9_]*)+. </xs:documentation> </xs:annotation> <xs:restriction base="xs:string"> - <xs:pattern value="[a-zA-Z_\\]+[a-zA-Z0-9_\\]+" /> + <xs:pattern value="([\\]?[a-zA-Z_][a-zA-Z0-9_]*)+" /> </xs:restriction> </xs:simpleType> </xs:schema> From 290b998c6309d52e77c0c226e5317643ce915677 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 6 Mar 2019 10:00:23 -0600 Subject: [PATCH 0878/1295] MAGETWO-98208: URLs not using SEO friendly format if you assign to website --- .../ProductToWebsiteChangeObserver.php | 9 +- .../ProductToWebsiteChangeObserverTest.php | 193 ++++++++++++++++++ 2 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductToWebsiteChangeObserverTest.php diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/ProductToWebsiteChangeObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/ProductToWebsiteChangeObserver.php index 94798753ca63f..12334a2a773cb 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/ProductToWebsiteChangeObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/ProductToWebsiteChangeObserver.php @@ -13,6 +13,8 @@ use Magento\Store\Model\Store; use Magento\UrlRewrite\Model\UrlPersistInterface; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException; /** * Observer to assign the products to website. @@ -61,21 +63,26 @@ public function __construct( * Generate urls for UrlRewrite and save it in storage * * @param \Magento\Framework\Event\Observer $observer + * @throws NoSuchEntityException + * @throws UrlAlreadyExistsException * @return void */ public function execute(\Magento\Framework\Event\Observer $observer) { foreach ($observer->getEvent()->getProducts() as $productId) { + $storeId = $this->request->getParam('store_id', Store::DEFAULT_STORE_ID); + $product = $this->productRepository->getById( $productId, false, - $this->request->getParam('store_id', Store::DEFAULT_STORE_ID) + $storeId ); if (!empty($this->productUrlRewriteGenerator->generate($product))) { $this->urlPersist->deleteByData([ UrlRewrite::ENTITY_ID => $product->getId(), UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, + UrlRewrite::STORE_ID => $storeId, ]); if ($product->getVisibility() != Visibility::VISIBILITY_NOT_VISIBLE) { $this->urlPersist->replace($this->productUrlRewriteGenerator->generate($product)); diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductToWebsiteChangeObserverTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductToWebsiteChangeObserverTest.php new file mode 100644 index 0000000000000..f383c949b4295 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductToWebsiteChangeObserverTest.php @@ -0,0 +1,193 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\CatalogUrlRewrite\Test\Unit\Observer; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\CatalogUrlRewrite\Observer\ProductToWebsiteChangeObserver; +use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; +use Magento\UrlRewrite\Model\UrlPersistInterface; +use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; +use Magento\Store\Model\Store; + +/** + * Test for ProductToWebsiteChangeObserver + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ProductToWebsiteChangeObserverTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ProductRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $productRepository; + + /** + * @var ProductUrlRewriteGenerator|\PHPUnit_Framework_MockObject_MockObject + */ + private $productUrlRewriteGenerator; + + /** + * @var UrlPersistInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $urlPersist; + + /** + * @var Event|\PHPUnit_Framework_MockObject_MockObject + */ + private $event; + + /** + * @var Observer|\PHPUnit_Framework_MockObject_MockObject + */ + private $observer; + + /** + * @var Product|\PHPUnit_Framework_MockObject_MockObject + */ + private $product; + + /** + * @var RequestInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $request; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var ProductToWebsiteChangeObserver + */ + private $model; + + /** + * @var int + */ + private $productId; + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->productId = 3; + + $this->urlPersist = $this->getMockBuilder(UrlPersistInterface::class) + ->setMethods(['deleteByData', 'replace']) + ->getMockForAbstractClass(); + $this->productRepository = $this->getMockBuilder(ProductRepositoryInterface::class) + ->setMethods(['getById']) + ->getMockForAbstractClass(); + $this->product = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->setMethods(['getId', 'getVisibility']) + ->getMock(); + $this->product->expects($this->any()) + ->method('getId') + ->willReturn($this->productId); + $this->productRepository->expects($this->any()) + ->method('getById') + ->with($this->productId, false, Store::DEFAULT_STORE_ID) + ->willReturn($this->product); + $this->productUrlRewriteGenerator = $this->getMockBuilder(ProductUrlRewriteGenerator::class) + ->disableOriginalConstructor() + ->setMethods(['generate']) + ->getMock(); + $this->event = $this->getMockBuilder(Event::class) + ->disableOriginalConstructor() + ->setMethods(['getProducts']) + ->getMock(); + $this->event->expects($this->any()) + ->method('getProducts') + ->willReturn([$this->productId]); + $this->observer = $this->getMockBuilder(Observer::class) + ->disableOriginalConstructor() + ->setMethods(['getEvent']) + ->getMock(); + $this->observer->expects($this->any()) + ->method('getEvent') + ->willReturn($this->event); + $this->request = $this->getMockBuilder(RequestInterface::class) + ->setMethods(['getParam']) + ->getMockForAbstractClass(); + $this->request->expects($this->any()) + ->method('getParam') + ->with('store_id', Store::DEFAULT_STORE_ID) + ->willReturn(Store::DEFAULT_STORE_ID); + + $this->objectManager = new ObjectManager($this); + $this->model = $this->objectManager->getObject( + ProductToWebsiteChangeObserver::class, + [ + 'productUrlRewriteGenerator' => $this->productUrlRewriteGenerator, + 'urlPersist' => $this->urlPersist, + 'productRepository' => $this->productRepository, + 'request' => $this->request + ] + ); + } + + /** + * @param array $urlRewriteGeneratorResult + * @param int $numberDeleteByData + * @param int $productVisibility + * @param int $numberReplace + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws \Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException + * @dataProvider executeDataProvider + */ + public function testExecute( + array $urlRewriteGeneratorResult, + int $numberDeleteByData, + int $productVisibility, + int $numberReplace + ) { + $this->productUrlRewriteGenerator->expects($this->any()) + ->method('generate') + ->willReturn($urlRewriteGeneratorResult); + $this->urlPersist->expects($this->exactly($numberDeleteByData)) + ->method('deleteByData') + ->with( + [ + UrlRewrite::ENTITY_ID => $this->productId, + UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, + UrlRewrite::STORE_ID => Store::DEFAULT_STORE_ID + ] + ); + $this->product->expects($this->any()) + ->method('getVisibility') + ->willReturn($productVisibility); + $this->urlPersist->expects($this->exactly($numberReplace)) + ->method('replace') + ->with($urlRewriteGeneratorResult); + + $this->model->execute($this->observer); + } + + /** + * Data provider for testExecute + * + * @return array + */ + public function executeDataProvider(): array + { + return [ + [[], 0, Visibility::VISIBILITY_NOT_VISIBLE, 0], + [['someRewrite'], 1, Visibility::VISIBILITY_NOT_VISIBLE, 0], + [['someRewrite'], 1, Visibility::VISIBILITY_BOTH, 1], + ]; + } +} From 715bd00fa49db50bdff175d8918e006120bcf2b9 Mon Sep 17 00:00:00 2001 From: Andrey Nikolaev <tonikolaev@gmail.com> Date: Wed, 13 Feb 2019 00:58:25 +0300 Subject: [PATCH 0879/1295] Fix issue with custom option file uploading --- lib/internal/Magento/Framework/Filesystem/DirectoryList.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/internal/Magento/Framework/Filesystem/DirectoryList.php b/lib/internal/Magento/Framework/Filesystem/DirectoryList.php index f07932d45ecb6..96818e4ff3b70 100644 --- a/lib/internal/Magento/Framework/Filesystem/DirectoryList.php +++ b/lib/internal/Magento/Framework/Filesystem/DirectoryList.php @@ -97,6 +97,10 @@ public function __construct($root, array $config = []) $this->root = $this->normalizePath($root); $this->directories = static::getDefaultConfig(); $this->directories[self::SYS_TMP] = [self::PATH => realpath(sys_get_temp_dir())]; + $uploadTmpDir = ini_get('upload_tmp_dir'); + if ($uploadTmpDir) { + $this->directories[self::SYS_TMP] = [self::PATH => realpath($uploadTmpDir)]; + } // inject custom values from constructor foreach ($this->directories as $code => $dir) { From 63e268c1332f8040cdb8b5c4583125b6e9e25dd4 Mon Sep 17 00:00:00 2001 From: Andrey Nikolaev <tonikolaev@gmail.com> Date: Wed, 13 Feb 2019 20:29:51 +0300 Subject: [PATCH 0880/1295] Simplify code by using ternary operator. --- .../Magento/Framework/Filesystem/DirectoryList.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/internal/Magento/Framework/Filesystem/DirectoryList.php b/lib/internal/Magento/Framework/Filesystem/DirectoryList.php index 96818e4ff3b70..1d73b79398d27 100644 --- a/lib/internal/Magento/Framework/Filesystem/DirectoryList.php +++ b/lib/internal/Magento/Framework/Filesystem/DirectoryList.php @@ -96,11 +96,8 @@ public function __construct($root, array $config = []) static::validate($config); $this->root = $this->normalizePath($root); $this->directories = static::getDefaultConfig(); - $this->directories[self::SYS_TMP] = [self::PATH => realpath(sys_get_temp_dir())]; - $uploadTmpDir = ini_get('upload_tmp_dir'); - if ($uploadTmpDir) { - $this->directories[self::SYS_TMP] = [self::PATH => realpath($uploadTmpDir)]; - } + $sysTmpPath = ini_get('upload_tmp_dir') ? ini_get('upload_tmp_dir') : sys_get_temp_dir(); + $this->directories[self::SYS_TMP] = [self::PATH => realpath($sysTmpPath)]; // inject custom values from constructor foreach ($this->directories as $code => $dir) { From 4b3127e50242bea2e2e6bf6ea2b137470b219684 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Mon, 4 Mar 2019 17:02:18 +0200 Subject: [PATCH 0881/1295] Fix static tests. --- lib/internal/Magento/Framework/Filesystem/DirectoryList.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Filesystem/DirectoryList.php b/lib/internal/Magento/Framework/Filesystem/DirectoryList.php index 1d73b79398d27..ef27cc19c4697 100644 --- a/lib/internal/Magento/Framework/Filesystem/DirectoryList.php +++ b/lib/internal/Magento/Framework/Filesystem/DirectoryList.php @@ -8,6 +8,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Framework\Filesystem; /** @@ -96,7 +97,7 @@ public function __construct($root, array $config = []) static::validate($config); $this->root = $this->normalizePath($root); $this->directories = static::getDefaultConfig(); - $sysTmpPath = ini_get('upload_tmp_dir') ? ini_get('upload_tmp_dir') : sys_get_temp_dir(); + $sysTmpPath = get_cfg_var('upload_tmp_dir') ?: sys_get_temp_dir(); $this->directories[self::SYS_TMP] = [self::PATH => realpath($sysTmpPath)]; // inject custom values from constructor From ec51033a066a2684350d30bab3b459f0b7f04960 Mon Sep 17 00:00:00 2001 From: Jamie Pates <cyanoxide@gmail.com> Date: Fri, 8 Mar 2019 15:56:30 +0000 Subject: [PATCH 0882/1295] Improve swatch table overflow handling Add overflow:auto to the three swatch tables to allow for scrolling if the amount of inputs exceeds the available space and increase the min width of inputs (150px) within the table for better usability. Intentionally left original input width (50px) as a fallback. --- .../Swatches/view/adminhtml/web/css/swatches.css | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Swatches/view/adminhtml/web/css/swatches.css b/app/code/Magento/Swatches/view/adminhtml/web/css/swatches.css index 02a3f0324d955..67a5537e3274f 100644 --- a/app/code/Magento/Swatches/view/adminhtml/web/css/swatches.css +++ b/app/code/Magento/Swatches/view/adminhtml/web/css/swatches.css @@ -150,7 +150,19 @@ } .col-swatch-min-width { - min-width: 30px; + min-width: 65px; +} + +[class^=swatch-col], +[class^=col-]:not(.col-draggable):not(.col-default) { + min-width: 150px; +} + +#swatch-visual-options-panel, +#swatch-text-options-panel, +#manage-options-panel { + overflow: auto; + width: 100%; } .swatches-visual-col.unavailable:after { From f80d38218d0d42a297fe6f6727f5d63191093fad Mon Sep 17 00:00:00 2001 From: Mahesh Singh <mahesh721@webkul.com> Date: Fri, 15 Feb 2019 23:56:52 +0530 Subject: [PATCH 0883/1295] sort order added to downloable product links column --- .../Product/Form/Modifier/Links.php | 36 ++++++++++++------- .../Product/Form/Modifier/Samples.php | 12 ++++--- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Links.php b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Links.php index a352c4bdf7bc3..e367405416f0d 100644 --- a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Links.php +++ b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Links.php @@ -208,12 +208,12 @@ protected function getRecord() 'children', $record, [ - 'container_link_title' => $this->getTitleColumn(), - 'container_link_price' => $this->getPriceColumn(), - 'container_file' => $this->getFileColumn(), - 'container_sample' => $this->getSampleColumn(), - 'is_shareable' => $this->getShareableColumn(), - 'max_downloads' => $this->getMaxDownloadsColumn(), + 'container_link_title' => $this->getTitleColumn(10), + 'container_link_price' => $this->getPriceColumn(20), + 'container_file' => $this->getFileColumn(30), + 'container_sample' => $this->getSampleColumn(40), + 'is_shareable' => $this->getShareableColumn(50), + 'max_downloads' => $this->getMaxDownloadsColumn(60), 'position' => $recordPosition, 'action_delete' => $recordActionDelete, ] @@ -221,9 +221,10 @@ protected function getRecord() } /** + * @param int $sortOrder * @return array */ - protected function getTitleColumn() + protected function getTitleColumn($sortOrder) { $titleContainer['arguments']['data']['config'] = [ 'componentType' => Container::NAME, @@ -232,6 +233,7 @@ protected function getTitleColumn() 'label' => __('Title'), 'showLabel' => false, 'dataScope' => '', + 'sortOrder' => $sortOrder, ]; $titleField['arguments']['data']['config'] = [ 'formElement' => Form\Element\Input::NAME, @@ -247,9 +249,10 @@ protected function getTitleColumn() } /** + * @param int $sortOrder * @return array */ - protected function getPriceColumn() + protected function getPriceColumn($sortOrder) { $priceContainer['arguments']['data']['config'] = [ 'componentType' => Container::NAME, @@ -258,6 +261,7 @@ protected function getPriceColumn() 'label' => __('Price'), 'showLabel' => false, 'dataScope' => '', + 'sortOrder' => $sortOrder, ]; $priceField['arguments']['data']['config'] = [ 'formElement' => Form\Element\Input::NAME, @@ -281,9 +285,10 @@ protected function getPriceColumn() } /** + * @param int $sortOrder * @return array */ - protected function getFileColumn() + protected function getFileColumn($sortOrder) { $fileContainer['arguments']['data']['config'] = [ 'componentType' => Container::NAME, @@ -292,6 +297,7 @@ protected function getFileColumn() 'label' => __('File'), 'showLabel' => false, 'dataScope' => '', + 'sortOrder' => $sortOrder, ]; $fileTypeField['arguments']['data']['config'] = [ 'formElement' => Form\Element\Select::NAME, @@ -344,9 +350,10 @@ protected function getFileColumn() } /** + * @param int $sortOrder * @return array */ - protected function getSampleColumn() + protected function getSampleColumn($sortOrder) { $sampleContainer['arguments']['data']['config'] = [ 'componentType' => Container::NAME, @@ -355,6 +362,7 @@ protected function getSampleColumn() 'label' => __('Sample'), 'showLabel' => false, 'dataScope' => '', + 'sortOrder' => $sortOrder, ]; $sampleTypeField['arguments']['data']['config'] = [ 'formElement' => Form\Element\Select::NAME, @@ -403,9 +411,10 @@ protected function getSampleColumn() } /** + * @param int $sortOrder * @return array */ - protected function getShareableColumn() + protected function getShareableColumn($sortOrder) { $shareableField['arguments']['data']['config'] = [ 'label' => __('Shareable'), @@ -413,6 +422,7 @@ protected function getShareableColumn() 'componentType' => Form\Field::NAME, 'dataType' => Form\Element\DataType\Number::NAME, 'dataScope' => 'is_shareable', + 'sortOrder' => $sortOrder, 'options' => $this->shareable->toOptionArray(), ]; @@ -420,9 +430,10 @@ protected function getShareableColumn() } /** + * @param int $sortOrder * @return array */ - protected function getMaxDownloadsColumn() + protected function getMaxDownloadsColumn($sortOrder) { $maxDownloadsContainer['arguments']['data']['config'] = [ 'componentType' => Container::NAME, @@ -431,6 +442,7 @@ protected function getMaxDownloadsColumn() 'label' => __('Max. Downloads'), 'showLabel' => false, 'dataScope' => '', + 'sortOrder' => $sortOrder, ]; $numberOfDownloadsField['arguments']['data']['config'] = [ 'formElement' => Form\Element\Input::NAME, diff --git a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Samples.php b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Samples.php index 1587163ba8121..0dd94f2584218 100644 --- a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Samples.php +++ b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Samples.php @@ -183,8 +183,8 @@ protected function getRecord() 'children', $record, [ - 'container_sample_title' => $this->getTitleColumn(), - 'container_sample' => $this->getSampleColumn(), + 'container_sample_title' => $this->getTitleColumn(10), + 'container_sample' => $this->getSampleColumn(20), 'position' => $recordPosition, 'action_delete' => $recordActionDelete, ] @@ -192,9 +192,10 @@ protected function getRecord() } /** + * @param int $sortOrder * @return array */ - protected function getTitleColumn() + protected function getTitleColumn($sortOrder) { $titleContainer['arguments']['data']['config'] = [ 'componentType' => Container::NAME, @@ -203,6 +204,7 @@ protected function getTitleColumn() 'showLabel' => false, 'label' => __('Title'), 'dataScope' => '', + 'sortOrder' => $sortOrder, ]; $titleField['arguments']['data']['config'] = [ 'formElement' => Form\Element\Input::NAME, @@ -218,9 +220,10 @@ protected function getTitleColumn() } /** + * @param int $sortOrder * @return array */ - protected function getSampleColumn() + protected function getSampleColumn($sortOrder) { $sampleContainer['arguments']['data']['config'] = [ 'componentType' => Container::NAME, @@ -229,6 +232,7 @@ protected function getSampleColumn() 'label' => __('File'), 'showLabel' => false, 'dataScope' => '', + 'sortOrder' => $sortOrder, ]; $sampleType['arguments']['data']['config'] = [ 'formElement' => Form\Element\Select::NAME, From b7ad0754b9f78510ec1f47ddf866d70ed05f91fc Mon Sep 17 00:00:00 2001 From: Mahesh Singh <mahesh721@webkul.com> Date: Tue, 19 Feb 2019 22:42:35 +0530 Subject: [PATCH 0884/1295] backward compatibilty fixed --- .../Product/Form/Modifier/Links.php | 42 ++++++++----------- .../Product/Form/Modifier/Samples.php | 14 +++---- 2 files changed, 24 insertions(+), 32 deletions(-) diff --git a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Links.php b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Links.php index e367405416f0d..e4889b160963f 100644 --- a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Links.php +++ b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Links.php @@ -208,12 +208,12 @@ protected function getRecord() 'children', $record, [ - 'container_link_title' => $this->getTitleColumn(10), - 'container_link_price' => $this->getPriceColumn(20), - 'container_file' => $this->getFileColumn(30), - 'container_sample' => $this->getSampleColumn(40), - 'is_shareable' => $this->getShareableColumn(50), - 'max_downloads' => $this->getMaxDownloadsColumn(60), + 'container_link_title' => $this->getTitleColumn(), + 'container_link_price' => $this->getPriceColumn(), + 'container_file' => $this->getFileColumn(), + 'container_sample' => $this->getSampleColumn(), + 'is_shareable' => $this->getShareableColumn(), + 'max_downloads' => $this->getMaxDownloadsColumn(), 'position' => $recordPosition, 'action_delete' => $recordActionDelete, ] @@ -221,10 +221,9 @@ protected function getRecord() } /** - * @param int $sortOrder * @return array */ - protected function getTitleColumn($sortOrder) + protected function getTitleColumn() { $titleContainer['arguments']['data']['config'] = [ 'componentType' => Container::NAME, @@ -233,7 +232,7 @@ protected function getTitleColumn($sortOrder) 'label' => __('Title'), 'showLabel' => false, 'dataScope' => '', - 'sortOrder' => $sortOrder, + 'sortOrder' => 10, ]; $titleField['arguments']['data']['config'] = [ 'formElement' => Form\Element\Input::NAME, @@ -249,10 +248,9 @@ protected function getTitleColumn($sortOrder) } /** - * @param int $sortOrder * @return array */ - protected function getPriceColumn($sortOrder) + protected function getPriceColumn() { $priceContainer['arguments']['data']['config'] = [ 'componentType' => Container::NAME, @@ -261,7 +259,7 @@ protected function getPriceColumn($sortOrder) 'label' => __('Price'), 'showLabel' => false, 'dataScope' => '', - 'sortOrder' => $sortOrder, + 'sortOrder' => 20, ]; $priceField['arguments']['data']['config'] = [ 'formElement' => Form\Element\Input::NAME, @@ -285,10 +283,9 @@ protected function getPriceColumn($sortOrder) } /** - * @param int $sortOrder * @return array */ - protected function getFileColumn($sortOrder) + protected function getFileColumn() { $fileContainer['arguments']['data']['config'] = [ 'componentType' => Container::NAME, @@ -297,7 +294,7 @@ protected function getFileColumn($sortOrder) 'label' => __('File'), 'showLabel' => false, 'dataScope' => '', - 'sortOrder' => $sortOrder, + 'sortOrder' => 30, ]; $fileTypeField['arguments']['data']['config'] = [ 'formElement' => Form\Element\Select::NAME, @@ -350,10 +347,9 @@ protected function getFileColumn($sortOrder) } /** - * @param int $sortOrder * @return array */ - protected function getSampleColumn($sortOrder) + protected function getSampleColumn() { $sampleContainer['arguments']['data']['config'] = [ 'componentType' => Container::NAME, @@ -362,7 +358,7 @@ protected function getSampleColumn($sortOrder) 'label' => __('Sample'), 'showLabel' => false, 'dataScope' => '', - 'sortOrder' => $sortOrder, + 'sortOrder' => 40, ]; $sampleTypeField['arguments']['data']['config'] = [ 'formElement' => Form\Element\Select::NAME, @@ -411,10 +407,9 @@ protected function getSampleColumn($sortOrder) } /** - * @param int $sortOrder * @return array */ - protected function getShareableColumn($sortOrder) + protected function getShareableColumn() { $shareableField['arguments']['data']['config'] = [ 'label' => __('Shareable'), @@ -422,7 +417,7 @@ protected function getShareableColumn($sortOrder) 'componentType' => Form\Field::NAME, 'dataType' => Form\Element\DataType\Number::NAME, 'dataScope' => 'is_shareable', - 'sortOrder' => $sortOrder, + 'sortOrder' => 50, 'options' => $this->shareable->toOptionArray(), ]; @@ -430,10 +425,9 @@ protected function getShareableColumn($sortOrder) } /** - * @param int $sortOrder * @return array */ - protected function getMaxDownloadsColumn($sortOrder) + protected function getMaxDownloadsColumn() { $maxDownloadsContainer['arguments']['data']['config'] = [ 'componentType' => Container::NAME, @@ -442,7 +436,7 @@ protected function getMaxDownloadsColumn($sortOrder) 'label' => __('Max. Downloads'), 'showLabel' => false, 'dataScope' => '', - 'sortOrder' => $sortOrder, + 'sortOrder' => 60, ]; $numberOfDownloadsField['arguments']['data']['config'] = [ 'formElement' => Form\Element\Input::NAME, diff --git a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Samples.php b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Samples.php index 0dd94f2584218..b12c415b0b0b1 100644 --- a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Samples.php +++ b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Samples.php @@ -183,8 +183,8 @@ protected function getRecord() 'children', $record, [ - 'container_sample_title' => $this->getTitleColumn(10), - 'container_sample' => $this->getSampleColumn(20), + 'container_sample_title' => $this->getTitleColumn(), + 'container_sample' => $this->getSampleColumn(), 'position' => $recordPosition, 'action_delete' => $recordActionDelete, ] @@ -192,10 +192,9 @@ protected function getRecord() } /** - * @param int $sortOrder * @return array */ - protected function getTitleColumn($sortOrder) + protected function getTitleColumn() { $titleContainer['arguments']['data']['config'] = [ 'componentType' => Container::NAME, @@ -204,7 +203,7 @@ protected function getTitleColumn($sortOrder) 'showLabel' => false, 'label' => __('Title'), 'dataScope' => '', - 'sortOrder' => $sortOrder, + 'sortOrder' => 10, ]; $titleField['arguments']['data']['config'] = [ 'formElement' => Form\Element\Input::NAME, @@ -220,10 +219,9 @@ protected function getTitleColumn($sortOrder) } /** - * @param int $sortOrder * @return array */ - protected function getSampleColumn($sortOrder) + protected function getSampleColumn() { $sampleContainer['arguments']['data']['config'] = [ 'componentType' => Container::NAME, @@ -232,7 +230,7 @@ protected function getSampleColumn($sortOrder) 'label' => __('File'), 'showLabel' => false, 'dataScope' => '', - 'sortOrder' => $sortOrder, + 'sortOrder' => 20, ]; $sampleType['arguments']['data']['config'] = [ 'formElement' => Form\Element\Select::NAME, From a9016e9a58187fa8bb81cfecd6e0bc5bfdd6ce90 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Mon, 4 Mar 2019 15:31:18 +0200 Subject: [PATCH 0885/1295] Fix static tests. --- .../Product/Form/Modifier/Links.php | 32 +++++++++++++++---- .../Product/Form/Modifier/Samples.php | 22 +++++++++---- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Links.php b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Links.php index e4889b160963f..9ab664cb27839 100644 --- a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Links.php +++ b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Links.php @@ -3,22 +3,24 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Downloadable\Ui\DataProvider\Product\Form\Modifier; -use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier; use Magento\Catalog\Model\Locator\LocatorInterface; +use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier; use Magento\Downloadable\Model\Product\Type; -use Magento\Downloadable\Model\Source\TypeUpload; use Magento\Downloadable\Model\Source\Shareable; -use Magento\Store\Model\StoreManagerInterface; +use Magento\Downloadable\Model\Source\TypeUpload; use Magento\Framework\Stdlib\ArrayManager; -use Magento\Ui\Component\DynamicRows; use Magento\Framework\UrlInterface; +use Magento\Store\Model\StoreManagerInterface; use Magento\Ui\Component\Container; +use Magento\Ui\Component\DynamicRows; use Magento\Ui\Component\Form; /** - * Class adds a grid with links + * Class adds a grid with links. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Links extends AbstractModifier @@ -86,7 +88,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function modifyData(array $data) { @@ -101,7 +103,7 @@ public function modifyData(array $data) } /** - * {@inheritdoc} + * @inheritdoc * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function modifyMeta(array $meta) @@ -160,6 +162,8 @@ public function modifyMeta(array $meta) } /** + * Get dynamic rows meta. + * * @return array */ protected function getDynamicRows() @@ -180,6 +184,8 @@ protected function getDynamicRows() } /** + * Get single link record meta. + * * @return array */ protected function getRecord() @@ -221,6 +227,8 @@ protected function getRecord() } /** + * Get link title meta. + * * @return array */ protected function getTitleColumn() @@ -248,6 +256,8 @@ protected function getTitleColumn() } /** + * Get link price meta. + * * @return array */ protected function getPriceColumn() @@ -283,6 +293,8 @@ protected function getPriceColumn() } /** + * Get link file element meta. + * * @return array */ protected function getFileColumn() @@ -347,6 +359,8 @@ protected function getFileColumn() } /** + * Get sample container meta. + * * @return array */ protected function getSampleColumn() @@ -407,6 +421,8 @@ protected function getSampleColumn() } /** + * Get link "is sharable" element meta. + * * @return array */ protected function getShareableColumn() @@ -425,6 +441,8 @@ protected function getShareableColumn() } /** + * Get link "max downloads" element meta. + * * @return array */ protected function getMaxDownloadsColumn() diff --git a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Samples.php b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Samples.php index b12c415b0b0b1..3890ee5b9e2b2 100644 --- a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Samples.php +++ b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Samples.php @@ -3,21 +3,23 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Downloadable\Ui\DataProvider\Product\Form\Modifier; -use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier; use Magento\Catalog\Model\Locator\LocatorInterface; +use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier; use Magento\Downloadable\Model\Product\Type; use Magento\Downloadable\Model\Source\TypeUpload; -use Magento\Store\Model\StoreManagerInterface; use Magento\Framework\Stdlib\ArrayManager; -use Magento\Ui\Component\DynamicRows; use Magento\Framework\UrlInterface; +use Magento\Store\Model\StoreManagerInterface; use Magento\Ui\Component\Container; +use Magento\Ui\Component\DynamicRows; use Magento\Ui\Component\Form; /** - * Class adds a grid with samples + * Class adds a grid with samples. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Samples extends AbstractModifier @@ -77,7 +79,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function modifyData(array $data) { @@ -90,7 +92,7 @@ public function modifyData(array $data) } /** - * {@inheritdoc} + * @inheritdoc * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function modifyMeta(array $meta) @@ -135,6 +137,8 @@ public function modifyMeta(array $meta) } /** + * Get sample rows meta. + * * @return array */ protected function getDynamicRows() @@ -155,6 +159,8 @@ protected function getDynamicRows() } /** + * Get single sample row meta. + * * @return array */ protected function getRecord() @@ -192,6 +198,8 @@ protected function getRecord() } /** + * Get sample title meta. + * * @return array */ protected function getTitleColumn() @@ -219,6 +227,8 @@ protected function getTitleColumn() } /** + * Get sample element meta. + * * @return array */ protected function getSampleColumn() From 6cb1f94d4396de971014683b5c2ff1a65b9695ff Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Mon, 11 Mar 2019 15:00:31 +0200 Subject: [PATCH 0886/1295] MAGETWO-95020: Clicking on a Root Category causes all other Root Categories to be expanded to the top-level categories --- .../Catalog/Block/Adminhtml/Category/Tree.php | 34 +++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php b/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php index ed615b41644e2..020666838a79a 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php @@ -73,7 +73,7 @@ public function __construct( } /** - * @return void + * @inheritdoc */ protected function _construct() { @@ -82,7 +82,7 @@ protected function _construct() } /** - * @return $this + * @inheritdoc */ protected function _prepareLayout() { @@ -182,6 +182,8 @@ public function getSuggestedCategoriesJson($namePart) } /** + * Get add root button html + * * @return string */ public function getAddRootButtonHtml() @@ -190,6 +192,8 @@ public function getAddRootButtonHtml() } /** + * Get add sub button html + * * @return string */ public function getAddSubButtonHtml() @@ -198,6 +202,8 @@ public function getAddSubButtonHtml() } /** + * Get expand button html + * * @return string */ public function getExpandButtonHtml() @@ -206,6 +212,8 @@ public function getExpandButtonHtml() } /** + * Get collapse button html + * * @return string */ public function getCollapseButtonHtml() @@ -214,6 +222,8 @@ public function getCollapseButtonHtml() } /** + * Get store switcher + * * @return string */ public function getStoreSwitcherHtml() @@ -222,6 +232,8 @@ public function getStoreSwitcherHtml() } /** + * Get loader tree url + * * @param bool|null $expanded * @return string */ @@ -235,6 +247,8 @@ public function getLoadTreeUrl($expanded = null) } /** + * Get nodes url + * * @return string */ public function getNodesUrl() @@ -243,6 +257,8 @@ public function getNodesUrl() } /** + * Get switcher tree url + * * @return string */ public function getSwitchTreeUrl() @@ -254,6 +270,8 @@ public function getSwitchTreeUrl() } /** + * Get is was expanded + * * @return bool * @SuppressWarnings(PHPMD.BooleanGetMethodName) */ @@ -263,6 +281,8 @@ public function getIsWasExpanded() } /** + * Get move url + * * @return string */ public function getMoveUrl() @@ -271,6 +291,8 @@ public function getMoveUrl() } /** + * Get tree + * * @param mixed|null $parenNodeCategory * @return array */ @@ -282,6 +304,8 @@ public function getTree($parenNodeCategory = null) } /** + * Get tree json + * * @param mixed|null $parenNodeCategory * @return string */ @@ -367,7 +391,7 @@ protected function _getNodeJson($node, $level = 0) } } - if ($isParent || $node->getLevel() < 2) { + if ($isParent || $node->getLevel() < 1) { $item['expanded'] = true; } @@ -390,6 +414,8 @@ public function buildNodeName($node) } /** + * Is category movable + * * @param Node|array $node * @return bool */ @@ -403,6 +429,8 @@ protected function _isCategoryMoveable($node) } /** + * Is parent selected category + * * @param Node|array $node * @return bool */ From adf4425136c276f0e482f154198c4f39cc15d00d Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Mon, 11 Mar 2019 16:40:15 +0200 Subject: [PATCH 0887/1295] Made configurable product variations table cell label hidden --- .../Form/Modifier/ConfigurablePanel.php | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/ConfigurablePanel.php b/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/ConfigurablePanel.php index 9fd225e8acaab..dcdffddbeeec0 100644 --- a/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/ConfigurablePanel.php +++ b/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/ConfigurablePanel.php @@ -5,14 +5,14 @@ */ namespace Magento\ConfigurableProduct\Ui\DataProvider\Product\Form\Modifier; +use Magento\Catalog\Model\Locator\LocatorInterface; use Magento\Catalog\Model\Product\Attribute\Backend\Sku; use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier; +use Magento\Framework\UrlInterface; use Magento\Ui\Component\Container; -use Magento\Ui\Component\Form; use Magento\Ui\Component\DynamicRows; +use Magento\Ui\Component\Form; use Magento\Ui\Component\Modal; -use Magento\Framework\UrlInterface; -use Magento\Catalog\Model\Locator\LocatorInterface; /** * Data provider for Configurable panel @@ -90,7 +90,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function modifyData(array $data) { @@ -98,7 +98,7 @@ public function modifyData(array $data) } /** - * {@inheritdoc} + * @inheritdoc * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function modifyMeta(array $meta) @@ -197,7 +197,7 @@ public function modifyMeta(array $meta) 'autoRender' => false, 'componentType' => 'insertListing', 'component' => 'Magento_ConfigurableProduct/js' - .'/components/associated-product-insert-listing', + . '/components/associated-product-insert-listing', 'dataScope' => $this->associatedListingPrefix . static::ASSOCIATED_PRODUCT_LISTING, 'externalProvider' => $this->associatedListingPrefix @@ -328,14 +328,12 @@ protected function getButtonSet() 'component' => 'Magento_Ui/js/form/components/button', 'actions' => [ [ - 'targetName' => - $this->dataScopeName . '.configurableModal', + 'targetName' => $this->dataScopeName . '.configurableModal', 'actionName' => 'trigger', 'params' => ['active', true], ], [ - 'targetName' => - $this->dataScopeName . '.configurableModal', + 'targetName' => $this->dataScopeName . '.configurableModal', 'actionName' => 'openModal', ], ], @@ -574,6 +572,7 @@ protected function getColumn( 'dataType' => Form\Element\DataType\Text::NAME, 'dataScope' => $name, 'visibleIfCanEdit' => false, + 'labelVisible' => false, 'imports' => [ 'visible' => '!${$.provider}:${$.parentScope}.canEdit' ], @@ -592,6 +591,7 @@ protected function getColumn( 'component' => 'Magento_Ui/js/form/components/group', 'label' => $label, 'dataScope' => '', + 'showLabel' => false ]; $container['children'] = [ $name . '_edit' => $fieldEdit, From b4283aa17b03927d8f2b0983b9b519b684610b5e Mon Sep 17 00:00:00 2001 From: Jitheesh <jitheeshvo@gmail.com> Date: Thu, 28 Feb 2019 15:04:11 +0530 Subject: [PATCH 0888/1295] Setting default sorting #21493 - Fixed default sort direction --- app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Orders.php | 3 ++- .../Magento/Customer/Block/Adminhtml/Edit/Tab/View/Cart.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Orders.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Orders.php index bb190260e4776..c1266febff99d 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Orders.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Orders.php @@ -63,7 +63,8 @@ protected function _construct() { parent::_construct(); $this->setId('customer_orders_grid'); - $this->setDefaultSort('created_at', 'desc'); + $this->setDefaultSort('created_at'); + $this->setDefaultDir('desc'); $this->setUseAjax(true); } diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/View/Cart.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/View/Cart.php index d0973d3baf383..988a157805b36 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/View/Cart.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/View/Cart.php @@ -77,7 +77,8 @@ protected function _construct() { parent::_construct(); $this->setId('customer_view_cart_grid'); - $this->setDefaultSort('added_at', 'desc'); + $this->setDefaultSort('added_at'); + $this->setDefaultDir('desc'); $this->setSortable(false); $this->setPagerVisibility(false); $this->setFilterVisibility(false); From a4a487829684c76c3b5dfd7e5786a9352dfb915f Mon Sep 17 00:00:00 2001 From: Sarfaraz Bheda <sarfaraz.bheda@krishtechnolabs.com> Date: Sat, 24 Nov 2018 16:16:25 +0530 Subject: [PATCH 0889/1295] 19276 - Fixed price renderer issue --- .../Swatches/view/frontend/web/js/swatch-renderer.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js index 3e28982ad44d3..2cb8bd1c25d46 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 @@ -918,7 +918,8 @@ define([ $productPrice = $product.find(this.options.selectorProductPrice), options = _.object(_.keys($widget.optionsMap), {}), result, - tierPriceHtml; + tierPriceHtml, + isShow; $widget.element.find('.' + $widget.options.classes.attributeClass + '[option-selected]').each(function () { var attributeId = $(this).attr('attribute-id'); @@ -935,11 +936,9 @@ define([ } ); - if (typeof result != 'undefined' && result.oldPrice.amount !== result.finalPrice.amount) { - $(this.options.slyOldPriceSelector).show(); - } else { - $(this.options.slyOldPriceSelector).hide(); - } + isShow = typeof result != 'undefined' && result.oldPrice.amount !== result.finalPrice.amount; + + $product.find(this.options.slyOldPriceSelector)[isShow ? 'show' : 'hide'](); if (typeof result != 'undefined' && result.tierPrices.length) { if (this.options.tierPriceTemplate) { From ec22cec535a36c0708cf2d0ce7fae97a11c4a644 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Mon, 4 Mar 2019 14:28:51 -0600 Subject: [PATCH 0890/1295] ENGCOM-4389: Adjust module naming convention to be compatible with 2.3.1 --- .../static/testsuite/Magento/Test/Integrity/ComposerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php index 6fc84486c626b..2f1ab7a75bc83 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php @@ -452,7 +452,7 @@ private function convertModuleToPackageName($moduleName) { list($vendor, $name) = explode('_', $moduleName, 2); $package = 'module'; - foreach (preg_split('/([A-Z][a-z\d]+)/', $name, -1, PREG_SPLIT_DELIM_CAPTURE) as $chunk) { + foreach (preg_split('/([A-Z\d][a-z]*)/', $name, -1, PREG_SPLIT_DELIM_CAPTURE) as $chunk) { $package .= $chunk ? "-{$chunk}" : ''; } return strtolower("{$vendor}/{$package}"); From 46810b0771c4b646c6791f17347848082f3ec0b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20F=C3=BChr?= <d.fuehr@techdivision.com> Date: Wed, 30 Jan 2019 16:03:13 +0100 Subject: [PATCH 0891/1295] 14857: prevent cache drop for frontend caches on sitemap generation https://github.com/magento/magento2/issues/14857 Introduces a (dummy) cache tag for frontend in sitemap and robots generation. This prevents the default and page_cache to be dropped completely. The cache tag for full_page cache is not modified. --- app/code/Magento/Robots/Model/Config/Value.php | 2 +- app/code/Magento/Sitemap/Model/Sitemap.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Robots/Model/Config/Value.php b/app/code/Magento/Robots/Model/Config/Value.php index 83c21d6602fca..0619e9fd88a78 100644 --- a/app/code/Magento/Robots/Model/Config/Value.php +++ b/app/code/Magento/Robots/Model/Config/Value.php @@ -35,7 +35,7 @@ class Value extends ConfigValue implements IdentityInterface * @var string * @since 100.2.0 */ - protected $_cacheTag = true; + protected $_cacheTag = [self::CACHE_TAG]; /** * @var StoreResolver diff --git a/app/code/Magento/Sitemap/Model/Sitemap.php b/app/code/Magento/Sitemap/Model/Sitemap.php index dda1697da7fdf..823e403ff8fa2 100644 --- a/app/code/Magento/Sitemap/Model/Sitemap.php +++ b/app/code/Magento/Sitemap/Model/Sitemap.php @@ -160,7 +160,7 @@ class Sitemap extends \Magento\Framework\Model\AbstractModel implements \Magento * @var string * @since 100.2.0 */ - protected $_cacheTag = true; + protected $_cacheTag = [Value::CACHE_TAG]; /** * Last mode min timestamp value From c5993ce4e0c1b4a9e460d45734a63cebaea7488a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20F=C3=BChr?= <d.fuehr@techdivision.com> Date: Thu, 31 Jan 2019 13:58:32 +0100 Subject: [PATCH 0892/1295] amend touched DocBlocks --- app/code/Magento/Robots/Model/Config/Value.php | 3 +-- app/code/Magento/Sitemap/Model/Sitemap.php | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Robots/Model/Config/Value.php b/app/code/Magento/Robots/Model/Config/Value.php index 0619e9fd88a78..e520810ee5ef9 100644 --- a/app/code/Magento/Robots/Model/Config/Value.php +++ b/app/code/Magento/Robots/Model/Config/Value.php @@ -30,9 +30,8 @@ class Value extends ConfigValue implements IdentityInterface const CACHE_TAG = 'robots'; /** - * Model cache tag for clear cache in after save and after delete + * @inheritdoc * - * @var string * @since 100.2.0 */ protected $_cacheTag = [self::CACHE_TAG]; diff --git a/app/code/Magento/Sitemap/Model/Sitemap.php b/app/code/Magento/Sitemap/Model/Sitemap.php index 823e403ff8fa2..ff95276827336 100644 --- a/app/code/Magento/Sitemap/Model/Sitemap.php +++ b/app/code/Magento/Sitemap/Model/Sitemap.php @@ -155,9 +155,8 @@ class Sitemap extends \Magento\Framework\Model\AbstractModel implements \Magento protected $dateTime; /** - * Model cache tag for clear cache in after save and after delete + * @inheritdoc * - * @var string * @since 100.2.0 */ protected $_cacheTag = [Value::CACHE_TAG]; From a69a3d01a9e94e98ec224cf575c4b536ded92a4a Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Fri, 1 Mar 2019 15:26:43 +0200 Subject: [PATCH 0893/1295] Fix static tests. --- app/code/Magento/Robots/Model/Config/Value.php | 1 + app/code/Magento/Sitemap/Model/Sitemap.php | 1 + 2 files changed, 2 insertions(+) diff --git a/app/code/Magento/Robots/Model/Config/Value.php b/app/code/Magento/Robots/Model/Config/Value.php index e520810ee5ef9..4c80588d814f6 100644 --- a/app/code/Magento/Robots/Model/Config/Value.php +++ b/app/code/Magento/Robots/Model/Config/Value.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Robots\Model\Config; use Magento\Framework\App\Cache\TypeListInterface; diff --git a/app/code/Magento/Sitemap/Model/Sitemap.php b/app/code/Magento/Sitemap/Model/Sitemap.php index ff95276827336..7279f6eda85d7 100644 --- a/app/code/Magento/Sitemap/Model/Sitemap.php +++ b/app/code/Magento/Sitemap/Model/Sitemap.php @@ -1,4 +1,5 @@ <?php + /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. From 9a839d176f1d8d1ec46d5ce47f7a8ba6cea76067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20F=C3=BChr?= <d.fuehr@techdivision.com> Date: Tue, 22 Jan 2019 11:43:21 +0100 Subject: [PATCH 0894/1295] 19117: fix performance leak in salesrule collection Github Issue: https://github.com/magento/magento2/issues/19117 Refactored sql query that created a huge temporary table for each request, when a greater amount of salesrules and coupon codes exists in database. The sorting of this table took a lot of cpu time. The statement now consists of two subselects that drill down the remaining lines as far as possible, so that the remaining temporary table is minimal and easily sorted. example: for 2,000 salesrules and 3,000,000 coupon codes the original query took about 2.4 seconds (mbp, server, aws). the optimized query takes about 5ms (about 100ms on aws). --- .../Model/ResourceModel/Rule/Collection.php | 185 ++++++++++-------- 1 file changed, 101 insertions(+), 84 deletions(-) diff --git a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php index 5e6f3847c8e31..352b5d4c809c9 100644 --- a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php +++ b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php @@ -107,12 +107,15 @@ protected function mapAssociatedEntities($entityType, $objectField) $associatedEntities = $this->getConnection()->fetchAll($select); - array_map(function ($associatedEntity) use ($entityInfo, $ruleIdField, $objectField) { - $item = $this->getItemByColumnValue($ruleIdField, $associatedEntity[$ruleIdField]); - $itemAssociatedValue = $item->getData($objectField) === null ? [] : $item->getData($objectField); - $itemAssociatedValue[] = $associatedEntity[$entityInfo['entity_id_field']]; - $item->setData($objectField, $itemAssociatedValue); - }, $associatedEntities); + array_map( + function ($associatedEntity) use ($entityInfo, $ruleIdField, $objectField) { + $item = $this->getItemByColumnValue($ruleIdField, $associatedEntity[$ruleIdField]); + $itemAssociatedValue = $item->getData($objectField) === null ? [] : $item->getData($objectField); + $itemAssociatedValue[] = $associatedEntity[$entityInfo['entity_id_field']]; + $item->setData($objectField, $itemAssociatedValue); + }, + $associatedEntities + ); } /** @@ -144,6 +147,7 @@ protected function _afterLoad() * @use $this->addWebsiteGroupDateFilter() * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @return $this + * @throws \Zend_Db_Select_Exception */ public function setValidationFilter( $websiteId, @@ -153,32 +157,21 @@ public function setValidationFilter( Address $address = null ) { if (!$this->getFlag('validation_filter')) { - /* We need to overwrite joinLeft if coupon is applied */ - $this->getSelect()->reset(); - parent::_initSelect(); - $this->addWebsiteGroupDateFilter($websiteId, $customerGroupId, $now); - $select = $this->getSelect(); + $this->prepareSelect($websiteId, $customerGroupId, $now); - $connection = $this->getConnection(); - if (strlen($couponCode)) { - $noCouponWhereCondition = $connection->quoteInto( - 'main_table.coupon_type = ?', - \Magento\SalesRule\Model\Rule::COUPON_TYPE_NO_COUPON - ); - $relatedRulesIds = $this->getCouponRelatedRuleIds($couponCode); - - $select->where( - $noCouponWhereCondition . ' OR main_table.rule_id IN (?)', - $relatedRulesIds, - Select::TYPE_CONDITION - ); + $noCouponRules = $this->getNoCouponCodeSelect(); + + if ($couponCode) { + $couponRules = $this->getCouponCodeSelect($couponCode); + $allAllowedRules = $this->getConnection()->select(); + $allAllowedRules->union([$noCouponRules, $couponRules], \Zend_Db_Select::SQL_UNION_ALL); + + $this->_select = $allAllowedRules; } else { - $this->addFieldToFilter( - 'main_table.coupon_type', - \Magento\SalesRule\Model\Rule::COUPON_TYPE_NO_COUPON - ); + $this->_select = $noCouponRules; } + $this->setOrder('sort_order', self::SORT_ORDER_ASC); $this->setFlag('validation_filter', true); } @@ -187,72 +180,96 @@ public function setValidationFilter( } /** - * Get rules ids related to coupon code + * Recreate the default select object for specific needs of salesrule evaluation with coupon codes. * - * @param string $couponCode - * @return array + * @param $websiteId + * @param $customerGroupId + * @param $now */ - private function getCouponRelatedRuleIds(string $couponCode): array + private function prepareSelect($websiteId, $customerGroupId, $now) { - $connection = $this->getConnection(); - $select = $connection->select()->from( - ['main_table' => $this->getTable('salesrule')], - 'rule_id' + $this->getSelect()->reset(); + parent::_initSelect(); + + $this->addWebsiteGroupDateFilter($websiteId, $customerGroupId, $now); + } + + /** + * Return select object to determine all active rules not needing a coupon code. + * + * @return Select + */ + private function getNoCouponCodeSelect() + { + $noCouponSelect = clone $this->getSelect(); + + $noCouponSelect->where( + 'main_table.coupon_type = ?', + Rule::COUPON_TYPE_NO_COUPON ); - $select->joinLeft( - ['rule_coupons' => $this->getTable('salesrule_coupon')], - $connection->quoteInto( - 'main_table.rule_id = rule_coupons.rule_id AND main_table.coupon_type != ?', - \Magento\SalesRule\Model\Rule::COUPON_TYPE_NO_COUPON, - null - ) + + $noCouponSelect->columns([Coupon::KEY_CODE => new \Zend_Db_Expr('NULL')]); + + return $noCouponSelect; + } + + /** + * Determine all active rules that are valid for the given coupon code. + * + * @param $couponCode + * @return Select + */ + private function getCouponCodeSelect($couponCode) + { + $couponSelect = clone $this->getSelect(); + + $this->joinCouponTable($couponCode, $couponSelect); + + $notExpired = $this->getConnection()->quoteInto( + '(rule_coupons.expiration_date IS NULL OR rule_coupons.expiration_date >= ?)', + $this->_date->date()->format('Y-m-d') ); - $autoGeneratedCouponCondition = [ - $connection->quoteInto( - "main_table.coupon_type = ?", - \Magento\SalesRule\Model\Rule::COUPON_TYPE_AUTO - ), - $connection->quoteInto( - "rule_coupons.type = ?", - \Magento\SalesRule\Api\Data\CouponInterface::TYPE_GENERATED - ), - ]; - - $orWhereConditions = [ - "(" . implode($autoGeneratedCouponCondition, " AND ") . ")", - $connection->quoteInto( - '(main_table.coupon_type = ? AND main_table.use_auto_generation = 1 AND rule_coupons.type = 1)', - \Magento\SalesRule\Model\Rule::COUPON_TYPE_SPECIFIC - ), - $connection->quoteInto( - '(main_table.coupon_type = ? AND main_table.use_auto_generation = 0 AND rule_coupons.type = 0)', - \Magento\SalesRule\Model\Rule::COUPON_TYPE_SPECIFIC - ), - ]; - - $andWhereConditions = [ - $connection->quoteInto( - 'rule_coupons.code = ?', - $couponCode - ), - $connection->quoteInto( - '(rule_coupons.expiration_date IS NULL OR rule_coupons.expiration_date >= ?)', - $this->_date->date()->format('Y-m-d') - ), - ]; - - $orWhereCondition = implode(' OR ', $orWhereConditions); - $andWhereCondition = implode(' AND ', $andWhereConditions); - - $select->where( - '(' . $orWhereCondition . ') AND ' . $andWhereCondition, + $isAutogeneratedCoupon = + $this->getConnection()->quoteInto('main_table.coupon_type = ?', Rule::COUPON_TYPE_AUTO) + . ' AND ' . + $this->getConnection()->quoteInto('rule_coupons.type = ?', CouponInterface::TYPE_GENERATED); + + $isValidSpecificCoupon = + $this->getConnection()->quoteInto('(main_table.coupon_type = ?)', Rule::COUPON_TYPE_SPECIFIC) + . ' AND (' . + '(main_table.use_auto_generation = 1 AND rule_coupons.type = 1)' + . ' OR ' . + '(main_table.use_auto_generation = 0 AND rule_coupons.type = 0)' + . ')'; + + $couponSelect->where( + "$notExpired AND ($isAutogeneratedCoupon OR $isValidSpecificCoupon)", null, Select::TYPE_CONDITION ); - $select->group('main_table.rule_id'); - return $connection->fetchCol($select); + return $couponSelect; + } + + /** + * @param $couponCode + * @param Select $couponSelect + */ + private function joinCouponTable($couponCode, Select $couponSelect) + { + $couponJoinCondition = + 'main_table.rule_id = rule_coupons.rule_id' + . ' AND ' . + $this->getConnection()->quoteInto('main_table.coupon_type <> ?', Rule::COUPON_TYPE_NO_COUPON) + . ' AND ' . + $this->getConnection()->quoteInto('rule_coupons.code = ?', $couponCode); + + $couponSelect->joinInner( + ['rule_coupons' => $this->getTable('salesrule_coupon')], + $couponJoinCondition, + [Coupon::KEY_CODE] + ); } /** From 8e7ddbf4656b15fd37b1321cadabed9d9175ea6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20F=C3=BChr?= <d.fuehr@techdivision.com> Date: Tue, 22 Jan 2019 20:56:31 +0100 Subject: [PATCH 0895/1295] Fix integration error preventing coupon codes to be applied. --- .../SalesRule/Model/ResourceModel/Rule/Collection.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php index 352b5d4c809c9..2a6bf17342504 100644 --- a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php +++ b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php @@ -167,7 +167,10 @@ public function setValidationFilter( $allAllowedRules = $this->getConnection()->select(); $allAllowedRules->union([$noCouponRules, $couponRules], \Zend_Db_Select::SQL_UNION_ALL); - $this->_select = $allAllowedRules; + $wrapper = $this->getConnection()->select(); + $wrapper->from($allAllowedRules); + + $this->_select = $wrapper; } else { $this->_select = $noCouponRules; } From a3e87bdfbefe486eb5217b8dbdded50fffde1236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20F=C3=BChr?= <d.fuehr@techdivision.com> Date: Wed, 23 Jan 2019 09:32:43 +0100 Subject: [PATCH 0896/1295] Rename variable to comply to codestyle standards --- .../SalesRule/Model/ResourceModel/Rule/Collection.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php index 2a6bf17342504..04c54bf5b7e01 100644 --- a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php +++ b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php @@ -233,12 +233,12 @@ private function getCouponCodeSelect($couponCode) $this->_date->date()->format('Y-m-d') ); - $isAutogeneratedCoupon = + $isAutogenerated = $this->getConnection()->quoteInto('main_table.coupon_type = ?', Rule::COUPON_TYPE_AUTO) . ' AND ' . $this->getConnection()->quoteInto('rule_coupons.type = ?', CouponInterface::TYPE_GENERATED); - $isValidSpecificCoupon = + $isValidSpecific = $this->getConnection()->quoteInto('(main_table.coupon_type = ?)', Rule::COUPON_TYPE_SPECIFIC) . ' AND (' . '(main_table.use_auto_generation = 1 AND rule_coupons.type = 1)' @@ -247,7 +247,7 @@ private function getCouponCodeSelect($couponCode) . ')'; $couponSelect->where( - "$notExpired AND ($isAutogeneratedCoupon OR $isValidSpecificCoupon)", + "$notExpired AND ($isAutogenerated OR $isValidSpecific)", null, Select::TYPE_CONDITION ); From bfd1d4f7c38c0fe3c8e17449b621bbc5c996d59b Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Fri, 1 Mar 2019 15:09:21 +0200 Subject: [PATCH 0897/1295] Fix static tests. --- .../Model/ResourceModel/Rule/Collection.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php index 04c54bf5b7e01..9446a053f6d0e 100644 --- a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php +++ b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php @@ -157,7 +157,6 @@ public function setValidationFilter( Address $address = null ) { if (!$this->getFlag('validation_filter')) { - $this->prepareSelect($websiteId, $customerGroupId, $now); $noCouponRules = $this->getNoCouponCodeSelect(); @@ -165,7 +164,7 @@ public function setValidationFilter( if ($couponCode) { $couponRules = $this->getCouponCodeSelect($couponCode); $allAllowedRules = $this->getConnection()->select(); - $allAllowedRules->union([$noCouponRules, $couponRules], \Zend_Db_Select::SQL_UNION_ALL); + $allAllowedRules->union([$noCouponRules, $couponRules], Select::SQL_UNION_ALL); $wrapper = $this->getConnection()->select(); $wrapper->from($allAllowedRules); @@ -185,9 +184,9 @@ public function setValidationFilter( /** * Recreate the default select object for specific needs of salesrule evaluation with coupon codes. * - * @param $websiteId - * @param $customerGroupId - * @param $now + * @param int $websiteId + * @param int $customerGroupId + * @param string $now */ private function prepareSelect($websiteId, $customerGroupId, $now) { @@ -219,7 +218,7 @@ private function getNoCouponCodeSelect() /** * Determine all active rules that are valid for the given coupon code. * - * @param $couponCode + * @param string $couponCode * @return Select */ private function getCouponCodeSelect($couponCode) @@ -256,7 +255,9 @@ private function getCouponCodeSelect($couponCode) } /** - * @param $couponCode + * Join coupon table to select. + * + * @param string $couponCode * @param Select $couponSelect */ private function joinCouponTable($couponCode, Select $couponSelect) From 8b8a46c4beb8cf3c4c0d881660f442d4df545782 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> Date: Tue, 12 Mar 2019 11:41:14 +0200 Subject: [PATCH 0898/1295] magento/magento2#19098 2.2.6 Use batches and direct queries to fix sales address upgrade Fix field name --- app/code/Magento/Sales/Setup/UpgradeData.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Setup/UpgradeData.php b/app/code/Magento/Sales/Setup/UpgradeData.php index 0f9833ed8f7f2..ee60eef03726d 100644 --- a/app/code/Magento/Sales/Setup/UpgradeData.php +++ b/app/code/Magento/Sales/Setup/UpgradeData.php @@ -244,7 +244,7 @@ private function fillQuoteAddressIdInSalesOrderAddressProcessBatch( 'quote_address_id' => $quoteAddresses[$orderAddress['quote_id']]['address_id'] ?? null, ]; $where = [ - 'orderAddressId' => $orderAddress['entity_id'] + 'entity_id' => $orderAddress['entity_id'] ]; $salesConnection->update($salesOrderAddressTable, $bind, $where); From 571cf9bfd6ba69ef2c8f4f7a6f1dacb17452a495 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> Date: Tue, 12 Mar 2019 11:55:18 +0200 Subject: [PATCH 0899/1295] magento/magento2#21699 Backport Fix performance leak in salesrule collection Unify PRs to be the same --- .../Magento/SalesRule/Model/ResourceModel/Rule/Collection.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php index 9446a053f6d0e..105bbeaa11d1b 100644 --- a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php +++ b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php @@ -163,6 +163,7 @@ public function setValidationFilter( if ($couponCode) { $couponRules = $this->getCouponCodeSelect($couponCode); + $allAllowedRules = $this->getConnection()->select(); $allAllowedRules->union([$noCouponRules, $couponRules], Select::SQL_UNION_ALL); From ea34b66bd90957d7495428f3adff94f83c92dbb5 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> Date: Tue, 12 Mar 2019 11:58:07 +0200 Subject: [PATCH 0900/1295] magento/magento2#21699 Backport Fix performance leak in salesrule collection Unify PRs to be the same --- .../Magento/SalesRule/Model/ResourceModel/Rule/Collection.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php index 105bbeaa11d1b..c45400f65047f 100644 --- a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php +++ b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php @@ -9,6 +9,9 @@ use Magento\Framework\DB\Select; use Magento\Framework\Serialize\Serializer\Json; use Magento\Quote\Model\Quote\Address; +use Magento\SalesRule\Api\Data\CouponInterface; +use Magento\SalesRule\Model\Coupon; +use Magento\SalesRule\Model\Rule; /** * Sales Rules resource collection model. From 3541b85d2d18902b3ad9669b706a672cdc9473a9 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 12 Mar 2019 11:59:18 +0200 Subject: [PATCH 0901/1295] MAGETWO-73727: Session initialized during reindex from CLI and result fatal error --- .../integration/etc/di/preferences/ce.php | 1 + .../Session/SessionStartChecker.php | 25 +++++ .../Framework/Session/SessionManager.php | 99 +++++++++++-------- .../Framework/Session/SessionStartChecker.php | 38 +++++++ 4 files changed, 122 insertions(+), 41 deletions(-) create mode 100644 dev/tests/integration/framework/Magento/TestFramework/Session/SessionStartChecker.php create mode 100644 lib/internal/Magento/Framework/Session/SessionStartChecker.php diff --git a/dev/tests/integration/etc/di/preferences/ce.php b/dev/tests/integration/etc/di/preferences/ce.php index 6839d0ae9a7ff..fe2c1c7d01740 100644 --- a/dev/tests/integration/etc/di/preferences/ce.php +++ b/dev/tests/integration/etc/di/preferences/ce.php @@ -29,4 +29,5 @@ \Magento\TestFramework\Lock\Backend\DummyLocker::class, \Magento\Framework\ShellInterface::class => \Magento\TestFramework\App\Shell::class, \Magento\Framework\App\Shell::class => \Magento\TestFramework\App\Shell::class, + \Magento\Framework\Session\SessionStartChecker::class => \Magento\TestFramework\Session\SessionStartChecker::class, ]; diff --git a/dev/tests/integration/framework/Magento/TestFramework/Session/SessionStartChecker.php b/dev/tests/integration/framework/Magento/TestFramework/Session/SessionStartChecker.php new file mode 100644 index 0000000000000..136b0565a729a --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Session/SessionStartChecker.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\TestFramework\Session; + +/** + * Class to check if session can be started or not. Dummy for integration tests. + */ +class SessionStartChecker extends \Magento\Framework\Session\SessionStartChecker +{ + /** + * Can session be started or not. + * + * @return bool + */ + public function check() : bool + { + return true; + } +} diff --git a/lib/internal/Magento/Framework/Session/SessionManager.php b/lib/internal/Magento/Framework/Session/SessionManager.php index 0d0838b47d9cf..11393674012f3 100644 --- a/lib/internal/Magento/Framework/Session/SessionManager.php +++ b/lib/internal/Magento/Framework/Session/SessionManager.php @@ -12,6 +12,7 @@ /** * Session Manager * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class SessionManager implements SessionManagerInterface { @@ -92,6 +93,11 @@ class SessionManager implements SessionManagerInterface */ private $appState; + /** + * @var SessionStartChecker + */ + private $sessionStartChecker; + /** * @param \Magento\Framework\App\Request\Http $request * @param SidResolverInterface $sidResolver @@ -102,7 +108,10 @@ class SessionManager implements SessionManagerInterface * @param \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager * @param \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory * @param \Magento\Framework\App\State $appState + * @param SessionStartChecker|null $sessionStartChecker * @throws \Magento\Framework\Exception\SessionException + * + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\Framework\App\Request\Http $request, @@ -113,7 +122,8 @@ public function __construct( StorageInterface $storage, \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager, \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory, - \Magento\Framework\App\State $appState + \Magento\Framework\App\State $appState, + SessionStartChecker $sessionStartChecker = null ) { $this->request = $request; $this->sidResolver = $sidResolver; @@ -124,6 +134,9 @@ public function __construct( $this->cookieManager = $cookieManager; $this->cookieMetadataFactory = $cookieMetadataFactory; $this->appState = $appState; + $this->sessionStartChecker = $sessionStartChecker ?: \Magento\Framework\App\ObjectManager::getInstance()->get( + SessionStartChecker::class + ); // Enable session.use_only_cookies ini_set('session.use_only_cookies', '1'); @@ -131,7 +144,8 @@ public function __construct( } /** - * This method needs to support sessions with APC enabled + * This method needs to support sessions with APC enabled. + * * @return void */ public function writeClose() @@ -166,47 +180,50 @@ public function __call($method, $args) */ public function start() { - if (!$this->isSessionExists()) { - \Magento\Framework\Profiler::start('session_start'); - - try { - $this->appState->getAreaCode(); - } catch (\Magento\Framework\Exception\LocalizedException $e) { - throw new \Magento\Framework\Exception\SessionException( - new \Magento\Framework\Phrase( - 'Area code not set: Area code must be set before starting a session.' - ), - $e - ); - } - - // Need to apply the config options so they can be ready by session_start - $this->initIniOptions(); - $this->registerSaveHandler(); - if (isset($_SESSION['new_session_id'])) { - // Not fully expired yet. Could be lost cookie by unstable network. - session_commit(); - session_id($_SESSION['new_session_id']); - } - $sid = $this->sidResolver->getSid($this); - // potential custom logic for session id (ex. switching between hosts) - $this->setSessionId($sid); - session_start(); - if (isset($_SESSION['destroyed']) - && $_SESSION['destroyed'] < time() - $this->sessionConfig->getCookieLifetime() - ) { - $this->destroy(['clear_storage' => true]); + if ($this->sessionStartChecker->check()) { + if (!$this->isSessionExists()) { + \Magento\Framework\Profiler::start('session_start'); + + try { + $this->appState->getAreaCode(); + } catch (\Magento\Framework\Exception\LocalizedException $e) { + throw new \Magento\Framework\Exception\SessionException( + new \Magento\Framework\Phrase( + 'Area code not set: Area code must be set before starting a session.' + ), + $e + ); + } + + // Need to apply the config options so they can be ready by session_start + $this->initIniOptions(); + $this->registerSaveHandler(); + if (isset($_SESSION['new_session_id'])) { + // Not fully expired yet. Could be lost cookie by unstable network. + session_commit(); + session_id($_SESSION['new_session_id']); + } + $sid = $this->sidResolver->getSid($this); + // potential custom logic for session id (ex. switching between hosts) + $this->setSessionId($sid); + session_start(); + if (isset($_SESSION['destroyed']) + && $_SESSION['destroyed'] < time() - $this->sessionConfig->getCookieLifetime() + ) { + $this->destroy(['clear_storage' => true]); + } + + $this->validator->validate($this); + $this->renewCookie($sid); + + register_shutdown_function([$this, 'writeClose']); + + $this->_addHost(); + \Magento\Framework\Profiler::stop('session_start'); } - - $this->validator->validate($this); - $this->renewCookie($sid); - - register_shutdown_function([$this, 'writeClose']); - - $this->_addHost(); - \Magento\Framework\Profiler::stop('session_start'); + $this->storage->init(isset($_SESSION) ? $_SESSION : []); } - $this->storage->init(isset($_SESSION) ? $_SESSION : []); + return $this; } diff --git a/lib/internal/Magento/Framework/Session/SessionStartChecker.php b/lib/internal/Magento/Framework/Session/SessionStartChecker.php new file mode 100644 index 0000000000000..9cc32268d574a --- /dev/null +++ b/lib/internal/Magento/Framework/Session/SessionStartChecker.php @@ -0,0 +1,38 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Framework\Session; + +/** + * Class to check if session can be started or not. + */ +class SessionStartChecker +{ + /** + * @var bool + */ + private $checkSapi; + + /** + * @param bool $checkSapi + */ + public function __construct(bool $checkSapi = true) + { + $this->checkSapi = $checkSapi; + } + + /** + * Can session be started or not. + * + * @return bool + */ + public function check() : bool + { + return !($this->checkSapi && PHP_SAPI === 'cli'); + } +} From dbf671438ef01bf9cc8882c7a36bb28bea280434 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> Date: Tue, 12 Mar 2019 12:00:20 +0200 Subject: [PATCH 0902/1295] magento/magento2#21699 Backport Fix performance leak in salesrule collection Unify PRs to be the same --- .../Magento/SalesRule/Model/ResourceModel/Rule/Collection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php index c45400f65047f..423cd1543117b 100644 --- a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php +++ b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php @@ -147,10 +147,10 @@ protected function _afterLoad() * @param string $couponCode * @param string|null $now * @param Address $address allow extensions to further filter out rules based on quote address + * @throws \Zend_Db_Select_Exception * @use $this->addWebsiteGroupDateFilter() * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @return $this - * @throws \Zend_Db_Select_Exception */ public function setValidationFilter( $websiteId, From 068eedf0b8fb069df8e8432e8b78d3a0dff33110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Mon, 11 Mar 2019 16:21:08 +0100 Subject: [PATCH 0903/1295] Move Magento\Sales\Model\Order\Address\Validator logic from construct to validate method --- .../Sales/Model/Order/Address/Validator.php | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Address/Validator.php b/app/code/Magento/Sales/Model/Order/Address/Validator.php index e6353f7f28899..84ddefec0111c 100644 --- a/app/code/Magento/Sales/Model/Order/Address/Validator.php +++ b/app/code/Magento/Sales/Model/Order/Address/Validator.php @@ -60,6 +60,16 @@ public function __construct( $this->countryFactory = $countryFactory; $this->eavConfig = $eavConfig ?: ObjectManager::getInstance() ->get(EavConfig::class); + } + + /** + * + * @param \Magento\Sales\Model\Order\Address $address + * @return array + */ + public function validate(Address $address) + { + $warnings = []; if ($this->isTelephoneRequired()) { $this->required['telephone'] = 'Phone Number'; @@ -72,16 +82,7 @@ public function __construct( if ($this->isFaxRequired()) { $this->required['fax'] = 'Fax'; } - } - /** - * - * @param \Magento\Sales\Model\Order\Address $address - * @return array - */ - public function validate(Address $address) - { - $warnings = []; foreach ($this->required as $code => $label) { if (!$address->hasData($code)) { $warnings[] = sprintf('%s is a required field', $label); From 57508634320241a9582d390760e118ee55af6760 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Tue, 12 Mar 2019 09:51:59 -0500 Subject: [PATCH 0904/1295] MAGETWO-98626: [Magento Cloud] Translated Arabic URL's are returning 404's --- .../UrlRewrite/Model/Storage/DbStorage.php | 121 ++++++++++++------ .../Model/UrlFinderInterfaceTest.php | 71 ++++++++++ .../UrlRewrite/_files/url_rewrites.php | 42 ++++++ .../_files/url_rewrites_rollback.php | 20 +++ 4 files changed, 218 insertions(+), 36 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/UrlRewrite/Model/UrlFinderInterfaceTest.php create mode 100644 dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites.php create mode 100644 dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites_rollback.php diff --git a/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php b/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php index 60b845f95e5cc..f98801c266109 100644 --- a/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php +++ b/app/code/Magento/UrlRewrite/Model/Storage/DbStorage.php @@ -14,6 +14,9 @@ use Psr\Log\LoggerInterface; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite as UrlRewriteData; +/** + * DB storage implementation for url rewrites + */ class DbStorage extends AbstractStorage { /** @@ -37,7 +40,7 @@ class DbStorage extends AbstractStorage protected $resource; /** - * @var \Psr\Log\LoggerInterface + * @var LoggerInterface */ private $logger; @@ -45,7 +48,7 @@ class DbStorage extends AbstractStorage * @param \Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory $urlRewriteFactory * @param DataObjectHelper $dataObjectHelper * @param \Magento\Framework\App\ResourceConnection $resource - * @param \Psr\Log\LoggerInterface|null $logger + * @param LoggerInterface|null $logger */ public function __construct( UrlRewriteFactory $urlRewriteFactory, @@ -56,7 +59,7 @@ public function __construct( $this->connection = $resource->getConnection(); $this->resource = $resource; $this->logger = $logger ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Psr\Log\LoggerInterface::class); + ->get(LoggerInterface::class); parent::__construct($urlRewriteFactory, $dataObjectHelper); } @@ -97,42 +100,18 @@ protected function doFindOneByData(array $data) $result = null; $requestPath = $data[UrlRewrite::REQUEST_PATH]; - - $data[UrlRewrite::REQUEST_PATH] = [ + $decodedRequestPath = urldecode($requestPath); + $data[UrlRewrite::REQUEST_PATH] = array_unique([ rtrim($requestPath, '/'), rtrim($requestPath, '/') . '/', - ]; + rtrim($decodedRequestPath, '/'), + rtrim($decodedRequestPath, '/') . '/', + ]); $resultsFromDb = $this->connection->fetchAll($this->prepareSelect($data)); - - if (count($resultsFromDb) === 1) { - $resultFromDb = current($resultsFromDb); - $redirectTypes = [OptionProvider::TEMPORARY, OptionProvider::PERMANENT]; - - // If request path matches the DB value or it's redirect - we can return result from DB - $canReturnResultFromDb = ($resultFromDb[UrlRewrite::REQUEST_PATH] === $requestPath - || in_array((int)$resultFromDb[UrlRewrite::REDIRECT_TYPE], $redirectTypes, true)); - - // Otherwise return 301 redirect to request path from DB results - $result = $canReturnResultFromDb ? $resultFromDb : [ - UrlRewrite::ENTITY_TYPE => 'custom', - UrlRewrite::ENTITY_ID => '0', - UrlRewrite::REQUEST_PATH => $requestPath, - UrlRewrite::TARGET_PATH => $resultFromDb[UrlRewrite::REQUEST_PATH], - UrlRewrite::REDIRECT_TYPE => OptionProvider::PERMANENT, - UrlRewrite::STORE_ID => $resultFromDb[UrlRewrite::STORE_ID], - UrlRewrite::DESCRIPTION => null, - UrlRewrite::IS_AUTOGENERATED => '0', - UrlRewrite::METADATA => null, - ]; - } else { - // If we have 2 results - return the row that matches request path - foreach ($resultsFromDb as $resultFromDb) { - if ($resultFromDb[UrlRewrite::REQUEST_PATH] === $requestPath) { - $result = $resultFromDb; - break; - } - } + if ($resultsFromDb) { + $urlRewrite = $this->extractMostRelevantUrlRewrite($requestPath, $resultsFromDb); + $result = $this->prepareUrlRewrite($requestPath, $urlRewrite); } return $result; @@ -142,8 +121,78 @@ protected function doFindOneByData(array $data) } /** - * @param UrlRewrite[] $urls + * Extract most relevant url rewrite from url rewrites list + * + * @param string $requestPath + * @param array $urlRewrites + * @return array|null + */ + private function extractMostRelevantUrlRewrite(string $requestPath, array $urlRewrites) + { + $prioritizedUrlRewrites = []; + foreach ($urlRewrites as $urlRewrite) { + switch (true) { + case $urlRewrite[UrlRewrite::REQUEST_PATH] === $requestPath: + $priority = 1; + break; + case $urlRewrite[UrlRewrite::REQUEST_PATH] === urldecode($requestPath): + $priority = 2; + break; + case rtrim($urlRewrite[UrlRewrite::REQUEST_PATH], '/') === rtrim($requestPath, '/'): + $priority = 3; + break; + case rtrim($urlRewrite[UrlRewrite::REQUEST_PATH], '/') === rtrim(urldecode($requestPath), '/'): + $priority = 4; + break; + default: + $priority = 5; + break; + } + $prioritizedUrlRewrites[$priority] = $urlRewrite; + } + ksort($prioritizedUrlRewrites); + + return array_shift($prioritizedUrlRewrites); + } + + /** + * Prepare url rewrite * + * If request path matches the DB value or it's redirect - we can return result from DB + * Otherwise return 301 redirect to request path from DB results + * + * @param string $requestPath + * @param array $urlRewrite + * @return array + */ + private function prepareUrlRewrite(string $requestPath, array $urlRewrite): array + { + $redirectTypes = [OptionProvider::TEMPORARY, OptionProvider::PERMANENT]; + $canReturnResultFromDb = ( + in_array($urlRewrite[UrlRewrite::REQUEST_PATH], [$requestPath, urldecode($requestPath)], true) + || in_array((int) $urlRewrite[UrlRewrite::REDIRECT_TYPE], $redirectTypes, true) + ); + if (!$canReturnResultFromDb) { + $urlRewrite = [ + UrlRewrite::ENTITY_TYPE => 'custom', + UrlRewrite::ENTITY_ID => '0', + UrlRewrite::REQUEST_PATH => $requestPath, + UrlRewrite::TARGET_PATH => $urlRewrite[UrlRewrite::REQUEST_PATH], + UrlRewrite::REDIRECT_TYPE => OptionProvider::PERMANENT, + UrlRewrite::STORE_ID => $urlRewrite[UrlRewrite::STORE_ID], + UrlRewrite::DESCRIPTION => null, + UrlRewrite::IS_AUTOGENERATED => '0', + UrlRewrite::METADATA => null, + ]; + } + + return $urlRewrite; + } + + /** + * Delete old url rewrites + * + * @param UrlRewrite[] $urls * @return void */ private function deleteOldUrls(array $urls) diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/UrlFinderInterfaceTest.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/UrlFinderInterfaceTest.php new file mode 100644 index 0000000000000..b6055f14e79d2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/UrlFinderInterfaceTest.php @@ -0,0 +1,71 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\UrlRewrite\Model; + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; + +/** + * @magentoDataFixture Magento/UrlRewrite/_files/url_rewrites.php + */ +class UrlFinderInterfaceTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var UrlFinderInterface + */ + private $urlFinder; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->urlFinder = Bootstrap::getObjectManager()->create(UrlFinderInterface::class); + } + + /** + * @dataProvider findOneDataProvider + * @param string $requestPath + * @param string $targetPath + * @param int $redirectType + */ + public function testFindOneByData(string $requestPath, string $targetPath, int $redirectType) + { + $data = [ + UrlRewrite::REQUEST_PATH => $requestPath, + ]; + $urlRewrite = $this->urlFinder->findOneByData($data); + $this->assertEquals($targetPath, $urlRewrite->getTargetPath()); + $this->assertEquals($redirectType, $urlRewrite->getRedirectType()); + } + + /** + * @return array + */ + public function findOneDataProvider(): array + { + return [ + ['string', 'test_page1', 0], + ['string/', 'string', 301], + ['string_permanent', 'test_page1', 301], + ['string_permanent/', 'test_page1', 301], + ['string_temporary', 'test_page1', 302], + ['string_temporary/', 'test_page1', 302], + ['строка', 'test_page1', 0], + ['строка/', 'строка', 301], + [urlencode('строка'), 'test_page2', 0], + [urlencode('строка') . '/', urlencode('строка'), 301], + ['другая_строка', 'test_page1', 302], + ['другая_строка/', 'test_page1', 302], + [urlencode('другая_строка'), 'test_page1', 302], + [urlencode('другая_строка') . '/', 'test_page1', 302], + ['السلسلة', 'test_page1', 0], + [urlencode('السلسلة'), 'test_page1', 0], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites.php new file mode 100644 index 0000000000000..9edc6507308ee --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites.php @@ -0,0 +1,42 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +$rewritesData = [ + [ + 'string', 'test_page1', 0 + ], + [ + 'string_permanent', 'test_page1', \Magento\UrlRewrite\Model\OptionProvider::PERMANENT + ], + [ + 'string_temporary', 'test_page1', \Magento\UrlRewrite\Model\OptionProvider::TEMPORARY + ], + [ + 'строка', 'test_page1', 0 + ], + [ + urlencode('строка'), 'test_page2', 0 + ], + [ + 'другая_строка', 'test_page1', \Magento\UrlRewrite\Model\OptionProvider::TEMPORARY + ], + [ + 'السلسلة', 'test_page1', 0 + ], +]; + +$rewriteResource = $objectManager->create(\Magento\UrlRewrite\Model\ResourceModel\UrlRewrite::class); +foreach ($rewritesData as $rewriteData) { + list ($requestPath, $targetPath, $redirectType) = $rewriteData; + $rewrite = $objectManager->create(\Magento\UrlRewrite\Model\UrlRewrite::class); + $rewrite->setEntityType('custom') + ->setRequestPath($requestPath) + ->setTargetPath($targetPath) + ->setRedirectType($redirectType); + $rewriteResource->save($rewrite); +} diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites_rollback.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites_rollback.php new file mode 100644 index 0000000000000..a98f947d614e0 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrites_rollback.php @@ -0,0 +1,20 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +$registry = $objectManager->get(\Magento\Framework\Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +$urlRewriteCollection = $objectManager->create(\Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection::class); +$collection = $urlRewriteCollection + ->addFieldToFilter('target_path', ['test_page1', 'test_page2']) + ->load() + ->walk('delete'); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); From df364432458bc551964a14cc1c1aaa50110c72e3 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> Date: Tue, 12 Mar 2019 20:08:59 +0200 Subject: [PATCH 0905/1295] magento/magento2#19098 Use batches and direct queries to fix sales address upgrade --- app/code/Magento/Sales/Setup/UpgradeData.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Setup/UpgradeData.php b/app/code/Magento/Sales/Setup/UpgradeData.php index ee60eef03726d..2e5a454e62fdd 100644 --- a/app/code/Magento/Sales/Setup/UpgradeData.php +++ b/app/code/Magento/Sales/Setup/UpgradeData.php @@ -244,7 +244,7 @@ private function fillQuoteAddressIdInSalesOrderAddressProcessBatch( 'quote_address_id' => $quoteAddresses[$orderAddress['quote_id']]['address_id'] ?? null, ]; $where = [ - 'entity_id' => $orderAddress['entity_id'] + 'entity_id = ?' => $orderAddress['entity_id'] ]; $salesConnection->update($salesOrderAddressTable, $bind, $where); From 5cc67fc4f8468dc340f9a9c210b6aa9c1db694c2 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Tue, 12 Mar 2019 15:44:08 -0500 Subject: [PATCH 0906/1295] MAGETWO-98470: Special price not removing at website scope --- .../Eav/Model/Entity/AbstractEntity.php | 8 +++-- .../Model/ResourceModel/ProductTest.php | 34 ++++++++++++++++--- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php index 7f01cf268d06b..7f6734b8b6b60 100644 --- a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php +++ b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php @@ -1695,14 +1695,16 @@ public function saveAttribute(DataObject $object, $attributeCode) $connection->beginTransaction(); try { - $select = $connection->select()->from($table, 'value_id')->where($where); - $origValueId = $connection->fetchOne($select); + $select = $connection->select()->from($table, ['value_id', 'value'])->where($where); + $origRow = $connection->fetchRow($select); + $origValueId = $origRow['value_id'] ?? false; + $origValue = $origRow['value'] ?? null; if ($origValueId === false && $newValue !== null) { $this->_insertAttribute($object, $attribute, $newValue); } elseif ($origValueId !== false && $newValue !== null) { $this->_updateAttribute($object, $attribute, $origValueId, $newValue); - } elseif ($origValueId !== false && $newValue === null) { + } elseif ($origValueId !== false && $newValue === null && $origValue !== null) { $connection->delete($table, $where); } $this->_processAttributeValues(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/ProductTest.php index 7954e2c36227f..476f01eb277df 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/ProductTest.php @@ -12,6 +12,11 @@ class ProductTest extends TestCase { + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + /** * @var Product */ @@ -29,7 +34,8 @@ protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); - $this->model = $this->objectManager->get(Product::class); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->model = $this->objectManager->create(Product::class); } /** @@ -42,11 +48,29 @@ public function testGetAttributeRawValue() $sku = 'simple'; $attribute = 'name'; - /** @var ProductRepositoryInterface $productRepository */ - $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); - $product = $productRepository->get($sku); - + $product = $this->productRepository->get($sku); $actual = $this->model->getAttributeRawValue($product->getId(), $attribute, null); self::assertEquals($product->getName(), $actual); } + + /** + * @magentoAppArea adminhtml + * @magentoDataFixture Magento/Catalog/_files/product_special_price.php + * @magentoAppIsolation enabled + * @magentoConfigFixture default_store catalog/price/scope 1 + */ + public function testUpdateStoreSpecificSpecialPrice() + { + /** @var \Magento\Catalog\Model\Product $product */ + $product = $this->productRepository->get('simple', true, 1); + $this->assertEquals(5.99, $product->getSpecialPrice()); + + $product->setSpecialPrice(''); + $this->model->save($product); + $product = $this->productRepository->get('simple', false, 1, true); + $this->assertEmpty($product->getSpecialPrice()); + + $product = $this->productRepository->get('simple', false, 0, true); + $this->assertEquals(5.99, $product->getSpecialPrice()); + } } From f8a1eb526a4dbac17f093fb29fb3a5e9e853ae82 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Tue, 12 Mar 2019 17:31:13 -0500 Subject: [PATCH 0907/1295] MAGETWO-98554: [Magento cloud] Media directories not sorted --- app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php | 3 ++- .../Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php b/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php index 2cd1647a1bf22..90dcf3dc8df78 100644 --- a/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php +++ b/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php @@ -270,7 +270,8 @@ public function getDirsCollection($path) $collection = $this->getCollection($path) ->setCollectDirs(true) ->setCollectFiles(false) - ->setCollectRecursively(false); + ->setCollectRecursively(false) + ->setOrder('basename', \Magento\Framework\Data\Collection\Filesystem::SORT_ORDER_ASC); $conditions = $this->getConditionsForExcludeDirs(); diff --git a/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php b/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php index 906a7d4fbc605..20c0b2075f5c3 100644 --- a/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php +++ b/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php @@ -414,6 +414,10 @@ protected function generalTestGetDirsCollection($path, $collectionArray = [], $e ->method('setCollectRecursively') ->with(false) ->willReturnSelf(); + $storageCollectionMock->expects($this->once()) + ->method('setOrder') + ->with('basename', \Magento\Framework\Data\Collection\Filesystem::SORT_ORDER_ASC) + ->willReturnSelf(); $storageCollectionMock->expects($this->once()) ->method('getIterator') ->willReturn(new \ArrayIterator($collectionArray)); From 4395ea068d276ce632ba3c8f7d55b21f61fee419 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <svizev.igor@gmail.com> Date: Thu, 14 Mar 2019 12:54:55 +0200 Subject: [PATCH 0908/1295] Custom option type select - Allow modify list of single selection option types --- .../Model/Product/Option/Type/Select.php | 20 +++++++++++++------ app/code/Magento/Catalog/etc/di.xml | 8 ++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type/Select.php b/app/code/Magento/Catalog/Model/Product/Option/Type/Select.php index d14f56718d159..7f622dcb54e48 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/Select.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/Select.php @@ -29,23 +29,35 @@ class Select extends \Magento\Catalog\Model\Product\Option\Type\DefaultType */ protected $string; + /** + * @var array + */ + private $singleSelectionTypes; + /** * @param \Magento\Checkout\Model\Session $checkoutSession * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Framework\Stdlib\StringUtils $string * @param \Magento\Framework\Escaper $escaper * @param array $data + * @param array $singleSelectionTypes */ public function __construct( \Magento\Checkout\Model\Session $checkoutSession, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Framework\Stdlib\StringUtils $string, \Magento\Framework\Escaper $escaper, - array $data = [] + array $data = [], + array $singleSelectionTypes = [] ) { $this->string = $string; $this->_escaper = $escaper; parent::__construct($checkoutSession, $scopeConfig, $data); + + $this->singleSelectionTypes = $singleSelectionTypes ?: [ + \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_DROP_DOWN, + \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_RADIO, + ]; } /** @@ -301,10 +313,6 @@ public function getOptionSku($optionValue, $skuDelimiter) */ protected function _isSingleSelection() { - $single = [ - \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_DROP_DOWN, - \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_RADIO, - ]; - return in_array($this->getOption()->getType(), $single); + return in_array($this->getOption()->getType(), $this->singleSelectionTypes, true); } } diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index 1d3e9a931b677..185e3017ec447 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -1164,4 +1164,12 @@ </argument> </arguments> </type> + <type name="Magento\Catalog\Model\Product\Option\Type\Select"> + <arguments> + <argument name="singleSelectionTypes" xsi:type="array"> + <item name="drop_down" xsi:type="const">Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_DROP_DOWN</item> + <item name="radio" xsi:type="const">Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_RADIO</item> + </argument> + </arguments> + </type> </config> From 4c246de7ce6251d2f32f26e9282aaaacc645b0ce Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Thu, 14 Mar 2019 15:04:59 -0500 Subject: [PATCH 0909/1295] MAGETWO-98679: Discounts of products disappear on storefront after drag & drop via Visual Merchandiser in category --- .../Model/Indexer/IndexBuilder.php | 100 ++++++++++++------ 1 file changed, 69 insertions(+), 31 deletions(-) diff --git a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php index 731cbe4531f42..8b34bde6cc49a 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php @@ -263,7 +263,10 @@ public function reindexByIds(array $ids) */ protected function doReindexByIds($ids) { - $this->cleanByIds($ids); + foreach ($this->getInactiveRules() as $rule) { + $this->cleanProductIndex($ids, $rule->getId()); + $this->cleanProductPriceIndex($ids, $rule->getId()); + } $products = $this->productLoader->getProducts($ids); foreach ($this->getActiveRules() as $rule) { @@ -315,6 +318,36 @@ protected function doReindexFull() ); } + /** + * Clean product index + * + * @param array|int $productIds + * @param int|null $ruleId + */ + private function cleanProductIndex($productIds, int $ruleId = null) + { + $where = ['product_id' => $productIds]; + if ($ruleId) { + $where['rule_id'] = $ruleId; + } + $this->connection->delete($this->getTable('catalogrule_product'), $where); + } + + /** + * Clean product price index + * + * @param array|int $productIds + * @param int|null $ruleId + */ + private function cleanProductPriceIndex($productIds, int $ruleId = null) + { + $where = ['product_id' => $productIds]; + if ($ruleId) { + $where['rule_id'] = $ruleId; + } + $this->connection->delete($this->getTable('catalogrule_product_price'), $where); + } + /** * Clean by product ids * @@ -323,24 +356,8 @@ protected function doReindexFull() */ protected function cleanByIds($productIds) { - $query = $this->connection->deleteFromSelect( - $this->connection - ->select() - ->from($this->resource->getTableName('catalogrule_product'), 'product_id') - ->distinct() - ->where('product_id IN (?)', $productIds), - $this->resource->getTableName('catalogrule_product') - ); - $this->connection->query($query); - - $query = $this->connection->deleteFromSelect( - $this->connection->select() - ->from($this->resource->getTableName('catalogrule_product_price'), 'product_id') - ->distinct() - ->where('product_id IN (?)', $productIds), - $this->resource->getTableName('catalogrule_product_price') - ); - $this->connection->query($query); + $this->cleanProductIndex($productIds); + $this->cleanProductPriceIndex($productIds); } /** @@ -357,17 +374,11 @@ protected function applyRule(Rule $rule, $product) $websiteIds = array_intersect($product->getWebsiteIds(), $rule->getWebsiteIds()); if (!$rule->validate($product)) { + $this->cleanProductIndex($productEntityId, $ruleId); + $this->cleanProductPriceIndex($productEntityId, $ruleId); return $this; } - $this->connection->delete( - $this->resource->getTableName('catalogrule_product'), - [ - $this->connection->quoteInto('rule_id = ?', $ruleId), - $this->connection->quoteInto('product_id = ?', $productEntityId) - ] - ); - $customerGroupIds = $rule->getCustomerGroupIds(); $fromTime = strtotime($rule->getFromDate()); $toTime = strtotime($rule->getToDate()); @@ -378,7 +389,12 @@ protected function applyRule(Rule $rule, $product) $actionStop = $rule->getStopRulesProcessing(); $rows = []; + $ruleProductTable = $this->getTable('catalogrule_product'); try { + $this->connection->beginTransaction(); + + $this->cleanProductIndex($productEntityId, $ruleId); + foreach ($websiteIds as $websiteId) { foreach ($customerGroupIds as $customerGroupId) { $rows[] = [ @@ -395,20 +411,32 @@ protected function applyRule(Rule $rule, $product) ]; if (count($rows) == $this->batchCount) { - $this->connection->insertMultiple($this->getTable('catalogrule_product'), $rows); + $this->connection->insertMultiple($ruleProductTable, $rows); $rows = []; } } } if (!empty($rows)) { - $this->connection->insertMultiple($this->resource->getTableName('catalogrule_product'), $rows); + $this->connection->insertMultiple($ruleProductTable, $rows); + unset($rows); } + $this->connection->commit(); } catch (\Exception $e) { + $this->connection->rollBack(); + throw $e; + } + + try { + $this->connection->beginTransaction(); + $this->cleanProductPriceIndex($productEntityId, $ruleId); + $this->reindexRuleProductPrice->execute($this->batchCount, $product); + $this->connection->commit(); + } catch (\Exception $e) { + $this->connection->rollBack(); throw $e; } - $this->reindexRuleProductPrice->execute($this->batchCount, $product); $this->reindexRuleGroupWebsite->execute(); return $this; @@ -528,13 +556,23 @@ protected function saveRuleProductPrices($arrData) /** * Get active rules * - * @return array + * @return \Magento\CatalogRule\Model\ResourceModel\Rule\Collection */ protected function getActiveRules() { return $this->ruleCollectionFactory->create()->addFieldToFilter('is_active', 1); } + /** + * Get inactive rules + * + * @return \Magento\CatalogRule\Model\ResourceModel\Rule\Collection + */ + private function getInactiveRules() + { + return $this->ruleCollectionFactory->create()->addFieldToFilter('is_active', 0); + } + /** * Get active rules * From 41c355a63e1192d0eea751aca5c803f3df9e0df4 Mon Sep 17 00:00:00 2001 From: aakimov <aakimov@magento.com> Date: Thu, 14 Mar 2019 15:38:20 -0500 Subject: [PATCH 0910/1295] MAGETWO-98699: CHANGELOG.md Update --- CHANGELOG.md | 473 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 473 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a9ca068019dc..4fb34dd58c46d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,476 @@ +2.2.8 +============= +* GitHub issues: + * [#15196](https://github.com/magento/magento2/issues/15196) -- 2.2.4 : Magento 2 integration tests enables all modules (fixed in [magento/magento2#16361](https://github.com/magento/magento2/pull/16361)) + * [#13720](https://github.com/magento/magento2/issues/13720) -- Only 2 related products are showing in backend . (fixed in [magento/magento2#17885](https://github.com/magento/magento2/pull/17885)) + * [#14050](https://github.com/magento/magento2/issues/14050) -- Import related products issue (fixed in [magento/magento2#17885](https://github.com/magento/magento2/pull/17885)) + * [#17890](https://github.com/magento/magento2/issues/17890) -- Magento 2.2.5 Product swatches does not shows correct value for related store view (fixed in [magento/magento2#17891](https://github.com/magento/magento2/pull/17891)) + * [#17567](https://github.com/magento/magento2/issues/17567) -- Currency symbol cannot be changed back to default value from admin panel in Single-store mode (fixed in [magento/magento2#17966](https://github.com/magento/magento2/pull/17966)) + * [#5402](https://github.com/magento/magento2/issues/5402) -- Menu does not work when you change from Mobile to Desktop mode (fixed in [magento/magento2#17990](https://github.com/magento/magento2/pull/17990)) + * [#13405](https://github.com/magento/magento2/issues/13405) -- No such entity error when saving product in single-store mode if website_id <> 1 (fixed in [magento/magento2#18001](https://github.com/magento/magento2/pull/18001)) + * [#5797](https://github.com/magento/magento2/issues/5797) -- [2.1.0] module:uninstall can remove code it uses itself (fixed in [magento/magento2#18002](https://github.com/magento/magento2/pull/18002)) + * [#17780](https://github.com/magento/magento2/issues/17780) -- Module uninstall does not work with composer (fixed in [magento/magento2#18002](https://github.com/magento/magento2/pull/18002)) + * [#7557](https://github.com/magento/magento2/issues/7557) -- Backend Security key broken for controllers with frontname not equal to route ID (fixed in [magento/magento2#18018](https://github.com/magento/magento2/pull/18018)) + * [#12095](https://github.com/magento/magento2/issues/12095) -- Update 2.2.1: One or more integrations have been reset because of a change to their xml configs. (fixed in [magento/magento2#14065](https://github.com/magento/magento2/pull/14065)) + * [#17582](https://github.com/magento/magento2/issues/17582) -- ./bin/magento config:show fails with a fatal error (fixed in [magento/magento2#17993](https://github.com/magento/magento2/pull/17993)) + * [#17999](https://github.com/magento/magento2/issues/17999) -- Sitemap grid display incorrect base URL in the grid if using multiple stores (fixed in [magento/magento2#18000](https://github.com/magento/magento2/pull/18000)) + * [#9830](https://github.com/magento/magento2/issues/9830) -- Null order in Magento\Sales\Block\Order\PrintShipment.php (fixed in [magento/magento2#17998](https://github.com/magento/magento2/pull/17998)) + * [#10530](https://github.com/magento/magento2/issues/10530) -- Print order error on magento 2.1.8 (fixed in [magento/magento2#17998](https://github.com/magento/magento2/pull/17998)) + * [#10440](https://github.com/magento/magento2/issues/10440) -- Missing $debugHintsPath when sending email via command (fixed in [magento/magento2#17984](https://github.com/magento/magento2/pull/17984)) + * [#18079](https://github.com/magento/magento2/issues/18079) -- Inconsistent return type for getStoreId() (fixed in [magento/magento2#18086](https://github.com/magento/magento2/pull/18086)) + * [#18138](https://github.com/magento/magento2/issues/18138) -- WYSIWYG editor fails to parse directives of files with special characters in URL (so random files) (fixed in [magento/magento2#18215](https://github.com/magento/magento2/pull/18215)) + * [#18101](https://github.com/magento/magento2/issues/18101) -- Wrong sort order for customer groups in customer grid filter (fixed in [magento/magento2#18280](https://github.com/magento/magento2/pull/18280)) + * [#17977](https://github.com/magento/magento2/issues/17977) -- Show Method if Not Applicable for Free Shipping doesn't work. (fixed in [magento/magento2#17982](https://github.com/magento/magento2/pull/17982)) + * [#17023](https://github.com/magento/magento2/issues/17023) -- CSV Import of `sku,attribute` empties `url_key` value (fixed in [magento/magento2#17882](https://github.com/magento/magento2/pull/17882)) + * [#18330](https://github.com/magento/magento2/issues/18330) -- Checkout - Infinite loading indicator when server returned error (fixed in [magento/magento2#18369](https://github.com/magento/magento2/pull/18369)) + * [#16497](https://github.com/magento/magento2/issues/16497) -- Magento 2.2.5: Google Analytics not added to head correctly (fixed in [magento/magento2#18375](https://github.com/magento/magento2/pull/18375)) + * [#17152](https://github.com/magento/magento2/issues/17152) -- Failure of "Send Order Email Copy" spams customers, every minute, forever. (fixed in [magento/magento2#18376](https://github.com/magento/magento2/pull/18376)) + * [#18162](https://github.com/magento/magento2/issues/18162) -- Cannot edit customer using inline edit if password is expired (fixed in [magento/magento2#18414](https://github.com/magento/magento2/pull/18414)) + * [#3283](https://github.com/magento/magento2/issues/3283) -- «Yes/No» attributes should be allowed in the Layered Navigation (fixed in [magento/magento2#17823](https://github.com/magento/magento2/pull/17823)) + * [#17493](https://github.com/magento/magento2/issues/17493) -- Catalog Rule & Selected Categories with level > 3 (fixed in [magento/magento2#18175](https://github.com/magento/magento2/pull/18175)) + * [#17770](https://github.com/magento/magento2/issues/17770) -- Table rate fail when using ZIP+4 shipping address (fixed in [magento/magento2#18166](https://github.com/magento/magento2/pull/18166)) + * [#13156](https://github.com/magento/magento2/issues/13156) -- Updating attribute option data through API will set unwanted source_model on the attribute (fixed in [magento/magento2#18390](https://github.com/magento/magento2/pull/18390)) + * [#17190](https://github.com/magento/magento2/issues/17190) -- system.log rapidly increasing after Magento CE 2.2.5 update (cron logs) (fixed in [magento/magento2#18389](https://github.com/magento/magento2/pull/18389)) + * [#15085](https://github.com/magento/magento2/issues/15085) -- StockRegistryInterface :: getLowStockItems() returns StockStatusCollection instead of StockItemCollection (fixed in [magento/magento2#18427](https://github.com/magento/magento2/pull/18427)) + * [#15652](https://github.com/magento/magento2/issues/15652) -- REST API create order POST /V1/orders (fixed in [magento/magento2#15683](https://github.com/magento/magento2/pull/15683)) + * [#4942](https://github.com/magento/magento2/issues/4942) -- On editing a Bundle product from shopping cart the user defined quantities of the options are overwritten (fixed in [magento/magento2#15905](https://github.com/magento/magento2/pull/15905)) + * [#17514](https://github.com/magento/magento2/issues/17514) -- Add Australian regions (fixed in [magento/magento2#17516](https://github.com/magento/magento2/pull/17516)) + * [#12479](https://github.com/magento/magento2/issues/12479) -- Saving Customer Model directly causes loss of data (fixed in [magento/magento2#17968](https://github.com/magento/magento2/pull/17968)) + * [#9219](https://github.com/magento/magento2/issues/9219) -- Custom Product Attribute changes 'backend_type' when 'is_user_defined = 1' and get updated/saved in Admin Backend (fixed in [magento/magento2#18196](https://github.com/magento/magento2/pull/18196)) + * [#18164](https://github.com/magento/magento2/issues/18164) -- Checkout - Cannot read property 'code' of undefined (fixed in [magento/magento2#18495](https://github.com/magento/magento2/pull/18495)) + * [#14555](https://github.com/magento/magento2/issues/14555) -- Communication's component validator does not propagate exceptions, obscuring the cause of the error (fixed in [magento/magento2#18554](https://github.com/magento/magento2/pull/18554)) + * [#18477](https://github.com/magento/magento2/issues/18477) -- Set maximum Qty Allowed in Shopping Cart is 0 still allow adding to cart (fixed in [magento/magento2#18552](https://github.com/magento/magento2/pull/18552)) + * [#12070](https://github.com/magento/magento2/issues/12070) -- M2.2.0 Admin Grid column ordering/positioning not working when single store mode set On (fixed in [magento/magento2#18561](https://github.com/magento/magento2/pull/18561)) + * [#18581](https://github.com/magento/magento2/issues/18581) -- Calendar Icon aligement Issue (fixed in [magento/magento2#18593](https://github.com/magento/magento2/pull/18593)) + * [#18585](https://github.com/magento/magento2/issues/18585) -- Navigation arrows zoomed fotorama disappear (fixed in [magento/magento2#18595](https://github.com/magento/magento2/pull/18595)) + * [#12969](https://github.com/magento/magento2/issues/12969) -- processor.php getHostUrl() does not detect the server port correctly (fixed in [magento/magento2#18659](https://github.com/magento/magento2/pull/18659)) + * [#14510](https://github.com/magento/magento2/issues/14510) -- Creating custom customer attribute with default value 0 will cause not saving value for customer entity (fixed in [magento/magento2#16915](https://github.com/magento/magento2/pull/16915)) + * [#18234](https://github.com/magento/magento2/issues/18234) -- Product Import -> Upsert Category: Url Rewrites are just created for default website (fixed in [magento/magento2#18563](https://github.com/magento/magento2/pull/18563)) + * [#5929](https://github.com/magento/magento2/issues/5929) -- Saving Product does not update URL rewrite in Magento 2.1.0 (fixed in [magento/magento2#18566](https://github.com/magento/magento2/pull/18566)) + * [#18532](https://github.com/magento/magento2/issues/18532) -- Module Catalog: product "Save and Duplicate" causes getting infinite loop (fixed in [magento/magento2#18566](https://github.com/magento/magento2/pull/18566)) + * [#18131](https://github.com/magento/magento2/issues/18131) -- Entity Type ID at Join (fixed in [magento/magento2#18658](https://github.com/magento/magento2/pull/18658)) + * [#15259](https://github.com/magento/magento2/issues/15259) -- Advanced Reporting > Unable to disable without providing Industry value (fixed in [magento/magento2#15366](https://github.com/magento/magento2/pull/15366)) + * [#18094](https://github.com/magento/magento2/issues/18094) -- Should getQty() return int/float or string? (fixed in [magento/magento2#18424](https://github.com/magento/magento2/pull/18424)) + * [#18534](https://github.com/magento/magento2/issues/18534) -- Bug when 2 wysiwyg editors are on category edit page or product edit page (fixed in [magento/magento2#18535](https://github.com/magento/magento2/pull/18535)) + * [#18589](https://github.com/magento/magento2/issues/18589) -- Empty cart button does not work (fixed in [magento/magento2#18597](https://github.com/magento/magento2/pull/18597)) + * [#18268](https://github.com/magento/magento2/issues/18268) -- M2.2.6 : Special price of 0.0000 is not shown on frontend, but is calculated in cart (fixed in [magento/magento2#18604](https://github.com/magento/magento2/pull/18604)) + * [#17954](https://github.com/magento/magento2/issues/17954) -- Customer get unsubscribe to newsletter on password reset email request with Newsletter Need to Confirm Set to Yes on admin settings (fixed in [magento/magento2#18643](https://github.com/magento/magento2/pull/18643)) + * [#16939](https://github.com/magento/magento2/issues/16939) -- Incorrect configuration scope is occasionally returned when attempting to resolve a null scope id (fixed in [magento/magento2#16940](https://github.com/magento/magento2/pull/16940)) + * [#18264](https://github.com/magento/magento2/issues/18264) -- M2.2.6 : "Order by price" not working in product listing (fixed in [magento/magento2#18737](https://github.com/magento/magento2/pull/18737)) + * [#17638](https://github.com/magento/magento2/issues/17638) -- Bundle Special Prices not correctly rounded (fixed in [magento/magento2#17971](https://github.com/magento/magento2/pull/17971)) + * [#17865](https://github.com/magento/magento2/issues/17865) -- import new products via csv: products are created with empty value when strings are too long (fixed in [magento/magento2#18591](https://github.com/magento/magento2/pull/18591)) + * [#12300](https://github.com/magento/magento2/issues/12300) -- SKU values are not trimmed with the space. (fixed in [magento/magento2#18862](https://github.com/magento/magento2/pull/18862)) + * [#16572](https://github.com/magento/magento2/issues/16572) -- Trim whitespace on SKU when saving product (fixed in [magento/magento2#18862](https://github.com/magento/magento2/pull/18862)) + * [#18458](https://github.com/magento/magento2/issues/18458) -- Magento version 2.2.6 Alert widget gets close when click anywhere on screen (fixed in [magento/magento2#18865](https://github.com/magento/magento2/pull/18865)) + * [#18779](https://github.com/magento/magento2/issues/18779) -- Translation issue send-friend in sendphtml (fixed in [magento/magento2#18886](https://github.com/magento/magento2/pull/18886)) + * [#18913](https://github.com/magento/magento2/issues/18913) -- Global-search icon misaligned (fixed in [magento/magento2#18917](https://github.com/magento/magento2/pull/18917)) + * [#17488](https://github.com/magento/magento2/issues/17488) -- Authenticating a customer via REST API does not update the last logged in data (fixed in [magento/magento2#17978](https://github.com/magento/magento2/pull/17978)) + * [#4468](https://github.com/magento/magento2/issues/4468) -- Unable to insert multiple catalog product list widgets in CMS page (fixed in [magento/magento2#18874](https://github.com/magento/magento2/pull/18874)) + * [#18355](https://github.com/magento/magento2/issues/18355) -- Typo in dispatched event name (fixed in [magento/magento2#18372](https://github.com/magento/magento2/pull/18372)) + * [#17744](https://github.com/magento/magento2/issues/17744) -- Virtual-only quotes use default shipping address for estimation instead of default billing address (fixed in [magento/magento2#18863](https://github.com/magento/magento2/pull/18863)) + * [#5021](https://github.com/magento/magento2/issues/5021) -- "Please specify a shipping method" Exception (fixed in [magento/magento2#18870](https://github.com/magento/magento2/pull/18870)) + * [#17485](https://github.com/magento/magento2/issues/17485) -- Adding billing information via mine API expects costumer id (fixed in [magento/magento2#18872](https://github.com/magento/magento2/pull/18872)) + * [#13083](https://github.com/magento/magento2/issues/13083) -- OptionManagement.validateOption throws NoSuchEntityException for "0" option label (fixed in [magento/magento2#18873](https://github.com/magento/magento2/pull/18873)) + * [#18729](https://github.com/magento/magento2/issues/18729) -- Bug in "_sections.less" mixins: missing rules and incorrect default variables (fixed in [magento/magento2#18875](https://github.com/magento/magento2/pull/18875)) + * [#18555](https://github.com/magento/magento2/issues/18555) -- Magento 2.2.6 Default values are not rendering on Wishlist product edit page. (fixed in [magento/magento2#18967](https://github.com/magento/magento2/pull/18967)) + * [#18907](https://github.com/magento/magento2/issues/18907) -- Unable to select payment method according to country of the address at checkout time (fixed in [magento/magento2#18908](https://github.com/magento/magento2/pull/18908)) + * [#16684](https://github.com/magento/magento2/issues/16684) -- Default tax region/state appears in customer & order data (fixed in [magento/magento2#18857](https://github.com/magento/magento2/pull/18857)) + * [#8348](https://github.com/magento/magento2/issues/8348) -- 1 exception(s): Exception #0 (Exception): Warning: Invalid argument supplied for foreach() in NotProtectedExtension.php on line 89 (fixed in [magento/magento2#19012](https://github.com/magento/magento2/pull/19012)) + * [#18323](https://github.com/magento/magento2/issues/18323) -- Order confirmation email for guest checkout does not include download links (fixed in [magento/magento2#19036](https://github.com/magento/magento2/pull/19036)) + * [#19003](https://github.com/magento/magento2/issues/19003) -- salesInvoiceOrder REST API does not make downloadable products available (fixed in [magento/magento2#19036](https://github.com/magento/magento2/pull/19036)) + * [#19034](https://github.com/magento/magento2/issues/19034) -- sales_order_item_save_commit_after and sales_order_save_commit_after events will never fire for guest checkout (fixed in [magento/magento2#19036](https://github.com/magento/magento2/pull/19036)) + * [#2618](https://github.com/magento/magento2/issues/2618) -- Class \Magento\Framework\Data\Form\Element\Fieldset breaks specification of the parent class \Magento\Framework\Data\Form\Element\AbstractElement by not calling the method getBeforeElementHtml (getAfterElementHtml is called) (fixed in [magento/magento2#18985](https://github.com/magento/magento2/pull/18985)) + * [#14007](https://github.com/magento/magento2/issues/14007) -- "Use in Layered Navigation: Filterable (no results)" not working for Price attribute. (fixed in [magento/magento2#19044](https://github.com/magento/magento2/pull/19044)) + * [#12399](https://github.com/magento/magento2/issues/12399) -- Exception Error in Catalog Price Rule while Backend language is not English (fixed in [magento/magento2#19074](https://github.com/magento/magento2/pull/19074)) + * [#18082](https://github.com/magento/magento2/issues/18082) -- Fatal Error when save configurable product in Magento 2.2.5 (fixed in [magento/magento2#18461](https://github.com/magento/magento2/pull/18461)) + * [#18617](https://github.com/magento/magento2/issues/18617) -- Missing Fixed Product Tax total on PDF (fixed in [magento/magento2#18649](https://github.com/magento/magento2/pull/18649)) + * [#18150](https://github.com/magento/magento2/issues/18150) -- Backups error from User Roles Permission 2.2.6 (fixed in [magento/magento2#18815](https://github.com/magento/magento2/pull/18815)) + * [#18901](https://github.com/magento/magento2/issues/18901) -- Forgot password form should not available while customer is logged in. (fixed in [magento/magento2#19089](https://github.com/magento/magento2/pull/19089)) + * [#18840](https://github.com/magento/magento2/issues/18840) -- Invalid Unit Test Annotations (fixed in [magento/magento2#19105](https://github.com/magento/magento2/pull/19105)) + * [#19060](https://github.com/magento/magento2/issues/19060) -- User created by admin cannot login (fixed in [magento/magento2#19110](https://github.com/magento/magento2/pull/19110)) + * [#14849](https://github.com/magento/magento2/issues/14849) -- In Sales Emails no translation using order.getStatusLabel() (fixed in [magento/magento2#14914](https://github.com/magento/magento2/pull/14914)) + * [#17625](https://github.com/magento/magento2/issues/17625) -- Translations done within a theme that's enabled through a category Design change aren't used (fixed in [magento/magento2#17854](https://github.com/magento/magento2/pull/17854)) + * [#17635](https://github.com/magento/magento2/issues/17635) -- addExpressionFieldToSelect has to be called after all addFieldToSelect (fixed in [magento/magento2#17915](https://github.com/magento/magento2/pull/17915)) + * [#18652](https://github.com/magento/magento2/issues/18652) -- Tierprice discount not calculated correctly if has specialprice. (fixed in [magento/magento2#18743](https://github.com/magento/magento2/pull/18743)) + * [#18939](https://github.com/magento/magento2/issues/18939) -- "Not yet calculated" for the tax in the summary section in the checkout is not translatable (fixed in [magento/magento2#18959](https://github.com/magento/magento2/pull/18959)) + * [#16434](https://github.com/magento/magento2/issues/16434) -- Bundle Product Options not showing in Customer Account - Items Ordered (fixed in [magento/magento2#17889](https://github.com/magento/magento2/pull/17889)) + * [#14020](https://github.com/magento/magento2/issues/14020) -- Cart Sales Rule with negated condition over special_price does not work for configurable products (fixed in [magento/magento2#16342](https://github.com/magento/magento2/pull/16342)) + * [#18685](https://github.com/magento/magento2/issues/18685) -- Quote Item Prices are NULL in cart related events. (fixed in [magento/magento2#18808](https://github.com/magento/magento2/pull/18808)) + * [#18956](https://github.com/magento/magento2/issues/18956) -- Import of RootCategoryId should be possbile (Magento/Store/Model/Config/Importer/Processor/Create.php) (fixed in [magento/magento2#19237](https://github.com/magento/magento2/pull/19237)) + * [#19205](https://github.com/magento/magento2/issues/19205) -- Bundle Product Option with input type is checkbox and add to cart with 3 values only 2 values added to cart (fixed in [magento/magento2#19260](https://github.com/magento/magento2/pull/19260)) + * [#6803](https://github.com/magento/magento2/issues/6803) -- Product::addImageToMediaGallery throws Exception (fixed in [magento/magento2#18951](https://github.com/magento/magento2/pull/18951)) + * [#18949](https://github.com/magento/magento2/issues/18949) -- dev/tools/grunt/configs/themes.js gets replaced after update magento (fixed in [magento/magento2#18960](https://github.com/magento/magento2/pull/18960)) + * [#19054](https://github.com/magento/magento2/issues/19054) -- Using Media Image custom attribute type could not display on frontend. (fixed in [magento/magento2#19068](https://github.com/magento/magento2/pull/19068)) + * [#19082](https://github.com/magento/magento2/issues/19082) -- Fatal error: Uncaught Error: Cannot call abstract method Magento\Framework\App\ActionInterface::execute() (fixed in [magento/magento2#19337](https://github.com/magento/magento2/pull/19337)) + * [#19263](https://github.com/magento/magento2/issues/19263) -- Broken backend popup view (fixed in [magento/magento2#19340](https://github.com/magento/magento2/pull/19340)) + * [#4136](https://github.com/magento/magento2/issues/4136) -- Widget condition with unexpected character not preventing from saving (fixed in [magento/magento2#14485](https://github.com/magento/magento2/pull/14485)) + * [#18615](https://github.com/magento/magento2/issues/18615) -- Field restriction incompatibilities between klarna_core_order and sales_order_payment last_trans_id (fixed in [magento/magento2#18621](https://github.com/magento/magento2/pull/18621)) + * [#18904](https://github.com/magento/magento2/issues/18904) -- Missing asterisk for admin required fields (fixed in [magento/magento2#18905](https://github.com/magento/magento2/pull/18905)) + * [#19286](https://github.com/magento/magento2/issues/19286) -- Wrong pager style (fixed in [magento/magento2#19296](https://github.com/magento/magento2/pull/19296)) + * [#13157](https://github.com/magento/magento2/issues/13157) -- Last Ordered Items block - bad js code (fixed in [magento/magento2#19357](https://github.com/magento/magento2/pull/19357)) + * [#17833](https://github.com/magento/magento2/issues/17833) -- Child theme does not inherit translations from parent theme (fixed in [magento/magento2#19023](https://github.com/magento/magento2/pull/19023)) + * [#18839](https://github.com/magento/magento2/issues/18839) -- can't import external http to https redirecting images by default csv import (fixed in [magento/magento2#18899](https://github.com/magento/magento2/pull/18899)) + * [#18887](https://github.com/magento/magento2/issues/18887) -- Magento backend Notifications counter round icon small cut from right side (fixed in [magento/magento2#19356](https://github.com/magento/magento2/pull/19356)) + * [#17813](https://github.com/magento/magento2/issues/17813) -- Huge "product_data_storage" in localStorage hangs the shop (fixed in [magento/magento2#19014](https://github.com/magento/magento2/pull/19014)) + * [#15505](https://github.com/magento/magento2/issues/15505) -- Interceptor class methods do not support nullable return types (fixed in [magento/magento2#19398](https://github.com/magento/magento2/pull/19398)) + * [#19172](https://github.com/magento/magento2/issues/19172) -- Newsletter subscription does not set the correct store_id if already subscribed. Not Fixed in 2.3-dev (fixed in [magento/magento2#19426](https://github.com/magento/magento2/pull/19426)) + * [#18918](https://github.com/magento/magento2/issues/18918) -- Asterisk sign display twice (fixed in [magento/magento2#18922](https://github.com/magento/magento2/pull/18922)) + * [#19127](https://github.com/magento/magento2/issues/19127) -- Cannot connect to Magento 2 market place (fixed in [magento/magento2#19239](https://github.com/magento/magento2/pull/19239)) + * [#19344](https://github.com/magento/magento2/issues/19344) -- Sample Link Issue in Downloadable product. (fixed in [magento/magento2#19431](https://github.com/magento/magento2/pull/19431)) + * [#15931](https://github.com/magento/magento2/issues/15931) -- events.xml cant have no childrens, others can [Magento 2.2.4] (fixed in [magento/magento2#19145](https://github.com/magento/magento2/pull/19145)) + * [#19418](https://github.com/magento/magento2/issues/19418) -- Cannot add additional field to Newsletter system configuration at desired position (fixed in [magento/magento2#19568](https://github.com/magento/magento2/pull/19568)) + * [#19424](https://github.com/magento/magento2/issues/19424) -- \Magento\Checkout\Observer\SalesQuoteSaveAfterObserver fails to update the checkout session quote id when applicable (fixed in [magento/magento2#19678](https://github.com/magento/magento2/pull/19678)) + * [#19796](https://github.com/magento/magento2/issues/19796) -- Sales Order invoice Update Qty's Button is misaligned (fixed in [magento/magento2#19804](https://github.com/magento/magento2/pull/19804)) + * [#19917](https://github.com/magento/magento2/issues/19917) -- allowDrug? ;-) (fixed in [magento/magento2#19949](https://github.com/magento/magento2/pull/19949)) + * [#19721](https://github.com/magento/magento2/issues/19721) -- Typo in SalesRule/Model/ResourceModel/Coupon/Usage.php (fixed in [magento/magento2#19968](https://github.com/magento/magento2/pull/19968)) + * [#8952](https://github.com/magento/magento2/issues/8952) -- You can't subscribe to newsletter if you already have an account (fixed in [magento/magento2#18912](https://github.com/magento/magento2/pull/18912)) + * [#19142](https://github.com/magento/magento2/issues/19142) -- Home page store loge should be clickable to reload page (fixed in [magento/magento2#19199](https://github.com/magento/magento2/pull/19199)) + * [#18374](https://github.com/magento/magento2/issues/18374) -- Unable to get product attribute value for store-view scope type in product collection loaded for a specific store. (fixed in [magento/magento2#19911](https://github.com/magento/magento2/pull/19911)) + * [#18941](https://github.com/magento/magento2/issues/18941) -- Calling getCurrentUrl on Store will wrongly add "___store" parameter (fixed in [magento/magento2#19945](https://github.com/magento/magento2/pull/19945)) + * [#19052](https://github.com/magento/magento2/issues/19052) -- Position order showing before the text box (fixed in [magento/magento2#19056](https://github.com/magento/magento2/pull/19056)) + * [#19285](https://github.com/magento/magento2/issues/19285) -- On Notification page Select All and Select Visible both works same (fixed in [magento/magento2#19910](https://github.com/magento/magento2/pull/19910)) + * [#19507](https://github.com/magento/magento2/issues/19507) -- Frontend Minicart dropdown alignment issue (fixed in [magento/magento2#19889](https://github.com/magento/magento2/pull/19889)) + * [#19605](https://github.com/magento/magento2/issues/19605) -- Don't static compile disabled modules (fixed in [magento/magento2#19989](https://github.com/magento/magento2/pull/19989)) + * [#19346](https://github.com/magento/magento2/issues/19346) -- Import data 2.2.6 Value for 'product_type' attribute contains incorrect value (fixed in [magento/magento2#20081](https://github.com/magento/magento2/pull/20081)) + * [#19780](https://github.com/magento/magento2/issues/19780) -- Incorrect class name on Orders and returns page. (fixed in [magento/magento2#20080](https://github.com/magento/magento2/pull/20080)) + * [#19230](https://github.com/magento/magento2/issues/19230) -- Can't Cancel Order (fixed in [magento/magento2#19423](https://github.com/magento/magento2/pull/19423)) + * [#19099](https://github.com/magento/magento2/issues/19099) -- New Link is not correctly shown as Current if contains default parts (fixed in [magento/magento2#19927](https://github.com/magento/magento2/pull/19927)) + * [#19940](https://github.com/magento/magento2/issues/19940) -- Exception undefined variable itemsOrderItemId while creating shipment through MSI (fixed in [magento/magento2#20082](https://github.com/magento/magento2/pull/20082)) + * [#19101](https://github.com/magento/magento2/issues/19101) -- API REST and Reserved Order Id (fixed in [magento/magento2#20208](https://github.com/magento/magento2/pull/20208)) + * [#20210](https://github.com/magento/magento2/issues/20210) -- Hamburger Icon was available on a page where menu was not present. Issue in responsive view (fixed in [magento/magento2#20219](https://github.com/magento/magento2/pull/20219)) + * [#16198](https://github.com/magento/magento2/issues/16198) -- Category image remain after deleted (fixed in [magento/magento2#20178](https://github.com/magento/magento2/pull/20178)) + * [#18192](https://github.com/magento/magento2/issues/18192) -- Backend issue : "ratings isn't available" website wise (fixed in [magento/magento2#20183](https://github.com/magento/magento2/pull/20183)) + * [#14937](https://github.com/magento/magento2/issues/14937) -- Javascript error thrown from uiComponent 'notification_area' if messages are malformed (fixed in [magento/magento2#20271](https://github.com/magento/magento2/pull/20271)) + * [#17819](https://github.com/magento/magento2/issues/17819) -- Wrong product url from getProductUrl when current category has not product object (fixed in [magento/magento2#20286](https://github.com/magento/magento2/pull/20286)) + * [#20296](https://github.com/magento/magento2/issues/20296) -- "@magentoDataIsolation" is used instead of "@magentoDbIsolation" in some integration tests. (fixed in [magento/magento2#20298](https://github.com/magento/magento2/pull/20298)) + * [#20158](https://github.com/magento/magento2/issues/20158) -- Store switcher not aligned proper in tab view (fixed in [magento/magento2#20325](https://github.com/magento/magento2/pull/20325)) + * [#20232](https://github.com/magento/magento2/issues/20232) -- Backend order credit card detail check box misaligned (fixed in [magento/magento2#20328](https://github.com/magento/magento2/pull/20328)) + * [#20098](https://github.com/magento/magento2/issues/20098) -- Product image failure when importing through CSV (fixed in [magento/magento2#20329](https://github.com/magento/magento2/pull/20329)) + * [#20352](https://github.com/magento/magento2/issues/20352) -- File type option value shows html content in admin order view. (fixed in [magento/magento2#20353](https://github.com/magento/magento2/pull/20353)) + * [#18170](https://github.com/magento/magento2/issues/18170) -- Unable to reset password if customer has address from not allowed country (fixed in [magento/magento2#19964](https://github.com/magento/magento2/pull/19964)) + * [#19982](https://github.com/magento/magento2/issues/19982) -- Catalogsearch Reindex (fixed in [magento/magento2#19984](https://github.com/magento/magento2/pull/19984)) + * [#9130](https://github.com/magento/magento2/issues/9130) -- If stock is bellow OutOfStock Threshold, a negative qty is displayed in Product List Page (fixed in [magento/magento2#20206](https://github.com/magento/magento2/pull/20206)) + * [#19609](https://github.com/magento/magento2/issues/19609) -- config:set --lock-config does not act on other scopes (fixed in [magento/magento2#20322](https://github.com/magento/magento2/pull/20322)) + * [#19399](https://github.com/magento/magento2/issues/19399) -- Add product customization option collapsible design issue (fixed in [magento/magento2#19400](https://github.com/magento/magento2/pull/19400)) + * [#20120](https://github.com/magento/magento2/issues/20120) -- Review Details Detailed Rating misaligned (fixed in [magento/magento2#20272](https://github.com/magento/magento2/pull/20272)) + * [#20172](https://github.com/magento/magento2/issues/20172) -- On customer login page input field are short width on tablet view (fixed in [magento/magento2#20369](https://github.com/magento/magento2/pull/20369)) + * [#19085](https://github.com/magento/magento2/issues/19085) -- Translation in tier_price.phtml not working (fixed in [magento/magento2#19377](https://github.com/magento/magento2/pull/19377)) + * [#18361](https://github.com/magento/magento2/issues/18361) -- Customer last name is encoded twice in the XML interface (fixed in [magento/magento2#18362](https://github.com/magento/magento2/pull/18362)) + * [#19887](https://github.com/magento/magento2/issues/19887) -- creating new shipment: gettting all trackers. after this commit 2307e16 (fixed in [magento/magento2#20184](https://github.com/magento/magento2/pull/20184)) + * [#19985](https://github.com/magento/magento2/issues/19985) -- Send email confirmation popup close button area overlapping to content (fixed in [magento/magento2#20541](https://github.com/magento/magento2/pull/20541)) + * [#17759](https://github.com/magento/magento2/issues/17759) -- M2.2.5 : CustomerRepository::getList() does not load custom attribute if the name is "company" (fixed in [magento/magento2#20284](https://github.com/magento/magento2/pull/20284)) + * [#19800](https://github.com/magento/magento2/issues/19800) -- Contact us : design improvement (fixed in [magento/magento2#20455](https://github.com/magento/magento2/pull/20455)) + * [#19645](https://github.com/magento/magento2/issues/19645) -- Area Frontend: Account information page checkbox alignment issue. (fixed in [magento/magento2#20457](https://github.com/magento/magento2/pull/20457)) + * [#19791](https://github.com/magento/magento2/issues/19791) -- Logo vertical misalignment. (fixed in [magento/magento2#20456](https://github.com/magento/magento2/pull/20456)) + * [#15950](https://github.com/magento/magento2/issues/15950) -- Magento2 CSV product import qty and is_in_stock not working correct (fixed in [magento/magento2#20177](https://github.com/magento/magento2/pull/20177)) + * [#19899](https://github.com/magento/magento2/issues/19899) -- Credit memo for $0 order without refunded shipping produces negative credit memo (fixed in [magento/magento2#20508](https://github.com/magento/magento2/pull/20508)) + * [#20121](https://github.com/magento/magento2/issues/20121) -- Cancel order increases stock although "Set Items' Status to be In Stock When Order is Cancelled" is set to No (fixed in [magento/magento2#20547](https://github.com/magento/magento2/pull/20547)) + * [#18027](https://github.com/magento/magento2/issues/18027) -- Cart Total is NaN in some circumstances (fixed in [magento/magento2#20638](https://github.com/magento/magento2/pull/20638)) + * [#20376](https://github.com/magento/magento2/issues/20376) -- Image gets uploaded if field is disable in Category (fixed in [magento/magento2#20636](https://github.com/magento/magento2/pull/20636)) + * [#20169](https://github.com/magento/magento2/issues/20169) -- Admin user with restricted "order create" access can "view", "cancel", etc via API (fixed in [magento/magento2#20542](https://github.com/magento/magento2/pull/20542)) + * [#20399](https://github.com/magento/magento2/issues/20399) -- On wish list page edit, remove item misalign in 640 X 767 resolution (fixed in [magento/magento2#20544](https://github.com/magento/magento2/pull/20544)) + * [#20373](https://github.com/magento/magento2/issues/20373) -- Order view invoices template not display proper on ipad (fixed in [magento/magento2#20546](https://github.com/magento/magento2/pull/20546)) + * [#18387](https://github.com/magento/magento2/issues/18387) -- catalog:images:resize fails to process all images -> Possible underlying Magento/Framework/DB/Query/Generator issue (fixed in [magento/magento2#18809](https://github.com/magento/magento2/pull/18809)) + * [#18931](https://github.com/magento/magento2/issues/18931) -- Product added to shopping cart / comparison list message not translated by default (fixed in [magento/magento2#19461](https://github.com/magento/magento2/pull/19461)) + * [#14712](https://github.com/magento/magento2/issues/14712) -- Shipping issue on PayPal Express (fixed in [magento/magento2#19655](https://github.com/magento/magento2/pull/19655)) + * [#20113](https://github.com/magento/magento2/issues/20113) -- Widget option labels are misalinged (fixed in [magento/magento2#20270](https://github.com/magento/magento2/pull/20270)) + * [#20304](https://github.com/magento/magento2/issues/20304) -- No space between step title and saved address in checkout (fixed in [magento/magento2#20418](https://github.com/magento/magento2/pull/20418)) + * [#20609](https://github.com/magento/magento2/issues/20609) -- Currency rate value not align proper in order information tab when we create creditmemo from admin (fixed in [magento/magento2#20613](https://github.com/magento/magento2/pull/20613)) + * [#20500](https://github.com/magento/magento2/issues/20500) -- Recent Order Product Title Misaligned in Sidebar (fixed in [magento/magento2#20744](https://github.com/magento/magento2/pull/20744)) + * [#20563](https://github.com/magento/magento2/issues/20563) -- Go to shipping information, Update qty & Addresses and Enter a new address button Not aligned from left and right in 767px screen size (fixed in [magento/magento2#20739](https://github.com/magento/magento2/pull/20739)) + * [#19436](https://github.com/magento/magento2/issues/19436) -- Attribute Option with zero at the bigining does not work if there is already option with the same number without the zero (REST API)) (fixed in [magento/magento2#19612](https://github.com/magento/magento2/pull/19612)) + * [#20604](https://github.com/magento/magento2/issues/20604) -- Gift option message overlap edit and remove button (fixed in [magento/magento2#20784](https://github.com/magento/magento2/pull/20784)) + * [#20137](https://github.com/magento/magento2/issues/20137) -- On checkout page apply discount button is not align with input box (fixed in [magento/magento2#20837](https://github.com/magento/magento2/pull/20837)) + * [#20624](https://github.com/magento/magento2/issues/20624) -- `\Magento\ImportExport\Block\Adminhtml\Export\Filter::_getSelectHtmlWithValue()` method overwrites self $value argument (fixed in [magento/magento2#20863](https://github.com/magento/magento2/pull/20863)) + * [#20409](https://github.com/magento/magento2/issues/20409) -- Magento\Catalog\Api\ProductRenderListInterface returns products regardless of visibility (fixed in [magento/magento2#20886](https://github.com/magento/magento2/pull/20886)) + * [#20259](https://github.com/magento/magento2/issues/20259) -- Store switcher not sliding up and down, only dropdown arrow working (fixed in [magento/magento2#20540](https://github.com/magento/magento2/pull/20540)) +* GitHub pull requests: + * [magento/magento2#16361](https://github.com/magento/magento2/pull/16361) -- Allow usage of config-global.php when running Integration Tests (by @jissereitsma) + * [magento/magento2#16422](https://github.com/magento/magento2/pull/16422) -- Replace intval() function by using direct type casting to (int) where no default value is needed (by @mhauri) + * [magento/magento2#17708](https://github.com/magento/magento2/pull/17708) -- Prevent rendering of "Ship here" button if it is not needed (by @marvinhuebner) + * [magento/magento2#17783](https://github.com/magento/magento2/pull/17783) -- Current password autocomplete for admin login (by @flancer64) + * [magento/magento2#17885](https://github.com/magento/magento2/pull/17885) -- Make sure all linked products (related, upsells, crosssells) show up … (by @hostep) + * [magento/magento2#17891](https://github.com/magento/magento2/pull/17891) -- #17890: show correct text swatch values per store view (by @magicaner) + * [magento/magento2#17919](https://github.com/magento/magento2/pull/17919) -- [remove] rich snippet declaration on grouped product (by @AurelienLavorel) + * [magento/magento2#17945](https://github.com/magento/magento2/pull/17945) -- [2.2] return $this from setters in Analytics/ReportXml/DB/SelectBuilder.php (by @TBlindaruk) + * [magento/magento2#17966](https://github.com/magento/magento2/pull/17966) -- Fix currency symbol setting back to default #17567 (by @magently) + * [magento/magento2#17970](https://github.com/magento/magento2/pull/17970) -- Integration test for swatches types in attribute configuration added (by @rogyar) + * [magento/magento2#17990](https://github.com/magento/magento2/pull/17990) -- Menu does not work when you change from Mobile to Desktop mode #5402 (by @emanuelarcos) + * [magento/magento2#18001](https://github.com/magento/magento2/pull/18001) -- Fixes saving product in single-store mode if website_id <> 1 (by @eduard13) + * [magento/magento2#18002](https://github.com/magento/magento2/pull/18002) -- Fix module uninstall shell command and composer removal w/out regression (by @Thundar) + * [magento/magento2#18004](https://github.com/magento/magento2/pull/18004) -- [CatalogUrlRewrite] Covering the CategoryProcessUrlRewriteMovingObserver by Unit Test (by @eduard13) + * [magento/magento2#18018](https://github.com/magento/magento2/pull/18018) -- [Backport] Use route ID when creating secret keys in backend menus instead of route name #17650 (by @lfolco) + * [magento/magento2#18034](https://github.com/magento/magento2/pull/18034) -- [Backport] fix notice undefined shipment: revert locale inside loop (by @dmytro-ch) + * [magento/magento2#18127](https://github.com/magento/magento2/pull/18127) -- [Backport] typofix: ImportCollection -> ItemCollection (by @dmytro-ch) + * [magento/magento2#18137](https://github.com/magento/magento2/pull/18137) -- [2.2] Update labels section in README.md (by @sidolov) + * [magento/magento2#14065](https://github.com/magento/magento2/pull/14065) -- Correctly convert config integration api resources (by @therool) + * [magento/magento2#17679](https://github.com/magento/magento2/pull/17679) -- Update shipment collection to unserialize packages attribute after load (by @dnsv) + * [magento/magento2#17993](https://github.com/magento/magento2/pull/17993) -- fix #17582 ./bin/magento config:show fails with a fatal error (by @keyurshah070) + * [magento/magento2#18000](https://github.com/magento/magento2/pull/18000) -- Fix sitemap grid render incorrect base urls for multiple stores (by @nntoan) + * [magento/magento2#18055](https://github.com/magento/magento2/pull/18055) -- fix: reset search mini-form when we have no data / an empty response (by @DanielRuf) + * [magento/magento2#18097](https://github.com/magento/magento2/pull/18097) -- [Backport] Fix import grouped products #12853 (by @insanityinside) + * [magento/magento2#18113](https://github.com/magento/magento2/pull/18113) -- [Backport] Fixes from #15947 (by @ihor-sviziev) + * [magento/magento2#18098](https://github.com/magento/magento2/pull/18098) -- Fix shipping discount failed to apply during place order (by @torreytsui) + * [magento/magento2#18126](https://github.com/magento/magento2/pull/18126) -- [Backport] [2.2] Changed intval($val) to (int) $val, since it is faster: (by @dmytro-ch) + * [magento/magento2#17511](https://github.com/magento/magento2/pull/17511) -- Use cast types instead of xyzval() (by @sreichel) + * [magento/magento2#17998](https://github.com/magento/magento2/pull/17998) -- 9830 - Null order in Magento\Sales\Block\Order\PrintShipment.php (by @MateuszChrapek) + * [magento/magento2#17984](https://github.com/magento/magento2/pull/17984) -- Implemeted MAGETWO-81170: Missing $debugHintsPath when sending email … (by @passtet) + * [magento/magento2#18225](https://github.com/magento/magento2/pull/18225) -- Module Catalog: fix issue with custom option price conversion for different base currency on website level (by @oleksii-lisovyi) + * [magento/magento2#16885](https://github.com/magento/magento2/pull/16885) -- [Fix] Do not modify current list of countries with require states during setup upgrade (by @jalogut) + * [magento/magento2#18086](https://github.com/magento/magento2/pull/18086) -- Cast products "getStoreId()" to int, closes #18079 (by @sreichel) + * [magento/magento2#18215](https://github.com/magento/magento2/pull/18215) -- fix wysiwyg editor not decoding base64 filenames special chars (by @adammada) + * [magento/magento2#18280](https://github.com/magento/magento2/pull/18280) -- [Backport] Change sort order for customer group options (by @dmytro-ch) + * [magento/magento2#18168](https://github.com/magento/magento2/pull/18168) -- Fixed issue with lib-line-height mixin failing when value of 'normal'… (by @CNanninga) + * [magento/magento2#18310](https://github.com/magento/magento2/pull/18310) -- [Backport] Sales: add missing unit tests for model classes (by @dmytro-ch) + * [magento/magento2#18311](https://github.com/magento/magento2/pull/18311) -- [Backport] Added integration test for gift message quote merge (by @dmytro-ch) + * [magento/magento2#17695](https://github.com/magento/magento2/pull/17695) -- ConfigurableProduct show prices in select options (by @alexeya-ven) + * [magento/magento2#17982](https://github.com/magento/magento2/pull/17982) -- add error message in else condition (by @vaibhavahalpara) + * [magento/magento2#18354](https://github.com/magento/magento2/pull/18354) -- Fix for parsing attribute options labels, when & used. (by @bartoszkubicki) + * [magento/magento2#17882](https://github.com/magento/magento2/pull/17882) -- Do not overwrite URL Key with blank value (by @josephmcdermott) + * [magento/magento2#17986](https://github.com/magento/magento2/pull/17986) -- Implemented 17964: Backend Order creation Authorizenet: If invalid cr… (by @passtet) + * [magento/magento2#18283](https://github.com/magento/magento2/pull/18283) -- [Backport] Fix for removing the dirs while creating a TAR archive (by @haroldclaus) + * [magento/magento2#18369](https://github.com/magento/magento2/pull/18369) -- [Backport] Fix throwing error by checkout error processor model (by @ihor-sviziev) + * [magento/magento2#18375](https://github.com/magento/magento2/pull/18375) -- Backport 2.2 - Fix wrong reference in google analytics module layout xml (by @sambolek) + * [magento/magento2#18377](https://github.com/magento/magento2/pull/18377) -- [Backport 2.2-develop] Refactor Mass Order Cancel code to use Interface (by @JeroenVanLeusden) + * [magento/magento2#18376](https://github.com/magento/magento2/pull/18376) -- Backport 2.2 - Fix issue 17152 - prevent email being marked as not se… (by @sambolek) + * [magento/magento2#18391](https://github.com/magento/magento2/pull/18391) -- Backport 2.2 - Allow keyboard navigation in browser on product detail… (by @hostep) + * [magento/magento2#18400](https://github.com/magento/magento2/pull/18400) -- Admin Login Form > Aliging Label (by @rafaelstz) + * [magento/magento2#18414](https://github.com/magento/magento2/pull/18414) -- [Backport] Fix the issue with customer inline edit when password is expired (by @dmytro-ch) + * [magento/magento2#18415](https://github.com/magento/magento2/pull/18415) -- [Backport] Added unit test for CRON converter plugin (by @dmytro-ch) + * [magento/magento2#18426](https://github.com/magento/magento2/pull/18426) -- [Backport] Removed unnecessary characters from comments (by @lewisvoncken) + * [magento/magento2#18428](https://github.com/magento/magento2/pull/18428) -- [Backport] small misspelling fixed (by @lewisvoncken) + * [magento/magento2#18429](https://github.com/magento/magento2/pull/18429) -- [Backport] Fix documentation grammar errors and typos in actions.js (by @lewisvoncken) + * [magento/magento2#18430](https://github.com/magento/magento2/pull/18430) -- [Backport] Fix documentation typos in registry.js (by @lewisvoncken) + * [magento/magento2#18433](https://github.com/magento/magento2/pull/18433) -- [Backport] Improve code quality subscriber new action (by @lewisvoncken) + * [magento/magento2#18432](https://github.com/magento/magento2/pull/18432) -- [Backport] Removed commented code (by @lewisvoncken) + * [magento/magento2#17823](https://github.com/magento/magento2/pull/17823) -- [FEATURE] [issue-3283] Added Filter Support for Yes/No (boolean) attr… (by @lewisvoncken) + * [magento/magento2#18175](https://github.com/magento/magento2/pull/18175) -- Fix category tree in cart price rule #17493 (by @magently) + * [magento/magento2#18166](https://github.com/magento/magento2/pull/18166) -- Fix table rate failing for zip+4 address #17770 (by @magently) + * [magento/magento2#18389](https://github.com/magento/magento2/pull/18389) -- Backport 2.2 - Introducing a dedicated cron.log file for logging cron… (by @hostep) + * [magento/magento2#18390](https://github.com/magento/magento2/pull/18390) -- Backport 2.2 - Don't set a source model on the attribute when it's no… (by @hostep) + * [magento/magento2#18422](https://github.com/magento/magento2/pull/18422) -- [BACKPORT] Replace sort callbacks to spaceship operator (by @lewisvoncken) + * [magento/magento2#18403](https://github.com/magento/magento2/pull/18403) -- Fix setup wizard page logo (by @rafaelstz) + * [magento/magento2#18425](https://github.com/magento/magento2/pull/18425) -- [Backport] Fixing Snake Case To Camel Case (by @lewisvoncken) + * [magento/magento2#18427](https://github.com/magento/magento2/pull/18427) -- [Backport] Fix wrong return type in StockRegistryInterface API (by @lewisvoncken) + * [magento/magento2#15683](https://github.com/magento/magento2/pull/15683) -- Added checks to see if the payment is available (by @michielgerritsen) + * [magento/magento2#15905](https://github.com/magento/magento2/pull/15905) -- #4942 and bundle checkbox bug (by @JosephMaxwell) + * [magento/magento2#16115](https://github.com/magento/magento2/pull/16115) -- Fix type hint of customer-data updateSectionId parameters (by @Vinai) + * [magento/magento2#17516](https://github.com/magento/magento2/pull/17516) -- Feature australian regions (by @maximbaibakov) + * [magento/magento2#18155](https://github.com/magento/magento2/pull/18155) -- Fix type hint of @message declaration as the "setWidgetParameters" method allows arrays too (by @avstudnitz) + * [magento/magento2#18401](https://github.com/magento/magento2/pull/18401) -- Admin > Footer > Aligning Proportionally (by @rafaelstz) + * [magento/magento2#17968](https://github.com/magento/magento2/pull/17968) -- Fix Customer custom attributes lost after save (by @Thundar) + * [magento/magento2#18196](https://github.com/magento/magento2/pull/18196) -- Fix for custom product attribute changing 'backend_type' when 'is_user_defined = 1' and get updated/saved in Admin Backend (by @bartoszkubicki) + * [magento/magento2#18495](https://github.com/magento/magento2/pull/18495) -- [Backport] Checkout - Fix "Cannot read property 'code' on undefined" issue (by @ihor-sviziev) + * [magento/magento2#18552](https://github.com/magento/magento2/pull/18552) -- [Backport] Added validation on maximum quantity allowed in shopping cart (by @gelanivishal) + * [magento/magento2#18554](https://github.com/magento/magento2/pull/18554) -- [Backport] throw exception InvalidArgumentException during validate scheme (by @gelanivishal) + * [magento/magento2#18556](https://github.com/magento/magento2/pull/18556) -- [Backport] Fixed typo from filed to field (by @gelanivishal) + * [magento/magento2#18559](https://github.com/magento/magento2/pull/18559) -- [Backport] Covering the AssignOrderToCustomerObserver by Unit Test (by @gelanivishal) + * [magento/magento2#18564](https://github.com/magento/magento2/pull/18564) -- [Backport] Empty option Label should always be blank even if attribute is required (by @gelanivishal) + * [magento/magento2#18561](https://github.com/magento/magento2/pull/18561) -- [2.2] added component status based filtering (by @gelanivishal) + * [magento/magento2#18569](https://github.com/magento/magento2/pull/18569) -- [Backport] Make it possible to disable report bugs link (by @gelanivishal) + * [magento/magento2#18587](https://github.com/magento/magento2/pull/18587) -- [Backport] Prevent XSS on checkout (by @dmytro-ch) + * [magento/magento2#18586](https://github.com/magento/magento2/pull/18586) -- [Backport] Added missing throw tag for exception to docblock of construct (by @dmytro-ch) + * [magento/magento2#18593](https://github.com/magento/magento2/pull/18593) -- Calendar icon in advance pricing alignment solved (by @speedy008) + * [magento/magento2#18595](https://github.com/magento/magento2/pull/18595) -- [Backport] Fix disappearing navigation arrows in fotorama zoom (by @luukschakenraad) + * [magento/magento2#18599](https://github.com/magento/magento2/pull/18599) -- [Backport] Do not use new Phrase in Link Current class (by @dmytro-ch) + * [magento/magento2#18619](https://github.com/magento/magento2/pull/18619) -- [Backport] Add required fields to templates (by @miguelbalparda) + * [magento/magento2#18656](https://github.com/magento/magento2/pull/18656) -- [Backport] Fix product details causing Validation error (by @gelanivishal) + * [magento/magento2#18657](https://github.com/magento/magento2/pull/18657) -- [Backport] Create empty modelData array to avoid undefined var error (by @gelanivishal) + * [magento/magento2#18659](https://github.com/magento/magento2/pull/18659) -- [Backport] Fix for #12969 - server port detection for errors (by @gelanivishal) + * [magento/magento2#18662](https://github.com/magento/magento2/pull/18662) -- [Backport] move hardcoded MIME types from class private to DI configuration (by @gelanivishal) + * [magento/magento2#16915](https://github.com/magento/magento2/pull/16915) -- magento/magento2#14510: Creating custom customer attribute with default value 0 will cause not saving value for customer entity. (by @swnsma) + * [magento/magento2#18563](https://github.com/magento/magento2/pull/18563) -- [Backport] Update CategoryProcessor.php (by @gelanivishal) + * [magento/magento2#18566](https://github.com/magento/magento2/pull/18566) -- Module Catalog URL Rewrite: fix issue with product URL Rewrites re-generation after changing product URL Key for product with existing url_path attribute value (by @oleksii-lisovyi) + * [magento/magento2#18670](https://github.com/magento/magento2/pull/18670) -- Remove unnecessary class import, see #18280 (by @sreichel) + * [magento/magento2#18658](https://github.com/magento/magento2/pull/18658) -- [Backport] MAGENTO-18131: Fixed EAV attributes values query (by @gelanivishal) + * [magento/magento2#15366](https://github.com/magento/magento2/pull/15366) -- 15259 : Unable to disable without providing Industry value (by @sunilit42) + * [magento/magento2#18424](https://github.com/magento/magento2/pull/18424) -- [BACKPORT] type casted $qty to float in \Magento\Catalog\Model\Produc… (by @lewisvoncken) + * [magento/magento2#18660](https://github.com/magento/magento2/pull/18660) -- [Backport] Fix of saving "clone_field" fields (by @gelanivishal) + * [magento/magento2#18758](https://github.com/magento/magento2/pull/18758) -- [Backport] Fix the typo in PHPDoc comment (by @dmytro-ch) + * [magento/magento2#18535](https://github.com/magento/magento2/pull/18535) -- Fixed issues-18534: 2 wysiwyg on catalog category edit page (by @k1las) + * [magento/magento2#18597](https://github.com/magento/magento2/pull/18597) -- [Backport] Fix empty cart button (by @luukschakenraad) + * [magento/magento2#18604](https://github.com/magento/magento2/pull/18604) -- Fixed Issue: Special price of 0.0000 is not shown on frontend, but is calculated in cart (by @maheshWebkul721) + * [magento/magento2#18643](https://github.com/magento/magento2/pull/18643) -- Fix customer unsubscribed issue (by @janakbhimani) + * [magento/magento2#18759](https://github.com/magento/magento2/pull/18759) -- [Backport] Backend: add missing unit test for ModuleService class (by @dmytro-ch) + * [magento/magento2#16940](https://github.com/magento/magento2/pull/16940) -- Resolve incorrect scope code selection when the requested scopeCode is null (by @matthew-muscat) + * [magento/magento2#18737](https://github.com/magento/magento2/pull/18737) -- [BUGFIX] GITHUB-18264 Backport of #17799 for the 2.2 branch (by @kanduvisla) + * [magento/magento2#17971](https://github.com/magento/magento2/pull/17971) -- Don't format Special Price value for Bundle Product (by @magently) + * [magento/magento2#18681](https://github.com/magento/magento2/pull/18681) -- [Backport] Set fallback values for email and _website columns to avoid 'undefined index' error in CustomerComposite.php (by @TomashKhamlai) + * [magento/magento2#18833](https://github.com/magento/magento2/pull/18833) -- [Backport] Cover \Magento\GiftMessage\Observer\SalesEventQuoteMerge with Unit test (by @vasilii-b) + * [magento/magento2#18834](https://github.com/magento/magento2/pull/18834) -- [Backport] Cover \Magento\Email\Model\Template\SenderResolver class with Unit test (by @vasilii-b) + * [magento/magento2#18835](https://github.com/magento/magento2/pull/18835) -- [Backport] Added Unit Test for WindowsSmtpConfig Plugin (by @vasilii-b) + * [magento/magento2#18876](https://github.com/magento/magento2/pull/18876) -- [Backport] Fix Useless use of Cat (by @gelanivishal) + * [magento/magento2#18591](https://github.com/magento/magento2/pull/18591) -- [Backport] Fix SKU limit in import new products (by @ravi-chandra3197) + * [magento/magento2#18862](https://github.com/magento/magento2/pull/18862) -- [Backport] Adding trimming sku value function to sku backend model. (by @gelanivishal) + * [magento/magento2#18865](https://github.com/magento/magento2/pull/18865) -- fixed issue #18458 : Alert widget gets close when click anywhere on screen #18576 (by @Shubham-Webkul) + * [magento/magento2#18886](https://github.com/magento/magento2/pull/18886) -- [Backport] fixed Translation issue send-friend in send.phtml (by @rahulwebkul) + * [magento/magento2#18917](https://github.com/magento/magento2/pull/18917) -- Fixed-Global-search icon misaligned (by @speedy008) + * [magento/magento2#17978](https://github.com/magento/magento2/pull/17978) -- #17488 Fix Authenticating a customer via REST API does not update the last logged in data (by @prakashpatel07) + * [magento/magento2#18287](https://github.com/magento/magento2/pull/18287) -- Ensure integer values are not quoted as strings (by @udovicic) + * [magento/magento2#18874](https://github.com/magento/magento2/pull/18874) -- [Backport] Fixed issue #4468 "Unable to insert multiple catalog product list wid… (by @gelanivishal) + * [magento/magento2#18372](https://github.com/magento/magento2/pull/18372) -- Resolve typo despatch event (by @neeta-wagento) + * [magento/magento2#18863](https://github.com/magento/magento2/pull/18863) -- [Backport] #17744 Adding logic to get default billing address used on Cart and Checkout (by @gelanivishal) + * [magento/magento2#18872](https://github.com/magento/magento2/pull/18872) -- [Backport] Allow set billing information via API with existing address (by @gelanivishal) + * [magento/magento2#18870](https://github.com/magento/magento2/pull/18870) -- [Backport] ISSUE-5021 - fixed place order for custom shipping methods with under… (by @gelanivishal) + * [magento/magento2#18875](https://github.com/magento/magento2/pull/18875) -- [Backport] Sections LESS mixins: fix the issue with missing rules and incorrect default variables (by @gelanivishal) + * [magento/magento2#18873](https://github.com/magento/magento2/pull/18873) -- [Backport] Prevent exception when option text converts to false (by @gelanivishal) + * [magento/magento2#18967](https://github.com/magento/magento2/pull/18967) -- fixed - Magento 2.2.6 Default values are not rendering on Wishlist product edit page (by @webkul-ratnesh) + * [magento/magento2#18908](https://github.com/magento/magento2/pull/18908) -- [Backport] fixed - Unable to select payment method according to country of the address at checkout time (by @rahulwebkul) + * [magento/magento2#18984](https://github.com/magento/magento2/pull/18984) -- [Backport] Reload cart totals when cart data changes (by @tdgroot) + * [magento/magento2#16887](https://github.com/magento/magento2/pull/16887) -- Fix blocked a frame with origin (by @iGerchak) + * [magento/magento2#18857](https://github.com/magento/magento2/pull/18857) -- Fixed - Default tax region/state appears in customer & order data #16684 (by @ssp58bleuciel) + * [magento/magento2#18964](https://github.com/magento/magento2/pull/18964) -- Backport [PR 18772] Remove unnecesary "header" block redeclaration (by @samuel27m) + * [magento/magento2#19012](https://github.com/magento/magento2/pull/19012) -- #18348 - In admin, last swatch option set to default upon save (by @RostislavS) + * [magento/magento2#19036](https://github.com/magento/magento2/pull/19036) -- magento/magento2#18323: Order confirmation email for guest checkout d… (by @swnsma) + * [magento/magento2#18985](https://github.com/magento/magento2/pull/18985) -- [Backport] Added form fieldset before html data to \Magento\Framework\Data\Form\Element\Fieldset in getElementHtml() method (by @vasilii-b) + * [magento/magento2#19002](https://github.com/magento/magento2/pull/19002) -- [Backport] Remove duplicated CSS selector (by @dmytro-ch) + * [magento/magento2#19044](https://github.com/magento/magento2/pull/19044) -- [2.2-develop] magento/magento2#14007: "Use in Layered Navigation: Filterable (no results)" property confuse for Price filter (by @vpodorozh) + * [magento/magento2#19074](https://github.com/magento/magento2/pull/19074) -- [Backport] Fix for #12399: Exception Error in Catalog Price Rule while Backend language is not English (by @Mardl) + * [magento/magento2#18461](https://github.com/magento/magento2/pull/18461) -- fix Fatal Error when save configurable product in Magento 2.2.5 #18082 (by @thiagolima-bm) + * [magento/magento2#18649](https://github.com/magento/magento2/pull/18649) -- [Backport] Issue Fixed: Missing Fixed Product Tax total on PDF (by @maheshWebkul721) + * [magento/magento2#18815](https://github.com/magento/magento2/pull/18815) -- [Backoport] Issue Fixed: Backups error from User Roles Permission 2.2.6 (by @maheshWebkul721) + * [magento/magento2#19073](https://github.com/magento/magento2/pull/19073) -- magento/magento2#19071: Password strength indicator shows No Password… (by @dimasalamatov) + * [magento/magento2#19089](https://github.com/magento/magento2/pull/19089) -- magento/magento#18901: Forgot password form should not available while customer is logged in. (by @swnsma) + * [magento/magento2#19105](https://github.com/magento/magento2/pull/19105) -- magento/magento2#18840: Invalid Unit Test Annotations. (by @swnsma) + * [magento/magento2#19110](https://github.com/magento/magento2/pull/19110) -- [Backport] Add additional check if password hash is empty in auth process (by @agorbulin) + * [magento/magento2#14914](https://github.com/magento/magento2/pull/14914) -- FIX for issue #14849 - In Sales Emails no translation using order.getStatusLabel() (by @phoenix128) + * [magento/magento2#17854](https://github.com/magento/magento2/pull/17854) -- Fix translations of category design theme not being applied (by @cezary-zeglen) + * [magento/magento2#17915](https://github.com/magento/magento2/pull/17915) -- Fix/add expresion (by @magently) + * [magento/magento2#18743](https://github.com/magento/magento2/pull/18743) -- Fixed tierprice discount not calculated correctly if has specialprice (by @gelanivishal) + * [magento/magento2#18959](https://github.com/magento/magento2/pull/18959) -- fixed js translation (by @torhoehn) + * [magento/magento2#19118](https://github.com/magento/magento2/pull/19118) -- [Backport] Add/update newsletter messages in translation file (by @arnoudhgz) + * [magento/magento2#17889](https://github.com/magento/magento2/pull/17889) -- Fixed child items showing on My Account order view (by @rogyar) + * [magento/magento2#19113](https://github.com/magento/magento2/pull/19113) -- [2.2 backport] fix cipherMethod detection for openssl 1.1.1 (by @BlackIkeEagle) + * [magento/magento2#16342](https://github.com/magento/magento2/pull/16342) -- #14020-Cart-Sales-Rule-with-negated-condition-over-special-price-does… (by @novikor) + * [magento/magento2#18808](https://github.com/magento/magento2/pull/18808) -- fixed Quote Item Prices are NULL in cart related events. #18685 (by @ashutoshwebkul) + * [magento/magento2#19216](https://github.com/magento/magento2/pull/19216) -- [Backport] Covering the \Magento\Weee observers by Unit Tests (by @eduard13) + * [magento/magento2#19217](https://github.com/magento/magento2/pull/19217) -- [Backport] Covering the CheckUserLoginBackendObserver by Unit Test (by @eduard13) + * [magento/magento2#19237](https://github.com/magento/magento2/pull/19237) -- [Backport] #18956 Fixes for set root_category_id (by @gelanivishal) + * [magento/magento2#19240](https://github.com/magento/magento2/pull/19240) -- [Backport] Add missing unit test for WishlistSettings plugin (by @gelanivishal) + * [magento/magento2#19260](https://github.com/magento/magento2/pull/19260) -- Issue #19205 Fixed: Bundle Product Option with input type is checkbox and add to cart with 3 values only 2 values added to cart. (by @maheshWebkul721) + * [magento/magento2#18642](https://github.com/magento/magento2/pull/18642) -- [Backport] Fix issue with unexpected changing of subscription status after customer saving (by @alexeya-ven) + * [magento/magento2#18951](https://github.com/magento/magento2/pull/18951) -- Magento 2.2 Fix Product::addImageToMediaGallery throws Exception (by @progreg) + * [magento/magento2#18960](https://github.com/magento/magento2/pull/18960) -- local themes should be added to git repo (by @torhoehn) + * [magento/magento2#19068](https://github.com/magento/magento2/pull/19068) -- Using Media Image custom attribute type could not display on frontend. #19054 (by @Nazar65) + * [magento/magento2#19337](https://github.com/magento/magento2/pull/19337) -- [Backport] 19082-Fatal-error-Uncaught-Error-Cannot-call-abstract-method-Magento-… (by @agorbulin) + * [magento/magento2#19336](https://github.com/magento/magento2/pull/19336) -- [Backport] small performance improvement on product listing (by @gelanivishal) + * [magento/magento2#19338](https://github.com/magento/magento2/pull/19338) -- [Backport] missing use statement in layout generator (by @gelanivishal) + * [magento/magento2#19340](https://github.com/magento/magento2/pull/19340) -- [Backport] Fix the issue: Content overlaps the close button #19263 (by @gelanivishal) + * [magento/magento2#14485](https://github.com/magento/magento2/pull/14485) -- Fix for Issue #4136, MAGETWO-53440 (by @vasilii-b) + * [magento/magento2#18621](https://github.com/magento/magento2/pull/18621) -- 18615 updates structure for last_trans_id to be varchar 255 which is … (by @iancassidyweb) + * [magento/magento2#18905](https://github.com/magento/magento2/pull/18905) -- Fix the issue with missing asterisk for admin required fields (by @dmytro-ch) + * [magento/magento2#19296](https://github.com/magento/magento2/pull/19296) -- Fix issue 19286 - Wrong pager style (by @speedy008) + * [magento/magento2#19355](https://github.com/magento/magento2/pull/19355) -- [Backport] Changed get product way in blocks with related products (by @gelanivishal) + * [magento/magento2#19357](https://github.com/magento/magento2/pull/19357) -- [Backport] #13157 - Last Ordered Items block - bad js code (by @gelanivishal) + * [magento/magento2#19023](https://github.com/magento/magento2/pull/19023) -- [2.2 develop] [backport #19018] [issue #17833] child theme does not inherit translations from parent theme (by @vpodorozh) + * [magento/magento2#19358](https://github.com/magento/magento2/pull/19358) -- [Backport] Fix the issue with repetitive "tbody" tag for order items table (by @gelanivishal) + * [magento/magento2#19365](https://github.com/magento/magento2/pull/19365) -- Fixing a test for Magento Newsletter. (by @tiagosampaio) + * [magento/magento2#18899](https://github.com/magento/magento2/pull/18899) -- [Backport] fixed - can't import external http to https redirecting images by default csv import (by @rahulwebkul) + * [magento/magento2#19356](https://github.com/magento/magento2/pull/19356) -- [Backport] Magento backend Notifications counter round icon small cut from right side (by @gelanivishal) + * [magento/magento2#19364](https://github.com/magento/magento2/pull/19364) -- [Backport] fix: remove old code in tabs, always set tabindex to 0 when tabs are … (by @DanielRuf) + * [magento/magento2#19374](https://github.com/magento/magento2/pull/19374) -- back-port-pull-19024 (by @agorbulin) + * [magento/magento2#19014](https://github.com/magento/magento2/pull/19014) -- [Backport] #17813 - Huge "product_data_storage" in localStorage hangs the shop (by @omiroshnichenko) + * [magento/magento2#19398](https://github.com/magento/magento2/pull/19398) -- [Backport-2.2] Code generation improvement for php 7.1 (by @swnsma) + * [magento/magento2#19422](https://github.com/magento/magento2/pull/19422) -- Fix for incorrectly escapeHtml'd JSON in commit b8f78cc6 (by @insanityinside) + * [magento/magento2#19426](https://github.com/magento/magento2/pull/19426) -- [Backport] Fixing the customer subscribing from different stores (by @eduard13) + * [magento/magento2#19427](https://github.com/magento/magento2/pull/19427) -- [Backport] Adding integration tests for wrong captcha (by @eduard13) + * [magento/magento2#18922](https://github.com/magento/magento2/pull/18922) -- Fixed 18918 Asterisk sign display twice (by @suryakant-krish) + * [magento/magento2#19239](https://github.com/magento/magento2/pull/19239) -- [Backport] Allow to read HTTP/2 response header. (by @gelanivishal) + * [magento/magento2#19430](https://github.com/magento/magento2/pull/19430) -- Fixed issue with Base Currency for website is CND when PayPal Payflow Pro is charging in USD (by @Rykh) + * [magento/magento2#19431](https://github.com/magento/magento2/pull/19431) -- [Backport] Sample Link Issue in Downloadable product in magento-2.2.6 #19344 (by @ansari-krish) + * [magento/magento2#19447](https://github.com/magento/magento2/pull/19447) -- [Backport] chore: remove unused code in admin view of catalog (by @DanielRuf) + * [magento/magento2#19145](https://github.com/magento/magento2/pull/19145) -- Add availability to leave empty config for events.xml (by @lisovyievhenii) + * [magento/magento2#19568](https://github.com/magento/magento2/pull/19568) -- [Backport] [Newsletter] #19418 Cannot add additional field to system configuration at desired position (by @vasilii-b) + * [magento/magento2#19678](https://github.com/magento/magento2/pull/19678) -- [Backport] Fix: SalesQuoteSaveAfterObserver fails to update the checkout session quote id when applicable (by @dmytro-ch) + * [magento/magento2#19668](https://github.com/magento/magento2/pull/19668) -- [Backport] style: change b to strong (a11y) (by @DanielRuf) + * [magento/magento2#19669](https://github.com/magento/magento2/pull/19669) -- [Backport] fix: remove unused params in categorySubmit invocation (by @DanielRuf) + * [magento/magento2#19804](https://github.com/magento/magento2/pull/19804) -- [Backport]Fix issue 19796 - Sales Order invoice Update Qty's Button is misaligned (by @speedy008) + * [magento/magento2#19949](https://github.com/magento/magento2/pull/19949) -- [Backport] Fixed Issue #19917 Changed allowDrug to allowDrag (by @maheshWebkul721) + * [magento/magento2#19967](https://github.com/magento/magento2/pull/19967) -- [Backport] Minor typos corrected. (by @milindsingh) + * [magento/magento2#19970](https://github.com/magento/magento2/pull/19970) -- [Backport] Typo taax -> tax (by @milindsingh) + * [magento/magento2#19968](https://github.com/magento/magento2/pull/19968) -- [Backport] Typo "customet_id" to "customer_id" fixed. (by @milindsingh) + * [magento/magento2#19972](https://github.com/magento/magento2/pull/19972) -- [Backport] Update bootstrap.js (by @milindsingh) + * [magento/magento2#19971](https://github.com/magento/magento2/pull/19971) -- [Backport] Typo corrected Update bound-nodes.js (by @milindsingh) + * [magento/magento2#18912](https://github.com/magento/magento2/pull/18912) -- [Backport] Fixed subscribe to newsletter if you already have an account issue (by @ravi-chandra3197) + * [magento/magento2#19199](https://github.com/magento/magento2/pull/19199) -- [Backport][2.2] Made logo clickable on home page (by @gwharton) + * [magento/magento2#19280](https://github.com/magento/magento2/pull/19280) -- [BackPort] resolve typos and correct variable names (by @viral-wagento) + * [magento/magento2#19690](https://github.com/magento/magento2/pull/19690) -- [Backport] Additional Cache Management title (by @thomas-blackbird) + * [magento/magento2#19693](https://github.com/magento/magento2/pull/19693) -- [Backport] Cancel expired orders using OrderManagementInterface (by @JeroenVanLeusden) + * [magento/magento2#19911](https://github.com/magento/magento2/pull/19911) -- [Backport] fixed store wise product filter issue (by @shikhamis11) + * [magento/magento2#19945](https://github.com/magento/magento2/pull/19945) -- [Backport] issue 18941 (by @Nazar65) + * [magento/magento2#19056](https://github.com/magento/magento2/pull/19056) -- Fix issue 19052- Position order showing before the text box (by @speedy008) + * [magento/magento2#19910](https://github.com/magento/magento2/pull/19910) -- [Backport] fixed Notification page Select Visible items issue (by @shikhamis11) + * [magento/magento2#19889](https://github.com/magento/magento2/pull/19889) -- [Backport]Fix issue 19507 - Frontend Minicart dropdown alignment issue (by @speedy008) + * [magento/magento2#19928](https://github.com/magento/magento2/pull/19928) -- [Backport] [Review] Integration tests for not allowed review submission (by @eduard13) + * [magento/magento2#19989](https://github.com/magento/magento2/pull/19989) -- [Backport] Fixed #19605 Don't static compile disabled modules (by @shikhamis11) + * [magento/magento2#20081](https://github.com/magento/magento2/pull/20081) -- [Backport] Fixed issue - #19346 Import data 2.2.6 Value for 'product_type' attribute contains incorrect value (by @GovindaSharma) + * [magento/magento2#20080](https://github.com/magento/magento2/pull/20080) -- [Backport] Fixed Incorrect class name on Orders and returns page. (by @shikhamis11) + * [magento/magento2#20083](https://github.com/magento/magento2/pull/20083) -- [Backport] fixed issue #19925 Close button overlapping in shipping address label whenever any user adding new shipping address in mobile view in checkout (by @GovindaSharma) + * [magento/magento2#19423](https://github.com/magento/magento2/pull/19423) -- Fixed bug, when exception occurred on order with coupons cancel, made by guest after creating of customer account. (by @Winfle) + * [magento/magento2#19927](https://github.com/magento/magento2/pull/19927) -- [Backport] [Framework] New Link is not correctly shown as Current if contains default parts (by @eduard13) + * [magento/magento2#20082](https://github.com/magento/magento2/pull/20082) -- [Backport] issue resolved:Undefined Variable $itemsOrderItemId (by @milindsingh) + * [magento/magento2#20208](https://github.com/magento/magento2/pull/20208) -- magento/magento2:#19101 - API REST and Reserved Order Id (by @saphaljha) + * [magento/magento2#20219](https://github.com/magento/magento2/pull/20219) -- Changes-Hamburger-Icon-was-available-on-a-page (by @amol2jcommerce) + * [magento/magento2#20178](https://github.com/magento/magento2/pull/20178) -- magento/magento2#16198: Category image remain after deleted. (by @p-bystritsky) + * [magento/magento2#20183](https://github.com/magento/magento2/pull/20183) -- 2.2 develop pr port 18888 (by @saphaljha) + * [magento/magento2#20185](https://github.com/magento/magento2/pull/20185) -- [Backport] Move website_name column into columnSet (by @mage2pratik) + * [magento/magento2#20271](https://github.com/magento/magento2/pull/20271) -- [Backport] Use the new json serializer which throws an error when failing (by @quisse) + * [magento/magento2#20286](https://github.com/magento/magento2/pull/20286) -- [Backport] Don't return categoryId from registry if the product doesn't belong in the current category (by @GovindaSharma) + * [magento/magento2#20298](https://github.com/magento/magento2/pull/20298) -- ISSUE-20296: "@magentoDataIsolation" is used instead of "@magentoDbIsolation" in some integration tests. (by @p-bystritsky) + * [magento/magento2#20325](https://github.com/magento/magento2/pull/20325) -- [Backport] issus fixed #20158 Store switcher not aligned proper in tab view (by @shikhamis11) + * [magento/magento2#20328](https://github.com/magento/magento2/pull/20328) -- [Backport] Fix issue 20232 : Backend order credit card detail check box misaligned (by @GovindaSharma) + * [magento/magento2#20329](https://github.com/magento/magento2/pull/20329) -- [Backport] Product image failure when importing through CSV #20098 (by @irajneeshgupta) + * [magento/magento2#20353](https://github.com/magento/magento2/pull/20353) -- Fixed#20352: displaying html content for file type option on order view admin area (by @maheshWebkul721) + * [magento/magento2#19964](https://github.com/magento/magento2/pull/19964) -- [Backport] Fix the issue with reset password when customer has address from not allowed country (by @dmytro-ch) + * [magento/magento2#19984](https://github.com/magento/magento2/pull/19984) -- [Backport] Remove unneeded, also mistyped, saveHandler from CatalogSearch indexer declaration (by @dmytro-ch) + * [magento/magento2#20206](https://github.com/magento/magento2/pull/20206) -- 9130 remove the negative qty block. (by @saphaljha) + * [magento/magento2#20322](https://github.com/magento/magento2/pull/20322) -- issue #19609 Fixed for 2.2-develop (by @maheshWebkul721) + * [magento/magento2#19400](https://github.com/magento/magento2/pull/19400) -- [Backport]Fix-issue-19399-Add product customization option collapsible design issue (by @speedy008) + * [magento/magento2#20272](https://github.com/magento/magento2/pull/20272) -- Fixed-Review-Details-Detailed-Rating-misaligned (by @amol2jcommerce) + * [magento/magento2#20369](https://github.com/magento/magento2/pull/20369) -- 'Fixes-for-customer-login-page-input-field' :: On customer login page… (by @nainesh2jcommerce) + * [magento/magento2#20375](https://github.com/magento/magento2/pull/20375) -- [Backport] [Forwardport]Fix issue 19902 - Store View label and Dropdown misaligned (by @speedy008) + * [magento/magento2#20433](https://github.com/magento/magento2/pull/20433) -- [Backport] Missing echo of php vars in widget template file - tabshoriz.phtml (by @irajneeshgupta) + * [magento/magento2#20439](https://github.com/magento/magento2/pull/20439) -- [Backport] Meassage icon is not proper aligned (by @saphaljha) + * [magento/magento2#19377](https://github.com/magento/magento2/pull/19377) -- Back port pull #19094 (by @agorbulin) + * [magento/magento2#18362](https://github.com/magento/magento2/pull/18362) -- [Backport] fix(Webapi Xml Renderer - 18361): removed the not needed ampersand re… (by @nickshatilo) + * [magento/magento2#20184](https://github.com/magento/magento2/pull/20184) -- [Backport] Fix issue 19887 creating new shipment: getting all trackers. (by @mage2pratik) + * [magento/magento2#20505](https://github.com/magento/magento2/pull/20505) -- [Backport] Added constants to unit codes to make it easier to reuse it if necessary (by @mageprince) + * [magento/magento2#20509](https://github.com/magento/magento2/pull/20509) -- [Backport] Added required error message. (by @mageprince) + * [magento/magento2#20522](https://github.com/magento/magento2/pull/20522) -- [Backport] Add useful debug info for which website has not been found (by @mageprince) + * [magento/magento2#20541](https://github.com/magento/magento2/pull/20541) -- [Backport] Issue fixed #19985 Send email confirmation popup close button area ov… (by @irajneeshgupta) + * [magento/magento2#20284](https://github.com/magento/magento2/pull/20284) -- [Backport] Fix issue causing attribute not loading when using getList (by @GovindaSharma) + * [magento/magento2#20455](https://github.com/magento/magento2/pull/20455) -- [Backport] Fixed 19800 Contact us : design improvement (by @suryakant-krish) + * [magento/magento2#20456](https://github.com/magento/magento2/pull/20456) -- [Backport] Fixed 19791: Logo vertical misalignment. (by @suryakant-krish) + * [magento/magento2#20457](https://github.com/magento/magento2/pull/20457) -- [Backport] Area Frontend: Fixed checkbox alignment account information page. (by @suryakant-krish) + * [magento/magento2#20177](https://github.com/magento/magento2/pull/20177) -- magento/magento2#15950: Magento2 CSV product import qty and is_in_stock not working correct. (by @p-bystritsky) + * [magento/magento2#20508](https://github.com/magento/magento2/pull/20508) -- [Backport] Fix negative credit memo #19899 (by @mageprince) + * [magento/magento2#20547](https://github.com/magento/magento2/pull/20547) -- [Backport] Fixed Issue #20121 Cancel order increases stock although "Set Items' Status to be In Stock When Order is Cancelled" is set to No (by @irajneeshgupta) + * [magento/magento2#20636](https://github.com/magento/magento2/pull/20636) -- [Backport] Fix issue with file uploading if an upload field is disabled (by @serhiyzhovnir) + * [magento/magento2#20638](https://github.com/magento/magento2/pull/20638) -- [Backport] Floating point overflows in checkout totals fixed (by @shikhamis11) + * [magento/magento2#20647](https://github.com/magento/magento2/pull/20647) -- [Backport] fixed Negative order amount in dashboard (by @amol2jcommerce) + * [magento/magento2#20542](https://github.com/magento/magento2/pull/20542) -- [Backport] Order API resources updated. #20169 (by @irajneeshgupta) + * [magento/magento2#20544](https://github.com/magento/magento2/pull/20544) -- [Backport] 'wishlist-page-edit-remove-item-misalign' :: On wish list page edit, … (by @irajneeshgupta) + * [magento/magento2#20546](https://github.com/magento/magento2/pull/20546) -- [Backport] Order-view-invoices :: Order view invoices template not display prope… (by @irajneeshgupta) + * [magento/magento2#20685](https://github.com/magento/magento2/pull/20685) -- [Backport] update-button-issue-while-updating-billing-and-shipping-address (by @cmtickle) + * [magento/magento2#18809](https://github.com/magento/magento2/pull/18809) -- [Backport] catalog:images:resize total images count calculates incorrectly #18387 (by @vpodorozh) + * [magento/magento2#19461](https://github.com/magento/magento2/pull/19461) -- [Backport 2.2] issue #18931 fixed. (by @JeroenVanLeusden) + * [magento/magento2#19655](https://github.com/magento/magento2/pull/19655) -- Fixed - Shipping issue on PayPal Express #14712 (by @ssp58bleuciel) + * [magento/magento2#20285](https://github.com/magento/magento2/pull/20285) -- [Backport]#20222 Canary islands in ups carrier 2.2 (by @duckchip) + * [magento/magento2#20270](https://github.com/magento/magento2/pull/20270) -- [Backport] Fixed-Widget-option-labels-are-misalinged (by @amol2jcommerce) + * [magento/magento2#20418](https://github.com/magento/magento2/pull/20418) -- [Backport] issue fixed #20304 No space between step title and saved address in c… (by @shikhamis11) + * [magento/magento2#20613](https://github.com/magento/magento2/pull/20613) -- [Backport] admin-order-info-issue2.2 (by @dipti2jcommerce) + * [magento/magento2#20744](https://github.com/magento/magento2/pull/20744) -- [Backport] recent-order-product-title-misaligned (by @amol2jcommerce) + * [magento/magento2#20739](https://github.com/magento/magento2/pull/20739) -- [Backport] issue fixed #20563 Go to shipping information, Update qty & Addresses… (by @amol2jcommerce) + * [magento/magento2#19612](https://github.com/magento/magento2/pull/19612) -- [Backport] Fix: Attribute Option with zero at the beginning does not work if there is already option with the same number without the zero [REST API] (by @SikailoISM) + * [magento/magento2#19667](https://github.com/magento/magento2/pull/19667) -- [Backport] chore: remove old code for IE9 (by @DanielRuf) + * [magento/magento2#20642](https://github.com/magento/magento2/pull/20642) -- [Backport] magento/magento2#12194: Tier price on configurable product sorting so… (by @amol2jcommerce) + * [magento/magento2#20784](https://github.com/magento/magento2/pull/20784) -- [Backport] Gift-option-message-overlap-edit-and-remove-button-2.2 (by @ajay2jcommerce) + * [magento/magento2#20837](https://github.com/magento/magento2/pull/20837) -- [Backport] Fixed apply discount button alignment on checkout page (by @amol2jcommerce) + * [magento/magento2#20863](https://github.com/magento/magento2/pull/20863) -- [Backport] Update Filter.php fix issue #20624 (by @irajneeshgupta) + * [magento/magento2#20886](https://github.com/magento/magento2/pull/20886) -- [Backport] #20409 Fixed Unnecessary slash in namespace (by @milindsingh) + * [magento/magento2#20929](https://github.com/magento/magento2/pull/20929) -- resolve typo errors for js record.js (by @neeta-wagento) + * [magento/magento2#20540](https://github.com/magento/magento2/pull/20540) -- [Backport] issue fixed #20259 Store switcher not sliding up and down, only dropd… (by @irajneeshgupta) + 2.2.7 ============= * GitHub issues: From dbc2144d5c8b64e1cc256867c1a0bf877e0049a6 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Thu, 14 Mar 2019 16:07:11 -0500 Subject: [PATCH 0911/1295] MAGETWO-98679: Discounts of products disappear on storefront after drag & drop via Visual Merchandiser in category --- .../CatalogRule/Model/Indexer/IndexBuilder.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php index 8b34bde6cc49a..94d13e1ec1e81 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php @@ -326,9 +326,12 @@ protected function doReindexFull() */ private function cleanProductIndex($productIds, int $ruleId = null) { - $where = ['product_id' => $productIds]; + $productIds = (array) $productIds; + $where = count($productIds) > 1 + ? ['product_id IN (?)' => $productIds] + : ['product_id = ?' => $productIds]; if ($ruleId) { - $where['rule_id'] = $ruleId; + $where['rule_id = ?'] = $ruleId; } $this->connection->delete($this->getTable('catalogrule_product'), $where); } @@ -341,9 +344,12 @@ private function cleanProductIndex($productIds, int $ruleId = null) */ private function cleanProductPriceIndex($productIds, int $ruleId = null) { - $where = ['product_id' => $productIds]; + $productIds = (array) $productIds; + $where = count($productIds) > 1 + ? ['product_id IN (?)' => $productIds] + : ['product_id = ?' => $productIds]; if ($ruleId) { - $where['rule_id'] = $ruleId; + $where['rule_id = ?'] = $ruleId; } $this->connection->delete($this->getTable('catalogrule_product_price'), $where); } From 2aed503fbb4b8bdee75bcc25ebded80b65ad8b5e Mon Sep 17 00:00:00 2001 From: Denis Kopylov <dkopylov@magenius.team> Date: Mon, 25 Feb 2019 19:06:22 +0300 Subject: [PATCH 0912/1295] Add review summary to wishlist (https://github.com/magento/magento2/issues/21419) --- .../Block/Customer/Wishlist/Item/Column/Actions.php | 1 + .../Block/Customer/Wishlist/Item/Column/Comment.php | 1 + .../Block/Customer/Wishlist/Item/Column/Edit.php | 1 + .../Block/Customer/Wishlist/Item/Column/Info.php | 1 + .../Block/Customer/Wishlist/Item/Column/Remove.php | 1 + .../view/frontend/layout/wishlist_index_index.xml | 13 +++++++------ .../frontend/templates/item/column/actions.phtml | 2 +- .../frontend/templates/item/column/comment.phtml | 2 +- .../view/frontend/templates/item/column/edit.phtml | 2 +- .../view/frontend/templates/item/column/name.phtml | 2 +- .../frontend/templates/item/column/remove.phtml | 2 +- .../frontend/templates/item/column/review.phtml | 10 ++++++++++ 12 files changed, 27 insertions(+), 11 deletions(-) create mode 100644 app/code/Magento/Wishlist/view/frontend/templates/item/column/review.phtml diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php index cafb6a5291481..0cf6b6a9867d1 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php @@ -13,6 +13,7 @@ /** * @api + * @deprecated Empty class * @since 100.0.2 */ class Actions extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php index 2d75956858a0a..5f40e96bfa6ff 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php @@ -13,6 +13,7 @@ /** * @api + * @deprecated Empty class * @since 100.0.2 */ class Comment extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php index 53ca78c63524d..c4e94ec503175 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php @@ -13,6 +13,7 @@ /** * @api + * @deprecated Empty class * @since 100.0.2 */ class Edit extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php index 33fb0f7325cdd..f83c122910ebd 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php @@ -13,6 +13,7 @@ /** * @api + * @deprecated Empty class * @since 100.0.2 */ class Info extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php index 57703b9300db8..3e6f225842e01 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php @@ -13,6 +13,7 @@ /** * @api + * @deprecated Empty class * @since 100.0.2 */ class Remove extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column diff --git a/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_index.xml b/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_index.xml index 243a06062425a..836f7f404a381 100644 --- a/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_index.xml +++ b/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_index.xml @@ -16,7 +16,8 @@ <block class="Magento\Wishlist\Block\Rss\Link" name="wishlist.rss.link" template="Magento_Wishlist::rss/wishlist.phtml"/> <block class="Magento\Wishlist\Block\Customer\Wishlist\Items" name="customer.wishlist.items" as="items" template="Magento_Wishlist::item/list.phtml" cacheable="false"> <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Image" name="customer.wishlist.item.image" template="Magento_Wishlist::item/column/image.phtml" cacheable="false"/> - <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Info" name="customer.wishlist.item.name" template="Magento_Wishlist::item/column/name.phtml" cacheable="false"/> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.name" template="Magento_Wishlist::item/column/name.phtml" cacheable="false"/> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.review" template="Magento_Wishlist::item/column/review.phtml" cacheable="false"/> <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Cart" name="customer.wishlist.item.price" template="Magento_Wishlist::item/column/price.phtml" cacheable="false"> <block class="Magento\Catalog\Pricing\Render" name="product.price.render.wishlist"> <arguments> @@ -28,11 +29,11 @@ </block> <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Options" name="customer.wishlist.item.options" cacheable="false"/> </block> - <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Actions" name="customer.wishlist.item.inner" template="Magento_Wishlist::item/column/actions.phtml" cacheable="false"> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.inner" template="Magento_Wishlist::item/column/actions.phtml" cacheable="false"> <arguments> <argument name="css_class" xsi:type="string">product-item-inner</argument> </arguments> - <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Comment" name="customer.wishlist.item.comment" template="Magento_Wishlist::item/column/comment.phtml" cacheable="false"> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.comment" template="Magento_Wishlist::item/column/comment.phtml" cacheable="false"> <arguments> <argument name="title" translate="true" xsi:type="string">Product Details and Comment</argument> </arguments> @@ -43,12 +44,12 @@ </arguments> </block> - <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Actions" name="customer.wishlist.item.actions" template="Magento_Wishlist::item/column/actions.phtml" cacheable="false"> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.actions" template="Magento_Wishlist::item/column/actions.phtml" cacheable="false"> <arguments> <argument name="css_class" xsi:type="string">product-item-actions</argument> </arguments> - <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Edit" name="customer.wishlist.item.edit" template="Magento_Wishlist::item/column/edit.phtml" before="-" cacheable="false"/> - <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Remove" name="customer.wishlist.item.remove" template="Magento_Wishlist::item/column/remove.phtml" cacheable="false"/> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.edit" template="Magento_Wishlist::item/column/edit.phtml" before="-" cacheable="false"/> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.remove" template="Magento_Wishlist::item/column/remove.phtml" cacheable="false"/> </block> </block> </block> diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/actions.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/actions.phtml index 0b50df8c59fbb..7fa55a839b2ea 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/actions.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/actions.phtml @@ -6,7 +6,7 @@ // @codingStandardsIgnoreFile -/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Actions $block */ +/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column $block */ ?> <?php $children = $block->getChildNames(); ?> <?php if ($children): ?> diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/comment.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/comment.phtml index 17e2404ee23cf..45bd7494fed4f 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/comment.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/comment.phtml @@ -6,7 +6,7 @@ // @codingStandardsIgnoreFile -/* @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Comment $block */ +/* @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column $block */ /* @var \Magento\Wishlist\Model\Item $item */ $item = $block->getItem(); diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/edit.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/edit.phtml index 1898ef6fc0d66..81d770d556734 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/edit.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/edit.phtml @@ -6,7 +6,7 @@ // @codingStandardsIgnoreFile -/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Edit $block */ +/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column $block */ /** @var \Magento\Wishlist\Model\Item $item */ $item = $block->getItem(); diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/name.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/name.phtml index 265f4635a6be3..53c5f35f886ea 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/name.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/name.phtml @@ -6,7 +6,7 @@ // @codingStandardsIgnoreFile -/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Info $block */ +/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column $block */ /** @var \Magento\Wishlist\Model\Item $item */ $item = $block->getItem(); diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/remove.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/remove.phtml index 57ae439d9995b..0bd78ba38ba58 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/remove.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/remove.phtml @@ -6,7 +6,7 @@ // @codingStandardsIgnoreFile -/* @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Remove $block */ +/* @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column $block */ ?> <a href="#" data-role="remove" data-post-remove='<?= /* @noEscape */ $block->getItemRemoveParams($block->getItem()) ?>' title="<?= $block->escapeHtmlAttr(__('Remove Item')) ?>" class="btn-remove action delete"> diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/review.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/review.phtml new file mode 100644 index 0000000000000..9120cc9fa684e --- /dev/null +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/review.phtml @@ -0,0 +1,10 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column $block */ +$product = $block->getItem()->getProduct(); +?> +<?= $block->getReviewsSummaryHtml($product, 'short') ?> From f0b3b92960f79366325ed8ce7f68e97bc23d5b71 Mon Sep 17 00:00:00 2001 From: Denis Kopylov <dkopylov@magenius.team> Date: Tue, 26 Feb 2019 19:31:34 +0300 Subject: [PATCH 0913/1295] Revert usage of deprecate marked classes in layout --- .../view/frontend/layout/wishlist_index_index.xml | 12 ++++++------ .../frontend/templates/item/column/actions.phtml | 2 +- .../frontend/templates/item/column/comment.phtml | 2 +- .../view/frontend/templates/item/column/edit.phtml | 2 +- .../view/frontend/templates/item/column/name.phtml | 2 +- .../view/frontend/templates/item/column/remove.phtml | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_index.xml b/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_index.xml index 836f7f404a381..218b5ac1c879b 100644 --- a/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_index.xml +++ b/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_index.xml @@ -16,7 +16,7 @@ <block class="Magento\Wishlist\Block\Rss\Link" name="wishlist.rss.link" template="Magento_Wishlist::rss/wishlist.phtml"/> <block class="Magento\Wishlist\Block\Customer\Wishlist\Items" name="customer.wishlist.items" as="items" template="Magento_Wishlist::item/list.phtml" cacheable="false"> <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Image" name="customer.wishlist.item.image" template="Magento_Wishlist::item/column/image.phtml" cacheable="false"/> - <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.name" template="Magento_Wishlist::item/column/name.phtml" cacheable="false"/> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Info" name="customer.wishlist.item.name" template="Magento_Wishlist::item/column/name.phtml" cacheable="false"/> <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.review" template="Magento_Wishlist::item/column/review.phtml" cacheable="false"/> <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Cart" name="customer.wishlist.item.price" template="Magento_Wishlist::item/column/price.phtml" cacheable="false"> <block class="Magento\Catalog\Pricing\Render" name="product.price.render.wishlist"> @@ -29,11 +29,11 @@ </block> <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Options" name="customer.wishlist.item.options" cacheable="false"/> </block> - <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.inner" template="Magento_Wishlist::item/column/actions.phtml" cacheable="false"> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Actions" name="customer.wishlist.item.inner" template="Magento_Wishlist::item/column/actions.phtml" cacheable="false"> <arguments> <argument name="css_class" xsi:type="string">product-item-inner</argument> </arguments> - <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.comment" template="Magento_Wishlist::item/column/comment.phtml" cacheable="false"> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Comment" name="customer.wishlist.item.comment" template="Magento_Wishlist::item/column/comment.phtml" cacheable="false"> <arguments> <argument name="title" translate="true" xsi:type="string">Product Details and Comment</argument> </arguments> @@ -44,12 +44,12 @@ </arguments> </block> - <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.actions" template="Magento_Wishlist::item/column/actions.phtml" cacheable="false"> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Actions" name="customer.wishlist.item.actions" template="Magento_Wishlist::item/column/actions.phtml" cacheable="false"> <arguments> <argument name="css_class" xsi:type="string">product-item-actions</argument> </arguments> - <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.edit" template="Magento_Wishlist::item/column/edit.phtml" before="-" cacheable="false"/> - <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column" name="customer.wishlist.item.remove" template="Magento_Wishlist::item/column/remove.phtml" cacheable="false"/> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Edit" name="customer.wishlist.item.edit" template="Magento_Wishlist::item/column/edit.phtml" before="-" cacheable="false"/> + <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Remove" name="customer.wishlist.item.remove" template="Magento_Wishlist::item/column/remove.phtml" cacheable="false"/> </block> </block> </block> diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/actions.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/actions.phtml index 7fa55a839b2ea..0b50df8c59fbb 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/actions.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/actions.phtml @@ -6,7 +6,7 @@ // @codingStandardsIgnoreFile -/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column $block */ +/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Actions $block */ ?> <?php $children = $block->getChildNames(); ?> <?php if ($children): ?> diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/comment.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/comment.phtml index 45bd7494fed4f..17e2404ee23cf 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/comment.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/comment.phtml @@ -6,7 +6,7 @@ // @codingStandardsIgnoreFile -/* @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column $block */ +/* @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Comment $block */ /* @var \Magento\Wishlist\Model\Item $item */ $item = $block->getItem(); diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/edit.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/edit.phtml index 81d770d556734..1898ef6fc0d66 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/edit.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/edit.phtml @@ -6,7 +6,7 @@ // @codingStandardsIgnoreFile -/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column $block */ +/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Edit $block */ /** @var \Magento\Wishlist\Model\Item $item */ $item = $block->getItem(); diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/name.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/name.phtml index 53c5f35f886ea..265f4635a6be3 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/name.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/name.phtml @@ -6,7 +6,7 @@ // @codingStandardsIgnoreFile -/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column $block */ +/** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Info $block */ /** @var \Magento\Wishlist\Model\Item $item */ $item = $block->getItem(); diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/remove.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/remove.phtml index 0bd78ba38ba58..57ae439d9995b 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/remove.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/remove.phtml @@ -6,7 +6,7 @@ // @codingStandardsIgnoreFile -/* @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column $block */ +/* @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Remove $block */ ?> <a href="#" data-role="remove" data-post-remove='<?= /* @noEscape */ $block->getItemRemoveParams($block->getItem()) ?>' title="<?= $block->escapeHtmlAttr(__('Remove Item')) ?>" class="btn-remove action delete"> From d9b01e35fb0e068b25db777a128144fede1c37fd Mon Sep 17 00:00:00 2001 From: Denis Kopylov <dkopylov@magenius.team> Date: Tue, 5 Mar 2019 09:42:36 +0300 Subject: [PATCH 0914/1295] Change deprecation annotation (https://github.com/magento/magento2/issues/21419) --- .../Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php | 2 +- .../Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php | 2 +- .../Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php | 2 +- .../Wishlist/Block/Customer/Wishlist/Item/Column/Info.php | 2 +- .../Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php index 0cf6b6a9867d1..41ce705c77607 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php @@ -13,7 +13,7 @@ /** * @api - * @deprecated Empty class + * @deprecated * @since 100.0.2 */ class Actions extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php index 5f40e96bfa6ff..f63d083bf56b6 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php @@ -13,7 +13,7 @@ /** * @api - * @deprecated Empty class + * @deprecated * @since 100.0.2 */ class Comment extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php index c4e94ec503175..1fc73c05f6f86 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php @@ -13,7 +13,7 @@ /** * @api - * @deprecated Empty class + * @deprecated * @since 100.0.2 */ class Edit extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php index f83c122910ebd..e6067392bae48 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php @@ -13,7 +13,7 @@ /** * @api - * @deprecated Empty class + * @deprecated * @since 100.0.2 */ class Info extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php index 3e6f225842e01..724ffd90f87db 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php @@ -13,7 +13,7 @@ /** * @api - * @deprecated Empty class + * @deprecated * @since 100.0.2 */ class Remove extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column From 9661291658022dc4b9ff60cc73bc320b101d48be Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Tue, 12 Mar 2019 17:01:01 +0200 Subject: [PATCH 0915/1295] ENGCOM-4460: Static test fix. --- .../Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php | 4 ++-- .../Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php | 4 ++-- .../Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php | 4 ++-- .../Wishlist/Block/Customer/Wishlist/Item/Column/Info.php | 4 ++-- .../Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php index 41ce705c77607..40882ae00dae1 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Actions.php @@ -5,13 +5,13 @@ */ /** - * Wishlist for item column in customer wishlist - * * @author Magento Core Team <core@magentocommerce.com> */ namespace Magento\Wishlist\Block\Customer\Wishlist\Item\Column; /** + * Model for item column in customer wishlist. + * * @api * @deprecated * @since 100.0.2 diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php index f63d083bf56b6..53f67626e956d 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Comment.php @@ -5,13 +5,13 @@ */ /** - * Wishlist block customer item cart column - * * @author Magento Core Team <core@magentocommerce.com> */ namespace Magento\Wishlist\Block\Customer\Wishlist\Item\Column; /** + * Wishlist block customer item cart column. + * * @api * @deprecated * @since 100.0.2 diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php index 1fc73c05f6f86..c4c786961694b 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Edit.php @@ -5,13 +5,13 @@ */ /** - * Edit item in customer wishlist table - * * @author Magento Core Team <core@magentocommerce.com> */ namespace Magento\Wishlist\Block\Customer\Wishlist\Item\Column; /** + * Edit item in customer wishlist table. + * * @api * @deprecated * @since 100.0.2 diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php index e6067392bae48..b7eaf53fc23b5 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Info.php @@ -5,13 +5,13 @@ */ /** - * Wishlist block customer item cart column - * * @author Magento Core Team <core@magentocommerce.com> */ namespace Magento\Wishlist\Block\Customer\Wishlist\Item\Column; /** + * Wishlist block customer item cart column. + * * @api * @deprecated * @since 100.0.2 diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php index 724ffd90f87db..09f5014edead6 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Remove.php @@ -5,13 +5,13 @@ */ /** - * Delete item column in customer wishlist table - * * @author Magento Core Team <core@magentocommerce.com> */ namespace Magento\Wishlist\Block\Customer\Wishlist\Item\Column; /** + * Delete item column in customer wishlist table + * * @api * @deprecated * @since 100.0.2 From 630445197f7bbee7c71855b6771ab6f881e3502f Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Fri, 15 Mar 2019 09:53:49 +0200 Subject: [PATCH 0916/1295] MAGETWO-95020: Clicking on a Root Category causes all other Root Categories to be expanded to the top-level categories --- app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php b/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php index 020666838a79a..a7bb242daf86f 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php @@ -284,6 +284,7 @@ public function getIsWasExpanded() * Get move url * * @return string + * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) */ public function getMoveUrl() { @@ -450,6 +451,7 @@ protected function _isParentSelectedCategory($node) * Check if page loaded by outside link to category edit * * @return boolean + * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) */ public function isClearEdit() { From f31e7f0949db2d817461473f57f162e550f253c2 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Fri, 15 Mar 2019 11:05:28 +0200 Subject: [PATCH 0917/1295] ENGCOM-4182: Static test fix. --- .../Adminhtml/Product/Edit/Action/Attribute/Tab/Inventory.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Inventory.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Inventory.php index edeb7f94c3ae5..964872b6e51bd 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Inventory.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Action/Attribute/Tab/Inventory.php @@ -109,7 +109,7 @@ public function getTabTitle() } /** - * @inheritdoc + * @inheritdoc */ public function canShowTab() { @@ -117,7 +117,7 @@ public function canShowTab() } /** - * @inheritdoc + * @inheritdoc */ public function isHidden() { From b8055004f7619d7a799769ed17f52fae8f75bb50 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Fri, 15 Mar 2019 13:46:16 +0200 Subject: [PATCH 0918/1295] MAGETWO-96375: Checkout Free Shipping Recalculation after Coupon Code Added --- ...frontFreeShippingRecalculationAfterCouponCodeAddedTest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml index 0407502b1a2d7..5ea04bbcbc13c 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml @@ -14,8 +14,8 @@ <stories value="Checkout Free Shipping Recalculation after Coupon Code Applied"/> <description value="User should be able to do checkout free shipping recalculation after adding coupon code"/> <severity value="MAJOR"/> - <testCaseId value="MAGETWO-96537"/> - <useCaseId value="MC-14807"/> + <testCaseId value="MC-15412"/> + <useCaseId value="MAGETWO-96375"/> <group value="sales"/> <group value="salesRule"/> </annotations> From 4b133afd2f5ff7bfb8daa048deeae278bb4b9a59 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Fri, 15 Mar 2019 16:36:45 +0200 Subject: [PATCH 0919/1295] MAGETWO-98380: The required product attribute is not displayed when change attribute set --- .../Controller/Adminhtml/Product/Builder.php | 8 +++ .../ActionGroup/AdminProductActionGroup.xml | 10 ++++ .../Test/Mftf/Data/ProductAttributeData.xml | 5 +- .../AdminChangeProductAttributeSetTest.xml | 57 +++++++++++++++++++ 4 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSetTest.xml diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Builder.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Builder.php index 125406061aed7..448de260f2eed 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Builder.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Builder.php @@ -15,6 +15,9 @@ use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Type as ProductTypes; +/** + * Build a product based on a request. + */ class Builder { /** @@ -92,6 +95,9 @@ public function build(RequestInterface $request) if ($productId) { try { $product = $this->productRepository->getById($productId, true, $storeId); + if ($attributeSetId) { + $product->setAttributeSetId($attributeSetId); + } } catch (\Exception $e) { $product = $this->createEmptyProduct(ProductTypes::DEFAULT_TYPE, $attributeSetId, $storeId); $this->logger->critical($e); @@ -113,6 +119,8 @@ public function build(RequestInterface $request) } /** + * Create a product with the given properties + * * @param int $typeId * @param int $attributeSetId * @param int $storeId diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index d64dfb928e651..12a11944e4290 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -257,4 +257,14 @@ <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton"/> <see selector="{{AdminMessagesSection.success}}" userInput="You saved the product." stepKey="seeSaveProductMessage"/> </actionGroup> + + <actionGroup name="AdminChangeProductAttributeSet"> + <arguments> + <argument name="attributeSet"/> + </arguments> + <scrollToTopOfPage stepKey="scrollToTop"/> + <click selector="{{AdminProductFormSection.attributeSet}}" stepKey="startEditAttrSet"/> + <fillField selector="{{AdminProductFormSection.attributeSetFilter}}" userInput="{{attributeSet.attribute_set_name}}" stepKey="searchForAttrSet"/> + <click selector="{{AdminProductFormSection.attributeSetFilterResult}}" stepKey="selectAttrSet"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml index d5f29230f0308..b2483c3d2d080 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml @@ -7,7 +7,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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="productAttributeWithTwoOptions" type="ProductAttribute"> <data key="name" unique="suffix">ProductAttributeWithTwoOptions</data> <data key="attribute_code" unique="suffix">attribute</data> @@ -74,4 +74,7 @@ <data key="used_for_sort_by">true</data> <requiredEntity type="FrontendLabel">ProductAttributeFrontendLabel</requiredEntity> </entity> + <entity name="ProductAttributeText" extends="productAttributeWithTwoOptions"> + <data key="frontend_input">text</data> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSetTest.xml new file mode 100644 index 0000000000000..0a9d5fa3857f5 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSetTest.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminChangeProductAttributeSetTest"> + <annotations> + <features value="Catalog"/> + <stories value="Update product attribute set"/> + <title value="Attributes from the selected attribute set should be shown"/> + <description value="Attributes from the selected attribute set should be shown"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-15414"/> + <useCaseId value="MAGETWO-98380"/> + <group value="catalog"/> + </annotations> + <before> + <!--Create category product, attribute, attribute set--> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ProductAttributeText" stepKey="createProductAttribute"/> + <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> + + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <!--Assign attribute to attribute set--> + <amOnPage url="{{AdminProductAttributeSetEditPage.url($$createAttributeSet.attribute_set_id$$)}}" stepKey="openAttributeSetEdit"/> + <actionGroup ref="AssignAttributeToGroup" stepKey="assignAttributeToGroup"> + <argument name="group" value="Product Details"/> + <argument name="attribute" value="$$createProductAttribute.attribute_code$$"/> + </actionGroup> + <actionGroup ref="SaveAttributeSet" stepKey="saveAttributeSet"/> + </before> + <after> + <!--Delete created entities--> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProductAttribute" stepKey="deleteProductAttribute"/> + <deleteData createDataKey="createAttributeSet" stepKey="deleteAttributeSet"/> + + <actionGroup ref="logout" stepKey="logoutAdminUserAfterTest"/> + </after> + <!--Open created product--> + <amOnPage url="{{AdminProductEditPage.url($$createSimpleProduct.id$$)}}" stepKey="openProductEditPage"/> + <!--Change product attribute set--> + <actionGroup ref="AdminChangeProductAttributeSet" stepKey="changeProductAttributeSet"> + <argument name="attributeSet" value="$$createAttributeSet$$"/> + </actionGroup> + <!--Check new attribute is visible on product edit page--> + <waitForText userInput="$$createProductAttribute.default_frontend_label$$" stepKey="seeAttributeInForm"/> + </test> +</tests> From 66eb64d325658d515ece30eda226d62ff26ca9ef Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Fri, 15 Mar 2019 17:05:51 +0200 Subject: [PATCH 0920/1295] MC-15077: Error at the end of a long list of Tax Rates --- app/code/Magento/Catalog/Model/CategoryList.php | 4 ++-- .../Catalog/Test/Unit/Model/CategoryListTest.php | 2 +- lib/internal/Magento/Framework/Data/Collection.php | 9 --------- .../Framework/Data/Test/Unit/CollectionTest.php | 13 ------------- 4 files changed, 3 insertions(+), 25 deletions(-) diff --git a/app/code/Magento/Catalog/Model/CategoryList.php b/app/code/Magento/Catalog/Model/CategoryList.php index 86692c7d6bc61..cab8e013d9ba1 100644 --- a/app/code/Magento/Catalog/Model/CategoryList.php +++ b/app/code/Magento/Catalog/Model/CategoryList.php @@ -78,8 +78,8 @@ public function getList(SearchCriteriaInterface $searchCriteria) $this->collectionProcessor->process($searchCriteria, $collection); $items = []; - foreach ($collection->getItems() as $category) { - $items[] = $this->categoryRepository->get($category->getId()); + foreach ($collection->getAllIds() as $id) { + $items[] = $this->categoryRepository->get($id); } /** @var CategorySearchResultsInterface $searchResult */ diff --git a/app/code/Magento/Catalog/Test/Unit/Model/CategoryListTest.php b/app/code/Magento/Catalog/Test/Unit/Model/CategoryListTest.php index ab0c0ea38d79c..467a9d6a0c8b1 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/CategoryListTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/CategoryListTest.php @@ -95,7 +95,7 @@ public function testGetList() $collection = $this->getMockBuilder(Collection::class)->disableOriginalConstructor()->getMock(); $collection->expects($this->once())->method('getSize')->willReturn($totalCount); - $collection->expects($this->once())->method('getItems')->willReturn([$categoryFirst, $categorySecond]); + $collection->expects($this->once())->method('getAllIds')->willReturn([$categoryIdFirst, $categoryIdSecond]); $this->collectionProcessorMock->expects($this->once()) ->method('process') diff --git a/lib/internal/Magento/Framework/Data/Collection.php b/lib/internal/Magento/Framework/Data/Collection.php index 4e9132d49a2e2..099753ac1b56f 100644 --- a/lib/internal/Magento/Framework/Data/Collection.php +++ b/lib/internal/Magento/Framework/Data/Collection.php @@ -7,7 +7,6 @@ use Magento\Framework\Data\Collection\EntityFactoryInterface; use Magento\Framework\Option\ArrayInterface; -use Magento\Framework\Exception\InputException; /** * Data collection @@ -235,20 +234,12 @@ protected function _setIsLoaded($flag = true) * Get current collection page * * @param int $displacement - * @throws \Magento\Framework\Exception\InputException * @return int */ public function getCurPage($displacement = 0) { if ($this->_curPage + $displacement < 1) { return 1; - } elseif ($this->_curPage > $this->getLastPageNumber() && $displacement === 0) { - throw new InputException( - __( - 'currentPage value %1 specified is greater than the %2 page(s) available.', - [$this->_curPage, $this->getLastPageNumber()] - ) - ); } elseif ($this->_curPage + $displacement > $this->getLastPageNumber()) { return $this->getLastPageNumber(); } else { diff --git a/lib/internal/Magento/Framework/Data/Test/Unit/CollectionTest.php b/lib/internal/Magento/Framework/Data/Test/Unit/CollectionTest.php index 0b6ddd6999d3e..a69f5af08a93c 100644 --- a/lib/internal/Magento/Framework/Data/Test/Unit/CollectionTest.php +++ b/lib/internal/Magento/Framework/Data/Test/Unit/CollectionTest.php @@ -146,19 +146,6 @@ public function testGetCurPage() $this->assertEquals(1, $this->_model->getCurPage()); } - /** - * Test for getCurPage with exception. - * - * @expectedException \Magento\Framework\Exception\InputException - * @expectedExceptionMessage currentPage value 10 specified is greater than the 1 page(s) available. - * @return void - */ - public function testGetCurPageWithException() - { - $this->_model->setCurPage(10); - $this->_model->getCurPage(); - } - /** * Test for method possibleFlowWithItem. * From 11045e88e32ba702e51ae8bf40e1c58e0d41abff Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Fri, 15 Mar 2019 22:17:08 +0200 Subject: [PATCH 0921/1295] MC-15077: Error at the end of a long list of Tax Rates --- app/code/Magento/Catalog/Test/Unit/Model/CategoryListTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/CategoryListTest.php b/app/code/Magento/Catalog/Test/Unit/Model/CategoryListTest.php index 467a9d6a0c8b1..b8b76524099f4 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/CategoryListTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/CategoryListTest.php @@ -86,9 +86,7 @@ public function testGetList() $categoryIdSecond = 2; $categoryFirst = $this->getMockBuilder(Category::class)->disableOriginalConstructor()->getMock(); - $categoryFirst->expects($this->atLeastOnce())->method('getId')->willReturn($categoryIdFirst); $categorySecond = $this->getMockBuilder(Category::class)->disableOriginalConstructor()->getMock(); - $categorySecond->expects($this->atLeastOnce())->method('getId')->willReturn($categoryIdSecond); /** @var SearchCriteriaInterface|\PHPUnit_Framework_MockObject_MockObject $searchCriteria */ $searchCriteria = $this->createMock(SearchCriteriaInterface::class); From 0c786907ffe03d0e2990612eec16ee58b00379c5 Mon Sep 17 00:00:00 2001 From: mage2-team <mage2-team@magento.com> Date: Fri, 15 Mar 2019 16:01:06 -0700 Subject: [PATCH 0922/1295] MAGETWO-97699: Magento 2.2.8 Publication (build 2.2.8.025) --- .../Magento/AdminNotification/composer.json | 2 +- app/code/Magento/Analytics/composer.json | 2 +- app/code/Magento/Authorizenet/composer.json | 2 +- app/code/Magento/Backend/composer.json | 2 +- app/code/Magento/Backup/composer.json | 2 +- app/code/Magento/Braintree/composer.json | 2 +- app/code/Magento/Bundle/composer.json | 2 +- .../Magento/BundleImportExport/composer.json | 2 +- .../Magento/CacheInvalidate/composer.json | 2 +- app/code/Magento/Captcha/composer.json | 2 +- app/code/Magento/Catalog/composer.json | 2 +- .../Magento/CatalogImportExport/composer.json | 2 +- .../Magento/CatalogInventory/composer.json | 2 +- app/code/Magento/CatalogRule/composer.json | 2 +- app/code/Magento/CatalogSearch/composer.json | 2 +- .../Magento/CatalogUrlRewrite/composer.json | 4 +- app/code/Magento/CatalogWidget/composer.json | 2 +- app/code/Magento/Checkout/composer.json | 2 +- .../Magento/CheckoutAgreements/composer.json | 2 +- app/code/Magento/Cms/composer.json | 2 +- app/code/Magento/Config/composer.json | 2 +- .../ConfigurableImportExport/composer.json | 2 +- .../Magento/ConfigurableProduct/composer.json | 2 +- app/code/Magento/Contact/composer.json | 2 +- app/code/Magento/Cron/composer.json | 2 +- app/code/Magento/CurrencySymbol/composer.json | 2 +- app/code/Magento/Customer/composer.json | 2 +- .../CustomerImportExport/composer.json | 2 +- app/code/Magento/Deploy/composer.json | 2 +- app/code/Magento/Developer/composer.json | 2 +- app/code/Magento/Directory/composer.json | 2 +- app/code/Magento/Downloadable/composer.json | 2 +- app/code/Magento/Eav/composer.json | 2 +- app/code/Magento/Email/composer.json | 2 +- app/code/Magento/GiftMessage/composer.json | 2 +- .../Magento/GoogleAnalytics/composer.json | 2 +- .../Magento/GroupedImportExport/composer.json | 2 +- app/code/Magento/GroupedProduct/composer.json | 2 +- app/code/Magento/ImportExport/composer.json | 2 +- app/code/Magento/Indexer/composer.json | 2 +- .../Magento/InstantPurchase/composer.json | 2 +- app/code/Magento/Integration/composer.json | 2 +- .../Magento/LayeredNavigation/composer.json | 2 +- app/code/Magento/Msrp/composer.json | 2 +- app/code/Magento/Multishipping/composer.json | 2 +- .../Magento/NewRelicReporting/composer.json | 2 +- app/code/Magento/Newsletter/composer.json | 2 +- .../Magento/OfflineShipping/composer.json | 2 +- app/code/Magento/PageCache/composer.json | 2 +- app/code/Magento/Payment/composer.json | 2 +- app/code/Magento/Paypal/composer.json | 2 +- app/code/Magento/Persistent/composer.json | 2 +- app/code/Magento/ProductVideo/composer.json | 2 +- app/code/Magento/Quote/composer.json | 2 +- app/code/Magento/Reports/composer.json | 2 +- app/code/Magento/Review/composer.json | 2 +- app/code/Magento/Rule/composer.json | 2 +- app/code/Magento/Sales/composer.json | 2 +- app/code/Magento/SalesRule/composer.json | 2 +- app/code/Magento/Search/composer.json | 2 +- app/code/Magento/Security/composer.json | 2 +- app/code/Magento/SendFriend/composer.json | 2 +- app/code/Magento/Shipping/composer.json | 2 +- app/code/Magento/Signifyd/composer.json | 2 +- app/code/Magento/Sitemap/composer.json | 2 +- app/code/Magento/Store/composer.json | 2 +- app/code/Magento/Swatches/composer.json | 2 +- app/code/Magento/Tax/composer.json | 2 +- app/code/Magento/Theme/composer.json | 2 +- app/code/Magento/Ui/composer.json | 2 +- app/code/Magento/Ups/composer.json | 2 +- app/code/Magento/UrlRewrite/composer.json | 2 +- app/code/Magento/User/composer.json | 2 +- app/code/Magento/Usps/composer.json | 2 +- app/code/Magento/Webapi/composer.json | 2 +- app/code/Magento/Weee/composer.json | 2 +- app/code/Magento/Widget/composer.json | 2 +- app/code/Magento/Wishlist/composer.json | 2 +- .../adminhtml/Magento/backend/composer.json | 2 +- .../frontend/Magento/blank/composer.json | 2 +- .../frontend/Magento/luma/composer.json | 2 +- composer.json | 166 +-- composer.lock | 1029 ++++++++++------- lib/internal/Magento/Framework/composer.json | 2 +- 84 files changed, 807 insertions(+), 554 deletions(-) diff --git a/app/code/Magento/AdminNotification/composer.json b/app/code/Magento/AdminNotification/composer.json index c577a2479f209..14afd21079f34 100644 --- a/app/code/Magento/AdminNotification/composer.json +++ b/app/code/Magento/AdminNotification/composer.json @@ -11,7 +11,7 @@ "lib-libxml": "*" }, "type": "magento2-module", - "version": "100.2.5", + "version": "100.2.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Analytics/composer.json b/app/code/Magento/Analytics/composer.json index 3eebcafaba98f..11acfec76ca24 100644 --- a/app/code/Magento/Analytics/composer.json +++ b/app/code/Magento/Analytics/composer.json @@ -10,7 +10,7 @@ "magento/framework": "101.0.*" }, "type": "magento2-module", - "version": "100.2.4", + "version": "100.2.5", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Authorizenet/composer.json b/app/code/Magento/Authorizenet/composer.json index 0e6d1e8296c8a..d397f5cfae81e 100644 --- a/app/code/Magento/Authorizenet/composer.json +++ b/app/code/Magento/Authorizenet/composer.json @@ -16,7 +16,7 @@ "magento/module-config": "101.0.*" }, "type": "magento2-module", - "version": "100.2.3", + "version": "100.2.4", "license": [ "proprietary" ], diff --git a/app/code/Magento/Backend/composer.json b/app/code/Magento/Backend/composer.json index dfd71f4ecd4d0..d3b766da0006f 100644 --- a/app/code/Magento/Backend/composer.json +++ b/app/code/Magento/Backend/composer.json @@ -24,7 +24,7 @@ "magento/module-theme": "100.2.*" }, "type": "magento2-module", - "version": "100.2.7", + "version": "100.2.8", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Backup/composer.json b/app/code/Magento/Backup/composer.json index b613170b6d661..6e741ae29cab5 100644 --- a/app/code/Magento/Backup/composer.json +++ b/app/code/Magento/Backup/composer.json @@ -9,7 +9,7 @@ "magento/framework": "101.0.*" }, "type": "magento2-module", - "version": "100.2.6", + "version": "100.2.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Braintree/composer.json b/app/code/Magento/Braintree/composer.json index 8830333ac4f7f..ccfb268856bb6 100644 --- a/app/code/Magento/Braintree/composer.json +++ b/app/code/Magento/Braintree/composer.json @@ -25,7 +25,7 @@ "magento/module-theme": "100.2.*" }, "type": "magento2-module", - "version": "100.2.7", + "version": "100.2.8", "license": [ "proprietary" ], diff --git a/app/code/Magento/Bundle/composer.json b/app/code/Magento/Bundle/composer.json index d12d5e715eb3c..d82e4201bbda4 100644 --- a/app/code/Magento/Bundle/composer.json +++ b/app/code/Magento/Bundle/composer.json @@ -26,7 +26,7 @@ "magento/module-sales-rule": "101.0.*" }, "type": "magento2-module", - "version": "100.2.6", + "version": "100.2.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/BundleImportExport/composer.json b/app/code/Magento/BundleImportExport/composer.json index bfb8bd2b663a1..985b83a197369 100644 --- a/app/code/Magento/BundleImportExport/composer.json +++ b/app/code/Magento/BundleImportExport/composer.json @@ -12,7 +12,7 @@ "magento/framework": "101.0.*" }, "type": "magento2-module", - "version": "100.2.4", + "version": "100.2.5", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/CacheInvalidate/composer.json b/app/code/Magento/CacheInvalidate/composer.json index e35efe0cd2e4c..4b9ef4e140f35 100644 --- a/app/code/Magento/CacheInvalidate/composer.json +++ b/app/code/Magento/CacheInvalidate/composer.json @@ -7,7 +7,7 @@ "magento/framework": "101.0.*" }, "type": "magento2-module", - "version": "100.2.3", + "version": "100.2.4", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Captcha/composer.json b/app/code/Magento/Captcha/composer.json index 09104f37814ee..471c4f976a300 100644 --- a/app/code/Magento/Captcha/composer.json +++ b/app/code/Magento/Captcha/composer.json @@ -13,7 +13,7 @@ "zendframework/zend-session": "^2.7.3" }, "type": "magento2-module", - "version": "100.2.4", + "version": "100.2.5", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Catalog/composer.json b/app/code/Magento/Catalog/composer.json index 127b14e3b9d85..893f8d7190700 100644 --- a/app/code/Magento/Catalog/composer.json +++ b/app/code/Magento/Catalog/composer.json @@ -34,7 +34,7 @@ "magento/module-catalog-sample-data": "Sample Data version:100.2.*" }, "type": "magento2-module", - "version": "102.0.7", + "version": "102.0.8", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/CatalogImportExport/composer.json b/app/code/Magento/CatalogImportExport/composer.json index 60115e63403cf..180fdb95a4d9b 100644 --- a/app/code/Magento/CatalogImportExport/composer.json +++ b/app/code/Magento/CatalogImportExport/composer.json @@ -16,7 +16,7 @@ "ext-ctype": "*" }, "type": "magento2-module", - "version": "100.2.6", + "version": "100.2.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/CatalogInventory/composer.json b/app/code/Magento/CatalogInventory/composer.json index 1c34d360cf9f1..3086b50168aed 100644 --- a/app/code/Magento/CatalogInventory/composer.json +++ b/app/code/Magento/CatalogInventory/composer.json @@ -14,7 +14,7 @@ "magento/module-sales": "101.0.*" }, "type": "magento2-module", - "version": "100.2.6", + "version": "100.2.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/CatalogRule/composer.json b/app/code/Magento/CatalogRule/composer.json index cdcd7629c6040..ef4dece889f6b 100644 --- a/app/code/Magento/CatalogRule/composer.json +++ b/app/code/Magento/CatalogRule/composer.json @@ -17,7 +17,7 @@ "magento/module-catalog-rule-sample-data": "Sample Data version:100.2.*" }, "type": "magento2-module", - "version": "101.0.6", + "version": "101.0.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/CatalogSearch/composer.json b/app/code/Magento/CatalogSearch/composer.json index 0b935c71c17d0..a5f1e5f6fc4f2 100644 --- a/app/code/Magento/CatalogSearch/composer.json +++ b/app/code/Magento/CatalogSearch/composer.json @@ -20,7 +20,7 @@ "magento/module-config": "101.0.*" }, "type": "magento2-module", - "version": "100.2.6", + "version": "100.2.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/CatalogUrlRewrite/composer.json b/app/code/Magento/CatalogUrlRewrite/composer.json index 2f34ee54f36e1..a486b7bb3f6b8 100644 --- a/app/code/Magento/CatalogUrlRewrite/composer.json +++ b/app/code/Magento/CatalogUrlRewrite/composer.json @@ -14,10 +14,10 @@ "magento/module-ui": "101.0.*" }, "suggest": { - "magento/module-webapi": "*" + "magento/module-webapi": "100.2.*" }, "type": "magento2-module", - "version": "100.2.6", + "version": "100.2.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/CatalogWidget/composer.json b/app/code/Magento/CatalogWidget/composer.json index 7bc1240c43276..510026b008f4e 100644 --- a/app/code/Magento/CatalogWidget/composer.json +++ b/app/code/Magento/CatalogWidget/composer.json @@ -14,7 +14,7 @@ "magento/framework": "101.0.*" }, "type": "magento2-module", - "version": "100.2.4", + "version": "100.2.5", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Checkout/composer.json b/app/code/Magento/Checkout/composer.json index 0fbfaffa7f22a..c46aaa9cb612c 100644 --- a/app/code/Magento/Checkout/composer.json +++ b/app/code/Magento/Checkout/composer.json @@ -26,7 +26,7 @@ "magento/module-cookie": "100.2.*" }, "type": "magento2-module", - "version": "100.2.7", + "version": "100.2.8", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/CheckoutAgreements/composer.json b/app/code/Magento/CheckoutAgreements/composer.json index c6c2102600974..93ecb3bf836ae 100644 --- a/app/code/Magento/CheckoutAgreements/composer.json +++ b/app/code/Magento/CheckoutAgreements/composer.json @@ -10,7 +10,7 @@ "magento/framework": "101.0.*" }, "type": "magento2-module", - "version": "100.2.3", + "version": "100.2.4", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Cms/composer.json b/app/code/Magento/Cms/composer.json index 3f425e91b89e2..d463cedd8dcd2 100644 --- a/app/code/Magento/Cms/composer.json +++ b/app/code/Magento/Cms/composer.json @@ -18,7 +18,7 @@ "magento/module-cms-sample-data": "Sample Data version:100.2.*" }, "type": "magento2-module", - "version": "102.0.7", + "version": "102.0.8", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Config/composer.json b/app/code/Magento/Config/composer.json index 793d423280414..f36c29d387c9b 100644 --- a/app/code/Magento/Config/composer.json +++ b/app/code/Magento/Config/composer.json @@ -13,7 +13,7 @@ "magento/module-deploy": "100.2.*" }, "type": "magento2-module", - "version": "101.0.7", + "version": "101.0.8", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/ConfigurableImportExport/composer.json b/app/code/Magento/ConfigurableImportExport/composer.json index b3d8af9f419d2..cf0e0e819a89c 100644 --- a/app/code/Magento/ConfigurableImportExport/composer.json +++ b/app/code/Magento/ConfigurableImportExport/composer.json @@ -12,7 +12,7 @@ "magento/module-store": "100.2.*" }, "type": "magento2-module", - "version": "100.2.4", + "version": "100.2.5", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/ConfigurableProduct/composer.json b/app/code/Magento/ConfigurableProduct/composer.json index 74e611af2cbbc..01a80ac1f24d5 100644 --- a/app/code/Magento/ConfigurableProduct/composer.json +++ b/app/code/Magento/ConfigurableProduct/composer.json @@ -25,7 +25,7 @@ "magento/module-product-links-sample-data": "Sample Data version:100.2.*" }, "type": "magento2-module", - "version": "100.2.7", + "version": "100.2.8", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Contact/composer.json b/app/code/Magento/Contact/composer.json index 72c8005c53432..bb95e20c1172a 100644 --- a/app/code/Magento/Contact/composer.json +++ b/app/code/Magento/Contact/composer.json @@ -10,7 +10,7 @@ "magento/framework": "101.0.*" }, "type": "magento2-module", - "version": "100.2.4", + "version": "100.2.5", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Cron/composer.json b/app/code/Magento/Cron/composer.json index ddbe7df2c0a2e..a1f1e107a3c57 100644 --- a/app/code/Magento/Cron/composer.json +++ b/app/code/Magento/Cron/composer.json @@ -10,7 +10,7 @@ "magento/module-config": "101.0.*" }, "type": "magento2-module", - "version": "100.2.5", + "version": "100.2.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/CurrencySymbol/composer.json b/app/code/Magento/CurrencySymbol/composer.json index 021fe7ae9bc56..378b0df398ef5 100644 --- a/app/code/Magento/CurrencySymbol/composer.json +++ b/app/code/Magento/CurrencySymbol/composer.json @@ -11,7 +11,7 @@ "magento/framework": "101.0.*" }, "type": "magento2-module", - "version": "100.2.3", + "version": "100.2.4", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Customer/composer.json b/app/code/Magento/Customer/composer.json index af45eb7931308..e8d5fe2aaff48 100644 --- a/app/code/Magento/Customer/composer.json +++ b/app/code/Magento/Customer/composer.json @@ -29,7 +29,7 @@ "magento/module-customer-sample-data": "Sample Data version:100.2.*" }, "type": "magento2-module", - "version": "101.0.7", + "version": "101.0.8", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/CustomerImportExport/composer.json b/app/code/Magento/CustomerImportExport/composer.json index ebf8f7f52b99a..e0571ba1247ea 100644 --- a/app/code/Magento/CustomerImportExport/composer.json +++ b/app/code/Magento/CustomerImportExport/composer.json @@ -12,7 +12,7 @@ "magento/framework": "101.0.*" }, "type": "magento2-module", - "version": "100.2.5", + "version": "100.2.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Deploy/composer.json b/app/code/Magento/Deploy/composer.json index 79ccab30d2924..b33313081b0f0 100644 --- a/app/code/Magento/Deploy/composer.json +++ b/app/code/Magento/Deploy/composer.json @@ -10,7 +10,7 @@ "magento/module-config": "101.0.*" }, "type": "magento2-module", - "version": "100.2.6", + "version": "100.2.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Developer/composer.json b/app/code/Magento/Developer/composer.json index 771853609211a..776d565bf5efa 100644 --- a/app/code/Magento/Developer/composer.json +++ b/app/code/Magento/Developer/composer.json @@ -8,7 +8,7 @@ "magento/module-config": "101.0.*" }, "type": "magento2-module", - "version": "100.2.5", + "version": "100.2.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Directory/composer.json b/app/code/Magento/Directory/composer.json index a4b5991095307..9681a951d9d06 100644 --- a/app/code/Magento/Directory/composer.json +++ b/app/code/Magento/Directory/composer.json @@ -10,7 +10,7 @@ "lib-libxml": "*" }, "type": "magento2-module", - "version": "100.2.6", + "version": "100.2.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Downloadable/composer.json b/app/code/Magento/Downloadable/composer.json index e8a885cb49806..3ea7f69c2fad5 100644 --- a/app/code/Magento/Downloadable/composer.json +++ b/app/code/Magento/Downloadable/composer.json @@ -25,7 +25,7 @@ "magento/module-downloadable-sample-data": "Sample Data version:100.2.*" }, "type": "magento2-module", - "version": "100.2.6", + "version": "100.2.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Eav/composer.json b/app/code/Magento/Eav/composer.json index b1eec7fcaa602..b5f3c86947a0f 100644 --- a/app/code/Magento/Eav/composer.json +++ b/app/code/Magento/Eav/composer.json @@ -11,7 +11,7 @@ "magento/framework": "101.0.*" }, "type": "magento2-module", - "version": "101.0.6", + "version": "101.0.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Email/composer.json b/app/code/Magento/Email/composer.json index 482b63a2a34d7..6fda406b73483 100644 --- a/app/code/Magento/Email/composer.json +++ b/app/code/Magento/Email/composer.json @@ -15,7 +15,7 @@ "magento/module-theme": "100.2.*" }, "type": "magento2-module", - "version": "100.2.5", + "version": "100.2.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/GiftMessage/composer.json b/app/code/Magento/GiftMessage/composer.json index d8fa39fa590a4..7ad147a15989a 100644 --- a/app/code/Magento/GiftMessage/composer.json +++ b/app/code/Magento/GiftMessage/composer.json @@ -17,7 +17,7 @@ "magento/module-multishipping": "100.2.*" }, "type": "magento2-module", - "version": "100.2.3", + "version": "100.2.4", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/GoogleAnalytics/composer.json b/app/code/Magento/GoogleAnalytics/composer.json index 138aa560e8bcd..7c6a511f8b5e4 100644 --- a/app/code/Magento/GoogleAnalytics/composer.json +++ b/app/code/Magento/GoogleAnalytics/composer.json @@ -12,7 +12,7 @@ "magento/module-config": "101.0.*" }, "type": "magento2-module", - "version": "100.2.5", + "version": "100.2.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/GroupedImportExport/composer.json b/app/code/Magento/GroupedImportExport/composer.json index 092c40d011580..4e7573761f3a1 100644 --- a/app/code/Magento/GroupedImportExport/composer.json +++ b/app/code/Magento/GroupedImportExport/composer.json @@ -11,7 +11,7 @@ "magento/framework": "101.0.*" }, "type": "magento2-module", - "version": "100.2.3", + "version": "100.2.4", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/GroupedProduct/composer.json b/app/code/Magento/GroupedProduct/composer.json index 7107903a91a58..508b424b2c022 100644 --- a/app/code/Magento/GroupedProduct/composer.json +++ b/app/code/Magento/GroupedProduct/composer.json @@ -21,7 +21,7 @@ "magento/module-grouped-product-sample-data": "Sample Data version:100.2.*" }, "type": "magento2-module", - "version": "100.2.5", + "version": "100.2.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/ImportExport/composer.json b/app/code/Magento/ImportExport/composer.json index 7df4f98de95f4..9e9f1babad4a3 100644 --- a/app/code/Magento/ImportExport/composer.json +++ b/app/code/Magento/ImportExport/composer.json @@ -12,7 +12,7 @@ "ext-ctype": "*" }, "type": "magento2-module", - "version": "100.2.7", + "version": "100.2.8", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Indexer/composer.json b/app/code/Magento/Indexer/composer.json index a0fa62c3d7358..6b8ee0825fc14 100644 --- a/app/code/Magento/Indexer/composer.json +++ b/app/code/Magento/Indexer/composer.json @@ -7,7 +7,7 @@ "magento/framework": "101.0.*" }, "type": "magento2-module", - "version": "100.2.5", + "version": "100.2.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/InstantPurchase/composer.json b/app/code/Magento/InstantPurchase/composer.json index d7257c7a46d90..1643692addbec 100644 --- a/app/code/Magento/InstantPurchase/composer.json +++ b/app/code/Magento/InstantPurchase/composer.json @@ -2,7 +2,7 @@ "name": "magento/module-instant-purchase", "description": "N/A", "type": "magento2-module", - "version": "100.2.3", + "version": "100.2.4", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Integration/composer.json b/app/code/Magento/Integration/composer.json index 74f2db3d68238..5d457d76cb94e 100644 --- a/app/code/Magento/Integration/composer.json +++ b/app/code/Magento/Integration/composer.json @@ -12,7 +12,7 @@ "magento/module-authorization": "100.2.*" }, "type": "magento2-module", - "version": "100.2.5", + "version": "100.2.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/LayeredNavigation/composer.json b/app/code/Magento/LayeredNavigation/composer.json index 8ce17fe30ec35..328549074f9b8 100644 --- a/app/code/Magento/LayeredNavigation/composer.json +++ b/app/code/Magento/LayeredNavigation/composer.json @@ -8,7 +8,7 @@ "magento/framework": "101.0.*" }, "type": "magento2-module", - "version": "100.2.4", + "version": "100.2.5", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Msrp/composer.json b/app/code/Magento/Msrp/composer.json index 625bec5d6c9d2..498f6a2550c61 100644 --- a/app/code/Magento/Msrp/composer.json +++ b/app/code/Magento/Msrp/composer.json @@ -16,7 +16,7 @@ "magento/module-msrp-sample-data": "Sample Data version:100.2.*" }, "type": "magento2-module", - "version": "100.2.3", + "version": "100.2.4", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Multishipping/composer.json b/app/code/Magento/Multishipping/composer.json index d32bb1f5bd82a..48095da856e02 100644 --- a/app/code/Magento/Multishipping/composer.json +++ b/app/code/Magento/Multishipping/composer.json @@ -15,7 +15,7 @@ "magento/module-theme": "100.2.*" }, "type": "magento2-module", - "version": "100.2.4", + "version": "100.2.5", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/NewRelicReporting/composer.json b/app/code/Magento/NewRelicReporting/composer.json index abb2a200ed723..b256b25716c87 100644 --- a/app/code/Magento/NewRelicReporting/composer.json +++ b/app/code/Magento/NewRelicReporting/composer.json @@ -13,7 +13,7 @@ "magento/magento-composer-installer": "*" }, "type": "magento2-module", - "version": "100.2.5", + "version": "100.2.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Newsletter/composer.json b/app/code/Magento/Newsletter/composer.json index a97d0bca5634d..9e02676e2488c 100644 --- a/app/code/Magento/Newsletter/composer.json +++ b/app/code/Magento/Newsletter/composer.json @@ -14,7 +14,7 @@ "magento/framework": "101.0.*" }, "type": "magento2-module", - "version": "100.2.6", + "version": "100.2.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/OfflineShipping/composer.json b/app/code/Magento/OfflineShipping/composer.json index e04816a3b1128..416181733dc07 100644 --- a/app/code/Magento/OfflineShipping/composer.json +++ b/app/code/Magento/OfflineShipping/composer.json @@ -19,7 +19,7 @@ "magento/module-offline-shipping-sample-data": "Sample Data version:100.2.*" }, "type": "magento2-module", - "version": "100.2.5", + "version": "100.2.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/PageCache/composer.json b/app/code/Magento/PageCache/composer.json index 2b6b62aef2c47..e83801344dae7 100644 --- a/app/code/Magento/PageCache/composer.json +++ b/app/code/Magento/PageCache/composer.json @@ -9,7 +9,7 @@ "magento/framework": "101.0.*" }, "type": "magento2-module", - "version": "100.2.4", + "version": "100.2.5", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Payment/composer.json b/app/code/Magento/Payment/composer.json index b5e7ecdfdaff9..2f00b878417bd 100644 --- a/app/code/Magento/Payment/composer.json +++ b/app/code/Magento/Payment/composer.json @@ -12,7 +12,7 @@ "magento/framework": "101.0.*" }, "type": "magento2-module", - "version": "100.2.5", + "version": "100.2.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Paypal/composer.json b/app/code/Magento/Paypal/composer.json index 331198b474783..5ee1739253ced 100644 --- a/app/code/Magento/Paypal/composer.json +++ b/app/code/Magento/Paypal/composer.json @@ -26,7 +26,7 @@ "magento/module-checkout-agreements": "100.2.*" }, "type": "magento2-module", - "version": "100.2.5", + "version": "100.2.6", "license": [ "proprietary" ], diff --git a/app/code/Magento/Persistent/composer.json b/app/code/Magento/Persistent/composer.json index 73184a0648d24..0bfbc30e28f69 100644 --- a/app/code/Magento/Persistent/composer.json +++ b/app/code/Magento/Persistent/composer.json @@ -12,7 +12,7 @@ "magento/framework": "101.0.*" }, "type": "magento2-module", - "version": "100.2.3", + "version": "100.2.4", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/ProductVideo/composer.json b/app/code/Magento/ProductVideo/composer.json index 7c5017eef4a5a..d92ab26d39fe5 100644 --- a/app/code/Magento/ProductVideo/composer.json +++ b/app/code/Magento/ProductVideo/composer.json @@ -16,7 +16,7 @@ "magento/module-config": "101.0.*" }, "type": "magento2-module", - "version": "100.2.5", + "version": "100.2.6", "license": [ "proprietary" ], diff --git a/app/code/Magento/Quote/composer.json b/app/code/Magento/Quote/composer.json index 5391d7779b420..202a5b601ceab 100644 --- a/app/code/Magento/Quote/composer.json +++ b/app/code/Magento/Quote/composer.json @@ -23,7 +23,7 @@ "magento/module-webapi": "100.2.*" }, "type": "magento2-module", - "version": "101.0.6", + "version": "101.0.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Reports/composer.json b/app/code/Magento/Reports/composer.json index 1abd03339a22a..f9c0c3568b077 100644 --- a/app/code/Magento/Reports/composer.json +++ b/app/code/Magento/Reports/composer.json @@ -22,7 +22,7 @@ "magento/framework": "101.0.*" }, "type": "magento2-module", - "version": "100.2.7", + "version": "100.2.8", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Review/composer.json b/app/code/Magento/Review/composer.json index c1d687c665199..4cc5cfc8d3f03 100644 --- a/app/code/Magento/Review/composer.json +++ b/app/code/Magento/Review/composer.json @@ -18,7 +18,7 @@ "magento/module-review-sample-data": "Sample Data version:100.2.*" }, "type": "magento2-module", - "version": "100.2.7", + "version": "100.2.8", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Rule/composer.json b/app/code/Magento/Rule/composer.json index e37274c19a969..33341dcf1e778 100644 --- a/app/code/Magento/Rule/composer.json +++ b/app/code/Magento/Rule/composer.json @@ -11,7 +11,7 @@ "lib-libxml": "*" }, "type": "magento2-module", - "version": "100.2.4", + "version": "100.2.5", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Sales/composer.json b/app/code/Magento/Sales/composer.json index ed5f939d81869..a2730b255639f 100644 --- a/app/code/Magento/Sales/composer.json +++ b/app/code/Magento/Sales/composer.json @@ -33,7 +33,7 @@ "magento/module-sales-sample-data": "Sample Data version:100.2.*" }, "type": "magento2-module", - "version": "101.0.6", + "version": "101.0.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/SalesRule/composer.json b/app/code/Magento/SalesRule/composer.json index 752c711ff4c3a..bf19520c7b552 100644 --- a/app/code/Magento/SalesRule/composer.json +++ b/app/code/Magento/SalesRule/composer.json @@ -25,7 +25,7 @@ "magento/module-sales-rule-sample-data": "Sample Data version:100.2.*" }, "type": "magento2-module", - "version": "101.0.5", + "version": "101.0.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Search/composer.json b/app/code/Magento/Search/composer.json index 3515ce33a4ee5..0ade5ca67b6ee 100644 --- a/app/code/Magento/Search/composer.json +++ b/app/code/Magento/Search/composer.json @@ -11,7 +11,7 @@ "magento/module-ui": "101.0.*" }, "type": "magento2-module", - "version": "100.2.6", + "version": "100.2.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Security/composer.json b/app/code/Magento/Security/composer.json index 4edfa9c55e2ee..c3e632a8216a4 100644 --- a/app/code/Magento/Security/composer.json +++ b/app/code/Magento/Security/composer.json @@ -11,7 +11,7 @@ "magento/module-customer": "101.0.*" }, "type": "magento2-module", - "version": "100.2.4", + "version": "100.2.5", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/SendFriend/composer.json b/app/code/Magento/SendFriend/composer.json index 9138f745eb266..de252db3f3969 100644 --- a/app/code/Magento/SendFriend/composer.json +++ b/app/code/Magento/SendFriend/composer.json @@ -11,7 +11,7 @@ "magento/module-authorization": "100.2.*" }, "type": "magento2-module", - "version": "100.2.3", + "version": "100.2.4", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Shipping/composer.json b/app/code/Magento/Shipping/composer.json index d887d9c984eb7..a8bb256cdd305 100644 --- a/app/code/Magento/Shipping/composer.json +++ b/app/code/Magento/Shipping/composer.json @@ -25,7 +25,7 @@ "magento/module-config": "101.0.*" }, "type": "magento2-module", - "version": "100.2.7", + "version": "100.2.8", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Signifyd/composer.json b/app/code/Magento/Signifyd/composer.json index 7140bf36518a3..62ecc442c3138 100644 --- a/app/code/Magento/Signifyd/composer.json +++ b/app/code/Magento/Signifyd/composer.json @@ -17,7 +17,7 @@ "magento/module-config": "101.0.*" }, "type": "magento2-module", - "version": "100.2.4", + "version": "100.2.5", "license": [ "proprietary" ], diff --git a/app/code/Magento/Sitemap/composer.json b/app/code/Magento/Sitemap/composer.json index 40f2c153f33be..acc5c9d8f33a0 100644 --- a/app/code/Magento/Sitemap/composer.json +++ b/app/code/Magento/Sitemap/composer.json @@ -18,7 +18,7 @@ "magento/module-config": "101.0.*" }, "type": "magento2-module", - "version": "100.2.6", + "version": "100.2.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Store/composer.json b/app/code/Magento/Store/composer.json index 8465d5118a45e..6059432d66038 100644 --- a/app/code/Magento/Store/composer.json +++ b/app/code/Magento/Store/composer.json @@ -14,7 +14,7 @@ "magento/module-deploy": "100.2.*" }, "type": "magento2-module", - "version": "100.2.6", + "version": "100.2.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Swatches/composer.json b/app/code/Magento/Swatches/composer.json index adce50b564a1d..29194ac9bef59 100644 --- a/app/code/Magento/Swatches/composer.json +++ b/app/code/Magento/Swatches/composer.json @@ -19,7 +19,7 @@ "magento/module-swatches-sample-data": "Sample Data version:100.2.*" }, "type": "magento2-module", - "version": "100.2.5", + "version": "100.2.6", "license": [ "proprietary" ], diff --git a/app/code/Magento/Tax/composer.json b/app/code/Magento/Tax/composer.json index 52f636d5db077..be194636c1ec4 100644 --- a/app/code/Magento/Tax/composer.json +++ b/app/code/Magento/Tax/composer.json @@ -22,7 +22,7 @@ "magento/module-tax-sample-data": "Sample Data version:100.2.*" }, "type": "magento2-module", - "version": "100.2.7", + "version": "100.2.8", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Theme/composer.json b/app/code/Magento/Theme/composer.json index a89fb10d9769d..e83b49bbbee03 100644 --- a/app/code/Magento/Theme/composer.json +++ b/app/code/Magento/Theme/composer.json @@ -22,7 +22,7 @@ "magento/module-directory": "100.2.*" }, "type": "magento2-module", - "version": "100.2.7", + "version": "100.2.8", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Ui/composer.json b/app/code/Magento/Ui/composer.json index effe010a79fe9..4ad83870331a4 100644 --- a/app/code/Magento/Ui/composer.json +++ b/app/code/Magento/Ui/composer.json @@ -13,7 +13,7 @@ "magento/module-config": "101.0.*" }, "type": "magento2-module", - "version": "101.0.7", + "version": "101.0.8", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Ups/composer.json b/app/code/Magento/Ups/composer.json index 9173ccbc3393b..8a124a7c10f80 100644 --- a/app/code/Magento/Ups/composer.json +++ b/app/code/Magento/Ups/composer.json @@ -16,7 +16,7 @@ "magento/module-config": "101.0.*" }, "type": "magento2-module", - "version": "100.2.5", + "version": "100.2.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/UrlRewrite/composer.json b/app/code/Magento/UrlRewrite/composer.json index 6247c47182804..82d0ce9cd8365 100644 --- a/app/code/Magento/UrlRewrite/composer.json +++ b/app/code/Magento/UrlRewrite/composer.json @@ -12,7 +12,7 @@ "magento/module-cms-url-rewrite": "100.2.*" }, "type": "magento2-module", - "version": "101.0.6", + "version": "101.0.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/User/composer.json b/app/code/Magento/User/composer.json index 431f33014d1a1..513a34639b0a5 100644 --- a/app/code/Magento/User/composer.json +++ b/app/code/Magento/User/composer.json @@ -12,7 +12,7 @@ "magento/framework": "101.0.*" }, "type": "magento2-module", - "version": "101.0.5", + "version": "101.0.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Usps/composer.json b/app/code/Magento/Usps/composer.json index 8cbc56b96943a..93456fb9a0a94 100644 --- a/app/code/Magento/Usps/composer.json +++ b/app/code/Magento/Usps/composer.json @@ -15,7 +15,7 @@ "lib-libxml": "*" }, "type": "magento2-module", - "version": "100.2.5", + "version": "100.2.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Webapi/composer.json b/app/code/Magento/Webapi/composer.json index 51c0302b5feab..d13034eddd5ac 100644 --- a/app/code/Magento/Webapi/composer.json +++ b/app/code/Magento/Webapi/composer.json @@ -14,7 +14,7 @@ "magento/module-customer": "101.0.*" }, "type": "magento2-module", - "version": "100.2.5", + "version": "100.2.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Weee/composer.json b/app/code/Magento/Weee/composer.json index cdcf68fb5422a..efb6bddb9289c 100644 --- a/app/code/Magento/Weee/composer.json +++ b/app/code/Magento/Weee/composer.json @@ -21,7 +21,7 @@ "magento/module-bundle": "100.2.*" }, "type": "magento2-module", - "version": "100.2.4", + "version": "100.2.5", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Widget/composer.json b/app/code/Magento/Widget/composer.json index eb3e0af2b2bc6..fc63ae00bdb7a 100644 --- a/app/code/Magento/Widget/composer.json +++ b/app/code/Magento/Widget/composer.json @@ -16,7 +16,7 @@ "magento/module-widget-sample-data": "Sample Data version:100.2.*" }, "type": "magento2-module", - "version": "101.0.5", + "version": "101.0.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Wishlist/composer.json b/app/code/Magento/Wishlist/composer.json index 74c87c3d32574..e99e0488284b9 100644 --- a/app/code/Magento/Wishlist/composer.json +++ b/app/code/Magento/Wishlist/composer.json @@ -24,7 +24,7 @@ "magento/module-wishlist-sample-data": "Sample Data version:100.2.*" }, "type": "magento2-module", - "version": "101.0.5", + "version": "101.0.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/design/adminhtml/Magento/backend/composer.json b/app/design/adminhtml/Magento/backend/composer.json index e15cd2e5f4271..fb11aa2dc159f 100644 --- a/app/design/adminhtml/Magento/backend/composer.json +++ b/app/design/adminhtml/Magento/backend/composer.json @@ -6,7 +6,7 @@ "magento/framework": "101.0.*" }, "type": "magento2-theme", - "version": "100.2.5", + "version": "100.2.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/design/frontend/Magento/blank/composer.json b/app/design/frontend/Magento/blank/composer.json index 2f40a5918c8ca..f691cc8e9eb5f 100644 --- a/app/design/frontend/Magento/blank/composer.json +++ b/app/design/frontend/Magento/blank/composer.json @@ -6,7 +6,7 @@ "magento/framework": "101.0.*" }, "type": "magento2-theme", - "version": "100.2.5", + "version": "100.2.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/design/frontend/Magento/luma/composer.json b/app/design/frontend/Magento/luma/composer.json index 0a82ce6fdea2e..f5ba64ad9ed57 100644 --- a/app/design/frontend/Magento/luma/composer.json +++ b/app/design/frontend/Magento/luma/composer.json @@ -7,7 +7,7 @@ "magento/framework": "101.0.*" }, "type": "magento2-theme", - "version": "100.2.6", + "version": "100.2.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/composer.json b/composer.json index d04bf269d4485..a559faaac5c7a 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "magento/magento2ce", "description": "Magento 2 (Open Source)", "type": "project", - "version": "2.2.8-dev", + "version": "2.2.8", "license": [ "OSL-3.0", "AFL-3.0" @@ -88,124 +88,124 @@ }, "replace": { "magento/module-marketplace": "100.2.4", - "magento/module-admin-notification": "100.2.5", + "magento/module-admin-notification": "100.2.6", "magento/module-advanced-pricing-import-export": "100.2.5", - "magento/module-analytics": "100.2.4", + "magento/module-analytics": "100.2.5", "magento/module-authorization": "100.2.3", - "magento/module-authorizenet": "100.2.3", - "magento/module-backend": "100.2.7", - "magento/module-backup": "100.2.6", - "magento/module-braintree": "100.2.7", - "magento/module-bundle": "100.2.6", - "magento/module-bundle-import-export": "100.2.4", - "magento/module-cache-invalidate": "100.2.3", - "magento/module-captcha": "100.2.4", - "magento/module-catalog": "102.0.7", + "magento/module-authorizenet": "100.2.4", + "magento/module-backend": "100.2.8", + "magento/module-backup": "100.2.7", + "magento/module-braintree": "100.2.8", + "magento/module-bundle": "100.2.7", + "magento/module-bundle-import-export": "100.2.5", + "magento/module-cache-invalidate": "100.2.4", + "magento/module-captcha": "100.2.5", + "magento/module-catalog": "102.0.8", "magento/module-catalog-analytics": "100.2.3", - "magento/module-catalog-import-export": "100.2.6", - "magento/module-catalog-inventory": "100.2.6", - "magento/module-catalog-rule": "101.0.6", + "magento/module-catalog-import-export": "100.2.7", + "magento/module-catalog-inventory": "100.2.7", + "magento/module-catalog-rule": "101.0.7", "magento/module-catalog-rule-configurable": "100.2.3", - "magento/module-catalog-search": "100.2.6", - "magento/module-catalog-url-rewrite": "100.2.6", - "magento/module-catalog-widget": "100.2.4", - "magento/module-checkout": "100.2.7", - "magento/module-checkout-agreements": "100.2.3", - "magento/module-cms": "102.0.7", + "magento/module-catalog-search": "100.2.7", + "magento/module-catalog-url-rewrite": "100.2.7", + "magento/module-catalog-widget": "100.2.5", + "magento/module-checkout": "100.2.8", + "magento/module-checkout-agreements": "100.2.4", + "magento/module-cms": "102.0.8", "magento/module-cms-url-rewrite": "100.2.3", - "magento/module-config": "101.0.7", - "magento/module-configurable-import-export": "100.2.4", - "magento/module-configurable-product": "100.2.7", + "magento/module-config": "101.0.8", + "magento/module-configurable-import-export": "100.2.5", + "magento/module-configurable-product": "100.2.8", "magento/module-configurable-product-sales": "100.2.4", - "magento/module-contact": "100.2.4", + "magento/module-contact": "100.2.5", "magento/module-cookie": "100.2.3", - "magento/module-cron": "100.2.5", - "magento/module-currency-symbol": "100.2.3", - "magento/module-customer": "101.0.7", + "magento/module-cron": "100.2.6", + "magento/module-currency-symbol": "100.2.4", + "magento/module-customer": "101.0.8", "magento/module-customer-analytics": "100.2.3", - "magento/module-customer-import-export": "100.2.5", - "magento/module-deploy": "100.2.6", - "magento/module-developer": "100.2.5", + "magento/module-customer-import-export": "100.2.6", + "magento/module-deploy": "100.2.7", + "magento/module-developer": "100.2.6", "magento/module-dhl": "100.2.4", - "magento/module-directory": "100.2.6", - "magento/module-downloadable": "100.2.6", + "magento/module-directory": "100.2.7", + "magento/module-downloadable": "100.2.7", "magento/module-downloadable-import-export": "100.2.3", - "magento/module-eav": "101.0.6", - "magento/module-email": "100.2.5", + "magento/module-eav": "101.0.7", + "magento/module-email": "100.2.6", "magento/module-encryption-key": "100.2.3", "magento/module-fedex": "100.2.4", - "magento/module-gift-message": "100.2.3", + "magento/module-gift-message": "100.2.4", "magento/module-google-adwords": "100.2.3", - "magento/module-google-analytics": "100.2.5", + "magento/module-google-analytics": "100.2.6", "magento/module-google-optimizer": "100.2.4", - "magento/module-grouped-import-export": "100.2.3", - "magento/module-grouped-product": "100.2.5", - "magento/module-import-export": "100.2.7", - "magento/module-indexer": "100.2.5", - "magento/module-instant-purchase": "100.2.3", - "magento/module-integration": "100.2.5", - "magento/module-layered-navigation": "100.2.4", + "magento/module-grouped-import-export": "100.2.4", + "magento/module-grouped-product": "100.2.6", + "magento/module-import-export": "100.2.8", + "magento/module-indexer": "100.2.6", + "magento/module-instant-purchase": "100.2.4", + "magento/module-integration": "100.2.6", + "magento/module-layered-navigation": "100.2.5", "magento/module-media-storage": "100.2.3", - "magento/module-msrp": "100.2.3", - "magento/module-multishipping": "100.2.4", - "magento/module-new-relic-reporting": "100.2.5", - "magento/module-newsletter": "100.2.6", + "magento/module-msrp": "100.2.4", + "magento/module-multishipping": "100.2.5", + "magento/module-new-relic-reporting": "100.2.6", + "magento/module-newsletter": "100.2.7", "magento/module-offline-payments": "100.2.3", - "magento/module-offline-shipping": "100.2.5", - "magento/module-page-cache": "100.2.4", - "magento/module-payment": "100.2.5", - "magento/module-paypal": "100.2.5", - "magento/module-persistent": "100.2.3", + "magento/module-offline-shipping": "100.2.6", + "magento/module-page-cache": "100.2.5", + "magento/module-payment": "100.2.6", + "magento/module-paypal": "100.2.6", + "magento/module-persistent": "100.2.4", "magento/module-product-alert": "100.2.4", - "magento/module-product-video": "100.2.5", - "magento/module-quote": "101.0.6", + "magento/module-product-video": "100.2.6", + "magento/module-quote": "101.0.7", "magento/module-quote-analytics": "100.2.3", "magento/module-release-notification": "100.2.4", - "magento/module-reports": "100.2.7", + "magento/module-reports": "100.2.8", "magento/module-require-js": "100.2.4", - "magento/module-review": "100.2.7", + "magento/module-review": "100.2.8", "magento/module-review-analytics": "100.2.3", "magento/module-robots": "100.2.4", "magento/module-rss": "100.2.3", - "magento/module-rule": "100.2.4", - "magento/module-sales": "101.0.6", + "magento/module-rule": "100.2.5", + "magento/module-sales": "101.0.7", "magento/module-sales-analytics": "100.2.3", "magento/module-sales-inventory": "100.2.3", - "magento/module-sales-rule": "101.0.5", + "magento/module-sales-rule": "101.0.6", "magento/module-sales-sequence": "100.2.3", "magento/module-sample-data": "100.2.5", - "magento/module-search": "100.2.6", - "magento/module-security": "100.2.4", - "magento/module-send-friend": "100.2.3", - "magento/module-shipping": "100.2.7", - "magento/module-signifyd": "100.2.4", - "magento/module-sitemap": "100.2.6", - "magento/module-store": "100.2.6", + "magento/module-search": "100.2.7", + "magento/module-security": "100.2.5", + "magento/module-send-friend": "100.2.4", + "magento/module-shipping": "100.2.8", + "magento/module-signifyd": "100.2.5", + "magento/module-sitemap": "100.2.7", + "magento/module-store": "100.2.7", "magento/module-swagger-webapi": "100.2.1", "magento/module-swagger": "100.2.5", - "magento/module-swatches": "100.2.5", + "magento/module-swatches": "100.2.6", "magento/module-swatches-layered-navigation": "100.2.3", - "magento/module-tax": "100.2.7", + "magento/module-tax": "100.2.8", "magento/module-tax-import-export": "100.2.3", - "magento/module-theme": "100.2.7", + "magento/module-theme": "100.2.8", "magento/module-translation": "100.2.6", - "magento/module-ui": "101.0.7", - "magento/module-ups": "100.2.5", - "magento/module-url-rewrite": "101.0.6", - "magento/module-user": "101.0.5", - "magento/module-usps": "100.2.5", + "magento/module-ui": "101.0.8", + "magento/module-ups": "100.2.6", + "magento/module-url-rewrite": "101.0.7", + "magento/module-user": "101.0.6", + "magento/module-usps": "100.2.6", "magento/module-variable": "100.2.6", "magento/module-vault": "101.0.5", "magento/module-version": "100.2.3", - "magento/module-webapi": "100.2.5", + "magento/module-webapi": "100.2.6", "magento/module-webapi-security": "100.2.4", - "magento/module-weee": "100.2.4", - "magento/module-widget": "101.0.5", - "magento/module-wishlist": "101.0.5", + "magento/module-weee": "100.2.5", + "magento/module-widget": "101.0.6", + "magento/module-wishlist": "101.0.6", "magento/module-wishlist-analytics": "100.2.3", - "magento/theme-adminhtml-backend": "100.2.5", - "magento/theme-frontend-blank": "100.2.5", - "magento/theme-frontend-luma": "100.2.6", + "magento/theme-adminhtml-backend": "100.2.6", + "magento/theme-frontend-blank": "100.2.6", + "magento/theme-frontend-luma": "100.2.7", "magento/language-de_de": "100.2.0", "magento/language-en_us": "100.2.0", "magento/language-es_es": "100.2.0", @@ -213,7 +213,7 @@ "magento/language-nl_nl": "100.2.0", "magento/language-pt_br": "100.2.0", "magento/language-zh_hans_cn": "100.2.0", - "magento/framework": "101.0.7", + "magento/framework": "101.0.8", "trentrichardson/jquery-timepicker-addon": "1.4.3", "components/jquery": "1.11.0", "blueimp/jquery-file-upload": "5.6.14", diff --git a/composer.lock b/composer.lock index 384ff14618a80..b138c355db957 100644 --- a/composer.lock +++ b/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "d57f148fd874b8685bf73bbf0a0ea75a", + "content-hash": "d5391224a802468fe867679c3b597d9f", "packages": [ { "name": "braintree/braintree_php", @@ -204,16 +204,16 @@ }, { "name": "composer/ca-bundle", - "version": "1.1.2", + "version": "1.1.4", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "46afded9720f40b9dc63542af4e3e43a1177acb0" + "reference": "558f321c52faeb4828c03e7dc0cfe39a09e09a2d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/46afded9720f40b9dc63542af4e3e43a1177acb0", - "reference": "46afded9720f40b9dc63542af4e3e43a1177acb0", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/558f321c52faeb4828c03e7dc0cfe39a09e09a2d", + "reference": "558f321c52faeb4828c03e7dc0cfe39a09e09a2d", "shasum": "" }, "require": { @@ -256,7 +256,7 @@ "ssl", "tls" ], - "time": "2018-08-08T08:57:40+00:00" + "time": "2019-01-28T09:30:10+00:00" }, { "name": "composer/composer", @@ -399,16 +399,16 @@ }, { "name": "composer/spdx-licenses", - "version": "1.4.0", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/composer/spdx-licenses.git", - "reference": "cb17687e9f936acd7e7245ad3890f953770dec1b" + "reference": "7a9556b22bd9d4df7cad89876b00af58ef20d3a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/cb17687e9f936acd7e7245ad3890f953770dec1b", - "reference": "cb17687e9f936acd7e7245ad3890f953770dec1b", + "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/7a9556b22bd9d4df7cad89876b00af58ef20d3a2", + "reference": "7a9556b22bd9d4df7cad89876b00af58ef20d3a2", "shasum": "" }, "require": { @@ -456,7 +456,7 @@ "spdx", "validator" ], - "time": "2018-04-30T10:33:04+00:00" + "time": "2018-11-01T09:45:54+00:00" }, { "name": "container-interop/container-interop", @@ -491,23 +491,23 @@ }, { "name": "justinrainbow/json-schema", - "version": "5.2.7", + "version": "5.2.8", "source": { "type": "git", "url": "https://github.com/justinrainbow/json-schema.git", - "reference": "8560d4314577199ba51bf2032f02cd1315587c23" + "reference": "dcb6e1006bb5fd1e392b4daa68932880f37550d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/8560d4314577199ba51bf2032f02cd1315587c23", - "reference": "8560d4314577199ba51bf2032f02cd1315587c23", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/dcb6e1006bb5fd1e392b4daa68932880f37550d4", + "reference": "dcb6e1006bb5fd1e392b4daa68932880f37550d4", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.1", + "friendsofphp/php-cs-fixer": "~2.2.20", "json-schema/json-schema-test-suite": "1.2.0", "phpunit/phpunit": "^4.8.35" }, @@ -553,7 +553,7 @@ "json", "schema" ], - "time": "2018-02-14T22:26:30+00:00" + "time": "2019-01-14T23:55:14+00:00" }, { "name": "magento/composer", @@ -719,16 +719,16 @@ }, { "name": "monolog/monolog", - "version": "1.23.0", + "version": "1.24.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4" + "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/fd8c787753b3a2ad11bc60c063cff1358a32a3b4", - "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266", + "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266", "shasum": "" }, "require": { @@ -793,7 +793,7 @@ "logging", "psr-3" ], - "time": "2017-06-19T01:22:40+00:00" + "time": "2018-11-05T09:00:11+00:00" }, { "name": "oyejorge/less.php", @@ -859,16 +859,16 @@ }, { "name": "paragonie/random_compat", - "version": "v2.0.17", + "version": "v2.0.18", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", - "reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d" + "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/29af24f25bab834fcbb38ad2a69fa93b867e070d", - "reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/0a58ef6e3146256cc3dc7cc393927bcc7d1b72db", + "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db", "shasum": "" }, "require": { @@ -904,28 +904,33 @@ "pseudorandom", "random" ], - "time": "2018-07-04T16:31:37+00:00" + "time": "2019-01-03T20:59:08+00:00" }, { "name": "pelago/emogrifier", - "version": "v2.0.0", + "version": "v2.1.1", "source": { "type": "git", "url": "https://github.com/MyIntervals/emogrifier.git", - "reference": "8babf8ddbf348f26b29674e2f84db66ff7e3d95e" + "reference": "8ee7fb5ad772915451ed3415c1992bd3697d4983" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/MyIntervals/emogrifier/zipball/8babf8ddbf348f26b29674e2f84db66ff7e3d95e", - "reference": "8babf8ddbf348f26b29674e2f84db66ff7e3d95e", + "url": "https://api.github.com/repos/MyIntervals/emogrifier/zipball/8ee7fb5ad772915451ed3415c1992bd3697d4983", + "reference": "8ee7fb5ad772915451ed3415c1992bd3697d4983", "shasum": "" }, "require": { - "php": "^5.5.0 || ~7.0.0 || ~7.1.0 || ~7.2.0" + "ext-dom": "*", + "ext-libxml": "*", + "php": "^5.5.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0", + "symfony/css-selector": "^3.4.0 || ^4.0.0" }, "require-dev": { + "friendsofphp/php-cs-fixer": "^2.2.0", + "phpmd/phpmd": "^2.6.0", "phpunit/phpunit": "^4.8.0", - "squizlabs/php_codesniffer": "^3.1.0" + "squizlabs/php_codesniffer": "^3.3.2" }, "type": "library", "extra": { @@ -935,7 +940,7 @@ }, "autoload": { "psr-4": { - "Pelago\\": "Classes/" + "Pelago\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -953,10 +958,6 @@ { "name": "Jaime Prado" }, - { - "name": "Roman Ožana", - "email": "ozana@omdesign.cz" - }, { "name": "Oliver Klee", "email": "github@oliverklee.de" @@ -964,6 +965,10 @@ { "name": "Zoli Szabó", "email": "zoli.szabo+github@gmail.com" + }, + { + "name": "Jake Hotson", + "email": "jake@qzdesign.co.uk" } ], "description": "Converts CSS styles into inline style attributes in your HTML code", @@ -973,20 +978,20 @@ "email", "pre-processing" ], - "time": "2018-01-05T23:30:21+00:00" + "time": "2018-12-10T10:36:30+00:00" }, { "name": "phpseclib/phpseclib", - "version": "2.0.11", + "version": "2.0.15", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "7053f06f91b3de78e143d430e55a8f7889efc08b" + "reference": "11cf67cf78dc4acb18dc9149a57be4aee5036ce0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/7053f06f91b3de78e143d430e55a8f7889efc08b", - "reference": "7053f06f91b3de78e143d430e55a8f7889efc08b", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/11cf67cf78dc4acb18dc9149a57be4aee5036ce0", + "reference": "11cf67cf78dc4acb18dc9149a57be4aee5036ce0", "shasum": "" }, "require": { @@ -1065,7 +1070,7 @@ "x.509", "x509" ], - "time": "2018-04-15T16:55:05+00:00" + "time": "2019-03-10T16:53:45+00:00" }, { "name": "psr/container", @@ -1168,16 +1173,16 @@ }, { "name": "psr/log", - "version": "1.0.2", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", - "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", + "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", "shasum": "" }, "require": { @@ -1211,7 +1216,7 @@ "psr", "psr-3" ], - "time": "2016-10-10T12:19:37+00:00" + "time": "2018-11-20T15:27:04+00:00" }, { "name": "ramsey/uuid", @@ -1436,16 +1441,16 @@ }, { "name": "symfony/console", - "version": "v2.8.46", + "version": "v2.8.49", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "aca0dcc0c75496e17e2aa0303bb9c8e6d79ed789" + "reference": "cbcf4b5e233af15cd2bbd50dee1ccc9b7927dc12" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/aca0dcc0c75496e17e2aa0303bb9c8e6d79ed789", - "reference": "aca0dcc0c75496e17e2aa0303bb9c8e6d79ed789", + "url": "https://api.github.com/repos/symfony/console/zipball/cbcf4b5e233af15cd2bbd50dee1ccc9b7927dc12", + "reference": "cbcf4b5e233af15cd2bbd50dee1ccc9b7927dc12", "shasum": "" }, "require": { @@ -1493,7 +1498,60 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-09-30T03:33:07+00:00" + "time": "2018-11-20T15:55:20+00:00" + }, + { + "name": "symfony/css-selector", + "version": "v3.4.23", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "8ca29297c29b64fb3a1a135e71cb25f67f9fdccf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/8ca29297c29b64fb3a1a135e71cb25f67f9fdccf", + "reference": "8ca29297c29b64fb3a1a135e71cb25f67f9fdccf", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony CssSelector Component", + "homepage": "https://symfony.com", + "time": "2019-01-16T09:39:14+00:00" }, { "name": "symfony/debug", @@ -1554,16 +1612,16 @@ }, { "name": "symfony/event-dispatcher", - "version": "v2.8.46", + "version": "v2.8.49", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "84ae343f39947aa084426ed1138bb96bf94d1f12" + "reference": "a77e974a5fecb4398833b0709210e3d5e334ffb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/84ae343f39947aa084426ed1138bb96bf94d1f12", - "reference": "84ae343f39947aa084426ed1138bb96bf94d1f12", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a77e974a5fecb4398833b0709210e3d5e334ffb0", + "reference": "a77e974a5fecb4398833b0709210e3d5e334ffb0", "shasum": "" }, "require": { @@ -1610,20 +1668,20 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2018-07-26T09:03:18+00:00" + "time": "2018-11-21T14:20:20+00:00" }, { "name": "symfony/filesystem", - "version": "v3.4.17", + "version": "v3.4.23", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "d69930fc337d767607267d57c20a7403d0a822a4" + "reference": "acf99758b1df8e9295e6b85aa69f294565c9fedb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/d69930fc337d767607267d57c20a7403d0a822a4", - "reference": "d69930fc337d767607267d57c20a7403d0a822a4", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/acf99758b1df8e9295e6b85aa69f294565c9fedb", + "reference": "acf99758b1df8e9295e6b85aa69f294565c9fedb", "shasum": "" }, "require": { @@ -1660,20 +1718,20 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2018-10-02T12:28:39+00:00" + "time": "2019-02-04T21:34:32+00:00" }, { "name": "symfony/finder", - "version": "v3.4.17", + "version": "v3.4.23", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "54ba444dddc5bd5708a34bd095ea67c6eb54644d" + "reference": "fcdde4aa38f48190ce70d782c166f23930084f9b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/54ba444dddc5bd5708a34bd095ea67c6eb54644d", - "reference": "54ba444dddc5bd5708a34bd095ea67c6eb54644d", + "url": "https://api.github.com/repos/symfony/finder/zipball/fcdde4aa38f48190ce70d782c166f23930084f9b", + "reference": "fcdde4aa38f48190ce70d782c166f23930084f9b", "shasum": "" }, "require": { @@ -1709,11 +1767,11 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2018-10-03T08:46:40+00:00" + "time": "2019-02-22T14:44:53+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.9.0", + "version": "v1.10.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -1771,16 +1829,16 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.9.0", + "version": "v1.10.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8" + "reference": "c79c051f5b3a46be09205c73b80b346e4153e494" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d0cd638f4634c16d8df4508e847f14e9e43168b8", - "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494", + "reference": "c79c051f5b3a46be09205c73b80b346e4153e494", "shasum": "" }, "require": { @@ -1826,20 +1884,20 @@ "portable", "shim" ], - "time": "2018-08-06T14:22:27+00:00" + "time": "2018-09-21T13:07:52+00:00" }, { "name": "symfony/process", - "version": "v2.8.46", + "version": "v2.8.49", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "f09e21b7c5aba06c47bbfad9cbcf13ac7f0db0a6" + "reference": "c3591a09c78639822b0b290d44edb69bf9f05dc8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/f09e21b7c5aba06c47bbfad9cbcf13ac7f0db0a6", - "reference": "f09e21b7c5aba06c47bbfad9cbcf13ac7f0db0a6", + "url": "https://api.github.com/repos/symfony/process/zipball/c3591a09c78639822b0b290d44edb69bf9f05dc8", + "reference": "c3591a09c78639822b0b290d44edb69bf9f05dc8", "shasum": "" }, "require": { @@ -1875,7 +1933,7 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2018-09-06T17:11:15+00:00" + "time": "2018-11-11T11:18:13+00:00" }, { "name": "tedivm/jshrink", @@ -2294,16 +2352,16 @@ }, { "name": "zendframework/zend-db", - "version": "2.9.3", + "version": "2.10.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-db.git", - "reference": "5b4f2c42f94c9f7f4b2f456a0ebe459fab12b3d9" + "reference": "77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-db/zipball/5b4f2c42f94c9f7f4b2f456a0ebe459fab12b3d9", - "reference": "5b4f2c42f94c9f7f4b2f456a0ebe459fab12b3d9", + "url": "https://api.github.com/repos/zendframework/zend-db/zipball/77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", + "reference": "77022f06f6ffd384fa86d22ab8d8bbdb925a1e8e", "shasum": "" }, "require": { @@ -2314,7 +2372,7 @@ "phpunit/phpunit": "^5.7.25 || ^6.4.4", "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-hydrator": "^1.1 || ^2.1", + "zendframework/zend-hydrator": "^1.1 || ^2.1 || ^3.0", "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { @@ -2348,7 +2406,7 @@ "db", "zf" ], - "time": "2018-04-09T13:21:36+00:00" + "time": "2019-02-25T11:37:45+00:00" }, { "name": "zendframework/zend-di", @@ -2555,16 +2613,16 @@ }, { "name": "zendframework/zend-filter", - "version": "2.8.0", + "version": "2.9.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-filter.git", - "reference": "7b997dbe79459f1652deccc8786d7407fb66caa9" + "reference": "1c3e6d02f9cd5f6c929c9859498f5efbe216e86f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-filter/zipball/7b997dbe79459f1652deccc8786d7407fb66caa9", - "reference": "7b997dbe79459f1652deccc8786d7407fb66caa9", + "url": "https://api.github.com/repos/zendframework/zend-filter/zipball/1c3e6d02f9cd5f6c929c9859498f5efbe216e86f", + "reference": "1c3e6d02f9cd5f6c929c9859498f5efbe216e86f", "shasum": "" }, "require": { @@ -2577,12 +2635,14 @@ "require-dev": { "pear/archive_tar": "^1.4.3", "phpunit/phpunit": "^5.7.23 || ^6.4.3", + "psr/http-factory": "^1.0", "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-crypt": "^3.2.1", "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", "zendframework/zend-uri": "^2.6" }, "suggest": { + "psr/http-factory-implementation": "psr/http-factory-implementation, for creating file upload instances when consuming PSR-7 in file upload filters", "zendframework/zend-crypt": "Zend\\Crypt component, for encryption filters", "zendframework/zend-i18n": "Zend\\I18n component for filters depending on i18n functionality", "zendframework/zend-servicemanager": "Zend\\ServiceManager component, for using the filter chain functionality", @@ -2591,8 +2651,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8.x-dev", - "dev-develop": "2.9.x-dev" + "dev-master": "2.9.x-dev", + "dev-develop": "2.10.x-dev" }, "zf": { "component": "Zend\\Filter", @@ -2614,25 +2674,25 @@ "filter", "zf" ], - "time": "2018-04-11T16:20:04+00:00" + "time": "2018-12-17T16:00:04+00:00" }, { "name": "zendframework/zend-form", - "version": "2.12.0", + "version": "2.13.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-form.git", - "reference": "565fb4f4bb3e0dbeea0173c923c4a8be77de9441" + "reference": "c713a12ccbd43148b71c9339e171ca11e3f8a1da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-form/zipball/565fb4f4bb3e0dbeea0173c923c4a8be77de9441", - "reference": "565fb4f4bb3e0dbeea0173c923c4a8be77de9441", + "url": "https://api.github.com/repos/zendframework/zend-form/zipball/c713a12ccbd43148b71c9339e171ca11e3f8a1da", + "reference": "c713a12ccbd43148b71c9339e171ca11e3f8a1da", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-hydrator": "^1.1 || ^2.1", + "zendframework/zend-hydrator": "^1.1 || ^2.1 || ^3.0", "zendframework/zend-inputfilter": "^2.8", "zendframework/zend-stdlib": "^2.7 || ^3.0" }, @@ -2666,8 +2726,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.12.x-dev", - "dev-develop": "2.13.x-dev" + "dev-master": "2.13.x-dev", + "dev-develop": "2.14.x-dev" }, "zf": { "component": "Zend\\Form", @@ -2692,20 +2752,20 @@ "form", "zf" ], - "time": "2018-05-16T18:49:44+00:00" + "time": "2018-12-11T22:51:29+00:00" }, { "name": "zendframework/zend-http", - "version": "2.8.2", + "version": "2.8.4", "source": { "type": "git", "url": "https://github.com/zendframework/zend-http.git", - "reference": "2c8aed3d25522618573194e7cc51351f8cd4a45b" + "reference": "d160aedc096be230af0fe9c31151b2b33ad4e807" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-http/zipball/2c8aed3d25522618573194e7cc51351f8cd4a45b", - "reference": "2c8aed3d25522618573194e7cc51351f8cd4a45b", + "url": "https://api.github.com/repos/zendframework/zend-http/zipball/d160aedc096be230af0fe9c31151b2b33ad4e807", + "reference": "d160aedc096be230af0fe9c31151b2b33ad4e807", "shasum": "" }, "require": { @@ -2747,7 +2807,7 @@ "zend", "zf" ], - "time": "2018-08-13T18:47:03+00:00" + "time": "2019-02-07T17:47:08+00:00" }, { "name": "zendframework/zend-hydrator", @@ -2877,34 +2937,38 @@ }, { "name": "zendframework/zend-inputfilter", - "version": "2.8.2", + "version": "2.10.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-inputfilter.git", - "reference": "3f02179e014d9ef0faccda2ad6c65d38adc338d8" + "reference": "4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-inputfilter/zipball/3f02179e014d9ef0faccda2ad6c65d38adc338d8", - "reference": "3f02179e014d9ef0faccda2ad6c65d38adc338d8", + "url": "https://api.github.com/repos/zendframework/zend-inputfilter/zipball/4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c", + "reference": "4f52b71ec9cef3a06e3bba8f5c2124e94055ec0c", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-filter": "^2.6", + "zendframework/zend-filter": "^2.9.1", "zendframework/zend-servicemanager": "^2.7.10 || ^3.3.1", "zendframework/zend-stdlib": "^2.7 || ^3.0", - "zendframework/zend-validator": "^2.10.1" + "zendframework/zend-validator": "^2.11" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", + "psr/http-message": "^1.0", "zendframework/zend-coding-standard": "~1.0.0" }, + "suggest": { + "psr/http-message-implementation": "PSR-7 is required if you wish to validate PSR-7 UploadedFileInterface payloads" + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8.x-dev", - "dev-develop": "2.9.x-dev" + "dev-master": "2.10.x-dev", + "dev-develop": "2.11.x-dev" }, "zf": { "component": "Zend\\InputFilter", @@ -2926,7 +2990,7 @@ "inputfilter", "zf" ], - "time": "2018-05-14T17:38:03+00:00" + "time": "2019-01-30T16:58:51+00:00" }, { "name": "zendframework/zend-json", @@ -3163,16 +3227,16 @@ }, { "name": "zendframework/zend-math", - "version": "2.7.0", + "version": "2.7.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-math.git", - "reference": "f4358090d5d23973121f1ed0b376184b66d9edec" + "reference": "1abce074004dacac1a32cd54de94ad47ef960d38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-math/zipball/f4358090d5d23973121f1ed0b376184b66d9edec", - "reference": "f4358090d5d23973121f1ed0b376184b66d9edec", + "url": "https://api.github.com/repos/zendframework/zend-math/zipball/1abce074004dacac1a32cd54de94ad47ef960d38", + "reference": "1abce074004dacac1a32cd54de94ad47ef960d38", "shasum": "" }, "require": { @@ -3209,7 +3273,7 @@ "math", "zf2" ], - "time": "2016-04-07T16:29:53+00:00" + "time": "2018-12-04T15:34:17+00:00" }, { "name": "zendframework/zend-mime", @@ -3851,16 +3915,16 @@ }, { "name": "zendframework/zend-uri", - "version": "2.6.1", + "version": "2.7.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-uri.git", - "reference": "3b6463645c6766f78ce537c70cb4fdabee1e725f" + "reference": "b2785cd38fe379a784645449db86f21b7739b1ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-uri/zipball/3b6463645c6766f78ce537c70cb4fdabee1e725f", - "reference": "3b6463645c6766f78ce537c70cb4fdabee1e725f", + "url": "https://api.github.com/repos/zendframework/zend-uri/zipball/b2785cd38fe379a784645449db86f21b7739b1ee", + "reference": "b2785cd38fe379a784645449db86f21b7739b1ee", "shasum": "" }, "require": { @@ -3875,8 +3939,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6.x-dev", - "dev-develop": "2.7.x-dev" + "dev-master": "2.7.x-dev", + "dev-develop": "2.8.x-dev" } }, "autoload": { @@ -3894,20 +3958,20 @@ "uri", "zf" ], - "time": "2018-04-30T13:40:08+00:00" + "time": "2019-02-27T21:39:04+00:00" }, { "name": "zendframework/zend-validator", - "version": "2.10.2", + "version": "2.11.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-validator.git", - "reference": "38109ed7d8e46cfa71bccbe7e6ca80cdd035f8c9" + "reference": "3c28dfe4e5951ba38059cea895244d9d206190b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/38109ed7d8e46cfa71bccbe7e6ca80cdd035f8c9", - "reference": "38109ed7d8e46cfa71bccbe7e6ca80cdd035f8c9", + "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/3c28dfe4e5951ba38059cea895244d9d206190b3", + "reference": "3c28dfe4e5951ba38059cea895244d9d206190b3", "shasum": "" }, "require": { @@ -3917,6 +3981,7 @@ }, "require-dev": { "phpunit/phpunit": "^6.0.8 || ^5.7.15", + "psr/http-message": "^1.0", "zendframework/zend-cache": "^2.6.1", "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-config": "^2.6", @@ -3930,6 +3995,7 @@ "zendframework/zend-uri": "^2.5" }, "suggest": { + "psr/http-message": "psr/http-message, required when validating PSR-7 UploadedFileInterface instances via the Upload and UploadFile validators", "zendframework/zend-db": "Zend\\Db component, required by the (No)RecordExists validator", "zendframework/zend-filter": "Zend\\Filter component, required by the Digits validator", "zendframework/zend-i18n": "Zend\\I18n component to allow translation of validation error messages", @@ -3942,8 +4008,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.10.x-dev", - "dev-develop": "2.11.x-dev" + "dev-master": "2.11.x-dev", + "dev-develop": "2.12.x-dev" }, "zf": { "component": "Zend\\Validator", @@ -3965,25 +4031,26 @@ "validator", "zf2" ], - "time": "2018-02-01T17:05:33+00:00" + "time": "2019-01-29T22:26:39+00:00" }, { "name": "zendframework/zend-view", - "version": "2.10.0", + "version": "2.11.2", "source": { "type": "git", "url": "https://github.com/zendframework/zend-view.git", - "reference": "4478cc5dd960e2339d88b363ef99fa278700e80e" + "reference": "4f5cb653ed4c64bb8d9bf05b294300feb00c67f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-view/zipball/4478cc5dd960e2339d88b363ef99fa278700e80e", - "reference": "4478cc5dd960e2339d88b363ef99fa278700e80e", + "url": "https://api.github.com/repos/zendframework/zend-view/zipball/4f5cb653ed4c64bb8d9bf05b294300feb00c67f2", + "reference": "4f5cb653ed4c64bb8d9bf05b294300feb00c67f2", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", + "zendframework/zend-json": "^2.6.1 || ^3.0", "zendframework/zend-loader": "^2.5", "zendframework/zend-stdlib": "^2.7 || ^3.0" }, @@ -3999,10 +4066,9 @@ "zendframework/zend-filter": "^2.6.1", "zendframework/zend-http": "^2.5.4", "zendframework/zend-i18n": "^2.6", - "zendframework/zend-json": "^2.6.1", "zendframework/zend-log": "^2.7", "zendframework/zend-modulemanager": "^2.7.1", - "zendframework/zend-mvc": "^2.7 || ^3.0", + "zendframework/zend-mvc": "^2.7.14 || ^3.0", "zendframework/zend-navigation": "^2.5", "zendframework/zend-paginator": "^2.5", "zendframework/zend-permissions-acl": "^2.6", @@ -4019,8 +4085,8 @@ "zendframework/zend-filter": "Zend\\Filter component", "zendframework/zend-http": "Zend\\Http component", "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-json": "Zend\\Json component", "zendframework/zend-mvc": "Zend\\Mvc component", + "zendframework/zend-mvc-plugin-flashmessenger": "zend-mvc-plugin-flashmessenger component, if you want to use the FlashMessenger view helper with zend-mvc versions 3 and up", "zendframework/zend-navigation": "Zend\\Navigation component", "zendframework/zend-paginator": "Zend\\Paginator component", "zendframework/zend-permissions-acl": "Zend\\Permissions\\Acl component", @@ -4033,8 +4099,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.10.x-dev", - "dev-develop": "2.11.x-dev" + "dev-master": "2.11.x-dev", + "dev-develop": "2.12.x-dev" } }, "autoload": { @@ -4052,7 +4118,7 @@ "view", "zf2" ], - "time": "2018-01-17T22:21:50+00:00" + "time": "2019-02-19T17:40:15+00:00" } ], "packages-dev": [ @@ -4161,16 +4227,16 @@ }, { "name": "behat/gherkin", - "version": "v4.4.5", + "version": "v4.6.0", "source": { "type": "git", "url": "https://github.com/Behat/Gherkin.git", - "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74" + "reference": "ab0a02ea14893860bca00f225f5621d351a3ad07" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/5c14cff4f955b17d20d088dec1bde61c0539ec74", - "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/ab0a02ea14893860bca00f225f5621d351a3ad07", + "reference": "ab0a02ea14893860bca00f225f5621d351a3ad07", "shasum": "" }, "require": { @@ -4178,8 +4244,8 @@ }, "require-dev": { "phpunit/phpunit": "~4.5|~5", - "symfony/phpunit-bridge": "~2.7|~3", - "symfony/yaml": "~2.3|~3" + "symfony/phpunit-bridge": "~2.7|~3|~4", + "symfony/yaml": "~2.3|~3|~4" }, "suggest": { "symfony/yaml": "If you want to parse features, represented in YAML files" @@ -4216,35 +4282,32 @@ "gherkin", "parser" ], - "time": "2016-10-30T11:50:56+00:00" + "time": "2019-01-16T14:22:17+00:00" }, { "name": "codeception/codeception", - "version": "2.3.9", + "version": "2.4.5", "source": { "type": "git", "url": "https://github.com/Codeception/Codeception.git", - "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e" + "reference": "5fee32d5c82791548931cbc34806b4de6aa1abfc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Codeception/zipball/104f46fa0bde339f1bcc3a375aac21eb36e65a1e", - "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e", + "url": "https://api.github.com/repos/Codeception/Codeception/zipball/5fee32d5c82791548931cbc34806b4de6aa1abfc", + "reference": "5fee32d5c82791548931cbc34806b4de6aa1abfc", "shasum": "" }, "require": { - "behat/gherkin": "~4.4.0", - "codeception/stub": "^1.0", + "behat/gherkin": "^4.4.0", + "codeception/phpunit-wrapper": "^6.0.9|^7.0.6", + "codeception/stub": "^2.0", "ext-json": "*", "ext-mbstring": "*", "facebook/webdriver": ">=1.1.3 <2.0", "guzzlehttp/guzzle": ">=4.1.4 <7.0", "guzzlehttp/psr7": "~1.0", - "php": ">=5.4.0 <8.0", - "phpunit/php-code-coverage": ">=2.2.4 <6.0", - "phpunit/phpunit": ">=4.8.28 <5.0.0 || >=5.6.3 <7.0", - "sebastian/comparator": ">1.1 <3.0", - "sebastian/diff": ">=1.4 <3.0", + "php": ">=5.6.0 <8.0", "symfony/browser-kit": ">=2.7 <5.0", "symfony/console": ">=2.7 <5.0", "symfony/css-selector": ">=2.7 <5.0", @@ -4310,27 +4373,70 @@ "functional testing", "unit testing" ], - "time": "2018-02-26T23:29:41+00:00" + "time": "2018-08-01T07:21:49+00:00" }, { - "name": "codeception/stub", - "version": "1.0.4", + "name": "codeception/phpunit-wrapper", + "version": "6.0.16", "source": { "type": "git", - "url": "https://github.com/Codeception/Stub.git", - "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805" + "url": "https://github.com/Codeception/phpunit-wrapper.git", + "reference": "299e3aece31489ed962e6c39fe2fb6f3bbd2eb16" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Stub/zipball/681b62348837a5ef07d10d8a226f5bc358cc8805", - "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805", + "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/299e3aece31489ed962e6c39fe2fb6f3bbd2eb16", + "reference": "299e3aece31489ed962e6c39fe2fb6f3bbd2eb16", "shasum": "" }, "require": { - "phpunit/phpunit-mock-objects": ">2.3 <7.0" + "phpunit/php-code-coverage": ">=4.0.4 <6.0", + "phpunit/phpunit": ">=5.7.27 <6.5.13", + "sebastian/comparator": ">=1.2.4 <3.0", + "sebastian/diff": ">=1.4 <4.0" + }, + "replace": { + "codeception/phpunit-wrapper": "*" }, "require-dev": { - "phpunit/phpunit": ">=4.8 <8.0" + "codeception/specify": "*", + "vlucas/phpdotenv": "^3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Codeception\\PHPUnit\\": "src\\" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Davert", + "email": "davert.php@resend.cc" + } + ], + "description": "PHPUnit classes used by Codeception", + "time": "2019-02-26T20:47:56+00:00" + }, + { + "name": "codeception/stub", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/Codeception/Stub.git", + "reference": "853657f988942f7afb69becf3fd0059f192c705a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/Stub/zipball/853657f988942f7afb69becf3fd0059f192c705a", + "reference": "853657f988942f7afb69becf3fd0059f192c705a", + "shasum": "" + }, + "require": { + "codeception/phpunit-wrapper": ">6.0.15 <6.1.0 | ^6.6.1 | ^7.7.1 | ^8.0.3" }, "type": "library", "autoload": { @@ -4343,20 +4449,20 @@ "MIT" ], "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", - "time": "2018-05-17T09:31:08+00:00" + "time": "2019-03-02T15:35:10+00:00" }, { "name": "composer/xdebug-handler", - "version": "1.3.0", + "version": "1.3.2", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "b8e9745fb9b06ea6664d8872c4505fb16df4611c" + "reference": "d17708133b6c276d6e42ef887a877866b909d892" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/b8e9745fb9b06ea6664d8872c4505fb16df4611c", - "reference": "b8e9745fb9b06ea6664d8872c4505fb16df4611c", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/d17708133b6c276d6e42ef887a877866b909d892", + "reference": "d17708133b6c276d6e42ef887a877866b909d892", "shasum": "" }, "require": { @@ -4387,38 +4493,82 @@ "Xdebug", "performance" ], - "time": "2018-08-31T19:07:57+00:00" + "time": "2019-01-28T20:25:53+00:00" }, { "name": "consolidation/annotated-command", - "version": "2.9.1", + "version": "2.12.0", "source": { "type": "git", "url": "https://github.com/consolidation/annotated-command.git", - "reference": "4bdbb8fa149e1cc1511bd77b0bc4729fd66bccac" + "reference": "512a2e54c98f3af377589de76c43b24652bcb789" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/4bdbb8fa149e1cc1511bd77b0bc4729fd66bccac", - "reference": "4bdbb8fa149e1cc1511bd77b0bc4729fd66bccac", + "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/512a2e54c98f3af377589de76c43b24652bcb789", + "reference": "512a2e54c98f3af377589de76c43b24652bcb789", "shasum": "" }, "require": { - "consolidation/output-formatters": "^3.1.12", - "php": ">=5.4.0", + "consolidation/output-formatters": "^3.4", + "php": ">=5.4.5", "psr/log": "^1", "symfony/console": "^2.8|^3|^4", "symfony/event-dispatcher": "^2.5|^3|^4", "symfony/finder": "^2.5|^3|^4" }, "require-dev": { - "g1a/composer-test-scenarios": "^2", + "g1a/composer-test-scenarios": "^3", + "php-coveralls/php-coveralls": "^1", "phpunit/phpunit": "^6", - "satooshi/php-coveralls": "^2", "squizlabs/php_codesniffer": "^2.7" }, "type": "library", "extra": { + "scenarios": { + "symfony4": { + "require": { + "symfony/console": "^4.0" + }, + "config": { + "platform": { + "php": "7.1.3" + } + } + }, + "symfony2": { + "require": { + "symfony/console": "^2.8" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.36" + }, + "remove": [ + "php-coveralls/php-coveralls" + ], + "config": { + "platform": { + "php": "5.4.8" + } + }, + "scenario-options": { + "create-lockfile": "false" + } + }, + "phpunit4": { + "require-dev": { + "phpunit/phpunit": "^4.8.36" + }, + "remove": [ + "php-coveralls/php-coveralls" + ], + "config": { + "platform": { + "php": "5.4.8" + } + } + } + }, "branch-alias": { "dev-master": "2.x-dev" } @@ -4439,20 +4589,20 @@ } ], "description": "Initialize Symfony Console commands from annotated command class methods.", - "time": "2018-09-19T17:47:18+00:00" + "time": "2019-03-08T16:55:03+00:00" }, { "name": "consolidation/config", - "version": "1.1.0", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/consolidation/config.git", - "reference": "c9fc25e9088a708637e18a256321addc0670e578" + "reference": "cac1279bae7efb5c7fb2ca4c3ba4b8eb741a96c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/config/zipball/c9fc25e9088a708637e18a256321addc0670e578", - "reference": "c9fc25e9088a708637e18a256321addc0670e578", + "url": "https://api.github.com/repos/consolidation/config/zipball/cac1279bae7efb5c7fb2ca4c3ba4b8eb741a96c1", + "reference": "cac1279bae7efb5c7fb2ca4c3ba4b8eb741a96c1", "shasum": "" }, "require": { @@ -4461,9 +4611,9 @@ "php": ">=5.4.0" }, "require-dev": { - "g1a/composer-test-scenarios": "^1", + "g1a/composer-test-scenarios": "^3", + "php-coveralls/php-coveralls": "^1", "phpunit/phpunit": "^5", - "satooshi/php-coveralls": "^1.0", "squizlabs/php_codesniffer": "2.*", "symfony/console": "^2.5|^3|^4", "symfony/yaml": "^2.8.11|^3|^4" @@ -4473,6 +4623,33 @@ }, "type": "library", "extra": { + "scenarios": { + "symfony4": { + "require-dev": { + "symfony/console": "^4.0" + }, + "config": { + "platform": { + "php": "7.1.3" + } + } + }, + "symfony2": { + "require-dev": { + "symfony/console": "^2.8", + "symfony/event-dispatcher": "^2.8", + "phpunit/phpunit": "^4.8.36" + }, + "remove": [ + "php-coveralls/php-coveralls" + ], + "config": { + "platform": { + "php": "5.4.8" + } + } + } + }, "branch-alias": { "dev-master": "1.x-dev" } @@ -4493,35 +4670,76 @@ } ], "description": "Provide configuration services for a commandline tool.", - "time": "2018-08-07T22:57:00+00:00" + "time": "2019-03-03T19:37:04+00:00" }, { "name": "consolidation/log", - "version": "1.0.6", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/consolidation/log.git", - "reference": "dfd8189a771fe047bf3cd669111b2de5f1c79395" + "reference": "b2e887325ee90abc96b0a8b7b474cd9e7c896e3a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/log/zipball/dfd8189a771fe047bf3cd669111b2de5f1c79395", - "reference": "dfd8189a771fe047bf3cd669111b2de5f1c79395", + "url": "https://api.github.com/repos/consolidation/log/zipball/b2e887325ee90abc96b0a8b7b474cd9e7c896e3a", + "reference": "b2e887325ee90abc96b0a8b7b474cd9e7c896e3a", "shasum": "" }, "require": { - "php": ">=5.5.0", - "psr/log": "~1.0", + "php": ">=5.4.5", + "psr/log": "^1.0", "symfony/console": "^2.8|^3|^4" }, "require-dev": { - "g1a/composer-test-scenarios": "^1", - "phpunit/phpunit": "4.*", - "satooshi/php-coveralls": "^2", - "squizlabs/php_codesniffer": "2.*" + "g1a/composer-test-scenarios": "^3", + "php-coveralls/php-coveralls": "^1", + "phpunit/phpunit": "^6", + "squizlabs/php_codesniffer": "^2" }, "type": "library", "extra": { + "scenarios": { + "symfony4": { + "require": { + "symfony/console": "^4.0" + }, + "config": { + "platform": { + "php": "7.1.3" + } + } + }, + "symfony2": { + "require": { + "symfony/console": "^2.8" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.36" + }, + "remove": [ + "php-coveralls/php-coveralls" + ], + "config": { + "platform": { + "php": "5.4.8" + } + } + }, + "phpunit4": { + "require-dev": { + "phpunit/phpunit": "^4.8.36" + }, + "remove": [ + "php-coveralls/php-coveralls" + ], + "config": { + "platform": { + "php": "5.4.8" + } + } + } + }, "branch-alias": { "dev-master": "1.x-dev" } @@ -4542,33 +4760,33 @@ } ], "description": "Improved Psr-3 / Psr\\Log logger based on Symfony Console components.", - "time": "2018-05-25T18:14:39+00:00" + "time": "2019-01-01T17:30:51+00:00" }, { "name": "consolidation/output-formatters", - "version": "3.2.1", + "version": "3.4.1", "source": { "type": "git", "url": "https://github.com/consolidation/output-formatters.git", - "reference": "d78ef59aea19d3e2e5a23f90a055155ee78a0ad5" + "reference": "0881112642ad9059071f13f397f571035b527cb9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/d78ef59aea19d3e2e5a23f90a055155ee78a0ad5", - "reference": "d78ef59aea19d3e2e5a23f90a055155ee78a0ad5", + "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/0881112642ad9059071f13f397f571035b527cb9", + "reference": "0881112642ad9059071f13f397f571035b527cb9", "shasum": "" }, "require": { + "dflydev/dot-access-data": "^1.1.0", "php": ">=5.4.0", "symfony/console": "^2.8|^3|^4", "symfony/finder": "^2.5|^3|^4" }, "require-dev": { - "g1a/composer-test-scenarios": "^2", + "g1a/composer-test-scenarios": "^3", + "php-coveralls/php-coveralls": "^1", "phpunit/phpunit": "^5.7.27", - "satooshi/php-coveralls": "^2", "squizlabs/php_codesniffer": "^2.7", - "symfony/console": "3.2.3", "symfony/var-dumper": "^2.8|^3|^4", "victorjonsson/markdowndocs": "^1.3" }, @@ -4577,6 +4795,52 @@ }, "type": "library", "extra": { + "scenarios": { + "symfony4": { + "require": { + "symfony/console": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^6" + }, + "config": { + "platform": { + "php": "7.1.3" + } + } + }, + "symfony3": { + "require": { + "symfony/console": "^3.4", + "symfony/finder": "^3.4", + "symfony/var-dumper": "^3.4" + }, + "config": { + "platform": { + "php": "5.6.32" + } + } + }, + "symfony2": { + "require": { + "symfony/console": "^2.8" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.36" + }, + "remove": [ + "php-coveralls/php-coveralls" + ], + "config": { + "platform": { + "php": "5.4.8" + } + }, + "scenario-options": { + "create-lockfile": "false" + } + } + }, "branch-alias": { "dev-master": "3.x-dev" } @@ -4597,29 +4861,28 @@ } ], "description": "Format text by applying transformations provided by plug-in formatters.", - "time": "2018-05-25T18:02:34+00:00" + "time": "2019-03-14T03:45:44+00:00" }, { "name": "consolidation/robo", - "version": "1.3.1", + "version": "1.4.6", "source": { "type": "git", "url": "https://github.com/consolidation/Robo.git", - "reference": "31f2d2562c4e1dcde70f2659eefd59aa9c7f5b2d" + "reference": "d4805a1abbc730e9a6d64ede2eba56f91a2b4eb3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/Robo/zipball/31f2d2562c4e1dcde70f2659eefd59aa9c7f5b2d", - "reference": "31f2d2562c4e1dcde70f2659eefd59aa9c7f5b2d", + "url": "https://api.github.com/repos/consolidation/Robo/zipball/d4805a1abbc730e9a6d64ede2eba56f91a2b4eb3", + "reference": "d4805a1abbc730e9a6d64ede2eba56f91a2b4eb3", "shasum": "" }, "require": { - "consolidation/annotated-command": "^2.8.2", + "consolidation/annotated-command": "^2.10.2", "consolidation/config": "^1.0.10", "consolidation/log": "~1", "consolidation/output-formatters": "^3.1.13", "consolidation/self-update": "^1", - "g1a/composer-test-scenarios": "^2", "grasmash/yaml-expander": "^1.3", "league/container": "^2.2", "php": ">=5.5.0", @@ -4636,14 +4899,15 @@ "codeception/aspect-mock": "^1|^2.1.1", "codeception/base": "^2.3.7", "codeception/verify": "^0.3.2", + "g1a/composer-test-scenarios": "^3", "goaop/framework": "~2.1.2", "goaop/parser-reflection": "^1.1.0", "natxet/cssmin": "3.0.4", "nikic/php-parser": "^3.1.5", "patchwork/jsqueeze": "~2", - "pear/archive_tar": "^1.4.2", + "pear/archive_tar": "^1.4.4", + "php-coveralls/php-coveralls": "^1", "phpunit/php-code-coverage": "~2|~4", - "satooshi/php-coveralls": "^2", "squizlabs/php_codesniffer": "^2.8" }, "suggest": { @@ -4657,9 +4921,36 @@ ], "type": "library", "extra": { + "scenarios": { + "symfony4": { + "require": { + "symfony/console": "^4" + }, + "config": { + "platform": { + "php": "7.1.3" + } + } + }, + "symfony2": { + "require": { + "symfony/console": "^2.8" + }, + "remove": [ + "goaop/framework" + ], + "config": { + "platform": { + "php": "5.5.9" + } + }, + "scenario-options": { + "create-lockfile": "false" + } + } + }, "branch-alias": { - "dev-master": "1.x-dev", - "dev-state": "1.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { @@ -4678,20 +4969,20 @@ } ], "description": "Modern task runner", - "time": "2018-08-17T18:44:18+00:00" + "time": "2019-02-17T05:32:27+00:00" }, { "name": "consolidation/self-update", - "version": "1.1.3", + "version": "1.1.5", "source": { "type": "git", "url": "https://github.com/consolidation/self-update.git", - "reference": "de33822f907e0beb0ffad24cf4b1b4fae5ada318" + "reference": "a1c273b14ce334789825a09d06d4c87c0a02ad54" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/self-update/zipball/de33822f907e0beb0ffad24cf4b1b4fae5ada318", - "reference": "de33822f907e0beb0ffad24cf4b1b4fae5ada318", + "url": "https://api.github.com/repos/consolidation/self-update/zipball/a1c273b14ce334789825a09d06d4c87c0a02ad54", + "reference": "a1c273b14ce334789825a09d06d4c87c0a02ad54", "shasum": "" }, "require": { @@ -4728,7 +5019,7 @@ } ], "description": "Provides a self:update command for Symfony Console applications.", - "time": "2018-08-24T17:01:46+00:00" + "time": "2018-10-28T01:52:03+00:00" }, { "name": "dflydev/dot-access-data", @@ -5317,39 +5608,6 @@ ], "time": "2018-07-12T10:23:15+00:00" }, - { - "name": "g1a/composer-test-scenarios", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/g1a/composer-test-scenarios.git", - "reference": "a166fd15191aceab89f30c097e694b7cf3db4880" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/g1a/composer-test-scenarios/zipball/a166fd15191aceab89f30c097e694b7cf3db4880", - "reference": "a166fd15191aceab89f30c097e694b7cf3db4880", - "shasum": "" - }, - "bin": [ - "scripts/create-scenario", - "scripts/dependency-licenses", - "scripts/install-scenario" - ], - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Greg Anderson", - "email": "greg.1.anderson@greenknowe.org" - } - ], - "description": "Useful scripts for testing multiple sets of Composer dependencies.", - "time": "2018-08-08T23:37:23+00:00" - }, { "name": "grasmash/expander", "version": "1.0.0", @@ -5563,32 +5821,33 @@ }, { "name": "guzzlehttp/psr7", - "version": "1.4.2", + "version": "1.5.2", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c" + "reference": "9f83dded91781a01c63574e387eaa769be769115" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c", - "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/9f83dded91781a01c63574e387eaa769be769115", + "reference": "9f83dded91781a01c63574e387eaa769be769115", "shasum": "" }, "require": { "php": ">=5.4.0", - "psr/http-message": "~1.0" + "psr/http-message": "~1.0", + "ralouphie/getallheaders": "^2.0.5" }, "provide": { "psr/http-message-implementation": "1.0" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "1.5-dev" } }, "autoload": { @@ -5618,13 +5877,14 @@ "keywords": [ "http", "message", + "psr-7", "request", "response", "stream", "uri", "url" ], - "time": "2017-03-20T17:10:46+00:00" + "time": "2018-12-04T20:46:45+00:00" }, { "name": "ircmaxell/password-compat", @@ -5670,16 +5930,16 @@ }, { "name": "jms/metadata", - "version": "1.6.0", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/schmittjoh/metadata.git", - "reference": "6a06970a10e0a532fb52d3959547123b84a3b3ab" + "reference": "e5854ab1aa643623dc64adde718a8eec32b957a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/metadata/zipball/6a06970a10e0a532fb52d3959547123b84a3b3ab", - "reference": "6a06970a10e0a532fb52d3959547123b84a3b3ab", + "url": "https://api.github.com/repos/schmittjoh/metadata/zipball/e5854ab1aa643623dc64adde718a8eec32b957a8", + "reference": "e5854ab1aa643623dc64adde718a8eec32b957a8", "shasum": "" }, "require": { @@ -5702,9 +5962,13 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache-2.0" + "MIT" ], "authors": [ + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" + }, { "name": "Johannes M. Schmitt", "email": "schmittjoh@gmail.com" @@ -5717,7 +5981,7 @@ "xml", "yaml" ], - "time": "2016-12-05T10:18:33+00:00" + "time": "2018-10-26T12:40:10+00:00" }, { "name": "jms/parser-lib", @@ -7096,6 +7360,46 @@ ], "time": "2017-08-03T14:08:16+00:00" }, + { + "name": "ralouphie/getallheaders", + "version": "2.0.5", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/5601c8a83fbba7ef674a7369456d12f1e0d0eafa", + "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "require-dev": { + "phpunit/phpunit": "~3.7.0", + "satooshi/php-coveralls": ">=1.0" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "time": "2016-02-11T07:05:27+00:00" + }, { "name": "sebastian/code-unit-reverse-lookup", "version": "1.0.1", @@ -7798,16 +8102,16 @@ }, { "name": "symfony/browser-kit", - "version": "v3.4.17", + "version": "v3.4.23", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "f6668d1a6182d5a8dec65a1c863a4c1d963816c0" + "reference": "c0fadd368c1031109e996316e53ffeb886d37ea1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/f6668d1a6182d5a8dec65a1c863a4c1d963816c0", - "reference": "f6668d1a6182d5a8dec65a1c863a4c1d963816c0", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/c0fadd368c1031109e996316e53ffeb886d37ea1", + "reference": "c0fadd368c1031109e996316e53ffeb886d37ea1", "shasum": "" }, "require": { @@ -7851,20 +8155,20 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2018-07-26T09:06:28+00:00" + "time": "2019-02-23T15:06:07+00:00" }, { "name": "symfony/config", - "version": "v3.4.17", + "version": "v3.4.23", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "e5389132dc6320682de3643091121c048ff796b3" + "reference": "177a276c01575253c95cefe0866e3d1b57637fe0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/e5389132dc6320682de3643091121c048ff796b3", - "reference": "e5389132dc6320682de3643091121c048ff796b3", + "url": "https://api.github.com/repos/symfony/config/zipball/177a276c01575253c95cefe0866e3d1b57637fe0", + "reference": "177a276c01575253c95cefe0866e3d1b57637fe0", "shasum": "" }, "require": { @@ -7915,60 +8219,7 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2018-09-08T13:15:14+00:00" - }, - { - "name": "symfony/css-selector", - "version": "v3.4.17", - "source": { - "type": "git", - "url": "https://github.com/symfony/css-selector.git", - "reference": "3503415d4aafabc31cd08c3a4ebac7f43fde8feb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/3503415d4aafabc31cd08c3a4ebac7f43fde8feb", - "reference": "3503415d4aafabc31cd08c3a4ebac7f43fde8feb", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\CssSelector\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jean-François Simon", - "email": "jeanfrancois.simon@sensiolabs.com" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony CssSelector Component", - "homepage": "https://symfony.com", - "time": "2018-10-02T16:33:53+00:00" + "time": "2019-02-23T15:06:07+00:00" }, { "name": "symfony/dependency-injection", @@ -8042,16 +8293,16 @@ }, { "name": "symfony/dom-crawler", - "version": "v3.4.17", + "version": "v3.4.23", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "c705bee03ade5b47c087807dd9ffaaec8dda2722" + "reference": "d40023c057393fb25f7ca80af2a56ed948c45a09" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/c705bee03ade5b47c087807dd9ffaaec8dda2722", - "reference": "c705bee03ade5b47c087807dd9ffaaec8dda2722", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/d40023c057393fb25f7ca80af2a56ed948c45a09", + "reference": "d40023c057393fb25f7ca80af2a56ed948c45a09", "shasum": "" }, "require": { @@ -8095,20 +8346,20 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2018-10-02T12:28:39+00:00" + "time": "2019-02-23T15:06:07+00:00" }, { "name": "symfony/http-foundation", - "version": "v3.4.17", + "version": "v3.4.23", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "3a4498236ade473c52b92d509303e5fd1b211ab1" + "reference": "9a96d77ceb1fd913c9d4a89e8a7e1be87604be8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3a4498236ade473c52b92d509303e5fd1b211ab1", - "reference": "3a4498236ade473c52b92d509303e5fd1b211ab1", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9a96d77ceb1fd913c9d4a89e8a7e1be87604be8a", + "reference": "9a96d77ceb1fd913c9d4a89e8a7e1be87604be8a", "shasum": "" }, "require": { @@ -8149,20 +8400,20 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2018-10-03T08:48:18+00:00" + "time": "2019-02-23T15:06:07+00:00" }, { "name": "symfony/options-resolver", - "version": "v3.4.17", + "version": "v3.4.23", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "1cf7d8e704a9cc4164c92e430f2dfa3e6983661d" + "reference": "926e3b797e6bb66c0e4d7da7eff3a174f7378bcf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/1cf7d8e704a9cc4164c92e430f2dfa3e6983661d", - "reference": "1cf7d8e704a9cc4164c92e430f2dfa3e6983661d", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/926e3b797e6bb66c0e4d7da7eff3a174f7378bcf", + "reference": "926e3b797e6bb66c0e4d7da7eff3a174f7378bcf", "shasum": "" }, "require": { @@ -8203,11 +8454,11 @@ "configuration", "options" ], - "time": "2018-09-17T17:29:18+00:00" + "time": "2019-02-23T15:06:07+00:00" }, { "name": "symfony/polyfill-php54", - "version": "v1.9.0", + "version": "v1.10.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php54.git", @@ -8265,16 +8516,16 @@ }, { "name": "symfony/polyfill-php55", - "version": "v1.9.0", + "version": "v1.10.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php55.git", - "reference": "578b8528da843de0fc65ec395900fa3181f2ead7" + "reference": "42a4c00a347625ac8853c3358c47eeadc7fd4e96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/578b8528da843de0fc65ec395900fa3181f2ead7", - "reference": "578b8528da843de0fc65ec395900fa3181f2ead7", + "url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/42a4c00a347625ac8853c3358c47eeadc7fd4e96", + "reference": "42a4c00a347625ac8853c3358c47eeadc7fd4e96", "shasum": "" }, "require": { @@ -8317,20 +8568,20 @@ "portable", "shim" ], - "time": "2018-08-06T14:22:27+00:00" + "time": "2018-10-31T12:13:01+00:00" }, { "name": "symfony/polyfill-php70", - "version": "v1.9.0", + "version": "v1.10.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "1e24b0c4a56d55aaf368763a06c6d1c7d3194934" + "reference": "6b88000cdd431cd2e940caa2cb569201f3f84224" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/1e24b0c4a56d55aaf368763a06c6d1c7d3194934", - "reference": "1e24b0c4a56d55aaf368763a06c6d1c7d3194934", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/6b88000cdd431cd2e940caa2cb569201f3f84224", + "reference": "6b88000cdd431cd2e940caa2cb569201f3f84224", "shasum": "" }, "require": { @@ -8376,20 +8627,20 @@ "portable", "shim" ], - "time": "2018-08-06T14:22:27+00:00" + "time": "2018-09-21T06:26:08+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.9.0", + "version": "v1.10.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "95c50420b0baed23852452a7f0c7b527303ed5ae" + "reference": "9050816e2ca34a8e916c3a0ae8b9c2fccf68b631" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/95c50420b0baed23852452a7f0c7b527303ed5ae", - "reference": "95c50420b0baed23852452a7f0c7b527303ed5ae", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/9050816e2ca34a8e916c3a0ae8b9c2fccf68b631", + "reference": "9050816e2ca34a8e916c3a0ae8b9c2fccf68b631", "shasum": "" }, "require": { @@ -8431,20 +8682,20 @@ "portable", "shim" ], - "time": "2018-08-06T14:22:27+00:00" + "time": "2018-09-21T13:07:52+00:00" }, { "name": "symfony/stopwatch", - "version": "v3.4.17", + "version": "v3.4.23", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "05e52a39de52ba690aebaed462b2bc8a9649f0a4" + "reference": "2a651c2645c10bbedd21170771f122d935e0dd58" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/05e52a39de52ba690aebaed462b2bc8a9649f0a4", - "reference": "05e52a39de52ba690aebaed462b2bc8a9649f0a4", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/2a651c2645c10bbedd21170771f122d935e0dd58", + "reference": "2a651c2645c10bbedd21170771f122d935e0dd58", "shasum": "" }, "require": { @@ -8480,7 +8731,7 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2018-10-02T12:28:39+00:00" + "time": "2019-01-16T09:39:14+00:00" }, { "name": "symfony/yaml", @@ -8619,20 +8870,21 @@ }, { "name": "vlucas/phpdotenv", - "version": "v2.5.1", + "version": "v2.6.1", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "8abb4f9aa89ddea9d52112c65bbe8d0125e2fa8e" + "reference": "2a7dcf7e3e02dc5e701004e51a6f304b713107d5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/8abb4f9aa89ddea9d52112c65bbe8d0125e2fa8e", - "reference": "8abb4f9aa89ddea9d52112c65bbe8d0125e2fa8e", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/2a7dcf7e3e02dc5e701004e51a6f304b713107d5", + "reference": "2a7dcf7e3e02dc5e701004e51a6f304b713107d5", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": ">=5.3.9", + "symfony/polyfill-ctype": "^1.9" }, "require-dev": { "phpunit/phpunit": "^4.8.35 || ^5.0" @@ -8640,7 +8892,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.6-dev" } }, "autoload": { @@ -8665,24 +8917,25 @@ "env", "environment" ], - "time": "2018-07-29T20:33:41+00:00" + "time": "2019-01-29T11:11:52+00:00" }, { "name": "webmozart/assert", - "version": "1.3.0", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a" + "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a", + "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9", + "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": "^5.3.3 || ^7.0", + "symfony/polyfill-ctype": "^1.8" }, "require-dev": { "phpunit/phpunit": "^4.6", @@ -8715,7 +8968,7 @@ "check", "validate" ], - "time": "2018-01-29T19:49:41+00:00" + "time": "2018-12-25T11:19:39+00:00" } ], "aliases": [], diff --git a/lib/internal/Magento/Framework/composer.json b/lib/internal/Magento/Framework/composer.json index 105c3f0721819..c674010af1a1f 100644 --- a/lib/internal/Magento/Framework/composer.json +++ b/lib/internal/Magento/Framework/composer.json @@ -2,7 +2,7 @@ "name": "magento/framework", "description": "N/A", "type": "magento2-library", - "version": "101.0.7", + "version": "101.0.8", "license": [ "OSL-3.0", "AFL-3.0" From 27d113fc9295cff8909913a41867d64dbbcfb377 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Wed, 13 Mar 2019 10:58:31 +0200 Subject: [PATCH 0923/1295] Vitalii Boiko: fixed wrong proxing in the inventory observer --- app/code/Magento/CatalogInventory/etc/di.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogInventory/etc/di.xml b/app/code/Magento/CatalogInventory/etc/di.xml index 535e91ba30f52..8c47865da6aa7 100644 --- a/app/code/Magento/CatalogInventory/etc/di.xml +++ b/app/code/Magento/CatalogInventory/etc/di.xml @@ -44,7 +44,7 @@ </type> <type name="Magento\CatalogInventory\Observer\UpdateItemsStockUponConfigChangeObserver"> <arguments> - <argument name="resourceStock" xsi:type="object">Magento\CatalogInventory\Model\ResourceModel\Stock\Proxy</argument> + <argument name="resourceStockItem" xsi:type="object">Magento\CatalogInventory\Model\ResourceModel\Stock\Item\Proxy</argument> </arguments> </type> <type name="Magento\Catalog\Model\Layer"> From 095b9f4ef371d9e5692f42b655c81294f14c5137 Mon Sep 17 00:00:00 2001 From: Ananth Iyer <iyerananth3@gmail.com> Date: Mon, 25 Feb 2019 23:48:59 +0530 Subject: [PATCH 0924/1295] Disable dropdown in JavaScript and CSS Settings in developer configuration --- app/code/Magento/Backend/etc/adminhtml/system.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Backend/etc/adminhtml/system.xml b/app/code/Magento/Backend/etc/adminhtml/system.xml index e3411166ee4a4..44ab1dcc00176 100644 --- a/app/code/Magento/Backend/etc/adminhtml/system.xml +++ b/app/code/Magento/Backend/etc/adminhtml/system.xml @@ -153,15 +153,15 @@ </group> <group id="js" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="1"> <label>JavaScript Settings</label> - <field id="merge_files" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="merge_files" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Merge JavaScript Files</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="enable_js_bundling" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="enable_js_bundling" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Enable JavaScript Bundling</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="minify_files" translate="label comment" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="minify_files" translate="label comment" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Minify JavaScript Files</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Minification is not applied in developer mode.</comment> @@ -169,11 +169,11 @@ </group> <group id="css" translate="label" type="text" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="1"> <label>CSS Settings</label> - <field id="merge_css_files" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="merge_css_files" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Merge CSS Files</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="minify_files" translate="label comment" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="minify_files" translate="label comment" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Minify CSS Files</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Minification is not applied in developer mode.</comment> From e7fb7fe185639ca6f811c63023eb0d04ceda892b Mon Sep 17 00:00:00 2001 From: Aditi Singh <36220507+aditisinghcedcoss@users.noreply.github.com> Date: Fri, 28 Dec 2018 13:09:54 +0530 Subject: [PATCH 0925/1295] Update AbstactSource.php Issue #13612 fixed. --- .../Eav/Model/Entity/Attribute/Source/AbstractSource.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Source/AbstractSource.php b/app/code/Magento/Eav/Model/Entity/Attribute/Source/AbstractSource.php index 0991b3f9f4b23..8d5b501654749 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Source/AbstractSource.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Source/AbstractSource.php @@ -73,7 +73,7 @@ public function getOptionText($value) } } // End - if (isset($options[$value])) { + if (is_scalar($value) && isset($options[$value])) { return $options[$value]; } return false; From dcf808e1922de7665c27d8d7b9f292ca96c333b9 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Fri, 25 Jan 2019 13:24:27 +0200 Subject: [PATCH 0926/1295] ENGCOM-3810: Static test fix. --- .../Eav/Model/Entity/Attribute/Source/AbstractSource.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Source/AbstractSource.php b/app/code/Magento/Eav/Model/Entity/Attribute/Source/AbstractSource.php index 8d5b501654749..36ad026029056 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Source/AbstractSource.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Source/AbstractSource.php @@ -80,6 +80,8 @@ public function getOptionText($value) } /** + * Get option id. + * * @param string $value * @return null|string */ From ab7aee90f55ccd02738ff98a69e6e29b083faed8 Mon Sep 17 00:00:00 2001 From: Danilo Argentiero <danilo.argentiero@magespecialist.it> Date: Fri, 25 Aug 2017 10:28:49 +0200 Subject: [PATCH 0927/1295] Issue #10645 - Allow BEM class via attribute tag. --- lib/internal/Magento/Framework/View/Page/Config.php | 2 +- .../View/Test/Unit/Page/Config/Generator/BodyTest.php | 4 ++-- .../Framework/View/Test/Unit/Page/Config/StructureTest.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/internal/Magento/Framework/View/Page/Config.php b/lib/internal/Magento/Framework/View/Page/Config.php index 226abc538112b..ee6c8f1d555b6 100644 --- a/lib/internal/Magento/Framework/View/Page/Config.php +++ b/lib/internal/Magento/Framework/View/Page/Config.php @@ -498,7 +498,7 @@ public function addRss($title, $href) */ public function addBodyClass($className) { - $className = preg_replace('#[^a-z0-9]+#', '-', strtolower($className)); + $className = preg_replace('#[^a-z0-9-]+#', '-', strtolower($className)); $bodyClasses = $this->getElementAttribute(self::ELEMENT_TYPE_BODY, self::BODY_ATTRIBUTE_CLASS); $bodyClasses = $bodyClasses ? explode(' ', $bodyClasses) : []; $bodyClasses[] = $className; diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Page/Config/Generator/BodyTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Page/Config/Generator/BodyTest.php index 0f59c302f943f..ed926afa00856 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Page/Config/Generator/BodyTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Page/Config/Generator/BodyTest.php @@ -57,13 +57,13 @@ public function testProcess() ->method('getPageConfigStructure') ->willReturn($structureMock); - $bodyClasses = ['class_1', 'class_2']; + $bodyClasses = ['class_1', 'class--2']; $structureMock->expects($this->once()) ->method('getBodyClasses') ->will($this->returnValue($bodyClasses)); $this->pageConfigMock->expects($this->exactly(2)) ->method('addBodyClass') - ->withConsecutive(['class_1'], ['class_2']); + ->withConsecutive(['class_1'], ['class--2']); $this->assertEquals( $this->bodyGenerator, diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Page/Config/StructureTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Page/Config/StructureTest.php index ed15a356cc4c7..d2eba5d2fa1b3 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Page/Config/StructureTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Page/Config/StructureTest.php @@ -58,7 +58,7 @@ public function testSetElementAttribute() public function testSetBodyClass() { $class1 = 'class_1'; - $class2 = 'class_2'; + $class2 = 'class--2'; $expected = [$class1, $class2]; $this->structure->setBodyClass($class1); $this->structure->setBodyClass($class2); From 743c034291d62a4329350d637832ad0ef7b8439b Mon Sep 17 00:00:00 2001 From: Danilo Argentiero <danilo.argentiero@magespecialist.it> Date: Fri, 25 Aug 2017 14:55:17 +0200 Subject: [PATCH 0928/1295] Issue #10645 Allow underscore charachter for BEM class via attribute tag --- lib/internal/Magento/Framework/View/Page/Config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/View/Page/Config.php b/lib/internal/Magento/Framework/View/Page/Config.php index ee6c8f1d555b6..da7bcb128f4b8 100644 --- a/lib/internal/Magento/Framework/View/Page/Config.php +++ b/lib/internal/Magento/Framework/View/Page/Config.php @@ -498,7 +498,7 @@ public function addRss($title, $href) */ public function addBodyClass($className) { - $className = preg_replace('#[^a-z0-9-]+#', '-', strtolower($className)); + $className = preg_replace('#[^a-z0-9-_]+#', '-', strtolower($className)); $bodyClasses = $this->getElementAttribute(self::ELEMENT_TYPE_BODY, self::BODY_ATTRIBUTE_CLASS); $bodyClasses = $bodyClasses ? explode(' ', $bodyClasses) : []; $bodyClasses[] = $className; From 2314bd8a8d703e9c029e2a70c6d2b202e7b92cba Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Mon, 18 Mar 2019 11:03:34 +0200 Subject: [PATCH 0929/1295] MAGETWO-98380: The required product attribute is not displayed when change attribute set --- .../Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index 12a11944e4290..6a310e60994f8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -265,6 +265,6 @@ <scrollToTopOfPage stepKey="scrollToTop"/> <click selector="{{AdminProductFormSection.attributeSet}}" stepKey="startEditAttrSet"/> <fillField selector="{{AdminProductFormSection.attributeSetFilter}}" userInput="{{attributeSet.attribute_set_name}}" stepKey="searchForAttrSet"/> - <click selector="{{AdminProductFormSection.attributeSetFilterResult}}" stepKey="selectAttrSet"/> + <click selector="{{AdminProductFormSection.attributeSetFilterResultByName(attributeSet.attribute_set_name)}}" stepKey="selectAttrSet"/> </actionGroup> </actionGroups> From 6bb24ef4cccf52d56ee5c8ef43c9365509075d8a Mon Sep 17 00:00:00 2001 From: RomanKis <roman.kis.y@gmail.com> Date: Mon, 18 Mar 2019 11:39:46 +0200 Subject: [PATCH 0930/1295] #21734 Error in JS validation rule --- .../Magento/Ui/view/base/web/js/lib/validation/rules.js | 6 +++--- lib/web/mage/validation.js | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js index cbfc0dae90dda..831f11976fb2f 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 @@ -919,12 +919,12 @@ define([ ], 'validate-per-page-value-list': [ function (value) { - var isValid = utils.isEmpty(value), + var isValid = true, values = value.split(','), i; - if (isValid) { - return true; + if (utils.isEmpty(value)) { + return isValid; } for (i = 0; i < values.length; i++) { diff --git a/lib/web/mage/validation.js b/lib/web/mage/validation.js index a742b8e6bbb27..cd3d3bea4eabf 100644 --- a/lib/web/mage/validation.js +++ b/lib/web/mage/validation.js @@ -1425,10 +1425,14 @@ ], 'validate-per-page-value-list': [ function (v) { - var isValid = !$.mage.isEmpty(v), + var isValid = true, values = v.split(','), i; + if ($.mage.isEmpty(v)) { + return isValid; + } + for (i = 0; i < values.length; i++) { if (!/^[0-9]+$/.test(values[i])) { isValid = false; From 0ce67ad7b81891a4e2d6565e3a96de5c3954ba4e Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Mon, 18 Mar 2019 12:14:52 +0200 Subject: [PATCH 0931/1295] MC-15007: [Backport for 2.2.x] Add New Attribute on the admin product page with JS error --- .../web/catalog/product/edit/attribute.js | 6 +++--- .../adminhtml/templates/class/page/edit.phtml | 20 ------------------- .../view/adminhtml/web/js/page/validate.js | 15 -------------- 3 files changed, 3 insertions(+), 38 deletions(-) delete mode 100644 app/code/Magento/Tax/view/adminhtml/templates/class/page/edit.phtml delete mode 100644 app/code/Magento/Tax/view/adminhtml/web/js/page/validate.js diff --git a/app/code/Magento/Catalog/view/adminhtml/web/catalog/product/edit/attribute.js b/app/code/Magento/Catalog/view/adminhtml/web/catalog/product/edit/attribute.js index 407fd1fe28e39..e1923dc46d68e 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/catalog/product/edit/attribute.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/catalog/product/edit/attribute.js @@ -5,13 +5,13 @@ define([ 'jquery', - 'mage/mage' + 'mage/mage', + 'validation' ], function ($) { 'use strict'; return function (config, element) { - - $(element).mage('form').mage('validation', { + $(element).mage('form').validation({ validationUrl: config.validationUrl }); }; diff --git a/app/code/Magento/Tax/view/adminhtml/templates/class/page/edit.phtml b/app/code/Magento/Tax/view/adminhtml/templates/class/page/edit.phtml deleted file mode 100644 index 18e86549a1ff9..0000000000000 --- a/app/code/Magento/Tax/view/adminhtml/templates/class/page/edit.phtml +++ /dev/null @@ -1,20 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -?> -<div data-mage-init='{"floatingHeader": {}}' class="page-actions"> - <?= $block->getBackButtonHtml() ?> - <?= $block->getResetButtonHtml() ?> - <?= $block->getDeleteButtonHtml() ?> - <?= $block->getSaveButtonHtml() ?> -</div> -<?= $block->getRenameFormHtml() ?> -<script type="text/x-magento-init"> - { - "#<?= /* @escapeNotVerified */ $block->getRenameFormId() ?>": { - "Magento_Tax/js/page/validate": {} - } - } -</script> diff --git a/app/code/Magento/Tax/view/adminhtml/web/js/page/validate.js b/app/code/Magento/Tax/view/adminhtml/web/js/page/validate.js deleted file mode 100644 index a49f199ba56b6..0000000000000 --- a/app/code/Magento/Tax/view/adminhtml/web/js/page/validate.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'jquery', - 'mage/mage' -], function (jQuery) { - 'use strict'; - - return function (data, element) { - jQuery(element).mage('form').mage('validation'); - }; -}); From 3fa5a07399597cf9487f457361dd15b2b36ee5d4 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Mon, 4 Mar 2019 16:51:03 +0200 Subject: [PATCH 0932/1295] ENGCOM-4000: Upgrade data script. --- .../CatalogWidget/Setup/UpgradeData.php | 103 ++++++++++++++++++ app/code/Magento/CatalogWidget/etc/module.xml | 2 +- 2 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/CatalogWidget/Setup/UpgradeData.php diff --git a/app/code/Magento/CatalogWidget/Setup/UpgradeData.php b/app/code/Magento/CatalogWidget/Setup/UpgradeData.php new file mode 100644 index 0000000000000..5ebdbe2390d51 --- /dev/null +++ b/app/code/Magento/CatalogWidget/Setup/UpgradeData.php @@ -0,0 +1,103 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogWidget\Setup; + +use Magento\CatalogWidget\Block\Product\ProductsList; +use Magento\CatalogWidget\Model\Rule\Condition\Product as ConditionProduct; +use Magento\Framework\Serialize\Serializer\Json as Serializer; +use Magento\Framework\Setup\ModuleContextInterface; +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\UpgradeDataInterface; + +/** + * Upgrade data for CatalogWidget module. + */ +class UpgradeData implements UpgradeDataInterface +{ + /** + * @var Serializer + */ + private $serializer; + + /** + * @param Serializer $serializer + */ + public function __construct( + Serializer $serializer + ) { + $this->serializer = $serializer; + } + + /** + * @inheritdoc + */ + public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context) + { + if (version_compare($context->getVersion(), '2.0.1', '<')) { + $this->replaceIsWithIsOneOf($setup); + } + } + + /** + * Replace 'is' condition with 'is one of' in database. + * + * If 'is' product list condition is used with multiple skus it should be replaced by 'is one of' condition. + * + * @param ModuleDataSetupInterface $setup + */ + private function replaceIsWithIsOneOf(ModuleDataSetupInterface $setup) + { + $tableName = $setup->getTable('widget_instance'); + $connection = $setup->getConnection(); + $select = $connection->select() + ->from( + $tableName, + [ + 'instance_id', + 'widget_parameters', + ] + )->where('instance_type = ? ', ProductsList::class); + + $result = $setup->getConnection()->fetchAll($select); + + if ($result) { + $updatedData = $this->updateWidgetData($result); + + $connection->insertOnDuplicate( + $tableName, + $updatedData + ); + } + } + + /** + * Replace 'is' condition with 'is one of' in widget parameters. + * + * @param array $result + * @return array + */ + private function updateWidgetData(array $result): array + { + return array_map( + function ($widgetData) { + $widgetParameters = $this->serializer->unserialize($widgetData['widget_parameters']); + foreach ($widgetParameters['conditions'] as &$condition) { + if (ConditionProduct::class === $condition['type'] && + 'sku' === $condition['attribute'] && + '==' === $condition['operator']) { + $condition['operator'] = '()'; + } + } + $widgetData['widget_parameters'] = $this->serializer->serialize($widgetParameters); + + return $widgetData; + }, + $result + ); + } +} diff --git a/app/code/Magento/CatalogWidget/etc/module.xml b/app/code/Magento/CatalogWidget/etc/module.xml index 8954f11f954f7..1f2d84bef2d6b 100644 --- a/app/code/Magento/CatalogWidget/etc/module.xml +++ b/app/code/Magento/CatalogWidget/etc/module.xml @@ -6,7 +6,7 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_CatalogWidget" setup_version="2.0.0"> + <module name="Magento_CatalogWidget" setup_version="2.0.1"> <sequence> <module name="Magento_Catalog"/> <module name="Magento_Widget"/> From d0094363f1829b5608a8e8bd8a795e32871d7bfe Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <svizev.igor@gmail.com> Date: Mon, 18 Mar 2019 15:11:21 +0200 Subject: [PATCH 0933/1295] Remove all marketing get params on Varnish to minimize the cache objects --- app/code/Magento/PageCache/etc/varnish4.vcl | 9 +++++---- app/code/Magento/PageCache/etc/varnish5.vcl | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/PageCache/etc/varnish4.vcl b/app/code/Magento/PageCache/etc/varnish4.vcl index 793f8f81a03f9..6b64b5b59f51f 100644 --- a/app/code/Magento/PageCache/etc/varnish4.vcl +++ b/app/code/Magento/PageCache/etc/varnish4.vcl @@ -91,10 +91,11 @@ sub vcl_recv { } } - # Remove Google gclid parameters to minimize the cache objects - set req.url = regsuball(req.url,"\?gclid=[^&]+$",""); # strips when QS = "?gclid=AAA" - set req.url = regsuball(req.url,"\?gclid=[^&]+&","?"); # strips when QS = "?gclid=AAA&foo=bar" - set req.url = regsuball(req.url,"&gclid=[^&]+",""); # strips when QS = "?foo=bar&gclid=AAA" or QS = "?foo=bar&gclid=AAA&bar=baz" + # Remove all marketing get parameters to minimize the cache objects + if (req.url ~ "(\?|&)(gclid|cx|ie|cof|siteurl|zanpid|origin|mc_[a-z]+|utm_[a-z]+)=") { + set req.url = regsuball(req.url, "(gclid|cx|ie|cof|siteurl|zanpid|origin|mc_[a-z]+|utm_[a-z]+)=[-_A-z0-9+()%.]+&?", ""); + set req.url = regsub(req.url, "[?|&]+$", ""); + } # Static files caching if (req.url ~ "^/(pub/)?(media|static)/") { diff --git a/app/code/Magento/PageCache/etc/varnish5.vcl b/app/code/Magento/PageCache/etc/varnish5.vcl index 4dce6356d1e73..2dfa4af25dd52 100644 --- a/app/code/Magento/PageCache/etc/varnish5.vcl +++ b/app/code/Magento/PageCache/etc/varnish5.vcl @@ -92,10 +92,11 @@ sub vcl_recv { } } - # Remove Google gclid parameters to minimize the cache objects - set req.url = regsuball(req.url,"\?gclid=[^&]+$",""); # strips when QS = "?gclid=AAA" - set req.url = regsuball(req.url,"\?gclid=[^&]+&","?"); # strips when QS = "?gclid=AAA&foo=bar" - set req.url = regsuball(req.url,"&gclid=[^&]+",""); # strips when QS = "?foo=bar&gclid=AAA" or QS = "?foo=bar&gclid=AAA&bar=baz" + # Remove all marketing get parameters to minimize the cache objects + if (req.url ~ "(\?|&)(gclid|cx|ie|cof|siteurl|zanpid|origin|mc_[a-z]+|utm_[a-z]+)=") { + set req.url = regsuball(req.url, "(gclid|cx|ie|cof|siteurl|zanpid|origin|mc_[a-z]+|utm_[a-z]+)=[-_A-z0-9+()%.]+&?", ""); + set req.url = regsub(req.url, "[?|&]+$", ""); + } # Static files caching if (req.url ~ "^/(pub/)?(media|static)/") { From cdd8ef4a42f2e2205deaa58067fdc18c0d1b3c69 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Mon, 18 Mar 2019 16:25:14 +0200 Subject: [PATCH 0934/1295] MAGETWO-96375: Checkout Free Shipping Recalculation after Coupon Code Added --- .../Mftf/ActionGroup/CheckoutActionGroup.xml | 4 +-- .../Mftf/Section/CheckoutPaymentSection.xml | 1 + ...gRecalculationAfterCouponCodeAddedTest.xml | 12 ++++---- .../ApplyCartRuleOnStorefrontActionGroup.xml | 5 ++-- .../Section/StorefrontDiscountSection.xml | 4 +-- .../Mftf/Data/StoreShippingMethodsData.xml | 11 +++++--- .../Metadata/store_shipping_methods-meta.xml | 28 ++----------------- 7 files changed, 24 insertions(+), 41 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml index 354ea15c226bf..1cbaa66e2fd8b 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml @@ -115,8 +115,8 @@ <conditionalClick selector="{{CheckoutShippingMethodsSection.checkShippingMethodByName(shippingMethod)}}" dependentSelector="{{CheckoutShippingMethodsSection.checkShippingMethodByName(shippingMethod)}}" visible="true" stepKey="selectShippingMethod"/> <waitForLoadingMaskToDisappear stepKey="waitForNextButton"/> <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="clickNext"/> - <waitForLoadingMaskToDisappear stepKey="waitForPaymentMethod"/> - <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" stepKey="waitForPaymentSectionLoaded"/> + <waitForPageLoad stepKey="waitForPaymentMethod"/> + <waitForElementVisible selector="{{CheckoutPaymentSection.paymentSectionTitle}}" stepKey="waitForPaymentSectionLoaded"/> <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml index ae8f9aa5f2aa7..e22a70df50f67 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml @@ -25,6 +25,7 @@ <element name="cartItems" type="text" selector=".minicart-items"/> <element name="billingAddress" type="text" selector="div.billing-address-details"/> <element name="placeOrder" type="button" selector=".payment-method._active button.action.primary.checkout" timeout="30"/> + <element name="placeOrderNoWait" type="button" selector=".payment-method._active button.action.primary.checkout"/> <element name="productOptionsByProductItemPrice" type="text" selector="//div[@class='product-item-inner']//div[@class='subtotal']//span[@class='price'][contains(.,'{{price}}')]//ancestor::div[@class='product-item-details']//div[@class='product options']" parameterized="true"/> <element name="productOptionsActiveByProductItemPrice" type="text" selector="//div[@class='subtotal']//span[@class='price'][contains(.,'{{price}}')]//ancestor::div[@class='product-item-details']//div[@class='product options active']" parameterized="true"/> <element name="productItemPriceByName" type="text" selector="//div[@class='product-item-details'][contains(., '{{ProductName}}')]//span[@class='price']" parameterized="true"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml index 5ea04bbcbc13c..0dac0266904ff 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml @@ -65,7 +65,7 @@ <!-- Proceed to Checkout and make sure Free Shipping method isn't displaying --> <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> - <dontSee selector="{{CheckoutShippingMethodsSection.shippingMethodRowByName('Free')}}" stepKey="dontSeeFreeShipping"/> + <dontSeeElement selector="{{CheckoutShippingMethodsSection.shippingMethodRowByName('Free')}}" stepKey="dontSeeFreeShipping"/> <!-- Back to Shopping Cart page and cancel coupon--> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToShoppingCartPage"/> @@ -81,12 +81,12 @@ <argument name="couponCode" value="$$createCouponForCartPriceRule.code$$"/> <argument name="successMessage" value="Your coupon was successfully applied."/> </actionGroup> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectPaymentMethod"/> <!-- Try to Place Order --> - <waitForElementVisible selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="waitForPlaceOrderButton"/> - <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> - <waitForElementVisible selector="{{StorefrontMessagesSection.errorMessage}}" stepKey="waitForError"/> - <see selector="{{StorefrontMessagesSection.errorMessage}}" userInput="The shipping method is missing. Select the shipping method and try again." stepKey="seeShippingMethodError"/> + <waitForElementVisible selector="{{CheckoutPaymentSection.placeOrderNoWait}}" stepKey="waitForPlaceOrderButton"/> + <click selector="{{CheckoutPaymentSection.placeOrderNoWait}}" stepKey="clickPlaceOrder"/> + <waitForText selector="{{StorefrontMessagesSection.errorMessage}}" userInput="The shipping method is missing. Select the shipping method and try again." time="30" stepKey="seeShippingMethodError"/> <!-- Go back to Shipping step and select Shipping method --> <amOnPage url="{{CheckoutPage.url}}/#shipping" stepKey="navigateToShippingStep"/> @@ -95,7 +95,7 @@ </actionGroup> <!-- Select Payment method and Place order--> - <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectPaymentMethod"/> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectPaymentMethod1"/> <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="placeOrder"> <argument name="orderNumberMessage" value="CONST.successCheckoutOrderNumberMessage"/> <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml index a63fcec9f92bb..c8f2263d9734a 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml @@ -28,7 +28,7 @@ <arguments> <argument name="couponCode" type="string"/> </arguments> - <waitForElement selector="{{StorefrontDiscountSection.discountTab}}" time="30" stepKey="waitForCouponHeader" /> + <waitForElementVisible selector="{{StorefrontDiscountSection.discountTab}}" time="30" stepKey="waitForCouponHeader" /> <conditionalClick selector="{{StorefrontDiscountSection.discountTab}}" dependentSelector="{{StorefrontDiscountSection.discountBlockActive}}" visible="false" stepKey="clickCouponHeader" /> <waitForElementVisible selector="{{StorefrontDiscountSection.couponInput}}" stepKey="waitForCouponField" /> <fillField userInput="{{couponCode}}" selector="{{StorefrontDiscountSection.couponInput}}" stepKey="fillCouponField"/> @@ -47,7 +47,7 @@ <!-- Cancel Sales Rule Coupon applied to the cart --> <actionGroup name="StorefrontCancelCouponActionGroup"> - <waitForElement selector="{{StorefrontDiscountSection.discountTab}}" time="30" stepKey="waitForCouponHeader" /> + <waitForElementVisible selector="{{StorefrontDiscountSection.discountTab}}" time="30" stepKey="waitForCouponHeader" /> <conditionalClick selector="{{StorefrontDiscountSection.discountTab}}" dependentSelector="{{AdminCartPriceRuleDiscountSection.discountBlockActive}}" visible="false" stepKey="clickCouponHeader" /> <waitForElementVisible selector="{{StorefrontDiscountSection.couponInput}}" stepKey="waitForCouponField" /> <click selector="{{StorefrontDiscountSection.cancelCoupon}}" stepKey="clickCancelButton"/> @@ -61,6 +61,7 @@ </arguments> <waitForElementVisible selector="{{StorefrontDiscountSection.discountInput}}" stepKey="waitForCouponField"/> <fillField userInput="{{couponCode}}" selector="{{StorefrontDiscountSection.discountInput}}" stepKey="fillCouponField"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.successMessage}}" stepKey="waitForSuccessMessage" /> <see userInput='{{successMessage}}' selector="{{StorefrontMessagesSection.successMessage}}" stepKey="seeSuccessMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/StorefrontDiscountSection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/StorefrontDiscountSection.xml index 359e2eecb6b7e..582e50cc766e0 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Section/StorefrontDiscountSection.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/StorefrontDiscountSection.xml @@ -10,8 +10,8 @@ <section name="StorefrontDiscountSection"> <element name="discountTab" type="button" selector="#block-discount-heading"/> <element name="couponInput" type="input" selector="#coupon_code"/> - <element name="applyCodeBtn" type="button" selector="//span[text()='Apply Discount']"/> - <element name="cancelCoupon" type="button" selector="//button[@value='Cancel Coupon']"/> + <element name="applyCodeBtn" type="button" selector="button[value='Apply Discount']"/> + <element name="cancelCoupon" type="button" selector="button[value='Cancel Coupon']"/> <element name="discountInput" type="input" selector="#discount-code"/> <element name="discountBlockActive" type="text" selector=".block.discount.active"/> </section> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreShippingMethodsData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreShippingMethodsData.xml index 6b29baeb9eea3..c9d0fb628b853 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreShippingMethodsData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreShippingMethodsData.xml @@ -14,14 +14,14 @@ <data key="value">1</data> </entity> - <entity name="MinimumOrderAmount90" type="minimum_order_amount"> + <entity name="MinimumOrderAmount90" type="free_shipping_config_state"> <requiredEntity type="free_shipping_subtotal">Price</requiredEntity> </entity> <entity name="Price" type="free_shipping_subtotal"> <data key="value">90</data> </entity> - <entity name="DefaultMinimumOrderAmount" type="minimum_order_amount"> + <entity name="DefaultMinimumOrderAmount" type="free_shipping_config_state"> <requiredEntity type="free_shipping_subtotal">DefaultPrice</requiredEntity> </entity> <entity name="DefaultPrice" type="free_shipping_subtotal"> @@ -29,9 +29,12 @@ </entity> <entity name="DefaultShippingMethodsConfig" type="free_shipping_config_state"> - <requiredEntity type="active">DefaultFreeShipping</requiredEntity> + <requiredEntity type="active">DefaultActive</requiredEntity> </entity> - <entity name="DefaultFreeShipping" type="active"> + <entity name="DefaultActive" type="active"> + <requiredEntity type="active_inherit">DefaultFreeShipping</requiredEntity> + </entity> + <entity name="DefaultFreeShipping" type="active_inherit"> <data key="value">0</data> </entity> </entities> diff --git a/app/code/Magento/Store/Test/Mftf/Metadata/store_shipping_methods-meta.xml b/app/code/Magento/Store/Test/Mftf/Metadata/store_shipping_methods-meta.xml index 46132973e52c8..32aa22fecf9cd 100644 --- a/app/code/Magento/Store/Test/Mftf/Metadata/store_shipping_methods-meta.xml +++ b/app/code/Magento/Store/Test/Mftf/Metadata/store_shipping_methods-meta.xml @@ -8,39 +8,17 @@ <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> - <operation name="EnableFreeShippingConfigState" dataType="free_shipping_config_state" type="create" auth="adminFormKey" - url="/admin/system_config/save/section/carriers/" method="POST"> + <operation name="ChangeFreeShippingConfiguration" dataType="free_shipping_config_state" type="create" auth="adminFormKey" + url="/admin/system_config/save/section/carriers/" method="POST" successRegex="/messages-message-success/"> <object key="groups" dataType="free_shipping_config_state"> <object key="freeshipping" dataType="free_shipping_config_state"> <object key="fields" dataType="free_shipping_config_state"> <object key="active" dataType="active"> <field key="value">string</field> - </object> - </object> - </object> - </object> - </operation> - - <operation name="DisableFreeShippingConfigState" dataType="disable_free_shipping_config_state" type="create" auth="adminFormKey" - url="/admin/system_config/save/section/carriers/" method="POST"> - <object key="groups" dataType="disable_free_shipping_config_state"> - <object key="freeshipping" dataType="disable_free_shipping_config_state"> - <object key="fields" dataType="disable_free_shipping_config_state"> - <object key="active" dataType="disable_free_shipping_config_state"> - <object key="inherit" dataType="disableFreeShipping"> + <object key="inherit" dataType="active_inherit"> <field key="value">integer</field> </object> </object> - </object> - </object> - </object> - </operation> - - <operation name="MinimumOrderAmount" dataType="minimum_order_amount" type="create" auth="adminFormKey" - url="/admin/system_config/save/section/carriers/" method="POST"> - <object key="groups" dataType="minimum_order_amount"> - <object key="freeshipping" dataType="minimum_order_amount"> - <object key="fields" dataType="minimum_order_amount"> <object key="free_shipping_subtotal" dataType="free_shipping_subtotal"> <field key="value">string</field> </object> From 8a4cfa4eac8b5d18d1c9b30655dbacabce9d9d53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Thu, 14 Mar 2019 17:15:44 +0100 Subject: [PATCH 0935/1295] Don't check default locale if Magento is not installed, fix NumberFormatter initialization when no currency is set --- .../Magento/Framework/Locale/Format.php | 2 +- .../Magento/Framework/Locale/Resolver.php | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/lib/internal/Magento/Framework/Locale/Format.php b/lib/internal/Magento/Framework/Locale/Format.php index ca50cdb2440f4..0cdd80208fbcf 100644 --- a/lib/internal/Magento/Framework/Locale/Format.php +++ b/lib/internal/Magento/Framework/Locale/Format.php @@ -100,7 +100,7 @@ public function getPriceFormat($localeCode = null, $currencyCode = null) } $formatter = new \NumberFormatter( - $localeCode . '@currency=' . $currency->getCode(), + $currency->getCode() ? $localeCode . '@currency=' . $currency->getCode() : $localeCode, \NumberFormatter::CURRENCY ); $format = $formatter->getPattern(); diff --git a/lib/internal/Magento/Framework/Locale/Resolver.php b/lib/internal/Magento/Framework/Locale/Resolver.php index 8372908a380ff..32c4f035ac954 100644 --- a/lib/internal/Magento/Framework/Locale/Resolver.php +++ b/lib/internal/Magento/Framework/Locale/Resolver.php @@ -6,6 +6,8 @@ namespace Magento\Framework\Locale; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\App\ObjectManager; class Resolver implements ResolverInterface { @@ -47,21 +49,29 @@ class Resolver implements ResolverInterface */ protected $emulatedLocales = []; + /** + * @var DeploymentConfig + */ + private $deploymentConfig; + /** * @param ScopeConfigInterface $scopeConfig * @param string $defaultLocalePath * @param string $scopeType * @param mixed $locale + * @param DeploymentConfig|null $deploymentConfig */ public function __construct( ScopeConfigInterface $scopeConfig, $defaultLocalePath, $scopeType, - $locale = null + $locale = null, + DeploymentConfig $deploymentConfig = null ) { $this->scopeConfig = $scopeConfig; $this->defaultLocalePath = $defaultLocalePath; $this->scopeType = $scopeType; + $this->deploymentConfig = $deploymentConfig ?: ObjectManager::getInstance()->create(DeploymentConfig::class); $this->setLocale($locale); } @@ -88,7 +98,10 @@ public function setDefaultLocale($locale) public function getDefaultLocale() { if (!$this->defaultLocale) { - $locale = $this->scopeConfig->getValue($this->getDefaultLocalePath(), $this->scopeType); + $locale = false; + if ($this->deploymentConfig->isAvailable() && $this->deploymentConfig->isDbAvailable()) { + $locale = $this->scopeConfig->getValue($this->getDefaultLocalePath(), $this->scopeType); + } if (!$locale) { $locale = self::DEFAULT_LOCALE; } From 43cc9a4d3ab36e292382ca06c8256b47452cfed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Tue, 19 Mar 2019 00:33:17 +0100 Subject: [PATCH 0936/1295] Fix unit test --- lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php index aa7ca377efa03..f6d7326f52764 100644 --- a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php +++ b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php @@ -53,6 +53,7 @@ protected function setUp() /** @var \Magento\Directory\Model\CurrencyFactory|\PHPUnit_Framework_MockObject_MockObject $currencyFactory */ $currencyFactory = $this->getMockBuilder(\Magento\Directory\Model\CurrencyFactory::class) + ->disableOriginalConstructor() ->getMock(); $this->formatModel = new \Magento\Framework\Locale\Format( From 0add8b722cb1591a3a2b8fe74e20d492431f5b18 Mon Sep 17 00:00:00 2001 From: RomanKis <roman.kis.y@gmail.com> Date: Tue, 19 Mar 2019 09:50:14 +0200 Subject: [PATCH 0937/1295] #21734 Error in JS validation rule --- app/code/Magento/Catalog/etc/adminhtml/system.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/etc/adminhtml/system.xml b/app/code/Magento/Catalog/etc/adminhtml/system.xml index 9c99a72c12d1c..6a432c1809ba5 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/system.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/system.xml @@ -56,7 +56,7 @@ <field id="grid_per_page_values" translate="label comment" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Products per Page on Grid Allowed Values</label> <comment>Comma-separated.</comment> - <validate>validate-per-page-value-list</validate> + <validate>validate-per-page-value-list required-entry</validate> </field> <field id="grid_per_page" translate="label comment" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Products per Page on Grid Default Value</label> @@ -66,7 +66,7 @@ <field id="list_per_page_values" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Products per Page on List Allowed Values</label> <comment>Comma-separated.</comment> - <validate>validate-per-page-value-list</validate> + <validate>validate-per-page-value-list required-entry</validate> </field> <field id="list_per_page" translate="label comment" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Products per Page on List Default Value</label> From d5e40099b688e1265a1281df2f0c54d10128fbbf Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Tue, 19 Mar 2019 11:24:28 +0200 Subject: [PATCH 0938/1295] MC-15097: Incorrect Prolong responses --- .../src/Magento/Setup/Controller/Session.php | 50 +++++++++------ .../Test/Unit/Controller/SessionTest.php | 62 ++++++++++++++----- 2 files changed, 78 insertions(+), 34 deletions(-) diff --git a/setup/src/Magento/Setup/Controller/Session.php b/setup/src/Magento/Setup/Controller/Session.php index e310dd485ace5..adcbe1f5e0c49 100644 --- a/setup/src/Magento/Setup/Controller/Session.php +++ b/setup/src/Magento/Setup/Controller/Session.php @@ -5,6 +5,9 @@ */ namespace Magento\Setup\Controller; +/** + * Sets up session for setup/index.php/session/prolong or redirects to error page. + */ class Session extends \Zend\Mvc\Controller\AbstractActionController { /** @@ -30,7 +33,7 @@ public function __construct( } /** - * No index action, return 404 error page + * No index action, return 404 error page. * * @return \Zend\View\Model\ViewModel|\Zend\Http\Response */ @@ -39,11 +42,12 @@ public function indexAction() $view = new \Zend\View\Model\ViewModel(); $view->setTemplate('/error/404.phtml'); $this->getResponse()->setStatusCode(\Zend\Http\Response::STATUS_CODE_404); + return $view; } /** - * Prolong session + * Prolong session. * * @return string */ @@ -52,32 +56,41 @@ public function prolongAction() try { if ($this->serviceManager->get(\Magento\Framework\App\DeploymentConfig::class)->isAvailable()) { $objectManager = $this->objectManagerProvider->get(); - /** @var \Magento\Framework\App\State $adminAppState */ - $adminAppState = $objectManager->get(\Magento\Framework\App\State::class); - $adminAppState->setAreaCode(\Magento\Framework\App\Area::AREA_ADMINHTML); - $sessionConfig = $objectManager->get(\Magento\Backend\Model\Session\AdminConfig::class); - /** @var \Magento\Backend\Model\Url $backendUrl */ - $backendUrl = $objectManager->get(\Magento\Backend\Model\Url::class); - $urlPath = parse_url($backendUrl->getBaseUrl(), PHP_URL_PATH); - $cookiePath = $urlPath . 'setup'; - $sessionConfig->setCookiePath($cookiePath); /* @var \Magento\Backend\Model\Auth\Session $session */ - $session = $objectManager->create( - \Magento\Backend\Model\Auth\Session::class, - [ - 'sessionConfig' => $sessionConfig, - 'appState' => $adminAppState - ] - ); + $session = $objectManager->get(\Magento\Backend\Model\Auth\Session::class); + // check if session was already set in \Magento\Setup\Mvc\Bootstrap\InitParamListener::authPreDispatch + if (!$session->isSessionExists()) { + /** @var \Magento\Framework\App\State $adminAppState */ + $adminAppState = $objectManager->get(\Magento\Framework\App\State::class); + $adminAppState->setAreaCode(\Magento\Framework\App\Area::AREA_ADMINHTML); + $sessionConfig = $objectManager->get(\Magento\Backend\Model\Session\AdminConfig::class); + /** @var \Magento\Backend\Model\Url $backendUrl */ + $backendUrl = $objectManager->get(\Magento\Backend\Model\Url::class); + $urlPath = parse_url($backendUrl->getBaseUrl(), PHP_URL_PATH); + $cookiePath = $urlPath . 'setup'; + $sessionConfig->setCookiePath($cookiePath); + /* @var \Magento\Backend\Model\Auth\Session $session */ + $session = $objectManager->create( + \Magento\Backend\Model\Auth\Session::class, + [ + 'sessionConfig' => $sessionConfig, + 'appState' => $adminAppState + ] + ); + } $session->prolong(); + return new \Zend\View\Model\JsonModel(['success' => true]); } } catch (\Exception $e) { } + return new \Zend\View\Model\JsonModel(['success' => false]); } /** + * Unlogin action, return 401 error page. + * * @return \Zend\View\Model\ViewModel|\Zend\Http\Response */ public function unloginAction() @@ -85,6 +98,7 @@ public function unloginAction() $view = new \Zend\View\Model\ViewModel(); $view->setTemplate('/error/401.phtml'); $this->getResponse()->setStatusCode(\Zend\Http\Response::STATUS_CODE_401); + return $view; } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/SessionTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/SessionTest.php index f8e5e7cdc4d70..18d299cec37a1 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/SessionTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/SessionTest.php @@ -8,6 +8,9 @@ use \Magento\Setup\Controller\Session; +/** + * Unit test for \Magento\Setup\Controller\Session. + */ class SessionTest extends \PHPUnit\Framework\TestCase { /** @@ -16,15 +19,18 @@ class SessionTest extends \PHPUnit\Framework\TestCase private $objectManager; /** - * @var \PHPUnit_Framework_MockObject_MockObject| \Magento\Setup\Model\ObjectManagerProvider + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Setup\Model\ObjectManagerProvider */ private $objectManagerProvider; /** - * @var \Zend\ServiceManager\ServiceManager + * @var \PHPUnit_Framework_MockObject_MockObject|\Zend\ServiceManager\ServiceManager */ private $serviceManager; + /** + * @inheritdoc + */ public function setUp() { $objectManager = @@ -41,37 +47,40 @@ public function setUp() */ public function testUnloginAction() { - $this->objectManagerProvider->expects($this->once())->method('get')->will( - $this->returnValue($this->objectManager) + $this->objectManagerProvider->expects($this->once())->method('get')->willReturn($this->objectManager); + $deployConfigMock = $this->createPartialMock(\Magento\Framework\App\DeploymentConfig::class, ['isAvailable']); + $deployConfigMock->expects($this->once())->method('isAvailable')->willReturn(true); + + $sessionMock = $this->createPartialMock( + \Magento\Backend\Model\Auth\Session::class, + ['prolong', 'isSessionExists'] ); - $deployConfigMock = - $this->createPartialMock(\Magento\Framework\App\DeploymentConfig::class, ['isAvailable']); - $deployConfigMock->expects($this->once())->method('isAvailable')->will($this->returnValue(true)); + $sessionMock->expects($this->once())->method('isSessionExists')->willReturn(false); $stateMock = $this->createPartialMock(\Magento\Framework\App\State::class, ['setAreaCode']); $stateMock->expects($this->once())->method('setAreaCode'); - $sessionConfigMock = - $this->createPartialMock(\Magento\Backend\Model\Session\AdminConfig::class, ['setCookiePath']); + $sessionConfigMock = $this->createPartialMock( + \Magento\Backend\Model\Session\AdminConfig::class, + ['setCookiePath'] + ); $sessionConfigMock->expects($this->once())->method('setCookiePath'); $urlMock = $this->createMock(\Magento\Backend\Model\Url::class); $returnValueMap = [ + [\Magento\Backend\Model\Auth\Session::class, $sessionMock], [\Magento\Framework\App\State::class, $stateMock], [\Magento\Backend\Model\Session\AdminConfig::class, $sessionConfigMock], - [\Magento\Backend\Model\Url::class, $urlMock] + [\Magento\Backend\Model\Url::class, $urlMock], ]; - $this->serviceManager->expects($this->once())->method('get')->will($this->returnValue($deployConfigMock)); + $this->serviceManager->expects($this->once())->method('get')->willReturn($deployConfigMock); $this->objectManager->expects($this->atLeastOnce()) ->method('get') - ->will($this->returnValueMap($returnValueMap)); + ->willReturnMap($returnValueMap); - $sessionMock = $this->createPartialMock(\Magento\Backend\Model\Auth\Session::class, ['prolong']); - $this->objectManager->expects($this->once()) - ->method('create') - ->will($this->returnValue($sessionMock)); + $this->objectManager->expects($this->once())->method('create')->willReturn($sessionMock); $controller = new Session($this->serviceManager, $this->objectManagerProvider); $urlMock->expects($this->once())->method('getBaseUrl'); $controller->prolongAction(); @@ -87,4 +96,25 @@ public function testIndexAction() $viewModel = $controller->unloginAction(); $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); } + + /** + * @covers \Magento\Setup\Controller\SystemConfig::prolongAction + */ + public function testProlongActionWithExistingSession() + { + $this->objectManagerProvider->expects($this->once())->method('get')->willReturn($this->objectManager); + $deployConfigMock = $this->createPartialMock(\Magento\Framework\App\DeploymentConfig::class, ['isAvailable']); + $deployConfigMock->expects($this->once())->method('isAvailable')->willReturn(true); + $sessionMock = $this->createPartialMock( + \Magento\Backend\Model\Auth\Session::class, + ['prolong', 'isSessionExists'] + ); + $sessionMock->expects($this->once())->method('isSessionExists')->willReturn(true); + + $this->serviceManager->expects($this->once())->method('get')->willReturn($deployConfigMock); + $this->objectManager->expects($this->once())->method('get')->willReturn($sessionMock); + + $controller = new Session($this->serviceManager, $this->objectManagerProvider); + $this->assertEquals(new \Zend\View\Model\JsonModel(['success' => true]), $controller->prolongAction()); + } } From 3dea8d0f34f9e4d033a7a82a22cbe93f418635ff Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Tue, 19 Mar 2019 12:11:31 +0200 Subject: [PATCH 0939/1295] ENGCOM-4513: Static test fix. --- .../Wishlist/view/frontend/templates/item/column/review.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/review.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/review.phtml index 9120cc9fa684e..3fd492233bdd5 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/review.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/review.phtml @@ -7,4 +7,4 @@ /** @var \Magento\Wishlist\Block\Customer\Wishlist\Item\Column $block */ $product = $block->getItem()->getProduct(); ?> -<?= $block->getReviewsSummaryHtml($product, 'short') ?> +<?= $block->getReviewsSummaryHtml($product, 'short'); From 7b544a2278fc08f599f1ab04190adf6a1b189398 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Tue, 19 Mar 2019 16:23:54 +0200 Subject: [PATCH 0940/1295] MAGETWO-98380: The required product attribute is not displayed when change attribute set --- .../Test/Mftf/ActionGroup/AdminProductActionGroup.xml | 7 ++++--- .../Catalog/Test/Mftf/Section/AdminProductFormSection.xml | 1 + .../Test/Mftf/Test/AdminChangeProductAttributeSetTest.xml | 6 ++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index 6a310e60994f8..8ef25d6b97408 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -263,8 +263,9 @@ <argument name="attributeSet"/> </arguments> <scrollToTopOfPage stepKey="scrollToTop"/> - <click selector="{{AdminProductFormSection.attributeSet}}" stepKey="startEditAttrSet"/> - <fillField selector="{{AdminProductFormSection.attributeSetFilter}}" userInput="{{attributeSet.attribute_set_name}}" stepKey="searchForAttrSet"/> - <click selector="{{AdminProductFormSection.attributeSetFilterResultByName(attributeSet.attribute_set_name)}}" stepKey="selectAttrSet"/> + <click selector="{{AdminProductFormSection.attributeSet}}" stepKey="startEditAttributeSet"/> + <fillField selector="{{AdminProductFormSection.attributeSetFilter}}" userInput="{{attributeSet.attribute_set_name}}" stepKey="searchForAttributeSet"/> + <waitForElementVisible selector="{{AdminProductFormSection.attributeSetFilterResultByName(attributeSet.attribute_set_name)}}" stepKey="waitForNewAttributeSetIsShown"/> + <click selector="{{AdminProductFormSection.attributeSetFilterResultByName(attributeSet.attribute_set_name)}}" stepKey="selectAttributeSet"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml index 50b6fe6b3ec9e..2255f3fbbb3fb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml @@ -41,6 +41,7 @@ <element name="requiredNameIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=name]>.admin__field-label span'), ':after').getPropertyValue('content');"/> <element name="requiredSkuIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('._required[data-index=sku]>.admin__field-label span'), ':after').getPropertyValue('content');"/> <element name="customAttributeDropdownField" type="select" selector="select[name='product[{{attributeCode}}]']" parameterized="true"/> + <element name="customAttributeInputField" type="select" selector="input[name='product[{{attributeCode}}]']" parameterized="true"/> </section> <section name="ProductInWebsitesSection"> <element name="sectionHeader" type="button" selector="div[data-index='websites']" timeout="30"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSetTest.xml index 0a9d5fa3857f5..01967fa1e0851 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSetTest.xml @@ -10,7 +10,7 @@ <test name="AdminChangeProductAttributeSetTest"> <annotations> <features value="Catalog"/> - <stories value="Update product attribute set"/> + <stories value="Update product"/> <title value="Attributes from the selected attribute set should be shown"/> <description value="Attributes from the selected attribute set should be shown"/> <severity value="CRITICAL"/> @@ -47,11 +47,13 @@ </after> <!--Open created product--> <amOnPage url="{{AdminProductEditPage.url($$createSimpleProduct.id$$)}}" stepKey="openProductEditPage"/> + <waitForPageLoad time="30" stepKey="waitForProductPageIsLoaded"/> + <dontSeeElement selector="{{AdminProductFormSection.customAttributeInputField($$createProductAttribute.attribute_code$$)}}" stepKey="dontSeeCreatedAttribute"/> <!--Change product attribute set--> <actionGroup ref="AdminChangeProductAttributeSet" stepKey="changeProductAttributeSet"> <argument name="attributeSet" value="$$createAttributeSet$$"/> </actionGroup> <!--Check new attribute is visible on product edit page--> - <waitForText userInput="$$createProductAttribute.default_frontend_label$$" stepKey="seeAttributeInForm"/> + <seeElement selector="{{AdminProductFormSection.customAttributeInputField($$createProductAttribute.attribute_code$$)}}" stepKey="seeAttributeInForm"/> </test> </tests> From 079ecb3689e76e1d5ed85de20f19b59eb347cd66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Fri, 8 Mar 2019 13:48:06 +0200 Subject: [PATCH 0941/1295] remove refactored code not needed with correct html classes --- .../css/source/module/checkout/_fields.less | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_fields.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_fields.less index 4479c070a4e17..8dec680b58726 100644 --- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_fields.less +++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_fields.less @@ -55,31 +55,3 @@ } } } - -// -// Desktop -// _____________________________________________ - -.media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) { - // ToDo UI: remove with global blank theme .field.required update - .opc-wrapper { - .fieldset { - > .field { - &.required, - &._required { - position: relative; - - > label { - padding-right: 25px; - - &:after { - margin-left: @indent__s; - position: absolute; - top: 9px; - } - } - } - } - } - } -} From 943fb3374c379c9d00236251d6901a371ee98682 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Fri, 8 Mar 2019 13:52:26 +0200 Subject: [PATCH 0942/1295] fix asterisk added correct class for asterisk --- .../frontend/web/template/checkout/checkout-agreements.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html b/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html index a448537d64e83..db9da71ebf739 100644 --- a/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html +++ b/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html @@ -5,17 +5,17 @@ */ --> <div data-role="checkout-agreements"> - <div class="checkout-agreements" data-bind="visible: isVisible"> + <div class="checkout-agreements fieldset" data-bind="visible: isVisible"> <!-- ko foreach: agreements --> <!-- ko if: ($parent.isAgreementRequired($data)) --> - <div class="checkout-agreement required"> + <div class="checkout-agreement field required"> <input type="checkbox" class="required-entry" data-bind="attr: { 'id': $parent.getCheckboxId($parentContext, agreementId), 'name': 'agreement[' + agreementId + ']', 'value': agreementId }"/> - <label data-bind="attr: {'for': $parent.getCheckboxId($parentContext, agreementId)}"> + <label class="label" data-bind="attr: {'for': $parent.getCheckboxId($parentContext, agreementId)}"> <button type="button" class="action action-show" data-bind="click: function(data, event) { return $parent.showContent(data, event) }" From 745df9a3c45a38bbb6da3bfc81d01f7ad2f5563b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Wed, 13 Mar 2019 11:38:58 +0200 Subject: [PATCH 0943/1295] add choice class --- .../frontend/web/template/checkout/checkout-agreements.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html b/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html index db9da71ebf739..4b1a68624e547 100644 --- a/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html +++ b/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html @@ -8,7 +8,7 @@ <div class="checkout-agreements fieldset" data-bind="visible: isVisible"> <!-- ko foreach: agreements --> <!-- ko if: ($parent.isAgreementRequired($data)) --> - <div class="checkout-agreement field required"> + <div class="checkout-agreement field choice required"> <input type="checkbox" class="required-entry" data-bind="attr: { 'id': $parent.getCheckboxId($parentContext, agreementId), From 8c609c4f546316d3726594cb8ea71f1e91a6594f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Wed, 13 Mar 2019 11:53:26 +0200 Subject: [PATCH 0944/1295] fix choice aligment --- .../web/css/source/module/checkout/_payments.less | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_payments.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_payments.less index 5f8134193c67f..35445b0989e86 100644 --- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_payments.less +++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_payments.less @@ -209,6 +209,13 @@ .fieldset { > .field { margin: 0 0 @indent__base; + + &.choice { + &:before { + padding: 0; + width: 0; + } + } &.type { .control { From 0039acbbd55e29aa881408ff443765f7420f8bde Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Tue, 19 Mar 2019 10:17:36 -0500 Subject: [PATCH 0945/1295] MAGETWO-98679: Discounts of products disappear on storefront after drag & drop via Visual Merchandiser in category --- .../Model/Indexer/IndexBuilder.php | 143 ++++++++---------- .../Indexer/RuleProductPricesPersistor.php | 30 ++-- app/code/Magento/CatalogRule/Model/Rule.php | 1 - 3 files changed, 72 insertions(+), 102 deletions(-) diff --git a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php index 94d13e1ec1e81..e2a5e1aafc3b2 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php @@ -7,6 +7,7 @@ namespace Magento\CatalogRule\Model\Indexer; use Magento\Catalog\Model\Product; +use Magento\CatalogRule\Model\ResourceModel\Rule\Collection as RuleCollection; use Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory as RuleCollectionFactory; use Magento\CatalogRule\Model\Rule; use Magento\Framework\App\ObjectManager; @@ -263,14 +264,11 @@ public function reindexByIds(array $ids) */ protected function doReindexByIds($ids) { - foreach ($this->getInactiveRules() as $rule) { - $this->cleanProductIndex($ids, $rule->getId()); - $this->cleanProductPriceIndex($ids, $rule->getId()); - } + $this->cleanByIds($ids); $products = $this->productLoader->getProducts($ids); - foreach ($this->getActiveRules() as $rule) { - foreach ($products as $product) { + foreach ($products as $product) { + foreach ($this->getActiveRules() as $rule) { $this->applyRule($rule, $product); } } @@ -321,36 +319,22 @@ protected function doReindexFull() /** * Clean product index * - * @param array|int $productIds - * @param int|null $ruleId + * @param array $productIds */ - private function cleanProductIndex($productIds, int $ruleId = null) + private function cleanProductIndex(array $productIds) { - $productIds = (array) $productIds; - $where = count($productIds) > 1 - ? ['product_id IN (?)' => $productIds] - : ['product_id = ?' => $productIds]; - if ($ruleId) { - $where['rule_id = ?'] = $ruleId; - } + $where = ['product_id IN (?)' => $productIds]; $this->connection->delete($this->getTable('catalogrule_product'), $where); } /** * Clean product price index * - * @param array|int $productIds - * @param int|null $ruleId + * @param array $productIds */ - private function cleanProductPriceIndex($productIds, int $ruleId = null) + private function cleanProductPriceIndex(array $productIds) { - $productIds = (array) $productIds; - $where = count($productIds) > 1 - ? ['product_id IN (?)' => $productIds] - : ['product_id = ?' => $productIds]; - if ($ruleId) { - $where['rule_id = ?'] = $ruleId; - } + $where = ['product_id IN (?)' => $productIds]; $this->connection->delete($this->getTable('catalogrule_product_price'), $where); } @@ -367,6 +351,8 @@ protected function cleanByIds($productIds) } /** + * Apply rule + * * @param Rule $rule * @param Product $product * @return $this @@ -380,11 +366,17 @@ protected function applyRule(Rule $rule, $product) $websiteIds = array_intersect($product->getWebsiteIds(), $rule->getWebsiteIds()); if (!$rule->validate($product)) { - $this->cleanProductIndex($productEntityId, $ruleId); - $this->cleanProductPriceIndex($productEntityId, $ruleId); return $this; } + $this->connection->delete( + $this->resource->getTableName('catalogrule_product'), + [ + $this->connection->quoteInto('rule_id = ?', $ruleId), + $this->connection->quoteInto('product_id = ?', $productEntityId) + ] + ); + $customerGroupIds = $rule->getCustomerGroupIds(); $fromTime = strtotime($rule->getFromDate()); $toTime = strtotime($rule->getToDate()); @@ -395,60 +387,41 @@ protected function applyRule(Rule $rule, $product) $actionStop = $rule->getStopRulesProcessing(); $rows = []; - $ruleProductTable = $this->getTable('catalogrule_product'); - try { - $this->connection->beginTransaction(); - - $this->cleanProductIndex($productEntityId, $ruleId); - - foreach ($websiteIds as $websiteId) { - foreach ($customerGroupIds as $customerGroupId) { - $rows[] = [ - 'rule_id' => $ruleId, - 'from_time' => $fromTime, - 'to_time' => $toTime, - 'website_id' => $websiteId, - 'customer_group_id' => $customerGroupId, - 'product_id' => $productEntityId, - 'action_operator' => $actionOperator, - 'action_amount' => $actionAmount, - 'action_stop' => $actionStop, - 'sort_order' => $sortOrder, - ]; - - if (count($rows) == $this->batchCount) { - $this->connection->insertMultiple($ruleProductTable, $rows); - $rows = []; - } + foreach ($websiteIds as $websiteId) { + foreach ($customerGroupIds as $customerGroupId) { + $rows[] = [ + 'rule_id' => $ruleId, + 'from_time' => $fromTime, + 'to_time' => $toTime, + 'website_id' => $websiteId, + 'customer_group_id' => $customerGroupId, + 'product_id' => $productEntityId, + 'action_operator' => $actionOperator, + 'action_amount' => $actionAmount, + 'action_stop' => $actionStop, + 'sort_order' => $sortOrder, + ]; + + if (count($rows) == $this->batchCount) { + $this->connection->insertMultiple($this->getTable('catalogrule_product'), $rows); + $rows = []; } } - - if (!empty($rows)) { - $this->connection->insertMultiple($ruleProductTable, $rows); - unset($rows); - } - $this->connection->commit(); - } catch (\Exception $e) { - $this->connection->rollBack(); - throw $e; } - - try { - $this->connection->beginTransaction(); - $this->cleanProductPriceIndex($productEntityId, $ruleId); - $this->reindexRuleProductPrice->execute($this->batchCount, $product); - $this->connection->commit(); - } catch (\Exception $e) { - $this->connection->rollBack(); - throw $e; + if (!empty($rows)) { + $this->connection->insertMultiple($this->resource->getTableName('catalogrule_product'), $rows); + unset($rows); } + $this->reindexRuleProductPrice->execute($this->batchCount, $product); $this->reindexRuleGroupWebsite->execute(); return $this; } /** + * Retrieve table name + * * @param string $tableName * @return string */ @@ -458,6 +431,8 @@ protected function getTable($tableName) } /** + * Update rule product data + * * @param Rule $rule * @return $this * @deprecated 100.2.0 @@ -483,6 +458,8 @@ protected function updateRuleProductData(Rule $rule) } /** + * Apply all rules + * * @param Product|null $product * @throws \Exception * @return $this @@ -522,6 +499,8 @@ protected function deleteOldData() } /** + * Calculate rule product price + * * @param array $ruleData * @param null $productData * @return float @@ -534,6 +513,8 @@ protected function calcRuleProductPrice($ruleData, $productData = null) } /** + * Get rule products statement + * * @param int $websiteId * @param Product|null $product * @return \Zend_Db_Statement_Interface @@ -547,6 +528,8 @@ protected function getRuleProductsStmt($websiteId, Product $product = null) } /** + * Save rule product prices + * * @param array $arrData * @return $this * @throws \Exception @@ -562,27 +545,17 @@ protected function saveRuleProductPrices($arrData) /** * Get active rules * - * @return \Magento\CatalogRule\Model\ResourceModel\Rule\Collection + * @return RuleCollection */ protected function getActiveRules() { return $this->ruleCollectionFactory->create()->addFieldToFilter('is_active', 1); } - /** - * Get inactive rules - * - * @return \Magento\CatalogRule\Model\ResourceModel\Rule\Collection - */ - private function getInactiveRules() - { - return $this->ruleCollectionFactory->create()->addFieldToFilter('is_active', 0); - } - /** * Get active rules * - * @return array + * @return RuleCollection */ protected function getAllRules() { @@ -590,6 +563,8 @@ protected function getAllRules() } /** + * Get product + * * @param int $productId * @return Product */ @@ -602,6 +577,8 @@ protected function getProduct($productId) } /** + * Log critical exception + * * @param \Exception $e * @return void */ diff --git a/app/code/Magento/CatalogRule/Model/Indexer/RuleProductPricesPersistor.php b/app/code/Magento/CatalogRule/Model/Indexer/RuleProductPricesPersistor.php index 537741024c5f9..9ac23e0b9158c 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/RuleProductPricesPersistor.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/RuleProductPricesPersistor.php @@ -72,25 +72,19 @@ public function execute(array $priceData, $useAdditionalTable = false) ); } - $productIds = []; - - try { - foreach ($priceData as $key => $data) { - $productIds['product_id'] = $data['product_id']; - $priceData[$key]['rule_date'] = $this->dateFormat->formatDate($data['rule_date'], false); - $priceData[$key]['latest_start_date'] = $this->dateFormat->formatDate( - $data['latest_start_date'], - false - ); - $priceData[$key]['earliest_end_date'] = $this->dateFormat->formatDate( - $data['earliest_end_date'], - false - ); - } - $connection->insertOnDuplicate($indexTable, $priceData); - } catch (\Exception $e) { - throw $e; + foreach ($priceData as $key => $data) { + $priceData[$key]['rule_date'] = $this->dateFormat->formatDate($data['rule_date'], false); + $priceData[$key]['latest_start_date'] = $this->dateFormat->formatDate( + $data['latest_start_date'], + false + ); + $priceData[$key]['earliest_end_date'] = $this->dateFormat->formatDate( + $data['earliest_end_date'], + false + ); } + $connection->insertOnDuplicate($indexTable, $priceData); + return true; } } diff --git a/app/code/Magento/CatalogRule/Model/Rule.php b/app/code/Magento/CatalogRule/Model/Rule.php index 57578bb0558b7..ebfe91504417c 100644 --- a/app/code/Magento/CatalogRule/Model/Rule.php +++ b/app/code/Magento/CatalogRule/Model/Rule.php @@ -41,7 +41,6 @@ * @method \Magento\CatalogRule\Model\Rule setFromDate(string $value) * @method \Magento\CatalogRule\Model\Rule setToDate(string $value) * @method \Magento\CatalogRule\Model\Rule setCustomerGroupIds(string $value) - * @method string getWebsiteIds() * @method \Magento\CatalogRule\Model\Rule setWebsiteIds(string $value) * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) From 0c65f5dc8917af75e159951136e934c7162873d0 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Tue, 19 Mar 2019 15:57:05 -0500 Subject: [PATCH 0946/1295] magento-engcom/magento2ce#2686: Skipped unstable test --- .../Magento/Backend/Controller/Adminhtml/DashboardTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php index 07af21505f180..20fbe2a3f3f62 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php @@ -21,6 +21,8 @@ public function testAjaxBlockAction() public function testTunnelAction() { + $this->markTestSkipped('MAGETWO-98803'); + $testUrl = \Magento\Backend\Block\Dashboard\Graph::API_URL . '?cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World'; $handle = curl_init(); curl_setopt($handle, CURLOPT_URL, $testUrl); From 9afef9930cceeceff76bb3aa9311ee56a656dbca Mon Sep 17 00:00:00 2001 From: Adarsh Khatri <me.adarshkhatri@gmail.com> Date: Wed, 27 Feb 2019 17:14:44 +1100 Subject: [PATCH 0947/1295] Update price-bundle.js By default tier price is sorted by "price_id". With this tier price calculation while displaying in bundle product is wrong. Need to sort tier price by "price_qty". Issue created: https://github.com/magento/magento2/issues/21467 --- app/code/Magento/Bundle/view/base/web/js/price-bundle.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js index e56cc6f32d804..a832390b83dc7 100644 --- a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js +++ b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js @@ -375,6 +375,11 @@ define([ var tiers = optionConfig.tierPrice, magicKey = _.keys(oneItemPrice)[0], lowest = false; + + //sorting based on "price_qty" + tiers.sort(function(a, b) { + return a.price_qty - b.price_qty; + }); _.each(tiers, function (tier, index) { if (tier['price_qty'] > qty) { From 6577c64b27fc6bb031cf9a760793d84e9b45c088 Mon Sep 17 00:00:00 2001 From: Adarsh Khatri <me.adarshkhatri@gmail.com> Date: Wed, 27 Feb 2019 19:28:16 +1100 Subject: [PATCH 0948/1295] Update price-bundle.js By default tier price is sorted by "price_id". With this tier price calculation while displaying in bundle product is wrong. Need to sort tier price by "price_qty". --- app/code/Magento/Bundle/view/base/web/js/price-bundle.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js index a832390b83dc7..d298c4d6845f0 100644 --- a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js +++ b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js @@ -377,9 +377,11 @@ define([ lowest = false; //sorting based on "price_qty" - tiers.sort(function(a, b) { - return a.price_qty - b.price_qty; - }); + if(tiers){ + tiers.sort(function (a, b) { + return a.price_qty - b.price_qty; + }); + } _.each(tiers, function (tier, index) { if (tier['price_qty'] > qty) { From 6fd1080ffac2350f378f7a3fa2a3e49b511c9019 Mon Sep 17 00:00:00 2001 From: Adarsh Khatri <me.adarshkhatri@gmail.com> Date: Thu, 28 Feb 2019 11:10:10 +1100 Subject: [PATCH 0949/1295] Update price-bundle.js --- .../Bundle/view/base/web/js/price-bundle.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js index d298c4d6845f0..ee3ab25e90875 100644 --- a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js +++ b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js @@ -376,12 +376,16 @@ define([ magicKey = _.keys(oneItemPrice)[0], lowest = false; - //sorting based on "price_qty" - if(tiers){ - tiers.sort(function (a, b) { - return a.price_qty - b.price_qty; - }); - } + //tiers is undefined when options has only one option + if (undefined == tiers) { + var firstKey = Object.keys(optionConfig)[0]; + tiers = optionConfig[firstKey].tierPrice; + } + + //sorting based on "price_qty" + tiers.sort( function (a, b) { + return a['price_qty'] - b['price_qty']; + }); _.each(tiers, function (tier, index) { if (tier['price_qty'] > qty) { From 8f085e6e1aee2b415a867d0475a65c7b4c1316e4 Mon Sep 17 00:00:00 2001 From: Adarsh Khatri <me.adarshkhatri@gmail.com> Date: Fri, 1 Mar 2019 09:55:10 +1100 Subject: [PATCH 0950/1295] Update price-bundle.js --- .../Bundle/view/base/web/js/price-bundle.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js index ee3ab25e90875..02d55d50025cc 100644 --- a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js +++ b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js @@ -374,16 +374,14 @@ define([ function applyTierPrice(oneItemPrice, qty, optionConfig) { var tiers = optionConfig.tierPrice, magicKey = _.keys(oneItemPrice)[0], + tiersFirstKey = _.keys(optionConfig)[0], lowest = false; - - //tiers is undefined when options has only one option - if (undefined == tiers) { - var firstKey = Object.keys(optionConfig)[0]; - tiers = optionConfig[firstKey].tierPrice; - } - - //sorting based on "price_qty" - tiers.sort( function (a, b) { + + if (undefined === tiers) {//tiers is undefined when options has only one option + tiers = optionConfig[tiersFirstKey].tierPrice; + } + + tiers.sort( function (a, b) {//sorting based on "price_qty" return a['price_qty'] - b['price_qty']; }); From 134bfd5636a00e68fb978c361dc78d96898c1d6d Mon Sep 17 00:00:00 2001 From: Adarsh Khatri <me.adarshkhatri@gmail.com> Date: Fri, 1 Mar 2019 10:01:05 +1100 Subject: [PATCH 0951/1295] Update price-bundle.js --- app/code/Magento/Bundle/view/base/web/js/price-bundle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js index 02d55d50025cc..c05c9ef58b938 100644 --- a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js +++ b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js @@ -381,7 +381,7 @@ define([ tiers = optionConfig[tiersFirstKey].tierPrice; } - tiers.sort( function (a, b) {//sorting based on "price_qty" + tiers.sort(function (a, b) {//sorting based on "price_qty" return a['price_qty'] - b['price_qty']; }); From a14c80439f97bb4a954e39f44c3372587f07ff5d Mon Sep 17 00:00:00 2001 From: Adarsh Khatri <me.adarshkhatri@gmail.com> Date: Fri, 1 Mar 2019 22:59:03 +1100 Subject: [PATCH 0952/1295] Update price-bundle.js --- app/code/Magento/Bundle/view/base/web/js/price-bundle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js index c05c9ef58b938..f8d2f8bc11116 100644 --- a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js +++ b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js @@ -377,7 +377,7 @@ define([ tiersFirstKey = _.keys(optionConfig)[0], lowest = false; - if (undefined === tiers) {//tiers is undefined when options has only one option + if (!tiers) {//tiers is undefined when options has only one option tiers = optionConfig[tiersFirstKey].tierPrice; } From 9529d318c284fc153b4edba9c24b428e57395b95 Mon Sep 17 00:00:00 2001 From: AleksLi <aleksliwork@gmail.com> Date: Sun, 17 Feb 2019 19:32:44 +0100 Subject: [PATCH 0953/1295] magento/magento2#12396: Total Amount cart rule without tax - Added new condition type to give user opportunity to choose the configuration. --- app/code/Magento/SalesRule/Model/Rule/Condition/Address.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/SalesRule/Model/Rule/Condition/Address.php b/app/code/Magento/SalesRule/Model/Rule/Condition/Address.php index fd5953697c7db..96867222d170a 100644 --- a/app/code/Magento/SalesRule/Model/Rule/Condition/Address.php +++ b/app/code/Magento/SalesRule/Model/Rule/Condition/Address.php @@ -58,6 +58,7 @@ public function __construct( public function loadAttributeOptions() { $attributes = [ + 'base_subtotal_with_discount' => __('Subtotal (Excl. Tax)'), 'base_subtotal' => __('Subtotal'), 'total_qty' => __('Total Items Quantity'), 'weight' => __('Total Weight'), From 362bbf239373ed70d11da43b04a861b76e5d4190 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Fri, 15 Mar 2019 13:39:25 +0200 Subject: [PATCH 0954/1295] Fix functional tests. --- .../Mtf/Client/Element/ConditionsElement.php | 2 +- .../Client/Element/SelectconditionElement.php | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectconditionElement.php diff --git a/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php b/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php index 6dbf2b1aa6a12..f532b12c61492 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php +++ b/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php @@ -285,7 +285,7 @@ protected function addCondition($type, ElementInterface $context) $newCondition->find($this->addNew, Locator::SELECTOR_XPATH)->click(); try { - $newCondition->find($this->typeNew, Locator::SELECTOR_XPATH, 'select')->setValue($type); + $newCondition->find($this->typeNew, Locator::SELECTOR_XPATH, 'selectcondition')->setValue($type); $isSetType = true; } catch (\PHPUnit_Extensions_Selenium2TestCase_WebDriverException $e) { $isSetType = false; diff --git a/dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectconditionElement.php b/dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectconditionElement.php new file mode 100644 index 0000000000000..1fe096670135d --- /dev/null +++ b/dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectconditionElement.php @@ -0,0 +1,20 @@ +<?php +/** + * Copyright © 2017 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Mtf\Client\Element; + +/** + * @inheritdoc + */ +class SelectconditionElement extends SelectElement +{ + /** + * @inheritdoc + */ + protected $optionByValue = './/option[normalize-space(.)=%s]'; +} From a5608f0d6c9c476d18f9dfe59a9053b7fac29393 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Fri, 15 Mar 2019 17:38:44 +0200 Subject: [PATCH 0955/1295] Fix functional tests. --- .../Mtf/Client/Element/ConditionsElement.php | 19 ++++++++++++++++--- .../Client/Element/SelectconditionElement.php | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php b/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php index f532b12c61492..9edd087020a72 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php +++ b/dev/tests/functional/lib/Magento/Mtf/Client/Element/ConditionsElement.php @@ -195,6 +195,13 @@ class ConditionsElement extends SimpleElement */ protected $exception; + /** + * Condition option text selector. + * + * @var string + */ + private $conditionOptionTextSelector = '//option[normalize-space(text())="%s"]'; + /** * @inheritdoc */ @@ -282,10 +289,16 @@ protected function addCondition($type, ElementInterface $context) $count = 0; do { - $newCondition->find($this->addNew, Locator::SELECTOR_XPATH)->click(); - try { - $newCondition->find($this->typeNew, Locator::SELECTOR_XPATH, 'selectcondition')->setValue($type); + $specificType = $newCondition->find( + sprintf($this->conditionOptionTextSelector, $type), + Locator::SELECTOR_XPATH + )->isPresent(); + $newCondition->find($this->addNew, Locator::SELECTOR_XPATH)->click(); + $condition = $specificType + ? $newCondition->find($this->typeNew, Locator::SELECTOR_XPATH, 'selectcondition') + : $newCondition->find($this->typeNew, Locator::SELECTOR_XPATH, 'select'); + $condition->setValue($type); $isSetType = true; } catch (\PHPUnit_Extensions_Selenium2TestCase_WebDriverException $e) { $isSetType = false; diff --git a/dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectconditionElement.php b/dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectconditionElement.php index 1fe096670135d..15a799eac5188 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectconditionElement.php +++ b/dev/tests/functional/lib/Magento/Mtf/Client/Element/SelectconditionElement.php @@ -1,6 +1,6 @@ <?php /** - * Copyright © 2017 Magento. All rights reserved. + * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ From dd3a94a7a0bd2ec81c6a58538efa0ff0882b7367 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Wed, 20 Mar 2019 10:40:09 +0200 Subject: [PATCH 0956/1295] Fixing the Pending Reviews label and adding menu for pending reviews --- app/code/Magento/Review/Block/Adminhtml/Edit.php | 4 ++-- .../Magento/Review/Controller/Adminhtml/Product/Save.php | 7 ++++++- app/code/Magento/Review/etc/acl.xml | 2 +- app/code/Magento/Review/etc/adminhtml/menu.xml | 1 + 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Review/Block/Adminhtml/Edit.php b/app/code/Magento/Review/Block/Adminhtml/Edit.php index d6868eae6fcbc..f6f0ccef9b4e7 100644 --- a/app/code/Magento/Review/Block/Adminhtml/Edit.php +++ b/app/code/Magento/Review/Block/Adminhtml/Edit.php @@ -159,13 +159,13 @@ protected function _construct() } if ($this->getRequest()->getParam('ret', false) == 'pending') { - $this->buttonList->update('back', 'onclick', 'setLocation(\'' . $this->getUrl('catalog/*/pending') . '\')'); + $this->buttonList->update('back', 'onclick', 'setLocation(\'' . $this->getUrl('review/*/pending') . '\')'); $this->buttonList->update( 'delete', 'onclick', 'deleteConfirm(' . '\'' . __( 'Are you sure you want to do this?' - ) . '\' ' . '\'' . $this->getUrl( + ) . '\', ' . '\'' . $this->getUrl( '*/*/delete', [$this->_objectId => $this->getRequest()->getParam($this->_objectId), 'ret' => 'pending'] ) . '\'' . ')' diff --git a/app/code/Magento/Review/Controller/Adminhtml/Product/Save.php b/app/code/Magento/Review/Controller/Adminhtml/Product/Save.php index 7159b1825dc4d..857f36b19a19c 100644 --- a/app/code/Magento/Review/Controller/Adminhtml/Product/Save.php +++ b/app/code/Magento/Review/Controller/Adminhtml/Product/Save.php @@ -9,9 +9,14 @@ use Magento\Framework\Controller\ResultFactory; use Magento\Framework\Exception\LocalizedException; +/** + * Save Review action. + */ class Save extends ProductController { /** + * Save Review action. + * * @return \Magento\Backend\Model\View\Result\Redirect * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ @@ -63,7 +68,7 @@ public function execute() if ($nextId) { $resultRedirect->setPath('review/*/edit', ['id' => $nextId]); } elseif ($this->getRequest()->getParam('ret') == 'pending') { - $resultRedirect->setPath('*/*/pending'); + $resultRedirect->setPath('review/*/pending'); } else { $resultRedirect->setPath('*/*/'); } diff --git a/app/code/Magento/Review/etc/acl.xml b/app/code/Magento/Review/etc/acl.xml index 397cc1cce61d6..09b80750da14d 100644 --- a/app/code/Magento/Review/etc/acl.xml +++ b/app/code/Magento/Review/etc/acl.xml @@ -17,7 +17,7 @@ <resource id="Magento_Backend::marketing"> <resource id="Magento_Backend::marketing_user_content"> <resource id="Magento_Review::reviews_all" title="Reviews" translate="title" sortOrder="10"/> - <resource id="Magento_Review::pending" title="Reviews" translate="title" sortOrder="20"/> + <resource id="Magento_Review::pending" title="Pending Reviews" translate="title" sortOrder="20"/> </resource> </resource> </resource> diff --git a/app/code/Magento/Review/etc/adminhtml/menu.xml b/app/code/Magento/Review/etc/adminhtml/menu.xml index e3532483f88af..8b56f36bce68e 100644 --- a/app/code/Magento/Review/etc/adminhtml/menu.xml +++ b/app/code/Magento/Review/etc/adminhtml/menu.xml @@ -9,6 +9,7 @@ <menu> <add id="Magento_Review::catalog_reviews_ratings_ratings" title="Rating" translate="title" module="Magento_Review" sortOrder="60" parent="Magento_Backend::stores_attributes" action="review/rating/" resource="Magento_Review::ratings"/> <add id="Magento_Review::catalog_reviews_ratings_reviews_all" title="Reviews" translate="title" module="Magento_Review" parent="Magento_Backend::marketing_user_content" sortOrder="10" action="review/product/index" resource="Magento_Review::reviews_all"/> + <add id="Magento_Review::catalog_reviews_ratings_pending" title="Pending Reviews" translate="title" module="Magento_Review" parent="Magento_Backend::marketing_user_content" sortOrder="15" action="review/product/pending" resource="Magento_Review::pending"/> <add id="Magento_Review::report_review" title="Reviews" translate="title" module="Magento_Reports" sortOrder="20" parent="Magento_Reports::report" resource="Magento_Reports::review"/> <add id="Magento_Review::report_review_customer" title="By Customers" translate="title" sortOrder="10" module="Magento_Review" parent="Magento_Review::report_review" action="reports/report_review/customer" resource="Magento_Reports::review_customer"/> <add id="Magento_Review::report_review_product" title="By Products" translate="title" sortOrder="20" module="Magento_Review" parent="Magento_Review::report_review" action="reports/report_review/product" resource="Magento_Reports::review_product"/> From ffba5a2e74210efe85e14483648335c937d2bfa7 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Wed, 20 Mar 2019 14:08:17 +0200 Subject: [PATCH 0957/1295] MAGETWO-96375: Checkout Free Shipping Recalculation after Coupon Code Added --- .../Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml index c8f2263d9734a..4cd0637e83b77 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml @@ -39,6 +39,7 @@ <!-- Apply Sales Rule Coupon to the cart --> <actionGroup name="StorefrontTryingToApplyCouponActionGroup" extends="StorefrontApplyCouponActionGroup"> + <remove keyForRemoval="waitForSuccessMessage"/> <remove keyForRemoval="seeSuccessMessage"/> <waitForElementVisible selector="{{StorefrontMessagesSection.error}}" stepKey="waitError"/> <see selector="{{StorefrontMessagesSection.error}}" userInput='The coupon code "{{couponCode}}" is not valid.' From ff01cd61b82ce883114fc31ef05693b617c16c41 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Wed, 20 Mar 2019 14:26:15 +0200 Subject: [PATCH 0958/1295] MC-15360: Improve checkout through BrainTree(Paypal) --- ...frontPayWithPaypalFromMiniCartActionGroup.xml | 16 ++++++++++++++++ .../Braintree/Test/Mftf/Data/BraintreeData.xml | 8 ++++++++ .../Test/Mftf/Metadata/braintree_config-meta.xml | 3 +++ .../Braintree/Test/Mftf/Page/ReviewOrderPage.xml | 15 +++++++++++++++ .../Mftf/Section/StorefrontMiniCartSection.xml | 15 +++++++++++++++ .../StorefrontReviewOrderItemsSection.xml | 15 +++++++++++++++ .../StorefrontReviewOrderShippingSection.xml | 14 ++++++++++++++ 7 files changed, 86 insertions(+) create mode 100644 app/code/Magento/Braintree/Test/Mftf/ActionGroup/StorefrontPayWithPaypalFromMiniCartActionGroup.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Page/ReviewOrderPage.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/StorefrontMiniCartSection.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/StorefrontReviewOrderItemsSection.xml create mode 100644 app/code/Magento/Braintree/Test/Mftf/Section/StorefrontReviewOrderShippingSection.xml diff --git a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/StorefrontPayWithPaypalFromMiniCartActionGroup.xml b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/StorefrontPayWithPaypalFromMiniCartActionGroup.xml new file mode 100644 index 0000000000000..8ad866b77e7ff --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/StorefrontPayWithPaypalFromMiniCartActionGroup.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontPayWithPaypalFromMiniCartActionGroup" extends="StorefrontViewAndEditCartFromMiniCartActionGroup"> + <remove keyForRemoval="viewAndEditCart"/> + <remove keyForRemoval="seeInCurrentUrl"/> + <waitForElementVisible selector="{{StorefrontMiniCartSection.payWithPayPal}}" stepKey="waitForPaypalButtonIsVisible"/> + <click selector="{{StorefrontMiniCartSection.payWithPayPal}}" stepKey="clickPayWithPaypalButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml b/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml index 4e8526a2b0e2b..a7730192af16e 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml @@ -44,6 +44,7 @@ <requiredEntity type="public_key">DefaultPublicKey</requiredEntity> <requiredEntity type="private_key">DefaultPrivateKey</requiredEntity> <requiredEntity type="active">DefaultActive</requiredEntity> + <requiredEntity type="active_braintree_paypal">DefaultActiveBraintreePaypal</requiredEntity> <requiredEntity type="merchant_account_id">DefaultMerchantAccountId</requiredEntity> </entity> <entity name="DefaultTitle" type="title"> @@ -67,6 +68,9 @@ <entity name="DefaultActive" type="active"> <data key="value">0</data> </entity> + <entity name="DefaultActiveBraintreePaypal" type="active_braintree_paypal"> + <data key="value">0</data> + </entity> <entity name="DefaultMerchantAccountId" type="merchant_account_id"> <data key="value"/> </entity> @@ -79,6 +83,7 @@ <requiredEntity type="public_key">EnabledPublicKey</requiredEntity> <requiredEntity type="private_key">EnabledPrivateKey</requiredEntity> <requiredEntity type="active">EnabledActive</requiredEntity> + <requiredEntity type="active_braintree_paypal">EnableActiveBraintreePaypal</requiredEntity> <requiredEntity type="merchant_account_id">EnabledMerchantAccountId</requiredEntity> </entity> <entity name="EnabledTitle" type="title"> @@ -102,6 +107,9 @@ <entity name="EnabledActive" type="active"> <data key="value">1</data> </entity> + <entity name="EnableActiveBraintreePaypal" type="active_braintree_paypal"> + <data key="value">1</data> + </entity> <entity name="EnabledMerchantAccountId" type="merchant_account_id"> <data key="value">Magneto</data> </entity> diff --git a/app/code/Magento/Braintree/Test/Mftf/Metadata/braintree_config-meta.xml b/app/code/Magento/Braintree/Test/Mftf/Metadata/braintree_config-meta.xml index 0f734e5c02d56..04b3cf38d27a7 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Metadata/braintree_config-meta.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Metadata/braintree_config-meta.xml @@ -49,6 +49,9 @@ <object key="active" dataType="active"> <field key="value">integer</field> </object> + <object key="active_braintree_paypal" dataType="active_braintree_paypal"> + <field key="value">integer</field> + </object> </object> </object> </object> diff --git a/app/code/Magento/Braintree/Test/Mftf/Page/ReviewOrderPage.xml b/app/code/Magento/Braintree/Test/Mftf/Page/ReviewOrderPage.xml new file mode 100644 index 0000000000000..96eff7a76a7f6 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Page/ReviewOrderPage.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="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="ReviewOrderPage" url="/braintree/paypal/review" area="storefront" module="Magento_Braintree"> + <section name="StorefrontReviewOrderShippingSection"/> + <section name="StorefrontReviewOrderItemsSection"/> + </page> +</pages> diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontMiniCartSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontMiniCartSection.xml new file mode 100644 index 0000000000000..bd4a5b72daa8a --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontMiniCartSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontMiniCartSection"> + <element name="payWithPayPal" type="button" selector=".braintree-paypal-button-hidden" timeout="30"/> + <element name="proceedWithSandboxPurchase" type="text" selector="#return_url"/> + </section> +</sections> diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontReviewOrderItemsSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontReviewOrderItemsSection.xml new file mode 100644 index 0000000000000..b751e67a7ac14 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontReviewOrderItemsSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontReviewOrderItemsSection"> + <element name="placeOrder" type="button" selector="#review-button" timeout="30"/> + <element name="orderSummaryTotal" type="text" selector="tr.grand.totals span.price"/> + </section> +</sections> diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontReviewOrderShippingSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontReviewOrderShippingSection.xml new file mode 100644 index 0000000000000..4e1b3370432ad --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontReviewOrderShippingSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontReviewOrderShippingSection"> + <element name="selectShippingMethodByName" type="select" selector="//select[@id='shipping-method']//option[contains(text(),'{{shippingMethod}}')]" parameterized="true"/> + </section> +</sections> From 229da15cd22f9688b1018dd796da655571132a3c Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Sat, 16 Mar 2019 10:33:50 +0200 Subject: [PATCH 0959/1295] Covering the Share Wishist by integration test --- .../Magento/Wishlist/Controller/ShareTest.php | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Wishlist/Controller/ShareTest.php diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/ShareTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/ShareTest.php new file mode 100644 index 0000000000000..83d79a43620ff --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/ShareTest.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Controller; + +use Magento\Customer\Model\Session; +use Magento\Framework\App\Area; +use Magento\Framework\Data\Form\FormKey; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Request; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * @magentoAppIsolation enabled + */ +class ShareTest extends AbstractController +{ + /** + * Test share wishlist with correct data + * + * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + */ + public function testSuccessfullyShareWishlist() + { + $this->login(1); + $this->prepareRequestData(); + $this->dispatch('wishlist/index/send/'); + + $this->assertSessionMessages( + $this->equalTo(['Your wish list has been shared.']), + MessageInterface::TYPE_SUCCESS + ); + } + + /** + * Test share wishlist with incorrect data + * + * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + */ + public function testShareWishlistWithoutEmails() + { + $this->login(1); + $this->prepareRequestData(true); + $this->dispatch('wishlist/index/send/'); + + $this->assertSessionMessages( + $this->equalTo(['Please enter an email address.']), + MessageInterface::TYPE_ERROR + ); + } + + /** + * Login the user + * + * @param string $customerId Customer to mark as logged in for the session + * @return void + */ + protected function login($customerId) + { + /** @var Session $session */ + $session = $this->_objectManager->get(Session::class); + $session->loginById($customerId); + } + + /** + * Prepares the request with data + * + * @param bool $invalidData + * @return void + */ + private function prepareRequestData($invalidData = false) + { + Bootstrap::getInstance()->loadArea(Area::AREA_FRONTEND); + $emails = !$invalidData ? 'email-1@example.com,email-1@example.com' : ''; + + /** @var FormKey $formKey */ + $formKey = $this->_objectManager->get(FormKey::class); + $post = [ + 'emails' => $emails, + 'message' => '', + 'form_key' => $formKey->getFormKey(), + ]; + + $this->getRequest()->setMethod(Request::METHOD_POST); + $this->getRequest()->setPostValue($post); + } +} From 89bcefa5296d551ef4fa568066f235f6b30c65f4 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Sat, 16 Mar 2019 16:07:32 +0200 Subject: [PATCH 0960/1295] Covering the Wishlist classes by integration and Unit Tests --- .../Product/AttributeValueProviderTest.php | 178 ++++++++++++++++++ .../Magento/Wishlist/Controller/ShareTest.php | 2 +- 2 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php diff --git a/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php b/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php new file mode 100644 index 0000000000000..baafbdef47fe8 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php @@ -0,0 +1,178 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Test\Unit\Model\Product; + +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Wishlist\Model\Product\AttributeValueProvider; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject; + +/** + * @covers CreditCardTokenFormatter + */ +class AttributeValueProviderTest extends TestCase +{ + /** + * @var AttributeValueProvider|PHPUnit_Framework_MockObject_MockObject + */ + private $attributeValueProvider; + + /** + * @var CollectionFactory|PHPUnit_Framework_MockObject_MockObject + */ + private $productCollectionFactoryMock; + + /** + * @var @var Product|PHPUnit_Framework_MockObject_MockObject + */ + private $productMock; + + /** + * @var AdapterInterface|PHPUnit_Framework_MockObject_MockObject + */ + private $connectionMock; + + /** + * Set Up + * + * @return void + */ + protected function setUp() + { + $this->productCollectionFactoryMock = $this->createPartialMock( + CollectionFactory::class, + ['create'] + ); + $this->attributeValueProvider = new AttributeValueProvider( + $this->productCollectionFactoryMock + ); + } + + /** + * Get attribute text when the flat table is disabled + * + * @param int $productId + * @param string $attributeCode + * @param string $attributeText + * @return void + * @dataProvider attributeDataProvider + */ + public function testGetAttributeTextWhenFlatIsDisabled(int $productId, string $attributeCode, string $attributeText) + { + $this->productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->setMethods(['getData']) + ->getMock(); + + $this->productMock->expects($this->any()) + ->method('getData') + ->with($attributeCode) + ->willReturn($attributeText); + + $productCollection = $this->getMockBuilder(Collection::class) + ->disableOriginalConstructor() + ->setMethods([ + 'addIdFilter', 'addStoreFilter', 'addAttributeToSelect', 'isEnabledFlat', 'getFirstItem' + ])->getMock(); + + $productCollection->expects($this->any()) + ->method('addIdFilter') + ->willReturnSelf(); + $productCollection->expects($this->any()) + ->method('addStoreFilter') + ->willReturnSelf(); + $productCollection->expects($this->any()) + ->method('addAttributeToSelect') + ->willReturnSelf(); + $productCollection->expects($this->any()) + ->method('isEnabledFlat') + ->willReturn(false); + $productCollection->expects($this->any()) + ->method('getFirstItem') + ->willReturn($this->productMock); + + $this->productCollectionFactoryMock->expects($this->atLeastOnce()) + ->method('create') + ->willReturn($productCollection); + + $actual = $this->attributeValueProvider->getRawAttributeValue($productId, $attributeCode); + + $this->assertEquals($attributeText, $actual); + } + + + /** + * Get attribute text when the flat table is enabled + * + * @dataProvider attributeDataProvider + * @param int $productId + * @param string $attributeCode + * @param string $attributeText + * @return void + */ + public function testGetAttributeTextWhenFlatIsEnabled(int $productId, string $attributeCode, string $attributeText) + { + $this->connectionMock = $this->getMockBuilder(AdapterInterface::class)->getMockForAbstractClass(); + $this->connectionMock->expects($this->any()) + ->method('fetchRow') + ->willReturn([ + $attributeCode => $attributeText + ]); + $this->productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->setMethods(['getData']) + ->getMock(); + $this->productMock->expects($this->any()) + ->method('getData') + ->with($attributeCode) + ->willReturn($attributeText); + + $productCollection = $this->getMockBuilder(Collection::class) + ->disableOriginalConstructor() + ->setMethods([ + 'addIdFilter', 'addStoreFilter', 'addAttributeToSelect', 'isEnabledFlat', 'getConnection' + ])->getMock(); + + $productCollection->expects($this->any()) + ->method('addIdFilter') + ->willReturnSelf(); + $productCollection->expects($this->any()) + ->method('addStoreFilter') + ->willReturnSelf(); + $productCollection->expects($this->any()) + ->method('addAttributeToSelect') + ->willReturnSelf(); + $productCollection->expects($this->any()) + ->method('isEnabledFlat') + ->willReturn(true); + $productCollection->expects($this->any()) + ->method('getConnection') + ->willReturn($this->connectionMock); + + $this->productCollectionFactoryMock->expects($this->atLeastOnce()) + ->method('create') + ->willReturn($productCollection); + + $actual = $this->attributeValueProvider->getRawAttributeValue($productId, $attributeCode); + + $this->assertEquals($attributeText, $actual); + } + + /** + * @return array + */ + public function attributeDataProvider(): array + { + return [ + [1, 'attribute_code', 'Attribute Text'] + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/ShareTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/ShareTest.php index 83d79a43620ff..47705262caaf3 100644 --- a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/ShareTest.php +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/ShareTest.php @@ -76,7 +76,7 @@ protected function login($customerId) private function prepareRequestData($invalidData = false) { Bootstrap::getInstance()->loadArea(Area::AREA_FRONTEND); - $emails = !$invalidData ? 'email-1@example.com,email-1@example.com' : ''; + $emails = !$invalidData ? 'email-1@example.com,email-2@example.com' : ''; /** @var FormKey $formKey */ $formKey = $this->_objectManager->get(FormKey::class); From 0cd94e2ea07bfe29b21efacfae61bd92be646576 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Sat, 16 Mar 2019 16:12:16 +0200 Subject: [PATCH 0961/1295] Small adjustment --- .../Test/Unit/Model/Product/AttributeValueProviderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php b/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php index baafbdef47fe8..e5f6b84bfc3da 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php @@ -16,7 +16,7 @@ use PHPUnit_Framework_MockObject_MockObject; /** - * @covers CreditCardTokenFormatter + * AttributeValueProviderTest */ class AttributeValueProviderTest extends TestCase { From afa9a80a9cbe246cf37d544ad9209ed8adfe8ed4 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Sat, 16 Mar 2019 16:17:55 +0200 Subject: [PATCH 0962/1295] Small adjustments --- .../Test/Unit/Model/Product/AttributeValueProviderTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php b/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php index e5f6b84bfc3da..fb0113eb6ae75 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Model/Product/AttributeValueProviderTest.php @@ -31,7 +31,7 @@ class AttributeValueProviderTest extends TestCase private $productCollectionFactoryMock; /** - * @var @var Product|PHPUnit_Framework_MockObject_MockObject + * @var Product|PHPUnit_Framework_MockObject_MockObject */ private $productMock; @@ -108,7 +108,6 @@ public function testGetAttributeTextWhenFlatIsDisabled(int $productId, string $a $this->assertEquals($attributeText, $actual); } - /** * Get attribute text when the flat table is enabled * From 10558725d1ad2cccf917d1777621b611786ab165 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Wed, 20 Mar 2019 14:09:17 -0500 Subject: [PATCH 0963/1295] MAGETWO-98679: Discounts of products disappear on storefront after drag & drop via Visual Merchandiser in category --- .../Model/Indexer/IndexBuilder.php | 61 ++++++++++++------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php index e2a5e1aafc3b2..d9e6e338e6d9d 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php @@ -264,14 +264,14 @@ public function reindexByIds(array $ids) */ protected function doReindexByIds($ids) { - $this->cleanByIds($ids); + $this->cleanProductIndex($ids); $products = $this->productLoader->getProducts($ids); + $activeRules = $this->getActiveRules(); foreach ($products as $product) { - foreach ($this->getActiveRules() as $rule) { - $this->applyRule($rule, $product); - } + $this->applyRules($activeRules, $product); } + $this->reindexRuleGroupWebsite->execute(); } /** @@ -351,32 +351,30 @@ protected function cleanByIds($productIds) } /** - * Apply rule + * Assign product to rule * * @param Rule $rule * @param Product $product - * @return $this - * @throws \Exception - * @SuppressWarnings(PHPMD.NPathComplexity) + * @return void */ - protected function applyRule(Rule $rule, $product) + private function assignProductToRule(Rule $rule, Product $product) { - $ruleId = $rule->getId(); - $productEntityId = $product->getId(); - $websiteIds = array_intersect($product->getWebsiteIds(), $rule->getWebsiteIds()); - if (!$rule->validate($product)) { - return $this; + return; } + $ruleId = (int) $rule->getId(); + $productEntityId = (int) $product->getId(); + $ruleProductTable = $this->getTable('catalogrule_product'); $this->connection->delete( - $this->resource->getTableName('catalogrule_product'), + $ruleProductTable, [ - $this->connection->quoteInto('rule_id = ?', $ruleId), - $this->connection->quoteInto('product_id = ?', $productEntityId) + 'rule_id = ?' => $ruleId, + 'product_id = ?' => $productEntityId, ] ); + $websiteIds = array_intersect($product->getWebsiteIds(), $rule->getWebsiteIds()); $customerGroupIds = $rule->getCustomerGroupIds(); $fromTime = strtotime($rule->getFromDate()); $toTime = strtotime($rule->getToDate()); @@ -403,22 +401,43 @@ protected function applyRule(Rule $rule, $product) ]; if (count($rows) == $this->batchCount) { - $this->connection->insertMultiple($this->getTable('catalogrule_product'), $rows); + $this->connection->insertMultiple($ruleProductTable, $rows); $rows = []; } } } - if (!empty($rows)) { - $this->connection->insertMultiple($this->resource->getTableName('catalogrule_product'), $rows); - unset($rows); + if ($rows) { + $this->connection->insertMultiple($ruleProductTable, $rows); } + } + /** + * Apply rule + * + * @param Rule $rule + * @param Product $product + * @return $this + * @throws \Exception + */ + protected function applyRule(Rule $rule, $product) + { + $this->assignProductToRule($rule, $product); $this->reindexRuleProductPrice->execute($this->batchCount, $product); $this->reindexRuleGroupWebsite->execute(); return $this; } + private function applyRules(RuleCollection $ruleCollection, Product $product) + { + foreach ($ruleCollection as $rule) { + $this->assignProductToRule($rule, $product); + } + + $this->cleanProductPriceIndex([$product->getId()]); + $this->reindexRuleProductPrice->execute($this->batchCount, $product); + } + /** * Retrieve table name * From ed2f9053eac453ba1a646c54823152d13267dc2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Saarem=C3=A4e?= <karlasaaremae@gmail.com> Date: Thu, 14 Mar 2019 16:39:41 +0200 Subject: [PATCH 0964/1295] remove translation of attribute label --- .../view/frontend/templates/product/view/attributes.phtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/attributes.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/attributes.phtml index c930d2195a01b..1c4a37fedebe3 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/attributes.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/attributes.phtml @@ -23,8 +23,8 @@ <tbody> <?php foreach ($_additional as $_data): ?> <tr> - <th class="col label" scope="row"><?= $block->escapeHtml(__($_data['label'])) ?></th> - <td class="col data" data-th="<?= $block->escapeHtml(__($_data['label'])) ?>"><?= /* @escapeNotVerified */ $_helper->productAttribute($_product, $_data['value'], $_data['code']) ?></td> + <th class="col label" scope="row"><?= $block->escapeHtml($_data['label']) ?></th> + <td class="col data" data-th="<?= $block->escapeHtml($_data['label']) ?>"><?= /* @escapeNotVerified */ $_helper->productAttribute($_product, $_data['value'], $_data['code']) ?></td> </tr> <?php endforeach; ?> </tbody> From 95c1bc86ca8e5de7e5607d1b83d0fa46e9ded636 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Thu, 21 Mar 2019 10:49:12 +0200 Subject: [PATCH 0965/1295] Fix static test. --- app/code/Magento/Review/Block/Adminhtml/Edit.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Review/Block/Adminhtml/Edit.php b/app/code/Magento/Review/Block/Adminhtml/Edit.php index f6f0ccef9b4e7..9331a3d4610e0 100644 --- a/app/code/Magento/Review/Block/Adminhtml/Edit.php +++ b/app/code/Magento/Review/Block/Adminhtml/Edit.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Review\Block\Adminhtml; /** @@ -56,6 +57,7 @@ public function __construct( * * @return void * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) */ protected function _construct() { From 470c0ec43d9030b02d2ce05f92b8decbd20db4bc Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Thu, 21 Mar 2019 15:18:39 +0200 Subject: [PATCH 0966/1295] MC-15074: Add entity to Product data --- app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 8ee8b49d0a10a..ea73ec234e3fb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -277,6 +277,10 @@ <var key="sku" entityType="product" entityKey="sku" /> <requiredEntity type="product_option">ProductOptionDropDownWithLongValuesTitle</requiredEntity> </entity> + <entity name="ProductWithFileOption" type="product"> + <var key="sku" entityType="product" entityKey="sku" /> + <requiredEntity type="product_option">ProductOptionFile</requiredEntity> + </entity> <entity name="SimpleProductWithCustomAttributeSet" type="product"> <data key="sku" unique="suffix">testSku</data> <data key="type_id">simple</data> From c4312eb23f5d3b9502f866322c4b71dea23be2fd Mon Sep 17 00:00:00 2001 From: Pratik Oza <magepratik@gmail.com> Date: Thu, 21 Mar 2019 18:50:55 +0530 Subject: [PATCH 0967/1295] [Backport] [TASK] Remove translation of attribute store label in getAdditionalData --- app/code/Magento/Catalog/Block/Product/View/Attributes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Block/Product/View/Attributes.php b/app/code/Magento/Catalog/Block/Product/View/Attributes.php index 8494b690bad9f..69c8b78b017d2 100644 --- a/app/code/Magento/Catalog/Block/Product/View/Attributes.php +++ b/app/code/Magento/Catalog/Block/Product/View/Attributes.php @@ -97,7 +97,7 @@ public function getAdditionalData(array $excludeAttr = []) if (is_string($value) && strlen(trim($value))) { $data[$attribute->getAttributeCode()] = [ - 'label' => __($attribute->getStoreLabel()), + 'label' => $attribute->getStoreLabel(), 'value' => $value, 'code' => $attribute->getAttributeCode(), ]; From 8e59620bf7830b2b1ee1c60e77383241a2debeb8 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Thu, 21 Mar 2019 16:22:50 +0200 Subject: [PATCH 0968/1295] MAGETWO-96265: Customer Attribute page not loaded if set default value --- .../Framework/Data/Form/Element/Date.php | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Date.php b/lib/internal/Magento/Framework/Data/Form/Element/Date.php index e762a641bfdcc..1412e027c1b19 100644 --- a/lib/internal/Magento/Framework/Data/Form/Element/Date.php +++ b/lib/internal/Magento/Framework/Data/Form/Element/Date.php @@ -49,6 +49,18 @@ public function __construct( } } + /** + * Check if a string is a date value + * + * @param string $value + * @return bool + */ + private function isDate(string $value): bool + { + $date = date_parse($value); + return !empty($date['year']) && !empty($date['month']) && !empty($date['day']); + } + /** * If script executes on x64 system, converts large * numeric values to timestamp limit @@ -85,9 +97,10 @@ public function setValue($value) try { if (preg_match('/^[0-9]+$/', $value)) { $this->_value = (new \DateTime())->setTimestamp($this->_toTimestamp($value)); + } else if ($this->isDate($value)) { + $this->_value = new \DateTime($value, new \DateTimeZone($this->localeDate->getConfigTimezone())); } else { - $this->_value = new \DateTime($value); - $this->_value->setTimezone(new \DateTimeZone($this->localeDate->getConfigTimezone())); + $this->_value = ''; } } catch (\Exception $e) { $this->_value = ''; From 3fb3b1b0d8fd0b8c25c45eb28923614207215614 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Thu, 21 Mar 2019 20:06:46 +0200 Subject: [PATCH 0969/1295] MAGETWO-97405: Unable to find product on product grid page using store view level attribute --- .../AdminProductGridActionGroup.xml | 12 ++++ ...lterByNameByStoreViewOnProductGridTest.xml | 45 +++++++++++++ .../ProductCustomOptionsDataProviderTest.php | 11 +++- .../Product/ProductCollection.php | 63 +++++++++++++++++++ .../Product/ProductDataProvider.php | 2 + 5 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml index a6213c459396a..e2d94e6253a8a 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 Name field--> + <actionGroup name="filterProductGridByName"> + <arguments> + <argument name="product" defaultValue="_defaultProduct"/> + </arguments> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{product.name}}" stepKey="fillProductNameFilter"/> + <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/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml new file mode 100644 index 0000000000000..34c4869e67cc3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminFilterByNameByStoreViewOnProductGridTest"> + <annotations> + <features value="Catalog"/> + <stories value="Filter products"/> + <title value="Product grid filtering by store view level attribute"/> + <description value="Verify that products grid can be filtered on all store view level by attribute"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-98755"/> + <useCaseId value="MAGETWO-97405"/> + <group value="catalog"/> + </annotations> + <before> + <createData entity="SimpleProduct3" stepKey="createSimpleProduct"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <actionGroup ref="ClearProductsFilterActionGroup" stepKey="clearProductsFilter"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <amOnPage url="{{AdminProductEditPage.url($$createSimpleProduct.id$$)}}" stepKey="goToEditPage"/> + <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="switchToDefaultStoreView"> + <argument name="scopeName" value="_defaultStore.name"/> + </actionGroup> + <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> + <click selector="{{AdminProductFormSection.productNameUseDefault}}" stepKey="uncheckUseDefault"/> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{SimpleProduct.name}}" stepKey="fillNewName"/> + <actionGroup ref="saveProductForm" stepKey="saveSimpleProduct"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <actionGroup ref="filterProductGridByName" stepKey="filterGridByName"> + <argument name="product" value="SimpleProduct"/> + </actionGroup> + <see selector="{{AdminProductGridSection.firstRow}}" userInput="{{SimpleProduct3.name}}" stepKey="seeProductNameInGrid"/> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/ProductCustomOptionsDataProviderTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/ProductCustomOptionsDataProviderTest.php index 6d7c8814bd474..35daac491d583 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/ProductCustomOptionsDataProviderTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/ProductCustomOptionsDataProviderTest.php @@ -54,7 +54,16 @@ protected function setUp() ->getMockForAbstractClass(); $this->collectionMock = $this->getMockBuilder(AbstractCollection::class) ->disableOriginalConstructor() - ->setMethods(['load', 'getSelect', 'getTable', 'getIterator', 'isLoaded', 'toArray', 'getSize']) + ->setMethods([ + 'load', + 'getSelect', + 'getTable', + 'getIterator', + 'isLoaded', + 'toArray', + 'getSize', + 'setStoreId', + ]) ->getMockForAbstractClass(); $this->dbSelectMock = $this->getMockBuilder(DbSelect::class) ->disableOriginalConstructor() diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/ProductCollection.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/ProductCollection.php index f4334bc25efd8..29a19036f3bf3 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/ProductCollection.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/ProductCollection.php @@ -5,6 +5,10 @@ */ namespace Magento\Catalog\Ui\DataProvider\Product; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +use Magento\Framework\Exception\LocalizedException; +use Magento\Eav\Model\Entity\Attribute\AttributeInterface; + /** * Collection which is used for rendering product list in the backend. * @@ -25,4 +29,63 @@ protected function _productLimitationJoinPrice() $this->_productLimitationFilters->setUsePriceIndex(false); return $this->_productLimitationPrice(true); } + + /** + * Add attribute filter to collection + * + * @param AttributeInterface|integer|string|array $attribute + * @param null|string|array $condition + * @param string $joinType + * @return $this + * @throws LocalizedException + */ + public function addAttributeToFilter($attribute, $condition = null, $joinType = 'inner') + { + $storeId = (int)$this->getStoreId(); + if ($attribute === 'is_saleable' + || is_array($attribute) + || $storeId !== $this->getDefaultStoreId() + ) { + return parent::addAttributeToFilter($attribute, $condition, $joinType); + } + + if ($attribute instanceof AttributeInterface) { + $attributeModel = $attribute; + } else { + $attributeModel = $this->getEntity()->getAttribute($attribute); + if ($attributeModel === false) { + throw new LocalizedException( + __('Invalid attribute identifier for filter (%1)', get_class($attribute)) + ); + } + } + + if ($attributeModel->isScopeGlobal() || $attributeModel->getBackend()->isStatic()) { + return parent::addAttributeToFilter($attribute, $condition, $joinType); + } + + $this->addAttributeToFilterAllStores($attributeModel, $condition); + + return $this; + } + + /** + * Add attribute to filter by all stores + * + * @param Attribute $attributeModel + * @param array $condition + * @return void + */ + private function addAttributeToFilterAllStores(Attribute $attributeModel, array $condition) + { + $tableName = $this->getTable($attributeModel->getBackendTable()); + $entity = $this->getEntity(); + $fKey = 'e.' . $this->getEntityPkName($entity); + $pKey = $tableName . '.' . $this->getEntityPkName($entity); + $condition = "({$pKey} = {$fKey}) AND (" + . $this->_getConditionSql("{$tableName}.value", $condition) + . ')'; + $selectExistsInAllStores = $this->getConnection()->select()->from($tableName); + $this->getSelect()->exists($selectExistsInAllStores, $condition); + } } diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/ProductDataProvider.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/ProductDataProvider.php index e31eca89f63c2..44aa33367c6be 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/ProductDataProvider.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/ProductDataProvider.php @@ -6,6 +6,7 @@ namespace Magento\Catalog\Ui\DataProvider\Product; use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; +use Magento\Store\Model\Store; /** * Class ProductDataProvider @@ -58,6 +59,7 @@ public function __construct( $this->collection = $collectionFactory->create(); $this->addFieldStrategies = $addFieldStrategies; $this->addFilterStrategies = $addFilterStrategies; + $this->collection->setStoreId(Store::DEFAULT_STORE_ID); } /** From 37a256ad8338c9852d84792795bb56e7de02222a Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Thu, 21 Mar 2019 14:59:35 -0500 Subject: [PATCH 0970/1295] MAGETWO-98679: Discounts of products disappear on storefront after drag & drop via Visual Merchandiser in category --- .../Unit/Model/Indexer/IndexBuilderTest.php | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/IndexBuilderTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/IndexBuilderTest.php index 8252b512e7810..5827bff42b038 100644 --- a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/IndexBuilderTest.php +++ b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/IndexBuilderTest.php @@ -144,14 +144,12 @@ protected function setUp() ); $this->ruleCollectionFactory = $this->createPartialMock( \Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory::class, - ['create', 'addFieldToFilter'] + ['create'] ); $this->backend = $this->createMock(\Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend::class); $this->select = $this->createMock(\Magento\Framework\DB\Select::class); $this->metadataPool = $this->createMock(\Magento\Framework\EntityManager\MetadataPool::class); - $metadata = $this->getMockBuilder(\Magento\Framework\EntityManager\EntityMetadata::class) - ->disableOriginalConstructor() - ->getMock(); + $metadata = $this->createMock(\Magento\Framework\EntityManager\EntityMetadata::class); $this->metadataPool->expects($this->any())->method('getMetadata')->willReturn($metadata); $this->connection = $this->createMock(\Magento\Framework\DB\Adapter\AdapterInterface::class); $this->db = $this->createMock(\Zend_Db_Statement_Interface::class); @@ -181,10 +179,16 @@ protected function setUp() $this->rules->expects($this->any())->method('getWebsiteIds')->will($this->returnValue([1])); $this->rules->expects($this->any())->method('getCustomerGroupIds')->will($this->returnValue([1])); - $this->ruleCollectionFactory->expects($this->any())->method('create')->will($this->returnSelf()); - $this->ruleCollectionFactory->expects($this->any())->method('addFieldToFilter')->will( - $this->returnValue([$this->rules]) - ); + $ruleCollection = $this->createMock(\Magento\CatalogRule\Model\ResourceModel\Rule\Collection::class); + $this->ruleCollectionFactory->expects($this->once()) + ->method('create') + ->willReturn($ruleCollection); + $ruleCollection->expects($this->once()) + ->method('addFieldToFilter') + ->willReturnSelf(); + $ruleIterator = new \ArrayIterator([$this->rules]); + $ruleCollection->method('getIterator') + ->willReturn($ruleIterator); $this->product->expects($this->any())->method('load')->will($this->returnSelf()); $this->product->expects($this->any())->method('getId')->will($this->returnValue(1)); @@ -213,14 +217,12 @@ protected function setUp() ] ); - $this->reindexRuleProductPrice = - $this->getMockBuilder(\Magento\CatalogRule\Model\Indexer\ReindexRuleProductPrice::class) - ->disableOriginalConstructor() - ->getMock(); - $this->reindexRuleGroupWebsite = - $this->getMockBuilder(\Magento\CatalogRule\Model\Indexer\ReindexRuleGroupWebsite::class) - ->disableOriginalConstructor() - ->getMock(); + $this->reindexRuleProductPrice = $this->createMock( + \Magento\CatalogRule\Model\Indexer\ReindexRuleProductPrice::class + ); + $this->reindexRuleGroupWebsite = $this->createMock( + \Magento\CatalogRule\Model\Indexer\ReindexRuleGroupWebsite::class + ); $this->setProperties($this->indexBuilder, [ 'metadataPool' => $this->metadataPool, 'reindexRuleProductPrice' => $this->reindexRuleProductPrice, From f94640a814040f25173d2c5a6d6392b568a1cf31 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Fri, 22 Mar 2019 09:27:59 +0200 Subject: [PATCH 0971/1295] MAGETWO-96265: Customer Attribute page not loaded if set default value --- lib/internal/Magento/Framework/Data/Form/Element/Date.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Date.php b/lib/internal/Magento/Framework/Data/Form/Element/Date.php index 1412e027c1b19..52d3eae7d66ac 100644 --- a/lib/internal/Magento/Framework/Data/Form/Element/Date.php +++ b/lib/internal/Magento/Framework/Data/Form/Element/Date.php @@ -58,6 +58,7 @@ public function __construct( private function isDate(string $value): bool { $date = date_parse($value); + return !empty($date['year']) && !empty($date['month']) && !empty($date['day']); } @@ -97,7 +98,7 @@ public function setValue($value) try { if (preg_match('/^[0-9]+$/', $value)) { $this->_value = (new \DateTime())->setTimestamp($this->_toTimestamp($value)); - } else if ($this->isDate($value)) { + } else if (is_string($value) && $this->isDate($value)) { $this->_value = new \DateTime($value, new \DateTimeZone($this->localeDate->getConfigTimezone())); } else { $this->_value = ''; From d50ecda433331af7433318d833ad5b71b93aa73a Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Fri, 22 Mar 2019 10:38:49 +0200 Subject: [PATCH 0972/1295] MC-15360: Improve checkout through BrainTree(Paypal) --- .../Test/Mftf/Data/BraintreeData.xml | 22 ++++++++++++------- ...rderPage.xml => PaypalReviewOrderPage.xml} | 6 ++--- .../StorefrontReviewOrderItemsSection.xml | 2 +- .../StorefrontReviewOrderShippingSection.xml | 2 +- 4 files changed, 19 insertions(+), 13 deletions(-) rename app/code/Magento/Braintree/Test/Mftf/Page/{ReviewOrderPage.xml => PaypalReviewOrderPage.xml} (55%) diff --git a/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml b/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml index a7730192af16e..aa0f5a936fd7e 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Data/BraintreeData.xml @@ -44,7 +44,6 @@ <requiredEntity type="public_key">DefaultPublicKey</requiredEntity> <requiredEntity type="private_key">DefaultPrivateKey</requiredEntity> <requiredEntity type="active">DefaultActive</requiredEntity> - <requiredEntity type="active_braintree_paypal">DefaultActiveBraintreePaypal</requiredEntity> <requiredEntity type="merchant_account_id">DefaultMerchantAccountId</requiredEntity> </entity> <entity name="DefaultTitle" type="title"> @@ -68,13 +67,17 @@ <entity name="DefaultActive" type="active"> <data key="value">0</data> </entity> - <entity name="DefaultActiveBraintreePaypal" type="active_braintree_paypal"> - <data key="value">0</data> - </entity> <entity name="DefaultMerchantAccountId" type="merchant_account_id"> <data key="value"/> </entity> + <entity name="DefaultBraintreeWithPaypalConfig" extends="DefaultBraintreeConfig"> + <requiredEntity type="active_braintree_paypal">DefaultActiveBraintreePaypal</requiredEntity> + </entity> + <entity name="DefaultActiveBraintreePaypal" type="active_braintree_paypal"> + <data key="value">0</data> + </entity> + <entity name="SandboxBraintreeConfig" type="braintree_config_state"> <requiredEntity type="title">EnabledTitle</requiredEntity> <requiredEntity type="payment_action">AuthorizePaymentAction</requiredEntity> @@ -83,7 +86,6 @@ <requiredEntity type="public_key">EnabledPublicKey</requiredEntity> <requiredEntity type="private_key">EnabledPrivateKey</requiredEntity> <requiredEntity type="active">EnabledActive</requiredEntity> - <requiredEntity type="active_braintree_paypal">EnableActiveBraintreePaypal</requiredEntity> <requiredEntity type="merchant_account_id">EnabledMerchantAccountId</requiredEntity> </entity> <entity name="EnabledTitle" type="title"> @@ -107,13 +109,17 @@ <entity name="EnabledActive" type="active"> <data key="value">1</data> </entity> - <entity name="EnableActiveBraintreePaypal" type="active_braintree_paypal"> - <data key="value">1</data> - </entity> <entity name="EnabledMerchantAccountId" type="merchant_account_id"> <data key="value">Magneto</data> </entity> + <entity name="SandboxBraintreeWithPaypalConfig" extends="SandboxBraintreeConfig"> + <requiredEntity type="active_braintree_paypal">EnableActiveBraintreePaypal</requiredEntity> + </entity> + <entity name="EnableActiveBraintreePaypal" type="active_braintree_paypal"> + <data key="value">1</data> + </entity> + <entity name="BraintreeCard" type="creditCard"> <data key="cc_type">MasterCard</data> <data key="card_number">5105105105105100</data> diff --git a/app/code/Magento/Braintree/Test/Mftf/Page/ReviewOrderPage.xml b/app/code/Magento/Braintree/Test/Mftf/Page/PaypalReviewOrderPage.xml similarity index 55% rename from app/code/Magento/Braintree/Test/Mftf/Page/ReviewOrderPage.xml rename to app/code/Magento/Braintree/Test/Mftf/Page/PaypalReviewOrderPage.xml index 96eff7a76a7f6..fa21d3c8fd9a1 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Page/ReviewOrderPage.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Page/PaypalReviewOrderPage.xml @@ -8,8 +8,8 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="ReviewOrderPage" url="/braintree/paypal/review" area="storefront" module="Magento_Braintree"> - <section name="StorefrontReviewOrderShippingSection"/> - <section name="StorefrontReviewOrderItemsSection"/> + <page name="PaypalReviewOrderPage" url="/braintree/paypal/review" area="storefront" module="Magento_Braintree"> + <section name="StorefrontPaypalReviewOrderShippingSection"/> + <section name="StorefrontPaypalReviewOrderItemsSection"/> </page> </pages> diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontReviewOrderItemsSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontReviewOrderItemsSection.xml index b751e67a7ac14..8c834f7003eb5 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontReviewOrderItemsSection.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontReviewOrderItemsSection.xml @@ -8,7 +8,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="StorefrontReviewOrderItemsSection"> + <section name="StorefrontPaypalReviewOrderItemsSection"> <element name="placeOrder" type="button" selector="#review-button" timeout="30"/> <element name="orderSummaryTotal" type="text" selector="tr.grand.totals span.price"/> </section> diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontReviewOrderShippingSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontReviewOrderShippingSection.xml index 4e1b3370432ad..2e4e6047f57bb 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontReviewOrderShippingSection.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontReviewOrderShippingSection.xml @@ -8,7 +8,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="StorefrontReviewOrderShippingSection"> + <section name="StorefrontPaypalReviewOrderShippingSection"> <element name="selectShippingMethodByName" type="select" selector="//select[@id='shipping-method']//option[contains(text(),'{{shippingMethod}}')]" parameterized="true"/> </section> </sections> From 4326a8672f2f629752e130f6a3512c6040bbd8f3 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Fri, 22 Mar 2019 10:50:17 +0200 Subject: [PATCH 0973/1295] MC-15360: Improve checkout through BrainTree(Paypal) --- ...lReviewOrderPage.xml => StorefrontPaypalReviewOrderPage.xml} | 2 +- ...sSection.xml => StorefrontPaypalReviewOrderItemsSection.xml} | 0 ...ction.xml => StorefrontPaypalReviewOrderShippingSection.xml} | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename app/code/Magento/Braintree/Test/Mftf/Page/{PaypalReviewOrderPage.xml => StorefrontPaypalReviewOrderPage.xml} (78%) rename app/code/Magento/Braintree/Test/Mftf/Section/{StorefrontReviewOrderItemsSection.xml => StorefrontPaypalReviewOrderItemsSection.xml} (100%) rename app/code/Magento/Braintree/Test/Mftf/Section/{StorefrontReviewOrderShippingSection.xml => StorefrontPaypalReviewOrderShippingSection.xml} (100%) diff --git a/app/code/Magento/Braintree/Test/Mftf/Page/PaypalReviewOrderPage.xml b/app/code/Magento/Braintree/Test/Mftf/Page/StorefrontPaypalReviewOrderPage.xml similarity index 78% rename from app/code/Magento/Braintree/Test/Mftf/Page/PaypalReviewOrderPage.xml rename to app/code/Magento/Braintree/Test/Mftf/Page/StorefrontPaypalReviewOrderPage.xml index fa21d3c8fd9a1..82c0fa2a075d4 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Page/PaypalReviewOrderPage.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Page/StorefrontPaypalReviewOrderPage.xml @@ -8,7 +8,7 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="PaypalReviewOrderPage" url="/braintree/paypal/review" area="storefront" module="Magento_Braintree"> + <page name="StorefrontPaypalReviewOrderPage" url="/braintree/paypal/review" area="storefront" module="Magento_Braintree"> <section name="StorefrontPaypalReviewOrderShippingSection"/> <section name="StorefrontPaypalReviewOrderItemsSection"/> </page> diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontReviewOrderItemsSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontPaypalReviewOrderItemsSection.xml similarity index 100% rename from app/code/Magento/Braintree/Test/Mftf/Section/StorefrontReviewOrderItemsSection.xml rename to app/code/Magento/Braintree/Test/Mftf/Section/StorefrontPaypalReviewOrderItemsSection.xml diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontReviewOrderShippingSection.xml b/app/code/Magento/Braintree/Test/Mftf/Section/StorefrontPaypalReviewOrderShippingSection.xml similarity index 100% rename from app/code/Magento/Braintree/Test/Mftf/Section/StorefrontReviewOrderShippingSection.xml rename to app/code/Magento/Braintree/Test/Mftf/Section/StorefrontPaypalReviewOrderShippingSection.xml From 3f85d47b87c5aa802fc463466ba4303d2ef3ded5 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Fri, 22 Mar 2019 13:34:57 +0200 Subject: [PATCH 0974/1295] MAGETWO-97405: Unable to find product on product grid page using store view level attribute --- .../ActionGroup/SearchForProductOnBackendActionGroup.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchForProductOnBackendActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchForProductOnBackendActionGroup.xml index 6cb939a7af410..3e262e8a7bfb7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchForProductOnBackendActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchForProductOnBackendActionGroup.xml @@ -26,4 +26,9 @@ <remove keyForRemoval="fillSkuFieldOnFiltersSection"/> <fillField userInput="{{productName}}" selector="{{AdminProductFiltersSection.NameInput}}" after="cleanFiltersIfTheySet" stepKey="fillNameFieldOnFiltersSection"/> </actionGroup> + <actionGroup name="ClearProductsFilterActionGroup"> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad time="30" stepKey="waitForProductsPageToLoad"/> + <conditionalClick selector="{{AdminProductFiltersSection.clearFiltersButton}}" dependentSelector="{{AdminProductFiltersSection.clearFiltersButton}}" visible="true" stepKey="cleanFiltersIfTheySet"/> + </actionGroup> </actionGroups> From 67b732c00de7a3720d342d945099ede93b600ef7 Mon Sep 17 00:00:00 2001 From: amol2jcommerce <amol@twojay.in> Date: Fri, 22 Mar 2019 17:35:40 +0530 Subject: [PATCH 0975/1295] changes for advance search --- .../Magento_Customer/web/css/source/_module.less | 12 ++++++++++++ .../Magento_Customer/web/css/source/_module.less | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less index 0ebd722429480..f4101da6005c1 100644 --- a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less @@ -388,6 +388,17 @@ position: relative; } } + + .form.search.advanced { + .field.price { + .with-addon { + .input-text { + flex-basis: auto; + width: 100%; + } + } + } + } } // @@ -452,6 +463,7 @@ .form.send.confirmation, .form.password.forget, .form.create.account, + .form.search.advanced, .form.form-orders-search { min-width: 600px; width: 50%; diff --git a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less index 460f8e50d59a9..e839c66b93e58 100755 --- a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less @@ -403,6 +403,7 @@ .form.send.confirmation, .form.password.forget, .form.create.account, + .form.search.advanced, .form.form-orders-search { min-width: 600px; width: 50%; @@ -603,4 +604,15 @@ position: relative; } } + + .form.search.advanced { + .field.price { + .with-addon { + .input-text { + flex-basis: auto; + width: 100%; + } + } + } + } } From b52400e8fae65575d98610d653465518dd2a1a50 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Fri, 22 Mar 2019 15:50:31 +0200 Subject: [PATCH 0976/1295] MAGETWO-97405: Unable to find product on product grid page using store view level attribute --- .../Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml | 1 - .../ActionGroup/SearchForProductOnBackendActionGroup.xml | 5 ----- ...t.xml => AdminFilterProductGridByNameByStoreViewTest.xml} | 5 +++-- 3 files changed, 3 insertions(+), 8 deletions(-) rename app/code/Magento/Catalog/Test/Mftf/Test/{AdminFilterByNameByStoreViewOnProductGridTest.xml => AdminFilterProductGridByNameByStoreViewTest.xml} (90%) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml index e2d94e6253a8a..f620b3c042a86 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml @@ -38,7 +38,6 @@ <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{product.name}}" stepKey="fillProductNameFilter"/> <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--> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchForProductOnBackendActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchForProductOnBackendActionGroup.xml index 3e262e8a7bfb7..6cb939a7af410 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchForProductOnBackendActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchForProductOnBackendActionGroup.xml @@ -26,9 +26,4 @@ <remove keyForRemoval="fillSkuFieldOnFiltersSection"/> <fillField userInput="{{productName}}" selector="{{AdminProductFiltersSection.NameInput}}" after="cleanFiltersIfTheySet" stepKey="fillNameFieldOnFiltersSection"/> </actionGroup> - <actionGroup name="ClearProductsFilterActionGroup"> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <waitForPageLoad time="30" stepKey="waitForProductsPageToLoad"/> - <conditionalClick selector="{{AdminProductFiltersSection.clearFiltersButton}}" dependentSelector="{{AdminProductFiltersSection.clearFiltersButton}}" visible="true" stepKey="cleanFiltersIfTheySet"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterProductGridByNameByStoreViewTest.xml similarity index 90% rename from app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml rename to app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterProductGridByNameByStoreViewTest.xml index 34c4869e67cc3..539d87b4a1064 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterProductGridByNameByStoreViewTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminFilterByNameByStoreViewOnProductGridTest"> + <test name="AdminFilterProductGridByNameByStoreViewTest"> <annotations> <features value="Catalog"/> <stories value="Filter products"/> @@ -25,7 +25,8 @@ </before> <after> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="ClearProductsFilterActionGroup" stepKey="clearProductsFilter"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearGridFilter"/> <actionGroup ref="logout" stepKey="logout"/> </after> <amOnPage url="{{AdminProductEditPage.url($$createSimpleProduct.id$$)}}" stepKey="goToEditPage"/> From e6b17ad869da220c265b2ae2e227c67e85d1d193 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Tue, 19 Mar 2019 16:37:23 +0200 Subject: [PATCH 0977/1295] ENGCOM-2667: Functional tests fix. --- ...CreateRootCategoryAndSubcategoriesTest.xml | 21 ++++----- .../Test/Mftf/Test/DeleteCategoriesTest.xml | 14 +++--- .../AdminCreateNewStoreGroupActionGroup.xml | 2 +- .../AdminCreateWebsiteActionGroup.xml | 2 +- .../AdminDeleteStoreViewActionGroup.xml | 11 +++-- .../AdminDeleteWebsiteActionGroup.xml | 14 +++--- .../AdminStoresFilterGridActionGroup.xml | 43 +++++++++++++++++++ .../DeleteCustomStoreActionGroup.xml | 11 ++--- .../Section/AdminStoresGridFilterSection.xml | 22 ++++++++++ .../Mftf/Section/AdminStoresGridSection.xml | 11 ++--- .../Mftf/Test/AdminCreateStoreGroupTest.xml | 21 ++++----- .../Test/Block/System/Store/StoreGrid.php | 16 ++++++- 12 files changed, 129 insertions(+), 59 deletions(-) create mode 100644 app/code/Magento/Store/Test/Mftf/ActionGroup/AdminStoresFilterGridActionGroup.xml create mode 100644 app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridFilterSection.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml index 0fb4f2fd784e3..35b663da4f5ad 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml @@ -22,12 +22,10 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin2"/> <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnPageAdminSystemStore"/> <waitForPageLoad stepKey="waitForPageAdminSystemStoreLoad" /> - <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="clickOnResetButton"/> - <waitForPageLoad time="10" stepKey="waitForPageAdminStoresGridLoadAfterResetButton"/> - <fillField selector="{{AdminStoresGridSection.storeGrpFilterTextField}}" userInput="Main Website Store" stepKey="fillFieldOnWebsiteStore"/> - <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickOnSearchButton"/> - <waitForPageLoad stepKey="waitForPageAdminStoresGridLoadAfterSearchButton"/> - <click selector="{{AdminStoresGridSection.storeGrpNameInFirstRow}}" stepKey="clickOnstoreGrpNameInFirstRow"/> + <actionGroup ref="filterStoresGridByStore" stepKey="enterStoreGroup1Name"> + <argument name="store" value="Main Website Store"/> + </actionGroup> + <click selector="{{AdminStoresGridSection.storeInFirstRow}}" stepKey="clickOnstoreInFirstRow"/> <waitForPageLoad stepKey="waitForPageAdminSystemStoreLoad1" /> <selectOption userInput="Default Category" selector="{{AdminNewStoreGroupSection.storeRootCategoryDropdown}}" stepKey="selectOptionDefaultCategory"/> <click selector="{{AdminStoreGroupActionsSection.saveButton}}" stepKey="clickSaveStoreButton"/> @@ -59,12 +57,11 @@ <!--Assign new created root category to store--> <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnPageAdminSystemStore"/> <waitForPageLoad stepKey="waitForPageAdminSystemStoreLoad" /> - <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="clickOnResetButton"/> - <waitForPageLoad time="10" stepKey="waitForPageAdminStoresGridLoadAfterResetButton"/> - <fillField selector="{{AdminStoresGridSection.storeGrpFilterTextField}}" userInput="Main Website Store" stepKey="fillFieldOnWebsiteStore"/> - <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickOnSearchButton"/> + <actionGroup ref="filterStoresGridByStore" stepKey="enterStoreGroup1Name"> + <argument name="store" value="Main Website Store"/> + </actionGroup> <waitForPageLoad stepKey="waitForPageAdminStoresGridLoadAfterSearchButton"/> - <click selector="{{AdminStoresGridSection.storeGrpNameInFirstRow}}" stepKey="clickOnstoreGrpNameInFirstRow"/> + <click selector="{{AdminStoresGridSection.storeInFirstRow}}" stepKey="clickOnstoreInFirstRow"/> <waitForPageLoad stepKey="waitForPageAdminStoresGroupEditLoad" /> <selectOption userInput="{{NewRootCategory.name}}" selector="{{AdminNewStoreGroupSection.storeRootCategoryDropdown}}" stepKey="selectOptionCreatedNewRootCategory"/> <click selector="{{AdminStoreGroupActionsSection.saveButton}}" stepKey="clickSaveStoreButton"/> @@ -80,4 +77,4 @@ <argument name="categoryEntity" value="SubCategoryWithParent"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml index e4ea511efe46c..f333db0e65aa8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml @@ -58,19 +58,17 @@ <!-- Change root category for Main Website Store. --> <amOnPage stepKey="s1" url="{{AdminSystemStorePage.url}}"/> <waitForPageLoad stepKey="waitForPageAdminSystemStoreLoad" /> - <click stepKey="s2" selector="{{AdminStoresGridSection.resetButton}}"/> - <waitForPageLoad stepKey="waitForPageAdminStoresGridLoadAfterResetButton" time="10"/> - <fillField stepKey="s4" selector="{{AdminStoresGridSection.storeGrpFilterTextField}}" userInput="Main Website Store"/> - <click stepKey="s5" selector="{{AdminStoresGridSection.searchButton}}"/> - <waitForPageLoad stepKey="waitForPageAdminStoresGridLoadAfterSearchButton"/> - <click stepKey="s7" selector="{{AdminStoresGridSection.storeGrpNameInFirstRow}}" /> + <actionGroup ref="filterStoresGridByStore" stepKey="filterStoresGridByStore"> + <argument name="store" value="Main Website Store"/> + </actionGroup> + <click stepKey="s7" selector="{{AdminStoresGridSection.storeInFirstRow}}" /> <waitForPageLoad stepKey="waitForPageAdminStoresGroupEditLoad" /> <selectOption selector="{{AdminNewStoreGroupSection.storeRootCategoryDropdown}}" userInput="{{NewRootCategory.name}}" stepKey="setNewCategoryForStoreGroup"/> <click selector="{{AdminNewStoreViewActionsSection.saveButton}}" stepKey="clickSaveStoreGroup"/> <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForModalSaveStoreGroup"/> <see selector="{{AdminConfirmationModalSection.title}}" userInput="Warning message" stepKey="seeWarning"/> <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="acceptModal" /> - <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" stepKey="waitForPageAdminStoresGridReload"/> + <waitForElementVisible selector="{{AdminStoresGridFilterSection.filters}}" stepKey="waitForPageAdminStoresGridReload"/> <see userInput="You saved the store." stepKey="seeSavedMessage"/> <!-- @TODO: Uncomment commented below code after MQE-903 is fixed --> @@ -160,4 +158,4 @@ <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategoryDefaultCategory"/> <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccessMessageAfterSaveDefaultCategory"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateNewStoreGroupActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateNewStoreGroupActionGroup.xml index f241531f61ffd..0b9676b14b776 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateNewStoreGroupActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateNewStoreGroupActionGroup.xml @@ -22,7 +22,7 @@ <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"/> + <waitForElementVisible selector="{{AdminStoresGridFilterSection.filters}}" stepKey="waitForStoreGridReload"/> <see userInput="You saved the store." stepKey="seeSavedMessage" /> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml index 9d7c538d3a3c4..10303684e19d5 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml @@ -20,7 +20,7 @@ <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"/> + <waitForElementVisible selector="{{AdminStoresGridFilterSection.filters}}" stepKey="waitForStoreGridToReload"/> <see userInput="You saved the website." stepKey="seeSavedMessage" /> </actionGroup> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml index 8b059ac164c0f..77473ae235c4a 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml @@ -14,9 +14,12 @@ </arguments> <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="navigateToStoresIndex"/> <waitForPageLoad stepKey="waitStoreIndexPageLoad" /> - <fillField selector="{{AdminStoresGridSection.storeFilterTextField}}" userInput="{{customStore.name}}" stepKey="fillStoreViewFilterField"/> - <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearch"/> - <click selector="{{AdminStoresGridSection.storeNameInFirstRow}}" stepKey="clickStoreViewInGrid"/> + <conditionalClick selector="{{AdminStoresGridFilterSection.clearFilters}}" dependentSelector="{{AdminStoresGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <click selector="{{AdminStoresGridFilterSection.filters}}" stepKey="openStoresFilters"/> + <fillField selector="{{AdminStoresGridFilterSection.storeViewFilter}}" userInput="{{customStore.name}}" stepKey="fillStoreViewFilterField"/> + <click selector="{{AdminStoresGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <waitForLoadingMaskToDisappear stepKey="waitForGridLoad"/> + <click selector="{{AdminStoresGridSection.storeViewInFirstRow}}" stepKey="clickStoreViewInGrid"/> <waitForPageLoad stepKey="waitForStoreViewPage"/> <click selector="{{AdminNewStoreViewActionsSection.delete}}" stepKey="clickDeleteStoreView"/> <selectOption selector="{{AdminStoreBackupOptionsSection.createBackupSelect}}" userInput="No" stepKey="dontCreateDbBackup"/> @@ -29,6 +32,6 @@ <arguments> <argument name="customStoreName" type="string"/> </arguments> - <fillField selector="{{AdminStoresGridSection.storeFilterTextField}}" userInput="{{customStoreName}}" stepKey="fillStoreViewFilterField"/> + <fillField selector="{{AdminStoresGridFilterSection.storeViewFilter}}" userInput="{{customStoreName}}" stepKey="fillStoreViewFilterField"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml index 395ba02d5a9de..e809f09565fd6 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml @@ -12,16 +12,18 @@ <argument name="websiteName" type="string"/> </arguments> <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> - <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> - <fillField userInput="{{websiteName}}" selector="{{AdminStoresGridSection.websiteFilterTextField}}" stepKey="fillSearchWebsiteField"/> - <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton"/> - <see userInput="{{websiteName}}" selector="{{AdminStoresGridSection.websiteNameInFirstRow}}" stepKey="verifyThatCorrectWebsiteFound"/> - <click selector="{{AdminStoresGridSection.websiteNameInFirstRow}}" stepKey="clickEditExistingStoreRow"/> + <conditionalClick selector="{{AdminStoresGridFilterSection.clearFilters}}" dependentSelector="{{AdminStoresGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <click selector="{{AdminStoresGridFilterSection.filters}}" stepKey="openStoresFilters"/> + <fillField selector="{{AdminStoresGridFilterSection.websiteFilter}}" userInput="{{websiteName}}" stepKey="fillWebsiteFilter"/> + <click selector="{{AdminStoresGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <waitForLoadingMaskToDisappear stepKey="waitForGridLoad"/> + <see userInput="{{websiteName}}" selector="{{AdminStoresGridSection.websiteInFirstRow}}" stepKey="verifyThatCorrectWebsiteFound"/> + <click selector="{{AdminStoresGridSection.websiteInFirstRow}}" stepKey="clickEditExistingStoreRow"/> <waitForPageLoad stepKey="waitForStoreToLoad"/> <click selector="{{AdminStoresMainActionsSection.deleteButton}}" stepKey="clickDeleteWebsiteButtonOnEditWebsitePage"/> <selectOption userInput="No" selector="{{AdminStoresDeleteStoreGroupSection.createDbBackup}}" stepKey="setCreateDbBackupToNo"/> <click selector="{{AdminStoresDeleteStoreGroupSection.deleteStoreGroupButton}}" stepKey="clickDeleteWebsiteButton"/> - <waitForElementVisible selector="{{AdminStoresGridSection.websiteFilterTextField}}" stepKey="waitForStoreGridToReload"/> + <waitForElementVisible selector="{{AdminStoresGridFilterSection.filters}}" stepKey="waitForStoreGridToReload"/> <see userInput="You deleted the website." stepKey="seeSavedMessage"/> <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFilters"/> </actionGroup> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminStoresFilterGridActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminStoresFilterGridActionGroup.xml new file mode 100644 index 0000000000000..2ccfc0dcc1277 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminStoresFilterGridActionGroup.xml @@ -0,0 +1,43 @@ +<?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="filterStoresGridByWebsite"> + <arguments> + <argument name="website" type="string"/> + </arguments> + <conditionalClick selector="{{AdminStoresGridFilterSection.clearFilters}}" dependentSelector="{{AdminStoresGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <click selector="{{AdminStoresGridFilterSection.filters}}" stepKey="openStoresFilters"/> + <fillField selector="{{AdminStoresGridFilterSection.websiteFilter}}" userInput="{{website}}" stepKey="fillWebsiteFilter"/> + <click selector="{{AdminStoresGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <waitForLoadingMaskToDisappear stepKey="waitForGridLoad"/> + </actionGroup> + + <actionGroup name="filterStoresGridByStore"> + <arguments> + <argument name="store" type="string"/> + </arguments> + <conditionalClick selector="{{AdminStoresGridFilterSection.clearFilters}}" dependentSelector="{{AdminStoresGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <click selector="{{AdminStoresGridFilterSection.filters}}" stepKey="openStoresFilters"/> + <fillField selector="{{AdminStoresGridFilterSection.storeFilter}}" userInput="{{store}}" stepKey="fillStoreFilter"/> + <click selector="{{AdminStoresGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <waitForLoadingMaskToDisappear stepKey="waitForGridLoad"/> + </actionGroup> + + <actionGroup name="filterStoresGridByStoreView"> + <arguments> + <argument name="storeView" type="string"/> + </arguments> + <conditionalClick selector="{{AdminStoresGridFilterSection.clearFilters}}" dependentSelector="{{AdminStoresGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <click selector="{{AdminStoresGridFilterSection.filters}}" stepKey="openStoresFilters"/> + <fillField selector="{{AdminStoresGridFilterSection.storeViewFilter}}" userInput="{{storeView}}" stepKey="fillStoreViewFilter"/> + <click selector="{{AdminStoresGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <waitForLoadingMaskToDisappear stepKey="waitForGridLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomStoreActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomStoreActionGroup.xml index 7c18ca5375c93..d35cf13890114 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomStoreActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomStoreActionGroup.xml @@ -13,11 +13,12 @@ <argument name="storeGroupName" defaultValue="customStoreGroup.name"/> </arguments> <amOnPage stepKey="amOnAdminSystemStorePage" url="{{AdminSystemStorePage.url}}"/> - <click stepKey="resetSearchFilter" selector="{{AdminStoresGridSection.resetButton}}"/> - <fillField stepKey="fillSearchStoreGroupField" selector="{{AdminStoresGridSection.storeGrpFilterTextField}}" userInput="{{storeGroupName}}"/> - <click stepKey="clickSearchButton" selector="{{AdminStoresGridSection.searchButton}}"/> - <see stepKey="verifyThatCorrectStoreGroupFound" selector="{{AdminStoresGridSection.storeGrpNameInFirstRow}}" userInput="{{storeGroupName}}"/> - <click stepKey="clickEditExistingStoreRow" selector="{{AdminStoresGridSection.storeGrpNameInFirstRow}}"/> + <conditionalClick stepKey="resetSearchFilter" selector="{{AdminStoresGridFilterSection.clearAll}}" dependentSelector="{{AdminStoresGridFilterSection.clearAll}}" visible="true"/> + <click selector="{{AdminStoresGridFilterSection.filters}}" stepKey="openStoresFilters"/> + <fillField stepKey="fillSearchStoreGroupField" selector="{{AdminStoresGridFilterSection.storeFilter}}" userInput="{{storeGroupName}}"/> + <click stepKey="clickSearchButton" selector="{{AdminStoresGridFilterSection.applyFilters}}"/> + <see stepKey="verifyThatCorrectStoreGroupFound" selector="{{AdminStoresGridSection.storeInFirstRow}}" userInput="{{storeGroupName}}"/> + <click stepKey="clickEditExistingStoreRow" selector="{{AdminStoresGridSection.storeInFirstRow}}"/> <waitForPageLoad stepKey="waitForStoreToLoad"/> <click stepKey="clickDeleteStoreGroupButtonOnEditStorePage" selector="{{AdminStoresMainActionsSection.deleteButton}}"/> <selectOption stepKey="setCreateDbBackupToNo" selector="{{AdminStoresDeleteStoreGroupSection.createDbBackup}}" userInput="No"/> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridFilterSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridFilterSection.xml new file mode 100644 index 0000000000000..45028b3af1ade --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridFilterSection.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + <section name="AdminStoresGridFilterSection"> + <element name="filters" type="button" selector="button[data-action='grid-filter-expand']"/> + <element name="clearAll" type="button" selector=".admin__data-grid-header .admin__data-grid-filters-current._show .action-clear" timeout="30"/> + <element name="columnsDropdown" type="button" selector=".admin__data-grid-action-columns button.admin__action-dropdown"/> + <element name="viewColumnOption" type="checkbox" selector="//div[contains(@class, '_active')]//div[contains(@class, 'admin__data-grid-action-columns-menu')]//div[@class='admin__field-option']//label[text()='{{col}}']/preceding-sibling::input" parameterized="true"/> + <element name="clearFilters" type="button" selector=".admin__data-grid-header button[data-action='grid-filter-reset']" timeout="30"/> + <element name="applyFilters" type="button" selector="button[data-action='grid-filter-apply']" timeout="30"/> + <element name="websiteFilter" type="input" selector="input.admin__control-text[name='name']"/> + <element name="storeFilter" type="input" selector="input.admin__control-text[name='group_title']"/> + <element name="storeViewFilter" type="input" selector="input.admin__control-text[name='store_title']"/> + </section> +</sections> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml index d0a67b18f6cd5..5e2128a4231a4 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml @@ -7,13 +7,8 @@ --> <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="AdminStoresGridSection"> - <element name="storeGrpFilterTextField" type="input" selector="#storeGrid_filter_group_title"/> - <element name="websiteFilterTextField" type="input" selector="#storeGrid_filter_website_title"/> - <element name="storeFilterTextField" type="input" selector="#storeGrid_filter_store_title"/> - <element name="searchButton" type="button" selector=".admin__data-grid-header button[title=Search]"/> - <element name="resetButton" type="button" selector="button[title='Reset Filter']"/> - <element name="websiteNameInFirstRow" type="text" selector=".col-website_title>a"/> - <element name="storeGrpNameInFirstRow" type="text" selector=".col-group_title>a"/> - <element name="storeNameInFirstRow" type="text" selector=".col-store_title>a"/> + <element name="websiteInFirstRow" type="text" selector=".data-row[data-repeat-index='0'] td:nth-of-type(1) a"/> + <element name="storeInFirstRow" type="text" selector=".data-row[data-repeat-index='0'] td:nth-of-type(2) a"/> + <element name="storeViewInFirstRow" type="text" selector=".data-row[data-repeat-index='0'] td:nth-of-type(3) a"/> </section> </sections> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupTest.xml index 860fb4a6a9327..bb1dbb3559d74 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupTest.xml @@ -38,19 +38,14 @@ <amOnPage stepKey="openAdminSystemStorePage" url="{{AdminSystemStorePage.url}}"/> - <click stepKey="clickResetButton" selector="{{AdminStoresGridSection.resetButton}}"/> - <waitForPageLoad stepKey="waitForPageLoadAfterReset" time="10"/> + <actionGroup ref="filterStoresGridByStore" stepKey="enterStoreGroup1Name"> + <argument name="store" value="$$createCustomStoreGroup1.group[name]$$"/> + </actionGroup> + <see stepKey="seeStoreGroup1NameAfterSearch" selector="{{AdminStoresGridSection.storeInFirstRow}}" userInput="$$createCustomStoreGroup1.group[name]$$"/> - <fillField stepKey="enterStoreGroup1Name" selector="{{AdminStoresGridSection.storeGrpFilterTextField}}" userInput="$$createCustomStoreGroup1.group[name]$$"/> - <click stepKey="searchStoreGroup1Name" selector="{{AdminStoresGridSection.searchButton}}"/> - <waitForPageLoad stepKey="waitForPageLoadAfterSearch" time="10"/> - <see stepKey="seeStoreGroup1NameAfterSearch" selector="{{AdminStoresGridSection.storeGrpNameInFirstRow}}" userInput="$$createCustomStoreGroup1.group[name]$$"/> - - <click stepKey="clickResetButton2" selector="{{AdminStoresGridSection.resetButton}}"/> - <waitForPageLoad stepKey="waitForPageLoadAfterReset2" time="10"/> - <fillField stepKey="enterStoreGroup2Name2" selector="{{AdminStoresGridSection.storeGrpFilterTextField}}" userInput="$$createCustomStoreGroup2.group[name]$$"/> - <click stepKey="searchStoreGroup2Name" selector="{{AdminStoresGridSection.searchButton}}"/> - <waitForPageLoad stepKey="waitForPageLoadAfterSearch2" time="10"/> - <see stepKey="seeStoreGroup1NameAfterSearch2" selector="{{AdminStoresGridSection.storeGrpNameInFirstRow}}" userInput="$$createCustomStoreGroup2.group[name]$$"/> + <actionGroup ref="filterStoresGridByStore" stepKey="enterStoreGroup2Name"> + <argument name="store" value="$$createCustomStoreGroup2.group[name]$$"/> + </actionGroup> + <see stepKey="seeStoreGroup1NameAfterSearch2" selector="{{AdminStoresGridSection.storeInFirstRow}}" userInput="$$createCustomStoreGroup2.group[name]$$"/> </test> </tests> diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/System/Store/StoreGrid.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/System/Store/StoreGrid.php index 41eda1d206f9d..275b1225f71a7 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/System/Store/StoreGrid.php +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/System/Store/StoreGrid.php @@ -6,10 +6,10 @@ namespace Magento\Backend\Test\Block\System\Store; +use Magento\Mtf\Client\Locator; use Magento\Store\Test\Fixture\Store; use Magento\Store\Test\Fixture\StoreGroup; use Magento\Store\Test\Fixture\Website; -use Magento\Mtf\Client\Locator; use Magento\Ui\Test\Block\Adminhtml\DataGrid; /** @@ -17,6 +17,20 @@ */ class StoreGrid extends DataGrid { + /** + * Secondary part of row locator template for getRow() method + * + * @var string + */ + protected $rowTemplate = 'td[div[*[contains(.,normalize-space("%s"))]]]'; + + /** + * Secondary part of row locator template for getRow() method with strict option + * + * @var string + */ + protected $rowTemplateStrict = 'td[div[*[text()[normalize-space()="%s"]]]]'; + /** * Locator value for opening needed row. * From bd3fcb00f44def21b6d45b2908dd33e7d75efa3a Mon Sep 17 00:00:00 2001 From: Dmytro Yushkin <dyushkin@adobe.com> Date: Fri, 22 Mar 2019 12:05:03 -0500 Subject: [PATCH 0978/1295] MAGETWO-96957: Pager does not work on Newsletter Subscribers Admin page --- .../Grid/Massaction/AbstractMassaction.php | 29 +++-- .../Unit/Block/Widget/Grid/MassactionTest.php | 56 ---------- .../Block/Widget/Grid/MassactionTest.php | 52 --------- .../Block/Adminhtml/Subscriber/GridTest.php | 105 ++++++++++++++++++ 4 files changed, 119 insertions(+), 123 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Newsletter/Block/Adminhtml/Subscriber/GridTest.php diff --git a/app/code/Magento/Backend/Block/Widget/Grid/Massaction/AbstractMassaction.php b/app/code/Magento/Backend/Block/Widget/Grid/Massaction/AbstractMassaction.php index ddabeb90921c2..968e34d211cfd 100644 --- a/app/code/Magento/Backend/Block/Widget/Grid/Massaction/AbstractMassaction.php +++ b/app/code/Magento/Backend/Block/Widget/Grid/Massaction/AbstractMassaction.php @@ -281,24 +281,23 @@ public function getGridIdsJson() if (!$this->getUseSelectAll()) { return ''; } - /** @var \Magento\Framework\Data\Collection $allIdsCollection */ - $allIdsCollection = clone $this->getParentBlock()->getCollection(); - if ($this->getMassactionIdField()) { - $massActionIdField = $this->getMassactionIdField(); + /** @var \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection $collection */ + $collection = clone $this->getParentBlock()->getCollection(); + + if ($collection instanceof AbstractDb) { + $idsSelect = clone $collection->getSelect(); + $idsSelect->reset(\Magento\Framework\DB\Select::ORDER); + $idsSelect->reset(\Magento\Framework\DB\Select::LIMIT_COUNT); + $idsSelect->reset(\Magento\Framework\DB\Select::LIMIT_OFFSET); + $idsSelect->reset(\Magento\Framework\DB\Select::COLUMNS); + $idsSelect->columns($this->getMassactionIdField(), 'main_table'); + $idList = $collection->getConnection()->fetchCol($idsSelect); } else { - $massActionIdField = $this->getParentBlock()->getMassactionIdField(); + $idList = $collection->setPageSize(0)->getColumnValues($this->getMassactionIdField()); } - if ($allIdsCollection instanceof AbstractDb) { - $allIdsCollection->getSelect()->limit(); - $allIdsCollection->clear(); - } - - $gridIds = $allIdsCollection->setPageSize(0)->getColumnValues($massActionIdField); - if (!empty($gridIds)) { - return join(",", $gridIds); - } - return ''; + + return implode(',', $idList); } /** diff --git a/app/code/Magento/Backend/Test/Unit/Block/Widget/Grid/MassactionTest.php b/app/code/Magento/Backend/Test/Unit/Block/Widget/Grid/MassactionTest.php index e8143b5f6b43a..e62b73f39241d 100644 --- a/app/code/Magento/Backend/Test/Unit/Block/Widget/Grid/MassactionTest.php +++ b/app/code/Magento/Backend/Test/Unit/Block/Widget/Grid/MassactionTest.php @@ -269,62 +269,6 @@ public function testGetGridIdsJsonWithoutUseSelectAll() $this->assertEmpty($this->_block->getGridIdsJson()); } - /** - * @param array $items - * @param string $result - * - * @dataProvider dataProviderGetGridIdsJsonWithUseSelectAll - */ - public function testGetGridIdsJsonWithUseSelectAll(array $items, $result) - { - $this->_block->setUseSelectAll(true); - - if ($this->_block->getMassactionIdField()) { - $massActionIdField = $this->_block->getMassactionIdField(); - } else { - $massActionIdField = $this->_block->getParentBlock()->getMassactionIdField(); - } - - $collectionMock = $this->getMockBuilder(\Magento\Framework\Data\Collection::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->_gridMock->expects($this->once()) - ->method('getCollection') - ->willReturn($collectionMock); - $collectionMock->expects($this->once()) - ->method('setPageSize') - ->with(0) - ->willReturnSelf(); - $collectionMock->expects($this->once()) - ->method('getColumnValues') - ->with($massActionIdField) - ->willReturn($items); - - $this->assertEquals($result, $this->_block->getGridIdsJson()); - } - - /** - * @return array - */ - public function dataProviderGetGridIdsJsonWithUseSelectAll() - { - return [ - [ - [], - '', - ], - [ - [1], - '1', - ], - [ - [1, 2, 3], - '1,2,3', - ], - ]; - } - /** * @param string $itemId * @param array|\Magento\Framework\DataObject $item diff --git a/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Grid/MassactionTest.php b/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Grid/MassactionTest.php index 8aeee9cf12494..e11c5ce5d9cf3 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Grid/MassactionTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Block/Widget/Grid/MassactionTest.php @@ -87,41 +87,6 @@ public function testMassactionDefaultValues() $this->assertFalse($blockEmpty->isAvailable()); } - public function testGetJavaScript() - { - $this->loadLayout(); - - $javascript = $this->_block->getJavaScript(); - - $expectedItemFirst = '#"option_id1":{"label":"Option One",' . - '"url":"http:\\\/\\\/localhost\\\/index\.php\\\/(?:key\\\/([\w\d]+)\\\/)?",' . - '"complete":"Test","id":"option_id1"}#'; - $this->assertRegExp($expectedItemFirst, $javascript); - - $expectedItemSecond = '#"option_id2":{"label":"Option Two",' . - '"url":"http:\\\/\\\/localhost\\\/index\.php\\\/(?:key\\\/([\w\d]+)\\\/)?",' . - '"confirm":"Are you sure\?","id":"option_id2"}#'; - $this->assertRegExp($expectedItemSecond, $javascript); - } - - public function testGetJavaScriptWithAddedItem() - { - $this->loadLayout(); - - $input = [ - 'id' => 'option_id3', - 'label' => 'Option Three', - 'url' => '*/*/option3', - 'block_name' => 'admin.test.grid.massaction.option3', - ]; - $expected = '#"option_id3":{"id":"option_id3","label":"Option Three",' . - '"url":"http:\\\/\\\/localhost\\\/index\.php\\\/(?:key\\\/([\w\d]+)\\\/)?",' . - '"block_name":"admin.test.grid.massaction.option3"}#'; - - $this->_block->addItem($input['id'], $input); - $this->assertRegExp($expected, $this->_block->getJavaScript()); - } - /** * @param string $mageMode * @param int $expectedCount @@ -213,21 +178,4 @@ public function getItemsDataProvider() ] ]; } - - public function testGridContainsMassactionColumn() - { - $this->loadLayout(); - $this->_layout->getBlock('admin.test.grid')->toHtml(); - - $gridMassactionColumn = $this->_layout->getBlock('admin.test.grid') - ->getColumnSet() - ->getChildBlock('massaction'); - - $this->assertNotNull($gridMassactionColumn, 'Massaction column does not exist in the grid column set'); - $this->assertInstanceOf( - \Magento\Backend\Block\Widget\Grid\Column::class, - $gridMassactionColumn, - 'Massaction column is not an instance of \Magento\Backend\Block\Widget\Column' - ); - } } diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Block/Adminhtml/Subscriber/GridTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Block/Adminhtml/Subscriber/GridTest.php new file mode 100644 index 0000000000000..48d3356525f49 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Block/Adminhtml/Subscriber/GridTest.php @@ -0,0 +1,105 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Newsletter\Block\Adminhtml\Subscriber; + +/** + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + * + * @see \Magento\Newsletter\Block\Adminhtml\Subscriber\Grid + */ +class GridTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var null|\Magento\Framework\ObjectManagerInterface + */ + private $objectManager = null; + /** + * @var null|\Magento\Framework\View\LayoutInterface + */ + private $layout = null; + + /** + * Set up layout. + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + + $this->layout = $this->objectManager->create(\Magento\Framework\View\LayoutInterface::class); + $this->layout->getUpdate()->load('newsletter_subscriber_grid'); + $this->layout->generateXml(); + $this->layout->generateElements(); + } + + /** + * Check if mass action block exists. + */ + public function testMassActionBlockExists() + { + $this->assertNotFalse( + $this->getMassActionBlock(), + 'Mass action block does not exist in the grid, or it name was changed.' + ); + } + + /** + * Check if mass action id field is correct. + */ + public function testMassActionFieldIdIsCorrect() + { + $this->assertEquals( + 'subscriber_id', + $this->getMassActionBlock()->getMassactionIdField(), + 'Mass action id field is incorrect.' + ); + } + + /** + * Check if function returns correct result. + * + * @magentoDataFixture Magento/Newsletter/_files/subscribers.php + */ + public function testMassActionBlockContainsCorrectIdList() + { + $this->assertEquals( + implode(',', $this->getAllSubscriberIdList()), + $this->getMassActionBlock()->getGridIdsJson(), + 'Function returns incorrect result.' + ); + } + + /** + * Retrieve mass action block. + * + * @return bool|\Magento\Backend\Block\Widget\Grid\Massaction + */ + private function getMassActionBlock() + { + return $this->layout->getBlock('adminhtml.newslettrer.subscriber.grid.massaction'); + } + + /** + * Retrieve list of id of all subscribers. + * + * @return array + */ + private function getAllSubscriberIdList() + { + /** @var \Magento\Framework\App\ResourceConnection $resourceConnection */ + $resourceConnection = $this->objectManager->get(\Magento\Framework\App\ResourceConnection::class); + $select = $resourceConnection->getConnection() + ->select() + ->from($resourceConnection->getTableName('newsletter_subscriber')) + ->columns(['subscriber_id' => 'subscriber_id']); + + return $resourceConnection->getConnection()->fetchCol($select); + } +} From f68a99bae37d9a4d15b2a98c23b6f782bc913574 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov <korablov@adobe.com> Date: Fri, 22 Mar 2019 14:15:04 -0500 Subject: [PATCH 0979/1295] MAGETWO-98843: [Backport] Add support ZooKeeper and flock locks --- app/etc/di.xml | 2 +- .../Framework/Lock/Backend/FileLockTest.php | 55 +++ .../Framework/Lock/Backend/ZookeeperTest.php | 90 +++++ .../Framework/Lock/Backend/FileLock.php | 194 ++++++++++ .../Framework/Lock/Backend/Zookeeper.php | 280 ++++++++++++++ .../Framework/Lock/LockBackendFactory.php | 111 ++++++ .../Framework/Lock/LockManagerInterface.php | 2 +- lib/internal/Magento/Framework/Lock/Proxy.php | 83 +++++ .../Lock/Test/Unit/Backend/ZookeeperTest.php | 68 ++++ .../Lock/Test/Unit/LockBackendFactoryTest.php | 116 ++++++ .../Framework/Lock/Test/Unit/ProxyTest.php | 106 ++++++ .../Magento/Setup/Model/ConfigOptionsList.php | 3 +- .../Setup/Model/ConfigOptionsList/Lock.php | 342 ++++++++++++++++++ .../Unit/Model/ConfigOptionsList/LockTest.php | 232 ++++++++++++ .../Test/Unit/Model/ConfigOptionsListTest.php | 17 +- 15 files changed, 1692 insertions(+), 9 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileLockTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php create mode 100644 lib/internal/Magento/Framework/Lock/Backend/FileLock.php create mode 100644 lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php create mode 100644 lib/internal/Magento/Framework/Lock/LockBackendFactory.php create mode 100644 lib/internal/Magento/Framework/Lock/Proxy.php create mode 100644 lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php create mode 100644 lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php create mode 100644 lib/internal/Magento/Framework/Lock/Test/Unit/ProxyTest.php create mode 100644 setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php create mode 100644 setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php diff --git a/app/etc/di.xml b/app/etc/di.xml index 05fd34a178ded..5e7d4f67b8b23 100755 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -37,7 +37,7 @@ <preference for="Magento\Framework\Locale\ListsInterface" type="Magento\Framework\Locale\TranslatedLists" /> <preference for="Magento\Framework\Locale\AvailableLocalesInterface" type="Magento\Framework\Locale\Deployed\Codes" /> <preference for="Magento\Framework\Locale\OptionInterface" type="Magento\Framework\Locale\Deployed\Options" /> - <preference for="Magento\Framework\Lock\LockManagerInterface" type="Magento\Framework\Lock\Backend\Database" /> + <preference for="Magento\Framework\Lock\LockManagerInterface" type="Magento\Framework\Lock\Proxy" /> <preference for="Magento\Framework\Api\AttributeTypeResolverInterface" type="Magento\Framework\Reflection\AttributeTypeResolver" /> <preference for="Magento\Framework\Api\Search\SearchResultInterface" type="Magento\Framework\Api\Search\SearchResult" /> <preference for="Magento\Framework\Api\Search\SearchCriteriaInterface" type="Magento\Framework\Api\Search\SearchCriteria"/> diff --git a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileLockTest.php b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileLockTest.php new file mode 100644 index 0000000000000..e64b3c505acf1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/FileLockTest.php @@ -0,0 +1,55 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Lock\Backend; + +/** + * \Magento\Framework\Lock\Backend\File test case + */ +class FileLockTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Framework\Lock\Backend\FileLock + */ + private $model; + + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + + protected function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->model = $this->objectManager->create( + \Magento\Framework\Lock\Backend\FileLock::class, + ['path' => '/tmp'] + ); + } + + public function testLockAndUnlock() + { + $name = 'test_lock'; + + $this->assertFalse($this->model->isLocked($name)); + + $this->assertTrue($this->model->lock($name)); + $this->assertTrue($this->model->isLocked($name)); + $this->assertFalse($this->model->lock($name, 2)); + + $this->assertTrue($this->model->unlock($name)); + $this->assertFalse($this->model->isLocked($name)); + } + + public function testUnlockWithoutExistingLock() + { + $name = 'test_lock'; + + $this->assertFalse($this->model->isLocked($name)); + $this->assertFalse($this->model->unlock($name)); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php new file mode 100644 index 0000000000000..8d0caad5d55e4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Lock/Backend/ZookeeperTest.php @@ -0,0 +1,90 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Lock\Backend; + +use Magento\Framework\Lock\Backend\Zookeeper as ZookeeperLock; +use Magento\Framework\Lock\LockBackendFactory; +use Magento\Framework\Config\File\ConfigFilePool; +use Magento\Framework\App\DeploymentConfig\FileReader; +use Magento\Framework\Stdlib\ArrayManager; + +/** + * \Magento\Framework\Lock\Backend\Zookeeper test case + */ +class ZookeeperTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var FileReader + */ + private $configReader; + + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + + /** + * @var LockBackendFactory + */ + private $lockBackendFactory; + + /** + * @var ArrayManager + */ + private $arrayManager; + + /** + * @var ZookeeperLock + */ + private $model; + + /** + * @inheritdoc + */ + protected function setUp() + { + if (!extension_loaded('zookeeper')) { + $this->markTestSkipped('php extension Zookeeper is not installed.'); + } + + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->configReader = $this->objectManager->get(FileReader::class); + $this->lockBackendFactory = $this->objectManager->create(LockBackendFactory::class); + $this->arrayManager = $this->objectManager->create(ArrayManager::class); + $config = $this->configReader->load(ConfigFilePool::APP_ENV); + + if ($this->arrayManager->get('lock/provider', $config) !== 'zookeeper') { + $this->markTestSkipped('Zookeeper is not configured during installation.'); + } + + $this->model = $this->lockBackendFactory->create(); + $this->assertInstanceOf(ZookeeperLock::class, $this->model); + } + + public function testLockAndUnlock() + { + $name = 'test_lock'; + + $this->assertFalse($this->model->isLocked($name)); + + $this->assertTrue($this->model->lock($name)); + $this->assertTrue($this->model->isLocked($name)); + $this->assertFalse($this->model->lock($name, 2)); + + $this->assertTrue($this->model->unlock($name)); + $this->assertFalse($this->model->isLocked($name)); + } + + public function testUnlockWithoutExistingLock() + { + $name = 'test_lock'; + + $this->assertFalse($this->model->isLocked($name)); + $this->assertFalse($this->model->unlock($name)); + } +} diff --git a/lib/internal/Magento/Framework/Lock/Backend/FileLock.php b/lib/internal/Magento/Framework/Lock/Backend/FileLock.php new file mode 100644 index 0000000000000..d168e910a4ab7 --- /dev/null +++ b/lib/internal/Magento/Framework/Lock/Backend/FileLock.php @@ -0,0 +1,194 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Lock\Backend; + +use Magento\Framework\Lock\LockManagerInterface; +use Magento\Framework\Filesystem\Driver\File as FileDriver; +use Magento\Framework\Exception\RuntimeException; +use Magento\Framework\Exception\FileSystemException; +use Magento\Framework\Phrase; + +/** + * LockManager using the file system for locks + */ +class FileLock implements LockManagerInterface +{ + /** + * The file driver instance + * + * @var FileDriver + */ + private $fileDriver; + + /** + * The path to the locks storage folder + * + * @var string + */ + private $path; + + /** + * How many microseconds to wait before re-try to acquire a lock + * + * @var int + */ + private $sleepCycle = 100000; + + /** + * The mapping list of the path lock with the file resource + * + * @var array + */ + private $locks = []; + + /** + * @param FileDriver $fileDriver The file driver + * @param string $path The path to the locks storage folder + * @throws RuntimeException Throws RuntimeException if $path is empty + * or cannot create the directory for locks + */ + public function __construct(FileDriver $fileDriver, string $path) + { + if (!$path) { + throw new RuntimeException(new Phrase('The path needs to be a non-empty string.')); + } + + $this->fileDriver = $fileDriver; + $this->path = rtrim($path, '/') . '/'; + + try { + if (!$this->fileDriver->isExists($this->path)) { + $this->fileDriver->createDirectory($this->path); + } + } catch (FileSystemException $exception) { + throw new RuntimeException( + new Phrase('Cannot create the directory for locks: %1', [$this->path]), + $exception + ); + } + } + + /** + * Acquires a lock by name + * + * @param string $name The lock name + * @param int $timeout Timeout in seconds. A negative timeout value means infinite timeout + * @return bool Returns true if the lock is acquired, otherwise returns false + * @throws RuntimeException Throws RuntimeException if cannot acquires the lock because FS problems + */ + public function lock(string $name, int $timeout = -1): bool + { + try { + $lockFile = $this->getLockPath($name); + $fileResource = $this->fileDriver->fileOpen($lockFile, 'w+'); + $skipDeadline = $timeout < 0; + $deadline = microtime(true) + $timeout; + + while (!$this->tryToLock($fileResource)) { + if (!$skipDeadline && $deadline <= microtime(true)) { + $this->fileDriver->fileClose($fileResource); + return false; + } + usleep($this->sleepCycle); + } + } catch (FileSystemException $exception) { + throw new RuntimeException(new Phrase('Cannot acquire a lock.'), $exception); + } + + $this->locks[$lockFile] = $fileResource; + return true; + } + + /** + * Checks if a lock exists by name + * + * @param string $name The lock name + * @return bool Returns true if the lock exists, otherwise returns false + * @throws RuntimeException Throws RuntimeException if cannot check that the lock exists + */ + public function isLocked(string $name): bool + { + $lockFile = $this->getLockPath($name); + $result = false; + + try { + if ($this->fileDriver->isExists($lockFile)) { + $fileResource = $this->fileDriver->fileOpen($lockFile, 'w+'); + if ($this->tryToLock($fileResource)) { + $result = false; + } else { + $result = true; + } + $this->fileDriver->fileClose($fileResource); + } + } catch (FileSystemException $exception) { + throw new RuntimeException(new Phrase('Cannot verify that the lock exists.'), $exception); + } + + return $result; + } + + /** + * Remove the lock by name + * + * @param string $name The lock name + * @return bool If the lock is removed returns true, otherwise returns false + */ + public function unlock(string $name): bool + { + $lockFile = $this->getLockPath($name); + + if (isset($this->locks[$lockFile]) && $this->tryToUnlock($this->locks[$lockFile])) { + unset($this->locks[$lockFile]); + return true; + } + + return false; + } + + /** + * Returns the full path to the lock file by name + * + * @param string $name The lock name + * @return string The path to the lock file + */ + private function getLockPath(string $name): string + { + return $this->path . $name; + } + + /** + * Tries to lock a file resource + * + * @param resource $resource The file resource + * @return bool If the lock is acquired returns true, otherwise returns false + */ + private function tryToLock($resource): bool + { + try { + return $this->fileDriver->fileLock($resource, LOCK_EX | LOCK_NB); + } catch (FileSystemException $exception) { + return false; + } + } + + /** + * Tries to unlock a file resource + * + * @param resource $resource The file resource + * @return bool If the lock is removed returns true, otherwise returns false + */ + private function tryToUnlock($resource): bool + { + try { + return $this->fileDriver->fileLock($resource, LOCK_UN | LOCK_NB); + } catch (FileSystemException $exception) { + return false; + } + } +} diff --git a/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php new file mode 100644 index 0000000000000..cbba981ae1b51 --- /dev/null +++ b/lib/internal/Magento/Framework/Lock/Backend/Zookeeper.php @@ -0,0 +1,280 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Lock\Backend; + +use Magento\Framework\Lock\LockManagerInterface; +use Magento\Framework\Exception\RuntimeException; +use Magento\Framework\Phrase; + +/** + * LockManager using the Zookeeper for locks + */ +class Zookeeper implements LockManagerInterface +{ + /** + * Zookeeper provider + * + * @var \Zookeeper + */ + private $zookeeper; + + /** + * The base path to locks in Zookeeper + * + * @var string + */ + private $path; + + /** + * The name of sequence nodes + * + * @var string + */ + private $lockName = 'lock-'; + + /** + * The host to connect to Zookeeper + * + * @var string + */ + private $host; + + /** + * How many seconds to wait before timing out on connections + * + * @var int + */ + private $connectionTimeout = 2; + + /** + * How many microseconds to wait before recheck connections or nodes + * + * @var int + */ + private $sleepCycle = 100000; + + /** + * The default permissions for Zookeeper nodes + * + * @var array + */ + private $acl = [['perms'=>\Zookeeper::PERM_ALL, 'scheme' => 'world', 'id' => 'anyone']]; + + /** + * The mapping list of the lock name with the full lock path + * + * @var array + */ + private $locks = []; + + /** + * The default path to storage locks + */ + const DEFAULT_PATH = '/magento/locks'; + + /** + * @param string $host The host to connect to Zookeeper + * @param string $path The base path to locks in Zookeeper + * @throws RuntimeException + */ + public function __construct(string $host, string $path = self::DEFAULT_PATH) + { + if (!$path) { + throw new RuntimeException( + new Phrase('The path needs to be a non-empty string.') + ); + } + + if (!$host) { + throw new RuntimeException( + new Phrase('The host needs to be a non-empty string.') + ); + } + + $this->host = $host; + $this->path = rtrim($path, '/') . '/'; + } + + /** + * @inheritdoc + * + * You can see the lock algorithm by the link + * @link https://zookeeper.apache.org/doc/r3.1.2/recipes.html#sc_recipes_Locks + * + * @throws RuntimeException + */ + public function lock(string $name, int $timeout = -1): bool + { + $skipDeadline = $timeout < 0; + $lockPath = $this->getFullPathToLock($name); + $deadline = microtime(true) + $timeout; + + if (!$this->checkAndCreateParentNode($lockPath)) { + throw new RuntimeException(new Phrase('Failed creating the path %1', [$lockPath])); + } + + $lockKey = $this->getProvider() + ->create($lockPath, '1', $this->acl, \Zookeeper::EPHEMERAL | \Zookeeper::SEQUENCE); + + if (!$lockKey) { + throw new RuntimeException(new Phrase('Failed creating lock %1', [$lockPath])); + } + + while ($this->isAnyLock($lockKey, $this->getIndex($lockKey))) { + if (!$skipDeadline && $deadline <= microtime(true)) { + $this->getProvider()->delete($lockKey); + return false; + } + + usleep($this->sleepCycle); + } + + $this->locks[$name] = $lockKey; + + return true; + } + + /** + * @inheritdoc + * + * @throws RuntimeException + */ + public function unlock(string $name): bool + { + if (!isset($this->locks[$name])) { + return false; + } + + return $this->getProvider()->delete($this->locks[$name]); + } + + /** + * @inheritdoc + * + * @throws RuntimeException + */ + public function isLocked(string $name): bool + { + return $this->isAnyLock($this->getFullPathToLock($name)); + } + + /** + * Gets full path to lock by its name + * + * @param string $name + * @return string + */ + private function getFullPathToLock(string $name): string + { + return $this->path . $name . '/' . $this->lockName; + } + + /** + * Initiolizes and returns Zookeeper provider + * + * @return \Zookeeper + * @throws RuntimeException + */ + private function getProvider(): \Zookeeper + { + if (!$this->zookeeper) { + $this->zookeeper = new \Zookeeper($this->host); + } + + $deadline = microtime(true) + $this->connectionTimeout; + while ($this->zookeeper->getState() != \Zookeeper::CONNECTED_STATE) { + if ($deadline <= microtime(true)) { + throw new RuntimeException(new Phrase('Zookeeper connection timed out!')); + } + usleep($this->sleepCycle); + } + + return $this->zookeeper; + } + + /** + * Checks and creates base path recursively + * + * @param string $path + * @return bool + * @throws RuntimeException + */ + private function checkAndCreateParentNode(string $path): bool + { + $path = dirname($path); + if ($this->getProvider()->exists($path)) { + return true; + } + + if (!$this->checkAndCreateParentNode($path)) { + return false; + } + + if ($this->getProvider()->create($path, '1', $this->acl)) { + return true; + } + + return $this->getProvider()->exists($path); + } + + /** + * Gets int increment of lock key + * + * @param string $key + * @return int|null + */ + private function getIndex(string $key) + { + if (!preg_match('/' . $this->lockName . '([0-9]+)$/', $key, $matches)) { + return null; + } + + return intval($matches[1]); + } + + /** + * Checks if there is any sequence node under parent of $fullKey. + * + * At first checks that the $fullKey node is present, if not - returns false. + * If $indexKey is non-null and there is a smaller index than $indexKey then returns true, + * otherwise returns false. + * + * @param string $fullKey The full path without any sequence info + * @param int|null $indexKey The index to compare + * @return bool + * @throws RuntimeException + */ + private function isAnyLock(string $fullKey, int $indexKey = null): bool + { + $parent = dirname($fullKey); + + if (!$this->getProvider()->exists($parent)) { + return false; + } + + $children = $this->getProvider()->getChildren($parent); + + if (null === $indexKey && !empty($children)) { + return true; + } + + foreach ($children as $childKey) { + $childIndex = $this->getIndex($childKey); + + if (null === $childIndex) { + continue; + } + + if ($childIndex < $indexKey) { + return true; + } + } + + return false; + } +} diff --git a/lib/internal/Magento/Framework/Lock/LockBackendFactory.php b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php new file mode 100644 index 0000000000000..b142085ef6563 --- /dev/null +++ b/lib/internal/Magento/Framework/Lock/LockBackendFactory.php @@ -0,0 +1,111 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Lock; + +use Magento\Framework\Phrase; +use Magento\Framework\Exception\RuntimeException; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\Lock\Backend\Database as DatabaseLock; +use Magento\Framework\Lock\Backend\Zookeeper as ZookeeperLock; +use Magento\Framework\Lock\Backend\Cache as CacheLock; +use Magento\Framework\Lock\Backend\FileLock; + +/** + * The factory to create object that implements LockManagerInterface + */ +class LockBackendFactory +{ + /** + * The Object Manager instance + * + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * The Application deployment configuration + * + * @var DeploymentConfig + */ + private $deploymentConfig; + + /** + * DB lock provider name + * + * @const string + */ + const LOCK_DB = 'db'; + + /** + * Zookeeper lock provider name + * + * @const string + */ + const LOCK_ZOOKEEPER = 'zookeeper'; + + /** + * Cache lock provider name + * + * @const string + */ + const LOCK_CACHE = 'cache'; + + /** + * File lock provider name + * + * @const string + */ + const LOCK_FILE = 'file'; + + /** + * The list of lock providers with mapping on classes + * + * @var array + */ + private $lockers = [ + self::LOCK_DB => DatabaseLock::class, + self::LOCK_ZOOKEEPER => ZookeeperLock::class, + self::LOCK_CACHE => CacheLock::class, + self::LOCK_FILE => FileLock::class, + ]; + + /** + * @param ObjectManagerInterface $objectManager The Object Manager instance + * @param DeploymentConfig $deploymentConfig The Application deployment configuration + */ + public function __construct( + ObjectManagerInterface $objectManager, + DeploymentConfig $deploymentConfig + ) { + $this->objectManager = $objectManager; + $this->deploymentConfig = $deploymentConfig; + } + + /** + * Creates an instance of LockManagerInterface using information from deployment config + * + * @return LockManagerInterface + * @throws RuntimeException + */ + public function create(): LockManagerInterface + { + $provider = $this->deploymentConfig->get('lock/provider', self::LOCK_DB); + $config = $this->deploymentConfig->get('lock/config', []); + + if (!isset($this->lockers[$provider])) { + throw new RuntimeException(new Phrase('Unknown locks provider: %1', [$provider])); + } + + if (self::LOCK_ZOOKEEPER === $provider && !extension_loaded(self::LOCK_ZOOKEEPER)) { + throw new RuntimeException(new Phrase('php extension Zookeeper is not installed.')); + } + + return $this->objectManager->create($this->lockers[$provider], $config); + } +} diff --git a/lib/internal/Magento/Framework/Lock/LockManagerInterface.php b/lib/internal/Magento/Framework/Lock/LockManagerInterface.php index 9df65f45adac3..76cc8506eb182 100644 --- a/lib/internal/Magento/Framework/Lock/LockManagerInterface.php +++ b/lib/internal/Magento/Framework/Lock/LockManagerInterface.php @@ -3,8 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - declare(strict_types=1); + namespace Magento\Framework\Lock; /** diff --git a/lib/internal/Magento/Framework/Lock/Proxy.php b/lib/internal/Magento/Framework/Lock/Proxy.php new file mode 100644 index 0000000000000..2718bf6cb3456 --- /dev/null +++ b/lib/internal/Magento/Framework/Lock/Proxy.php @@ -0,0 +1,83 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Lock; + +use Magento\Framework\Exception\RuntimeException; + +/** + * Proxy for LockManagers + */ +class Proxy implements LockManagerInterface +{ + /** + * The factory to create LockManagerInterface implementation + * + * @var LockBackendFactory + */ + private $factory; + + /** + * A LockManagerInterface implementation + * + * @var LockManagerInterface + */ + private $locker; + + /** + * @param LockBackendFactory $factory The factory to create LockManagerInterface implementation + */ + public function __construct(LockBackendFactory $factory) + { + $this->factory = $factory; + } + + /** + * @inheritdoc + * + * @throws RuntimeException + */ + public function isLocked(string $name): bool + { + return $this->getLocker()->isLocked($name); + } + + /** + * @inheritdoc + * + * @throws RuntimeException + */ + public function lock(string $name, int $timeout = -1): bool + { + return $this->getLocker()->lock($name, $timeout); + } + + /** + * @inheritdoc + * + * @throws RuntimeException + */ + public function unlock(string $name): bool + { + return $this->getLocker()->unlock($name); + } + + /** + * Gets LockManagerInterface implementation using Factory + * + * @return LockManagerInterface + * @throws RuntimeException + */ + private function getLocker(): LockManagerInterface + { + if (!$this->locker) { + $this->locker = $this->factory->create(); + } + + return $this->locker; + } +} diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php new file mode 100644 index 0000000000000..62521b9de3082 --- /dev/null +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/ZookeeperTest.php @@ -0,0 +1,68 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Lock\Test\Unit\Backend; + +use Magento\Framework\Lock\Backend\Zookeeper as ZookeeperProvider; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; + +class ZookeeperTest extends TestCase +{ + /** + * @var ZookeeperProvider + */ + private $zookeeperProvider; + + /** + * @var string + */ + private $host = 'localhost:123'; + + /** + * @var string + */ + private $path = '/some/path'; + + /** + * @inheritdoc + */ + protected function setUp() + { + if (!extension_loaded('zookeeper')) { + $this->markTestSkipped('Test was skipped because php extension Zookeeper is not installed.'); + } + } + + /** + * @expectedException \Magento\Framework\Exception\RuntimeException + * @expectedExceptionMessage The path needs to be a non-empty string. + * @return void + */ + public function testConstructionWithPathException() + { + $this->zookeeperProvider = new ZookeeperProvider($this->host, ''); + } + + /** + * @expectedException \Magento\Framework\Exception\RuntimeException + * @expectedExceptionMessage The host needs to be a non-empty string. + * @return void + */ + public function testConstructionWithHostException() + { + $this->zookeeperProvider = new ZookeeperProvider('', $this->path); + } + + /** + * @return void + */ + public function testConstructionWithoutException() + { + $this->zookeeperProvider = new ZookeeperProvider($this->host, $this->path); + } +} diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php new file mode 100644 index 0000000000000..ebf2f54f3e093 --- /dev/null +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/LockBackendFactoryTest.php @@ -0,0 +1,116 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Lock\Test\Unit; + +use Magento\Framework\Lock\Backend\Database as DatabaseLock; +use Magento\Framework\Lock\Backend\Zookeeper as ZookeeperLock; +use Magento\Framework\Lock\Backend\Cache as CacheLock; +use Magento\Framework\Lock\Backend\FileLock; +use Magento\Framework\Lock\LockBackendFactory; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Lock\LockManagerInterface; +use Magento\Framework\App\DeploymentConfig; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; + +class LockBackendFactoryTest extends TestCase +{ + /** + * @var ObjectManagerInterface|MockObject + */ + private $objectManagerMock; + + /** + * @var DeploymentConfig|MockObject + */ + private $deploymentConfigMock; + + /** + * @var LockBackendFactory + */ + private $factory; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManagerMock = $this->getMockForAbstractClass(ObjectManagerInterface::class); + $this->deploymentConfigMock = $this->createMock(DeploymentConfig::class); + $this->factory = new LockBackendFactory($this->objectManagerMock, $this->deploymentConfigMock); + } + + /** + * @expectedException \Magento\Framework\Exception\RuntimeException + * @expectedExceptionMessage Unknown locks provider: someProvider + */ + public function testCreateWithException() + { + $this->deploymentConfigMock->expects($this->exactly(2)) + ->method('get') + ->withConsecutive(['lock/provider', LockBackendFactory::LOCK_DB], ['lock/config', []]) + ->willReturnOnConsecutiveCalls('someProvider', []); + + $this->factory->create(); + } + + /** + * @param string $lockProvider + * @param string $lockProviderClass + * @param array $config + * @dataProvider createDataProvider + */ + public function testCreate(string $lockProvider, string $lockProviderClass, array $config) + { + $lockManagerMock = $this->getMockForAbstractClass(LockManagerInterface::class); + $this->deploymentConfigMock->expects($this->exactly(2)) + ->method('get') + ->withConsecutive(['lock/provider', LockBackendFactory::LOCK_DB], ['lock/config', []]) + ->willReturnOnConsecutiveCalls($lockProvider, $config); + $this->objectManagerMock->expects($this->once()) + ->method('create') + ->with($lockProviderClass, $config) + ->willReturn($lockManagerMock); + + $this->assertSame($lockManagerMock, $this->factory->create()); + } + + /** + * @return array + */ + public function createDataProvider(): array + { + $data = [ + 'db' => [ + 'lockProvider' => LockBackendFactory::LOCK_DB, + 'lockProviderClass' => DatabaseLock::class, + 'config' => ['prefix' => 'somePrefix'], + ], + 'cache' => [ + 'lockProvider' => LockBackendFactory::LOCK_CACHE, + 'lockProviderClass' => CacheLock::class, + 'config' => [], + ], + 'file' => [ + 'lockProvider' => LockBackendFactory::LOCK_FILE, + 'lockProviderClass' => FileLock::class, + 'config' => ['path' => '/my/path'], + ], + ]; + + if (extension_loaded('zookeeper')) { + $data['zookeeper'] = [ + 'lockProvider' => LockBackendFactory::LOCK_ZOOKEEPER, + 'lockProviderClass' => ZookeeperLock::class, + 'config' => ['host' => 'some host'], + ]; + } + + return $data; + } +} diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/ProxyTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/ProxyTest.php new file mode 100644 index 0000000000000..c71dad701d715 --- /dev/null +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/ProxyTest.php @@ -0,0 +1,106 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Lock\Test\Unit; + +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; +use Magento\Framework\Lock\Proxy; +use Magento\Framework\Lock\LockBackendFactory; +use Magento\Framework\Lock\LockManagerInterface; + +/** + * @inheritdoc + */ +class ProxyTest extends TestCase +{ + /** + * @var LockBackendFactory|MockObject + */ + private $factoryMock; + + /** + * @var LockManagerInterface|MockObject + */ + private $lockerMock; + + /** + * @var Proxy + */ + private $proxy; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->factoryMock = $this->createMock(LockBackendFactory::class); + $this->lockerMock = $this->getMockForAbstractClass(LockManagerInterface::class); + $this->proxy = new Proxy($this->factoryMock); + } + + /** + * @return void + */ + public function testIsLocked() + { + $lockName = 'testLock'; + $this->factoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->lockerMock); + $this->lockerMock->expects($this->exactly(2)) + ->method('isLocked') + ->with($lockName) + ->willReturn(true); + + $this->assertTrue($this->proxy->isLocked($lockName)); + + // Call one more time to check that method Factory::create is called one time + $this->assertTrue($this->proxy->isLocked($lockName)); + } + + /** + * @return void + */ + public function testLock() + { + $lockName = 'testLock'; + $timeout = 123; + $this->factoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->lockerMock); + $this->lockerMock->expects($this->exactly(2)) + ->method('lock') + ->with($lockName, $timeout) + ->willReturn(true); + + $this->assertTrue($this->proxy->lock($lockName, $timeout)); + + // Call one more time to check that method Factory::create is called one time + $this->assertTrue($this->proxy->lock($lockName, $timeout)); + } + + /** + * @return void + */ + public function testUnlock() + { + $lockName = 'testLock'; + $this->factoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->lockerMock); + $this->lockerMock->expects($this->exactly(2)) + ->method('unlock') + ->with($lockName) + ->willReturn(true); + + $this->assertTrue($this->proxy->unlock($lockName)); + + // Call one more time to check that method Factory::create is called one time + $this->assertTrue($this->proxy->unlock($lockName)); + } +} diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList.php b/setup/src/Magento/Setup/Model/ConfigOptionsList.php index fa79139e73313..29a868b1f0eb2 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList.php @@ -44,7 +44,8 @@ class ConfigOptionsList implements ConfigOptionsListInterface private $configOptionsListClasses = [ \Magento\Setup\Model\ConfigOptionsList\Session::class, \Magento\Setup\Model\ConfigOptionsList\Cache::class, - \Magento\Setup\Model\ConfigOptionsList\PageCache::class + \Magento\Setup\Model\ConfigOptionsList\PageCache::class, + \Magento\Setup\Model\ConfigOptionsList\Lock::class, ]; /** diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php new file mode 100644 index 0000000000000..66f41128c46b1 --- /dev/null +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Lock.php @@ -0,0 +1,342 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Setup\Model\ConfigOptionsList; + +use Magento\Framework\Lock\Backend\Zookeeper as ZookeeperLock; +use Magento\Framework\Lock\LockBackendFactory; +use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\Config\Data\ConfigData; +use Magento\Framework\Config\File\ConfigFilePool; +use Magento\Framework\Setup\ConfigOptionsListInterface; +use Magento\Framework\Setup\Option\SelectConfigOption; +use Magento\Framework\Setup\Option\TextConfigOption; + +/** + * Deployment configuration options for locks + */ +class Lock implements ConfigOptionsListInterface +{ + /** + * The name of an option to set lock provider + * + * @const string + */ + const INPUT_KEY_LOCK_PROVIDER = 'lock-provider'; + + /** + * The name of an option to set DB prefix + * + * @const string + */ + const INPUT_KEY_LOCK_DB_PREFIX = 'lock-db-prefix'; + + /** + * The name of an option to set Zookeeper host + * + * @const string + */ + const INPUT_KEY_LOCK_ZOOKEEPER_HOST = 'lock-zookeeper-host'; + + /** + * The name of an option to set Zookeeper path + * + * @const string + */ + const INPUT_KEY_LOCK_ZOOKEEPER_PATH = 'lock-zookeeper-path'; + + /** + * The name of an option to set File path + * + * @const string + */ + const INPUT_KEY_LOCK_FILE_PATH = 'lock-file-path'; + + /** + * The configuration path to save lock provider + * + * @const string + */ + const CONFIG_PATH_LOCK_PROVIDER = 'lock/provider'; + + /** + * The configuration path to save DB prefix + * + * @const string + */ + const CONFIG_PATH_LOCK_DB_PREFIX = 'lock/config/prefix'; + + /** + * The configuration path to save Zookeeper host + * + * @const string + */ + const CONFIG_PATH_LOCK_ZOOKEEPER_HOST = 'lock/config/host'; + + /** + * The configuration path to save Zookeeper path + * + * @const string + */ + const CONFIG_PATH_LOCK_ZOOKEEPER_PATH = 'lock/config/path'; + + /** + * The configuration path to save locks directory path + * + * @const string + */ + const CONFIG_PATH_LOCK_FILE_PATH = 'lock/config/path'; + + /** + * The list of lock providers + * + * @var array + */ + private $validLockProviders = [ + LockBackendFactory::LOCK_DB, + LockBackendFactory::LOCK_ZOOKEEPER, + LockBackendFactory::LOCK_CACHE, + LockBackendFactory::LOCK_FILE, + ]; + + /** + * The mapping input keys with their configuration paths + * + * @var array + */ + private $mappingInputKeyToConfigPath = [ + LockBackendFactory::LOCK_DB => [ + self::INPUT_KEY_LOCK_PROVIDER => self::CONFIG_PATH_LOCK_PROVIDER, + self::INPUT_KEY_LOCK_DB_PREFIX => self::CONFIG_PATH_LOCK_DB_PREFIX, + ], + LockBackendFactory::LOCK_ZOOKEEPER => [ + self::INPUT_KEY_LOCK_PROVIDER => self::CONFIG_PATH_LOCK_PROVIDER, + self::INPUT_KEY_LOCK_ZOOKEEPER_HOST => self::CONFIG_PATH_LOCK_ZOOKEEPER_HOST, + self::INPUT_KEY_LOCK_ZOOKEEPER_PATH => self::CONFIG_PATH_LOCK_ZOOKEEPER_PATH, + ], + LockBackendFactory::LOCK_CACHE => [ + self::INPUT_KEY_LOCK_PROVIDER => self::CONFIG_PATH_LOCK_PROVIDER, + ], + LockBackendFactory::LOCK_FILE => [ + self::INPUT_KEY_LOCK_PROVIDER => self::CONFIG_PATH_LOCK_PROVIDER, + self::INPUT_KEY_LOCK_FILE_PATH => self::CONFIG_PATH_LOCK_FILE_PATH, + ], + ]; + + /** + * The list of default values + * + * @var array + */ + private $defaultConfigValues = [ + self::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_DB, + self::INPUT_KEY_LOCK_DB_PREFIX => null, + self::INPUT_KEY_LOCK_ZOOKEEPER_PATH => ZookeeperLock::DEFAULT_PATH, + ]; + + /** + * @inheritdoc + */ + public function getOptions() + { + return [ + new SelectConfigOption( + self::INPUT_KEY_LOCK_PROVIDER, + SelectConfigOption::FRONTEND_WIZARD_SELECT, + $this->validLockProviders, + self::CONFIG_PATH_LOCK_PROVIDER, + 'Lock provider name', + LockBackendFactory::LOCK_DB + ), + new TextConfigOption( + self::INPUT_KEY_LOCK_DB_PREFIX, + TextConfigOption::FRONTEND_WIZARD_TEXT, + self::CONFIG_PATH_LOCK_DB_PREFIX, + 'Installation specific lock prefix to avoid lock conflicts' + ), + new TextConfigOption( + self::INPUT_KEY_LOCK_ZOOKEEPER_HOST, + TextConfigOption::FRONTEND_WIZARD_TEXT, + self::CONFIG_PATH_LOCK_ZOOKEEPER_HOST, + 'Host and port to connect to Zookeeper cluster. For example: 127.0.0.1:2181' + ), + new TextConfigOption( + self::INPUT_KEY_LOCK_ZOOKEEPER_PATH, + TextConfigOption::FRONTEND_WIZARD_TEXT, + self::CONFIG_PATH_LOCK_ZOOKEEPER_PATH, + 'The path where Zookeeper will save locks. The default path is: ' . ZookeeperLock::DEFAULT_PATH + ), + new TextConfigOption( + self::INPUT_KEY_LOCK_FILE_PATH, + TextConfigOption::FRONTEND_WIZARD_TEXT, + self::CONFIG_PATH_LOCK_FILE_PATH, + 'The path where file locks will be saved.' + ), + ]; + } + + /** + * @inheritdoc + */ + public function createConfig(array $options, DeploymentConfig $deploymentConfig) + { + $configData = new ConfigData(ConfigFilePool::APP_ENV); + $configData->setOverrideWhenSave(true); + $lockProvider = $this->getLockProvider($options, $deploymentConfig); + + $this->setDefaultConfiguration($configData, $deploymentConfig, $lockProvider); + + foreach ($this->mappingInputKeyToConfigPath[$lockProvider] as $input => $path) { + if (isset($options[$input])) { + $configData->set($path, $options[$input]); + } + } + + return $configData; + } + + /** + * @inheritdoc + */ + public function validate(array $options, DeploymentConfig $deploymentConfig) + { + $lockProvider = $this->getLockProvider($options, $deploymentConfig); + switch ($lockProvider) { + case LockBackendFactory::LOCK_ZOOKEEPER: + $errors = $this->validateZookeeperConfig($options, $deploymentConfig); + break; + case LockBackendFactory::LOCK_FILE: + $errors = $this->validateFileConfig($options, $deploymentConfig); + break; + case LockBackendFactory::LOCK_CACHE: + case LockBackendFactory::LOCK_DB: + $errors = []; + break; + default: + $errors[] = 'The lock provider ' . $lockProvider . ' does not exist.'; + } + + return $errors; + } + + /** + * Validates File locks configuration + * + * @param array $options + * @param DeploymentConfig $deploymentConfig + * @return array + */ + private function validateFileConfig(array $options, DeploymentConfig $deploymentConfig): array + { + $errors = []; + + $path = $options[self::INPUT_KEY_LOCK_FILE_PATH] + ?? $deploymentConfig->get( + self::CONFIG_PATH_LOCK_FILE_PATH, + $this->getDefaultValue(self::INPUT_KEY_LOCK_FILE_PATH) + ); + + if (!$path) { + $errors[] = 'The path needs to be a non-empty string.'; + } + + return $errors; + } + + /** + * Validates Zookeeper configuration + * + * @param array $options + * @param DeploymentConfig $deploymentConfig + * @return array + */ + private function validateZookeeperConfig(array $options, DeploymentConfig $deploymentConfig): array + { + $errors = []; + + if (!extension_loaded(LockBackendFactory::LOCK_ZOOKEEPER)) { + $errors[] = 'php extension Zookeeper is not installed.'; + } + + $host = $options[self::INPUT_KEY_LOCK_ZOOKEEPER_HOST] + ?? $deploymentConfig->get( + self::CONFIG_PATH_LOCK_ZOOKEEPER_HOST, + $this->getDefaultValue(self::INPUT_KEY_LOCK_ZOOKEEPER_HOST) + ); + $path = $options[self::INPUT_KEY_LOCK_ZOOKEEPER_PATH] + ?? $deploymentConfig->get( + self::CONFIG_PATH_LOCK_ZOOKEEPER_PATH, + $this->getDefaultValue(self::INPUT_KEY_LOCK_ZOOKEEPER_PATH) + ); + + if (!$path) { + $errors[] = 'Zookeeper path needs to be a non-empty string.'; + } + + if (!$host) { + $errors[] = 'Zookeeper host is should be set.'; + } + + return $errors; + } + + /** + * Returns the name of lock provider + * + * @param array $options + * @param DeploymentConfig $deploymentConfig + * @return string + */ + private function getLockProvider(array $options, DeploymentConfig $deploymentConfig): string + { + if (!isset($options[self::INPUT_KEY_LOCK_PROVIDER])) { + return (string) $deploymentConfig->get( + self::CONFIG_PATH_LOCK_PROVIDER, + $this->getDefaultValue(self::INPUT_KEY_LOCK_PROVIDER) + ); + } + + return (string) $options[self::INPUT_KEY_LOCK_PROVIDER]; + } + + /** + * Sets default configuration for locks + * + * @param ConfigData $configData + * @param DeploymentConfig $deploymentConfig + * @param string $lockProvider + * @return ConfigData + */ + private function setDefaultConfiguration( + ConfigData $configData, + DeploymentConfig $deploymentConfig, + string $lockProvider + ) { + foreach ($this->mappingInputKeyToConfigPath[$lockProvider] as $input => $path) { + $configData->set($path, $deploymentConfig->get($path, $this->getDefaultValue($input))); + } + + return $configData; + } + + /** + * Returns default value by input key + * + * If default value is not set returns null + * + * @param string $inputKey + * @return mixed|null + */ + private function getDefaultValue(string $inputKey) + { + if (isset($this->defaultConfigValues[$inputKey])) { + return $this->defaultConfigValues[$inputKey]; + } else { + return null; + } + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php new file mode 100644 index 0000000000000..1a46bddf5f21a --- /dev/null +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/LockTest.php @@ -0,0 +1,232 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Setup\Test\Unit\Model\ConfigOptionsList; + +use Magento\Setup\Model\ConfigOptionsList\Lock as LockConfigOptionsList; +use Magento\Framework\Lock\Backend\Zookeeper as ZookeeperLock; +use Magento\Framework\Lock\LockBackendFactory; +use Magento\Framework\Config\Data\ConfigData; +use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\Setup\Option\SelectConfigOption; +use Magento\Framework\Setup\Option\TextConfigOption; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class LockTest extends TestCase +{ + /** + * @var DeploymentConfig|MockObject + */ + private $deploymentConfigMock; + + /** + * @var LockConfigOptionsList + */ + private $lockConfigOptionsList; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->deploymentConfigMock = $this->createMock(DeploymentConfig::class); + $this->lockConfigOptionsList = new LockConfigOptionsList(); + } + + /** + * @return void + */ + public function testGetOptions() + { + $options = $this->lockConfigOptionsList->getOptions(); + $this->assertSame(5, count($options)); + + $this->assertArrayHasKey(0, $options); + $this->assertInstanceOf(SelectConfigOption::class, $options[0]); + $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER, $options[0]->getName()); + + $this->assertArrayHasKey(1, $options); + $this->assertInstanceOf(TextConfigOption::class, $options[1]); + $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_DB_PREFIX, $options[1]->getName()); + + $this->assertArrayHasKey(2, $options); + $this->assertInstanceOf(TextConfigOption::class, $options[2]); + $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_HOST, $options[2]->getName()); + + $this->assertArrayHasKey(3, $options); + $this->assertInstanceOf(TextConfigOption::class, $options[3]); + $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_PATH, $options[3]->getName()); + + $this->assertArrayHasKey(4, $options); + $this->assertInstanceOf(TextConfigOption::class, $options[4]); + $this->assertEquals(LockConfigOptionsList::INPUT_KEY_LOCK_FILE_PATH, $options[4]->getName()); + } + + /** + * @param array $options + * @param array $expectedResult + * @dataProvider createConfigDataProvider + */ + public function testCreateConfig(array $options, array $expectedResult) + { + $this->deploymentConfigMock->expects($this->any()) + ->method('get') + ->willReturnArgument(1); + $data = $this->lockConfigOptionsList->createConfig($options, $this->deploymentConfigMock); + $this->assertInstanceOf(ConfigData::class, $data); + $this->assertTrue($data->isOverrideWhenSave()); + $this->assertSame($expectedResult, $data->getData()); + } + + /** + * @return array + */ + public function createConfigDataProvider(): array + { + return [ + 'Check default values' => [ + 'options' => [], + 'expectedResult' => [ + 'lock' => [ + 'provider' => LockBackendFactory::LOCK_DB, + 'config' => [ + 'prefix' => null, + ], + ], + ], + ], + 'Check default value for cache lock' => [ + 'options' => [ + LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_CACHE, + ], + 'expectedResult' => [ + 'lock' => [ + 'provider' => LockBackendFactory::LOCK_CACHE, + ], + ], + ], + 'Check default value for zookeeper lock' => [ + 'options' => [ + LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_ZOOKEEPER, + ], + 'expectedResult' => [ + 'lock' => [ + 'provider' => LockBackendFactory::LOCK_ZOOKEEPER, + 'config' => [ + 'host' => null, + 'path' => ZookeeperLock::DEFAULT_PATH, + ], + ], + ], + ], + 'Check specific db lock options' => [ + 'options' => [ + LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_DB, + LockConfigOptionsList::INPUT_KEY_LOCK_DB_PREFIX => 'my_prefix' + ], + 'expectedResult' => [ + 'lock' => [ + 'provider' => LockBackendFactory::LOCK_DB, + 'config' => [ + 'prefix' => 'my_prefix', + ], + ], + ], + ], + 'Check specific zookeeper lock options' => [ + 'options' => [ + LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_ZOOKEEPER, + LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_HOST => '123.45.67.89:10', + LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_PATH => '/some/path', + ], + 'expectedResult' => [ + 'lock' => [ + 'provider' => LockBackendFactory::LOCK_ZOOKEEPER, + 'config' => [ + 'host' => '123.45.67.89:10', + 'path' => '/some/path', + ], + ], + ], + ], + 'Check specific file lock options' => [ + 'options' => [ + LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_FILE, + LockConfigOptionsList::INPUT_KEY_LOCK_FILE_PATH => '/my/path' + ], + 'expectedResult' => [ + 'lock' => [ + 'provider' => LockBackendFactory::LOCK_FILE, + 'config' => [ + 'path' => '/my/path', + ], + ], + ], + ], + ]; + } + + /** + * @param array $options + * @param array $expectedResult + * @dataProvider validateDataProvider + */ + public function testValidate(array $options, array $expectedResult) + { + $this->deploymentConfigMock->expects($this->any()) + ->method('get') + ->willReturnArgument(1); + $this->assertSame( + $expectedResult, + $this->lockConfigOptionsList->validate($options, $this->deploymentConfigMock) + ); + } + + /** + * @return array + */ + public function validateDataProvider(): array + { + return [ + 'Wrong lock provider' => [ + 'options' => [ + LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => 'SomeProvider', + ], + 'expectedResult' => [ + 'The lock provider SomeProvider does not exist.', + ], + ], + 'Empty host and path for Zookeeper' => [ + 'options' => [ + LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_ZOOKEEPER, + LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_HOST => '', + LockConfigOptionsList::INPUT_KEY_LOCK_ZOOKEEPER_PATH => '', + ], + 'expectedResult' => extension_loaded('zookeeper') + ? [ + 'Zookeeper path needs to be a non-empty string.', + 'Zookeeper host is should be set.', + ] + : [ + 'php extension Zookeeper is not installed.', + 'Zookeeper path needs to be a non-empty string.', + 'Zookeeper host is should be set.', + ], + ], + 'Empty path for File lock' => [ + 'options' => [ + LockConfigOptionsList::INPUT_KEY_LOCK_PROVIDER => LockBackendFactory::LOCK_FILE, + LockConfigOptionsList::INPUT_KEY_LOCK_FILE_PATH => '', + ], + 'expectedResult' => [ + 'The path needs to be a non-empty string.', + ], + ], + ]; + } +} diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php index f342a11493498..99ef82dd9d355 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php @@ -7,6 +7,7 @@ namespace Magento\Setup\Test\Unit\Model; use Magento\Framework\Config\ConfigOptionsListConstants; +use Magento\Setup\Model\ConfigOptionsList\Lock; use Magento\Setup\Model\ConfigGenerator; use Magento\Setup\Model\ConfigOptionsList; use Magento\Setup\Validator\DbValidator; @@ -82,7 +83,7 @@ public function testCreateOptions() $this->generator->expects($this->once())->method('createXFrameConfig')->willReturn($configDataMock); $this->generator->expects($this->once())->method('createCacheHostsConfig')->willReturn($configDataMock); - $configData = $this->object->createConfig([], $this->deploymentConfig); + $configData = $this->object->createConfig([Lock::INPUT_KEY_LOCK_PROVIDER => 'db'], $this->deploymentConfig); $this->assertGreaterThanOrEqual(6, count($configData)); } @@ -96,7 +97,7 @@ public function testCreateOptionsWithOptionalNull() $this->generator->expects($this->once())->method('createXFrameConfig')->willReturn($configDataMock); $this->generator->expects($this->once())->method('createCacheHostsConfig')->willReturn($configDataMock); - $configData = $this->object->createConfig([], $this->deploymentConfig); + $configData = $this->object->createConfig([Lock::INPUT_KEY_LOCK_PROVIDER => 'db'], $this->deploymentConfig); $this->assertGreaterThanOrEqual(6, count($configData)); } @@ -109,7 +110,8 @@ public function testValidateSuccess() ConfigOptionsListConstants::INPUT_KEY_DB_NAME => 'name', ConfigOptionsListConstants::INPUT_KEY_DB_HOST => 'host', ConfigOptionsListConstants::INPUT_KEY_DB_USER => 'user', - ConfigOptionsListConstants::INPUT_KEY_DB_PASSWORD => 'pass' + ConfigOptionsListConstants::INPUT_KEY_DB_PASSWORD => 'pass', + Lock::INPUT_KEY_LOCK_PROVIDER => 'db' ]; $this->prepareValidationMocks(); @@ -127,7 +129,8 @@ public function testValidateInvalidSessionHandler() ConfigOptionsListConstants::INPUT_KEY_DB_NAME => 'name', ConfigOptionsListConstants::INPUT_KEY_DB_HOST => 'host', ConfigOptionsListConstants::INPUT_KEY_DB_USER => 'user', - ConfigOptionsListConstants::INPUT_KEY_DB_PASSWORD => 'pass' + ConfigOptionsListConstants::INPUT_KEY_DB_PASSWORD => 'pass', + Lock::INPUT_KEY_LOCK_PROVIDER => 'db' ]; $this->prepareValidationMocks(); @@ -141,7 +144,8 @@ public function testValidateEmptyEncryptionKey() { $options = [ ConfigOptionsListConstants::INPUT_KEY_SKIP_DB_VALIDATION => true, - ConfigOptionsListConstants::INPUT_KEY_ENCRYPTION_KEY => '' + ConfigOptionsListConstants::INPUT_KEY_ENCRYPTION_KEY => '', + Lock::INPUT_KEY_LOCK_PROVIDER => 'db' ]; $this->assertEquals( ['Invalid encryption key'], @@ -167,7 +171,8 @@ public function testValidateCacheHosts($hosts, $expectedError) { $options = [ ConfigOptionsListConstants::INPUT_KEY_SKIP_DB_VALIDATION => true, - ConfigOptionsListConstants::INPUT_KEY_CACHE_HOSTS => $hosts + ConfigOptionsListConstants::INPUT_KEY_CACHE_HOSTS => $hosts, + Lock::INPUT_KEY_LOCK_PROVIDER => 'db' ]; $result = $this->object->validate($options, $this->deploymentConfig); if ($expectedError) { From a65bc160e85ea7f31e9da3349d355c823e7ea271 Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Fri, 22 Mar 2019 16:43:36 -0500 Subject: [PATCH 0980/1295] magento-engcom/magento2ce#2699: Removed void return type --- .../Unit/Gateway/Request/VaultCaptureDataBuilderTest.php | 2 +- .../Magento/Framework/Code/Generator/AutoloaderTest.php | 8 ++++---- .../Magento/Framework/Code/Generator/Autoloader.php | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Braintree/Test/Unit/Gateway/Request/VaultCaptureDataBuilderTest.php b/app/code/Magento/Braintree/Test/Unit/Gateway/Request/VaultCaptureDataBuilderTest.php index 80d333db80f0a..6925e37b580ac 100644 --- a/app/code/Magento/Braintree/Test/Unit/Gateway/Request/VaultCaptureDataBuilderTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Gateway/Request/VaultCaptureDataBuilderTest.php @@ -111,7 +111,7 @@ public function testBuild() * @expectedException \Magento\Payment\Gateway\Command\CommandException * @expectedExceptionMessage The Payment Token is not available to perform the request. */ - public function testBuildWithoutPaymentToken(): void + public function testBuildWithoutPaymentToken() { $amount = 30.00; $buildSubject = [ diff --git a/dev/tests/integration/testsuite/Magento/Framework/Code/Generator/AutoloaderTest.php b/dev/tests/integration/testsuite/Magento/Framework/Code/Generator/AutoloaderTest.php index 0e1b51b3ae273..aaa3aa6c97a7e 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Code/Generator/AutoloaderTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Code/Generator/AutoloaderTest.php @@ -31,7 +31,7 @@ private function getTestFrameworkObjectManager() /** * @before */ - public function setupLoggerTestDouble(): void + public function setupLoggerTestDouble() { $loggerTestDouble = $this->createMock(LoggerInterface::class); $this->getTestFrameworkObjectManager()->addSharedInstance($loggerTestDouble, MagentoMonologLogger::class); @@ -40,7 +40,7 @@ public function setupLoggerTestDouble(): void /** * @after */ - public function removeLoggerTestDouble(): void + public function removeLoggerTestDouble() { $this->getTestFrameworkObjectManager()->removeSharedInstance(MagentoMonologLogger::class); } @@ -58,7 +58,7 @@ private function createExceptionThrowingGeneratorTestDouble(\RuntimeException $t return $generatorStub; } - public function testLogsExceptionDuringGeneration(): void + public function testLogsExceptionDuringGeneration() { $exceptionMessage = 'Test exception thrown during generation'; $testException = new \RuntimeException($exceptionMessage); @@ -70,7 +70,7 @@ public function testLogsExceptionDuringGeneration(): void $this->assertNull($autoloader->load(NonExistingClassName::class)); } - public function testFiltersDuplicateExceptionMessages(): void + public function testFiltersDuplicateExceptionMessages() { $exceptionMessage = 'Test exception thrown during generation'; $testException = new \RuntimeException($exceptionMessage); diff --git a/lib/internal/Magento/Framework/Code/Generator/Autoloader.php b/lib/internal/Magento/Framework/Code/Generator/Autoloader.php index 35c138147e9d3..f8e469fe05265 100644 --- a/lib/internal/Magento/Framework/Code/Generator/Autoloader.php +++ b/lib/internal/Magento/Framework/Code/Generator/Autoloader.php @@ -62,7 +62,7 @@ public function load($className) * * @param \Exception $exception */ - private function tryToLogExceptionMessageIfNotDuplicate(\Exception $exception): void + private function tryToLogExceptionMessageIfNotDuplicate(\Exception $exception) { if ($this->lastGenerationErrorMessage !== $exception->getMessage()) { $this->lastGenerationErrorMessage = $exception->getMessage(); @@ -84,7 +84,7 @@ private function tryToLogExceptionMessageIfNotDuplicate(\Exception $exception): * @param \Exception $exception * @return void */ - private function tryToLogException(\Exception $exception): void + private function tryToLogException(\Exception $exception) { try { $logger = ObjectManager::getInstance()->get(LoggerInterface::class); From af75c5abe40b169dea6f798b081ef00d3007b7a3 Mon Sep 17 00:00:00 2001 From: amol2jcommerce <amol@twojay.in> Date: Mon, 25 Mar 2019 11:12:36 +0530 Subject: [PATCH 0981/1295] changes for advanced-Search-layout-2.2 --- .../web/css/source/_module.less | 16 ++++++++++++++++ .../web/css/source/_module.less | 12 ------------ .../web/css/source/_module.less | 18 ++++++++++++++++++ .../web/css/source/_module.less | 12 ------------ 4 files changed, 34 insertions(+), 24 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_CatalogSearch/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_CatalogSearch/web/css/source/_module.less index daed96db717c7..b7255f9792993 100644 --- a/app/design/frontend/Magento/blank/Magento_CatalogSearch/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_CatalogSearch/web/css/source/_module.less @@ -188,6 +188,17 @@ margin-bottom: 0; } } + + .form.search.advanced { + .field.price { + .with-addon { + .input-text { + flex-basis: auto; + width: 100%; + } + } + } + } } .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__s) { @@ -256,4 +267,9 @@ .search-autocomplete { margin-top: 0; } + + .form.search.advanced { + min-width: 600px; + width: 50%; + } } diff --git a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less index f4101da6005c1..0ebd722429480 100644 --- a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less @@ -388,17 +388,6 @@ position: relative; } } - - .form.search.advanced { - .field.price { - .with-addon { - .input-text { - flex-basis: auto; - width: 100%; - } - } - } - } } // @@ -463,7 +452,6 @@ .form.send.confirmation, .form.password.forget, .form.create.account, - .form.search.advanced, .form.form-orders-search { min-width: 600px; width: 50%; diff --git a/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less index 0f91f857a715c..cdc9ce2b3be72 100644 --- a/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_CatalogSearch/web/css/source/_module.less @@ -213,6 +213,19 @@ // Mobile // _____________________________________________ +.media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { + .form.search.advanced { + .field.price { + .with-addon { + .input-text { + flex-basis: auto; + width: 100%; + } + } + } + } +} + .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__s) { .block-search { margin-top: @indent__s; @@ -275,4 +288,9 @@ .search-autocomplete { margin-top: 0; } + + .form.search.advanced { + min-width: 600px; + width: 50%; + } } diff --git a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less index e839c66b93e58..460f8e50d59a9 100755 --- a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less @@ -403,7 +403,6 @@ .form.send.confirmation, .form.password.forget, .form.create.account, - .form.search.advanced, .form.form-orders-search { min-width: 600px; width: 50%; @@ -604,15 +603,4 @@ position: relative; } } - - .form.search.advanced { - .field.price { - .with-addon { - .input-text { - flex-basis: auto; - width: 100%; - } - } - } - } } From 6c8502eea768ddca6856815c8c8cf5554e4f0b49 Mon Sep 17 00:00:00 2001 From: Ivan Gerchak <ivang@ven.com> Date: Mon, 25 Mar 2019 12:05:04 +0200 Subject: [PATCH 0982/1295] Change event "mouseup" to "mousedown" --- lib/web/mage/gallery/gallery.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/mage/gallery/gallery.js b/lib/web/mage/gallery/gallery.js index db98a1cc39a30..c20329c0ce63d 100644 --- a/lib/web/mage/gallery/gallery.js +++ b/lib/web/mage/gallery/gallery.js @@ -141,7 +141,7 @@ define([ this.setupBreakpoints(); this.initFullscreenSettings(); - this.settings.$element.on('mouseup', '.fotorama__stage__frame', function () { + this.settings.$element.on('mousedown', '.fotorama__stage__frame', function () { if ( !$(this).parents('.fotorama__shadows--left, .fotorama__shadows--right').length && !$(this).hasClass('fotorama-video-container') From 6958066399f094d06e2ea26c3d65fa199b0e85dc Mon Sep 17 00:00:00 2001 From: RomanKis <roman.kis.y@gmail.com> Date: Mon, 18 Mar 2019 15:48:22 +0200 Subject: [PATCH 0983/1295] #20825 Missing required argument $productAvailabilityChecks of Magento\Sales\Model\Order\Reorder\OrderedProductAvailabilityChecker. --- .../Magento/ConfigurableProductSales/etc/di.xml | 16 ---------------- app/code/Magento/Sales/etc/di.xml | 7 +++++++ 2 files changed, 7 insertions(+), 16 deletions(-) delete mode 100644 app/code/Magento/ConfigurableProductSales/etc/di.xml diff --git a/app/code/Magento/ConfigurableProductSales/etc/di.xml b/app/code/Magento/ConfigurableProductSales/etc/di.xml deleted file mode 100644 index b53faf74ffa1d..0000000000000 --- a/app/code/Magento/ConfigurableProductSales/etc/di.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> - <type name="Magento\Sales\Model\Order\Reorder\OrderedProductAvailabilityChecker"> - <arguments> - <argument name="productAvailabilityChecks" xsi:type="array"> - <item name="configurable" xsi:type="object">Magento\ConfigurableProductSales\Model\Order\Reorder\OrderedProductAvailabilityChecker</item> - </argument> - </arguments> - </type> -</config> diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml index b4c1e63902121..064ca3efb4829 100644 --- a/app/code/Magento/Sales/etc/di.xml +++ b/app/code/Magento/Sales/etc/di.xml @@ -1009,4 +1009,11 @@ <preference for="Magento\Sales\Api\OrderCustomerDelegateInterface" type="Magento\Sales\Model\Order\OrderCustomerDelegate" /> + <type name="Magento\Sales\Model\Order\Reorder\OrderedProductAvailabilityChecker"> + <arguments> + <argument name="productAvailabilityChecks" xsi:type="array"> + <item name="configurable" xsi:type="object">Magento\ConfigurableProductSales\Model\Order\Reorder\OrderedProductAvailabilityChecker</item> + </argument> + </arguments> + </type> </config> From 61d71584bb4b0f71b1f7a679b950fc47646a96fc Mon Sep 17 00:00:00 2001 From: RomanKis <roman.kis.y@gmail.com> Date: Wed, 20 Mar 2019 12:15:51 +0200 Subject: [PATCH 0984/1295] #20825 Missing required argument $productAvailabilityChecks of Magento\Sales\Model\Order\Reorder\OrderedProductAvailabilityChecker. --- .../Magento/ConfigurableProductSales/etc/di.xml | 16 ++++++++++++++++ app/code/Magento/Sales/etc/di.xml | 4 +--- 2 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 app/code/Magento/ConfigurableProductSales/etc/di.xml diff --git a/app/code/Magento/ConfigurableProductSales/etc/di.xml b/app/code/Magento/ConfigurableProductSales/etc/di.xml new file mode 100644 index 0000000000000..b53faf74ffa1d --- /dev/null +++ b/app/code/Magento/ConfigurableProductSales/etc/di.xml @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\Sales\Model\Order\Reorder\OrderedProductAvailabilityChecker"> + <arguments> + <argument name="productAvailabilityChecks" xsi:type="array"> + <item name="configurable" xsi:type="object">Magento\ConfigurableProductSales\Model\Order\Reorder\OrderedProductAvailabilityChecker</item> + </argument> + </arguments> + </type> +</config> diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml index 064ca3efb4829..6311ed60dafe7 100644 --- a/app/code/Magento/Sales/etc/di.xml +++ b/app/code/Magento/Sales/etc/di.xml @@ -1011,9 +1011,7 @@ type="Magento\Sales\Model\Order\OrderCustomerDelegate" /> <type name="Magento\Sales\Model\Order\Reorder\OrderedProductAvailabilityChecker"> <arguments> - <argument name="productAvailabilityChecks" xsi:type="array"> - <item name="configurable" xsi:type="object">Magento\ConfigurableProductSales\Model\Order\Reorder\OrderedProductAvailabilityChecker</item> - </argument> + <argument name="productAvailabilityChecks" xsi:type="array" /> </arguments> </type> </config> From f77f54dac19e6d9853b0e34a4bda1b39fd62fd3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Mon, 25 Mar 2019 16:06:12 +0100 Subject: [PATCH 0985/1295] Backport additional changes --- .../Sales/Model/Order/Address/Validator.php | 13 +++++++++++-- .../Magento/Framework/Locale/Format.php | 3 +++ .../Magento/Framework/Locale/Resolver.php | 17 ++++++++++------- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Address/Validator.php b/app/code/Magento/Sales/Model/Order/Address/Validator.php index 84ddefec0111c..a90b438946b8d 100644 --- a/app/code/Magento/Sales/Model/Order/Address/Validator.php +++ b/app/code/Magento/Sales/Model/Order/Address/Validator.php @@ -48,8 +48,8 @@ class Validator /** * @param DirectoryHelper $directoryHelper - * @param CountryFactory $countryFactory - * @param EavConfig $eavConfig + * @param CountryFactory $countryFactory + * @param EavConfig $eavConfig */ public function __construct( DirectoryHelper $directoryHelper, @@ -195,7 +195,10 @@ protected function isStateRequired($countryId) } /** + * Check whether telephone is required for address. + * * @return bool + * @throws \Magento\Framework\Exception\LocalizedException */ protected function isTelephoneRequired() { @@ -203,7 +206,10 @@ protected function isTelephoneRequired() } /** + * Check whether company is required for address. + * * @return bool + * @throws \Magento\Framework\Exception\LocalizedException */ protected function isCompanyRequired() { @@ -211,7 +217,10 @@ protected function isCompanyRequired() } /** + * Check whether fax is required for address. + * * @return bool + * @throws \Magento\Framework\Exception\LocalizedException */ protected function isFaxRequired() { diff --git a/lib/internal/Magento/Framework/Locale/Format.php b/lib/internal/Magento/Framework/Locale/Format.php index 0cdd80208fbcf..fa3831d5a343a 100644 --- a/lib/internal/Magento/Framework/Locale/Format.php +++ b/lib/internal/Magento/Framework/Locale/Format.php @@ -5,6 +5,9 @@ */ namespace Magento\Framework\Locale; +/** + * Price locale format. + */ class Format implements \Magento\Framework\Locale\FormatInterface { /** diff --git a/lib/internal/Magento/Framework/Locale/Resolver.php b/lib/internal/Magento/Framework/Locale/Resolver.php index 32c4f035ac954..abbfbdc5c6c37 100644 --- a/lib/internal/Magento/Framework/Locale/Resolver.php +++ b/lib/internal/Magento/Framework/Locale/Resolver.php @@ -9,6 +9,9 @@ use Magento\Framework\App\DeploymentConfig; use Magento\Framework\App\ObjectManager; +/** + * Manages locale config information. + */ class Resolver implements ResolverInterface { /** @@ -76,7 +79,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function getDefaultLocalePath() { @@ -84,7 +87,7 @@ public function getDefaultLocalePath() } /** - * {@inheritdoc} + * @inheritdoc */ public function setDefaultLocale($locale) { @@ -93,7 +96,7 @@ public function setDefaultLocale($locale) } /** - * {@inheritdoc} + * @inheritdoc */ public function getDefaultLocale() { @@ -111,7 +114,7 @@ public function getDefaultLocale() } /** - * {@inheritdoc} + * @inheritdoc */ public function setLocale($locale = null) { @@ -124,7 +127,7 @@ public function setLocale($locale = null) } /** - * {@inheritdoc} + * @inheritdoc */ public function getLocale() { @@ -135,7 +138,7 @@ public function getLocale() } /** - * {@inheritdoc} + * @inheritdoc */ public function emulate($scopeId) { @@ -155,7 +158,7 @@ public function emulate($scopeId) } /** - * {@inheritdoc} + * @inheritdoc */ public function revert() { From 3aa19eb049208f2db54eee06929a42e80d2d9119 Mon Sep 17 00:00:00 2001 From: Oleksandr Shmyheliuk <oshmyheliuk@magento.com> Date: Mon, 25 Mar 2019 10:33:50 -0500 Subject: [PATCH 0986/1295] MAGETWO-98860: [Backport 2.2.x] Error during setup:static-content:deploy: Error while waiting for package deployed --- .../Deploy/Console/DeployStaticOptions.php | 14 ++++++++ .../Deploy/Service/DeployStaticContent.php | 34 +++++++++++-------- .../Unit/Service/DeployStaticContentTest.php | 32 +++++++++++++++++ 3 files changed, 66 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Deploy/Console/DeployStaticOptions.php b/app/code/Magento/Deploy/Console/DeployStaticOptions.php index 9a73dd5d65fc7..096d926262057 100644 --- a/app/code/Magento/Deploy/Console/DeployStaticOptions.php +++ b/app/code/Magento/Deploy/Console/DeployStaticOptions.php @@ -6,6 +6,7 @@ namespace Magento\Deploy\Console; +use Magento\Deploy\Process\Queue; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; @@ -57,6 +58,11 @@ class DeployStaticOptions */ const JOBS_AMOUNT = 'jobs'; + /** + * Key for max execution time option + */ + const MAX_EXECUTION_TIME = 'max-execution-time'; + /** * Force run of static deploy */ @@ -150,6 +156,7 @@ public function getOptionsList() * Basic options * * @return array + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ private function getBasicOptions() { @@ -216,6 +223,13 @@ private function getBasicOptions() 'Enable parallel processing using the specified number of jobs.', self::DEFAULT_JOBS_AMOUNT ), + new InputOption( + self::MAX_EXECUTION_TIME, + null, + InputOption::VALUE_OPTIONAL, + 'The maximum expected execution time of deployment static process (in seconds).', + Queue::DEFAULT_MAX_EXEC_TIME + ), new InputOption( self::SYMLINK_LOCALE, null, diff --git a/app/code/Magento/Deploy/Service/DeployStaticContent.php b/app/code/Magento/Deploy/Service/DeployStaticContent.php index 72de645868e22..aaef3f6e9ea5b 100644 --- a/app/code/Magento/Deploy/Service/DeployStaticContent.php +++ b/app/code/Magento/Deploy/Service/DeployStaticContent.php @@ -85,24 +85,26 @@ public function deploy(array $options) return; } - $queue = $this->queueFactory->create( - [ - 'logger' => $this->logger, - 'options' => $options, - 'maxProcesses' => $this->getProcessesAmount($options), - 'deployPackageService' => $this->objectManager->create( - \Magento\Deploy\Service\DeployPackage::class, - [ - 'logger' => $this->logger - ] - ) - ] - ); + $queueOptions = [ + 'logger' => $this->logger, + 'options' => $options, + 'maxProcesses' => $this->getProcessesAmount($options), + 'deployPackageService' => $this->objectManager->create( + \Magento\Deploy\Service\DeployPackage::class, + [ + 'logger' => $this->logger + ] + ) + ]; + + if (isset($options[Options::MAX_EXECUTION_TIME])) { + $queueOptions['maxExecTime'] = (int)$options[Options::MAX_EXECUTION_TIME]; + } $deployStrategy = $this->deployStrategyFactory->create( $options[Options::STRATEGY], [ - 'queue' => $queue + 'queue' => $this->queueFactory->create($queueOptions) ] ); @@ -134,6 +136,8 @@ public function deploy(array $options) } /** + * Returns amount of parallel processes, returns zero if option wasn't set. + * * @param array $options * @return int */ @@ -143,6 +147,8 @@ private function getProcessesAmount(array $options) } /** + * Checks if need to refresh only version. + * * @param array $options * @return bool */ diff --git a/app/code/Magento/Deploy/Test/Unit/Service/DeployStaticContentTest.php b/app/code/Magento/Deploy/Test/Unit/Service/DeployStaticContentTest.php index 75edc8cb4f6ee..396381960e544 100644 --- a/app/code/Magento/Deploy/Test/Unit/Service/DeployStaticContentTest.php +++ b/app/code/Magento/Deploy/Test/Unit/Service/DeployStaticContentTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Deploy\Test\Unit\Service; +use Magento\Deploy\Console\DeployStaticOptions; use Magento\Deploy\Package\Package; use Magento\Deploy\Process\Queue; use Magento\Deploy\Service\Bundle; @@ -221,4 +222,35 @@ public function deployDataProvider() ] ]; } + + public function testMaxExecutionTimeOptionPassed() + { + $options = [ + DeployStaticOptions::MAX_EXECUTION_TIME => 100, + DeployStaticOptions::REFRESH_CONTENT_VERSION_ONLY => false, + DeployStaticOptions::JOBS_AMOUNT => 3, + DeployStaticOptions::STRATEGY => 'compact', + DeployStaticOptions::NO_JAVASCRIPT => true, + DeployStaticOptions::NO_HTML_MINIFY => true, + ]; + + $queueMock = $this->createMock(Queue::class); + $strategyMock = $this->createMock(CompactDeploy::class); + $this->queueFactory->expects($this->once()) + ->method('create') + ->with([ + 'logger' => $this->logger, + 'maxExecTime' => 100, + 'maxProcesses' => 3, + 'options' => $options, + 'deployPackageService' => null + ]) + ->willReturn($queueMock); + $this->deployStrategyFactory->expects($this->once()) + ->method('create') + ->with('compact', ['queue' => $queueMock]) + ->willReturn($strategyMock); + + $this->service->deploy($options); + } } From cece0869efc4333abdd410f0482d0d6586a04ce8 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> Date: Mon, 25 Mar 2019 18:24:53 +0200 Subject: [PATCH 0987/1295] magento/magento2#21719 Backport Fix #21692 #21752 Add missing phpdoc clock --- app/code/Magento/Sales/Model/Order/Address/Validator.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Sales/Model/Order/Address/Validator.php b/app/code/Magento/Sales/Model/Order/Address/Validator.php index a90b438946b8d..e970cd66635f9 100644 --- a/app/code/Magento/Sales/Model/Order/Address/Validator.php +++ b/app/code/Magento/Sales/Model/Order/Address/Validator.php @@ -63,6 +63,7 @@ public function __construct( } /** + * Validate address. * * @param \Magento\Sales\Model\Order\Address $address * @return array From 67b1d0a12fea5f46073ca4e4133e5debd3bf6ede Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> Date: Mon, 25 Mar 2019 18:28:57 +0200 Subject: [PATCH 0988/1295] magento/magento2#21719 Backport Fix #21692 #21752 Add missing phpdoc clock --- lib/internal/Magento/Framework/Locale/Format.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Locale/Format.php b/lib/internal/Magento/Framework/Locale/Format.php index fa3831d5a343a..adcffe01b910e 100644 --- a/lib/internal/Magento/Framework/Locale/Format.php +++ b/lib/internal/Magento/Framework/Locale/Format.php @@ -41,7 +41,8 @@ public function __construct( } /** - * Returns the first found number from a string + * Returns the first found number from a string. + * * Parsing depends on given locale (grouping and decimal) * * Examples for input: From 843a0b5936050e56b8aeb395379f11afc9454e06 Mon Sep 17 00:00:00 2001 From: hiren pandya <hiren.pandya@krishtechnolabs.com> Date: Tue, 26 Mar 2019 19:46:15 +0530 Subject: [PATCH 0989/1295] Fixed Inline block edit identifier validation - Backport --- .../Cms/view/adminhtml/ui_component/cms_block_listing.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_block_listing.xml b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_block_listing.xml index 9f886f6f1345e..793fc7d26cb4a 100644 --- a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_block_listing.xml +++ b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_block_listing.xml @@ -146,7 +146,6 @@ <editor> <validation> <rule name="required-entry" xsi:type="boolean">true</rule> - <rule name="validate-xml-identifier" xsi:type="boolean">true</rule> </validation> <editorType>text</editorType> </editor> From d8fd378040de6f29b55047ef15cbb3dddf621a93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Ca=C3=A7ador?= <samuelmoreira27@gmail.com> Date: Fri, 15 Mar 2019 16:47:36 +0000 Subject: [PATCH 0990/1295] Multishipping checkout agreements now are the same as default checkout agreements --- .../multishipping_checkout_overview.xml | 2 +- .../templates/multishipping_agreements.phtml | 49 ------------------- 2 files changed, 1 insertion(+), 50 deletions(-) delete mode 100644 app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml diff --git a/app/code/Magento/CheckoutAgreements/view/frontend/layout/multishipping_checkout_overview.xml b/app/code/Magento/CheckoutAgreements/view/frontend/layout/multishipping_checkout_overview.xml index 3f742de0177da..122160f1a10cd 100644 --- a/app/code/Magento/CheckoutAgreements/view/frontend/layout/multishipping_checkout_overview.xml +++ b/app/code/Magento/CheckoutAgreements/view/frontend/layout/multishipping_checkout_overview.xml @@ -8,7 +8,7 @@ <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceBlock name="checkout_overview"> - <block class="Magento\CheckoutAgreements\Block\Agreements" name="checkout.multishipping.agreements" as="agreements" template="Magento_CheckoutAgreements::multishipping_agreements.phtml"/> + <block class="Magento\CheckoutAgreements\Block\Agreements" name="checkout.multishipping.agreements" as="agreements" template="Magento_CheckoutAgreements::additional_agreements.phtml"/> </referenceBlock> </body> </page> diff --git a/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml b/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml deleted file mode 100644 index 3400770f5cee8..0000000000000 --- a/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml +++ /dev/null @@ -1,49 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -// @codingStandardsIgnoreFile - -?> -<?php -/** - * @var $block \Magento\CheckoutAgreements\Block\Agreements - */ -?> -<?php if (!$block->getAgreements()) { - return; -} ?> -<ol id="checkout-agreements" class="agreements checkout items"> - <?php /** @var \Magento\CheckoutAgreements\Api\Data\AgreementInterface $agreement */ ?> - <?php foreach ($block->getAgreements() as $agreement): ?> - <li class="item"> - <div class="checkout-agreement-item-content"<?= ($agreement->getContentHeight() ? ' style="height:' . $agreement->getContentHeight() . '"' : '') ?>> - <?php if ($agreement->getIsHtml()):?> - <?= /* @escapeNotVerified */ $agreement->getContent() ?> - <?php else:?> - <?= nl2br($block->escapeHtml($agreement->getContent())) ?> - <?php endif; ?> - </div> - <?php if($agreement->getMode() == \Magento\CheckoutAgreements\Model\AgreementModeOptions::MODE_MANUAL): ?> - <div id="checkout-agreements-form-<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>" class="field choice agree required"> - <input type="checkbox" - id="agreement-<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>" - name="agreement[<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>]" - value="1" - title="<?= $block->escapeHtml($agreement->getCheckboxText()) ?>" - class="checkbox" - data-validate="{required:true}"/> - <label class="label" for="agreement-<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>"> - <span><?= $agreement->getIsHtml() ? $agreement->getCheckboxText() : $block->escapeHtml($agreement->getCheckboxText()) ?></span> - </label> - </div> - <?php elseif($agreement->getMode() == \Magento\CheckoutAgreements\Model\AgreementModeOptions::MODE_AUTO): ?> - <div id="checkout-agreements-form-<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>" class="field choice agree"> - <span><?= $agreement->getIsHtml() ? $agreement->getCheckboxText() : $block->escapeHtml($agreement->getCheckboxText()) ?></span> - </div> - <?php endif; ?> - </li> - <?php endforeach ?> -</ol> From e283d9a0191466f46578dd4c98625c2fed7d01d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Ca=C3=A7ador?= <samuelmoreira27@gmail.com> Date: Sat, 16 Mar 2019 17:26:49 +0000 Subject: [PATCH 0991/1295] Restore multishipping_agreements file for backward compatibility --- .../templates/multishipping_agreements.phtml | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml diff --git a/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml b/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml new file mode 100644 index 0000000000000..2d0667ee27cf8 --- /dev/null +++ b/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +// @codingStandardsIgnoreFile + +?> +<?php +/** + * @var $block \Magento\CheckoutAgreements\Block\Agreements + */ +?> +<?php if (!$block->getAgreements()) { + return; +} ?> +<ol id="checkout-agreements" class="agreements checkout items"> + <?php /** @var \Magento\CheckoutAgreements\Api\Data\AgreementInterface $agreement */ ?> + <?php foreach ($block->getAgreements() as $agreement): ?> + <li class="item"> + <div class="checkout-agreement-item-content"<?= ($agreement->getContentHeight() ? ' style="height:' . $agreement->getContentHeight() . '"' : '') ?>> + <?php if ($agreement->getIsHtml()):?> + <?= /* @escapeNotVerified */ $agreement->getContent() ?> + <?php else:?> + <?= nl2br($block->escapeHtml($agreement->getContent())) ?> + <?php endif; ?> + </div> + <?php if($agreement->getMode() == \Magento\CheckoutAgreements\Model\AgreementModeOptions::MODE_MANUAL): ?> + <div id="checkout-agreements-form-<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>" class="field choice agree required"> + <input type="checkbox" + id="agreement-<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>" + name="agreement[<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>]" + value="1" + title="<?= $block->escapeHtml($agreement->getCheckboxText()) ?>" + class="checkbox" + data-validate="{required:true}"/> + <label class="label" for="agreement-<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>"> + <span><?= $agreement->getIsHtml() ? $agreement->getCheckboxText() : $block->escapeHtml($agreement->getCheckboxText()) ?></span> + </label> + </div> + <?php elseif($agreement->getMode() == \Magento\CheckoutAgreements\Model\AgreementModeOptions::MODE_AUTO): ?> + <div id="checkout-agreements-form-<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>" class="field choice agree"> + <span><?= $agreement->getIsHtml() ? $agreement->getCheckboxText() : $block->escapeHtml($agreement->getCheckboxText()) ?></span> + </div> + <?php endif; ?> + </li> + <?php endforeach ?> +</ol> \ No newline at end of file From fff0b4070ef9741e9e87de79e11ad937357d954e Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <d.cheshun@atwix.com> Date: Mon, 18 Mar 2019 20:59:08 +0200 Subject: [PATCH 0992/1295] Fix phpcs issue: restore new line at the end of file --- .../view/frontend/templates/multishipping_agreements.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml b/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml index 2d0667ee27cf8..3400770f5cee8 100644 --- a/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml +++ b/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml @@ -46,4 +46,4 @@ <?php endif; ?> </li> <?php endforeach ?> -</ol> \ No newline at end of file +</ol> From 70268598c59c60ffa8264741b9644b2b269cb0bb Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <d.cheshun@atwix.com> Date: Mon, 18 Mar 2019 21:01:44 +0200 Subject: [PATCH 0993/1295] Add deprecated tag for legacy template --- .../view/frontend/templates/multishipping_agreements.phtml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml b/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml index 3400770f5cee8..33227f0cdce3c 100644 --- a/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml +++ b/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml @@ -4,6 +4,7 @@ * See COPYING.txt for license details. */ +// @deprecated // @codingStandardsIgnoreFile ?> From a0c3ac5243036e2da921cc09052e5222952675de Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Fri, 22 Mar 2019 14:09:49 +0200 Subject: [PATCH 0994/1295] Fix functional test. --- app/code/Magento/Multishipping/view/frontend/web/js/overview.js | 2 +- .../Test/Page/MultishippingCheckoutOverview.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Multishipping/view/frontend/web/js/overview.js b/app/code/Magento/Multishipping/view/frontend/web/js/overview.js index 9b867cd7217b1..3a6d73e304974 100644 --- a/app/code/Magento/Multishipping/view/frontend/web/js/overview.js +++ b/app/code/Magento/Multishipping/view/frontend/web/js/overview.js @@ -15,7 +15,7 @@ define([ opacity: 0.5, // CSS opacity for the 'Place Order' button when it's clicked and then disabled. pleaseWaitLoader: 'span.please-wait', // 'Submitting order information...' Ajax loader. placeOrderSubmit: 'button[type="submit"]', // The 'Place Order' button. - agreements: '#checkout-agreements' // Container for all of the checkout agreements and terms/conditions + agreements: '.checkout-agreements' // Container for all of the checkout agreements and terms/conditions }, /** diff --git a/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/Page/MultishippingCheckoutOverview.xml b/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/Page/MultishippingCheckoutOverview.xml index d304d305a7265..a266b09278ddb 100644 --- a/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/Page/MultishippingCheckoutOverview.xml +++ b/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/Page/MultishippingCheckoutOverview.xml @@ -7,6 +7,6 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/pages.xsd"> <page name="MultishippingCheckoutOverview" mca="multishipping/checkout/overview" module="Magento_Checkout"> - <block name="agreementReview" class="Magento\CheckoutAgreements\Test\Block\Multishipping\MultishippingAgreementReview" locator="#checkout-agreements" strategy="css selector"/> + <block name="agreementReview" class="Magento\CheckoutAgreements\Test\Block\Multishipping\MultishippingAgreementReview" locator=".checkout-agreements" strategy="css selector"/> </page> </config> From 7d81b55eb5fd9b170dc6c6c0f60fd9bf4d12274e Mon Sep 17 00:00:00 2001 From: "Rav [RedChamps]" <getravindersingh@gmail.com> Date: Sat, 16 Feb 2019 10:38:57 +0530 Subject: [PATCH 0995/1295] Added refunded discount amount to the total revenue calculation and fixed Negative order amount in dashboard when viewing particular store data --- .../Reports/Model/ResourceModel/Order/Collection.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php index da7ab97a1b211..a51007c3e93ee 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php @@ -773,6 +773,7 @@ public function addRevenueToSelect($convertCurrency = false) !$convertCurrency, $this->getConnection()->getIfNullSql('main_table.base_subtotal_refunded', 0), $this->getConnection()->getIfNullSql('main_table.base_subtotal_canceled', 0), + $this->getConnection()->getIfNullSql('main_table.base_discount_refunded', 0), $this->getConnection()->getIfNullSql('main_table.base_discount_canceled', 0) ); $this->getSelect()->columns(['revenue' => $expr]); @@ -795,6 +796,7 @@ public function addSumAvgTotals($storeId = 0) $storeId, $this->getConnection()->getIfNullSql('main_table.base_subtotal_refunded', 0), $this->getConnection()->getIfNullSql('main_table.base_subtotal_canceled', 0), + $this->getConnection()->getIfNullSql('main_table.base_discount_refunded', 0), $this->getConnection()->getIfNullSql('main_table.base_discount_canceled', 0) ); @@ -813,6 +815,7 @@ public function addSumAvgTotals($storeId = 0) * @param int $storeId * @param string $baseSubtotalRefunded * @param string $baseSubtotalCanceled + * @param string $baseDiscountRefunded * @param string $baseDiscountCanceled * @return string */ @@ -820,13 +823,14 @@ protected function getTotalsExpression( $storeId, $baseSubtotalRefunded, $baseSubtotalCanceled, + $baseDiscountRefunded, $baseDiscountCanceled ) { $template = ($storeId != 0) - ? '(main_table.base_subtotal - %2$s - %1$s - ABS(main_table.base_discount_amount) - %3$s)' - : '((main_table.base_subtotal - %1$s - %2$s - ABS(main_table.base_discount_amount) + %3$s) ' + ? '(main_table.base_subtotal - %2$s - %1$s - ABS(main_table.base_discount_amount) + %3$s + %4$s)' + : '((main_table.base_subtotal - %1$s - %2$s - ABS(main_table.base_discount_amount) + %3$s + %4$s) ' . ' * main_table.base_to_global_rate)'; - return sprintf($template, $baseSubtotalRefunded, $baseSubtotalCanceled, $baseDiscountCanceled); + return sprintf($template, $baseSubtotalRefunded, $baseSubtotalCanceled, $baseDiscountRefunded, $baseDiscountCanceled); } /** From 0cea56222c7c1ba0c361de223a1daf42c2e63fc6 Mon Sep 17 00:00:00 2001 From: "Rav [RedChamps]" <getravindersingh@gmail.com> Date: Sat, 16 Feb 2019 19:55:46 +0530 Subject: [PATCH 0996/1295] Fixed bug that total amount is showing negative when order with discount is partially refunded --- .../Reports/Model/ResourceModel/Order/Collection.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php index a51007c3e93ee..e5f14e9089b4e 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php @@ -773,8 +773,8 @@ public function addRevenueToSelect($convertCurrency = false) !$convertCurrency, $this->getConnection()->getIfNullSql('main_table.base_subtotal_refunded', 0), $this->getConnection()->getIfNullSql('main_table.base_subtotal_canceled', 0), - $this->getConnection()->getIfNullSql('main_table.base_discount_refunded', 0), - $this->getConnection()->getIfNullSql('main_table.base_discount_canceled', 0) + $this->getConnection()->getIfNullSql('ABS(main_table.base_discount_refunded)', 0), + $this->getConnection()->getIfNullSql('ABS(main_table.base_discount_canceled)', 0) ); $this->getSelect()->columns(['revenue' => $expr]); @@ -796,8 +796,8 @@ public function addSumAvgTotals($storeId = 0) $storeId, $this->getConnection()->getIfNullSql('main_table.base_subtotal_refunded', 0), $this->getConnection()->getIfNullSql('main_table.base_subtotal_canceled', 0), - $this->getConnection()->getIfNullSql('main_table.base_discount_refunded', 0), - $this->getConnection()->getIfNullSql('main_table.base_discount_canceled', 0) + $this->getConnection()->getIfNullSql('ABS(main_table.base_discount_refunded)', 0), + $this->getConnection()->getIfNullSql('ABS(main_table.base_discount_canceled)', 0) ); $this->getSelect()->columns( @@ -827,8 +827,8 @@ protected function getTotalsExpression( $baseDiscountCanceled ) { $template = ($storeId != 0) - ? '(main_table.base_subtotal - %2$s - %1$s - ABS(main_table.base_discount_amount) + %3$s + %4$s)' - : '((main_table.base_subtotal - %1$s - %2$s - ABS(main_table.base_discount_amount) + %3$s + %4$s) ' + ? '(main_table.base_subtotal - %2$s - %1$s - (ABS(main_table.base_discount_amount) - %3$s - %4$s))' + : '((main_table.base_subtotal - %1$s - %2$s - (ABS(main_table.base_discount_amount) - %3$s - %4$s)) ' . ' * main_table.base_to_global_rate)'; return sprintf($template, $baseSubtotalRefunded, $baseSubtotalCanceled, $baseDiscountRefunded, $baseDiscountCanceled); } From 891e2e1e3afefae742e6c106c1252b881930242a Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Tue, 5 Mar 2019 17:32:16 +0200 Subject: [PATCH 0997/1295] ENGCOM-4302: Static test fix. --- .../Reports/Model/ResourceModel/Order/Collection.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php index e5f14e9089b4e..1da2192fa50ce 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php @@ -830,7 +830,13 @@ protected function getTotalsExpression( ? '(main_table.base_subtotal - %2$s - %1$s - (ABS(main_table.base_discount_amount) - %3$s - %4$s))' : '((main_table.base_subtotal - %1$s - %2$s - (ABS(main_table.base_discount_amount) - %3$s - %4$s)) ' . ' * main_table.base_to_global_rate)'; - return sprintf($template, $baseSubtotalRefunded, $baseSubtotalCanceled, $baseDiscountRefunded, $baseDiscountCanceled); + return sprintf( + $template, + $baseSubtotalRefunded, + $baseSubtotalCanceled, + $baseDiscountRefunded, + $baseDiscountCanceled + ); } /** From a54bba35bbc7716455bd72cfb72fd1939e9a3885 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Fri, 22 Mar 2019 10:32:50 +0200 Subject: [PATCH 0998/1295] ENGCOM-4302: Backward compatibility fix. --- .../Model/ResourceModel/Order/Collection.php | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php index 1da2192fa50ce..d89a118bff94b 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Order/Collection.php @@ -769,7 +769,7 @@ public function addOrdersCount() */ public function addRevenueToSelect($convertCurrency = false) { - $expr = $this->getTotalsExpression( + $expr = $this->getTotalsExpressionWithDiscountRefunded( !$convertCurrency, $this->getConnection()->getIfNullSql('main_table.base_subtotal_refunded', 0), $this->getConnection()->getIfNullSql('main_table.base_subtotal_canceled', 0), @@ -792,7 +792,7 @@ public function addSumAvgTotals($storeId = 0) /** * calculate average and total amount */ - $expr = $this->getTotalsExpression( + $expr = $this->getTotalsExpressionWithDiscountRefunded( $storeId, $this->getConnection()->getIfNullSql('main_table.base_subtotal_refunded', 0), $this->getConnection()->getIfNullSql('main_table.base_subtotal_canceled', 0), @@ -810,16 +810,40 @@ public function addSumAvgTotals($storeId = 0) } /** - * Get SQL expression for totals + * Get SQL expression for totals. * * @param int $storeId * @param string $baseSubtotalRefunded * @param string $baseSubtotalCanceled - * @param string $baseDiscountRefunded * @param string $baseDiscountCanceled * @return string + * @deprecated + * @see getTotalsExpressionWithDiscountRefunded */ protected function getTotalsExpression( + $storeId, + $baseSubtotalRefunded, + $baseSubtotalCanceled, + $baseDiscountCanceled + ) { + $template = ($storeId != 0) + ? '(main_table.base_subtotal - %2$s - %1$s - ABS(main_table.base_discount_amount) - %3$s)' + : '((main_table.base_subtotal - %1$s - %2$s - ABS(main_table.base_discount_amount) + %3$s) ' + . ' * main_table.base_to_global_rate)'; + return sprintf($template, $baseSubtotalRefunded, $baseSubtotalCanceled, $baseDiscountCanceled); + } + + /** + * Get SQL expression for totals with discount refunded. + * + * @param int $storeId + * @param string $baseSubtotalRefunded + * @param string $baseSubtotalCanceled + * @param string $baseDiscountRefunded + * @param string $baseDiscountCanceled + * @return string + */ + private function getTotalsExpressionWithDiscountRefunded( $storeId, $baseSubtotalRefunded, $baseSubtotalCanceled, From 8e04a95b3a8e81464e91fd197c73b01b3b736118 Mon Sep 17 00:00:00 2001 From: Javier Villanueva <javier@medialounge.co.uk> Date: Fri, 22 Mar 2019 15:54:05 +0000 Subject: [PATCH 0999/1295] Trigger contentUpdate on reviews load --- app/code/Magento/Review/view/frontend/web/js/process-reviews.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Review/view/frontend/web/js/process-reviews.js b/app/code/Magento/Review/view/frontend/web/js/process-reviews.js index d1c40959e3ec2..88c61fa38af34 100644 --- a/app/code/Magento/Review/view/frontend/web/js/process-reviews.js +++ b/app/code/Magento/Review/view/frontend/web/js/process-reviews.js @@ -20,7 +20,7 @@ define([ showLoader: false, loaderContext: $('.product.data.items') }).done(function (data) { - $('#product-review-container').html(data); + $('#product-review-container').html(data).trigger('contentUpdated'); $('[data-role="product-review"] .pages a').each(function (index, element) { $(element).click(function (event) { //eslint-disable-line max-nested-callbacks processReviews($(element).attr('href'), true); From 2d291a73a65a6009ce99edf32569e484e6fc3a19 Mon Sep 17 00:00:00 2001 From: Fabian Schmengler <fs@integer-net.de> Date: Fri, 11 Jan 2019 11:18:28 +0100 Subject: [PATCH 1000/1295] Deny access to XML and PHTML files in pub/errors For apache via .htaccess and in nginx sample configuration --- nginx.conf.sample | 7 ++++++- pub/errors/.htaccess | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/nginx.conf.sample b/nginx.conf.sample index 90604808f6ec0..80cc88431eff8 100644 --- a/nginx.conf.sample +++ b/nginx.conf.sample @@ -159,6 +159,11 @@ location /media/downloadable/ { location /media/import/ { deny all; } +location /errors/ { + location ~* \.xml$ { + deny all; + } +} # PHP entry point for main application location ~ ^/(index|get|static|errors/report|errors/404|errors/503|health_check)\.php$ { @@ -198,6 +203,6 @@ gzip_types gzip_vary on; # Banned locations (only reached if the earlier PHP entry point regexes don't match) -location ~* (\.php$|\.htaccess$|\.git) { +location ~* (\.php$|\.phtml$|\.htaccess$|\.git) { deny all; } diff --git a/pub/errors/.htaccess b/pub/errors/.htaccess index 3692dd439e2ff..a7b9cbda05893 100644 --- a/pub/errors/.htaccess +++ b/pub/errors/.htaccess @@ -1,4 +1,7 @@ Options None +<FilesMatch "\.(xml|phtml)$"> + Deny from all +</FilesMatch> <IfModule mod_rewrite.c> RewriteEngine Off </IfModule> From bd87d77581c8c62d1df853c6504df79ba66727f3 Mon Sep 17 00:00:00 2001 From: hiren pandya <hiren.pandya@krishtechnolabs.com> Date: Wed, 27 Mar 2019 14:54:18 +0530 Subject: [PATCH 1001/1295] Full Tax Summary display wrong numbers -Backport --- .../web/js/view/checkout/summary/tax.js | 40 ++++++++++++++++--- .../template/checkout/cart/totals/tax.html | 24 +++++------ .../web/template/checkout/summary/tax.html | 24 +++++------ 3 files changed, 57 insertions(+), 31 deletions(-) diff --git a/app/code/Magento/Tax/view/frontend/web/js/view/checkout/summary/tax.js b/app/code/Magento/Tax/view/frontend/web/js/view/checkout/summary/tax.js index 39220ae0fac66..6d152035baa1c 100644 --- a/app/code/Magento/Tax/view/frontend/web/js/view/checkout/summary/tax.js +++ b/app/code/Magento/Tax/view/frontend/web/js/view/checkout/summary/tax.js @@ -13,13 +13,16 @@ define([ 'Magento_Checkout/js/model/quote', 'Magento_Checkout/js/model/totals', 'jquery', - 'mage/translate' -], function (ko, Component, quote, totals, $, $t) { + 'mage/translate', + 'underscore' +], function (ko, Component, quote, totals, $t, _) { 'use strict'; var isTaxDisplayedInGrandTotal = window.checkoutConfig.includeTaxInGrandTotal, isFullTaxSummaryDisplayed = window.checkoutConfig.isFullTaxSummaryDisplayed, - isZeroTaxDisplayed = window.checkoutConfig.isZeroTaxDisplayed; + isZeroTaxDisplayed = window.checkoutConfig.isZeroTaxDisplayed, + taxAmount = 0, + rates = 0; return Component.extend({ defaults: { @@ -67,7 +70,7 @@ define([ } } - return parseFloat(amount); + return amount; }, /** @@ -99,6 +102,33 @@ define([ return this.getFormattedPrice(amount); }, + /** + * @param {*} parent + * @param {*} percentage + * @return {*|String} + */ + getTaxAmount: function (parent, percentage) { + var totalPercentage = 0; + + taxAmount = parent.amount; + rates = parent.rates; + _.each(rates, function (rate) { + totalPercentage += parseFloat(rate.percent); + }); + + return this.getFormattedPrice(this.getPercentAmount(taxAmount, totalPercentage, percentage)); + }, + + /** + * @param {*} amount + * @param {*} totalPercentage + * @param {*} percentage + * @return {*|String} + */ + getPercentAmount: function (amount, totalPercentage, percentage) { + return parseFloat(amount * percentage / totalPercentage); + }, + /** * @return {Array} */ @@ -112,4 +142,4 @@ define([ return []; } }); -}); +}); \ No newline at end of file diff --git a/app/code/Magento/Tax/view/frontend/web/template/checkout/cart/totals/tax.html b/app/code/Magento/Tax/view/frontend/web/template/checkout/cart/totals/tax.html index 9c45e73db6fa4..0e27ada83ea46 100644 --- a/app/code/Magento/Tax/view/frontend/web/template/checkout/cart/totals/tax.html +++ b/app/code/Magento/Tax/view/frontend/web/template/checkout/cart/totals/tax.html @@ -32,19 +32,17 @@ <!-- ko if: !percent --> <th class="mark" scope="row" colspan="1" data-bind="text: title"></th> <!-- /ko --> - <!-- ko if: $index() == 0 --> - <td class="amount" rowspan="1"> - <!-- ko if: $parents[1].isCalculated() --> - <span class="price" - data-bind="text: $parents[1].formatPrice($parents[0].amount), attr: {'data-th': title, 'rowspan': $parents[0].rates.length }"></span> - <!-- /ko --> - <!-- ko ifnot: $parents[1].isCalculated() --> - <span class="not-calculated" - data-bind="text: $parents[1].formatPrice($parents[0].amount), attr: {'data-th': title, 'rowspan': $parents[0].rates.length }"></span> - <!-- /ko --> - </td> - <!-- /ko --> + <td class="amount" rowspan="1"> + <!-- ko if: $parents[1].isCalculated() --> + <span class="price" + data-bind="text: $parents[1].getTaxAmount($parents[0], percent), attr: {'data-th': title, 'rowspan': $parents[0].rates.length }"></span> + <!-- /ko --> + <!-- ko ifnot: $parents[1].isCalculated() --> + <span class="not-calculated" + data-bind="text: $parents[1].getTaxAmount($parents[0], percent), attr: {'data-th': title, 'rowspan': $parents[0].rates.length }"></span> + <!-- /ko --> + </td> </tr> <!-- /ko --> <!-- /ko --> -<!-- /ko --> +<!-- /ko --> \ No newline at end of file diff --git a/app/code/Magento/Tax/view/frontend/web/template/checkout/summary/tax.html b/app/code/Magento/Tax/view/frontend/web/template/checkout/summary/tax.html index 0f2e3251bcfdb..3cef09a7a4468 100644 --- a/app/code/Magento/Tax/view/frontend/web/template/checkout/summary/tax.html +++ b/app/code/Magento/Tax/view/frontend/web/template/checkout/summary/tax.html @@ -43,19 +43,17 @@ <!-- ko if: !percent --> <th class="mark" scope="row" data-bind="text: title"></th> <!-- /ko --> - <!-- ko if: $index() == 0 --> - <td class="amount"> - <!-- ko if: $parents[1].isCalculated() --> - <span class="price" - data-bind="text: $parents[1].formatPrice($parents[0].amount), attr: {'data-th': title, 'rowspan': $parents[0].rates.length }"></span> - <!-- /ko --> - <!-- ko ifnot: $parents[1].isCalculated() --> - <span class="not-calculated" - data-bind="text: $parents[1].formatPrice($parents[0].amount), attr: {'data-th': title, 'rowspan': $parents[0].rates.length }"></span> - <!-- /ko --> - </td> - <!-- /ko --> + <td class="amount"> + <!-- ko if: $parents[1].isCalculated() --> + <span class="price" + data-bind="text: $parents[1].getTaxAmount($parents[0], percent), attr: {'data-th': title, 'rowspan': $parents[0].rates.length }"></span> + <!-- /ko --> + <!-- ko ifnot: $parents[1].isCalculated() --> + <span class="not-calculated" + data-bind="text: $parents[1].getTaxAmount($parents[0], percent), attr: {'data-th': title, 'rowspan': $parents[0].rates.length }"></span> + <!-- /ko --> + </td> </tr> <!-- /ko --> <!-- /ko --> -<!-- /ko --> +<!-- /ko --> \ No newline at end of file From 544100fc4b5bcfd0339e7bb61a393e53839e89d7 Mon Sep 17 00:00:00 2001 From: hiren pandya <hiren.pandya@krishtechnolabs.com> Date: Thu, 28 Mar 2019 10:23:46 +0530 Subject: [PATCH 1002/1295] Resolve travis error --- .../Tax/view/frontend/web/js/view/checkout/summary/tax.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Tax/view/frontend/web/js/view/checkout/summary/tax.js b/app/code/Magento/Tax/view/frontend/web/js/view/checkout/summary/tax.js index 6d152035baa1c..4b10fe86015dc 100644 --- a/app/code/Magento/Tax/view/frontend/web/js/view/checkout/summary/tax.js +++ b/app/code/Magento/Tax/view/frontend/web/js/view/checkout/summary/tax.js @@ -142,4 +142,4 @@ define([ return []; } }); -}); \ No newline at end of file +}); From 5c7a4035f0e194171654860e27cd3b6eb26758e2 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Thu, 28 Mar 2019 11:18:06 +0200 Subject: [PATCH 1003/1295] MAGETWO-74081: [GitHub] Can't use "configurable" as group name in attribute sets M2.1 #6123 --- .../Magento/ConfigurableProduct/etc/di.xml | 7 +++ .../Eav/Model/Entity/Attribute/Group.php | 57 ++++++++++++------- .../Unit/Model/Entity/Attribute/GroupTest.php | 25 +++++--- 3 files changed, 60 insertions(+), 29 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/etc/di.xml b/app/code/Magento/ConfigurableProduct/etc/di.xml index a390cb375befc..dd39bcb477699 100644 --- a/app/code/Magento/ConfigurableProduct/etc/di.xml +++ b/app/code/Magento/ConfigurableProduct/etc/di.xml @@ -248,4 +248,11 @@ <type name="Magento\Tax\Model\Sales\Total\Quote\CommonTaxCollector"> <plugin name="apply_tax_class_id" type="Magento\ConfigurableProduct\Plugin\Tax\Model\Sales\Total\Quote\CommonTaxCollector" /> </type> + <type name="Magento\Eav\Model\Entity\Attribute\Group"> + <arguments> + <argument name="reservedSystemNames" xsi:type="array"> + <item name="configurable" xsi:type="string">configurable</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Group.php b/app/code/Magento/Eav/Model/Entity/Attribute/Group.php index 0b6ac2b998de7..7c7fcec6fc8f8 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Group.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Group.php @@ -5,9 +5,14 @@ */ namespace Magento\Eav\Model\Entity\Attribute; +use Magento\Eav\Api\Data\AttributeGroupExtensionInterface; +use Magento\Eav\Api\Data\AttributeGroupInterface; use Magento\Framework\Api\AttributeValueFactory; +use Magento\Framework\Model\AbstractExtensibleModel; /** + * Entity attribute group model. + * * @api * @method int getSortOrder() * @method \Magento\Eav\Model\Entity\Attribute\Group setSortOrder(int $value) @@ -19,14 +24,18 @@ * @method \Magento\Eav\Model\Entity\Attribute\Group setTabGroupCode(string $value) * @since 100.0.2 */ -class Group extends \Magento\Framework\Model\AbstractExtensibleModel implements - \Magento\Eav\Api\Data\AttributeGroupInterface +class Group extends AbstractExtensibleModel implements AttributeGroupInterface { /** * @var \Magento\Framework\Filter\Translit */ private $translitFilter; + /** + * @var array + */ + private $reservedSystemNames = []; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -36,6 +45,7 @@ class Group extends \Magento\Framework\Model\AbstractExtensibleModel implements * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data + * @param array $reservedSystemNames */ public function __construct( \Magento\Framework\Model\Context $context, @@ -45,7 +55,8 @@ public function __construct( \Magento\Framework\Filter\Translit $translitFilter, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, - array $data = [] + array $data = [], + array $reservedSystemNames = [] ) { parent::__construct( $context, @@ -57,10 +68,11 @@ public function __construct( $data ); $this->translitFilter = $translitFilter; + $this->reservedSystemNames = $reservedSystemNames; } /** - * Resource initialization + * Resource initialization. * * @return void * @codeCoverageIgnore @@ -71,7 +83,7 @@ protected function _construct() } /** - * Checks if current attribute group exists + * Checks if current attribute group exists. * * @return bool * @codeCoverageIgnore @@ -82,7 +94,7 @@ public function itemExists() } /** - * Delete groups + * Delete groups. * * @return $this * @codeCoverageIgnore @@ -93,7 +105,7 @@ public function deleteGroups() } /** - * Processing object before save data + * Processing object before save data. * * @return $this */ @@ -110,18 +122,20 @@ public function beforeSave() ), '-' ); - if (empty($attributeGroupCode)) { + $isReservedSystemName = in_array(strtolower($attributeGroupCode), $this->reservedSystemNames); + if (empty($attributeGroupCode) || $isReservedSystemName) { // in the following code md5 is not used for security purposes - $attributeGroupCode = md5($groupName); + $attributeGroupCode = md5(strtolower($groupName)); } $this->setAttributeGroupCode($attributeGroupCode); } } + return parent::beforeSave(); } /** - * {@inheritdoc} + * @inheritdoc * @codeCoverageIgnoreStart */ public function getAttributeGroupId() @@ -130,7 +144,7 @@ public function getAttributeGroupId() } /** - * {@inheritdoc} + * @inheritdoc */ public function getAttributeGroupName() { @@ -138,7 +152,7 @@ public function getAttributeGroupName() } /** - * {@inheritdoc} + * @inheritdoc */ public function getAttributeSetId() { @@ -146,7 +160,7 @@ public function getAttributeSetId() } /** - * {@inheritdoc} + * @inheritdoc */ public function setAttributeGroupId($attributeGroupId) { @@ -154,7 +168,7 @@ public function setAttributeGroupId($attributeGroupId) } /** - * {@inheritdoc} + * @inheritdoc */ public function setAttributeGroupName($attributeGroupName) { @@ -162,7 +176,7 @@ public function setAttributeGroupName($attributeGroupName) } /** - * {@inheritdoc} + * @inheritdoc */ public function setAttributeSetId($attributeSetId) { @@ -170,9 +184,9 @@ public function setAttributeSetId($attributeSetId) } /** - * {@inheritdoc} + * @inheritdoc * - * @return \Magento\Eav\Api\Data\AttributeGroupExtensionInterface|null + * @return AttributeGroupExtensionInterface|null */ public function getExtensionAttributes() { @@ -180,14 +194,13 @@ public function getExtensionAttributes() } /** - * {@inheritdoc} + * @inheritdoc * - * @param \Magento\Eav\Api\Data\AttributeGroupExtensionInterface $extensionAttributes + * @param AttributeGroupExtensionInterface $extensionAttributes * @return $this */ - public function setExtensionAttributes( - \Magento\Eav\Api\Data\AttributeGroupExtensionInterface $extensionAttributes - ) { + public function setExtensionAttributes(AttributeGroupExtensionInterface $extensionAttributes) + { return $this->_setExtensionAttributes($extensionAttributes); } diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/GroupTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/GroupTest.php index d4c91e98d9608..3f663558f4b8c 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/GroupTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/GroupTest.php @@ -6,7 +6,12 @@ namespace Magento\Eav\Test\Unit\Model\Entity\Attribute; use Magento\Eav\Model\Entity\Attribute\Group; +use Magento\Eav\Model\ResourceModel\Entity\Attribute\Group as ResourceGroup; +use Magento\Framework\Event\ManagerInterface; +use Magento\Framework\Filter\Translit; +use Magento\Framework\Model\Context; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit_Framework_MockObject_MockObject as MockObject; class GroupTest extends \PHPUnit\Framework\TestCase { @@ -16,34 +21,38 @@ class GroupTest extends \PHPUnit\Framework\TestCase private $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ResourceGroup|MockObject */ private $resourceMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ManagerInterface|MockObject */ private $eventManagerMock; + /** + * @inheritdoc + */ protected function setUp() { - $this->resourceMock = $this->createMock(\Magento\Eav\Model\ResourceModel\Entity\Attribute\Group::class); - $translitFilter = $this->getMockBuilder(\Magento\Framework\Filter\Translit::class) + $this->resourceMock = $this->createMock(ResourceGroup::class); + $translitFilter = $this->getMockBuilder(Translit::class) ->disableOriginalConstructor() ->getMock(); $translitFilter->expects($this->atLeastOnce())->method('filter')->willReturnArgument(0); - $this->eventManagerMock = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); - $contextMock = $this->createMock(\Magento\Framework\Model\Context::class); + $this->eventManagerMock = $this->createMock(ManagerInterface::class); + $contextMock = $this->createMock(Context::class); $contextMock->expects($this->any())->method('getEventDispatcher')->willReturn($this->eventManagerMock); $constructorArguments = [ 'resource' => $this->resourceMock, 'translitFilter' => $translitFilter, 'context' => $contextMock, + 'reservedSystemNames' => ['configurable'], ]; $objectManager = new ObjectManager($this); $this->model = $objectManager->getObject( - \Magento\Eav\Model\Entity\Attribute\Group::class, + Group::class, $constructorArguments ); } @@ -67,6 +76,8 @@ public function attributeGroupCodeDataProvider() { return [ ['General Group', 'general-group'], + ['configurable', md5('configurable')], + ['configurAble', md5('configurable')], ['///', md5('///')], ]; } From 200fac8d63966b699a7a6e73f92b701e34c919c7 Mon Sep 17 00:00:00 2001 From: hiren pandya <hiren.pandya@krishtechnolabs.com> Date: Thu, 28 Mar 2019 19:26:18 +0530 Subject: [PATCH 1004/1295] add blank space at the end of html template --- .../view/frontend/web/template/checkout/cart/totals/tax.html | 2 +- .../Tax/view/frontend/web/template/checkout/summary/tax.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Tax/view/frontend/web/template/checkout/cart/totals/tax.html b/app/code/Magento/Tax/view/frontend/web/template/checkout/cart/totals/tax.html index 0e27ada83ea46..45c468096abe1 100644 --- a/app/code/Magento/Tax/view/frontend/web/template/checkout/cart/totals/tax.html +++ b/app/code/Magento/Tax/view/frontend/web/template/checkout/cart/totals/tax.html @@ -45,4 +45,4 @@ </tr> <!-- /ko --> <!-- /ko --> -<!-- /ko --> \ No newline at end of file +<!-- /ko --> diff --git a/app/code/Magento/Tax/view/frontend/web/template/checkout/summary/tax.html b/app/code/Magento/Tax/view/frontend/web/template/checkout/summary/tax.html index 3cef09a7a4468..5f1ac86e38ffd 100644 --- a/app/code/Magento/Tax/view/frontend/web/template/checkout/summary/tax.html +++ b/app/code/Magento/Tax/view/frontend/web/template/checkout/summary/tax.html @@ -56,4 +56,4 @@ </tr> <!-- /ko --> <!-- /ko --> -<!-- /ko --> \ No newline at end of file +<!-- /ko --> From 39c6ee778057c60cd485842542da5ca1487b576d Mon Sep 17 00:00:00 2001 From: Roman Zhupanyn <roma.dj.elf@gmail.com> Date: Thu, 28 Mar 2019 17:31:11 +0200 Subject: [PATCH 1005/1295] MAGETWO-98568: Cart Price Rule code is absent on "view Order/Invoice" Admin page if had been used for Free Shipping --- .../Magento/Sales/Block/Adminhtml/Totals.php | 30 +++++++++- .../Sales/Block/Adminhtml/TotalsTest.php | 55 +++++++++++++++++++ .../order_with_free_shipping_by_coupon.php | 35 ++++++++++++ ..._with_free_shipping_by_coupon_rollback.php | 8 +++ 4 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/TotalsTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_free_shipping_by_coupon.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_free_shipping_by_coupon_rollback.php diff --git a/app/code/Magento/Sales/Block/Adminhtml/Totals.php b/app/code/Magento/Sales/Block/Adminhtml/Totals.php index 83b155293c2b9..7e6862eeef323 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Totals.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Totals.php @@ -5,6 +5,11 @@ */ namespace Magento\Sales\Block\Adminhtml; +use Magento\Sales\Model\Order; + +/** + * Adminhtml sales totals block + */ class Totals extends \Magento\Sales\Block\Order\Totals { /** @@ -67,12 +72,16 @@ protected function _initTotals() if (!$this->getSource()->getIsVirtual() && ((double)$this->getSource()->getShippingAmount() || $this->getSource()->getShippingDescription()) ) { + $shippingLabel = __('Shipping & Handling'); + if ($this->isFreeShipping($this->getSource()) && $this->getSource()->getDiscountDescription()) { + $shippingLabel .= sprintf(' (%s)', $this->getSource()->getDiscountDescription()); + } $this->_totals['shipping'] = new \Magento\Framework\DataObject( [ 'code' => 'shipping', 'value' => $this->getSource()->getShippingAmount(), 'base_value' => $this->getSource()->getBaseShippingAmount(), - 'label' => __('Shipping & Handling'), + 'label' => $shippingLabel, ] ); } @@ -109,4 +118,23 @@ protected function _initTotals() return $this; } + + /** + * Availability of free shipping in at least one order item + * + * @param Order $order + * @return bool + */ + private function isFreeShipping(Order $order): bool + { + $isFreeShipping = false; + foreach ($order->getItems() as $orderItem) { + if ($orderItem->getFreeShipping() == '1') { + $isFreeShipping = true; + break; + } + } + + return $isFreeShipping; + } } diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/TotalsTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/TotalsTest.php new file mode 100644 index 0000000000000..5d8e1e4bc5644 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/TotalsTest.php @@ -0,0 +1,55 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Block\Adminhtml; + +use Magento\Framework\View\LayoutInterface; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\OrderFactory; + +/** + * Test class for \Magento\Sales\Block\Adminhtml\Totals + */ +class TotalsTest extends \Magento\TestFramework\TestCase\AbstractBackendController +{ + /** @var LayoutInterface */ + private $layout; + + /** @var Totals */ + private $block; + + /** @var OrderFactory */ + private $orderFactory; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->layout = $this->_objectManager->get(LayoutInterface::class); + $this->block = $this->layout->createBlock(Totals::class, 'totals_block'); + $this->orderFactory = $this->_objectManager->get(OrderFactory::class); + } + + /** + * @magentoDataFixture Magento/Sales/_files/order_with_free_shipping_by_coupon.php + */ + public function testShowShippingCoupon() + { + /** @var Order $order */ + $order = $this->orderFactory->create(); + $order->loadByIncrementId('100000001'); + + $this->block->setOrder($order); + $this->block->toHtml(); + + $shippingTotal = $this->block->getTotal('shipping'); + $this->assertNotFalse($shippingTotal, 'Shipping method is absent on the total\'s block.'); + $this->assertContains('1234567890', $shippingTotal->getLabel(), 'Coupon code is absent in the shipping method label name.'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_free_shipping_by_coupon.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_free_shipping_by_coupon.php new file mode 100644 index 0000000000000..57ccffadaa4d0 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_free_shipping_by_coupon.php @@ -0,0 +1,35 @@ +<?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\Item as OrderItem; + +require __DIR__ . '/../../../Magento/Sales/_files/order.php'; +/** @var \Magento\Catalog\Model\Product $product */ + +/** @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') + ->setName($product->getName()) + ->setFreeShipping('1'); + +/** @var Order $order */ +$order->setShippingDescription('Flat Rate - Fixed') + ->setShippingAmount(0) + ->setCouponCode('1234567890') + ->setDiscountDescription('1234567890') + ->addItem($orderItem); + +/** @var OrderRepositoryInterface $orderRepository */ +$orderRepository = $objectManager->create(OrderRepositoryInterface::class); +$orderRepository->save($order); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_free_shipping_by_coupon_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_free_shipping_by_coupon_rollback.php new file mode 100644 index 0000000000000..1fb4b4636ab29 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_free_shipping_by_coupon_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require 'default_rollback.php'; From 4ff4b6a9e741dec3a954e6bb835c55fbe5f80caf Mon Sep 17 00:00:00 2001 From: Oleksii Lisovyi <olexii.lisovyi@ven.com> Date: Thu, 28 Mar 2019 17:37:51 +0200 Subject: [PATCH 1006/1295] Magento Catalog - fix custom option type text price conversion for multi currency website --- .../Catalog/Model/ResourceModel/Product/Option.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option.php index 179da06b59990..1a261f693268f 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option.php @@ -6,6 +6,7 @@ namespace Magento\Catalog\Model\ResourceModel\Product; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Store\Model\ScopeInterface; /** * Catalog product custom option resource model @@ -154,21 +155,24 @@ protected function _saveValuePrices(\Magento\Framework\Model\AbstractModel $obje $scope = (int)$this->_config->getValue( \Magento\Store\Model\Store::XML_PATH_PRICE_SCOPE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ScopeInterface::SCOPE_STORE ); if ($object->getStoreId() != '0' && $scope == \Magento\Store\Model\Store::PRICE_SCOPE_WEBSITE) { - $baseCurrency = $this->_config->getValue( + $website = $this->_storeManager->getStore($object->getStoreId())->getWebsite(); + + $websiteBaseCurrency = $this->_config->getValue( \Magento\Directory\Model\Currency::XML_PATH_CURRENCY_BASE, - 'default' + ScopeInterface::SCOPE_WEBSITE, + $website ); - $storeIds = $this->_storeManager->getStore($object->getStoreId())->getWebsite()->getStoreIds(); + $storeIds = $website->getStoreIds(); if (is_array($storeIds)) { foreach ($storeIds as $storeId) { if ($object->getPriceType() == 'fixed') { $storeCurrency = $this->_storeManager->getStore($storeId)->getBaseCurrencyCode(); - $rate = $this->_currencyFactory->create()->load($baseCurrency)->getRate($storeCurrency); + $rate = $this->_currencyFactory->create()->load($websiteBaseCurrency)->getRate($storeCurrency); if (!$rate) { $rate = 1; } From 68a659f7a951dce7a30166f8c92ff483cebeeba7 Mon Sep 17 00:00:00 2001 From: Roman Zhupanyn <roma.dj.elf@gmail.com> Date: Thu, 28 Mar 2019 22:24:01 +0200 Subject: [PATCH 1007/1295] MAGETWO-98568: Cart Price Rule code is absent on "view Order/Invoice" Admin page if had been used for Free Shipping --- app/code/Magento/Sales/Block/Adminhtml/Totals.php | 2 +- .../testsuite/Magento/Sales/Block/Adminhtml/TotalsTest.php | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Totals.php b/app/code/Magento/Sales/Block/Adminhtml/Totals.php index 7e6862eeef323..8172a3c0db4ad 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Totals.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Totals.php @@ -73,7 +73,7 @@ protected function _initTotals() $this->getSource()->getShippingDescription()) ) { $shippingLabel = __('Shipping & Handling'); - if ($this->isFreeShipping($this->getSource()) && $this->getSource()->getDiscountDescription()) { + if ($this->isFreeShipping($this->getOrder()) && $this->getSource()->getDiscountDescription()) { $shippingLabel .= sprintf(' (%s)', $this->getSource()->getDiscountDescription()); } $this->_totals['shipping'] = new \Magento\Framework\DataObject( diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/TotalsTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/TotalsTest.php index 5d8e1e4bc5644..1125fc1730718 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/TotalsTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/TotalsTest.php @@ -50,6 +50,10 @@ public function testShowShippingCoupon() $shippingTotal = $this->block->getTotal('shipping'); $this->assertNotFalse($shippingTotal, 'Shipping method is absent on the total\'s block.'); - $this->assertContains('1234567890', $shippingTotal->getLabel(), 'Coupon code is absent in the shipping method label name.'); + $this->assertContains( + '1234567890', + $shippingTotal->getLabel(), + 'Coupon code is absent in the shipping method label name.' + ); } } From e7297da0eeed711158164fd2468b127accd8ed6c Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Fri, 29 Mar 2019 11:06:54 +0200 Subject: [PATCH 1008/1295] MAGETWO-96375: Checkout Free Shipping Recalculation after Coupon Code Added --- .../Mftf/ActionGroup/CheckoutActionGroup.xml | 9 +++++++++ .../GoToCheckoutFromMinicartActionGroup.xml | 3 ++- ...ingRecalculationAfterCouponCodeAddedTest.xml | 17 ++++++----------- .../Test/Mftf/Data/StoreShippingMethodsData.xml | 14 ++++---------- 4 files changed, 21 insertions(+), 22 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml index 1cbaa66e2fd8b..09197a90542ee 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml @@ -119,4 +119,13 @@ <waitForElementVisible selector="{{CheckoutPaymentSection.paymentSectionTitle}}" stepKey="waitForPaymentSectionLoaded"/> <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> </actionGroup> + + <actionGroup name="AssertStorefrontErrorMessageOnOrderSubmit"> + <arguments> + <argument name="errorMessage" type="string"/> + </arguments> + <waitForElementVisible selector="{{CheckoutPaymentSection.placeOrderNoWait}}" stepKey="waitForPlaceOrderButton"/> + <click selector="{{CheckoutPaymentSection.placeOrderNoWait}}" stepKey="clickPlaceOrder"/> + <waitForText selector="{{StorefrontMessagesSection.errorMessage}}" userInput="{{errorMessage}}" time="30" stepKey="seeShippingMethodError"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GoToCheckoutFromMinicartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GoToCheckoutFromMinicartActionGroup.xml index f03be61cccd3a..c06ff0cb96b58 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GoToCheckoutFromMinicartActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GoToCheckoutFromMinicartActionGroup.xml @@ -10,10 +10,11 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- Go to checkout from minicart --> <actionGroup name="GoToCheckoutFromMinicartActionGroup"> + <waitForPageLoad stepKey="waitForPageLoad"/> <waitForElement selector="{{StorefrontMinicartSection.showCart}}" stepKey="waitMiniCartSectionShow" /> <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> <waitForElementVisible selector="{{StorefrontMinicartSection.goToCheckout}}" time="30" stepKey="waitForGoToCheckoutButtonVisible"/> <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="clickGoToCheckoutButton"/> - <waitForPageLoad stepKey="waitForPageLoad"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml index 0dac0266904ff..5f0a5078734d7 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml @@ -29,8 +29,7 @@ <createData entity="Simple_US_Customer" stepKey="createSimpleUsCustomer"/> <!-- Enable Free Shipping and set minimum order amount --> - <createData entity="FreeShippingMethodsSettingConfig" stepKey="freeShippingMethodSettingConfig"/> - <createData entity="MinimumOrderAmount90" stepKey="setMinimumOrderAmount"/> + <createData entity="FreeShippingMethodWithMinimumOrderAmount90" stepKey="enableFreeShippingAndSetMinimumOrderAmount"/> <!-- Create Cart Price Rule --> <createData entity="SalesRuleSpecificCouponWithPercentDiscount" stepKey="createCartPriceRule"> @@ -39,20 +38,17 @@ <createData entity="SimpleSalesRuleCoupon" stepKey="createCouponForCartPriceRule"> <requiredEntity createDataKey="createCartPriceRule"/> </createData> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <actionGroup ref="CustomerLoginOnStorefront" stepKey="loginToStorefront"> <argument name="customer" value="$$createSimpleUsCustomer$$"/> </actionGroup> </before> + <after> <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createSimpleUsCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createCartPriceRule" stepKey="deleteCartPriceRule"/> - <createData entity="DefaultShippingMethodsConfig" stepKey="setDefaultShippingMethodsConfig"/> - <createData entity="DefaultMinimumOrderAmount" stepKey="setDefaultMinimumOrderAmount"/> - <actionGroup ref="logout" stepKey="logout"/> + <createData entity="ResetFreeShippingMethodWithMinimumOrderAmount90" stepKey="resetFreeShippingMethodAndMinimumOrderAmount"/> <actionGroup ref="CustomerLogoutStorefrontActionGroup" stepKey="logoutFromStorefront"/> </after> @@ -70,7 +66,6 @@ <!-- Back to Shopping Cart page and cancel coupon--> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToShoppingCartPage"/> <actionGroup ref="StorefrontCancelCouponActionGroup" stepKey="cancelCoupon"/> - <waitForPageLoad stepKey="waitForPageLoad"/> <!-- Proceed to Checkout, select Free Shipping method and apply coupon --> <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart1"/> @@ -84,9 +79,9 @@ <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectPaymentMethod"/> <!-- Try to Place Order --> - <waitForElementVisible selector="{{CheckoutPaymentSection.placeOrderNoWait}}" stepKey="waitForPlaceOrderButton"/> - <click selector="{{CheckoutPaymentSection.placeOrderNoWait}}" stepKey="clickPlaceOrder"/> - <waitForText selector="{{StorefrontMessagesSection.errorMessage}}" userInput="The shipping method is missing. Select the shipping method and try again." time="30" stepKey="seeShippingMethodError"/> + <actionGroup ref="AssertStorefrontErrorMessageOnOrderSubmit" stepKey="tryToPlaceOrder"> + <argument name="errorMessage" value="The shipping method is missing. Select the shipping method and try again."/> + </actionGroup> <!-- Go back to Shipping step and select Shipping method --> <amOnPage url="{{CheckoutPage.url}}/#shipping" stepKey="navigateToShippingStep"/> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreShippingMethodsData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreShippingMethodsData.xml index c9d0fb628b853..413540a8f4c12 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreShippingMethodsData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreShippingMethodsData.xml @@ -7,30 +7,24 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> - <entity name="FreeShippingMethodsSettingConfig" type="free_shipping_config_state"> + <entity name="FreeShippingMethodWithMinimumOrderAmount90" type="free_shipping_config_state"> <requiredEntity type="active">Active</requiredEntity> + <requiredEntity type="free_shipping_subtotal">Price</requiredEntity> </entity> <entity name="Active" type="active"> <data key="value">1</data> </entity> - - <entity name="MinimumOrderAmount90" type="free_shipping_config_state"> - <requiredEntity type="free_shipping_subtotal">Price</requiredEntity> - </entity> <entity name="Price" type="free_shipping_subtotal"> <data key="value">90</data> </entity> - <entity name="DefaultMinimumOrderAmount" type="free_shipping_config_state"> + <entity name="ResetFreeShippingMethodWithMinimumOrderAmount90" type="free_shipping_config_state"> <requiredEntity type="free_shipping_subtotal">DefaultPrice</requiredEntity> + <requiredEntity type="active">DefaultActive</requiredEntity> </entity> <entity name="DefaultPrice" type="free_shipping_subtotal"> <data key="value">0</data> </entity> - - <entity name="DefaultShippingMethodsConfig" type="free_shipping_config_state"> - <requiredEntity type="active">DefaultActive</requiredEntity> - </entity> <entity name="DefaultActive" type="active"> <requiredEntity type="active_inherit">DefaultFreeShipping</requiredEntity> </entity> From 780255821d920bd1b00e459eee8971a559651cb7 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Fri, 29 Mar 2019 16:16:05 +0200 Subject: [PATCH 1009/1295] MAGETWO-74455: Product duplication allows to save non-unique values for product attributes --- .../Controller/Adminhtml/Product/Save.php | 25 +++- .../Controller/Adminhtml/Product/SaveTest.php | 129 +++++++++++++++++- 2 files changed, 151 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php index bf0d740fc98fb..fd44e85868609 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php @@ -79,11 +79,12 @@ public function __construct( } /** - * Save product action + * Save product action. * * @return \Magento\Backend\Model\View\Result\Redirect * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function execute() { @@ -143,6 +144,7 @@ public function execute() if ($redirectBack === 'duplicate') { $product->unsetData('quantity_and_stock_status'); $newProduct = $this->productCopier->copy($product); + $this->checkUniqueAttributes($product); $this->messageManager->addSuccessMessage(__('You duplicated the product.')); } } catch (\Magento\Framework\Exception\LocalizedException $e) { @@ -321,4 +323,25 @@ private function persistMediaData(ProductInterface $product, array $data) return $data; } + + /** + * Check unique attributes and add error to message manager. + * + * @param \Magento\Catalog\Model\Product $product + */ + private function checkUniqueAttributes(\Magento\Catalog\Model\Product $product) + { + $uniqueLabels = []; + foreach ($product->getAttributes() as $attribute) { + if ($attribute->getIsUnique() && $attribute->getIsUserDefined() + && $product->getData($attribute->getAttributeCode()) !== null + ) { + $uniqueLabels[] = $attribute->getDefaultFrontendLabel(); + } + } + if ($uniqueLabels) { + $uniqueLabels = implode('", "', $uniqueLabels); + $this->messageManager->addErrorMessage(__('The value of attribute(s) "%1" must be unique', $uniqueLabels)); + } + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/SaveTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/SaveTest.php index a10814371577e..0c7ce74a216bb 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/SaveTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/SaveTest.php @@ -5,10 +5,15 @@ */ namespace Magento\Catalog\Test\Unit\Controller\Adminhtml\Product; +use Magento\Catalog\Api\CategoryLinkManagementInterface; +use Magento\Catalog\Api\Data\ProductAttributeInterface; use Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper; +use Magento\Catalog\Model\Product\Copier; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; /** + * Unit tests for \Magento\Catalog\Controller\Adminhtml\Product\Save class. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class SaveTest extends \Magento\Catalog\Test\Unit\Controller\Adminhtml\ProductTest @@ -28,6 +33,21 @@ class SaveTest extends \Magento\Catalog\Test\Unit\Controller\Adminhtml\ProductTe /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject */ private $product; + /** + * @var Copier|\PHPUnit_Framework_MockObject_MockObject + */ + private $productCopierMock; + + /** + * @var ProductAttributeInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $productAttributeMock; + + /** + * @var CategoryLinkManagementInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $categoryLinkManagementMock; + /** @var \Magento\Backend\Model\View\Result\RedirectFactory|\PHPUnit_Framework_MockObject_MockObject */ private $resultRedirectFactory; @@ -42,6 +62,7 @@ class SaveTest extends \Magento\Catalog\Test\Unit\Controller\Adminhtml\ProductTe /** * @return void + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function setUp() { @@ -49,11 +70,33 @@ protected function setUp() \Magento\Catalog\Controller\Adminhtml\Product\Builder::class, ['build'] ); - $this->product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class)->disableOriginalConstructor() - ->setMethods(['addData', 'getSku', 'getTypeId', 'getStoreId', '__sleep', '__wakeup'])->getMock(); + $this->product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + ->disableOriginalConstructor() + ->setMethods([ + 'addData', + 'unsetData', + 'getData', + 'getSku', + 'getCategoryIds', + 'getAttributes', + 'getTypeId', + 'getStoreId', + 'save', + '__sleep', + '__wakeup' + ]) + ->getMock(); $this->product->expects($this->any())->method('getTypeId')->will($this->returnValue('simple')); $this->product->expects($this->any())->method('getStoreId')->will($this->returnValue('1')); $this->productBuilder->expects($this->any())->method('build')->will($this->returnValue($this->product)); + $this->productCopierMock = $this->getMockBuilder(Copier::class) + ->disableOriginalConstructor() + ->getMock(); + $this->productAttributeMock = $this->getMockBuilder(ProductAttributeInterface::class) + ->setMethods(['getIsUnique', 'getIsUserDefined', 'getAttributeCode', 'getDefaultFrontendLabel']) + ->getMockForAbstractClass(); + $this->categoryLinkManagementMock = $this->getMockBuilder(CategoryLinkManagementInterface::class) + ->getMockForAbstractClass(); $this->messageManagerMock = $this->getMockForAbstractClass( \Magento\Framework\Message\ManagerInterface::class @@ -155,4 +198,86 @@ public function exceptionTypeDataProvider() ['Exception', 'addErrorMessage'] ]; } + + /** + * @return void + */ + public function testExecuteCheckUniqueAttributesOnDuplicate() + { + $productSku = 'test_sku'; + $attributeCode = 'test_attribute_code'; + + $productData = [ + 'product' => [ + 'name' => 'test-name', + 'sku' => $productSku, + $attributeCode => 'test_attribute', + ] + ]; + + $this->request->expects($this->at(1)) + ->method('getParam') + ->with('back', false) + ->willReturn('duplicate'); + + $this->request->expects($this->any())->method('getPostValue')->willReturn($productData); + $this->initializationHelper->expects($this->any())->method('initialize') + ->willReturn($this->product); + + $this->product->expects($this->once()) + ->method('save') + ->willReturnSelf(); + $this->product->expects($this->any()) + ->method('getSku') + ->willReturn($productSku); + $this->product->expects($this->any()) + ->method('getCategoryIds') + ->willReturn([]); + + $this->categoryLinkManagementMock->expects($this->any()) + ->method('assignProductToCategories') + ->with($productSku, []) + ->willReturn(true); + + $this->product->expects($this->once()) + ->method('unsetData') + ->with('quantity_and_stock_status') + ->willReturnSelf(); + + $this->productCopierMock->expects($this->any()) + ->method('copy') + ->with($this->product) + ->willReturn($this->product); + + $this->product->expects($this->once()) + ->method('getAttributes') + ->willReturn([$this->productAttributeMock]); + + $this->productAttributeMock->expects($this->atLeastOnce()) + ->method('getIsUnique') + ->willReturn('1'); + $this->productAttributeMock->expects($this->atLeastOnce()) + ->method('getIsUserDefined') + ->willReturn('1'); + $this->productAttributeMock->expects($this->atLeastOnce()) + ->method('getAttributeCode') + ->willReturn($attributeCode); + + $this->product->expects($this->any()) + ->method('getData') + ->willReturnMap([ + [$attributeCode, null, $productData['product'][$attributeCode]] + ]); + + $this->productAttributeMock->expects($this->atLeastOnce()) + ->method('getDefaultFrontendLabel') + ->willReturn('Test Attribute Label'); + + $this->messageManagerMock->expects($this->once()) + ->method('addErrorMessage'); + $this->messageManagerMock->expects($this->atLeastOnce()) + ->method('addSuccessMessage'); + + $this->action->execute(); + } } From d91acbe552375ce794e97894644afe7851ca201f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20F=C3=BChr?= <d.fuehr@techdivision.com> Date: Fri, 8 Mar 2019 23:20:21 +0100 Subject: [PATCH 1010/1295] 18752: make root cause visible for exception on submitQuote Github Issue: https://github.com/magento/magento2/issues/18752 If an exceptions happens in orderManagement->place($order) or an "sales_model_service_quote_submit_success" observer, the catch block itself fires an event that currently fails for guest checkouts in Magento\CatalogInventory\Model\ResourceModel\Stock->correctItemsQty(). This second exception hides the root exception and is logged to the exception log with the message "Rolled back transaction has not been completed correctly". This is not bound for this observer, but may occur in every other observer that is currently register or may be registered in the future. Therefore the failure event is wrapped in a try-catch itself and throws a combined exception that is logged in the exception.log. --- .../Magento/Quote/Model/QuoteManagement.php | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Quote/Model/QuoteManagement.php b/app/code/Magento/Quote/Model/QuoteManagement.php index e21ae7fa1af37..085a0a74bf759 100644 --- a/app/code/Magento/Quote/Model/QuoteManagement.php +++ b/app/code/Magento/Quote/Model/QuoteManagement.php @@ -12,6 +12,7 @@ use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\StateException; +use Magento\Framework\Phrase; use Magento\Quote\Api\Data\PaymentInterface; use Magento\Quote\Model\Quote\Address\ToOrder as ToOrderConverter; use Magento\Quote\Model\Quote\Address\ToOrderAddress as ToOrderAddressConverter; @@ -529,19 +530,31 @@ protected function submitQuote(QuoteEntity $quote, $orderData = []) ); $this->quoteRepository->save($quote); } catch (\Exception $e) { - if (!empty($this->addressesToSync)) { - foreach ($this->addressesToSync as $addressId) { - $this->addressRepository->deleteById($addressId); + try { + if (!empty($this->addressesToSync)) { + foreach ($this->addressesToSync as $addressId) { + $this->addressRepository->deleteById($addressId); + } } + $this->eventManager->dispatch( + 'sales_model_service_quote_submit_failure', + [ + 'order' => $order, + 'quote' => $quote, + 'exception' => $e, + ] + ); + } catch (\Exception $consecutiveException) { + $message = new Phrase( + "An exception occurred on 'sales_model_service_quote_submit_failure' event: %1\n%2", + [ + $consecutiveException->getMessage(), + $consecutiveException->getTraceAsString() + ] + ); + + throw new LocalizedException($message, $e); } - $this->eventManager->dispatch( - 'sales_model_service_quote_submit_failure', - [ - 'order' => $order, - 'quote' => $quote, - 'exception' => $e - ] - ); throw $e; } return $order; From 91410778d09b45bd2582683b88ff6169ac7898eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20F=C3=BChr?= <d.fuehr@techdivision.com> Date: Mon, 11 Mar 2019 15:48:37 +0100 Subject: [PATCH 1011/1295] amend consecutive exception handling - support masking of root cause in frontend - remove stack trace for consecutive exception to comply to default behaviour --- app/code/Magento/Quote/Model/QuoteManagement.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Quote/Model/QuoteManagement.php b/app/code/Magento/Quote/Model/QuoteManagement.php index 085a0a74bf759..b959dddf550ba 100644 --- a/app/code/Magento/Quote/Model/QuoteManagement.php +++ b/app/code/Magento/Quote/Model/QuoteManagement.php @@ -12,7 +12,6 @@ use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\StateException; -use Magento\Framework\Phrase; use Magento\Quote\Api\Data\PaymentInterface; use Magento\Quote\Model\Quote\Address\ToOrder as ToOrderConverter; use Magento\Quote\Model\Quote\Address\ToOrderAddress as ToOrderAddressConverter; @@ -545,15 +544,12 @@ protected function submitQuote(QuoteEntity $quote, $orderData = []) ] ); } catch (\Exception $consecutiveException) { - $message = new Phrase( - "An exception occurred on 'sales_model_service_quote_submit_failure' event: %1\n%2", - [ - $consecutiveException->getMessage(), - $consecutiveException->getTraceAsString() - ] + $message = sprintf( + "An exception occurred on 'sales_model_service_quote_submit_failure' event: %s", + $consecutiveException->getMessage() ); - throw new LocalizedException($message, $e); + throw new \Exception($message, 0, $e); } throw $e; } From 0168b1fdda9ab5ae770fb69ee519948a0529fd18 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Mon, 25 Mar 2019 14:58:00 +0200 Subject: [PATCH 1012/1295] Fix static tests. --- .../Magento/Quote/Model/QuoteManagement.php | 60 ++++++++++++------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/app/code/Magento/Quote/Model/QuoteManagement.php b/app/code/Magento/Quote/Model/QuoteManagement.php index b959dddf550ba..1ebf637706dd3 100644 --- a/app/code/Magento/Quote/Model/QuoteManagement.php +++ b/app/code/Magento/Quote/Model/QuoteManagement.php @@ -529,28 +529,7 @@ protected function submitQuote(QuoteEntity $quote, $orderData = []) ); $this->quoteRepository->save($quote); } catch (\Exception $e) { - try { - if (!empty($this->addressesToSync)) { - foreach ($this->addressesToSync as $addressId) { - $this->addressRepository->deleteById($addressId); - } - } - $this->eventManager->dispatch( - 'sales_model_service_quote_submit_failure', - [ - 'order' => $order, - 'quote' => $quote, - 'exception' => $e, - ] - ); - } catch (\Exception $consecutiveException) { - $message = sprintf( - "An exception occurred on 'sales_model_service_quote_submit_failure' event: %s", - $consecutiveException->getMessage() - ); - - throw new \Exception($message, 0, $e); - } + $this->rollbackAddresses($quote, $order, $e); throw $e; } return $order; @@ -617,4 +596,41 @@ protected function _prepareCustomerQuote($quote) $shipping->setIsDefaultBilling(true); } } + + /** + * Remove related to order and quote addresses and submit exception to further processing. + * + * @param Quote $quote + * @param \Magento\Sales\Api\Data\OrderInterface $order + * @param \Exception $e + * @throws \Exception + */ + private function rollbackAddresses( + QuoteEntity $quote, + \Magento\Sales\Api\Data\OrderInterface $order, + \Exception $e + ): void { + try { + if (!empty($this->addressesToSync)) { + foreach ($this->addressesToSync as $addressId) { + $this->addressRepository->deleteById($addressId); + } + } + $this->eventManager->dispatch( + 'sales_model_service_quote_submit_failure', + [ + 'order' => $order, + 'quote' => $quote, + 'exception' => $e, + ] + ); + } catch (\Exception $consecutiveException) { + $message = sprintf( + "An exception occurred on 'sales_model_service_quote_submit_failure' event: %s", + $consecutiveException->getMessage() + ); + + throw new \Exception($message, 0, $e); + } + } } From bbcdc1b8e709613f30663a2f2de52780bd3751f8 Mon Sep 17 00:00:00 2001 From: Erfan <erfan@speqs.com> Date: Wed, 19 Dec 2018 16:36:15 +0800 Subject: [PATCH 1013/1295] Added custom_options file upload directory to .gitignore. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 75e5f11d8a8e7..73b2737d1f3dc 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,7 @@ atlassian* /pub/media/import/* !/pub/media/import/.htaccess /pub/media/logo/* +/pub/media/custom_options/* /pub/media/theme/* /pub/media/theme_customization/* !/pub/media/theme_customization/.htaccess From 16ede56b8acf14b8daa4423bccfab61f8406b9ae Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <d.cheshun@atwix.com> Date: Fri, 22 Mar 2019 13:40:26 +0200 Subject: [PATCH 1014/1295] Exclude htaccess form gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 73b2737d1f3dc..a79b7990a7576 100644 --- a/.gitignore +++ b/.gitignore @@ -50,6 +50,7 @@ atlassian* !/pub/media/import/.htaccess /pub/media/logo/* /pub/media/custom_options/* +!/pub/media/custom_options/.htaccess /pub/media/theme/* /pub/media/theme_customization/* !/pub/media/theme_customization/.htaccess From c0475579d203b73b2e61794d2ffd2d92dc07d699 Mon Sep 17 00:00:00 2001 From: Maksym Aposov <maposov@magento.com> Date: Fri, 29 Mar 2019 12:42:19 -0500 Subject: [PATCH 1015/1295] MAGETWO-98951: Merge release branch into 2.2-develop - integration test fix --- .../Magento/Checkout/Controller/Cart/Index/CouponPostTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/Index/CouponPostTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/Index/CouponPostTest.php index 50faf38fc8f4d..ec450d3f2fdda 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/Index/CouponPostTest.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/Index/CouponPostTest.php @@ -66,7 +66,7 @@ public function testAddingValidCoupon() ); $this->assertSessionMessages( - $this->equalTo(['You used coupon code "' . $couponCode . '".']), + $this->equalTo(['You used coupon code "' . $couponCode . '".']), \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS ); } From d6c12e5ea2ea391221231bbee8cf6d5094b793f6 Mon Sep 17 00:00:00 2001 From: vbmagento <vaibhavbhalerao.15@gmail.com> Date: Tue, 19 Mar 2019 10:58:24 +0000 Subject: [PATCH 1016/1295] Add argument to show filter text in URL rewrite grid after click on back button --- .../view/adminhtml/layout/adminhtml_url_rewrite_index.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/UrlRewrite/view/adminhtml/layout/adminhtml_url_rewrite_index.xml b/app/code/Magento/UrlRewrite/view/adminhtml/layout/adminhtml_url_rewrite_index.xml index 7d8151d270308..10f73829ada2c 100644 --- a/app/code/Magento/UrlRewrite/view/adminhtml/layout/adminhtml_url_rewrite_index.xml +++ b/app/code/Magento/UrlRewrite/view/adminhtml/layout/adminhtml_url_rewrite_index.xml @@ -14,6 +14,7 @@ <argument name="id" xsi:type="string">urlrewriteGrid</argument> <argument name="dataSource" xsi:type="object">Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection</argument> <argument name="default_sort" xsi:type="string">url_rewrite_id</argument> + <argument name="save_parameters_in_session" xsi:type="string">1</argument> </arguments> <block class="Magento\Backend\Block\Widget\Grid\ColumnSet" as="grid.columnSet" name="adminhtml.url_rewrite.grid.columnSet"> <arguments> From 646200a14003f75e8be34772b9826df4b395b993 Mon Sep 17 00:00:00 2001 From: vbmagento <vaibhavbhalerao.15@gmail.com> Date: Wed, 20 Mar 2019 09:57:01 +0000 Subject: [PATCH 1017/1295] Add comment in xml file --- .../view/adminhtml/layout/adminhtml_url_rewrite_index.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/UrlRewrite/view/adminhtml/layout/adminhtml_url_rewrite_index.xml b/app/code/Magento/UrlRewrite/view/adminhtml/layout/adminhtml_url_rewrite_index.xml index 10f73829ada2c..e32397921a032 100644 --- a/app/code/Magento/UrlRewrite/view/adminhtml/layout/adminhtml_url_rewrite_index.xml +++ b/app/code/Magento/UrlRewrite/view/adminhtml/layout/adminhtml_url_rewrite_index.xml @@ -14,6 +14,7 @@ <argument name="id" xsi:type="string">urlrewriteGrid</argument> <argument name="dataSource" xsi:type="object">Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection</argument> <argument name="default_sort" xsi:type="string">url_rewrite_id</argument> + <!-- Add below argument to save session parameter in URL rewrite grid --> <argument name="save_parameters_in_session" xsi:type="string">1</argument> </arguments> <block class="Magento\Backend\Block\Widget\Grid\ColumnSet" as="grid.columnSet" name="adminhtml.url_rewrite.grid.columnSet"> From 2a39dd308271ce1699cf64bd5f8c6c10605a0039 Mon Sep 17 00:00:00 2001 From: Vaibhav Bhalerao <vaibhavbhalerao.15@gmail.com> Date: Wed, 20 Mar 2019 18:40:37 +0530 Subject: [PATCH 1018/1295] Update adminhtml_url_rewrite_index.xml Used spaces instead of tabs --- .../view/adminhtml/layout/adminhtml_url_rewrite_index.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/UrlRewrite/view/adminhtml/layout/adminhtml_url_rewrite_index.xml b/app/code/Magento/UrlRewrite/view/adminhtml/layout/adminhtml_url_rewrite_index.xml index e32397921a032..bae22d0a10385 100644 --- a/app/code/Magento/UrlRewrite/view/adminhtml/layout/adminhtml_url_rewrite_index.xml +++ b/app/code/Magento/UrlRewrite/view/adminhtml/layout/adminhtml_url_rewrite_index.xml @@ -14,8 +14,8 @@ <argument name="id" xsi:type="string">urlrewriteGrid</argument> <argument name="dataSource" xsi:type="object">Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection</argument> <argument name="default_sort" xsi:type="string">url_rewrite_id</argument> - <!-- Add below argument to save session parameter in URL rewrite grid --> - <argument name="save_parameters_in_session" xsi:type="string">1</argument> + <!-- Add below argument to save session parameter in URL rewrite grid --> + <argument name="save_parameters_in_session" xsi:type="string">1</argument> </arguments> <block class="Magento\Backend\Block\Widget\Grid\ColumnSet" as="grid.columnSet" name="adminhtml.url_rewrite.grid.columnSet"> <arguments> From c126f399f0ffc1fe382a37cdf90791beb869bbd6 Mon Sep 17 00:00:00 2001 From: Vaibhav Bhalerao <vaibhavbhalerao.15@gmail.com> Date: Wed, 20 Mar 2019 19:15:19 +0530 Subject: [PATCH 1019/1295] Update adminhtml_url_rewrite_index.xml --- .../view/adminhtml/layout/adminhtml_url_rewrite_index.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/UrlRewrite/view/adminhtml/layout/adminhtml_url_rewrite_index.xml b/app/code/Magento/UrlRewrite/view/adminhtml/layout/adminhtml_url_rewrite_index.xml index bae22d0a10385..de8575178d06d 100644 --- a/app/code/Magento/UrlRewrite/view/adminhtml/layout/adminhtml_url_rewrite_index.xml +++ b/app/code/Magento/UrlRewrite/view/adminhtml/layout/adminhtml_url_rewrite_index.xml @@ -14,8 +14,8 @@ <argument name="id" xsi:type="string">urlrewriteGrid</argument> <argument name="dataSource" xsi:type="object">Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection</argument> <argument name="default_sort" xsi:type="string">url_rewrite_id</argument> - <!-- Add below argument to save session parameter in URL rewrite grid --> - <argument name="save_parameters_in_session" xsi:type="string">1</argument> + <!-- Add below argument to save session parameter in URL rewrite grid --> + <argument name="save_parameters_in_session" xsi:type="string">1</argument> </arguments> <block class="Magento\Backend\Block\Widget\Grid\ColumnSet" as="grid.columnSet" name="adminhtml.url_rewrite.grid.columnSet"> <arguments> From 3e37244e8d1c6c529cfa5132b848b01fab6faf91 Mon Sep 17 00:00:00 2001 From: Scott Buchanan <sbuchanan@ripen.com> Date: Tue, 26 Feb 2019 12:49:36 -0500 Subject: [PATCH 1020/1295] populate labels for street lines in checkout --- app/code/Magento/Checkout/Block/Checkout/AttributeMerger.php | 1 + .../Magento/blank/Magento_Customer/web/css/source/_module.less | 2 +- .../Magento/luma/Magento_Customer/web/css/source/_module.less | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Checkout/Block/Checkout/AttributeMerger.php b/app/code/Magento/Checkout/Block/Checkout/AttributeMerger.php index bf0884a8c83ea..5a01f524edeb1 100644 --- a/app/code/Magento/Checkout/Block/Checkout/AttributeMerger.php +++ b/app/code/Magento/Checkout/Block/Checkout/AttributeMerger.php @@ -274,6 +274,7 @@ protected function getMultilineFieldConfig($attributeCode, array $attributeConfi for ($lineIndex = 0; $lineIndex < (int)$attributeConfig['size']; $lineIndex++) { $isFirstLine = $lineIndex === 0; $line = [ + 'label' => __("%1: Line %2", $attributeConfig['label'], $lineIndex + 1), 'component' => 'Magento_Ui/js/form/element/abstract', 'config' => [ // customScope is used to group elements within a single form e.g. they can be validated separately diff --git a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less index 0ebd722429480..2b6eda56331f3 100644 --- a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less @@ -165,7 +165,7 @@ // Checkout address (create shipping address) .field.street { - .field.additional { + .field { .label { &:extend(.abs-visually-hidden all); } diff --git a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less index 460f8e50d59a9..f283a11da3400 100755 --- a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less @@ -199,7 +199,7 @@ // Checkout address (create shipping address) .field.street { - .field.additional { + .field { .label { &:extend(.abs-visually-hidden all); } From aaae03f8e964c5b097d17b2c74e578ce33b26f80 Mon Sep 17 00:00:00 2001 From: Jeff Coleman <jeff@jeffcolemanwrites.com> Date: Sun, 31 Mar 2019 07:00:07 -0700 Subject: [PATCH 1021/1295] Setting initial downloadable item status to available if \Magento\Downloadable\Model\Link\Purchased\Item::XML_PATH_ORDER_ITEM_STATUS is set to 'Pending' (https://github.com/magento/magento2/issues/21753) --- .../Observer/SaveDownloadableOrderItemObserver.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/code/Magento/Downloadable/Observer/SaveDownloadableOrderItemObserver.php b/app/code/Magento/Downloadable/Observer/SaveDownloadableOrderItemObserver.php index 64305cfce9b08..ef763336a6515 100644 --- a/app/code/Magento/Downloadable/Observer/SaveDownloadableOrderItemObserver.php +++ b/app/code/Magento/Downloadable/Observer/SaveDownloadableOrderItemObserver.php @@ -92,6 +92,11 @@ public function execute(\Magento\Framework\Event\Observer $observer) if ($purchasedLink->getId()) { return $this; } + $orderItemStatusToEnable = $this->_scopeConfig->getValue( + \Magento\Downloadable\Model\Link\Purchased\Item::XML_PATH_ORDER_ITEM_STATUS, + ScopeInterface::SCOPE_STORE, + $orderItem->getOrder()->getStoreId() + ); if (!$product) { $product = $this->_createProductModel()->setStoreId( $orderItem->getOrder()->getStoreId() @@ -150,6 +155,8 @@ public function execute(\Magento\Framework\Event\Observer $observer) )->setNumberOfDownloadsBought( $numberOfDownloads )->setStatus( + 1 == $orderItemStatusToEnable ? + \Magento\Downloadable\Model\Link\Purchased\Item::LINK_STATUS_AVAILABLE : \Magento\Downloadable\Model\Link\Purchased\Item::LINK_STATUS_PENDING )->setCreatedAt( $orderItem->getCreatedAt() From e644ea7da8381ecc3db0e0d011eee9ef825858d1 Mon Sep 17 00:00:00 2001 From: Jeff Coleman <jeff@jeffcolemanwrites.com> Date: Sun, 31 Mar 2019 08:16:40 -0700 Subject: [PATCH 1022/1295] fix for coding style --- .../Observer/SaveDownloadableOrderItemObserver.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Downloadable/Observer/SaveDownloadableOrderItemObserver.php b/app/code/Magento/Downloadable/Observer/SaveDownloadableOrderItemObserver.php index ef763336a6515..f2a1e981c2357 100644 --- a/app/code/Magento/Downloadable/Observer/SaveDownloadableOrderItemObserver.php +++ b/app/code/Magento/Downloadable/Observer/SaveDownloadableOrderItemObserver.php @@ -92,7 +92,7 @@ public function execute(\Magento\Framework\Event\Observer $observer) if ($purchasedLink->getId()) { return $this; } - $orderItemStatusToEnable = $this->_scopeConfig->getValue( + $statusToEnable = $this->_scopeConfig->getValue( \Magento\Downloadable\Model\Link\Purchased\Item::XML_PATH_ORDER_ITEM_STATUS, ScopeInterface::SCOPE_STORE, $orderItem->getOrder()->getStoreId() @@ -155,7 +155,7 @@ public function execute(\Magento\Framework\Event\Observer $observer) )->setNumberOfDownloadsBought( $numberOfDownloads )->setStatus( - 1 == $orderItemStatusToEnable ? + 1 == $statusToEnable ? \Magento\Downloadable\Model\Link\Purchased\Item::LINK_STATUS_AVAILABLE : \Magento\Downloadable\Model\Link\Purchased\Item::LINK_STATUS_PENDING )->setCreatedAt( From 19cc7689797171853de564bfc98f74c4062385fd Mon Sep 17 00:00:00 2001 From: Jeff Coleman <jeff@jeffcolemanwrites.com> Date: Sun, 31 Mar 2019 15:27:46 -0700 Subject: [PATCH 1023/1295] renamed variable for clarity and used defined constant for comparison --- .../Observer/SaveDownloadableOrderItemObserver.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Downloadable/Observer/SaveDownloadableOrderItemObserver.php b/app/code/Magento/Downloadable/Observer/SaveDownloadableOrderItemObserver.php index f2a1e981c2357..caf4cd25b870f 100644 --- a/app/code/Magento/Downloadable/Observer/SaveDownloadableOrderItemObserver.php +++ b/app/code/Magento/Downloadable/Observer/SaveDownloadableOrderItemObserver.php @@ -92,7 +92,7 @@ public function execute(\Magento\Framework\Event\Observer $observer) if ($purchasedLink->getId()) { return $this; } - $statusToEnable = $this->_scopeConfig->getValue( + $orderStatusToEnableItem = $this->_scopeConfig->getValue( \Magento\Downloadable\Model\Link\Purchased\Item::XML_PATH_ORDER_ITEM_STATUS, ScopeInterface::SCOPE_STORE, $orderItem->getOrder()->getStoreId() @@ -155,7 +155,7 @@ public function execute(\Magento\Framework\Event\Observer $observer) )->setNumberOfDownloadsBought( $numberOfDownloads )->setStatus( - 1 == $statusToEnable ? + \Magento\Sales\Model\Order\Item::STATUS_PENDING == $orderStatusToEnableItem ? \Magento\Downloadable\Model\Link\Purchased\Item::LINK_STATUS_AVAILABLE : \Magento\Downloadable\Model\Link\Purchased\Item::LINK_STATUS_PENDING )->setCreatedAt( From 744986b6b081c2f1e562ec25988a22a5e5970767 Mon Sep 17 00:00:00 2001 From: Nazarn96 <nazarn96@gmail.com> Date: Mon, 1 Apr 2019 09:55:47 +0300 Subject: [PATCH 1024/1295] [backport] issue - 21507 --- lib/web/fotorama/fotorama.js | 12 ++++++++++-- lib/web/fotorama/fotorama.min.js | 5 +---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/web/fotorama/fotorama.js b/lib/web/fotorama/fotorama.js index 4062c501cb798..9c8c86a65b94d 100644 --- a/lib/web/fotorama/fotorama.js +++ b/lib/web/fotorama/fotorama.js @@ -1455,16 +1455,24 @@ fotoramaVersion = '4.6.4'; } } else { stopEvent(e); - (options.onMove || noop).call(el, e, {touch: touchFLAG}); + if (movedEnough(xDiff,yDiff)) { + (options.onMove || noop).call(el, e, {touch: touchFLAG}); + } } - if (!moved && Math.sqrt(Math.pow(xDiff, 2) + Math.pow(yDiff, 2)) > tolerance) { + if (!moved && movedEnough(xDiff, yDiff) && Math.sqrt(Math.pow(xDiff, 2) + Math.pow(yDiff, 2)) > tolerance) + { moved = true; } tail.checked = tail.checked || xWin || yWin; } + + function movedEnough(xDiff, yDiff) { + return xDiff > yDiff && xDiff > 1.5; + } + function onEnd(e) { (options.onTouchEnd || noop)(); diff --git a/lib/web/fotorama/fotorama.min.js b/lib/web/fotorama/fotorama.min.js index e8eb9fbda63ef..f416d57488925 100644 --- a/lib/web/fotorama/fotorama.min.js +++ b/lib/web/fotorama/fotorama.min.js @@ -1,4 +1 @@ -/*! - * Fotorama 4.6.4 | http://fotorama.io/license/ - */ -fotoramaVersion="4.6.4";(function(bo,k,a3,bV,aP){var ag="fotorama",bH="fotorama__fullscreen",ae=ag+"__wrap",ah=ae+"--css2",aX=ae+"--css3",bt=ae+"--video",ar=ae+"--fade",aw=ae+"--slide",P=ae+"--no-controls",aM=ae+"--no-shadows",U=ae+"--pan-y",a0=ae+"--rtl",az=ae+"--only-active",bN=ae+"--no-captions",f=ae+"--toggle-arrows",a7=ag+"__stage",x=a7+"__frame",l=x+"--video",B=a7+"__shaft",aB=ag+"__grab",bC=ag+"__pointer",aK=ag+"__arr",F=aK+"--disabled",bc=aK+"--prev",r=aK+"--next",bO=ag+"__nav",bq=bO+"-wrap",aH=bO+"__shaft",b=bq+"--vertical",ax=bq+"--list",bZ=bq+"--horizontal",bW=bO+"--dots",ai=bO+"--thumbs",aG=bO+"__frame",br=ag+"__fade",al=br+"-front",n=br+"-rear",aW=ag+"__shadow",bz=aW+"s",S=bz+"--left",aL=bz+"--right",a2=bz+"--top",aR=bz+"--bottom",a4=ag+"__active",a9=ag+"__select",bs=ag+"--hidden",M=ag+"--fullscreen",aJ=ag+"__fullscreen-icon",bP=ag+"__error",bM=ag+"__loading",c=ag+"__loaded",b3=c+"--full",bg=c+"--img",bR=ag+"__grabbing",J=ag+"__img",Y=J+"--full",bS=ag+"__thumb",b0=bS+"__arr--left",H=bS+"__arr--right",cb=bS+"-border",bd=ag+"__html",af=ag+"-video-container",bJ=ag+"__video",T=bJ+"-play",w=bJ+"-close",au=ag+"_horizontal_ratio",aY=ag+"_vertical_ratio",ca=ag+"__spinner",Z=ca+"--show";var E=bV&&bV.fn.jquery.split(".");if(!E||E[0]<1||(E[0]==1&&E[1]<8)){throw"Fotorama requires jQuery 1.8 or later and will not run without it."}var bx={};var ap=(function(co,ct,cj){var cf="2.8.3",cm={},cD=ct.documentElement,cE="modernizr",cB=ct.createElement(cE),cp=cB.style,cg,cw={}.toString,cy=" -webkit- -moz- -o- -ms- ".split(" "),cd="Webkit Moz O ms",cG=cd.split(" "),cq=cd.toLowerCase().split(" "),ck={},ce={},cu={},cA=[],cv=cA.slice,cc,cz=function(cQ,cS,cK,cR){var cJ,cP,cM,cN,cI=ct.createElement("div"),cO=ct.body,cL=cO||ct.createElement("body");if(parseInt(cK,10)){while(cK--){cM=ct.createElement("div");cM.id=cR?cR[cK]:cE+(cK+1);cI.appendChild(cM)}}cJ=["­",'<style id="s',cE,'">',cQ,"</style>"].join("");cI.id=cE;(cO?cI:cL).innerHTML+=cJ;cL.appendChild(cI);if(!cO){cL.style.background="";cL.style.overflow="hidden";cN=cD.style.overflow;cD.style.overflow="hidden";cD.appendChild(cL)}cP=cS(cI,cQ);if(!cO){cL.parentNode.removeChild(cL);cD.style.overflow=cN}else{cI.parentNode.removeChild(cI)}return !!cP},cs=({}).hasOwnProperty,cC;if(!cl(cs,"undefined")&&!cl(cs.call,"undefined")){cC=function(cI,cJ){return cs.call(cI,cJ)}}else{cC=function(cI,cJ){return((cJ in cI)&&cl(cI.constructor.prototype[cJ],"undefined"))}}if(!Function.prototype.bind){Function.prototype.bind=function cH(cK){var cL=this;if(typeof cL!="function"){throw new TypeError()}var cI=cv.call(arguments,1),cJ=function(){if(this instanceof cJ){var cO=function(){};cO.prototype=cL.prototype;var cN=new cO();var cM=cL.apply(cN,cI.concat(cv.call(arguments)));if(Object(cM)===cM){return cM}return cN}else{return cL.apply(cK,cI.concat(cv.call(arguments)))}};return cJ}}function cr(cI){cp.cssText=cI}function ci(cJ,cI){return cr(cy.join(cJ+";")+(cI||""))}function cl(cJ,cI){return typeof cJ===cI}function cn(cJ,cI){return !!~(""+cJ).indexOf(cI)}function cF(cK,cI){for(var cJ in cK){var cL=cK[cJ];if(!cn(cL,"-")&&cp[cL]!==cj){return cI=="pfx"?cL:true}}return false}function cx(cJ,cM,cL){for(var cI in cJ){var cK=cM[cJ[cI]];if(cK!==cj){if(cL===false){return cJ[cI]}if(cl(cK,"function")){return cK.bind(cL||cM)}return cK}}return false}function i(cM,cI,cL){var cJ=cM.charAt(0).toUpperCase()+cM.slice(1),cK=(cM+" "+cG.join(cJ+" ")+cJ).split(" ");if(cl(cI,"string")||cl(cI,"undefined")){return cF(cK,cI)}else{cK=(cM+" "+(cq).join(cJ+" ")+cJ).split(" ");return cx(cK,cI,cL)}}ck.touch=function(){var cI;if(("ontouchstart" in co)||co.DocumentTouch&&ct instanceof DocumentTouch){cI=true}else{cz(["@media (",cy.join("touch-enabled),("),cE,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(cJ){cI=cJ.offsetTop===9})}return cI};ck.csstransforms3d=function(){var cI=!!i("perspective");if(cI&&"webkitPerspective" in cD.style){cz("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(cJ,cK){cI=cJ.offsetLeft===9&&cJ.offsetHeight===3})}return cI};ck.csstransitions=function(){return i("transition")};for(var ch in ck){if(cC(ck,ch)){cc=ch.toLowerCase();cm[cc]=ck[ch]();cA.push((cm[cc]?"":"no-")+cc)}}cm.addTest=function(cJ,cK){if(typeof cJ=="object"){for(var cI in cJ){if(cC(cJ,cI)){cm.addTest(cI,cJ[cI])}}}else{cJ=cJ.toLowerCase();if(cm[cJ]!==cj){return cm}cK=typeof cK=="function"?cK():cK;if(typeof enableClasses!=="undefined"&&enableClasses){cD.className+=" "+(cK?"":"no-")+cJ}cm[cJ]=cK}return cm};cr("");cB=cg=null;cm._version=cf;cm._prefixes=cy;cm._domPrefixes=cq;cm._cssomPrefixes=cG;cm.testProp=function(cI){return cF([cI])};cm.testAllProps=i;cm.testStyles=cz;cm.prefixed=function(cK,cJ,cI){if(!cJ){return i(cK,"pfx")}else{return i(cK,cJ,cI)}};return cm})(bo,k);var bB={ok:false,is:function(){return false},request:function(){},cancel:function(){},event:"",prefix:""},h="webkit moz o ms khtml".split(" ");if(typeof k.cancelFullScreen!="undefined"){bB.ok=true}else{for(var bv=0,N=h.length;bv<N;bv++){bB.prefix=h[bv];if(typeof k[bB.prefix+"CancelFullScreen"]!="undefined"){bB.ok=true;break}}}if(bB.ok){bB.event=bB.prefix+"fullscreenchange";bB.is=function(){switch(this.prefix){case"":return k.fullScreen;case"webkit":return k.webkitIsFullScreen;default:return k[this.prefix+"FullScreen"]}};bB.request=function(i){return(this.prefix==="")?i.requestFullScreen():i[this.prefix+"RequestFullScreen"]()};bB.cancel=function(i){return(this.prefix==="")?k.cancelFullScreen():k[this.prefix+"CancelFullScreen"]()}}function a6(i){var cc="bez_"+bV.makeArray(arguments).join("_").replace(".","p");if(typeof bV.easing[cc]!=="function"){var cd=function(ck,ci){var cf=[null,null],cl=[null,null],cj=[null,null],ch=function(cm,cn){cj[cn]=3*ck[cn];cl[cn]=3*(ci[cn]-ck[cn])-cj[cn];cf[cn]=1-cj[cn]-cl[cn];return cm*(cj[cn]+cm*(cl[cn]+cm*cf[cn]))},cg=function(cm){return cj[0]+cm*(2*cl[0]+3*cf[0]*cm)},ce=function(co){var cm=co,cn=0,cp;while(++cn<14){cp=ch(cm,0)-co;if(Math.abs(cp)<0.001){break}cm-=cp/cg(cm)}return cm};return function(cm){return ch(ce(cm),1)}};bV.easing[cc]=function(cf,cg,ce,ci,ch){return ci*cd([i[0],i[1]],[i[2],i[3]])(cg/ch)+ce}}return cc}var bf=bV(bo),bw=bV(k),R,I,bT=a3.hash.replace("#","")==="quirks",ac=ap.csstransforms3d,aA=ac&&!bT,aN=ac||k.compatMode==="CSS1Compat",s=bB.ok,am=navigator.userAgent.match(/Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/i),bh=!aA||am,aZ=navigator.msPointerEnabled,y="onwheel" in k.createElement("div")?"wheel":k.onmousewheel!==aP?"mousewheel":"DOMMouseScroll",b8=250,ba=300,bY=1400,bQ=5000,b9=2,L=64,bk=500,bE=333,bu="$stageFrame",b7="$navDotFrame",bl="$navThumbFrame",bF="auto",u=a6([0.1,0,0.25,1]),bK=1200,b4=1,Q={width:null,minwidth:null,maxwidth:"100%",height:null,minheight:null,maxheight:null,ratio:null,margin:b9,nav:"dots",navposition:"bottom",navwidth:null,thumbwidth:L,thumbheight:L,thumbmargin:b9,thumbborderwidth:b9,allowfullscreen:false,transition:"slide",clicktransition:null,transitionduration:ba,captions:true,startindex:0,loop:false,autoplay:false,stopautoplayontouch:true,keyboard:false,arrows:true,click:true,swipe:false,trackpad:false,shuffle:false,direction:"ltr",shadows:true,showcaption:true,navdir:"horizontal",navarrows:true,navtype:"thumbs"},p={left:true,right:true,down:true,up:true,space:false,home:false,end:false};function g(){}function bb(cd,cc,i){return Math.max(isNaN(cc)?-Infinity:cc,Math.min(isNaN(i)?Infinity:i,cd))}function bi(cc,i){return cc.match(/ma/)&&cc.match(/-?\d+(?!d)/g)[cc.match(/3d/)?(i==="vertical"?13:12):(i==="vertical"?5:4)]}function aa(cc,i){if(aA){return +bi(cc.css("transform"),i)}else{return +cc.css(i==="vertical"?"top":"left").replace("px","")}}function b2(cd,cc){var i={};if(aA){switch(cc){case"vertical":i.transform="translate3d(0, "+(cd)+"px,0)";break;case"list":break;default:i.transform="translate3d("+(cd)+"px,0,0)";break}}else{cc==="vertical"?i.top=cd:i.left=cd}return i}function b6(i){return{"transition-duration":i+"ms"}}function aV(cc,i){return isNaN(cc)?i:cc}function m(cc,i){return aV(+String(cc).replace(i||"px",""))}function K(i){return/%$/.test(i)?m(i,"%"):aP}function d(cc,i){return aV(K(cc)/100*i,m(cc))}function t(i){return(!isNaN(m(i))||!isNaN(m(i,"%")))&&i}function a8(cc,cd,ce,i){return(cc-(i||0))*(cd+(ce||0))}function by(ce,cc,cd,i){return -Math.round(ce/(cc+(cd||0))-(i||0))}function aO(cd){var cc=cd.data();if(cc.tEnd){return}var ce=cd[0],i={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",msTransition:"MSTransitionEnd",transition:"transitionend"};D(ce,i[ap.prefixed("transition")],function(cf){cc.tProp&&cf.propertyName.match(cc.tProp)&&cc.onEndFn()});cc.tEnd=true}function X(cd,cf,ce,cg){var cc,i=cd.data();if(i){i.onEndFn=function(){if(cc){return}cc=true;clearTimeout(i.tT);ce()};i.tProp=cf;clearTimeout(i.tT);i.tT=setTimeout(function(){i.onEndFn()},cg*1.5);aO(cd)}}function a1(cd,cf){var cc=cd.navdir||"horizontal";if(cd.length){var i=cd.data();if(aA){cd.css(b6(0));i.onEndFn=g;clearTimeout(i.tT)}else{cd.stop()}var ce=bj(cf,function(){return aa(cd,cc)});cd.css(b2(ce,cc));return ce}}function bj(){var cc;for(var cd=0,i=arguments.length;cd<i;cd++){cc=cd?arguments[cd]():arguments[cd];if(typeof cc==="number"){break}}return cc}function aF(cc,i){return Math.round(cc+((i-cc)/1.5))}function aU(){aU.p=aU.p||(a3.protocol==="https:"?"https://":"http://");return aU.p}function ak(cc){var i=k.createElement("a");i.href=cc;return i}function at(i,cd){if(typeof i!=="string"){return i}i=ak(i);var cf,ce;if(i.host.match(/youtube\.com/)&&i.search){cf=i.search.split("v=")[1];if(cf){var cc=cf.indexOf("&");if(cc!==-1){cf=cf.substring(0,cc)}ce="youtube"}}else{if(i.host.match(/youtube\.com|youtu\.be/)){cf=i.pathname.replace(/^\/(embed\/|v\/)?/,"").replace(/\/.*/,"");ce="youtube"}else{if(i.host.match(/vimeo\.com/)){ce="vimeo";cf=i.pathname.replace(/^\/(video\/)?/,"").replace(/\/.*/,"")}}}if((!cf||!ce)&&cd){cf=i.href;ce="custom"}return cf?{id:cf,type:ce,s:i.search.replace(/^\?/,""),p:aU()}:false}function aQ(cg,ce,cf){var cc,i,cd=cg.video;if(cd.type==="youtube"){i=aU()+"img.youtube.com/vi/"+cd.id+"/default.jpg";cc=i.replace(/\/default.jpg$/,"/hqdefault.jpg");cg.thumbsReady=true}else{if(cd.type==="vimeo"){bV.ajax({url:aU()+"vimeo.com/api/v2/video/"+cd.id+".json",dataType:"jsonp",success:function(ch){cg.thumbsReady=true;v(ce,{img:ch[0].thumbnail_large,thumb:ch[0].thumbnail_small},cg.i,cf)}})}else{cg.thumbsReady=true}}return{img:cc,thumb:i}}function v(ch,cd,cf,ci){for(var cg=0,ce=ch.length;cg<ce;cg++){var cj=ch[cg];if(cj.i===cf&&cj.thumbsReady){var cc={videoReady:true};cc[bu]=cc[bl]=cc[b7]=false;ci.splice(cg,1,bV.extend({},cj,cc,cd));break}}}function bI(cc){var ce=[];function cd(cf,ch,cn){var ci=cf.children("img").eq(0),cm=cf.attr("href"),ck=cf.attr("src"),cl=ci.attr("src"),cj=ch.video,cg=cn?at(cm,cj===true):false;if(cg){cm=false}else{cg=cj}i(cf,ci,bV.extend(ch,{video:cg,img:ch.img||cm||ck||cl,thumb:ch.thumb||cl||ck||cm}))}function i(cg,cj,ck){var ci=ck.thumb&&ck.img!==ck.thumb,ch=m(ck.width||cg.attr("width")),cf=m(ck.height||cg.attr("height"));bV.extend(ck,{width:ch,height:cf,thumbratio:bm(ck.thumbratio||(m(ck.thumbwidth||(cj&&cj.attr("width"))||ci||ch)/m(ck.thumbheight||(cj&&cj.attr("height"))||ci||cf)))})}cc.children().each(function(){var cf=bV(this),cg=bA(bV.extend(cf.data(),{id:cf.attr("id")}));if(cf.is("a, img")){cd(cf,cg,true)}else{if(!cf.is(":empty")){i(cf,null,bV.extend(cg,{html:this,_html:cf.html()}))}else{return}}ce.push(cg)});return ce}function W(i){return i.offsetWidth===0&&i.offsetHeight===0}function be(i){return !bV.contains(k.documentElement,i)}function bX(cf,cd,ce,cc){if(!bX.i){bX.i=1;bX.ii=[true]}cc=cc||bX.i;if(typeof bX.ii[cc]==="undefined"){bX.ii[cc]=true}if(cf()){cd()}else{bX.ii[cc]&&setTimeout(function(){bX.ii[cc]&&bX(cf,cd,ce,cc)},ce||100)}return bX.i++}bX.stop=function(cc){bX.ii[cc]=false};function V(ce,cd){var cc=ce.data(),cg=cc.measures;if(cg&&(!cc.l||cc.l.W!==cg.width||cc.l.H!==cg.height||cc.l.r!==cg.ratio||cc.l.w!==cd.w||cc.l.h!==cd.h)){var i=bb(cd.h,0,cg.height),cf=i*cg.ratio;aS.setRatio(ce,cf,i);cc.l={W:cg.width,H:cg.height,r:cg.ratio,w:cd.w,h:cd.h}}return true}function an(i,cd){var cc=i[0];if(cc.styleSheet){cc.styleSheet.cssText=cd}else{i.html(cd)}}function bp(ce,cd,i,cc){return cd===i?false:cc==="vertical"?(ce<=cd?"top":ce>=i?"bottom":"top bottom"):(ce<=cd?"left":ce>=i?"right":"left right")}function z(cc,cd,i){i=i||{};cc.each(function(){var cg=bV(this),cf=cg.data(),ce;if(cf.clickOn){return}cf.clickOn=true;bV.extend(aI(cg,{onStart:function(ch){ce=ch;(i.onStart||g).call(this,ch)},onMove:i.onMove||g,onTouchEnd:i.onTouchEnd||g,onEnd:function(ch){if(ch.moved){return}cd.call(this,ce)}}),{noMove:true})})}function ab(i,cc){return'<div class="'+i+'">'+(cc||"")+"</div>"}function aT(i){return"."+i}function q(i){var cc='<iframe src="'+i.p+i.type+".com/embed/"+i.id+'" frameborder="0" allowfullscreen></iframe>';return cc}function aC(cf){var cc=cf.length;while(cc){var ce=Math.floor(Math.random()*cc--);var cd=cf[cc];cf[cc]=cf[ce];cf[ce]=cd}return cf}function bG(i){return Object.prototype.toString.call(i)=="[object Array]"&&bV.map(i,function(cc){return bV.extend({},cc)})}function bU(i,cd,cc){i.scrollLeft(cd||0).scrollTop(cc||0)}function bA(i){if(i){var cc={};bV.each(i,function(cd,ce){cc[cd.toLowerCase()]=ce});return cc}}function bm(i){if(!i){return}var cc=+i;if(!isNaN(cc)){return cc}else{cc=i.split("/");return +cc[0]/+cc[1]||aP}}function D(cd,ce,cc,i){if(!ce){return}cd.addEventListener?cd.addEventListener(ce,cc,!!i):cd.attachEvent("on"+ce,cc)}function a5(i,cc){if(i>cc.max){i=cc.max}else{if(i<cc.min){i=cc.min}}return i}function aD(i,ck,ch,cf,ce,cd,cc){var cg,cj,ci;if(cc==="horizontal"){cj=i.thumbwidth;ci=cd.width()}else{cj=i.thumbheight;ci=cd.height()}if((cj+i.margin)*(ch+1)>=(ci-cf)){if(cc==="horizontal"){cg=-ce.position().left}else{cg=-ce.position().top}}else{if((cj+i.margin)*(ch)<=Math.abs(cf)){if(cc==="horizontal"){cg=-ce.position().left+ci-(cj+i.margin)}else{cg=-ce.position().top+ci-(cj+i.margin)}}else{cg=cf}}cg=a5(cg,ck);return cg||0}function aj(i){return !!i.getAttribute("disabled")}function ad(cc,i){if(i){return{disabled:cc}}else{return{tabindex:cc*-1+"",disabled:cc}}}function a(cc,i){D(cc,"keyup",function(cd){aj(cc)||cd.keyCode==13&&i.call(cc,cd)})}function bL(cc,i){D(cc,"focus",cc.onfocusin=function(cd){i.call(cc,cd)},true)}function O(cc,i){cc.preventDefault?cc.preventDefault():(cc.returnValue=false);i&&cc.stopPropagation&&cc.stopPropagation()}function aE(cd,cc){var i=/iP(ad|hone|od)/i.test(bo.navigator.userAgent);if(i&&cc==="touchend"){cd.on("touchend",function(ce){bw.trigger("mouseup",ce)})}cd.on(cc,function(ce){O(ce,true);return false})}function ay(i){return i?">":"<"}var aS=(function(){function cd(ch,ce,cg){var cf=ce/cg;if(cf<=1){ch.parent().removeClass(au);ch.parent().addClass(aY)}else{ch.parent().removeClass(aY);ch.parent().addClass(au)}}function i(cf,cg,ch){var ce=ch;if(!cf.attr(ce)&&cf.attr(ce)!==aP){cf.attr(ce,cg)}if(cf.find("["+ce+"]").length){cf.find("["+ce+"]").each(function(){bV(this).attr(ce,cg)})}}function cc(cf,ce,ci){var cg=false,ch;cf.showCaption===ci||cf.showCaption===true?ch=true:ch=false;if(!ce){return false}if(cf.caption&&ch){cg=true}return cg}return{setRatio:cd,setThumbAttr:i,isExpectedCaption:cc}}(aS||{},jQuery));function A(ce,cd){var cc=ce.data(),i=Math.round(cd.pos),cf=function(){if(cc&&cc.sliding){cc.sliding=false}(cd.onEnd||g)()};if(typeof cd.overPos!=="undefined"&&cd.overPos!==cd.pos){i=cd.overPos}var cg=bV.extend(b2(i,cd.direction),cd.width&&{width:cd.width},cd.height&&{height:cd.height});if(cc&&cc.sliding){cc.sliding=true}if(aA){ce.css(bV.extend(b6(cd.time),cg));if(cd.time>10){X(ce,"transform",cf,cd.time)}else{cf()}}else{ce.stop().animate(cg,cd.time,u,cf)}}function aq(ck,cj,cc,cm,ce,i){var ch=typeof i!=="undefined";if(!ch){ce.push(arguments);Array.prototype.push.call(arguments,ce.length);if(ce.length>1){return}}ck=ck||bV(ck);cj=cj||bV(cj);var ci=ck[0],cg=cj[0],cf=cm.method==="crossfade",cl=function(){if(!cl.done){cl.done=true;var cn=(ch||ce.shift())&&ce.shift();cn&&aq.apply(this,cn);(cm.onEnd||g)(!!cn)}},cd=cm.time/(i||1);cc.removeClass(n+" "+al);ck.stop().addClass(n);cj.stop().addClass(al);cf&&cg&&ck.fadeTo(0,0);ck.fadeTo(cf?cd:0,1,cf&&cl);cj.fadeTo(cd,0,cl);(ci&&cf)||cg||cl()}var G,b5,e,j,bD;function bn(i){var cc=(i.touches||[])[0]||i;i._x=cc.pageX||cc.originalEvent.pageX;i._y=cc.clientY||cc.originalEvent.clientY;i._now=bV.now()}function aI(cr,cg){var cc=cr[0],cj={},i,cl,cf,cn,cs,cd,ce,co,ch;function cq(ct){cf=bV(ct.target);cj.checked=cd=ce=ch=false;if(i||cj.flow||(ct.touches&&ct.touches.length>1)||ct.which>1||(G&&G.type!==ct.type&&e)||(cd=cg.select&&cf.is(cg.select,cc))){return cd}cs=ct.type==="touchstart";ce=cf.is("a, a *",cc);cn=cj.control;co=(cj.noMove||cj.noSwipe||cn)?16:!cj.snap?4:0;bn(ct);cl=G=ct;b5=ct.type.replace(/down|start/,"move").replace(/Down/,"Move");(cg.onStart||g).call(cc,ct,{control:cn,$target:cf});i=cj.flow=true;if(!cs||cj.go){O(ct)}}function ck(cx){if((cx.touches&&cx.touches.length>1)||(aZ&&!cx.isPrimary)||b5!==cx.type||!i){i&&ci();(cg.onTouchEnd||g)();return}bn(cx);var cy=Math.abs(cx._x-cl._x),cu=Math.abs(cx._y-cl._y),cw=cy-cu,cv=(cj.go||cj.x||cw>=0)&&!cj.noSwipe,ct=cw<0;if(cs&&!cj.checked){if(i=cv){O(cx)}}else{O(cx);(cg.onMove||g).call(cc,cx,{touch:cs})}if(!ch&&Math.sqrt(Math.pow(cy,2)+Math.pow(cu,2))>co){ch=true}cj.checked=cj.checked||cv||ct}function ci(cu){(cg.onTouchEnd||g)();var ct=i;cj.control=i=false;if(ct){cj.flow=false}if(!ct||(ce&&!cj.checked)){return}cu&&O(cu);e=true;clearTimeout(j);j=setTimeout(function(){e=false},1000);(cg.onEnd||g).call(cc,{moved:ch,$target:cf,control:cn,touch:cs,startEvent:cl,aborted:!cu||cu.type==="MSPointerCancel"})}function cm(){if(cj.flow){return}cj.flow=true}function cp(){if(!cj.flow){return}cj.flow=false}if(aZ){D(cc,"MSPointerDown",cq);D(k,"MSPointerMove",ck);D(k,"MSPointerCancel",ci);D(k,"MSPointerUp",ci)}else{D(cc,"touchstart",cq);D(cc,"touchmove",ck);D(cc,"touchend",ci);D(k,"touchstart",cm);D(k,"touchend",cp);D(k,"touchcancel",cp);bf.on("scroll",cp);cr.on("mousedown pointerdown",cq);bw.on("mousemove pointermove",ck).on("mouseup pointerup",ci)}if(ap.touch){bD="a"}else{bD="div"}cr.on("click",bD,function(ct){cj.checked&&O(ct)});return cj}function ao(cz,cd){var cc=cz[0],ce=cz.data(),cm={},cw,cf,cx,cj,ch,cy,co,cg,cr,ct,cp,cq,i,cv,ci,cn;function cs(cA,cB){cn=true;cw=cf=(cq==="vertical")?cA._y:cA._x;co=cA._now;cy=[[co,cw]];cx=cj=cm.noMove||cB?0:a1(cz,(cd.getPos||g)());(cd.onStart||g).call(cc,cA)}function cu(cB,cA){cr=cm.min;ct=cm.max;cp=cm.snap,cq=cm.direction||"horizontal",cz.navdir=cq;i=cB.altKey;cn=ci=false;cv=cA.control;if(!cv&&!ce.sliding){cs(cB)}}function cl(cB,cA){if(!cm.noSwipe){if(!cn){cs(cB)}cf=(cq==="vertical")?cB._y:cB._x;cy.push([cB._now,cf]);cj=cx-(cw-cf);ch=bp(cj,cr,ct,cq);if(cj<=cr){cj=aF(cj,cr)}else{if(cj>=ct){cj=aF(cj,ct)}}if(!cm.noMove){cz.css(b2(cj,cq));if(!ci){ci=true;cA.touch||aZ||cz.addClass(bR)}(cd.onMove||g).call(cc,cB,{pos:cj,edge:ch})}}}function ck(cJ){if(cm.noSwipe&&cJ.moved){return}if(!cn){cs(cJ.startEvent,true)}cJ.touch||aZ||cz.removeClass(bR);cg=bV.now();var cG=cg-b8,cK,cP,cQ,cS=null,cA,cE,cN,cD,cF,cI=ba,cO,cH=cd.friction;for(var cC=cy.length-1;cC>=0;cC--){cK=cy[cC][0];cP=Math.abs(cK-cG);if(cS===null||cP<cQ){cS=cK;cA=cy[cC][1]}else{if(cS===cG||cP>cQ){break}}cQ=cP}cD=bb(cj,cr,ct);var cT=cA-cf,cR=cT>=0,cL=cg-cS,cB=cL>b8,cM=!cB&&cj!==cx&&cD===cj;if(cp){cD=bb(Math[cM?(cR?"floor":"ceil"):"round"](cj/cp)*cp,cr,ct);cr=ct=cD}if(cM&&(cp||cD===cj)){cO=-(cT/cL);cI*=bb(Math.abs(cO),cd.timeLow,cd.timeHigh);cE=Math.round(cj+cO*cI/cH);if(!cp){cD=cE}if(!cR&&cE>ct||cR&&cE<cr){cN=cR?cr:ct;cF=cE-cN;if(!cp){cD=cN}cF=bb(cD+cF*0.03,cN-50,cN+50);cI=Math.abs((cj-cF)/(cO/cH))}}cI*=i?10:1;(cd.onEnd||g).call(cc,bV.extend(cJ,{moved:cJ.moved||cB&&cp,pos:cj,newPos:cD,overPos:cF,time:cI,dir:cq}))}cm=bV.extend(aI(cd.$wrap,bV.extend({},cd,{onStart:cu,onMove:cl,onEnd:ck})),cm);return cm}function o(ce,cd){var cg=ce[0],ch,cf,i,cc={prevent:{}};D(cg,y,function(co){var cl=co.wheelDeltaY||-1*co.deltaY||0,cn=co.wheelDeltaX||-1*co.deltaX||0,ck=Math.abs(cn)&&!Math.abs(cl),cm=ay(cn<0),cp=cf===cm,ci=bV.now(),cj=ci-i<b8;cf=cm;i=ci;if(!ck||!cc.ok||cc.prevent[cm]&&!ch){return}else{O(co,true);if(ch&&cp&&cj){return}}if(cd.shift){ch=true;clearTimeout(cc.t);cc.t=setTimeout(function(){ch=false},bY)}(cd.onEnd||g)(co,cd.shift?cm:cn)});return cc}jQuery.Fotorama=function(d6,c3){R=bV("html");I=bV("body");var cg=this,cC=bV.now(),cQ=ag+cC,eu=d6[0],dP,cY=1,cR=d6.data(),c1,de=bV("<style></style>"),c9=bV(ab(bs)),dk=d6.find(aT(ae)),cf=dk.find(aT(a7)),dY=cf[0],cl=d6.find(aT(B)),c8=bV(),dW=d6.find(aT(bc)),da=d6.find(aT(r)),cU=d6.find(aT(aK)),dU=d6.find(aT(bq)),dO=dU.find(aT(bO)),cF=dO.find(aT(aH)),dA,cB=bV(),cW=bV(),dS=cl.data(),cX=cF.data(),c7=d6.find(aT(cb)),eg=d6.find(aT(b0)),dX=d6.find(aT(H)),dM=d6.find(aT(aJ)),dD=dM[0],cH=bV(ab(T)),dt=d6.find(aT(w)),d1=dt[0],eb=d6.find(aT(ca)),dg,eo=false,dF,ea,c2,ed,dw,d4,cN,cK,dx,dj,cq,c0,d8,c4,d2,cv,ch,ej,ds,cu,ec,dH,dE,d0={},en={},dG,d5={},cG={},dy={},ef={},cs,cT,ee,cj,el,cd={},er={},dZ,c6,dz,dr,d3=0,cI=[];dk[bu]=bV('<div class="'+x+'"></div>');dk[bl]=bV(bV.Fotorama.jst.thumb());dk[b7]=bV(bV.Fotorama.jst.dots());cd[bu]=[];cd[bl]=[];cd[b7]=[];er[bu]={};dk.addClass(aA?aX:ah);cR.fotorama=this;function ep(){bV.each(dP,function(ey,eA){if(!eA.i){eA.i=cY++;var ez=at(eA.video,true);if(ez){var ex={};eA.video=ez;if(!eA.img&&!eA.thumb){ex=aQ(eA,dP,cg)}else{eA.thumbsReady=true}v(dP,{img:ex.img,thumb:ex.thumb},eA.i,cg)}}})}function df(ex){return dE[ex]}function i(){if(cf!==aP){if(c3.navdir=="vertical"){var ex=c3.thumbwidth+c3.thumbmargin;cf.css("left",ex);da.css("right",ex);dM.css("right",ex);dk.css("width",dk.css("width")+ex);cl.css("max-width",dk.width()-ex)}else{cf.css("left","");da.css("right","");dM.css("right","");dk.css("width",dk.css("width")+ex);cl.css("max-width","")}}}function ek(eB){var eC="keydown."+ag,eD=ag+cC,ex="keydown."+eD,eA="keyup."+eD,ey="resize."+eD+" orientationchange."+eD,ez;if(eB){bw.on(ex,function(eG){var eF,eE;if(dg&&eG.keyCode===27){eF=true;cO(dg,true,true)}else{if(cg.fullScreen||(c3.keyboard&&!cg.index)){if(eG.keyCode===27){eF=true;cg.cancelFullScreen()}else{if((eG.shiftKey&&eG.keyCode===32&&df("space"))||(!eG.altKey&&!eG.metaKey&&eG.keyCode===37&&df("left"))||(eG.keyCode===38&&df("up")&&bV(":focus").attr("data-gallery-role"))){cg.longPress.progress();eE="<"}else{if((eG.keyCode===32&&df("space"))||(!eG.altKey&&!eG.metaKey&&eG.keyCode===39&&df("right"))||(eG.keyCode===40&&df("down")&&bV(":focus").attr("data-gallery-role"))){cg.longPress.progress();eE=">"}else{if(eG.keyCode===36&&df("home")){cg.longPress.progress();eE="<<"}else{if(eG.keyCode===35&&df("end")){cg.longPress.progress();eE=">>"}}}}}}}(eF||eE)&&O(eG);ez={index:eE,slow:eG.altKey,user:true};eE&&(cg.longPress.inProgress?cg.showWhileLongPress(ez):cg.show(ez))});if(eB){bw.on(eA,function(eE){if(cg.longPress.inProgress){cg.showEndLongPress({user:true})}cg.longPress.reset()})}if(!cg.index){bw.off(eC).on(eC,"textarea, input, select",function(eE){!I.hasClass(bH)&&eE.stopPropagation()})}bf.on(ey,cg.resize)}else{bw.off(ex);bf.off(ey)}}function dd(ex){if(ex===dd.f){return}if(ex){d6.addClass(ag+" "+cQ).before(c9).before(de);C(cg)}else{c9.detach();de.detach();d6.html(cR.urtext).removeClass(cQ);av(cg)}ek(ex);dd.f=ex}function dn(){dP=cg.data=dP||bG(c3.data)||bI(d6);c1=cg.size=dP.length;eq.ok&&c3.shuffle&&aC(dP);ep();eo=cn(eo);c1&&dd(true)}function em(){var ex=c1<2||dg;d5.noMove=ex||cv;d5.noSwipe=ex||!c3.swipe;!cu&&cl.toggleClass(aB,!c3.click&&!d5.noMove&&!d5.noSwipe);aZ&&dk.toggleClass(U,!d5.noSwipe)}function dq(ex){if(ex===true){ex=""}c3.autoplay=Math.max(+ex||bQ,ds*1.5)}function db(ex){if(ex.navarrows&&ex.nav==="thumbs"){eg.show();dX.show()}else{eg.hide();dX.hide()}}function ck(ex,ey){return Math.floor(dk.width()/(ey.thumbwidth+ey.thumbmargin))}function dQ(){if(!c3.nav||c3.nav==="dots"){c3.navdir="horizontal"}cg.options=c3=bA(c3);b4=ck(dk,c3);cv=(c3.transition==="crossfade"||c3.transition==="dissolve");dj=c3.loop&&(c1>2||(cv&&(!cu||cu!=="slide")));ds=+c3.transitionduration||ba;dH=c3.direction==="rtl";dE=bV.extend({},c3.keyboard&&p,c3.keyboard);db(c3);var ey={add:[],remove:[]};function ex(ez,eA){ey[ez?"add":"remove"].push(eA)}if(c1>1){cq=c3.nav;d8=c3.navposition==="top";ey.remove.push(a9);cU.toggle(!!c3.arrows)}else{cq=false;cU.hide()}dh();cJ();ev();if(c3.autoplay){dq(c3.autoplay)}ch=m(c3.thumbwidth)||L;ej=m(c3.thumbheight)||L;cG.ok=ef.ok=c3.trackpad&&!bh;em();dL(c3,[en]);c0=cq==="thumbs";if(dU.filter(":hidden")&&!!cq){dU.show()}if(c0){dl(c1,"navThumb");dA=cW;dr=bl;an(de,bV.Fotorama.jst.style({w:ch,h:ej,b:c3.thumbborderwidth,m:c3.thumbmargin,s:cC,q:!aN}));dO.addClass(ai).removeClass(bW)}else{if(cq==="dots"){dl(c1,"navDot");dA=cB;dr=b7;dO.addClass(bW).removeClass(ai)}else{dU.hide();cq=false;dO.removeClass(ai+" "+bW)}}if(cq){if(d8){dU.insertBefore(cf)}else{dU.insertAfter(cf)}cz.nav=false;cz(dA,cF,"nav")}c4=c3.allowfullscreen;if(c4){dM.prependTo(cf);d2=s&&c4==="native";aE(dM,"touchend")}else{dM.detach();d2=false}ex(cv,ar);ex(!cv,aw);ex(!c3.captions,bN);ex(dH,a0);ex(c3.arrows,f);ec=c3.shadows&&!bh;ex(!ec,aM);dk.addClass(ey.add.join(" ")).removeClass(ey.remove.join(" "));d0=bV.extend({},c3);i()}function cZ(ex){return ex<0?(c1+(ex%c1))%c1:ex>=c1?ex%c1:ex}function cn(ex){return bb(ex,0,c1-1)}function du(ex){return dj?cZ(ex):cn(ex)}function dB(ex){return ex>0||dj?ex-1:false}function ci(ex){return ex<c1-1||dj?ex+1:false}function d9(){d5.min=dj?-Infinity:-a8(c1-1,en.w,c3.margin,c2);d5.max=dj?Infinity:-a8(0,en.w,c3.margin,c2);d5.snap=en.w+c3.margin}function c5(){var ex=(c3.navdir==="vertical");var ez=ex?cF.height():cF.width();var ey=ex?en.h:en.nw;dy.min=Math.min(0,ey-ez);dy.max=0;dy.direction=c3.navdir;cF.toggleClass(aB,!(dy.noMove=dy.min===dy.max))}function dm(ey,eA,ez){if(typeof ey==="number"){ey=new Array(ey);var ex=true}return bV.each(ey,function(eD,eB){if(ex){eB=eD}if(typeof eB==="number"){var eF=dP[cZ(eB)];if(eF){var eC="$"+eA+"Frame",eE=eF[eC];ez.call(this,eD,eB,eF,eE,eC,eE&&eE.data())}}})}function cc(eA,ex,ez,ey){if(!dG||(dG==="*"&&ey===dx)){eA=t(c3.width)||t(eA)||bk;ex=t(c3.height)||t(ex)||bE;cg.resize({width:eA,ratio:c3.ratio||ez||eA/ex},0,ey!==dx&&"*")}}function cx(ex,ey,eA,ez){dm(ex,ey,function(eM,eE,eD,eC,eR,eB){if(!eC){return}var eN=cg.fullScreen&&!eB.$full&&ey==="stage";if(eB.$img&&!ez&&!eN){return}var eS=new Image(),eG=bV(eS),eO=eG.data();eB[eN?"$full":"$img"]=eG;var eJ=ey==="stage"?(eN?"full":"img"):"thumb",eF=eD[eJ],eP=eN?eD.img:eD[ey==="stage"?"thumb":"img"];if(ey==="navThumb"){eC=eB.$wrap}function eH(eT){var eU=cZ(eE);dc(eT,{index:eU,src:eF,frame:dP[eU]})}function eK(){eG.remove();bV.Fotorama.cache[eF]="error";if((!eD.html||ey!=="stage")&&eP&&eP!==eF){eD[eJ]=eF=eP;eB.$full=null;cx([eE],ey,eA,true)}else{if(eF&&!eD.html&&!eN){eC.trigger("f:error").removeClass(bM).addClass(bP);eH("error")}else{if(ey==="stage"){eC.trigger("f:load").removeClass(bM+" "+bP).addClass(c);eH("load");cc()}}eB.state="error";if(c1>1&&dP[eE]===eD&&!eD.html&&!eD.deleted&&!eD.video&&!eN){eD.deleted=true;cg.splice(eE,1)}}}function eL(){bV.Fotorama.measures[eF]=eO.measures=bV.Fotorama.measures[eF]||{width:eS.width,height:eS.height,ratio:eS.width/eS.height};cc(eO.measures.width,eO.measures.height,eO.measures.ratio,eE);eG.off("load error").addClass(""+(eN?Y:J)).attr("aria-hidden","false").prependTo(eC);if(eC.hasClass(x)&&!eC.hasClass(af)){eC.attr("href",eG.attr("src"))}V(eG,(bV.isFunction(eA)?eA():eA)||en);bV.Fotorama.cache[eF]=eB.state="loaded";setTimeout(function(){eC.trigger("f:load").removeClass(bM+" "+bP).addClass(c+" "+(eN?b3:bg));if(ey==="stage"){eH("load")}else{if(eD.thumbratio===bF||!eD.thumbratio&&c3.thumbratio===bF){eD.thumbratio=eO.measures.ratio;dV()}}},0)}if(!eF){eK();return}function eI(){var eT=10;bX(function(){return !c6||!eT--&&!bh},function(){eL()})}if(!bV.Fotorama.cache[eF]){bV.Fotorama.cache[eF]="*";eG.on("load",eI).on("error",eK)}else{(function eQ(){if(bV.Fotorama.cache[eF]==="error"){eK()}else{if(bV.Fotorama.cache[eF]==="loaded"){setTimeout(eI,0)}else{setTimeout(eQ,100)}}})()}eB.state="";eS.src=eF;if(eB.data.caption){eS.alt=eB.data.caption||""}if(eB.data.full){bV(eS).data("original",eB.data.full)}if(aS.isExpectedCaption(eD,c3.showcaption)){bV(eS).attr("aria-labelledby",eD.labelledby)}})}function cy(){var ex=dF[bu];if(ex&&!ex.data().state){eb.addClass(Z);ex.on("f:load f:error",function(){ex.off("f:load f:error");eb.removeClass(Z)})}}function cL(ex){a(ex,dJ);bL(ex,function(){setTimeout(function(){bU(dO)},0);dT({time:ds,guessIndex:bV(this).data().eq,minMax:dy})})}function dl(ex,ey){dm(ex,ey,function(eB,ez,eG,eD,eA,eC){if(eD){return}eD=eG[eA]=dk[eA].clone();eC=eD.data();eC.data=eG;var eF=eD[0],eE="labelledby"+bV.now();if(ey==="stage"){if(eG.html){bV('<div class="'+bd+'"></div>').append(eG._html?bV(eG.html).removeAttr("id").html(eG._html):eG.html).appendTo(eD)}if(eG.id){eE=eG.id||eE}eG.labelledby=eE;if(aS.isExpectedCaption(eG,c3.showcaption)){bV(bV.Fotorama.jst.frameCaption({caption:eG.caption,labelledby:eE})).appendTo(eD)}eG.video&&eD.addClass(l).append(cH.clone());bL(eF,function(){setTimeout(function(){bU(cf)},0);cm({index:eC.eq,user:true})});c8=c8.add(eD)}else{if(ey==="navDot"){cL(eF);cB=cB.add(eD)}else{if(ey==="navThumb"){cL(eF);eC.$wrap=eD.children(":first");cW=cW.add(eD);if(eG.video){eC.$wrap.append(cH.clone())}}}}})}function cM(ey,ex){return ey&&ey.length&&V(ey,ex)}function di(ex){dm(ex,"stage",function(eB,ez,eE,eD,eA,eC){if(!eD){return}var ey=cZ(ez);eC.eq=ey;er[bu][ey]=eD.css(bV.extend({left:cv?0:a8(ez,en.w,c3.margin,c2)},cv&&b6(0)));if(be(eD[0])){eD.appendTo(cl);cO(eE.$video)}cM(eC.$img,en);cM(eC.$full,en);if(eD.hasClass(x)&&!(eD.attr("aria-hidden")==="false"&&eD.hasClass(a4))){eD.attr("aria-hidden","true")}})}function dp(eB,ex){var ey,ez,eA;if(cq!=="thumbs"||isNaN(eB)){return}ey=-eB;ez=-eB+en.nw;if(c3.navdir==="vertical"){eB=eB-c3.thumbheight;ez=-eB+en.h}cW.each(function(){var eH=bV(this),eD=eH.data(),eC=eD.eq,eG=function(){return{h:ej,w:eD.w}},eF=eG(),eE=c3.navdir==="vertical"?eD.t>ez:eD.l>ez;eF.w=eD.w;if(eD.l+eD.w<ey||eE||cM(eD.$img,eF)){return}ex&&cx([eC],"navThumb",eG)})}function cz(ex,eC,ey){if(!cz[ey]){var eB=ey==="nav"&&c0,eA=0,ez=0;eC.append(ex.filter(function(){var eH,eG=bV(this),eE=eG.data();for(var eF=0,eD=dP.length;eF<eD;eF++){if(eE.data===dP[eF]){eH=true;eE.eq=eF;break}}return eH||eG.remove()&&false}).sort(function(eE,eD){return bV(eE).data().eq-bV(eD).data().eq}).each(function(){var eE=bV(this),eD=eE.data();aS.setThumbAttr(eE,eD.data.caption,"aria-label")}).each(function(){if(!eB){return}var eF=bV(this),eE=eF.data(),eG=Math.round(ej*eE.data.thumbratio)||ch,eD=Math.round(ch/eE.data.thumbratio)||ej;eE.t=ez;eE.h=eD;eE.l=eA;eE.w=eG;eF.css({width:eG});ez+=eD+c3.thumbmargin;eA+=eG+c3.thumbmargin}));cz[ey]=true}}function eh(ex){return ex-d3>en.w/3}function cE(ex){return !dj&&(!(eo+ex)||!(eo-c1+ex))&&!dg}function dh(){var ey=cE(0),ex=cE(1);dW.toggleClass(F,ey).attr(ad(ey,false));da.toggleClass(F,ex).attr(ad(ex,false))}function ev(){var ex=false,ey=false;if(c3.navtype==="thumbs"&&!c3.loop){(eo==0)?ex=true:ex=false;(eo==c3.data.length-1)?ey=true:ey=false}if(c3.navtype==="slides"){var ez=aa(cF,c3.navdir);ez>=dy.max?ex=true:ex=false;ez<=dy.min?ey=true:ey=false}eg.toggleClass(F,ex).attr(ad(ex,true));dX.toggleClass(F,ey).attr(ad(ey,true))}function cJ(){if(cG.ok){cG.prevent={"<":cE(0),">":cE(1)}}}function dI(eD){var eA=eD.data(),eC,eB,ez,ex;if(c0){eC=eA.l;eB=eA.t;ez=eA.w;ex=eA.h}else{eC=eD.position().left;ez=eD.width()}var ey={c:eC+ez/2,min:-eC+c3.thumbmargin*10,max:-eC+en.w-ez-c3.thumbmargin*10};var eE={c:eB+ex/2,min:-eB+c3.thumbmargin*10,max:-eB+en.h-ex-c3.thumbmargin*10};return c3.navdir==="vertical"?eE:ey}function d7(ey){var ex=dF[dr].data();A(c7,{time:ey*1.2,pos:(c3.navdir==="vertical"?ex.t:ex.l),width:ex.w,height:ex.h,direction:c3.navdir})}function dT(eH){var eB=dP[eH.guessIndex][dr],ez=c3.navtype;var eD,ex,eA,eG,eC,ey,eE,eF;if(eB){if(ez==="thumbs"){eD=dy.min!==dy.max;eA=eH.minMax||eD&&dI(dF[dr]);eG=eD&&(eH.keep&&dT.t?dT.l:bb((eH.coo||en.nw/2)-dI(eB).c,eA.min,eA.max));eC=eD&&(eH.keep&&dT.l?dT.l:bb((eH.coo||en.nw/2)-dI(eB).c,eA.min,eA.max));ey=(c3.navdir==="vertical"?eG:eC);eE=eD&&bb(ey,dy.min,dy.max)||0;ex=eH.time*1.1;A(cF,{time:ex,pos:eE,direction:c3.navdir,onEnd:function(){dp(eE,true);ev()}});co(dO,bp(eE,dy.min,dy.max,c3.navdir));dT.l=ey}else{eF=aa(cF,c3.navdir);ex=eH.time*1.11;eE=aD(c3,dy,eH.guessIndex,eF,eB,dU,c3.navdir);A(cF,{time:ex,pos:eE,direction:c3.navdir,onEnd:function(){dp(eE,true);ev()}});co(dO,bp(eE,dy.min,dy.max,c3.navdir))}}}function cS(){dN(dr);cd[dr].push(dF[dr].addClass(a4).attr("data-active",true))}function dN(ey){var ex=cd[ey];while(ex.length){ex.shift().removeClass(a4).attr("data-active",false)}}function ce(ey){var ex=er[ey];bV.each(ea,function(eA,ez){delete ex[cZ(ez)]});bV.each(ex,function(ez,eA){delete ex[ez];eA.detach()})}function dC(ey){c2=ed=eo;var ex=dF[bu];if(ex){dN(bu);cd[bu].push(ex.addClass(a4).attr("data-active",true));if(ex.hasClass(x)){ex.attr("aria-hidden","false")}ey||cg.showStage.onEnd(true);a1(cl,0,true);ce(bu);di(ea);d9();c5();a(cl[0],function(){if(!d6.hasClass(M)){cg.requestFullScreen();dM.focus()}})}}function dL(ey,ex){if(!ey){return}bV.each(ex,function(ez,eA){if(!eA){return}bV.extend(eA,{width:ey.width||eA.width,height:ey.height,minwidth:ey.minwidth,maxwidth:ey.maxwidth,minheight:ey.minheight,maxheight:ey.maxheight,ratio:bm(ey.ratio)})})}function dc(ey,ex){d6.trigger(ag+":"+ey,[cg,ex])}function dR(){clearTimeout(cr.t);c6=1;if(c3.stopautoplayontouch){cg.stopAutoplay()}else{cj=true}}function cr(){if(!c6){return}if(!c3.stopautoplayontouch){cw();es()}cr.t=setTimeout(function(){c6=0},ba+b8)}function cw(){cj=!!(dg||el)}function es(){clearTimeout(es.t);bX.stop(es.w);if(!c3.autoplay||cj){if(cg.autoplay){cg.autoplay=false;dc("stopautoplay")}return}if(!cg.autoplay){cg.autoplay=true;dc("startautoplay")}var ey=eo;var ex=dF[bu].data();es.w=bX(function(){return ex.state||ey!==eo},function(){es.t=setTimeout(function(){if(cj||ey!==eo){return}var ez=cK,eA=dP[ez][bu].data();es.w=bX(function(){return eA.state||ez!==cK},function(){if(cj||ez!==cK){return}cg.show(dj?ay(!dH):cK)})},c3.autoplay)})}cg.startAutoplay=function(ex){if(cg.autoplay){return this}cj=el=false;dq(ex||c3.autoplay);es();return this};cg.stopAutoplay=function(){if(cg.autoplay){cj=el=true;es()}return this};cg.showSlide=function(ez){var eA=aa(cF,c3.navdir),eC,eB=500*1.1,ey=c3.navdir==="horizontal"?c3.thumbwidth:c3.thumbheight,ex=function(){ev()};if(ez==="next"){eC=eA-(ey+c3.margin)*b4}if(ez==="prev"){eC=eA+(ey+c3.margin)*b4}eC=a5(eC,dy);dp(eC,true);A(cF,{time:eB,pos:eC,direction:c3.navdir,onEnd:ex})};cg.showWhileLongPress=function(eA){if(cg.longPress.singlePressInProgress){return}var ez=dK(eA);ew(ez);var eB=cA(eA)/50;var ey=dF;cg.activeFrame=dF=dP[eo];var ex=ey===dF&&!eA.user;cg.showNav(ex,eA,eB);return this};cg.showEndLongPress=function(eA){if(cg.longPress.singlePressInProgress){return}var ez=dK(eA);ew(ez);var eB=cA(eA)/50;var ey=dF;cg.activeFrame=dF=dP[eo];var ex=ey===dF&&!eA.user;cg.showStage(ex,eA,eB);ee=typeof dw!=="undefined"&&dw!==eo;dw=eo;return this};function dK(ey){var ex;if(typeof ey!=="object"){ex=ey;ey={}}else{ex=ey.index}ex=ex===">"?ed+1:ex==="<"?ed-1:ex==="<<"?0:ex===">>"?c1-1:ex;ex=isNaN(ex)?aP:ex;ex=typeof ex==="undefined"?eo||0:ex;return ex}function ew(ex){cg.activeIndex=eo=du(ex);d4=dB(eo);cN=ci(eo);cK=cZ(eo+(dH?-1:1));ea=[eo,d4,cN];ed=dj?ex:eo}function cA(ey){var ex=Math.abs(dw-ed),ez=bj(ey.time,function(){return Math.min(ds*(1+(ex-1)/12),ds*2)});if(ey.slow){ez*=10}return ez}cg.showStage=function(ey,eA,eD){cO(dg,dF.i!==dP[cZ(c2)].i);dl(ea,"stage");di(bh?[ed]:[ed,dB(ed),ci(ed)]);cD("go",true);ey||dc("show",{user:eA.user,time:eD});cj=true;var eC=eA.overPos;var ez=cg.showStage.onEnd=function(eE){if(ez.ok){return}ez.ok=true;eE||dC(true);if(!ey){dc("showend",{user:eA.user})}if(!eE&&cu&&cu!==c3.transition){cg.setOptions({transition:cu});cu=false;return}cy();cx(ea,"stage");cD("go",false);cJ();ei();cw();es();if(cg.fullScreen){dF[bu].find("."+Y).attr("aria-hidden",false);dF[bu].find("."+J).attr("aria-hidden",true)}else{dF[bu].find("."+Y).attr("aria-hidden",true);dF[bu].find("."+J).attr("aria-hidden",false)}};if(!cv){A(cl,{pos:-a8(ed,en.w,c3.margin,c2),overPos:eC,time:eD,onEnd:ez})}else{var ex=dF[bu],eB=dP[dw]&&eo!==dw?dP[dw][bu]:null;aq(ex,eB,c8,{time:eD,method:c3.transition,onEnd:ez},cI)}dh()};cg.showNav=function(ey,ez,eA){ev();if(cq){cS();var ex=cn(eo+bb(ed-dw,-1,1));dT({time:eA,coo:ex!==eo&&ez.coo,guessIndex:typeof ez.coo!=="undefined"?ex:eo,keep:ey});if(c0){d7(eA)}}};cg.show=function(eA){cg.longPress.singlePressInProgress=true;var ez=dK(eA);ew(ez);var eB=cA(eA);var ey=dF;cg.activeFrame=dF=dP[eo];var ex=ey===dF&&!eA.user;cg.showStage(ex,eA,eB);cg.showNav(ex,eA,eB);ee=typeof dw!=="undefined"&&dw!==eo;dw=eo;cg.longPress.singlePressInProgress=false;return this};cg.requestFullScreen=function(){if(c4&&!cg.fullScreen){var ex=bV((cg.activeFrame||{}).$stageFrame||{}).hasClass("fotorama-video-container");if(ex){return}cs=bf.scrollTop();cT=bf.scrollLeft();bU(bf);cD("x",true);dZ=bV.extend({},en);d6.addClass(M).appendTo(I.addClass(bH));R.addClass(bH);cO(dg,true,true);cg.fullScreen=true;if(d2){bB.request(eu)}cg.resize();cx(ea,"stage");cy();dc("fullscreenenter");if(!("ontouchstart" in bo)){dM.focus()}}return this};function cP(){if(cg.fullScreen){cg.fullScreen=false;if(s){bB.cancel(eu)}I.removeClass(bH);R.removeClass(bH);d6.removeClass(M).insertAfter(c9);en=bV.extend({},dZ);cO(dg,true,true);cD("x",false);cg.resize();cx(ea,"stage");bU(bf,cT,cs);dc("fullscreenexit")}}cg.cancelFullScreen=function(){if(d2&&bB.is()){bB.cancel(k)}else{cP()}return this};cg.toggleFullScreen=function(){return cg[(cg.fullScreen?"cancel":"request")+"FullScreen"]()};cg.resize=function(ez){if(!dP){return this}var eC=arguments[1]||0,ey=arguments[2];b4=ck(dk,c3);dL(!cg.fullScreen?bA(ez):{width:bV(bo).width(),maxwidth:null,minwidth:null,height:bV(bo).height(),maxheight:null,minheight:null},[en,ey||cg.fullScreen||c3]);var eB=en.width,ex=en.height,eA=en.ratio,eD=bf.height()-(cq?dO.height():0);if(t(eB)){dk.css({width:""});dk.css({height:""});cf.css({width:""});cf.css({height:""});cl.css({width:""});cl.css({height:""});dO.css({width:""});dO.css({height:""});dk.css({minWidth:en.minwidth||0,maxWidth:en.maxwidth||bK});if(cq==="dots"){dU.hide()}eB=en.W=en.w=dk.width();en.nw=cq&&d(c3.navwidth,eB)||eB;cl.css({width:en.w,marginLeft:(en.W-en.w)/2});ex=d(ex,eD);ex=ex||(eA&&eB/eA);if(ex){eB=Math.round(eB);ex=en.h=Math.round(bb(ex,d(en.minheight,eD),d(en.maxheight,eD)));cf.css({width:eB,height:ex});if(c3.navdir==="vertical"&&!cg.fullscreen){dO.width(c3.thumbwidth+c3.thumbmargin*2)}if(c3.navdir==="horizontal"&&!cg.fullscreen){dO.height(c3.thumbheight+c3.thumbmargin*2)}if(cq==="dots"){dO.width(eB).height("auto");dU.show()}if(c3.navdir==="vertical"&&cg.fullScreen){cf.css("height",bf.height())}if(c3.navdir==="horizontal"&&cg.fullScreen){cf.css("height",bf.height()-dO.height())}if(cq){switch(c3.navdir){case"vertical":dU.removeClass(bZ);dU.removeClass(ax);dU.addClass(b);dO.stop().animate({height:en.h,width:c3.thumbwidth},eC);break;case"list":dU.removeClass(b);dU.removeClass(bZ);dU.addClass(ax);break;default:dU.removeClass(b);dU.removeClass(ax);dU.addClass(bZ);dO.stop().animate({width:en.nw},eC);break}dC();dT({guessIndex:eo,time:eC,keep:true});if(c0&&cz.nav){d7(eC)}}dG=ey||true;eq.ok=true;eq()}}d3=cf.offset().left;i();return this};cg.setOptions=function(ex){bV.extend(c3,ex);dV();return this};cg.shuffle=function(){dP&&aC(dP)&&dV();return this};function co(ex,ey){if(ec){ex.removeClass(S+" "+aL);ex.removeClass(a2+" "+aR);ey&&!dg&&ex.addClass(ey.replace(/^|\s/g," "+bz+"--"))}}cg.longPress={threshold:1,count:0,thumbSlideTime:20,progress:function(){if(!this.inProgress){this.count++;this.inProgress=this.count>this.threshold}},end:function(){if(this.inProgress){this.isEnded=true}},reset:function(){this.count=0;this.inProgress=false;this.isEnded=false}};cg.destroy=function(){cg.cancelFullScreen();cg.stopAutoplay();dP=cg.data=null;dd();ea=[];ce(bu);dV.ok=false;return this};cg.playVideo=function(){var ez=dF,ex=ez.video,ey=eo;if(typeof ex==="object"&&ez.videoReady){d2&&cg.fullScreen&&cg.cancelFullScreen();bX(function(){return !bB.is()||ey!==eo},function(){if(ey===eo){ez.$video=ez.$video||bV(ab(bJ)).append(q(ex));ez.$video.appendTo(ez[bu]);dk.addClass(bt);dg=ez.$video;em();cU.blur();dM.blur();dc("loadvideo")}})}return this};cg.stopVideo=function(){cO(dg,true,true);return this};cg.spliceByIndex=function(ex,ey){ey.i=ex+1;ey.img&&bV.ajax({url:ey.img,type:"HEAD",success:function(){dP.splice(ex,1,ey);dV()}})};function cO(ex,ez,ey){if(ez){dk.removeClass(bt);dg=false;em()}if(ex&&ex!==dg){ex.remove();dc("unloadvideo")}if(ey){cw();es()}}function cp(ex){dk.toggleClass(P,ex)}function ei(ez){if(d5.flow){return}var ex=ez?ez.pageX:ei.x,ey=ex&&!cE(eh(ex))&&c3.click;if(ei.p!==ey&&cf.toggleClass(bC,ey)){ei.p=ey;ei.x=ex}}cf.on("mousemove",ei);function cm(ex){clearTimeout(cm.t);if(c3.clicktransition&&c3.clicktransition!==c3.transition){setTimeout(function(){var ey=c3.transition;cg.setOptions({transition:c3.clicktransition});cu=ey;cm.t=setTimeout(function(){cg.show(ex)},10)},0)}else{cg.show(ex)}}function ct(eA,ey){var ez=eA.target,ex=bV(ez);if(ex.hasClass(T)){cg.playVideo()}else{if(ez===dD){cg.toggleFullScreen()}else{if(dg){ez===d1&&cO(dg,true,true)}else{if(!d6.hasClass(M)){cg.requestFullScreen()}}}}O(eA,true)}function cD(ex,ey){d5[ex]=dy[ex]=ey}d5=ao(cl,{onStart:dR,onMove:function(ey,ex){co(cf,ex.edge)},onTouchEnd:cr,onEnd:function(ex){var ez;co(cf);ez=(aZ&&!dz||ex.touch)&&c3.arrows;if((ex.moved||(ez&&ex.pos!==ex.newPos&&!ex.control))&&ex.$target[0]!==dM[0]){var ey=by(ex.newPos,en.w,c3.margin,c2);cg.show({index:ey,time:cv?ds:ex.time,overPos:ex.overPos,user:true})}else{if(!ex.aborted&&!ex.control){ct(ex.startEvent,ez)}}},timeLow:1,timeHigh:1,friction:2,select:"."+a9+", ."+a9+" *",$wrap:cf,direction:"horizontal"});dy=ao(cF,{onStart:dR,onMove:function(ey,ex){co(dO,ex.edge)},onTouchEnd:cr,onEnd:function(ex){function ey(){dT.l=ex.newPos;cw();es();dp(ex.newPos,true);ev()}if(!ex.moved){var ez=ex.$target.closest("."+aG,cF)[0];ez&&dJ.call(ez,ex.startEvent)}else{if(ex.pos!==ex.newPos){cj=true;A(cF,{time:ex.time,pos:ex.newPos,overPos:ex.overPos,direction:c3.navdir,onEnd:ey});dp(ex.newPos);ec&&co(dO,bp(ex.newPos,dy.min,dy.max,ex.dir))}else{ey()}}},timeLow:0.5,timeHigh:2,friction:5,$wrap:dO,direction:c3.navdir});cG=o(cf,{shift:true,onEnd:function(ey,ex){dR();cr();cg.show({index:ex,slow:ey.altKey})}});ef=o(dO,{onEnd:function(ez,ey){dR();cr();var ex=a1(cF)+ey*0.25;cF.css(b2(bb(ex,dy.min,dy.max),c3.navdir));ec&&co(dO,bp(ex,dy.min,dy.max,c3.navdir));ef.prevent={"<":ex>=dy.max,">":ex<=dy.min};clearTimeout(ef.t);ef.t=setTimeout(function(){dT.l=ex;dp(ex,true)},b8);dp(ex)}});dk.hover(function(){setTimeout(function(){if(c6){return}cp(!(dz=true))},0)},function(){if(!dz){return}cp(!(dz=false))});function dJ(ey){var ex=bV(this).data().eq;if(c3.navtype==="thumbs"){cm({index:ex,slow:ey.altKey,user:true,coo:ey._x-dO.offset().left})}else{cm({index:ex,slow:ey.altKey,user:true})}}function et(ex){cm({index:cU.index(this)?">":"<",slow:ex.altKey,user:true})}z(cU,function(ex){O(ex);et.call(this,ex)},{onStart:function(){dR();d5.control=true},onTouchEnd:cr});z(eg,function(ex){O(ex);if(c3.navtype==="thumbs"){cg.show("<")}else{cg.showSlide("prev")}});z(dX,function(ex){O(ex);if(c3.navtype==="thumbs"){cg.show(">")}else{cg.showSlide("next")}});function dv(ex){bL(ex,function(){setTimeout(function(){bU(cf)},0);cp(false)})}cU.each(function(){a(this,function(ex){et.call(this,ex)});dv(this)});a(dD,function(){if(d6.hasClass(M)){cg.cancelFullScreen();cl.focus()}else{cg.requestFullScreen();dM.focus()}});dv(dD);function dV(){dn();dQ();if(!dV.i){dV.i=true;var ex=c3.startindex;eo=c2=ed=dw=dx=du(ex)||0}if(c1){if(cV()){return}if(dg){cO(dg,true)}ea=[];ce(bu);dV.ok=true;cg.show({index:eo,time:0});cg.resize()}else{cg.destroy()}}function cV(){if(!cV.f===dH){cV.f=dH;eo=c1-1-eo;cg.reverse();return true}}bV.each("load push pop shift unshift reverse sort splice".split(" "),function(ex,ey){cg[ey]=function(){dP=dP||[];if(ey!=="load"){Array.prototype[ey].apply(dP,arguments)}else{if(arguments[0]&&typeof arguments[0]==="object"&&arguments[0].length){dP=bG(arguments[0])}}dV();return cg}});function eq(){if(eq.ok){eq.ok=false;dc("ready")}}dV()};bV.fn.fotorama=function(i){return this.each(function(){var ce=this,cd=bV(this),cc=cd.data(),cf=cc.fotorama;if(!cf){bX(function(){return !W(ce)},function(){cc.urtext=cd.html();new bV.Fotorama(cd,bV.extend({},Q,bo.fotoramaDefaults,i,cc))})}else{cf.setOptions(i,true)}})};bV.Fotorama.instances=[];function b1(){bV.each(bV.Fotorama.instances,function(cc,i){i.index=cc})}function C(i){bV.Fotorama.instances.push(i);b1()}function av(i){bV.Fotorama.instances.splice(i.index,1);b1()}bV.Fotorama.cache={};bV.Fotorama.measures={};bV=bV||{};bV.Fotorama=bV.Fotorama||{};bV.Fotorama.jst=bV.Fotorama.jst||{};bV.Fotorama.jst.dots=function(cc){var i,ce="",cd=bx.escape;ce+='<div class="fotorama__nav__frame fotorama__nav__frame--dot" tabindex="0" role="button" data-gallery-role="nav-frame" data-nav-type="thumb" aria-label>\r\n <div class="fotorama__dot"></div>\r\n</div>';return ce};bV.Fotorama.jst.frameCaption=function(cc){var i,ce="",cd=bx.escape;ce+='<div class="fotorama__caption" aria-hidden="true">\r\n <div class="fotorama__caption__wrap" id="'+((i=(cc.labelledby))==null?"":i)+'">'+((i=(cc.caption))==null?"":i)+"</div>\r\n</div>\r\n";return ce};bV.Fotorama.jst.style=function(cc){var i,ce="",cd=bx.escape;ce+=".fotorama"+((i=(cc.s))==null?"":i)+" .fotorama__nav--thumbs .fotorama__nav__frame{\r\npadding:"+((i=(cc.m))==null?"":i)+"px;\r\nheight:"+((i=(cc.h))==null?"":i)+"px}\r\n.fotorama"+((i=(cc.s))==null?"":i)+" .fotorama__thumb-border{\r\nheight:"+((i=(cc.h))==null?"":i)+"px;\r\nborder-width:"+((i=(cc.b))==null?"":i)+"px;\r\nmargin-top:"+((i=(cc.m))==null?"":i)+"px}";return ce};bV.Fotorama.jst.thumb=function(cc){var i,ce="",cd=bx.escape;ce+='<div class="fotorama__nav__frame fotorama__nav__frame--thumb" tabindex="0" role="button" data-gallery-role="nav-frame" data-nav-type="thumb" aria-label>\r\n <div class="fotorama__thumb">\r\n </div>\r\n</div>';return ce}})(window,document,location,typeof jQuery!=="undefined"&&jQuery); +fotoramaVersion="4.6.4",function(t,e,n,o,a){"use strict";var i="fotorama",r="fotorama__fullscreen",s=i+"__wrap",u=s+"--css2",l=s+"--css3",c=s+"--video",d=s+"--fade",h=s+"--slide",f=s+"--no-controls",m=s+"--no-shadows",v=s+"--pan-y",p=s+"--rtl",g=s+"--no-captions",w=s+"--toggle-arrows",b=i+"__stage",y=b+"__frame",x=y+"--video",_=b+"__shaft",C=i+"__grab",k=i+"__pointer",P=i+"__arr",S=P+"--disabled",T=P+"--prev",F=P+"--next",E=i+"__nav",M=E+"-wrap",j=E+"__shaft",$=M+"--vertical",z=M+"--list",q=M+"--horizontal",N=E+"--dots",A=E+"--thumbs",L=E+"__frame",O=i+"__fade",D=O+"-front",I=O+"-rear",W=i+"__shadow"+"s",R=W+"--left",H=W+"--right",K=W+"--top",Q=W+"--bottom",V=i+"__active",X=i+"__select",B=i+"--hidden",Y=i+"--fullscreen",U=i+"__fullscreen-icon",G=i+"__error",J=i+"__loading",Z=i+"__loaded",tt=Z+"--full",et=Z+"--img",nt=i+"__grabbing",ot=i+"__img",at=ot+"--full",it=i+"__thumb",rt=it+"__arr--left",st=it+"__arr--right",ut=it+"-border",lt=i+"__html",ct=i+"-video-container",dt=i+"__video",ht=dt+"-play",ft=dt+"-close",mt=i+"_horizontal_ratio",vt=i+"_vertical_ratio",pt=i+"__spinner",gt=pt+"--show",wt=o&&o.fn.jquery.split(".");if(!wt||wt[0]<1||1==wt[0]&&wt[1]<8)throw"Fotorama requires jQuery 1.8 or later and will not run without it.";var bt=function(t,e,n){var o,a,i={},r=e.documentElement,s="modernizr",u=e.createElement(s),l=u.style,c=" -webkit- -moz- -o- -ms- ".split(" "),d="Webkit Moz O ms",h=d.split(" "),f=d.toLowerCase().split(" "),m={},v=[],p=v.slice,g=function(t,n,o,a){var i,u,l,c,d=e.createElement("div"),h=e.body,f=h||e.createElement("body");if(parseInt(o,10))for(;o--;)(l=e.createElement("div")).id=a?a[o]:s+(o+1),d.appendChild(l);return i=["­",'<style id="s',s,'">',t,"</style>"].join(""),d.id=s,(h?d:f).innerHTML+=i,f.appendChild(d),h||(f.style.background="",f.style.overflow="hidden",c=r.style.overflow,r.style.overflow="hidden",r.appendChild(f)),u=n(d,t),h?d.parentNode.removeChild(d):(f.parentNode.removeChild(f),r.style.overflow=c),!!u},w={}.hasOwnProperty;function b(t){l.cssText=t}function y(t,e){return typeof t===e}function x(t,e){for(var o in t){var a=t[o];if(!~(""+a).indexOf("-")&&l[a]!==n)return"pfx"!=e||a}return!1}function _(t,e,o){var a=t.charAt(0).toUpperCase()+t.slice(1),i=(t+" "+h.join(a+" ")+a).split(" ");return y(e,"string")||y(e,"undefined")?x(i,e):function(t,e,o){for(var a in t){var i=e[t[a]];if(i!==n)return!1===o?t[a]:y(i,"function")?i.bind(o||e):i}return!1}(i=(t+" "+f.join(a+" ")+a).split(" "),e,o)}for(var C in a=y(w,"undefined")||y(w.call,"undefined")?function(t,e){return e in t&&y(t.constructor.prototype[e],"undefined")}:function(t,e){return w.call(t,e)},Function.prototype.bind||(Function.prototype.bind=function(t){var e=this;if("function"!=typeof e)throw new TypeError;var n=p.call(arguments,1),o=function(){if(this instanceof o){var a=function(){};a.prototype=e.prototype;var i=new a,r=e.apply(i,n.concat(p.call(arguments)));return Object(r)===r?r:i}return e.apply(t,n.concat(p.call(arguments)))};return o}),m.touch=function(){var n;return"ontouchstart"in t||t.DocumentTouch&&e instanceof DocumentTouch?n=!0:g(["@media (",c.join("touch-enabled),("),s,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(t){n=9===t.offsetTop}),n},m.csstransforms3d=function(){var t=!!_("perspective");return t&&"webkitPerspective"in r.style&&g("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(e,n){t=9===e.offsetLeft&&3===e.offsetHeight}),t},m.csstransitions=function(){return _("transition")},m)a(m,C)&&(o=C.toLowerCase(),i[o]=m[C](),v.push((i[o]?"":"no-")+o));return i.addTest=function(t,e){if("object"==typeof t)for(var o in t)a(t,o)&&i.addTest(o,t[o]);else{if(t=t.toLowerCase(),i[t]!==n)return i;e="function"==typeof e?e():e,"undefined"!=typeof enableClasses&&enableClasses&&(r.className+=" "+(e?"":"no-")+t),i[t]=e}return i},b(""),u=null,i._version="2.8.3",i._prefixes=c,i._domPrefixes=f,i._cssomPrefixes=h,i.testProp=function(t){return x([t])},i.testAllProps=_,i.testStyles=g,i.prefixed=function(t,e,n){return e?_(t,e,n):_(t,"pfx")},i}(t,e),yt={ok:!1,is:function(){return!1},request:function(){},cancel:function(){},event:"",prefix:""},xt="webkit moz o ms khtml".split(" ");if(void 0!==e.cancelFullScreen)yt.ok=!0;else for(var _t=0,Ct=xt.length;_t<Ct;_t++)if(yt.prefix=xt[_t],void 0!==e[yt.prefix+"CancelFullScreen"]){yt.ok=!0;break}yt.ok&&(yt.event=yt.prefix+"fullscreenchange",yt.is=function(){switch(this.prefix){case"":return e.fullScreen;case"webkit":return e.webkitIsFullScreen;default:return e[this.prefix+"FullScreen"]}},yt.request=function(t){return""===this.prefix?t.requestFullScreen():t[this.prefix+"RequestFullScreen"]()},yt.cancel=function(t){return""===this.prefix?e.cancelFullScreen():e[this.prefix+"CancelFullScreen"]()});var kt,Pt,St=o(t),Tt=o(e),Ft="quirks"===n.hash.replace("#",""),Et=bt.csstransforms3d,Mt=Et&&!Ft,jt=Et||"CSS1Compat"===e.compatMode,$t=yt.ok,zt=navigator.userAgent.match(/Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/i),qt=!Mt||zt,Nt=navigator.msPointerEnabled,At="onwheel"in e.createElement("div")?"wheel":e.onmousewheel!==a?"mousewheel":"DOMMouseScroll",Lt=250,Ot=300,Dt=1400,It=5e3,Wt=64,Rt=500,Ht=333,Kt="$stageFrame",Qt="$navDotFrame",Vt="$navThumbFrame",Xt="auto",Bt=function(t){var e="bez_"+o.makeArray(arguments).join("_").replace(".","p");if("function"!=typeof o.easing[e]){var n=function(t,e){var n=[null,null],o=[null,null],a=[null,null],i=function(i,r){return a[r]=3*t[r],o[r]=3*(e[r]-t[r])-a[r],n[r]=1-a[r]-o[r],i*(a[r]+i*(o[r]+i*n[r]))};return function(t){return i(function(t){for(var e,r,s=t,u=0;++u<14&&(e=i(s,0)-t,!(Math.abs(e)<.001));)s-=e/(r=s,a[0]+r*(2*o[0]+3*n[0]*r));return s}(t),1)}};o.easing[e]=function(e,o,a,i,r){return i*n([t[0],t[1]],[t[2],t[3]])(o/r)+a}}return e}([.1,0,.25,1]),Yt=1,Ut={width:null,minwidth:null,maxwidth:"100%",height:null,minheight:null,maxheight:null,ratio:null,margin:2,nav:"dots",navposition:"bottom",navwidth:null,thumbwidth:Wt,thumbheight:Wt,thumbmargin:2,thumbborderwidth:2,allowfullscreen:!1,transition:"slide",clicktransition:null,transitionduration:Ot,captions:!0,startindex:0,loop:!1,autoplay:!1,stopautoplayontouch:!0,keyboard:!1,arrows:!0,click:!0,swipe:!1,trackpad:!1,shuffle:!1,direction:"ltr",shadows:!0,showcaption:!0,navdir:"horizontal",navarrows:!0,navtype:"thumbs"},Gt={left:!0,right:!0,down:!0,up:!0,space:!1,home:!1,end:!1};function Jt(){}function Zt(t,e,n){return Math.max(isNaN(e)?-1/0:e,Math.min(isNaN(n)?1/0:n,t))}function te(t,e){return Mt?(n=t.css("transform"),o=e,+(n.match(/ma/)&&n.match(/-?\d+(?!d)/g)[n.match(/3d/)?"vertical"===o?13:12:"vertical"===o?5:4])):+t.css("vertical"===e?"top":"left").replace("px","");var n,o}function ee(t,e){var n={};if(Mt)switch(e){case"vertical":n.transform="translate3d(0, "+t+"px,0)";break;case"list":break;default:n.transform="translate3d("+t+"px,0,0)"}else"vertical"===e?n.top=t:n.left=t;return n}function ne(t){return{"transition-duration":t+"ms"}}function oe(t,e){return isNaN(t)?e:t}function ae(t,e){return oe(+String(t).replace(e||"px",""))}function ie(t,e){return oe((/%$/.test(n=t)?ae(n,"%"):a)/100*e,ae(t));var n}function re(t){return(!isNaN(ae(t))||!isNaN(ae(t,"%")))&&t}function se(t,e,n,o){return(t-(o||0))*(e+(n||0))}function ue(t,e,n,o){var a,i,r,s=t.data();s&&(s.onEndFn=function(){a||(a=!0,clearTimeout(s.tT),n())},s.tProp=e,clearTimeout(s.tT),s.tT=setTimeout(function(){s.onEndFn()},1.5*o),(r=(i=t).data()).tEnd||(Te(i[0],{WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",msTransition:"MSTransitionEnd",transition:"transitionend"}[bt.prefixed("transition")],function(t){r.tProp&&t.propertyName.match(r.tProp)&&r.onEndFn()}),r.tEnd=!0))}function le(t,e){var n=t.navdir||"horizontal";if(t.length){var o=t.data();Mt?(t.css(ne(0)),o.onEndFn=Jt,clearTimeout(o.tT)):t.stop();var a=ce(e,function(){return te(t,n)});return t.css(ee(a,n)),a}}function ce(){for(var t,e=0,n=arguments.length;e<n&&"number"!=typeof(t=e?arguments[e]():arguments[e]);e++);return t}function de(t,e){return Math.round(t+(e-t)/1.5)}function he(){return he.p=he.p||("https:"===n.protocol?"https://":"http://"),he.p}function fe(t,n){if("string"!=typeof t)return t;var o,a,i,r;if(o=t,(a=e.createElement("a")).href=o,(t=a).host.match(/youtube\.com/)&&t.search){if(i=t.search.split("v=")[1]){var s=i.indexOf("&");-1!==s&&(i=i.substring(0,s)),r="youtube"}}else t.host.match(/youtube\.com|youtu\.be/)?(i=t.pathname.replace(/^\/(embed\/|v\/)?/,"").replace(/\/.*/,""),r="youtube"):t.host.match(/vimeo\.com/)&&(r="vimeo",i=t.pathname.replace(/^\/(video\/)?/,"").replace(/\/.*/,""));return i&&r||!n||(i=t.href,r="custom"),!!i&&{id:i,type:r,s:t.search.replace(/^\?/,""),p:he()}}function me(t,e,n,a){for(var i=0,r=t.length;i<r;i++){var s=t[i];if(s.i===n&&s.thumbsReady){var u={videoReady:!0};u[Kt]=u[Vt]=u[Qt]=!1,a.splice(i,1,o.extend({},s,u,e));break}}}function ve(t){var e=[];function n(t,e,n){var a=n.thumb&&n.img!==n.thumb,i=ae(n.width||t.attr("width")),r=ae(n.height||t.attr("height"));o.extend(n,{width:i,height:r,thumbratio:Se(n.thumbratio||ae(n.thumbwidth||e&&e.attr("width")||a||i)/ae(n.thumbheight||e&&e.attr("height")||a||r))})}return t.children().each(function(){var t,a,i,r,s,u,l,c,d,h=o(this),f=Pe(o.extend(h.data(),{id:h.attr("id")}));if(h.is("a, img"))a=f,i=!0,r=(t=h).children("img").eq(0),s=t.attr("href"),u=t.attr("src"),l=r.attr("src"),c=a.video,(d=!!i&&fe(s,!0===c))?s=!1:d=c,n(t,r,o.extend(a,{video:d,img:a.img||s||u||l,thumb:a.thumb||l||u||s}));else{if(h.is(":empty"))return;n(h,null,o.extend(f,{html:this,_html:h.html()}))}e.push(f)}),e}function pe(t,e,n,o){return pe.i||(pe.i=1,pe.ii=[!0]),o=o||pe.i,void 0===pe.ii[o]&&(pe.ii[o]=!0),t()?e():pe.ii[o]&&setTimeout(function(){pe.ii[o]&&pe(t,e,n,o)},n||100),pe.i++}function ge(t,e){var n=t.data(),o=n.measures;if(o&&(!n.l||n.l.W!==o.width||n.l.H!==o.height||n.l.r!==o.ratio||n.l.w!==e.w||n.l.h!==e.h)){var a=Zt(e.h,0,o.height),i=a*o.ratio;De.setRatio(t,i,a),n.l={W:o.width,H:o.height,r:o.ratio,w:e.w,h:e.h}}return!0}function we(t,e,n,o){return e!==n&&("vertical"===o?t<=e?"top":t>=n?"bottom":"top bottom":t<=e?"left":t>=n?"right":"left right")}function be(t,e,n){n=n||{},t.each(function(){var t,a=o(this),i=a.data();i.clickOn||(i.clickOn=!0,o.extend(Re(a,{onStart:function(e){t=e,(n.onStart||Jt).call(this,e)},onMove:n.onMove||Jt,onTouchEnd:n.onTouchEnd||Jt,onEnd:function(n){n.moved||e.call(this,t)}}),{noMove:!0}))})}function ye(t,e){return'<div class="'+t+'">'+(e||"")+"</div>"}function xe(t){return"."+t}function _e(t){for(var e=t.length;e;){var n=Math.floor(Math.random()*e--),o=t[e];t[e]=t[n],t[n]=o}return t}function Ce(t){return"[object Array]"==Object.prototype.toString.call(t)&&o.map(t,function(t){return o.extend({},t)})}function ke(t,e,n){t.scrollLeft(e||0).scrollTop(n||0)}function Pe(t){if(t){var e={};return o.each(t,function(t,n){e[t.toLowerCase()]=n}),e}}function Se(t){if(t){var e=+t;return isNaN(e)?+(e=t.split("/"))[0]/+e[1]||a:e}}function Te(t,e,n,o){e&&(t.addEventListener?t.addEventListener(e,n,!!o):t.attachEvent("on"+e,n))}function Fe(t,e){return t>e.max?t=e.max:t<e.min&&(t=e.min),t}function Ee(t,e){return e?{disabled:t}:{tabindex:-1*t+"",disabled:t}}function Me(t,e){Te(t,"keyup",function(n){t.getAttribute("disabled")||13==n.keyCode&&e.call(t,n)})}function je(t,e){Te(t,"focus",t.onfocusin=function(n){e.call(t,n)},!0)}function $e(t,e){t.preventDefault?t.preventDefault():t.returnValue=!1,e&&t.stopPropagation&&t.stopPropagation()}function ze(t){return t?">":"<"}pe.stop=function(t){pe.ii[t]=!1};var qe,Ne,Ae,Le,Oe,De=function(){return{setRatio:function(t,e,n){e/n<=1?(t.parent().removeClass(mt),t.parent().addClass(vt)):(t.parent().removeClass(vt),t.parent().addClass(mt))},setThumbAttr:function(t,e,n){var i=n;t.attr(i)||t.attr(i)===a||t.attr(i,e),t.find("["+i+"]").length&&t.find("["+i+"]").each(function(){o(this).attr(i,e)})},isExpectedCaption:function(t,e,n){var o,a=!1;return o=t.showCaption===n||!0===t.showCaption,!!e&&(t.caption&&o&&(a=!0),a)}}}(jQuery);function Ie(t,e){var n=t.data(),a=Math.round(e.pos),i=function(){n&&n.sliding&&(n.sliding=!1),(e.onEnd||Jt)()};void 0!==e.overPos&&e.overPos!==e.pos&&(a=e.overPos);var r=o.extend(ee(a,e.direction),e.width&&{width:e.width},e.height&&{height:e.height});n&&n.sliding&&(n.sliding=!0),Mt?(t.css(o.extend(ne(e.time),r)),e.time>10?ue(t,"transform",i,e.time):i()):t.stop().animate(r,e.time,Bt,i)}function We(t){var e=(t.touches||[])[0]||t;t._x=e.pageX||e.originalEvent.pageX,t._y=e.clientY||e.originalEvent.clientY,t._now=o.now()}function Re(t,n){var a,i,r,s,u,l,c,d,h,f=t[0],m={};function v(t){if(r=o(t.target),m.checked=l=c=h=!1,a||m.flow||t.touches&&t.touches.length>1||t.which>1||qe&&qe.type!==t.type&&Ae||(l=n.select&&r.is(n.select,f)))return l;u="touchstart"===t.type,c=r.is("a, a *",f),s=m.control,d=m.noMove||m.noSwipe||s?16:m.snap?0:4,We(t),i=qe=t,Ne=t.type.replace(/down|start/,"move").replace(/Down/,"Move"),(n.onStart||Jt).call(f,t,{control:s,$target:r}),a=m.flow=!0,u&&!m.go||$e(t)}function p(t){if(t.touches&&t.touches.length>1||Nt&&!t.isPrimary||Ne!==t.type||!a)return a&&w(),void(n.onTouchEnd||Jt)();We(t);var e=Math.abs(t._x-i._x),o=Math.abs(t._y-i._y),r=e-o,s=(m.go||m.x||r>=0)&&!m.noSwipe,l=r<0;u&&!m.checked?(a=s)&&$e(t):($e(t),g(e,o)&&(n.onMove||Jt).call(f,t,{touch:u})),!h&&g(e,o)&&Math.sqrt(Math.pow(e,2)+Math.pow(o,2))>d&&(h=!0),m.checked=m.checked||s||l}function g(t,e){return t>e&&t>1.5}function w(t){(n.onTouchEnd||Jt)();var e=a;m.control=a=!1,e&&(m.flow=!1),!e||c&&!m.checked||(t&&$e(t),Ae=!0,clearTimeout(Le),Le=setTimeout(function(){Ae=!1},1e3),(n.onEnd||Jt).call(f,{moved:h,$target:r,control:s,touch:u,startEvent:i,aborted:!t||"MSPointerCancel"===t.type}))}function b(){m.flow&&(m.flow=!1)}return Nt?(Te(f,"MSPointerDown",v),Te(e,"MSPointerMove",p),Te(e,"MSPointerCancel",w),Te(e,"MSPointerUp",w)):(Te(f,"touchstart",v),Te(f,"touchmove",p),Te(f,"touchend",w),Te(e,"touchstart",function(){m.flow||(m.flow=!0)}),Te(e,"touchend",b),Te(e,"touchcancel",b),St.on("scroll",b),t.on("mousedown pointerdown",v),Tt.on("mousemove pointermove",p).on("mouseup pointerup",w)),Oe=bt.touch?"a":"div",t.on("click",Oe,function(t){m.checked&&$e(t)}),m}function He(t,e){var n,a,i,r,s,u,l,c,d,h,f,m,v,p,g,w=t[0],b=t.data(),y={};function x(o,s){g=!0,n=a="vertical"===m?o._y:o._x,l=o._now,u=[[l,n]],i=r=y.noMove||s?0:le(t,(e.getPos||Jt)()),(e.onStart||Jt).call(w,o)}return y=o.extend(Re(e.$wrap,o.extend({},e,{onStart:function(e,n){d=y.min,h=y.max,f=y.snap,m=y.direction||"horizontal",t.navdir=m,v=e.altKey,g=p=!1,n.control||b.sliding||x(e)},onMove:function(o,l){y.noSwipe||(g||x(o),a="vertical"===m?o._y:o._x,u.push([o._now,a]),s=we(r=i-(n-a),d,h,m),r<=d?r=de(r,d):r>=h&&(r=de(r,h)),y.noMove||(t.css(ee(r,m)),p||(p=!0,l.touch||Nt||t.addClass(nt)),(e.onMove||Jt).call(w,o,{pos:r,edge:s})))},onEnd:function(n){if(!y.noSwipe||!n.moved){g||x(n.startEvent,!0),n.touch||Nt||t.removeClass(nt);for(var s,l,p,b,_,C,k,P,S,T=(c=o.now())-Lt,F=null,E=Ot,M=e.friction,j=u.length-1;j>=0;j--){if(s=u[j][0],l=Math.abs(s-T),null===F||l<p)F=s,b=u[j][1];else if(F===T||l>p)break;p=l}k=Zt(r,d,h);var $=b-a,z=$>=0,q=c-F,N=q>Lt,A=!N&&r!==i&&k===r;f&&(k=Zt(Math[A?z?"floor":"ceil":"round"](r/f)*f,d,h),d=h=k),A&&(f||k===r)&&(S=-$/q,E*=Zt(Math.abs(S),e.timeLow,e.timeHigh),_=Math.round(r+S*E/M),f||(k=_),(!z&&_>h||z&&_<d)&&(C=z?d:h,f||(k=C),P=Zt(k+.03*(P=_-C),C-50,C+50),E=Math.abs((r-P)/(S/M)))),E*=v?10:1,(e.onEnd||Jt).call(w,o.extend(n,{moved:n.moved||N&&f,pos:r,newPos:k,overPos:P,time:E,dir:m}))}}})),y)}function Ke(t,e){var n,a,i,r=t[0],s={prevent:{}};return Te(r,At,function(t){var r=t.wheelDeltaY||-1*t.deltaY||0,u=t.wheelDeltaX||-1*t.deltaX||0,l=Math.abs(u)&&!Math.abs(r),c=ze(u<0),d=a===c,h=o.now(),f=h-i<Lt;a=c,i=h,l&&s.ok&&(!s.prevent[c]||n)&&($e(t,!0),n&&d&&f||(e.shift&&(n=!0,clearTimeout(s.t),s.t=setTimeout(function(){n=!1},Dt)),(e.onEnd||Jt)(t,e.shift?c:u)))}),s}function Qe(){o.each(o.Fotorama.instances,function(t,e){e.index=t})}jQuery.Fotorama=function(n,O){kt=o("html"),Pt=o("body");var nt,it,mt,vt,wt,bt,xt,_t,Ct,Ft,Et,zt,At,Dt,Bt,Ut,oe,ue,de,Te,qe,Ne,Ae,Le,Oe,We,Re,Ve,Xe,Be,Ye,Ue,Ge,Je,Ze,tn,en=this,nn=o.now(),on=i+nn,an=n[0],rn=1,sn=n.data(),un=o("<style></style>"),ln=o(ye(B)),cn=n.find(xe(s)),dn=cn.find(xe(b)),hn=(dn[0],n.find(xe(_))),fn=o(),mn=n.find(xe(T)),vn=n.find(xe(F)),pn=n.find(xe(P)),gn=n.find(xe(M)),wn=gn.find(xe(E)),bn=wn.find(xe(j)),yn=o(),xn=o(),_n=(hn.data(),bn.data(),n.find(xe(ut))),Cn=n.find(xe(rt)),kn=n.find(xe(st)),Pn=n.find(xe(U)),Sn=Pn[0],Tn=o(ye(ht)),Fn=n.find(xe(ft))[0],En=n.find(xe(pt)),Mn=!1,jn={},$n={},zn={},qn={},Nn={},An={},Ln={},On=0,Dn=[];function In(){o.each(nt,function(t,e){if(!e.i){e.i=rn++;var n=fe(e.video,!0);if(n){var a={};e.video=n,e.img||e.thumb?e.thumbsReady=!0:(r=nt,s=en,"youtube"===(c=(i=e).video).type?(u=(l=he()+"img.youtube.com/vi/"+c.id+"/default.jpg").replace(/\/default.jpg$/,"/hqdefault.jpg"),i.thumbsReady=!0):"vimeo"===c.type?o.ajax({url:he()+"vimeo.com/api/v2/video/"+c.id+".json",dataType:"jsonp",success:function(t){i.thumbsReady=!0,me(r,{img:t[0].thumbnail_large,thumb:t[0].thumbnail_small},i.i,s)}}):i.thumbsReady=!0,a={img:u,thumb:l}),me(nt,{img:a.img,thumb:a.thumb},e.i,en)}}var i,r,s,u,l,c})}function Wn(t){return Re[t]}function Rn(){if(dn!==a)if("vertical"==O.navdir){var t=O.thumbwidth+O.thumbmargin;dn.css("left",t),vn.css("right",t),Pn.css("right",t),cn.css("width",cn.css("width")+t),hn.css("max-width",cn.width()-t)}else dn.css("left",""),vn.css("right",""),Pn.css("right",""),cn.css("width",cn.css("width")+t),hn.css("max-width","")}function Hn(t){var e,a,s,u,l,c,d,h,f;t!==Hn.f&&(t?(n.addClass(i+" "+on).before(ln).before(un),a=en,o.Fotorama.instances.push(a),Qe()):(ln.detach(),un.detach(),n.html(sn.urtext).removeClass(on),e=en,o.Fotorama.instances.splice(e.index,1),Qe()),l="keydown."+i,d="keydown."+(c=i+nn),h="keyup."+c,f="resize."+c+" orientationchange."+c,(s=t)?(Tt.on(d,function(t){var e,n;vt&&27===t.keyCode?(e=!0,Mo(vt,!0,!0)):(en.fullScreen||O.keyboard&&!en.index)&&(27===t.keyCode?(e=!0,en.cancelFullScreen()):t.shiftKey&&32===t.keyCode&&Wn("space")||37===t.keyCode&&Wn("left")||38===t.keyCode&&Wn("up")&&o(":focus").attr("data-gallery-role")?(en.longPress.progress(),n="<"):32===t.keyCode&&Wn("space")||39===t.keyCode&&Wn("right")||40===t.keyCode&&Wn("down")&&o(":focus").attr("data-gallery-role")?(en.longPress.progress(),n=">"):36===t.keyCode&&Wn("home")?(en.longPress.progress(),n="<<"):35===t.keyCode&&Wn("end")&&(en.longPress.progress(),n=">>")),(e||n)&&$e(t),u={index:n,slow:t.altKey,user:!0},n&&(en.longPress.inProgress?en.showWhileLongPress(u):en.show(u))}),s&&Tt.on(h,function(t){en.longPress.inProgress&&en.showEndLongPress({user:!0}),en.longPress.reset()}),en.index||Tt.off(l).on(l,"textarea, input, select",function(t){!Pt.hasClass(r)&&t.stopPropagation()}),St.on(f,en.resize)):(Tt.off(d),St.off(f)),Hn.f=t)}function Kn(){var t=it<2||vt;$n.noMove=t||Te,$n.noSwipe=t||!O.swipe,!Le&&hn.toggleClass(C,!O.click&&!$n.noMove&&!$n.noSwipe),Nt&&cn.toggleClass(v,!$n.noSwipe)}function Qn(t){!0===t&&(t=""),O.autoplay=Math.max(+t||It,1.5*Ae)}function Vn(t,e){return Math.floor(cn.width()/(e.thumbwidth+e.thumbmargin))}function Xn(){var t;O.nav&&"dots"!==O.nav||(O.navdir="horizontal"),en.options=O=Pe(O),Yt=Vn(0,O),Te="crossfade"===O.transition||"dissolve"===O.transition,Dt=O.loop&&(it>2||Te&&(!Le||"slide"!==Le)),Ae=+O.transitionduration||Ot,We="rtl"===O.direction,Re=o.extend({},O.keyboard&&Gt,O.keyboard),(t=O).navarrows&&"thumbs"===t.nav?(Cn.show(),kn.show()):(Cn.hide(),kn.hide());var e,n,a,i,r={add:[],remove:[]};function s(t,e){r[t?"add":"remove"].push(e)}it>1?(Bt=O.nav,oe="top"===O.navposition,r.remove.push(X),pn.toggle(O.arrows)):(Bt=!1,pn.hide()),co(),fo(),ho(),O.autoplay&&Qn(O.autoplay),qe=ae(O.thumbwidth)||Wt,Ne=ae(O.thumbheight)||Wt,zn.ok=Nn.ok=O.trackpad&&!qt,Kn(),yo(O,[jn]),Ut="thumbs"===Bt,gn.filter(":hidden")&&Bt&&gn.show(),Ut?(ao(it,"navThumb"),mt=xn,tn=Vt,e=un,n=o.Fotorama.jst.style({w:qe,h:Ne,b:O.thumbborderwidth,m:O.thumbmargin,s:nn,q:!jt}),(a=e[0]).styleSheet?a.styleSheet.cssText=n:e.html(n),wn.addClass(A).removeClass(N)):"dots"===Bt?(ao(it,"navDot"),mt=yn,tn=Qt,wn.addClass(N).removeClass(A)):(gn.hide(),Bt=!1,wn.removeClass(A+" "+N)),Bt&&(oe?gn.insertBefore(dn):gn.insertAfter(dn),uo.nav=!1,uo(mt,bn,"nav")),(ue=O.allowfullscreen)?(Pn.prependTo(dn),de=$t&&"native"===ue,i="touchend",Pn.on(i,function(t){return $e(t,!0),!1})):(Pn.detach(),de=!1),s(Te,d),s(!Te,h),s(!O.captions,g),s(We,p),s(O.arrows,w),s(!(Oe=O.shadows&&!qt),m),cn.addClass(r.add.join(" ")).removeClass(r.remove.join(" ")),o.extend({},O),Rn()}function Bn(t){return t<0?(it+t%it)%it:t>=it?t%it:t}function Yn(t){return Zt(t,0,it-1)}function Un(t){return Dt?Bn(t):Yn(t)}function Gn(t){return!!(t>0||Dt)&&t-1}function Jn(t){return!!(t<it-1||Dt)&&t+1}function Zn(t,e,n){if("number"==typeof t){t=new Array(t);var a=!0}return o.each(t,function(t,o){if(a&&(o=t),"number"==typeof o){var i=nt[Bn(o)];if(i){var r="$"+e+"Frame",s=i[r];n.call(this,t,o,i,s,r,s&&s.data())}}})}function to(t,e,n,o){(!Ve||"*"===Ve&&o===At)&&(t=re(O.width)||re(t)||Rt,e=re(O.height)||re(e)||Ht,en.resize({width:t,ratio:O.ratio||n||t/e},0,o!==At&&"*"))}function eo(t,e,n,a){Zn(t,e,function(t,i,r,s,u,l){if(s){var c=en.fullScreen&&!l.$full&&"stage"===e;if(!l.$img||a||c){var d=new Image,h=o(d),f=h.data();l[c?"$full":"$img"]=h;var m="stage"===e?c?"full":"img":"thumb",v=r[m],p=c?r.img:r["stage"===e?"thumb":"img"];"navThumb"===e&&(s=l.$wrap),v?(o.Fotorama.cache[v]?function t(){"error"===o.Fotorama.cache[v]?w():"loaded"===o.Fotorama.cache[v]?setTimeout(b,0):setTimeout(t,100)}():(o.Fotorama.cache[v]="*",h.on("load",b).on("error",w)),l.state="",d.src=v,l.data.caption&&(d.alt=l.data.caption||""),l.data.full&&o(d).data("original",l.data.full),De.isExpectedCaption(r,O.showcaption)&&o(d).attr("aria-labelledby",r.labelledby)):w()}}function g(t){var e=Bn(i);xo(t,{index:e,src:v,frame:nt[e]})}function w(){h.remove(),o.Fotorama.cache[v]="error",r.html&&"stage"===e||!p||p===v?(!v||r.html||c?"stage"===e&&(s.trigger("f:load").removeClass(J+" "+G).addClass(Z),g("load"),to()):(s.trigger("f:error").removeClass(J).addClass(G),g("error")),l.state="error",!(it>1&&nt[i]===r)||r.html||r.deleted||r.video||c||(r.deleted=!0,en.splice(i,1))):(r[m]=v=p,l.$full=null,eo([i],e,n,!0))}function b(){var t=10;pe(function(){return!Je||!t--&&!qt},function(){o.Fotorama.measures[v]=f.measures=o.Fotorama.measures[v]||{width:d.width,height:d.height,ratio:d.width/d.height},to(f.measures.width,f.measures.height,f.measures.ratio,i),h.off("load error").addClass(""+(c?at:ot)).attr("aria-hidden","false").prependTo(s),s.hasClass(y)&&!s.hasClass(ct)&&s.attr("href",h.attr("src")),ge(h,(o.isFunction(n)?n():n)||jn),o.Fotorama.cache[v]=l.state="loaded",setTimeout(function(){s.trigger("f:load").removeClass(J+" "+G).addClass(Z+" "+(c?tt:et)),"stage"===e?g("load"):(r.thumbratio===Xt||!r.thumbratio&&O.thumbratio===Xt)&&(r.thumbratio=f.measures.ratio,Oo())},0)})}})}function no(){var t=wt[Kt];t&&!t.data().state&&(En.addClass(gt),t.on("f:load f:error",function(){t.off("f:load f:error"),En.removeClass(gt)}))}function oo(t){Me(t,No),je(t,function(){setTimeout(function(){ke(wn)},0),po({time:Ae,guessIndex:o(this).data().eq,minMax:qn})})}function ao(t,e){Zn(t,e,function(t,n,a,i,r,s){if(!i){i=a[r]=cn[r].clone(),(s=i.data()).data=a;var u=i[0],l="labelledby"+o.now();"stage"===e?(a.html&&o('<div class="'+lt+'"></div>').append(a._html?o(a.html).removeAttr("id").html(a._html):a.html).appendTo(i),a.id&&(l=a.id||l),a.labelledby=l,De.isExpectedCaption(a,O.showcaption)&&o(o.Fotorama.jst.frameCaption({caption:a.caption,labelledby:l})).appendTo(i),a.video&&i.addClass(x).append(Tn.clone()),je(u,function(){setTimeout(function(){ke(dn)},0),zo({index:s.eq,user:!0})}),fn=fn.add(i)):"navDot"===e?(oo(u),yn=yn.add(i)):"navThumb"===e&&(oo(u),s.$wrap=i.children(":first"),xn=xn.add(i),a.video&&s.$wrap.append(Tn.clone()))}})}function io(t,e){return t&&t.length&&ge(t,e)}function ro(t){Zn(t,"stage",function(t,n,a,i,r,s){if(i){var u,l=Bn(n);s.eq=l,Ln[Kt][l]=i.css(o.extend({left:Te?0:se(n,jn.w,O.margin,xt)},Te&&ne(0))),u=i[0],o.contains(e.documentElement,u)||(i.appendTo(hn),Mo(a.$video)),io(s.$img,jn),io(s.$full,jn),!i.hasClass(y)||"false"===i.attr("aria-hidden")&&i.hasClass(V)||i.attr("aria-hidden","true")}})}function so(t,e){var n,a;"thumbs"!==Bt||isNaN(t)||(n=-t,a=-t+jn.nw,"vertical"===O.navdir&&(t-=O.thumbheight,a=-t+jn.h),xn.each(function(){var t=o(this).data(),i=t.eq,r=function(){return{h:Ne,w:t.w}},s=r(),u="vertical"===O.navdir?t.t>a:t.l>a;s.w=t.w,t.l+t.w<n||u||io(t.$img,s)||e&&eo([i],"navThumb",r)}))}function uo(t,e,n){if(!uo[n]){var a="nav"===n&&Ut,i=0,r=0;e.append(t.filter(function(){for(var t,e=o(this),n=e.data(),a=0,i=nt.length;a<i;a++)if(n.data===nt[a]){t=!0,n.eq=a;break}return t||e.remove()&&!1}).sort(function(t,e){return o(t).data().eq-o(e).data().eq}).each(function(){var t=o(this),e=t.data();De.setThumbAttr(t,e.data.caption,"aria-label")}).each(function(){if(a){var t=o(this),e=t.data(),n=Math.round(Ne*e.data.thumbratio)||qe,s=Math.round(qe/e.data.thumbratio)||Ne;e.t=r,e.h=s,e.l=i,e.w=n,t.css({width:n}),r+=s+O.thumbmargin,i+=n+O.thumbmargin}})),uo[n]=!0}}function lo(t){return!(Dt||Mn+t&&Mn-it+t||vt)}function co(){var t=lo(0),e=lo(1);mn.toggleClass(S,t).attr(Ee(t,!1)),vn.toggleClass(S,e).attr(Ee(e,!1))}function ho(){var t=!1,e=!1;if("thumbs"!==O.navtype||O.loop||(t=0==Mn,e=Mn==O.data.length-1),"slides"===O.navtype){var n=te(bn,O.navdir);t=n>=qn.max,e=n<=qn.min}Cn.toggleClass(S,t).attr(Ee(t,!0)),kn.toggleClass(S,e).attr(Ee(e,!0))}function fo(){zn.ok&&(zn.prevent={"<":lo(0),">":lo(1)})}function mo(t){var e,n,o,a,i=t.data();Ut?(e=i.l,n=i.t,o=i.w,a=i.h):(e=t.position().left,o=t.width());var r={c:e+o/2,min:-e+10*O.thumbmargin,max:-e+jn.w-o-10*O.thumbmargin},s={c:n+a/2,min:-n+10*O.thumbmargin,max:-n+jn.h-a-10*O.thumbmargin};return"vertical"===O.navdir?s:r}function vo(t){var e=wt[tn].data();Ie(_n,{time:1.2*t,pos:"vertical"===O.navdir?e.t:e.l,width:e.w,height:e.h,direction:O.navdir})}function po(t){var e,n,o,a,i,r,s,u,l,c,d,h,f,m,v,p,g,w=nt[t.guessIndex][tn],b=O.navtype;w&&("thumbs"===b?(e=qn.min!==qn.max,o=t.minMax||e&&mo(wt[tn]),a=e&&(t.keep&&po.t?po.l:Zt((t.coo||jn.nw/2)-mo(w).c,o.min,o.max)),i=e&&(t.keep&&po.l?po.l:Zt((t.coo||jn.nw/2)-mo(w).c,o.min,o.max)),r="vertical"===O.navdir?a:i,s=e&&Zt(r,qn.min,qn.max)||0,n=1.1*t.time,Ie(bn,{time:n,pos:s,direction:O.navdir,onEnd:function(){so(s,!0),ho()}}),Eo(wn,we(s,qn.min,qn.max,O.navdir)),po.l=r):(u=te(bn,O.navdir),n=1.11*t.time,l=O,c=qn,d=t.guessIndex,h=u,f=w,m=gn,"horizontal"===(v=O.navdir)?(p=l.thumbwidth,g=m.width()):(p=l.thumbheight,g=m.height()),s=Fe((p+l.margin)*(d+1)>=g-h?"horizontal"===v?-f.position().left:-f.position().top:(p+l.margin)*d<=Math.abs(h)?"horizontal"===v?-f.position().left+g-(p+l.margin):-f.position().top+g-(p+l.margin):h,c)||0,Ie(bn,{time:n,pos:s,direction:O.navdir,onEnd:function(){so(s,!0),ho()}}),Eo(wn,we(s,qn.min,qn.max,O.navdir))))}function go(t){for(var e=An[t];e.length;)e.shift().removeClass(V).attr("data-active",!1)}function wo(t){var e=Ln[t];o.each(bt,function(t,n){delete e[Bn(n)]}),o.each(e,function(t,n){delete e[t],n.detach()})}function bo(t){xt=_t=Mn;var e,o,a,i=wt[Kt];i&&(go(Kt),An[Kt].push(i.addClass(V).attr("data-active",!0)),i.hasClass(y)&&i.attr("aria-hidden","false"),t||en.showStage.onEnd(!0),le(hn,0),wo(Kt),ro(bt),$n.min=Dt?-1/0:-se(it-1,jn.w,O.margin,xt),$n.max=Dt?1/0:-se(0,jn.w,O.margin,xt),$n.snap=jn.w+O.margin,e="vertical"===O.navdir,o=e?bn.height():bn.width(),a=e?jn.h:jn.nw,qn.min=Math.min(0,a-o),qn.max=0,qn.direction=O.navdir,bn.toggleClass(C,!(qn.noMove=qn.min===qn.max)),Me(hn[0],function(){n.hasClass(Y)||(en.requestFullScreen(),Pn.focus())}))}function yo(t,e){t&&o.each(e,function(e,n){n&&o.extend(n,{width:t.width||n.width,height:t.height,minwidth:t.minwidth,maxwidth:t.maxwidth,minheight:t.minheight,maxheight:t.maxheight,ratio:Se(t.ratio)})})}function xo(t,e){n.trigger(i+":"+t,[en,e])}function _o(){clearTimeout(Co.t),Je=1,O.stopautoplayontouch?en.stopAutoplay():Ye=!0}function Co(){Je&&(O.stopautoplayontouch||(ko(),Po()),Co.t=setTimeout(function(){Je=0},Ot+Lt))}function ko(){Ye=!(!vt&&!Ue)}function Po(){if(clearTimeout(Po.t),pe.stop(Po.w),O.autoplay&&!Ye){en.autoplay||(en.autoplay=!0,xo("startautoplay"));var t=Mn,e=wt[Kt].data();Po.w=pe(function(){return e.state||t!==Mn},function(){Po.t=setTimeout(function(){if(!Ye&&t===Mn){var e=zt,n=nt[e][Kt].data();Po.w=pe(function(){return n.state||e!==zt},function(){Ye||e!==zt||en.show(Dt?ze(!We):zt)})}},O.autoplay)})}else en.autoplay&&(en.autoplay=!1,xo("stopautoplay"))}function So(t){var e;return"object"!=typeof t?(e=t,t={}):e=t.index,e=">"===e?_t+1:"<"===e?_t-1:"<<"===e?0:">>"===e?it-1:e,e=void 0===(e=isNaN(e)?a:e)?Mn||0:e}function To(t){en.activeIndex=Mn=Un(t),Ft=Gn(Mn),Et=Jn(Mn),zt=Bn(Mn+(We?-1:1)),bt=[Mn,Ft,Et],_t=Dt?t:Mn}function Fo(t){var e=Math.abs(Ct-_t),n=ce(t.time,function(){return Math.min(Ae*(1+(e-1)/12),2*Ae)});return t.slow&&(n*=10),n}function Eo(t,e){Oe&&(t.removeClass(R+" "+H),t.removeClass(K+" "+Q),e&&!vt&&t.addClass(e.replace(/^|\s/g," "+W+"--")))}function Mo(t,e,n){e&&(cn.removeClass(c),vt=!1,Kn()),t&&t!==vt&&(t.remove(),xo("unloadvideo")),n&&(ko(),Po())}function jo(t){cn.toggleClass(f,t)}function $o(t){if(!$n.flow){var e,n=t?t.pageX:$o.x,o=n&&!lo((e=n,e-On>jn.w/3))&&O.click;$o.p!==o&&dn.toggleClass(k,o)&&($o.p=o,$o.x=n)}}function zo(t){clearTimeout(zo.t),O.clicktransition&&O.clicktransition!==O.transition?setTimeout(function(){var e=O.transition;en.setOptions({transition:O.clicktransition}),Le=e,zo.t=setTimeout(function(){en.show(t)},10)},0):en.show(t)}function qo(t,e){$n[t]=qn[t]=e}function No(t){var e=o(this).data().eq;"thumbs"===O.navtype?zo({index:e,slow:t.altKey,user:!0,coo:t._x-wn.offset().left}):zo({index:e,slow:t.altKey,user:!0})}function Ao(t){zo({index:pn.index(this)?">":"<",slow:t.altKey,user:!0})}function Lo(t){je(t,function(){setTimeout(function(){ke(dn)},0),jo(!1)})}function Oo(){if(nt=en.data=nt||Ce(O.data)||ve(n),it=en.size=nt.length,Do.ok&&O.shuffle&&_e(nt),In(),Mn=Yn(Mn),it&&Hn(!0),Xn(),!Oo.i){Oo.i=!0;var t=O.startindex;Mn=xt=_t=Ct=At=Un(t)||0}if(it){if(function t(){if(!t.f===We)return t.f=We,Mn=it-1-Mn,en.reverse(),!0}())return;vt&&Mo(vt,!0),bt=[],wo(Kt),Oo.ok=!0,en.show({index:Mn,time:0}),en.resize()}else en.destroy()}function Do(){Do.ok&&(Do.ok=!1,xo("ready"))}cn[Kt]=o('<div class="'+y+'"></div>'),cn[Vt]=o(o.Fotorama.jst.thumb()),cn[Qt]=o(o.Fotorama.jst.dots()),An[Kt]=[],An[Vt]=[],An[Qt]=[],Ln[Kt]={},cn.addClass(Mt?l:u),sn.fotorama=this,en.startAutoplay=function(t){return en.autoplay?this:(Ye=Ue=!1,Qn(t||O.autoplay),Po(),this)},en.stopAutoplay=function(){return en.autoplay&&(Ye=Ue=!0,Po()),this},en.showSlide=function(t){var e,n=te(bn,O.navdir),o="horizontal"===O.navdir?O.thumbwidth:O.thumbheight;"next"===t&&(e=n-(o+O.margin)*Yt),"prev"===t&&(e=n+(o+O.margin)*Yt),so(e=Fe(e,qn),!0),Ie(bn,{time:550,pos:e,direction:O.navdir,onEnd:function(){ho()}})},en.showWhileLongPress=function(t){if(!en.longPress.singlePressInProgress){To(So(t));var e=Fo(t)/50,n=wt;en.activeFrame=wt=nt[Mn];var o=n===wt&&!t.user;return en.showNav(o,t,e),this}},en.showEndLongPress=function(t){if(!en.longPress.singlePressInProgress){To(So(t));var e=Fo(t)/50,n=wt;en.activeFrame=wt=nt[Mn];var o=n===wt&&!t.user;return en.showStage(o,t,e),void 0!==Ct&&Ct!==Mn,Ct=Mn,this}},en.showStage=function(t,e,n){Mo(vt,wt.i!==nt[Bn(xt)].i),ao(bt,"stage"),ro(qt?[_t]:[_t,Gn(_t),Jn(_t)]),qo("go",!0),t||xo("show",{user:e.user,time:n}),Ye=!0;var a=e.overPos,i=en.showStage.onEnd=function(n){if(!i.ok){if(i.ok=!0,n||bo(!0),t||xo("showend",{user:e.user}),!n&&Le&&Le!==O.transition)return en.setOptions({transition:Le}),void(Le=!1);no(),eo(bt,"stage"),qo("go",!1),fo(),$o(),ko(),Po(),en.fullScreen?(wt[Kt].find("."+at).attr("aria-hidden",!1),wt[Kt].find("."+ot).attr("aria-hidden",!0)):(wt[Kt].find("."+at).attr("aria-hidden",!0),wt[Kt].find("."+ot).attr("aria-hidden",!1))}};Te?function t(e,n,a,i,r,s){var u=void 0!==s;if(u||(r.push(arguments),Array.prototype.push.call(arguments,r.length),!(r.length>1))){e=e||o(e),n=n||o(n);var l=e[0],c=n[0],d="crossfade"===i.method,h=function(){if(!h.done){h.done=!0;var e=(u||r.shift())&&r.shift();e&&t.apply(this,e),(i.onEnd||Jt)(!!e)}},f=i.time/(s||1);a.removeClass(I+" "+D),e.stop().addClass(I),n.stop().addClass(D),d&&c&&e.fadeTo(0,0),e.fadeTo(d?f:0,1,d&&h),n.fadeTo(f,0,h),l&&d||c||h()}}(wt[Kt],nt[Ct]&&Mn!==Ct?nt[Ct][Kt]:null,fn,{time:n,method:O.transition,onEnd:i},Dn):Ie(hn,{pos:-se(_t,jn.w,O.margin,xt),overPos:a,time:n,onEnd:i});co()},en.showNav=function(t,e,n){if(ho(),Bt){go(tn),An[tn].push(wt[tn].addClass(V).attr("data-active",!0));var o=Yn(Mn+Zt(_t-Ct,-1,1));po({time:n,coo:o!==Mn&&e.coo,guessIndex:void 0!==e.coo?o:Mn,keep:t}),Ut&&vo(n)}},en.show=function(t){en.longPress.singlePressInProgress=!0,To(So(t));var e=Fo(t),n=wt;en.activeFrame=wt=nt[Mn];var o=n===wt&&!t.user;return en.showStage(o,t,e),en.showNav(o,t,e),void 0!==Ct&&Ct!==Mn,Ct=Mn,en.longPress.singlePressInProgress=!1,this},en.requestFullScreen=function(){if(ue&&!en.fullScreen){if(o((en.activeFrame||{}).$stageFrame||{}).hasClass("fotorama-video-container"))return;Xe=St.scrollTop(),Be=St.scrollLeft(),ke(St),qo("x",!0),Ge=o.extend({},jn),n.addClass(Y).appendTo(Pt.addClass(r)),kt.addClass(r),Mo(vt,!0,!0),en.fullScreen=!0,de&&yt.request(an),en.resize(),eo(bt,"stage"),no(),xo("fullscreenenter"),"ontouchstart"in t||Pn.focus()}return this},en.cancelFullScreen=function(){return de&&yt.is()?yt.cancel(e):en.fullScreen&&(en.fullScreen=!1,$t&&yt.cancel(an),Pt.removeClass(r),kt.removeClass(r),n.removeClass(Y).insertAfter(ln),jn=o.extend({},Ge),Mo(vt,!0,!0),qo("x",!1),en.resize(),eo(bt,"stage"),ke(St,Be,Xe),xo("fullscreenexit")),this},en.toggleFullScreen=function(){return en[(en.fullScreen?"cancel":"request")+"FullScreen"]()},en.resize=function(e){if(!nt)return this;var n=arguments[1]||0,a=arguments[2];Yt=Vn(0,O),yo(en.fullScreen?{width:o(t).width(),maxwidth:null,minwidth:null,height:o(t).height(),maxheight:null,minheight:null}:Pe(e),[jn,a||en.fullScreen||O]);var i=jn.width,r=jn.height,s=jn.ratio,u=St.height()-(Bt?wn.height():0);if(re(i)&&(cn.css({width:""}),cn.css({height:""}),dn.css({width:""}),dn.css({height:""}),hn.css({width:""}),hn.css({height:""}),wn.css({width:""}),wn.css({height:""}),cn.css({minWidth:jn.minwidth||0,maxWidth:jn.maxwidth||1200}),"dots"===Bt&&gn.hide(),i=jn.W=jn.w=cn.width(),jn.nw=Bt&&ie(O.navwidth,i)||i,hn.css({width:jn.w,marginLeft:(jn.W-jn.w)/2}),r=(r=ie(r,u))||s&&i/s)){if(i=Math.round(i),r=jn.h=Math.round(Zt(r,ie(jn.minheight,u),ie(jn.maxheight,u))),dn.css({width:i,height:r}),"vertical"!==O.navdir||en.fullscreen||wn.width(O.thumbwidth+2*O.thumbmargin),"horizontal"!==O.navdir||en.fullscreen||wn.height(O.thumbheight+2*O.thumbmargin),"dots"===Bt&&(wn.width(i).height("auto"),gn.show()),"vertical"===O.navdir&&en.fullScreen&&dn.css("height",St.height()),"horizontal"===O.navdir&&en.fullScreen&&dn.css("height",St.height()-wn.height()),Bt){switch(O.navdir){case"vertical":gn.removeClass(q),gn.removeClass(z),gn.addClass($),wn.stop().animate({height:jn.h,width:O.thumbwidth},n);break;case"list":gn.removeClass($),gn.removeClass(q),gn.addClass(z);break;default:gn.removeClass($),gn.removeClass(z),gn.addClass(q),wn.stop().animate({width:jn.nw},n)}bo(),po({guessIndex:Mn,time:n,keep:!0}),Ut&&uo.nav&&vo(n)}Ve=a||!0,Do.ok=!0,Do()}return On=dn.offset().left,Rn(),this},en.setOptions=function(t){return o.extend(O,t),Oo(),this},en.shuffle=function(){return nt&&_e(nt)&&Oo(),this},en.longPress={threshold:1,count:0,thumbSlideTime:20,progress:function(){this.inProgress||(this.count++,this.inProgress=this.count>this.threshold)},end:function(){this.inProgress&&(this.isEnded=!0)},reset:function(){this.count=0,this.inProgress=!1,this.isEnded=!1}},en.destroy=function(){return en.cancelFullScreen(),en.stopAutoplay(),nt=en.data=null,Hn(),bt=[],wo(Kt),Oo.ok=!1,this},en.playVideo=function(){var t=wt,e=t.video,n=Mn;return"object"==typeof e&&t.videoReady&&(de&&en.fullScreen&&en.cancelFullScreen(),pe(function(){return!yt.is()||n!==Mn},function(){var a;n===Mn&&(t.$video=t.$video||o(ye(dt)).append('<iframe src="'+(a=e).p+a.type+".com/embed/"+a.id+'" frameborder="0" allowfullscreen></iframe>'),t.$video.appendTo(t[Kt]),cn.addClass(c),vt=t.$video,Kn(),pn.blur(),Pn.blur(),xo("loadvideo"))})),this},en.stopVideo=function(){return Mo(vt,!0,!0),this},en.spliceByIndex=function(t,e){e.i=t+1,e.img&&o.ajax({url:e.img,type:"HEAD",success:function(){nt.splice(t,1,e),Oo()}})},dn.on("mousemove",$o),$n=He(hn,{onStart:_o,onMove:function(t,e){Eo(dn,e.edge)},onTouchEnd:Co,onEnd:function(t){var e,a,i,r,s,u,l;if(Eo(dn),e=(Nt&&!Ze||t.touch)&&O.arrows,(t.moved||e&&t.pos!==t.newPos&&!t.control)&&t.$target[0]!==Pn[0]){var c=(r=t.newPos,s=jn.w,u=O.margin,l=xt,-Math.round(r/(s+(u||0))-(l||0)));en.show({index:c,time:Te?Ae:t.time,overPos:t.overPos,user:!0})}else t.aborted||t.control||(a=t.startEvent,i=a.target,o(i).hasClass(ht)?en.playVideo():i===Sn?en.toggleFullScreen():vt?i===Fn&&Mo(vt,!0,!0):n.hasClass(Y)||en.requestFullScreen())},timeLow:1,timeHigh:1,friction:2,select:"."+X+", ."+X+" *",$wrap:dn,direction:"horizontal"}),qn=He(bn,{onStart:_o,onMove:function(t,e){Eo(wn,e.edge)},onTouchEnd:Co,onEnd:function(t){function e(){po.l=t.newPos,ko(),Po(),so(t.newPos,!0),ho()}if(t.moved)t.pos!==t.newPos?(Ye=!0,Ie(bn,{time:t.time,pos:t.newPos,overPos:t.overPos,direction:O.navdir,onEnd:e}),so(t.newPos),Oe&&Eo(wn,we(t.newPos,qn.min,qn.max,t.dir))):e();else{var n=t.$target.closest("."+L,bn)[0];n&&No.call(n,t.startEvent)}},timeLow:.5,timeHigh:2,friction:5,$wrap:wn,direction:O.navdir}),zn=Ke(dn,{shift:!0,onEnd:function(t,e){_o(),Co(),en.show({index:e,slow:t.altKey})}}),Nn=Ke(wn,{onEnd:function(t,e){_o(),Co();var n=le(bn)+.25*e;bn.css(ee(Zt(n,qn.min,qn.max),O.navdir)),Oe&&Eo(wn,we(n,qn.min,qn.max,O.navdir)),Nn.prevent={"<":n>=qn.max,">":n<=qn.min},clearTimeout(Nn.t),Nn.t=setTimeout(function(){po.l=n,so(n,!0)},Lt),so(n)}}),cn.hover(function(){setTimeout(function(){Je||jo(!(Ze=!0))},0)},function(){Ze&&jo(!(Ze=!1))}),be(pn,function(t){$e(t),Ao.call(this,t)},{onStart:function(){_o(),$n.control=!0},onTouchEnd:Co}),be(Cn,function(t){$e(t),"thumbs"===O.navtype?en.show("<"):en.showSlide("prev")}),be(kn,function(t){$e(t),"thumbs"===O.navtype?en.show(">"):en.showSlide("next")}),pn.each(function(){Me(this,function(t){Ao.call(this,t)}),Lo(this)}),Me(Sn,function(){n.hasClass(Y)?(en.cancelFullScreen(),hn.focus()):(en.requestFullScreen(),Pn.focus())}),Lo(Sn),o.each("load push pop shift unshift reverse sort splice".split(" "),function(t,e){en[e]=function(){return nt=nt||[],"load"!==e?Array.prototype[e].apply(nt,arguments):arguments[0]&&"object"==typeof arguments[0]&&arguments[0].length&&(nt=Ce(arguments[0])),Oo(),en}}),Oo()},o.fn.fotorama=function(e){return this.each(function(){var n=this,a=o(this),i=a.data(),r=i.fotorama;r?r.setOptions(e,!0):pe(function(){return!(0===(t=n).offsetWidth&&0===t.offsetHeight);var t},function(){i.urtext=a.html(),new o.Fotorama(a,o.extend({},Ut,t.fotoramaDefaults,e,i))})})},o.Fotorama.instances=[],o.Fotorama.cache={},o.Fotorama.measures={},(o=o||{}).Fotorama=o.Fotorama||{},o.Fotorama.jst=o.Fotorama.jst||{},o.Fotorama.jst.dots=function(t){return'<div class="fotorama__nav__frame fotorama__nav__frame--dot" tabindex="0" role="button" data-gallery-role="nav-frame" data-nav-type="thumb" aria-label>\r\n <div class="fotorama__dot"></div>\r\n</div>','<div class="fotorama__nav__frame fotorama__nav__frame--dot" tabindex="0" role="button" data-gallery-role="nav-frame" data-nav-type="thumb" aria-label>\r\n <div class="fotorama__dot"></div>\r\n</div>'},o.Fotorama.jst.frameCaption=function(t){var e,n="";return n+='<div class="fotorama__caption" aria-hidden="true">\r\n <div class="fotorama__caption__wrap" id="'+(null==(e=t.labelledby)?"":e)+'">'+(null==(e=t.caption)?"":e)+"</div>\r\n</div>\r\n"},o.Fotorama.jst.style=function(t){var e,n="";return n+=".fotorama"+(null==(e=t.s)?"":e)+" .fotorama__nav--thumbs .fotorama__nav__frame{\r\npadding:"+(null==(e=t.m)?"":e)+"px;\r\nheight:"+(null==(e=t.h)?"":e)+"px}\r\n.fotorama"+(null==(e=t.s)?"":e)+" .fotorama__thumb-border{\r\nheight:"+(null==(e=t.h)?"":e)+"px;\r\nborder-width:"+(null==(e=t.b)?"":e)+"px;\r\nmargin-top:"+(null==(e=t.m)?"":e)+"px}"},o.Fotorama.jst.thumb=function(t){return'<div class="fotorama__nav__frame fotorama__nav__frame--thumb" tabindex="0" role="button" data-gallery-role="nav-frame" data-nav-type="thumb" aria-label>\r\n <div class="fotorama__thumb">\r\n </div>\r\n</div>','<div class="fotorama__nav__frame fotorama__nav__frame--thumb" tabindex="0" role="button" data-gallery-role="nav-frame" data-nav-type="thumb" aria-label>\r\n <div class="fotorama__thumb">\r\n </div>\r\n</div>'}}(window,document,location,"undefined"!=typeof jQuery&&jQuery); \ No newline at end of file From 5916f40d6b776a72ca3d7bbe7e8e17809c09030c Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Mon, 1 Apr 2019 14:45:49 +0300 Subject: [PATCH 1025/1295] MAGETWO-72961: Customer Address "default billing address" Attribute Not Used in Checkout #8777 --- ...ddressShouldBeCheckedOnPaymentPageTest.xml | 61 +++++++++++++++++++ ...OrderWithNewAddressesThatWasEditedTest.xml | 2 +- .../web/js/model/checkout-data-resolver.js | 3 +- .../model/shipping-save-processor/default.js | 5 +- .../Test/TestCase/OnePageCheckoutTest.xml | 7 +-- .../TestStep/FillBillingInformationStep.php | 33 ++++++++-- .../payment/method-renderer/cc-form.test.js | 12 ++++ .../payment/method-renderer/paypal.test.js | 12 ++++ .../paypal-express-abstract.test.js | 12 ++++ 9 files changed, 134 insertions(+), 13 deletions(-) create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/DefaultBillingAddressShouldBeCheckedOnPaymentPageTest.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/DefaultBillingAddressShouldBeCheckedOnPaymentPageTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/DefaultBillingAddressShouldBeCheckedOnPaymentPageTest.xml new file mode 100644 index 0000000000000..5dfc08c9df302 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/DefaultBillingAddressShouldBeCheckedOnPaymentPageTest.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="DefaultBillingAddressShouldBeCheckedOnPaymentPageTest"> + <annotations> + <features value="Checkout"/> + <stories value="Checkout via the Storefront"/> + <title value="The default billing address should be used on checkout"/> + <description value="Default billing address should be preselected on payments page on checkout if it exist"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-98974"/> + <useCaseId value="MAGETWO-72961"/> + <group value="checkout"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <!--Go to Storefront as Customer--> + <actionGroup ref="CustomerLoginOnStorefront" stepKey="loginToStorefrontAccount"> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + </before> + <after> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <!--Logout from customer account--> + <actionGroup ref="CustomerLogoutStorefrontActionGroup" stepKey="logoutCustomer"/> + </after> + <!-- Add simple product to cart and go to checkout--> + <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> + <!-- Click "+ New Address" and Fill new address--> + <click selector="{{CheckoutShippingSection.newAdress}}" stepKey="addAddress"/> + <actionGroup ref="LoggedInUserCheckoutAddNewAddressInShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingAddress"> + <argument name="customerVar" value="CustomerEntityOne"/> + <argument name="customerAddressVar" value="US_Address_CA"/> + </actionGroup> + <dontSeeElement selector="{{CheckoutAddressPopupSection.newAddressModalPopup}}" stepKey="dontSeeModalPopup"/> + <!--Select Shipping Rate "Flat Rate" and click "Next" button--> + <actionGroup ref="CheckoutSelectFlatRateShippingMethodActionGroup" stepKey="selectFlatRateShipping"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask2"/> + <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="clickNext"/> + <waitForPageLoad stepKey="waitForAddressSaved"/> + <!--Verify that "My billing and shipping address are the same" is unchecked and billing address is preselected--> + <dontSeeCheckboxIsChecked selector="{{CheckoutPaymentSection.billingAddressSameAsShipping}}" stepKey="shippingAndBillingAddressIsSameUnchecked"/> + <see selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{US_Address_TX.street[0]}}" stepKey="assertBillingAddress"/> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml index c80e284633f12..9163be0b9b0f1 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml @@ -82,7 +82,7 @@ <amOnPage url="{{StorefrontCustomerOrderViewPage.url({$grabOrderNumber})}}" stepKey="goToOrderReviewPage"/> <see userInput="{{UK_Default_Address.street[0]}} {{UK_Default_Address.city}}, {{UK_Default_Address.postcode}}" selector="{{StorefrontCustomerOrderViewSection.shippingAddress}}" stepKey="checkShippingAddress"/> - <see userInput="{{UK_Default_Address.street[0]}} {{UK_Default_Address.city}}, {{UK_Default_Address.postcode}}" + <see userInput="{{US_Address_TX.street[0]}}" selector="{{StorefrontCustomerOrderViewSection.billingAddress}}" stepKey="checkBillingAddress"/> </test> </tests> diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js index bf152f68e25e5..e54f464f24d02 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js @@ -60,7 +60,6 @@ define([ this.resolveBillingAddress(); } } - }, /** @@ -244,7 +243,7 @@ define([ return; } - if (quote.isVirtual()) { + if (quote.isVirtual() || !quote.billingAddress()) { isBillingAddressInitialized = addressList.some(function (addrs) { if (addrs.isDefaultBilling()) { selectBillingAddress(addrs); diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-save-processor/default.js b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-save-processor/default.js index 447d626b339bd..e34f861f7714f 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-save-processor/default.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-save-processor/default.js @@ -35,8 +35,9 @@ define([ saveShippingInformation: function () { var payload; - /* Assign selected address every time buyer selects address*/ - selectBillingAddressAction(quote.shippingAddress()); + if (!quote.billingAddress() && quote.shippingAddress().canUseForBilling()) { + selectBillingAddressAction(quote.shippingAddress()); + } payload = { addressInformation: { diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutTest.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutTest.xml index 9e20bbdaac1d9..6716e4aacab81 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutTest.xml +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutTest.xml @@ -15,13 +15,11 @@ <data name="shippingAddressCustomer" xsi:type="array"> <item name="added" xsi:type="number">1</item> </data> - <data name="billingAddressCustomer" xsi:type="array"> - <item name="added" xsi:type="number">1</item> - </data> <data name="prices" xsi:type="array"> <item name="grandTotal" xsi:type="string">565.00</item> </data> <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> + <data name="editBillingInformation" xsi:type="boolean">false</data> <data name="shipping/shipping_method" xsi:type="string">Fixed</data> <data name="payment/method" xsi:type="string">checkmo</data> <data name="configData" xsi:type="string">checkmo</data> @@ -43,6 +41,7 @@ <item name="grandTotal" xsi:type="string">565.00</item> </data> <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> + <data name="editBillingInformation" xsi:type="boolean">false</data> <data name="shipping/shipping_method" xsi:type="string">Fixed</data> <data name="payment/method" xsi:type="string">checkmo</data> <data name="configData" xsi:type="string">checkmo</data> @@ -61,7 +60,7 @@ <data name="shippingAddress/dataset" xsi:type="string">UK_address_without_email</data> <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> <data name="shipping/shipping_method" xsi:type="string">Fixed</data> - <data name="billingCheckboxState" xsi:type="string">Yes</data> + <data name="editBillingInformation" xsi:type="boolean">false</data> <data name="billingAddress/dataset" xsi:type="string">US_address_1_without_email</data> <data name="payment/method" xsi:type="string">checkmo</data> <data name="configData" xsi:type="string">checkmo</data> diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/FillBillingInformationStep.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/FillBillingInformationStep.php index aa7eba634145f..f13fb93e89b71 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/FillBillingInformationStep.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestStep/FillBillingInformationStep.php @@ -126,11 +126,15 @@ public function run() if ($this->billingCheckboxState) { $this->assertBillingAddressCheckbox->processAssert($this->checkoutOnepage, $this->billingCheckboxState); } - if ($this->billingCheckboxState === 'Yes' && !$this->editBillingInformation) { - return [ - 'billingAddress' => $this->shippingAddress - ]; + + if (!$this->editBillingInformation) { + $billingAddress = $this->billingCheckboxState === 'Yes' + ? $this->shippingAddress + : $this->getDefaultBillingAddress(); + + return ['billingAddress' => $billingAddress]; } + if ($this->billingAddress) { $selectedPaymentMethod = $this->checkoutOnepage->getPaymentBlock()->getSelectedPaymentMethodBlock(); if ($this->shippingAddress) { @@ -156,4 +160,25 @@ public function run() 'billingAddress' => $billingAddress ]; } + + /** + * Get default billing address + * + * @return Address|null + */ + private function getDefaultBillingAddress() + { + $addresses = $this->customer->hasData('address') + ? $this->customer->getDataFieldConfig('address')['source']->getAddresses() + : []; + $defaultAddress = null; + foreach ($addresses as $address) { + if ($address->getDefaultBilling() === 'Yes') { + $defaultAddress = $address; + break; + } + } + + return $defaultAddress; + } } diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js index df6996afeb965..6062439db2365 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js @@ -15,6 +15,18 @@ define([ describe('Magento_Braintree/js/view/payment/method-renderer/cc-form', function () { var injector = new Squire(), mocks = { + 'Magento_Checkout/js/model/checkout-data-resolver': { + + /** Stub */ + applyBillingAddress: function () { + return true; + }, + + /** Stub */ + resolveBillingAddress: function () { + return true; + } + }, 'Magento_Checkout/js/model/quote': { billingAddress: ko.observable(), shippingAddress: ko.observable(), diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js index a2373cfb99091..d58c301e5d934 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js @@ -14,6 +14,18 @@ define([ var injector = new Squire(), mocks = { + 'Magento_Checkout/js/model/checkout-data-resolver': { + + /** Stub */ + applyBillingAddress: function () { + return true; + }, + + /** Stub */ + resolveBillingAddress: function () { + return true; + } + }, 'Magento_Checkout/js/model/quote': { billingAddress: ko.observable(), shippingAddress: ko.observable({ diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Paypal/frontend/js/view/payment/method-renderer/paypal-express-abstract.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Paypal/frontend/js/view/payment/method-renderer/paypal-express-abstract.test.js index 12e12eb492c89..cc40386a7779d 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Paypal/frontend/js/view/payment/method-renderer/paypal-express-abstract.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Paypal/frontend/js/view/payment/method-renderer/paypal-express-abstract.test.js @@ -24,6 +24,18 @@ define([ return true; }).and.callThrough(), mocks = { + 'Magento_Checkout/js/model/checkout-data-resolver': { + + /** Stub */ + applyBillingAddress: function () { + return true; + }, + + /** Stub */ + resolveBillingAddress: function () { + return true; + } + }, 'Magento_Checkout/js/model/quote': { billingAddress: ko.observable(), shippingAddress: ko.observable(), From a1d6ed616b6d59c5238f102281fb36482a41acfd Mon Sep 17 00:00:00 2001 From: Wojtek Naruniec <wojtek@mediotype.com> Date: Mon, 11 Feb 2019 13:45:13 +0100 Subject: [PATCH 1026/1295] Add missing attributes initialization to avoid throwing foreach error --- app/code/Magento/Eav/Model/Form.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Eav/Model/Form.php b/app/code/Magento/Eav/Model/Form.php index c8c50521f5509..1d867109aaf38 100644 --- a/app/code/Magento/Eav/Model/Form.php +++ b/app/code/Magento/Eav/Model/Form.php @@ -323,6 +323,8 @@ public function getAttributes() if ($this->_attributes === null) { $this->_attributes = []; $this->_userAttributes = []; + $this->_systemAttributes = []; + $this->_allowedAttributes = []; /** @var $attribute \Magento\Eav\Model\Attribute */ foreach ($this->_getFilteredFormAttributeCollection() as $attribute) { $this->_attributes[$attribute->getAttributeCode()] = $attribute; From a7511e0bff4355b71bc21438ab333fcb04672e17 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Tue, 26 Mar 2019 15:27:12 +0200 Subject: [PATCH 1027/1295] Fix static tests. --- app/code/Magento/Eav/Model/Form.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Model/Form.php b/app/code/Magento/Eav/Model/Form.php index 1d867109aaf38..a34b53eede354 100644 --- a/app/code/Magento/Eav/Model/Form.php +++ b/app/code/Magento/Eav/Model/Form.php @@ -286,7 +286,8 @@ public function getFormCode() } /** - * Return entity type instance + * Return entity type instance. + * * Return EAV entity type if entity type is not defined * * @return \Magento\Eav\Model\Entity\Type From 2192af8a1d12fc3457c514a08af7e24c896fe634 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Mon, 1 Apr 2019 16:40:30 +0300 Subject: [PATCH 1028/1295] MAGETWO-94426: Admin date wrong formatting for French locale --- .../ActionGroup/AdminAccountActionGroup.xml | 24 +++++++++++++++++++ .../Test/Mftf/Page/AdminSystemAccountPage.xml | 14 +++++++++++ .../Section/AdminSystemAccountSection.xml | 15 ++++++++++++ .../Test/Mftf/Data/LocaleOptionsData.xml | 24 +++++++++++++++++++ .../Metadata/locale_options_config-meta.xml | 21 ++++++++++++++++ ...frontCustomerAccountInformationSection.xml | 20 ++++++++++++++++ .../Magento/Catalog/_files/products_new.php | 4 ++-- 7 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminAccountActionGroup.xml create mode 100644 app/code/Magento/Backend/Test/Mftf/Page/AdminSystemAccountPage.xml create mode 100644 app/code/Magento/Backend/Test/Mftf/Section/AdminSystemAccountSection.xml create mode 100644 app/code/Magento/Config/Test/Mftf/Data/LocaleOptionsData.xml create mode 100644 app/code/Magento/Config/Test/Mftf/Metadata/locale_options_config-meta.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountInformationSection.xml diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminAccountActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminAccountActionGroup.xml new file mode 100644 index 0000000000000..01e3450b78c85 --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminAccountActionGroup.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAccountSetInterfaceLocaleActionGroup"> + <arguments> + <argument name="localeName" type="string"/> + </arguments> + <!-- Navigate to admin System Account Page--> + <amOnPage url="{{AdminSystemAccountPage.url}}" stepKey="openAdminSystemAccountPage"/> + <!-- Change Admin locale to {{localeName}} --> + <selectOption selector="{{AdminSystemAccountSection.interfaceLocale}}" userInput="{{localeName}}" stepKey="setInterfaceLocale"/> + <fillField selector="{{AdminSystemAccountSection.currentPassword}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="fillCurrentPassword"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSave"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the account." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Backend/Test/Mftf/Page/AdminSystemAccountPage.xml b/app/code/Magento/Backend/Test/Mftf/Page/AdminSystemAccountPage.xml new file mode 100644 index 0000000000000..2f04c2c11d288 --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Page/AdminSystemAccountPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminSystemAccountPage" url="admin/system_account/index/" area="admin" module="Magento_Backend"> + <section name="AdminSystemAccountSection"/> + </page> +</pages> diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminSystemAccountSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminSystemAccountSection.xml new file mode 100644 index 0000000000000..b9570ce945943 --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminSystemAccountSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminSystemAccountSection"> + <element name="interfaceLocale" type="text" selector="#interface_locale"/> + <element name="currentPassword" type="text" selector="#current_password"/> + </section> +</sections> diff --git a/app/code/Magento/Config/Test/Mftf/Data/LocaleOptionsData.xml b/app/code/Magento/Config/Test/Mftf/Data/LocaleOptionsData.xml new file mode 100644 index 0000000000000..e998730d11ae7 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Data/LocaleOptionsData.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="SetLocaleOptions" type="locale_options_config"> + <requiredEntity type="code">LocaleOptionsFrance</requiredEntity> + </entity> + <entity name="LocaleOptionsFrance" type="code"> + <data key="value">fr_FR</data> + </entity> + + <entity name="DefaultLocaleOptions" type="locale_options_config"> + <requiredEntity type="code">LocaleOptionsUSA</requiredEntity> + </entity> + <entity name="LocaleOptionsUSA" type="code"> + <data key="value">en_US</data> + </entity> +</entities> diff --git a/app/code/Magento/Config/Test/Mftf/Metadata/locale_options_config-meta.xml b/app/code/Magento/Config/Test/Mftf/Metadata/locale_options_config-meta.xml new file mode 100644 index 0000000000000..6398d51cda916 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Metadata/locale_options_config-meta.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="GeneralLocaleOptionsConfig" dataType="locale_options_config" type="create" auth="adminFormKey" url="/admin/system_config/save/section/general/" method="POST" successRegex="/messages-message-success/"> + <object key="groups" dataType="locale_options_config"> + <object key="locale" dataType="locale_options_config"> + <object key="fields" dataType="locale_options_config"> + <object key="code" dataType="code"> + <field key="value">string</field> + </object> + </object> + </object> + </object> + </operation> +</operations> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountInformationSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountInformationSection.xml new file mode 100644 index 0000000000000..361d245676576 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountInformationSection.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCustomerAccountInformationSection"> + <element name="title" type="text" selector=".page-title span"/> + <element name="firstName" type="input" selector="#firstname"/> + <element name="lastName" type="input" selector="#lastname"/> + <element name="changeEmail" type="checkbox" selector="#change_email"/> + <element name="changePassword" type="checkbox" selector="#change_password"/> + <element name="customAttributeFiled" type="input" selector="#{{attribute_code}}" parameterized="true"/> + <element name="saveButton" type="button" selector="#form-validate .action.save.primary"/> + </section> +</sections> diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_new.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_new.php index 15e274541bac4..a5ca4573d98b0 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_new.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_new.php @@ -15,8 +15,8 @@ ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) ->setWebsiteIds([1]) ->setStockData(['qty' => 100, 'is_in_stock' => 1]) - ->setNewsFromDate(date('Y-m-d', strtotime('-2 day'))) - ->setNewsToDate(date('Y-m-d', strtotime('+2 day'))) + ->setNewsFromDate(date('Y-m-d H:i:s', strtotime('-2 day'))) + ->setNewsToDate(date('Y-m-d H:i:s', strtotime('+2 day'))) ->setDescription('description') ->setShortDescription('short desc') ->save(); From f4e6adc8b4d23b530565f9ac40001c0990a86e3d Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Mon, 1 Apr 2019 19:08:29 +0300 Subject: [PATCH 1029/1295] MAGETWO-72961: Customer Address "default billing address" Attribute Not Used in Checkout #8777 --- .../Test/Mftf/Section/CheckoutPaymentSection.xml | 3 +++ ...TestWithRestrictedCountriesForPaymentTest.xml | 11 ----------- ...TestWithRestrictedCountriesForPaymentTest.xml | 16 +++++++++------- .../EditShippingAddressOnePageCheckoutTest.xml | 1 - 4 files changed, 12 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml index ae8f9aa5f2aa7..fd0d9b6768443 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml @@ -44,5 +44,8 @@ <element name="checkPaymentMethodByName" type="radio" selector="//div[@id='checkout-payment-method-load']//div[contains(., '{{paymentName}}')]/..//input[@type='radio']" parameterized="true"/> <element name="orderSummaryShippingTotal" type="text" selector=".totals.shipping.excl span.price"/> <element name="noPaymentMethods" type="text" selector=".no-quotes-block"/> + <element name="billingAddressSelectShared" type="select" selector=".checkout-billing-address select[name='billing_address_id']"/> + <element name="billingAddressSameAsShippingShared" type="checkbox" selector="#billing-address-same-as-shipping-shared"/> + <element name="addressAction" type="button" selector="//span[text()='{{action}}']" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml index a4583fb7fa50c..f7da0c39274f8 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml @@ -40,17 +40,6 @@ <click selector="{{CheckoutShippingMethodsSection.next}}" after="selectShippingMethod" stepKey="clickNextButton" /> <waitForPageLoad after="clickNextButton" stepKey="waitBillingForm"/> <seeElement selector="{{CheckoutPaymentSection.isPaymentSection}}" after="waitBillingForm" stepKey="checkoutPaymentStepIsOpened"/> - - <!-- Fill UK Address and verify that payment available and checkout successful --> - <click selector="{{CheckoutShippingSection.newAdress}}" after="shippingStepIsOpened1" stepKey="fillNewAddress" /> - <actionGroup ref="LoggedInUserCheckoutAddNewShippingSectionWithoutRegionActionGroup" after="fillNewAddress" stepKey="customerCheckoutFillingShippingSectionUK"> - <argument name="customerVar" value="$$createSimpleUsCustomer$$" /> - <argument name="customerAddressVar" value="UK_Default_Address" /> - </actionGroup> - <waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" after="customerCheckoutFillingShippingSectionUK" stepKey="waitNextButton1"/> - <actionGroup ref="CheckoutSelectFlatRateShippingMethodActionGroup" after="waitNextButton1" stepKey="selectShippingMethod1"/> - <click selector="{{CheckoutShippingMethodsSection.next}}" after="selectShippingMethod1" stepKey="clickNextButton1" /> - <actionGroup ref="CheckoutPlaceOrderActionGroup" after="selectCheckMoneyOrderPayment" stepKey="customerPlaceorder"> <argument name="orderNumberMessage" value="CONST.successCheckoutOrderNumberMessage" /> <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage" /> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml index 3d1dc2cf66689..11f2d87c4c75c 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml @@ -23,6 +23,7 @@ <createData entity="ApiSimpleProduct" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> </createData> + <magentoCLI command="config:set checkout/options/display_billing_address_on 1" stepKey="setShowBillingAddressOnPaymentPage" /> <magentoCLI command="config:set payment/checkmo/allowspecific" arguments="1" stepKey="setAllowSpecificCountiesValue" /> <magentoCLI command="config:set payment/checkmo/specificcountry" arguments="GB" stepKey="setSpecificCountryValue" /> </before> @@ -31,6 +32,7 @@ <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <magentoCLI command="config:set payment/checkmo/allowspecific 0" stepKey="unsetAllowSpecificCountiesValue"/> <magentoCLI command="config:set payment/checkmo/specificcountry ''" stepKey="unsetSpecificCountryValue" /> + <magentoCLI command="config:set checkout/options/display_billing_address_on 0" stepKey="setDisplayBillingAddressOnPaymentMethod" /> </after> <!-- Add product to cart --> @@ -53,14 +55,14 @@ <see userInput="No Payment method available." stepKey="checkMessage"/> <!-- Fill UK Address and verify that payment available and checkout successful --> - <click selector="{{CheckoutHeaderSection.shippingMethodStep}}" stepKey="goToShipping" /> - <waitForElementVisible selector="{{CheckoutShippingSection.isShippingStep}}" stepKey="shippingStepIsOpened1"/> - - <actionGroup ref="GuestCheckoutFillingShippingSectionWithoutRegionActionGroup" stepKey="guestCheckoutFillingShippingSectionUK"> - <argument name="customerVar" value="Simple_US_Customer" /> - <argument name="customerAddressVar" value="UK_Default_Address" /> - <argument name="shippingMethod" value="Flat Rate" type="string"/> + <uncheckOption selector="{{CheckoutPaymentSection.billingAddressSameAsShippingShared}}" stepKey="uncheckBillingAddressSameAsShippingCheckCheckBox"/> + <selectOption selector="{{CheckoutPaymentSection.billingAddressSelectShared}}" userInput="New Address" stepKey="clickOnNewAddress"/> + <waitForPageLoad stepKey="waitNewAddressBillingForm"/> + <actionGroup ref="LoggedInCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress"> + <argument name="customerAddress" value="UK_Default_Address"/> + <argument name="classPrefix" value="[aria-hidden=false]"/> </actionGroup> + <click selector="{{CheckoutPaymentSection.addressAction('Update')}}" stepKey="clickUpdateBillingAddressButton" /> <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyOrderPayment" /> <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="guestPlaceOrder"> <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage" /> diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/EditShippingAddressOnePageCheckoutTest.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/EditShippingAddressOnePageCheckoutTest.xml index 3c88d9193db28..f41731287a5ec 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/EditShippingAddressOnePageCheckoutTest.xml +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/EditShippingAddressOnePageCheckoutTest.xml @@ -14,7 +14,6 @@ <data name="editShippingAddress/dataset" xsi:type="string">empty_UK_address_without_email</data> <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> <data name="shipping/shipping_method" xsi:type="string">Fixed</data> - <data name="billingCheckboxState" xsi:type="string">Yes</data> <data name="products" xsi:type="array"> <item name="0" xsi:type="string">catalogProductSimple::default</item> </data> From 539cb3d25357571c7a37bd9347647525c7950818 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Mon, 1 Apr 2019 20:27:15 +0300 Subject: [PATCH 1030/1295] MAGETWO-72961: Customer Address "default billing address" Attribute Not Used in Checkout #8777 --- ...CustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml | 1 + ...ontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml index f7da0c39274f8..5f0b6b35c53cb 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml @@ -38,6 +38,7 @@ <waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" after="seeRegion" stepKey="waitNextButton"/> <actionGroup ref="CheckoutSelectFlatRateShippingMethodActionGroup" after="waitNextButton" stepKey="selectShippingMethod"/> <click selector="{{CheckoutShippingMethodsSection.next}}" after="selectShippingMethod" stepKey="clickNextButton" /> + <selectOption selector="{{CheckoutPaymentSection.billingAddressSelectShared}}" userInput="New Address" after="uncheckBillingAddressSameAsShippingCheckCheckBox" stepKey="clickOnNewAddress"/> <waitForPageLoad after="clickNextButton" stepKey="waitBillingForm"/> <seeElement selector="{{CheckoutPaymentSection.isPaymentSection}}" after="waitBillingForm" stepKey="checkoutPaymentStepIsOpened"/> <actionGroup ref="CheckoutPlaceOrderActionGroup" after="selectCheckMoneyOrderPayment" stepKey="customerPlaceorder"> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml index 11f2d87c4c75c..bce6db7bab205 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml @@ -56,7 +56,6 @@ <!-- Fill UK Address and verify that payment available and checkout successful --> <uncheckOption selector="{{CheckoutPaymentSection.billingAddressSameAsShippingShared}}" stepKey="uncheckBillingAddressSameAsShippingCheckCheckBox"/> - <selectOption selector="{{CheckoutPaymentSection.billingAddressSelectShared}}" userInput="New Address" stepKey="clickOnNewAddress"/> <waitForPageLoad stepKey="waitNewAddressBillingForm"/> <actionGroup ref="LoggedInCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress"> <argument name="customerAddress" value="UK_Default_Address"/> From 3751e4b1d86aa0f462e1c84e73cfde5c6d8b6e99 Mon Sep 17 00:00:00 2001 From: Serhiy Zhovnir <s.zhovnir@atwix.com> Date: Tue, 2 Apr 2019 11:27:15 +0300 Subject: [PATCH 1031/1295] #21786 Fix the issue with asynchronous email sending for the sales entities --- .../Model/Order/Creditmemo/Sender/EmailSender.php | 4 +++- .../Model/Order/Email/Sender/CreditmemoSender.php | 4 +++- .../Sales/Model/Order/Email/Sender/InvoiceSender.php | 4 +++- .../Sales/Model/Order/Email/Sender/ShipmentSender.php | 4 +++- .../Sales/Model/Order/Invoice/Sender/EmailSender.php | 4 +++- .../Sales/Model/Order/Shipment/Sender/EmailSender.php | 4 +++- .../Model/Order/Creditmemo/Sender/EmailSenderTest.php | 4 ++-- .../Model/Order/Email/Sender/CreditmemoSenderTest.php | 10 ++++++---- .../Model/Order/Email/Sender/InvoiceSenderTest.php | 8 ++++---- .../Model/Order/Email/Sender/ShipmentSenderTest.php | 8 ++++---- .../Model/Order/Invoice/Sender/EmailSenderTest.php | 4 ++-- .../Model/Order/Shipment/Sender/EmailSenderTest.php | 4 ++-- 12 files changed, 38 insertions(+), 24 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo/Sender/EmailSender.php b/app/code/Magento/Sales/Model/Order/Creditmemo/Sender/EmailSender.php index ecd5670a319e7..3d2c13cbaaaa9 100644 --- a/app/code/Magento/Sales/Model/Order/Creditmemo/Sender/EmailSender.php +++ b/app/code/Magento/Sales/Model/Order/Creditmemo/Sender/EmailSender.php @@ -89,6 +89,7 @@ public function __construct( * @param bool $forceSyncMode * * @return bool + * @throws \Exception */ public function send( \Magento\Sales\Api\Data\OrderInterface $order, @@ -96,7 +97,7 @@ public function send( \Magento\Sales\Api\Data\CreditmemoCommentCreationInterface $comment = null, $forceSyncMode = false ) { - $creditmemo->setSendEmail(true); + $creditmemo->setSendEmail($this->identityContainer->isEnabled()); if (!$this->globalConfig->getValue('sales_email/general/async_sending') || $forceSyncMode) { $transport = [ @@ -145,6 +146,7 @@ public function send( * @param \Magento\Sales\Api\Data\OrderInterface $order * * @return string + * @throws \Exception */ private function getPaymentHtml(\Magento\Sales\Api\Data\OrderInterface $order) { diff --git a/app/code/Magento/Sales/Model/Order/Email/Sender/CreditmemoSender.php b/app/code/Magento/Sales/Model/Order/Email/Sender/CreditmemoSender.php index 8004483583114..be7fa8296a264 100644 --- a/app/code/Magento/Sales/Model/Order/Email/Sender/CreditmemoSender.php +++ b/app/code/Magento/Sales/Model/Order/Email/Sender/CreditmemoSender.php @@ -96,10 +96,11 @@ public function __construct( * @param Creditmemo $creditmemo * @param bool $forceSyncMode * @return bool + * @throws \Exception */ public function send(Creditmemo $creditmemo, $forceSyncMode = false) { - $creditmemo->setSendEmail(true); + $creditmemo->setSendEmail($this->identityContainer->isEnabled()); if (!$this->globalConfig->getValue('sales_email/general/async_sending') || $forceSyncMode) { $order = $creditmemo->getOrder(); @@ -146,6 +147,7 @@ public function send(Creditmemo $creditmemo, $forceSyncMode = false) * * @param Order $order * @return string + * @throws \Exception */ protected function getPaymentHtml(Order $order) { diff --git a/app/code/Magento/Sales/Model/Order/Email/Sender/InvoiceSender.php b/app/code/Magento/Sales/Model/Order/Email/Sender/InvoiceSender.php index 994fd79945cfd..bd67de7322a62 100644 --- a/app/code/Magento/Sales/Model/Order/Email/Sender/InvoiceSender.php +++ b/app/code/Magento/Sales/Model/Order/Email/Sender/InvoiceSender.php @@ -96,10 +96,11 @@ public function __construct( * @param Invoice $invoice * @param bool $forceSyncMode * @return bool + * @throws \Exception */ public function send(Invoice $invoice, $forceSyncMode = false) { - $invoice->setSendEmail(true); + $invoice->setSendEmail($this->identityContainer->isEnabled()); if (!$this->globalConfig->getValue('sales_email/general/async_sending') || $forceSyncMode) { $order = $invoice->getOrder(); @@ -146,6 +147,7 @@ public function send(Invoice $invoice, $forceSyncMode = false) * * @param Order $order * @return string + * @throws \Exception */ protected function getPaymentHtml(Order $order) { diff --git a/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentSender.php b/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentSender.php index 6729c746f5565..2b10d25b87a04 100644 --- a/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentSender.php +++ b/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentSender.php @@ -96,10 +96,11 @@ public function __construct( * @param Shipment $shipment * @param bool $forceSyncMode * @return bool + * @throws \Exception */ public function send(Shipment $shipment, $forceSyncMode = false) { - $shipment->setSendEmail(true); + $shipment->setSendEmail($this->identityContainer->isEnabled()); if (!$this->globalConfig->getValue('sales_email/general/async_sending') || $forceSyncMode) { $order = $shipment->getOrder(); @@ -146,6 +147,7 @@ public function send(Shipment $shipment, $forceSyncMode = false) * * @param Order $order * @return string + * @throws \Exception */ protected function getPaymentHtml(Order $order) { diff --git a/app/code/Magento/Sales/Model/Order/Invoice/Sender/EmailSender.php b/app/code/Magento/Sales/Model/Order/Invoice/Sender/EmailSender.php index aa0687bee504f..5ae3306ddf75b 100644 --- a/app/code/Magento/Sales/Model/Order/Invoice/Sender/EmailSender.php +++ b/app/code/Magento/Sales/Model/Order/Invoice/Sender/EmailSender.php @@ -89,6 +89,7 @@ public function __construct( * @param bool $forceSyncMode * * @return bool + * @throws \Exception */ public function send( \Magento\Sales\Api\Data\OrderInterface $order, @@ -96,7 +97,7 @@ public function send( \Magento\Sales\Api\Data\InvoiceCommentCreationInterface $comment = null, $forceSyncMode = false ) { - $invoice->setSendEmail(true); + $invoice->setSendEmail($this->identityContainer->isEnabled()); if (!$this->globalConfig->getValue('sales_email/general/async_sending') || $forceSyncMode) { $transport = [ @@ -145,6 +146,7 @@ public function send( * @param \Magento\Sales\Api\Data\OrderInterface $order * * @return string + * @throws \Exception */ private function getPaymentHtml(\Magento\Sales\Api\Data\OrderInterface $order) { diff --git a/app/code/Magento/Sales/Model/Order/Shipment/Sender/EmailSender.php b/app/code/Magento/Sales/Model/Order/Shipment/Sender/EmailSender.php index 0a393548069f5..3657f84d4445d 100644 --- a/app/code/Magento/Sales/Model/Order/Shipment/Sender/EmailSender.php +++ b/app/code/Magento/Sales/Model/Order/Shipment/Sender/EmailSender.php @@ -89,6 +89,7 @@ public function __construct( * @param bool $forceSyncMode * * @return bool + * @throws \Exception */ public function send( \Magento\Sales\Api\Data\OrderInterface $order, @@ -96,7 +97,7 @@ public function send( \Magento\Sales\Api\Data\ShipmentCommentCreationInterface $comment = null, $forceSyncMode = false ) { - $shipment->setSendEmail(true); + $shipment->setSendEmail($this->identityContainer->isEnabled()); if (!$this->globalConfig->getValue('sales_email/general/async_sending') || $forceSyncMode) { $transport = [ @@ -145,6 +146,7 @@ public function send( * @param \Magento\Sales\Api\Data\OrderInterface $order * * @return string + * @throws \Exception */ private function getPaymentHtml(\Magento\Sales\Api\Data\OrderInterface $order) { diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Sender/EmailSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Sender/EmailSenderTest.php index 9fd2a8b0d929f..859fbde31f5d8 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Sender/EmailSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Sender/EmailSenderTest.php @@ -249,7 +249,7 @@ public function testSend($configValue, $forceSyncMode, $isComment, $emailSending $this->creditmemoMock->expects($this->once()) ->method('setSendEmail') - ->with(true); + ->with($emailSendingResult); if (!$configValue || $forceSyncMode) { $transport = [ @@ -279,7 +279,7 @@ public function testSend($configValue, $forceSyncMode, $isComment, $emailSending ->method('setTemplateVars') ->with($transport->getData()); - $this->identityContainerMock->expects($this->once()) + $this->identityContainerMock->expects($this->exactly(2)) ->method('isEnabled') ->willReturn($emailSendingResult); diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoSenderTest.php index 31bf846689230..02a2bbec72389 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoSenderTest.php @@ -90,7 +90,7 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema $this->creditmemoMock->expects($this->once()) ->method('setSendEmail') - ->with(true); + ->with($emailSendingResult); $this->globalConfig->expects($this->once()) ->method('getValue') @@ -130,7 +130,7 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema ] ); - $this->identityContainerMock->expects($this->once()) + $this->identityContainerMock->expects($this->exactly(2)) ->method('isEnabled') ->willReturn($emailSendingResult); @@ -197,6 +197,8 @@ public function sendDataProvider() * @param bool $isVirtualOrder * @param int $formatCallCount * @param string|null $expectedShippingAddress + * + * @return void * @dataProvider sendVirtualOrderDataProvider */ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expectedShippingAddress) @@ -207,7 +209,7 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte $this->creditmemoMock->expects($this->once()) ->method('setSendEmail') - ->with(true); + ->with(false); $this->globalConfig->expects($this->once()) ->method('getValue') @@ -242,7 +244,7 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte ] ); - $this->identityContainerMock->expects($this->once()) + $this->identityContainerMock->expects($this->exactly(2)) ->method('isEnabled') ->willReturn(false); diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceSenderTest.php index 9c54c716e4207..ba2f1166baf3c 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceSenderTest.php @@ -90,7 +90,7 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema $this->invoiceMock->expects($this->once()) ->method('setSendEmail') - ->with(true); + ->with($emailSendingResult); $this->globalConfig->expects($this->once()) ->method('getValue') @@ -136,7 +136,7 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema ] ); - $this->identityContainerMock->expects($this->once()) + $this->identityContainerMock->expects($this->exactly(2)) ->method('isEnabled') ->willReturn($emailSendingResult); @@ -212,7 +212,7 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte $this->invoiceMock->expects($this->once()) ->method('setSendEmail') - ->with(true); + ->with(false); $this->globalConfig->expects($this->once()) ->method('getValue') @@ -247,7 +247,7 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte ] ); - $this->identityContainerMock->expects($this->once()) + $this->identityContainerMock->expects($this->exactly(2)) ->method('isEnabled') ->willReturn(false); diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php index b1b18af63b590..8a71c738e9fbe 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php @@ -90,7 +90,7 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema $this->shipmentMock->expects($this->once()) ->method('setSendEmail') - ->with(true); + ->with($emailSendingResult); $this->globalConfig->expects($this->once()) ->method('getValue') @@ -136,7 +136,7 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema ] ); - $this->identityContainerMock->expects($this->once()) + $this->identityContainerMock->expects($this->exactly(2)) ->method('isEnabled') ->willReturn($emailSendingResult); @@ -212,7 +212,7 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte $this->shipmentMock->expects($this->once()) ->method('setSendEmail') - ->with(true); + ->with(false); $this->globalConfig->expects($this->once()) ->method('getValue') @@ -247,7 +247,7 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte ] ); - $this->identityContainerMock->expects($this->once()) + $this->identityContainerMock->expects($this->exactly(2)) ->method('isEnabled') ->willReturn(false); $this->shipmentResourceMock->expects($this->once()) diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Invoice/Sender/EmailSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Invoice/Sender/EmailSenderTest.php index 8a4e2920ba207..dcf689cf7d53b 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Invoice/Sender/EmailSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Invoice/Sender/EmailSenderTest.php @@ -247,7 +247,7 @@ public function testSend($configValue, $forceSyncMode, $isComment, $emailSending $this->invoiceMock->expects($this->once()) ->method('setSendEmail') - ->with(true); + ->with($emailSendingResult); if (!$configValue || $forceSyncMode) { $transport = [ @@ -277,7 +277,7 @@ public function testSend($configValue, $forceSyncMode, $isComment, $emailSending ->method('setTemplateVars') ->with($transport->getData()); - $this->identityContainerMock->expects($this->once()) + $this->identityContainerMock->expects($this->exactly(2)) ->method('isEnabled') ->willReturn($emailSendingResult); diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Sender/EmailSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Sender/EmailSenderTest.php index 94347e8b32d54..391e99ba6f835 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Sender/EmailSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Shipment/Sender/EmailSenderTest.php @@ -249,7 +249,7 @@ public function testSend($configValue, $forceSyncMode, $isComment, $emailSending $this->shipmentMock->expects($this->once()) ->method('setSendEmail') - ->with(true); + ->with($emailSendingResult); if (!$configValue || $forceSyncMode) { $transport = [ @@ -279,7 +279,7 @@ public function testSend($configValue, $forceSyncMode, $isComment, $emailSending ->method('setTemplateVars') ->with($transport->getData()); - $this->identityContainerMock->expects($this->once()) + $this->identityContainerMock->expects($this->exactly(2)) ->method('isEnabled') ->willReturn($emailSendingResult); From 2a9b75f103b12840803482c7d1594b47c90ca432 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 2 Apr 2019 13:53:22 +0300 Subject: [PATCH 1032/1295] MAGETWO-94426: Admin date wrong formatting for French locale --- .../StorefrontCustomerAccountActionGroup.xml | 6 ++++++ .../StorefrontCustomerAccountInformationPage.xml | 15 +++++++++++++++ ...torefrontCustomerAccountInformationSection.xml | 2 +- 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerAccountInformationPage.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerAccountActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerAccountActionGroup.xml index 50a238323e331..aa764e5f51de1 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerAccountActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerAccountActionGroup.xml @@ -17,4 +17,10 @@ <waitForPageLoad stepKey="waitForPageLoad"/> <see selector="{{StorefrontHeaderSection.mainTitle}}" userInput="{{tabName}}" stepKey="checkTabTitle"/> </actionGroup> + + <!--Go to Storefront > Account Information--> + <actionGroup name="StorefrontStartCustomerAccountInformationEdit"> + <amOnPage url="{{StorefrontCustomerAccountInformationPage.url}}" stepKey="goToAccountInformationEditPage"/> + <see selector="{{StorefrontCustomerAccountInformationSection.title}}" userInput="Edit Account Information" stepKey="seeEditAccountInformationPageTitle"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerAccountInformationPage.xml b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerAccountInformationPage.xml new file mode 100644 index 0000000000000..80caea5a1f541 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerAccountInformationPage.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="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="StorefrontCustomerAccountInformationPage" url="/customer/account/edit" area="storefront" module="Magento_Customer"> + <section name="StorefrontCustomerSidebarSection"/> + <section name="StorefrontCustomerAccountInformationSection" /> + </page> +</pages> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountInformationSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountInformationSection.xml index 361d245676576..f6706d0e16ab3 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountInformationSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountInformationSection.xml @@ -15,6 +15,6 @@ <element name="changeEmail" type="checkbox" selector="#change_email"/> <element name="changePassword" type="checkbox" selector="#change_password"/> <element name="customAttributeFiled" type="input" selector="#{{attribute_code}}" parameterized="true"/> - <element name="saveButton" type="button" selector="#form-validate .action.save.primary"/> + <element name="saveButton" type="button" selector="#form-validate .action.save.primary" timeout="30"/> </section> </sections> From 7b97783101becbc19131de71440c362622cdaa9f Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Tue, 2 Apr 2019 14:13:40 +0300 Subject: [PATCH 1033/1295] MAGETWO-74455: Product duplication allows to save non-unique values for product attributes --- app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php | 2 +- .../Catalog/Test/Unit/Controller/Adminhtml/Product/SaveTest.php | 2 +- 2 files 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 fd44e85868609..7a382d1cb31bc 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Save.php @@ -334,7 +334,7 @@ private function checkUniqueAttributes(\Magento\Catalog\Model\Product $product) $uniqueLabels = []; foreach ($product->getAttributes() as $attribute) { if ($attribute->getIsUnique() && $attribute->getIsUserDefined() - && $product->getData($attribute->getAttributeCode()) !== null + && !empty($product->getData($attribute->getAttributeCode())) ) { $uniqueLabels[] = $attribute->getDefaultFrontendLabel(); } diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/SaveTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/SaveTest.php index 0c7ce74a216bb..22dbc32c2c3ab 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/SaveTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/SaveTest.php @@ -83,7 +83,7 @@ protected function setUp() 'getStoreId', 'save', '__sleep', - '__wakeup' + '__wakeup', ]) ->getMock(); $this->product->expects($this->any())->method('getTypeId')->will($this->returnValue('simple')); From 330d202567bf262040d6997a71e4e42505915a41 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Tue, 2 Apr 2019 14:49:16 +0300 Subject: [PATCH 1034/1295] Fix functional test. --- .../Mftf/Section/StorefrontCustomerWishlistSection.xml | 2 +- .../Test/StorefrontDeletePersistedWishlistTest.xml | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistSection.xml b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistSection.xml index 5d40d9a38d656..bfacc8093bede 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistSection.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistSection.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="StorefrontCustomerWishlistSection"> <element name="productItemNameText" type="text" selector=".products-grid .product-item-name a"/> - <element name="removeWishlistButton" type="button" selector=".products-grid .btn-remove.action.delete>span" timeout="30"/> + <element name="removeWishlistButton" type="button" selector=".products-grid .btn-remove.action.delete" timeout="30"/> <element name="emptyWishlistText" type="text" selector=".message.info.empty>span"/> <element name="successMsg" type="text" selector="div.message-success.success.message"/> </section> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeletePersistedWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeletePersistedWishlistTest.xml index 66c636871c39a..2f17c29a6556b 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeletePersistedWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeletePersistedWishlistTest.xml @@ -6,14 +6,16 @@ */ --> -<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"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontDeletePersistedWishlistTest"> <annotations> - <features value="Delete a persist wishlist for a customer"/> + <features value="Wishlist"/> <stories value="Delete a persist wishlist for a customer"/> - <title value="Delete a persist wishlist for a customer"/> - <description value="Delete a persist wishlist for a customer"/> + <title value="Customer should be able to delete a persistent wishlist"/> + <description value="Customer should be able to delete a persistent wishlist"/> + <severity value="AVERAGE"/> <group value="wishlist"/> + <testCaseId value="MC-4110"/> </annotations> <before> <createData stepKey="category" entity="SimpleSubCategory"/> From db9285797f689ac22b7a2a140fe5d11d8a20276c Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Tue, 2 Apr 2019 14:53:58 +0300 Subject: [PATCH 1035/1295] MAGETWO-72961: Customer Address "default billing address" Attribute Not Used in Checkout #8777 --- .../GuestCheckoutFillNewBillingAddressActionGroup.xml | 9 +++++++++ .../Test/Mftf/Section/CheckoutPaymentSection.xml | 2 +- ...ultBillingAddressShouldBeCheckedOnPaymentPageTest.xml | 2 +- ...CheckoutTestWithRestrictedCountriesForPaymentTest.xml | 6 +++--- ...stomerPlaceOrderWithNewAddressesThatWasEditedTest.xml | 1 + ...CheckoutTestWithRestrictedCountriesForPaymentTest.xml | 5 +++-- 6 files changed, 18 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml index 11cd095334113..93ed6f2c61d40 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml @@ -56,4 +56,13 @@ <selectOption selector="{{classPrefix}} {{CheckoutShippingSection.country}}" userInput="" stepKey="clearFieldCounty"/> <clearField selector="{{classPrefix}} {{CheckoutShippingSection.telephone}}" stepKey="clearFieldPhoneNumber"/> </actionGroup> + + <actionGroup name="GuestCheckoutFillNewBillingAddressActionGroupWithoutEmail" + extends="GuestCheckoutFillNewBillingAddressActionGroup"> + <arguments> + <argument name="classPrefix" type="string" defaultValue=""/> + </arguments> + <remove keyForRemoval="enterEmail"/> + <selectOption stepKey="selectCounty" selector="{{classPrefix}} {{CheckoutShippingSection.country}}" userInput="{{customerAddressVar.country_id}}"/> + </actionGroup> </actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml index fd0d9b6768443..7c19e969daad3 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml @@ -46,6 +46,6 @@ <element name="noPaymentMethods" type="text" selector=".no-quotes-block"/> <element name="billingAddressSelectShared" type="select" selector=".checkout-billing-address select[name='billing_address_id']"/> <element name="billingAddressSameAsShippingShared" type="checkbox" selector="#billing-address-same-as-shipping-shared"/> - <element name="addressAction" type="button" selector="//span[text()='{{action}}']" parameterized="true"/> + <element name="addressAction" type="button" selector="//div[@class='actions-toolbar']//span[text()='{{action}}']" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/DefaultBillingAddressShouldBeCheckedOnPaymentPageTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/DefaultBillingAddressShouldBeCheckedOnPaymentPageTest.xml index 5dfc08c9df302..256c59ae05c20 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/DefaultBillingAddressShouldBeCheckedOnPaymentPageTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/DefaultBillingAddressShouldBeCheckedOnPaymentPageTest.xml @@ -51,7 +51,7 @@ <dontSeeElement selector="{{CheckoutAddressPopupSection.newAddressModalPopup}}" stepKey="dontSeeModalPopup"/> <!--Select Shipping Rate "Flat Rate" and click "Next" button--> <actionGroup ref="CheckoutSelectFlatRateShippingMethodActionGroup" stepKey="selectFlatRateShipping"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask2"/> + <waitForLoadingMaskToDisappear stepKey="waitForShippingMethodMaskDisappear"/> <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="clickNext"/> <waitForPageLoad stepKey="waitForAddressSaved"/> <!--Verify that "My billing and shipping address are the same" is unchecked and billing address is preselected--> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml index 5f0b6b35c53cb..fef6b9a203735 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTestWithRestrictedCountriesForPaymentTest.xml @@ -38,10 +38,10 @@ <waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" after="seeRegion" stepKey="waitNextButton"/> <actionGroup ref="CheckoutSelectFlatRateShippingMethodActionGroup" after="waitNextButton" stepKey="selectShippingMethod"/> <click selector="{{CheckoutShippingMethodsSection.next}}" after="selectShippingMethod" stepKey="clickNextButton" /> + <waitForPageLoad after="clickNextButton" stepKey="waitForPaymentStep"/> <selectOption selector="{{CheckoutPaymentSection.billingAddressSelectShared}}" userInput="New Address" after="uncheckBillingAddressSameAsShippingCheckCheckBox" stepKey="clickOnNewAddress"/> - <waitForPageLoad after="clickNextButton" stepKey="waitBillingForm"/> - <seeElement selector="{{CheckoutPaymentSection.isPaymentSection}}" after="waitBillingForm" stepKey="checkoutPaymentStepIsOpened"/> - <actionGroup ref="CheckoutPlaceOrderActionGroup" after="selectCheckMoneyOrderPayment" stepKey="customerPlaceorder"> + <waitForPageLoad stepKey="waitBillingAddressForm"/> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="customerPlaceorder"> <argument name="orderNumberMessage" value="CONST.successCheckoutOrderNumberMessage" /> <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage" /> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml index 9163be0b9b0f1..f2c41e0a08763 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml @@ -71,6 +71,7 @@ <!--Refresh Page and Place Order--> <reloadPage stepKey="reloadPage"/> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectPaymentMethod"/> <actionGroup ref="ClickPlaceOrderActionGroup" stepKey="placeOrder"/> <grabTextFrom selector="{{CheckoutSuccessMainSection.orderLink}}" stepKey="grabOrderNumber"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml index bce6db7bab205..bd5d01c7f5f92 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml @@ -57,8 +57,9 @@ <!-- Fill UK Address and verify that payment available and checkout successful --> <uncheckOption selector="{{CheckoutPaymentSection.billingAddressSameAsShippingShared}}" stepKey="uncheckBillingAddressSameAsShippingCheckCheckBox"/> <waitForPageLoad stepKey="waitNewAddressBillingForm"/> - <actionGroup ref="LoggedInCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress"> - <argument name="customerAddress" value="UK_Default_Address"/> + <actionGroup ref="GuestCheckoutFillNewBillingAddressActionGroupWithoutEmail" stepKey="guestCheckoutFillingShippingAddress"> + <argument name="customerVar" value="CustomerEntityOne" /> + <argument name="customerAddressVar" value="UK_Default_Address" /> <argument name="classPrefix" value="[aria-hidden=false]"/> </actionGroup> <click selector="{{CheckoutPaymentSection.addressAction('Update')}}" stepKey="clickUpdateBillingAddressButton" /> From 701d6861b427f35169486812eefeb8e4b8a8a011 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Tue, 2 Apr 2019 17:56:56 +0300 Subject: [PATCH 1036/1295] MAGETWO-72961: Customer Address "default billing address" Attribute Not Used in Checkout #8777 --- .../GuestCheckoutFillNewBillingAddressActionGroup.xml | 7 ++----- .../Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml | 1 + ...stCheckoutTestWithRestrictedCountriesForPaymentTest.xml | 3 +-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml index 93ed6f2c61d40..7fe9fcb74719d 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml @@ -57,12 +57,9 @@ <clearField selector="{{classPrefix}} {{CheckoutShippingSection.telephone}}" stepKey="clearFieldPhoneNumber"/> </actionGroup> - <actionGroup name="GuestCheckoutFillNewBillingAddressActionGroupWithoutEmail" + <actionGroup name="GuestCheckoutFillNewBillingAddressWithoutEmailActionGroup" extends="GuestCheckoutFillNewBillingAddressActionGroup"> - <arguments> - <argument name="classPrefix" type="string" defaultValue=""/> - </arguments> <remove keyForRemoval="enterEmail"/> - <selectOption stepKey="selectCounty" selector="{{classPrefix}} {{CheckoutShippingSection.country}}" userInput="{{customerAddressVar.country_id}}"/> + <selectOption selector="{{CheckoutPaymentSection.country}}" userInput="{{customerAddressVar.country_id}}" stepKey="selectCounty" /> </actionGroup> </actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml index 7c19e969daad3..ee9f9236fdec6 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml @@ -22,6 +22,7 @@ <element name="guestRegion" type="select" selector=".billing-address-form select[name*='region_id']"/> <element name="guestPostcode" type="input" selector=".billing-address-form input[name*='postcode']"/> <element name="guestTelephone" type="input" selector=".billing-address-form input[name*='telephone']"/> + <element name="country" type="select" selector=".billing-address-form select[name='country_id']"/> <element name="cartItems" type="text" selector=".minicart-items"/> <element name="billingAddress" type="text" selector="div.billing-address-details"/> <element name="placeOrder" type="button" selector=".payment-method._active button.action.primary.checkout" timeout="30"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml index bd5d01c7f5f92..49018d6d9df4c 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTestWithRestrictedCountriesForPaymentTest.xml @@ -57,10 +57,9 @@ <!-- Fill UK Address and verify that payment available and checkout successful --> <uncheckOption selector="{{CheckoutPaymentSection.billingAddressSameAsShippingShared}}" stepKey="uncheckBillingAddressSameAsShippingCheckCheckBox"/> <waitForPageLoad stepKey="waitNewAddressBillingForm"/> - <actionGroup ref="GuestCheckoutFillNewBillingAddressActionGroupWithoutEmail" stepKey="guestCheckoutFillingShippingAddress"> + <actionGroup ref="GuestCheckoutFillNewBillingAddressWithoutEmailActionGroup" stepKey="guestCheckoutFillingShippingAddress"> <argument name="customerVar" value="CustomerEntityOne" /> <argument name="customerAddressVar" value="UK_Default_Address" /> - <argument name="classPrefix" value="[aria-hidden=false]"/> </actionGroup> <click selector="{{CheckoutPaymentSection.addressAction('Update')}}" stepKey="clickUpdateBillingAddressButton" /> <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyOrderPayment" /> From 893ce79938bad222c6a254a783d154b10f03f466 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Tue, 2 Apr 2019 18:01:41 +0300 Subject: [PATCH 1037/1295] MAGETWO-72961: Customer Address "default billing address" Attribute Not Used in Checkout #8777 --- .../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 ee9f9236fdec6..823a670d78aa9 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml @@ -22,7 +22,7 @@ <element name="guestRegion" type="select" selector=".billing-address-form select[name*='region_id']"/> <element name="guestPostcode" type="input" selector=".billing-address-form input[name*='postcode']"/> <element name="guestTelephone" type="input" selector=".billing-address-form input[name*='telephone']"/> - <element name="country" type="select" selector=".billing-address-form select[name='country_id']"/> + <element name="country" type="select" selector=".billing-address-form select[name*='country_id']"/> <element name="cartItems" type="text" selector=".minicart-items"/> <element name="billingAddress" type="text" selector="div.billing-address-details"/> <element name="placeOrder" type="button" selector=".payment-method._active button.action.primary.checkout" timeout="30"/> From fcf2dace00c983d64b6677f6bb36e0f4e206ccd2 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Tue, 2 Apr 2019 10:25:03 -0500 Subject: [PATCH 1038/1295] MAGETWO-98774: [2.2.x] Token Abuse in PayPal Payflow module - Added PayPal Captcha module that provides a possibility to enable Captcha validation on Payflow Pro payment form --- app/code/Magento/PaypalCaptcha/LICENSE.txt | 48 +++ .../Magento/PaypalCaptcha/LICENSE_AFL.txt | 48 +++ .../Model/Checkout/ConfigProviderPayPal.php | 135 ++++++ .../Observer/CaptchaRequestToken.php | 76 ++++ app/code/Magento/PaypalCaptcha/README.md | 1 + app/code/Magento/PaypalCaptcha/composer.json | 31 ++ .../PaypalCaptcha/etc/adminhtml/system.xml | 18 + app/code/Magento/PaypalCaptcha/etc/config.xml | 30 ++ .../Magento/PaypalCaptcha/etc/frontend/di.xml | 16 + .../PaypalCaptcha/etc/frontend/events.xml | 12 + app/code/Magento/PaypalCaptcha/etc/module.xml | 15 + .../Magento/PaypalCaptcha/registration.php | 9 + .../frontend/layout/checkout_index_index.xml | 56 +++ .../view/frontend/requirejs-config.js | 14 + .../web/js/view/checkout/paymentCaptcha.js | 44 ++ .../web/js/view/payment/list-mixin.js | 54 +++ .../web/template/payment/payflowpro-form.html | 90 ++++ composer.json | 1 + composer.lock | 395 +++++++++++------- 19 files changed, 933 insertions(+), 160 deletions(-) create mode 100644 app/code/Magento/PaypalCaptcha/LICENSE.txt create mode 100644 app/code/Magento/PaypalCaptcha/LICENSE_AFL.txt create mode 100644 app/code/Magento/PaypalCaptcha/Model/Checkout/ConfigProviderPayPal.php create mode 100644 app/code/Magento/PaypalCaptcha/Observer/CaptchaRequestToken.php create mode 100644 app/code/Magento/PaypalCaptcha/README.md create mode 100644 app/code/Magento/PaypalCaptcha/composer.json create mode 100644 app/code/Magento/PaypalCaptcha/etc/adminhtml/system.xml create mode 100644 app/code/Magento/PaypalCaptcha/etc/config.xml create mode 100644 app/code/Magento/PaypalCaptcha/etc/frontend/di.xml create mode 100644 app/code/Magento/PaypalCaptcha/etc/frontend/events.xml create mode 100644 app/code/Magento/PaypalCaptcha/etc/module.xml create mode 100644 app/code/Magento/PaypalCaptcha/registration.php create mode 100644 app/code/Magento/PaypalCaptcha/view/frontend/layout/checkout_index_index.xml create mode 100644 app/code/Magento/PaypalCaptcha/view/frontend/requirejs-config.js create mode 100644 app/code/Magento/PaypalCaptcha/view/frontend/web/js/view/checkout/paymentCaptcha.js create mode 100644 app/code/Magento/PaypalCaptcha/view/frontend/web/js/view/payment/list-mixin.js create mode 100644 app/code/Magento/PaypalCaptcha/view/frontend/web/template/payment/payflowpro-form.html diff --git a/app/code/Magento/PaypalCaptcha/LICENSE.txt b/app/code/Magento/PaypalCaptcha/LICENSE.txt new file mode 100644 index 0000000000000..49525fd99da9c --- /dev/null +++ b/app/code/Magento/PaypalCaptcha/LICENSE.txt @@ -0,0 +1,48 @@ + +Open Software License ("OSL") v. 3.0 + +This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Open Software License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/app/code/Magento/PaypalCaptcha/LICENSE_AFL.txt b/app/code/Magento/PaypalCaptcha/LICENSE_AFL.txt new file mode 100644 index 0000000000000..f39d641b18a19 --- /dev/null +++ b/app/code/Magento/PaypalCaptcha/LICENSE_AFL.txt @@ -0,0 +1,48 @@ + +Academic Free License ("AFL") v. 3.0 + +This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Academic Free License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/app/code/Magento/PaypalCaptcha/Model/Checkout/ConfigProviderPayPal.php b/app/code/Magento/PaypalCaptcha/Model/Checkout/ConfigProviderPayPal.php new file mode 100644 index 0000000000000..289a1631ed1f6 --- /dev/null +++ b/app/code/Magento/PaypalCaptcha/Model/Checkout/ConfigProviderPayPal.php @@ -0,0 +1,135 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\PaypalCaptcha\Model\Checkout; + +use Magento\Captcha\Helper\Data; +use Magento\Captcha\Model\CaptchaInterface; +use Magento\Checkout\Model\ConfigProviderInterface; +use Magento\Store\Model\StoreManagerInterface; + +/** + * Configuration provider for Captcha rendering. + */ +class ConfigProviderPayPal implements ConfigProviderInterface +{ + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @var Data + */ + private $captchaData; + + /** + * @var string + */ + private static $formId = 'co-payment-form'; + + /** + * @param StoreManagerInterface $storeManager + * @param Data $captchaData + */ + public function __construct( + StoreManagerInterface $storeManager, + Data $captchaData + ) { + $this->storeManager = $storeManager; + $this->captchaData = $captchaData; + } + + /** + * @inheritdoc + */ + public function getConfig(): array + { + $config['captchaPayments'][self::$formId] = [ + 'isCaseSensitive' => $this->isCaseSensitive(self::$formId), + 'imageHeight' => $this->getImageHeight(self::$formId), + 'imageSrc' => $this->getImageSrc(self::$formId), + 'refreshUrl' => $this->getRefreshUrl(), + 'isRequired' => $this->isRequired(self::$formId), + 'timestamp' => time() + ]; + + return $config; + } + + /** + * Returns is captcha case sensitive + * + * @param string $formId + * @return bool + */ + private function isCaseSensitive(string $formId): bool + { + return (bool)$this->getCaptchaModel($formId)->isCaseSensitive(); + } + + /** + * Returns captcha image height + * + * @param string $formId + * @return int + */ + private function getImageHeight(string $formId): int + { + return (int)$this->getCaptchaModel($formId)->getHeight(); + } + + /** + * Returns captcha image source path + * + * @param string $formId + * @return string + */ + private function getImageSrc(string $formId): string + { + if ($this->isRequired($formId)) { + $captcha = $this->getCaptchaModel($formId); + $captcha->generate(); + return $captcha->getImgSrc(); + } + + return ''; + } + + /** + * Returns URL to controller action which returns new captcha image + * + * @return string + */ + private function getRefreshUrl(): string + { + $store = $this->storeManager->getStore(); + return $store->getUrl('captcha/refresh', ['_secure' => $store->isCurrentlySecure()]); + } + + /** + * Whether captcha is required to be inserted to this form + * + * @param string $formId + * @return bool + */ + private function isRequired(string $formId): bool + { + return (bool)$this->getCaptchaModel($formId)->isRequired(); + } + + /** + * Return captcha model for specified form + * + * @param string $formId + * @return CaptchaInterface + */ + private function getCaptchaModel(string $formId): CaptchaInterface + { + return $this->captchaData->getCaptcha($formId); + } +} diff --git a/app/code/Magento/PaypalCaptcha/Observer/CaptchaRequestToken.php b/app/code/Magento/PaypalCaptcha/Observer/CaptchaRequestToken.php new file mode 100644 index 0000000000000..e7cb282b1799b --- /dev/null +++ b/app/code/Magento/PaypalCaptcha/Observer/CaptchaRequestToken.php @@ -0,0 +1,76 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\PaypalCaptcha\Observer; + +use Magento\Captcha\Helper\Data; +use Magento\Framework\App\Action\Action; +use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\App\ActionFlag ; + +/** + * Validates Captcha for Request Token controller + */ +class CaptchaRequestToken implements ObserverInterface +{ + /** + * @var Data + */ + private $helper; + + /** + * @var Json + */ + private $jsonSerializer; + + /** + * @var ActionFlag + */ + private $actionFlag; + + /** + * @param Data $helper + * @param Json $jsonSerializer + * @param ActionFlag $actionFlag + */ + public function __construct(Data $helper, Json $jsonSerializer, ActionFlag $actionFlag) + { + $this->helper = $helper; + $this->jsonSerializer = $jsonSerializer; + $this->actionFlag = $actionFlag; + } + + /** + * @inheritdoc + */ + public function execute(Observer $observer) + { + $formId = 'co-payment-form'; + $captcha = $this->helper->getCaptcha($formId); + + if (!$captcha->isRequired()) { + return; + } + + /** @var Action $controller */ + $controller = $observer->getControllerAction(); + $word = $controller->getRequest()->getPost('captcha_string'); + if ($captcha->isCorrect($word)) { + return; + } + + $data = $this->jsonSerializer->serialize([ + 'success' => false, + 'error' => true, + 'error_messages' => __('Incorrect CAPTCHA.') + ]); + $this->actionFlag->set('', Action::FLAG_NO_DISPATCH, true); + $controller->getResponse()->representJson($data); + } +} diff --git a/app/code/Magento/PaypalCaptcha/README.md b/app/code/Magento/PaypalCaptcha/README.md new file mode 100644 index 0000000000000..71588599a5ecd --- /dev/null +++ b/app/code/Magento/PaypalCaptcha/README.md @@ -0,0 +1 @@ +The PayPal Captcha module provides a possibility to enable Captcha validation on Payflow Pro payment form. \ No newline at end of file diff --git a/app/code/Magento/PaypalCaptcha/composer.json b/app/code/Magento/PaypalCaptcha/composer.json new file mode 100644 index 0000000000000..3b47bd213874c --- /dev/null +++ b/app/code/Magento/PaypalCaptcha/composer.json @@ -0,0 +1,31 @@ +{ + "name": "magento/module-paypal-captcha", + "description": "Provides CAPTCHA validation for PayPal Payflow Pro", + "config": { + "sort-packages": true + }, + "require": { + "php": "~7.0.13|~7.1.0", + "magento/framework": "*", + "magento/module-captcha": "*", + "magento/module-checkout": "*", + "magento/module-store": "*" + }, + "suggest": { + "magento/module-paypal": "*" + }, + "type": "magento2-module", + "version": "100.2.0", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\PaypalCaptcha\\": "" + } + } +} diff --git a/app/code/Magento/PaypalCaptcha/etc/adminhtml/system.xml b/app/code/Magento/PaypalCaptcha/etc/adminhtml/system.xml new file mode 100644 index 0000000000000..12afd8ceda60e --- /dev/null +++ b/app/code/Magento/PaypalCaptcha/etc/adminhtml/system.xml @@ -0,0 +1,18 @@ +<?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:module:Magento_Config:etc/system_file.xsd"> + <system> + <section id="customer"> + <group id="captcha" translate="label" type="text" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="0"> + <field id="forms" translate="label comment" type="multiselect" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <comment>CAPTCHA for "Create user", "Forgot password", "Payflow Pro" forms is always enabled if chosen.</comment> + </field> + </group> + </section> + </system> +</config> diff --git a/app/code/Magento/PaypalCaptcha/etc/config.xml b/app/code/Magento/PaypalCaptcha/etc/config.xml new file mode 100644 index 0000000000000..133a78a42f7b4 --- /dev/null +++ b/app/code/Magento/PaypalCaptcha/etc/config.xml @@ -0,0 +1,30 @@ +<?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:module:Magento_Store:etc/config.xsd"> + <default> + <customer> + <captcha> + <shown_to_logged_in_user> + <co-payment-form>1</co-payment-form> + </shown_to_logged_in_user> + <always_for> + <co-payment-form>1</co-payment-form> + </always_for> + </captcha> + </customer> + <captcha translate="label"> + <frontend> + <areas> + <co-payment-form> + <label>Payflow Pro</label> + </co-payment-form> + </areas> + </frontend> + </captcha> + </default> +</config> diff --git a/app/code/Magento/PaypalCaptcha/etc/frontend/di.xml b/app/code/Magento/PaypalCaptcha/etc/frontend/di.xml new file mode 100644 index 0000000000000..c236d5ea04ca0 --- /dev/null +++ b/app/code/Magento/PaypalCaptcha/etc/frontend/di.xml @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\Checkout\Model\CompositeConfigProvider"> + <arguments> + <argument name="configProviders" xsi:type="array"> + <item name="paypal_captcha_config_provider" xsi:type="object">Magento\PaypalCaptcha\Model\Checkout\ConfigProviderPayPal</item> + </argument> + </arguments> + </type> +</config> diff --git a/app/code/Magento/PaypalCaptcha/etc/frontend/events.xml b/app/code/Magento/PaypalCaptcha/etc/frontend/events.xml new file mode 100644 index 0000000000000..ae706c4485d61 --- /dev/null +++ b/app/code/Magento/PaypalCaptcha/etc/frontend/events.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> + <event name="controller_action_predispatch_paypal_transparent_requestsecuretoken"> + <observer name="captcha_request_token" instance="Magento\PaypalCaptcha\Observer\CaptchaRequestToken"/> + </event> +</config> diff --git a/app/code/Magento/PaypalCaptcha/etc/module.xml b/app/code/Magento/PaypalCaptcha/etc/module.xml new file mode 100644 index 0000000000000..a456cb0584fe6 --- /dev/null +++ b/app/code/Magento/PaypalCaptcha/etc/module.xml @@ -0,0 +1,15 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + <module name="Magento_PaypalCaptcha" setup_version="2.0.0"> + <sequence> + <module name="Magento_Captcha"/> + <module name="Magento_Paypal"/> + </sequence> + </module> +</config> diff --git a/app/code/Magento/PaypalCaptcha/registration.php b/app/code/Magento/PaypalCaptcha/registration.php new file mode 100644 index 0000000000000..4dac0582a6d1b --- /dev/null +++ b/app/code/Magento/PaypalCaptcha/registration.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use \Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_PaypalCaptcha', __DIR__); diff --git a/app/code/Magento/PaypalCaptcha/view/frontend/layout/checkout_index_index.xml b/app/code/Magento/PaypalCaptcha/view/frontend/layout/checkout_index_index.xml new file mode 100644 index 0000000000000..9837068faab73 --- /dev/null +++ b/app/code/Magento/PaypalCaptcha/view/frontend/layout/checkout_index_index.xml @@ -0,0 +1,56 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> + <body> + <referenceBlock name="checkout.root"> + <arguments> + <argument name="jsLayout" xsi:type="array"> + <item name="components" xsi:type="array"> + <item name="checkout" xsi:type="array"> + <item name="children" xsi:type="array"> + <item name="steps" xsi:type="array"> + <item name="children" xsi:type="array"> + <item name="billing-step" xsi:type="array"> + <item name="children" xsi:type="array"> + <item name="payment" xsi:type="array"> + <item name="children" xsi:type="array"> + <item name="payments-list" xsi:type="array"> + <item name="children" xsi:type="array"> + <item name="paypal-captcha" xsi:type="array"> + <item name="component" xsi:type="string">uiComponent</item> + <item name="displayArea" xsi:type="string">paypal-captcha</item> + <item name="dataScope" xsi:type="string">paypal-captcha</item> + <item name="provider" xsi:type="string">checkoutProvider</item> + <item name="config" xsi:type="array"> + <item name="template" xsi:type="string">Magento_Checkout/payment/before-place-order</item> + </item> + <item name="children" xsi:type="array"> + <item name="captcha" xsi:type="array"> + <item name="component" xsi:type="string">Magento_PaypalCaptcha/js/view/checkout/paymentCaptcha</item> + <item name="displayArea" xsi:type="string">paypal-captcha</item> + <item name="formId" xsi:type="string">co-payment-form</item> + <item name="configSource" xsi:type="string">checkoutConfig</item> + </item> + </item> + </item> + </item> + </item> + </item> + </item> + </item> + </item> + </item> + </item> + </item> + </item> + </item> + </argument> + </arguments> + </referenceBlock> + </body> +</page> diff --git a/app/code/Magento/PaypalCaptcha/view/frontend/requirejs-config.js b/app/code/Magento/PaypalCaptcha/view/frontend/requirejs-config.js new file mode 100644 index 0000000000000..78e7add4ec690 --- /dev/null +++ b/app/code/Magento/PaypalCaptcha/view/frontend/requirejs-config.js @@ -0,0 +1,14 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +var config = { + config: { + mixins: { + 'Magento_Checkout/js/view/payment/list': { + 'Magento_PaypalCaptcha/js/view/payment/list-mixin': true + } + } + } +}; diff --git a/app/code/Magento/PaypalCaptcha/view/frontend/web/js/view/checkout/paymentCaptcha.js b/app/code/Magento/PaypalCaptcha/view/frontend/web/js/view/checkout/paymentCaptcha.js new file mode 100644 index 0000000000000..f8f119e3b3396 --- /dev/null +++ b/app/code/Magento/PaypalCaptcha/view/frontend/web/js/view/checkout/paymentCaptcha.js @@ -0,0 +1,44 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'jquery', + 'Magento_Captcha/js/view/checkout/defaultCaptcha', + 'Magento_Captcha/js/model/captchaList', + 'Magento_Captcha/js/model/captcha' +], +function ($, defaultCaptcha, captchaList, Captcha) { + 'use strict'; + + return defaultCaptcha.extend({ + + /** @inheritdoc */ + initialize: function () { + var captchaConfigPayment, + currentCaptcha; + + this._super(); + + if (window[this.configSource] && window[this.configSource].captchaPayments) { + captchaConfigPayment = window[this.configSource].captchaPayments; + + $.each(captchaConfigPayment, function (formId, captchaData) { + var captcha; + + captchaData.formId = formId; + captcha = Captcha(captchaData); + captchaList.add(captcha); + }); + } + + currentCaptcha = captchaList.getCaptchaByFormId(this.formId); + + if (currentCaptcha != null) { + currentCaptcha.setIsVisible(true); + this.setCurrentCaptcha(currentCaptcha); + } + } + }); +}); diff --git a/app/code/Magento/PaypalCaptcha/view/frontend/web/js/view/payment/list-mixin.js b/app/code/Magento/PaypalCaptcha/view/frontend/web/js/view/payment/list-mixin.js new file mode 100644 index 0000000000000..60172f696e9ed --- /dev/null +++ b/app/code/Magento/PaypalCaptcha/view/frontend/web/js/view/payment/list-mixin.js @@ -0,0 +1,54 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'jquery', + 'Magento_Captcha/js/model/captchaList' +], function ($, captchaList) { + 'use strict'; + + var mixin = { + + formId: 'co-payment-form', + + /** + * Sets custom template for Payflow Pro + * + * @param {Object} payment + * @returns {Object} + */ + createComponent: function (payment) { + + var component = this._super(payment); + + if (component.component === 'Magento_Paypal/js/view/payment/method-renderer/payflowpro-method') { + component.template = 'Magento_PaypalCaptcha/payment/payflowpro-form'; + $(window).off('clearTimeout') + .on('clearTimeout', this.clearTimeout.bind(this)); + } + + return component; + }, + + /** + * Overrides default window.clearTimeout() to catch errors from iframe and reload Captcha. + */ + clearTimeout: function () { + var captcha = captchaList.getCaptchaByFormId(this.formId); + + if (captcha !== null) { + captcha.refresh(); + } + clearTimeout(); + } + }; + + /** + * Overrides `Magento_Checkout/js/view/payment/list::createComponent` + */ + return function (target) { + return target.extend(mixin); + }; +}); diff --git a/app/code/Magento/PaypalCaptcha/view/frontend/web/template/payment/payflowpro-form.html b/app/code/Magento/PaypalCaptcha/view/frontend/web/template/payment/payflowpro-form.html new file mode 100644 index 0000000000000..fec5cf96b0324 --- /dev/null +++ b/app/code/Magento/PaypalCaptcha/view/frontend/web/template/payment/payflowpro-form.html @@ -0,0 +1,90 @@ +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<div class="payment-method" data-bind="css: {'_active': (getCode() == isChecked())}"> + <div class="payment-method-title field choice"> + <input type="radio" + name="payment[method]" + class="radio" + data-bind="attr: {'id': getCode()}, value: getCode(), checked: isChecked, click: selectPaymentMethod, visible: isRadioButtonVisible()"/> + <label class="label" data-bind="attr: {'for': getCode()}"> + <span data-bind="text: getTitle()"></span> + </label> + </div> + + <div class="payment-method-content"> + <!-- ko foreach: getRegion('messages') --> + <!-- ko template: getTemplate() --><!-- /ko --> + <!--/ko--> + <div class="payment-method-billing-address"> + <!-- ko foreach: $parent.getRegion(getBillingAddressFormName()) --> + <!-- ko template: getTemplate() --><!-- /ko --> + <!--/ko--> + </div> + <iframe width="0" + height="0" + data-bind="src: getSource(), attr: {id: getCode() + '-transparent-iframe', 'data-container': getCode() + '-transparent-iframe'}" + allowtransparency="true" + frameborder="0" + name="iframeTransparent" + class="payment-method-iframe"> + </iframe> + <form class="form" id="co-transparent-form" action="#" method="post" data-bind="mageInit: { + 'transparent':{ + 'context': context(), + 'controller': getControllerName(), + 'gateway': getCode(), + 'orderSaveUrl':getPlaceOrderUrl(), + 'cgiUrl': getCgiUrl(), + 'dateDelim': getDateDelim(), + 'cardFieldsMap': getCardFieldsMap(), + 'nativeAction': getSaveOrderUrl() + }, 'validation':[]}"> + + <!-- ko template: 'Magento_Payment/payment/cc-form' --><!-- /ko --> + + <!-- ko if: (isVaultEnabled())--> + <div class="field-tooltip-content"> + <input type="checkbox" + name="vault[is_enabled]" + class="checkbox-inline" + data-bind="attr: {'id': getCode() + '_enable_vault'}, checked: vaultEnabler.isActivePaymentTokenEnabler"/> + <label class="label" data-bind="attr: {'for': getCode() + '_enable_vault'}"> + <span><!-- ko i18n: 'Save credit card information for future use.'--><!-- /ko --></span> + </label> + </div> + <!-- /ko --> + </form> + <fieldset class="fieldset payment items ccard"> + <!-- ko foreach: $parent.getRegion('paypal-captcha') --> + <!-- ko template: getTemplate() --><!-- /ko --> + <!-- /ko --> + </fieldset> + + + <div class="checkout-agreements-block"> + <!-- ko foreach: $parent.getRegion('before-place-order') --> + <!-- ko template: getTemplate() --><!-- /ko --> + <!--/ko--> + </div> + <div class="actions-toolbar"> + <div class="primary"> + <button data-role="review-save" + type="submit" + data-bind=" + attr: {title: $t('Place Order')}, + enable: (getCode() == isChecked()), + click: placeOrder, + css: {disabled: !isPlaceOrderActionAllowed()} + " + class="action primary checkout" + disabled> + <span data-bind="i18n: 'Place Order'"></span> + </button> + </div> + </div> + </div> +</div> diff --git a/composer.json b/composer.json index 8d04461cc2007..8df1d26295124 100644 --- a/composer.json +++ b/composer.json @@ -155,6 +155,7 @@ "magento/module-page-cache": "100.2.5", "magento/module-payment": "100.2.6", "magento/module-paypal": "100.2.6", + "magento/module-paypal-captcha": "100.2.0", "magento/module-persistent": "100.2.4", "magento/module-product-alert": "100.2.4", "magento/module-product-video": "100.2.6", diff --git a/composer.lock b/composer.lock index 93ebf746b1682..fd19d058a6b1d 100644 --- a/composer.lock +++ b/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8f98a6f6679dd8ad27e4bab3058007aa", + "content-hash": "442a108524a23e16cbb70c8957cd3097", "packages": [ { "name": "braintree/braintree_php", @@ -337,16 +337,16 @@ }, { "name": "composer/semver", - "version": "1.4.2", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573" + "reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/c7cb9a2095a074d131b65a8a0cd294479d785573", - "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573", + "url": "https://api.github.com/repos/composer/semver/zipball/46d9139568ccb8d9e7cdd4539cab7347568a5e2e", + "reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e", "shasum": "" }, "require": { @@ -395,28 +395,27 @@ "validation", "versioning" ], - "time": "2016-08-30T16:08:34+00:00" + "time": "2019-03-19T17:25:45+00:00" }, { "name": "composer/spdx-licenses", - "version": "1.5.0", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/composer/spdx-licenses.git", - "reference": "7a9556b22bd9d4df7cad89876b00af58ef20d3a2" + "reference": "a1aa51cf3ab838b83b0867b14e56fc20fbd55b3d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/7a9556b22bd9d4df7cad89876b00af58ef20d3a2", - "reference": "7a9556b22bd9d4df7cad89876b00af58ef20d3a2", + "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/a1aa51cf3ab838b83b0867b14e56fc20fbd55b3d", + "reference": "a1aa51cf3ab838b83b0867b14e56fc20fbd55b3d", "shasum": "" }, "require": { - "php": "^5.3.2 || ^7.0" + "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5", - "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" + "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 7" }, "type": "library", "extra": { @@ -456,7 +455,7 @@ "spdx", "validator" ], - "time": "2018-11-01T09:45:54+00:00" + "time": "2019-03-26T10:23:26+00:00" }, { "name": "container-interop/container-interop", @@ -1502,25 +1501,25 @@ }, { "name": "symfony/css-selector", - "version": "v3.4.23", + "version": "v4.2.4", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "8ca29297c29b64fb3a1a135e71cb25f67f9fdccf" + "reference": "48eddf66950fa57996e1be4a55916d65c10c604a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/8ca29297c29b64fb3a1a135e71cb25f67f9fdccf", - "reference": "8ca29297c29b64fb3a1a135e71cb25f67f9fdccf", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/48eddf66950fa57996e1be4a55916d65c10c604a", + "reference": "48eddf66950fa57996e1be4a55916d65c10c604a", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^7.1.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.2-dev" } }, "autoload": { @@ -1551,7 +1550,7 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2019-01-16T09:39:14+00:00" + "time": "2019-01-16T20:31:39+00:00" }, { "name": "symfony/debug", @@ -1771,16 +1770,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.10.0", + "version": "v1.11.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "e3d826245268269cd66f8326bd8bc066687b4a19" + "reference": "82ebae02209c21113908c229e9883c419720738a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19", - "reference": "e3d826245268269cd66f8326bd8bc066687b4a19", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a", + "reference": "82ebae02209c21113908c229e9883c419720738a", "shasum": "" }, "require": { @@ -1792,7 +1791,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.11-dev" } }, "autoload": { @@ -1825,20 +1824,20 @@ "polyfill", "portable" ], - "time": "2018-08-06T14:22:27+00:00" + "time": "2019-02-06T07:57:58+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.10.0", + "version": "v1.11.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "c79c051f5b3a46be09205c73b80b346e4153e494" + "reference": "fe5e94c604826c35a32fa832f35bd036b6799609" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494", - "reference": "c79c051f5b3a46be09205c73b80b346e4153e494", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609", + "reference": "fe5e94c604826c35a32fa832f35bd036b6799609", "shasum": "" }, "require": { @@ -1850,7 +1849,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.11-dev" } }, "autoload": { @@ -1884,7 +1883,7 @@ "portable", "shim" ], - "time": "2018-09-21T13:07:52+00:00" + "time": "2019-02-06T07:57:58+00:00" }, { "name": "symfony/process", @@ -4865,21 +4864,21 @@ }, { "name": "consolidation/robo", - "version": "1.4.6", + "version": "1.4.9", "source": { "type": "git", "url": "https://github.com/consolidation/Robo.git", - "reference": "d4805a1abbc730e9a6d64ede2eba56f91a2b4eb3" + "reference": "5c6b3840a45afda1cbffbb3bb1f94dd5f9f83345" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/Robo/zipball/d4805a1abbc730e9a6d64ede2eba56f91a2b4eb3", - "reference": "d4805a1abbc730e9a6d64ede2eba56f91a2b4eb3", + "url": "https://api.github.com/repos/consolidation/Robo/zipball/5c6b3840a45afda1cbffbb3bb1f94dd5f9f83345", + "reference": "5c6b3840a45afda1cbffbb3bb1f94dd5f9f83345", "shasum": "" }, "require": { "consolidation/annotated-command": "^2.10.2", - "consolidation/config": "^1.0.10", + "consolidation/config": "^1.2", "consolidation/log": "~1", "consolidation/output-formatters": "^3.1.13", "consolidation/self-update": "^1", @@ -4969,7 +4968,7 @@ } ], "description": "Modern task runner", - "time": "2019-02-17T05:32:27+00:00" + "time": "2019-03-19T18:07:19+00:00" }, { "name": "consolidation/self-update", @@ -5082,30 +5081,30 @@ }, { "name": "doctrine/annotations", - "version": "v1.4.0", + "version": "v1.6.1", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "54cacc9b81758b14e3ce750f205a393d52339e97" + "reference": "53120e0eb10355388d6ccbe462f1fea34ddadb24" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/54cacc9b81758b14e3ce750f205a393d52339e97", - "reference": "54cacc9b81758b14e3ce750f205a393d52339e97", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/53120e0eb10355388d6ccbe462f1fea34ddadb24", + "reference": "53120e0eb10355388d6ccbe462f1fea34ddadb24", "shasum": "" }, "require": { "doctrine/lexer": "1.*", - "php": "^5.6 || ^7.0" + "php": "^7.1" }, "require-dev": { "doctrine/cache": "1.*", - "phpunit/phpunit": "^5.7" + "phpunit/phpunit": "^6.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4.x-dev" + "dev-master": "1.6.x-dev" } }, "autoload": { @@ -5146,38 +5145,40 @@ "docblock", "parser" ], - "time": "2017-02-24T16:22:25+00:00" + "time": "2019-03-25T19:12:02+00:00" }, { "name": "doctrine/collections", - "version": "v1.4.0", + "version": "v1.6.1", "source": { "type": "git", "url": "https://github.com/doctrine/collections.git", - "reference": "1a4fb7e902202c33cce8c55989b945612943c2ba" + "reference": "d2ae4ef05e25197343b6a39bae1d3c427a2f6956" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/1a4fb7e902202c33cce8c55989b945612943c2ba", - "reference": "1a4fb7e902202c33cce8c55989b945612943c2ba", + "url": "https://api.github.com/repos/doctrine/collections/zipball/d2ae4ef05e25197343b6a39bae1d3c427a2f6956", + "reference": "d2ae4ef05e25197343b6a39bae1d3c427a2f6956", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.1.3" }, "require-dev": { - "doctrine/coding-standard": "~0.1@dev", - "phpunit/phpunit": "^5.7" + "doctrine/coding-standard": "^6.0", + "phpstan/phpstan-shim": "^0.9.2", + "phpunit/phpunit": "^7.0", + "vimeo/psalm": "^3.2.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3.x-dev" + "dev-master": "1.6.x-dev" } }, "autoload": { - "psr-0": { - "Doctrine\\Common\\Collections\\": "lib/" + "psr-4": { + "Doctrine\\Common\\Collections\\": "lib/Doctrine/Common/Collections" } }, "notification-url": "https://packagist.org/downloads/", @@ -5206,43 +5207,46 @@ "email": "schmittjoh@gmail.com" } ], - "description": "Collections Abstraction library", - "homepage": "http://www.doctrine-project.org", + "description": "PHP Doctrine Collections library that adds additional functionality on top of PHP arrays.", + "homepage": "https://www.doctrine-project.org/projects/collections.html", "keywords": [ "array", "collections", - "iterator" + "iterators", + "php" ], - "time": "2017-01-03T10:49:41+00:00" + "time": "2019-03-25T19:03:48+00:00" }, { "name": "doctrine/instantiator", - "version": "1.0.5", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + "reference": "a2c590166b2133a4633738648b6b064edae0814a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a", + "reference": "a2c590166b2133a4633738648b6b064edae0814a", "shasum": "" }, "require": { - "php": ">=5.3,<8.0-DEV" + "php": "^7.1" }, "require-dev": { - "athletic/athletic": "~0.1.8", + "doctrine/coding-standard": "^6.0", "ext-pdo": "*", "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" + "phpbench/phpbench": "^0.13", + "phpstan/phpstan-phpunit": "^0.11", + "phpstan/phpstan-shim": "^0.11", + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.2.x-dev" } }, "autoload": { @@ -5262,12 +5266,12 @@ } ], "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://github.com/doctrine/instantiator", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", "keywords": [ "constructor", "instantiate" ], - "time": "2015-06-14T21:17:01+00:00" + "time": "2019-03-17T17:37:11+00:00" }, { "name": "doctrine/lexer", @@ -6404,25 +6408,28 @@ }, { "name": "myclabs/deep-copy", - "version": "1.7.0", + "version": "1.8.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" + "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", + "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.1" + }, + "replace": { + "myclabs/deep-copy": "self.version" }, "require-dev": { "doctrine/collections": "^1.0", "doctrine/common": "^2.6", - "phpunit/phpunit": "^4.1" + "phpunit/phpunit": "^7.1" }, "type": "library", "autoload": { @@ -6445,7 +6452,7 @@ "object", "object graph" ], - "time": "2017-10-19T19:58:43+00:00" + "time": "2018-06-11T23:09:50+00:00" }, { "name": "pdepend/pdepend", @@ -8103,25 +8110,25 @@ }, { "name": "symfony/browser-kit", - "version": "v3.4.23", + "version": "v4.2.4", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "c0fadd368c1031109e996316e53ffeb886d37ea1" + "reference": "61d85c5af2fc058014c7c89504c3944e73a086f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/c0fadd368c1031109e996316e53ffeb886d37ea1", - "reference": "c0fadd368c1031109e996316e53ffeb886d37ea1", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/61d85c5af2fc058014c7c89504c3944e73a086f0", + "reference": "61d85c5af2fc058014c7c89504c3944e73a086f0", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/dom-crawler": "~2.8|~3.0|~4.0" + "php": "^7.1.3", + "symfony/dom-crawler": "~3.4|~4.0" }, "require-dev": { - "symfony/css-selector": "~2.8|~3.0|~4.0", - "symfony/process": "~2.8|~3.0|~4.0" + "symfony/css-selector": "~3.4|~4.0", + "symfony/process": "~3.4|~4.0" }, "suggest": { "symfony/process": "" @@ -8129,7 +8136,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.2-dev" } }, "autoload": { @@ -8156,36 +8163,35 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2019-02-23T15:06:07+00:00" + "time": "2019-02-23T15:17:42+00:00" }, { "name": "symfony/config", - "version": "v3.4.23", + "version": "v4.2.4", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "177a276c01575253c95cefe0866e3d1b57637fe0" + "reference": "7f70d79c7a24a94f8e98abb988049403a53d7b31" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/177a276c01575253c95cefe0866e3d1b57637fe0", - "reference": "177a276c01575253c95cefe0866e3d1b57637fe0", + "url": "https://api.github.com/repos/symfony/config/zipball/7f70d79c7a24a94f8e98abb988049403a53d7b31", + "reference": "7f70d79c7a24a94f8e98abb988049403a53d7b31", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/filesystem": "~2.8|~3.0|~4.0", + "php": "^7.1.3", + "symfony/filesystem": "~3.4|~4.0", "symfony/polyfill-ctype": "~1.8" }, "conflict": { - "symfony/dependency-injection": "<3.3", - "symfony/finder": "<3.3" + "symfony/finder": "<3.4" }, "require-dev": { - "symfony/dependency-injection": "~3.3|~4.0", - "symfony/event-dispatcher": "~3.3|~4.0", - "symfony/finder": "~3.3|~4.0", - "symfony/yaml": "~3.0|~4.0" + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/finder": "~3.4|~4.0", + "symfony/yaml": "~3.4|~4.0" }, "suggest": { "symfony/yaml": "To use the yaml reference dumper" @@ -8193,7 +8199,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.2-dev" } }, "autoload": { @@ -8220,7 +8226,75 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2019-02-23T15:06:07+00:00" + "time": "2019-02-23T15:17:42+00:00" + }, + { + "name": "symfony/contracts", + "version": "v1.0.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/contracts.git", + "reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/contracts/zipball/1aa7ab2429c3d594dd70689604b5cf7421254cdf", + "reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "require-dev": { + "psr/cache": "^1.0", + "psr/container": "^1.0" + }, + "suggest": { + "psr/cache": "When using the Cache contracts", + "psr/container": "When using the Service contracts", + "symfony/cache-contracts-implementation": "", + "symfony/service-contracts-implementation": "", + "symfony/translation-contracts-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\": "" + }, + "exclude-from-classmap": [ + "**/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A set of abstractions extracted out of the Symfony components", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "time": "2018-12-05T08:06:11+00:00" }, { "name": "symfony/dependency-injection", @@ -8294,25 +8368,25 @@ }, { "name": "symfony/dom-crawler", - "version": "v3.4.23", + "version": "v4.2.4", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "d40023c057393fb25f7ca80af2a56ed948c45a09" + "reference": "53c97769814c80a84a8403efcf3ae7ae966d53bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/d40023c057393fb25f7ca80af2a56ed948c45a09", - "reference": "d40023c057393fb25f7ca80af2a56ed948c45a09", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/53c97769814c80a84a8403efcf3ae7ae966d53bb", + "reference": "53c97769814c80a84a8403efcf3ae7ae966d53bb", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", + "php": "^7.1.3", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { - "symfony/css-selector": "~2.8|~3.0|~4.0" + "symfony/css-selector": "~3.4|~4.0" }, "suggest": { "symfony/css-selector": "" @@ -8320,7 +8394,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.2-dev" } }, "autoload": { @@ -8347,34 +8421,34 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2019-02-23T15:06:07+00:00" + "time": "2019-02-23T15:17:42+00:00" }, { "name": "symfony/http-foundation", - "version": "v3.4.23", + "version": "v4.2.4", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "9a96d77ceb1fd913c9d4a89e8a7e1be87604be8a" + "reference": "850a667d6254ccf6c61d853407b16f21c4579c77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9a96d77ceb1fd913c9d4a89e8a7e1be87604be8a", - "reference": "9a96d77ceb1fd913c9d4a89e8a7e1be87604be8a", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/850a667d6254ccf6c61d853407b16f21c4579c77", + "reference": "850a667d6254ccf6c61d853407b16f21c4579c77", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-mbstring": "~1.1", - "symfony/polyfill-php70": "~1.6" + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.1" }, "require-dev": { - "symfony/expression-language": "~2.8|~3.0|~4.0" + "predis/predis": "~1.0", + "symfony/expression-language": "~3.4|~4.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.2-dev" } }, "autoload": { @@ -8401,29 +8475,29 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2019-02-23T15:06:07+00:00" + "time": "2019-02-26T08:03:39+00:00" }, { "name": "symfony/options-resolver", - "version": "v3.4.23", + "version": "v4.2.4", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "926e3b797e6bb66c0e4d7da7eff3a174f7378bcf" + "reference": "3896e5a7d06fd15fa4947694c8dcdd371ff147d1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/926e3b797e6bb66c0e4d7da7eff3a174f7378bcf", - "reference": "926e3b797e6bb66c0e4d7da7eff3a174f7378bcf", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/3896e5a7d06fd15fa4947694c8dcdd371ff147d1", + "reference": "3896e5a7d06fd15fa4947694c8dcdd371ff147d1", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^7.1.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.2-dev" } }, "autoload": { @@ -8455,20 +8529,20 @@ "configuration", "options" ], - "time": "2019-02-23T15:06:07+00:00" + "time": "2019-02-23T15:17:42+00:00" }, { "name": "symfony/polyfill-php54", - "version": "v1.10.0", + "version": "v1.11.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php54.git", - "reference": "412977e090c6a8472dc39d50d1beb7d59495a965" + "reference": "2964b17ddc32dba7bcba009d5501c84d3fba1452" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php54/zipball/412977e090c6a8472dc39d50d1beb7d59495a965", - "reference": "412977e090c6a8472dc39d50d1beb7d59495a965", + "url": "https://api.github.com/repos/symfony/polyfill-php54/zipball/2964b17ddc32dba7bcba009d5501c84d3fba1452", + "reference": "2964b17ddc32dba7bcba009d5501c84d3fba1452", "shasum": "" }, "require": { @@ -8477,7 +8551,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.11-dev" } }, "autoload": { @@ -8513,20 +8587,20 @@ "portable", "shim" ], - "time": "2018-08-06T14:22:27+00:00" + "time": "2019-02-06T07:57:58+00:00" }, { "name": "symfony/polyfill-php55", - "version": "v1.10.0", + "version": "v1.11.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php55.git", - "reference": "42a4c00a347625ac8853c3358c47eeadc7fd4e96" + "reference": "96fa25cef405ea452919559a0025d5dc16e30e4c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/42a4c00a347625ac8853c3358c47eeadc7fd4e96", - "reference": "42a4c00a347625ac8853c3358c47eeadc7fd4e96", + "url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/96fa25cef405ea452919559a0025d5dc16e30e4c", + "reference": "96fa25cef405ea452919559a0025d5dc16e30e4c", "shasum": "" }, "require": { @@ -8536,7 +8610,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.11-dev" } }, "autoload": { @@ -8569,20 +8643,20 @@ "portable", "shim" ], - "time": "2018-10-31T12:13:01+00:00" + "time": "2019-02-06T07:57:58+00:00" }, { "name": "symfony/polyfill-php70", - "version": "v1.10.0", + "version": "v1.11.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "6b88000cdd431cd2e940caa2cb569201f3f84224" + "reference": "bc4858fb611bda58719124ca079baff854149c89" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/6b88000cdd431cd2e940caa2cb569201f3f84224", - "reference": "6b88000cdd431cd2e940caa2cb569201f3f84224", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/bc4858fb611bda58719124ca079baff854149c89", + "reference": "bc4858fb611bda58719124ca079baff854149c89", "shasum": "" }, "require": { @@ -8592,7 +8666,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.11-dev" } }, "autoload": { @@ -8628,20 +8702,20 @@ "portable", "shim" ], - "time": "2018-09-21T06:26:08+00:00" + "time": "2019-02-06T07:57:58+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.10.0", + "version": "v1.11.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "9050816e2ca34a8e916c3a0ae8b9c2fccf68b631" + "reference": "ab50dcf166d5f577978419edd37aa2bb8eabce0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/9050816e2ca34a8e916c3a0ae8b9c2fccf68b631", - "reference": "9050816e2ca34a8e916c3a0ae8b9c2fccf68b631", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/ab50dcf166d5f577978419edd37aa2bb8eabce0c", + "reference": "ab50dcf166d5f577978419edd37aa2bb8eabce0c", "shasum": "" }, "require": { @@ -8650,7 +8724,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.11-dev" } }, "autoload": { @@ -8683,29 +8757,30 @@ "portable", "shim" ], - "time": "2018-09-21T13:07:52+00:00" + "time": "2019-02-06T07:57:58+00:00" }, { "name": "symfony/stopwatch", - "version": "v3.4.23", + "version": "v4.2.4", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "2a651c2645c10bbedd21170771f122d935e0dd58" + "reference": "b1a5f646d56a3290230dbc8edf2a0d62cda23f67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/2a651c2645c10bbedd21170771f122d935e0dd58", - "reference": "2a651c2645c10bbedd21170771f122d935e0dd58", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/b1a5f646d56a3290230dbc8edf2a0d62cda23f67", + "reference": "b1a5f646d56a3290230dbc8edf2a0d62cda23f67", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^7.1.3", + "symfony/contracts": "^1.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.2-dev" } }, "autoload": { @@ -8732,7 +8807,7 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2019-01-16T09:39:14+00:00" + "time": "2019-01-16T20:31:39+00:00" }, { "name": "symfony/yaml", From 7101ee34417b4696ec08a907d3bed33a314a8cd3 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Tue, 2 Apr 2019 12:34:46 -0500 Subject: [PATCH 1039/1295] MAGETWO-98774: [2.2.x] Token Abuse in PayPal Payflow module - Update composer.lock --- composer.lock | 282 ++++++++++++++++++-------------------------------- 1 file changed, 103 insertions(+), 179 deletions(-) diff --git a/composer.lock b/composer.lock index fd19d058a6b1d..acdfe237272f1 100644 --- a/composer.lock +++ b/composer.lock @@ -1501,25 +1501,25 @@ }, { "name": "symfony/css-selector", - "version": "v4.2.4", + "version": "v3.4.23", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "48eddf66950fa57996e1be4a55916d65c10c604a" + "reference": "8ca29297c29b64fb3a1a135e71cb25f67f9fdccf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/48eddf66950fa57996e1be4a55916d65c10c604a", - "reference": "48eddf66950fa57996e1be4a55916d65c10c604a", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/8ca29297c29b64fb3a1a135e71cb25f67f9fdccf", + "reference": "8ca29297c29b64fb3a1a135e71cb25f67f9fdccf", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": "^5.5.9|>=7.0.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -1550,7 +1550,7 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2019-01-16T20:31:39+00:00" + "time": "2019-01-16T09:39:14+00:00" }, { "name": "symfony/debug", @@ -5081,30 +5081,30 @@ }, { "name": "doctrine/annotations", - "version": "v1.6.1", + "version": "v1.4.0", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "53120e0eb10355388d6ccbe462f1fea34ddadb24" + "reference": "54cacc9b81758b14e3ce750f205a393d52339e97" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/53120e0eb10355388d6ccbe462f1fea34ddadb24", - "reference": "53120e0eb10355388d6ccbe462f1fea34ddadb24", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/54cacc9b81758b14e3ce750f205a393d52339e97", + "reference": "54cacc9b81758b14e3ce750f205a393d52339e97", "shasum": "" }, "require": { "doctrine/lexer": "1.*", - "php": "^7.1" + "php": "^5.6 || ^7.0" }, "require-dev": { "doctrine/cache": "1.*", - "phpunit/phpunit": "^6.4" + "phpunit/phpunit": "^5.7" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.6.x-dev" + "dev-master": "1.4.x-dev" } }, "autoload": { @@ -5145,40 +5145,38 @@ "docblock", "parser" ], - "time": "2019-03-25T19:12:02+00:00" + "time": "2017-02-24T16:22:25+00:00" }, { "name": "doctrine/collections", - "version": "v1.6.1", + "version": "v1.4.0", "source": { "type": "git", "url": "https://github.com/doctrine/collections.git", - "reference": "d2ae4ef05e25197343b6a39bae1d3c427a2f6956" + "reference": "1a4fb7e902202c33cce8c55989b945612943c2ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/d2ae4ef05e25197343b6a39bae1d3c427a2f6956", - "reference": "d2ae4ef05e25197343b6a39bae1d3c427a2f6956", + "url": "https://api.github.com/repos/doctrine/collections/zipball/1a4fb7e902202c33cce8c55989b945612943c2ba", + "reference": "1a4fb7e902202c33cce8c55989b945612943c2ba", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": "^5.6 || ^7.0" }, "require-dev": { - "doctrine/coding-standard": "^6.0", - "phpstan/phpstan-shim": "^0.9.2", - "phpunit/phpunit": "^7.0", - "vimeo/psalm": "^3.2.2" + "doctrine/coding-standard": "~0.1@dev", + "phpunit/phpunit": "^5.7" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.6.x-dev" + "dev-master": "1.3.x-dev" } }, "autoload": { - "psr-4": { - "Doctrine\\Common\\Collections\\": "lib/Doctrine/Common/Collections" + "psr-0": { + "Doctrine\\Common\\Collections\\": "lib/" } }, "notification-url": "https://packagist.org/downloads/", @@ -5207,46 +5205,43 @@ "email": "schmittjoh@gmail.com" } ], - "description": "PHP Doctrine Collections library that adds additional functionality on top of PHP arrays.", - "homepage": "https://www.doctrine-project.org/projects/collections.html", + "description": "Collections Abstraction library", + "homepage": "http://www.doctrine-project.org", "keywords": [ "array", "collections", - "iterators", - "php" + "iterator" ], - "time": "2019-03-25T19:03:48+00:00" + "time": "2017-01-03T10:49:41+00:00" }, { "name": "doctrine/instantiator", - "version": "1.2.0", + "version": "1.0.5", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "a2c590166b2133a4633738648b6b064edae0814a" + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a", - "reference": "a2c590166b2133a4633738648b6b064edae0814a", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=5.3,<8.0-DEV" }, "require-dev": { - "doctrine/coding-standard": "^6.0", + "athletic/athletic": "~0.1.8", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^0.13", - "phpstan/phpstan-phpunit": "^0.11", - "phpstan/phpstan-shim": "^0.11", - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { @@ -5266,12 +5261,12 @@ } ], "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "homepage": "https://github.com/doctrine/instantiator", "keywords": [ "constructor", "instantiate" ], - "time": "2019-03-17T17:37:11+00:00" + "time": "2015-06-14T21:17:01+00:00" }, { "name": "doctrine/lexer", @@ -6408,28 +6403,25 @@ }, { "name": "myclabs/deep-copy", - "version": "1.8.1", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8" + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", - "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", "shasum": "" }, "require": { - "php": "^7.1" - }, - "replace": { - "myclabs/deep-copy": "self.version" + "php": "^5.6 || ^7.0" }, "require-dev": { "doctrine/collections": "^1.0", "doctrine/common": "^2.6", - "phpunit/phpunit": "^7.1" + "phpunit/phpunit": "^4.1" }, "type": "library", "autoload": { @@ -6452,7 +6444,7 @@ "object", "object graph" ], - "time": "2018-06-11T23:09:50+00:00" + "time": "2017-10-19T19:58:43+00:00" }, { "name": "pdepend/pdepend", @@ -8110,25 +8102,25 @@ }, { "name": "symfony/browser-kit", - "version": "v4.2.4", + "version": "v3.4.23", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "61d85c5af2fc058014c7c89504c3944e73a086f0" + "reference": "c0fadd368c1031109e996316e53ffeb886d37ea1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/61d85c5af2fc058014c7c89504c3944e73a086f0", - "reference": "61d85c5af2fc058014c7c89504c3944e73a086f0", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/c0fadd368c1031109e996316e53ffeb886d37ea1", + "reference": "c0fadd368c1031109e996316e53ffeb886d37ea1", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/dom-crawler": "~3.4|~4.0" + "php": "^5.5.9|>=7.0.8", + "symfony/dom-crawler": "~2.8|~3.0|~4.0" }, "require-dev": { - "symfony/css-selector": "~3.4|~4.0", - "symfony/process": "~3.4|~4.0" + "symfony/css-selector": "~2.8|~3.0|~4.0", + "symfony/process": "~2.8|~3.0|~4.0" }, "suggest": { "symfony/process": "" @@ -8136,7 +8128,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -8163,35 +8155,36 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2019-02-23T15:17:42+00:00" + "time": "2019-02-23T15:06:07+00:00" }, { "name": "symfony/config", - "version": "v4.2.4", + "version": "v3.4.23", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "7f70d79c7a24a94f8e98abb988049403a53d7b31" + "reference": "177a276c01575253c95cefe0866e3d1b57637fe0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/7f70d79c7a24a94f8e98abb988049403a53d7b31", - "reference": "7f70d79c7a24a94f8e98abb988049403a53d7b31", + "url": "https://api.github.com/repos/symfony/config/zipball/177a276c01575253c95cefe0866e3d1b57637fe0", + "reference": "177a276c01575253c95cefe0866e3d1b57637fe0", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/filesystem": "~3.4|~4.0", + "php": "^5.5.9|>=7.0.8", + "symfony/filesystem": "~2.8|~3.0|~4.0", "symfony/polyfill-ctype": "~1.8" }, "conflict": { - "symfony/finder": "<3.4" + "symfony/dependency-injection": "<3.3", + "symfony/finder": "<3.3" }, "require-dev": { - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~3.4|~4.0", - "symfony/finder": "~3.4|~4.0", - "symfony/yaml": "~3.4|~4.0" + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/event-dispatcher": "~3.3|~4.0", + "symfony/finder": "~3.3|~4.0", + "symfony/yaml": "~3.0|~4.0" }, "suggest": { "symfony/yaml": "To use the yaml reference dumper" @@ -8199,7 +8192,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -8226,75 +8219,7 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2019-02-23T15:17:42+00:00" - }, - { - "name": "symfony/contracts", - "version": "v1.0.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/contracts.git", - "reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/contracts/zipball/1aa7ab2429c3d594dd70689604b5cf7421254cdf", - "reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf", - "shasum": "" - }, - "require": { - "php": "^7.1.3" - }, - "require-dev": { - "psr/cache": "^1.0", - "psr/container": "^1.0" - }, - "suggest": { - "psr/cache": "When using the Cache contracts", - "psr/container": "When using the Service contracts", - "symfony/cache-contracts-implementation": "", - "symfony/service-contracts-implementation": "", - "symfony/translation-contracts-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\": "" - }, - "exclude-from-classmap": [ - "**/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A set of abstractions extracted out of the Symfony components", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "time": "2018-12-05T08:06:11+00:00" + "time": "2019-02-23T15:06:07+00:00" }, { "name": "symfony/dependency-injection", @@ -8368,25 +8293,25 @@ }, { "name": "symfony/dom-crawler", - "version": "v4.2.4", + "version": "v3.4.23", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "53c97769814c80a84a8403efcf3ae7ae966d53bb" + "reference": "d40023c057393fb25f7ca80af2a56ed948c45a09" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/53c97769814c80a84a8403efcf3ae7ae966d53bb", - "reference": "53c97769814c80a84a8403efcf3ae7ae966d53bb", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/d40023c057393fb25f7ca80af2a56ed948c45a09", + "reference": "d40023c057393fb25f7ca80af2a56ed948c45a09", "shasum": "" }, "require": { - "php": "^7.1.3", + "php": "^5.5.9|>=7.0.8", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { - "symfony/css-selector": "~3.4|~4.0" + "symfony/css-selector": "~2.8|~3.0|~4.0" }, "suggest": { "symfony/css-selector": "" @@ -8394,7 +8319,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -8421,34 +8346,34 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2019-02-23T15:17:42+00:00" + "time": "2019-02-23T15:06:07+00:00" }, { "name": "symfony/http-foundation", - "version": "v4.2.4", + "version": "v3.4.23", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "850a667d6254ccf6c61d853407b16f21c4579c77" + "reference": "9a96d77ceb1fd913c9d4a89e8a7e1be87604be8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/850a667d6254ccf6c61d853407b16f21c4579c77", - "reference": "850a667d6254ccf6c61d853407b16f21c4579c77", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9a96d77ceb1fd913c9d4a89e8a7e1be87604be8a", + "reference": "9a96d77ceb1fd913c9d4a89e8a7e1be87604be8a", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/polyfill-mbstring": "~1.1" + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php70": "~1.6" }, "require-dev": { - "predis/predis": "~1.0", - "symfony/expression-language": "~3.4|~4.0" + "symfony/expression-language": "~2.8|~3.0|~4.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -8475,29 +8400,29 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2019-02-26T08:03:39+00:00" + "time": "2019-02-23T15:06:07+00:00" }, { "name": "symfony/options-resolver", - "version": "v4.2.4", + "version": "v3.4.23", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "3896e5a7d06fd15fa4947694c8dcdd371ff147d1" + "reference": "926e3b797e6bb66c0e4d7da7eff3a174f7378bcf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/3896e5a7d06fd15fa4947694c8dcdd371ff147d1", - "reference": "3896e5a7d06fd15fa4947694c8dcdd371ff147d1", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/926e3b797e6bb66c0e4d7da7eff3a174f7378bcf", + "reference": "926e3b797e6bb66c0e4d7da7eff3a174f7378bcf", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": "^5.5.9|>=7.0.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -8529,7 +8454,7 @@ "configuration", "options" ], - "time": "2019-02-23T15:17:42+00:00" + "time": "2019-02-23T15:06:07+00:00" }, { "name": "symfony/polyfill-php54", @@ -8761,26 +8686,25 @@ }, { "name": "symfony/stopwatch", - "version": "v4.2.4", + "version": "v3.4.23", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "b1a5f646d56a3290230dbc8edf2a0d62cda23f67" + "reference": "2a651c2645c10bbedd21170771f122d935e0dd58" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/b1a5f646d56a3290230dbc8edf2a0d62cda23f67", - "reference": "b1a5f646d56a3290230dbc8edf2a0d62cda23f67", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/2a651c2645c10bbedd21170771f122d935e0dd58", + "reference": "2a651c2645c10bbedd21170771f122d935e0dd58", "shasum": "" }, "require": { - "php": "^7.1.3", - "symfony/contracts": "^1.0" + "php": "^5.5.9|>=7.0.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -8807,7 +8731,7 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2019-01-16T20:31:39+00:00" + "time": "2019-01-16T09:39:14+00:00" }, { "name": "symfony/yaml", From 7ccf7779c22950404da5c83e4ea8ab4e2d2254a4 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Tue, 2 Apr 2019 13:53:12 -0500 Subject: [PATCH 1040/1295] MAGETWO-98774: [2.2.x] Token Abuse in PayPal Payflow module - updated composer.json --- app/code/Magento/PaypalCaptcha/composer.json | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/PaypalCaptcha/composer.json b/app/code/Magento/PaypalCaptcha/composer.json index 3b47bd213874c..c2c7032ae8060 100644 --- a/app/code/Magento/PaypalCaptcha/composer.json +++ b/app/code/Magento/PaypalCaptcha/composer.json @@ -1,24 +1,20 @@ { "name": "magento/module-paypal-captcha", - "description": "Provides CAPTCHA validation for PayPal Payflow Pro", - "config": { - "sort-packages": true - }, + "description": "N/A", "require": { "php": "~7.0.13|~7.1.0", - "magento/framework": "*", - "magento/module-captcha": "*", - "magento/module-checkout": "*", - "magento/module-store": "*" + "magento/framework": "101.0.*", + "magento/module-captcha": "100.2.*", + "magento/module-checkout": "100.2.*", + "magento/module-store": "100.2.*" }, "suggest": { - "magento/module-paypal": "*" + "magento/module-paypal": "100.2.*" }, "type": "magento2-module", "version": "100.2.0", "license": [ - "OSL-3.0", - "AFL-3.0" + "proprietary" ], "autoload": { "files": [ From 9b12d6ed33e174c0c9f53027f7b484c90a2002c9 Mon Sep 17 00:00:00 2001 From: Dmytro Yushkin <dyushkin@adobe.com> Date: Tue, 2 Apr 2019 15:32:21 -0500 Subject: [PATCH 1041/1295] MAGETWO-98670: Braintree Displaying Incorrect Ship-To Name --- .../Model/Paypal/Helper/QuoteUpdater.php | 28 +++++++++++++++++-- .../Model/Paypal/Helper/QuoteUpdaterTest.php | 6 ++-- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php b/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php index aa23fa767d1ed..11c51e07f1dd9 100644 --- a/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php +++ b/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php @@ -123,8 +123,8 @@ private function updateShippingAddress(Quote $quote, array $details) { $shippingAddress = $quote->getShippingAddress(); - $shippingAddress->setLastname($details['lastName']); - $shippingAddress->setFirstname($details['firstName']); + $shippingAddress->setLastname($this->getShippingRecipientLastName($details)); + $shippingAddress->setFirstname($this->getShippingRecipientFirstName($details)); $shippingAddress->setEmail($details['email']); $shippingAddress->setCollectShippingRates(true); @@ -188,4 +188,28 @@ private function updateAddressData(Address $address, array $addressData) $address->setSameAsBilling(false); $address->setCustomerAddressId(null); } + + /** + * Returns shipping recipient first name. + * + * @param array $details + * @return string + */ + private function getShippingRecipientFirstName(array $details) + { + return explode(' ', $details['shippingAddress']['recipientName'], 2)[0] + ?? $details['firstName']; + } + + /** + * Returns shipping recipient last name. + * + * @param array $details + * @return string + */ + private function getShippingRecipientLastName(array $details) + { + return explode(' ', $details['shippingAddress']['recipientName'], 2)[1] + ?? $details['lastName']; + } } diff --git a/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php b/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php index 7475d81a56142..b67f7d09941ca 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php @@ -165,7 +165,7 @@ private function getDetails(): array 'region' => 'IL', 'postalCode' => '60618', 'countryCodeAlpha2' => 'US', - 'recipientName' => 'John Doe', + 'recipientName' => 'Jane Smith', ], 'billingAddress' => [ 'streetAddress' => '123 Billing Street', @@ -186,9 +186,9 @@ private function getDetails(): array private function updateShippingAddressStep(array $details) { $this->shippingAddress->method('setLastname') - ->with($details['lastName']); + ->with('Smith'); $this->shippingAddress->method('setFirstname') - ->with($details['firstName']); + ->with('Jane'); $this->shippingAddress->method('setEmail') ->with($details['email']); $this->shippingAddress->method('setCollectShippingRates') From ff3c01fa1b82eecbda0faa2bc6f2e76c190c9fc7 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Wed, 3 Apr 2019 08:59:49 +0300 Subject: [PATCH 1042/1295] MC-15055: Text is not displayed correctly --- .../adminhtml/Magento/backend/web/css/source/forms/_fields.less | 1 + 1 file changed, 1 insertion(+) 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 0275ac5084dba..0218744b80a4d 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 @@ -526,6 +526,7 @@ & > .admin__field-label { #mix-grid .column(@field-label-grid__column, @field-grid__columns); + background: @color-white; cursor: pointer; left: 0; position: absolute; From 1c5c0c86437ed6cc68aeb6ed50b0d12f490c6a27 Mon Sep 17 00:00:00 2001 From: Jeff Coleman <jeff@jeffcolemanwrites.com> Date: Wed, 3 Apr 2019 00:42:49 -0700 Subject: [PATCH 1043/1295] fix to call Magento\Sales\Model\Order::getStoreId() only once --- .../Observer/SaveDownloadableOrderItemObserver.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Downloadable/Observer/SaveDownloadableOrderItemObserver.php b/app/code/Magento/Downloadable/Observer/SaveDownloadableOrderItemObserver.php index caf4cd25b870f..4aed7818b5095 100644 --- a/app/code/Magento/Downloadable/Observer/SaveDownloadableOrderItemObserver.php +++ b/app/code/Magento/Downloadable/Observer/SaveDownloadableOrderItemObserver.php @@ -92,14 +92,15 @@ public function execute(\Magento\Framework\Event\Observer $observer) if ($purchasedLink->getId()) { return $this; } + $storeId = $orderItem->getOrder()->getStoreId(); $orderStatusToEnableItem = $this->_scopeConfig->getValue( \Magento\Downloadable\Model\Link\Purchased\Item::XML_PATH_ORDER_ITEM_STATUS, ScopeInterface::SCOPE_STORE, - $orderItem->getOrder()->getStoreId() + $storeId ); if (!$product) { $product = $this->_createProductModel()->setStoreId( - $orderItem->getOrder()->getStoreId() + $storeId )->load( $orderItem->getProductId() ); From 21abebe5f3cecf56cc32f7bc9f99ec580ac0297e Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Wed, 3 Apr 2019 12:22:39 +0300 Subject: [PATCH 1044/1295] MAGETWO-95281: quote error if a customer is changed to another company --- .../Mftf/Section/AdminPopupModalSection.xml | 1 + .../ActionGroup/AdminCustomerActionGroup.xml | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerActionGroup.xml diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminPopupModalSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminPopupModalSection.xml index 4ea184598663f..fd6c88d0f6572 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminPopupModalSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminPopupModalSection.xml @@ -10,5 +10,6 @@ <section name="AdminPopupModalSection"> <element name="message" type="text" selector="aside.modal-popup .modal-content .popup-window-content"/> <element name="ok" type="button" selector="//span[contains(text(),'Ok')]/ancestor::button"/> + <element name="confirm" type="button" selector="//aside[contains(@class, 'modal-popup')]//footer/button[normalize-space(.)='Confirm']"/> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerActionGroup.xml new file mode 100644 index 0000000000000..b9bac35b503b3 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSaveCustomerForm"> + <scrollToTopOfPage stepKey="scrollToPageTop"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveButton"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the customer." stepKey="seeSaveMessage"/> + </actionGroup> +</actionGroups> From 13d381bfcb864cf3ec4775f1908d204cc939b444 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Wed, 3 Apr 2019 16:32:41 +0300 Subject: [PATCH 1045/1295] MAGETWO-73613: My Wishlist - quantity input box issue --- .../StorefrontCustomerWishlistActionGroup.xml | 9 +++ .../Mftf/Data/ProductStockOptionsData.xml | 20 +++++ .../Metadata/product_stock_options-meta.xml | 24 ++++++ ...orefrontCustomerWishlistProductSection.xml | 1 + ...ontCheckingMaxQtyAllowedInWishlistTest.xml | 71 ++++++++++++++++ .../Wishlist/ViewModel/AllowedQuantity.php | 80 +++++++++++++++++++ .../frontend/layout/wishlist_index_index.xml | 1 + .../frontend/templates/item/column/cart.phtml | 5 +- .../view/frontend/web/js/add-to-wishlist.js | 15 +--- 9 files changed, 214 insertions(+), 12 deletions(-) create mode 100644 app/code/Magento/Wishlist/Test/Mftf/Data/ProductStockOptionsData.xml create mode 100644 app/code/Magento/Wishlist/Test/Mftf/Metadata/product_stock_options-meta.xml create mode 100644 app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontCheckingMaxQtyAllowedInWishlistTest.xml create mode 100644 app/code/Magento/Wishlist/ViewModel/AllowedQuantity.php diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml index 87a77526e0af5..3bbbf35d1b39c 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml @@ -87,4 +87,13 @@ <click selector="{{StorefrontCustomerWishlistProductSection.productUpdateWishList}}" stepKey="submitUpdateWishlist"/> <see selector="{{StorefrontMessagesSection.success}}" userInput="{{product.name}} has been updated in your Wish List." stepKey="successMessage"/> </actionGroup> + + <actionGroup name="StorefrontValidateQtyAfterEditProductInWishlist" extends="StorefrontCustomerEditProductInWishlist"> + <arguments> + <argument name="maxQtyAllowed" type="string" default="10000"/> + </arguments> + <remove keyForRemoval="successMessage"/> + <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.productInfoByName(product.name)}}" after="submitUpdateWishlist" stepKey="moveMouseOverProduct"/> + <see selector="{{StorefrontCustomerWishlistProductSection.productQtyError(product.name)}}" userInput="The maximum you may purchase is {{maxQtyAllowed}}." after="moveMouseOverProduct" stepKey="seeErrorMessage"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Data/ProductStockOptionsData.xml b/app/code/Magento/Wishlist/Test/Mftf/Data/ProductStockOptionsData.xml new file mode 100644 index 0000000000000..07a51e444a7e4 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/Data/ProductStockOptionsData.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="DefaultMaxQtyAllowedConfig" type="max_qty_allowed_config"> + <requiredEntity type="max_qty_allowed_config_default">DefaultMaxQtyAllowed</requiredEntity> + </entity> + <entity name="DefaultMaxQtyAllowed" type="max_qty_allowed_config_default"> + <data key="value">0</data> + </entity> + + <entity name="SetMaxQtyAllowedConfigZero" type="max_qty_allowed_config"> + <data key="value">0</data> + </entity> +</entities> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Metadata/product_stock_options-meta.xml b/app/code/Magento/Wishlist/Test/Mftf/Metadata/product_stock_options-meta.xml new file mode 100644 index 0000000000000..748144ca6dc9f --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/Metadata/product_stock_options-meta.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="MaxQtyAllowedConfig" dataType="max_qty_allowed_config" type="create" auth="adminFormKey" url="/admin/system_config/save/section/cataloginventory/" method="POST" successRegex="/messages-message-success/"> + <object key="groups" dataType="max_qty_allowed_config"> + <object key="item_options" dataType="max_qty_allowed_config"> + <object key="fields" dataType="max_qty_allowed_config"> + <object key="max_sale_qty" dataType="max_qty_allowed_config"> + <field key="value">string</field> + <object key="inherit" dataType="max_qty_allowed_config_default"> + <field key="value">integer</field> + </object> + </object> + </object> + </object> + </object> + </operation> +</operations> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml index 88d68a460da74..dd8814b199e13 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml @@ -19,5 +19,6 @@ <element name="productQuantity" type="input" selector="//a[contains(text(), '{{productName}}')]/ancestor::div[@class='product-item-info']//input[@class='input-text qty']" parameterized="true"/> <element name="productUpdateWishList" type="button" selector=".column.main .actions-toolbar .action.update" timeout="30"/> <element name="productAddAllToCart" type="button" selector=".column.main .actions-toolbar .action.tocart" timeout="30"/> + <element name="productQtyError" type="text" selector="//li[.//a[contains(text(), '{{productName}}')]]//div[@class='mage-error']" parameterized="true" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontCheckingMaxQtyAllowedInWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontCheckingMaxQtyAllowedInWishlistTest.xml new file mode 100644 index 0000000000000..3302f3761d719 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontCheckingMaxQtyAllowedInWishlistTest.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCheckingMaxQtyAllowedInWishlistTest"> + <annotations> + <features value="Wishlist"/> + <stories value="Add to Wishlist"/> + <title value="Checking of quantity input box in Wishlist"/> + <description value="Checking of quantity input box in Wishlist"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-7192"/> + <useCaseId value="MAGETWO-73613"/> + <group value="wishlist"/> + </annotations> + <before> + <!--Create category--> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <!--Create product--> + <createData entity="SimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!--Create customer--> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <!--Login as Customer--> + <actionGroup ref="CustomerLoginOnStorefront" stepKey="customerLogin"> + <argument name="customer" value="$createCustomer$" /> + </actionGroup> + </before> + <after> + <!--Delete entities--> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <!--Set "Maximum Qty Allowed in Shopping Cart" config to default--> + <createData entity="DefaultMaxQtyAllowedConfig" stepKey="setMaxQtyAllowedConfigToDefault"/> + <!--Logout--> + <actionGroup ref="CustomerLogoutStorefrontActionGroup" stepKey="customerLogout"/> + </after> + + <!--Go to product page--> + <amOnPage url="{{StorefrontProductPage.url($createProduct.name$)}}" stepKey="goToProductPage"/> + <!--Add product to Wishlist--> + <actionGroup ref="StorefrontCustomerAddProductToWishlistActionGroup" stepKey="addProductToWishlist"> + <argument name="productVar" value="$createProduct$"/> + </actionGroup> + <!--Validate quantity after edit product in Wishlist - Case 1--> + <actionGroup ref="StorefrontValidateQtyAfterEditProductInWishlist" stepKey="validateQtyFirstCase"> + <argument name="product" value="$createProduct$"/> + <argument name="description" value="Test Description"/> + <argument name="quantity" value="222222222222222"/> + <argument name="maxQtyAllowed" value="10000"/> + </actionGroup> + <!--Set "Maximum Qty Allowed in Shopping Cart" config to "0"--> + <createData entity="SetMaxQtyAllowedConfigZero" stepKey="setMaxQtyAllowedConfigToZero"/> + <!--Go to Wishlist page--> + <amOnPage url="{{StorefrontCustomerWishlistPage.url}}" stepKey="goToWishlist"/> + <!--Validate quantity after edit product in Wishlist - Case 1--> + <actionGroup ref="StorefrontValidateQtyAfterEditProductInWishlist" stepKey="validateQtySecondCase"> + <argument name="product" value="$createProduct$"/> + <argument name="description" value="Test Description"/> + <argument name="quantity" value="222222222222222"/> + <argument name="maxQtyAllowed" value="99999999"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Wishlist/ViewModel/AllowedQuantity.php b/app/code/Magento/Wishlist/ViewModel/AllowedQuantity.php new file mode 100644 index 0000000000000..e6bb908ebe82c --- /dev/null +++ b/app/code/Magento/Wishlist/ViewModel/AllowedQuantity.php @@ -0,0 +1,80 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\ViewModel; + +use Magento\Catalog\Controller\Adminhtml\Product\Initialization\StockDataFilter; +use Magento\Catalog\Model\Product\Configuration\Item\ItemInterface; +use Magento\CatalogInventory\Model\StockRegistry; +use Magento\Framework\View\Element\Block\ArgumentInterface; + +/** + * ViewModel for Wishlist Cart Block. + */ +class AllowedQuantity implements ArgumentInterface +{ + /** + * @var StockRegistry + */ + private $stockRegistry; + + /** + * @var ItemInterface + */ + private $item; + + /** + * @param StockRegistry $stockRegistry + */ + public function __construct(StockRegistry $stockRegistry) + { + $this->stockRegistry = $stockRegistry; + } + + /** + * Set product configuration item. + * + * @param ItemInterface $item + * @return self + */ + public function setItem(ItemInterface $item): self + { + $this->item = $item; + return $this; + } + + /** + * Get product configuration item. + * + * @return ItemInterface + */ + public function getItem(): ItemInterface + { + return $this->item; + } + + /** + * Get min and max qty for wishlist form. + * + * @return array + */ + public function getMinMaxQty(): array + { + $product = $this->getItem()->getProduct(); + $stockItem = $this->stockRegistry->getStockItem($product->getId(), $product->getStore()->getWebsiteId()); + $params = []; + + $params['minAllowed'] = (float)$stockItem->getMinSaleQty(); + if ($stockItem->getMaxSaleQty()) { + $params['maxAllowed'] = (float)$stockItem->getMaxSaleQty(); + } else { + $params['maxAllowed'] = (float)StockDataFilter::MAX_QTY_VALUE; + } + + return $params; + } +} diff --git a/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_index.xml b/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_index.xml index 218b5ac1c879b..05540e313f11d 100644 --- a/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_index.xml +++ b/app/code/Magento/Wishlist/view/frontend/layout/wishlist_index_index.xml @@ -40,6 +40,7 @@ </block> <block class="Magento\Wishlist\Block\Customer\Wishlist\Item\Column\Cart" name="customer.wishlist.item.cart" template="Magento_Wishlist::item/column/cart.phtml" cacheable="false"> <arguments> + <argument name="allowedQuantityViewModel" xsi:type="object">Magento\Wishlist\ViewModel\AllowedQuantity</argument> <argument name="title" translate="true" xsi:type="string">Add to Cart</argument> </arguments> </block> diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml index e2f22c111bdd2..4787ee04860e3 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml @@ -11,6 +11,9 @@ /** @var \Magento\Wishlist\Model\Item $item */ $item = $block->getItem(); $product = $item->getProduct(); +/** @var \Magento\Wishlist\ViewModel\AllowedQuantity $viewModel */ +$viewModel = $block->getData('allowedQuantityViewModel'); +$allowedQty = $viewModel->setItem($item)->getMinMaxQty(); ?> <?php foreach ($block->getChildNames() as $childName): ?> <?= /* @noEscape */ $block->getLayout()->renderElement($childName, false) ?> @@ -21,7 +24,7 @@ $product = $item->getProduct(); <div class="field qty"> <label class="label" for="qty[<?= $block->escapeHtmlAttr($item->getId()) ?>]"><span><?= $block->escapeHtml(__('Qty')) ?></span></label> <div class="control"> - <input type="number" data-role="qty" id="qty[<?= $block->escapeHtmlAttr($item->getId()) ?>]" class="input-text qty" data-validate="{'required-number':true 'validate-greater-than-zero':true}" + <input type="number" data-role="qty" id="qty[<?= $block->escapeHtmlAttr($item->getId()) ?>]" class="input-text qty" data-validate="{'required-number':true,'validate-greater-than-zero':true,'validate-item-quantity':{'minAllowed':<?= /* @noEscape */ $allowedQty['minAllowed'] ?>,'maxAllowed':<?= /* @noEscape */ $allowedQty['maxAllowed'] ?>}}" name="qty[<?= $block->escapeHtmlAttr($item->getId()) ?>]" value="<?= /* @noEscape */ (int)($block->getAddToCartQty($item) * 1) ?>"> </div> </div> diff --git a/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js b/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js index 7ce934317263b..db3b8c83a5064 100644 --- a/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js +++ b/app/code/Magento/Wishlist/view/frontend/web/js/add-to-wishlist.js @@ -162,18 +162,11 @@ define([ $.each(elementValue, function (key, option) { data[elementName + '[' + option + ']'] = option; }); + } else if (elementName.substr(elementName.length - 2) == '[]') { //eslint-disable-line eqeqeq, max-depth + elementName = elementName.substring(0, elementName.length - 2); + data[elementName + '[' + elementValue + ']'] = elementValue; } else { - if (elementValue) { //eslint-disable-line no-lonely-if - if (elementName.substr(elementName.length - 2) == '[]') { //eslint-disable-line eqeqeq, max-depth - elementName = elementName.substring(0, elementName.length - 2); - - if (elementValue) { //eslint-disable-line max-depth - data[elementName + '[' + elementValue + ']'] = elementValue; - } - } else { - data[elementName] = elementValue; - } - } + data[elementName] = elementValue; } return data; From 0b0fc92a7e650dec0c94d56e01f7106304a4f67c Mon Sep 17 00:00:00 2001 From: Pieter Hoste <hoste.pieter@gmail.com> Date: Sat, 15 Sep 2018 15:23:08 +0200 Subject: [PATCH 1046/1295] Backport for Magento 2.2 - Fixes variables in configuration not being replaced with actual values in the backend form fields. (cherry picked from commit a8f17290363aa157eb1ec77984b3df2201a97ea9) --- app/code/Magento/Config/Block/System/Config/Form.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/Config/Block/System/Config/Form.php b/app/code/Magento/Config/Block/System/Config/Form.php index a05151153daa2..63a2b811f93ec 100644 --- a/app/code/Magento/Config/Block/System/Config/Form.php +++ b/app/code/Magento/Config/Block/System/Config/Form.php @@ -423,6 +423,10 @@ private function getFieldData(\Magento\Config\Model\Config\Structure\Element\Fie $backendModel = $field->getBackendModel(); // Backend models which implement ProcessorInterface are processed by ScopeConfigInterface if (!$backendModel instanceof ProcessorInterface) { + if (array_key_exists($path, $this->_configData)) { + $data = $this->_configData[$path]; + } + $backendModel->setPath($path) ->setValue($data) ->setWebsite($this->getWebsiteCode()) From d937dcc2c0605cd28b924e8256e7c1babc9a51d5 Mon Sep 17 00:00:00 2001 From: Dmytro Yushkin <dyushkin@adobe.com> Date: Wed, 3 Apr 2019 15:47:58 -0500 Subject: [PATCH 1047/1295] MAGETWO-98670: Braintree Displaying Incorrect Ship-To Name - Logger added to controller - Recipient name array cell check added --- .../Magento/Braintree/Controller/Paypal/Review.php | 13 ++++++++++++- .../Braintree/Model/Paypal/Helper/QuoteUpdater.php | 10 ++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Braintree/Controller/Paypal/Review.php b/app/code/Magento/Braintree/Controller/Paypal/Review.php index 4576e3b033df8..6814fda8d3a62 100644 --- a/app/code/Magento/Braintree/Controller/Paypal/Review.php +++ b/app/code/Magento/Braintree/Controller/Paypal/Review.php @@ -11,6 +11,7 @@ use Magento\Braintree\Gateway\Config\PayPal\Config; use Magento\Braintree\Model\Paypal\Helper\QuoteUpdater; use Magento\Framework\Exception\LocalizedException; +use Magento\Payment\Model\Method\Logger; /** * Class Review @@ -22,6 +23,11 @@ class Review extends AbstractAction */ private $quoteUpdater; + /** + * @var Logger + */ + private $logger; + /** * @var string */ @@ -34,15 +40,18 @@ class Review extends AbstractAction * @param Config $config * @param Session $checkoutSession * @param QuoteUpdater $quoteUpdater + * @param Logger $logger */ public function __construct( Context $context, Config $config, Session $checkoutSession, - QuoteUpdater $quoteUpdater + QuoteUpdater $quoteUpdater, + Logger $logger ) { parent::__construct($context, $config, $checkoutSession); $this->quoteUpdater = $quoteUpdater; + $this->logger = $logger; } /** @@ -81,6 +90,8 @@ public function execute() return $resultPage; } catch (\Exception $e) { $this->messageManager->addExceptionMessage($e, $e->getMessage()); + } finally { + $this->logger->debug($requestData); } /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */ diff --git a/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php b/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php index 11c51e07f1dd9..ae2b1b1423640 100644 --- a/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php +++ b/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php @@ -197,8 +197,9 @@ private function updateAddressData(Address $address, array $addressData) */ private function getShippingRecipientFirstName(array $details) { - return explode(' ', $details['shippingAddress']['recipientName'], 2)[0] - ?? $details['firstName']; + return isset($details['shippingAddress']['recipientName']) + ? explode(' ', $details['shippingAddress']['recipientName'], 2)[0] + : $details['firstName']; } /** @@ -209,7 +210,8 @@ private function getShippingRecipientFirstName(array $details) */ private function getShippingRecipientLastName(array $details) { - return explode(' ', $details['shippingAddress']['recipientName'], 2)[1] - ?? $details['lastName']; + return isset($details['shippingAddress']['recipientName']) + ? explode(' ', $details['shippingAddress']['recipientName'], 2)[1] + : $details['lastName']; } } From 5698d5610acd6558f4f82450ce9907a275a11ba3 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Thu, 4 Apr 2019 10:17:20 +0300 Subject: [PATCH 1048/1295] MAGETWO-93592: Tax calculation process does not follow "Apply Tax On" setting --- .../AbstractAggregateCalculator.php | 31 ++++++- .../Sales/Total/Quote/CommonTaxCollector.php | 83 ++++++++++++++--- .../RowBaseAndTotalBaseCalculatorTestCase.php | 34 +++++-- .../Magento/Tax/etc/extension_attributes.xml | 3 + .../Tax/Model/Sales/Total/Quote/SetupUtil.php | 53 +++++++++-- ...x_apply_origin_price_with_custom_price.php | 92 +++++++++++++++++++ .../tax_calculation_data_aggregated.php | 6 +- 7 files changed, 273 insertions(+), 29 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/excluding_tax_apply_origin_price_with_custom_price.php diff --git a/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php b/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php index afcfa1bbebcb0..b50b6fa707ffe 100644 --- a/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php +++ b/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php @@ -7,6 +7,9 @@ use Magento\Tax\Api\Data\QuoteDetailsItemInterface; +/** + * Abstract aggregate calculator. + */ abstract class AbstractAggregateCalculator extends AbstractCalculator { /** @@ -106,11 +109,12 @@ protected function calculateWithTaxNotInPrice(QuoteDetailsItemInterface $item, $ $rowTaxes = []; $rowTaxesBeforeDiscount = []; $appliedTaxes = []; + $rowTotalForTaxCalculation = $this->getPriceForTaxCalculation($item, $price) * $quantity; //Apply each tax rate separately foreach ($appliedRates as $appliedRate) { $taxId = $appliedRate['id']; $taxRate = $appliedRate['percent']; - $rowTaxPerRate = $this->calculationTool->calcTaxAmount($rowTotal, $taxRate, false, false); + $rowTaxPerRate = $this->calculationTool->calcTaxAmount($rowTotalForTaxCalculation, $taxRate, false, false); $deltaRoundingType = self::KEY_REGULAR_DELTA_ROUNDING; if ($applyTaxAfterDiscount) { $deltaRoundingType = self::KEY_TAX_BEFORE_DISCOUNT_DELTA_ROUNDING; @@ -121,7 +125,10 @@ protected function calculateWithTaxNotInPrice(QuoteDetailsItemInterface $item, $ //Handle discount if ($applyTaxAfterDiscount) { //TODO: handle originalDiscountAmount - $taxableAmount = max($rowTotal - $discountAmount, 0); + $taxableAmount = max($rowTotalForTaxCalculation - $discountAmount, 0); + if ($taxableAmount && !$applyTaxAfterDiscount) { + $taxableAmount = $rowTotalForTaxCalculation; + } $rowTaxAfterDiscount = $this->calculationTool->calcTaxAmount( $taxableAmount, $taxRate, @@ -167,6 +174,26 @@ protected function calculateWithTaxNotInPrice(QuoteDetailsItemInterface $item, $ ->setAppliedTaxes($appliedTaxes); } + /** + * Get price for tax calculation. + * + * @param QuoteDetailsItemInterface $item + * @param float $price + * @return float + */ + private function getPriceForTaxCalculation(QuoteDetailsItemInterface $item, float $price) + { + if ($item->getExtensionAttributes() && $item->getExtensionAttributes()->getPriceForTaxCalculation()) { + $priceForTaxCalculation = $this->calculationTool->round( + $item->getExtensionAttributes()->getPriceForTaxCalculation() + ); + } else { + $priceForTaxCalculation = $price; + } + + return $priceForTaxCalculation; + } + /** * Round amount * diff --git a/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php b/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php index ff31aa4ba90cb..8017bde8c7a47 100644 --- a/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php +++ b/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php @@ -17,12 +17,17 @@ use Magento\Quote\Model\Quote\Item\AbstractItem; use Magento\Store\Model\Store; use Magento\Tax\Api\Data\QuoteDetailsInterfaceFactory; +use Magento\Tax\Api\Data\QuoteDetailsItemInterface; use Magento\Tax\Api\Data\TaxClassKeyInterfaceFactory; use Magento\Tax\Api\Data\TaxClassKeyInterface; use Magento\Tax\Api\Data\TaxDetailsInterface; use Magento\Tax\Api\Data\TaxDetailsItemInterface; use Magento\Tax\Api\Data\QuoteDetailsInterface; use Magento\Quote\Api\Data\ShippingAssignmentInterface; +use Magento\Tax\Helper\Data as TaxHelper; +use Magento\Framework\App\ObjectManager; +use Magento\Tax\Api\Data\QuoteDetailsItemExtensionInterface; +use Magento\Tax\Api\Data\QuoteDetailsItemExtensionInterfaceFactory; /** * Tax totals calculation model @@ -131,6 +136,16 @@ class CommonTaxCollector extends AbstractTotal */ protected $quoteDetailsItemDataObjectFactory; + /** + * @var TaxHelper + */ + private $taxHelper; + + /** + * @var QuoteDetailsItemExtensionInterfaceFactory + */ + private $quoteDetailsItemExtensionFactory; + /** * Class constructor * @@ -141,6 +156,8 @@ class CommonTaxCollector extends AbstractTotal * @param \Magento\Tax\Api\Data\TaxClassKeyInterfaceFactory $taxClassKeyDataObjectFactory * @param CustomerAddressFactory $customerAddressFactory * @param CustomerAddressRegionFactory $customerAddressRegionFactory + * @param TaxHelper|null $taxHelper + * @param QuoteDetailsItemExtensionInterfaceFactory|null $quoteDetailsItemExtensionInterfaceFactory */ public function __construct( \Magento\Tax\Model\Config $taxConfig, @@ -149,7 +166,9 @@ public function __construct( \Magento\Tax\Api\Data\QuoteDetailsItemInterfaceFactory $quoteDetailsItemDataObjectFactory, \Magento\Tax\Api\Data\TaxClassKeyInterfaceFactory $taxClassKeyDataObjectFactory, CustomerAddressFactory $customerAddressFactory, - CustomerAddressRegionFactory $customerAddressRegionFactory + CustomerAddressRegionFactory $customerAddressRegionFactory, + TaxHelper $taxHelper = null, + QuoteDetailsItemExtensionInterfaceFactory $quoteDetailsItemExtensionInterfaceFactory = null ) { $this->taxCalculationService = $taxCalculationService; $this->quoteDetailsDataObjectFactory = $quoteDetailsDataObjectFactory; @@ -158,6 +177,9 @@ public function __construct( $this->quoteDetailsItemDataObjectFactory = $quoteDetailsItemDataObjectFactory; $this->customerAddressFactory = $customerAddressFactory; $this->customerAddressRegionFactory = $customerAddressRegionFactory; + $this->taxHelper = $taxHelper ?: ObjectManager::getInstance()->get(TaxHelper::class); + $this->quoteDetailsItemExtensionFactory = $quoteDetailsItemExtensionInterfaceFactory ?: + ObjectManager::getInstance()->get(QuoteDetailsItemExtensionInterfaceFactory::class); } /** @@ -188,7 +210,7 @@ public function mapAddress(QuoteAddress $address) * @param bool $priceIncludesTax * @param bool $useBaseCurrency * @param string $parentCode - * @return \Magento\Tax\Api\Data\QuoteDetailsItemInterface + * @return QuoteDetailsItemInterface */ public function mapItem( \Magento\Tax\Api\Data\QuoteDetailsItemInterfaceFactory $itemDataObjectFactory, @@ -201,7 +223,7 @@ public function mapItem( $sequence = 'sequence-' . $this->getNextIncrement(); $item->setTaxCalculationItemId($sequence); } - /** @var \Magento\Tax\Api\Data\QuoteDetailsItemInterface $itemDataObject */ + /** @var QuoteDetailsItemInterface $itemDataObject */ $itemDataObject = $itemDataObjectFactory->create(); $itemDataObject->setCode($item->getTaxCalculationItemId()) ->setQuantity($item->getQty()) @@ -217,12 +239,28 @@ public function mapItem( if (!$item->getBaseTaxCalculationPrice()) { $item->setBaseTaxCalculationPrice($item->getBaseCalculationPriceOriginal()); } + + if ($this->taxHelper->applyTaxOnOriginalPrice()) { + $baseTaxCalculationPrice = $item->getBaseOriginalPrice(); + } else { + $baseTaxCalculationPrice = $item->getBaseCalculationPriceOriginal(); + } + $this->setPriceForTaxCalculation($itemDataObject, (float)$baseTaxCalculationPrice); + $itemDataObject->setUnitPrice($item->getBaseTaxCalculationPrice()) ->setDiscountAmount($item->getBaseDiscountAmount()); } else { if (!$item->getTaxCalculationPrice()) { $item->setTaxCalculationPrice($item->getCalculationPriceOriginal()); } + + if ($this->taxHelper->applyTaxOnOriginalPrice()) { + $taxCalculationPrice = $item->getOriginalPrice(); + } else { + $taxCalculationPrice = $item->getCalculationPriceOriginal(); + } + $this->setPriceForTaxCalculation($itemDataObject, (float)$taxCalculationPrice); + $itemDataObject->setUnitPrice($item->getTaxCalculationPrice()) ->setDiscountAmount($item->getDiscountAmount()); } @@ -232,6 +270,23 @@ public function mapItem( return $itemDataObject; } + /** + * Set price for tax calculation. + * + * @param QuoteDetailsItemInterface $quoteDetailsItem + * @param float $taxCalculationPrice + * @return void + */ + private function setPriceForTaxCalculation(QuoteDetailsItemInterface $quoteDetailsItem, float $taxCalculationPrice) + { + $extensionAttributes = $quoteDetailsItem->getExtensionAttributes(); + if (!$extensionAttributes) { + $extensionAttributes = $this->quoteDetailsItemExtensionFactory->create(); + } + $extensionAttributes->setPriceForTaxCalculation($taxCalculationPrice); + $quoteDetailsItem->setExtensionAttributes($extensionAttributes); + } + /** * Map item extra taxables * @@ -239,7 +294,7 @@ public function mapItem( * @param AbstractItem $item * @param bool $priceIncludesTax * @param bool $useBaseCurrency - * @return \Magento\Tax\Api\Data\QuoteDetailsItemInterface[] + * @return QuoteDetailsItemInterface[] */ public function mapItemExtraTaxables( \Magento\Tax\Api\Data\QuoteDetailsItemInterfaceFactory $itemDataObjectFactory, @@ -262,7 +317,7 @@ public function mapItemExtraTaxables( } else { $unitPrice = $extraTaxable[self::KEY_ASSOCIATED_TAXABLE_UNIT_PRICE]; } - /** @var \Magento\Tax\Api\Data\QuoteDetailsItemInterface $itemDataObject */ + /** @var QuoteDetailsItemInterface $itemDataObject */ $itemDataObject = $itemDataObjectFactory->create(); $itemDataObject->setCode($extraTaxable[self::KEY_ASSOCIATED_TAXABLE_CODE]) ->setType($extraTaxable[self::KEY_ASSOCIATED_TAXABLE_TYPE]) @@ -285,9 +340,9 @@ public function mapItemExtraTaxables( * Add quote items * * @param ShippingAssignmentInterface $shippingAssignment - * @param bool $useBaseCurrency * @param bool $priceIncludesTax - * @return \Magento\Tax\Api\Data\QuoteDetailsItemInterface[] + * @param bool $useBaseCurrency + * @return QuoteDetailsItemInterface[] */ public function mapItems( ShippingAssignmentInterface $shippingAssignment, @@ -358,10 +413,12 @@ public function populateAddressData(QuoteDetailsInterface $quoteDetails, QuoteAd } /** + * Get shipping data object. + * * @param ShippingAssignmentInterface $shippingAssignment * @param QuoteAddress\Total $total * @param bool $useBaseCurrency - * @return \Magento\Tax\Api\Data\QuoteDetailsItemInterface + * @return QuoteDetailsItemInterface */ public function getShippingDataObject( ShippingAssignmentInterface $shippingAssignment, @@ -376,7 +433,7 @@ public function getShippingDataObject( $total->setBaseShippingTaxCalculationAmount($total->getBaseShippingAmount()); } if ($total->getShippingTaxCalculationAmount() !== null) { - /** @var \Magento\Tax\Api\Data\QuoteDetailsItemInterface $itemDataObject */ + /** @var QuoteDetailsItemInterface $itemDataObject */ $itemDataObject = $this->quoteDetailsItemDataObjectFactory->create() ->setType(self::ITEM_TYPE_SHIPPING) ->setCode(self::ITEM_CODE_SHIPPING) @@ -411,7 +468,7 @@ public function getShippingDataObject( * Populate QuoteDetails object from quote address object * * @param ShippingAssignmentInterface $shippingAssignment - * @param \Magento\Tax\Api\Data\QuoteDetailsItemInterface[] $itemDataObjects + * @param QuoteDetailsItemInterface[] $itemDataObjects * @return \Magento\Tax\Api\Data\QuoteDetailsInterface */ protected function prepareQuoteDetails(ShippingAssignmentInterface $shippingAssignment, $itemDataObjects) @@ -540,6 +597,7 @@ protected function processProductItems( * Process applied taxes for items and quote * * @param QuoteAddress\Total $total + * @param ShippingAssignmentInterface $shippingAssignment * @param array $itemsByType * @return $this */ @@ -837,8 +895,9 @@ protected function saveAppliedTaxes() } /** - * Increment and return counter. This function is intended to be used to generate temporary - * id for an item. + * Increment and return counter. + * + * This function is intended to be used to generate temporary id for an item. * * @return int */ diff --git a/app/code/Magento/Tax/Test/Unit/Model/Calculation/RowBaseAndTotalBaseCalculatorTestCase.php b/app/code/Magento/Tax/Test/Unit/Model/Calculation/RowBaseAndTotalBaseCalculatorTestCase.php index 89800e3be872e..2a7eeb27ee07e 100644 --- a/app/code/Magento/Tax/Test/Unit/Model/Calculation/RowBaseAndTotalBaseCalculatorTestCase.php +++ b/app/code/Magento/Tax/Test/Unit/Model/Calculation/RowBaseAndTotalBaseCalculatorTestCase.php @@ -4,13 +4,12 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - namespace Magento\Tax\Test\Unit\Model\Calculation; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Tax\Model\Calculation\RowBaseCalculator; use Magento\Tax\Model\Calculation\TotalBaseCalculator; +use Magento\Tax\Api\Data\QuoteDetailsItemExtensionInterface; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -68,6 +67,11 @@ class RowBaseAndTotalBaseCalculatorTestCase extends \PHPUnit\Framework\TestCase */ protected $taxDetailsItem; + /** + * @var \Magento\Tax\Api\Data\QuoteDetailsItemExtensionInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $quoteDetailsItemExtension; + /** * initialize all mocks * @@ -84,7 +88,10 @@ public function initMocks($isTaxIncluded) protected function setUp() { $this->objectManager = new ObjectManager($this); - $this->taxItemDetailsDataObjectFactory = $this->createPartialMock(\Magento\Tax\Api\Data\TaxDetailsItemInterfaceFactory::class, ['create']); + $this->taxItemDetailsDataObjectFactory = $this->createPartialMock( + \Magento\Tax\Api\Data\TaxDetailsItemInterfaceFactory::class, + ['create'] + ); $this->taxDetailsItem = $this->objectManager->getObject(\Magento\Tax\Model\TaxDetails\ItemDetails::class); $this->taxItemDetailsDataObjectFactory->expects($this->any()) ->method('create') @@ -100,11 +107,24 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $this->mockItem = $this->getMockBuilder(\Magento\Tax\Api\Data\QuoteDetailsItemInterface::class)->getMock(); - - $this->appliedTaxDataObjectFactory = $this->createPartialMock(\Magento\Tax\Api\Data\AppliedTaxInterfaceFactory::class, ['create']); + $this->mockItem = $this->getMockBuilder(\Magento\Tax\Api\Data\QuoteDetailsItemInterface::class) + ->disableOriginalConstructor()->setMethods(['getExtensionAttributes', 'getUnitPrice']) + ->getMockForAbstractClass(); + $this->quoteDetailsItemExtension = $this->getMockBuilder(QuoteDetailsItemExtensionInterface::class) + ->disableOriginalConstructor()->setMethods(['getPriceForTaxCalculation']) + ->getMockForAbstractClass(); + $this->mockItem->expects($this->any())->method('getExtensionAttributes') + ->willReturn($this->quoteDetailsItemExtension); + + $this->appliedTaxDataObjectFactory = $this->createPartialMock( + \Magento\Tax\Api\Data\AppliedTaxInterfaceFactory::class, + ['create'] + ); - $this->appliedTaxRateDataObjectFactory = $this->createPartialMock(\Magento\Tax\Api\Data\AppliedTaxRateInterfaceFactory::class, ['create']); + $this->appliedTaxRateDataObjectFactory = $this->createPartialMock( + \Magento\Tax\Api\Data\AppliedTaxRateInterfaceFactory::class, + ['create'] + ); $this->appliedTaxRate = $this->objectManager->getObject(\Magento\Tax\Model\TaxDetails\AppliedTaxRate::class); $this->appliedTaxRateDataObjectFactory->expects($this->any()) ->method('create') diff --git a/app/code/Magento/Tax/etc/extension_attributes.xml b/app/code/Magento/Tax/etc/extension_attributes.xml index 90a5e6d2ecee3..41af1df836d6f 100644 --- a/app/code/Magento/Tax/etc/extension_attributes.xml +++ b/app/code/Magento/Tax/etc/extension_attributes.xml @@ -20,4 +20,7 @@ <extension_attributes for="Magento\Catalog\Api\Data\ProductRender\PriceInfoInterface"> <attribute code="tax_adjustments" type="Magento\Catalog\Api\Data\ProductRender\PriceInfoInterface" /> </extension_attributes> + <extension_attributes for="Magento\Tax\Api\Data\QuoteDetailsItemInterface"> + <attribute code="price_for_tax_calculation" type="float" /> + </extension_attributes> </config> 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 bd6c900cf203c..4f9778febfb1c 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 @@ -9,10 +9,19 @@ namespace Magento\Tax\Model\Sales\Total\Quote; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Quote\Model\Quote; use Magento\Tax\Model\Config; use Magento\Tax\Model\Calculation; +use Magento\Quote\Model\Quote\Item\Updater; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Framework\Api\Filter; +use Magento\Framework\Api\Search\FilterGroup; +use Magento\Framework\Api\SearchCriteriaInterface; /** + * Setup utility for quote + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class SetupUtil @@ -593,7 +602,7 @@ protected function createCartRule($ruleDataOverride) * * @param array $quoteData * @param \Magento\Customer\Api\Data\CustomerInterface $customer - * @return \Magento\Quote\Model\Quote + * @return Quote */ protected function createQuote($quoteData, $customer) { @@ -618,8 +627,8 @@ protected function createQuote($quoteData, $customer) $quoteBillingAddress = $this->objectManager->create(\Magento\Quote\Model\Quote\Address::class); $quoteBillingAddress->importCustomerAddressData($addressService->getById($billingAddress->getId())); - /** @var \Magento\Quote\Model\Quote $quote */ - $quote = $this->objectManager->create(\Magento\Quote\Model\Quote::class); + /** @var Quote $quote */ + $quote = $this->objectManager->create(Quote::class); $quote->setStoreId(1) ->setIsActive(true) ->setIsMultiShipping(false) @@ -633,7 +642,7 @@ protected function createQuote($quoteData, $customer) /** * Add products to quote * - * @param \Magento\Quote\Model\Quote $quote + * @param Quote $quote * @param array $itemsData * @return $this */ @@ -656,7 +665,8 @@ protected function addProductToQuote($quote, $itemsData) * Create a quote based on given data * * @param array $quoteData - * @return \Magento\Quote\Model\Quote + * + * @return Quote */ public function setupQuote($quoteData) { @@ -665,7 +675,9 @@ public function setupQuote($quoteData) $quote = $this->createQuote($quoteData, $customer); $this->addProductToQuote($quote, $quoteData['items']); - + if (isset($quoteData['update_items'])) { + $this->updateItems($quote, $quoteData['update_items']); + } //Set shipping amount if (isset($quoteData['shipping_method'])) { $quote->getShippingAddress()->setShippingMethod($quoteData['shipping_method']); @@ -682,4 +694,33 @@ public function setupQuote($quoteData) return $quote; } + + /** + * Update quote items + * + * @param Quote $quote + * @param array $items + * + * @return void + */ + private function updateItems(Quote $quote, array $items) + { + $updater = $this->objectManager->get(Updater::class); + $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $filter = $this->objectManager->create(Filter::class); + $filter->setField('sku')->setValue(array_keys($items)); + $filterGroup = $this->objectManager->create(FilterGroup::class); + $filterGroup->setFilters([$filter]); + $searchCriteria = $this->objectManager->create(SearchCriteriaInterface::class); + $searchCriteria->setFilterGroups([$filterGroup]); + $products = $productRepository->getList($searchCriteria)->getItems(); + /** @var ProductInterface $product */ + foreach ($products as $product) { + $quoteItem = $quote->getItemByProduct($product); + $updater->update( + $quoteItem, + $items[$product->getSku()] + ); + } + } } diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/excluding_tax_apply_origin_price_with_custom_price.php b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/excluding_tax_apply_origin_price_with_custom_price.php new file mode 100644 index 0000000000000..081b8e0a24620 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/excluding_tax_apply_origin_price_with_custom_price.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Tax\Model\Config; +use Magento\Tax\Model\Sales\Total\Quote\SetupUtil; + +$taxCalculationData['excluding_tax_apply_origin_price_with_custom_price'] = [ + 'config_data' => [ + SetupUtil::CONFIG_OVERRIDES => [ + Config::CONFIG_XML_PATH_APPLY_ON => 1, + ], + SetupUtil::TAX_RATE_OVERRIDES => [ + SetupUtil::TAX_RATE_TX => 8.25, + SetupUtil::TAX_STORE_RATE => 8.25, + ], + SetupUtil::TAX_RULE_OVERRIDES => [ + ], + ], + 'quote_data' => [ + 'billing_address' => [ + 'region_id' => SetupUtil::REGION_TX, + ], + 'shipping_address' => [ + 'region_id' => SetupUtil::REGION_TX, + ], + 'items' => [ + [ + 'sku' => 'simple1', + 'price' => 16.24, + 'qty' => 1, + ], + ], + 'update_items' => [ + 'simple1' => [ + 'custom_price' => 14, + 'qty' => 1, + ], + ], + ], + 'expected_results' => [ + 'address_data' => [ + 'subtotal' => 14, + 'base_subtotal' => 14, + 'subtotal_incl_tax' => 15.34, + 'base_subtotal_incl_tax' => 15.34, + 'tax_amount' => 1.34, + 'base_tax_amount' => 1.34, + 'shipping_amount' => 0, + 'base_shipping_amount' => 0, + 'shipping_incl_tax' => 0, + 'base_shipping_incl_tax' => 0, + 'shipping_taxable' => 0, + 'base_shipping_taxable' => 0, + 'shipping_tax_amount' => 0, + 'base_shipping_tax_amount' => 0, + 'discount_amount' => 0, + 'base_discount_amount' => 0, + 'discount_tax_compensation_amount' => 0, + 'base_discount_tax_compensation_amount' => 0, + 'shipping_discount_tax_compensation_amount' => 0, + 'base_shipping_discount_tax_compensation_amount' => 0, + 'grand_total' => 15.34, + 'base_grand_total' => 15.34, + ], + 'items_data' => [ + 'simple1' => [ + 'row_total' => 14, + 'base_row_total' => 14, + 'tax_percent' => 8.25, + 'price' => 14, + 'custom_price' => 14, + 'original_custom_price' => 14, + 'base_price' => 14, + 'price_incl_tax' => 15.34, + 'base_price_incl_tax' => 15.34, + 'row_total_incl_tax' => 15.34, + 'base_row_total_incl_tax' => 15.34, + 'tax_amount' => 1.34, + 'base_tax_amount' => 1.34, + 'discount_amount' => 0, + 'base_discount_amount' => 0, + 'discount_percent' => 0, + 'discount_tax_compensation_amount' => 0, + 'base_discount_tax_compensation_amount' => 0, + ], + ], + ], +]; 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 f22b48a259685..87791d83aabd4 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 @@ -3,14 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); /** - * Global array that holds test scenarios data + * Global array that holds test scenarios data. * * @var array */ $taxCalculationData = []; - +//phpcs:disable Magento2.Security.IncludeFile require_once __DIR__ . '/scenarios/excluding_tax_apply_tax_after_discount.php'; require_once __DIR__ . '/scenarios/excluding_tax_apply_tax_after_discount_discount_tax.php'; require_once __DIR__ . '/scenarios/excluding_tax_apply_tax_before_discount.php'; @@ -31,3 +32,4 @@ 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'; +require_once __DIR__ . '/scenarios/excluding_tax_apply_origin_price_with_custom_price.php'; From 5e241b93893851c2de7f70ff0f843a4202a73939 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Thu, 4 Apr 2019 10:54:13 +0300 Subject: [PATCH 1049/1295] MAGETWO-73613: My Wishlist - quantity input box issue --- .../ActionGroup/StorefrontCustomerWishlistActionGroup.xml | 4 +++- .../Test/StorefrontCheckingMaxQtyAllowedInWishlistTest.xml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml index 3bbbf35d1b39c..5c7cb8334e00f 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml @@ -93,7 +93,9 @@ <argument name="maxQtyAllowed" type="string" default="10000"/> </arguments> <remove keyForRemoval="successMessage"/> - <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.productInfoByName(product.name)}}" after="submitUpdateWishlist" stepKey="moveMouseOverProduct"/> + <waitForElement selector="{{StorefrontCustomerWishlistProductSection.productQtyError(product.name)}}" after="submitUpdateWishlist" stepKey="waitForErrorMessage"/> + <scrollToTopOfPage after="waitForErrorMessage" stepKey="scrollToTop"/> + <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.productInfoByName(product.name)}}" after="scrollToTop" stepKey="moveMouseOverProduct"/> <see selector="{{StorefrontCustomerWishlistProductSection.productQtyError(product.name)}}" userInput="The maximum you may purchase is {{maxQtyAllowed}}." after="moveMouseOverProduct" stepKey="seeErrorMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontCheckingMaxQtyAllowedInWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontCheckingMaxQtyAllowedInWishlistTest.xml index 3302f3761d719..7670c554d7c97 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontCheckingMaxQtyAllowedInWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontCheckingMaxQtyAllowedInWishlistTest.xml @@ -59,7 +59,7 @@ <!--Set "Maximum Qty Allowed in Shopping Cart" config to "0"--> <createData entity="SetMaxQtyAllowedConfigZero" stepKey="setMaxQtyAllowedConfigToZero"/> <!--Go to Wishlist page--> - <amOnPage url="{{StorefrontCustomerWishlistPage.url}}" stepKey="goToWishlist"/> + <amOnPage url="{{StorefrontCustomerWishlistPage.url}}" stepKey="goToWishlistPage"/> <!--Validate quantity after edit product in Wishlist - Case 1--> <actionGroup ref="StorefrontValidateQtyAfterEditProductInWishlist" stepKey="validateQtySecondCase"> <argument name="product" value="$createProduct$"/> From ad7a6c89aee2dc5fff957f5afafce497fb0989ac Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Thu, 4 Apr 2019 11:42:48 +0300 Subject: [PATCH 1050/1295] MAGETWO-96898: Fixed Tier Pricing for Bundle items doesn't work --- .../DataProviders/OptionPriceRenderer.php | 63 +++++++++++ .../Magento/Bundle/Model/Product/Price.php | 70 ++++++------ ...AdminCreateApiBundleProductActionGroup.xml | 63 +++++++++++ .../Mftf/Section/StorefrontBundledSection.xml | 4 + ...rontCheckBundleProductOptionTierPrices.xml | 107 ++++++++++++++++++ .../DataProviders/OptionPriceRendererTest.php | 95 ++++++++++++++++ .../Bundle/view/base/web/js/price-bundle.js | 34 +++++- .../catalog_product_view_type_bundle.xml | 18 ++- .../view/type/bundle/option/checkbox.phtml | 3 + .../view/type/bundle/option/radio.phtml | 3 + .../view/type/bundle/option/select.phtml | 10 ++ .../ActionGroup/AdminProductActionGroup.xml | 4 +- .../Test/CheckTierPricingOfProductsTest.xml | 8 +- .../Bundle/Model/Product/PriceTest.php | 51 ++++++++- .../product_with_simple_tier_pricing.php | 43 +++++++ ...duct_with_simple_tier_pricing_rollback.php | 24 ++++ 16 files changed, 557 insertions(+), 43 deletions(-) create mode 100644 app/code/Magento/Bundle/Block/DataProviders/OptionPriceRenderer.php create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPrices.xml create mode 100644 app/code/Magento/Bundle/Test/Unit/Block/DataProviders/OptionPriceRendererTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_simple_tier_pricing.php create mode 100644 dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_simple_tier_pricing_rollback.php diff --git a/app/code/Magento/Bundle/Block/DataProviders/OptionPriceRenderer.php b/app/code/Magento/Bundle/Block/DataProviders/OptionPriceRenderer.php new file mode 100644 index 0000000000000..058b3a981b52f --- /dev/null +++ b/app/code/Magento/Bundle/Block/DataProviders/OptionPriceRenderer.php @@ -0,0 +1,63 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Block\DataProviders; + +use Magento\Catalog\Model\Product; +use Magento\Catalog\Pricing\Price\TierPrice; +use Magento\Framework\Pricing\Render; +use Magento\Framework\View\Element\Block\ArgumentInterface; +use Magento\Framework\View\LayoutInterface; + +/** + * Provides additional data for bundle options + */ +class OptionPriceRenderer implements ArgumentInterface +{ + /** + * Parent layout of the block + * + * @var LayoutInterface + */ + private $layout; + + /** + * @param LayoutInterface $layout + */ + public function __construct(LayoutInterface $layout) + { + $this->layout = $layout; + } + + /** + * Format tier price string + * + * @param Product $selection + * @param array $arguments + * @return string + */ + public function renderTierPrice(Product $selection, array $arguments = []): string + { + if (!array_key_exists('zone', $arguments)) { + $arguments['zone'] = Render::ZONE_ITEM_OPTION; + } + + $priceHtml = ''; + + /** @var Render $priceRender */ + $priceRender = $this->layout->getBlock('product.price.render.default'); + if ($priceRender !== false) { + $priceHtml = $priceRender->render( + TierPrice::PRICE_CODE, + $selection, + $arguments + ); + } + + return $priceHtml; + } +} diff --git a/app/code/Magento/Bundle/Model/Product/Price.php b/app/code/Magento/Bundle/Model/Product/Price.php index 00b6b2d7a3f5a..c9ed97fada966 100644 --- a/app/code/Magento/Bundle/Model/Product/Price.php +++ b/app/code/Magento/Bundle/Model/Product/Price.php @@ -11,8 +11,11 @@ use Magento\Catalog\Api\Data\ProductTierPriceExtensionFactory; /** + * Bundle product type price model + * * @api * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @since 100.0.2 */ class Price extends \Magento\Catalog\Model\Product\Type\Price @@ -180,9 +183,9 @@ protected function getBundleSelectionIds(\Magento\Catalog\Model\Product $product /** * Get product final price * - * @param float $qty - * @param \Magento\Catalog\Model\Product $product - * @return float + * @param float $qty + * @param \Magento\Catalog\Model\Product $product + * @return float */ public function getFinalPrice($qty, $product) { @@ -207,9 +210,9 @@ public function getFinalPrice($qty, $product) * Returns final price of a child product * * @param \Magento\Catalog\Model\Product $product - * @param float $productQty + * @param float $productQty * @param \Magento\Catalog\Model\Product $childProduct - * @param float $childProductQty + * @param float $childProductQty * @return float */ public function getChildFinalPrice($product, $productQty, $childProduct, $childProductQty) @@ -220,10 +223,10 @@ public function getChildFinalPrice($product, $productQty, $childProduct, $childP /** * Retrieve Price considering tier price * - * @param \Magento\Catalog\Model\Product $product - * @param string|null $which - * @param bool|null $includeTax - * @param bool $takeTierPrice + * @param \Magento\Catalog\Model\Product $product + * @param string|null $which + * @param bool|null $includeTax + * @param bool $takeTierPrice * @return float|array * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) @@ -402,8 +405,8 @@ public function getOptions($product) * * @param \Magento\Catalog\Model\Product $bundleProduct * @param \Magento\Catalog\Model\Product $selectionProduct - * @param float|null $selectionQty - * @param null|bool $multiplyQty Whether to multiply selection's price by its quantity + * @param float|null $selectionQty + * @param null|bool $multiplyQty Whether to multiply selection's price by its quantity * @return float * * @see \Magento\Bundle\Model\Product\Price::getSelectionFinalTotalPrice() @@ -418,7 +421,7 @@ public function getSelectionPrice($bundleProduct, $selectionProduct, $selectionQ * * @param \Magento\Catalog\Model\Product $bundleProduct * @param \Magento\Catalog\Model\Product $selectionProduct - * @param float $qty + * @param float $qty * @return float */ public function getSelectionPreFinalPrice($bundleProduct, $selectionProduct, $qty = null) @@ -427,15 +430,14 @@ public function getSelectionPreFinalPrice($bundleProduct, $selectionProduct, $qt } /** - * Calculate final price of selection - * with take into account tier price + * Calculate final price of selection with take into account tier price * - * @param \Magento\Catalog\Model\Product $bundleProduct - * @param \Magento\Catalog\Model\Product $selectionProduct - * @param float $bundleQty - * @param float $selectionQty - * @param bool $multiplyQty - * @param bool $takeTierPrice + * @param \Magento\Catalog\Model\Product $bundleProduct + * @param \Magento\Catalog\Model\Product $selectionProduct + * @param float $bundleQty + * @param float $selectionQty + * @param bool $multiplyQty + * @param bool $takeTierPrice * @return float */ public function getSelectionFinalTotalPrice( @@ -454,7 +456,11 @@ public function getSelectionFinalTotalPrice( } if ($bundleProduct->getPriceType() == self::PRICE_TYPE_DYNAMIC) { - $price = $selectionProduct->getFinalPrice($takeTierPrice ? $selectionQty : 1); + $totalQty = $bundleQty * $selectionQty; + if (!$takeTierPrice || $totalQty === 0) { + $totalQty = 1; + } + $price = $selectionProduct->getFinalPrice($totalQty); } else { if ($selectionProduct->getSelectionPriceType()) { // percent @@ -485,10 +491,10 @@ public function getSelectionFinalTotalPrice( /** * Apply tier price for bundle * - * @param \Magento\Catalog\Model\Product $product - * @param float $qty - * @param float $finalPrice - * @return float + * @param \Magento\Catalog\Model\Product $product + * @param float $qty + * @param float $finalPrice + * @return float */ protected function _applyTierPrice($product, $qty, $finalPrice) { @@ -509,9 +515,9 @@ protected function _applyTierPrice($product, $qty, $finalPrice) /** * Get product tier price by qty * - * @param float $qty - * @param \Magento\Catalog\Model\Product $product - * @return float|array + * @param float $qty + * @param \Magento\Catalog\Model\Product $product + * @return float|array * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ @@ -605,11 +611,11 @@ public function getTierPrice($qty, $product) /** * Calculate and apply special price * - * @param float $finalPrice - * @param float $specialPrice + * @param float $finalPrice + * @param float $specialPrice * @param string $specialPriceFrom * @param string $specialPriceTo - * @param mixed $store + * @param mixed $store * @return float */ public function calculateSpecialPrice( @@ -634,7 +640,7 @@ public function calculateSpecialPrice( * * @param /Magento/Catalog/Model/Product $bundleProduct * @param float|string $price - * @param int $bundleQty + * @param int $bundleQty * @return float */ public function getLowestPrice($bundleProduct, $price, $bundleQty = 1) diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiBundleProductActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiBundleProductActionGroup.xml index 72140cf6d5848..2444776065f7e 100644 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiBundleProductActionGroup.xml +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiBundleProductActionGroup.xml @@ -106,4 +106,67 @@ <requiredEntity createDataKey="simpleProduct4"/> </createData> </actionGroup> + <actionGroup name="AdminCreateApiDynamicBundleProductAllOptionTypesActionGroup"> + <arguments> + <argument name="productName" defaultValue="Api Dynamic Bundle Product" type="string"/> + </arguments> + <!-- Create sub ctegory --> + <createData entity="SimpleSubCategory" stepKey="createSubCategory"/> + <!-- Create simple products --> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"> + <field key="price">10</field> + <requiredEntity createDataKey="createSubCategory"/> + </createData> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"> + <field key="price">20</field> + <requiredEntity createDataKey="createSubCategory"/> + </createData> + <!-- Create Bundle product --> + <createData entity="ApiBundleProduct" stepKey="createBundleProduct"> + <field key="name">{{productName}}</field> + <requiredEntity createDataKey="createSubCategory"/> + </createData> + <createData entity="DropDownBundleOption" stepKey="createDropDownBundleOption"> + <requiredEntity createDataKey="createBundleProduct"/> + <field key="title">Drop-down Option</field> + </createData> + <createData entity="RadioButtonsOption" stepKey="createBundleRadioButtonsOption"> + <requiredEntity createDataKey="createBundleProduct"/> + <field key="title">Radio Buttons Option</field> + </createData> + <createData entity="CheckboxOption" stepKey="createBundleCheckboxOption"> + <requiredEntity createDataKey="createBundleProduct"/> + <field key="title">Checkbox Option</field> + </createData> + <createData entity="ApiBundleLink" stepKey="linkCheckboxOptionToProduct1"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleCheckboxOption"/> + <requiredEntity createDataKey="simpleProduct1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkCheckboxOptionToProduct2"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleCheckboxOption"/> + <requiredEntity createDataKey="simpleProduct2"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkDropDownOptionToProduct1"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createDropDownBundleOption"/> + <requiredEntity createDataKey="simpleProduct1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkDropDownOptionToProduct2"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createDropDownBundleOption"/> + <requiredEntity createDataKey="simpleProduct2"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkRadioButtonsOptionToProduct1"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleRadioButtonsOption"/> + <requiredEntity createDataKey="simpleProduct1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkRadioButtonsOptionToProduct2"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleRadioButtonsOption"/> + <requiredEntity createDataKey="simpleProduct2"/> + </createData> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml index 473c3036cab2c..a5c70c24e3d9b 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml @@ -15,5 +15,9 @@ <element name="addToCartConfigured" type="button" selector="#product-addtocart-button" timeout="30"/> <element name="updateCart" type="button" selector="#product-updatecart-button" timeout="30"/> <element name="configuredPrice" type="block" selector=".price-configured_price .price"/> + <element name="dropDownOptionInput" type="select" selector="//label//span[contains(text(), '{{productName}}')]/../..//div[@class='control']//select" parameterized="true"/> + <element name="dropDownOptionTierPrices" type="text" selector="//label//span[contains(text(), '{{optionName}}')]/../..//div[@class='control']//div[@class='option-tier-prices']" parameterized="true"/> + <element name="radioButtonOptionLabel" type="text" selector="//label//span[contains(text(), '{{optionName}}')]/../..//div[@class='control']//div[@class='field choice']//label[contains(.,'{{productName}}')]" parameterized="true"/> + <element name="checkboxOptionLabel" type="text" selector="//label//span[contains(text(), '{{optionName}}')]/../..//div[@class='control']//div[@class='field choice']//label[contains(.,'{{productName}}')]" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPrices.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPrices.xml new file mode 100644 index 0000000000000..23282c2829138 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPrices.xml @@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCheckBundleProductOptionTierPrices"> + <annotations> + <features value="Bundle"/> + <stories value="View bundle products"/> + <title value="Check tier prices for bundle options"/> + <testCaseId value="MAGETWO-98968"/> + <useCaseId value="MAGETWO-98603"/> + <group value="catalog"/> + <group value="bundle"/> + </annotations> + <before> + <!-- Create Dynamic Bundle product --> + <actionGroup ref="AdminCreateApiDynamicBundleProductAllOptionTypesActionGroup" stepKey="createBundleProduct"/> + + <!-- Add tier prices to simple products --> + <!-- Simple product 1 --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <amOnPage url="{{AdminProductEditPage.url($$simpleProduct1CreateBundleProduct.id$$)}}" stepKey="openAdminEditPageProduct1"/> + <actionGroup ref="ProductSetAdvancedPricing" stepKey="addTierPriceProduct1"> + <argument name="website" value="All Websites [USD]"/> + <argument name="group" value="ALL GROUPS"/> + <argument name="quantity" value="5"/> + <argument name="price" value="Discount"/> + <argument name="amount" value="50"/> + </actionGroup> + <!-- Simple product 2 --> + <amOnPage url="{{AdminProductEditPage.url($$simpleProduct2CreateBundleProduct.id$$)}}" stepKey="openAdminEditPageProduct2"/> + <actionGroup ref="ProductSetAdvancedPricing" stepKey="addTierPriceProduct2"> + <argument name="website" value="All Websites [USD]"/> + <argument name="group" value="ALL GROUPS"/> + <argument name="quantity" value="7"/> + <argument name="price" value="Discount"/> + <argument name="amount" value="25"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logoutAsAdmin"/> + + <!-- Run reindex --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + </before> + <after> + <deleteData createDataKey="createBundleProductCreateBundleProduct" stepKey="deleteDynamicBundleProduct"/> + <deleteData createDataKey="simpleProduct1CreateBundleProduct" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2CreateBundleProduct" stepKey="deleteSimpleProduct2"/> + <deleteData createDataKey="createSubCategoryCreateBundleProduct" stepKey="deleteSubCategory"/> + </after> + + <!-- Go to storefront product page --> + <amOnPage url="{{StorefrontProductPage.url($$createBundleProductCreateBundleProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToBundleProductPage"/> + <click selector="{{StorefrontBundledSection.addToCart}}" stepKey="clickCustomize"/> + + <!--"Drop-down" type option--> + <!-- Check Tier Prices for product 1 --> + <selectOption selector="{{StorefrontBundledSection.dropDownOptionInput('Drop-down Option')}}" userInput="$$simpleProduct1CreateBundleProduct.sku$$ +$$$simpleProduct1CreateBundleProduct.price$$.00" stepKey="selectDropDownOptionProduct1"/> + <seeOptionIsSelected selector="{{StorefrontBundledSection.dropDownOptionInput('Drop-down Option')}}" userInput="$$simpleProduct1CreateBundleProduct.sku$$ +$$$simpleProduct1CreateBundleProduct.price$$.00" stepKey="checkDropDownOptionProduct1"/> + <grabTextFrom selector="{{StorefrontBundledSection.dropDownOptionTierPrices('Drop-down Option')}}" stepKey="DropDownTierPriceTextProduct1"/> + <assertContains stepKey="assertDropDownTierPriceTextProduct1"> + <expectedResult type="string">Buy 5 for $5.00 each and save 50%</expectedResult> + <actualResult type="variable">DropDownTierPriceTextProduct1</actualResult> + </assertContains> + <!-- Check Tier Prices for product 2 --> + <selectOption selector="{{StorefrontBundledSection.dropDownOptionInput('Drop-down Option')}}" userInput="$$simpleProduct2CreateBundleProduct.sku$$ +$$$simpleProduct2CreateBundleProduct.price$$.00" stepKey="selectDropDownOptionProduct2"/> + <seeOptionIsSelected selector="{{StorefrontBundledSection.dropDownOptionInput('Drop-down Option')}}" userInput="$$simpleProduct2CreateBundleProduct.sku$$ +$$$simpleProduct2CreateBundleProduct.price$$.00" stepKey="checkDropDownOptionProduct2"/> + <grabTextFrom selector="{{StorefrontBundledSection.dropDownOptionTierPrices('Drop-down Option')}}" stepKey="dropDownTierPriceTextProduct2"/> + <assertContains stepKey="assertDropDownTierPriceTextProduct2"> + <expectedResult type="string">Buy 7 for $15.00 each and save 25%</expectedResult> + <actualResult type="variable">dropDownTierPriceTextProduct2</actualResult> + </assertContains> + + <!--"Radio Buttons" type option--> + <!-- Check Tier Prices for product 1 --> + <grabTextFrom selector="{{StorefrontBundledSection.radioButtonOptionLabel('Radio Buttons Option', '$$simpleProduct1CreateBundleProduct.sku$$')}}" stepKey="radioButtonsOptionTierPriceTextProduct1"/> + <assertContains stepKey="assertRadioButtonsOptionTierPriceTextProduct1"> + <expectedResult type="string">Buy 5 for $5.00 each and save 50%</expectedResult> + <actualResult type="variable">radioButtonsOptionTierPriceTextProduct1</actualResult> + </assertContains> + <!-- Check Tier Prices for product 2 --> + <grabTextFrom selector="{{StorefrontBundledSection.radioButtonOptionLabel('Radio Buttons Option', '$$simpleProduct2CreateBundleProduct.sku$$')}}" stepKey="radioButtonsOptionTierPriceTextProduct2"/> + <assertContains stepKey="assertRadioButtonsOptionTierPriceTextProduct2"> + <expectedResult type="string">Buy 7 for $15.00 each and save 25%</expectedResult> + <actualResult type="variable">radioButtonsOptionTierPriceTextProduct2</actualResult> + </assertContains> + + <!--"Checkbox" type option--> + <!-- Check Tier Prices for product 1 --> + <grabTextFrom selector="{{StorefrontBundledSection.checkboxOptionLabel('Checkbox Option', '$$simpleProduct1CreateBundleProduct.sku$$')}}" stepKey="checkBoxOptionTierPriceTextProduct1"/> + <assertContains stepKey="assertCheckBoxOptionTierPriceTextProduct1"> + <expectedResult type="string">Buy 5 for $5.00 each and save 50%</expectedResult> + <actualResult type="variable">checkBoxOptionTierPriceTextProduct1</actualResult> + </assertContains> + <!-- Check Tier Prices for product 2 --> + <grabTextFrom selector="{{StorefrontBundledSection.checkboxOptionLabel('Checkbox Option', '$$simpleProduct2CreateBundleProduct.sku$$')}}" stepKey="checkBoxOptionTierPriceTextProduct2"/> + <assertContains stepKey="assertCheckBoxOptionTierPriceTextProduct2"> + <expectedResult type="string">Buy 7 for $15.00 each and save 25%</expectedResult> + <actualResult type="variable">checkBoxOptionTierPriceTextProduct2</actualResult> + </assertContains> + </test> +</tests> diff --git a/app/code/Magento/Bundle/Test/Unit/Block/DataProviders/OptionPriceRendererTest.php b/app/code/Magento/Bundle/Test/Unit/Block/DataProviders/OptionPriceRendererTest.php new file mode 100644 index 0000000000000..d335554ef373c --- /dev/null +++ b/app/code/Magento/Bundle/Test/Unit/Block/DataProviders/OptionPriceRendererTest.php @@ -0,0 +1,95 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Test\Unit\Block\DataProviders; + +use Magento\Bundle\Block\DataProviders\OptionPriceRenderer; +use Magento\Catalog\Model\Product; +use Magento\Framework\Pricing\Render; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\View\Element\BlockInterface; +use Magento\Framework\View\LayoutInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Class to test additional data for bundle options + */ +class OptionPriceRendererTest extends TestCase +{ + /** + * @var LayoutInterface|MockObject + */ + private $layoutMock; + + /** + * @var OptionPriceRenderer + */ + private $renderer; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = new ObjectManager($this); + + $this->layoutMock = $this->createMock( + LayoutInterface::class + ); + + $this->renderer = $objectManager->getObject( + OptionPriceRenderer::class, + ['layout' => $this->layoutMock] + ); + } + + /** + * Test to render Tier price html + */ + public function testRenderTierPrice() + { + $expectedHtml = 'tier price html'; + $expectedArguments = ['zone' => Render::ZONE_ITEM_OPTION]; + + $productMock = $this->createMock(Product::class); + + $priceRenderer = $this->createPartialMock(BlockInterface::class, ['toHtml', 'render']); + $priceRenderer->expects($this->once()) + ->method('render') + ->with('tier_price', $productMock, $expectedArguments) + ->willReturn($expectedHtml); + + $this->layoutMock->method('getBlock') + ->with('product.price.render.default') + ->willReturn($priceRenderer); + + $this->assertEquals( + $expectedHtml, + $this->renderer->renderTierPrice($productMock), + 'Render Tier price is wrong' + ); + } + + /** + * Test to render Tier price html when render block is not exists + */ + public function testRenderTierPriceNotExist() + { + $productMock = $this->createMock(Product::class); + + $this->layoutMock->method('getBlock') + ->with('product.price.render.default') + ->willReturn(false); + + $this->assertEquals( + '', + $this->renderer->renderTierPrice($productMock), + 'Render Tier price is wrong' + ); + } +} diff --git a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js index f8d2f8bc11116..49ee253ad1e88 100644 --- a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js +++ b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js @@ -27,7 +27,8 @@ define([ '<% } %>', controlContainer: 'dd', // should be eliminated priceFormat: {}, - isFixedPrice: false + isFixedPrice: false, + optionTierPricesBlocksSelector: '#option-tier-prices-{1} [data-role="selection-tier-prices"]' }; $.widget('mage.priceBundle', { @@ -91,6 +92,8 @@ define([ if (changes) { priceBox.trigger('updatePrice', changes); } + + this._displayTierPriceBlock(bundleOption); this.updateProductSummary(); }, @@ -207,6 +210,35 @@ define([ return this; }, + /** + * Show or hide option tier prices block + * + * @param {Object} optionElement + * @private + */ + _displayTierPriceBlock: function (optionElement) { + var optionType = optionElement.prop('type'), + optionId, + optionValue, + optionTierPricesElements; + + if (optionType === 'select-one') { + optionId = utils.findOptionId(optionElement[0]); + optionValue = optionElement.val() || null; + optionTierPricesElements = $(this.options.optionTierPricesBlocksSelector.replace('{1}', optionId)); + + _.each(optionTierPricesElements, function (tierPriceElement) { + var selectionId = $(tierPriceElement).data('selection-id') + ''; + + if (selectionId === optionValue) { + $(tierPriceElement).show(); + } else { + $(tierPriceElement).hide(); + } + }); + } + }, + /** * Handler to update productSummary box */ diff --git a/app/code/Magento/Bundle/view/frontend/layout/catalog_product_view_type_bundle.xml b/app/code/Magento/Bundle/view/frontend/layout/catalog_product_view_type_bundle.xml index 5b8c050e5af54..d12f2e8f6a952 100644 --- a/app/code/Magento/Bundle/view/frontend/layout/catalog_product_view_type_bundle.xml +++ b/app/code/Magento/Bundle/view/frontend/layout/catalog_product_view_type_bundle.xml @@ -29,10 +29,22 @@ <container name="product.info.bundle.options.top" as="product_info_bundle_options_top"> <block class="Magento\Catalog\Block\Product\View" name="bundle.back.button" as="backButton" before="-" template="Magento_Bundle::catalog/product/view/backbutton.phtml"/> </container> - <block class="Magento\Bundle\Block\Catalog\Product\View\Type\Bundle\Option\Select" name="product.info.bundle.options.select" as="select"/> + <block class="Magento\Bundle\Block\Catalog\Product\View\Type\Bundle\Option\Select" name="product.info.bundle.options.select" as="select"> + <arguments> + <argument name="tier_price_renderer" xsi:type="object">\Magento\Bundle\Block\DataProviders\OptionPriceRenderer</argument> + </arguments> + </block> <block class="Magento\Bundle\Block\Catalog\Product\View\Type\Bundle\Option\Multi" name="product.info.bundle.options.multi" as="multi"/> - <block class="Magento\Bundle\Block\Catalog\Product\View\Type\Bundle\Option\Radio" name="product.info.bundle.options.radio" as="radio"/> - <block class="Magento\Bundle\Block\Catalog\Product\View\Type\Bundle\Option\Checkbox" name="product.info.bundle.options.checkbox" as="checkbox"/> + <block class="Magento\Bundle\Block\Catalog\Product\View\Type\Bundle\Option\Radio" name="product.info.bundle.options.radio" as="radio"> + <arguments> + <argument name="tier_price_renderer" xsi:type="object">\Magento\Bundle\Block\DataProviders\OptionPriceRenderer</argument> + </arguments> + </block> + <block class="Magento\Bundle\Block\Catalog\Product\View\Type\Bundle\Option\Checkbox" name="product.info.bundle.options.checkbox" as="checkbox"> + <arguments> + <argument name="tier_price_renderer" xsi:type="object">\Magento\Bundle\Block\DataProviders\OptionPriceRenderer</argument> + </arguments> + </block> </block> </referenceBlock> <referenceBlock name="product.info.form.options"> diff --git a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/checkbox.phtml b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/checkbox.phtml index bda649eb603e6..830d03c826f32 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/checkbox.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/checkbox.phtml @@ -19,6 +19,7 @@ <div class="nested options-list"> <?php if ($block->showSingle()): ?> <?= /* @escapeNotVerified */ $block->getSelectionQtyTitlePrice($_selections[0]) ?> + <?= /* @noEscape */ $block->getTierPriceRenderer()->renderTierPrice($_selections[0]) ?> <input type="hidden" class="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?> product bundle option" name="bundle_option[<?= /* @escapeNotVerified */ $_option->getId() ?>]" @@ -38,6 +39,8 @@ <label class="label" for="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>-<?= /* @escapeNotVerified */ $_selection->getSelectionId() ?>"> <span><?= /* @escapeNotVerified */ $block->getSelectionQtyTitlePrice($_selection) ?></span> + <br/> + <?= /* @noEscape */ $block->getTierPriceRenderer()->renderTierPrice($_selection) ?> </label> </div> <?php endforeach; ?> diff --git a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/radio.phtml b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/radio.phtml index 7ea89e8609818..1f33d97227ea3 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/radio.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/radio.phtml @@ -21,6 +21,7 @@ <div class="nested options-list"> <?php if ($block->showSingle()): ?> <?= /* @escapeNotVerified */ $block->getSelectionTitlePrice($_selections[0]) ?> + <?= /* @noEscape */ $block->getTierPriceRenderer()->renderTierPrice($_selections[0]) ?> <input type="hidden" class="bundle-option-<?= (int)$_option->getId() ?> product bundle option" name="bundle_option[<?= (int)$_option->getId() ?>]" @@ -57,6 +58,8 @@ <label class="label" for="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>-<?= /* @escapeNotVerified */ $_selection->getSelectionId() ?>"> <span><?= /* @escapeNotVerified */ $block->getSelectionTitlePrice($_selection) ?></span> + <br/> + <?= /* @noEscape */ $block->getTierPriceRenderer()->renderTierPrice($_selection) ?> </label> </div> <?php endforeach; ?> diff --git a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/select.phtml b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/select.phtml index 977daa2b2a446..4ea00f62b2043 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/select.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/select.phtml @@ -20,6 +20,7 @@ <div class="control"> <?php if ($block->showSingle()): ?> <?= /* @escapeNotVerified */ $block->getSelectionTitlePrice($_selections[0]) ?> + <?= /* @noEscape */ $block->getTierPriceRenderer()->renderTierPrice($_selections[0]) ?> <input type="hidden" class="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?> product bundle option" name="bundle_option[<?= /* @escapeNotVerified */ $_option->getId() ?>]" @@ -39,6 +40,15 @@ </option> <?php endforeach; ?> </select> + <div id="option-tier-prices-<?= /* @escapeNotVerified */ $_option->getId() ?>" class="option-tier-prices"> + <?php foreach ($_selections as $_selection): ?> + <div data-role="selection-tier-prices" + data-selection-id="<?= /* @escapeNotVerified */ $_selection->getSelectionId() ?>" + class="selection-tier-prices"> + <?= /* @noEscape */ $block->getTierPriceRenderer()->renderTierPrice($_selection) ?> + </div> + <?php endforeach; ?> + </div> <?php endif; ?> <div class="nested"> <div class="field qty qty-holder"> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index 6dc1b48faa72c..e68c75858102f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -106,7 +106,7 @@ <actionGroup name="ProductSetAdvancedPricing"> <arguments> - <argument name="website"/> + <argument name="website" type="string" defaultValue="All Websites [USD]"/> <argument name="group" type="string" defaultValue="Retailer"/> <argument name="quantity" type="string" defaultValue="1"/> <argument name="price" type="string" defaultValue="Discount"/> @@ -117,7 +117,7 @@ <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForCustomerGroupPriceAddButton"/> <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="addCustomerGroupAllGroupsQty1PriceDiscountAnd10percent"/> <waitForElement selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" stepKey="waitForSelectCustomerGroupNameAttribute"/> - <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceWebsiteSelect('0')}}" userInput="{{website.name}}" stepKey="selectProductWebsiteValue"/> + <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceWebsiteSelect('0')}}" userInput="{{website}}" stepKey="selectProductWebsiteValue"/> <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" userInput="{{group}}" stepKey="selectProductCustomGroupValue"/> <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" userInput="{{quantity}}" stepKey="fillProductTierPriceQtyInput"/> <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceValueTypeSelect('0')}}" userInput="{{price}}" stepKey="selectProductTierPriceValueType"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml index a1d9b4fb7b9a2..fd364682011c6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml @@ -71,7 +71,7 @@ <argument name="website" value="SecondWebsite"/> </actionGroup> <actionGroup ref="ProductSetAdvancedPricing" stepKey="setAdvancedPricingForProduct1"> - <argument name="website" value="SecondWebsite"/> + <argument name="website" value="{{SecondWebsite.name}}"/> </actionGroup> <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForProduct2"> @@ -84,7 +84,7 @@ <argument name="website" value="SecondWebsite"/> </actionGroup> <actionGroup ref="ProductSetAdvancedPricing" stepKey="setAdvancedPricingForProduct2"> - <argument name="website" value="SecondWebsite"/> + <argument name="website" value="{{SecondWebsite.name}}"/> </actionGroup> <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForProduct3"> @@ -97,7 +97,7 @@ <argument name="website" value="SecondWebsite"/> </actionGroup> <actionGroup ref="ProductSetAdvancedPricing" stepKey="setAdvancedPricingForProduct3"> - <argument name="website" value="SecondWebsite"/> + <argument name="website" value="{{SecondWebsite.name}}"/> </actionGroup> <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForProduct4"> @@ -110,7 +110,7 @@ <argument name="website" value="SecondWebsite"/> </actionGroup> <actionGroup ref="ProductSetAdvancedPricing" stepKey="setAdvancedPricingForProduct4"> - <argument name="website" value="SecondWebsite"/> + <argument name="website" value="{{SecondWebsite.name}}"/> </actionGroup> <!--Flush cache--> diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceTest.php index 2a68ff48e5f9a..4a5757aae3134 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceTest.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceTest.php @@ -6,7 +6,7 @@ namespace Magento\Bundle\Model\Product; /** - * @magentoDataFixture Magento/Bundle/_files/product_with_tier_pricing.php + * Class to test bundle prices */ class PriceTest extends \PHPUnit\Framework\TestCase { @@ -22,6 +22,9 @@ protected function setUp() ); } + /** + * @magentoDataFixture Magento/Bundle/_files/product_with_tier_pricing.php + */ public function testGetTierPrice() { /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ @@ -37,4 +40,50 @@ public function testGetTierPrice() $this->assertEquals(20.0, $this->_model->getTierPrice(4, $product)); $this->assertEquals(30.0, $this->_model->getTierPrice(5, $product)); } + + /** + * Test calculation final price for bundle product with tire price in simple product + * + * @param float $bundleQty + * @param float $selectionQty + * @param float $finalPrice + * @magentoDataFixture Magento/Bundle/_files/product_with_simple_tier_pricing.php + * @dataProvider getSelectionFinalTotalPriceWithSimpleTierPriceDataProvider + */ + public function testGetSelectionFinalTotalPriceWithSimpleTierPrice( + float $bundleQty, + float $selectionQty, + float $finalPrice + ) { + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ + $productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $bundleProduct = $productRepository->get('bundle-product'); + $simpleProduct = $productRepository->get('simple'); + $simpleProduct->setCustomerGroupId(\Magento\Customer\Model\Group::CUST_GROUP_ALL); + + $this->assertEquals( + $finalPrice, + $this->_model->getSelectionFinalTotalPrice( + $bundleProduct, + $simpleProduct, + $bundleQty, + $selectionQty, + false + ), + 'Tier price calculation for Simple product is wrong' + ); + } + + /** + * @return array + */ + public function getSelectionFinalTotalPriceWithSimpleTierPriceDataProvider(): array + { + return [ + [1, 1, 10], + [2, 1, 8], + [5, 1, 5], + ]; + } } diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_simple_tier_pricing.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_simple_tier_pricing.php new file mode 100644 index 0000000000000..30f0978480701 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_simple_tier_pricing.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; + +/** @var $productFactory Magento\Catalog\Model\ProductFactory */ +$productFactory = $objectManager->create(\Magento\Catalog\Model\ProductFactory::class); +/** @var $bundleProduct \Magento\Catalog\Model\Product */ +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId('bundle') + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([1]) + ->setPriceType(\Magento\Bundle\Model\Product\Price::PRICE_TYPE_DYNAMIC) + ->setPriceView(1) + ->setName('Bundle Product') + ->setSku('bundle-product') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->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, + ]) + ->setBundleOptionsData( + [ + [ + 'title' => 'Bundle Product Items', + 'default_title' => 'Bundle Product Items', + 'type' => 'checkbox', + 'required' => 1, + 'delete' => '', + ], + ] + ) + ->setBundleSelectionsData( + [[['product_id' => $product->getId(), 'selection_qty' => 1, 'delete' => '']]] + ); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_simple_tier_pricing_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_simple_tier_pricing_rollback.php new file mode 100644 index 0000000000000..aa661c7412d42 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_simple_tier_pricing_rollback.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_rollback.php'; + +/** @var \Magento\Framework\Registry $registry */ +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +try { + $product = $productRepository->get('bundle-product'); + $productRepository->delete($product); +} catch (\Magento\Framework\Exception\NoSuchEntityException $exception) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); From e38ff89db6f5c14ed2cf5945b67e244afd928204 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Thu, 4 Apr 2019 13:28:55 +0300 Subject: [PATCH 1051/1295] MAGETWO-96898: Fixed Tier Pricing for Bundle items doesn't work --- .../Test/StorefrontCheckBundleProductOptionTierPrices.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPrices.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPrices.xml index 23282c2829138..89423c2c63658 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPrices.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPrices.xml @@ -13,8 +13,8 @@ <features value="Bundle"/> <stories value="View bundle products"/> <title value="Check tier prices for bundle options"/> - <testCaseId value="MAGETWO-98968"/> - <useCaseId value="MAGETWO-98603"/> + <testCaseId value="MAGETWO-99047"/> + <useCaseId value="MAGETWO-96898"/> <group value="catalog"/> <group value="bundle"/> </annotations> From aaf8ce2426a2c78c76379d27bdce91ecb556a123 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Thu, 4 Apr 2019 13:39:55 +0300 Subject: [PATCH 1052/1295] MAGETWO-98569: Shipping quote in cart not persisted for Guest customers when Persistent Shopping Cart is enabled --- .../StorefrontMiniCartActionGroup.xml | 7 ++ .../Magento/Persistent/Model/QuoteManager.php | 3 + .../CheckExpirePersistentQuoteObserver.php | 12 ++- .../SetQuotePersistentDataObserver.php | 10 ++- .../ShippingQuotePersistedForGuestTest.xml | 85 +++++++++++++++++++ ...CheckExpirePersistentQuoteObserverTest.php | 20 ++++- .../SetQuotePersistentDataObserverTest.php | 7 +- 7 files changed, 135 insertions(+), 9 deletions(-) create mode 100644 app/code/Magento/Persistent/Test/Mftf/Test/ShippingQuotePersistedForGuestTest.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontMiniCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontMiniCartActionGroup.xml index 3390dc588b69b..a02fbe1aea031 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontMiniCartActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontMiniCartActionGroup.xml @@ -35,4 +35,11 @@ <click selector="{{StoreFrontRemoveItemModalSection.ok}}" stepKey="confirmDelete"/> <waitForPageLoad stepKey="waitForDeleteToFinish"/> </actionGroup> + + <!--Check that the minicart is empty--> + <actionGroup name="assertMiniCartEmpty"> + <dontSeeElement selector="{{StorefrontMinicartSection.productCount}}" stepKey="dontSeeMinicartProductCount"/> + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="expandMinicart"/> + <see selector="{{StorefrontMinicartSection.minicartContent}}" userInput="You have no items in your shopping cart." stepKey="seeEmptyCartMessage"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Persistent/Model/QuoteManager.php b/app/code/Magento/Persistent/Model/QuoteManager.php index 35c2c70be30dc..8ae22e4c26c6f 100644 --- a/app/code/Magento/Persistent/Model/QuoteManager.php +++ b/app/code/Magento/Persistent/Model/QuoteManager.php @@ -7,6 +7,8 @@ /** * Class QuoteManager + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class QuoteManager { @@ -87,6 +89,7 @@ public function setGuest($checkQuote = false) ->setCustomerLastname(null) ->setCustomerGroupId(\Magento\Customer\Api\Data\GroupInterface::NOT_LOGGED_IN_ID) ->setIsPersistent(false) + ->setCustomerIsGuest(true) ->removeAllAddresses(); //Create guest addresses $quote->getShippingAddress(); diff --git a/app/code/Magento/Persistent/Observer/CheckExpirePersistentQuoteObserver.php b/app/code/Magento/Persistent/Observer/CheckExpirePersistentQuoteObserver.php index 42baf7d692a7c..7e5a5769e00a5 100644 --- a/app/code/Magento/Persistent/Observer/CheckExpirePersistentQuoteObserver.php +++ b/app/code/Magento/Persistent/Observer/CheckExpirePersistentQuoteObserver.php @@ -1,6 +1,5 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -11,6 +10,11 @@ use Magento\Framework\Event\ObserverInterface; +/** + * Observer of expired session + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) + */ class CheckExpirePersistentQuoteObserver implements ObserverInterface { /** @@ -110,8 +114,12 @@ public function execute(\Magento\Framework\Event\Observer $observer) !$this->_persistentSession->isPersistent() && !$this->_customerSession->isLoggedIn() && $this->_checkoutSession->getQuoteId() && - !$this->isRequestFromCheckoutPage($this->request) + !$this->isRequestFromCheckoutPage($this->request) && // persistent session does not expire on onepage checkout page + ( + $this->_checkoutSession->getQuote()->getIsPersistent() || + $this->_checkoutSession->getQuote()->getCustomerIsGuest() + ) ) { $this->_eventManager->dispatch('persistent_session_expired'); $this->quoteManager->expire(); diff --git a/app/code/Magento/Persistent/Observer/SetQuotePersistentDataObserver.php b/app/code/Magento/Persistent/Observer/SetQuotePersistentDataObserver.php index db6b6d1ee370d..2803bc998dcbe 100644 --- a/app/code/Magento/Persistent/Observer/SetQuotePersistentDataObserver.php +++ b/app/code/Magento/Persistent/Observer/SetQuotePersistentDataObserver.php @@ -1,6 +1,5 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -8,6 +7,11 @@ use Magento\Framework\Event\ObserverInterface; +/** + * Observer for setting "is_persistent" value to quote + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) + */ class SetQuotePersistentDataObserver implements ObserverInterface { /** @@ -73,8 +77,8 @@ public function execute(\Magento\Framework\Event\Observer $observer) } if (( - ($this->_persistentSession->isPersistent() && !$this->_customerSession->isLoggedIn()) - && !$this->_persistentData->isShoppingCartPersist() + ($this->_persistentSession->isPersistent()) + && $this->_persistentData->isShoppingCartPersist() ) && $this->quoteManager->isPersistent() ) { diff --git a/app/code/Magento/Persistent/Test/Mftf/Test/ShippingQuotePersistedForGuestTest.xml b/app/code/Magento/Persistent/Test/Mftf/Test/ShippingQuotePersistedForGuestTest.xml new file mode 100644 index 0000000000000..deb23e125b3f7 --- /dev/null +++ b/app/code/Magento/Persistent/Test/Mftf/Test/ShippingQuotePersistedForGuestTest.xml @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="ShippingQuotePersistedForGuestTest"> + <annotations> + <features value="Persistent"/> + <stories value="Guest checkout"/> + <title value="Estimate Shipping and Tax block sections on shipping cart saving correctly for Guest."/> + <description value="Verify that 'Estimate Shipping and Tax' block sections on shipping cart saving correctly for Guest after switching to another page. And check that the shopping cart is cleared after reset persistent cookie."/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-99048"/> + <useCaseId value="MAGETWO-98569"/> + <group value="persistent"/> + </annotations> + <before> + <!--Enabled The Persistent Shopping Cart feature --> + <createData entity="PersistentConfigEnabled" stepKey="enablePersistent"/> + <createData entity="PersistentLogoutClearDisabled" stepKey="persistentLogoutClearDisable"/> + <!--Create simple product--> + <createData entity="SimpleSubCategory" stepKey="createSubCategory"/> + <createData entity="SimpleProduct2" stepKey="createProduct"> + <requiredEntity createDataKey="createSubCategory"/> + </createData> + <!--Create customer--> + <createData entity="Simple_US_Customer" stepKey="createCustomer"> + <field key="firstname">John1</field> + <field key="lastname">Doe1</field> + </createData> + </before> + <after> + <!--Revert persistent configuration to default--> + <createData entity="PersistentConfigDefault" stepKey="setDefaultPersistentState"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!--Step 1: Login as a Customer with remember me checked--> + <actionGroup ref="CustomerLoginOnStorefrontWithRememberMeChecked" stepKey="loginToStorefrontAccountWithRememberMeChecked"> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <!--Step 2: Open the Product Page and add the product to shopping cart--> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToProductPageAsLoggedUser"/> + <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProductToCartAsLoggedUser"> + <argument name="productName" value="$$createProduct.name$$"/> + </actionGroup> + <!--Step 3: Log out, reset persistent cookie and go to homepage--> + <amOnPage url="{{StorefrontCustomerSignOutPage.url}}" stepKey="signOut"/> + <waitForLoadingMaskToDisappear stepKey="waitSignOutPage"/> + <resetCookie userInput="persistent_shopping_cart" stepKey="resetPersistentCookie"/> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePageAfterResetPersistentCookie"/> + <waitForPageLoad stepKey="waitHomePageLoadAfterResetCookie"/> + <!--Check that the minicart is empty--> + <actionGroup ref="assertMiniCartEmpty" after="waitHomePageLoadAfterResetCookie" stepKey="seeMinicartEmpty"/> + <!--Step 4: Add the product to shopping cart and open cart--> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToProductPageAsGuestUser"/> + <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProductToCartAsGuestUser"> + <argument name="productName" value="$$createProduct.name$$"/> + </actionGroup> + <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartBeforeChangeShippingAndTaxSection"/> + <!--Step 5: Open Estimate Shipping and Tax block and fill the sections--> + <conditionalClick selector="{{StorefrontCheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{StorefrontCheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingAndTax" /> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> + <selectOption selector="{{StorefrontCheckoutCartSummarySection.country}}" userInput="{{US_Address_CA.country}}" stepKey="selectUSCountry"/> + <selectOption selector="{{StorefrontCheckoutCartSummarySection.region}}" userInput="{{US_Address_CA.state}}" stepKey="selectCaliforniaRegion"/> + <fillField selector="{{StorefrontCheckoutCartSummarySection.postcode}}" userInput="{{US_Address_CA.postcode}}" stepKey="inputPostCode"/> + <!--Step 6: Go to Homepage--> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePageAfterChangingShippingAndTaxSection"/> + <!--Step 7: Go to shopping cart and check "Estimate Shipping and Tax" fields values are saved--> + <actionGroup ref="clickViewAndEditCartFromMiniCart" after="goToHomePageAfterChangingShippingAndTaxSection" stepKey="goToShoppingCartAfterChangingShippingAndTaxSection"/> + <conditionalClick selector="{{StorefrontCheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{StorefrontCheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingAndTaxAfterChanging" /> + <seeOptionIsSelected selector="{{StorefrontCheckoutCartSummarySection.country}}" userInput="{{US_Address_CA.country}}" stepKey="checkCustomerCountry" /> + <seeOptionIsSelected selector="{{StorefrontCheckoutCartSummarySection.region}}" userInput="{{US_Address_CA.state}}" stepKey="checkCustomerRegion" /> + <grabValueFrom selector="{{StorefrontCheckoutCartSummarySection.postcode}}" stepKey="grabTextPostCode"/> + <assertEquals message="Customer postcode is invalid" stepKey="checkCustomerPostcode"> + <expectedResult type="string">{{US_Address_CA.postcode}}</expectedResult> + <actualResult type="variable">grabTextPostCode</actualResult> + </assertEquals> + </test> +</tests> diff --git a/app/code/Magento/Persistent/Test/Unit/Observer/CheckExpirePersistentQuoteObserverTest.php b/app/code/Magento/Persistent/Test/Unit/Observer/CheckExpirePersistentQuoteObserverTest.php index 29a3196c5f45e..65e8cd2f6ae24 100644 --- a/app/code/Magento/Persistent/Test/Unit/Observer/CheckExpirePersistentQuoteObserverTest.php +++ b/app/code/Magento/Persistent/Test/Unit/Observer/CheckExpirePersistentQuoteObserverTest.php @@ -1,12 +1,16 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Persistent\Test\Unit\Observer; +use Magento\Quote\Model\Quote; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class CheckExpirePersistentQuoteObserverTest extends \PHPUnit\Framework\TestCase { /** @@ -54,6 +58,11 @@ class CheckExpirePersistentQuoteObserverTest extends \PHPUnit\Framework\TestCase */ private $requestMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject|Quote + */ + private $quoteMock; + protected function setUp() { $this->sessionMock = $this->createMock(\Magento\Persistent\Helper\Session::class); @@ -79,6 +88,10 @@ protected function setUp() $this->checkoutSessionMock, $this->requestMock ); + $this->quoteMock = $this->getMockBuilder(Quote::class) + ->setMethods(['getCustomerIsGuest', 'getIsPersistent']) + ->disableOriginalConstructor() + ->getMock(); } public function testExecuteWhenCanNotApplyPersistentData() @@ -128,6 +141,11 @@ public function testExecuteWhenPersistentIsEnabled( ->will($this->returnValue(true)); $this->persistentHelperMock->expects($this->once())->method('isEnabled')->will($this->returnValue(true)); $this->sessionMock->expects($this->once())->method('isPersistent')->will($this->returnValue(false)); + $this->checkoutSessionMock + ->method('getQuote') + ->willReturn($this->quoteMock); + $this->quoteMock->method('getCustomerIsGuest')->willReturn(true); + $this->quoteMock->method('getIsPersistent')->willReturn(true); $this->customerSessionMock ->expects($this->atLeastOnce()) ->method('isLoggedIn') diff --git a/app/code/Magento/Persistent/Test/Unit/Observer/SetQuotePersistentDataObserverTest.php b/app/code/Magento/Persistent/Test/Unit/Observer/SetQuotePersistentDataObserverTest.php index 6724743789cea..ffa829e8456cc 100644 --- a/app/code/Magento/Persistent/Test/Unit/Observer/SetQuotePersistentDataObserverTest.php +++ b/app/code/Magento/Persistent/Test/Unit/Observer/SetQuotePersistentDataObserverTest.php @@ -7,6 +7,9 @@ namespace Magento\Persistent\Test\Unit\Observer; +/** + * Observer test for setting "is_persistent" value to quote + */ class SetQuotePersistentDataObserverTest extends \PHPUnit\Framework\TestCase { /** @@ -83,7 +86,6 @@ public function testExecuteWhenQuoteNotExist() ->method('getEvent') ->will($this->returnValue($this->eventManagerMock)); $this->eventManagerMock->expects($this->once())->method('getQuote'); - $this->customerSessionMock->expects($this->never())->method('isLoggedIn'); $this->model->execute($this->observerMock); } @@ -98,8 +100,7 @@ public function testExecuteWhenSessionIsPersistent() ->expects($this->once()) ->method('getQuote') ->will($this->returnValue($this->quoteMock)); - $this->customerSessionMock->expects($this->once())->method('isLoggedIn')->will($this->returnValue(false)); - $this->helperMock->expects($this->once())->method('isShoppingCartPersist')->will($this->returnValue(false)); + $this->helperMock->expects($this->once())->method('isShoppingCartPersist')->will($this->returnValue(true)); $this->quoteManagerMock->expects($this->once())->method('isPersistent')->will($this->returnValue(true)); $this->quoteMock->expects($this->once())->method('setIsPersistent')->with(true); $this->model->execute($this->observerMock); From 804f2579dbb702ea4e42ec9f4ca004471aeac296 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Thu, 4 Apr 2019 13:54:32 +0300 Subject: [PATCH 1053/1295] MAGETWO-98569: Shipping quote in cart not persisted for Guest customers when Persistent Shopping Cart is enabled --- .../Test/Mftf/Test/ShippingQuotePersistedForGuestTest.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Persistent/Test/Mftf/Test/ShippingQuotePersistedForGuestTest.xml b/app/code/Magento/Persistent/Test/Mftf/Test/ShippingQuotePersistedForGuestTest.xml index deb23e125b3f7..2d46b7b56fd04 100644 --- a/app/code/Magento/Persistent/Test/Mftf/Test/ShippingQuotePersistedForGuestTest.xml +++ b/app/code/Magento/Persistent/Test/Mftf/Test/ShippingQuotePersistedForGuestTest.xml @@ -22,7 +22,7 @@ <!--Enabled The Persistent Shopping Cart feature --> <createData entity="PersistentConfigEnabled" stepKey="enablePersistent"/> <createData entity="PersistentLogoutClearDisabled" stepKey="persistentLogoutClearDisable"/> - <!--Create simple product--> + <!--Create category and simple product--> <createData entity="SimpleSubCategory" stepKey="createSubCategory"/> <createData entity="SimpleProduct2" stepKey="createProduct"> <requiredEntity createDataKey="createSubCategory"/> @@ -38,6 +38,7 @@ <createData entity="PersistentConfigDefault" stepKey="setDefaultPersistentState"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createSubCategory" stepKey="deleteSubCategory"/> <actionGroup ref="logout" stepKey="logout"/> </after> <!--Step 1: Login as a Customer with remember me checked--> From a5a034d097f5c982a989e7a1f856fb94a9264a71 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Thu, 4 Apr 2019 14:49:54 +0300 Subject: [PATCH 1054/1295] MAGETWO-95281: quote error if a customer is changed to another company --- .../Backend/Test/Mftf/Section/AdminPopupModalSection.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminPopupModalSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminPopupModalSection.xml index fd6c88d0f6572..4cd6dbe5d8584 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminPopupModalSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminPopupModalSection.xml @@ -10,6 +10,6 @@ <section name="AdminPopupModalSection"> <element name="message" type="text" selector="aside.modal-popup .modal-content .popup-window-content"/> <element name="ok" type="button" selector="//span[contains(text(),'Ok')]/ancestor::button"/> - <element name="confirm" type="button" selector="//aside[contains(@class, 'modal-popup')]//footer/button[normalize-space(.)='Confirm']"/> + <element name="confirm" type="button" selector="//aside[contains(@class, 'modal-popup')]//footer/button[normalize-space(.)='Confirm']" timeout="30"/> </section> </sections> From 5d0245df8119614ad52768f7d1b1bbfd35c4c626 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Thu, 4 Apr 2019 15:56:02 +0300 Subject: [PATCH 1055/1295] magento/magento2#21512: Static test fix. --- app/code/Magento/Checkout/view/frontend/web/js/shopping-cart.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/shopping-cart.js b/app/code/Magento/Checkout/view/frontend/web/js/shopping-cart.js index b09a003613970..8935242724f3e 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/shopping-cart.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/shopping-cart.js @@ -15,7 +15,7 @@ define([ var items, i; $(this.options.emptyCartButton).on('click', $.proxy(function (event) { - if (event.detail == 0) { + if (event.detail === 0) { return; } From 49f905ee881e06ce0d5d4c39b94ed3c097e00eca Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Thu, 4 Apr 2019 16:40:20 +0300 Subject: [PATCH 1056/1295] MAGETWO-98569: Shipping quote in cart not persisted for Guest customers when Persistent Shopping Cart is enabled --- .../StorefrontMiniCartActionGroup.xml | 2 +- ...ntQuoteShippingDataPersistedForGuestTest.xml} | 16 +++++++++------- .../CheckExpirePersistentQuoteObserverTest.php | 6 +++--- 3 files changed, 13 insertions(+), 11 deletions(-) rename app/code/Magento/Persistent/Test/Mftf/Test/{ShippingQuotePersistedForGuestTest.xml => StorefrontQuoteShippingDataPersistedForGuestTest.xml} (90%) diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontMiniCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontMiniCartActionGroup.xml index a02fbe1aea031..b3e3c4553d504 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontMiniCartActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontMiniCartActionGroup.xml @@ -37,7 +37,7 @@ </actionGroup> <!--Check that the minicart is empty--> - <actionGroup name="assertMiniCartEmpty"> + <actionGroup name="AssertMiniCartEmpty"> <dontSeeElement selector="{{StorefrontMinicartSection.productCount}}" stepKey="dontSeeMinicartProductCount"/> <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="expandMinicart"/> <see selector="{{StorefrontMinicartSection.minicartContent}}" userInput="You have no items in your shopping cart." stepKey="seeEmptyCartMessage"/> diff --git a/app/code/Magento/Persistent/Test/Mftf/Test/ShippingQuotePersistedForGuestTest.xml b/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontQuoteShippingDataPersistedForGuestTest.xml similarity index 90% rename from app/code/Magento/Persistent/Test/Mftf/Test/ShippingQuotePersistedForGuestTest.xml rename to app/code/Magento/Persistent/Test/Mftf/Test/StorefrontQuoteShippingDataPersistedForGuestTest.xml index 2d46b7b56fd04..908a037ed36a2 100644 --- a/app/code/Magento/Persistent/Test/Mftf/Test/ShippingQuotePersistedForGuestTest.xml +++ b/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontQuoteShippingDataPersistedForGuestTest.xml @@ -7,16 +7,17 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="ShippingQuotePersistedForGuestTest"> + <test name="StorefrontQuoteShippingDataPersistedForGuestTest"> <annotations> <features value="Persistent"/> - <stories value="Guest checkout"/> + <stories value="Checkout via Guest Checkout"/> <title value="Estimate Shipping and Tax block sections on shipping cart saving correctly for Guest."/> <description value="Verify that 'Estimate Shipping and Tax' block sections on shipping cart saving correctly for Guest after switching to another page. And check that the shopping cart is cleared after reset persistent cookie."/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-99048"/> <useCaseId value="MAGETWO-98569"/> <group value="persistent"/> + <group value="checkout"/> </annotations> <before> <!--Enabled The Persistent Shopping Cart feature --> @@ -39,7 +40,6 @@ <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createSubCategory" stepKey="deleteSubCategory"/> - <actionGroup ref="logout" stepKey="logout"/> </after> <!--Step 1: Login as a Customer with remember me checked--> <actionGroup ref="CustomerLoginOnStorefrontWithRememberMeChecked" stepKey="loginToStorefrontAccountWithRememberMeChecked"> @@ -51,13 +51,11 @@ <argument name="productName" value="$$createProduct.name$$"/> </actionGroup> <!--Step 3: Log out, reset persistent cookie and go to homepage--> - <amOnPage url="{{StorefrontCustomerSignOutPage.url}}" stepKey="signOut"/> - <waitForLoadingMaskToDisappear stepKey="waitSignOutPage"/> + <actionGroup ref="CustomerLogoutStorefrontActionGroup" stepKey="customerLogout"/> <resetCookie userInput="persistent_shopping_cart" stepKey="resetPersistentCookie"/> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePageAfterResetPersistentCookie"/> - <waitForPageLoad stepKey="waitHomePageLoadAfterResetCookie"/> <!--Check that the minicart is empty--> - <actionGroup ref="assertMiniCartEmpty" after="waitHomePageLoadAfterResetCookie" stepKey="seeMinicartEmpty"/> + <actionGroup ref="AssertMiniCartEmpty" after="amOnHomePageAfterResetPersistentCookie" stepKey="seeMinicartEmpty"/> <!--Step 4: Add the product to shopping cart and open cart--> <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToProductPageAsGuestUser"/> <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProductToCartAsGuestUser"> @@ -68,13 +66,17 @@ <conditionalClick selector="{{StorefrontCheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{StorefrontCheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingAndTax" /> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> <selectOption selector="{{StorefrontCheckoutCartSummarySection.country}}" userInput="{{US_Address_CA.country}}" stepKey="selectUSCountry"/> + <waitForPageLoad stepKey="waitAfterSelectCountry"/> <selectOption selector="{{StorefrontCheckoutCartSummarySection.region}}" userInput="{{US_Address_CA.state}}" stepKey="selectCaliforniaRegion"/> + <waitForPageLoad stepKey="waitAfterSelectRegion"/> <fillField selector="{{StorefrontCheckoutCartSummarySection.postcode}}" userInput="{{US_Address_CA.postcode}}" stepKey="inputPostCode"/> + <waitForPageLoad stepKey="waitAfterSelectPostcode"/> <!--Step 6: Go to Homepage--> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePageAfterChangingShippingAndTaxSection"/> <!--Step 7: Go to shopping cart and check "Estimate Shipping and Tax" fields values are saved--> <actionGroup ref="clickViewAndEditCartFromMiniCart" after="goToHomePageAfterChangingShippingAndTaxSection" stepKey="goToShoppingCartAfterChangingShippingAndTaxSection"/> <conditionalClick selector="{{StorefrontCheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{StorefrontCheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingAndTaxAfterChanging" /> + <waitForLoadingMaskToDisappear stepKey="waitEstimateBlock"/> <seeOptionIsSelected selector="{{StorefrontCheckoutCartSummarySection.country}}" userInput="{{US_Address_CA.country}}" stepKey="checkCustomerCountry" /> <seeOptionIsSelected selector="{{StorefrontCheckoutCartSummarySection.region}}" userInput="{{US_Address_CA.state}}" stepKey="checkCustomerRegion" /> <grabValueFrom selector="{{StorefrontCheckoutCartSummarySection.postcode}}" stepKey="grabTextPostCode"/> diff --git a/app/code/Magento/Persistent/Test/Unit/Observer/CheckExpirePersistentQuoteObserverTest.php b/app/code/Magento/Persistent/Test/Unit/Observer/CheckExpirePersistentQuoteObserverTest.php index 65e8cd2f6ae24..b6e88d4363b90 100644 --- a/app/code/Magento/Persistent/Test/Unit/Observer/CheckExpirePersistentQuoteObserverTest.php +++ b/app/code/Magento/Persistent/Test/Unit/Observer/CheckExpirePersistentQuoteObserverTest.php @@ -89,9 +89,9 @@ protected function setUp() $this->requestMock ); $this->quoteMock = $this->getMockBuilder(Quote::class) - ->setMethods(['getCustomerIsGuest', 'getIsPersistent']) - ->disableOriginalConstructor() - ->getMock(); + ->setMethods(['getCustomerIsGuest', 'getIsPersistent']) + ->disableOriginalConstructor() + ->getMock(); } public function testExecuteWhenCanNotApplyPersistentData() From 3ca6c98dc36f102cf68925339bf9e789e9e6f688 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Thu, 4 Apr 2019 17:04:01 +0300 Subject: [PATCH 1057/1295] MAGETWO-96898: Fixed Tier Pricing for Bundle items doesn't work --- .../catalog/product/view/type/bundle/option/select.phtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/select.phtml b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/select.phtml index 4ea00f62b2043..65d736f5792df 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/select.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/select.phtml @@ -40,10 +40,10 @@ </option> <?php endforeach; ?> </select> - <div id="option-tier-prices-<?= /* @escapeNotVerified */ $_option->getId() ?>" class="option-tier-prices"> + <div id="option-tier-prices-<?= $block->escapeHtml($_option->getId()) ?>" class="option-tier-prices"> <?php foreach ($_selections as $_selection): ?> <div data-role="selection-tier-prices" - data-selection-id="<?= /* @escapeNotVerified */ $_selection->getSelectionId() ?>" + data-selection-id="<?= $block->escapeHtml($_selection->getSelectionId()) ?>" class="selection-tier-prices"> <?= /* @noEscape */ $block->getTierPriceRenderer()->renderTierPrice($_selection) ?> </div> From f59a62d01c1569adc5539d2e63ef00efb60a937a Mon Sep 17 00:00:00 2001 From: Dmytro Yushkin <dyushkin@adobe.com> Date: Thu, 4 Apr 2019 10:53:39 -0500 Subject: [PATCH 1058/1295] MAGETWO-98670: Braintree Displaying Incorrect Ship-To Name - Logging moved from finally block --- app/code/Magento/Braintree/Controller/Paypal/Review.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Braintree/Controller/Paypal/Review.php b/app/code/Magento/Braintree/Controller/Paypal/Review.php index 6814fda8d3a62..15acb1859ec87 100644 --- a/app/code/Magento/Braintree/Controller/Paypal/Review.php +++ b/app/code/Magento/Braintree/Controller/Paypal/Review.php @@ -63,6 +63,7 @@ public function execute() $this->getRequest()->getPostValue('result', '{}'), true ); + $this->logger->debug($requestData); $quote = $this->checkoutSession->getQuote(); try { @@ -90,8 +91,6 @@ public function execute() return $resultPage; } catch (\Exception $e) { $this->messageManager->addExceptionMessage($e, $e->getMessage()); - } finally { - $this->logger->debug($requestData); } /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */ From 220c649838ec9f883819135dd98dd11e2ad54bc9 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Fri, 5 Apr 2019 09:53:19 +0300 Subject: [PATCH 1059/1295] MAGETWO-73613: My Wishlist - quantity input box issue --- .../ActionGroup/StorefrontCustomerWishlistActionGroup.xml | 7 ++++--- .../StorefrontCheckingMaxQtyAllowedInWishlistTest.xml | 8 ++++---- app/code/Magento/Wishlist/ViewModel/AllowedQuantity.php | 1 + 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml index 5c7cb8334e00f..7a88d6db79200 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml @@ -93,9 +93,10 @@ <argument name="maxQtyAllowed" type="string" default="10000"/> </arguments> <remove keyForRemoval="successMessage"/> - <waitForElement selector="{{StorefrontCustomerWishlistProductSection.productQtyError(product.name)}}" after="submitUpdateWishlist" stepKey="waitForErrorMessage"/> - <scrollToTopOfPage after="waitForErrorMessage" stepKey="scrollToTop"/> + <waitForAjaxLoad after="submitUpdateWishlist" stepKey="waitForAjaxLoad"/> + <scrollToTopOfPage after="waitForAjaxLoad" stepKey="scrollToTop"/> <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.productInfoByName(product.name)}}" after="scrollToTop" stepKey="moveMouseOverProduct"/> - <see selector="{{StorefrontCustomerWishlistProductSection.productQtyError(product.name)}}" userInput="The maximum you may purchase is {{maxQtyAllowed}}." after="moveMouseOverProduct" stepKey="seeErrorMessage"/> + <waitForElementVisible selector="{{StorefrontCustomerWishlistProductSection.productQtyError(product.name)}}" after="moveMouseOverProduct" stepKey="waitForErrorMessage"/> + <see selector="{{StorefrontCustomerWishlistProductSection.productQtyError(product.name)}}" userInput="The maximum you may purchase is {{maxQtyAllowed}}." after="waitForErrorMessage" stepKey="seeErrorMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontCheckingMaxQtyAllowedInWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontCheckingMaxQtyAllowedInWishlistTest.xml index 7670c554d7c97..4e9d3d9f1729b 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontCheckingMaxQtyAllowedInWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontCheckingMaxQtyAllowedInWishlistTest.xml @@ -10,9 +10,9 @@ <test name="StorefrontCheckingMaxQtyAllowedInWishlistTest"> <annotations> <features value="Wishlist"/> - <stories value="Add to Wishlist"/> - <title value="Checking of quantity input box in Wishlist"/> - <description value="Checking of quantity input box in Wishlist"/> + <stories value="Update wishlist item"/> + <title value="Validate quantity field during updating wishlist item"/> + <description value="Validate quantity field during updating wishlist item"/> <severity value="AVERAGE"/> <testCaseId value="MC-7192"/> <useCaseId value="MAGETWO-73613"/> @@ -44,7 +44,7 @@ </after> <!--Go to product page--> - <amOnPage url="{{StorefrontProductPage.url($createProduct.name$)}}" stepKey="goToProductPage"/> + <amOnPage url="{{StorefrontProductPage.url($createProduct.custom_attributes[url_key]$)}}" stepKey="goToProductPage"/> <!--Add product to Wishlist--> <actionGroup ref="StorefrontCustomerAddProductToWishlistActionGroup" stepKey="addProductToWishlist"> <argument name="productVar" value="$createProduct$"/> diff --git a/app/code/Magento/Wishlist/ViewModel/AllowedQuantity.php b/app/code/Magento/Wishlist/ViewModel/AllowedQuantity.php index e6bb908ebe82c..993d8817d035c 100644 --- a/app/code/Magento/Wishlist/ViewModel/AllowedQuantity.php +++ b/app/code/Magento/Wishlist/ViewModel/AllowedQuantity.php @@ -44,6 +44,7 @@ public function __construct(StockRegistry $stockRegistry) public function setItem(ItemInterface $item): self { $this->item = $item; + return $this; } From 8611d14d2cd91683348d4df075e1ba7df6171a0d Mon Sep 17 00:00:00 2001 From: Nazarn96 <nazarn96@gmail.com> Date: Fri, 5 Apr 2019 13:13:52 +0300 Subject: [PATCH 1060/1295] WYSIWYG Image-Popup is not working correctly with multipleEditors --- lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js index ed105bd2c18f5..604988588aac5 100755 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js @@ -238,12 +238,16 @@ define([ * @param {Object} o */ openFileBrowser: function (o) { + var targetElementID = tinymce.activeEditor.getElement().getAttribute('id'); + var originId = this.id; + this.initialize(targetElementID, this.config); + var typeTitle, storeId = this.config['store_id'] !== null ? this.config['store_id'] : 0, frameDialog = jQuery(o.win.frameElement).parents('[role="dialog"]'), wUrl = this.config['files_browser_window_url'] + - 'target_element_id/' + this.id + '/' + - 'store/' + storeId + '/'; + 'target_element_id/' + this.id + '/' + + 'store/' + storeId + '/'; this.mediaBrowserOpener = o.win; this.mediaBrowserTargetElementId = o.field; @@ -255,6 +259,8 @@ define([ typeTitle = this.translate('Insert File...'); } + this.initialize(originId, this.config); + frameDialog.hide(); jQuery('#mceModalBlocker').hide(); From 1489abaf8943264b1de2c72edd6a85ded232bbb3 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Fri, 5 Apr 2019 14:43:07 +0300 Subject: [PATCH 1061/1295] MAGETWO-73157: [GITHUB] Import customizable options adds it to another product if same SKU is filled#9457 --- .../Model/Product/Option/SaveHandler.php | 23 ++++- .../ActionGroup/CustomOptionsActionGroup.xml | 49 ++++++++--- .../Test/Mftf/Data/ProductOptionData.xml | 10 ++- ...AdminProductCustomizableOptionsSection.xml | 20 ++++- ...CustomizableOptionToProductWithSKUTest.xml | 87 +++++++++++++++++++ ...gurableProductWithFileCustomOptionTest.xml | 7 +- ...tSwatchProductWithFileCustomOptionTest.xml | 7 +- .../Mftf/Section/AdminMessagesSection.xml | 3 +- 8 files changed, 181 insertions(+), 25 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml diff --git a/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php b/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php index d15e8ef5efa55..bb3fe2880a39f 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php @@ -58,11 +58,28 @@ public function execute($entity, $arguments = []) } } if ($options) { - foreach ($options as $option) { - $this->optionRepository->save($option); - } + $this->processOptionsSaving($options, (bool)$entity->dataHasChangedFor('sku'), (string)$entity->getSku()); } return $entity; } + + /** + * Save custom options + * + * @param array $options + * @param bool $hasChangedSku + * @param string $newSku + * + * @return void + */ + private function processOptionsSaving(array $options, bool $hasChangedSku, string $newSku) + { + foreach ($options as $option) { + if ($hasChangedSku && $option->hasData('product_sku')) { + $option->setProductSku($newSku); + } + $this->optionRepository->save($option); + } + } } diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml index c095faa73d9b1..70e825498654f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml @@ -6,22 +6,51 @@ */ --> -<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"> - <!--Add a custom option of type "file" to a product--> - <actionGroup name="AddProductCustomOptionFile"> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddProductCustomOption"> <arguments> - <argument name="option" defaultValue="ProductOptionFile"/> + <argument name="option"/> + <argument name="optionIndex" type="string"/> </arguments> <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" visible="false" stepKey="openCustomOptionSection"/> <click selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" stepKey="clickAddOption"/> <waitForElementVisible selector="{{AdminProductCustomizableOptionsSection.lastOptionTitle}}" stepKey="waitForOption"/> <fillField selector="{{AdminProductCustomizableOptionsSection.lastOptionTitle}}" userInput="{{option.title}}" stepKey="fillTitle"/> <click selector="{{AdminProductCustomizableOptionsSection.lastOptionTypeParent}}" stepKey="openTypeSelect"/> - <click selector="{{AdminProductCustomizableOptionsSection.optionType('File')}}" stepKey="selectTypeFile"/> - <waitForElementVisible selector="{{AdminProductCustomizableOptionsSection.optionPrice}}" stepKey="waitForElements"/> - <fillField selector="{{AdminProductCustomizableOptionsSection.optionPrice}}" userInput="{{option.price}}" stepKey="fillPrice"/> - <selectOption selector="{{AdminProductCustomizableOptionsSection.optionPriceType}}" userInput="{{option.price_type}}" stepKey="selectPriceType"/> - <fillField selector="{{AdminProductCustomizableOptionsSection.optionFileExtensions}}" userInput="{{option.file_extension}}" stepKey="fillCompatibleExtensions"/> + <click selector="{{AdminProductCustomizableOptionsSection.optionType(option.type)}}" stepKey="selectTypeFile"/> + <waitForElementVisible selector="{{AdminProductCustomizableOptionsSection.optionPrice(optionIndex)}}" stepKey="waitForElements"/> + <fillField selector="{{AdminProductCustomizableOptionsSection.optionPrice(optionIndex)}}" userInput="{{option.price}}" stepKey="fillPrice"/> + <selectOption selector="{{AdminProductCustomizableOptionsSection.optionPriceType(optionIndex)}}" userInput="{{option.price_type}}" stepKey="selectPriceType"/> + <fillField selector="{{AdminProductCustomizableOptionsSection.optionSku(optionIndex)}}" userInput="{{option.title}}" stepKey="fillSku"/> + </actionGroup> + <!--Add a custom option of type "file" to a product--> + <actionGroup name="AddProductCustomOptionFile" extends="AddProductCustomOption"> + <fillField selector="{{AdminProductCustomizableOptionsSection.optionFileExtensions(optionIndex)}}" userInput="{{option.file_extension}}" after="fillSku" stepKey="fillCompatibleExtensions"/> + </actionGroup> + <actionGroup name="ImportProductCustomizableOptions"> + <arguments> + <argument name="productName" type="string"/> + </arguments> + <click selector="{{AdminProductCustomizableOptionsSection.importOptions}}" stepKey="clickImportOptions"/> + <waitForElementVisible selector="{{AdminProductImportOptionsSection.selectProductTitle}}" stepKey="waitForTitleVisible"/> + <conditionalClick selector="{{AdminProductImportOptionsSection.resetFiltersButton}}" dependentSelector="{{AdminProductImportOptionsSection.resetFiltersButton}}" visible="true" stepKey="clickResetFilters"/> + <click selector="{{AdminProductImportOptionsSection.filterButton}}" stepKey="clickFilterButton"/> + <waitForElementVisible selector="{{AdminProductImportOptionsSection.nameField}}" stepKey="waitForNameField"/> + <fillField selector="{{AdminProductImportOptionsSection.nameField}}" userInput="{{productName}}" stepKey="fillProductName"/> + <click selector="{{AdminProductImportOptionsSection.applyFiltersButton}}" stepKey="clickApplyFilters"/> + <checkOption selector="{{AdminProductImportOptionsSection.firstRowItemCheckbox}}" stepKey="checkProductCheckbox"/> + <click selector="{{AdminProductImportOptionsSection.importButton}}" stepKey="clickImport"/> + </actionGroup> + <actionGroup name="CheckCustomizableOptionImport"> + <arguments> + <argument name="option"/> + <argument name="optionIndex" type="string"/> + </arguments> + <grabValueFrom selector="{{AdminProductCustomizableOptionsSection.optionTitleInput(optionIndex)}}" stepKey="grabOptionTitle"/> + <grabValueFrom selector="{{AdminProductCustomizableOptionsSection.optionPrice(optionIndex)}}" stepKey="grabOptionPrice"/> + <grabValueFrom selector="{{AdminProductCustomizableOptionsSection.optionSku(optionIndex)}}" stepKey="grabOptionSku"/> + <assertEquals expected="{{option.title}}" expectedType="string" actual="$grabOptionTitle" stepKey="assertOptionTitle"/> + <assertEquals expected="{{option.price}}" expectedType="string" actual="$grabOptionPrice" stepKey="assertOptionPrice"/> + <assertEquals expected="{{option.title}}" expectedType="string" actual="$grabOptionSku" stepKey="assertOptionSku"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionData.xml index ae8bcf0893ed0..f720512ad2018 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionData.xml @@ -6,17 +6,21 @@ */ --> <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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="ProductOptionField" type="product_option"> <var key="product_sku" entityType="product" entityKey="sku" /> <data key="title">OptionField</data> - <data key="type">field</data> + <data key="type">Field</data> <data key="is_require">true</data> <data key="sort_order">1</data> <data key="price">10</data> <data key="price_type">fixed</data> <data key="max_characters">0</data> </entity> + <entity name="ProductOptionField2" type="product_option" extends="ProductOptionField"> + <data key="title">OptionField2</data> + <data key="price">20</data> + </entity> <entity name="ProductOptionArea" type="product_option"> <var key="product_sku" entityType="product" entityKey="sku" /> <data key="title">OptionArea</data> @@ -30,7 +34,7 @@ <entity name="ProductOptionFile" type="product_option"> <var key="product_sku" entityType="product" entityKey="sku" /> <data key="title">OptionFile</data> - <data key="type">file</data> + <data key="type">File</data> <data key="is_require">true</data> <data key="sort_order">3</data> <data key="price">9.99</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml index 303fa5ec6b942..033823574b441 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml @@ -7,7 +7,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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductCustomizableOptionsSection"> <element name="checkIfCustomizableOptionsTabOpen" type="text" selector="//span[text()='Customizable Options']/parent::strong/parent::*[@data-state-collapsible='closed']"/> <element name="customizableOptions" type="text" selector="//strong[contains(@class, 'admin__collapsible-title')]/span[text()='Customizable Options']" timeout="30"/> @@ -26,8 +26,20 @@ <element name="lastOptionTypeParent" type="block" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@class, 'admin__action-multiselect-text')]" /> <!-- var 1 represents the option type that you want to select, i.e "radio buttons" --> <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]']"/> + <element name="optionPrice" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr//*[@name='product[options][{{index}}][price]']" parameterized="true"/> + <element name="optionPriceType" type="select" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr//*[@name='product[options][{{index}}][price_type]']" parameterized="true"/> + <element name="optionFileExtensions" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr//*[@name='product[options][{{index}}][file_extension]']" parameterized="true"/> + <element name="optionSku" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr//*[@name='product[options][{{index}}][sku]']" parameterized="true"/> + <element name="optionTitleInput" type="input" selector="input[name='product[options][{{index}}][title]']" parameterized="true"/> + <element name="importOptions" type="button" selector="button[data-index='button_import']" timeout="30"/> + </section> + <section name="AdminProductImportOptionsSection"> + <element name="selectProductTitle" type="text" selector="//h1[contains(text(), 'Select Product')]"/> + <element name="filterButton" type="button" selector="button[data-action='grid-filter-expand']" timeout="30"/> + <element name="nameField" type="input" selector="input[name='name']"/> + <element name="applyFiltersButton" type="button" selector="button[data-action='grid-filter-apply']" timeout="30"/> + <element name="resetFiltersButton" type="button" selector="button[data-action='grid-filter-reset']" timeout="30"/> + <element name="firstRowItemCheckbox" type="input" selector="input[data-action='select-row']"/> + <element name="importButton" type="button" selector="//button[contains(@class, 'action-primary')]/span[contains(text(), 'Import')]" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml new file mode 100644 index 0000000000000..f5415d8ca81b9 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml @@ -0,0 +1,87 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminImportCustomizableOptionToProductWithSKUTest"> + <annotations> + <features value="Catalog"/> + <title value="Import customizable options to a product with existing SKU"/> + <description value="Import customizable options to a product with existing SKU"/> + <stories value="Import customizable options"/> + <severity value="MAJOR"/> + <testCaseId value="MC-15740"/> + <useCaseId value="MAGETWO-73157"/> + <group value="catalog"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createFirstProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct2" stepKey="createSecondProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="createFirstProduct" stepKey="deleteFirstProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="DeleteProductByName" stepKey="deleteSecondProduct"> + <argument name="product" value="$$createSecondProduct.name$$"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + </after> + + <!--Go to product page --> + <amOnPage url="{{AdminProductEditPage.url($$createFirstProduct.id$$)}}" stepKey="goToProductEditPage"/> + <actionGroup ref="AddProductCustomOption" stepKey="addFirstCustomOption"> + <argument name="option" value="ProductOptionField"/> + <argument name="optionIndex" value="0"/> + </actionGroup> + <actionGroup ref="AddProductCustomOption" stepKey="addSecondCustomOption"> + <argument name="option" value="ProductOptionField2"/> + <argument name="optionIndex" value="1"/> + </actionGroup> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + + <!--Change second product sku to first product sku--> + <amOnPage url="{{AdminProductEditPage.url($$createSecondProduct.id$$)}}" stepKey="goToSecondProductEditPage"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="$$createFirstProduct.sku$$" stepKey="fillProductSku"/> + <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" visible="false" stepKey="openCustomOptionSection"/> + + <!--Import customizable options and check--> + <actionGroup ref="ImportProductCustomizableOptions" stepKey="importProductCustomOptions"> + <argument name="productName" value="$$createFirstProduct.name$$"/> + </actionGroup> + <actionGroup ref="CheckCustomizableOptionImport" stepKey="checkFirstOptionImport"> + <argument name="option" value="ProductOptionField"/> + <argument name="optionIndex" value="0"/> + </actionGroup> + <actionGroup ref="CheckCustomizableOptionImport" stepKey="checkSecondOptionImport"> + <argument name="option" value="ProductOptionField2"/> + <argument name="optionIndex" value="1"/> + </actionGroup> + + <!--Save product and check sku changed message--> + <actionGroup ref="saveProductForm" stepKey="saveSecondProduct"/> + <waitForElementVisible selector="{{AdminMessagesSection.noticeMessage}}" stepKey="waitForSkuChangedMessage"/> + <see selector="{{AdminMessagesSection.noticeMessage}}" userInput="SKU for product $$createSecondProduct.name$$ has been changed to $$createFirstProduct.sku$$-1." stepKey="seeSkuChangedMessage"/> + + <!-- Check that custom options are present on Admin product page in Customizable Option section after Product save --> + <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" visible="false" stepKey="openCustomOptionSectionAfterProductSave"/> + <actionGroup ref="CheckCustomizableOptionImport" stepKey="checkFirstCustomOptionAfterProductSave"> + <argument name="option" value="ProductOptionField"/> + <argument name="optionIndex" value="0"/> + </actionGroup> + <actionGroup ref="CheckCustomizableOptionImport" stepKey="checkSecondCustomOptionAfterProductSave"> + <argument name="option" value="ProductOptionField2"/> + <argument name="optionIndex" value="1"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml index 36615d3af6b7b..31371c8611ca7 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontConfigurableProductWithFileCustomOptionTest"> <annotations> <features value="ConfigurableProduct"/> @@ -42,7 +42,10 @@ <argument name="category" value="$$createCategory$$"/> </actionGroup> <!--Add custom option to configurable product--> - <actionGroup ref="AddProductCustomOptionFile" stepKey="addCustomOptionToProduct"/> + <actionGroup ref="AddProductCustomOptionFile" stepKey="addCustomOptionToProduct"> + <argument name="option" value="ProductOptionFile"/> + <argument name="optionIndex" value="0"/> + </actionGroup> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> <!--Go to storefront--> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml index 8f13860f75ad1..0b2afcc348f65 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontSwatchProductWithFileCustomOptionTest"> <annotations> <features value="ConfigurableProduct"/> @@ -50,7 +50,10 @@ <!--Add swatch attribute to configurable product--> <actionGroup ref="CreateConfigurationsWithVisualSwatch" stepKey="createConfigurationsWithVisualSwatch"/> <!--Add custom option to configurable product--> - <actionGroup ref="AddProductCustomOptionFile" stepKey="addCustomOptionToProduct"/> + <actionGroup ref="AddProductCustomOptionFile" stepKey="addCustomOptionToProduct"> + <argument name="option" value="ProductOptionFile"/> + <argument name="optionIndex" value="0"/> + </actionGroup> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> <!--Go to storefront--> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminMessagesSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminMessagesSection.xml index 8880f7c3e1cc7..3d4efa13ce3a0 100644 --- a/app/code/Magento/Ui/Test/Mftf/Section/AdminMessagesSection.xml +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminMessagesSection.xml @@ -7,10 +7,11 @@ --> <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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminMessagesSection"> <element name="successMessage" type="text" selector=".message-success"/> <element name="errorMessage" type="text" selector=".message.message-error.error"/> <element name="warningMessage" type="text" selector=".message-warning"/> + <element name="noticeMessage" type="text" selector=".message-notice"/> </section> </sections> From 6ed43f624287ab83281c2426b54ad01e361fbe0c Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Thu, 14 Mar 2019 16:01:08 -0500 Subject: [PATCH 1062/1295] MAGETWO-97464: Braintree payment method not supported in multi-shipping - Add support of Braintree and Braintree PayPal payment methods on multi-shipping checkout --- .../Model/Multishipping/PlaceOrder.php | 177 ++++++++++++ app/code/Magento/Braintree/composer.json | 1 + app/code/Magento/Braintree/etc/config.xml | 4 + .../Magento/Braintree/etc/frontend/di.xml | 8 + app/code/Magento/Braintree/etc/payment.xml | 23 ++ .../layout/multishipping_checkout_billing.xml | 20 ++ .../templates/multishipping/form.phtml | 29 ++ .../templates/multishipping/form_paypal.phtml | 29 ++ .../multishipping/hosted-fields.js | 102 +++++++ .../method-renderer/multishipping/paypal.js | 143 ++++++++++ .../template/payment/multishipping/form.html | 106 ++++++++ .../payment/multishipping/paypal.html | 40 +++ .../frontend/templates/checkout/billing.phtml | 71 +++-- .../layout/multishipping_checkout_billing.xml | 19 ++ .../multishipping/checkmo_form.phtml | 28 ++ .../Fixtures/assign_items_per_address.php | 40 +++ .../Braintree/Fixtures/payment_braintree.php | 28 ++ .../Fixtures/payment_braintree_paypal.php | 28 ++ .../quote_with_split_items_braintree.php | 26 ++ ...uote_with_split_items_braintree_paypal.php | 26 ++ .../Braintree/Model/MultishippingTest.php | 254 ++++++++++++++++++ 21 files changed, 1173 insertions(+), 29 deletions(-) create mode 100644 app/code/Magento/Braintree/Model/Multishipping/PlaceOrder.php create mode 100644 app/code/Magento/Braintree/etc/payment.xml create mode 100644 app/code/Magento/Braintree/view/frontend/layout/multishipping_checkout_billing.xml create mode 100644 app/code/Magento/Braintree/view/frontend/templates/multishipping/form.phtml create mode 100644 app/code/Magento/Braintree/view/frontend/templates/multishipping/form_paypal.phtml create mode 100644 app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/hosted-fields.js create mode 100644 app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/paypal.js create mode 100644 app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/form.html create mode 100644 app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/paypal.html create mode 100644 app/code/Magento/OfflinePayments/view/frontend/layout/multishipping_checkout_billing.xml create mode 100644 app/code/Magento/OfflinePayments/view/frontend/templates/multishipping/checkmo_form.phtml create mode 100644 dev/tests/integration/testsuite/Magento/Braintree/Fixtures/assign_items_per_address.php create mode 100644 dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment_braintree.php create mode 100644 dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment_braintree_paypal.php create mode 100644 dev/tests/integration/testsuite/Magento/Braintree/Fixtures/quote_with_split_items_braintree.php create mode 100644 dev/tests/integration/testsuite/Magento/Braintree/Fixtures/quote_with_split_items_braintree_paypal.php create mode 100644 dev/tests/integration/testsuite/Magento/Braintree/Model/MultishippingTest.php diff --git a/app/code/Magento/Braintree/Model/Multishipping/PlaceOrder.php b/app/code/Magento/Braintree/Model/Multishipping/PlaceOrder.php new file mode 100644 index 0000000000000..8c8ea2ea69691 --- /dev/null +++ b/app/code/Magento/Braintree/Model/Multishipping/PlaceOrder.php @@ -0,0 +1,177 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Braintree\Model\Multishipping; + +use Magento\Braintree\Gateway\Command\GetPaymentNonceCommand; +use Magento\Braintree\Model\Ui\ConfigProvider; +use Magento\Braintree\Observer\DataAssignObserver; +use Magento\Braintree\Model\Ui\PayPal\ConfigProvider as PaypalConfigProvider; +use Magento\Multishipping\Model\Checkout\Type\Multishipping\PlaceOrderInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\Data\OrderPaymentExtensionInterface; +use Magento\Sales\Api\Data\OrderPaymentExtensionInterfaceFactory; +use Magento\Sales\Api\Data\OrderPaymentInterface; +use Magento\Sales\Api\OrderManagementInterface; +use Magento\Vault\Api\Data\PaymentTokenInterface; + +/** + * Order payments processing for multishipping checkout flow. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class PlaceOrder implements PlaceOrderInterface +{ + /** + * @var OrderManagementInterface + */ + private $orderManagement; + + /** + * @var OrderPaymentExtensionInterfaceFactory + */ + private $paymentExtensionFactory; + + /** + * @var GetPaymentNonceCommand + */ + private $getPaymentNonceCommand; + + /** + * @param OrderManagementInterface $orderManagement + * @param OrderPaymentExtensionInterfaceFactory $paymentExtensionFactory + * @param GetPaymentNonceCommand $getPaymentNonceCommand + */ + public function __construct( + OrderManagementInterface $orderManagement, + OrderPaymentExtensionInterfaceFactory $paymentExtensionFactory, + GetPaymentNonceCommand $getPaymentNonceCommand + ) { + $this->orderManagement = $orderManagement; + $this->paymentExtensionFactory = $paymentExtensionFactory; + $this->getPaymentNonceCommand = $getPaymentNonceCommand; + } + + /** + * @inheritdoc + */ + public function place(array $orderList): array + { + if (empty($orderList)) { + return []; + } + + $errorList = []; + $firstOrder = $this->orderManagement->place(array_shift($orderList)); + // get payment token from first placed order + $paymentToken = $this->getPaymentToken($firstOrder); + + foreach ($orderList as $order) { + try { + /** @var OrderInterface $order */ + $orderPayment = $order->getPayment(); + $this->setVaultPayment($orderPayment, $paymentToken); + $this->orderManagement->place($order); + } catch (\Exception $e) { + $incrementId = $order->getIncrementId(); + $errorList[$incrementId] = $e; + } + } + + return $errorList; + } + + /** + * Sets vault payment method. + * + * @param OrderPaymentInterface $orderPayment + * @param PaymentTokenInterface $paymentToken + * @return void + */ + private function setVaultPayment(OrderPaymentInterface $orderPayment, PaymentTokenInterface $paymentToken) + { + $vaultMethod = $this->getVaultPaymentMethod( + $orderPayment->getMethod() + ); + $orderPayment->setMethod($vaultMethod); + + $publicHash = $paymentToken->getPublicHash(); + $customerId = $paymentToken->getCustomerId(); + $result = $this->getPaymentNonceCommand->execute( + ['public_hash' => $publicHash, 'customer_id' => $customerId] + ) + ->get(); + + $orderPayment->setAdditionalInformation( + DataAssignObserver::PAYMENT_METHOD_NONCE, + $result['paymentMethodNonce'] + ); + $orderPayment->setAdditionalInformation( + PaymentTokenInterface::PUBLIC_HASH, + $publicHash + ); + $orderPayment->setAdditionalInformation( + PaymentTokenInterface::CUSTOMER_ID, + $customerId + ); + } + + /** + * Returns vault payment method. + * + * For placing sequence of orders, we need to replace the original method on the vault method. + * + * @param string $method + * @return string + */ + private function getVaultPaymentMethod(string $method): string + { + $vaultPaymentMap = [ + ConfigProvider::CODE => ConfigProvider::CC_VAULT_CODE, + PaypalConfigProvider::PAYPAL_CODE => PaypalConfigProvider::PAYPAL_VAULT_CODE + ]; + + return $vaultPaymentMap[$method] ?? $method; + } + + /** + * Returns payment token. + * + * @param OrderInterface $order + * @return PaymentTokenInterface + * @throws \BadMethodCallException + */ + private function getPaymentToken(OrderInterface $order): PaymentTokenInterface + { + $orderPayment = $order->getPayment(); + $extensionAttributes = $this->getExtensionAttributes($orderPayment); + $paymentToken = $extensionAttributes->getVaultPaymentToken(); + + if ($paymentToken === null) { + throw new \BadMethodCallException('Vault Payment Token should be defined for placed order payment.'); + } + + return $paymentToken; + } + + /** + * Gets payment extension attributes. + * + * @param OrderPaymentInterface $payment + * @return OrderPaymentExtensionInterface + */ + private function getExtensionAttributes(OrderPaymentInterface $payment): OrderPaymentExtensionInterface + { + $extensionAttributes = $payment->getExtensionAttributes(); + if (null === $extensionAttributes) { + $extensionAttributes = $this->paymentExtensionFactory->create(); + $payment->setExtensionAttributes($extensionAttributes); + } + + return $extensionAttributes; + } +} diff --git a/app/code/Magento/Braintree/composer.json b/app/code/Magento/Braintree/composer.json index ccfb268856bb6..0b9d76bc2ee9e 100644 --- a/app/code/Magento/Braintree/composer.json +++ b/app/code/Magento/Braintree/composer.json @@ -18,6 +18,7 @@ "magento/module-quote": "101.0.*", "magento/module-paypal": "100.2.*", "magento/module-ui": "101.0.*", + "magento/module-multishipping": "100.2.*", "braintree/braintree_php": "3.28.0" }, "suggest": { diff --git a/app/code/Magento/Braintree/etc/config.xml b/app/code/Magento/Braintree/etc/config.xml index a830c29368755..fe4cfab9c0e30 100644 --- a/app/code/Magento/Braintree/etc/config.xml +++ b/app/code/Magento/Braintree/etc/config.xml @@ -42,6 +42,7 @@ <paymentInfoKeys>cc_type,cc_number,avsPostalCodeResponseCode,avsStreetAddressResponseCode,cvvResponseCode,processorAuthorizationCode,processorResponseCode,processorResponseText,liabilityShifted,liabilityShiftPossible,riskDataId,riskDataDecision</paymentInfoKeys> <avs_ems_adapter>Magento\Braintree\Model\AvsEmsCodeMapper</avs_ems_adapter> <cvv_ems_adapter>Magento\Braintree\Model\CvvEmsCodeMapper</cvv_ems_adapter> + <group>braintree_group</group> </braintree> <braintree_paypal> <model>BraintreePayPalFacade</model> @@ -67,6 +68,7 @@ <privateInfoKeys>processorResponseCode,processorResponseText,paymentId</privateInfoKeys> <paymentInfoKeys>processorResponseCode,processorResponseText,paymentId,payerEmail</paymentInfoKeys> <supported_locales>en_US,en_GB,en_AU,da_DK,fr_FR,fr_CA,de_DE,zh_HK,it_IT,nl_NL,no_NO,pl_PL,es_ES,sv_SE,tr_TR,pt_BR,ja_JP,id_ID,ko_KR,pt_PT,ru_RU,th_TH,zh_CN,zh_TW</supported_locales> + <group>braintree_group</group> </braintree_paypal> <braintree_cc_vault> <model>BraintreeCreditCardVaultFacade</model> @@ -76,6 +78,7 @@ <tokenFormat>Magento\Braintree\Model\InstantPurchase\CreditCard\TokenFormatter</tokenFormat> <additionalInformation>Magento\Braintree\Model\InstantPurchase\PaymentAdditionalInformationProvider</additionalInformation> </instant_purchase> + <group>braintree_group</group> </braintree_cc_vault> <braintree_paypal_vault> <model>BraintreePayPalVaultFacade</model> @@ -85,6 +88,7 @@ <tokenFormat>Magento\Braintree\Model\InstantPurchase\PayPal\TokenFormatter</tokenFormat> <additionalInformation>Magento\Braintree\Model\InstantPurchase\PaymentAdditionalInformationProvider</additionalInformation> </instant_purchase> + <group>braintree_group</group> </braintree_paypal_vault> </payment> </default> diff --git a/app/code/Magento/Braintree/etc/frontend/di.xml b/app/code/Magento/Braintree/etc/frontend/di.xml index ea417c407dffd..d8d3a93b71dc3 100644 --- a/app/code/Magento/Braintree/etc/frontend/di.xml +++ b/app/code/Magento/Braintree/etc/frontend/di.xml @@ -61,4 +61,12 @@ <argument name="resolver" xsi:type="object">Magento\Braintree\Model\LocaleResolver</argument> </arguments> </type> + <type name="Magento\Multishipping\Model\Checkout\Type\Multishipping\PlaceOrderPool"> + <arguments> + <argument name="services" xsi:type="array"> + <item name="braintree" xsi:type="string">Magento\Braintree\Model\Multishipping\PlaceOrder</item> + <item name="braintree_paypal" xsi:type="string">Magento\Braintree\Model\Multishipping\PlaceOrder</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Braintree/etc/payment.xml b/app/code/Magento/Braintree/etc/payment.xml new file mode 100644 index 0000000000000..4cae049aaf5a9 --- /dev/null +++ b/app/code/Magento/Braintree/etc/payment.xml @@ -0,0 +1,23 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<payment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Payment:etc/payment.xsd"> + <groups> + <group id="braintree_group"> + <label>Braintree</label> + </group> + </groups> + <methods> + <method name="braintree"> + <allow_multiple_address>1</allow_multiple_address> + </method> + <method name="braintree_paypal"> + <allow_multiple_address>1</allow_multiple_address> + </method> + </methods> +</payment> diff --git a/app/code/Magento/Braintree/view/frontend/layout/multishipping_checkout_billing.xml b/app/code/Magento/Braintree/view/frontend/layout/multishipping_checkout_billing.xml new file mode 100644 index 0000000000000..06390d403e63d --- /dev/null +++ b/app/code/Magento/Braintree/view/frontend/layout/multishipping_checkout_billing.xml @@ -0,0 +1,20 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> + <body> + <referenceBlock name="checkout_billing"> + <arguments> + <argument name="form_templates" xsi:type="array"> + <item name="braintree" xsi:type="string">Magento_Braintree::multishipping/form.phtml</item> + <item name="braintree_paypal" xsi:type="string">Magento_Braintree::multishipping/form_paypal.phtml</item> + </argument> + </arguments> + </referenceBlock> + </body> +</page> diff --git a/app/code/Magento/Braintree/view/frontend/templates/multishipping/form.phtml b/app/code/Magento/Braintree/view/frontend/templates/multishipping/form.phtml new file mode 100644 index 0000000000000..bf8aa8dd09c2c --- /dev/null +++ b/app/code/Magento/Braintree/view/frontend/templates/multishipping/form.phtml @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +?> +<script> + require([ + 'uiLayout', + 'jquery' + ], function (layout, $) { + $(function () { + var paymentMethodData = { + method: 'braintree' + }; + layout([ + { + component: 'Magento_Braintree/js/view/payment/method-renderer/multishipping/hosted-fields', + name: 'payment_method_braintree', + method: paymentMethodData.method, + item: paymentMethodData + } + ]); + + $('body').trigger('contentUpdated'); + }) + }) +</script> +<!-- ko template: getTemplate() --><!-- /ko --> diff --git a/app/code/Magento/Braintree/view/frontend/templates/multishipping/form_paypal.phtml b/app/code/Magento/Braintree/view/frontend/templates/multishipping/form_paypal.phtml new file mode 100644 index 0000000000000..ea3eb2214c2d8 --- /dev/null +++ b/app/code/Magento/Braintree/view/frontend/templates/multishipping/form_paypal.phtml @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +?> +<script> + require([ + 'uiLayout', + 'jquery' + ], function (layout, $) { + $(function () { + var paymentMethodData = { + method: 'braintree_paypal' + }; + layout([ + { + component: 'Magento_Braintree/js/view/payment/method-renderer/multishipping/paypal', + name: 'payment_method_braintree_paypal', + method: paymentMethodData.method, + item: paymentMethodData + } + ]); + + $('body').trigger('contentUpdated'); + }) + }) +</script> +<!-- ko template: getTemplate() --><!-- /ko --> diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/hosted-fields.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/hosted-fields.js new file mode 100644 index 0000000000000..1ceebc8e66282 --- /dev/null +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/hosted-fields.js @@ -0,0 +1,102 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +/*browser:true*/ +/*global define*/ + +define([ + 'jquery', + 'Magento_Braintree/js/view/payment/method-renderer/hosted-fields', + 'Magento_Braintree/js/validator', + 'Magento_Ui/js/model/messageList', + 'mage/translate', + 'Magento_Checkout/js/model/full-screen-loader', + 'Magento_Checkout/js/action/set-payment-information', + 'Magento_Checkout/js/model/payment/additional-validators' +], function ( + $, + Component, + validator, + messageList, + $t, + fullScreenLoader, + setPaymentInformationAction, + additionalValidators +) { + 'use strict'; + + return Component.extend({ + defaults: { + template: 'Magento_Braintree/payment/multishipping/form' + }, + + /** + * Get list of available CC types + * + * @returns {Object} + */ + getCcAvailableTypes: function () { + var availableTypes = validator.getAvailableCardTypes(), + billingCountryId; + + billingCountryId = $('#multishipping_billing_country_id').val(); + + if (billingCountryId && validator.getCountrySpecificCardTypes(billingCountryId)) { + return validator.collectTypes( + availableTypes, validator.getCountrySpecificCardTypes(billingCountryId) + ); + } + + return availableTypes; + }, + + /** + * @override + */ + placeOrder: function () { + var self = this; + + this.validatorManager.validate(self, function () { + return self.setPaymentInformation(); + }); + }, + + /** + * @override + */ + setPaymentInformation: function () { + if (additionalValidators.validate()) { + + fullScreenLoader.startLoader(); + + $.when( + setPaymentInformationAction( + this.messageContainer, + this.getData() + ) + ).done(this.done.bind(this)) + .fail(this.fail.bind(this)); + } + }, + + /** + * {Function} + */ + fail: function () { + fullScreenLoader.stopLoader(); + + return this; + }, + + /** + * {Function} + */ + done: function () { + fullScreenLoader.stopLoader(); + $('#multishipping-billing-form').submit(); + + return this; + } + }); +}); diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/paypal.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/paypal.js new file mode 100644 index 0000000000000..6702e58d1214b --- /dev/null +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/paypal.js @@ -0,0 +1,143 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +/*browser:true*/ +/*global define*/ +define([ + 'jquery', + 'underscore', + 'Magento_Braintree/js/view/payment/method-renderer/paypal', + 'Magento_Checkout/js/action/set-payment-information', + 'Magento_Checkout/js/model/payment/additional-validators', + 'Magento_Checkout/js/model/full-screen-loader', + 'mage/translate' +], function ( + $, + _, + Component, + setPaymentInformationAction, + additionalValidators, + fullScreenLoader, + $t +) { + 'use strict'; + + return Component.extend({ + defaults: { + template: 'Magento_Braintree/payment/multishipping/paypal', + submitButtonSelector: '#payment-continue span' + }, + + /** + * @override + */ + onActiveChange: function (isActive) { + this.updateSubmitButtonTitle(isActive); + + this._super(isActive); + }, + + /** + * @override + */ + beforePlaceOrder: function (data) { + this._super(data); + + this.updateSubmitButtonTitle(true); + }, + + /** + * @override + */ + getShippingAddress: function () { + return {}; + }, + + /** + * @override + */ + getData: function () { + var data = this._super(); + + data['additional_data']['is_active_payment_token_enabler'] = true; + + return data; + }, + + /** + * @override + */ + isActiveVault: function () { + return true; + }, + + /** + * Skipping order review step on checkout with multiple addresses is not allowed. + * + * @returns {Boolean} + */ + isSkipOrderReview: function () { + return false; + }, + + /** + * Checks if payment method nonce is already received. + * + * @returns {Boolean} + */ + isPaymentMethodNonceReceived: function () { + return this.paymentMethodNonce !== null; + }, + + /** + * Updates submit button title on multi-addresses checkout billing form. + * + * @param {Boolean} isActive + */ + updateSubmitButtonTitle: function (isActive) { + var title = this.isPaymentMethodNonceReceived() || !isActive ? + $t('Go to Review Your Order') : $t('Continue to PayPal'); + + $(this.submitButtonSelector).html(title); + }, + + /** + * @override + */ + placeOrder: function () { + if (!this.isPaymentMethodNonceReceived()) { + this.payWithPayPal(); + } else { + fullScreenLoader.startLoader(); + + $.when( + setPaymentInformationAction( + this.messageContainer, + this.getData() + ) + ).done(this.done.bind(this)) + .fail(this.fail.bind(this)); + } + }, + + /** + * {Function} + */ + fail: function () { + fullScreenLoader.stopLoader(); + + return this; + }, + + /** + * {Function} + */ + done: function () { + fullScreenLoader.stopLoader(); + $('#multishipping-billing-form').submit(); + + return this; + } + }); +}); diff --git a/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/form.html b/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/form.html new file mode 100644 index 0000000000000..964e15df166d3 --- /dev/null +++ b/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/form.html @@ -0,0 +1,106 @@ +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<div data-bind="attr: {class: 'payment-method payment-method-' + getCode()}, css: {'_active': isActive()}"> + <div> + <form id="co-transparent-form-braintree" class="form" data-bind="" method="post" action="#" novalidate="novalidate"> + <fieldset data-bind="attr: {class: 'fieldset payment items ccard ' + getCode(), id: 'payment_form_' + getCode()}"> + <legend class="legend"> + <span><!-- ko i18n: 'Credit Card Information'--><!-- /ko --></span> + </legend> + <br> + <div class="field type"> + <div class="control"> + <ul class="credit-card-types"> + <!-- ko foreach: {data: getCcAvailableTypes(), as: 'item'} --> + <li class="item" data-bind="css: { + _active: $parent.selectedCardType() == item, + _inactive: $parent.selectedCardType() != null && $parent.selectedCardType() != item + } "> + <!--ko if: $parent.getIcons(item) --> + <img data-bind="attr: { + 'src': $parent.getIcons(item).url, + 'width': $parent.getIcons(item).width, + 'height': $parent.getIcons(item).height + }"> + <!--/ko--> + </li> + <!--/ko--> + </ul> + <input type="hidden" + name="payment[cc_type]" + class="input-text" + value="" + data-bind="attr: {id: getCode() + '_cc_type', 'data-container': getCode() + '-cc-type'}, + value: creditCardType + "> + </div> + </div> + <div class="field number required"> + <label data-bind="attr: {for: getCode() + '_cc_number'}" class="label"> + <span><!-- ko i18n: 'Credit Card Number'--><!-- /ko --></span> + </label> + <div class="control"> + <div data-bind="attr: {id: getCode() + '_cc_number'}" class="hosted-control"></div> + <div class="hosted-error"><!-- ko i18n: 'Please, enter valid Credit Card Number'--><!-- /ko --></div> + </div> + </div> + <div class="field number required"> + <label data-bind="attr: {for: getCode() + '_expiration'}" class="label"> + <span><!-- ko i18n: 'Expiration Date'--><!-- /ko --></span> + </label> + <div class="control"> + <div class="hosted-date-wrap"> + <div data-bind="attr: {id: getCode() + '_expirationMonth'}" + class="hosted-control hosted-date"></div> + + <div data-bind="attr: {id: getCode() + '_expirationYear'}" + class="hosted-control hosted-date"></div> + + <div class="hosted-error"><!-- ko i18n: 'Please, enter valid Expiration Date'--><!-- /ko --></div> + </div> + </div> + </div> + <!-- ko if: (hasVerification())--> + <div class="field cvv required" data-bind="attr: {id: getCode() + '_cc_type_cvv_div'}"> + <label data-bind="attr: {for: getCode() + '_cc_cid'}" class="label"> + <span><!-- ko i18n: 'Card Verification Number'--><!-- /ko --></span> + </label> + <div class="control _with-tooltip"> + <div data-bind="attr: {id: getCode() + '_cc_cid'}" class="hosted-control hosted-cid"></div> + <div class="hosted-error"><!-- ko i18n: 'Please, enter valid Card Verification Number'--><!-- /ko --></div> + + <div class="field-tooltip toggle"> + <span class="field-tooltip-action action-cvv" + tabindex="0" + data-toggle="dropdown" + data-bind="attr: {title: $t('What is this?')}, mageInit: {'dropdown':{'activeClass': '_active'}}"> + <span><!-- ko i18n: 'What is this?'--><!-- /ko --></span> + </span> + <div class="field-tooltip-content" + data-target="dropdown" + data-bind="html: getCvvImageHtml()"></div> + </div> + </div> + </div> + <!-- /ko --> + </fieldset> + <input type="submit" id="braintree_submit" style="display:none" /> + </form> + + <div class="actions-toolbar no-display"> + <div class="primary"> + <button data-role="review-save" + type="submit" + data-bind="{click: placeOrderClick}" + class="action primary checkout"> + <span data-bind="i18n: 'Place Order'"></span> + </button> + </div> + </div> + </div> +</div> \ No newline at end of file diff --git a/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/paypal.html b/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/paypal.html new file mode 100644 index 0000000000000..722989e41f98f --- /dev/null +++ b/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/paypal.html @@ -0,0 +1,40 @@ +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<div class="payment-method" data-bind="css: {'_active': isActive()}"> + <div class="payment-method-title field choice"> + <label class="label" data-bind="attr: {'for': getCode()}"> + <!-- PayPal Logo --> + <img data-bind="attr: {src: getPaymentAcceptanceMarkSrc(), alt: $t('Acceptance Mark'), title: $t('Acceptance Mark')}" + class="payment-icon"/> + <!-- PayPal Logo --> + <span text="getTitle()"></span> + </label> + </div> + + <div class="payment-method-content"> + <each args="getRegion('messages')" render=""></each> + <fieldset class="braintree-paypal-fieldset" data-bind='attr: {id: "payment_form_" + getCode()}'> + <div id="paypal-container"></div> + </fieldset> + <div class="actions-toolbar braintree-paypal-actions" data-bind="visible: isReviewRequired()"> + <div class="payment-method-item braintree-paypal-account"> + <span class="payment-method-type">PayPal</span> + <span class="payment-method-description" text="customerEmail()"></span> + </div> + <div class="actions-toolbar no-display"> + <div class="primary"> + <button data-button="paypal-place" data-role="review-save" + type="submit" + data-bind="{click: placeOrder}" + class="action primary checkout"> + <span data-bind="i18n: 'Place Order'"></span> + </button> + </div> + </div> + </div> + </div> +</div> diff --git a/app/code/Magento/Multishipping/view/frontend/templates/checkout/billing.phtml b/app/code/Magento/Multishipping/view/frontend/templates/checkout/billing.phtml index d8514ca77f9c2..4354cfb7c1c3e 100644 --- a/app/code/Magento/Multishipping/view/frontend/templates/checkout/billing.phtml +++ b/app/code/Magento/Multishipping/view/frontend/templates/checkout/billing.phtml @@ -55,6 +55,10 @@ <div class="box-content"> <address> <?= /* @noEscape */ $block->getCheckoutData()->getAddressHtml($block->getAddress()); ?> + <input type="hidden" + id="multishipping_billing_country_id" + value="<?= /* @noEscape */ $block->getAddress()->getCountryId(); ?>" + name="multishipping_billing_country_id"/> </address> </div> </div> @@ -79,36 +83,45 @@ if (isset($methodsForms[$code])) { $block->setMethodFormTemplate($code, $methodsForms[$code]); } - ?> - <dt class="item-title"> - <?php if ($methodsCount > 1) : ?> - <input type="radio" - id="p_method_<?= $block->escapeHtml($code); ?>" - value="<?= $block->escapeHtml($code); ?>" - name="payment[method]" - title="<?= $block->escapeHtml($_method->getTitle()) ?>" - <?php if ($checked) : ?> - checked="checked" + ?> + <div data-bind="scope: 'payment_method_<?= $block->escapeHtml($code);?>'"> + <dt class="item-title"> + <?php if ($methodsCount > 1) : ?> + <input type="radio" + id="p_method_<?= $block->escapeHtml($code); ?>" + value="<?= $block->escapeHtml($code); ?>" + name="payment[method]" + title="<?= $block->escapeHtml($_method->getTitle()) ?>" + data-bind=" + value: getCode(), + checked: isChecked, + click: selectPaymentMethod, + visible: isRadioButtonVisible()" + <?php if ($checked) : ?> + checked="checked" + <?php endif; ?> + class="radio"/> + <?php else : ?> + <input type="radio" + id="p_method_<?= $block->escapeHtml($code); ?>" + value="<?= $block->escapeHtml($code); ?>" + name="payment[method]" + data-bind=" + value: getCode(), + afterRender: selectPaymentMethod" + checked="checked" + class="radio solo method" /> <?php endif; ?> - class="radio"/> - <?php else : ?> - <input type="radio" - id="p_method_<?= $block->escapeHtml($code); ?>" - value="<?= $block->escapeHtml($code); ?>" - name="payment[method]" - checked="checked" - class="radio solo method" /> - <?php endif; ?> - <label for="p_method_<?= $block->escapeHtml($code); ?>"> - <?= $block->escapeHtml($_method->getTitle()) ?> - </label> - </dt> - <?php if ($html = $block->getChildHtml('payment.method.' . $code)) : ?> - <dd class="item-content <?= $checked ? '' : 'no-display'; ?>" - data-bind="scope: 'payment_method_<?= $block->escapeHtml($code);?>'"> - <?= /* @noEscape */ $html; ?> - </dd> - <?php endif; ?> + <label for="p_method_<?= $block->escapeHtml($code); ?>"> + <?= $block->escapeHtml($_method->getTitle()) ?> + </label> + </dt> + <?php if ($html = $block->getChildHtml('payment.method.' . $code)) : ?> + <dd class="item-content <?= $checked ? '' : 'no-display'; ?>"> + <?= /* @noEscape */ $html; ?> + </dd> + <?php endif; ?> + </div> <?php endforeach; ?> </dl> <?= $block->getChildHtml('payment_methods_after') ?> diff --git a/app/code/Magento/OfflinePayments/view/frontend/layout/multishipping_checkout_billing.xml b/app/code/Magento/OfflinePayments/view/frontend/layout/multishipping_checkout_billing.xml new file mode 100644 index 0000000000000..32810ecef20da --- /dev/null +++ b/app/code/Magento/OfflinePayments/view/frontend/layout/multishipping_checkout_billing.xml @@ -0,0 +1,19 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> + <body> + <referenceBlock name="checkout_billing"> + <arguments> + <argument name="form_templates" xsi:type="array"> + <item name="checkmo" xsi:type="string">Magento_OfflinePayments::multishipping/checkmo_form.phtml</item> + </argument> + </arguments> + </referenceBlock> + </body> +</page> diff --git a/app/code/Magento/OfflinePayments/view/frontend/templates/multishipping/checkmo_form.phtml b/app/code/Magento/OfflinePayments/view/frontend/templates/multishipping/checkmo_form.phtml new file mode 100644 index 0000000000000..b96918243a7a7 --- /dev/null +++ b/app/code/Magento/OfflinePayments/view/frontend/templates/multishipping/checkmo_form.phtml @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +?> +<script> + require([ + 'uiLayout', + 'jquery' + ], function (layout, $) { + $(function () { + var paymentMethodData = { + method: 'checkmo' + }; + layout([ + { + component: 'Magento_Checkout/js/view/payment/default', + name: 'payment_method_checkmo', + method: paymentMethodData.method, + item: paymentMethodData + } + ]); + + $('body').trigger('contentUpdated'); + }) + }) +</script> diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/assign_items_per_address.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/assign_items_per_address.php new file mode 100644 index 0000000000000..91cea7dc96602 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/assign_items_per_address.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Quote\Api\CartRepositoryInterface; + +$store = $storeManager->getStore(); +$quote->setReservedOrderId('multishipping_quote_id_braintree') + ->setStoreId($store->getId()) + ->setCustomerEmail('customer001@test.com'); + +/** @var CartRepositoryInterface $quoteRepository */ +$quoteRepository = $objectManager->get(CartRepositoryInterface::class); +$quote->collectTotals(); +$quoteRepository->save($quote); + +$items = $quote->getAllItems(); +$addressList = $quote->getAllShippingAddresses(); + +foreach ($addressList as $key => $address) { + $item = $items[$key]; + // set correct quantity per shipping address + $item->setQty(1); + $address->setTotalQty(1); + $address->addItem($item); +} + +// assign virtual product to the billing address +$billingAddress = $quote->getBillingAddress(); +$virtualItem = $items[sizeof($items) - 1]; +$billingAddress->setTotalQty(1); +$billingAddress->addItem($virtualItem); + +// need to recollect totals +$quote->setTotalsCollectedFlag(false); +$quote->collectTotals(); +$quoteRepository->save($quote); diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment_braintree.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment_braintree.php new file mode 100644 index 0000000000000..3e1db90f1f2c8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment_braintree.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Quote\Model\Quote\Payment; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use Magento\Quote\Api\Data\PaymentInterface; +use Magento\Braintree\Model\Ui\ConfigProvider; + +/** + * @var Magento\Quote\Model\Quote $quote + */ + +if (empty($quote)) { + throw new \Exception('$quote should be defined in the parent fixture'); +} + +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +/** @var PaymentInterface $payment */ +$payment = $objectManager->create(Payment::class); +$payment->setMethod(ConfigProvider::CODE); +$quote->setPayment($payment); diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment_braintree_paypal.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment_braintree_paypal.php new file mode 100644 index 0000000000000..e4bba222078b0 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment_braintree_paypal.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Quote\Model\Quote\Payment; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use Magento\Quote\Api\Data\PaymentInterface; +use Magento\Braintree\Model\Ui\PayPal\ConfigProvider; + +/** + * @var Magento\Quote\Model\Quote $quote + */ + +if (empty($quote)) { + throw new \Exception('$quote should be defined in the parent fixture'); +} + +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +/** @var PaymentInterface $payment */ +$payment = $objectManager->create(Payment::class); +$payment->setMethod(ConfigProvider::PAYPAL_CODE); +$quote->setPayment($payment); diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/quote_with_split_items_braintree.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/quote_with_split_items_braintree.php new file mode 100644 index 0000000000000..1c56e611dd6db --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/quote_with_split_items_braintree.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Quote\Model\Quote; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; + +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +/** @var StoreManagerInterface $storeManager */ +$storeManager = $objectManager->get(StoreManagerInterface::class); + +/** @var Quote $quote */ +$quote = $objectManager->create(Quote::class); + +require __DIR__ . '/../../../Magento/Multishipping/Fixtures/shipping_address_list.php'; +require __DIR__ . '/../../../Magento/Multishipping/Fixtures/billing_address.php'; +require __DIR__ . '/payment_braintree.php'; +require __DIR__ . '/../../../Magento/Multishipping/Fixtures/items.php'; +require __DIR__ . '/assign_items_per_address.php'; diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/quote_with_split_items_braintree_paypal.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/quote_with_split_items_braintree_paypal.php new file mode 100644 index 0000000000000..4bd8e926abb76 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/quote_with_split_items_braintree_paypal.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Quote\Model\Quote; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; + +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +/** @var StoreManagerInterface $storeManager */ +$storeManager = $objectManager->get(StoreManagerInterface::class); + +/** @var Quote $quote */ +$quote = $objectManager->create(Quote::class); + +require __DIR__ . '/../../../Magento/Multishipping/Fixtures/shipping_address_list.php'; +require __DIR__ . '/../../../Magento/Multishipping/Fixtures/billing_address.php'; +require __DIR__ . '/payment_braintree_paypal.php'; +require __DIR__ . '/../../../Magento/Multishipping/Fixtures/items.php'; +require __DIR__ . '/assign_items_per_address.php'; diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Model/MultishippingTest.php b/dev/tests/integration/testsuite/Magento/Braintree/Model/MultishippingTest.php new file mode 100644 index 0000000000000..91bc0388d8551 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Model/MultishippingTest.php @@ -0,0 +1,254 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Braintree\Model; + +use Braintree\Result\Successful; +use Braintree\Transaction; +use Magento\Braintree\Gateway\Command\GetPaymentNonceCommand; +use Magento\Braintree\Model\Adapter\BraintreeAdapter; +use Magento\Braintree\Model\Adapter\BraintreeAdapterFactory; +use Magento\Checkout\Model\Session as CheckoutSession; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Multishipping\Model\Checkout\Type\Multishipping; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Model\Quote; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order\Email\Sender\OrderSender; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use \PHPUnit_Framework_MockObject_MockObject as MockObject; +use Magento\Payment\Gateway\Command\ResultInterface as CommandResultInterface; + +/** + * Tests Magento\Multishipping\Model\Checkout\Type\Multishipping with Braintree and BraintreePayPal payments. + * + * @magentoAppArea frontend + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class MultishippingTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var BraintreeAdapter|MockObject + */ + private $adapter; + + /** + * @var Multishipping + */ + private $model; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + + $orderSender = $this->getMockBuilder(OrderSender::class) + ->disableOriginalConstructor() + ->getMock(); + + $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); + $this->objectManager->addSharedInstance($this->getPaymentNonceMock(), GetPaymentNonceCommand::class); + + $this->model = $this->objectManager->create( + Multishipping::class, + ['orderSender' => $orderSender] + ); + } + + /** + * Checks a case when multiple orders are created successfully using Braintree payment method. + * + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Braintree/Fixtures/quote_with_split_items_braintree.php + * @magentoConfigFixture current_store payment/braintree/active 1 + * @return void + */ + public function testCreateOrdersWithBraintree() + { + $this->adapter->method('sale') + ->willReturn( + $this->getTransactionStub() + ); + $this->createOrders(); + } + + /** + * Checks a case when multiple orders are created successfully using Braintree PayPal payment method. + * + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Braintree/Fixtures/quote_with_split_items_braintree_paypal.php + * @magentoConfigFixture current_store payment/braintree_paypal/active 1 + * @return void + */ + public function testCreateOrdersWithBraintreePaypal() + { + $this->adapter->method('sale') + ->willReturn( + $this->getTransactionPaypalStub() + ); + $this->createOrders(); + } + + /** + * Creates orders for multishipping checkout flow. + * + * @return void + */ + private function createOrders() + { + $expectedPlacedOrdersNumber = 3; + $quote = $this->getQuote('multishipping_quote_id_braintree'); + + /** @var CheckoutSession $session */ + $session = $this->objectManager->get(CheckoutSession::class); + $session->replaceQuote($quote); + + $this->model->createOrders(); + + $orderList = $this->getOrderList((int)$quote->getId()); + self::assertCount( + $expectedPlacedOrdersNumber, + $orderList, + 'Total successfully placed orders number mismatch' + ); + } + + /** + * Creates stub for Braintree capture Transaction. + * + * @return Successful + */ + private function getTransactionStub(): Successful + { + $transaction = $this->getMockBuilder(Transaction::class) + ->disableOriginalConstructor() + ->getMock(); + $transaction->status = 'submitted_for_settlement'; + $transaction->creditCard = [ + 'last4' => '1111', + 'cardType' => 'Visa', + 'expirationMonth' => '12', + 'expirationYear' => '2021' + ]; + + $creditCardDetails = new \stdClass(); + $creditCardDetails->token = '4fdg'; + $creditCardDetails->expirationMonth = '12'; + $creditCardDetails->expirationYear = '2021'; + $creditCardDetails->cardType = 'Visa'; + $creditCardDetails->last4 = '1111'; + $creditCardDetails->expirationDate = '12/2021'; + $transaction->creditCardDetails = $creditCardDetails; + + $response = new Successful(); + $response->success = true; + $response->transaction = $transaction; + + return $response; + } + + /** + * Creates stub for BraintreePaypal capture Transaction. + * + * @return Successful + */ + private function getTransactionPaypalStub(): Successful + { + $transaction = $this->getMockBuilder(Transaction::class) + ->disableOriginalConstructor() + ->getMock(); + $transaction->status = 'submitted_for_settlement'; + $transaction->paypal = [ + 'token' => 'fchxqx', + 'payerEmail' => 'payer@example.com', + 'paymentId' => 'PAY-33ac47a28e7f54791f6cda45', + ]; + $paypalDetails = new \stdClass(); + $paypalDetails->token = 'fchxqx'; + $paypalDetails->payerEmail = 'payer@example.com'; + $paypalDetails->paymentId = '33ac47a28e7f54791f6cda45'; + $transaction->paypalDetails = $paypalDetails; + + $response = new Successful(); + $response->success = true; + $response->transaction = $transaction; + + return $response; + } + + /** + * Retrieves 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); + } + + /** + * Get list of orders by quote id. + * + * @param int $quoteId + * @return array + */ + private function getOrderList(int $quoteId): array + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('quote_id', $quoteId) + ->create(); + + /** @var OrderRepositoryInterface $orderRepository */ + $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); + return $orderRepository->getList($searchCriteria)->getItems(); + } + + /** + * Returns GetPaymentNonceCommand command mock. + * + * @return MockObject + */ + private function getPaymentNonceMock(): MockObject + { + $commandResult = $this->createMock(CommandResultInterface::class); + $commandResult->method('get') + ->willReturn(['paymentMethodNonce' => 'testNonce']); + $paymentNonce = $this->createMock(GetPaymentNonceCommand::class); + $paymentNonce->method('execute') + ->willReturn($commandResult); + + return $paymentNonce; + } +} From d23dd1ed5df664dcfbe79106058049e7e0b1e7fc Mon Sep 17 00:00:00 2001 From: Jaimin Sutariya <jaimin.sutariya@krishtechnolabs.com> Date: Fri, 14 Dec 2018 11:27:43 +0530 Subject: [PATCH 1063/1295] Resolved undefined index issue for import adapter --- .../ImportExport/Model/Import/Entity/AbstractEntity.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php b/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php index f61f21b093fd3..f24ce7e27371a 100644 --- a/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php +++ b/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php @@ -408,7 +408,9 @@ protected function _saveValidatedBunches() if ($source->valid()) { try { $rowData = $source->current(); - $skuSet[$rowData['sku']] = true; + if (array_key_exists('sku', $rowData)) { + $skuSet[$rowData['sku']] = true; + } } catch (\InvalidArgumentException $e) { $this->addRowError($e->getMessage(), $this->_processedRowsCount); $this->_processedRowsCount++; From 60034d5540bb959c5bf724b0f18c9fb962dc49e4 Mon Sep 17 00:00:00 2001 From: Jaimin Sutariya <jaimin.sutariya@krishtechnolabs.com> Date: Sat, 22 Dec 2018 11:06:42 +0530 Subject: [PATCH 1064/1295] Updated code to get Entity count for product import --- .../Magento/ImportExport/Model/Import/Entity/AbstractEntity.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php b/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php index f24ce7e27371a..848c866dd75b3 100644 --- a/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php +++ b/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php @@ -438,7 +438,7 @@ protected function _saveValidatedBunches() $source->next(); } } - $this->_processedEntitiesCount = count($skuSet); + $this->_processedEntitiesCount = (count($skuSet)) ? : $this->_processedRowsCount; return $this; } From 5a5f2f92390ba7dcd5a2b163fefaa82335aa847e Mon Sep 17 00:00:00 2001 From: amol2jcommerce <amol@twojay.in> Date: Fri, 22 Mar 2019 18:32:08 +0530 Subject: [PATCH 1065/1295] changes for contact us layout in I-pad --- .../Magento/Contact/view/frontend/web/css/source/_module.less | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Contact/view/frontend/web/css/source/_module.less b/app/code/Magento/Contact/view/frontend/web/css/source/_module.less index 0aaec05aa2afe..b31c0731c7924 100644 --- a/app/code/Magento/Contact/view/frontend/web/css/source/_module.less +++ b/app/code/Magento/Contact/view/frontend/web/css/source/_module.less @@ -16,6 +16,7 @@ .form.contact { float: none; width: 50%; + min-width: 600px; } } } From cf69cc87414e4382d7fd79c75167ec56a2174054 Mon Sep 17 00:00:00 2001 From: amol2jcommerce <amol@twojay.in> Date: Fri, 22 Mar 2019 19:31:40 +0530 Subject: [PATCH 1066/1295] changes for contact us layout in I-pad --- .../Magento/Contact/view/frontend/web/css/source/_module.less | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Contact/view/frontend/web/css/source/_module.less b/app/code/Magento/Contact/view/frontend/web/css/source/_module.less index b31c0731c7924..cb41e025d665c 100644 --- a/app/code/Magento/Contact/view/frontend/web/css/source/_module.less +++ b/app/code/Magento/Contact/view/frontend/web/css/source/_module.less @@ -36,6 +36,7 @@ .form.contact { float: none; width: 100%; + min-width: auto; } } } From 3157b86722df914c018e7caee69c81424c6289a6 Mon Sep 17 00:00:00 2001 From: amol2jcommerce <amol@twojay.in> Date: Mon, 25 Mar 2019 10:57:16 +0530 Subject: [PATCH 1067/1295] changes for contact-us-layout-ipad-not-proper-2.3 --- .../view/frontend/web/css/source/_module.less | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Contact/view/frontend/web/css/source/_module.less b/app/code/Magento/Contact/view/frontend/web/css/source/_module.less index cb41e025d665c..a9454bb6be253 100644 --- a/app/code/Magento/Contact/view/frontend/web/css/source/_module.less +++ b/app/code/Magento/Contact/view/frontend/web/css/source/_module.less @@ -16,12 +16,21 @@ .form.contact { float: none; width: 50%; - min-width: 600px; } } } } +// +// Desktop +// _____________________________________________ + +.media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) { + .contact-index-index .column:not(.sidebar-additional) .form.contact { + min-width: 600px; + } +} + // Mobile .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { .contact-index-index { @@ -36,7 +45,6 @@ .form.contact { float: none; width: 100%; - min-width: auto; } } } From 3d84c0762b64e92a2c5c26eb266c9994c0226734 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Mon, 1 Apr 2019 12:55:16 +0300 Subject: [PATCH 1068/1295] magento/magento2#21896: Static test fix. --- .../Magento/Contact/view/frontend/web/css/source/_module.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Contact/view/frontend/web/css/source/_module.less b/app/code/Magento/Contact/view/frontend/web/css/source/_module.less index a9454bb6be253..d79806eecbe9b 100644 --- a/app/code/Magento/Contact/view/frontend/web/css/source/_module.less +++ b/app/code/Magento/Contact/view/frontend/web/css/source/_module.less @@ -22,8 +22,8 @@ } // -// Desktop -// _____________________________________________ +// Desktop +// _____________________________________________ .media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) { .contact-index-index .column:not(.sidebar-additional) .form.contact { From e29cf712b3d390687e9f6a03700f932166555e13 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Fri, 5 Apr 2019 15:10:35 -0500 Subject: [PATCH 1069/1295] MAGETWO-98831: The SKU was not found in the catalog --- .../Adminhtml/Order/Create/Search/Grid.php | 32 +++++++---- .../Grid/DataProvider/ProductCollection.php | 55 +++++++++++++++++++ 2 files changed, 77 insertions(+), 10 deletions(-) create mode 100644 app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid/DataProvider/ProductCollection.php diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php index 4bd2227d4bb1e..9a271f741edda 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php @@ -5,12 +5,17 @@ */ namespace Magento\Sales\Block\Adminhtml\Order\Create\Search; +use Magento\Sales\Block\Adminhtml\Order\Create\Search\Grid\DataProvider\ProductCollection + as ProductCollectionDataProvider; +use Magento\Framework\App\ObjectManager; + /** * Adminhtml sales order create search products block * * @api * @author Magento Core Team <core@magentocommerce.com> * @since 100.0.2 + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Grid extends \Magento\Backend\Block\Widget\Grid\Extended { @@ -42,6 +47,11 @@ class Grid extends \Magento\Backend\Block\Widget\Grid\Extended */ protected $_productFactory; + /** + * @var ProductCollectionDataProvider $productCollectionProvider + */ + private $productCollectionProvider; + /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Backend\Helper\Data $backendHelper @@ -50,6 +60,7 @@ class Grid extends \Magento\Backend\Block\Widget\Grid\Extended * @param \Magento\Backend\Model\Session\Quote $sessionQuote * @param \Magento\Sales\Model\Config $salesConfig * @param array $data + * @param ProductCollectionDataProvider|null $productCollectionProvider */ public function __construct( \Magento\Backend\Block\Template\Context $context, @@ -58,12 +69,15 @@ public function __construct( \Magento\Catalog\Model\Config $catalogConfig, \Magento\Backend\Model\Session\Quote $sessionQuote, \Magento\Sales\Model\Config $salesConfig, - array $data = [] + array $data = [], + ProductCollectionDataProvider $productCollectionProvider = null ) { $this->_productFactory = $productFactory; $this->_catalogConfig = $catalogConfig; $this->_sessionQuote = $sessionQuote; $this->_salesConfig = $salesConfig; + $this->productCollectionProvider = $productCollectionProvider + ?: ObjectManager::getInstance()->get(ProductCollectionDataProvider::class); parent::__construct($context, $backendHelper, $data); } @@ -140,20 +154,18 @@ protected function _addColumnFilterToCollection($column) */ protected function _prepareCollection() { + $attributes = $this->_catalogConfig->getProductAttributes(); + $store = $this->getStore(); + /* @var $collection \Magento\Catalog\Model\ResourceModel\Product\Collection */ - $collection = $this->_productFactory->create()->getCollection(); - $collection->setStore( - $this->getStore() - )->addAttributeToSelect( + $collection = $this->productCollectionProvider->getCollectionForStore($store); + $collection->addAttributeToSelect( $attributes - )->addAttributeToSelect( - 'sku' - )->addStoreFilter()->addAttributeToFilter( + ); + $collection->addAttributeToFilter( 'type_id', $this->_salesConfig->getAvailableProductTypes() - )->addAttributeToSelect( - 'gift_message_available' ); $this->setCollection($collection); diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid/DataProvider/ProductCollection.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid/DataProvider/ProductCollection.php new file mode 100644 index 0000000000000..733791a2f9549 --- /dev/null +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid/DataProvider/ProductCollection.php @@ -0,0 +1,55 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Block\Adminhtml\Order\Create\Search\Grid\DataProvider; + +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory; +use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\Store\Model\Store; + +/** + * Prepares product collection for the grid + */ +class ProductCollection +{ + /** + * @var ProductCollectionFactory + */ + private $collectionFactory; + + /** + * @param ProductCollectionFactory $collectionFactory + */ + public function __construct( + ProductCollectionFactory $collectionFactory + ) { + $this->collectionFactory = $collectionFactory; + } + + /** + * Provide products collection filtered with store + * + * @param Store $store + * @return Collection + */ + public function getCollectionForStore(Store $store):Collection + { + /** @var Collection $collection */ + $collection = $this->collectionFactory->create(); + + $collection->setStore($store); + $collection->addAttributeToSelect( + 'gift_message_available' + ); + $collection->addAttributeToSelect( + 'sku' + ); + $collection->addStoreFilter(); + + return $collection; + } +} From 062d571d1c37069f40c6118c6aadcf1183382ce3 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Mon, 8 Apr 2019 08:39:19 +0300 Subject: [PATCH 1070/1295] MAGETWO-73157: [GITHUB] Import customizable options adds it to another product if same SKU is filled#9457 --- .../Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml | 2 +- .../Magento/Catalog/Test/Mftf/Data/ProductOptionData.xml | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml index 70e825498654f..e248fc4f146d2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml @@ -17,7 +17,7 @@ <waitForElementVisible selector="{{AdminProductCustomizableOptionsSection.lastOptionTitle}}" stepKey="waitForOption"/> <fillField selector="{{AdminProductCustomizableOptionsSection.lastOptionTitle}}" userInput="{{option.title}}" stepKey="fillTitle"/> <click selector="{{AdminProductCustomizableOptionsSection.lastOptionTypeParent}}" stepKey="openTypeSelect"/> - <click selector="{{AdminProductCustomizableOptionsSection.optionType(option.type)}}" stepKey="selectTypeFile"/> + <click selector="{{AdminProductCustomizableOptionsSection.optionType(option.type_label)}}" stepKey="selectTypeFile"/> <waitForElementVisible selector="{{AdminProductCustomizableOptionsSection.optionPrice(optionIndex)}}" stepKey="waitForElements"/> <fillField selector="{{AdminProductCustomizableOptionsSection.optionPrice(optionIndex)}}" userInput="{{option.price}}" stepKey="fillPrice"/> <selectOption selector="{{AdminProductCustomizableOptionsSection.optionPriceType(optionIndex)}}" userInput="{{option.price_type}}" stepKey="selectPriceType"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionData.xml index f720512ad2018..14a3ff91c4b83 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionData.xml @@ -10,7 +10,8 @@ <entity name="ProductOptionField" type="product_option"> <var key="product_sku" entityType="product" entityKey="sku" /> <data key="title">OptionField</data> - <data key="type">Field</data> + <data key="type">field</data> + <data key="type_label">Field</data> <data key="is_require">true</data> <data key="sort_order">1</data> <data key="price">10</data> @@ -34,7 +35,8 @@ <entity name="ProductOptionFile" type="product_option"> <var key="product_sku" entityType="product" entityKey="sku" /> <data key="title">OptionFile</data> - <data key="type">File</data> + <data key="type">file</data> + <data key="type_label">File</data> <data key="is_require">true</data> <data key="sort_order">3</data> <data key="price">9.99</data> From 34d71f0f0aba7f0e7ac2d08f814483dbc4253599 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Mon, 8 Apr 2019 10:20:49 +0300 Subject: [PATCH 1071/1295] MC-15360: Improve checkout through BrainTree(Paypal) Fixing EngCom mess --- app/code/Magento/Quote/Model/QuoteManagement.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Quote/Model/QuoteManagement.php b/app/code/Magento/Quote/Model/QuoteManagement.php index 1ebf637706dd3..c3355a0c45e0b 100644 --- a/app/code/Magento/Quote/Model/QuoteManagement.php +++ b/app/code/Magento/Quote/Model/QuoteManagement.php @@ -609,7 +609,7 @@ private function rollbackAddresses( QuoteEntity $quote, \Magento\Sales\Api\Data\OrderInterface $order, \Exception $e - ): void { + ) { try { if (!empty($this->addressesToSync)) { foreach ($this->addressesToSync as $addressId) { From d9f805a761593a567cb996d6e4ed8bf69be6e9a9 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Mon, 8 Apr 2019 10:58:27 +0300 Subject: [PATCH 1072/1295] MAGETWO-74037: [Github] Product Attribute Option Values for storeview instead of admin on product creation #6507 --- .../Mftf/Data/ProductAttributeOptionData.xml | 8 +++ .../Catalog/Test/Mftf/Data/StoreLabelData.xml | 8 +++ ...ingAttributeValueOnProductEditPageTest.xml | 63 +++++++++++++++++++ .../Product/Form/Modifier/Eav.php | 2 +- 4 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckingAttributeValueOnProductEditPageTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml index ace2f2e6a02ab..b1d45dae1dc0f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml @@ -75,4 +75,12 @@ <requiredEntity type="StoreLabel">Option9Store0</requiredEntity> <requiredEntity type="StoreLabel">Option10Store1</requiredEntity> </entity> + <entity name="productAttributeAdminOption1" extends="productAttributeOption1"> + <requiredEntity type="StoreLabel">AdminOption1Store0</requiredEntity> + <requiredEntity type="StoreLabel">Option1Store1</requiredEntity> + </entity> + <entity name="productAttributeAdminOption2" extends="productAttributeOption2"> + <requiredEntity type="StoreLabel">AdminOption2Store0</requiredEntity> + <requiredEntity type="StoreLabel">Option2Store1</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml index 37489ac8143b9..3af9b2c54a4f0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml @@ -72,4 +72,12 @@ <data key="store_id">1</data> <data key="label">Red</data> </entity> + <entity name="AdminOption1Store0" type="StoreLabel"> + <data key="store_id">0</data> + <data key="label">admin_option_1</data> + </entity> + <entity name="AdminOption2Store0" type="StoreLabel"> + <data key="store_id">0</data> + <data key="label">admin_option_2</data> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckingAttributeValueOnProductEditPageTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckingAttributeValueOnProductEditPageTest.xml new file mode 100644 index 0000000000000..6b9bf9a5bff5b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckingAttributeValueOnProductEditPageTest.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckingAttributeValueOnProductEditPageTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create/configure Dropdown product attribute"/> + <title value="Checking attribute values on a product edit page"/> + <description value="Checking attribute values on a product edit page"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-15746"/> + <useCaseId value="MAGETWO-74037"/> + <group value="catalog"/> + </annotations> + <before> + <!--Create Dropdown product attribute--> + <createData entity="productAttributeWithDropdownTwoOptions" stepKey="createDropdownProductAttribute"/> + <!--Add options to attribute--> + <createData entity="productAttributeAdminOption1" stepKey="createFirstOption"> + <requiredEntity createDataKey="createDropdownProductAttribute"/> + </createData> + <createData entity="productAttributeAdminOption2" stepKey="createSecondOption"> + <requiredEntity createDataKey="createDropdownProductAttribute"/> + </createData> + <!--Add attribute to Default Attribute Set--> + <createData entity="AddToDefaultSet" stepKey="attributeSet"> + <requiredEntity createDataKey="createDropdownProductAttribute"/> + </createData> + <!--Create category--> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <!--Create Simple product--> + <createData entity="SimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!--Login as admin--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <!--Delete product attribute--> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createDropdownProductAttribute" stepKey="deleteDropdownProductAttribute"/> + <!--Logout--> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Go to Product edit page--> + <amOnPage url="{{AdminProductEditPage.url($$createSimpleProduct.id$$)}}" stepKey="goToProductEditPage"/> + <!--Click on attribute dropdown--> + <click selector="{{AdminProductFormSection.customAttributeDropdownField($$createDropdownProductAttribute.attribute[attribute_code]$$)}}" stepKey="clickOnAttributeDropdown"/> + <!--Check attribute dropdown options--> + <see selector="{{AdminProductFormSection.customAttributeDropdownField($$createDropdownProductAttribute.attribute[attribute_code]$$)}}" userInput="admin_option_1" stepKey="seeFirstAdminOption"/> + <see selector="{{AdminProductFormSection.customAttributeDropdownField($$createDropdownProductAttribute.attribute[attribute_code]$$)}}" userInput="admin_option_2" stepKey="seeSecondAdminOption"/> + <dontSee selector="{{AdminProductFormSection.customAttributeDropdownField($$createDropdownProductAttribute.attribute[attribute_code]$$)}}" userInput="option1" stepKey="dontSeeFirstStoreOption"/> + <dontSee selector="{{AdminProductFormSection.customAttributeDropdownField($$createDropdownProductAttribute.attribute[attribute_code]$$)}}" userInput="option2" stepKey="dontSeeSecondStoreOption"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php index 6a6d74c6cb9b3..af898bf3117c6 100755 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php @@ -655,7 +655,7 @@ public function setupAttributeMeta(ProductAttributeInterface $attribute, $groupC // TODO: Refactor to $attribute->getOptions() when MAGETWO-48289 is done $attributeModel = $this->getAttributeModel($attribute); if ($attributeModel->usesSource()) { - $options = $attributeModel->getSource()->getAllOptions(); + $options = $attributeModel->getSource()->getAllOptions(true, true); $meta = $this->arrayManager->merge($configPath, $meta, [ 'options' => $this->convertOptionsValueToString($options), ]); From 7d0320c71515549f193f9c6b19cd4008e8f7ab2b Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Mon, 8 Apr 2019 12:14:39 +0300 Subject: [PATCH 1073/1295] MAGETWO-74033: Calendar Custom Options are displayed broken on Storefront if JS validation is performed --- .../Magento/blank/web/css/source/_forms.less | 12 ++++++++++++ .../frontend/Magento/luma/web/css/source/_forms.less | 12 ++++++++++++ .../Test/Block/Product/View/CustomOptions.php | 3 ++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/blank/web/css/source/_forms.less b/app/design/frontend/Magento/blank/web/css/source/_forms.less index c9f3c3d72ef4c..26f5ff89e99e3 100644 --- a/app/design/frontend/Magento/blank/web/css/source/_forms.less +++ b/app/design/frontend/Magento/blank/web/css/source/_forms.less @@ -101,6 +101,18 @@ .lib-form-validation-note(); } + .product-options-wrapper { + .date { + &.required { + div[for*='options'] { + &.mage-error { + display: none !important; + } + } + } + } + } + .field .tooltip { .lib-tooltip(right); .tooltip-content { diff --git a/app/design/frontend/Magento/luma/web/css/source/_forms.less b/app/design/frontend/Magento/luma/web/css/source/_forms.less index 6701d5f9e9d21..fc637384e7a49 100644 --- a/app/design/frontend/Magento/luma/web/css/source/_forms.less +++ b/app/design/frontend/Magento/luma/web/css/source/_forms.less @@ -118,6 +118,18 @@ .lib-form-validation-note(); } + .product-options-wrapper { + .date { + &.required { + div[for*='options'] { + &.mage-error { + display: none !important; + } + } + } + } + } + // TEMP .field .tooltip { diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/View/CustomOptions.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/View/CustomOptions.php index ccb0d43337562..9f35059556fa8 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/View/CustomOptions.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/View/CustomOptions.php @@ -128,7 +128,8 @@ class CustomOptions extends Form * * @var string */ - private $validationErrorMessage = '//div[@class="mage-error"][contains(text(), "required field")]'; + private $validationErrorMessage = '//div[@class="mage-error"][contains(text(), "required field")' . + ' and not(contains(@style,"display"))]'; /** * Get product options From 3a0383808ced01ac1d6af8d7df8fad334866b3d5 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Wed, 6 Feb 2019 09:44:37 +0200 Subject: [PATCH 1074/1295] Fixing the multidimensional array as value for the widget's parameter --- .../Magento/Widget/Block/Adminhtml/Widget/Options.php | 4 +++- .../Magento/Framework/Data/Form/Element/Label.php | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php b/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php index beac66dce5016..e8f4baf6e3d9f 100644 --- a/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php +++ b/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php @@ -165,7 +165,9 @@ protected function _addField($parameter) if (is_array($data['value'])) { foreach ($data['value'] as &$value) { - $value = html_entity_decode($value); + if (!is_array($value)) { + $value = html_entity_decode($value); + } } } else { $data['value'] = html_entity_decode($data['value']); diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Label.php b/lib/internal/Magento/Framework/Data/Form/Element/Label.php index 901dcb5289e8d..a95c7646eadce 100644 --- a/lib/internal/Magento/Framework/Data/Form/Element/Label.php +++ b/lib/internal/Magento/Framework/Data/Form/Element/Label.php @@ -37,8 +37,14 @@ public function __construct( public function getElementHtml() { $html = $this->getBold() ? '<div class="control-value special">' : '<div class="control-value">'; - $html .= $this->getEscapedValue() . '</div>'; + if (is_array($this->getValue())) { + $html .= '</div>'; + } else { + $html .= $this->getEscapedValue() . '</div>'; + } + $html .= $this->getAfterElementHtml(); + return $html; } } From 4c84817c2a3c5b3b4985900e024437f03ab5704b Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Wed, 6 Feb 2019 12:31:31 +0200 Subject: [PATCH 1075/1295] Refactoring the if condition --- lib/internal/Magento/Framework/Data/Form/Element/Label.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Label.php b/lib/internal/Magento/Framework/Data/Form/Element/Label.php index a95c7646eadce..a2efb72c75542 100644 --- a/lib/internal/Magento/Framework/Data/Form/Element/Label.php +++ b/lib/internal/Magento/Framework/Data/Form/Element/Label.php @@ -37,12 +37,11 @@ public function __construct( public function getElementHtml() { $html = $this->getBold() ? '<div class="control-value special">' : '<div class="control-value">'; - if (is_array($this->getValue())) { - $html .= '</div>'; - } else { - $html .= $this->getEscapedValue() . '</div>'; + if (!is_array($this->getValue())) { + $html .= $this->getEscapedValue(); } + $html .= '</div>'; $html .= $this->getAfterElementHtml(); return $html; From 640127d884e8bf47206cc919f2af34b766b1222d Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Thu, 7 Feb 2019 13:25:45 +0200 Subject: [PATCH 1076/1295] Applying some minor adjustments --- app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php | 2 +- lib/internal/Magento/Framework/Data/Form/Element/Label.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php b/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php index e8f4baf6e3d9f..20eaa61ca6dcf 100644 --- a/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php +++ b/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php @@ -165,7 +165,7 @@ protected function _addField($parameter) if (is_array($data['value'])) { foreach ($data['value'] as &$value) { - if (!is_array($value)) { + if (is_string($value)) { $value = html_entity_decode($value); } } diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Label.php b/lib/internal/Magento/Framework/Data/Form/Element/Label.php index a2efb72c75542..d9834b8c15ee1 100644 --- a/lib/internal/Magento/Framework/Data/Form/Element/Label.php +++ b/lib/internal/Magento/Framework/Data/Form/Element/Label.php @@ -37,7 +37,7 @@ public function __construct( public function getElementHtml() { $html = $this->getBold() ? '<div class="control-value special">' : '<div class="control-value">'; - if (!is_array($this->getValue())) { + if (is_string($this->getValue())) { $html .= $this->getEscapedValue(); } From 3a4150d0910f75cd884325cdbd13f1e731da47cb Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Tue, 2 Apr 2019 11:51:09 +0300 Subject: [PATCH 1077/1295] Fix functional and static tests. --- .../Widget/Block/Adminhtml/Widget/Options.php | 5 +++-- .../Magento/Framework/Data/Form/Element/Label.php | 12 ++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php b/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php index 20eaa61ca6dcf..f7a2480a32b28 100644 --- a/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php +++ b/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php @@ -91,7 +91,7 @@ public function getMainFieldset() if ($this->_getData('main_fieldset') instanceof \Magento\Framework\Data\Form\Element\Fieldset) { return $this->_getData('main_fieldset'); } - $mainFieldsetHtmlId = 'options_fieldset' . md5($this->getWidgetType()); + $mainFieldsetHtmlId = 'options_fieldset' . hash('sha256', $this->getWidgetType()); $this->setMainFieldsetHtmlId($mainFieldsetHtmlId); $fieldset = $this->getForm()->addFieldset( $mainFieldsetHtmlId, @@ -141,7 +141,6 @@ protected function _addField($parameter) { $form = $this->getForm(); $fieldset = $this->getMainFieldset(); - //$form->getElement('options_fieldset'); // prepare element data with values (either from request of from default values) $fieldName = $parameter->getKey(); @@ -166,10 +165,12 @@ protected function _addField($parameter) if (is_array($data['value'])) { foreach ($data['value'] as &$value) { if (is_string($value)) { + // phpcs:ignore Magento2.Functions.DiscouragedFunction $value = html_entity_decode($value); } } } else { + // phpcs:ignore Magento2.Functions.DiscouragedFunction $data['value'] = html_entity_decode($data['value']); } diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Label.php b/lib/internal/Magento/Framework/Data/Form/Element/Label.php index d9834b8c15ee1..70b7885e7a0d0 100644 --- a/lib/internal/Magento/Framework/Data/Form/Element/Label.php +++ b/lib/internal/Magento/Framework/Data/Form/Element/Label.php @@ -4,13 +4,13 @@ * See COPYING.txt for license details. */ -/** - * Data form abstract class - * - * @author Magento Core Team <core@magentocommerce.com> - */ namespace Magento\Framework\Data\Form\Element; +use Magento\Framework\Phrase; + +/** + * Label form element. + */ class Label extends \Magento\Framework\Data\Form\Element\AbstractElement { /** @@ -37,7 +37,7 @@ public function __construct( public function getElementHtml() { $html = $this->getBold() ? '<div class="control-value special">' : '<div class="control-value">'; - if (is_string($this->getValue())) { + if (is_string($this->getValue()) || $this->getValue() instanceof Phrase) { $html .= $this->getEscapedValue(); } From 8aa5ac5198aa6435e91343d7af8fe0980eeab21b Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Mon, 8 Apr 2019 10:22:40 -0500 Subject: [PATCH 1078/1295] magento-engcom/magento2ce#2737: Fixed void return type --- app/code/Magento/Quote/Model/QuoteManagement.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Quote/Model/QuoteManagement.php b/app/code/Magento/Quote/Model/QuoteManagement.php index 1ebf637706dd3..b7d994138026a 100644 --- a/app/code/Magento/Quote/Model/QuoteManagement.php +++ b/app/code/Magento/Quote/Model/QuoteManagement.php @@ -604,12 +604,13 @@ protected function _prepareCustomerQuote($quote) * @param \Magento\Sales\Api\Data\OrderInterface $order * @param \Exception $e * @throws \Exception + * @return void */ private function rollbackAddresses( QuoteEntity $quote, \Magento\Sales\Api\Data\OrderInterface $order, \Exception $e - ): void { + ) { try { if (!empty($this->addressesToSync)) { foreach ($this->addressesToSync as $addressId) { From d353530bf68b943301a187132eefae6e0da0e2cc Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Mon, 8 Apr 2019 11:03:05 -0500 Subject: [PATCH 1079/1295] MAGETWO-98831: The SKU was not found in the catalog --- .../Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php index 9a271f741edda..2653167ff1b0e 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php @@ -95,6 +95,7 @@ protected function _construct() $this->setRowInitCallback('order.productGridRowInit.bind(order)'); $this->setDefaultSort('entity_id'); $this->setUseAjax(true); + // phpcs:ignore Generic.CodeAnalysis.RequestAwareBlockMethod if ($this->getRequest()->getParam('collapse')) { $this->setIsCollapsed(true); } @@ -263,6 +264,7 @@ public function getGridUrl() */ protected function _getSelectedProducts() { + // phpcs:ignore Generic.CodeAnalysis.RequestAwareBlockMethod $products = $this->getRequest()->getPost('products', []); return $products; From 9d8e7285d654a6bf3ba81208bcb137dcf859952b Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Mon, 8 Apr 2019 11:56:19 -0500 Subject: [PATCH 1080/1295] MAGETWO-98831: The SKU was not found in the catalog --- .../Sales/Block/Adminhtml/Order/Create/Search/Grid.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php index 2653167ff1b0e..3be0df001b1b5 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php @@ -85,6 +85,7 @@ public function __construct( * Constructor * * @return void + * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) */ protected function _construct() { @@ -95,7 +96,6 @@ protected function _construct() $this->setRowInitCallback('order.productGridRowInit.bind(order)'); $this->setDefaultSort('entity_id'); $this->setUseAjax(true); - // phpcs:ignore Generic.CodeAnalysis.RequestAwareBlockMethod if ($this->getRequest()->getParam('collapse')) { $this->setIsCollapsed(true); } @@ -261,10 +261,10 @@ public function getGridUrl() * Get selected products * * @return mixed + * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) */ protected function _getSelectedProducts() { - // phpcs:ignore Generic.CodeAnalysis.RequestAwareBlockMethod $products = $this->getRequest()->getPost('products', []); return $products; From d5a8db0bdb49928299e8d97271d32cf8991077b6 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Fri, 5 Apr 2019 15:10:35 -0500 Subject: [PATCH 1081/1295] MAGETWO-98794: The SKU was not found in the catalog --- .../Adminhtml/Order/Create/Search/Grid.php | 32 +++++++---- .../Grid/DataProvider/ProductCollection.php | 55 +++++++++++++++++++ 2 files changed, 77 insertions(+), 10 deletions(-) create mode 100644 app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid/DataProvider/ProductCollection.php diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php index 4bd2227d4bb1e..9a271f741edda 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php @@ -5,12 +5,17 @@ */ namespace Magento\Sales\Block\Adminhtml\Order\Create\Search; +use Magento\Sales\Block\Adminhtml\Order\Create\Search\Grid\DataProvider\ProductCollection + as ProductCollectionDataProvider; +use Magento\Framework\App\ObjectManager; + /** * Adminhtml sales order create search products block * * @api * @author Magento Core Team <core@magentocommerce.com> * @since 100.0.2 + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Grid extends \Magento\Backend\Block\Widget\Grid\Extended { @@ -42,6 +47,11 @@ class Grid extends \Magento\Backend\Block\Widget\Grid\Extended */ protected $_productFactory; + /** + * @var ProductCollectionDataProvider $productCollectionProvider + */ + private $productCollectionProvider; + /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Backend\Helper\Data $backendHelper @@ -50,6 +60,7 @@ class Grid extends \Magento\Backend\Block\Widget\Grid\Extended * @param \Magento\Backend\Model\Session\Quote $sessionQuote * @param \Magento\Sales\Model\Config $salesConfig * @param array $data + * @param ProductCollectionDataProvider|null $productCollectionProvider */ public function __construct( \Magento\Backend\Block\Template\Context $context, @@ -58,12 +69,15 @@ public function __construct( \Magento\Catalog\Model\Config $catalogConfig, \Magento\Backend\Model\Session\Quote $sessionQuote, \Magento\Sales\Model\Config $salesConfig, - array $data = [] + array $data = [], + ProductCollectionDataProvider $productCollectionProvider = null ) { $this->_productFactory = $productFactory; $this->_catalogConfig = $catalogConfig; $this->_sessionQuote = $sessionQuote; $this->_salesConfig = $salesConfig; + $this->productCollectionProvider = $productCollectionProvider + ?: ObjectManager::getInstance()->get(ProductCollectionDataProvider::class); parent::__construct($context, $backendHelper, $data); } @@ -140,20 +154,18 @@ protected function _addColumnFilterToCollection($column) */ protected function _prepareCollection() { + $attributes = $this->_catalogConfig->getProductAttributes(); + $store = $this->getStore(); + /* @var $collection \Magento\Catalog\Model\ResourceModel\Product\Collection */ - $collection = $this->_productFactory->create()->getCollection(); - $collection->setStore( - $this->getStore() - )->addAttributeToSelect( + $collection = $this->productCollectionProvider->getCollectionForStore($store); + $collection->addAttributeToSelect( $attributes - )->addAttributeToSelect( - 'sku' - )->addStoreFilter()->addAttributeToFilter( + ); + $collection->addAttributeToFilter( 'type_id', $this->_salesConfig->getAvailableProductTypes() - )->addAttributeToSelect( - 'gift_message_available' ); $this->setCollection($collection); diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid/DataProvider/ProductCollection.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid/DataProvider/ProductCollection.php new file mode 100644 index 0000000000000..733791a2f9549 --- /dev/null +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid/DataProvider/ProductCollection.php @@ -0,0 +1,55 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Block\Adminhtml\Order\Create\Search\Grid\DataProvider; + +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory; +use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\Store\Model\Store; + +/** + * Prepares product collection for the grid + */ +class ProductCollection +{ + /** + * @var ProductCollectionFactory + */ + private $collectionFactory; + + /** + * @param ProductCollectionFactory $collectionFactory + */ + public function __construct( + ProductCollectionFactory $collectionFactory + ) { + $this->collectionFactory = $collectionFactory; + } + + /** + * Provide products collection filtered with store + * + * @param Store $store + * @return Collection + */ + public function getCollectionForStore(Store $store):Collection + { + /** @var Collection $collection */ + $collection = $this->collectionFactory->create(); + + $collection->setStore($store); + $collection->addAttributeToSelect( + 'gift_message_available' + ); + $collection->addAttributeToSelect( + 'sku' + ); + $collection->addStoreFilter(); + + return $collection; + } +} From 5aa85ad45a3fd436e48c6450d74d49412576440d Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Mon, 8 Apr 2019 11:03:05 -0500 Subject: [PATCH 1082/1295] MAGETWO-98794: The SKU was not found in the catalog --- .../Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php index 9a271f741edda..2653167ff1b0e 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php @@ -95,6 +95,7 @@ protected function _construct() $this->setRowInitCallback('order.productGridRowInit.bind(order)'); $this->setDefaultSort('entity_id'); $this->setUseAjax(true); + // phpcs:ignore Generic.CodeAnalysis.RequestAwareBlockMethod if ($this->getRequest()->getParam('collapse')) { $this->setIsCollapsed(true); } @@ -263,6 +264,7 @@ public function getGridUrl() */ protected function _getSelectedProducts() { + // phpcs:ignore Generic.CodeAnalysis.RequestAwareBlockMethod $products = $this->getRequest()->getPost('products', []); return $products; From 70322620efd86fe06cd52bbcff948365f0133c5e Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Mon, 8 Apr 2019 11:56:19 -0500 Subject: [PATCH 1083/1295] MAGETWO-98794: The SKU was not found in the catalog --- .../Sales/Block/Adminhtml/Order/Create/Search/Grid.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php index 2653167ff1b0e..3be0df001b1b5 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php @@ -85,6 +85,7 @@ public function __construct( * Constructor * * @return void + * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) */ protected function _construct() { @@ -95,7 +96,6 @@ protected function _construct() $this->setRowInitCallback('order.productGridRowInit.bind(order)'); $this->setDefaultSort('entity_id'); $this->setUseAjax(true); - // phpcs:ignore Generic.CodeAnalysis.RequestAwareBlockMethod if ($this->getRequest()->getParam('collapse')) { $this->setIsCollapsed(true); } @@ -261,10 +261,10 @@ public function getGridUrl() * Get selected products * * @return mixed + * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) */ protected function _getSelectedProducts() { - // phpcs:ignore Generic.CodeAnalysis.RequestAwareBlockMethod $products = $this->getRequest()->getPost('products', []); return $products; From f68cff7feeacdd98faf7365fa676f9e867bffce3 Mon Sep 17 00:00:00 2001 From: ilnytskyi_sv <stailx1@gmail.com> Date: Fri, 30 Mar 2018 16:39:06 +0300 Subject: [PATCH 1084/1295] try to fix calculation #10790 --- .../Tax/Model/Calculation/AbstractAggregateCalculator.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php b/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php index afcfa1bbebcb0..58b158768dba8 100644 --- a/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php +++ b/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php @@ -149,9 +149,6 @@ protected function calculateWithTaxNotInPrice(QuoteDetailsItemInterface $item, $ $rowTaxBeforeDiscount = array_sum($rowTaxesBeforeDiscount); $rowTotalInclTax = $rowTotal + $rowTaxBeforeDiscount; $priceInclTax = $rowTotalInclTax / $quantity; - if ($round) { - $priceInclTax = $this->calculationTool->round($priceInclTax); - } return $this->taxDetailsItemDataObjectFactory->create() ->setCode($item->getCode()) From 8501089751d909786546e2d1631c7f6647d3cded Mon Sep 17 00:00:00 2001 From: ilnytskyi_sv <stailx1@gmail.com> Date: Fri, 30 Mar 2018 17:48:35 +0300 Subject: [PATCH 1085/1295] Check negative difference after rounding #10790 --- .../Model/Calculation/AbstractAggregateCalculator.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php b/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php index 58b158768dba8..855a3eaeabe8e 100644 --- a/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php +++ b/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php @@ -150,6 +150,16 @@ protected function calculateWithTaxNotInPrice(QuoteDetailsItemInterface $item, $ $rowTotalInclTax = $rowTotal + $rowTaxBeforeDiscount; $priceInclTax = $rowTotalInclTax / $quantity; + if ($round) { + $priceInclTax = $this->calculationTool->round($priceInclTax); + } + + $pricePerItemInclTax = $rowTotalInclTax / $quantity; + + if (($pricePerItemInclTax - $priceInclTax) < 0) { + $priceInclTax = $pricePerItemInclTax; + } + return $this->taxDetailsItemDataObjectFactory->create() ->setCode($item->getCode()) ->setType($item->getType()) From 7bbc018f04dfc866c89b3816d2b277e1a940d884 Mon Sep 17 00:00:00 2001 From: ilnytskyi_sv <stailx1@gmail.com> Date: Sun, 1 Apr 2018 18:45:15 +0300 Subject: [PATCH 1086/1295] Check negative totals after full discount #10790 --- app/code/Magento/SalesRule/Model/Utility.php | 19 +++++++++++++++++++ .../AbstractAggregateCalculator.php | 6 ------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/SalesRule/Model/Utility.php b/app/code/Magento/SalesRule/Model/Utility.php index a3876a9d7e046..45c5dc3da0ebd 100644 --- a/app/code/Magento/SalesRule/Model/Utility.php +++ b/app/code/Magento/SalesRule/Model/Utility.php @@ -189,6 +189,8 @@ public function deltaRoundingFix( ) { $discountAmount = $discountData->getAmount(); $baseDiscountAmount = $discountData->getBaseAmount(); + $rowTotalInclTax = $item->getRowTotalInclTax(); + $baseRowTotalInclTax = $item->getBaseRowTotalInclTax(); //TODO Seems \Magento\Quote\Model\Quote\Item\AbstractItem::getDiscountPercent() returns float value //that can not be used as array index @@ -205,6 +207,23 @@ public function deltaRoundingFix( - $this->priceCurrency->round($baseDiscountAmount); } + /** + * When we have 100% discount check if totals will not be negative + */ + + if ($percentKey == 100) { + $discountDelta = $rowTotalInclTax - $discountAmount; + $baseDiscountDelta = $baseRowTotalInclTax - $baseDiscountAmount; + + if ($discountDelta < 0) { + $discountAmount += $discountDelta; + } + + if ($baseDiscountDelta < 0) { + $baseDiscountAmount += $baseDiscountDelta; + } + } + $discountData->setAmount($this->priceCurrency->round($discountAmount)); $discountData->setBaseAmount($this->priceCurrency->round($baseDiscountAmount)); diff --git a/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php b/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php index 855a3eaeabe8e..77302bba82c67 100644 --- a/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php +++ b/app/code/Magento/Tax/Model/Calculation/AbstractAggregateCalculator.php @@ -154,12 +154,6 @@ protected function calculateWithTaxNotInPrice(QuoteDetailsItemInterface $item, $ $priceInclTax = $this->calculationTool->round($priceInclTax); } - $pricePerItemInclTax = $rowTotalInclTax / $quantity; - - if (($pricePerItemInclTax - $priceInclTax) < 0) { - $priceInclTax = $pricePerItemInclTax; - } - return $this->taxDetailsItemDataObjectFactory->create() ->setCode($item->getCode()) ->setType($item->getType()) From b30b1bb981e7168b279fb19454072379e96a8961 Mon Sep 17 00:00:00 2001 From: ilnytskyi_sv <stailx1@gmail.com> Date: Wed, 6 Jun 2018 15:33:44 +0300 Subject: [PATCH 1087/1295] Added integration test #10790 --- .../Tax/Model/Sales/Total/Quote/TaxTest.php | 155 ++++++++++++++++++ 1 file changed, 155 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php index 0513dd1c7d3c4..13cf2c07e23f5 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php +++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php @@ -124,6 +124,161 @@ public function testCollect() ); } + /** + * Test taxes collection with full discount for quote. + * + * Test tax calculation with certain configuration and price calculation of items when the discount may be bigger than total + * This method will test the collector through $quote->collectTotals() method + * + * @see \Magento\SalesRule\Model\Utility::deltaRoundingFix + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + */ + public function testFullDiscountWithDeltaRoundingFix() + { + $configData = array ( + 'config_overrides' => + array ( + 'tax/calculation/apply_after_discount' => 0, + 'tax/calculation/discount_tax' => 1, + 'tax/calculation/algorithm' => 'ROW_BASE_CALCULATION', + 'tax/classes/shipping_tax_class' => SetupUtil::SHIPPING_TAX_CLASS, + ), + 'tax_rate_overrides' => + array ( + SetupUtil::TAX_RATE_TX => 18, + SetupUtil::TAX_RATE_SHIPPING => 0, + ), + 'tax_rule_overrides' => + array ( + array ( + 'code' => 'Product Tax Rule', + 'product_tax_class_ids' => + array ( + SetupUtil::PRODUCT_TAX_CLASS_1 + ), + ), + array ( + 'code' => 'Shipping Tax Rule', + 'product_tax_class_ids' => + array ( + SetupUtil::SHIPPING_TAX_CLASS + ), + 'tax_rate_ids' => + array ( + SetupUtil::TAX_RATE_SHIPPING, + ), + ), + ), + ); + + $quoteData = array ( + 'billing_address' => + array ( + 'region_id' => SetupUtil::REGION_TX, + ), + 'shipping_address' => + array ( + 'region_id' => SetupUtil::REGION_TX, + ), + 'items' => + [ + [ + 'sku' => 'simple1', + 'price' => 2542.37, + 'qty' => 2, + ] + ], + 'shipping_method' => 'free', + 'shopping_cart_rules' => + array ( + array ( + 'discount_amount' => 100, + ), + ), + ); + + $expectedResults = array ( + 'address_data' => + array ( + 'subtotal' => 5084.74, + 'base_subtotal' => 5084.74, + 'subtotal_incl_tax' => 5999.99, + 'base_subtotal_incl_tax' => 5999.99, + 'tax_amount' => 915.25, + 'base_tax_amount' => 915.25, + 'shipping_amount' => 0, + 'base_shipping_amount' => 0, + 'shipping_incl_tax' => 0, + 'base_shipping_incl_tax' => 0, + 'shipping_tax_amount' => 0, + 'base_shipping_tax_amount' => 0, + 'discount_amount' => -5999.99, + 'base_discount_amount' => -5999.99, + 'discount_tax_compensation_amount' => 0, + 'base_discount_tax_compensation_amount' => 0, + 'shipping_discount_tax_compensation_amount' => 0, + 'base_shipping_discount_tax_compensation_amount' => 0, + 'grand_total' => 0, + 'base_grand_total' => 0, + 'applied_taxes' => + array ( + SetupUtil::TAX_RATE_TX => + array ( + 'percent' => 18, + 'amount' => 915.25, + 'base_amount' => 915.25, + 'rates' => + array ( + array ( + 'code' => SetupUtil::TAX_RATE_TX, + 'title' => SetupUtil::TAX_RATE_TX, + 'percent' => 18, + ), + ), + ) + ), + ), + 'items_data' => + array ( + 'simple1' => + array ( + 'row_total' => 5084.74, + 'base_row_total' => 5084.74, + 'tax_percent' => 18, + 'price' => 2542.37, + 'base_price' => 2542.37, + 'price_incl_tax' => 3000, + 'base_price_incl_tax' => 3000, + 'row_total_incl_tax' => 5999.99, + 'base_row_total_incl_tax' => 5999.99, + 'tax_amount' => 915.25, + 'base_tax_amount' => 915.25, + 'discount_amount' => 5999.99, + 'base_discount_amount' => 5999.99, + 'discount_percent' => 100, + 'discount_tax_compensation_amount' => 0, + 'base_discount_tax_compensation_amount' => 0, + ), + ), + ); + + /** @var \Magento\Framework\ObjectManagerInterface $objectManager */ + $objectManager = Bootstrap::getObjectManager(); + + //Setup tax configurations + $this->setupUtil = new SetupUtil($objectManager); + $this->setupUtil->setupTax($configData); + + $quote = $this->setupUtil->setupQuote($quoteData); + + $quote->collectTotals(); + + $quoteAddress = $quote->getShippingAddress(); + + $this->verifyResult($quoteAddress, $expectedResults); + } + /** * Verify fields in quote item * From a7f65ac40bcecab88a108f40e3ecd0000a42c6bb Mon Sep 17 00:00:00 2001 From: ilnytskyi_sv <stailx1@gmail.com> Date: Wed, 6 Jun 2018 15:56:24 +0300 Subject: [PATCH 1088/1295] Array style fix #10790 --- .../Tax/Model/Sales/Total/Quote/TaxTest.php | 90 +++++++++---------- 1 file changed, 44 insertions(+), 46 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php index 13cf2c07e23f5..c7855e2ccba3b 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php +++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php @@ -127,7 +127,7 @@ public function testCollect() /** * Test taxes collection with full discount for quote. * - * Test tax calculation with certain configuration and price calculation of items when the discount may be bigger than total + * Test tax calculation and price when the discount may be bigger than total * This method will test the collector through $quote->collectTotals() method * * @see \Magento\SalesRule\Model\Utility::deltaRoundingFix @@ -136,51 +136,51 @@ public function testCollect() */ public function testFullDiscountWithDeltaRoundingFix() { - $configData = array ( + $configData = [ 'config_overrides' => - array ( + [ 'tax/calculation/apply_after_discount' => 0, 'tax/calculation/discount_tax' => 1, 'tax/calculation/algorithm' => 'ROW_BASE_CALCULATION', 'tax/classes/shipping_tax_class' => SetupUtil::SHIPPING_TAX_CLASS, - ), + ], 'tax_rate_overrides' => - array ( + [ SetupUtil::TAX_RATE_TX => 18, SetupUtil::TAX_RATE_SHIPPING => 0, - ), + ], 'tax_rule_overrides' => - array ( - array ( + [ + [ 'code' => 'Product Tax Rule', 'product_tax_class_ids' => - array ( + [ SetupUtil::PRODUCT_TAX_CLASS_1 - ), - ), - array ( + ], + ], + [ 'code' => 'Shipping Tax Rule', 'product_tax_class_ids' => - array ( + [ SetupUtil::SHIPPING_TAX_CLASS - ), + ], 'tax_rate_ids' => - array ( + [ SetupUtil::TAX_RATE_SHIPPING, - ), - ), - ), - ); + ], + ], + ], + ]; - $quoteData = array ( + $quoteData = [ 'billing_address' => - array ( + [ 'region_id' => SetupUtil::REGION_TX, - ), + ], 'shipping_address' => - array ( + [ 'region_id' => SetupUtil::REGION_TX, - ), + ], 'items' => [ [ @@ -191,16 +191,14 @@ public function testFullDiscountWithDeltaRoundingFix() ], 'shipping_method' => 'free', 'shopping_cart_rules' => - array ( - array ( - 'discount_amount' => 100, - ), - ), - ); + [ + ['discount_amount' => 100], + ], + ]; - $expectedResults = array ( + $expectedResults = [ 'address_data' => - array ( + [ 'subtotal' => 5084.74, 'base_subtotal' => 5084.74, 'subtotal_incl_tax' => 5999.99, @@ -222,27 +220,27 @@ public function testFullDiscountWithDeltaRoundingFix() 'grand_total' => 0, 'base_grand_total' => 0, 'applied_taxes' => - array ( + [ SetupUtil::TAX_RATE_TX => - array ( + [ 'percent' => 18, 'amount' => 915.25, 'base_amount' => 915.25, 'rates' => - array ( - array ( + [ + [ 'code' => SetupUtil::TAX_RATE_TX, 'title' => SetupUtil::TAX_RATE_TX, 'percent' => 18, - ), - ), - ) - ), - ), + ], + ], + ] + ], + ], 'items_data' => - array ( + [ 'simple1' => - array ( + [ 'row_total' => 5084.74, 'base_row_total' => 5084.74, 'tax_percent' => 18, @@ -259,9 +257,9 @@ public function testFullDiscountWithDeltaRoundingFix() 'discount_percent' => 100, 'discount_tax_compensation_amount' => 0, 'base_discount_tax_compensation_amount' => 0, - ), - ), - ); + ], + ], + ]; /** @var \Magento\Framework\ObjectManagerInterface $objectManager */ $objectManager = Bootstrap::getObjectManager(); From 0d1a0b0de214004d60bfe564f00f3e9646916f45 Mon Sep 17 00:00:00 2001 From: ilnytskyi_sv <stailx1@gmail.com> Date: Wed, 6 Jun 2018 17:08:08 +0300 Subject: [PATCH 1089/1295] Store data in a separate file #10790 --- .../Tax/Model/Sales/Total/Quote/TaxTest.php | 130 +---------------- .../Tax/_files/full_discount_with_tax.php | 132 ++++++++++++++++++ 2 files changed, 138 insertions(+), 124 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php index c7855e2ccba3b..72abb137d3dbb 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php +++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php @@ -11,6 +11,7 @@ require_once __DIR__ . '/SetupUtil.php'; require_once __DIR__ . '/../../../../_files/tax_calculation_data_aggregated.php'; +require_once __DIR__ . '/../../../../_files/full_discount_with_tax.php'; /** * Class TaxTest @@ -131,135 +132,16 @@ public function testCollect() * This method will test the collector through $quote->collectTotals() method * * @see \Magento\SalesRule\Model\Utility::deltaRoundingFix + * @magentoDataFixture Magento/Tax/_files/full_discount_with_tax.php * @magentoDbIsolation enabled * @magentoAppIsolation enabled */ public function testFullDiscountWithDeltaRoundingFix() { - $configData = [ - 'config_overrides' => - [ - 'tax/calculation/apply_after_discount' => 0, - 'tax/calculation/discount_tax' => 1, - 'tax/calculation/algorithm' => 'ROW_BASE_CALCULATION', - 'tax/classes/shipping_tax_class' => SetupUtil::SHIPPING_TAX_CLASS, - ], - 'tax_rate_overrides' => - [ - SetupUtil::TAX_RATE_TX => 18, - SetupUtil::TAX_RATE_SHIPPING => 0, - ], - 'tax_rule_overrides' => - [ - [ - 'code' => 'Product Tax Rule', - 'product_tax_class_ids' => - [ - SetupUtil::PRODUCT_TAX_CLASS_1 - ], - ], - [ - 'code' => 'Shipping Tax Rule', - 'product_tax_class_ids' => - [ - SetupUtil::SHIPPING_TAX_CLASS - ], - 'tax_rate_ids' => - [ - SetupUtil::TAX_RATE_SHIPPING, - ], - ], - ], - ]; - - $quoteData = [ - 'billing_address' => - [ - 'region_id' => SetupUtil::REGION_TX, - ], - 'shipping_address' => - [ - 'region_id' => SetupUtil::REGION_TX, - ], - 'items' => - [ - [ - 'sku' => 'simple1', - 'price' => 2542.37, - 'qty' => 2, - ] - ], - 'shipping_method' => 'free', - 'shopping_cart_rules' => - [ - ['discount_amount' => 100], - ], - ]; - - $expectedResults = [ - 'address_data' => - [ - 'subtotal' => 5084.74, - 'base_subtotal' => 5084.74, - 'subtotal_incl_tax' => 5999.99, - 'base_subtotal_incl_tax' => 5999.99, - 'tax_amount' => 915.25, - 'base_tax_amount' => 915.25, - 'shipping_amount' => 0, - 'base_shipping_amount' => 0, - 'shipping_incl_tax' => 0, - 'base_shipping_incl_tax' => 0, - 'shipping_tax_amount' => 0, - 'base_shipping_tax_amount' => 0, - 'discount_amount' => -5999.99, - 'base_discount_amount' => -5999.99, - 'discount_tax_compensation_amount' => 0, - 'base_discount_tax_compensation_amount' => 0, - 'shipping_discount_tax_compensation_amount' => 0, - 'base_shipping_discount_tax_compensation_amount' => 0, - 'grand_total' => 0, - 'base_grand_total' => 0, - 'applied_taxes' => - [ - SetupUtil::TAX_RATE_TX => - [ - 'percent' => 18, - 'amount' => 915.25, - 'base_amount' => 915.25, - 'rates' => - [ - [ - 'code' => SetupUtil::TAX_RATE_TX, - 'title' => SetupUtil::TAX_RATE_TX, - 'percent' => 18, - ], - ], - ] - ], - ], - 'items_data' => - [ - 'simple1' => - [ - 'row_total' => 5084.74, - 'base_row_total' => 5084.74, - 'tax_percent' => 18, - 'price' => 2542.37, - 'base_price' => 2542.37, - 'price_incl_tax' => 3000, - 'base_price_incl_tax' => 3000, - 'row_total_incl_tax' => 5999.99, - 'base_row_total_incl_tax' => 5999.99, - 'tax_amount' => 915.25, - 'base_tax_amount' => 915.25, - 'discount_amount' => 5999.99, - 'base_discount_amount' => 5999.99, - 'discount_percent' => 100, - 'discount_tax_compensation_amount' => 0, - 'base_discount_tax_compensation_amount' => 0, - ], - ], - ]; + global $fullTaxDiscountWithTax; + $configData = $fullTaxDiscountWithTax['config_data']; + $quoteData = $fullTaxDiscountWithTax['quote_data']; + $expectedResults = $fullTaxDiscountWithTax['expected_result']; /** @var \Magento\Framework\ObjectManagerInterface $objectManager */ $objectManager = Bootstrap::getObjectManager(); diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php new file mode 100644 index 0000000000000..36ef77ffaaa22 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php @@ -0,0 +1,132 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +use Magento\Tax\Model\Config; +use Magento\Tax\Model\Sales\Total\Quote\SetupUtil; + +$fullTaxDiscountWithTax = [ + 'config_data' => [ + 'config_overrides' => + [ + Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 0, + Config::CONFIG_XML_PATH_DISCOUNT_TAX => 1, + Config::XML_PATH_ALGORITHM => 'ROW_BASE_CALCULATION', + Config::CONFIG_XML_PATH_SHIPPING_TAX_CLASS => SetupUtil::SHIPPING_TAX_CLASS, + ], + 'tax_rate_overrides' => + [ + SetupUtil::TAX_RATE_TX => 18, + SetupUtil::TAX_RATE_SHIPPING => 0, + ], + 'tax_rule_overrides' => + [ + [ + 'code' => 'Product Tax Rule', + 'product_tax_class_ids' => + [ + SetupUtil::PRODUCT_TAX_CLASS_1 + ], + ], + [ + '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' => 2542.37, + 'qty' => 2, + ] + ], + 'shipping_method' => 'free', + 'shopping_cart_rules' => + [ + ['discount_amount' => 100], + ], + ], + 'expected_result' => [ + 'address_data' => + [ + 'subtotal' => 5084.74, + 'base_subtotal' => 5084.74, + 'subtotal_incl_tax' => 5999.99, + 'base_subtotal_incl_tax' => 5999.99, + 'tax_amount' => 915.25, + 'base_tax_amount' => 915.25, + 'shipping_amount' => 0, + 'base_shipping_amount' => 0, + 'shipping_incl_tax' => 0, + 'base_shipping_incl_tax' => 0, + 'shipping_tax_amount' => 0, + 'base_shipping_tax_amount' => 0, + 'discount_amount' => -5999.99, + 'base_discount_amount' => -5999.99, + 'discount_tax_compensation_amount' => 0, + 'base_discount_tax_compensation_amount' => 0, + 'shipping_discount_tax_compensation_amount' => 0, + 'base_shipping_discount_tax_compensation_amount' => 0, + 'grand_total' => 0, + 'base_grand_total' => 0, + 'applied_taxes' => + [ + SetupUtil::TAX_RATE_TX => + [ + 'percent' => 18, + 'amount' => 915.25, + 'base_amount' => 915.25, + 'rates' => + [ + [ + 'code' => SetupUtil::TAX_RATE_TX, + 'title' => SetupUtil::TAX_RATE_TX, + 'percent' => 18, + ], + ], + ] + ], + ], + 'items_data' => + [ + 'simple1' => + [ + 'row_total' => 5084.74, + 'base_row_total' => 5084.74, + 'tax_percent' => 18, + 'price' => 2542.37, + 'base_price' => 2542.37, + 'price_incl_tax' => 3000, + 'base_price_incl_tax' => 3000, + 'row_total_incl_tax' => 5999.99, + 'base_row_total_incl_tax' => 5999.99, + 'tax_amount' => 915.25, + 'base_tax_amount' => 915.25, + 'discount_amount' => 5999.99, + 'base_discount_amount' => 5999.99, + 'discount_percent' => 100, + 'discount_tax_compensation_amount' => 0, + 'base_discount_tax_compensation_amount' => 0, + ], + ], + ] +]; From ea7993768b88465a71c8acba5e84923a4abfb546 Mon Sep 17 00:00:00 2001 From: ilnytskyi_sv <stailx1@gmail.com> Date: Wed, 6 Jun 2018 17:23:30 +0300 Subject: [PATCH 1090/1295] Array tab #10790 --- .../Tax/_files/full_discount_with_tax.php | 224 +++++++++--------- 1 file changed, 112 insertions(+), 112 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php index 36ef77ffaaa22..232ae4ea063b1 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php +++ b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php @@ -7,126 +7,126 @@ use Magento\Tax\Model\Sales\Total\Quote\SetupUtil; $fullTaxDiscountWithTax = [ - 'config_data' => [ - 'config_overrides' => - [ - Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 0, - Config::CONFIG_XML_PATH_DISCOUNT_TAX => 1, - Config::XML_PATH_ALGORITHM => 'ROW_BASE_CALCULATION', - Config::CONFIG_XML_PATH_SHIPPING_TAX_CLASS => SetupUtil::SHIPPING_TAX_CLASS, - ], - 'tax_rate_overrides' => - [ - SetupUtil::TAX_RATE_TX => 18, - SetupUtil::TAX_RATE_SHIPPING => 0, - ], - 'tax_rule_overrides' => - [ + 'config_data' => [ + 'config_overrides' => [ - 'code' => 'Product Tax Rule', - 'product_tax_class_ids' => - [ - SetupUtil::PRODUCT_TAX_CLASS_1 - ], + Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 0, + Config::CONFIG_XML_PATH_DISCOUNT_TAX => 1, + Config::XML_PATH_ALGORITHM => 'ROW_BASE_CALCULATION', + Config::CONFIG_XML_PATH_SHIPPING_TAX_CLASS => SetupUtil::SHIPPING_TAX_CLASS, ], + 'tax_rate_overrides' => [ - 'code' => 'Shipping Tax Rule', - 'product_tax_class_ids' => - [ - SetupUtil::SHIPPING_TAX_CLASS - ], - 'tax_rate_ids' => - [ - SetupUtil::TAX_RATE_SHIPPING, - ], + SetupUtil::TAX_RATE_TX => 18, + SetupUtil::TAX_RATE_SHIPPING => 0, ], - ], - ], - 'quote_data' => [ - 'billing_address' => - [ - 'region_id' => SetupUtil::REGION_TX, - ], - 'shipping_address' => - [ - 'region_id' => SetupUtil::REGION_TX, - ], - 'items' => - [ + 'tax_rule_overrides' => [ - 'sku' => 'simple1', - 'price' => 2542.37, - 'qty' => 2, - ] - ], - 'shipping_method' => 'free', - 'shopping_cart_rules' => - [ - ['discount_amount' => 100], - ], - ], - 'expected_result' => [ - 'address_data' => - [ - 'subtotal' => 5084.74, - 'base_subtotal' => 5084.74, - 'subtotal_incl_tax' => 5999.99, - 'base_subtotal_incl_tax' => 5999.99, - 'tax_amount' => 915.25, - 'base_tax_amount' => 915.25, - 'shipping_amount' => 0, - 'base_shipping_amount' => 0, - 'shipping_incl_tax' => 0, - 'base_shipping_incl_tax' => 0, - 'shipping_tax_amount' => 0, - 'base_shipping_tax_amount' => 0, - 'discount_amount' => -5999.99, - 'base_discount_amount' => -5999.99, - 'discount_tax_compensation_amount' => 0, - 'base_discount_tax_compensation_amount' => 0, - 'shipping_discount_tax_compensation_amount' => 0, - 'base_shipping_discount_tax_compensation_amount' => 0, - 'grand_total' => 0, - 'base_grand_total' => 0, - 'applied_taxes' => [ - SetupUtil::TAX_RATE_TX => + 'code' => 'Product Tax Rule', + 'product_tax_class_ids' => [ - 'percent' => 18, - 'amount' => 915.25, - 'base_amount' => 915.25, - 'rates' => - [ - [ - 'code' => SetupUtil::TAX_RATE_TX, - 'title' => SetupUtil::TAX_RATE_TX, - 'percent' => 18, - ], - ], - ] + SetupUtil::PRODUCT_TAX_CLASS_1 + ], ], - ], - 'items_data' => - [ - 'simple1' => [ - 'row_total' => 5084.74, - 'base_row_total' => 5084.74, - 'tax_percent' => 18, - 'price' => 2542.37, - 'base_price' => 2542.37, - 'price_incl_tax' => 3000, - 'base_price_incl_tax' => 3000, - 'row_total_incl_tax' => 5999.99, - 'base_row_total_incl_tax' => 5999.99, - 'tax_amount' => 915.25, - 'base_tax_amount' => 915.25, - 'discount_amount' => 5999.99, - 'base_discount_amount' => 5999.99, - 'discount_percent' => 100, - 'discount_tax_compensation_amount' => 0, - 'base_discount_tax_compensation_amount' => 0, + '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' => 2542.37, + 'qty' => 2, + ] + ], + 'shipping_method' => 'free', + 'shopping_cart_rules' => + [ + ['discount_amount' => 100], + ], + ], + 'expected_result' => [ + 'address_data' => + [ + 'subtotal' => 5084.74, + 'base_subtotal' => 5084.74, + 'subtotal_incl_tax' => 5999.99, + 'base_subtotal_incl_tax' => 5999.99, + 'tax_amount' => 915.25, + 'base_tax_amount' => 915.25, + 'shipping_amount' => 0, + 'base_shipping_amount' => 0, + 'shipping_incl_tax' => 0, + 'base_shipping_incl_tax' => 0, + 'shipping_tax_amount' => 0, + 'base_shipping_tax_amount' => 0, + 'discount_amount' => -5999.99, + 'base_discount_amount' => -5999.99, + 'discount_tax_compensation_amount' => 0, + 'base_discount_tax_compensation_amount' => 0, + 'shipping_discount_tax_compensation_amount' => 0, + 'base_shipping_discount_tax_compensation_amount' => 0, + 'grand_total' => 0, + 'base_grand_total' => 0, + 'applied_taxes' => + [ + SetupUtil::TAX_RATE_TX => + [ + 'percent' => 18, + 'amount' => 915.25, + 'base_amount' => 915.25, + 'rates' => + [ + [ + 'code' => SetupUtil::TAX_RATE_TX, + 'title' => SetupUtil::TAX_RATE_TX, + 'percent' => 18, + ], + ], + ] + ], + ], + 'items_data' => + [ + 'simple1' => + [ + 'row_total' => 5084.74, + 'base_row_total' => 5084.74, + 'tax_percent' => 18, + 'price' => 2542.37, + 'base_price' => 2542.37, + 'price_incl_tax' => 3000, + 'base_price_incl_tax' => 3000, + 'row_total_incl_tax' => 5999.99, + 'base_row_total_incl_tax' => 5999.99, + 'tax_amount' => 915.25, + 'base_tax_amount' => 915.25, + 'discount_amount' => 5999.99, + 'base_discount_amount' => 5999.99, + 'discount_percent' => 100, + 'discount_tax_compensation_amount' => 0, + 'base_discount_tax_compensation_amount' => 0, + ], + ], + ] ]; From ec8bbce2059ace80602b1583f9e9783b2ad6a219 Mon Sep 17 00:00:00 2001 From: ilnytskyi_sv <stailx1@gmail.com> Date: Wed, 6 Jun 2018 17:41:42 +0300 Subject: [PATCH 1091/1295] #10790 --- .../Tax/_files/full_discount_with_tax.php | 218 +++++++++--------- 1 file changed, 109 insertions(+), 109 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php index 232ae4ea063b1..7be414e046ddf 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php +++ b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php @@ -8,125 +8,125 @@ $fullTaxDiscountWithTax = [ 'config_data' => [ - 'config_overrides' => - [ - Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 0, - Config::CONFIG_XML_PATH_DISCOUNT_TAX => 1, - Config::XML_PATH_ALGORITHM => 'ROW_BASE_CALCULATION', - Config::CONFIG_XML_PATH_SHIPPING_TAX_CLASS => SetupUtil::SHIPPING_TAX_CLASS, - ], - 'tax_rate_overrides' => - [ - SetupUtil::TAX_RATE_TX => 18, - SetupUtil::TAX_RATE_SHIPPING => 0, - ], - 'tax_rule_overrides' => - [ + 'config_overrides' => [ - 'code' => 'Product Tax Rule', - 'product_tax_class_ids' => - [ - SetupUtil::PRODUCT_TAX_CLASS_1 - ], + Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 0, + Config::CONFIG_XML_PATH_DISCOUNT_TAX => 1, + Config::XML_PATH_ALGORITHM => 'ROW_BASE_CALCULATION', + Config::CONFIG_XML_PATH_SHIPPING_TAX_CLASS => SetupUtil::SHIPPING_TAX_CLASS, ], + 'tax_rate_overrides' => [ - 'code' => 'Shipping Tax Rule', - 'product_tax_class_ids' => - [ - SetupUtil::SHIPPING_TAX_CLASS - ], - 'tax_rate_ids' => - [ - SetupUtil::TAX_RATE_SHIPPING, - ], + SetupUtil::TAX_RATE_TX => 18, + SetupUtil::TAX_RATE_SHIPPING => 0, + ], + 'tax_rule_overrides' => + [ + [ + 'code' => 'Product Tax Rule', + 'product_tax_class_ids' => + [ + SetupUtil::PRODUCT_TAX_CLASS_1 + ], + ], + [ + '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' => - [ + 'billing_address' => + [ + 'region_id' => SetupUtil::REGION_TX, + ], + 'shipping_address' => + [ + 'region_id' => SetupUtil::REGION_TX, + ], + 'items' => [ - 'sku' => 'simple1', - 'price' => 2542.37, - 'qty' => 2, - ] - ], - 'shipping_method' => 'free', - 'shopping_cart_rules' => - [ - ['discount_amount' => 100], - ], + [ + 'sku' => 'simple1', + 'price' => 2542.37, + 'qty' => 2, + ] + ], + 'shipping_method' => 'free', + 'shopping_cart_rules' => + [ + ['discount_amount' => 100], + ], ], 'expected_result' => [ - 'address_data' => - [ - 'subtotal' => 5084.74, - 'base_subtotal' => 5084.74, - 'subtotal_incl_tax' => 5999.99, - 'base_subtotal_incl_tax' => 5999.99, - 'tax_amount' => 915.25, - 'base_tax_amount' => 915.25, - 'shipping_amount' => 0, - 'base_shipping_amount' => 0, - 'shipping_incl_tax' => 0, - 'base_shipping_incl_tax' => 0, - 'shipping_tax_amount' => 0, - 'base_shipping_tax_amount' => 0, - 'discount_amount' => -5999.99, - 'base_discount_amount' => -5999.99, - 'discount_tax_compensation_amount' => 0, - 'base_discount_tax_compensation_amount' => 0, - 'shipping_discount_tax_compensation_amount' => 0, - 'base_shipping_discount_tax_compensation_amount' => 0, - 'grand_total' => 0, - 'base_grand_total' => 0, - 'applied_taxes' => - [ - SetupUtil::TAX_RATE_TX => - [ - 'percent' => 18, - 'amount' => 915.25, - 'base_amount' => 915.25, - 'rates' => - [ + 'address_data' => + [ + 'subtotal' => 5084.74, + 'base_subtotal' => 5084.74, + 'subtotal_incl_tax' => 5999.99, + 'base_subtotal_incl_tax' => 5999.99, + 'tax_amount' => 915.25, + 'base_tax_amount' => 915.25, + 'shipping_amount' => 0, + 'base_shipping_amount' => 0, + 'shipping_incl_tax' => 0, + 'base_shipping_incl_tax' => 0, + 'shipping_tax_amount' => 0, + 'base_shipping_tax_amount' => 0, + 'discount_amount' => -5999.99, + 'base_discount_amount' => -5999.99, + 'discount_tax_compensation_amount' => 0, + 'base_discount_tax_compensation_amount' => 0, + 'shipping_discount_tax_compensation_amount' => 0, + 'base_shipping_discount_tax_compensation_amount' => 0, + 'grand_total' => 0, + 'base_grand_total' => 0, + 'applied_taxes' => + [ + SetupUtil::TAX_RATE_TX => + [ + 'percent' => 18, + 'amount' => 915.25, + 'base_amount' => 915.25, + 'rates' => [ - 'code' => SetupUtil::TAX_RATE_TX, - 'title' => SetupUtil::TAX_RATE_TX, - 'percent' => 18, + [ + 'code' => SetupUtil::TAX_RATE_TX, + 'title' => SetupUtil::TAX_RATE_TX, + 'percent' => 18, + ], ], - ], - ] - ], - ], - 'items_data' => - [ - 'simple1' => - [ - 'row_total' => 5084.74, - 'base_row_total' => 5084.74, - 'tax_percent' => 18, - 'price' => 2542.37, - 'base_price' => 2542.37, - 'price_incl_tax' => 3000, - 'base_price_incl_tax' => 3000, - 'row_total_incl_tax' => 5999.99, - 'base_row_total_incl_tax' => 5999.99, - 'tax_amount' => 915.25, - 'base_tax_amount' => 915.25, - 'discount_amount' => 5999.99, - 'base_discount_amount' => 5999.99, - 'discount_percent' => 100, - 'discount_tax_compensation_amount' => 0, - 'base_discount_tax_compensation_amount' => 0, - ], - ], + ] + ], + ], + 'items_data' => + [ + 'simple1' => + [ + 'row_total' => 5084.74, + 'base_row_total' => 5084.74, + 'tax_percent' => 18, + 'price' => 2542.37, + 'base_price' => 2542.37, + 'price_incl_tax' => 3000, + 'base_price_incl_tax' => 3000, + 'row_total_incl_tax' => 5999.99, + 'base_row_total_incl_tax' => 5999.99, + 'tax_amount' => 915.25, + 'base_tax_amount' => 915.25, + 'discount_amount' => 5999.99, + 'base_discount_amount' => 5999.99, + 'discount_percent' => 100, + 'discount_tax_compensation_amount' => 0, + 'base_discount_tax_compensation_amount' => 0, + ], + ], ] ]; From 17f7b776c52f6268c8578962985ceb9d1d358c3a Mon Sep 17 00:00:00 2001 From: ilnytskyi_sv <stailx1@gmail.com> Date: Thu, 7 Jun 2018 10:43:26 +0300 Subject: [PATCH 1092/1295] #10790 --- .../Tax/_files/full_discount_with_tax.php | 52 +++++++------------ 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php index 7be414e046ddf..ecdbb3c6f3b1f 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php +++ b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php @@ -8,51 +8,42 @@ $fullTaxDiscountWithTax = [ 'config_data' => [ - 'config_overrides' => - [ + 'config_overrides' => [ Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 0, Config::CONFIG_XML_PATH_DISCOUNT_TAX => 1, Config::XML_PATH_ALGORITHM => 'ROW_BASE_CALCULATION', Config::CONFIG_XML_PATH_SHIPPING_TAX_CLASS => SetupUtil::SHIPPING_TAX_CLASS, ], - 'tax_rate_overrides' => - [ + 'tax_rate_overrides' => [ SetupUtil::TAX_RATE_TX => 18, SetupUtil::TAX_RATE_SHIPPING => 0, ], - 'tax_rule_overrides' => - [ + 'tax_rule_overrides' => [ [ 'code' => 'Product Tax Rule', - 'product_tax_class_ids' => - [ + 'product_tax_class_ids' => [ SetupUtil::PRODUCT_TAX_CLASS_1 ], ], [ 'code' => 'Shipping Tax Rule', - 'product_tax_class_ids' => - [ + 'product_tax_class_ids' => [ SetupUtil::SHIPPING_TAX_CLASS ], - 'tax_rate_ids' => - [ + 'tax_rate_ids' => [ SetupUtil::TAX_RATE_SHIPPING, ], ], ], ], 'quote_data' => [ - 'billing_address' => - [ + 'billing_address' => [ 'region_id' => SetupUtil::REGION_TX, ], - 'shipping_address' => - [ + 'shipping_address' => [ 'region_id' => SetupUtil::REGION_TX, ], - 'items' => - [ + 'items' => [ [ 'sku' => 'simple1', 'price' => 2542.37, @@ -60,14 +51,14 @@ ] ], 'shipping_method' => 'free', - 'shopping_cart_rules' => - [ - ['discount_amount' => 100], + 'shopping_cart_rules' => [ + [ + 'discount_amount' => 100 + ], ], ], 'expected_result' => [ - 'address_data' => - [ + 'address_data' => [ 'subtotal' => 5084.74, 'base_subtotal' => 5084.74, 'subtotal_incl_tax' => 5999.99, @@ -88,15 +79,12 @@ 'base_shipping_discount_tax_compensation_amount' => 0, 'grand_total' => 0, 'base_grand_total' => 0, - 'applied_taxes' => - [ - SetupUtil::TAX_RATE_TX => - [ + 'applied_taxes' => [ + SetupUtil::TAX_RATE_TX => [ 'percent' => 18, 'amount' => 915.25, 'base_amount' => 915.25, - 'rates' => - [ + 'rates' => [ [ 'code' => SetupUtil::TAX_RATE_TX, 'title' => SetupUtil::TAX_RATE_TX, @@ -106,10 +94,8 @@ ] ], ], - 'items_data' => - [ - 'simple1' => - [ + 'items_data' => [ + 'simple1' => [ 'row_total' => 5084.74, 'base_row_total' => 5084.74, 'tax_percent' => 18, From d5be0aa37df4dcace02c7a6275cce616062231c4 Mon Sep 17 00:00:00 2001 From: ilnytskyi_sv <stailx1@gmail.com> Date: Thu, 7 Jun 2018 11:32:03 +0300 Subject: [PATCH 1093/1295] #10790 --- .../Magento/Tax/Model/Sales/Total/Quote/TaxTest.php | 8 ++++---- .../Magento/Tax/_files/full_discount_with_tax.php | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php index 72abb137d3dbb..ebf2c2eea9553 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php +++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php @@ -138,10 +138,10 @@ public function testCollect() */ public function testFullDiscountWithDeltaRoundingFix() { - global $fullTaxDiscountWithTax; - $configData = $fullTaxDiscountWithTax['config_data']; - $quoteData = $fullTaxDiscountWithTax['quote_data']; - $expectedResults = $fullTaxDiscountWithTax['expected_result']; + global $fullDiscountIncTax; + $configData = $fullDiscountIncTax['config_data']; + $quoteData = $fullDiscountIncTax['quote_data']; + $expectedResults = $fullDiscountIncTax['expected_result']; /** @var \Magento\Framework\ObjectManagerInterface $objectManager */ $objectManager = Bootstrap::getObjectManager(); diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php index ecdbb3c6f3b1f..335390d39006a 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php +++ b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php @@ -6,7 +6,7 @@ use Magento\Tax\Model\Config; use Magento\Tax\Model\Sales\Total\Quote\SetupUtil; -$fullTaxDiscountWithTax = [ +$fullDiscountIncTax = [ 'config_data' => [ 'config_overrides' => [ Config::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT => 0, From 98a1561c4b0fbe2cc09000056fa82daa1c39964d Mon Sep 17 00:00:00 2001 From: ilnytskyi_sv <stailx1@gmail.com> Date: Sat, 9 Jun 2018 14:12:48 +0300 Subject: [PATCH 1094/1295] #10790 --- .../testsuite/Magento/Tax/_files/full_discount_with_tax.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php index 335390d39006a..2b5ef07de341a 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php +++ b/dev/tests/integration/testsuite/Magento/Tax/_files/full_discount_with_tax.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + use Magento\Tax\Model\Config; use Magento\Tax\Model\Sales\Total\Quote\SetupUtil; From feea2962157eaad832205c10ef35b272bb711df1 Mon Sep 17 00:00:00 2001 From: Graham Wharton <graham@gwharton.me.uk> Date: Mon, 8 Apr 2019 20:36:14 +0100 Subject: [PATCH 1095/1295] Moved generation of Gallery options and Gallery fullscreen options to own block functions. --- .../Block/Product/View/GalleryOptions.php | 154 ++++++++++++ .../Block/Product/View/GalleryOptionsTest.php | 223 ++++++++++++++++++ .../frontend/layout/catalog_product_view.xml | 6 +- .../templates/product/view/gallery.phtml | 75 +----- .../Block/Product/View/GalleryTest.php | 5 +- 5 files changed, 388 insertions(+), 75 deletions(-) create mode 100644 app/code/Magento/Catalog/Block/Product/View/GalleryOptions.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Block/Product/View/GalleryOptionsTest.php diff --git a/app/code/Magento/Catalog/Block/Product/View/GalleryOptions.php b/app/code/Magento/Catalog/Block/Product/View/GalleryOptions.php new file mode 100644 index 0000000000000..7790785133ddf --- /dev/null +++ b/app/code/Magento/Catalog/Block/Product/View/GalleryOptions.php @@ -0,0 +1,154 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Block\Product\View; + +use Magento\Framework\View\Element\Block\ArgumentInterface; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Catalog\Block\Product\Context; +use Magento\Framework\Stdlib\ArrayUtils; + +class GalleryOptions extends AbstractView implements ArgumentInterface +{ + /** + * @var Json + */ + private $jsonSerializer; + + /** + * @var Gallery + */ + private $gallery; + + /** + * @param Context $context + * @param ArrayUtils $arrayUtils + * @param Json $jsonSerializer + * @param Gallery $gallery + * @param array $data + */ + public function __construct( + Context $context, + ArrayUtils $arrayUtils, + Json $jsonSerializer, + Gallery $gallery, + array $data = [] + ) { + $this->gallery = $gallery; + $this->jsonSerializer = $jsonSerializer; + parent::__construct($context, $arrayUtils, $data); + } + + /** + * Retrieve gallery options in JSON format + * + * @return string + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + * @SuppressWarnings(PHPMD.ElseExpression) + */ + public function getOptionsJson() + { + $optionItems = null; + + //Special case for gallery/nav which can be the string "thumbs/false/dots" + if (is_bool($this->getVar("gallery/nav"))) { + $optionItems['nav'] = $this->getVar("gallery/nav") ? 'true' : 'false'; + } else { + $optionItems['nav'] = $this->escapeHtml($this->getVar("gallery/nav")); + } + + $optionItems['loop'] = $this->getVar("gallery/loop"); + $optionItems['keyboard'] = $this->getVar("gallery/keyboard"); + $optionItems['arrows'] = $this->getVar("gallery/arrows"); + $optionItems['allowfullscreen'] = $this->getVar("gallery/allowfullscreen"); + $optionItems['showCaption'] = $this->getVar("gallery/caption"); + $optionItems['width'] = (int)$this->escapeHtml( + $this->gallery->getImageAttribute('product_page_image_medium', 'width') + ); + $optionItems['thumbwidth'] = (int)$this->escapeHtml( + $this->gallery->getImageAttribute('product_page_image_small', 'width') + ); + + if ($this->gallery->getImageAttribute('product_page_image_small', 'height') || + $this->gallery->getImageAttribute('product_page_image_small', 'width')) { + $optionItems['thumbheight'] = (int)$this->escapeHtml( + $this->gallery->getImageAttribute('product_page_image_small', 'height') ?: + $this->gallery->getImageAttribute('product_page_image_small', 'width') + ); + } + + if ($this->gallery->getImageAttribute('product_page_image_medium', 'height') || + $this->gallery->getImageAttribute('product_page_image_medium', 'width')) { + $optionItems['height'] = (int)$this->escapeHtml( + $this->gallery->getImageAttribute('product_page_image_medium', 'height') ?: + $this->gallery->getImageAttribute('product_page_image_medium', 'width') + ); + } + + if ($this->getVar("gallery/transition/duration")) { + $optionItems['transitionduration'] = + (int)$this->escapeHtml($this->getVar("gallery/transition/duration")); + } + + $optionItems['transition'] = $this->escapeHtml($this->getVar("gallery/transition/effect")); + $optionItems['navarrows'] = $this->getVar("gallery/navarrows"); + $optionItems['navtype'] = $this->escapeHtml($this->getVar("gallery/navtype")); + $optionItems['navdir'] = $this->escapeHtml($this->getVar("gallery/navdir")); + + if ($this->getVar("gallery/thumbmargin")) { + $optionItems['thumbmargin'] = (int)$this->escapeHtml($this->getVar("gallery/thumbmargin")); + } + + return $this->jsonSerializer->serialize($optionItems); + } + + /** + * Retrieve gallery fullscreen options in JSON format + * + * @return string + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + * @SuppressWarnings(PHPMD.ElseExpression) + */ + public function getFSOptionsJson() + { + $fsOptionItems = null; + + //Special case for gallery/nav which can be the string "thumbs/false/dots" + if (is_bool($this->getVar("gallery/fullscreen/nav"))) { + $fsOptionItems['nav'] = $this->getVar("gallery/fullscreen/nav") ? 'true' : 'false'; + } else { + $fsOptionItems['nav'] = $this->escapeHtml($this->getVar("gallery/fullscreen/nav")); + } + + $fsOptionItems['loop'] = $this->getVar("gallery/fullscreen/loop"); + $fsOptionItems['navdir'] = $this->escapeHtml($this->getVar("gallery/fullscreen/navdir")); + $fsOptionItems['navarrows'] = $this->getVar("gallery/fullscreen/navarrows"); + $fsOptionItems['navtype'] = $this->escapeHtml($this->getVar("gallery/fullscreen/navtype")); + $fsOptionItems['arrows'] = $this->getVar("gallery/fullscreen/arrows"); + $fsOptionItems['showCaption'] = $this->getVar("gallery/fullscreen/caption"); + + if ($this->getVar("gallery/fullscreen/transition/duration")) { + $fsOptionItems['transitionduration'] = (int)$this->escapeHtml( + $this->getVar("gallery/fullscreen/transition/duration") + ); + } + + $fsOptionItems['transition'] = $this->escapeHtml($this->getVar("gallery/fullscreen/transition/effect")); + + if ($this->getVar("gallery/fullscreen/keyboard")) { + $fsOptionItems['keyboard'] = $this->getVar("gallery/fullscreen/keyboard"); + } + + if ($this->getVar("gallery/fullscreen/thumbmargin")) { + $fsOptionItems['thumbmargin'] = + (int)$this->escapeHtml($this->getVar("gallery/fullscreen/thumbmargin")); + } + + return $this->jsonSerializer->serialize($fsOptionItems); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/View/GalleryOptionsTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/View/GalleryOptionsTest.php new file mode 100644 index 0000000000000..102b810b0e0a8 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/View/GalleryOptionsTest.php @@ -0,0 +1,223 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Test\Unit\Block\Product\View; + +use Magento\Catalog\Block\Product\Context; +use Magento\Catalog\Block\Product\View\Gallery; +use Magento\Catalog\Block\Product\View\GalleryOptions; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\Escaper; +use Magento\Framework\View\Config; +use Magento\Framework\Config\View; +use Magento\Framework\Serialize\Serializer\Json; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class GalleryOptionsTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var GalleryOptions + */ + private $model; + + /** + * @var Gallery|\PHPUnit_Framework_MockObject_MockObject + */ + private $gallery; + + /** + * @var Context|\PHPUnit_Framework_MockObject_MockObject + */ + private $context; + + /** + * @var Json + */ + private $jsonSerializer; + + /** + * @var View|\PHPUnit_Framework_MockObject_MockObject + */ + private $configView; + + /** + * @var Config|\PHPUnit_Framework_MockObject_MockObject + */ + private $viewConfig; + + /** + * @var Escaper + */ + private $escaper; + + protected function setUp() + { + $objectManager = new ObjectManager($this); + + $this->escaper = $objectManager->getObject(Escaper::class); + $this->configView = $this->createMock(View::class); + + $this->viewConfig = $this->createConfiguredMock( + Config::class, + [ + 'getViewConfig' => $this->configView + ] + ); + + $this->context = $this->createConfiguredMock( + Context::class, + [ + 'getEscaper' => $this->escaper, + 'getViewConfig' => $this->viewConfig + ] + ); + + $this->gallery = $this->createMock(Gallery::class); + + $this->jsonSerializer = $objectManager->getObject( + Json::class + ); + + $this->model = $objectManager->getObject(GalleryOptions::class, [ + 'context' => $this->context, + 'jsonSerializer' => $this->jsonSerializer, + 'gallery' => $this->gallery + ]); + } + + public function testGetOptionsJson() + { + $configMap = [ + ['Magento_Catalog', 'gallery/nav', 'thumbs'], + ['Magento_Catalog', 'gallery/loop', false], + ['Magento_Catalog', 'gallery/keyboard', true], + ['Magento_Catalog', 'gallery/arrows', true], + ['Magento_Catalog', 'gallery/caption', false], + ['Magento_Catalog', 'gallery/allowfullscreen', true], + ['Magento_Catalog', 'gallery/navdir', 'horizontal'], + ['Magento_Catalog', 'gallery/navarrows', true], + ['Magento_Catalog', 'gallery/navtype', 'slides'], + ['Magento_Catalog', 'gallery/thumbmargin', '5'], + ['Magento_Catalog', 'gallery/transition/effect', 'slide'], + ['Magento_Catalog', 'gallery/transition/duration', '500'], + ]; + + $imageAttributesMap = [ + ['product_page_image_medium','height',null, 100], + ['product_page_image_medium','width',null, 200], + ['product_page_image_small','height',null, 300], + ['product_page_image_small','width',null, 400] + ]; + + $this->configView->expects($this->any()) + ->method('getVarValue') + ->will($this->returnValueMap($configMap)); + $this->gallery->expects($this->any()) + ->method('getImageAttribute') + ->will($this->returnValueMap($imageAttributesMap)); + + $json = $this->model->getOptionsJson(); + + $decodedJson = $this->jsonSerializer->unserialize($json); + + $this->assertSame('thumbs', $decodedJson['nav']); + $this->assertSame(false, $decodedJson['loop']); + $this->assertSame(true, $decodedJson['keyboard']); + $this->assertSame(true, $decodedJson['arrows']); + $this->assertSame(false, $decodedJson['showCaption']); + $this->assertSame(true, $decodedJson['allowfullscreen']); + $this->assertSame('horizontal', $decodedJson['navdir']); + $this->assertSame(true, $decodedJson['navarrows']); + $this->assertSame('slides', $decodedJson['navtype']); + $this->assertSame(5, $decodedJson['thumbmargin']); + $this->assertSame('slide', $decodedJson['transition']); + $this->assertSame(500, $decodedJson['transitionduration']); + $this->assertSame(100, $decodedJson['height']); + $this->assertSame(200, $decodedJson['width']); + $this->assertSame(300, $decodedJson['thumbheight']); + $this->assertSame(400, $decodedJson['thumbwidth']); + } + + public function testGetFSOptionsJson() + { + $configMap = [ + ['Magento_Catalog', 'gallery/fullscreen/nav', false], + ['Magento_Catalog', 'gallery/fullscreen/loop', true], + ['Magento_Catalog', 'gallery/fullscreen/keyboard', true], + ['Magento_Catalog', 'gallery/fullscreen/arrows', false], + ['Magento_Catalog', 'gallery/fullscreen/caption', true], + ['Magento_Catalog', 'gallery/fullscreen/navdir', 'vertical'], + ['Magento_Catalog', 'gallery/fullscreen/navarrows', false], + ['Magento_Catalog', 'gallery/fullscreen/navtype', 'thumbs'], + ['Magento_Catalog', 'gallery/fullscreen/thumbmargin', '10'], + ['Magento_Catalog', 'gallery/fullscreen/transition/effect', 'dissolve'], + ['Magento_Catalog', 'gallery/fullscreen/transition/duration', '300'] + ]; + + $this->configView->expects($this->any()) + ->method('getVarValue') + ->will($this->returnValueMap($configMap)); + + $json = $this->model->getFSOptionsJson(); + + $decodedJson = $this->jsonSerializer->unserialize($json); + + //Note, this tests the special case for nav variable set to false. It + //Should not be converted to boolean. + $this->assertSame('false', $decodedJson['nav']); + $this->assertSame(true, $decodedJson['loop']); + $this->assertSame(false, $decodedJson['arrows']); + $this->assertSame(true, $decodedJson['keyboard']); + $this->assertSame(true, $decodedJson['showCaption']); + $this->assertSame('vertical', $decodedJson['navdir']); + $this->assertSame(false, $decodedJson['navarrows']); + $this->assertSame(10, $decodedJson['thumbmargin']); + $this->assertSame('thumbs', $decodedJson['navtype']); + $this->assertSame('dissolve', $decodedJson['transition']); + $this->assertSame(300, $decodedJson['transitionduration']); + } + + public function testGetOptionsJsonOptionals() + { + $configMap = [ + ['Magento_Catalog', 'gallery/fullscreen/thumbmargin', false], + ['Magento_Catalog', 'gallery/fullscreen/transition/duration', false] + ]; + + $this->configView->expects($this->any()) + ->method('getVarValue') + ->will($this->returnValueMap($configMap)); + + $json = $this->model->getOptionsJson(); + + $decodedJson = $this->jsonSerializer->unserialize($json); + + $this->assertArrayNotHasKey('thumbmargin', $decodedJson); + $this->assertArrayNotHasKey('transitionduration', $decodedJson); + } + + public function testGetFSOptionsJsonOptionals() + { + $configMap = [ + ['Magento_Catalog', 'gallery/fullscreen/keyboard', false], + ['Magento_Catalog', 'gallery/fullscreen/thumbmargin', false], + ['Magento_Catalog', 'gallery/fullscreen/transition/duration', false] + ]; + + $this->configView->expects($this->any()) + ->method('getVarValue') + ->will($this->returnValueMap($configMap)); + + $json = $this->model->getFSOptionsJson(); + + $decodedJson = $this->jsonSerializer->unserialize($json); + + $this->assertArrayNotHasKey('thumbmargin', $decodedJson); + $this->assertArrayNotHasKey('keyboard', $decodedJson); + $this->assertArrayNotHasKey('transitionduration', $decodedJson); + } +} diff --git a/app/code/Magento/Catalog/view/frontend/layout/catalog_product_view.xml b/app/code/Magento/Catalog/view/frontend/layout/catalog_product_view.xml index 3630fddb326a7..aa6ffa1bd33c4 100644 --- a/app/code/Magento/Catalog/view/frontend/layout/catalog_product_view.xml +++ b/app/code/Magento/Catalog/view/frontend/layout/catalog_product_view.xml @@ -121,7 +121,11 @@ </arguments> </block> </container> - <block class="Magento\Catalog\Block\Product\View\Gallery" name="product.info.media.image" template="Magento_Catalog::product/view/gallery.phtml"/> + <block class="Magento\Catalog\Block\Product\View\Gallery" name="product.info.media.image" template="Magento_Catalog::product/view/gallery.phtml"> + <arguments> + <argument name="gallery_options" xsi:type="object">Magento\Catalog\Block\Product\View\GalleryOptions</argument> + </arguments> + </block> <container name="skip_gallery_after.wrapper" htmlTag="div" htmlClass="action-skip-wrapper"> <block class="Magento\Framework\View\Element\Template" after="product.info.media.image" name="skip_gallery_after" template="Magento_Theme::html/skip.phtml"> <arguments> 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 b2fa8e9aaf80f..0f65e39284392 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 @@ -43,79 +43,8 @@ "mixins":["magnifier/magnify"], "magnifierOpts": <?= /* @noEscape */ $block->getMagnifier() ?>, "data": <?= /* @noEscape */ $block->getGalleryImagesJson() ?>, - "options": { - "nav": "<?= $block->escapeHtml($block->getVar("gallery/nav")) ?>", - <?php if (($block->getVar("gallery/loop"))) : ?> - "loop": <?= $block->escapeHtml($block->getVar("gallery/loop")) ?>, - <?php endif; ?> - <?php if (($block->getVar("gallery/keyboard"))) : ?> - "keyboard": <?= $block->escapeHtml($block->getVar("gallery/keyboard")) ?>, - <?php endif; ?> - <?php if (($block->getVar("gallery/arrows"))) : ?> - "arrows": <?= $block->escapeHtml($block->getVar("gallery/arrows")) ?>, - <?php endif; ?> - <?php if (($block->getVar("gallery/allowfullscreen"))) : ?> - "allowfullscreen": <?= $block->escapeHtml($block->getVar("gallery/allowfullscreen")) ?>, - <?php endif; ?> - <?php if (is_bool($block->getVar("gallery/caption"))) : ?> - "showCaption": <?= /* @noEscape */ $block->getVar("gallery/caption") ? 'true' : 'false'; ?>, - <?php endif; ?> - <?php - $imgWidth = $block->getImageAttribute('product_page_image_medium', 'width'); - $thumbWidth = $block->getImageAttribute('product_page_image_small', 'width'); - ?> - "width": "<?= $block->escapeHtml($imgWidth) ?>", - "thumbwidth": "<?= $block->escapeHtml($thumbWidth) ?>", - <?php - $thumbHeight = $block->getImageAttribute('product_page_image_small', 'height') - ?: $block->getImageAttribute('product_page_image_small', 'width'); - ?> - <?php if ($thumbHeight) : ?> - "thumbheight": <?= $block->escapeHtml($thumbHeight); ?>, - <?php endif; ?> - <?php if (($block->getVar("gallery/thumbmargin"))) : ?> - "thumbmargin": <?= (int)$block->getVar("gallery/thumbmargin"); ?>, - <?php endif; ?> - <?php - $imgHeight = $block->getImageAttribute('product_page_image_medium', 'height') - ?: $block->getImageAttribute('product_page_image_medium', 'width') - ?> - <?php if ($imgHeight) : ?> - "height": <?= $block->escapeHtml($imgHeight); ?>, - <?php endif; ?> - <?php if ($block->getVar("gallery/transition/duration")) : ?> - "transitionduration": <?= $block->escapeHtml($block->getVar("gallery/transition/duration")) ?>, - <?php endif; ?> - "transition": "<?= $block->escapeHtml($block->getVar("gallery/transition/effect")) ?>", - <?php if (($block->getVar("gallery/navarrows"))) : ?> - "navarrows": <?= $block->escapeHtml($block->getVar("gallery/navarrows")) ?>, - <?php endif; ?> - "navtype": "<?= $block->escapeHtml($block->getVar("gallery/navtype")) ?>", - "navdir": "<?= $block->escapeHtml($block->getVar("gallery/navdir")) ?>" - }, - "fullscreen": { - "nav": "<?= $block->escapeHtml($block->getVar("gallery/fullscreen/nav")) ?>", - <?php if ($block->getVar("gallery/fullscreen/loop")) : ?> - "loop": <?= $block->escapeHtml($block->getVar("gallery/fullscreen/loop")) ?>, - <?php endif; ?> - "navdir": "<?= $block->escapeHtml($block->getVar("gallery/fullscreen/navdir")) ?>", - <?php if ($block->getVar("gallery/transition/navarrows")) : ?> - "navarrows": <?= $block->escapeHtml($block->getVar("gallery/fullscreen/navarrows")) ?>, - <?php endif; ?> - "navtype": "<?= $block->escapeHtml($block->getVar("gallery/fullscreen/navtype")) ?>", - <?php if ($block->getVar("gallery/fullscreen/arrows")) : ?> - "arrows": <?= $block->escapeHtml($block->getVar("gallery/fullscreen/arrows")) ?>, - <?php endif; ?> - <?php if (is_bool($block->getVar("gallery/fullscreen/caption"))) : ?> - <?php $showCaption = $block->getVar("gallery/fullscreen/caption") ? 'true' : 'false'; ?> - "showCaption": <?= /* @noEscape */ $showCaption ?>, - <?php endif; ?> - <?php if ($block->getVar("gallery/fullscreen/transition/duration")) : ?> - "transitionduration": <?= - $block->escapeHtml($block->getVar("gallery/fullscreen/transition/duration")) ?>, - <?php endif; ?> - "transition": "<?= $block->escapeHtml($block->getVar("gallery/fullscreen/transition/effect")) ?>" - }, + "options": <?= /* @noEscape */ $block->getGalleryOptions()->getOptionsJson() ?>, + "fullscreen": <?= /* @noEscape */ $block->getGalleryOptions()->getFSOptionsJson() ?>, "breakpoints": <?= /* @noEscape */ $block->getBreakpoints() ?> } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php index c97dc821913f9..eb08509a9e36d 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php @@ -12,6 +12,7 @@ use Magento\Framework\ObjectManagerInterface; use Magento\Framework\View\LayoutInterface; use Magento\TestFramework\Helper\Bootstrap; +use Magento\Catalog\Block\Product\View\GalleryOptions; class GalleryTest extends \PHPUnit\Framework\TestCase { @@ -41,10 +42,12 @@ public function testHtml() /** @var Gallery $block */ $block = $layout->createBlock(Gallery::class); $block->setData('product', $product); + $galleryoptions = $this->objectManager->get(GalleryOptions::class); + $block->setData('gallery_options', $galleryoptions); $block->setTemplate("Magento_Catalog::product/view/gallery.phtml"); $showCaption = $block->getVar('gallery/caption'); - self::assertContains('"showCaption": ' . $showCaption, $block->toHtml()); + self::assertContains('"showCaption":' . $showCaption, $block->toHtml()); } } From 4223d2b6e0b2f119d6e34c618a0dc78fbbecdcd1 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Tue, 9 Apr 2019 08:40:15 +0300 Subject: [PATCH 1096/1295] Magento should create log if an observer not implement ObserverInterface --- .../Event/Invoker/InvokerDefault.php | 30 ++++++++++++++++--- .../Test/Unit/Invoker/InvokerDefaultTest.php | 16 ++++++++-- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/lib/internal/Magento/Framework/Event/Invoker/InvokerDefault.php b/lib/internal/Magento/Framework/Event/Invoker/InvokerDefault.php index a7a387b5def81..acd0a61633557 100644 --- a/lib/internal/Magento/Framework/Event/Invoker/InvokerDefault.php +++ b/lib/internal/Magento/Framework/Event/Invoker/InvokerDefault.php @@ -9,7 +9,12 @@ namespace Magento\Framework\Event\Invoker; use Magento\Framework\Event\Observer; +use Psr\Log\LoggerInterface; +use Magento\Framework\App\State; +/** + * Default Invoker. + */ class InvokerDefault implements \Magento\Framework\Event\InvokerInterface { /** @@ -22,20 +27,29 @@ class InvokerDefault implements \Magento\Framework\Event\InvokerInterface /** * Application state * - * @var \Magento\Framework\App\State + * @var State */ protected $_appState; + /** + * @var LoggerInterface + */ + private $logger; + /** * @param \Magento\Framework\Event\ObserverFactory $observerFactory - * @param \Magento\Framework\App\State $appState + * @param State $appState + * @param LoggerInterface $logger */ public function __construct( \Magento\Framework\Event\ObserverFactory $observerFactory, - \Magento\Framework\App\State $appState + State $appState, + LoggerInterface $logger = null ) { $this->_observerFactory = $observerFactory; $this->_appState = $appState; + $this->logger = $logger ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(LoggerInterface::class); } /** @@ -61,6 +75,8 @@ public function dispatch(array $configuration, Observer $observer) } /** + * Execute Observer. + * * @param \Magento\Framework\Event\ObserverInterface $object * @param Observer $observer * @return $this @@ -70,7 +86,7 @@ protected function _callObserverMethod($object, $observer) { if ($object instanceof \Magento\Framework\Event\ObserverInterface) { $object->execute($observer); - } elseif ($this->_appState->getMode() == \Magento\Framework\App\State::MODE_DEVELOPER) { + } elseif ($this->_appState->getMode() == State::MODE_DEVELOPER) { throw new \LogicException( sprintf( 'Observer "%s" must implement interface "%s"', @@ -78,6 +94,12 @@ protected function _callObserverMethod($object, $observer) \Magento\Framework\Event\ObserverInterface::class ) ); + } else { + $this->logger->warning(sprintf( + 'Observer "%s" must implement interface "%s"', + get_class($object), + \Magento\Framework\Event\ObserverInterface::class + )); } return $this; } diff --git a/lib/internal/Magento/Framework/Event/Test/Unit/Invoker/InvokerDefaultTest.php b/lib/internal/Magento/Framework/Event/Test/Unit/Invoker/InvokerDefaultTest.php index 37f650dbef6a0..e6ec123823854 100644 --- a/lib/internal/Magento/Framework/Event/Test/Unit/Invoker/InvokerDefaultTest.php +++ b/lib/internal/Magento/Framework/Event/Test/Unit/Invoker/InvokerDefaultTest.php @@ -5,6 +5,9 @@ */ namespace Magento\Framework\Event\Test\Unit\Invoker; +/** + * Test for Magento\Framework\Event\Invoker\InvokerDefault. + */ class InvokerDefaultTest extends \PHPUnit\Framework\TestCase { /** @@ -32,6 +35,11 @@ class InvokerDefaultTest extends \PHPUnit\Framework\TestCase */ protected $_invokerDefault; + /** + * @var |Psr\Log|LoggerInterface + */ + private $loggerMock; + protected function setUp() { $this->_observerFactoryMock = $this->createMock(\Magento\Framework\Event\ObserverFactory::class); @@ -41,10 +49,12 @@ protected function setUp() ['execute'] ); $this->_appStateMock = $this->createMock(\Magento\Framework\App\State::class); + $this->loggerMock = $this->createMock(\Psr\Log\LoggerInterface::class); $this->_invokerDefault = new \Magento\Framework\Event\Invoker\InvokerDefault( $this->_observerFactoryMock, - $this->_appStateMock + $this->_appStateMock, + $this->loggerMock ); } @@ -166,13 +176,15 @@ public function testWrongInterfaceCallWithDisabledDeveloperMode($shared) $this->returnValue($notObserver) ); $this->_appStateMock->expects( - $this->once() + $this->exactly(1) )->method( 'getMode' )->will( $this->returnValue(\Magento\Framework\App\State::MODE_PRODUCTION) ); + $this->loggerMock->expects($this->once())->method('warning'); + $this->_invokerDefault->dispatch( [ 'shared' => $shared, From f3765f67a0c021304337cdcc578d19f30a5eee4d Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Tue, 9 Apr 2019 09:56:08 +0300 Subject: [PATCH 1097/1295] MAGETWO-74037: [Github] Product Attribute Option Values for storeview instead of admin on product creation #6507 --- .../Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml | 4 ++-- .../Test/AdminCheckingAttributeValueOnProductEditPageTest.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml index b1d45dae1dc0f..8ecae212b7c2d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml @@ -75,11 +75,11 @@ <requiredEntity type="StoreLabel">Option9Store0</requiredEntity> <requiredEntity type="StoreLabel">Option10Store1</requiredEntity> </entity> - <entity name="productAttributeAdminOption1" extends="productAttributeOption1"> + <entity name="ProductAttributeAdminOption1" extends="productAttributeOption1"> <requiredEntity type="StoreLabel">AdminOption1Store0</requiredEntity> <requiredEntity type="StoreLabel">Option1Store1</requiredEntity> </entity> - <entity name="productAttributeAdminOption2" extends="productAttributeOption2"> + <entity name="ProductAttributeAdminOption2" extends="productAttributeOption2"> <requiredEntity type="StoreLabel">AdminOption2Store0</requiredEntity> <requiredEntity type="StoreLabel">Option2Store1</requiredEntity> </entity> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckingAttributeValueOnProductEditPageTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckingAttributeValueOnProductEditPageTest.xml index 6b9bf9a5bff5b..59ac446d7a608 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckingAttributeValueOnProductEditPageTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckingAttributeValueOnProductEditPageTest.xml @@ -22,10 +22,10 @@ <!--Create Dropdown product attribute--> <createData entity="productAttributeWithDropdownTwoOptions" stepKey="createDropdownProductAttribute"/> <!--Add options to attribute--> - <createData entity="productAttributeAdminOption1" stepKey="createFirstOption"> + <createData entity="ProductAttributeAdminOption1" stepKey="createFirstOption"> <requiredEntity createDataKey="createDropdownProductAttribute"/> </createData> - <createData entity="productAttributeAdminOption2" stepKey="createSecondOption"> + <createData entity="ProductAttributeAdminOption2" stepKey="createSecondOption"> <requiredEntity createDataKey="createDropdownProductAttribute"/> </createData> <!--Add attribute to Default Attribute Set--> From 0f3304ee17cdeff802a6eb22227edd8a874b4656 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 9 Apr 2019 13:52:17 +0300 Subject: [PATCH 1098/1295] MC-15722: [FT] [MFTF] StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest fails --- ...tProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml index 9fee0302345d5..736b5e66558d0 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml @@ -52,6 +52,7 @@ <actionGroup ref="SwitchToTheNewStoreView" stepKey="switchToCustomStoreView"> <argument name="storeViewName" value="customStore"/> </actionGroup> + <scrollToTopOfPage stepKey="scrolToShowNameField"/> <click selector="{{AdminProductFormSection.productNameUseDefault}}" stepKey="uncheckUseDefault"/> <fillField selector="{{AdminProductFormSection.productName}}" userInput="$$createProduct.name$$-new" stepKey="fillProductName"/> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> From 40b6d35e2a143496d65a077f1a0dcdcb6e3e8cf5 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 9 Apr 2019 14:18:15 +0300 Subject: [PATCH 1099/1295] MAGETWO-99024: [FT] [MFTF] StorefrontCheckingConfigurableProductPriceWithMapTest fails on CE 2.2-develop --- .../Msrp/Test/Mftf/ActionGroup/AdminProductActionGroup.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Msrp/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Msrp/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index a53a7e34924ed..c8cdf8db42a7b 100644 --- a/app/code/Magento/Msrp/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Msrp/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -13,9 +13,9 @@ <argument name="msrp" type="string" defaultValue="100"/> </arguments> <amOnPage url="{{AdminProductEditPage.url(product.id)}}" stepKey="goToProductEditPage"/> - <waitForPageLoad stepKey="waitForProductEditPageLoad"/> + <scrollToTopOfPage stepKey="scrollToTopToSeeAdvancedPricingButton"/> <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickAdvancedPricingLink"/> - <waitForElement selector="{{AdminProductFormAdvancedPricingSection.msrp}}" stepKey="waitForMsrpElement"/> + <waitForElementVisible selector="{{AdminProductFormAdvancedPricingSection.msrp}}" stepKey="waitForMsrpElement"/> <fillField selector="{{AdminProductFormAdvancedPricingSection.msrp}}" userInput="{{msrp}}" stepKey="fillMsrpField"/> <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton"/> </actionGroup> From 66bacdb4129b0fc7beff62f517d06fe8b4e623b7 Mon Sep 17 00:00:00 2001 From: Denis Kopylov <dkopylov@magenius.team> Date: Tue, 9 Apr 2019 22:34:57 +0300 Subject: [PATCH 1100/1295] [PATCH] Fix gallery event observer --- lib/web/mage/gallery/gallery.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/mage/gallery/gallery.js b/lib/web/mage/gallery/gallery.js index 2ed3f8b0d620c..be78856b21fcd 100644 --- a/lib/web/mage/gallery/gallery.js +++ b/lib/web/mage/gallery/gallery.js @@ -141,7 +141,7 @@ define([ this.setupBreakpoints(); this.initFullscreenSettings(); - this.settings.$element.on('mousedown', '.fotorama__stage__frame', function () { + this.settings.$element.on('click', '.fotorama__stage__frame', function () { if ( !$(this).parents('.fotorama__shadows--left, .fotorama__shadows--right').length && !$(this).hasClass('fotorama-video-container') From fba3925a8da688a7881b31ce854ac00c2c3996af Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@adobe.com> Date: Tue, 9 Apr 2019 16:30:17 -0500 Subject: [PATCH 1101/1295] MAGETWO-99019: Magento\Install\Test\TestCase\InstallTest is failing on php 7.0 - revert MQE-1430: bug fix in dev/tests/functional/utils/command.php etc --- .../lib/Magento/Mtf/Util/Command/Cli.php | 41 +++--------- .../Mtf/Util/Command/File/Export/Reader.php | 38 +++-------- .../Command/File/Export/ReaderInterface.php | 2 +- .../lib/Magento/Mtf/Util/Command/File/Log.php | 38 +++-------- .../Mtf/Util/Command/GeneratedCode.php | 39 ++--------- .../lib/Magento/Mtf/Util/Command/Locales.php | 42 +++--------- .../Magento/Mtf/Util/Command/PathChecker.php | 43 +++--------- .../lib/Magento/Mtf/Util/Command/Website.php | 40 ++++------- .../CurlTransport/BackendDecorator.php | 67 +++++-------------- .../CurlTransport/WebapiDecorator.php | 32 +-------- dev/tests/functional/utils/authenticate.php | 29 -------- dev/tests/functional/utils/command.php | 26 +++---- .../utils/deleteMagentoGeneratedCode.php | 11 +-- dev/tests/functional/utils/export.php | 39 +++++------ dev/tests/functional/utils/locales.php | 26 +++---- dev/tests/functional/utils/log.php | 24 +++---- dev/tests/functional/utils/pathChecker.php | 17 ++--- dev/tests/functional/utils/website.php | 39 +++++------ 18 files changed, 156 insertions(+), 437 deletions(-) delete mode 100644 dev/tests/functional/utils/authenticate.php diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php index f0abd280f3ebc..8fa22122cce89 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php @@ -8,7 +8,6 @@ use Magento\Mtf\Util\Protocol\CurlInterface; use Magento\Mtf\Util\Protocol\CurlTransport; -use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * Perform bin/magento commands from command line for functional tests executions. @@ -18,7 +17,7 @@ class Cli /** * Url to command.php. */ - const URL = '/dev/tests/functional/utils/command.php'; + const URL = 'dev/tests/functional/utils/command.php'; /** * Curl transport protocol. @@ -27,21 +26,12 @@ class Cli */ private $transport; - /** - * Webapi handler. - * - * @var WebapiDecorator - */ - private $webapiHandler; - /** * @param CurlTransport $transport - * @param WebapiDecorator $webapiHandler */ - public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler) + public function __construct(CurlTransport $transport) { $this->transport = $transport; - $this->webapiHandler = $webapiHandler; } /** @@ -53,31 +43,22 @@ public function __construct(CurlTransport $transport, WebapiDecorator $webapiHan */ public function execute($command, $options = []) { - $this->transport->write( - rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, - $this->prepareParamArray($command, $options), - CurlInterface::POST, - [] - ); - $this->transport->read(); - $this->transport->close(); + $curl = $this->transport; + $curl->write($this->prepareUrl($command, $options), [], CurlInterface::GET); + $curl->read(); + $curl->close(); } /** - * Prepare parameter array. + * Prepare url. * * @param string $command * @param array $options [optional] - * @return array + * @return string */ - private function prepareParamArray($command, $options = []) + private function prepareUrl($command, $options = []) { - if (!empty($options)) { - $command .= ' ' . implode(' ', $options); - } - return [ - 'token' => urlencode($this->webapiHandler->getWebapiToken()), - 'command' => urlencode($command) - ]; + $command .= ' ' . implode(' ', $options); + return $_ENV['app_frontend_url'] . self::URL . '?command=' . urlencode($command); } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/Reader.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/Reader.php index 69df78a5cad64..1c05fbaebf625 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/Reader.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/Reader.php @@ -3,12 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Mtf\Util\Command\File\Export; use Magento\Mtf\ObjectManagerInterface; use Magento\Mtf\Util\Protocol\CurlTransport; use Magento\Mtf\Util\Protocol\CurlInterface; -use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * File reader for Magento export files. @@ -36,29 +36,16 @@ class Reader implements ReaderInterface */ private $transport; - /** - * Webapi handler. - * - * @var WebapiDecorator - */ - private $webapiHandler; - /** * @param ObjectManagerInterface $objectManager * @param CurlTransport $transport - * @param WebapiDecorator $webapiHandler * @param string $template */ - public function __construct( - ObjectManagerInterface $objectManager, - CurlTransport $transport, - WebapiDecorator $webapiHandler, - $template - ) { + public function __construct(ObjectManagerInterface $objectManager, CurlTransport $transport, $template) + { $this->objectManager = $objectManager; $this->template = $template; $this->transport = $transport; - $this->webapiHandler = $webapiHandler; } /** @@ -83,27 +70,20 @@ public function getData() */ private function getFiles() { - $this->transport->write( - rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, - $this->prepareParamArray(), - CurlInterface::POST, - [] - ); + $this->transport->write($this->prepareUrl(), [], CurlInterface::GET); $serializedFiles = $this->transport->read(); $this->transport->close(); + return unserialize($serializedFiles); } /** - * Prepare parameter array. + * Prepare url. * - * @return array + * @return string */ - private function prepareParamArray() + private function prepareUrl() { - return [ - 'token' => urlencode($this->webapiHandler->getWebapiToken()), - 'template' => urlencode($this->template) - ]; + return $_ENV['app_frontend_url'] . self::URL . '?template=' . urlencode($this->template); } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/ReaderInterface.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/ReaderInterface.php index 3666e8643efa3..93f7cf1ce9764 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/ReaderInterface.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/ReaderInterface.php @@ -14,7 +14,7 @@ interface ReaderInterface /** * Url to export.php. */ - const URL = '/dev/tests/functional/utils/export.php'; + const URL = 'dev/tests/functional/utils/export.php'; /** * Exporting files as Data object from Magento. diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Log.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Log.php index 820a5b0a82228..8b41924fe0a90 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Log.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Log.php @@ -7,7 +7,6 @@ namespace Magento\Mtf\Util\Command\File; use Magento\Mtf\Util\Protocol\CurlTransport; -use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * Get content of log file in var/log folder. @@ -17,7 +16,7 @@ class Log /** * Url to log.php. */ - const URL = '/dev/tests/functional/utils/log.php'; + const URL = 'dev/tests/functional/utils/log.php'; /** * Curl transport protocol. @@ -26,21 +25,12 @@ class Log */ private $transport; - /** - * Webapi handler. - * - * @var WebapiDecorator - */ - private $webapiHandler; - /** * @param CurlTransport $transport - * @param WebapiDecorator $webapiHandler */ - public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler) + public function __construct(CurlTransport $transport) { $this->transport = $transport; - $this->webapiHandler = $webapiHandler; } /** @@ -51,28 +41,22 @@ public function __construct(CurlTransport $transport, WebapiDecorator $webapiHan */ public function getFileContent($name) { - $this->transport->write( - rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, - $this->prepareParamArray($name), - CurlInterface::POST, - [] - ); - $data = $this->transport->read(); - $this->transport->close(); + $curl = $this->transport; + $curl->write($this->prepareUrl($name), [], CurlTransport::GET); + $data = $curl->read(); + $curl->close(); + return unserialize($data); } /** - * Prepare parameter array. + * Prepare url. * * @param string $name - * @return array + * @return string */ - private function prepareParamArray($name) + private function prepareUrl($name) { - return [ - 'token' => urlencode($this->webapiHandler->getWebapiToken()), - 'name' => urlencode($name) - ]; + return $_ENV['app_frontend_url'] . self::URL . '?name=' . urlencode($name); } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/GeneratedCode.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/GeneratedCode.php index a9fefa25ffa24..dde3409ed1562 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/GeneratedCode.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/GeneratedCode.php @@ -7,7 +7,6 @@ use Magento\Mtf\Util\Protocol\CurlInterface; use Magento\Mtf\Util\Protocol\CurlTransport; -use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * GeneratedCode removes generated code of Magento (like generated/code and generated/metadata). @@ -17,7 +16,7 @@ class GeneratedCode /** * Url to deleteMagentoGeneratedCode.php. */ - const URL = '/dev/tests/functional/utils/deleteMagentoGeneratedCode.php'; + const URL = 'dev/tests/functional/utils/deleteMagentoGeneratedCode.php'; /** * Curl transport protocol. @@ -26,21 +25,12 @@ class GeneratedCode */ private $transport; - /** - * Webapi handler. - * - * @var WebapiDecorator - */ - private $webapiHandler; - /** * @param CurlTransport $transport - * @param WebapiDecorator $webapiHandler */ - public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler) + public function __construct(CurlTransport $transport) { $this->transport = $transport; - $this->webapiHandler = $webapiHandler; } /** @@ -50,25 +40,10 @@ public function __construct(CurlTransport $transport, WebapiDecorator $webapiHan */ public function delete() { - $this->transport->write( - rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, - $this->prepareParamArray(), - CurlInterface::POST, - [] - ); - $this->transport->read(); - $this->transport->close(); - } - - /** - * Prepare parameter array. - * - * @return array - */ - private function prepareParamArray() - { - return [ - 'token' => urlencode($this->webapiHandler->getWebapiToken()) - ]; + $url = $_ENV['app_frontend_url'] . self::URL; + $curl = $this->transport; + $curl->write($url, [], CurlInterface::GET); + $curl->read(); + $curl->close(); } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Locales.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Locales.php index a55d803f43087..f669d91f2f2e5 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Locales.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Locales.php @@ -7,7 +7,6 @@ use Magento\Mtf\Util\Protocol\CurlInterface; use Magento\Mtf\Util\Protocol\CurlTransport; -use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * Returns array of locales depends on fetching type. @@ -27,7 +26,7 @@ class Locales /** * Url to locales.php. */ - const URL = '/dev/tests/functional/utils/locales.php'; + const URL = 'dev/tests/functional/utils/locales.php'; /** * Curl transport protocol. @@ -36,21 +35,12 @@ class Locales */ private $transport; - /** - * Webapi handler. - * - * @var WebapiDecorator - */ - private $webapiHandler; - /** * @param CurlTransport $transport Curl transport protocol - * @param WebapiDecorator $webapiHandler */ - public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler) + public function __construct(CurlTransport $transport) { $this->transport = $transport; - $this->webapiHandler = $webapiHandler; } /** @@ -61,28 +51,12 @@ public function __construct(CurlTransport $transport, WebapiDecorator $webapiHan */ public function getList($type = self::TYPE_ALL) { - $this->transport->write( - rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, - $this->prepareParamArray($type), - CurlInterface::POST, - [] - ); - $result = $this->transport->read(); - $this->transport->close(); - return explode('|', $result); - } + $url = $_ENV['app_frontend_url'] . self::URL . '?type=' . $type; + $curl = $this->transport; + $curl->write($url, [], CurlInterface::GET); + $result = $curl->read(); + $curl->close(); - /** - * Prepare parameter array. - * - * @param string $type - * @return array - */ - private function prepareParamArray($type) - { - return [ - 'token' => urlencode($this->webapiHandler->getWebapiToken()), - 'type' => urlencode($type) - ]; + return explode('|', $result); } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/PathChecker.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/PathChecker.php index 4b12f6eec87aa..fd1f746a6f09c 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/PathChecker.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/PathChecker.php @@ -7,7 +7,6 @@ use Magento\Mtf\Util\Protocol\CurlInterface; use Magento\Mtf\Util\Protocol\CurlTransport; -use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * PathChecker checks that path to file or directory exists. @@ -17,7 +16,7 @@ class PathChecker /** * Url to checkPath.php. */ - const URL = '/dev/tests/functional/utils/pathChecker.php'; + const URL = 'dev/tests/functional/utils/pathChecker.php'; /** * Curl transport protocol. @@ -27,21 +26,11 @@ class PathChecker private $transport; /** - * Webapi handler. - * - * @var WebapiDecorator - */ - private $webapiHandler; - - /** - * @constructor * @param CurlTransport $transport - * @param WebapiDecorator $webapiHandler */ - public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler) + public function __construct(CurlTransport $transport) { $this->transport = $transport; - $this->webapiHandler = $webapiHandler; } /** @@ -52,28 +41,12 @@ public function __construct(CurlTransport $transport, WebapiDecorator $webapiHan */ public function pathExists($path) { - $this->transport->write( - rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, - $this->prepareParamArray($path), - CurlInterface::POST, - [] - ); - $result = $this->transport->read(); - $this->transport->close(); - return strpos($result, 'path exists: true') !== false; - } + $url = $_ENV['app_frontend_url'] . self::URL . '?path=' . urlencode($path); + $curl = $this->transport; + $curl->write($url, [], CurlInterface::GET); + $result = $curl->read(); + $curl->close(); - /** - * Prepare parameter array. - * - * @param string $path - * @return array - */ - private function prepareParamArray($path) - { - return [ - 'token' => urlencode($this->webapiHandler->getWebapiToken()), - 'path' => urlencode($path) - ]; + return strpos($result, 'path exists: true') !== false; } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Website.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Website.php index fec20bb2a8715..7d73634c0360d 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Website.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Website.php @@ -3,11 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Mtf\Util\Command; use Magento\Mtf\Util\Protocol\CurlInterface; use Magento\Mtf\Util\Protocol\CurlTransport; -use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * Perform Website folder creation for functional tests executions. @@ -17,7 +17,7 @@ class Website /** * Url to website.php. */ - const URL = '/dev/tests/functional/utils/website.php'; + const URL = 'dev/tests/functional/utils/website.php'; /** * Curl transport protocol. @@ -26,22 +26,13 @@ class Website */ private $transport; - /** - * Webapi handler. - * - * @var WebapiDecorator - */ - private $webapiHandler; - /** * @constructor * @param CurlTransport $transport - * @param WebapiDecorator $webapiHandler */ - public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler) + public function __construct(CurlTransport $transport) { $this->transport = $transport; - $this->webapiHandler = $webapiHandler; } /** @@ -52,28 +43,21 @@ public function __construct(CurlTransport $transport, WebapiDecorator $webapiHan */ public function create($websiteCode) { - $this->transport->addOption(CURLOPT_HEADER, 1); - $this->transport->write( - rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, - $this->prepareParamArray($websiteCode), - CurlInterface::POST, - [] - ); - $this->transport->read(); - $this->transport->close(); + $curl = $this->transport; + $curl->addOption(CURLOPT_HEADER, 1); + $curl->write($this->prepareUrl($websiteCode), [], CurlInterface::GET); + $curl->read(); + $curl->close(); } /** - * Prepare parameter array. + * Prepare url. * * @param string $websiteCode - * @return array + * @return string */ - private function prepareParamArray($websiteCode) + private function prepareUrl($websiteCode) { - return [ - 'token' => urlencode($this->webapiHandler->getWebapiToken()), - 'website_code' => urlencode($websiteCode) - ]; + return $_ENV['app_frontend_url'] . self::URL . '?website_code=' . urlencode($websiteCode); } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php index 2fc26a975e159..ab333dc7c005a 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php @@ -63,56 +63,23 @@ public function __construct(CurlTransport $transport, DataInterface $configurati */ protected function authorize() { - // There are situations where magento application backend url could be slightly different from the environment - // variable we know. It could be intentionally (e.g. InstallTest) or unintentionally. We would still want tests - // to run in this case. - // When the original app_backend_url does not work, we will try 4 variants of the it. i.e. with and without - // url rewrite, http and https. - $urls = []; - $originalUrl = rtrim($_ENV['app_backend_url'], '/') . '/'; - $urls[] = $originalUrl; - // It could be the case that the page needs a refresh, so we will try the original one twice - $urls[] = $originalUrl; - if (strpos($originalUrl, '/index.php') !== false) { - $url2 = str_replace('/index.php', '', $originalUrl); - } else { - $url2 = $originalUrl . 'index.php/'; - } - $urls[] = $url2; - if (strpos($originalUrl, 'https') !== false) { - $urls[] = str_replace('https', 'http', $originalUrl); - } else { - $urls[] = str_replace('http', 'https', $url2); - } - - $isAuthorized = false; - foreach ($urls as $url) { - try { - // Perform GET to backend url so form_key is set - $this->transport->write($url, [], CurlInterface::GET); - $this->read(); - - $authUrl = $url . $this->configuration->get('application/0/backendLoginUrl/0/value'); - $data = [ - 'login[username]' => $this->configuration->get('application/0/backendLogin/0/value'), - 'login[password]' => $this->configuration->get('application/0/backendPassword/0/value'), - 'form_key' => $this->formKey, - ]; - - $this->transport->write($authUrl, $data, CurlInterface::POST); - $response = $this->read(); - if (strpos($response, 'login-form') !== false) { - continue; - } - $isAuthorized = true; - $_ENV['app_backend_url'] = $url; - break; - } catch (\Exception $e) { - continue; - } - } - if ($isAuthorized == false) { - throw new \Exception('Admin user cannot be logged in by curl handler!'); + // Perform GET to backend url so form_key is set + $url = $_ENV['app_backend_url']; + $this->transport->write($url, [], CurlInterface::GET); + $this->read(); + + $url = $_ENV['app_backend_url'] . $this->configuration->get('application/0/backendLoginUrl/0/value'); + $data = [ + 'login[username]' => $this->configuration->get('application/0/backendLogin/0/value'), + 'login[password]' => $this->configuration->get('application/0/backendPassword/0/value'), + 'form_key' => $this->formKey, + ]; + $this->transport->write($url, $data, CurlInterface::POST); + $response = $this->read(); + if (strpos($response, 'login-form')) { + throw new \Exception( + 'Admin user cannot be logged in by curl handler!' + ); } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/WebapiDecorator.php b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/WebapiDecorator.php index df5ab45a3f96d..3aa756904ab00 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/WebapiDecorator.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/WebapiDecorator.php @@ -70,13 +70,6 @@ class WebapiDecorator implements CurlInterface */ protected $response; - /** - * Webapi token. - * - * @var string - */ - protected $webapiToken; - /** * @construct * @param ObjectManager $objectManager @@ -117,9 +110,6 @@ protected function init() $integration->persist(); $this->setConfiguration($integration); - $this->webapiToken = $integration->getToken(); - } else { - $this->webapiToken = $integrationToken; } } @@ -171,13 +161,7 @@ protected function setConfiguration(Integration $integration) */ protected function isValidIntegration() { - $url = rtrim($_ENV['app_frontend_url'], '/'); - if (strpos($url, 'index.php') === false) { - $url .= '/index.php/rest/V1/modules'; - } else { - $url .= '/rest/V1/modules'; - } - $this->write($url, [], CurlInterface::GET); + $this->write($_ENV['app_frontend_url'] . 'rest/V1/modules', [], CurlInterface::GET); $response = json_decode($this->read(), true); return (null !== $response) && !isset($response['message']); @@ -235,18 +219,4 @@ public function close() { $this->transport->close(); } - - /** - * Return webapiToken. - * - * @return string - */ - public function getWebapiToken() - { - // Request token if integration is no longer valid - if (!$this->isValidIntegration()) { - $this->init(); - } - return $this->webapiToken; - } } diff --git a/dev/tests/functional/utils/authenticate.php b/dev/tests/functional/utils/authenticate.php deleted file mode 100644 index 15851f6e8000a..0000000000000 --- a/dev/tests/functional/utils/authenticate.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -/** - * Check if token passed in is a valid auth token. - * - * @param string $token - * @return bool - */ -function authenticate($token) -{ - require_once __DIR__ . '/../../../../app/bootstrap.php'; - - $magentoObjectManagerFactory = \Magento\Framework\App\Bootstrap::createObjectManagerFactory(BP, $_SERVER); - $magentoObjectManager = $magentoObjectManagerFactory->create($_SERVER); - $tokenModel = $magentoObjectManager->get(\Magento\Integration\Model\Oauth\Token::class); - - $tokenPassedIn = $token; - // Token returned will be null if the token we passed in is invalid - $tokenFromMagento = $tokenModel->loadByToken($tokenPassedIn)->getToken(); - if (!empty($tokenFromMagento) && ($tokenFromMagento == $tokenPassedIn)) { - return true; - } else { - return false; - } -} diff --git a/dev/tests/functional/utils/command.php b/dev/tests/functional/utils/command.php index e7b336464682d..8eaf82475a4e4 100644 --- a/dev/tests/functional/utils/command.php +++ b/dev/tests/functional/utils/command.php @@ -3,25 +3,21 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -include __DIR__ . '/authenticate.php'; + require_once __DIR__ . '/../../../../app/bootstrap.php'; use Symfony\Component\Console\Input\StringInput; use Symfony\Component\Console\Output\NullOutput; -if (!empty($_POST['token']) && !empty($_POST['command'])) { - if (authenticate(urldecode($_POST['token']))) { - $command = urldecode($_POST['command']); - $magentoObjectManagerFactory = \Magento\Framework\App\Bootstrap::createObjectManagerFactory(BP, $_SERVER); - $magentoObjectManager = $magentoObjectManagerFactory->create($_SERVER); - $cli = $magentoObjectManager->create(\Magento\Framework\Console\Cli::class); - $input = new StringInput($command); - $input->setInteractive(false); - $output = new NullOutput(); - $cli->doRun($input, $output); - } else { - echo "Command not unauthorized."; - } +if (isset($_GET['command'])) { + $command = urldecode($_GET['command']); + $magentoObjectManagerFactory = \Magento\Framework\App\Bootstrap::createObjectManagerFactory(BP, $_SERVER); + $magentoObjectManager = $magentoObjectManagerFactory->create($_SERVER); + $cli = $magentoObjectManager->create(\Magento\Framework\Console\Cli::class); + $input = new StringInput($command); + $input->setInteractive(false); + $output = new NullOutput(); + $cli->doRun($input, $output); } else { - echo "'token' or 'command' parameter is not set."; + throw new \InvalidArgumentException("Command GET parameter is not set."); } diff --git a/dev/tests/functional/utils/deleteMagentoGeneratedCode.php b/dev/tests/functional/utils/deleteMagentoGeneratedCode.php index 17e3575c87686..99aa9af06e92a 100644 --- a/dev/tests/functional/utils/deleteMagentoGeneratedCode.php +++ b/dev/tests/functional/utils/deleteMagentoGeneratedCode.php @@ -3,14 +3,5 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -include __DIR__ . '/authenticate.php'; -if (!empty($_POST['token']) && !empty($_POST['path'])) { - if (authenticate(urldecode($_POST['token']))) { - exec('rm -rf ../../../../generated/*'); - } else { - echo "Command not unauthorized."; - } -} else { - echo "'token' parameter is not set."; -} +exec('rm -rf ../../../../generated/*'); diff --git a/dev/tests/functional/utils/export.php b/dev/tests/functional/utils/export.php index 9357bfa459be0..343dcc557c832 100644 --- a/dev/tests/functional/utils/export.php +++ b/dev/tests/functional/utils/export.php @@ -3,30 +3,25 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -include __DIR__ . '/authenticate.php'; -if (!empty($_POST['token']) && !empty($_POST['template'])) { - if (authenticate(urldecode($_POST['token']))) { - $varDir = '../../../../var/'; - $template = urldecode($_POST['template']); - $fileList = scandir($varDir, SCANDIR_SORT_NONE); - $files = []; +if (!isset($_GET['template'])) { + throw new \InvalidArgumentException('Argument "template" must be set.'); +} - foreach ($fileList as $fileName) { - if (preg_match("`$template`", $fileName) === 1) { - $filePath = $varDir . $fileName; - $files[] = [ - 'content' => file_get_contents($filePath), - 'name' => $fileName, - 'date' => filectime($filePath), - ]; - } - } +$varDir = '../../../../var/'; +$template = urldecode($_GET['template']); +$fileList = scandir($varDir, SCANDIR_SORT_NONE); +$files = []; - echo serialize($files); - } else { - echo "Command not unauthorized."; +foreach ($fileList as $fileName) { + if (preg_match("`$template`", $fileName) === 1) { + $filePath = $varDir . $fileName; + $files[] = [ + 'content' => file_get_contents($filePath), + 'name' => $fileName, + 'date' => filectime($filePath), + ]; } -} else { - echo "'token' or 'template' parameter is not set."; } + +echo serialize($files); diff --git a/dev/tests/functional/utils/locales.php b/dev/tests/functional/utils/locales.php index a3b4ec05eed65..827b8b1b89448 100644 --- a/dev/tests/functional/utils/locales.php +++ b/dev/tests/functional/utils/locales.php @@ -3,23 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -include __DIR__ . '/authenticate.php'; -if (!empty($_POST['token'])) { - if (authenticate(urldecode($_POST['token']))) { - if ($_POST['type'] == 'deployed') { - $themePath = isset($_POST['theme_path']) ? $_POST['theme_path'] : 'adminhtml/Magento/backend'; - $directory = __DIR__ . '/../../../../pub/static/' . $themePath; - $locales = array_diff(scandir($directory), ['..', '.']); - } else { - require_once __DIR__ . DIRECTORY_SEPARATOR . 'bootstrap.php'; - $localeConfig = $magentoObjectManager->create(\Magento\Framework\Locale\Config::class); - $locales = $localeConfig->getAllowedLocales(); - } - echo implode('|', $locales); - } else { - echo "Command not unauthorized."; - } +if (isset($_GET['type']) && $_GET['type'] == 'deployed') { + $themePath = isset($_GET['theme_path']) ? $_GET['theme_path'] : 'adminhtml/Magento/backend'; + $directory = __DIR__ . '/../../../../pub/static/' . $themePath; + $locales = array_diff(scandir($directory), ['..', '.']); } else { - echo "'token' parameter is not set."; + require_once __DIR__ . DIRECTORY_SEPARATOR . 'bootstrap.php'; + $localeConfig = $magentoObjectManager->create(\Magento\Framework\Locale\Config::class); + $locales = $localeConfig->getAllowedLocales(); } + +echo implode('|', $locales); diff --git a/dev/tests/functional/utils/log.php b/dev/tests/functional/utils/log.php index 0d6f1fa2ac5cf..8f07d72e2a569 100644 --- a/dev/tests/functional/utils/log.php +++ b/dev/tests/functional/utils/log.php @@ -3,19 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -include __DIR__ . '/authenticate.php'; -if (!empty($_POST['token']) && !empty($_POST['name'])) { - if (authenticate(urldecode($_POST['token']))) { - $name = urldecode($_POST['name']); - if (preg_match('/\.\.(\\\|\/)/', $name)) { - throw new \InvalidArgumentException('Invalid log file name'); - } - - echo serialize(file_get_contents('../../../../var/log' . '/' . $name)); - } else { - echo "Command not unauthorized."; - } -} else { - echo "'token' or 'name' parameter is not set."; +if (!isset($_GET['name'])) { + throw new \InvalidArgumentException( + 'The name of log file is required for getting logs.' + ); +} +$name = urldecode($_GET['name']); +if (preg_match('/\.\.(\\\|\/)/', $name)) { + throw new \InvalidArgumentException('Invalid log file name'); } + +echo serialize(file_get_contents('../../../../var/log' .'/' .$name)); diff --git a/dev/tests/functional/utils/pathChecker.php b/dev/tests/functional/utils/pathChecker.php index b5a2ddb405bde..11f8229bce56f 100644 --- a/dev/tests/functional/utils/pathChecker.php +++ b/dev/tests/functional/utils/pathChecker.php @@ -3,20 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -include __DIR__ . '/authenticate.php'; -if (!empty($_POST['token']) && !empty($_POST['path'])) { - if (authenticate(urldecode($_POST['token']))) { - $path = urldecode($_POST['path']); +if (isset($_GET['path'])) { + $path = urldecode($_GET['path']); - if (file_exists('../../../../' . $path)) { - echo 'path exists: true'; - } else { - echo 'path exists: false'; - } + if (file_exists('../../../../' . $path)) { + echo 'path exists: true'; } else { - echo "Command not unauthorized."; + echo 'path exists: false'; } } else { - echo "'token' or 'path' parameter is not set."; + throw new \InvalidArgumentException("GET parameter 'path' is not set."); } diff --git a/dev/tests/functional/utils/website.php b/dev/tests/functional/utils/website.php index ab8e3742f55ae..625f5c6b483f8 100644 --- a/dev/tests/functional/utils/website.php +++ b/dev/tests/functional/utils/website.php @@ -3,35 +3,30 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -include __DIR__ . '/authenticate.php'; -if (!empty($_POST['token']) && !empty($_POST['website_code'])) { - if (authenticate(urldecode($_POST['token']))) { - $websiteCode = urldecode($_POST['website_code']); - $rootDir = '../../../../'; - $websiteDir = $rootDir . 'websites/' . $websiteCode . '/'; - $contents = file_get_contents($rootDir . 'index.php'); +if (!isset($_GET['website_code'])) { + throw new \Exception("website_code GET parameter is not set."); +} + +$websiteCode = urldecode($_GET['website_code']); +$rootDir = '../../../../'; +$websiteDir = $rootDir . 'websites/' . $websiteCode . '/'; +$contents = file_get_contents($rootDir . 'index.php'); - $websiteParam = <<<EOD +$websiteParam = <<<EOD \$params = \$_SERVER; \$params[\Magento\Store\Model\StoreManager::PARAM_RUN_CODE] = '$websiteCode'; \$params[\Magento\Store\Model\StoreManager::PARAM_RUN_TYPE] = 'website'; EOD; - $pattern = '`(try {.*?)(\/app\/bootstrap.*?}\n)(.*?)\$_SERVER`mis'; - $replacement = "$1/../..$2\n$websiteParam$3\$params"; +$pattern = '`(try {.*?)(\/app\/bootstrap.*?}\n)(.*?)\$_SERVER`mis'; +$replacement = "$1/../..$2\n$websiteParam$3\$params"; - $contents = preg_replace($pattern, $replacement, $contents); +$contents = preg_replace($pattern, $replacement, $contents); - $old = umask(0); - mkdir($websiteDir, 0760, true); - umask($old); +$old = umask(0); +mkdir($websiteDir, 0760, true); +umask($old); - copy($rootDir . '.htaccess', $websiteDir . '.htaccess'); - file_put_contents($websiteDir . 'index.php', $contents); - } else { - echo "Command not unauthorized."; - } -} else { - echo "'token' or 'website_code' parameter is not set."; -} +copy($rootDir . '.htaccess', $websiteDir . '.htaccess'); +file_put_contents($websiteDir . 'index.php', $contents); From 3d971376470986eb251bc5b2e96c1aa3caeb024c Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Wed, 10 Apr 2019 01:55:08 +0300 Subject: [PATCH 1102/1295] MAGETWO-97096: Multistore Allowed Countries List Problem --- .../Customer/Model/AccountManagement.php | 29 +++++++++++- app/code/Magento/Quote/Model/Quote.php | 47 +++++++++++++++++-- .../Backend/_files/allowed_countries_fr.php | 16 +++++++ .../_files/allowed_countries_fr_rollback.php | 16 +++++++ .../Customer/Model/AccountManagementTest.php | 45 ++++++++++++++++++ .../Magento/Quote/Model/QuoteTest.php | 20 ++++++++ 6 files changed, 169 insertions(+), 4 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Backend/_files/allowed_countries_fr.php create mode 100644 dev/tests/integration/testsuite/Magento/Backend/_files/allowed_countries_fr_rollback.php diff --git a/app/code/Magento/Customer/Model/AccountManagement.php b/app/code/Magento/Customer/Model/AccountManagement.php index e837517762473..16675731724fd 100644 --- a/app/code/Magento/Customer/Model/AccountManagement.php +++ b/app/code/Magento/Customer/Model/AccountManagement.php @@ -19,6 +19,7 @@ use Magento\Customer\Model\Data\Customer; use Magento\Customer\Model\Metadata\Validator; use Magento\Customer\Model\ResourceModel\Visitor\CollectionFactory; +use Magento\Directory\Model\AllowedCountries; use Magento\Eav\Model\Validator\Attribute\Backend; use Magento\Framework\Api\ExtensibleDataObjectConverter; use Magento\Framework\Api\SearchCriteriaBuilder; @@ -339,6 +340,11 @@ class AccountManagement implements AccountManagementInterface */ private $addressRegistry; + /** + * @var AllowedCountries + */ + private $allowedCountriesReader; + /** * @param CustomerFactory $customerFactory * @param ManagerInterface $eventManager @@ -371,6 +377,7 @@ class AccountManagement implements AccountManagementInterface * @param CollectionFactory|null $visitorCollectionFactory * @param SearchCriteriaBuilder|null $searchCriteriaBuilder * @param AddressRegistry|null $addressRegistry + * @param AllowedCountries|null $allowedCountriesReader * @SuppressWarnings(PHPMD.ExcessiveParameterList) * @SuppressWarnings(PHPMD.NPathComplexity) */ @@ -405,7 +412,8 @@ public function __construct( SaveHandlerInterface $saveHandler = null, CollectionFactory $visitorCollectionFactory = null, SearchCriteriaBuilder $searchCriteriaBuilder = null, - AddressRegistry $addressRegistry = null + AddressRegistry $addressRegistry = null, + AllowedCountries $allowedCountriesReader = null ) { $this->customerFactory = $customerFactory; $this->eventManager = $eventManager; @@ -445,6 +453,8 @@ public function __construct( ?: ObjectManager::getInstance()->get(SearchCriteriaBuilder::class); $this->addressRegistry = $addressRegistry ?: ObjectManager::getInstance()->get(AddressRegistry::class); + $this->allowedCountriesReader = $allowedCountriesReader + ?: ObjectManager::getInstance()->get(AllowedCountries::class); } /** @@ -882,6 +892,9 @@ public function createAccountWithPasswordHash( } try { foreach ($customerAddresses as $address) { + if (!$this->isAddressAllowedForWebsite($address, $customer->getStoreId())) { + continue; + } if ($address->getId()) { $newAddress = clone $address; $newAddress->setId(null); @@ -1572,4 +1585,18 @@ private function setIgnoreValidationFlag(Customer $customer) { $customer->setData('ignore_validation_flag', true); } + + /** + * Check is address allowed for store + * + * @param AddressInterface $address + * @param int|null $storeId + * @return bool + */ + private function isAddressAllowedForWebsite(AddressInterface $address, $storeId): bool + { + $allowedCountries = $this->allowedCountriesReader->getAllowedCountries(ScopeInterface::SCOPE_STORE, $storeId); + + return in_array($address->getCountryId(), $allowedCountries); + } } diff --git a/app/code/Magento/Quote/Model/Quote.php b/app/code/Magento/Quote/Model/Quote.php index 6ea70e63fdbf6..da21d5db77d97 100644 --- a/app/code/Magento/Quote/Model/Quote.php +++ b/app/code/Magento/Quote/Model/Quote.php @@ -7,6 +7,7 @@ use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Api\Data\GroupInterface; +use Magento\Directory\Model\AllowedCountries; use Magento\Framework\Api\AttributeValueFactory; use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface; use Magento\Framework\Model\AbstractExtensibleModel; @@ -14,6 +15,7 @@ use Magento\Quote\Model\Quote\Address; use Magento\Quote\Model\Quote\Address\Total as AddressTotal; use Magento\Sales\Model\Status; +use Magento\Store\Model\ScopeInterface; use Magento\Framework\App\ObjectManager; use Magento\Sales\Model\OrderIncrementIdChecker; @@ -360,6 +362,11 @@ class Quote extends AbstractExtensibleModel implements \Magento\Quote\Api\Data\C */ private $orderIncrementIdChecker; + /** + * @var AllowedCountries + */ + private $allowedCountriesReader; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -402,6 +409,7 @@ class Quote extends AbstractExtensibleModel implements \Magento\Quote\Api\Data\C * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection * @param array $data * @param OrderIncrementIdChecker|null $orderIncrementIdChecker + * @param AllowedCountries|null $allowedCountriesReader * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -445,7 +453,8 @@ public function __construct( \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [], - OrderIncrementIdChecker $orderIncrementIdChecker = null + OrderIncrementIdChecker $orderIncrementIdChecker = null, + AllowedCountries $allowedCountriesReader = null ) { $this->quoteValidator = $quoteValidator; $this->_catalogProduct = $catalogProduct; @@ -482,6 +491,8 @@ public function __construct( $this->shippingAssignmentFactory = $shippingAssignmentFactory; $this->orderIncrementIdChecker = $orderIncrementIdChecker ?: ObjectManager::getInstance() ->get(OrderIncrementIdChecker::class); + $this->allowedCountriesReader = $allowedCountriesReader + ?: ObjectManager::getInstance()->get(AllowedCountries::class); parent::__construct( $context, $registry, @@ -941,7 +952,7 @@ public function assignCustomerWithAddressChange( /** @var \Magento\Quote\Model\Quote\Address $billingAddress */ $billingAddress = $this->_quoteAddressFactory->create(); $billingAddress->importCustomerAddressData($defaultBillingAddress); - $this->setBillingAddress($billingAddress); + $this->assignAddress($billingAddress); } } @@ -959,7 +970,7 @@ public function assignCustomerWithAddressChange( $shippingAddress = $this->_quoteAddressFactory->create(); } } - $this->setShippingAddress($shippingAddress); + $this->assignAddress($shippingAddress, false); } return $this; @@ -2570,4 +2581,34 @@ public function setExtensionAttributes(\Magento\Quote\Api\Data\CartExtensionInte { return $this->_setExtensionAttributes($extensionAttributes); } + + /** + * Check is address allowed for store + * + * @param Address $address + * @param int|null $storeId + * @return bool + */ + private function isAddressAllowedForWebsite(Address $address, $storeId): bool + { + $allowedCountries = $this->allowedCountriesReader->getAllowedCountries(ScopeInterface::SCOPE_STORE, $storeId); + + return in_array($address->getCountryId(), $allowedCountries); + } + + /** + * Assign address to quote + * + * @param Address $address + * @param bool $isBillingAddress + * @return void + */ + private function assignAddress(Address $address, bool $isBillingAddress = true): void + { + if ($this->isAddressAllowedForWebsite($address, $this->getStoreId())) { + $isBillingAddress + ? $this->setBillingAddress($address) + : $this->setShippingAddress($address); + } + } } diff --git a/dev/tests/integration/testsuite/Magento/Backend/_files/allowed_countries_fr.php b/dev/tests/integration/testsuite/Magento/Backend/_files/allowed_countries_fr.php new file mode 100644 index 0000000000000..4bce8d95dafa6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Backend/_files/allowed_countries_fr.php @@ -0,0 +1,16 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types = 1); + +use Magento\Framework\App\Config\ConfigResource\ConfigInterface; +use Magento\Framework\App\Config\ReinitableConfigInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var ConfigInterface $config */ +$config = $objectManager->get(ConfigInterface::class); +$config->saveConfig('general/country/allow', 'FR'); +$objectManager->get(ReinitableConfigInterface::class)->reinit(); diff --git a/dev/tests/integration/testsuite/Magento/Backend/_files/allowed_countries_fr_rollback.php b/dev/tests/integration/testsuite/Magento/Backend/_files/allowed_countries_fr_rollback.php new file mode 100644 index 0000000000000..711d985786329 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Backend/_files/allowed_countries_fr_rollback.php @@ -0,0 +1,16 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types = 1); + +use Magento\Framework\App\Config\ConfigResource\ConfigInterface; +use Magento\Framework\App\Config\ReinitableConfigInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var ConfigInterface $config */ +$config = $objectManager->get(ConfigInterface::class); +$config->deleteConfig('general/country/allow'); +$objectManager->get(ReinitableConfigInterface::class)->reinit(); diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php index 2e132d27f5cb1..6c9bd115917d3 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php @@ -14,6 +14,7 @@ use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\State\ExpiredException; use Magento\Framework\Reflection\DataObjectProcessor; +use Magento\Store\Model\StoreManagerInterface; use Magento\TestFramework\Helper\Bootstrap; /** @@ -53,6 +54,9 @@ class AccountManagementTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Framework\Api\ExtensibleDataObjectConverter */ private $extensibleDataObjectConverter; + /** @var StoreManagerInterface */ + private $storeManager; + /** @var \Magento\Framework\Api\DataObjectHelper */ protected $dataObjectHelper; @@ -114,6 +118,9 @@ protected function setUp() $this->extensibleDataObjectConverter = $this->objectManager ->create(\Magento\Framework\Api\ExtensibleDataObjectConverter::class); + + $this->storeManager = $this->objectManager + ->create(StoreManagerInterface::class); } /** @@ -850,6 +857,44 @@ public function testCreateNewCustomerWithPasswordHash() ); } + /** + * Customer has two addresses one of it is allowed in website and second is not + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php + * @magentoDataFixture Magento/Store/_files/websites_different_countries.php + * @magentoConfigFixture fixture_second_store_store general/country/allow UA + * @return void + */ + public function testCreateNewCustomerWithPasswordHashWithNotAllowedCountry() + { + $customerId = 1; + $allowedCountryIdForSecondWebsite = 'UA'; + $store = $this->storeManager->getStore('fixture_second_store'); + $customerData = $this->customerRepository->getById($customerId); + $customerData->getAddresses()[1]->setRegion(null)->setCountryId($allowedCountryIdForSecondWebsite) + ->setRegionId(null); + $customerData->setStoreId($store->getId())->setWebsiteId($store->getWebsiteId())->setId(null); + $encryptor = $this->objectManager->get(\Magento\Framework\Encryption\EncryptorInterface::class); + /** @var \Magento\Framework\Math\Random $mathRandom */ + $password = $this->objectManager->get(\Magento\Framework\Math\Random::class)->getRandomString(8); + $passwordHash = $encryptor->getHash($password, true); + $savedCustomer = $this->accountManagement->createAccountWithPasswordHash( + $customerData, + $passwordHash + ); + $this->assertCount( + 1, + $savedCustomer->getAddresses(), + 'The wrong address quantity was saved' + ); + $this->assertSame( + 'UA', + $savedCustomer->getAddresses()[0]->getCountryId(), + 'The address with the disallowed country was saved' + ); + } + /** * @magentoAppArea frontend * @magentoDataFixture Magento/Customer/_files/customer.php diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php index 3a516befc37ff..ce5180710728f 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php @@ -311,6 +311,26 @@ public function testAssignCustomerWithAddressChange() } } + /** + * Customer has address with country which not allowed in website + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + * @magentoDataFixture Magento/Backend/_files/allowed_countries_fr.php + * @return void + */ + public function testAssignCustomerWithAddressChangeWithNotAllowedCountry() + { + /** @var Quote $quote */ + $quote = $this->objectManager->create(Quote::class); + $customerData = $this->_prepareQuoteForTestAssignCustomerWithAddressChange($quote); + $quote->assignCustomerWithAddressChange($customerData); + + /** Check that addresses are empty */ + $this->assertNull($quote->getBillingAddress()->getCountryId()); + $this->assertNull($quote->getShippingAddress()->getCountryId()); + } + /** * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php */ From 0a8f8e2fe2f0f6bcdbe4909f35e247fa196ea7c6 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Tue, 9 Apr 2019 13:22:31 -0500 Subject: [PATCH 1103/1295] MAGETWO-98991: Orders in PayPal, but not created in Magento due to missing credit card exp_month - Expiration month and year read from PayPal response --- .../Payflow/Service/Response/Transaction.php | 52 ++++++- .../Controller/Transparent/ResponseTest.php | 135 ++++++++++++++++++ 2 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 dev/tests/integration/testsuite/Magento/Paypal/Controller/Transparent/ResponseTest.php diff --git a/app/code/Magento/Paypal/Model/Payflow/Service/Response/Transaction.php b/app/code/Magento/Paypal/Model/Payflow/Service/Response/Transaction.php index 06a8a5b680bf4..259f00ec5a9c5 100644 --- a/app/code/Magento/Paypal/Model/Payflow/Service/Response/Transaction.php +++ b/app/code/Magento/Paypal/Model/Payflow/Service/Response/Transaction.php @@ -3,9 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Paypal\Model\Payflow\Service\Response; use Magento\Framework\DataObject; +use Magento\Framework\Intl\DateTimeFactory; use Magento\Payment\Model\Method\Logger; use Magento\Paypal\Model\Payflow\Service\Response\Handler\HandlerInterface; use Magento\Framework\Session\Generic; @@ -18,6 +21,8 @@ /** * Class Transaction + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Transaction { @@ -51,6 +56,11 @@ class Transaction */ private $logger; + /** + * @var DateTimeFactory + */ + private $dateTimeFactory; + /** * @param Generic $sessionTransparent * @param CartRepositoryInterface $quoteRepository @@ -58,6 +68,7 @@ class Transaction * @param PaymentMethodManagementInterface $paymentManagement * @param HandlerInterface $errorHandler * @param Logger $logger + * @param DateTimeFactory $dateTimeFactory */ public function __construct( Generic $sessionTransparent, @@ -65,7 +76,8 @@ public function __construct( Transparent $transparent, PaymentMethodManagementInterface $paymentManagement, HandlerInterface $errorHandler, - Logger $logger + Logger $logger, + DateTimeFactory $dateTimeFactory ) { $this->sessionTransparent = $sessionTransparent; $this->quoteRepository = $quoteRepository; @@ -73,6 +85,7 @@ public function __construct( $this->paymentManagement = $paymentManagement; $this->errorHandler = $errorHandler; $this->logger = $logger; + $this->dateTimeFactory = $dateTimeFactory; } /** @@ -114,8 +127,45 @@ public function savePaymentInQuote($response) $payment->setData(OrderPaymentInterface::CC_TYPE, $response->getData(OrderPaymentInterface::CC_TYPE)); $payment->setAdditionalInformation(Payflowpro::PNREF, $response->getData(Payflowpro::PNREF)); + $expDate = $response->getData('expdate'); + $expMonth = $this->getCcExpMonth($expDate); + $payment->setCcExpMonth($expMonth); + $expYear = $this->getCcExpYear($expDate); + $payment->setCcExpYear($expYear); + $this->errorHandler->handle($payment, $response); $this->paymentManagement->set($quote->getId(), $payment); } + + /** + * Extracts expiration month from PayPal response expiration date. + * + * @param string $expDate format {MMYY} + * @return int + */ + private function getCcExpMonth(string $expDate): int + { + return (int)substr($expDate, 0, 2); + } + + /** + * Extracts expiration year from PayPal response expiration date. + * + * @param string $expDate format {MMYY} + * @return int + */ + private function getCcExpYear(string $expDate): int + { + $last2YearDigits = (int)substr($expDate, 2, 2); + $currentDate = $this->dateTimeFactory->create('now', new \DateTimeZone('UTC')); + $first2YearDigits = (int)substr($currentDate->format('Y'), 0, 2); + + // case when credit card expires at next century + if ((int)$currentDate->format('y') > $last2YearDigits) { + $first2YearDigits++; + } + + return 100 * $first2YearDigits + $last2YearDigits; + } } diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Controller/Transparent/ResponseTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Controller/Transparent/ResponseTest.php new file mode 100644 index 0000000000000..e2bb1d7b8f7c6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Paypal/Controller/Transparent/ResponseTest.php @@ -0,0 +1,135 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Paypal\Controller\Transparent; + +use Magento\Checkout\Model\Session; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Intl\DateTimeFactory; +use Magento\Framework\Session\Generic as GenericSession; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\Data\CartInterface; +use Magento\Quote\Api\PaymentMethodManagementInterface; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Tests PayPal transparent response controller. + */ +class ResponseTest extends AbstractController +{ + /** + * Tests setting credit card expiration month and year to payment from PayPal response. + * + * @param string $currentDateTime + * @param string $paypalExpDate + * @param string $expectedCcMonth + * @param string $expectedCcYear + * @throws NoSuchEntityException + * + * @magentoConfigFixture current_store payment/payflowpro/active 1 + * @magentoDataFixture Magento/Sales/_files/quote.php + * @dataProvider paymentCcExpirationDateDataProvider + */ + public function testPaymentCcExpirationDate( + string $currentDateTime, + string $paypalExpDate, + string $expectedCcMonth, + string $expectedCcYear + ) { + $reservedOrderId = 'test01'; + $postData = [ + 'EXPDATE' => $paypalExpDate, + 'AMT' => '0.00', + 'RESPMSG' => 'Verified', + 'CVV2MATCH' => 'Y', + 'PNREF' => 'A10AAD866C87', + 'SECURETOKEN' => '3HYEHfG06skydAdBXbpIl8QJZ', + 'AVSDATA' => 'YNY', + 'RESULT' => '0', + 'IAVS' => 'N', + 'AVSADDR' => 'Y', + 'SECURETOKENID' => 'yqanLisRZbI0HAG8q3SbbKbhiwjNZAGf', + ]; + + $quote = $this->getQuote($reservedOrderId); + $this->getRequest()->setPostValue($postData); + + /** @var Session $checkoutSession */ + $checkoutSession = $this->_objectManager->get(GenericSession::class); + $checkoutSession->setQuoteId($quote->getId()); + $this->setCurrentDateTime($currentDateTime); + + $this->dispatch('paypal/transparent/response'); + + /** @var PaymentMethodManagementInterface $paymentManagment */ + $paymentManagment = $this->_objectManager->get(PaymentMethodManagementInterface::class); + $payment = $paymentManagment->get($quote->getId()); + + $this->assertEquals($expectedCcMonth, $payment->getCcExpMonth()); + $this->assertEquals($expectedCcYear, $payment->getCcExpYear()); + } + + /** + * @return array + */ + public function paymentCcExpirationDateDataProvider(): array + { + return [ + 'Expiration year in current century' => [ + 'currentDateTime' => '2019-07-05 00:00:00', + 'paypalExpDate' => '0321', + 'expectedCcMonth' => 3, + 'expectedCcYear' => 2021 + ], + 'Expiration year in next century' => [ + 'currentDateTime' => '2099-01-01 00:00:00', + 'paypalExpDate' => '1002', + 'expectedCcMonth' => 10, + 'expectedCcYear' => 2102 + ] + ]; + } + + /** + * Sets current date and time. + * + * @param string $date + */ + private function setCurrentDateTime(string $dateTime) + { + $dateTime = new \DateTime($dateTime, new \DateTimeZone('UTC')); + $dateTimeFactory = $this->getMockBuilder(DateTimeFactory::class) + ->disableOriginalConstructor() + ->getMock(); + + $dateTimeFactory->method('create') + ->willReturn($dateTime); + + $this->_objectManager->addSharedInstance($dateTimeFactory, DateTimeFactory::class); + } + + /** + * Gets quote by reserved order ID. + * + * @param string $reservedOrderId + * @return CartInterface + */ + private function getQuote(string $reservedOrderId): CartInterface + { + $searchCriteria = $this->_objectManager->get(SearchCriteriaBuilder::class) + ->addFilter('reserved_order_id', $reservedOrderId) + ->create(); + + /** @var CartRepositoryInterface $quoteRepository */ + $quoteRepository = $this->_objectManager->get(CartRepositoryInterface::class); + $items = $quoteRepository->getList($searchCriteria) + ->getItems(); + + return array_pop($items); + } +} From 7e3c1beafa300df08ea74487a60d7d18d8ae51f9 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Wed, 10 Apr 2019 03:22:07 +0300 Subject: [PATCH 1104/1295] MAGETWO-97096: Multistore Allowed Countries List Problem --- app/code/Magento/Quote/Model/Quote.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Quote/Model/Quote.php b/app/code/Magento/Quote/Model/Quote.php index da21d5db77d97..064b0c60e199c 100644 --- a/app/code/Magento/Quote/Model/Quote.php +++ b/app/code/Magento/Quote/Model/Quote.php @@ -2603,7 +2603,7 @@ private function isAddressAllowedForWebsite(Address $address, $storeId): bool * @param bool $isBillingAddress * @return void */ - private function assignAddress(Address $address, bool $isBillingAddress = true): void + private function assignAddress(Address $address, bool $isBillingAddress = true) { if ($this->isAddressAllowedForWebsite($address, $this->getStoreId())) { $isBillingAddress From d41773877558c955cb8acbaa21a44209fd8de222 Mon Sep 17 00:00:00 2001 From: Hiren Pandya <43466338+hiren0241@users.noreply.github.com> Date: Wed, 10 Apr 2019 10:22:40 +0530 Subject: [PATCH 1105/1295] Remove unnecessary js dependancy --- .../Tax/view/frontend/web/js/view/checkout/summary/tax.js | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Tax/view/frontend/web/js/view/checkout/summary/tax.js b/app/code/Magento/Tax/view/frontend/web/js/view/checkout/summary/tax.js index 4b10fe86015dc..2b1f387f5c8c4 100644 --- a/app/code/Magento/Tax/view/frontend/web/js/view/checkout/summary/tax.js +++ b/app/code/Magento/Tax/view/frontend/web/js/view/checkout/summary/tax.js @@ -12,7 +12,6 @@ define([ 'Magento_Checkout/js/view/summary/abstract-total', 'Magento_Checkout/js/model/quote', 'Magento_Checkout/js/model/totals', - 'jquery', 'mage/translate', 'underscore' ], function (ko, Component, quote, totals, $t, _) { From bd0af5333b94abda89ef6989c369c4c3817224d8 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Wed, 10 Apr 2019 09:18:46 +0300 Subject: [PATCH 1106/1295] MAGETWO-99006: JS errors prevent Adding or Edditing CMS pages in IE11 --- .../Magento/Ui/view/base/web/js/form/element/wysiwyg.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/wysiwyg.js b/app/code/Magento/Ui/view/base/web/js/form/element/wysiwyg.js index 547e6cde59839..b25bdc165ccba 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/wysiwyg.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/wysiwyg.js @@ -18,16 +18,16 @@ define([ return Abstract.extend({ defaults: { elementSelector: 'textarea', - suffixRegExpPattern: '\\${ \\$.wysiwygUniqueSuffix }', + suffixRegExpPattern: '${ $.wysiwygUniqueSuffix }', $wysiwygEditorButton: '', links: { value: '${ $.provider }:${ $.dataScope }' }, template: 'ui/form/field', elementTmpl: 'ui/form/element/wysiwyg', - content: '', - showSpinner: false, - loading: false, + content: '', + showSpinner: false, + loading: false, listens: { disabled: 'setDisabled' } @@ -56,6 +56,7 @@ define([ initConfig: function (config) { var pattern = config.suffixRegExpPattern || this.constructor.defaults.suffixRegExpPattern; + pattern = pattern.replace(/\$/g, '\\$&'); config.content = config.content.replace(new RegExp(pattern, 'g'), this.getUniqueSuffix(config)); this._super(); From 179bedf3de99244b384c97cf7b2be106967f47cf Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Wed, 10 Apr 2019 16:51:47 +0300 Subject: [PATCH 1107/1295] magento2-22238: removed backward incompatible change from the options block --- .../Magento/Catalog/Block/Product/View/Options/Type/Select.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Block/Product/View/Options/Type/Select.php b/app/code/Magento/Catalog/Block/Product/View/Options/Type/Select.php index 429e8fb44316d..52de7939d7eb2 100644 --- a/app/code/Magento/Catalog/Block/Product/View/Options/Type/Select.php +++ b/app/code/Magento/Catalog/Block/Product/View/Options/Type/Select.php @@ -59,7 +59,7 @@ public function __construct( * * @return string */ - public function getValuesHtml(): string + public function getValuesHtml() { $option = $this->getOption(); $optionType = $option->getType(); From 5a00e8b7dbf2233fd79accca134e7e93c7d81677 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Wed, 10 Apr 2019 20:54:01 +0300 Subject: [PATCH 1108/1295] MAGETWO-97096: Multistore Allowed Countries List Problem --- .../Customer/Model/AccountManagement.php | 1 + .../Customer/Model/AccountManagementTest.php | 76 +++++++++---------- 2 files changed, 39 insertions(+), 38 deletions(-) diff --git a/app/code/Magento/Customer/Model/AccountManagement.php b/app/code/Magento/Customer/Model/AccountManagement.php index 16675731724fd..379b4979e2070 100644 --- a/app/code/Magento/Customer/Model/AccountManagement.php +++ b/app/code/Magento/Customer/Model/AccountManagement.php @@ -380,6 +380,7 @@ class AccountManagement implements AccountManagementInterface * @param AllowedCountries|null $allowedCountriesReader * @SuppressWarnings(PHPMD.ExcessiveParameterList) * @SuppressWarnings(PHPMD.NPathComplexity) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function __construct( CustomerFactory $customerFactory, diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php index 6c9bd115917d3..507150029c0c6 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php @@ -857,44 +857,6 @@ public function testCreateNewCustomerWithPasswordHash() ); } - /** - * Customer has two addresses one of it is allowed in website and second is not - * - * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php - * @magentoDataFixture Magento/Store/_files/websites_different_countries.php - * @magentoConfigFixture fixture_second_store_store general/country/allow UA - * @return void - */ - public function testCreateNewCustomerWithPasswordHashWithNotAllowedCountry() - { - $customerId = 1; - $allowedCountryIdForSecondWebsite = 'UA'; - $store = $this->storeManager->getStore('fixture_second_store'); - $customerData = $this->customerRepository->getById($customerId); - $customerData->getAddresses()[1]->setRegion(null)->setCountryId($allowedCountryIdForSecondWebsite) - ->setRegionId(null); - $customerData->setStoreId($store->getId())->setWebsiteId($store->getWebsiteId())->setId(null); - $encryptor = $this->objectManager->get(\Magento\Framework\Encryption\EncryptorInterface::class); - /** @var \Magento\Framework\Math\Random $mathRandom */ - $password = $this->objectManager->get(\Magento\Framework\Math\Random::class)->getRandomString(8); - $passwordHash = $encryptor->getHash($password, true); - $savedCustomer = $this->accountManagement->createAccountWithPasswordHash( - $customerData, - $passwordHash - ); - $this->assertCount( - 1, - $savedCustomer->getAddresses(), - 'The wrong address quantity was saved' - ); - $this->assertSame( - 'UA', - $savedCustomer->getAddresses()[0]->getCountryId(), - 'The address with the disallowed country was saved' - ); - } - /** * @magentoAppArea frontend * @magentoDataFixture Magento/Customer/_files/customer.php @@ -1073,4 +1035,42 @@ protected function setResetPasswordData( $customerModel->setRpTokenCreatedAt(date($date)); $customerModel->save(); } + + /** + * Customer has two addresses one of it is allowed in website and second is not + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php + * @magentoDataFixture Magento/Store/_files/websites_different_countries.php + * @magentoConfigFixture fixture_second_store_store general/country/allow UA + * @return void + */ + public function testCreateNewCustomerWithPasswordHashWithNotAllowedCountry() + { + $customerId = 1; + $allowedCountryIdForSecondWebsite = 'UA'; + $store = $this->storeManager->getStore('fixture_second_store'); + $customerData = $this->customerRepository->getById($customerId); + $customerData->getAddresses()[1]->setRegion(null)->setCountryId($allowedCountryIdForSecondWebsite) + ->setRegionId(null); + $customerData->setStoreId($store->getId())->setWebsiteId($store->getWebsiteId())->setId(null); + $encryptor = $this->objectManager->get(\Magento\Framework\Encryption\EncryptorInterface::class); + /** @var \Magento\Framework\Math\Random $mathRandom */ + $password = $this->objectManager->get(\Magento\Framework\Math\Random::class)->getRandomString(8); + $passwordHash = $encryptor->getHash($password, true); + $savedCustomer = $this->accountManagement->createAccountWithPasswordHash( + $customerData, + $passwordHash + ); + $this->assertCount( + 1, + $savedCustomer->getAddresses(), + 'The wrong address quantity was saved' + ); + $this->assertSame( + 'UA', + $savedCustomer->getAddresses()[0]->getCountryId(), + 'The address with the disallowed country was saved' + ); + } } From e12eeaac9a1892bab43b8ab7083e03922da3b2ab Mon Sep 17 00:00:00 2001 From: David Alger <davidmalger@gmail.com> Date: Wed, 10 Apr 2019 15:58:26 -0500 Subject: [PATCH 1109/1295] Backport #22281 (commit 8434d74abd6) fixing #15090 on 2.2 allowing admin/url/use_custom in env.php --- .../Magento/Config/Model/Config/Backend/Admin/Usecustom.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Config/Model/Config/Backend/Admin/Usecustom.php b/app/code/Magento/Config/Model/Config/Backend/Admin/Usecustom.php index 9a483de6a695b..d12569eebe5b2 100644 --- a/app/code/Magento/Config/Model/Config/Backend/Admin/Usecustom.php +++ b/app/code/Magento/Config/Model/Config/Backend/Admin/Usecustom.php @@ -56,8 +56,9 @@ public function beforeSave() { $value = $this->getValue(); if ($value == 1) { - $customUrl = $this->getData('groups/url/fields/custom/value'); - if (empty($customUrl)) { + $customUrlField = $this->getData('groups/url/fields/custom/value'); + $customUrlConfig = $this->_config->getValue('admin/url/custom'); + if (empty($customUrlField) && empty($customUrlConfig)) { throw new \Magento\Framework\Exception\LocalizedException(__('Please specify the admin custom URL.')); } } From 0c5f027a0481dd5f2873aa58f83e2375e14cd4cd Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Thu, 11 Apr 2019 10:16:27 +0300 Subject: [PATCH 1110/1295] MAGETWO-73157: [GITHUB] Import customizable options adds it to another product if same SKU is filled#9457 --- .../Model/Product/Option/SaveHandler.php | 2 ++ .../ActionGroup/CustomOptionsActionGroup.xml | 17 ++++++++--------- .../Catalog/Test/Mftf/Data/ProductData.xml | 5 +++++ .../Test/Mftf/Data/ProductOptionData.xml | 2 ++ .../AdminProductCustomizableOptionsSection.xml | 12 +++++++----- ...rtCustomizableOptionToProductWithSKUTest.xml | 13 +------------ ...figurableProductWithFileCustomOptionTest.xml | 1 - ...ontSwatchProductWithFileCustomOptionTest.xml | 1 - 8 files changed, 25 insertions(+), 28 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php b/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php index bb3fe2880a39f..c55df92ed6dfd 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Model\Product\Option; use Magento\Catalog\Api\ProductCustomOptionRepositoryInterface as OptionRepository; diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml index e248fc4f146d2..f157808c0045a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml @@ -10,7 +10,6 @@ <actionGroup name="AddProductCustomOption"> <arguments> <argument name="option"/> - <argument name="optionIndex" type="string"/> </arguments> <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" visible="false" stepKey="openCustomOptionSection"/> <click selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" stepKey="clickAddOption"/> @@ -18,14 +17,14 @@ <fillField selector="{{AdminProductCustomizableOptionsSection.lastOptionTitle}}" userInput="{{option.title}}" stepKey="fillTitle"/> <click selector="{{AdminProductCustomizableOptionsSection.lastOptionTypeParent}}" stepKey="openTypeSelect"/> <click selector="{{AdminProductCustomizableOptionsSection.optionType(option.type_label)}}" stepKey="selectTypeFile"/> - <waitForElementVisible selector="{{AdminProductCustomizableOptionsSection.optionPrice(optionIndex)}}" stepKey="waitForElements"/> - <fillField selector="{{AdminProductCustomizableOptionsSection.optionPrice(optionIndex)}}" userInput="{{option.price}}" stepKey="fillPrice"/> - <selectOption selector="{{AdminProductCustomizableOptionsSection.optionPriceType(optionIndex)}}" userInput="{{option.price_type}}" stepKey="selectPriceType"/> - <fillField selector="{{AdminProductCustomizableOptionsSection.optionSku(optionIndex)}}" userInput="{{option.title}}" stepKey="fillSku"/> + <waitForElementVisible selector="{{AdminProductCustomizableOptionsSection.optionPrice}}" stepKey="waitForElements"/> + <fillField selector="{{AdminProductCustomizableOptionsSection.optionPrice}}" userInput="{{option.price}}" stepKey="fillPrice"/> + <selectOption selector="{{AdminProductCustomizableOptionsSection.optionPriceType}}" userInput="{{option.price_type}}" stepKey="selectPriceType"/> + <fillField selector="{{AdminProductCustomizableOptionsSection.optionSku}}" userInput="{{option.title}}" stepKey="fillSku"/> </actionGroup> <!--Add a custom option of type "file" to a product--> <actionGroup name="AddProductCustomOptionFile" extends="AddProductCustomOption"> - <fillField selector="{{AdminProductCustomizableOptionsSection.optionFileExtensions(optionIndex)}}" userInput="{{option.file_extension}}" after="fillSku" stepKey="fillCompatibleExtensions"/> + <fillField selector="{{AdminProductCustomizableOptionsSection.optionFileExtensions}}" userInput="{{option.file_extension}}" after="fillSku" stepKey="fillCompatibleExtensions"/> </actionGroup> <actionGroup name="ImportProductCustomizableOptions"> <arguments> @@ -46,9 +45,9 @@ <argument name="option"/> <argument name="optionIndex" type="string"/> </arguments> - <grabValueFrom selector="{{AdminProductCustomizableOptionsSection.optionTitleInput(optionIndex)}}" stepKey="grabOptionTitle"/> - <grabValueFrom selector="{{AdminProductCustomizableOptionsSection.optionPrice(optionIndex)}}" stepKey="grabOptionPrice"/> - <grabValueFrom selector="{{AdminProductCustomizableOptionsSection.optionSku(optionIndex)}}" stepKey="grabOptionSku"/> + <grabValueFrom selector="{{AdminProductCustomizableOptionsSection.optionTitleInputByIndex(optionIndex)}}" stepKey="grabOptionTitle"/> + <grabValueFrom selector="{{AdminProductCustomizableOptionsSection.optionPriceByIndex(optionIndex)}}" stepKey="grabOptionPrice"/> + <grabValueFrom selector="{{AdminProductCustomizableOptionsSection.optionSkuByIndex(optionIndex)}}" stepKey="grabOptionSku"/> <assertEquals expected="{{option.title}}" expectedType="string" actual="$grabOptionTitle" stepKey="assertOptionTitle"/> <assertEquals expected="{{option.price}}" expectedType="string" actual="$grabOptionPrice" stepKey="assertOptionPrice"/> <assertEquals expected="{{option.title}}" expectedType="string" actual="$grabOptionSku" stepKey="assertOptionSku"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index ea73ec234e3fb..f6fb47c731790 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -306,4 +306,9 @@ <data key="quantity">100</data> <requiredEntity type="product_extension_attribute">EavStock100</requiredEntity> </entity> + <entity name="ProductWithFieldOptions" type="product"> + <var key="sku" entityType="product" entityKey="sku" /> + <requiredEntity type="product_option">ProductOptionField</requiredEntity> + <requiredEntity type="product_option">ProductOptionField2</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 14a3ff91c4b83..82ce0d076f115 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionData.xml @@ -10,6 +10,7 @@ <entity name="ProductOptionField" type="product_option"> <var key="product_sku" entityType="product" entityKey="sku" /> <data key="title">OptionField</data> + <data key="sku">OptionField</data> <data key="type">field</data> <data key="type_label">Field</data> <data key="is_require">true</data> @@ -20,6 +21,7 @@ </entity> <entity name="ProductOptionField2" type="product_option" extends="ProductOptionField"> <data key="title">OptionField2</data> + <data key="sku">OptionField2</data> <data key="price">20</data> </entity> <entity name="ProductOptionArea" type="product_option"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml index 033823574b441..c2458bcb41d78 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml @@ -26,12 +26,14 @@ <element name="lastOptionTypeParent" type="block" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@class, 'admin__action-multiselect-text')]" /> <!-- var 1 represents the option type that you want to select, i.e "radio buttons" --> <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//*[@name='product[options][{{index}}][price]']" parameterized="true"/> - <element name="optionPriceType" type="select" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr//*[@name='product[options][{{index}}][price_type]']" parameterized="true"/> - <element name="optionFileExtensions" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr//*[@name='product[options][{{index}}][file_extension]']" parameterized="true"/> - <element name="optionSku" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr//*[@name='product[options][{{index}}][sku]']" parameterized="true"/> - <element name="optionTitleInput" type="input" selector="input[name='product[options][{{index}}][title]']" parameterized="true"/> + <element name="optionPrice" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//input[contains(@name,'price')]"/> + <element name="optionPriceType" type="select" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//select[contains(@name,'price_type')]"/> + <element name="optionFileExtensions" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//input[contains(@name,'file_extension')]"/> + <element name="optionSku" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//input[contains(@name,'sku')]"/> + <element name="optionTitleInputByIndex" type="input" selector="input[name='product[options][{{index}}][title]']" parameterized="true"/> <element name="importOptions" type="button" selector="button[data-index='button_import']" timeout="30"/> + <element name="optionPriceByIndex" type="input" selector="input[name='product[options][{{index}}][price]']" parameterized="true"/> + <element name="optionSkuByIndex" type="input" selector="input[name='product[options][{{index}}][sku]']" parameterized="true"/> </section> <section name="AdminProductImportOptionsSection"> <element name="selectProductTitle" type="text" selector="//h1[contains(text(), 'Select Product')]"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml index f5415d8ca81b9..ce9337dead3f7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml @@ -27,6 +27,7 @@ <createData entity="SimpleProduct2" stepKey="createSecondProduct"> <requiredEntity createDataKey="createCategory"/> </createData> + <updateData createDataKey="createFirstProduct" entity="ProductWithFieldOptions" stepKey="updateProductCustomOptions" /> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> @@ -38,18 +39,6 @@ <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> - <!--Go to product page --> - <amOnPage url="{{AdminProductEditPage.url($$createFirstProduct.id$$)}}" stepKey="goToProductEditPage"/> - <actionGroup ref="AddProductCustomOption" stepKey="addFirstCustomOption"> - <argument name="option" value="ProductOptionField"/> - <argument name="optionIndex" value="0"/> - </actionGroup> - <actionGroup ref="AddProductCustomOption" stepKey="addSecondCustomOption"> - <argument name="option" value="ProductOptionField2"/> - <argument name="optionIndex" value="1"/> - </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> - <!--Change second product sku to first product sku--> <amOnPage url="{{AdminProductEditPage.url($$createSecondProduct.id$$)}}" stepKey="goToSecondProductEditPage"/> <fillField selector="{{AdminProductFormSection.productSku}}" userInput="$$createFirstProduct.sku$$" stepKey="fillProductSku"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml index 31371c8611ca7..bddd71f565c33 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml @@ -44,7 +44,6 @@ <!--Add custom option to configurable product--> <actionGroup ref="AddProductCustomOptionFile" stepKey="addCustomOptionToProduct"> <argument name="option" value="ProductOptionFile"/> - <argument name="optionIndex" value="0"/> </actionGroup> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml index 0b2afcc348f65..6f51bcf33bd7e 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml @@ -52,7 +52,6 @@ <!--Add custom option to configurable product--> <actionGroup ref="AddProductCustomOptionFile" stepKey="addCustomOptionToProduct"> <argument name="option" value="ProductOptionFile"/> - <argument name="optionIndex" value="0"/> </actionGroup> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> From 758245aba20a940bd917bc0e19252b8fd25c0bc2 Mon Sep 17 00:00:00 2001 From: Oleksii Lisovyi <olexii.lisovyi@ven.com> Date: Thu, 11 Apr 2019 11:05:55 +0300 Subject: [PATCH 1111/1295] Fix line limit issue --- .../Magento/Catalog/Model/ResourceModel/Product/Option.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option.php index 1a261f693268f..7e690ef3dbfc2 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option.php @@ -172,7 +172,8 @@ protected function _saveValuePrices(\Magento\Framework\Model\AbstractModel $obje foreach ($storeIds as $storeId) { if ($object->getPriceType() == 'fixed') { $storeCurrency = $this->_storeManager->getStore($storeId)->getBaseCurrencyCode(); - $rate = $this->_currencyFactory->create()->load($websiteBaseCurrency)->getRate($storeCurrency); + $rate = $this->_currencyFactory->create()->load($websiteBaseCurrency) + ->getRate($storeCurrency); if (!$rate) { $rate = 1; } From e083208addc0ff39a183adc34eb902c523ef8e06 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Thu, 11 Apr 2019 16:49:24 +0300 Subject: [PATCH 1112/1295] MAGETWO-98990: [MAGENTO CLOUD] The Update and Cancel buttons are missing in WYSIWYG editor in IE11 --- lib/web/tiny_mce/themes/advanced/js/source_editor.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/web/tiny_mce/themes/advanced/js/source_editor.js b/lib/web/tiny_mce/themes/advanced/js/source_editor.js index 9cf6b1a29cdaf..e90ee4d99628d 100644 --- a/lib/web/tiny_mce/themes/advanced/js/source_editor.js +++ b/lib/web/tiny_mce/themes/advanced/js/source_editor.js @@ -10,8 +10,9 @@ function onLoadInit() { tinyMCEPopup.resizeToInnerSize(); // Remove Gecko spellchecking - if (tinymce.isGecko) - document.body.spellcheck = tinyMCEPopup.editor.getParam("gecko_spellcheck"); + if (tinymce.isGecko) { + document.body.spellcheck = tinyMCEPopup.editor.getParam("gecko_spellcheck", false); + } document.getElementById('htmlSource').value = tinyMCEPopup.editor.getContent({source_view : true}); From b43d29263d52a580c811bcfd925ffabece736389 Mon Sep 17 00:00:00 2001 From: Slava Mankivski <mankivsk@adobe.com> Date: Thu, 11 Apr 2019 16:22:12 -0500 Subject: [PATCH 1113/1295] MAGETWO-97586: Unify report for JS Unit Tests --- .../static/testsuite/Magento/Test/Legacy/_files/words_ce.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/words_ce.xml b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/words_ce.xml index 4790ce24fd87f..62406b6cbd30a 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/words_ce.xml +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/words_ce.xml @@ -69,5 +69,8 @@ <item> <path>dev/build/publication/sanity/ce.xml</path> </item> + <item> + <path>dev/composer.lock</path> + </item> </whitelist> </config> From 1ec99eecdcde8a702facfa1d3a5b308bae3f181c Mon Sep 17 00:00:00 2001 From: Pedro Sousa <pedrosousa13@users.noreply.github.com> Date: Fri, 8 Feb 2019 17:15:22 +0100 Subject: [PATCH 1114/1295] Turn on edit mode for product repository when adding children This should fix the issue with the configurable products not having any options with Bulk and Async API. Related issue: https://github.com/magento/magento2/issues/20366 --- app/code/Magento/ConfigurableProduct/Model/LinkManagement.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/ConfigurableProduct/Model/LinkManagement.php b/app/code/Magento/ConfigurableProduct/Model/LinkManagement.php index 01981b5dae9db..01ad9f56f4fc0 100644 --- a/app/code/Magento/ConfigurableProduct/Model/LinkManagement.php +++ b/app/code/Magento/ConfigurableProduct/Model/LinkManagement.php @@ -110,7 +110,7 @@ public function getChildren($sku) */ public function addChild($sku, $childSku) { - $product = $this->productRepository->get($sku); + $product = $this->productRepository->get($sku, true); $child = $this->productRepository->get($childSku); $childrenIds = array_values($this->configurableType->getChildrenIds($product->getId())[0]); From 1b7a2e0b2396a600c50ebdef451465de2711d02f Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Mon, 1 Apr 2019 16:30:18 +0300 Subject: [PATCH 1115/1295] magento/magento2#21083: Static test fix. --- .../ConfigurableProduct/Model/LinkManagement.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Model/LinkManagement.php b/app/code/Magento/ConfigurableProduct/Model/LinkManagement.php index 01ad9f56f4fc0..8e5abed72eaa2 100644 --- a/app/code/Magento/ConfigurableProduct/Model/LinkManagement.php +++ b/app/code/Magento/ConfigurableProduct/Model/LinkManagement.php @@ -1,6 +1,5 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -10,6 +9,9 @@ use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\StateException; +/** + * Configurable product link management. + */ class LinkManagement implements \Magento\ConfigurableProduct\Api\LinkManagementInterface { /** @@ -67,7 +69,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function getChildren($sku) { @@ -106,7 +108,7 @@ public function getChildren($sku) } /** - * {@inheritdoc} + * @inheritdoc */ public function addChild($sku, $childSku) { @@ -144,7 +146,7 @@ public function addChild($sku, $childSku) } /** - * {@inheritdoc} + * @inheritdoc */ public function removeChild($sku, $childSku) { From fce2445035f1650f2a26df6481e2d5c7ae339100 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Tue, 2 Apr 2019 10:24:26 +0300 Subject: [PATCH 1116/1295] magento/magento2#21083: Static test fix. --- .../ConfigurableProduct/Model/LinkManagement.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/code/Magento/ConfigurableProduct/Model/LinkManagement.php b/app/code/Magento/ConfigurableProduct/Model/LinkManagement.php index 8e5abed72eaa2..fcbd0075b4cd0 100644 --- a/app/code/Magento/ConfigurableProduct/Model/LinkManagement.php +++ b/app/code/Magento/ConfigurableProduct/Model/LinkManagement.php @@ -11,6 +11,8 @@ /** * Configurable product link management. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class LinkManagement implements \Magento\ConfigurableProduct\Api\LinkManagementInterface { @@ -109,6 +111,10 @@ public function getChildren($sku) /** * @inheritdoc + * @throws InputException + * @throws NoSuchEntityException + * @throws StateException + * @throws \Magento\Framework\Exception\CouldNotSaveException */ public function addChild($sku, $childSku) { @@ -147,6 +153,10 @@ public function addChild($sku, $childSku) /** * @inheritdoc + * @throws InputException + * @throws NoSuchEntityException + * @throws StateException + * @throws \Magento\Framework\Exception\CouldNotSaveException */ public function removeChild($sku, $childSku) { From 9634b50bb6f29473d731a1674e63faa6d4401edc Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Fri, 12 Apr 2019 09:20:25 +0300 Subject: [PATCH 1117/1295] MAGETWO-97008: Price column in sales_order_item table shows the price including tax when Custom Price is applied on admin order --- .../Sales/Total/Quote/CommonTaxCollector.php | 7 +- .../Total/Quote/CommonTaxCollectorTest.php | 130 ++++++++++++++---- .../including_tax_with_custom_price.php | 93 +++++++++++++ .../tax_calculation_data_aggregated.php | 1 + 4 files changed, 199 insertions(+), 32 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_with_custom_price.php diff --git a/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php b/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php index 8017bde8c7a47..65af4d7863830 100644 --- a/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php +++ b/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php @@ -350,7 +350,7 @@ public function mapItems( $useBaseCurrency ) { $items = $shippingAssignment->getItems(); - if (!count($items)) { + if (empty($items)) { return []; } @@ -475,7 +475,7 @@ protected function prepareQuoteDetails(ShippingAssignmentInterface $shippingAssi { $items = $shippingAssignment->getItems(); $address = $shippingAssignment->getShipping()->getAddress(); - if (!count($items)) { + if (empty($items)) { return $this->quoteDetailsDataObjectFactory->create(); } @@ -685,6 +685,9 @@ public function updateItemTaxInfo($quoteItem, $itemTaxDetails, $baseItemTaxDetai { //The price should be base price $quoteItem->setPrice($baseItemTaxDetails->getPrice()); + if ($quoteItem->getCustomPrice() && $this->taxHelper->applyTaxOnCustomPrice()) { + $quoteItem->setCustomPrice($baseItemTaxDetails->getPrice()); + } $quoteItem->setConvertedPrice($itemTaxDetails->getPrice()); $quoteItem->setPriceInclTax($itemTaxDetails->getPriceInclTax()); $quoteItem->setRowTotal($itemTaxDetails->getRowTotal()); diff --git a/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/CommonTaxCollectorTest.php b/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/CommonTaxCollectorTest.php index 9b963434e321d..8e0f9b8226f42 100644 --- a/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/CommonTaxCollectorTest.php +++ b/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/CommonTaxCollectorTest.php @@ -3,81 +3,108 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); // @codingStandardsIgnoreFile namespace Magento\Tax\Test\Unit\Model\Sales\Total\Quote; -/** - * Test class for \Magento\Tax\Model\Sales\Total\Quote\Tax - */ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Tax\Helper\Data as TaxHelper; +use Magento\Tax\Api\Data\TaxDetailsItemInterface; +use Magento\Quote\Model\Quote\Item as QuoteItem; +use Magento\Store\Model\Store; +use Magento\Tax\Model\Sales\Total\Quote\CommonTaxCollector; +use Magento\Tax\Model\Config; +use Magento\Quote\Model\Quote\Address as QuoteAddress; +use Magento\Quote\Model\Quote; +use Magento\Tax\Api\Data\QuoteDetailsItemInterface; +use Magento\Tax\Api\Data\TaxClassKeyInterface; +use Magento\Tax\Model\Sales\Quote\ItemDetails; +use Magento\Tax\Model\TaxClass\Key as TaxClassKey; +use Magento\Tax\Api\Data\QuoteDetailsItemInterfaceFactory; +use Magento\Tax\Api\Data\TaxClassKeyInterfaceFactory; +use Magento\Quote\Api\Data\ShippingAssignmentInterface; +use Magento\Quote\Api\Data\ShippingInterface; +use Magento\Quote\Model\Quote\Address\Total as QuoteAddressTotal; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** + * Common tax collector test + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class CommonTaxCollectorTest extends \PHPUnit\Framework\TestCase +class CommonTaxCollectorTest extends TestCase { /** - * @var \Magento\Tax\Model\Sales\Total\Quote\CommonTaxCollector + * @var CommonTaxCollector */ private $commonTaxCollector; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Tax\Model\Config + * @var MockObject|Config */ private $taxConfig; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Quote\Model\Quote\Address + * @var MockObject|QuoteAddress */ private $address; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Quote\Model\Quote + * @var MockObject|Quote */ private $quote; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Store\Model\Store + * @var MockObject|Store */ private $store; /** - * @var \PHPUnit_Framework_MockObject_MockObject| + * @var MockObject */ protected $taxClassKeyDataObjectFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject| + * @var MockObject */ protected $quoteDetailsItemDataObjectFactoryMock; /** - * @var \Magento\Tax\Api\Data\QuoteDetailsItemInterface + * @var QuoteDetailsItemInterface */ protected $quoteDetailsItemDataObject; /** - * @var \Magento\Tax\Api\Data\TaxClassKeyInterface + * @var TaxClassKeyInterface */ protected $taxClassKeyDataObject; + /** + * @var TaxHelper + */ + private $taxHelper; + + /** + * {@inheritdoc} + */ protected function setUp() { $objectManager = new ObjectManager($this); - $this->taxConfig = $this->getMockBuilder(\Magento\Tax\Model\Config::class) + $this->taxConfig = $this->getMockBuilder(Config::class) ->disableOriginalConstructor() - ->setMethods(['getShippingTaxClass', 'shippingPriceIncludesTax']) + ->setMethods(['getShippingTaxClass', 'shippingPriceIncludesTax', 'discountTax']) ->getMock(); - $this->store = $this->getMockBuilder(\Magento\Store\Model\Store::class) + $this->store = $this->getMockBuilder(Store::class) ->disableOriginalConstructor() ->setMethods(['__wakeup']) ->getMock(); - $this->quote = $this->getMockBuilder(\Magento\Quote\Model\Quote::class) + $this->quote = $this->getMockBuilder(Quote::class) ->disableOriginalConstructor() ->setMethods(['__wakeup', 'getStore']) ->getMock(); @@ -86,7 +113,7 @@ protected function setUp() ->method('getStore') ->will($this->returnValue($this->store)); - $this->address = $this->getMockBuilder(\Magento\Quote\Model\Quote\Address::class) + $this->address = $this->getMockBuilder(QuoteAddress::class) ->disableOriginalConstructor() ->getMock(); @@ -94,35 +121,41 @@ protected function setUp() ->method('getQuote') ->will($this->returnValue($this->quote)); $methods = ['create']; - $this->quoteDetailsItemDataObject = $objectManager->getObject( - \Magento\Tax\Model\Sales\Quote\ItemDetails::class - ); - $this->taxClassKeyDataObject = $objectManager->getObject(\Magento\Tax\Model\TaxClass\Key::class); + $this->quoteDetailsItemDataObject = $objectManager->getObject(ItemDetails::class); + $this->taxClassKeyDataObject = $objectManager->getObject(TaxClassKey::class); $this->quoteDetailsItemDataObjectFactoryMock - = $this->createPartialMock(\Magento\Tax\Api\Data\QuoteDetailsItemInterfaceFactory::class, $methods); + = $this->createPartialMock(QuoteDetailsItemInterfaceFactory::class, $methods); $this->quoteDetailsItemDataObjectFactoryMock->expects($this->any()) ->method('create') ->willReturn($this->quoteDetailsItemDataObject); $this->taxClassKeyDataObjectFactoryMock = - $this->createPartialMock(\Magento\Tax\Api\Data\TaxClassKeyInterfaceFactory::class, $methods); + $this->createPartialMock(TaxClassKeyInterfaceFactory::class, $methods); $this->taxClassKeyDataObjectFactoryMock->expects($this->any()) ->method('create') ->willReturn($this->taxClassKeyDataObject); + $this->taxHelper = $this->getMockBuilder(TaxHelper::class) + ->disableOriginalConstructor() + ->getMock(); $this->commonTaxCollector = $objectManager->getObject( - \Magento\Tax\Model\Sales\Total\Quote\CommonTaxCollector::class, + CommonTaxCollector::class, [ 'taxConfig' => $this->taxConfig, 'quoteDetailsItemDataObjectFactory' => $this->quoteDetailsItemDataObjectFactoryMock, - 'taxClassKeyDataObjectFactory' => $this->taxClassKeyDataObjectFactoryMock + 'taxClassKeyDataObjectFactory' => $this->taxClassKeyDataObjectFactoryMock, + 'taxHelper' => $this->taxHelper, ] ); } /** + * Test for GetShippingDataObject + * * @param array $addressData * @param bool $useBaseCurrency * @param string $shippingTaxClass * @param bool $shippingPriceInclTax + * + * @return void * @dataProvider getShippingDataObjectDataProvider */ public function testGetShippingDataObject( @@ -131,7 +164,7 @@ public function testGetShippingDataObject( $shippingTaxClass, $shippingPriceInclTax ) { - $shippingAssignmentMock = $this->createMock(\Magento\Quote\Api\Data\ShippingAssignmentInterface::class); + $shippingAssignmentMock = $this->createMock(ShippingAssignmentInterface::class); $methods = [ 'getShippingDiscountAmount', 'getShippingTaxCalculationAmount', @@ -141,8 +174,10 @@ public function testGetShippingDataObject( 'getBaseShippingAmount', 'getBaseShippingDiscountAmount' ]; - $totalsMock = $this->createPartialMock(\Magento\Quote\Model\Quote\Address\Total::class, $methods); - $shippingMock = $this->createMock(\Magento\Quote\Api\Data\ShippingInterface::class); + /** @var MockObject|QuoteAddressTotal $totalsMock */ + $totalsMock = $this->createPartialMock(QuoteAddressTotal::class, $methods); + $shippingMock = $this->createMock(ShippingInterface::class); + /** @var MockObject|ShippingAssignmentInterface $shippingAssignmentMock */ $shippingAssignmentMock->expects($this->once())->method('getShipping')->willReturn($shippingMock); $shippingMock->expects($this->once())->method('getAddress')->willReturn($this->address); $baseShippingAmount = $addressData['base_shipping_amount']; @@ -184,9 +219,44 @@ public function testGetShippingDataObject( } /** + * Update item tax info + * + * @return void + */ + public function testUpdateItemTaxInfo() + { + /** @var MockObject|QuoteItem $quoteItem */ + $quoteItem = $this->getMockBuilder(QuoteItem::class) + ->disableOriginalConstructor() + ->setMethods(['getPrice', 'setPrice', 'getCustomPrice', 'setCustomPrice']) + ->getMock(); + $this->taxHelper->method('applyTaxOnCustomPrice')->willReturn(true); + $quoteItem->method('getCustomPrice')->willReturn(true); + /** @var MockObject|TaxDetailsItemInterface $itemTaxDetails */ + $itemTaxDetails = $this->getMockBuilder(TaxDetailsItemInterface::class) + ->disableOriginalConstructor() + ->getMock(); + /** @var MockObject|TaxDetailsItemInterface $baseItemTaxDetails */ + $baseItemTaxDetails = $this->getMockBuilder(TaxDetailsItemInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $quoteItem->expects($this->once())->method('setCustomPrice'); + + $this->commonTaxCollector->updateItemTaxInfo( + $quoteItem, + $itemTaxDetails, + $baseItemTaxDetails, + $this->store + ); + } + + /** + * Data for testGetShippingDataObject + * * @return array */ - public function getShippingDataObjectDataProvider() + public function getShippingDataObjectDataProvider(): array { $data = [ 'free_shipping' => [ diff --git a/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_with_custom_price.php b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_with_custom_price.php new file mode 100644 index 0000000000000..290c133f455f6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Tax/_files/scenarios/including_tax_with_custom_price.php @@ -0,0 +1,93 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Tax\Model\Config; +use Magento\Tax\Model\Sales\Total\Quote\SetupUtil; + +$taxCalculationData['including_tax_with_custom_price'] = [ + 'config_data' => [ + SetupUtil::CONFIG_OVERRIDES => [ + Config::CONFIG_XML_PATH_PRICE_INCLUDES_TAX => 1, + Config::CONFIG_XML_PATH_APPLY_ON => 0, + ], + SetupUtil::TAX_RATE_OVERRIDES => [ + SetupUtil::TAX_RATE_TX => 8.25, + SetupUtil::TAX_STORE_RATE => 8.25, + ], + SetupUtil::TAX_RULE_OVERRIDES => [ + ], + ], + 'quote_data' => [ + 'billing_address' => [ + 'region_id' => SetupUtil::REGION_TX, + ], + 'shipping_address' => [ + 'region_id' => SetupUtil::REGION_TX, + ], + 'items' => [ + [ + 'sku' => 'simple1', + 'price' => 16.24, + 'qty' => 1, + ], + ], + 'update_items' => [ + 'simple1' => [ + 'custom_price' => 14, + 'qty' => 1, + ], + ], + ], + 'expected_results' => [ + 'address_data' => [ + 'subtotal' => 12.93, + 'base_subtotal' => 12.93, + 'subtotal_incl_tax' => 14, + 'base_subtotal_incl_tax' => 14, + 'tax_amount' => 1.07, + 'base_tax_amount' => 1.07, + 'shipping_amount' => 0, + 'base_shipping_amount' => 0, + 'shipping_incl_tax' => 0, + 'base_shipping_incl_tax' => 0, + 'shipping_taxable' => 0, + 'base_shipping_taxable' => 0, + 'shipping_tax_amount' => 0, + 'base_shipping_tax_amount' => 0, + 'discount_amount' => 0, + 'base_discount_amount' => 0, + 'discount_tax_compensation_amount' => 0, + 'base_discount_tax_compensation_amount' => 0, + 'shipping_discount_tax_compensation_amount' => 0, + 'base_shipping_discount_tax_compensation_amount' => 0, + 'grand_total' => 14, + 'base_grand_total' => 14, + ], + 'items_data' => [ + 'simple1' => [ + 'row_total' => 12.93, + 'base_row_total' => 12.93, + 'tax_percent' => 8.25, + 'price' => 12.93, + 'custom_price' => 12.93, + 'original_custom_price' => 14, + 'base_price' => 12.93, + 'price_incl_tax' => 14, + 'base_price_incl_tax' => 14, + 'row_total_incl_tax' => 14, + 'base_row_total_incl_tax' => 14, + 'tax_amount' => 1.07, + 'base_tax_amount' => 1.07, + 'discount_amount' => 0, + 'base_discount_amount' => 0, + 'discount_percent' => 0, + 'discount_tax_compensation_amount' => 0, + 'base_discount_tax_compensation_amount' => 0, + ], + ], + ], +]; 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 87791d83aabd4..d3716ae260e0f 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 @@ -32,4 +32,5 @@ 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'; +require_once __DIR__ . '/scenarios/including_tax_with_custom_price.php'; require_once __DIR__ . '/scenarios/excluding_tax_apply_origin_price_with_custom_price.php'; From f684639208686af7710a2f76925f01c747b4461d Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Fri, 12 Apr 2019 10:46:40 +0300 Subject: [PATCH 1118/1295] MC-15456: Upgrade DHL schema to 6.0 --- app/code/Magento/Dhl/Model/Carrier.php | 270 +++++++++++------- .../Dhl/Test/Unit/Model/CarrierTest.php | 216 +++++++++++--- .../Dhl/Test/Unit/Model/_files/countries.xml | 2 +- .../Model/_files/dhl_quote_request_data.php | 5 +- .../Unit/Model/_files/dhl_quote_response.xml | 18 +- .../Model/_files/dhl_quote_response_rates.php | 12 +- .../_files/domestic_shipment_request.xml | 88 ++++++ .../Unit/Model/_files/shipment_request.xml | 93 ++++++ app/code/Magento/Dhl/etc/countries.xml | 56 ++-- .../Magento/Dhl/Model/CarrierTest.php | 252 ++++++++++++++++ ...SingleknownTrackResponse-no-data-found.xml | 28 ++ .../Dhl/_files/Track-res-XML-Parse-Err.xml | 26 ++ .../_files/TrackingRequest_MultipleAWB.xml | 24 ++ .../Dhl/_files/TrackingRequest_SingleAWB.xml | 20 ++ .../_files/TrackingResponse_MultipleAWB.xml | 174 +++++++++++ .../Dhl/_files/TrackingResponse_SingleAWB.xml | 69 +++++ 16 files changed, 1162 insertions(+), 191 deletions(-) create mode 100644 app/code/Magento/Dhl/Test/Unit/Model/_files/domestic_shipment_request.xml create mode 100644 app/code/Magento/Dhl/Test/Unit/Model/_files/shipment_request.xml create mode 100644 dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Dhl/_files/SingleknownTrackResponse-no-data-found.xml create mode 100644 dev/tests/integration/testsuite/Magento/Dhl/_files/Track-res-XML-Parse-Err.xml create mode 100644 dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingRequest_MultipleAWB.xml create mode 100644 dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingRequest_SingleAWB.xml create mode 100644 dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingResponse_MultipleAWB.xml create mode 100644 dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingResponse_SingleAWB.xml diff --git a/app/code/Magento/Dhl/Model/Carrier.php b/app/code/Magento/Dhl/Model/Carrier.php index 731b1d9c7c201..6cb9f0e917dfb 100644 --- a/app/code/Magento/Dhl/Model/Carrier.php +++ b/app/code/Magento/Dhl/Model/Carrier.php @@ -7,6 +7,7 @@ namespace Magento\Dhl\Model; use Magento\Catalog\Model\Product\Type; +use Magento\Framework\App\ProductMetadataInterface; use Magento\Framework\Module\Dir; use Magento\Sales\Exception\DocumentValidationException; use Magento\Sales\Model\Order\Shipment; @@ -56,6 +57,27 @@ class Carrier extends \Magento\Dhl\Model\AbstractDhl implements \Magento\Shippin */ const CODE = 'dhl'; + /** + * DHL service Quote prefix used for message reference. + * + * @var string + */ + private $servicePrefixQuote = 'QUOT'; + + /** + * DHL service Shipping prefix used for message reference. + * + * @var string + */ + private $servicePrefixShipval = 'SHIP'; + + /** + * DHL service Tracking prefix used for message reference. + * + * @var string + */ + private $servicePrefixTracking = 'TRCK'; + /** * Rate request data * @@ -206,6 +228,11 @@ class Carrier extends \Magento\Dhl\Model\AbstractDhl implements \Magento\Shippin */ private $xmlValidator; + /** + * @var ProductMetadataInterface + */ + private $productMetadata; + /** * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory @@ -232,7 +259,8 @@ class Carrier extends \Magento\Dhl\Model\AbstractDhl implements \Magento\Shippin * @param \Magento\Framework\Stdlib\DateTime $dateTime * @param \Magento\Framework\HTTP\ZendClientFactory $httpClientFactory * @param array $data - * @param \Magento\Dhl\Model\Validator\XmlValidatorFactory $xmlValidatorFactory + * @param \Magento\Dhl\Model\Validator\XmlValidatorFactory|null $xmlValidatorFactory + * @param ProductMetadataInterface|null $productMetadata * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -261,7 +289,8 @@ public function __construct( \Magento\Framework\Stdlib\DateTime $dateTime, \Magento\Framework\HTTP\ZendClientFactory $httpClientFactory, array $data = [], - \Magento\Dhl\Model\Validator\XmlValidator $xmlValidator = null + \Magento\Dhl\Model\Validator\XmlValidator $xmlValidator = null, + ProductMetadataInterface $productMetadata = null ) { $this->readFactory = $readFactory; $this->_carrierHelper = $carrierHelper; @@ -295,6 +324,8 @@ public function __construct( } $this->xmlValidator = $xmlValidator ?: \Magento\Framework\App\ObjectManager::getInstance()->get(XmlValidator::class); + $this->productMetadata = $productMetadata + ?: \Magento\Framework\App\ObjectManager::getInstance()->get(ProductMetadataInterface::class); } /** @@ -983,19 +1014,30 @@ protected function _getQuotesFromServer($request) protected function _buildQuotesRequestXml() { $rawRequest = $this->_rawRequest; - $xmlStr = '<?xml version = "1.0" encoding = "UTF-8"?>' . - '<p:DCTRequest xmlns:p="http://www.dhl.com" xmlns:p1="http://www.dhl.com/datatypes" ' . - 'xmlns:p2="http://www.dhl.com/DCTRequestdatatypes" ' . + + $xmlStr = '<?xml version="1.0" encoding="UTF-8"?>' . + '<req:DCTRequest schemaVersion="2.0" ' . + 'xmlns:req="http://www.dhl.com" ' . 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' . - 'xsi:schemaLocation="http://www.dhl.com DCT-req.xsd "/>'; + 'xsi:schemaLocation="http://www.dhl.com DCT-req_global-2.0.xsd"/>'; + $xml = $this->_xmlElFactory->create(['data' => $xmlStr]); $nodeGetQuote = $xml->addChild('GetQuote', '', ''); $nodeRequest = $nodeGetQuote->addChild('Request'); $nodeServiceHeader = $nodeRequest->addChild('ServiceHeader'); + $nodeServiceHeader->addChild('MessageTime', $this->buildMessageTimestamp()); + $nodeServiceHeader->addChild( + 'MessageReference', + $this->buildMessageReference($this->servicePrefixQuote) + ); $nodeServiceHeader->addChild('SiteID', (string)$this->getConfigData('id')); $nodeServiceHeader->addChild('Password', (string)$this->getConfigData('password')); + $nodeMetaData = $nodeRequest->addChild('MetaData'); + $nodeMetaData->addChild('SoftwareName', $this->buildSoftwareName()); + $nodeMetaData->addChild('SoftwareVersion', $this->buildSoftwareVersion()); + $nodeFrom = $nodeGetQuote->addChild('From'); $nodeFrom->addChild('CountryCode', $rawRequest->getOrigCountryId()); $nodeFrom->addChild('Postalcode', $rawRequest->getOrigPostal()); @@ -1386,44 +1428,37 @@ protected function _doRequest() { $rawRequest = $this->_request; - $originRegion = $this->getCountryParams( - $this->_scopeConfig->getValue( - Shipment::XML_PATH_STORE_COUNTRY_ID, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, - $this->getStore() - ) - )->getRegion(); - - if (!$originRegion) { - throw new \Magento\Framework\Exception\LocalizedException(__('Wrong Region')); - } - - if ($originRegion == 'AM') { - $originRegion = ''; - } - $xmlStr = '<?xml version="1.0" encoding="UTF-8"?>' . - '<req:ShipmentValidateRequest' . - $originRegion . + '<req:ShipmentRequest' . ' xmlns:req="http://www.dhl.com"' . ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' . - ' xsi:schemaLocation="http://www.dhl.com ship-val-req' . - ($originRegion ? '_' . - $originRegion : '') . - '.xsd" />'; + ' xsi:schemaLocation="http://www.dhl.com ship-val-global-req-6.0.xsd"' . + ' schemaVersion="6.0" />'; $xml = $this->_xmlElFactory->create(['data' => $xmlStr]); $nodeRequest = $xml->addChild('Request', '', ''); $nodeServiceHeader = $nodeRequest->addChild('ServiceHeader'); + $nodeServiceHeader->addChild('MessageTime', $this->buildMessageTimestamp()); + // MessageReference must be 28 to 32 chars. + $nodeServiceHeader->addChild( + 'MessageReference', + $this->buildMessageReference($this->servicePrefixShipval) + ); $nodeServiceHeader->addChild('SiteID', (string)$this->getConfigData('id')); $nodeServiceHeader->addChild('Password', (string)$this->getConfigData('password')); - if (!$originRegion) { - $xml->addChild('RequestedPickupTime', 'N', ''); - } - if ($originRegion !== 'AP') { - $xml->addChild('NewShipper', 'N', ''); + $originRegion = $this->getCountryParams( + $this->_scopeConfig->getValue( + Shipment::XML_PATH_STORE_COUNTRY_ID, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + $this->getStore() + ) + )->getRegion(); + if ($originRegion) { + $xml->addChild('RegionCode', $originRegion, ''); } + $xml->addChild('RequestedPickupTime', 'N', ''); + $xml->addChild('NewShipper', 'N', ''); $xml->addChild('LanguageCode', 'EN', ''); $xml->addChild('PiecesEnabled', 'Y', ''); @@ -1465,8 +1500,9 @@ protected function _doRequest() } $nodeConsignee->addChild('City', $rawRequest->getRecipientAddressCity()); - if ($originRegion !== 'AP') { - $nodeConsignee->addChild('Division', $rawRequest->getRecipientAddressStateOrProvinceCode()); + $recipientAddressStateOrProvinceCode = $rawRequest->getRecipientAddressStateOrProvinceCode(); + if ($recipientAddressStateOrProvinceCode) { + $nodeConsignee->addChild('Division', $recipientAddressStateOrProvinceCode); } $nodeConsignee->addChild('PostalCode', $rawRequest->getRecipientAddressPostalCode()); $nodeConsignee->addChild('CountryCode', $rawRequest->getRecipientAddressCountryCode()); @@ -1510,15 +1546,13 @@ protected function _doRequest() $nodeReference->addChild('ReferenceType', 'St'); /** Shipment Details */ - $this->_shipmentDetails($xml, $rawRequest, $originRegion); + $this->_shipmentDetails($xml, $rawRequest); /** Shipper */ $nodeShipper = $xml->addChild('Shipper', '', ''); $nodeShipper->addChild('ShipperID', (string)$this->getConfigData('account')); $nodeShipper->addChild('CompanyName', $rawRequest->getShipperContactCompanyName()); - if ($originRegion !== 'AP') { - $nodeShipper->addChild('RegisteredAccount', (string)$this->getConfigData('account')); - } + $nodeShipper->addChild('RegisteredAccount', (string)$this->getConfigData('account')); $address = $rawRequest->getShipperAddressStreet1() . ' ' . $rawRequest->getShipperAddressStreet2(); $address = $this->string->split($address, 35, false, true); @@ -1531,8 +1565,9 @@ protected function _doRequest() } $nodeShipper->addChild('City', $rawRequest->getShipperAddressCity()); - if ($originRegion !== 'AP') { - $nodeShipper->addChild('Division', $rawRequest->getShipperAddressStateOrProvinceCode()); + $shipperAddressStateOrProvinceCode = $rawRequest->getShipperAddressStateOrProvinceCode(); + if ($shipperAddressStateOrProvinceCode) { + $nodeShipper->addChild('Division', $shipperAddressStateOrProvinceCode); } $nodeShipper->addChild('PostalCode', $rawRequest->getShipperAddressPostalCode()); $nodeShipper->addChild('CountryCode', $rawRequest->getShipperAddressCountryCode()); @@ -1584,19 +1619,13 @@ protected function _doRequest() * @return void * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ protected function _shipmentDetails($xml, $rawRequest, $originRegion = '') { $nodeShipmentDetails = $xml->addChild('ShipmentDetails', '', ''); $nodeShipmentDetails->addChild('NumberOfPieces', count($rawRequest->getPackages())); - if ($originRegion) { - $nodeShipmentDetails->addChild( - 'CurrencyCode', - $this->_storeManager->getWebsite($this->_request->getWebsiteId())->getBaseCurrencyCode() - ); - } - $nodePieces = $nodeShipmentDetails->addChild('Pieces', '', ''); /* @@ -1615,18 +1644,12 @@ protected function _shipmentDetails($xml, $rawRequest, $originRegion = '') } $nodePiece->addChild('PieceID', ++$i); $nodePiece->addChild('PackageType', $packageType); - $nodePiece->addChild('Weight', sprintf('%.1f', $package['params']['weight'])); + $nodePiece->addChild('Weight', sprintf('%.3f', $package['params']['weight'])); $params = $package['params']; if ($params['width'] && $params['length'] && $params['height']) { - if (!$originRegion) { - $nodePiece->addChild('Width', round($params['width'])); - $nodePiece->addChild('Height', round($params['height'])); - $nodePiece->addChild('Depth', round($params['length'])); - } else { - $nodePiece->addChild('Depth', round($params['length'])); - $nodePiece->addChild('Width', round($params['width'])); - $nodePiece->addChild('Height', round($params['height'])); - } + $nodePiece->addChild('Width', round($params['width'])); + $nodePiece->addChild('Height', round($params['height'])); + $nodePiece->addChild('Depth', round($params['length'])); } $content = []; foreach ($package['items'] as $item) { @@ -1635,51 +1658,34 @@ protected function _shipmentDetails($xml, $rawRequest, $originRegion = '') $nodePiece->addChild('PieceContents', substr(implode(',', $content), 0, 34)); } - if (!$originRegion) { - $nodeShipmentDetails->addChild('Weight', sprintf('%.1f', $rawRequest->getPackageWeight())); - $nodeShipmentDetails->addChild('WeightUnit', substr($this->_getWeightUnit(), 0, 1)); - $nodeShipmentDetails->addChild('GlobalProductCode', $rawRequest->getShippingMethod()); - $nodeShipmentDetails->addChild('LocalProductCode', $rawRequest->getShippingMethod()); - $nodeShipmentDetails->addChild('Date', $this->_coreDate->date('Y-m-d')); - $nodeShipmentDetails->addChild('Contents', 'DHL Parcel'); - /** - * The DoorTo Element defines the type of delivery service that applies to the shipment. - * The valid values are DD (Door to Door), DA (Door to Airport) , AA and DC (Door to - * Door non-compliant) - */ - $nodeShipmentDetails->addChild('DoorTo', 'DD'); - $nodeShipmentDetails->addChild('DimensionUnit', substr($this->_getDimensionUnit(), 0, 1)); - if ($package['params']['container'] == self::DHL_CONTENT_TYPE_NON_DOC) { - $packageType = 'CP'; - } - $nodeShipmentDetails->addChild('PackageType', $packageType); - if ($this->isDutiable($rawRequest->getOrigCountryId(), $rawRequest->getDestCountryId())) { - $nodeShipmentDetails->addChild('IsDutiable', 'Y'); - } - $nodeShipmentDetails->addChild( - 'CurrencyCode', - $this->_storeManager->getWebsite($this->_request->getWebsiteId())->getBaseCurrencyCode() - ); - } else { - if ($package['params']['container'] == self::DHL_CONTENT_TYPE_NON_DOC) { - $packageType = 'CP'; - } - $nodeShipmentDetails->addChild('PackageType', $packageType); - $nodeShipmentDetails->addChild('Weight', sprintf('%.3f', $rawRequest->getPackageWeight())); - $nodeShipmentDetails->addChild('DimensionUnit', substr($this->_getDimensionUnit(), 0, 1)); - $nodeShipmentDetails->addChild('WeightUnit', substr($this->_getWeightUnit(), 0, 1)); - $nodeShipmentDetails->addChild('GlobalProductCode', $rawRequest->getShippingMethod()); - $nodeShipmentDetails->addChild('LocalProductCode', $rawRequest->getShippingMethod()); - - /** - * The DoorTo Element defines the type of delivery service that applies to the shipment. - * The valid values are DD (Door to Door), DA (Door to Airport) , AA and DC (Door to - * Door non-compliant) - */ - $nodeShipmentDetails->addChild('DoorTo', 'DD'); - $nodeShipmentDetails->addChild('Date', $this->_coreDate->date('Y-m-d')); - $nodeShipmentDetails->addChild('Contents', 'DHL Parcel TEST'); + $nodeShipmentDetails->addChild('Weight', sprintf('%.3f', $rawRequest->getPackageWeight())); + $nodeShipmentDetails->addChild('WeightUnit', substr($this->_getWeightUnit(), 0, 1)); + $nodeShipmentDetails->addChild('GlobalProductCode', $rawRequest->getShippingMethod()); + $nodeShipmentDetails->addChild('LocalProductCode', $rawRequest->getShippingMethod()); + $nodeShipmentDetails->addChild( + 'Date', + $this->_coreDate->date('Y-m-d', strtotime('now + 1day')) + ); + $nodeShipmentDetails->addChild('Contents', 'DHL Parcel'); + + /** + * The DoorTo Element defines the type of delivery service that applies to the shipment. + * The valid values are DD (Door to Door), DA (Door to Airport) , AA and DC (Door to + * Door non-compliant) + */ + $nodeShipmentDetails->addChild('DoorTo', 'DD'); + $nodeShipmentDetails->addChild('DimensionUnit', substr($this->_getDimensionUnit(), 0, 1)); + if ($package['params']['container'] == self::DHL_CONTENT_TYPE_NON_DOC) { + $packageType = 'CP'; + } + $nodeShipmentDetails->addChild('PackageType', $packageType); + if ($this->isDutiable($rawRequest->getOrigCountryId(), $rawRequest->getDestCountryId())) { + $nodeShipmentDetails->addChild('IsDutiable', 'Y'); } + $nodeShipmentDetails->addChild( + 'CurrencyCode', + $this->_storeManager->getWebsite($this->_request->getWebsiteId())->getBaseCurrencyCode() + ); } /** @@ -1710,12 +1716,15 @@ protected function _getXMLTracking($trackings) '<req:KnownTrackingRequest' . ' xmlns:req="http://www.dhl.com"' . ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' . - ' xsi:schemaLocation="http://www.dhl.com TrackingRequestKnown.xsd" />'; + ' xsi:schemaLocation="http://www.dhl.com TrackingRequestKnown-1.0.xsd"' . + ' schemaVersion="1.0" />'; $xml = $this->_xmlElFactory->create(['data' => $xmlStr]); $requestNode = $xml->addChild('Request', '', ''); $serviceHeaderNode = $requestNode->addChild('ServiceHeader', '', ''); + $serviceHeaderNode->addChild('MessageTime', $this->buildMessageTimestamp()); + $serviceHeaderNode->addChild('MessageReference', $this->buildMessageReference($this->servicePrefixTracking)); $serviceHeaderNode->addChild('SiteID', (string)$this->getConfigData('id')); $serviceHeaderNode->addChild('Password', (string)$this->getConfigData('password')); @@ -1970,4 +1979,59 @@ protected function isDutiable($origCountryId, $destCountryId) return !$this->_isDomestic; } + + /** + * Builds a datetime string to be used as the MessageTime in accordance to the expected format. + * + * @param string|null $datetime + * @return string + */ + private function buildMessageTimestamp(string $datetime = null): string + { + return $this->_coreDate->date(\DATE_RFC3339, $datetime); + } + + /** + * Builds a string to be used as the MessageReference. + * + * @param string $servicePrefix + * @return string + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function buildMessageReference(string $servicePrefix): string + { + $validPrefixes = [ + $this->servicePrefixQuote, + $this->servicePrefixShipval, + $this->servicePrefixTracking, + ]; + + if (!in_array($servicePrefix, $validPrefixes)) { + throw new \Magento\Framework\Exception\LocalizedException( + __("Invalid service prefix \"$servicePrefix\" provided while attempting to build MessageReference") + ); + } + + return str_replace('.', '', uniqid("MAGE_{$servicePrefix}_", true)); + } + + /** + * Builds a string to be used as the request SoftwareName. + * + * @return string + */ + private function buildSoftwareName(): string + { + return substr($this->productMetadata->getName(), 0, 30); + } + + /** + * Builds a string to be used as the request SoftwareVersion. + * + * @return string + */ + private function buildSoftwareVersion(): string + { + return substr($this->productMetadata->getVersion(), 0, 10); + } } diff --git a/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php b/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php index 41e545412aea1..c3d82ef34a448 100644 --- a/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php +++ b/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php @@ -9,12 +9,14 @@ use Magento\Dhl\Model\Carrier; use Magento\Dhl\Model\Validator\XmlValidator; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ProductMetadataInterface; use Magento\Framework\Filesystem\Directory\Read; use Magento\Framework\Filesystem\Directory\ReadFactory; use Magento\Framework\HTTP\ZendClient; use Magento\Framework\HTTP\ZendClientFactory; use Magento\Framework\Locale\ResolverInterface; use Magento\Framework\Module\Dir\Reader; +use Magento\Framework\Stdlib\DateTime\DateTime; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\Xml\Security; use Magento\Quote\Model\Quote\Address\RateRequest; @@ -84,6 +86,16 @@ class CarrierTest extends \PHPUnit\Framework\TestCase */ private $logger; + /** + * @var DateTime|MockObject + */ + private $coreDateMock; + + /** + * @var ProductMetadataInterface + */ + private $productMetadataMock; + /** * @inheritdoc */ @@ -109,23 +121,39 @@ protected function setUp() $this->logger = $this->getMockForAbstractClass(LoggerInterface::class); + $this->coreDateMock = $this->getMockBuilder(DateTime::class) + ->disableOriginalConstructor() + ->getMock(); + $this->coreDateMock->method('date') + ->willReturn('currentTime'); + + $this->productMetadataMock = $this->getMockBuilder(ProductMetadataInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->productMetadataMock->method('getName') + ->willReturn('Software_Product_Name_30_Char_123456789'); + $this->productMetadataMock->method('getVersion') + ->willReturn('10Char_Ver123456789'); + $this->model = $this->objectManager->getObject( Carrier::class, [ 'scopeConfig' => $this->scope, - 'xmlSecurity' => new Security(), + 'rateErrorFactory' => $this->errorFactory, 'logger' => $this->logger, + 'xmlSecurity' => new Security(), 'xmlElFactory' => $this->getXmlFactory(), 'rateFactory' => $this->getRateFactory(), - 'rateErrorFactory' => $this->errorFactory, 'rateMethodFactory' => $this->getRateMethodFactory(), - 'httpClientFactory' => $this->getHttpClientFactory(), - 'readFactory' => $this->getReadFactory(), - 'storeManager' => $this->getStoreManager(), - 'configReader' => $this->getConfigReader(), 'carrierHelper' => $this->getCarrierHelper(), + 'configReader' => $this->getConfigReader(), + 'storeManager' => $this->getStoreManager(), + 'readFactory' => $this->getReadFactory(), + 'httpClientFactory' => $this->getHttpClientFactory(), 'data' => ['id' => 'dhl', 'store' => '1'], - 'xmlValidator' => $this->xmlValidator + 'xmlValidator' => $this->xmlValidator, + 'coreDate' => $this->coreDateMock, + 'productMetadata' => $this->productMetadataMock ] ); } @@ -242,6 +270,11 @@ public function testCollectRates() $this->httpResponse->method('getBody') ->willReturn($responseXml); + $this->coreDateMock->method('date') + ->willReturnCallback(function () { + return date(\DATE_RFC3339); + }); + $request = $this->objectManager->getObject(RateRequest::class, $requestData); $reflectionClass = new \ReflectionObject($this->httpClient); @@ -300,12 +333,8 @@ public function testCollectRatesErrorMessage() * @throws \Magento\Framework\Exception\LocalizedException * @throws \ReflectionException */ - public function testRequestToShipment( - string $origCountryId, - string $expectedRegionCode, - string $destCountryId, - bool $dutiable - ) { + public function testRequestToShipment(string $origCountryId, string $expectedRegionCode, string $destCountryId) + { $scopeConfigValueMap = [ ['carriers/dhl/account', 'store', null, '1234567890'], ['carriers/dhl/gateway_url', 'store', null, 'https://xmlpi-ea.dhl.com/XMLShippingServlet'], @@ -337,8 +366,14 @@ public function testRequestToShipment( $requestXml = $rawPostData->getValue($this->httpClient); $requestElement = new Element($requestXml); + $messageReference = $requestElement->Request->ServiceHeader->MessageReference->__toString(); + $this->assertStringStartsWith('MAGE_SHIP_', $messageReference); + $this->assertGreaterThanOrEqual(28, strlen($messageReference)); + $this->assertLessThanOrEqual(32, strlen($messageReference)); + $requestElement->Request->ServiceHeader->MessageReference = 'MAGE_SHIP_28TO32_Char_CHECKED'; + $this->assertXmlStringEqualsXmlString( - $this->getExpectedRequestXml($origCountryId, $destCountryId, $expectedRegionCode, $dutiable)->asXML(), + $this->getExpectedRequestXml($origCountryId, $destCountryId, $expectedRegionCode)->asXML(), $requestElement->asXML() ); } @@ -421,25 +456,23 @@ private function getRequest(string $origCountryId, string $destCountryId) * @param string $destCountryId * @return Element */ - private function getExpectedRequestXml( - string $origCountryId, - string $destCountryId, - string $regionCode, - bool $dutiable - ) { - $requestXmlPath = $regionCode === "EU" - ? ($dutiable ? '/_files/euregion_dutiable_shipment_request.xml' : '/_files/euregion_shipment_request.xml') - : '/_files/apregion_shipment_request.xml'; + private function getExpectedRequestXml(string $origCountryId, string $destCountryId, string $regionCode) + { + $requestXmlPath = $origCountryId == $destCountryId + ? '/_files/domestic_shipment_request.xml' + : '/_files/shipment_request.xml'; + + $expectedRequestElement = new Element(file_get_contents(__DIR__ . $requestXmlPath)); - $requestElement = new Element(file_get_contents(__DIR__ . $requestXmlPath)); + $expectedRequestElement->Consignee->CountryCode = $destCountryId; + $expectedRequestElement->Consignee->CountryName = $this->getCountryName($destCountryId); - $requestElement->Consignee->CountryCode = $destCountryId; - $requestElement->Consignee->CountryName = $this->getCountryName($destCountryId); + $expectedRequestElement->Shipper->CountryCode = $origCountryId; + $expectedRequestElement->Shipper->CountryName = $this->getCountryName($origCountryId); - $requestElement->Shipper->CountryCode = $origCountryId; - $requestElement->Shipper->CountryName = $this->getCountryName($origCountryId); + $expectedRequestElement->RegionCode = $regionCode; - return $requestElement; + return $expectedRequestElement; } /** @@ -451,7 +484,7 @@ private function getExpectedRequestXml( private function getCountryName($countryCode) { $countryNames = [ - 'US' => 'United States Of America', + 'US' => 'United States of America', 'SG' => 'Singapore', 'GB' => 'United Kingdom', 'DE' => 'Germany', @@ -468,13 +501,13 @@ public function requestToShipmentDataProvider() { return [ [ - 'GB', 'EU', 'US', 1 + 'GB', 'EU', 'US' ], [ - 'SG', 'AP', 'US', 0 + 'SG', 'AP', 'US' ], [ - 'DE', 'EU', 'DE', 0 + 'DE', 'EU', 'DE' ] ]; } @@ -496,7 +529,7 @@ public function testGetDhlProducts(string $docType, array $products) * * @return array */ - public function dhlProductsDataProvider() : array + public function dhlProductsDataProvider(): array { return [ 'doc' => [ @@ -522,7 +555,7 @@ public function dhlProductsDataProvider() : array 'S' => 'Same day', 'T' => 'Express 12:00', 'X' => 'Express envelope', - ] + ], ], 'non-doc' => [ 'docType' => Carrier::DHL_CONTENT_TYPE_NON_DOC, @@ -540,8 +573,115 @@ public function dhlProductsDataProvider() : array 'M' => 'Express 10:30', 'V' => 'Europack', 'Y' => 'Express 12:00', - ] - ] + ], + ], + ]; + } + + /** + * Tests that the built MessageReference string is of the appropriate format. + * + * @dataProvider buildMessageReferenceDataProvider + * @param $servicePrefix + * @throws \ReflectionException + */ + public function testBuildMessageReference($servicePrefix) + { + $method = new \ReflectionMethod($this->model, 'buildMessageReference'); + $method->setAccessible(true); + + $messageReference = $method->invoke($this->model, $servicePrefix); + $this->assertGreaterThanOrEqual(28, strlen($messageReference)); + $this->assertLessThanOrEqual(32, strlen($messageReference)); + } + + /** + * Build message reference data provider + * + * @return array + */ + public function buildMessageReferenceDataProvider() + { + return [ + 'quote_prefix' => ['QUOT'], + 'shipval_prefix' => ['SHIP'], + 'tracking_prefix' => ['TRCK'] + ]; + } + + /** + * Tests that an exception is thrown when an invalid service prefix is provided. + * + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Invalid service prefix + */ + public function testBuildMessageReferenceInvalidPrefix() + { + $method = new \ReflectionMethod($this->model, 'buildMessageReference'); + $method->setAccessible(true); + + $method->invoke($this->model, 'INVALID'); + } + + /** + * Tests that the built software name string is of the appropriate format. + * + * @dataProvider buildSoftwareNameDataProvider + * @param $productName + * @throws \ReflectionException + */ + public function testBuildSoftwareName($productName) + { + $method = new \ReflectionMethod($this->model, 'buildSoftwareName'); + $method->setAccessible(true); + + $this->productMetadataMock->method('getName')->willReturn($productName); + + $softwareName = $method->invoke($this->model); + $this->assertLessThanOrEqual(30, strlen($softwareName)); + } + + /** + * Data provider for testBuildSoftwareName + * + * @return array + */ + public function buildSoftwareNameDataProvider() + { + return [ + 'valid_length' => ['Magento'], + 'exceeds_length' => ['Product_Name_Longer_Than_30_Char'] + ]; + } + + /** + * Tests that the built software version string is of the appropriate format. + * + * @dataProvider buildSoftwareVersionProvider + * @param $productVersion + * @throws \ReflectionException + */ + public function testBuildSoftwareVersion($productVersion) + { + $method = new \ReflectionMethod($this->model, 'buildSoftwareVersion'); + $method->setAccessible(true); + + $this->productMetadataMock->method('getVersion')->willReturn($productVersion); + + $softwareVersion = $method->invoke($this->model); + $this->assertLessThanOrEqual(10, strlen($softwareVersion)); + } + + /** + * Data provider for testBuildSoftwareVersion + * + * @return array + */ + public function buildSoftwareVersionProvider() + { + return [ + 'valid_length' => ['2.3.1'], + 'exceeds_length' => ['dev-MC-1000'] ]; } @@ -691,7 +831,7 @@ private function getCarrierHelper(): CarrierHelper $carrierHelper = $this->objectManager->getObject( CarrierHelper::class, [ - 'localeResolver' => $localeResolver + 'localeResolver' => $localeResolver, ] ); diff --git a/app/code/Magento/Dhl/Test/Unit/Model/_files/countries.xml b/app/code/Magento/Dhl/Test/Unit/Model/_files/countries.xml index 792465ce45942..fb9188fa37653 100644 --- a/app/code/Magento/Dhl/Test/Unit/Model/_files/countries.xml +++ b/app/code/Magento/Dhl/Test/Unit/Model/_files/countries.xml @@ -1417,7 +1417,7 @@ <weight_unit>LB</weight_unit> <measure_unit>IN</measure_unit> <region>AM</region> - <name>United States Of America</name> + <name>United States of America</name> </US> <UY> <currency>UYU</currency> diff --git a/app/code/Magento/Dhl/Test/Unit/Model/_files/dhl_quote_request_data.php b/app/code/Magento/Dhl/Test/Unit/Model/_files/dhl_quote_request_data.php index f762b99735233..0de635388e583 100644 --- a/app/code/Magento/Dhl/Test/Unit/Model/_files/dhl_quote_request_data.php +++ b/app/code/Magento/Dhl/Test/Unit/Model/_files/dhl_quote_request_data.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + return [ 'data' => [ 'dest_country_id' => 'DE', @@ -24,6 +26,7 @@ 'limit_carrier' => null, 'base_subtotal_incl_tax' => '5', 'orig_country_id' => 'US', + 'country_id' => 'US', 'orig_region_id' => '12', 'orig_city' => 'Fremont', 'orig_postcode' => '94538', @@ -44,5 +47,5 @@ 'ship_date' => '2014-01-09', 'action' => 'RateEstimate', 'all_items' => [], - ] + ], ]; diff --git a/app/code/Magento/Dhl/Test/Unit/Model/_files/dhl_quote_response.xml b/app/code/Magento/Dhl/Test/Unit/Model/_files/dhl_quote_response.xml index 7421707966411..349cfbd4f1ce5 100644 --- a/app/code/Magento/Dhl/Test/Unit/Model/_files/dhl_quote_response.xml +++ b/app/code/Magento/Dhl/Test/Unit/Model/_files/dhl_quote_response.xml @@ -351,14 +351,6 @@ <BookingCutoffOffset>PT1H</BookingCutoffOffset> </QtdShp> <QtdShp> - <OriginServiceArea> - <FacilityCode>NUQ</FacilityCode> - <ServiceAreaCode>NUQ</ServiceAreaCode> - </OriginServiceArea> - <DestinationServiceArea> - <FacilityCode>BER</FacilityCode> - <ServiceAreaCode>BER</ServiceAreaCode> - </DestinationServiceArea> <GlobalProductCode>P</GlobalProductCode> <LocalProductCode>P</LocalProductCode> <ProductShortName>EXPRESS WORLDWIDE</ProductShortName> @@ -376,10 +368,9 @@ <TotalTransitDays>2</TotalTransitDays> <PickupPostalLocAddDays>0</PickupPostalLocAddDays> <DeliveryPostalLocAddDays>0</DeliveryPostalLocAddDays> - <DeliveryDate> - <DlvyDateTime>2014-01-13 11:59:00</DlvyDateTime> - <DeliveryDateTimeOffset>+00:00</DeliveryDateTimeOffset> - </DeliveryDate> + <PickupNonDHLCourierCode /> + <DeliveryNonDHLCourierCode /> + <DeliveryDate>2014-01-13</DeliveryDate> <DeliveryTime>PT23H59M</DeliveryTime> <DimensionalWeight>2.205</DimensionalWeight> <WeightUnit>LB</WeightUnit> @@ -436,9 +427,6 @@ <TotalTaxAmount>0.000</TotalTaxAmount> <WeightChargeTax>0.000</WeightChargeTax> </QtdSInAdCur> - <PickupWindowEarliestTime>09:00:00</PickupWindowEarliestTime> - <PickupWindowLatestTime>17:00:00</PickupWindowLatestTime> - <BookingCutoffOffset>PT1H</BookingCutoffOffset> </QtdShp> </BkgDetails> <Srvs> diff --git a/app/code/Magento/Dhl/Test/Unit/Model/_files/dhl_quote_response_rates.php b/app/code/Magento/Dhl/Test/Unit/Model/_files/dhl_quote_response_rates.php index ddd7b2e4f97c5..5646bb8464bf0 100644 --- a/app/code/Magento/Dhl/Test/Unit/Model/_files/dhl_quote_response_rates.php +++ b/app/code/Magento/Dhl/Test/Unit/Model/_files/dhl_quote_response_rates.php @@ -3,29 +3,31 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + return [ [ 'carrier' => 'dhl', 'carrier_title' => 'DHL Title', 'cost' => 45.85, - 'method' => 'E' + 'method' => 'E', ], [ 'carrier' => 'dhl', 'carrier_title' => 'DHL Title', 'cost' => 35.26, - 'method' => 'Q' + 'method' => 'Q', ], [ 'carrier' => 'dhl', 'carrier_title' => 'DHL Title', 'cost' => 37.38, - 'method' => 'Y' + 'method' => 'Y', ], [ 'carrier' => 'dhl', 'carrier_title' => 'DHL Title', 'cost' => 35.26, - 'method' => 'P' - ] + 'method' => 'P', + ], ]; diff --git a/app/code/Magento/Dhl/Test/Unit/Model/_files/domestic_shipment_request.xml b/app/code/Magento/Dhl/Test/Unit/Model/_files/domestic_shipment_request.xml new file mode 100644 index 0000000000000..e4d72bb94e78e --- /dev/null +++ b/app/code/Magento/Dhl/Test/Unit/Model/_files/domestic_shipment_request.xml @@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<req:ShipmentRequest xmlns:req="http://www.dhl.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.dhl.com ship-val-global-req-6.0.xsd" schemaVersion="6.0"> + <Request xmlns=""> + <ServiceHeader> + <MessageTime>currentTime</MessageTime> + <MessageReference>MAGE_SHIP_28TO32_Char_CHECKED</MessageReference> + <SiteID>some ID</SiteID> + <Password>some password</Password> + </ServiceHeader> + </Request> + <RegionCode xmlns="">CHECKED</RegionCode> + <RequestedPickupTime xmlns="">N</RequestedPickupTime> + <NewShipper xmlns="">N</NewShipper> + <LanguageCode xmlns="">EN</LanguageCode> + <PiecesEnabled xmlns="">Y</PiecesEnabled> + <Billing xmlns=""> + <ShipperAccountNumber>1234567890</ShipperAccountNumber> + <ShippingPaymentType>S</ShippingPaymentType> + <BillingAccountNumber>1234567890</BillingAccountNumber> + <DutyPaymentType>S</DutyPaymentType> + <DutyAccountNumber>1234567890</DutyAccountNumber> + </Billing> + <Consignee xmlns=""> + <CompanyName/> + <AddressLine/> + <City/> + <PostalCode/> + <CountryCode/> + <CountryName/> + <Contact> + <PersonName/> + <PhoneNumber/> + </Contact> + </Consignee> + <Commodity xmlns=""> + <CommodityCode>1</CommodityCode> + </Commodity> + <Reference xmlns=""> + <ReferenceID>shipment reference</ReferenceID> + <ReferenceType>St</ReferenceType> + </Reference> + <ShipmentDetails xmlns=""> + <NumberOfPieces>1</NumberOfPieces> + <Pieces xmlns=""> + <Piece xmlns=""> + <PieceID>1</PieceID> + <PackageType>CP</PackageType> + <Weight>0.454</Weight> + <Width>3</Width> + <Height>3</Height> + <Depth>3</Depth> + <PieceContents>item_name</PieceContents> + </Piece> + </Pieces> + <Weight>0.454</Weight> + <WeightUnit>K</WeightUnit> + <GlobalProductCode/> + <LocalProductCode/> + <Date>currentTime</Date> + <Contents>DHL Parcel</Contents> + <DoorTo>DD</DoorTo> + <DimensionUnit>C</DimensionUnit> + <PackageType>CP</PackageType> + <CurrencyCode>USD</CurrencyCode> + </ShipmentDetails> + <Shipper xmlns=""> + <ShipperID>1234567890</ShipperID> + <CompanyName/> + <RegisteredAccount>1234567890</RegisteredAccount> + <AddressLine/> + <City/> + <PostalCode/> + <CountryCode/> + <CountryName/> + <Contact xmlns=""> + <PersonName/> + <PhoneNumber/> + </Contact> + </Shipper> + <LabelImageFormat xmlns="">PDF</LabelImageFormat> +</req:ShipmentRequest> diff --git a/app/code/Magento/Dhl/Test/Unit/Model/_files/shipment_request.xml b/app/code/Magento/Dhl/Test/Unit/Model/_files/shipment_request.xml new file mode 100644 index 0000000000000..d411041c96072 --- /dev/null +++ b/app/code/Magento/Dhl/Test/Unit/Model/_files/shipment_request.xml @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<req:ShipmentRequest xmlns:req="http://www.dhl.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.dhl.com ship-val-global-req-6.0.xsd" schemaVersion="6.0"> + <Request xmlns=""> + <ServiceHeader> + <MessageTime>currentTime</MessageTime> + <MessageReference>MAGE_SHIP_28TO32_Char_CHECKED</MessageReference> + <SiteID>some ID</SiteID> + <Password>some password</Password> + </ServiceHeader> + </Request> + <RegionCode xmlns="">CHECKED</RegionCode> + <RequestedPickupTime xmlns="">N</RequestedPickupTime> + <NewShipper xmlns="">N</NewShipper> + <LanguageCode xmlns="">EN</LanguageCode> + <PiecesEnabled xmlns="">Y</PiecesEnabled> + <Billing xmlns=""> + <ShipperAccountNumber>1234567890</ShipperAccountNumber> + <ShippingPaymentType>S</ShippingPaymentType> + <BillingAccountNumber>1234567890</BillingAccountNumber> + <DutyPaymentType>S</DutyPaymentType> + <DutyAccountNumber>1234567890</DutyAccountNumber> + </Billing> + <Consignee xmlns=""> + <CompanyName/> + <AddressLine/> + <City/> + <PostalCode/> + <CountryCode/> + <CountryName/> + <Contact> + <PersonName/> + <PhoneNumber/> + </Contact> + </Consignee> + <Commodity xmlns=""> + <CommodityCode>1</CommodityCode> + </Commodity> + <Dutiable xmlns=""> + <DeclaredValue>10.00</DeclaredValue> + <DeclaredCurrency>USD</DeclaredCurrency> + </Dutiable> + <Reference xmlns=""> + <ReferenceID>shipment reference</ReferenceID> + <ReferenceType>St</ReferenceType> + </Reference> + <ShipmentDetails xmlns=""> + <NumberOfPieces>1</NumberOfPieces> + <Pieces xmlns=""> + <Piece xmlns=""> + <PieceID>1</PieceID> + <PackageType>CP</PackageType> + <Weight>0.454</Weight> + <Width>3</Width> + <Height>3</Height> + <Depth>3</Depth> + <PieceContents>item_name</PieceContents> + </Piece> + </Pieces> + <Weight>0.454</Weight> + <WeightUnit>K</WeightUnit> + <GlobalProductCode/> + <LocalProductCode/> + <Date>currentTime</Date> + <Contents>DHL Parcel</Contents> + <DoorTo>DD</DoorTo> + <DimensionUnit>C</DimensionUnit> + <PackageType>CP</PackageType> + <IsDutiable>Y</IsDutiable> + <CurrencyCode>USD</CurrencyCode> + </ShipmentDetails> + <Shipper xmlns=""> + <ShipperID>1234567890</ShipperID> + <CompanyName/> + <RegisteredAccount>1234567890</RegisteredAccount> + <AddressLine/> + <City/> + <PostalCode/> + <CountryCode/> + <CountryName/> + <Contact xmlns=""> + <PersonName/> + <PhoneNumber/> + </Contact> + </Shipper> + <LabelImageFormat xmlns="">PDF</LabelImageFormat> +</req:ShipmentRequest> diff --git a/app/code/Magento/Dhl/etc/countries.xml b/app/code/Magento/Dhl/etc/countries.xml index 48837dbefb576..792465ce45942 100644 --- a/app/code/Magento/Dhl/etc/countries.xml +++ b/app/code/Magento/Dhl/etc/countries.xml @@ -83,7 +83,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Austria</name> <domestic>1</domestic> </AT> @@ -132,7 +132,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Belgium</name> <domestic>1</domestic> </BE> @@ -146,7 +146,7 @@ <currency>BGN</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Bulgaria</name> <domestic>1</domestic> </BG> @@ -257,7 +257,7 @@ <currency>CHF</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Switzerland</name> </CH> <CI> @@ -331,7 +331,7 @@ <currency>CZK</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Czech Republic, The</name> <domestic>1</domestic> </CZ> @@ -339,7 +339,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Germany</name> <domestic>1</domestic> </DE> @@ -353,7 +353,7 @@ <currency>DKK</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Denmark</name> <domestic>1</domestic> </DK> @@ -389,7 +389,7 @@ <currency>EEK</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Estonia</name> <domestic>1</domestic> </EE> @@ -410,7 +410,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Spain</name> <domestic>1</domestic> </ES> @@ -424,7 +424,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Finland</name> <domestic>1</domestic> </FI> @@ -457,7 +457,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>France</name> <domestic>1</domestic> </FR> @@ -471,7 +471,7 @@ <currency>GBP</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>United Kingdom</name> <domestic>1</domestic> </GB> @@ -549,7 +549,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Greece</name> <domestic>1</domestic> </GR> @@ -612,7 +612,7 @@ <currency>HUF</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Hungary</name> <domestic>1</domestic> </HU> @@ -633,7 +633,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Ireland, Republic Of</name> <domestic>1</domestic> </IE> @@ -668,14 +668,14 @@ <currency>ISK</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Iceland</name> </IS> <IT> <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Italy</name> <domestic>1</domestic> </IT> @@ -834,7 +834,7 @@ <currency>LTL</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Lithuania</name> <domestic>1</domestic> </LT> @@ -842,7 +842,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Luxembourg</name> <domestic>1</domestic> </LU> @@ -850,7 +850,7 @@ <currency>LVL</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Latvia</name> <domestic>1</domestic> </LV> @@ -1039,7 +1039,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Netherlands, The</name> <domestic>1</domestic> </NL> @@ -1047,7 +1047,7 @@ <currency>NOK</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Norway</name> </NO> <NP> @@ -1127,7 +1127,7 @@ <currency>PLN</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Poland</name> <domestic>1</domestic> </PL> @@ -1142,7 +1142,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Portugal</name> <domestic>1</domestic> </PT> @@ -1177,7 +1177,7 @@ <currency>RON</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Romania</name> <domestic>1</domestic> </RO> @@ -1231,7 +1231,7 @@ <currency>SEK</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Sweden</name> <domestic>1</domestic> </SE> @@ -1246,7 +1246,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Slovenia</name> <domestic>1</domestic> </SI> @@ -1254,7 +1254,7 @@ <currency>EUR</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> - <region>EA</region> + <region>EU</region> <name>Slovakia</name> <domestic>1</domestic> </SK> diff --git a/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php b/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php new file mode 100644 index 0000000000000..1f9287e03342f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php @@ -0,0 +1,252 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Dhl\Model; + +use Magento\Framework\HTTP\ZendClient; +use Magento\Framework\HTTP\ZendClientFactory; +use Magento\Framework\Simplexml\Element; +use Magento\Shipping\Model\Tracking\Result\Status; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * Test DHL Shipping Method. + */ +class CarrierTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Dhl\Model\Carrier + */ + private $dhlCarrier; + + /** + * @var ZendClient|MockObject + */ + private $httpClientMock; + + /** + * @var \Zend_Http_Response|MockObject + */ + private $httpResponseMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->dhlCarrier = $objectManager->create( + \Magento\Dhl\Model\Carrier::class, + ['httpClientFactory' => $this->getHttpClientFactory()] + ); + } + + /** + * @magentoDbIsolation enabled + * + * @magentoConfigFixture default_store carriers/dhl/id CustomerSiteID + * @magentoConfigFixture default_store carriers/dhl/password CustomerPassword + * + * @param array $trackingNumbers + * @param string $responseXml + * @param array $expectedTrackingData + * @param string $expectedRequestXml + * @dataProvider getTrackingDataProvider + * + * @return void + */ + public function testGetTracking( + array $trackingNumbers, + string $responseXml, + array $expectedTrackingData, + string $expectedRequestXml = '' + ) { + $this->httpResponseMock->method('getBody') + ->willReturn($responseXml); + $trackingResult = $this->dhlCarrier->getTracking($trackingNumbers); + $this->assertTrackingResult($expectedTrackingData, $trackingResult->getAllTrackings()); + if ($expectedRequestXml !== '') { + $method = new \ReflectionMethod($this->httpClientMock, '_prepareBody'); + $method->setAccessible(true); + $requestXml = $method->invoke($this->httpClientMock); + $this->assertRequest($expectedRequestXml, $requestXml); + } + } + + /** + * Get tracking data provider. + * + * @return array + */ + public function getTrackingDataProvider(): array + { + $expectedMultiAWBRequestXml = file_get_contents(__DIR__ . '/../_files/TrackingRequest_MultipleAWB.xml'); + $multiAWBResponseXml = file_get_contents(__DIR__ . '/../_files/TrackingResponse_MultipleAWB.xml'); + $expectedSingleAWBRequestXml = file_get_contents(__DIR__ . '/../_files/TrackingRequest_SingleAWB.xml'); + $singleAWBResponseXml = file_get_contents(__DIR__ . '/../_files/TrackingResponse_SingleAWB.xml'); + $singleNoDataResponseXml = file_get_contents(__DIR__ . '/../_files/SingleknownTrackResponse-no-data-found.xml'); + $failedResponseXml = file_get_contents(__DIR__ . '/../_files/Track-res-XML-Parse-Err.xml'); + $expectedTrackingDataA = [ + 'carrier' => 'dhl', + 'carrier_title' => 'DHL', + 'tracking' => 4781584780, + 'service' => 'DOCUMENT', + 'progressdetail' => [ + [ + 'activity' => 'SD Shipment information received', + 'deliverydate' => '2017-12-25', + 'deliverytime' => '14:38:00', + 'deliverylocation' => 'BEIJING-CHN [PEK]', + ], + ], + 'weight' => '0.5 K', + ]; + $expectedTrackingDataB = [ + 'carrier' => 'dhl', + 'carrier_title' => 'DHL', + 'tracking' => 4781585060, + 'service' => 'NOT RESTRICTED FOR TRANSPORT,', + 'progressdetail' => [ + [ + 'activity' => 'SD Shipment information received', + 'deliverydate' => '2017-12-24', + 'deliverytime' => '13:35:00', + 'deliverylocation' => 'HONG KONG-HKG [HKG]', + ], + ], + 'weight' => '2.0 K', + ]; + $expectedTrackingDataC = [ + 'carrier' => 'dhl', + 'carrier_title' => 'DHL', + 'tracking' => 5702254250, + 'service' => 'CD', + 'progressdetail' => [ + [ + 'activity' => 'SD Shipment information received', + 'deliverydate' => '2017-12-24', + 'deliverytime' => '04:12:00', + 'deliverylocation' => 'BIRMINGHAM-GBR [BHX]', + ], + ], + 'weight' => '0.12 K', + ]; + $expectedTrackingDataD = [ + 'carrier' => 'dhl', + 'carrier_title' => 'DHL', + 'tracking' => 4781585060, + 'error_message' => __('Unable to retrieve tracking'), + ]; + $expectedTrackingDataE = [ + 'carrier' => 'dhl', + 'carrier_title' => 'DHL', + 'tracking' => 111, + 'error_message' => __( + 'Error #%1 : %2', + '111', + ' Error Parsing incoming request XML + Error: The content of element type + "ShipperReference" must match + "(ReferenceID,ReferenceType?)". at line + 16, column 22' + ), + ]; + + return [ + 'multi-AWB' => [ + ['4781584780', '4781585060', '5702254250'], + $multiAWBResponseXml, + [$expectedTrackingDataA, $expectedTrackingDataB, $expectedTrackingDataC], + $expectedMultiAWBRequestXml, + ], + 'single-AWB' => [ + ['4781585060'], + $singleAWBResponseXml, + [$expectedTrackingDataB], + $expectedSingleAWBRequestXml, + ], + 'single-AWB-no-data' => [ + ['4781585061'], + $singleNoDataResponseXml, + [$expectedTrackingDataD], + ], + 'failed-response' => [ + ['4781585060-failed'], + $failedResponseXml, + [$expectedTrackingDataE], + ], + ]; + } + + /** + * Get mocked Http Client Factory. + * + * @return MockObject + */ + private function getHttpClientFactory(): MockObject + { + $this->httpResponseMock = $this->getMockBuilder(\Zend_Http_Response::class) + ->disableOriginalConstructor() + ->getMock(); + $this->httpClientMock = $this->getMockBuilder(ZendClient::class) + ->disableOriginalConstructor() + ->setMethods(['request']) + ->getMock(); + $this->httpClientMock->method('request') + ->willReturn($this->httpResponseMock); + /** @var ZendClientFactory|MockObject $httpClientFactoryMock */ + $httpClientFactoryMock = $this->getMockBuilder(ZendClientFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $httpClientFactoryMock->method('create') + ->willReturn($this->httpClientMock); + + return $httpClientFactoryMock; + } + + /** + * Assert request. + * + * @param string $expectedRequestXml + * @param string $requestXml + * + * @return void + */ + private function assertRequest(string $expectedRequestXml, string $requestXml) + { + $expectedRequestElement = new Element($expectedRequestXml); + $requestElement = new Element($requestXml); + $requestMessageTime = $requestElement->Request->ServiceHeader->MessageTime->__toString(); + $this->assertRegexp( + "/\d{4}\-\d{2}\-\d{2}T\d{2}\:\d{2}\:\d{2}\+\d{2}\:\d{2}/", + $requestMessageTime + ); + $expectedRequestElement->Request->ServiceHeader->MessageTime = $requestMessageTime; + $messageReference = $requestElement->Request->ServiceHeader->MessageReference->__toString(); + $this->assertStringStartsWith('MAGE_TRCK_', $messageReference); + $this->assertGreaterThanOrEqual(28, strlen($messageReference)); + $this->assertLessThanOrEqual(32, strlen($messageReference)); + $requestElement->Request->ServiceHeader->MessageReference = 'MAGE_TRCK_28TO32_Char_CHECKED'; + $this->assertXmlStringEqualsXmlString($expectedRequestElement->asXML(), $requestElement->asXML()); + } + + /** + * Assert tracking. + * + * @param array|null $expectedTrackingData + * @param Status[]|null $trackingResults + * + * @return void + */ + private function assertTrackingResult($expectedTrackingData, $trackingResults) + { + $ctr = 0; + foreach ($trackingResults as $trackingResult) { + $this->assertEquals($expectedTrackingData[$ctr++], $trackingResult->getData()); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Dhl/_files/SingleknownTrackResponse-no-data-found.xml b/dev/tests/integration/testsuite/Magento/Dhl/_files/SingleknownTrackResponse-no-data-found.xml new file mode 100644 index 0000000000000..80e7b42e4c534 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Dhl/_files/SingleknownTrackResponse-no-data-found.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<req:TrackingResponse xmlns:req="http://www.dhl.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.dhl.com TrackingResponse.xsd"> + <Response> + <ServiceHeader> + <MessageTime>2018-02-27T12:59:34+01:00</MessageTime> + <MessageReference>1234567890123456789012345678</MessageReference> + <SiteID>CustomerSiteID</SiteID> + </ServiceHeader> + </Response> + <AWBInfo> + <AWBNumber>4781585060</AWBNumber> + <Status> + <ActionStatus>No Shipments Found</ActionStatus> + <Condition> + <ConditionCode>209</ConditionCode> + <ConditionData>No Shipments Found for AWBNumber 6017300993</ConditionData> + </Condition> + </Status> + </AWBInfo> + <LanguageCode>String</LanguageCode> +</req:TrackingResponse> + <!-- ServiceInvocationId:20180227125934_5793_74fbd9e1-a8b0-4f6a-a326-26aae979e5f0 --> diff --git a/dev/tests/integration/testsuite/Magento/Dhl/_files/Track-res-XML-Parse-Err.xml b/dev/tests/integration/testsuite/Magento/Dhl/_files/Track-res-XML-Parse-Err.xml new file mode 100644 index 0000000000000..a3b4729fb21ae --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Dhl/_files/Track-res-XML-Parse-Err.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<req:ShipmentTrackingErrorResponse xmlns:req="http://www.dhl.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.dhl.com track-err-res.xsd"> + <Response> + <ServiceHeader> + <MessageTime>2018-02-27T12:55:05+01:00</MessageTime> + </ServiceHeader> + <Status> + <ActionStatus>Failure</ActionStatus> + <Condition> + <ConditionCode>111</ConditionCode> + <ConditionData> Error Parsing incoming request XML + Error: The content of element type + "ShipperReference" must match + "(ReferenceID,ReferenceType?)". at line + 16, column 22</ConditionData> + </Condition> + </Status> + </Response> +</req:ShipmentTrackingErrorResponse> + <!-- ServiceInvocationId:20180227125505_5793_2008671c-9292-4790-87b6-b02ccdf913db --> diff --git a/dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingRequest_MultipleAWB.xml b/dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingRequest_MultipleAWB.xml new file mode 100644 index 0000000000000..fefadf2d4ebde --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingRequest_MultipleAWB.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<req:KnownTrackingRequest xmlns:req="http://www.dhl.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.dhl.com TrackingRequestKnown-1.0.xsd" schemaVersion="1.0"> + <Request> + <ServiceHeader> + <MessageTime>2002-06-25T11:28:56-08:00</MessageTime> + <MessageReference>MAGE_TRCK_28TO32_Char_CHECKED</MessageReference> + <SiteID>CustomerSiteID</SiteID> + <Password>CustomerPassword</Password> + </ServiceHeader> + </Request> + <LanguageCode>EN</LanguageCode> + <AWBNumber>4781584780</AWBNumber> + <AWBNumber>4781585060</AWBNumber> + <AWBNumber>5702254250</AWBNumber> + <LevelOfDetails>ALL_CHECK_POINTS</LevelOfDetails> +</req:KnownTrackingRequest> + + diff --git a/dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingRequest_SingleAWB.xml b/dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingRequest_SingleAWB.xml new file mode 100644 index 0000000000000..e9968e1464906 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingRequest_SingleAWB.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<req:KnownTrackingRequest xmlns:req="http://www.dhl.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.dhl.com TrackingRequestKnown-1.0.xsd" schemaVersion="1.0"> + <Request> + <ServiceHeader> + <MessageTime>2002-06-25T11:28:56-08:00</MessageTime> + <MessageReference>MAGE_TRCK_28TO32_Char_CHECKED</MessageReference> + <SiteID>CustomerSiteID</SiteID> + <Password>CustomerPassword</Password> + </ServiceHeader> + </Request> + <LanguageCode>EN</LanguageCode> + <AWBNumber>4781585060</AWBNumber> + <LevelOfDetails>ALL_CHECK_POINTS</LevelOfDetails> +</req:KnownTrackingRequest> diff --git a/dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingResponse_MultipleAWB.xml b/dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingResponse_MultipleAWB.xml new file mode 100644 index 0000000000000..618bbb4de8e78 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingResponse_MultipleAWB.xml @@ -0,0 +1,174 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<req:TrackingResponse xmlns:req="http://www.dhl.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.dhl.com TrackingResponse.xsd"> + <Response> + <ServiceHeader> + <MessageTime>2018-02-27T12:43:44+01:00</MessageTime> + <MessageReference>1234567890123456789012345678</MessageReference> + <SiteID>CustomerSiteID</SiteID> + </ServiceHeader> + </Response> + <AWBInfo> + <AWBNumber>4781584780</AWBNumber> + <Status> + <ActionStatus>success</ActionStatus> + </Status> + <ShipmentInfo> + <OriginServiceArea> + <ServiceAreaCode>PEK</ServiceAreaCode> + <Description>BEIJING-CHN</Description> + </OriginServiceArea> + <DestinationServiceArea> + <ServiceAreaCode>PHL</ServiceAreaCode> + <Description>WEST PHILADELPHIA,PA-USA</Description> + </DestinationServiceArea> + <ShipperName>THE EXP HIGH SCH ATT TO BNU</ShipperName> + <ShipperAccountNumber>123456789</ShipperAccountNumber> + <ConsigneeName>HAVEFORD COLLEGE</ConsigneeName> + <ShipmentDate>2017-12-25T14:38:00</ShipmentDate> + <Pieces>1</Pieces> + <Weight>0.5</Weight> + <WeightUnit>K</WeightUnit> + <GlobalProductCode>D</GlobalProductCode> + <ShipmentDesc>DOCUMENT</ShipmentDesc> + <DlvyNotificationFlag>Y</DlvyNotificationFlag> + <Shipper> + <City>BEIJING</City> + <PostalCode>100032</PostalCode> + <CountryCode>CN</CountryCode> + </Shipper> + <Consignee> + <City>HAVERFORD</City> + <DivisionCode>PA</DivisionCode> + <PostalCode>19041</PostalCode> + <CountryCode>US</CountryCode> + </Consignee> + <ShipperReference> + <ReferenceID>2469</ReferenceID> + </ShipperReference> + <ShipmentEvent> + <Date>2017-12-25</Date> + <Time>14:38:00</Time> + <ServiceEvent> + <EventCode>SD</EventCode> + <Description>Shipment information received</Description> + </ServiceEvent> + <Signatory/> + <ServiceArea> + <ServiceAreaCode>PEK</ServiceAreaCode> + <Description>BEIJING-CHN</Description> + </ServiceArea> + </ShipmentEvent> + </ShipmentInfo> + </AWBInfo> + <AWBInfo> + <AWBNumber>4781585060</AWBNumber> + <Status> + <ActionStatus>success</ActionStatus> + </Status> + <ShipmentInfo> + <OriginServiceArea> + <ServiceAreaCode>HKG</ServiceAreaCode> + <Description>HONG KONG-HKG</Description> + </OriginServiceArea> + <DestinationServiceArea> + <ServiceAreaCode>HKG</ServiceAreaCode> + <Description>HONG KONG-HKG</Description> + </DestinationServiceArea> + <ShipperName>NET-A-PORTER</ShipperName> + <ShipperAccountNumber>123456789</ShipperAccountNumber> + <ConsigneeName>NICOLE LI</ConsigneeName> + <ShipmentDate>2017-12-24T13:35:00</ShipmentDate> + <Pieces>1</Pieces> + <Weight>2.0</Weight> + <WeightUnit>K</WeightUnit> + <GlobalProductCode>N</GlobalProductCode> + <ShipmentDesc>NOT RESTRICTED FOR TRANSPORT,</ShipmentDesc> + <DlvyNotificationFlag>Y</DlvyNotificationFlag> + <Shipper> + <City>HONG KONG</City> + <CountryCode>HK</CountryCode> + </Shipper> + <Consignee> + <City>HONG KONG</City> + <DivisionCode>CH</DivisionCode> + <CountryCode>HK</CountryCode> + </Consignee> + <ShipperReference> + <ReferenceID>1060571</ReferenceID> + </ShipperReference> + <ShipmentEvent> + <Date>2017-12-24</Date> + <Time>13:35:00</Time> + <ServiceEvent> + <EventCode>SD</EventCode> + <Description>Shipment information received</Description> + </ServiceEvent> + <Signatory/> + <ServiceArea> + <ServiceAreaCode>HKG</ServiceAreaCode> + <Description>HONG KONG-HKG</Description> + </ServiceArea> + </ShipmentEvent> + </ShipmentInfo> + </AWBInfo> + <AWBInfo> + <AWBNumber>5702254250</AWBNumber> + <Status> + <ActionStatus>success</ActionStatus> + </Status> + <ShipmentInfo> + <OriginServiceArea> + <ServiceAreaCode>BHX</ServiceAreaCode> + <Description>BIRMINGHAM-GBR</Description> + </OriginServiceArea> + <DestinationServiceArea> + <ServiceAreaCode>AOI</ServiceAreaCode> + <Description>ANCONA-ITA</Description> + </DestinationServiceArea> + <ShipperName>AMAZON EU SARL</ShipperName> + <ShipperAccountNumber>123456789</ShipperAccountNumber> + <ConsigneeName>MATTEO LOMBO</ConsigneeName> + <ShipmentDate>2017-12-24T04:12:00</ShipmentDate> + <Pieces>1</Pieces> + <Weight>0.12</Weight> + <WeightUnit>K</WeightUnit> + <GlobalProductCode>U</GlobalProductCode> + <ShipmentDesc>CD</ShipmentDesc> + <DlvyNotificationFlag>Y</DlvyNotificationFlag> + <Shipper> + <City>PETERBOROUGH</City> + <PostalCode>PE2 9EN</PostalCode> + <CountryCode>GB</CountryCode> + </Shipper> + <Consignee> + <City>ORTONA</City> + <PostalCode>66026</PostalCode> + <CountryCode>IT</CountryCode> + </Consignee> + <ShipperReference> + <ReferenceID>DGWYDy4xN_1</ReferenceID> + </ShipperReference> + <ShipmentEvent> + <Date>2017-12-24</Date> + <Time>04:12:00</Time> + <ServiceEvent> + <EventCode>SD</EventCode> + <Description>Shipment information received</Description> + </ServiceEvent> + <Signatory/> + <ServiceArea> + <ServiceAreaCode>BHX</ServiceAreaCode> + <Description>BIRMINGHAM-GBR</Description> + </ServiceArea> + </ShipmentEvent> + </ShipmentInfo> + </AWBInfo> + <LanguageCode>en</LanguageCode> +</req:TrackingResponse> + <!-- ServiceInvocationId:20180227124344_5793_23bed3d9-e792-4955-8055-9472b1b41929 --> diff --git a/dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingResponse_SingleAWB.xml b/dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingResponse_SingleAWB.xml new file mode 100644 index 0000000000000..fa31b898b7a1f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Dhl/_files/TrackingResponse_SingleAWB.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<req:TrackingResponse xmlns:req="http://www.dhl.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.dhl.com TrackingResponse.xsd"> + <Response> + <ServiceHeader> + <MessageTime>2018-02-27T12:27:42+01:00</MessageTime> + <MessageReference>1234567890123456789012345678</MessageReference> + <SiteID>CustomerSiteID</SiteID> + </ServiceHeader> + </Response> + <AWBInfo> + <AWBNumber>4781585060</AWBNumber> + <Status> + <ActionStatus>success</ActionStatus> + </Status> + <ShipmentInfo> + <OriginServiceArea> + <ServiceAreaCode>HKG</ServiceAreaCode> + <Description>HONG KONG-HKG</Description> + </OriginServiceArea> + <DestinationServiceArea> + <ServiceAreaCode>HKG</ServiceAreaCode> + <Description>HONG KONG-HKG</Description> + </DestinationServiceArea> + <ShipperName>NET-A-PORTER</ShipperName> + <ShipperAccountNumber>123456789</ShipperAccountNumber> + <ConsigneeName>NICOLE LI</ConsigneeName> + <ShipmentDate>2017-12-24T13:35:00</ShipmentDate> + <Pieces>1</Pieces> + <Weight>2.0</Weight> + <WeightUnit>K</WeightUnit> + <GlobalProductCode>N</GlobalProductCode> + <ShipmentDesc>NOT RESTRICTED FOR TRANSPORT,</ShipmentDesc> + <DlvyNotificationFlag>Y</DlvyNotificationFlag> + <Shipper> + <City>HONG KONG</City> + <CountryCode>HK</CountryCode> + </Shipper> + <Consignee> + <City>HONG KONG</City> + <DivisionCode>CH</DivisionCode> + <CountryCode>HK</CountryCode> + </Consignee> + <ShipperReference> + <ReferenceID>1060571</ReferenceID> + </ShipperReference> + <ShipmentEvent> + <Date>2017-12-24</Date> + <Time>13:35:00</Time> + <ServiceEvent> + <EventCode>SD</EventCode> + <Description>Shipment information received</Description> + </ServiceEvent> + <Signatory/> + <ServiceArea> + <ServiceAreaCode>HKG</ServiceAreaCode> + <Description>HONG KONG-HKG</Description> + </ServiceArea> + </ShipmentEvent> + </ShipmentInfo> + </AWBInfo> + <LanguageCode>en</LanguageCode> +</req:TrackingResponse> + <!-- ServiceInvocationId:20180227122741_5793_e0f8c40e-5245-4737-ab31-323030366721 --> From 9df2936fd709a38b7930d42783713a683d064d04 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Fri, 12 Apr 2019 16:03:32 +0300 Subject: [PATCH 1119/1295] MAGETWO-73157: [GITHUB] Import customizable options adds it to another product if same SKU is filled#9457 --- .../Model/Product/Option/SaveHandler.php | 2 +- .../ActionGroup/CustomOptionsActionGroup.xml | 17 +++++++++-------- .../Test/Mftf/Page/AdminProductCreatePage.xml | 3 ++- ...tCustomizableOptionsImportModalSection.xml | 16 ++++++++++++++++ ...AdminProductCustomizableOptionsSection.xml | 19 +++++-------------- 5 files changed, 33 insertions(+), 24 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsImportModalSection.xml diff --git a/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php b/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php index c55df92ed6dfd..6eec110e76c10 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php @@ -60,7 +60,7 @@ public function execute($entity, $arguments = []) } } if ($options) { - $this->processOptionsSaving($options, (bool)$entity->dataHasChangedFor('sku'), (string)$entity->getSku()); + $this->processOptionsSaving($options, $entity->dataHasChangedFor('sku'), $entity->getSku()); } return $entity; diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml index f157808c0045a..233a62afbbb80 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml @@ -31,14 +31,14 @@ <argument name="productName" type="string"/> </arguments> <click selector="{{AdminProductCustomizableOptionsSection.importOptions}}" stepKey="clickImportOptions"/> - <waitForElementVisible selector="{{AdminProductImportOptionsSection.selectProductTitle}}" stepKey="waitForTitleVisible"/> - <conditionalClick selector="{{AdminProductImportOptionsSection.resetFiltersButton}}" dependentSelector="{{AdminProductImportOptionsSection.resetFiltersButton}}" visible="true" stepKey="clickResetFilters"/> - <click selector="{{AdminProductImportOptionsSection.filterButton}}" stepKey="clickFilterButton"/> - <waitForElementVisible selector="{{AdminProductImportOptionsSection.nameField}}" stepKey="waitForNameField"/> - <fillField selector="{{AdminProductImportOptionsSection.nameField}}" userInput="{{productName}}" stepKey="fillProductName"/> - <click selector="{{AdminProductImportOptionsSection.applyFiltersButton}}" stepKey="clickApplyFilters"/> - <checkOption selector="{{AdminProductImportOptionsSection.firstRowItemCheckbox}}" stepKey="checkProductCheckbox"/> - <click selector="{{AdminProductImportOptionsSection.importButton}}" stepKey="clickImport"/> + <waitForElementVisible selector="{{AdminProductCustomizableOptionsImportModalSection.selectProductTitle}}" stepKey="waitForTitleVisible"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickResetFilters"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="clickFilterButton"/> + <waitForElementVisible selector="{{AdminProductCustomizableOptionsImportModalSection.nameField}}" stepKey="waitForNameField"/> + <fillField selector="{{AdminProductCustomizableOptionsImportModalSection.nameField}}" userInput="{{productName}}" stepKey="fillProductName"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFilters"/> + <checkOption selector="{{AdminDataGridTableSection.rowCheckbox('1')}}" stepKey="checkProductCheckbox"/> + <click selector="{{AdminProductCustomizableOptionsImportModalSection.importButton}}" stepKey="clickImport"/> </actionGroup> <actionGroup name="CheckCustomizableOptionImport"> <arguments> @@ -53,3 +53,4 @@ <assertEquals expected="{{option.title}}" expectedType="string" actual="$grabOptionSku" stepKey="assertOptionSku"/> </actionGroup> </actionGroups> + diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml index 8c8ae0ede2eb8..164ec8cb28a22 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml @@ -7,7 +7,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"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminProductCreatePage" url="catalog/product/new/set/{{set}}/type/{{type}}/" area="admin" module="Magento_Catalog" parameterized="true"> <section name="AdminProductFormSection"/> <section name="AdminProductFormActionSection"/> @@ -18,5 +18,6 @@ <section name="AdminProductCustomizableOptionsSection" /> <section name="AdminAddProductsToOptionPanelSection" /> <section name="AdminProductFormAdvancedPricingSection"/> + <section name="AdminProductCustomizableOptionsImportModalSection"/> </page> </pages> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsImportModalSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsImportModalSection.xml new file mode 100644 index 0000000000000..f9d9f531a9b8b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsImportModalSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminProductCustomizableOptionsImportModalSection"> + <element name="selectProductTitle" type="text" selector="//aside[contains(@class, '_show')]//h1[normalize-space(text())='Select Product']"/> + <element name="nameField" type="input" selector="input[name='name']"/> + <element name="importButton" type="button" selector="//aside[contains(@class, '_show')]//button[contains(@class, 'action-primary') and normalize-space(.)='Import']" timeout="30"/> + </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 c2458bcb41d78..a65bfa86d97bc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml @@ -26,22 +26,13 @@ <element name="lastOptionTypeParent" type="block" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//*[contains(@class, 'admin__action-multiselect-text')]" /> <!-- var 1 represents the option type that you want to select, i.e "radio buttons" --> <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()]//input[contains(@name,'price')]"/> - <element name="optionPriceType" type="select" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//select[contains(@name,'price_type')]"/> - <element name="optionFileExtensions" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//input[contains(@name,'file_extension')]"/> - <element name="optionSku" type="input" selector="//*[@data-index='custom_options']//*[@data-index='options']/tbody/tr[last()]//input[contains(@name,'sku')]"/> + <element name="optionPrice" type="input" selector="[data-index='custom_options'] [data-index='options'] tbody tr:nth-last-of-type(1) input[name*='price']"/> + <element name="optionPriceType" type="select" selector="[data-index='custom_options'] [data-index='options'] tbody tr:nth-last-of-type(1) input[name*='price_type']"/> + <element name="optionFileExtensions" type="input" selector="[data-index='custom_options'] [data-index='options'] tbody tr:nth-last-of-type(1) input[name*='file_extension']"/> + <element name="optionSku" type="input" selector="[data-index='custom_options'] [data-index='options'] tbody tr:nth-last-of-type(1) input[name*='sku']"/> <element name="optionTitleInputByIndex" type="input" selector="input[name='product[options][{{index}}][title]']" parameterized="true"/> - <element name="importOptions" type="button" selector="button[data-index='button_import']" timeout="30"/> + <element name="importOptions" type="button" selector="[data-index='custom_options'] [data-index='button_import']" timeout="30"/> <element name="optionPriceByIndex" type="input" selector="input[name='product[options][{{index}}][price]']" parameterized="true"/> <element name="optionSkuByIndex" type="input" selector="input[name='product[options][{{index}}][sku]']" parameterized="true"/> </section> - <section name="AdminProductImportOptionsSection"> - <element name="selectProductTitle" type="text" selector="//h1[contains(text(), 'Select Product')]"/> - <element name="filterButton" type="button" selector="button[data-action='grid-filter-expand']" timeout="30"/> - <element name="nameField" type="input" selector="input[name='name']"/> - <element name="applyFiltersButton" type="button" selector="button[data-action='grid-filter-apply']" timeout="30"/> - <element name="resetFiltersButton" type="button" selector="button[data-action='grid-filter-reset']" timeout="30"/> - <element name="firstRowItemCheckbox" type="input" selector="input[data-action='select-row']"/> - <element name="importButton" type="button" selector="//button[contains(@class, 'action-primary')]/span[contains(text(), 'Import')]" timeout="30"/> - </section> </sections> From 84f0af856f3c0d81463963b5b2fa55567f16ae05 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Fri, 12 Apr 2019 16:34:34 +0300 Subject: [PATCH 1120/1295] Fix static tests. --- .../mage/adminhtml/wysiwyg/tiny_mce/setup.js | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js index 4ba8627ff7df3..23f24bfcd1a9b 100755 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js @@ -238,16 +238,20 @@ define([ * @param {Object} o */ openFileBrowser: function (o) { - var targetElementID = tinymce.activeEditor.getElement().getAttribute('id'); - var originId = this.id; + var targetElementID = tinyMCE.activeEditor.getElement().getAttribute('id'), + originId = this.id, + typeTitle, + storeId, + frameDialog, + wUrl; + this.initialize(targetElementID, this.config); - var typeTitle, - storeId = this.config['store_id'] !== null ? this.config['store_id'] : 0, - frameDialog = jQuery(o.win.frameElement).parents('[role="dialog"]'), - wUrl = this.config['files_browser_window_url'] + - 'target_element_id/' + this.id + '/' + - 'store/' + storeId + '/'; + storeId = this.config['store_id'] !== null ? this.config['store_id'] : 0; + frameDialog = jQuery(o.win.frameElement).parents('[role="dialog"]'); + wUrl = this.config['files_browser_window_url'] + + 'target_element_id/' + this.id + '/' + + 'store/' + storeId + '/'; this.mediaBrowserOpener = o.win; this.mediaBrowserTargetElementId = o.field; From 386f736a5275b90373c12eff379858b95b8f54b7 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Fri, 12 Apr 2019 16:37:11 +0300 Subject: [PATCH 1121/1295] MC-15456: Upgrade DHL schema to 6.0 --- .../Dhl/Test/Unit/Model/CarrierTest.php | 35 +++++++++++-------- .../Magento/Dhl/Model/CarrierTest.php | 4 +-- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php b/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php index c3d82ef34a448..74217c24d3b10 100644 --- a/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php +++ b/app/code/Magento/Dhl/Test/Unit/Model/CarrierTest.php @@ -582,10 +582,11 @@ public function dhlProductsDataProvider(): array * Tests that the built MessageReference string is of the appropriate format. * * @dataProvider buildMessageReferenceDataProvider - * @param $servicePrefix - * @throws \ReflectionException + * @param string $servicePrefix + * + * @return void */ - public function testBuildMessageReference($servicePrefix) + public function testBuildMessageReference(string $servicePrefix) { $method = new \ReflectionMethod($this->model, 'buildMessageReference'); $method->setAccessible(true); @@ -600,12 +601,12 @@ public function testBuildMessageReference($servicePrefix) * * @return array */ - public function buildMessageReferenceDataProvider() + public function buildMessageReferenceDataProvider(): array { return [ 'quote_prefix' => ['QUOT'], 'shipval_prefix' => ['SHIP'], - 'tracking_prefix' => ['TRCK'] + 'tracking_prefix' => ['TRCK'], ]; } @@ -614,6 +615,8 @@ public function buildMessageReferenceDataProvider() * * @expectedException \Magento\Framework\Exception\LocalizedException * @expectedExceptionMessage Invalid service prefix + * + * @return void */ public function testBuildMessageReferenceInvalidPrefix() { @@ -627,10 +630,11 @@ public function testBuildMessageReferenceInvalidPrefix() * Tests that the built software name string is of the appropriate format. * * @dataProvider buildSoftwareNameDataProvider - * @param $productName - * @throws \ReflectionException + * @param string $productName + * + * @return void */ - public function testBuildSoftwareName($productName) + public function testBuildSoftwareName(string $productName) { $method = new \ReflectionMethod($this->model, 'buildSoftwareName'); $method->setAccessible(true); @@ -646,11 +650,11 @@ public function testBuildSoftwareName($productName) * * @return array */ - public function buildSoftwareNameDataProvider() + public function buildSoftwareNameDataProvider(): array { return [ 'valid_length' => ['Magento'], - 'exceeds_length' => ['Product_Name_Longer_Than_30_Char'] + 'exceeds_length' => ['Product_Name_Longer_Than_30_Char'], ]; } @@ -658,10 +662,11 @@ public function buildSoftwareNameDataProvider() * Tests that the built software version string is of the appropriate format. * * @dataProvider buildSoftwareVersionProvider - * @param $productVersion - * @throws \ReflectionException + * @param string $productVersion + * + * @return void */ - public function testBuildSoftwareVersion($productVersion) + public function testBuildSoftwareVersion(string $productVersion) { $method = new \ReflectionMethod($this->model, 'buildSoftwareVersion'); $method->setAccessible(true); @@ -677,11 +682,11 @@ public function testBuildSoftwareVersion($productVersion) * * @return array */ - public function buildSoftwareVersionProvider() + public function buildSoftwareVersionProvider(): array { return [ 'valid_length' => ['2.3.1'], - 'exceeds_length' => ['dev-MC-1000'] + 'exceeds_length' => ['dev-MC-1000'], ]; } diff --git a/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php b/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php index 1f9287e03342f..dd1f917e34d5e 100644 --- a/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php +++ b/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php @@ -19,7 +19,7 @@ class CarrierTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Dhl\Model\Carrier + * @var Carrier */ private $dhlCarrier; @@ -40,7 +40,7 @@ protected function setUp() { $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); $this->dhlCarrier = $objectManager->create( - \Magento\Dhl\Model\Carrier::class, + Carrier::class, ['httpClientFactory' => $this->getHttpClientFactory()] ); } From 3833a24a2b154cf9b6011b44cee874bf4841f6ff Mon Sep 17 00:00:00 2001 From: Oleksandr Dubovyk <odubovyk@magento.com> Date: Fri, 12 Apr 2019 16:11:18 -0500 Subject: [PATCH 1122/1295] MAGETWO-98679: Discounts of products disappear on storefront after drag & drop via Visual Merchandiser in category - skipped @magentoIndexerDimensionMode annotation and tests --- ...BundlePriceCalculatorWithDimensionTest.php | 8 +++++- ...BundlePriceCalculatorWithDimensionTest.php | 8 +++++- .../Model/Product/PriceWithDimensionTest.php | 5 +++- ...eWithOptionsTierPriceWithDimensionTest.php | 5 +++- .../Product/Type/PriceWithDimensionTest.php | 26 ++++++++++++++++++- .../Model/ProductPriceWithDimensionTest.php | 26 ++++++++++++++++++- .../SpecialPriceIndexerWithDimensionTest.php | 8 +++++- ...edOnIsProductListFlagWithDimensionTest.php | 8 +++++- 8 files changed, 86 insertions(+), 8 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/DynamicBundlePriceCalculatorWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/DynamicBundlePriceCalculatorWithDimensionTest.php index 6794a686146f9..c1750332fa568 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/DynamicBundlePriceCalculatorWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/DynamicBundlePriceCalculatorWithDimensionTest.php @@ -8,7 +8,7 @@ /** * @magentoDbIsolation disabled - * @magentoIndexerDimensionMode catalog_product_price website_and_customer_group + * @--magentoIndexerDimensionMode catalog_product_price website_and_customer_group * @group indexer_dimension * @magentoAppArea frontend */ @@ -24,6 +24,9 @@ class DynamicBundlePriceCalculatorWithDimensionTest extends BundlePriceAbstract */ public function testPriceForDynamicBundle(array $strategyModifiers, array $expectedResults) { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); $this->prepareFixture($strategyModifiers, 'bundle_product'); $bundleProduct = $this->productRepository->get('bundle_product', false, null, true); @@ -63,6 +66,9 @@ public function testPriceForDynamicBundle(array $strategyModifiers, array $expec */ public function testPriceForDynamicBundleInWebsiteScope(array $strategyModifiers, array $expectedResults) { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); $this->prepareFixture($strategyModifiers, 'bundle_product'); $bundleProduct = $this->productRepository->get('bundle_product', false, null, true); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php index ffc24b2f45d5c..9e7c8d9836c74 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundlePriceCalculatorWithDimensionTest.php @@ -10,7 +10,7 @@ /** * @magentoDbIsolation disabled - * @magentoIndexerDimensionMode catalog_product_price website_and_customer_group + * @--magentoIndexerDimensionMode catalog_product_price website_and_customer_group * @group indexer_dimension * @magentoAppArea frontend */ @@ -26,6 +26,9 @@ class FixedBundlePriceCalculatorWithDimensionTest extends BundlePriceAbstract */ public function testPriceForFixedBundle(array $strategyModifiers, array $expectedResults) { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); $this->prepareFixture($strategyModifiers, 'bundle_product'); $bundleProduct = $this->productRepository->get('bundle_product', false, null, true); @@ -65,6 +68,9 @@ public function testPriceForFixedBundle(array $strategyModifiers, array $expecte */ public function testPriceForFixedBundleInWebsiteScope(array $strategyModifiers, array $expectedResults) { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); $this->prepareFixture($strategyModifiers, 'bundle_product'); $bundleProduct = $this->productRepository->get('bundle_product', false, null, true); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceWithDimensionTest.php index 3b3b1ed5cbd07..a1516c6a74808 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceWithDimensionTest.php @@ -7,7 +7,7 @@ /** * @magentoDbIsolation disabled - * @magentoIndexerDimensionMode catalog_product_price website_and_customer_group + * @--magentoIndexerDimensionMode catalog_product_price website_and_customer_group * @group indexer_dimension * @magentoDataFixture Magento/Bundle/_files/product_with_tier_pricing.php */ @@ -27,6 +27,9 @@ protected function setUp() public function testGetTierPrice() { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ $productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Price/SimpleWithOptionsTierPriceWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Price/SimpleWithOptionsTierPriceWithDimensionTest.php index 40607cd85b3b4..e70fa8f52d269 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Price/SimpleWithOptionsTierPriceWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Price/SimpleWithOptionsTierPriceWithDimensionTest.php @@ -42,11 +42,14 @@ protected function setUp() /** * @magentoDbIsolation disabled - * @magentoIndexerDimensionMode catalog_product_price website_and_customer_group + * @--magentoIndexerDimensionMode catalog_product_price website_and_customer_group * @magentoDataFixture Magento/Catalog/_files/category_product.php */ public function testTierPrice() { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); $tierPriceValue = 9.00; $tierPrice = $this->objectManager->create(ProductTierPriceInterfaceFactory::class) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceWithDimensionTest.php index 12b7da2bd6e35..e0f26a7aab01b 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceWithDimensionTest.php @@ -17,7 +17,7 @@ /** * @magentoDbIsolation disabled - * @magentoIndexerDimensionMode catalog_product_price website_and_customer_group + * @--magentoIndexerDimensionMode catalog_product_price website_and_customer_group * @group indexer_dimension * @magentoDataFixture Magento/Catalog/_files/product_simple.php */ @@ -37,6 +37,9 @@ protected function setUp() public function testGetPriceFromIndexer() { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); /** @var PriceTableResolver $tableResolver */ $tableResolver = Bootstrap::getObjectManager()->create(PriceTableResolver::class); @@ -66,11 +69,17 @@ public function testGetPriceFromIndexer() public function testGetPrice() { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); $this->assertEquals('test', $this->_model->getPrice(new DataObject(['price' => 'test']))); } public function testGetFinalPrice() { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); $repository = Bootstrap::getObjectManager()->create( ProductRepository::class ); @@ -95,6 +104,9 @@ public function testGetFinalPrice() public function testGetFormatedPrice() { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); $repository = Bootstrap::getObjectManager()->create( ProductRepository::class ); @@ -105,12 +117,18 @@ public function testGetFormatedPrice() public function testCalculatePrice() { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); $this->assertEquals(10, $this->_model->calculatePrice(10, 8, '1970-12-12 23:59:59', '1971-01-01 01:01:01')); $this->assertEquals(8, $this->_model->calculatePrice(10, 8, '1970-12-12 23:59:59', '2034-01-01 01:01:01')); } public function testCalculateSpecialPrice() { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); $this->assertEquals( 10, $this->_model->calculateSpecialPrice(10, 8, '1970-12-12 23:59:59', '1971-01-01 01:01:01') @@ -123,6 +141,9 @@ public function testCalculateSpecialPrice() public function testIsTierPriceFixed() { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); $this->assertTrue($this->_model->isTierPriceFixed()); } @@ -134,6 +155,9 @@ public function testIsTierPriceFixed() */ private function prepareBuyRequest(Product $product) { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); $options = []; /** @var $option \Magento\Catalog\Model\Product\Option */ foreach ($product->getOptions() as $option) { diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceWithDimensionTest.php index 9e3db8c155e28..cb776fb08723f 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductPriceWithDimensionTest.php @@ -15,7 +15,7 @@ * - pricing behaviour is tested * @group indexer_dimension * @magentoDbIsolation disabled - * @magentoIndexerDimensionMode catalog_product_price website_and_customer_group + * @--magentoIndexerDimensionMode catalog_product_price website_and_customer_group * @see \Magento\Catalog\Model\ProductTest * @see \Magento\Catalog\Model\ProductExternalTest */ @@ -39,6 +39,9 @@ protected function setUp() public function testGetPrice() { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); $this->assertEmpty($this->_model->getPrice()); $this->_model->setPrice(10.0); $this->assertEquals(10.0, $this->_model->getPrice()); @@ -46,6 +49,9 @@ public function testGetPrice() public function testGetPriceModel() { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); $default = $this->_model->getPriceModel(); $this->assertInstanceOf(\Magento\Catalog\Model\Product\Type\Price::class, $default); $this->assertSame($default, $this->_model->getPriceModel()); @@ -56,6 +62,9 @@ public function testGetPriceModel() */ public function testGetTierPrice() { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); $this->assertEquals([], $this->_model->getTierPrice()); } @@ -64,6 +73,9 @@ public function testGetTierPrice() */ public function testGetTierPriceCount() { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); $this->assertEquals(0, $this->_model->getTierPriceCount()); } @@ -72,11 +84,17 @@ public function testGetTierPriceCount() */ public function testGetFormatedPrice() { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); $this->assertEquals('<span class="price">$0.00</span>', $this->_model->getFormatedPrice()); } public function testSetGetFinalPrice() { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); $this->assertEquals(0, $this->_model->getFinalPrice()); $this->_model->setPrice(10); $this->_model->setFinalPrice(10); @@ -88,6 +106,9 @@ public function testSetGetFinalPrice() */ public function testGetMinPrice() { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); $product = $this->productRepository->get('simple'); $collection = Bootstrap::getObjectManager()->create(Collection::class); $collection->addIdFilter($product->getId()); @@ -103,6 +124,9 @@ public function testGetMinPrice() */ public function testGetMinPriceForComposite() { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); $confProduct = $this->productRepository->get('configurable'); $collection = Bootstrap::getObjectManager()->create(Collection::class); $collection->addIdFilter($confProduct->getId()); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/SpecialPriceIndexerWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/SpecialPriceIndexerWithDimensionTest.php index f08f0a4543ea3..140df500472b3 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/SpecialPriceIndexerWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/SpecialPriceIndexerWithDimensionTest.php @@ -16,7 +16,7 @@ /** * @magentoDbIsolation disabled * @group indexer_dimension - * @magentoIndexerDimensionMode catalog_product_price website_and_customer_group + * @--magentoIndexerDimensionMode catalog_product_price website_and_customer_group */ class SpecialPriceIndexerWithDimensionTest extends \PHPUnit\Framework\TestCase { @@ -51,6 +51,9 @@ protected function setUp() */ public function testFullReindexIfChildHasSpecialPrice() { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); $specialPrice = 2; /** @var Product $childProduct */ $childProduct = $this->productRepository->get('simple_10', true); @@ -88,6 +91,9 @@ public function testFullReindexIfChildHasSpecialPrice() */ public function testOnSaveIndexationIfChildHasSpecialPrice() { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); $specialPrice = 2; /** @var Product $childProduct */ $childProduct = $this->productRepository->get('simple_10', true); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Render/FinalPriceBox/RenderingBasedOnIsProductListFlagWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Render/FinalPriceBox/RenderingBasedOnIsProductListFlagWithDimensionTest.php index bddbb38e9f019..214dcafc9f686 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Render/FinalPriceBox/RenderingBasedOnIsProductListFlagWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Render/FinalPriceBox/RenderingBasedOnIsProductListFlagWithDimensionTest.php @@ -16,7 +16,7 @@ /** * @magentoDbIsolation disabled - * @magentoIndexerDimensionMode catalog_product_price website_and_customer_group + * @--magentoIndexerDimensionMode catalog_product_price website_and_customer_group * @group indexer_dimension * Test price rendering according to is_product_list flag for Configurable product */ @@ -82,6 +82,9 @@ protected function setUp() */ public function testRenderingByDefault() { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); $html = $this->finalPriceBox->toHtml(); self::assertContains('5.99', $html); $this->assertGreaterThanOrEqual( @@ -117,6 +120,9 @@ public function testRenderingByDefault() */ public function testRenderingAccordingToIsProductListFlag($flag, $count) { + $this->markTestSkipped( + 'Skipped because of MAGETWO-99136' + ); $this->finalPriceBox->setData('is_product_list', $flag); $html = $this->finalPriceBox->toHtml(); self::assertContains('5.99', $html); From 6463fddcc9b1edcaa4607846848b328e070de51d Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Mon, 15 Apr 2019 08:57:23 +0300 Subject: [PATCH 1123/1295] MAGETWO-73157: [GITHUB] Import customizable options adds it to another product if same SKU is filled#9457 --- .../Model/Product/Option/SaveHandlerTest.php | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/SaveHandlerTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/SaveHandlerTest.php index 6fe0594be08f2..6f209398a362e 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/SaveHandlerTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/SaveHandlerTest.php @@ -48,7 +48,12 @@ public function setUp() $this->model = new SaveHandler($this->optionRepository); } - public function testExecute() + /** + * @dataProvider testExecuteDataProvider + * @param bool $dataHasChangedFor + * @return void + */ + public function testExecute($dataHasChangedFor) { $this->optionMock->expects($this->any())->method('getOptionId')->willReturn(5); $this->entity->expects($this->once())->method('getOptions')->willReturn([$this->optionMock]); @@ -63,10 +68,27 @@ public function testExecute() ->method('getProductOptions') ->with($this->entity) ->willReturn([$this->optionMock, $secondOptionMock]); - + $this->entity->expects($this->once()) + ->method('dataHasChangedFor') + ->with('sku') + ->willReturn($dataHasChangedFor); + $this->entity->expects($this->once()) + ->method('getSku') + ->willReturn('product_sku'); $this->optionRepository->expects($this->once())->method('delete'); $this->optionRepository->expects($this->once())->method('save')->with($this->optionMock); $this->assertEquals($this->entity, $this->model->execute($this->entity)); } + + /** + * @return array + */ + public function testExecuteDataProvider(): array + { + return [ + [true], + [false], + ]; + } } From 252aa80f6eceb428fb8052f0dd670bf27b9d4e26 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Mon, 15 Apr 2019 11:08:48 +0300 Subject: [PATCH 1124/1295] MAGETWO-73157: [GITHUB] Import customizable options adds it to another product if same SKU is filled#9457 --- .../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 a65bfa86d97bc..ca4e17fe92d6b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml @@ -27,7 +27,7 @@ <!-- var 1 represents the option type that you want to select, i.e "radio buttons" --> <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:nth-last-of-type(1) input[name*='price']"/> - <element name="optionPriceType" type="select" selector="[data-index='custom_options'] [data-index='options'] tbody tr:nth-last-of-type(1) input[name*='price_type']"/> + <element name="optionPriceType" type="select" selector="[data-index='custom_options'] [data-index='options'] tbody tr:nth-last-of-type(1) select[name*='price_type']"/> <element name="optionFileExtensions" type="input" selector="[data-index='custom_options'] [data-index='options'] tbody tr:nth-last-of-type(1) input[name*='file_extension']"/> <element name="optionSku" type="input" selector="[data-index='custom_options'] [data-index='options'] tbody tr:nth-last-of-type(1) input[name*='sku']"/> <element name="optionTitleInputByIndex" type="input" selector="input[name='product[options][{{index}}][title]']" parameterized="true"/> From c66d4fba733eb8dfb1052fd7de3a8d2f430ec6a1 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Mon, 15 Apr 2019 16:18:35 +0300 Subject: [PATCH 1125/1295] MAGETWO-73157: [GITHUB] Import customizable options adds it to another product if same SKU is filled#9457 --- .../Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml | 4 ++-- .../AdminProductCustomizableOptionsImportModalSection.xml | 2 +- .../Test/Unit/Model/Product/Option/SaveHandlerTest.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml index 233a62afbbb80..e679d59fde791 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml @@ -34,8 +34,8 @@ <waitForElementVisible selector="{{AdminProductCustomizableOptionsImportModalSection.selectProductTitle}}" stepKey="waitForTitleVisible"/> <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickResetFilters"/> <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="clickFilterButton"/> - <waitForElementVisible selector="{{AdminProductCustomizableOptionsImportModalSection.nameField}}" stepKey="waitForNameField"/> - <fillField selector="{{AdminProductCustomizableOptionsImportModalSection.nameField}}" userInput="{{productName}}" stepKey="fillProductName"/> + <waitForElementVisible selector="{{AdminProductCustomizableOptionsImportModalSection.nameFilter}}" stepKey="waitForNameField"/> + <fillField selector="{{AdminProductCustomizableOptionsImportModalSection.nameFilter}}" userInput="{{productName}}" stepKey="fillProductName"/> <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFilters"/> <checkOption selector="{{AdminDataGridTableSection.rowCheckbox('1')}}" stepKey="checkProductCheckbox"/> <click selector="{{AdminProductCustomizableOptionsImportModalSection.importButton}}" stepKey="clickImport"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsImportModalSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsImportModalSection.xml index f9d9f531a9b8b..0b1b5c966422a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsImportModalSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsImportModalSection.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductCustomizableOptionsImportModalSection"> <element name="selectProductTitle" type="text" selector="//aside[contains(@class, '_show')]//h1[normalize-space(text())='Select Product']"/> - <element name="nameField" type="input" selector="input[name='name']"/> + <element name="nameFilter" type="input" selector="//aside[contains(@class, '_show')]//input[@name='name']"/> <element name="importButton" type="button" selector="//aside[contains(@class, '_show')]//button[contains(@class, 'action-primary') and normalize-space(.)='Import']" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/SaveHandlerTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/SaveHandlerTest.php index 6f209398a362e..7b830124a365b 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/SaveHandlerTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/SaveHandlerTest.php @@ -53,7 +53,7 @@ public function setUp() * @param bool $dataHasChangedFor * @return void */ - public function testExecute($dataHasChangedFor) + public function testExecute(bool $dataHasChangedFor) { $this->optionMock->expects($this->any())->method('getOptionId')->willReturn(5); $this->entity->expects($this->once())->method('getOptions')->willReturn([$this->optionMock]); From f4ab0e6d9280920b6c64b789ec7a155330cfb681 Mon Sep 17 00:00:00 2001 From: Wirson <m.wirson@gmail.com> Date: Mon, 15 Apr 2019 16:44:59 +0200 Subject: [PATCH 1126/1295] #222249 configurable product images wrong sorting fix --- .../Magento/Swatches/view/frontend/web/js/swatch-renderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js index 41688dac440c7..9abbd2b9cbf3e 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 @@ -728,7 +728,7 @@ define([ */ _sortImages: function (images) { return _.sortBy(images, function (image) { - return image.position; + return parseInt(image.position, 10); }); }, From c18bb4b1625aac4929f6b42080a54b7f804b6aff Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Mon, 15 Apr 2019 11:02:32 -0500 Subject: [PATCH 1127/1295] MAGETWO-87222: The cart rules disappear with error: Item (Magento\SalesRule\Model\Rule\Interceptor) with the same ID "140" already exists --- .../Rule/Collection/AbstractCollection.php | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Rule/Model/ResourceModel/Rule/Collection/AbstractCollection.php b/app/code/Magento/Rule/Model/ResourceModel/Rule/Collection/AbstractCollection.php index b3d761b378d94..e0468f17e587a 100644 --- a/app/code/Magento/Rule/Model/ResourceModel/Rule/Collection/AbstractCollection.php +++ b/app/code/Magento/Rule/Model/ResourceModel/Rule/Collection/AbstractCollection.php @@ -83,11 +83,21 @@ public function addWebsiteFilter($websiteId) if ($website instanceof \Magento\Store\Model\Website) { $websiteIds[$index] = $website->getId(); } + $websiteIds[$index] = (int) $websiteIds[$index]; } + + $websiteSelect = $this->getConnection()->select(); + $websiteSelect->from( + $this->getTable($entityInfo['associations_table']), + [$entityInfo['rule_id_field']] + )->distinct( + true + )->where( + $this->getConnection()->quoteInto($entityInfo['entity_id_field'] . ' IN (?)', $websiteIds) + ); $this->getSelect()->join( - ['website' => $this->getTable($entityInfo['associations_table'])], - $this->getConnection()->quoteInto('website.' . $entityInfo['entity_id_field'] . ' IN (?)', $websiteIds) - . ' AND main_table.' . $entityInfo['rule_id_field'] . ' = website.' . $entityInfo['rule_id_field'], + ['website' => $websiteSelect], + 'main_table.' . $entityInfo['rule_id_field'] . ' = website.' . $entityInfo['rule_id_field'], [] ); } From f925d23dcedd9ecdf74287fa34199a538e0e2569 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Mon, 15 Apr 2019 12:15:34 -0500 Subject: [PATCH 1128/1295] MAGETWO-87222: The cart rules disappear with error: Item (Magento\SalesRule\Model\Rule\Interceptor) with the same ID "140" already exists --- .../Collection/AbstractCollectionTest.php | 200 ------------------ .../ResourceModel/Rule/CollectionTest.php | 144 ++++++++----- .../SalesRule/_files/multi_websites_rules.php | 16 ++ .../_files/multi_websites_rules_rollback.php | 8 + .../Magento/SalesRule/_files/rules.php | 30 +-- 5 files changed, 132 insertions(+), 266 deletions(-) delete mode 100644 app/code/Magento/Rule/Test/Unit/Model/ResourceModel/Rule/Collection/AbstractCollectionTest.php create mode 100644 dev/tests/integration/testsuite/Magento/SalesRule/_files/multi_websites_rules.php create mode 100644 dev/tests/integration/testsuite/Magento/SalesRule/_files/multi_websites_rules_rollback.php diff --git a/app/code/Magento/Rule/Test/Unit/Model/ResourceModel/Rule/Collection/AbstractCollectionTest.php b/app/code/Magento/Rule/Test/Unit/Model/ResourceModel/Rule/Collection/AbstractCollectionTest.php deleted file mode 100644 index c4e7a591212c5..0000000000000 --- a/app/code/Magento/Rule/Test/Unit/Model/ResourceModel/Rule/Collection/AbstractCollectionTest.php +++ /dev/null @@ -1,200 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Rule\Test\Unit\Model\ResourceModel\Rule\Collection; - -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; - -class AbstractCollectionTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $abstractCollection; - - /** - * @var ObjectManagerHelper - */ - protected $objectManagerHelper; - - /** - * @var \Magento\Framework\Data\Collection\EntityFactoryInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $_entityFactoryMock; - - /** - * @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $_loggerMock; - - /** - * @var \Magento\Framework\Data\Collection\Db\FetchStrategyInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $_fetchStrategyMock; - - /** - * @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $_managerMock; - - /** - * @var \Magento\Framework\Model\ResourceModel\Db\AbstractDb|\PHPUnit_Framework_MockObject_MockObject - */ - protected $_db; - - /** - * @var \Magento\Framework\DB\Adapter\AdapterInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $connectionMock; - - /** - * @var \Magento\Framework\DB\Select|\PHPUnit_Framework_MockObject_MockObject - */ - private $selectMock; - - protected function setUp() - { - $this->_entityFactoryMock = $this->createMock(\Magento\Framework\Data\Collection\EntityFactoryInterface::class); - $this->_loggerMock = $this->createMock(\Psr\Log\LoggerInterface::class); - $this->_fetchStrategyMock = $this->createMock( - \Magento\Framework\Data\Collection\Db\FetchStrategyInterface::class - ); - $this->_managerMock = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); - $this->_db = $this->getMockForAbstractClass( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, - [], - '', - false, - false, - true, - ['__sleep', '__wakeup', 'getTable'] - ); - $this->objectManagerHelper = new ObjectManagerHelper($this); - $this->abstractCollection = $this->getMockForAbstractClass( - \Magento\Rule\Model\ResourceModel\Rule\Collection\AbstractCollection::class, - [ - 'entityFactory' => $this->_entityFactoryMock, - 'logger' => $this->_loggerMock, - 'fetchStrategy' => $this->_fetchStrategyMock, - 'eventManager' => $this->_managerMock, - null, - $this->_db - ], - '', - false, - false, - true, - ['__sleep', '__wakeup', '_getAssociatedEntityInfo', 'getConnection', 'getSelect', 'getTable'] - ); - } - - /** - * @return array - */ - public function addWebsitesToResultDataProvider() - { - return [ - [null, true], - [true, true], - [false, false] - ]; - } - - /** - * @dataProvider addWebsitesToResultDataProvider - */ - public function testAddWebsitesToResult($flag, $expectedResult) - { - $this->abstractCollection->addWebsitesToResult($flag); - $this->assertEquals($expectedResult, $this->abstractCollection->getFlag('add_websites_to_result')); - } - - protected function _prepareAddFilterStubs() - { - $entityInfo = []; - $entityInfo['entity_id_field'] = 'entity_id'; - $entityInfo['rule_id_field'] = 'rule_id'; - $entityInfo['associations_table'] = 'assoc_table'; - - $connection = $this->createMock(\Magento\Framework\DB\Adapter\AdapterInterface::class); - $select = $this->createMock(\Magento\Framework\DB\Select::class); - $collectionSelect = $this->createMock(\Magento\Framework\DB\Select::class); - - $connection->expects($this->any()) - ->method('select') - ->will($this->returnValue($select)); - - $select->expects($this->any()) - ->method('from') - ->will($this->returnSelf()); - - $select->expects($this->any()) - ->method('where') - ->will($this->returnSelf()); - - $this->abstractCollection->expects($this->any()) - ->method('getConnection') - ->will($this->returnValue($connection)); - - $this->_db->expects($this->any()) - ->method('getTable') - ->will($this->returnArgument(0)); - - $this->abstractCollection->expects($this->any()) - ->method('getSelect') - ->will($this->returnValue($collectionSelect)); - - $this->abstractCollection->expects($this->any()) - ->method('_getAssociatedEntityInfo') - ->will($this->returnValue($entityInfo)); - } - - public function testAddWebsiteFilter() - { - $this->_prepareAddFilterStubs(); - $website = $this->createPartialMock(\Magento\Store\Model\Website::class, ['getId', '__sleep', '__wakeup']); - - $website->expects($this->any()) - ->method('getId') - ->will($this->returnValue(1)); - - $this->assertInstanceOf( - \Magento\Rule\Model\ResourceModel\Rule\Collection\AbstractCollection::class, - $this->abstractCollection->addWebsiteFilter($website) - ); - } - - public function testAddWebsiteFilterArray() - { - $this->selectMock = $this->getMockBuilder(\Magento\Framework\DB\Select::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->connectionMock = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $this->connectionMock->expects($this->atLeastOnce()) - ->method('quoteInto') - ->with($this->equalTo('website. IN (?)'), $this->equalTo(['2', '3'])) - ->willReturn(true); - - $this->abstractCollection->expects($this->atLeastOnce())->method('getSelect')->willReturn($this->selectMock); - $this->abstractCollection->expects($this->atLeastOnce())->method('getConnection') - ->willReturn($this->connectionMock); - - $this->assertInstanceOf( - \Magento\Rule\Model\ResourceModel\Rule\Collection\AbstractCollection::class, - $this->abstractCollection->addWebsiteFilter(['2', '3']) - ); - } - - public function testAddFieldToFilter() - { - $this->_prepareAddFilterStubs(); - $result = $this->abstractCollection->addFieldToFilter('website_ids', []); - $this->assertNotNull($result); - } -} diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Model/ResourceModel/Rule/CollectionTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Model/ResourceModel/Rule/CollectionTest.php index f619193d4921f..9ea97b6e939cc 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Model/ResourceModel/Rule/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Model/ResourceModel/Rule/CollectionTest.php @@ -5,12 +5,39 @@ */ namespace Magento\SalesRule\Model\ResourceModel\Rule; +use Magento\Config\Model\Config\Backend\Admin\Custom as AdminBackendConfig; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + /** * @magentoDbIsolation enabled * @magentoAppIsolation enabled */ -class CollectionTest extends \PHPUnit\Framework\TestCase +class CollectionTest extends TestCase { + /** + * @var Collection + */ + private $collection; + + /** + * @var string + */ + private $defaultTimezone; + + /** + * @inheritDoc + */ + protected function setUp() + { + $scopeConfig = Bootstrap::getObjectManager()->get(ScopeConfigInterface::class); + $this->defaultTimezone = $scopeConfig->getValue(AdminBackendConfig::XML_PATH_GENERAL_LOCALE_TIMEZONE); + + $this->collection = Bootstrap::getObjectManager()->create(Collection::class); + } + /** * @magentoDataFixture Magento/SalesRule/_files/rules.php * @magentoDataFixture Magento/SalesRule/_files/coupons.php @@ -21,12 +48,8 @@ class CollectionTest extends \PHPUnit\Framework\TestCase */ public function testSetValidationFilter($couponCode, $expectedItems) { - $collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\SalesRule\Model\ResourceModel\Rule\Collection::class - ); - $items = array_values($collection->setValidationFilter(1, 0, $couponCode)->getItems()); - - $ids = []; + /** @var \Magento\SalesRule\Model\Rule[] $items */ + $items = array_values($this->collection->setValidationFilter(1, 0, $couponCode)->getItems()); $this->assertEquals( count($expectedItems), @@ -34,6 +57,7 @@ public function testSetValidationFilter($couponCode, $expectedItems) 'Invalid number of items in the result collection' ); + $ids = []; foreach ($items as $key => $item) { $this->assertEquals($expectedItems[$key], $item->getName()); $this->assertFalse( @@ -71,7 +95,7 @@ public function setValidationFilterDataProvider() */ public function testSetValidationFilterWithGroup() { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $objectManager = Bootstrap::getObjectManager(); /** @var \Magento\SalesRule\Model\Rule $rule */ $rule = $objectManager->get(\Magento\Framework\Registry::class) @@ -82,13 +106,8 @@ public function testSetValidationFilterWithGroup() $quote->load('test_order_item_with_items', 'reserved_order_id'); //gather only the existing rules that obey the validation filter - /** @var \Magento\SalesRule\Model\ResourceModel\Rule\Collection $ruleCollection */ - $ruleCollection = $objectManager->create( - \Magento\SalesRule\Model\ResourceModel\Rule\Collection::class - ); - $appliedRulesArray = array_keys( - $ruleCollection->setValidationFilter( + $this->collection->setValidationFilter( $quote->getStore()->getWebsiteId(), 0, '', @@ -108,7 +127,7 @@ public function testSetValidationFilterWithGroup() */ public function testSetValidationFilterAnyCategory() { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $objectManager = Bootstrap::getObjectManager(); /** @var \Magento\SalesRule\Model\Rule $rule */ $rule = $objectManager->get(\Magento\Framework\Registry::class) @@ -119,13 +138,8 @@ public function testSetValidationFilterAnyCategory() $quote->load('test_order_item_with_items', 'reserved_order_id'); //gather only the existing rules that obey the validation filter - /** @var \Magento\SalesRule\Model\ResourceModel\Rule\Collection $ruleCollection */ - $ruleCollection = $objectManager->create( - \Magento\SalesRule\Model\ResourceModel\Rule\Collection::class - ); - $appliedRulesArray = array_keys( - $ruleCollection->setValidationFilter( + $this->collection->setValidationFilter( $quote->getStore()->getWebsiteId(), 0, '', @@ -146,20 +160,15 @@ public function testSetValidationFilterAnyCategory() */ public function testSetValidationFilterOther() { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $objectManager = Bootstrap::getObjectManager(); /** @var \Magento\Quote\Model\Quote $quote */ $quote = $objectManager->create(\Magento\Quote\Model\Quote::class); $quote->load('test_order_item_with_items', 'reserved_order_id'); //gather only the existing rules that obey the validation filter - /** @var \Magento\SalesRule\Model\ResourceModel\Rule\Collection $ruleCollection */ - $ruleCollection = $objectManager->create( - \Magento\SalesRule\Model\ResourceModel\Rule\Collection::class - ); - $appliedRulesArray = array_keys( - $ruleCollection->setValidationFilter( + $this->collection->setValidationFilter( $quote->getStore()->getWebsiteId(), 0, '', @@ -181,11 +190,8 @@ public function testSetValidationFilterOther() public function testMultiRulesWithTimezone() { $this->setSpecificTimezone('Europe/Kiev'); - $collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\SalesRule\Model\ResourceModel\Rule\Collection::class - ); - $collection->addWebsiteGroupDateFilter(1, 0); - $items = array_values($collection->getItems()); + $this->collection->addWebsiteGroupDateFilter(1, 0); + $items = array_values($this->collection->getItems()); $this->assertNotEmpty($items); } @@ -200,11 +206,8 @@ public function testMultiRulesWithTimezone() public function testMultiRulesWithDifferentTimezone() { $this->setSpecificTimezone('Australia/Sydney'); - $collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\SalesRule\Model\ResourceModel\Rule\Collection::class - ); - $collection->addWebsiteGroupDateFilter(1, 0); - $items = array_values($collection->getItems()); + $this->collection->addWebsiteGroupDateFilter(1, 0); + $items = array_values($this->collection->getItems()); $this->assertNotEmpty($items); } @@ -224,7 +227,7 @@ protected function setSpecificTimezone($timezone) ] ] ]; - \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Config\Model\Config\Factory::class) + Bootstrap::getObjectManager()->get(\Magento\Config\Model\Config\Factory::class) ->create() ->addData($localeData) ->save(); @@ -239,11 +242,9 @@ protected function setSpecificTimezone($timezone) */ public function testAddAttributeInConditionFilterPositive() { - $collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\SalesRule\Model\ResourceModel\Rule\Collection::class - ); - $collection->addAttributeInConditionFilter('attribute_for_sales_rule_1'); - $item = $collection->getFirstItem(); + $this->collection->addAttributeInConditionFilter('attribute_for_sales_rule_1'); + /** @var \Magento\SalesRule\Model\Rule $item */ + $item = $this->collection->getFirstItem(); $this->assertEquals('50% Off on some attribute', $item->getName()); } @@ -256,16 +257,57 @@ public function testAddAttributeInConditionFilterPositive() */ public function testAddAttributeInConditionFilterNegative() { - $collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\SalesRule\Model\ResourceModel\Rule\Collection::class - ); - $collection->addAttributeInConditionFilter('attribute_for_sales_rule_2'); - $this->assertEquals(0, $collection->count()); + $this->collection->addAttributeInConditionFilter('attribute_for_sales_rule_2'); + $this->assertEquals(0, $this->collection->count()); + } + + /** + * @magentoAppIsolation disabled + * @magentoDataFixture Magento/SalesRule/_files/multi_websites_rules.php + * @dataProvider addWebsiteFilterDataProvider + * @param string[] $websiteCodes + * @param int $count + */ + public function testAddWebsiteFilter(array $websiteCodes, int $count) + { + $websiteRepository = Bootstrap::getObjectManager()->get(WebsiteRepositoryInterface::class); + $websiteIds = []; + foreach ($websiteCodes as $websiteCode) { + $websiteIds[] = (int) $websiteRepository->get($websiteCode)->getId(); + } + + $this->collection->addWebsiteFilter($websiteIds); + $this->assertEquals($count, $this->collection->getSize()); + $this->assertCount($count, $this->collection->getItems()); } - public function tearDown() + /** + * @return array + */ + public function addWebsiteFilterDataProvider(): array + { + return [ + [ + ['base'], + 4, + ], + [ + ['test'], + 2, + ], + [ + ['base', 'test'], + 5, + ], + ]; + } + + /** + * @inheritDoc + */ + protected function tearDown() { // restore default timezone - $this->setSpecificTimezone('America/Los_Angeles'); + $this->setSpecificTimezone($this->defaultTimezone); } } diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/multi_websites_rules.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/multi_websites_rules.php new file mode 100644 index 0000000000000..a43df3d67c077 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/multi_websites_rules.php @@ -0,0 +1,16 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +include __DIR__ . '/../../Store/_files/website.php'; +include __DIR__ . '/rules.php'; + +/** @var \Magento\SalesRule\Model\Rule $rule2 */ +$rule2->setWebsiteIds($website->getId()) + ->save(); + +/** @var \Magento\SalesRule\Model\Rule $rule3 */ +$rule3->setWebsiteIds(implode(',', [1, $website->getId()])) + ->save(); diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/multi_websites_rules_rollback.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/multi_websites_rules_rollback.php new file mode 100644 index 0000000000000..9e0e01b9fc51c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/multi_websites_rules_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +include __DIR__ . '/rules_rollback.php'; +include __DIR__ . '/../../Store/_files/website_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/rules.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/rules.php index f2366305473a0..ebedc20814c8b 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/_files/rules.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/rules.php @@ -4,9 +4,9 @@ * See COPYING.txt for license details. */ -/** @var \Magento\SalesRule\Model\Rule $rule */ -$rule = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class); -$rule->setName( +/** @var \Magento\SalesRule\Model\Rule $rule1 */ +$rule1 = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class); +$rule1->setName( '#1' )->setIsActive( 1 @@ -27,9 +27,9 @@ )->setSortOrder(1) ->save(); -/** @var \Magento\SalesRule\Model\Rule $rule */ -$rule = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class); -$rule->setName( +/** @var \Magento\SalesRule\Model\Rule $rule2 */ +$rule2 = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class); +$rule2->setName( '#2' )->setIsActive( 1 @@ -50,9 +50,9 @@ )->setSortOrder(2) ->save(); -/** @var \Magento\SalesRule\Model\Rule $rule */ -$rule = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class); -$rule->setName( +/** @var \Magento\SalesRule\Model\Rule $rule3 */ +$rule3 = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class); +$rule3->setName( '#3' )->setIsActive( 1 @@ -73,9 +73,9 @@ )->setSortOrder(3) ->save(); -/** @var \Magento\SalesRule\Model\Rule $rule */ -$rule = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class); -$rule->setName( +/** @var \Magento\SalesRule\Model\Rule $rule4 */ +$rule4 = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class); +$rule4->setName( '#4' )->setIsActive( 1 @@ -96,9 +96,9 @@ )->setSortOrder(4) ->save(); -/** @var \Magento\SalesRule\Model\Rule $rule */ -$rule = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class); -$rule->setName( +/** @var \Magento\SalesRule\Model\Rule $rule5 */ +$rule5 = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class); +$rule5->setName( '#5' )->setIsActive( 1 From bcc45269c187cdafb144dc1c2e40b78be6050fc8 Mon Sep 17 00:00:00 2001 From: Yuriy <yvechirko@magecom.net> Date: Tue, 2 Apr 2019 16:13:58 +0300 Subject: [PATCH 1129/1295] Previous scrolling to invalid form element is not being canceled on hitting submit multiple times #21715 --- lib/web/mage/validation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/mage/validation.js b/lib/web/mage/validation.js index cd3d3bea4eabf..8c19669699b9d 100644 --- a/lib/web/mage/validation.js +++ b/lib/web/mage/validation.js @@ -1948,7 +1948,7 @@ } if (firstActive.length) { - $('html, body').animate({ + $('html, body').stop().animate({ scrollTop: firstActive.offset().top }); firstActive.focus(); From 5d30662c7603c4ab00d4952a3ccf454888cc9a17 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Tue, 16 Apr 2019 14:06:02 +0300 Subject: [PATCH 1130/1295] MAGETWO-98899: Special Prices cannot save over 4 characters in Japanese Yen - The comma separator seems to be the issue --- .../Magento/Framework/Locale/Format.php | 31 ++++++++++++++++++- .../Framework/Locale/Test/Unit/FormatTest.php | 24 ++++++++++---- 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/lib/internal/Magento/Framework/Locale/Format.php b/lib/internal/Magento/Framework/Locale/Format.php index adcffe01b910e..a66f14260c9cb 100644 --- a/lib/internal/Magento/Framework/Locale/Format.php +++ b/lib/internal/Magento/Framework/Locale/Format.php @@ -25,6 +25,11 @@ class Format implements \Magento\Framework\Locale\FormatInterface */ protected $currencyFactory; + /** + * @var array + */ + private $groupSeparatorByLocale = []; + /** * @param \Magento\Framework\App\ScopeResolverInterface $scopeResolver * @param ResolverInterface $localeResolver @@ -81,7 +86,13 @@ public function getNumber($value) $value = str_replace(',', '', $value); } } elseif ($separatorComa !== false) { - $value = str_replace(',', '.', $value); + $locale = $this->_localeResolver->getLocale(); + $groupSeparator = $this->retrieveLocaleGroupSeparator($locale); + if ($groupSeparator === ',') { + $value = str_replace(',', '', $value); + } else { + $value = str_replace(',', '.', $value); + } } return (float)$value; @@ -149,4 +160,22 @@ public function getPriceFormat($localeCode = null, $currencyCode = null) return $result; } + + /** + * Retrieve group separator symbol by locale + * + * @param string $locale + * @return string + */ + private function retrieveLocaleGroupSeparator(string $locale): string + { + if (!array_key_exists($locale, $this->groupSeparatorByLocale)) { + $formatter = new \NumberFormatter($locale, \NumberFormatter::DECIMAL); + $this->groupSeparatorByLocale[$locale] = $formatter->getSymbol( + \NumberFormatter::GROUPING_SEPARATOR_SYMBOL + ); + } + + return $this->groupSeparatorByLocale[$locale]; + } } diff --git a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php index f6d7326f52764..09b6d982c252d 100644 --- a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php +++ b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php @@ -6,6 +6,9 @@ namespace Magento\Framework\Locale\Test\Unit; +/** + * Tests class for Number locale format + */ class FormatTest extends \PHPUnit\Framework\TestCase { /** @@ -84,21 +87,26 @@ public function testGetPriceFormat($localeCode, $expectedResult) */ public function getPriceFormatDataProvider() { + $swissGroupSymbol = INTL_ICU_VERSION >= 59.1 ? '’' : '\''; return [ ['en_US', ['decimalSymbol' => '.', 'groupSymbol' => ',']], ['de_DE', ['decimalSymbol' => ',', 'groupSymbol' => '.']], - ['de_CH', ['decimalSymbol' => '.', 'groupSymbol' => '\'']], + ['de_CH', ['decimalSymbol' => '.', 'groupSymbol' => $swissGroupSymbol]], ['uk_UA', ['decimalSymbol' => ',', 'groupSymbol' => ' ']] ]; } /** - * @param float | null $expected * @param string|float|int $value + * @param float | null $expected + * @param string $locale * @dataProvider provideNumbers */ - public function testGetNumber($value, $expected) + public function testGetNumber(string $value, float $expected, string $locale = null) { + if ($locale !== null) { + $this->localeResolver->method('getLocale')->willReturn($locale); + } $this->assertEquals($expected, $this->formatModel->getNumber($value)); } @@ -113,11 +121,15 @@ public function provideNumbers(): array ['12343', 12343], ['-9456km', -9456], ['0', 0], - ['2 054,10', 2054.1], - ['2046,45', 2046.45], + ['2 054,10', 205410, 'en_US'], + ['2 054,10', 2054.1, 'de_DE'], + ['2046,45', 204645, 'en_US'], + ['2046,45', 2046.45, 'de_DE'], ['2 054.52', 2054.52], - ['2,46 GB', 2.46], + ['2,46 GB', 246, 'en_US'], + ['2,46 GB', 2.46, 'de_DE'], ['2,054.00', 2054], + ['2,000', 2000, 'ja_JP'], ]; } } From 10de6de532aea6ca7a7b58334387f126e151dee2 Mon Sep 17 00:00:00 2001 From: Jeroen van Leusden <jeroen@reachdigital.nl> Date: Thu, 10 Jan 2019 16:51:48 +0100 Subject: [PATCH 1131/1295] Use correct base path to check if setup folder exists --- .../Magento/Framework/App/DocRootLocator.php | 17 ++++++++++++++--- .../App/Test/Unit/DocRootLocatorTest.php | 10 +++++++--- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/lib/internal/Magento/Framework/App/DocRootLocator.php b/lib/internal/Magento/Framework/App/DocRootLocator.php index 6fb35c42f1330..d73baf8e4e742 100644 --- a/lib/internal/Magento/Framework/App/DocRootLocator.php +++ b/lib/internal/Magento/Framework/App/DocRootLocator.php @@ -3,10 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Framework\App; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Filesystem; use Magento\Framework\Filesystem\Directory\ReadFactory; /** @@ -20,18 +22,26 @@ class DocRootLocator private $request; /** + * @deprecated * @var ReadFactory */ private $readFactory; + /** + * @var Filesystem + */ + private $filesystem; + /** * @param RequestInterface $request * @param ReadFactory $readFactory + * @param Filesystem|null $filesystem */ - public function __construct(RequestInterface $request, ReadFactory $readFactory) + public function __construct(RequestInterface $request, ReadFactory $readFactory, Filesystem $filesystem = null) { $this->request = $request; $this->readFactory = $readFactory; + $this->filesystem = $filesystem ?: ObjectManager::getInstance()->get(Filesystem::class); } /** @@ -42,7 +52,8 @@ public function __construct(RequestInterface $request, ReadFactory $readFactory) public function isPub() { $rootBasePath = $this->request->getServer('DOCUMENT_ROOT'); - $readDirectory = $this->readFactory->create(DirectoryList::ROOT); - return (substr($rootBasePath, -strlen('/pub')) === '/pub') && !$readDirectory->isExist($rootBasePath . 'setup'); + $readDirectory = $this->filesystem->getDirectoryRead(DirectoryList::ROOT); + + return (substr($rootBasePath, -\strlen('/pub')) === '/pub') && ! $readDirectory->isExist('setup'); } } diff --git a/lib/internal/Magento/Framework/App/Test/Unit/DocRootLocatorTest.php b/lib/internal/Magento/Framework/App/Test/Unit/DocRootLocatorTest.php index 23afbbc73d2b9..fd6c1f208080a 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/DocRootLocatorTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/DocRootLocatorTest.php @@ -21,11 +21,15 @@ public function testIsPub($path, $isExist, $result) { $request = $this->createMock(\Magento\Framework\App\Request\Http::class); $request->expects($this->once())->method('getServer')->willReturn($path); + + $readFactory = $this->createMock(\Magento\Framework\Filesystem\Directory\ReadFactory::class); + $reader = $this->createMock(\Magento\Framework\Filesystem\Directory\Read::class); + $filesystem = $this->createMock(\Magento\Framework\Filesystem::class); + $filesystem->expects($this->once())->method('getDirectoryRead')->willReturn($reader); $reader->expects($this->any())->method('isExist')->willReturn($isExist); - $readFactory = $this->createMock(\Magento\Framework\Filesystem\Directory\ReadFactory::class); - $readFactory->expects($this->once())->method('create')->willReturn($reader); - $model = new DocRootLocator($request, $readFactory); + + $model = new DocRootLocator($request, $readFactory, $filesystem); $this->assertSame($result, $model->isPub()); } From 40b5d2e2493f5ad8943203e7af920d703c47d499 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Fri, 12 Apr 2019 13:30:56 +0300 Subject: [PATCH 1132/1295] magento/magento2#20182: Static test fix. --- .../Magento/Framework/App/Test/Unit/DocRootLocatorTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/internal/Magento/Framework/App/Test/Unit/DocRootLocatorTest.php b/lib/internal/Magento/Framework/App/Test/Unit/DocRootLocatorTest.php index fd6c1f208080a..ef4152ba2e49e 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/DocRootLocatorTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/DocRootLocatorTest.php @@ -8,6 +8,9 @@ use Magento\Framework\App\DocRootLocator; +/** + * Test for Magento\Framework\App\DocRootLocator class. + */ class DocRootLocatorTest extends \PHPUnit\Framework\TestCase { /** From 40b3feeb684354d14c6b5cd7af0ffd11fef37088 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Tue, 16 Apr 2019 10:16:10 -0500 Subject: [PATCH 1133/1295] MAGETWO-97464: Braintree payment method not supported in multi-shipping - Fix integration test --- .../CaseCaptchaIsRequiredAfterFailedLoginAttemptsTest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Captcha/Observer/CaseCaptchaIsRequiredAfterFailedLoginAttemptsTest.php b/dev/tests/integration/testsuite/Magento/Captcha/Observer/CaseCaptchaIsRequiredAfterFailedLoginAttemptsTest.php index 32fab1f456b3e..4c469421402c2 100644 --- a/dev/tests/integration/testsuite/Magento/Captcha/Observer/CaseCaptchaIsRequiredAfterFailedLoginAttemptsTest.php +++ b/dev/tests/integration/testsuite/Magento/Captcha/Observer/CaseCaptchaIsRequiredAfterFailedLoginAttemptsTest.php @@ -7,9 +7,14 @@ use Magento\Framework\Message\MessageInterface; +/** + * @magentoAppArea adminhtml + */ class CaseCaptchaIsRequiredAfterFailedLoginAttemptsTest extends \Magento\TestFramework\TestCase\AbstractController { /** + * Tests backend login action with invalid captcha. + * * @magentoAdminConfigFixture admin/captcha/forms backend_login * @magentoAdminConfigFixture admin/captcha/enable 1 * @magentoAdminConfigFixture admin/captcha/mode always From cf60a15704f1e848aaa255e0658dc784afda8ec3 Mon Sep 17 00:00:00 2001 From: Dmytro Yushkin <dyushkin@adobe.com> Date: Tue, 16 Apr 2019 10:49:49 -0500 Subject: [PATCH 1134/1295] MAGETWO-97400: Name of categories in the Category tree on Product Edit page is not displayed according to the selected store view scope --- .../Product/Form/Modifier/CategoriesTest.php | 34 -------- .../Product/Form/Modifier/Categories.php | 80 +++++++++++++++---- 2 files changed, 66 insertions(+), 48 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php index cc3dda6e2d7b1..e741070547163 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php @@ -155,38 +155,4 @@ public function modifyMetaLockedDataProvider() { return [[true], [false]]; } - - public function testModifyMetaWithCaching() - { - $this->arrayManagerMock->expects($this->exactly(2)) - ->method('findPath') - ->willReturn(true); - $cacheManager = $this->getMockBuilder(CacheInterface::class) - ->getMockForAbstractClass(); - $cacheManager->expects($this->once()) - ->method('load') - ->with(Categories::CATEGORY_TREE_ID . '_'); - $cacheManager->expects($this->once()) - ->method('save'); - - $modifier = $this->createModel(); - $cacheContextProperty = new \ReflectionProperty( - Categories::class, - 'cacheManager' - ); - $cacheContextProperty->setAccessible(true); - $cacheContextProperty->setValue($modifier, $cacheManager); - - $groupCode = 'test_group_code'; - $meta = [ - $groupCode => [ - 'children' => [ - 'category_ids' => [ - 'sortOrder' => 10, - ], - ], - ], - ]; - $modifier->modifyMeta($meta); - } } diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php index 2dad7e8495b11..cb6ad00e30298 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Ui\DataProvider\Product\Form\Modifier; use Magento\Catalog\Model\Locator\LocatorInterface; @@ -11,6 +13,7 @@ use Magento\Framework\App\CacheInterface; use Magento\Framework\DB\Helper as DbHelper; use Magento\Catalog\Model\Category as CategoryModel; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\UrlInterface; use Magento\Framework\Stdlib\ArrayManager; @@ -202,6 +205,7 @@ protected function createNewCategoryModal(array $meta) * * @param array $meta * @return array + * @throws LocalizedException * @since 101.0.0 */ protected function customizeCategoriesField(array $meta) @@ -306,20 +310,64 @@ protected function customizeCategoriesField(array $meta) * * @param string|null $filter * @return array + * @throws LocalizedException * @since 101.0.0 */ protected function getCategoriesTree($filter = null) { - $categoryTree = $this->getCacheManager()->load(self::CATEGORY_TREE_ID . '_' . $filter); - if ($categoryTree) { - return $this->serializer->unserialize($categoryTree); + $storeId = (int) $this->locator->getStore()->getId(); + + $cachedCategoriesTree = $this->getCacheManager() + ->load($this->getCategoriesTreeCacheId($storeId, (string) $filter)); + if (!empty($cachedCategoriesTree)) { + return $this->serializer->unserialize($cachedCategoriesTree); } - $storeId = $this->locator->getStore()->getId(); + $categoriesTree = $this->retrieveCategoriesTree( + $storeId, + $this->retrieveShownCategoriesIds($storeId, (string) $filter) + ); + + $this->getCacheManager()->save( + $this->serializer->serialize($categoriesTree), + $this->getCategoriesTreeCacheId($storeId, (string) $filter), + [ + \Magento\Catalog\Model\Category::CACHE_TAG, + \Magento\Framework\App\Cache\Type\Block::CACHE_TAG + ] + ); + + return $categoriesTree; + } + + /** + * Get cache id for categories tree. + * + * @param int $storeId + * @param string $filter + * @return string + */ + private function getCategoriesTreeCacheId(int $storeId, string $filter = '') : string + { + return self::CATEGORY_TREE_ID + . '_' . (string) $storeId + . '_' . $filter; + } + + /** + * Retrieve filtered list of categories id. + * + * @param int $storeId + * @param string $filter + * @return array + * @throws LocalizedException + */ + private function retrieveShownCategoriesIds(int $storeId, string $filter = '') : array + { /* @var $matchingNamesCollection \Magento\Catalog\Model\ResourceModel\Category\Collection */ $matchingNamesCollection = $this->categoryCollectionFactory->create(); - if ($filter !== null) { + if (!empty($filter)) { $matchingNamesCollection->addAttributeToFilter( 'name', ['like' => $this->dbHelper->addLikeEscape($filter, ['position' => 'any'])] @@ -339,6 +387,19 @@ protected function getCategoriesTree($filter = null) } } + return $shownCategoriesIds; + } + + /** + * Retrieve tree of categories with attributes. + * + * @param int $storeId + * @param array $shownCategoriesIds + * @return array + * @throws LocalizedException + */ + private function retrieveCategoriesTree(int $storeId, array $shownCategoriesIds) : array + { /* @var $collection \Magento\Catalog\Model\ResourceModel\Category\Collection */ $collection = $this->categoryCollectionFactory->create(); @@ -365,15 +426,6 @@ protected function getCategoriesTree($filter = null) $categoryById[$category->getParentId()]['optgroup'][] = &$categoryById[$category->getId()]; } - $this->getCacheManager()->save( - $this->serializer->serialize($categoryById[CategoryModel::TREE_ROOT_ID]['optgroup']), - self::CATEGORY_TREE_ID . '_' . $filter, - [ - \Magento\Catalog\Model\Category::CACHE_TAG, - \Magento\Framework\App\Cache\Type\Block::CACHE_TAG - ] - ); - return $categoryById[CategoryModel::TREE_ROOT_ID]['optgroup']; } } From c866e77e5b36ebc423684474757f7288ff891519 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Tue, 16 Apr 2019 12:53:46 -0500 Subject: [PATCH 1135/1295] MAGETWO-97464: Braintree payment method not supported in multi-shipping - Updated usps gateway url --- app/code/Magento/Ups/Model/Carrier.php | 2 +- app/code/Magento/Ups/etc/config.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Ups/Model/Carrier.php b/app/code/Magento/Ups/Model/Carrier.php index a9e289d57300b..247422e065f75 100644 --- a/app/code/Magento/Ups/Model/Carrier.php +++ b/app/code/Magento/Ups/Model/Carrier.php @@ -488,7 +488,7 @@ protected function _getCgiQuotes() } $client = new \Zend_Http_Client(); $client->setUri($url); - $client->setConfig(['maxredirects' => 0, 'timeout' => 30]); + $client->setConfig(['maxredirects' => 2, 'timeout' => 30]); $client->setParameterGet($params); $response = $client->request(); $responseBody = $response->getBody(); diff --git a/app/code/Magento/Ups/etc/config.xml b/app/code/Magento/Ups/etc/config.xml index e2ac1c6d6c443..791b325c65e3f 100644 --- a/app/code/Magento/Ups/etc/config.xml +++ b/app/code/Magento/Ups/etc/config.xml @@ -19,7 +19,7 @@ <cutoff_cost /> <dest_type>RES</dest_type> <free_method>GND</free_method> - <gateway_url>http://www.ups.com/using/services/rave/qcostcgi.cgi</gateway_url> + <gateway_url>https://www.ups.com/using/services/rave/qcostcgi.cgi</gateway_url> <gateway_xml_url>https://onlinetools.ups.com/ups.app/xml/Rate</gateway_xml_url> <handling>0</handling> <model>Magento\Ups\Model\Carrier</model> From 57af527696d73c9488afbb15f9642785ef110819 Mon Sep 17 00:00:00 2001 From: Jitheesh <jitheeshvo@gmail.com> Date: Thu, 28 Feb 2019 15:51:33 +0530 Subject: [PATCH 1136/1295] Same product quantity not increment when added with guest user. #21375 - removed non required item options while comparing with merge items --- app/code/Magento/Quote/Model/Quote/Item/Compare.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Quote/Model/Quote/Item/Compare.php b/app/code/Magento/Quote/Model/Quote/Item/Compare.php index ddaa636ef32b3..76ba324518dc1 100644 --- a/app/code/Magento/Quote/Model/Quote/Item/Compare.php +++ b/app/code/Magento/Quote/Model/Quote/Item/Compare.php @@ -50,7 +50,7 @@ protected function getOptionValues($value) if (is_string($value) && $this->jsonValidator->isValid($value)) { $value = $this->serializer->unserialize($value); if (is_array($value)) { - unset($value['qty'], $value['uenc']); + unset($value['qty'], $value['uenc'], $value['related_product'], $value['item']); $value = array_filter($value, function ($optionValue) { return !empty($optionValue); }); From cd05ead490d1a31f49a9feed7f7915f061a1f499 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Tue, 16 Apr 2019 21:56:27 +0300 Subject: [PATCH 1137/1295] MAGETWO-98700: Shared catalog configurable product with out of stock options and instock options loads with empty drop down for non logged in customers --- .../ActionGroup/AdminProductActionGroup.xml | 6 +++++ .../StorefrontCategoryProductSection.xml | 1 + .../Mftf/Data/CatalogInventoryConfigData.xml | 23 +++++++++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 app/code/Magento/CatalogInventory/Test/Mftf/Data/CatalogInventoryConfigData.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index e68c75858102f..606d63825b660 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -269,4 +269,10 @@ <waitForElementVisible selector="{{AdminProductFormSection.attributeSetFilterResultByName(attributeSet.attribute_set_name)}}" stepKey="waitForNewAttributeSetIsShown"/> <click selector="{{AdminProductFormSection.attributeSetFilterResultByName(attributeSet.attribute_set_name)}}" stepKey="selectAttributeSet"/> </actionGroup> + <actionGroup name="SetCategoryByName"> + <arguments> + <argument name="categoryName" type="string"/> + </arguments> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{categoryName}}]" stepKey="searchAndSelectCategory"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml index ccb5ae60db59b..275021302a4d2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml @@ -24,5 +24,6 @@ <element name="productPriceOld" type="text" selector="//span[@data-price-type='oldPrice']//span[@class='price'][contains(., '{{var1}}')]" parameterized="true"/> <element name="productPriceLabel" type="text" selector="//span[@class='price-label'][contains(text(),'{{var1}}')]" parameterized="true"/> <element name="productPriceLinkAfterLabel" type="text" selector="//span[@class='price-label'][contains(text(),'{{var1}}')]/following::span[contains(text(), '{{var2}}')]" parameterized="true"/> + <element name="ProductStockUnavailable" type="text" selector="//*[text()='Out of stock']"/> </section> </sections> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Data/CatalogInventoryConfigData.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Data/CatalogInventoryConfigData.xml new file mode 100644 index 0000000000000..e14c36446fc2b --- /dev/null +++ b/app/code/Magento/CatalogInventory/Test/Mftf/Data/CatalogInventoryConfigData.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="StockOptionsDisplayOutOfStockProductsEnable"> + <data key="path">cataloginventory/options/show_out_of_stock</data> + <data key="scope_id">0</data> + <data key="label">Yes</data> + <data key="value">1</data> + </entity> + <entity name="StockOptionsDisplayOutOfStockProductsDisable"> + <data key="path">cataloginventory/options/show_out_of_stock</data> + <data key="scope_id">0</data> + <data key="label">No</data> + <data key="value">0</data> + </entity> +</entities> From 1443ee2c8de1d761bf77c3f31f7e5b6bb12b98c6 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Wed, 17 Apr 2019 00:35:01 +0300 Subject: [PATCH 1138/1295] MAGETWO-98700: Shared catalog configurable product with out of stock options and instock options loads with empty drop down for non logged in customers --- .../Test/Mftf/ActionGroup/AdminProductActionGroup.xml | 11 +++++------ .../Mftf/Section/StorefrontCategoryProductSection.xml | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index 606d63825b660..89d9cf8fe2822 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -248,15 +248,14 @@ <see selector="{{AdminMessagesSection.success}}" userInput="You saved the product." stepKey="seeSaveConfirmation"/> </actionGroup> - <actionGroup name="AdminAssignProductToCategory"> + <actionGroup name="AdminAssignProductToCategory" extends="AdminProductAssignCategory"> <arguments> <argument name="productId" type="string"/> <argument name="categoryName" type="string"/> </arguments> - <amOnPage url="{{AdminProductEditPage.url(productId)}}" stepKey="amOnPage"/> - <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{categoryName}}]" stepKey="selectCategory"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton"/> - <see selector="{{AdminMessagesSection.success}}" userInput="You saved the product." stepKey="seeSaveProductMessage"/> + <amOnPage url="{{AdminProductEditPage.url(productId)}}" before="searchAndSelectCategory" stepKey="amOnPage"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" after="searchAndSelectCategory" stepKey="clickOnSaveButton"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the product." after="clickOnSaveButton" stepKey="seeSaveProductMessage"/> </actionGroup> <actionGroup name="AdminChangeProductAttributeSet"> @@ -269,7 +268,7 @@ <waitForElementVisible selector="{{AdminProductFormSection.attributeSetFilterResultByName(attributeSet.attribute_set_name)}}" stepKey="waitForNewAttributeSetIsShown"/> <click selector="{{AdminProductFormSection.attributeSetFilterResultByName(attributeSet.attribute_set_name)}}" stepKey="selectAttributeSet"/> </actionGroup> - <actionGroup name="SetCategoryByName"> + <actionGroup name="AdminProductAssignCategory"> <arguments> <argument name="categoryName" type="string"/> </arguments> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml index 275021302a4d2..ceec6839f6381 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml @@ -24,6 +24,6 @@ <element name="productPriceOld" type="text" selector="//span[@data-price-type='oldPrice']//span[@class='price'][contains(., '{{var1}}')]" parameterized="true"/> <element name="productPriceLabel" type="text" selector="//span[@class='price-label'][contains(text(),'{{var1}}')]" parameterized="true"/> <element name="productPriceLinkAfterLabel" type="text" selector="//span[@class='price-label'][contains(text(),'{{var1}}')]/following::span[contains(text(), '{{var2}}')]" parameterized="true"/> - <element name="ProductStockUnavailable" type="text" selector="//*[text()='Out of stock']"/> + <element name="productStockUnavailable" type="text" selector=".product-item-actions .stock.unavailable"/> </section> </sections> From 4a88bfd0ce0591956bf1724a34ab034e85a06650 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Wed, 17 Apr 2019 04:45:34 +0300 Subject: [PATCH 1139/1295] MAGETWO-98700: Shared catalog configurable product with out of stock options and instock options loads with empty drop down for non logged in customers --- .../Test/Mftf/Section/StorefrontCategoryProductSection.xml | 2 +- .../AdminCreateApiConfigurableProductActionGroup.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml index ceec6839f6381..26c9035d6b136 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml @@ -24,6 +24,6 @@ <element name="productPriceOld" type="text" selector="//span[@data-price-type='oldPrice']//span[@class='price'][contains(., '{{var1}}')]" parameterized="true"/> <element name="productPriceLabel" type="text" selector="//span[@class='price-label'][contains(text(),'{{var1}}')]" parameterized="true"/> <element name="productPriceLinkAfterLabel" type="text" selector="//span[@class='price-label'][contains(text(),'{{var1}}')]/following::span[contains(text(), '{{var2}}')]" parameterized="true"/> - <element name="productStockUnavailable" type="text" selector=".product-item-actions .stock.unavailable"/> + <element name="productStockUnavailableByName" type="text" selector="//a[contains(@class, 'product-item-link') and normalize-space(text())='{{productName}}']/ancestor::div[contains(@class, 'product-item-details')]//span[contains(text(),'Out of stock')]" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml index cdb147e99ad58..abbef02adc520 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminCreateApiConfigurableProductActionGroup"> <arguments> - <argument name="productName" defaultValue="ApiConfigurableProductWithOutCategory" type="string"/> + <argument name="productName" defaultValue="{{ApiConfigurableProductWithOutCategory.name}}" type="string"/> </arguments> <!-- Create the configurable product based on the data in the /data folder --> From 7a556aea88c16d351d2a4209e1b10090a7a99637 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Wed, 17 Apr 2019 13:49:58 +0300 Subject: [PATCH 1140/1295] MC-15443: Out of memory exception thrown when indexer:reindex is run --- .../Indexer/Category/Product/Action/Full.php | 127 +++++++----- .../Model/Indexer/Product/Eav/Action/Full.php | 108 ++++++---- .../Indexer/Product/Price/Action/Full.php | 195 ++++++++++-------- .../Indexer/Product/Eav/Action/FullTest.php | 73 +++++-- .../Model/Indexer/Stock/Action/Full.php | 51 +++-- 5 files changed, 330 insertions(+), 224 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php index f8121b55dbf99..c92ef80dabbbc 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php @@ -5,31 +5,41 @@ */ namespace Magento\Catalog\Model\Indexer\Category\Product\Action; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Config; +use Magento\Catalog\Model\Indexer\Category\Product\AbstractAction; use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\DB\Query\Generator as QueryGenerator; use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Select; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Framework\Indexer\BatchProviderInterface; +use Magento\Framework\Indexer\BatchSizeManagementInterface; use Magento\Indexer\Model\ProcessManager; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; /** * Class Full reindex action * - * @package Magento\Catalog\Model\Indexer\Category\Product\Action * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Full extends \Magento\Catalog\Model\Indexer\Category\Product\AbstractAction +class Full extends AbstractAction { /** - * @var \Magento\Framework\Indexer\BatchSizeManagementInterface + * @var BatchSizeManagementInterface */ private $batchSizeManagement; /** - * @var \Magento\Framework\Indexer\BatchProviderInterface + * @var BatchProviderInterface */ private $batchProvider; /** - * @var \Magento\Framework\EntityManager\MetadataPool + * @var MetadataPool */ protected $metadataPool; @@ -52,25 +62,25 @@ class Full extends \Magento\Catalog\Model\Indexer\Category\Product\AbstractActio /** * @param ResourceConnection $resource - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Catalog\Model\Config $config + * @param StoreManagerInterface $storeManager + * @param Config $config * @param QueryGenerator|null $queryGenerator - * @param \Magento\Framework\Indexer\BatchSizeManagementInterface|null $batchSizeManagement - * @param \Magento\Framework\Indexer\BatchProviderInterface|null $batchProvider - * @param \Magento\Framework\EntityManager\MetadataPool|null $metadataPool + * @param BatchSizeManagementInterface|null $batchSizeManagement + * @param BatchProviderInterface|null $batchProvider + * @param MetadataPool|null $metadataPool * @param int|null $batchRowsCount * @param ActiveTableSwitcher|null $activeTableSwitcher * @param ProcessManager $processManager * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( - \Magento\Framework\App\ResourceConnection $resource, - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Catalog\Model\Config $config, + ResourceConnection $resource, + StoreManagerInterface $storeManager, + Config $config, QueryGenerator $queryGenerator = null, - \Magento\Framework\Indexer\BatchSizeManagementInterface $batchSizeManagement = null, - \Magento\Framework\Indexer\BatchProviderInterface $batchProvider = null, - \Magento\Framework\EntityManager\MetadataPool $metadataPool = null, + BatchSizeManagementInterface $batchSizeManagement = null, + BatchProviderInterface $batchProvider = null, + MetadataPool $metadataPool = null, $batchRowsCount = null, ActiveTableSwitcher $activeTableSwitcher = null, ProcessManager $processManager = null @@ -81,15 +91,15 @@ public function __construct( $config, $queryGenerator ); - $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); + $objectManager = ObjectManager::getInstance(); $this->batchSizeManagement = $batchSizeManagement ?: $objectManager->get( - \Magento\Framework\Indexer\BatchSizeManagementInterface::class + BatchSizeManagementInterface::class ); $this->batchProvider = $batchProvider ?: $objectManager->get( - \Magento\Framework\Indexer\BatchProviderInterface::class + BatchProviderInterface::class ); $this->metadataPool = $metadataPool ?: $objectManager->get( - \Magento\Framework\EntityManager\MetadataPool::class + MetadataPool::class ); $this->batchRowsCount = $batchRowsCount; $this->activeTableSwitcher = $activeTableSwitcher ?: $objectManager->get(ActiveTableSwitcher::class); @@ -97,33 +107,39 @@ public function __construct( } /** + * Create the store tables + * * @return void */ private function createTables() { foreach ($this->storeManager->getStores() as $store) { - $this->tableMaintainer->createTablesForStore($store->getId()); + $this->tableMaintainer->createTablesForStore((int)$store->getId()); } } /** + * Truncates the replica tables + * * @return void */ private function clearReplicaTables() { foreach ($this->storeManager->getStores() as $store) { - $this->connection->truncateTable($this->tableMaintainer->getMainReplicaTable($store->getId())); + $this->connection->truncateTable($this->tableMaintainer->getMainReplicaTable((int)$store->getId())); } } /** + * Switches the active table + * * @return void */ private function switchTables() { $tablesToSwitch = []; foreach ($this->storeManager->getStores() as $store) { - $tablesToSwitch[] = $this->tableMaintainer->getMainTable($store->getId()); + $tablesToSwitch[] = $this->tableMaintainer->getMainTable((int)$store->getId()); } $this->activeTableSwitcher->switchTable($this->connection, $tablesToSwitch); } @@ -133,12 +149,13 @@ private function switchTables() * * @return $this */ - public function execute() + public function execute(): self { $this->createTables(); $this->clearReplicaTables(); $this->reindex(); $this->switchTables(); + return $this; } @@ -165,7 +182,7 @@ protected function reindex() /** * Execute indexation by store * - * @param \Magento\Store\Model\Store $store + * @param Store $store */ private function reindexStore($store) { @@ -177,31 +194,31 @@ private function reindexStore($store) /** * Publish data from tmp to replica table * - * @param \Magento\Store\Model\Store $store + * @param Store $store * @return void */ private function publishData($store) { - $select = $this->connection->select()->from($this->tableMaintainer->getMainTmpTable($store->getId())); + $select = $this->connection->select()->from($this->tableMaintainer->getMainTmpTable((int)$store->getId())); $columns = array_keys( - $this->connection->describeTable($this->tableMaintainer->getMainReplicaTable($store->getId())) + $this->connection->describeTable($this->tableMaintainer->getMainReplicaTable((int)$store->getId())) ); - $tableName = $this->tableMaintainer->getMainReplicaTable($store->getId()); + $tableName = $this->tableMaintainer->getMainReplicaTable((int)$store->getId()); $this->connection->query( $this->connection->insertFromSelect( $select, $tableName, $columns, - \Magento\Framework\DB\Adapter\AdapterInterface::INSERT_ON_DUPLICATE + AdapterInterface::INSERT_ON_DUPLICATE ) ); } /** - * {@inheritdoc} + * @inheritdoc */ - protected function reindexRootCategory(\Magento\Store\Model\Store $store) + protected function reindexRootCategory(Store $store) { if ($this->isIndexRootCategoryNeeded()) { $this->reindexCategoriesBySelect($this->getAllProducts($store), 'cp.entity_id IN (?)', $store); @@ -211,10 +228,10 @@ protected function reindexRootCategory(\Magento\Store\Model\Store $store) /** * Reindex products of anchor categories * - * @param \Magento\Store\Model\Store $store + * @param Store $store * @return void */ - protected function reindexAnchorCategories(\Magento\Store\Model\Store $store) + protected function reindexAnchorCategories(Store $store) { $this->reindexCategoriesBySelect($this->getAnchorCategoriesSelect($store), 'ccp.product_id IN (?)', $store); } @@ -222,10 +239,10 @@ protected function reindexAnchorCategories(\Magento\Store\Model\Store $store) /** * Reindex products of non anchor categories * - * @param \Magento\Store\Model\Store $store + * @param Store $store * @return void */ - protected function reindexNonAnchorCategories(\Magento\Store\Model\Store $store) + protected function reindexNonAnchorCategories(Store $store) { $this->reindexCategoriesBySelect($this->getNonAnchorCategoriesSelect($store), 'ccp.product_id IN (?)', $store); } @@ -233,40 +250,42 @@ protected function reindexNonAnchorCategories(\Magento\Store\Model\Store $store) /** * Reindex categories using given SQL select and condition. * - * @param \Magento\Framework\DB\Select $basicSelect + * @param Select $basicSelect * @param string $whereCondition - * @param \Magento\Store\Model\Store $store + * @param Store $store * @return void */ - private function reindexCategoriesBySelect(\Magento\Framework\DB\Select $basicSelect, $whereCondition, $store) + private function reindexCategoriesBySelect(Select $basicSelect, $whereCondition, $store) { - $this->tableMaintainer->createMainTmpTable($store->getId()); + $this->tableMaintainer->createMainTmpTable((int)$store->getId()); - $entityMetadata = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class); + $entityMetadata = $this->metadataPool->getMetadata(ProductInterface::class); $columns = array_keys( - $this->connection->describeTable($this->tableMaintainer->getMainTmpTable($store->getId())) + $this->connection->describeTable($this->tableMaintainer->getMainTmpTable((int)$store->getId())) ); $this->batchSizeManagement->ensureBatchSize($this->connection, $this->batchRowsCount); - $batches = $this->batchProvider->getBatches( - $this->connection, - $entityMetadata->getEntityTable(), + + $select = $this->connection->select(); + $select->distinct(true); + $select->from(['e' => $entityMetadata->getEntityTable()], $entityMetadata->getIdentifierField()); + + $batchQueries = $this->prepareSelectsByRange( + $select, $entityMetadata->getIdentifierField(), - $this->batchRowsCount + (int)$this->batchRowsCount ); - foreach ($batches as $batch) { - $this->connection->delete($this->tableMaintainer->getMainTmpTable($store->getId())); + + foreach ($batchQueries as $query) { + $this->connection->delete($this->tableMaintainer->getMainTmpTable((int)$store->getId())); + $entityIds = $this->connection->fetchCol($query); $resultSelect = clone $basicSelect; - $select = $this->connection->select(); - $select->distinct(true); - $select->from(['e' => $entityMetadata->getEntityTable()], $entityMetadata->getIdentifierField()); - $entityIds = $this->batchProvider->getBatchIds($this->connection, $select, $batch); $resultSelect->where($whereCondition, $entityIds); $this->connection->query( $this->connection->insertFromSelect( $resultSelect, - $this->tableMaintainer->getMainTmpTable($store->getId()), + $this->tableMaintainer->getMainTmpTable((int)$store->getId()), $columns, - \Magento\Framework\DB\Adapter\AdapterInterface::INSERT_ON_DUPLICATE + AdapterInterface::INSERT_ON_DUPLICATE ) ); $this->publishData($store); diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Eav/Action/Full.php b/app/code/Magento/Catalog/Model/Indexer/Product/Eav/Action/Full.php index 802176092d147..b9ca4f342b45b 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Eav/Action/Full.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Eav/Action/Full.php @@ -7,26 +7,41 @@ namespace Magento\Catalog\Model\Indexer\Product\Eav\Action; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Indexer\Product\Eav\AbstractAction; use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Query\BatchIteratorInterface; +use Magento\Framework\DB\Query\Generator as QueryGenerator; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Indexer\BatchProviderInterface; +use Magento\Store\Model\ScopeInterface; /** * Class Full reindex action + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Full extends \Magento\Catalog\Model\Indexer\Product\Eav\AbstractAction +class Full extends AbstractAction { /** - * @var \Magento\Framework\EntityManager\MetadataPool + * @var MetadataPool */ private $metadataPool; /** - * @var \Magento\Framework\Indexer\BatchProviderInterface + * @var BatchProviderInterface */ private $batchProvider; /** - * @var \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator + * @var BatchSizeCalculator */ private $batchSizeCalculator; @@ -36,44 +51,54 @@ class Full extends \Magento\Catalog\Model\Indexer\Product\Eav\AbstractAction private $activeTableSwitcher; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ScopeConfigInterface */ private $scopeConfig; /** - * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory $eavDecimalFactory - * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory $eavSourceFactory - * @param \Magento\Framework\EntityManager\MetadataPool|null $metadataPool - * @param \Magento\Framework\Indexer\BatchProviderInterface|null $batchProvider - * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator $batchSizeCalculator + * @var QueryGenerator|null + */ + private $batchQueryGenerator; + + /** + * @param DecimalFactory $eavDecimalFactory + * @param SourceFactory $eavSourceFactory + * @param MetadataPool|null $metadataPool + * @param BatchProviderInterface|null $batchProvider + * @param BatchSizeCalculator $batchSizeCalculator * @param ActiveTableSwitcher|null $activeTableSwitcher - * @param \Magento\Framework\App\Config\ScopeConfigInterface|null $scopeConfig + * @param ScopeConfigInterface|null $scopeConfig + * @param QueryGenerator|null $batchQueryGenerator */ public function __construct( - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory $eavDecimalFactory, - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory $eavSourceFactory, - \Magento\Framework\EntityManager\MetadataPool $metadataPool = null, - \Magento\Framework\Indexer\BatchProviderInterface $batchProvider = null, - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator $batchSizeCalculator = null, + DecimalFactory $eavDecimalFactory, + SourceFactory $eavSourceFactory, + MetadataPool $metadataPool = null, + BatchProviderInterface $batchProvider = null, + BatchSizeCalculator $batchSizeCalculator = null, ActiveTableSwitcher $activeTableSwitcher = null, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig = null + ScopeConfigInterface $scopeConfig = null, + QueryGenerator $batchQueryGenerator = null ) { - $this->scopeConfig = $scopeConfig ?: \Magento\Framework\App\ObjectManager::getInstance()->get( - \Magento\Framework\App\Config\ScopeConfigInterface::class + $this->scopeConfig = $scopeConfig ?: ObjectManager::getInstance()->get( + ScopeConfigInterface::class ); parent::__construct($eavDecimalFactory, $eavSourceFactory, $scopeConfig); - $this->metadataPool = $metadataPool ?: \Magento\Framework\App\ObjectManager::getInstance()->get( - \Magento\Framework\EntityManager\MetadataPool::class + $this->metadataPool = $metadataPool ?: ObjectManager::getInstance()->get( + MetadataPool::class ); - $this->batchProvider = $batchProvider ?: \Magento\Framework\App\ObjectManager::getInstance()->get( - \Magento\Framework\Indexer\BatchProviderInterface::class + $this->batchProvider = $batchProvider ?: ObjectManager::getInstance()->get( + BatchProviderInterface::class ); - $this->batchSizeCalculator = $batchSizeCalculator ?: \Magento\Framework\App\ObjectManager::getInstance()->get( - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator::class + $this->batchSizeCalculator = $batchSizeCalculator ?: ObjectManager::getInstance()->get( + BatchSizeCalculator::class ); - $this->activeTableSwitcher = $activeTableSwitcher ?: \Magento\Framework\App\ObjectManager::getInstance()->get( + $this->activeTableSwitcher = $activeTableSwitcher ?: ObjectManager::getInstance()->get( ActiveTableSwitcher::class ); + $this->batchQueryGenerator = $batchQueryGenerator ?: ObjectManager::getInstance()->get( + QueryGenerator::class + ); } /** @@ -81,7 +106,7 @@ public function __construct( * * @param array|int|null $ids * @return void - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function execute($ids = null) @@ -94,20 +119,21 @@ public function execute($ids = null) $connection = $indexer->getConnection(); $mainTable = $this->activeTableSwitcher->getAdditionalTableName($indexer->getMainTable()); $connection->truncateTable($mainTable); - $entityMetadata = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class); - $batches = $this->batchProvider->getBatches( - $connection, - $entityMetadata->getEntityTable(), + $entityMetadata = $this->metadataPool->getMetadata(ProductInterface::class); + + $select = $connection->select(); + $select->distinct(true); + $select->from(['e' => $entityMetadata->getEntityTable()], $entityMetadata->getIdentifierField()); + + $batchQueries = $this->batchQueryGenerator->generate( $entityMetadata->getIdentifierField(), - $this->batchSizeCalculator->estimateBatchSize($connection, $indexerName) + $select, + $this->batchSizeCalculator->estimateBatchSize($connection, $indexerName), + BatchIteratorInterface::NON_UNIQUE_FIELD_ITERATOR ); - foreach ($batches as $batch) { - /** @var \Magento\Framework\DB\Select $select */ - $select = $connection->select(); - $select->distinct(true); - $select->from(['e' => $entityMetadata->getEntityTable()], $entityMetadata->getIdentifierField()); - $entityIds = $this->batchProvider->getBatchIds($connection, $select, $batch); + foreach ($batchQueries as $query) { + $entityIds = $connection->fetchCol($query); if (!empty($entityIds)) { $indexer->reindexEntities($this->processRelations($indexer, $entityIds, true)); $this->syncData($indexer, $mainTable); @@ -116,7 +142,7 @@ public function execute($ids = null) $this->activeTableSwitcher->switchTable($indexer->getConnection(), [$indexer->getMainTable()]); } } catch (\Exception $e) { - throw new \Magento\Framework\Exception\LocalizedException(__($e->getMessage()), $e); + throw new LocalizedException(__($e->getMessage()), $e); } } @@ -136,7 +162,7 @@ protected function syncData($indexer, $destinationTable, $ids = null) $select, $destinationTable, $targetColumns, - \Magento\Framework\DB\Adapter\AdapterInterface::INSERT_ON_DUPLICATE + AdapterInterface::INSERT_ON_DUPLICATE ); $connection->query($query); $connection->commit(); @@ -155,7 +181,7 @@ private function isEavIndexerEnabled(): bool { $eavIndexerStatus = $this->scopeConfig->getValue( self::ENABLE_EAV_INDEXER, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ScopeInterface::SCOPE_STORE ); return (bool)$eavIndexerStatus; diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php index 1a75751570658..e0091ffdf7c52 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php @@ -3,41 +3,64 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Catalog\Model\Indexer\Product\Price\Action; +use Magento\Catalog\Model\Indexer\Product\Price\AbstractAction; +use Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory; +use Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BatchSizeCalculator; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\Factory; +use Magento\Directory\Model\CurrencyFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\ObjectManager; use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\PriceInterface; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Query\BatchIterator; +use Magento\Framework\DB\Query\Generator as QueryGenerator; +use Magento\Framework\DB\Select; use Magento\Framework\EntityManager\EntityMetadataInterface; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Indexer\BatchProviderInterface; use Magento\Framework\Indexer\DimensionalIndexerInterface; use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; +use Magento\Framework\Stdlib\DateTime; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Indexer\Model\ProcessManager; use Magento\Store\Model\Indexer\WebsiteDimensionProvider; +use Magento\Store\Model\StoreManagerInterface; /** * Class Full reindex action * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Full extends \Magento\Catalog\Model\Indexer\Product\Price\AbstractAction +class Full extends AbstractAction { /** - * @var \Magento\Framework\EntityManager\MetadataPool + * @var MetadataPool */ private $metadataPool; /** - * @var \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BatchSizeCalculator + * @var BatchSizeCalculator */ private $batchSizeCalculator; /** - * @var \Magento\Framework\Indexer\BatchProviderInterface + * @var BatchProviderInterface */ private $batchProvider; /** - * @var \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher + * @var ActiveTableSwitcher */ private $activeTableSwitcher; @@ -47,54 +70,61 @@ class Full extends \Magento\Catalog\Model\Indexer\Product\Price\AbstractAction private $productMetaDataCached; /** - * @var \Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory + * @var DimensionCollectionFactory */ private $dimensionCollectionFactory; /** - * @var \Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer + * @var TableMaintainer */ private $dimensionTableMaintainer; /** - * @var \Magento\Indexer\Model\ProcessManager + * @var ProcessManager */ private $processManager; /** - * @param \Magento\Framework\App\Config\ScopeConfigInterface $config - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Directory\Model\CurrencyFactory $currencyFactory - * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate - * @param \Magento\Framework\Stdlib\DateTime $dateTime - * @param \Magento\Catalog\Model\Product\Type $catalogProductType - * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\Factory $indexerPriceFactory - * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice $defaultIndexerResource - * @param \Magento\Framework\EntityManager\MetadataPool|null $metadataPool - * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BatchSizeCalculator|null $batchSizeCalculator - * @param \Magento\Framework\Indexer\BatchProviderInterface|null $batchProvider - * @param \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher|null $activeTableSwitcher - * @param \Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory|null $dimensionCollectionFactory - * @param \Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer|null $dimensionTableMaintainer - * @param \Magento\Indexer\Model\ProcessManager $processManager + * @var QueryGenerator|null + */ + private $batchQueryGenerator; + + /** + * @param ScopeConfigInterface $config + * @param StoreManagerInterface $storeManager + * @param CurrencyFactory $currencyFactory + * @param TimezoneInterface $localeDate + * @param DateTime $dateTime + * @param Type $catalogProductType + * @param Factory $indexerPriceFactory + * @param DefaultPrice $defaultIndexerResource + * @param MetadataPool|null $metadataPool + * @param BatchSizeCalculator|null $batchSizeCalculator + * @param BatchProviderInterface|null $batchProvider + * @param ActiveTableSwitcher|null $activeTableSwitcher + * @param DimensionCollectionFactory|null $dimensionCollectionFactory + * @param TableMaintainer|null $dimensionTableMaintainer + * @param ProcessManager $processManager + * @param QueryGenerator|null $batchQueryGenerator * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( - \Magento\Framework\App\Config\ScopeConfigInterface $config, - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Directory\Model\CurrencyFactory $currencyFactory, - \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, - \Magento\Framework\Stdlib\DateTime $dateTime, - \Magento\Catalog\Model\Product\Type $catalogProductType, - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\Factory $indexerPriceFactory, - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice $defaultIndexerResource, - \Magento\Framework\EntityManager\MetadataPool $metadataPool = null, - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BatchSizeCalculator $batchSizeCalculator = null, - \Magento\Framework\Indexer\BatchProviderInterface $batchProvider = null, - \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher $activeTableSwitcher = null, - \Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory $dimensionCollectionFactory = null, - \Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer $dimensionTableMaintainer = null, - \Magento\Indexer\Model\ProcessManager $processManager = null + ScopeConfigInterface $config, + StoreManagerInterface $storeManager, + CurrencyFactory $currencyFactory, + TimezoneInterface $localeDate, + DateTime $dateTime, + Type $catalogProductType, + Factory $indexerPriceFactory, + DefaultPrice $defaultIndexerResource, + MetadataPool $metadataPool = null, + BatchSizeCalculator $batchSizeCalculator = null, + BatchProviderInterface $batchProvider = null, + ActiveTableSwitcher $activeTableSwitcher = null, + DimensionCollectionFactory $dimensionCollectionFactory = null, + TableMaintainer $dimensionTableMaintainer = null, + ProcessManager $processManager = null, + QueryGenerator $batchQueryGenerator = null ) { parent::__construct( $config, @@ -107,26 +137,27 @@ public function __construct( $defaultIndexerResource ); $this->metadataPool = $metadataPool ?: ObjectManager::getInstance()->get( - \Magento\Framework\EntityManager\MetadataPool::class + MetadataPool::class ); $this->batchSizeCalculator = $batchSizeCalculator ?: ObjectManager::getInstance()->get( - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BatchSizeCalculator::class + BatchSizeCalculator::class ); $this->batchProvider = $batchProvider ?: ObjectManager::getInstance()->get( - \Magento\Framework\Indexer\BatchProviderInterface::class + BatchProviderInterface::class ); $this->activeTableSwitcher = $activeTableSwitcher ?: ObjectManager::getInstance()->get( - \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher::class + ActiveTableSwitcher::class ); $this->dimensionCollectionFactory = $dimensionCollectionFactory ?: ObjectManager::getInstance()->get( - \Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory::class + DimensionCollectionFactory::class ); $this->dimensionTableMaintainer = $dimensionTableMaintainer ?: ObjectManager::getInstance()->get( - \Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer::class + TableMaintainer::class ); $this->processManager = $processManager ?: ObjectManager::getInstance()->get( - \Magento\Indexer\Model\ProcessManager::class + ProcessManager::class ); + $this->batchQueryGenerator = $batchQueryGenerator ?? ObjectManager::getInstance()->get(QueryGenerator::class); } /** @@ -143,7 +174,7 @@ public function execute($ids = null) //Prepare indexer tables before full reindex $this->prepareTables(); - /** @var \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice $indexer */ + /** @var DefaultPrice $indexer */ foreach ($this->getTypeIndexers(true) as $typeId => $priceIndexer) { if ($priceIndexer instanceof DimensionalIndexerInterface) { //New price reindex mechanism @@ -207,7 +238,7 @@ private function reindexProductTypeWithDimensions(DimensionalIndexerInterface $p $userFunctions = []; foreach ($this->dimensionCollectionFactory->create() as $dimensions) { $userFunctions[] = function () use ($priceIndexer, $dimensions, $typeId) { - return $this->reindexByBatches($priceIndexer, $dimensions, $typeId); + $this->reindexByBatches($priceIndexer, $dimensions, $typeId); }; } $this->processManager->execute($userFunctions); @@ -226,7 +257,7 @@ private function reindexProductTypeWithDimensions(DimensionalIndexerInterface $p private function reindexByBatches(DimensionalIndexerInterface $priceIndexer, array $dimensions, string $typeId) { foreach ($this->getBatchesForIndexer($typeId) as $batch) { - $this->reindexByBatchWithDimensions($priceIndexer, $batch, $dimensions, $typeId); + $this->reindexByBatchWithDimensions($priceIndexer, $batch, $dimensions); } } @@ -235,16 +266,20 @@ private function reindexByBatches(DimensionalIndexerInterface $priceIndexer, arr * * @param string $typeId * - * @return \Generator + * @return BatchIterator * @throws \Exception */ - private function getBatchesForIndexer(string $typeId) + private function getBatchesForIndexer(string $typeId): BatchIterator { $connection = $this->_defaultIndexerResource->getConnection(); - return $this->batchProvider->getBatches( - $connection, - $this->getProductMetaData()->getEntityTable(), + $entityMetadata = $this->getProductMetaData(); + $select = $connection->select(); + $select->distinct(true); + $select->from(['e' => $entityMetadata->getEntityTable()], $entityMetadata->getIdentifierField()); + + return $this->batchQueryGenerator->generate( $this->getProductMetaData()->getIdentifierField(), + $select, $this->batchSizeCalculator->estimateBatchSize( $connection, $typeId @@ -256,20 +291,18 @@ private function getBatchesForIndexer(string $typeId) * Reindex by batch for new 'Dimensional' price indexer * * @param DimensionalIndexerInterface $priceIndexer - * @param array $batch + * @param Select $batchQuery * @param array $dimensions - * @param string $typeId * * @return void * @throws \Exception */ private function reindexByBatchWithDimensions( DimensionalIndexerInterface $priceIndexer, - array $batch, - array $dimensions, - string $typeId + Select $batchQuery, + array $dimensions ) { - $entityIds = $this->getEntityIdsFromBatch($typeId, $batch); + $entityIds = $this->getEntityIdsFromBatch($batchQuery); if (!empty($entityIds)) { $this->dimensionTableMaintainer->createMainTmpTable($dimensions); @@ -298,7 +331,7 @@ private function reindexByBatchWithDimensions( private function reindexProductType(PriceInterface $priceIndexer, string $typeId) { foreach ($this->getBatchesForIndexer($typeId) as $batch) { - $this->reindexBatch($priceIndexer, $batch, $typeId); + $this->reindexBatch($priceIndexer, $batch); } } @@ -306,15 +339,13 @@ private function reindexProductType(PriceInterface $priceIndexer, string $typeId * Reindex by batch for old price indexer * * @param PriceInterface $priceIndexer - * @param array $batch - * @param string $typeId - * + * @param Select $batch * @return void * @throws \Exception */ - private function reindexBatch(PriceInterface $priceIndexer, array $batch, string $typeId) + private function reindexBatch(PriceInterface $priceIndexer, Select $batch) { - $entityIds = $this->getEntityIdsFromBatch($typeId, $batch); + $entityIds = $this->getEntityIdsFromBatch($batch); if (!empty($entityIds)) { // Temporary table will created if not exists @@ -339,27 +370,15 @@ private function reindexBatch(PriceInterface $priceIndexer, array $batch, string /** * Get Entity Ids from batch * - * @param string $typeId - * @param array $batch - * + * @param Select $batch * @return array * @throws \Exception */ - private function getEntityIdsFromBatch(string $typeId, array $batch) + private function getEntityIdsFromBatch(Select $batch): array { $connection = $this->_defaultIndexerResource->getConnection(); - // Get entity ids from batch - $select = $connection - ->select() - ->distinct(true) - ->from( - ['e' => $this->getProductMetaData()->getEntityTable()], - $this->getProductMetaData()->getIdentifierField() - ) - ->where('type_id = ?', $typeId); - - return $this->batchProvider->getBatchIds($connection, $select, $batch); + return $connection->fetchCol($batch); } /** @@ -368,7 +387,7 @@ private function getEntityIdsFromBatch(string $typeId, array $batch) * @return EntityMetadataInterface * @throws \Exception */ - private function getProductMetaData() + private function getProductMetaData(): EntityMetadataInterface { if ($this->productMetaDataCached === null) { $this->productMetaDataCached = $this->metadataPool->getMetadata(ProductInterface::class); @@ -383,7 +402,7 @@ private function getProductMetaData() * @return string * @throws \Exception */ - private function getReplicaTable() + private function getReplicaTable(): string { return $this->activeTableSwitcher->getAdditionalTableName( $this->_defaultIndexerResource->getMainTable() @@ -394,6 +413,7 @@ private function getReplicaTable() * Replacement of tables from replica to main * * @return void + * @throws \Zend_Db_Statement_Exception */ private function switchTables() { @@ -417,11 +437,12 @@ private function switchTables() /** * Move data from old price indexer mechanism to new indexer mechanism by dimensions. + * * Used only for backward compatibility * * @param array $dimensions - * * @return void + * @throws \Zend_Db_Statement_Exception */ private function moveDataFromReplicaTableToReplicaTables(array $dimensions) { @@ -455,17 +476,17 @@ private function moveDataFromReplicaTableToReplicaTables(array $dimensions) $select, $replicaTablesByDimension, [], - \Magento\Framework\DB\Adapter\AdapterInterface::INSERT_ON_DUPLICATE + AdapterInterface::INSERT_ON_DUPLICATE ) ); } /** - * @deprecated + * Retrieves the index table that should be used * - * @inheritdoc + * @deprecated */ - protected function getIndexTargetTable() + protected function getIndexTargetTable(): string { return $this->activeTableSwitcher->getAdditionalTableName($this->_defaultIndexerResource->getMainTable()); } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/FullTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/FullTest.php index cf9e83ed39650..967a2167c688a 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/FullTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/FullTest.php @@ -5,13 +5,23 @@ */ namespace Magento\Catalog\Test\Unit\Model\Indexer\Product\Eav\Action; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Indexer\Product\Eav\Action\Full; use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\Decimal; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\Source; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Query\Generator; +use Magento\Framework\DB\Select; +use Magento\Framework\EntityManager\EntityMetadataInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory; use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory; use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\Indexer\BatchProviderInterface; use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator; +use PHPUnit\Framework\MockObject\MockObject as MockObject; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -19,60 +29,69 @@ class FullTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Catalog\Model\Indexer\Product\Eav\Action\Full|\PHPUnit_Framework_MockObject_MockObject + * @var Full|MockObject */ private $model; /** - * @var DecimalFactory|\PHPUnit_Framework_MockObject_MockObject + * @var DecimalFactory|MockObject */ private $eavDecimalFactory; /** - * @var SourceFactory|\PHPUnit_Framework_MockObject_MockObject + * @var SourceFactory|MockObject */ private $eavSourceFactory; /** - * @var MetadataPool|\PHPUnit_Framework_MockObject_MockObject + * @var MetadataPool|MockObject */ private $metadataPool; /** - * @var BatchProviderInterface|\PHPUnit_Framework_MockObject_MockObject + * @var BatchProviderInterface|MockObject */ private $batchProvider; /** - * @var BatchSizeCalculator|\PHPUnit_Framework_MockObject_MockObject + * @var BatchSizeCalculator|MockObject */ private $batchSizeCalculator; /** - * @var ActiveTableSwitcher|\PHPUnit_Framework_MockObject_MockObject + * @var ActiveTableSwitcher|MockObject */ private $activeTableSwitcher; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ private $scopeConfig; + /** + * @var Generator + */ + private $batchQueryGenerator; + + /** + * @inheritdoc + */ protected function setUp() { $this->eavDecimalFactory = $this->createPartialMock(DecimalFactory::class, ['create']); $this->eavSourceFactory = $this->createPartialMock(SourceFactory::class, ['create']); $this->metadataPool = $this->createMock(MetadataPool::class); $this->batchProvider = $this->getMockForAbstractClass(BatchProviderInterface::class); + $this->batchQueryGenerator = $this->createMock(Generator::class); $this->batchSizeCalculator = $this->createMock(BatchSizeCalculator::class); $this->activeTableSwitcher = $this->createMock(ActiveTableSwitcher::class); - $this->scopeConfig = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) + $this->scopeConfig = $this->getMockBuilder(ScopeConfigInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); $objectManager = new ObjectManager($this); $this->model = $objectManager->getObject( - \Magento\Catalog\Model\Indexer\Product\Eav\Action\Full::class, + Full::class, [ 'eavDecimalFactory' => $this->eavDecimalFactory, 'eavSourceFactory' => $this->eavSourceFactory, @@ -80,7 +99,8 @@ protected function setUp() 'batchProvider' => $this->batchProvider, 'batchSizeCalculator' => $this->batchSizeCalculator, 'activeTableSwitcher' => $this->activeTableSwitcher, - 'scopeConfig' => $this->scopeConfig + 'scopeConfig' => $this->scopeConfig, + 'batchQueryGenerator' => $this->batchQueryGenerator, ] ); } @@ -93,15 +113,15 @@ public function testExecute() $this->scopeConfig->expects($this->once())->method('getValue')->willReturn(1); $ids = [1, 2, 3]; - $connectionMock = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) + $connectionMock = $this->getMockBuilder(AdapterInterface::class) ->getMockForAbstractClass(); $connectionMock->expects($this->atLeastOnce())->method('describeTable')->willReturn(['id' => []]); - $eavSource = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\Source::class) + $eavSource = $this->getMockBuilder(Source::class) ->disableOriginalConstructor() ->getMock(); - $eavDecimal = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\Decimal::class) + $eavDecimal = $this->getMockBuilder(Decimal::class) ->disableOriginalConstructor() ->getMock(); @@ -122,22 +142,28 @@ public function testExecute() $this->eavSourceFactory->expects($this->once())->method('create')->will($this->returnValue($eavDecimal)); - $entityMetadataMock = $this->getMockBuilder(\Magento\Framework\EntityManager\EntityMetadataInterface::class) + $entityMetadataMock = $this->getMockBuilder(EntityMetadataInterface::class) ->getMockForAbstractClass(); $this->metadataPool->expects($this->atLeastOnce()) ->method('getMetadata') - ->with(\Magento\Catalog\Api\Data\ProductInterface::class) + ->with(ProductInterface::class) ->willReturn($entityMetadataMock); - $this->batchProvider->expects($this->atLeastOnce()) - ->method('getBatches') - ->willReturn([['from' => 10, 'to' => 100]]); - $this->batchProvider->expects($this->atLeastOnce()) - ->method('getBatchIds') + // Super inefficient algorithm in some cases + $this->batchProvider->expects($this->never()) + ->method('getBatches'); + + $batchQuery = $this->createMock(Select::class); + + $connectionMock->method('fetchCol') + ->with($batchQuery) ->willReturn($ids); - $selectMock = $this->getMockBuilder(\Magento\Framework\DB\Select::class) + $this->batchQueryGenerator->method('generate') + ->willReturn([$batchQuery]); + + $selectMock = $this->getMockBuilder(Select::class) ->disableOriginalConstructor() ->getMock(); @@ -148,6 +174,9 @@ public function testExecute() $this->model->execute(); } + /** + * @return void + */ public function testExecuteWithDisabledEavIndexer() { $this->scopeConfig->expects($this->once())->method('getValue')->willReturn(0); diff --git a/app/code/Magento/CatalogInventory/Model/Indexer/Stock/Action/Full.php b/app/code/Magento/CatalogInventory/Model/Indexer/Stock/Action/Full.php index bc10d38173b4d..f5c26d4294927 100644 --- a/app/code/Magento/CatalogInventory/Model/Indexer/Stock/Action/Full.php +++ b/app/code/Magento/CatalogInventory/Model/Indexer/Stock/Action/Full.php @@ -6,12 +6,19 @@ * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\CatalogInventory\Model\Indexer\Stock\Action; +use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher; +use Magento\CatalogInventory\Model\Indexer\Stock\BatchSizeManagement; +use Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\DefaultStock; use Magento\Framework\App\ResourceConnection; use Magento\CatalogInventory\Model\ResourceModel\Indexer\StockFactory; use Magento\Catalog\Model\Product\Type as ProductType; +use Magento\Framework\DB\Query\BatchIteratorInterface; +use Magento\Framework\DB\Query\Generator as QueryGenerator; use Magento\Framework\Indexer\CacheContext; use Magento\Framework\Event\ManagerInterface as EventManager; use Magento\Framework\EntityManager\MetadataPool; @@ -25,7 +32,6 @@ /** * Class Full reindex action * - * @package Magento\CatalogInventory\Model\Indexer\Stock\Action * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Full extends AbstractAction @@ -60,6 +66,11 @@ class Full extends AbstractAction */ private $activeTableSwitcher; + /** + * @var QueryGenerator|null + */ + private $batchQueryGenerator; + /** * @param ResourceConnection $resource * @param StockFactory $indexerFactory @@ -71,7 +82,7 @@ class Full extends AbstractAction * @param BatchProviderInterface|null $batchProvider * @param array $batchRowsCount * @param ActiveTableSwitcher|null $activeTableSwitcher - * + * @param QueryGenerator|null $batchQueryGenerator * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -84,7 +95,8 @@ public function __construct( BatchSizeManagementInterface $batchSizeManagement = null, BatchProviderInterface $batchProvider = null, array $batchRowsCount = [], - ActiveTableSwitcher $activeTableSwitcher = null + ActiveTableSwitcher $activeTableSwitcher = null, + QueryGenerator $batchQueryGenerator = null ) { parent::__construct( $resource, @@ -97,11 +109,12 @@ public function __construct( $this->metadataPool = $metadataPool ?: ObjectManager::getInstance()->get(MetadataPool::class); $this->batchProvider = $batchProvider ?: ObjectManager::getInstance()->get(BatchProviderInterface::class); $this->batchSizeManagement = $batchSizeManagement ?: ObjectManager::getInstance()->get( - \Magento\CatalogInventory\Model\Indexer\Stock\BatchSizeManagement::class + BatchSizeManagement::class ); $this->batchRowsCount = $batchRowsCount; $this->activeTableSwitcher = $activeTableSwitcher ?: ObjectManager::getInstance() ->get(ActiveTableSwitcher::class); + $this->batchQueryGenerator = $batchQueryGenerator ?: ObjectManager::getInstance()->get(QueryGenerator::class); } /** @@ -109,9 +122,7 @@ public function __construct( * * @param null|array $ids * @throws LocalizedException - * * @return void - * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function execute($ids = null) @@ -120,11 +131,11 @@ public function execute($ids = null) $this->useIdxTable(false); $this->cleanIndexersTables($this->_getTypeIndexers()); - $entityMetadata = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class); + $entityMetadata = $this->metadataPool->getMetadata(ProductInterface::class); $columns = array_keys($this->_getConnection()->describeTable($this->_getIdxTable())); - /** @var \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\DefaultStock $indexer */ + /** @var DefaultStock $indexer */ foreach ($this->_getTypeIndexers() as $indexer) { $indexer->setActionType(self::ACTION_TYPE); $connection = $indexer->getConnection(); @@ -135,22 +146,21 @@ public function execute($ids = null) : $this->batchRowsCount['default']; $this->batchSizeManagement->ensureBatchSize($connection, $batchRowCount); - $batches = $this->batchProvider->getBatches( - $connection, - $entityMetadata->getEntityTable(), + + $select = $connection->select(); + $select->distinct(true); + $select->from(['e' => $entityMetadata->getEntityTable()], $entityMetadata->getIdentifierField()); + + $batchQueries = $this->batchQueryGenerator->generate( $entityMetadata->getIdentifierField(), - $batchRowCount + $select, + $batchRowCount, + BatchIteratorInterface::NON_UNIQUE_FIELD_ITERATOR ); - foreach ($batches as $batch) { + foreach ($batchQueries as $query) { $this->clearTemporaryIndexTable(); - // Get entity ids from batch - $select = $connection->select(); - $select->distinct(true); - $select->from(['e' => $entityMetadata->getEntityTable()], $entityMetadata->getIdentifierField()); - $select->where('type_id = ?', $indexer->getTypeId()); - - $entityIds = $this->batchProvider->getBatchIds($connection, $select, $batch); + $entityIds = $connection->fetchCol($query); if (!empty($entityIds)) { $indexer->reindexEntity($entityIds); $select = $connection->select()->from($this->_getIdxTable(), $columns); @@ -167,6 +177,7 @@ public function execute($ids = null) /** * Delete all records from index table + * * Used to clean table before re-indexation * * @param array $indexers From ba17fa0c2a48d31c57b758acab3fdb01349b6f02 Mon Sep 17 00:00:00 2001 From: Oleksandr Dubovyk <odubovyk@magento.com> Date: Wed, 17 Apr 2019 09:57:17 -0500 Subject: [PATCH 1141/1295] MAGETWO-98694: [Magento Cloud] Translated Arabic URL's are returning 404's - fixed - added tests --- .../Model/Import/Product.php | 3 +- .../Model/ProductUrlPathGenerator.php | 61 +++++++------ .../Model/ProductUrlPathGeneratorTest.php | 16 ++-- .../product_simple_with_non_latin_url_key.php | 63 ++++++++++++++ ...simple_with_non_latin_url_key_rollback.php | 37 ++++++++ .../Model/Import/ProductTest.php | 86 ++++++++++++++++++- ...ucts_to_import_with_non_latin_url_keys.csv | 5 ++ 7 files changed, 236 insertions(+), 35 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_non_latin_url_keys.csv diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 9cb6d4bd2390d..d37b755e8c57a 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -2838,7 +2838,8 @@ protected function getProductUrlSuffix($storeId = null) protected function getUrlKey($rowData) { if (!empty($rowData[self::URL_KEY])) { - return $this->productUrl->formatUrlKey($rowData[self::URL_KEY]); + $urlKey = (string) $rowData[self::URL_KEY]; + return trim(strtolower($urlKey)); } if (!empty($rowData[self::COL_NAME])) { diff --git a/app/code/Magento/CatalogUrlRewrite/Model/ProductUrlPathGenerator.php b/app/code/Magento/CatalogUrlRewrite/Model/ProductUrlPathGenerator.php index 64f032f2d16e9..3e858e96500c5 100644 --- a/app/code/Magento/CatalogUrlRewrite/Model/ProductUrlPathGenerator.php +++ b/app/code/Magento/CatalogUrlRewrite/Model/ProductUrlPathGenerator.php @@ -3,9 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\CatalogUrlRewrite\Model; -use Magento\Store\Model\Store; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Product; +use Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManagerInterface; class ProductUrlPathGenerator { @@ -19,36 +26,36 @@ class ProductUrlPathGenerator protected $productUrlSuffix = []; /** - * @var \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface */ protected $storeManager; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ScopeConfigInterface */ protected $scopeConfig; /** - * @var \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator + * @var CategoryUrlPathGenerator */ protected $categoryUrlPathGenerator; /** - * @var \Magento\Catalog\Api\ProductRepositoryInterface + * @var ProductRepositoryInterface */ protected $productRepository; /** - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + * @param StoreManagerInterface $storeManager + * @param ScopeConfigInterface $scopeConfig * @param CategoryUrlPathGenerator $categoryUrlPathGenerator - * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository + * @param ProductRepositoryInterface $productRepository */ public function __construct( - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator $categoryUrlPathGenerator, - \Magento\Catalog\Api\ProductRepositoryInterface $productRepository + StoreManagerInterface $storeManager, + ScopeConfigInterface $scopeConfig, + CategoryUrlPathGenerator $categoryUrlPathGenerator, + ProductRepositoryInterface $productRepository ) { $this->storeManager = $storeManager; $this->scopeConfig = $scopeConfig; @@ -59,8 +66,8 @@ public function __construct( /** * Retrieve Product Url path (with category if exists) * - * @param \Magento\Catalog\Model\Product $product - * @param \Magento\Catalog\Model\Category $category + * @param Product $product + * @param Category $category * * @return string */ @@ -80,10 +87,10 @@ public function getUrlPath($product, $category = null) /** * Prepare URL Key with stored product data (fallback for "Use Default Value" logic) * - * @param \Magento\Catalog\Model\Product $product + * @param Product $product * @return string */ - protected function prepareProductDefaultUrlKey(\Magento\Catalog\Model\Product $product) + protected function prepareProductDefaultUrlKey(Product $product) { $storedProduct = $this->productRepository->getById($product->getId()); $storedUrlKey = $storedProduct->getUrlKey(); @@ -93,9 +100,9 @@ protected function prepareProductDefaultUrlKey(\Magento\Catalog\Model\Product $p /** * Retrieve Product Url path with suffix * - * @param \Magento\Catalog\Model\Product $product + * @param Product $product * @param int $storeId - * @param \Magento\Catalog\Model\Category $category + * @param Category $category * @return string */ public function getUrlPathWithSuffix($product, $storeId, $category = null) @@ -106,8 +113,8 @@ public function getUrlPathWithSuffix($product, $storeId, $category = null) /** * Get canonical product url path * - * @param \Magento\Catalog\Model\Product $product - * @param \Magento\Catalog\Model\Category|null $category + * @param Product $product + * @param Category|null $category * @return string */ public function getCanonicalUrlPath($product, $category = null) @@ -119,7 +126,7 @@ public function getCanonicalUrlPath($product, $category = null) /** * Generate product url key based on url_key entered by merchant or product name * - * @param \Magento\Catalog\Model\Product $product + * @param Product $product * @return string|null */ public function getUrlKey($product) @@ -131,13 +138,15 @@ public function getUrlKey($product) /** * Prepare url key for product * - * @param \Magento\Catalog\Model\Product $product + * @param Product $product * @return string */ - protected function prepareProductUrlKey(\Magento\Catalog\Model\Product $product) + protected function prepareProductUrlKey(Product $product) { - $urlKey = $product->getUrlKey(); - return $product->formatUrlKey($urlKey === '' || $urlKey === null ? $product->getName() : $urlKey); + $urlKey = (string)$product->getUrlKey(); + $urlKey = trim(strtolower($urlKey)); + + return $urlKey ?: $product->formatUrlKey($product->getName()); } /** @@ -155,7 +164,7 @@ protected function getProductUrlSuffix($storeId = null) if (!isset($this->productUrlSuffix[$storeId])) { $this->productUrlSuffix[$storeId] = $this->scopeConfig->getValue( self::XML_PATH_PRODUCT_URL_SUFFIX, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + ScopeInterface::SCOPE_STORE, $storeId ); } diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductUrlPathGeneratorTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductUrlPathGeneratorTest.php index 956fe1b88e0ad..14f30eb7607d3 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductUrlPathGeneratorTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductUrlPathGeneratorTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\CatalogUrlRewrite\Test\Unit\Model; use Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator; @@ -72,10 +73,11 @@ protected function setUp() public function getUrlPathDataProvider() { return [ - 'path based on url key' => ['url-key', null, 'url-key'], - 'path based on product name 1' => ['', 'product-name', 'product-name'], - 'path based on product name 2' => [null, 'product-name', 'product-name'], - 'path based on product name 3' => [false, 'product-name', 'product-name'] + 'path based on url key uppercase' => ['Url Key', null, 0, 'url key'], + 'path based on url key' => ['url-key', null, 0, 'url-key'], + 'path based on product name 1' => ['', 'product-name', 1, 'product-name'], + 'path based on product name 2' => [null, 'product-name', 1, 'product-name'], + 'path based on product name 3' => [false, 'product-name', 1, 'product-name'] ]; } @@ -83,15 +85,17 @@ public function getUrlPathDataProvider() * @dataProvider getUrlPathDataProvider * @param string|null|bool $urlKey * @param string|null|bool $productName + * @param int $formatterCalled * @param string $result */ - public function testGetUrlPath($urlKey, $productName, $result) + public function testGetUrlPath($urlKey, $productName, $formatterCalled, $result) { $this->product->expects($this->once())->method('getData')->with('url_path') ->will($this->returnValue(null)); $this->product->expects($this->any())->method('getUrlKey')->will($this->returnValue($urlKey)); $this->product->expects($this->any())->method('getName')->will($this->returnValue($productName)); - $this->product->expects($this->once())->method('formatUrlKey')->will($this->returnArgument(0)); + $this->product->expects($this->exactly($formatterCalled)) + ->method('formatUrlKey')->will($this->returnArgument(0)); $this->assertEquals($result, $this->productUrlPathGenerator->getUrlPath($this->product, null)); } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key.php new file mode 100644 index 0000000000000..23fd8d7fe324e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key.php @@ -0,0 +1,63 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$stockDataConfig = [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1 +]; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); + +/** @var ProductInterface $product */ +$product = $objectManager->create(ProductInterface::class); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Чудовий продукт без Url Key') + ->setSku('ukrainian-without-url-key') + ->setPrice(10) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([2]) + ->setStockData($stockDataConfig); +try { + $productRepository->save($product); +} catch (\Exception $e) { + // problems during save +}; + +/** @var ProductInterface $product */ +$product = $objectManager->create(ProductInterface::class); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Надзвичайний продукт з Url Key') + ->setSku('ukrainian-with-url-key') + ->setPrice(10) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([2]) + ->setStockData($stockDataConfig) + ->setUrlKey('надзвичайний продукт на кожен день'); +try { + $productRepository->save($product); +} catch (\Exception $e) { + // problems during save +}; diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key_rollback.php new file mode 100644 index 0000000000000..d4592430c0e94 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key_rollback.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +Bootstrap::getInstance()->getInstance()->reinitialize(); + +/** @var Registry $registry */ +$registry = Bootstrap::getObjectManager()->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = Bootstrap::getObjectManager() + ->get(ProductRepositoryInterface::class); + +$productSkus = [ + 'ukrainian-with-url-key', + 'ukrainian-without-url-key', +]; +try { + foreach ($productSkus as $sku) { + $product = $productRepository->get($sku, false, null, true); + $productRepository->delete($product); + } +} catch (NoSuchEntityException $e) { + // nothing to delete +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index 6e361dfb05de0..cb96910ec86e1 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -28,11 +28,13 @@ use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface; use Magento\Store\Model\Store; use Psr\Log\LoggerInterface; +use Magento\Framework\Exception\NoSuchEntityException; /** * Class ProductTest * @magentoAppIsolation enabled * @magentoDbIsolation enabled + * @magentoAppArea adminhtml * @magentoDataFixtureBeforeTransaction Magento/Catalog/_files/enable_reindex_schedule.php * @magentoDataFixtureBeforeTransaction Magento/Catalog/_files/enable_catalog_product_reindex_schedule.php * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -71,6 +73,11 @@ class ProductTest extends \Magento\TestFramework\Indexer\TestCase */ private $logger; + /** + * @var array + */ + private $importedProducts; + /** * @inheritdoc */ @@ -84,10 +91,27 @@ protected function setUp() \Magento\CatalogImportExport\Model\Import\Product::class, ['logger' => $this->logger] ); + $this->importedProducts = []; parent::setUp(); } + protected function tearDown() + { + /* We rollback here the products created during the Import because they were + created during test execution and we do not have the rollback for them */ + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + foreach ($this->importedProducts as $productSku) { + try { + $product = $productRepository->get($productSku, false, null, true); + $productRepository->delete($product); + } catch (NoSuchEntityException $e) { + // nothing to delete + } + } + } + /** * Options for assertion * @@ -271,6 +295,8 @@ public function testStockState() * @param string $importFile * @param string $sku * @param int $expectedOptionsQty + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException * @magentoAppIsolation enabled */ public function testSaveCustomOptions($importFile, $sku, $expectedOptionsQty) @@ -1244,6 +1270,8 @@ public function testProductPositionInCategory() * @magentoAppIsolation enabled * @magentoDataFixture Magento/Catalog/_files/category_product.php * @return void + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testNewProductPositionInCategory() { @@ -1369,6 +1397,7 @@ protected function loadCategoryByName($categoryName) * @dataProvider validateUrlKeysDataProvider * @param $importFile string * @param $expectedErrors array + * @throws \Magento\Framework\Exception\LocalizedException */ public function testValidateUrlKeys($importFile, $expectedErrors) { @@ -1597,12 +1626,13 @@ public function testImportWithoutUrlKeys() * @magentoDbIsolation disabled * @magentoAppIsolation enabled * @return void + * @throws \Magento\Framework\Exception\LocalizedException */ public function testImportWithUrlKeysWithSpaces() { $products = [ - 'simple1' => 'url-key-with-spaces1', - 'simple2' => 'url-key-with-spaces2', + 'simple1' => 'url key with spaces1', + 'simple2' => 'url key with spaces2', ]; $filesystem = $this->objectManager->create(\Magento\Framework\Filesystem::class); $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); @@ -1629,6 +1659,52 @@ public function testImportWithUrlKeysWithSpaces() } } + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_non_latin_url_key.php + * @magentoDbIsolation disabled + * @magentoAppIsolation enabled + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function testImportWithNonLatinUrlKeys() + { + $productsCreatedByFixture = [ + 'ukrainian-with-url-key' => 'nove-im-ja-pislja-importu-scho-stane-url-key', + 'ukrainian-without-url-key' => 'новий url key після імпорту', + ]; + $productsImportedByCsv = [ + 'imported-ukrainian-with-url-key' => 'імпортований продукт', + 'imported-ukrainian-without-url-key' => 'importovanij-produkt-bez-url-key', + ]; + $productSkuMap = array_merge($productsCreatedByFixture, $productsImportedByCsv); + $this->importedProducts = array_keys($productsImportedByCsv); + + $filesystem = $this->objectManager->create(\Magento\Framework\Filesystem::class); + $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); + $source = $this->objectManager->create( + \Magento\ImportExport\Model\Import\Source\Csv::class, + [ + 'file' => __DIR__ . '/_files/products_to_import_with_non_latin_url_keys.csv', + 'directory' => $directory, + ] + ); + + $errors = $this->_model->setParameters( + ['behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_ADD_UPDATE, 'entity' => 'catalog_product'] + ) + ->setSource($source) + ->validateData(); + + $this->assertEquals($errors->getErrorsCount(), 0); + $this->_model->importData(); + + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + foreach ($productSkuMap as $productSku => $productUrlKey) { + $this->assertEquals($productUrlKey, $productRepository->get($productSku)->getUrlKey()); + } + } + /** * Make sure the absence of a url_key column in the csv file won't erase the url key of the existing products. * To reach the goal we need to not send the name column, as the url key is generated from it. @@ -1844,6 +1920,7 @@ public function testProductWithWrappedAdditionalAttributes() * * @param string $fileName * @param int $expectedErrors + * @throws \Magento\Framework\Exception\LocalizedException */ private function importDataForMediaTest(string $fileName, int $expectedErrors = 0) { @@ -2266,6 +2343,7 @@ public function testImportWithBackordersDisabled() * Import file by providing import filename in parameters * * @param string $fileName + * @throws \Magento\Framework\Exception\LocalizedException */ private function importFile(string $fileName) { @@ -2297,6 +2375,7 @@ private function importFile(string $fileName) * Import file with non-existing images and skip-errors strategy. * * @return void + * @throws \Magento\Framework\Exception\LocalizedException */ public function testImportWithSkipErrorsAndNonExistingImage() { @@ -2382,6 +2461,7 @@ public function testImportProductWithUpdateUrlKey() * @magentoDataFixture mediaImportImageFixture * @magentoAppIsolation enabled * @return void + * @throws \Magento\Framework\Exception\LocalizedException */ public function testSaveProductOnImportNonExistingImage() { @@ -2414,6 +2494,8 @@ public function testSaveProductOnImportNonExistingImage() * @magentoDbIsolation disabled * @magentoAppIsolation enabled * @return void + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testImportProductWithContinueOnError() { diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_non_latin_url_keys.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_non_latin_url_keys.csv new file mode 100644 index 0000000000000..8b324a5330779 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_non_latin_url_keys.csv @@ -0,0 +1,5 @@ +sku,product_type,store_view_code,name,price,attribute_set_code,url_key +imported-ukrainian-with-url-key,simple,,"Імпортований продукт з Url Key",50,Default,"імпортований продукт" +imported-ukrainian-without-url-key,simple,,"Імпортований продукт без Url Key",55,Default, +ukrainian-without-url-key,simple,,"Чудовий продукт без Url Key",55,Default,"новий url key після імпорту" +ukrainian-with-url-key,simple,,"Нове ім'я після імпорту що стане url key",55,Default, \ No newline at end of file From f9fe45dc36238f07b79324f860391a99b27f953e Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Wed, 17 Apr 2019 21:57:19 +0300 Subject: [PATCH 1142/1295] MAGETWO-98700: Shared catalog configurable product with out of stock options and instock options loads with empty drop down for non logged in customers --- .../Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index 89d9cf8fe2822..ba50a0974ec87 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -251,11 +251,11 @@ <actionGroup name="AdminAssignProductToCategory" extends="AdminProductAssignCategory"> <arguments> <argument name="productId" type="string"/> - <argument name="categoryName" type="string"/> </arguments> <amOnPage url="{{AdminProductEditPage.url(productId)}}" before="searchAndSelectCategory" stepKey="amOnPage"/> <click selector="{{AdminProductFormActionSection.saveButton}}" after="searchAndSelectCategory" stepKey="clickOnSaveButton"/> - <see selector="{{AdminMessagesSection.success}}" userInput="You saved the product." after="clickOnSaveButton" stepKey="seeSaveProductMessage"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" after="clickOnSaveButton" stepKey="waitForSaveProductMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the product." after="waitForSaveProductMessage" stepKey="seeSaveProductMessage"/> </actionGroup> <actionGroup name="AdminChangeProductAttributeSet"> From ba29d6f4322beebe6eb54f1cabf8ed30479ed1d5 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 18 Apr 2019 12:19:43 +0300 Subject: [PATCH 1143/1295] MC-15443: Out of memory exception thrown when indexer:reindex is run --- .../Model/Indexer/Category/Product/Action/Full.php | 6 ++---- .../Catalog/Model/Indexer/Product/Price/Action/Full.php | 8 +------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php index c92ef80dabbbc..b12ffe1ac1f87 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php @@ -145,11 +145,9 @@ private function switchTables() } /** - * Refresh entities index - * - * @return $this + * @inheritdoc */ - public function execute(): self + public function execute() { $this->createTables(); $this->clearReplicaTables(); diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php index e0091ffdf7c52..79eeb3cc3225d 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php @@ -267,7 +267,6 @@ private function reindexByBatches(DimensionalIndexerInterface $priceIndexer, arr * @param string $typeId * * @return BatchIterator - * @throws \Exception */ private function getBatchesForIndexer(string $typeId): BatchIterator { @@ -278,7 +277,7 @@ private function getBatchesForIndexer(string $typeId): BatchIterator $select->from(['e' => $entityMetadata->getEntityTable()], $entityMetadata->getIdentifierField()); return $this->batchQueryGenerator->generate( - $this->getProductMetaData()->getIdentifierField(), + $entityMetadata->getIdentifierField(), $select, $this->batchSizeCalculator->estimateBatchSize( $connection, @@ -372,7 +371,6 @@ private function reindexBatch(PriceInterface $priceIndexer, Select $batch) * * @param Select $batch * @return array - * @throws \Exception */ private function getEntityIdsFromBatch(Select $batch): array { @@ -385,7 +383,6 @@ private function getEntityIdsFromBatch(Select $batch): array * Get product meta data * * @return EntityMetadataInterface - * @throws \Exception */ private function getProductMetaData(): EntityMetadataInterface { @@ -400,7 +397,6 @@ private function getProductMetaData(): EntityMetadataInterface * Get replica table * * @return string - * @throws \Exception */ private function getReplicaTable(): string { @@ -413,7 +409,6 @@ private function getReplicaTable(): string * Replacement of tables from replica to main * * @return void - * @throws \Zend_Db_Statement_Exception */ private function switchTables() { @@ -442,7 +437,6 @@ private function switchTables() * * @param array $dimensions * @return void - * @throws \Zend_Db_Statement_Exception */ private function moveDataFromReplicaTableToReplicaTables(array $dimensions) { From 27bb13c4c5c612d26ffc8622732ffc05615f839a Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Thu, 18 Apr 2019 13:21:47 +0300 Subject: [PATCH 1144/1295] MAGETWO-98899: Special Prices cannot save over 4 characters in Japanese Yen - The comma separator seems to be the issue --- .../Magento/Framework/Locale/Format.php | 43 ++++++------------- .../Framework/Locale/Test/Unit/FormatTest.php | 13 +++--- 2 files changed, 20 insertions(+), 36 deletions(-) diff --git a/lib/internal/Magento/Framework/Locale/Format.php b/lib/internal/Magento/Framework/Locale/Format.php index a66f14260c9cb..c5e9ad26a25e1 100644 --- a/lib/internal/Magento/Framework/Locale/Format.php +++ b/lib/internal/Magento/Framework/Locale/Format.php @@ -10,6 +10,11 @@ */ class Format implements \Magento\Framework\Locale\FormatInterface { + /** + * Japan locale code + */ + private static $japanLocaleCode = 'ja_JP'; + /** * @var \Magento\Framework\App\ScopeResolverInterface */ @@ -25,11 +30,6 @@ class Format implements \Magento\Framework\Locale\FormatInterface */ protected $currencyFactory; - /** - * @var array - */ - private $groupSeparatorByLocale = []; - /** * @param \Magento\Framework\App\ScopeResolverInterface $scopeResolver * @param ResolverInterface $localeResolver @@ -87,12 +87,15 @@ public function getNumber($value) } } elseif ($separatorComa !== false) { $locale = $this->_localeResolver->getLocale(); - $groupSeparator = $this->retrieveLocaleGroupSeparator($locale); - if ($groupSeparator === ',') { - $value = str_replace(',', '', $value); - } else { - $value = str_replace(',', '.', $value); - } + /** + * It's hard code for Japan locale. + * The comma separator uses as group separator: 4,000 saves as 4,000.00 + */ + $value = str_replace( + ',', + $locale === self::$japanLocaleCode ? '' : '.', + $value + ); } return (float)$value; @@ -160,22 +163,4 @@ public function getPriceFormat($localeCode = null, $currencyCode = null) return $result; } - - /** - * Retrieve group separator symbol by locale - * - * @param string $locale - * @return string - */ - private function retrieveLocaleGroupSeparator(string $locale): string - { - if (!array_key_exists($locale, $this->groupSeparatorByLocale)) { - $formatter = new \NumberFormatter($locale, \NumberFormatter::DECIMAL); - $this->groupSeparatorByLocale[$locale] = $formatter->getSymbol( - \NumberFormatter::GROUPING_SEPARATOR_SYMBOL - ); - } - - return $this->groupSeparatorByLocale[$locale]; - } } diff --git a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php index 09b6d982c252d..8c8e118aa3169 100644 --- a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php +++ b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php @@ -111,6 +111,7 @@ public function testGetNumber(string $value, float $expected, string $locale = n } /** + * * @return array */ public function provideNumbers(): array @@ -121,15 +122,13 @@ public function provideNumbers(): array ['12343', 12343], ['-9456km', -9456], ['0', 0], - ['2 054,10', 205410, 'en_US'], - ['2 054,10', 2054.1, 'de_DE'], - ['2046,45', 204645, 'en_US'], - ['2046,45', 2046.45, 'de_DE'], + ['2 054,10', 2054.1], + ['2046,45', 2046.45], ['2 054.52', 2054.52], - ['2,46 GB', 246, 'en_US'], - ['2,46 GB', 2.46, 'de_DE'], + ['2,46 GB', 2.46], ['2,054.00', 2054], - ['2,000', 2000, 'ja_JP'], + ['4,000', 4000.0, 'ja_JP'], + ['4,000', 4.0, 'en_US'], ]; } } From 2910efd0af93fc5d0345349132b9ba76550e855b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Culotta?= <damianculotta@gmail.com> Date: Thu, 18 Apr 2019 10:56:30 -0300 Subject: [PATCH 1145/1295] Checkout Totals Sort Order fields can't be empty and should be a number. Same fix than PR 22411 on 2.3. --- app/code/Magento/Sales/etc/adminhtml/system.xml | 5 +++++ app/code/Magento/Weee/etc/adminhtml/system.xml | 1 + 2 files changed, 6 insertions(+) diff --git a/app/code/Magento/Sales/etc/adminhtml/system.xml b/app/code/Magento/Sales/etc/adminhtml/system.xml index 2dc467d6ca247..e437918b683b2 100644 --- a/app/code/Magento/Sales/etc/adminhtml/system.xml +++ b/app/code/Magento/Sales/etc/adminhtml/system.xml @@ -27,18 +27,23 @@ <label>Checkout Totals Sort Order</label> <field id="discount" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Discount</label> + <validate>required-number validate-number</validate> </field> <field id="grand_total" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Grand Total</label> + <validate>required-number validate-number</validate> </field> <field id="shipping" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Shipping</label> + <validate>required-number validate-number</validate> </field> <field id="subtotal" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Subtotal</label> + <validate>required-number validate-number</validate> </field> <field id="tax" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Tax</label> + <validate>required-number validate-number</validate> </field> </group> <group id="reorder" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> diff --git a/app/code/Magento/Weee/etc/adminhtml/system.xml b/app/code/Magento/Weee/etc/adminhtml/system.xml index ae02b27d10c72..d3e9efb8f0b46 100644 --- a/app/code/Magento/Weee/etc/adminhtml/system.xml +++ b/app/code/Magento/Weee/etc/adminhtml/system.xml @@ -44,6 +44,7 @@ <group id="totals_sort"> <field id="weee" translate="label" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Fixed Product Tax</label> + <validate>required-number validate-number</validate> </field> </group> </section> From 79e90baca6442c0fb3627dd1fa1b083c8df4fa01 Mon Sep 17 00:00:00 2001 From: Bettina Cerban <bettinacerban@gmail.com> Date: Thu, 18 Apr 2019 11:46:11 -0300 Subject: [PATCH 1146/1295] filter config values on testSuiteStart --- .../Magento/TestFramework/Isolation/DeploymentConfig.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/framework/Magento/TestFramework/Isolation/DeploymentConfig.php b/dev/tests/integration/framework/Magento/TestFramework/Isolation/DeploymentConfig.php index 3132aed4d21e3..ae59bc004db0e 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Isolation/DeploymentConfig.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Isolation/DeploymentConfig.php @@ -51,7 +51,7 @@ public function startTestSuite() { if (null === $this->reader) { $this->reader = Bootstrap::getObjectManager()->get(\Magento\Framework\App\DeploymentConfig\Reader::class); - $this->config = $this->reader->load(); + $this->config = $this->filterIgnoredConfigValues($this->reader->load()); } } From 0e810133f1bc5f49aa7261a149599664e36b5782 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Thu, 18 Apr 2019 10:50:47 -0500 Subject: [PATCH 1147/1295] MAGETWO-93521: Custom customer attributes don't show in admin unless they're configured to show on frontend --- .../Customer/Model/Customer/DataProvider.php | 36 +++---------------- .../Unit/Model/Customer/DataProviderTest.php | 16 ++++----- 2 files changed, 11 insertions(+), 41 deletions(-) diff --git a/app/code/Magento/Customer/Model/Customer/DataProvider.php b/app/code/Magento/Customer/Model/Customer/DataProvider.php index ce976d3f62c74..9c9f04185477e 100644 --- a/app/code/Magento/Customer/Model/Customer/DataProvider.php +++ b/app/code/Magento/Customer/Model/Customer/DataProvider.php @@ -375,45 +375,17 @@ protected function getAttributesMeta(Type $entityType) return $meta; } - /** - * Check whether the specific attribute can be shown in form: customer registration, customer edit, etc... - * - * @param Attribute $customerAttribute - * @return bool - */ - private function canShowAttributeInForm(AbstractAttribute $customerAttribute) - { - $isRegistration = $this->context->getRequestParam($this->getRequestFieldName()) === null; - - if ($customerAttribute->getEntityType()->getEntityTypeCode() === 'customer') { - return is_array($customerAttribute->getUsedInForms()) && - ( - (in_array('customer_account_create', $customerAttribute->getUsedInForms()) && $isRegistration) || - (in_array('customer_account_edit', $customerAttribute->getUsedInForms()) && !$isRegistration) - ); - } else { - return is_array($customerAttribute->getUsedInForms()) && - in_array('customer_address_edit', $customerAttribute->getUsedInForms()); - } - } - /** * Detect can we show attribute on specific form or not * * @param Attribute $customerAttribute * @return bool */ - private function canShowAttribute(AbstractAttribute $customerAttribute) + private function canShowAttribute(AbstractAttribute $customerAttribute): bool { - $userDefined = (bool) $customerAttribute->getIsUserDefined(); - if (!$userDefined) { - return $customerAttribute->getIsVisible(); - } - - $canShowOnForm = $this->canShowAttributeInForm($customerAttribute); - - return ($this->allowToShowHiddenAttributes && $canShowOnForm) || - (!$this->allowToShowHiddenAttributes && $canShowOnForm && $customerAttribute->getIsVisible()); + return $this->allowToShowHiddenAttributes && (bool) $customerAttribute->getIsUserDefined() + ? true + : (bool) $customerAttribute->getIsVisible(); } /** diff --git a/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php index 50c21379054bf..f6a3ecb810aa5 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php @@ -1295,16 +1295,15 @@ public function testGetDataWithVisibleAttributesWithAccountEdit() $meta = $dataProvider->getMeta(); $this->assertNotEmpty($meta); - $this->assertEquals($this->getExpectationForVisibleAttributes(false), $meta); + $this->assertEquals($this->getExpectationForVisibleAttributes(), $meta); } /** * Retrieve all customer variations of attributes with all variations of visibility * - * @param bool $isRegistration * @return array */ - private function getCustomerAttributeExpectations($isRegistration) + private function getCustomerAttributeExpectations() { return [ self::ATTRIBUTE_CODE . "_1" => [ @@ -1314,7 +1313,7 @@ private function getCustomerAttributeExpectations($isRegistration) 'dataType' => 'frontend_input', 'formElement' => 'frontend_input', 'options' => 'test-options', - 'visible' => !$isRegistration, + 'visible' => true, 'required' => 'is_required', 'label' => __('frontend_label'), 'sortOrder' => 'sort_order', @@ -1351,7 +1350,7 @@ private function getCustomerAttributeExpectations($isRegistration) 'config' => [ 'dataType' => 'frontend_input', 'formElement' => 'frontend_input', - 'visible' => $isRegistration, + 'visible' => true, 'required' => 'is_required', 'label' => __('frontend_label'), 'sortOrder' => 'sort_order', @@ -1374,7 +1373,7 @@ private function getCustomerAttributeExpectations($isRegistration) 'config' => [ 'dataType' => 'frontend_input', 'formElement' => 'frontend_input', - 'visible' => $isRegistration, + 'visible' => true, 'required' => 'is_required', 'label' => __('frontend_label'), 'sortOrder' => 'sort_order', @@ -1397,14 +1396,13 @@ private function getCustomerAttributeExpectations($isRegistration) /** * Retrieve all variations of attributes with all variations of visibility * - * @param bool $isRegistration * @return array */ - private function getExpectationForVisibleAttributes($isRegistration = true) + private function getExpectationForVisibleAttributes() { return [ 'customer' => [ - 'children' => $this->getCustomerAttributeExpectations($isRegistration), + 'children' => $this->getCustomerAttributeExpectations(), ], 'address' => [ 'children' => [ From 72980c1b6230ca311569fa518e751873a30557c4 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Thu, 18 Apr 2019 09:40:09 -0500 Subject: [PATCH 1148/1295] MAGETWO-99251: Duplicate orders with same quote ID when form is submitted using Enter key - Eliminated sending payment information multiple times --- .../js/view/payment/method-renderer/cc-form.js | 1 - .../payment/method-renderer/hosted-fields.js | 1 - .../frontend/web/js/view/payment/default.js | 15 +++++++++------ .../frontend/web/js/view/payment/iframe.js | 18 ++++++++++++++---- .../method-renderer/payflowpro-method.js | 5 ++++- .../payment/method-renderer/cc-form.test.js | 16 ---------------- 6 files changed, 27 insertions(+), 29 deletions(-) diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js index d078cacb96c2d..73a7b10d5d30f 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js @@ -79,7 +79,6 @@ define( */ onError: function (response) { braintree.showError($t('Payment ' + this.getTitle() + ' can\'t be initialized')); - this.isPlaceOrderActionAllowed(true); throw response.message; }, diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/hosted-fields.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/hosted-fields.js index bfdc2974dd4b0..cb9110ee8afa1 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/hosted-fields.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/hosted-fields.js @@ -156,7 +156,6 @@ define([ */ placeOrderClick: function () { if (this.validateCardType()) { - this.isPlaceOrderActionAllowed(false); $(this.getSelector('submit')).trigger('click'); } }, diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/payment/default.js b/app/code/Magento/Checkout/view/frontend/web/js/view/payment/default.js index 7b200860c4d55..1b5463c0770a3 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/payment/default.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/payment/default.js @@ -133,15 +133,14 @@ define([ event.preventDefault(); } - if (this.validate() && additionalValidators.validate()) { + if (this.validate() && + additionalValidators.validate() && + this.isPlaceOrderActionAllowed() === true + ) { this.isPlaceOrderActionAllowed(false); this.getPlaceOrderDeferredObject() - .fail( - function () { - self.isPlaceOrderActionAllowed(true); - } - ).done( + .done( function () { self.afterPlaceOrder(); @@ -149,6 +148,10 @@ define([ redirectOnSuccessAction.execute(); } } + ).always( + function () { + self.isPlaceOrderActionAllowed(true); + } ); return true; diff --git a/app/code/Magento/Payment/view/frontend/web/js/view/payment/iframe.js b/app/code/Magento/Payment/view/frontend/web/js/view/payment/iframe.js index 5eba4fd89d338..1e352e4297131 100644 --- a/app/code/Magento/Payment/view/frontend/web/js/view/payment/iframe.js +++ b/app/code/Magento/Payment/view/frontend/web/js/view/payment/iframe.js @@ -114,8 +114,12 @@ define([ * @override */ placeOrder: function () { - if (this.validateHandler() && additionalValidators.validate()) { + var self = this; + if (this.validateHandler() && + additionalValidators.validate() && + this.isPlaceOrderActionAllowed() === true + ) { fullScreenLoader.startLoader(); this.isPlaceOrderActionAllowed(false); @@ -127,8 +131,15 @@ define([ method: this.getCode() } ) - ).done(this.done.bind(this)) - .fail(this.fail.bind(this)); + ).done( + this.done.bind(this) + ).fail( + this.fail.bind(this) + ).always( + function () { + self.isPlaceOrderActionAllowed(true); + } + ); this.initTimeoutHandler(); } @@ -192,7 +203,6 @@ define([ */ fail: function () { fullScreenLoader.stopLoader(); - this.isPlaceOrderActionAllowed(true); return this; }, diff --git a/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/payflowpro-method.js b/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/payflowpro-method.js index 786f1a5aa85fd..24d06b7d0f8f2 100644 --- a/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/payflowpro-method.js +++ b/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/payflowpro-method.js @@ -79,7 +79,10 @@ define([ placeOrder: function () { var self = this; - if (this.validateHandler() && additionalValidators.validate()) { + if (this.validateHandler() && + additionalValidators.validate() && + this.isPlaceOrderActionAllowed() === true + ) { this.isPlaceOrderActionAllowed(false); fullScreenLoader.startLoader(); $.when( diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js index 6062439db2365..817553c30d0f9 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js @@ -85,21 +85,5 @@ define([ expect(braintreeCcForm.getCode()).toEqual(expectedCode); expect(braintreeCcForm.messageContainer).toEqual(expectedMessageContainer); }); - - it('Check if form validation fails when "Place Order" button should be active.', function () { - var errorMessage = 'Something went wrong.', - - /** - * Anonymous wrapper - */ - func = function () { - braintreeCcForm.clientConfig.onError({ - 'message': errorMessage - }); - }; - - expect(func).toThrow(errorMessage); - expect(braintreeCcForm.isPlaceOrderActionAllowed()).toBeTruthy(); - }); }); }); From 35ad60264bda2df015454b2a8deda8215e815b85 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Thu, 18 Apr 2019 16:00:55 -0500 Subject: [PATCH 1149/1295] MC-15874: Catch unserializer exception --- .../Model/Config/Backend/Serialized.php | 13 +++++++++++- .../Model/Config/Backend/SerializedTest.php | 21 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Config/Model/Config/Backend/Serialized.php b/app/code/Magento/Config/Model/Config/Backend/Serialized.php index 3d5713357c39c..4d5da764db470 100644 --- a/app/code/Magento/Config/Model/Config/Backend/Serialized.php +++ b/app/code/Magento/Config/Model/Config/Backend/Serialized.php @@ -52,7 +52,18 @@ protected function _afterLoad() { $value = $this->getValue(); if (!is_array($value)) { - $this->setValue(empty($value) ? false : $this->serializer->unserialize($value)); + try { + $this->setValue(empty($value) ? false : $this->serializer->unserialize($value)); + } catch (\Exception $e) { + $this->_logger->critical( + sprintf( + 'Failed to unserialize %s config value. The error is: %s', + $this->getPath(), + $e->getMessage() + ) + ); + $this->setValue(false); + } } } diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Backend/SerializedTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Backend/SerializedTest.php index bb1e0e0225901..048df95f98649 100644 --- a/app/code/Magento/Config/Test/Unit/Model/Config/Backend/SerializedTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/Config/Backend/SerializedTest.php @@ -9,6 +9,7 @@ use Magento\Framework\Model\Context; use Magento\Framework\Serialize\Serializer\Json; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Psr\Log\LoggerInterface; class SerializedTest extends \PHPUnit\Framework\TestCase { @@ -18,14 +19,20 @@ class SerializedTest extends \PHPUnit\Framework\TestCase /** @var Json|\PHPUnit_Framework_MockObject_MockObject */ private $serializerMock; + /** @var LoggerInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $loggerMock; + protected function setUp() { $objectManager = new ObjectManager($this); $this->serializerMock = $this->createMock(Json::class); + $this->loggerMock = $this->createMock(LoggerInterface::class); $contextMock = $this->createMock(Context::class); $eventManagerMock = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); $contextMock->method('getEventDispatcher') ->willReturn($eventManagerMock); + $contextMock->method('getLogger') + ->willReturn($this->loggerMock); $this->serializedConfig = $objectManager->getObject( Serialized::class, [ @@ -72,6 +79,20 @@ public function afterLoadDataProvider() ]; } + public function testAfterLoadWithException() + { + $value = '{"key":'; + $expected = false; + $this->serializedConfig->setValue($value); + $this->serializerMock->expects($this->once()) + ->method('unserialize') + ->willThrowException(new \Exception()); + $this->loggerMock->expects($this->once()) + ->method('critical'); + $this->serializedConfig->afterLoad(); + $this->assertEquals($expected, $this->serializedConfig->getValue()); + } + /** * @param string $expected * @param int|double|string|array|boolean|null $value From e1f91d1a87dabac77264f40992007124b8febd4e Mon Sep 17 00:00:00 2001 From: Lorenzo Stramaccia <lorenzo.stramaccia@magespecialist.it> Date: Thu, 21 Mar 2019 10:48:53 +0100 Subject: [PATCH 1150/1295] Fix importFromArray by setting _isCollectionLoaded to true after import --- .../Magento/Eav/Model/Entity/Collection/AbstractCollection.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php b/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php index 7ac77a66482b8..242a44d9dabfd 100644 --- a/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php +++ b/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php @@ -1035,6 +1035,7 @@ public function importFromArray($arr) $this->_items[$entityId]->addData($row); } } + $this->_setIsLoaded(); return $this; } From 23b736f3eee1d663b527b73a8e85fabc824ead5e Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Fri, 19 Apr 2019 15:32:07 +0300 Subject: [PATCH 1151/1295] MAGETWO-98583: Google chart API used by Magento dashboard scheduled to be turned off --- .../Magento/Backend/Block/Dashboard/Graph.php | 158 +++++++----------- .../Backend/Block/Dashboard/GraphTest.php | 4 +- .../Controller/Adminhtml/DashboardTest.php | 12 +- 3 files changed, 78 insertions(+), 96 deletions(-) diff --git a/app/code/Magento/Backend/Block/Dashboard/Graph.php b/app/code/Magento/Backend/Block/Dashboard/Graph.php index 8e238ccab44cb..c94bbd25e1fed 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Graph.php +++ b/app/code/Magento/Backend/Block/Dashboard/Graph.php @@ -15,7 +15,7 @@ class Graph extends \Magento\Backend\Block\Dashboard\AbstractDashboard /** * Api URL */ - const API_URL = 'http://chart.apis.google.com/chart'; + const API_URL = 'https://image-charts.com/chart'; /** * All series @@ -76,6 +76,7 @@ class Graph extends \Magento\Backend\Block\Dashboard\AbstractDashboard /** * Google chart api data encoding * + * @deprecated since the Google Image Charts API not accessible from March 14, 2019 * @var string */ protected $_encoding = 'e'; @@ -187,11 +188,12 @@ public function getChartUrl($directUrl = true) { $params = [ 'cht' => 'lc', - 'chf' => 'bg,s,ffffff', - 'chco' => 'ef672f', 'chls' => '7', - 'chxs' => '0,676056,15,0,l,676056|1,676056,15,0,l,676056', - 'chm' => 'h,f2ebde,0,0:1:.1,1,-1', + 'chf' => 'bg,s,f4f4f4|c,lg,90,ffffff,0.1,ededed,0', + 'chm' => 'B,f4d4b2,0,0,0', + 'chco' => 'db4814', + 'chxs' => '0,0,11|1,0,11', + 'chma' => '15,15,15,15', ]; $this->_allSeries = $this->getRowsData($this->_dataRows); @@ -279,20 +281,11 @@ public function getChartUrl($directUrl = true) $this->_axisLabels['x'] = $dates; $this->_allSeries = $datas; - //Google encoding values - if ($this->_encoding == "s") { - // simple encoding - $params['chd'] = "s:"; - $dataDelimiter = ""; - $dataSetdelimiter = ","; - $dataMissing = "_"; - } else { - // extended encoding - $params['chd'] = "e:"; - $dataDelimiter = ""; - $dataSetdelimiter = ","; - $dataMissing = "__"; - } + // Image-Charts Awesome data format values + $params['chd'] = "a:"; + $dataDelimiter = ","; + $dataSetdelimiter = "|"; + $dataMissing = "_"; // process each string in the array, and find the max length $localmaxvalue = [0]; @@ -306,7 +299,6 @@ public function getChartUrl($directUrl = true) $minvalue = min($localminvalue); // default values - $yrange = 0; $yLabels = []; $miny = 0; $maxy = 0; @@ -321,7 +313,6 @@ public function getChartUrl($directUrl = true) $maxy = ceil($maxvalue + 1); $yLabels = range($miny, $maxy, 1); } - $yrange = $maxy; $yorigin = 0; } @@ -329,44 +320,14 @@ public function getChartUrl($directUrl = true) foreach ($this->getAllSeries() as $index => $serie) { $thisdataarray = $serie; - if ($this->_encoding == "s") { - // SIMPLE ENCODING - for ($j = 0; $j < sizeof($thisdataarray); $j++) { - $currentvalue = $thisdataarray[$j]; - if (is_numeric($currentvalue)) { - $ylocation = round( - (strlen($this->_simpleEncoding) - 1) * ($yorigin + $currentvalue) / $yrange - ); - $chartdata[] = substr($this->_simpleEncoding, $ylocation, 1) . $dataDelimiter; - } else { - $chartdata[] = $dataMissing . $dataDelimiter; - } - } - } else { - // EXTENDED ENCODING - for ($j = 0; $j < sizeof($thisdataarray); $j++) { - $currentvalue = $thisdataarray[$j]; - if (is_numeric($currentvalue)) { - if ($yrange) { - $ylocation = 4095 * ($yorigin + $currentvalue) / $yrange; - } else { - $ylocation = 0; - } - $firstchar = floor($ylocation / 64); - $secondchar = $ylocation % 64; - $mappedchar = substr( - $this->_extendedEncoding, - $firstchar, - 1 - ) . substr( - $this->_extendedEncoding, - $secondchar, - 1 - ); - $chartdata[] = $mappedchar . $dataDelimiter; - } else { - $chartdata[] = $dataMissing . $dataDelimiter; - } + $count = count($thisdataarray); + for ($j = 0; $j < $count; $j++) { + $currentvalue = $thisdataarray[$j]; + if (is_numeric($currentvalue)) { + $ylocation = $yorigin + $currentvalue; + $chartdata[] = $ylocation . $dataDelimiter; + } else { + $chartdata[] = $dataMissing . $dataDelimiter; } } $chartdata[] = $dataSetdelimiter; @@ -381,45 +342,13 @@ public function getChartUrl($directUrl = true) $valueBuffer = []; - if (sizeof($this->_axisLabels) > 0) { + if (count($this->_axisLabels) > 0) { $params['chxt'] = implode(',', array_keys($this->_axisLabels)); $indexid = 0; foreach ($this->_axisLabels as $idx => $labels) { if ($idx == 'x') { - /** - * Format date - */ - foreach ($this->_axisLabels[$idx] as $_index => $_label) { - if ($_label != '') { - $period = new \DateTime($_label, new \DateTimeZone($timezoneLocal)); - switch ($this->getDataHelper()->getParam('period')) { - case '24h': - $this->_axisLabels[$idx][$_index] = $this->_localeDate->formatDateTime( - $period->setTime($period->format('H'), 0, 0), - \IntlDateFormatter::NONE, - \IntlDateFormatter::SHORT - ); - break; - case '7d': - case '1m': - $this->_axisLabels[$idx][$_index] = $this->_localeDate->formatDateTime( - $period, - \IntlDateFormatter::SHORT, - \IntlDateFormatter::NONE - ); - break; - case '1y': - case '2y': - $this->_axisLabels[$idx][$_index] = date('m/Y', strtotime($_label)); - break; - } - } else { - $this->_axisLabels[$idx][$_index] = ''; - } - } - + $this->formatAxisLabelDate($idx, $timezoneLocal); $tmpstring = implode('|', $this->_axisLabels[$idx]); - $valueBuffer[] = $indexid . ":|" . $tmpstring; } elseif ($idx == 'y') { $valueBuffer[] = $indexid . ":|" . implode('|', $yLabels); @@ -438,15 +367,56 @@ public function getChartUrl($directUrl = true) foreach ($params as $name => $value) { $p[] = $name . '=' . urlencode($value); } + return self::API_URL . '?' . implode('&', $p); } else { $gaData = urlencode(base64_encode(json_encode($params))); $gaHash = $this->_dashboardData->getChartDataHash($gaData); $params = ['ga' => $gaData, 'h' => $gaHash]; + return $this->getUrl('adminhtml/*/tunnel', ['_query' => $params]); } } + /** + * Format dates for axis labels. + * + * @param string $idx + * @param string $timezoneLocal + * @return void + */ + private function formatAxisLabelDate($idx, $timezoneLocal) + { + foreach ($this->_axisLabels[$idx] as $_index => $_label) { + if ($_label != '') { + $period = new \DateTime($_label, new \DateTimeZone($timezoneLocal)); + switch ($this->getDataHelper()->getParam('period')) { + case '24h': + $this->_axisLabels[$idx][$_index] = $this->_localeDate->formatDateTime( + $period->setTime($period->format('H'), 0, 0), + \IntlDateFormatter::NONE, + \IntlDateFormatter::SHORT + ); + break; + case '7d': + case '1m': + $this->_axisLabels[$idx][$_index] = $this->_localeDate->formatDateTime( + $period, + \IntlDateFormatter::SHORT, + \IntlDateFormatter::NONE + ); + break; + case '1y': + case '2y': + $this->_axisLabels[$idx][$_index] = date('m/Y', strtotime($_label)); + break; + } + } else { + $this->_axisLabels[$idx][$_index] = ''; + } + } + } + /** * Get rows data * @@ -540,6 +510,8 @@ protected function getHeight() } /** + * Sets data helper. + * * @param \Magento\Backend\Helper\Dashboard\AbstractDashboard $dataHelper * @return void */ diff --git a/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/GraphTest.php b/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/GraphTest.php index 17863cd709580..497deb2c99110 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/GraphTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/GraphTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Backend\Block\Dashboard; /** @@ -27,6 +29,6 @@ protected function setUp() public function testGetChartUrl() { - $this->assertStringStartsWith('http://chart.apis.google.com/chart', $this->_block->getChartUrl()); + $this->assertStringStartsWith('https://image-charts.com/chart', $this->_block->getChartUrl()); } } diff --git a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php index 20fbe2a3f3f62..d0b1e2e24427d 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Backend\Controller\Adminhtml; /** @@ -19,10 +21,15 @@ public function testAjaxBlockAction() $this->assertContains('dashboard-diagram', $actual); } + /** + * Tests tunnelAction. + * + * @return void + * @throws \Exception + */ public function testTunnelAction() { - $this->markTestSkipped('MAGETWO-98803'); - + // phpcs:disable Magento2.Functions.DiscouragedFunction $testUrl = \Magento\Backend\Block\Dashboard\Graph::API_URL . '?cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World'; $handle = curl_init(); curl_setopt($handle, CURLOPT_URL, $testUrl); @@ -36,6 +43,7 @@ public function testTunnelAction() curl_close($handle); throw $e; } + // phpcs:enable $gaData = [ 'cht' => 'lc', From 1c5f7da8102793b0a6349f1bd3af3c8a0ad8cf74 Mon Sep 17 00:00:00 2001 From: Dmytro Yushkin <dyushkin@adobe.com> Date: Fri, 19 Apr 2019 10:49:13 -0500 Subject: [PATCH 1152/1295] MAGETWO-97400: Name of categories in the Category tree on Product Edit page is not displayed according to the selected store view scope - Fixed returnable type --- .../Ui/DataProvider/Product/Form/Modifier/Categories.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php index cb6ad00e30298..d5426afb0cbf0 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php @@ -395,10 +395,10 @@ private function retrieveShownCategoriesIds(int $storeId, string $filter = '') : * * @param int $storeId * @param array $shownCategoriesIds - * @return array + * @return array|null * @throws LocalizedException */ - private function retrieveCategoriesTree(int $storeId, array $shownCategoriesIds) : array + private function retrieveCategoriesTree(int $storeId, array $shownCategoriesIds) : ?array { /* @var $collection \Magento\Catalog\Model\ResourceModel\Category\Collection */ $collection = $this->categoryCollectionFactory->create(); From a4f313a1528c566f3db7e5d2deea6423daf0e314 Mon Sep 17 00:00:00 2001 From: Dmytro Yushkin <dyushkin@adobe.com> Date: Fri, 19 Apr 2019 10:51:16 -0500 Subject: [PATCH 1153/1295] MAGETWO-98670: Braintree Displaying Incorrect Ship-To Name - Fixed unit test --- .../Test/Unit/Controller/Paypal/ReviewTest.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Braintree/Test/Unit/Controller/Paypal/ReviewTest.php b/app/code/Magento/Braintree/Test/Unit/Controller/Paypal/ReviewTest.php index cb911a8396b36..cc79b5b008e6e 100644 --- a/app/code/Magento/Braintree/Test/Unit/Controller/Paypal/ReviewTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Controller/Paypal/ReviewTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Braintree\Test\Unit\Controller\Paypal; +use Magento\Payment\Model\Method\Logger; use Magento\Quote\Model\Quote; use Magento\Framework\View\Layout; use Magento\Checkout\Model\Session; @@ -63,6 +64,14 @@ class ReviewTest extends \PHPUnit\Framework\TestCase * @var Review */ private $review; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + + /** + * @var Logger|\PHPUnit_Framework_MockObject_MockObject + */ + private $loggerMock; protected function setUp() { @@ -87,6 +96,9 @@ protected function setUp() ->getMock(); $this->messageManagerMock = $this->getMockBuilder(ManagerInterface::class) ->getMockForAbstractClass(); + $this->loggerMock = $this->getMockBuilder(Logger::class) + ->disableOriginalConstructor() + ->getMock(); $contextMock->expects(self::once()) ->method('getRequest') @@ -102,7 +114,8 @@ protected function setUp() $contextMock, $this->configMock, $this->checkoutSessionMock, - $this->quoteUpdaterMock + $this->quoteUpdaterMock, + $this->loggerMock ); } From 690a2e326604a3da73984e8cf8716503aeec3a36 Mon Sep 17 00:00:00 2001 From: Dmytro Yushkin <dyushkin@adobe.com> Date: Fri, 19 Apr 2019 11:10:08 -0500 Subject: [PATCH 1154/1295] MAGETWO-97400: Name of categories in the Category tree on Product Edit page is not displayed according to the selected store view scope - Removed returnable type --- .../Ui/DataProvider/Product/Form/Modifier/Categories.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php index d5426afb0cbf0..a3baf7b14a229 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php @@ -398,7 +398,7 @@ private function retrieveShownCategoriesIds(int $storeId, string $filter = '') : * @return array|null * @throws LocalizedException */ - private function retrieveCategoriesTree(int $storeId, array $shownCategoriesIds) : ?array + private function retrieveCategoriesTree(int $storeId, array $shownCategoriesIds) { /* @var $collection \Magento\Catalog\Model\ResourceModel\Category\Collection */ $collection = $this->categoryCollectionFactory->create(); From 28f35bcc6adb00a8e8825da17f992f23105d1ebd Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Fri, 19 Apr 2019 11:55:59 -0500 Subject: [PATCH 1155/1295] MAGETWO-99247: Customizable Option Price input is not saved on Store View level when Catalog Price Scope set to Global --- .../Model/ResourceModel/Product/Option/Value.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php index 5ffc9fbd575b6..3927b37016c1e 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php @@ -17,11 +17,12 @@ use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\Store; use Magento\Store\Model\StoreManagerInterface; +use Magento\Catalog\Helper\Data; /** * Catalog product custom option resource model * - * @author Magento Core Team <core@magentocommerce.com> + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Value extends AbstractDb { @@ -51,6 +52,11 @@ class Value extends AbstractDb */ private $localeFormat; + /** + * @var Data + */ + private $dataHelper; + /** * Class constructor * @@ -59,17 +65,20 @@ class Value extends AbstractDb * @param StoreManagerInterface $storeManager * @param ScopeConfigInterface $config * @param string $connectionName + * @param Data $dataHelper */ public function __construct( Context $context, CurrencyFactory $currencyFactory, StoreManagerInterface $storeManager, ScopeConfigInterface $config, - $connectionName = null + $connectionName = null, + Data $dataHelper = null ) { $this->_currencyFactory = $currencyFactory; $this->_storeManager = $storeManager; $this->_config = $config; + $this->dataHelper = $dataHelper ?: ObjectManager::getInstance()->get(Data::class); parent::__construct($context, $connectionName); } @@ -130,7 +139,7 @@ protected function _saveValuePrices(AbstractModel $object) $optionTypeId = $this->getConnection()->fetchOne($select); if ($optionTypeId) { - if ($object->getStoreId() == '0') { + if ($object->getStoreId() == '0' || $this->dataHelper->isPriceGlobal()) { $bind = ['price' => $price, 'price_type' => $priceType]; $where = [ 'option_type_id = ?' => $optionTypeId, From e4c5c82b08ee06ffc72f86a7ea818d618414e32d Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Fri, 19 Apr 2019 15:14:33 -0500 Subject: [PATCH 1156/1295] MAGETWO-99247: Customizable Option Price input is not saved on Store View level when Catalog Price Scope set to Global --- .../Catalog/Model/ResourceModel/Product/Option/Value.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php index 3927b37016c1e..0362df7a846bb 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php @@ -139,7 +139,8 @@ protected function _saveValuePrices(AbstractModel $object) $optionTypeId = $this->getConnection()->fetchOne($select); if ($optionTypeId) { - if ($object->getStoreId() == '0' || $this->dataHelper->isPriceGlobal()) { + if ($object->getStoreId() == '0' //|| $this->dataHelper->isPriceGlobal() + ) { $bind = ['price' => $price, 'price_type' => $priceType]; $where = [ 'option_type_id = ?' => $optionTypeId, From 4b60fad08f07d39e052ef69746b9233a3270f618 Mon Sep 17 00:00:00 2001 From: Fabian Schmengler <fs@integer-net.de> Date: Tue, 16 Oct 2018 09:21:09 +0200 Subject: [PATCH 1157/1295] Cherry Pick issue 18641 --- .../Setup/Model/ConfigOptionsList/Cache.php | 23 ++++++ .../Model/ConfigOptionsList/PageCache.php | 23 ++++++ .../Model/ConfigOptionsList/CacheTest.php | 76 +++++++++++++++++- .../Model/ConfigOptionsList/PageCacheTest.php | 77 ++++++++++++++++++- 4 files changed, 193 insertions(+), 6 deletions(-) diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php index 04ec83a3d0ca2..8bdfd2b0a91a5 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/Cache.php @@ -27,12 +27,14 @@ class Cache implements ConfigOptionsListInterface 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 INPUT_KEY_CACHE_ID_PREFIX = 'cache-id-prefix'; 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'; + const CONFIG_PATH_CACHE_ID_PREFIX = 'cache/frontend/default/id_prefix'; /** * @var array @@ -112,6 +114,12 @@ public function getOptions() TextConfigOption::FRONTEND_WIZARD_TEXT, self::CONFIG_PATH_CACHE_BACKEND_PASSWORD, 'Redis server password' + ), + new TextConfigOption( + self::INPUT_KEY_CACHE_ID_PREFIX, + TextConfigOption::FRONTEND_WIZARD_TEXT, + self::CONFIG_PATH_CACHE_ID_PREFIX, + 'ID prefix for cache keys' ) ]; } @@ -122,6 +130,11 @@ public function getOptions() public function createConfig(array $options, DeploymentConfig $deploymentConfig) { $configData = new ConfigData(ConfigFilePool::APP_ENV); + if (isset($options[self::INPUT_KEY_CACHE_ID_PREFIX])) { + $configData->set(self::CONFIG_PATH_CACHE_ID_PREFIX, $options[self::INPUT_KEY_CACHE_ID_PREFIX]); + } else { + $configData->set(self::CONFIG_PATH_CACHE_ID_PREFIX, $this->generateCachePrefix()); + } if (isset($options[self::INPUT_KEY_CACHE_BACKEND])) { if ($options[self::INPUT_KEY_CACHE_BACKEND] == self::INPUT_VALUE_CACHE_REDIS) { @@ -241,4 +254,14 @@ private function getDefaultConfigValue($inputKey) return ''; } } + + /** + * Generate default cache ID prefix based on installation dir + * + * @return string + */ + private function generateCachePrefix(): string + { + return substr(\md5(dirname(__DIR__, 6)), 0, 3) . '_'; + } } diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php index 944c543495751..a0dd19034621b 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList/PageCache.php @@ -28,6 +28,7 @@ class PageCache implements ConfigOptionsListInterface 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 INPUT_KEY_PAGE_CACHE_ID_PREFIX = 'page-cache-id-prefix'; 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'; @@ -35,6 +36,7 @@ class PageCache implements ConfigOptionsListInterface 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'; + const CONFIG_PATH_PAGE_CACHE_ID_PREFIX = 'cache/frontend/page_cache/id_prefix'; /** * @var array @@ -122,6 +124,12 @@ public function getOptions() TextConfigOption::FRONTEND_WIZARD_TEXT, self::CONFIG_PATH_PAGE_CACHE_BACKEND_PASSWORD, 'Redis server password' + ), + new TextConfigOption( + self::INPUT_KEY_PAGE_CACHE_ID_PREFIX, + TextConfigOption::FRONTEND_WIZARD_TEXT, + self::CONFIG_PATH_PAGE_CACHE_ID_PREFIX, + 'ID prefix for cache keys' ) ]; } @@ -132,6 +140,11 @@ public function getOptions() public function createConfig(array $options, DeploymentConfig $deploymentConfig) { $configData = new ConfigData(ConfigFilePool::APP_ENV); + if (isset($options[self::INPUT_KEY_PAGE_CACHE_ID_PREFIX])) { + $configData->set(self::CONFIG_PATH_PAGE_CACHE_ID_PREFIX, $options[self::INPUT_KEY_PAGE_CACHE_ID_PREFIX]); + } else { + $configData->set(self::CONFIG_PATH_PAGE_CACHE_ID_PREFIX, $this->generateCachePrefix()); + } if (isset($options[self::INPUT_KEY_PAGE_CACHE_BACKEND])) { if ($options[self::INPUT_KEY_PAGE_CACHE_BACKEND] == self::INPUT_VALUE_PAGE_CACHE_REDIS) { @@ -252,4 +265,14 @@ private function getDefaultConfigValue($inputKey) return ''; } } + + /** + * Generate default cache ID prefix based on installation dir + * + * @return string + */ + private function generateCachePrefix(): string + { + return substr(\md5(dirname(__DIR__, 6)), 0, 3) . '_'; + } } 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 39b95953c6347..da3fc662e3adf 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(5, $options); + $this->assertCount(6, $options); $this->assertArrayHasKey(0, $options); $this->assertInstanceOf(SelectConfigOption::class, $options[0]); @@ -60,6 +60,10 @@ public function testGetOptions() $this->assertArrayHasKey(4, $options); $this->assertInstanceOf(TextConfigOption::class, $options[4]); $this->assertEquals('cache-backend-redis-password', $options[4]->getName()); + + $this->assertArrayHasKey(5, $options); + $this->assertInstanceOf(TextConfigOption::class, $options[5]); + $this->assertEquals('cache-id-prefix', $options[5]->getName()); } public function testCreateConfigCacheRedis() @@ -76,7 +80,8 @@ public function testCreateConfigCacheRedis() 'port' => '', 'database' => '', 'password' => '' - ] + ], + 'id_prefix' => $this->expectedIdPrefix(), ] ] ] @@ -99,7 +104,8 @@ public function testCreateConfigWithRedisConfig() 'port' => '1234', 'database' => '5', 'password' => '' - ] + ], + 'id_prefix' => $this->expectedIdPrefix(), ] ] ] @@ -116,6 +122,60 @@ public function testCreateConfigWithRedisConfig() $this->assertEquals($expectedConfigData, $configData->getData()); } +<<<<<<< HEAD +======= + /** + * testCreateConfigCacheRedis + */ + public function testCreateConfigWithFileCache() + { + $this->deploymentConfigMock->method('get')->willReturn(''); + + $expectedConfigData = [ + 'cache' => [ + 'frontend' => [ + 'default' => [ + 'id_prefix' => $this->expectedIdPrefix(), + ] + ] + ] + ]; + + $configData = $this->configOptionsList->createConfig([], $this->deploymentConfigMock); + + $this->assertEquals($expectedConfigData, $configData->getData()); + } + + /** + * testCreateConfigCacheRedis + */ + public function testCreateConfigWithIdPrefix() + { + $this->deploymentConfigMock->method('get')->willReturn(''); + + $explicitPrefix = 'XXX_'; + $expectedConfigData = [ + 'cache' => [ + 'frontend' => [ + 'default' => [ + 'id_prefix' => $explicitPrefix, + ] + ] + ] + ]; + + $configData = $this->configOptionsList->createConfig( + ['cache-id-prefix' => $explicitPrefix], + $this->deploymentConfigMock + ); + + $this->assertEquals($expectedConfigData, $configData->getData()); + } + + /** + * testValidateWithValidInput + */ +>>>>>>> 12b7e08c2f26... Set cache id prefix on installation public function testValidateWithValidInput() { $options = [ @@ -142,4 +202,14 @@ public function testValidateWithInvalidInput() $this->assertCount(1, $errors); $this->assertEquals("Invalid cache handler 'clay-tablet'", $errors[0]); } + + /** + * The default ID prefix, based on installation directory + * + * @return string + */ + private function expectedIdPrefix(): string + { + return substr(\md5(dirname(__DIR__, 8)), 0, 3) . '_'; + } } 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 ed0e567820ad1..5bac79e4cde79 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(6, $options); + $this->assertCount(7, $options); $this->assertArrayHasKey(0, $options); $this->assertInstanceOf(SelectConfigOption::class, $options[0]); @@ -64,6 +64,10 @@ public function testGetOptions() $this->assertArrayHasKey(5, $options); $this->assertInstanceOf(TextConfigOption::class, $options[5]); $this->assertEquals('page-cache-redis-password', $options[5]->getName()); + + $this->assertArrayHasKey(6, $options); + $this->assertInstanceOf(TextConfigOption::class, $options[6]); + $this->assertEquals('page-cache-id-prefix', $options[6]->getName()); } public function testCreateConfigWithRedis() @@ -81,7 +85,8 @@ public function testCreateConfigWithRedis() 'database' => '', 'compress_data' => '', 'password' => '' - ] + ], + 'id_prefix' => $this->expectedIdPrefix(), ] ] ] @@ -105,7 +110,8 @@ public function testCreateConfigWithRedisConfiguration() 'database' => '6', 'compress_data' => '1', 'password' => '' - ] + ], + 'id_prefix' => $this->expectedIdPrefix(), ] ] ] @@ -124,6 +130,61 @@ public function testCreateConfigWithRedisConfiguration() $this->assertEquals($expectedConfigData, $configData->getData()); } +<<<<<<< HEAD +======= + /** + * testCreateConfigWithRedis + */ + public function testCreateConfigWithFileCache() + { + $this->deploymentConfigMock->method('get')->willReturn(''); + + $expectedConfigData = [ + 'cache' => [ + 'frontend' => [ + 'page_cache' => [ + 'id_prefix' => $this->expectedIdPrefix(), + ] + ] + ] + ]; + + $configData = $this->configList->createConfig([], $this->deploymentConfigMock); + + $this->assertEquals($expectedConfigData, $configData->getData()); + } + + + /** + * testCreateConfigCacheRedis + */ + public function testCreateConfigWithIdPrefix() + { + $this->deploymentConfigMock->method('get')->willReturn(''); + + $explicitPrefix = 'XXX_'; + $expectedConfigData = [ + 'cache' => [ + 'frontend' => [ + 'page_cache' => [ + 'id_prefix' => $explicitPrefix, + ] + ] + ] + ]; + + $configData = $this->configList->createConfig( + ['page-cache-id-prefix' => $explicitPrefix], + $this->deploymentConfigMock + ); + + $this->assertEquals($expectedConfigData, $configData->getData()); + } + + /** + * testValidationWithValidData + */ +>>>>>>> 12b7e08c2f26... Set cache id prefix on installation public function testValidationWithValidData() { $this->validatorMock->expects($this->once()) @@ -151,4 +212,14 @@ public function testValidationWithInvalidData() $this->assertCount(1, $errors); $this->assertEquals('Invalid cache handler \'foobar\'', $errors[0]); } + + /** + * The default ID prefix, based on installation directory + * + * @return string + */ + private function expectedIdPrefix(): string + { + return substr(\md5(dirname(__DIR__, 8)), 0, 3) . '_'; + } } From 20efb0563782aacef6511cc9cd55f971f2e18657 Mon Sep 17 00:00:00 2001 From: niravkrish <nirav.patel@krishtechnolabs.com> Date: Tue, 16 Apr 2019 14:38:49 +0530 Subject: [PATCH 1158/1295] Fixed - Category API update Issue required name on update --- app/code/Magento/Catalog/Api/Data/CategoryInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Api/Data/CategoryInterface.php b/app/code/Magento/Catalog/Api/Data/CategoryInterface.php index b65cdafbe26f4..cf5e49c954b43 100644 --- a/app/code/Magento/Catalog/Api/Data/CategoryInterface.php +++ b/app/code/Magento/Catalog/Api/Data/CategoryInterface.php @@ -43,7 +43,7 @@ public function setParentId($parentId); /** * Get category name * - * @return string + * @return string|null */ public function getName(); From b3a799deb5e744e286a37e04abc8425eacc4e878 Mon Sep 17 00:00:00 2001 From: niravkrish <nirav.patel@krishtechnolabs.com> Date: Mon, 15 Apr 2019 14:20:41 +0530 Subject: [PATCH 1159/1295] Fixed - search icon click not working issue in admin ui grid sticky header --- .../Ui/view/base/web/js/grid/search/search.js | 23 +++++++++++++++---- .../web/templates/grid/search/search.html | 3 ++- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/search/search.js b/app/code/Magento/Ui/view/base/web/js/grid/search/search.js index be825a391cf07..bb74b84541a57 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/search/search.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/search/search.js @@ -11,8 +11,9 @@ define([ 'uiLayout', 'mage/translate', 'mageUtils', - 'uiElement' -], function (_, layout, $t, utils, Element) { + 'uiElement', + 'jquery' +], function (_, layout, $t, utils, Element, $) { 'use strict'; return Element.extend({ @@ -29,11 +30,13 @@ define([ tracks: { value: true, previews: true, - inputValue: true + inputValue: true, + focused: true }, imports: { inputValue: 'value', - updatePreview: 'value' + updatePreview: 'value', + focused: false }, exports: { value: '${ $.provider }:params.search' @@ -88,6 +91,18 @@ define([ return this; }, + /** + * Click To ScrollTop. + */ + scrollTo: function ($data) { + $('html, body').animate({ + scrollTop: 0 + }, 'slow', function () { + $data.focused = false; + $data.focused = true; + }); + }, + /** * Resets input value to the last applied state. * diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/search/search.html b/app/code/Magento/Ui/view/base/web/templates/grid/search/search.html index 13b82a93eca25..fcad729a95fbb 100644 --- a/app/code/Magento/Ui/view/base/web/templates/grid/search/search.html +++ b/app/code/Magento/Ui/view/base/web/templates/grid/search/search.html @@ -5,7 +5,7 @@ */ --> <div class="data-grid-search-control-wrap"> - <label class="data-grid-search-label" attr="title: $t('Search'), for: index"> + <label class="data-grid-search-label" attr="title: $t('Search'), for: index" data-bind="click: scrollTo"> <span translate="'Search'"/> </label> <input class="admin__control-text data-grid-search-control" type="text" @@ -16,6 +16,7 @@ placeholder: $t(placeholder) }, textInput: inputValue, + hasFocus: focused, keyboard: { 13: apply.bind($data, false), 27: cancel From ed864111ecf0f07869faa5cf7d5219f53b54fd9f Mon Sep 17 00:00:00 2001 From: Yogesh Suhagiya <yksuhagiya@gmail.com> Date: Mon, 22 Apr 2019 10:36:09 +0530 Subject: [PATCH 1160/1295] Corrected the translation for comment tag --- .../system/paypal_payflowpro_with_express_checkout.xml | 4 ++-- app/code/Magento/Paypal/i18n/en_US.csv | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro_with_express_checkout.xml b/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro_with_express_checkout.xml index 90ffeddbb812e..ae9ab5c0b407f 100644 --- a/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro_with_express_checkout.xml +++ b/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro_with_express_checkout.xml @@ -6,7 +6,7 @@ */ --> <include xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_include.xsd"> - <group id="paypal_payflowpro_with_express_checkout" translate="label comment" extends="payment_all_paypal/paypal_payflowpro"> + <group id="paypal_payflowpro_with_express_checkout" translate="label" extends="payment_all_paypal/paypal_payflowpro"> <label>Payflow Pro</label> <attribute type="paypal_ec_separate">0</attribute> <group id="paypal_payflow_required" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> @@ -30,7 +30,7 @@ <field id="enable_paypal_payflow"/> </requires> </field> - <field id="enable_express_checkout_bml_payflow" translate="label" type="select" sortOrder="21" showInWebsite="1" showInDefault="1"> + <field id="enable_express_checkout_bml_payflow" translate="label comment" type="select" sortOrder="21" showInWebsite="1" showInDefault="1"> <label>Enable PayPal Credit</label> <comment><![CDATA[PayPal Express Checkout Payflow Edition lets you give customers access to financing through PayPal Credit® - at no additional cost to you. You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. diff --git a/app/code/Magento/Paypal/i18n/en_US.csv b/app/code/Magento/Paypal/i18n/en_US.csv index 2a1a6bbb48ef2..6c22ba09ca7cd 100644 --- a/app/code/Magento/Paypal/i18n/en_US.csv +++ b/app/code/Magento/Paypal/i18n/en_US.csv @@ -697,3 +697,8 @@ User,User The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% or more. <a href=""https:/financing.paypal.com/ppfinportal/content/forrester"" target=""_blank"">See Details</a>. " +"PayPal Express Checkout Payflow Edition lets you give customers access to financing through PayPal Credit® - at no additional cost to you. + You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. + <a href=""https://www.paypal.com/webapps/mpp/promotional-financing"" target=""_blank"">Learn More</a>","PayPal Express Checkout Payflow Edition lets you give customers access to financing through PayPal Credit® - at no additional cost to you. + You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. + <a href=""https://www.paypal.com/webapps/mpp/promotional-financing"" target=""_blank"">Learn More</a>" From e95fd4a05a8e438a452d0062edf2956da7570e52 Mon Sep 17 00:00:00 2001 From: Yogesh Suhagiya <yksuhagiya@gmail.com> Date: Mon, 22 Apr 2019 10:40:17 +0530 Subject: [PATCH 1161/1295] Translated exception message --- app/code/Magento/Braintree/Controller/Paypal/PlaceOrder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Braintree/Controller/Paypal/PlaceOrder.php b/app/code/Magento/Braintree/Controller/Paypal/PlaceOrder.php index 1440d495e8a7d..09be3723e4518 100644 --- a/app/code/Magento/Braintree/Controller/Paypal/PlaceOrder.php +++ b/app/code/Magento/Braintree/Controller/Paypal/PlaceOrder.php @@ -74,7 +74,7 @@ public function execute() $this->logger->critical($e); $this->messageManager->addExceptionMessage( $e, - 'The order #' . $quote->getReservedOrderId() . ' cannot be processed.' + __('The order #%1 cannot be processed.', $quote->getReservedOrderId()) ); } From eb152f038bbd623ff30516d83bd60892a02d8836 Mon Sep 17 00:00:00 2001 From: Yogesh Suhagiya <yksuhagiya@gmail.com> Date: Mon, 22 Apr 2019 10:45:43 +0530 Subject: [PATCH 1162/1295] Translate comment in DHL config settings --- app/code/Magento/Dhl/etc/adminhtml/system.xml | 2 +- app/code/Magento/Dhl/i18n/en_US.csv | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Dhl/etc/adminhtml/system.xml b/app/code/Magento/Dhl/etc/adminhtml/system.xml index 37b653225c7b9..7ab37de2f3658 100644 --- a/app/code/Magento/Dhl/etc/adminhtml/system.xml +++ b/app/code/Magento/Dhl/etc/adminhtml/system.xml @@ -31,7 +31,7 @@ <field id="account" translate="label" type="text" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Account Number</label> </field> - <field id="content_type" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <field id="content_type" translate="label comment" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Content Type (Non Domestic)</label> <comment>Whether to use Documents or NonDocuments service for non domestic shipments. (Shipments within the EU are classed as domestic)</comment> <source_model>Magento\Dhl\Model\Source\Contenttype</source_model> diff --git a/app/code/Magento/Dhl/i18n/en_US.csv b/app/code/Magento/Dhl/i18n/en_US.csv index a5532c2cea963..0e4c7a8385b93 100644 --- a/app/code/Magento/Dhl/i18n/en_US.csv +++ b/app/code/Magento/Dhl/i18n/en_US.csv @@ -61,6 +61,7 @@ Title,Title Password,Password "Account Number","Account Number" "Content Type","Content Type" +"Whether to use Documents or NonDocuments service for non domestic shipments. (Shipments within the EU are classed as domestic)","Whether to use Documents or NonDocuments service for non domestic shipments. (Shipments within the EU are classed as domestic)" "Calculate Handling Fee","Calculate Handling Fee" "Handling Applied","Handling Applied" """Per Order"" allows a single handling fee for the entire order. ""Per Package"" allows an individual handling fee for each package.","""Per Order"" allows a single handling fee for the entire order. ""Per Package"" allows an individual handling fee for each package." From 34b2dccf322c1af0d5c563063e439de1b4a9cbe9 Mon Sep 17 00:00:00 2001 From: Nazarn96 <nazarn96@gmail.com> Date: Mon, 22 Apr 2019 14:34:11 +0300 Subject: [PATCH 1163/1295] =?UTF-8?q?Layered=20Navigation:=20=E2=80=9CEqua?= =?UTF-8?q?lize=20product=20count=E2=80=9D=20not=20working=20as=20expected?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Magento/CatalogSearch/Model/Layer/Filter/Decimal.php | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Decimal.php b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Decimal.php index e61a886a41d6f..e9fb1070fedd5 100644 --- a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Decimal.php +++ b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Decimal.php @@ -111,12 +111,9 @@ protected function _getItemsData() $from = ''; } if ($to == '*') { - $to = ''; + $to = null; } - $label = $this->renderRangeLabel( - empty($from) ? 0 : $from, - empty($to) ? 0 : $to - ); + $label = $this->renderRangeLabel(empty($from) ? 0 : $from, $to); $value = $from . '-' . $to; $data[] = [ @@ -141,7 +138,7 @@ protected function _getItemsData() protected function renderRangeLabel($fromPrice, $toPrice) { $formattedFromPrice = $this->priceCurrency->format($fromPrice); - if ($toPrice === '') { + if ($toPrice === null) { return __('%1 and above', $formattedFromPrice); } else { if ($fromPrice != $toPrice) { From c365d6cb5def47773e9a06eebd196039b7b76d52 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Mon, 22 Apr 2019 17:41:07 +0300 Subject: [PATCH 1164/1295] Fix static test. --- app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php b/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php index f7a2480a32b28..c1182505bc0c6 100644 --- a/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php +++ b/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php @@ -158,7 +158,7 @@ protected function _addField($parameter) $data['value'] = $parameter->getValue(); //prepare unique id value if ($fieldName == 'unique_id' && $data['value'] == '') { - $data['value'] = md5(microtime(1)); + $data['value'] = hash('sha256', microtime(1)); } } From 92ac18d011796d983422b0cbc116cecb587c3e84 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Tue, 23 Apr 2019 12:15:00 +0300 Subject: [PATCH 1165/1295] MAGETWO-98583: Google chart API used by Magento dashboard scheduled to be turned off --- .../Magento/Backend/Block/Dashboard/Graph.php | 31 +++++++++---------- .../Backend/Block/Dashboard/GraphTest.php | 10 ++++-- .../Controller/Adminhtml/DashboardTest.php | 2 -- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/app/code/Magento/Backend/Block/Dashboard/Graph.php b/app/code/Magento/Backend/Block/Dashboard/Graph.php index c94bbd25e1fed..6c48eea66a951 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Graph.php +++ b/app/code/Magento/Backend/Block/Dashboard/Graph.php @@ -112,8 +112,8 @@ public function __construct( \Magento\Backend\Helper\Dashboard\Data $dashboardData, array $data = [] ) { - $this->_dashboardData = $dashboardData; parent::__construct($context, $collectionFactory, $data); + $this->_dashboardData = $dashboardData; } /** @@ -127,9 +127,9 @@ protected function _getTabTemplate() } /** - * Set data rows + * Set data rows. * - * @param array $rows + * @param string $rows * @return void */ public function setDataRows($rows) @@ -150,18 +150,18 @@ public function addSeries($seriesId, array $options) } /** - * Get series + * Get series. * * @param string $seriesId - * @return array|false + * @return array|bool */ public function getSeries($seriesId) { if (isset($this->_allSeries[$seriesId])) { return $this->_allSeries[$seriesId]; - } else { - return false; } + + return false; } /** @@ -306,7 +306,7 @@ public function getChartUrl($directUrl = true) if ($minvalue >= 0 && $maxvalue >= 0) { if ($maxvalue > 10) { - $p = pow(10, $this->_getPow($maxvalue)); + $p = pow(10, $this->_getPow((int)$maxvalue)); $maxy = ceil($maxvalue / $p) * $p; $yLabels = range($miny, $maxy, $p); } else { @@ -369,13 +369,12 @@ public function getChartUrl($directUrl = true) } return self::API_URL . '?' . implode('&', $p); - } else { - $gaData = urlencode(base64_encode(json_encode($params))); - $gaHash = $this->_dashboardData->getChartDataHash($gaData); - $params = ['ga' => $gaData, 'h' => $gaHash]; - - return $this->getUrl('adminhtml/*/tunnel', ['_query' => $params]); } + $gaData = urlencode(base64_encode(json_encode($params))); + $gaHash = $this->_dashboardData->getChartDataHash($gaData); + $params = ['ga' => $gaData, 'h' => $gaHash]; + + return $this->getUrl('adminhtml/*/tunnel', ['_query' => $params]); } /** @@ -385,7 +384,7 @@ public function getChartUrl($directUrl = true) * @param string $timezoneLocal * @return void */ - private function formatAxisLabelDate($idx, $timezoneLocal) + private function formatAxisLabelDate(string $idx, string $timezoneLocal) { foreach ($this->_axisLabels[$idx] as $_index => $_label) { if ($_label != '') { @@ -393,7 +392,7 @@ private function formatAxisLabelDate($idx, $timezoneLocal) switch ($this->getDataHelper()->getParam('period')) { case '24h': $this->_axisLabels[$idx][$_index] = $this->_localeDate->formatDateTime( - $period->setTime($period->format('H'), 0, 0), + $period->setTime((int)$period->format('H'), 0, 0), \IntlDateFormatter::NONE, \IntlDateFormatter::SHORT ); diff --git a/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/GraphTest.php b/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/GraphTest.php index 497deb2c99110..3c8930fb78097 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/GraphTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/GraphTest.php @@ -3,8 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -declare(strict_types=1); - namespace Magento\Backend\Block\Dashboard; /** @@ -17,6 +15,9 @@ class GraphTest extends \PHPUnit\Framework\TestCase */ protected $_block; + /** + * @inheritdoc + */ protected function setUp() { parent::setUp(); @@ -27,6 +28,11 @@ protected function setUp() $this->_block->setDataHelper($objectManager->get(\Magento\Backend\Helper\Dashboard\Order::class)); } + /** + * Tests getChartUrl. + * + * @return void + */ public function testGetChartUrl() { $this->assertStringStartsWith('https://image-charts.com/chart', $this->_block->getChartUrl()); diff --git a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php index d0b1e2e24427d..4373523350c49 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/DashboardTest.php @@ -3,8 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -declare(strict_types=1); - namespace Magento\Backend\Controller\Adminhtml; /** From 60d754238e00cb4f03759b0b16f2792b16669a0f Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Tue, 23 Apr 2019 13:20:00 +0300 Subject: [PATCH 1166/1295] MAGETWO-97413: [Magento Cloud] Grouped Products lose the associated SKUs after scheduled update expires --- .../ActionGroup/AdminProductActionGroup.xml | 8 ++++++++ ...resenceOnGroupedProductPageActionGroup.xml | 19 +++++++++++++++++++ .../StorefrontProductInfoMainSection.xml | 14 ++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AssertLinkPresenceOnGroupedProductPageActionGroup.xml create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Section/StorefrontProductInfoMainSection.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index e68c75858102f..3dfdd76733f40 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -269,4 +269,12 @@ <waitForElementVisible selector="{{AdminProductFormSection.attributeSetFilterResultByName(attributeSet.attribute_set_name)}}" stepKey="waitForNewAttributeSetIsShown"/> <click selector="{{AdminProductFormSection.attributeSetFilterResultByName(attributeSet.attribute_set_name)}}" stepKey="selectAttributeSet"/> </actionGroup> + + <!--Navigate to created product page directly via ID--> + <actionGroup name="goToProductPageViaID"> + <arguments> + <argument name="productId" type="string"/> + </arguments> + <amOnPage url="{{AdminProductEditPage.url(productId)}}" stepKey="goToProduct"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AssertLinkPresenceOnGroupedProductPageActionGroup.xml b/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AssertLinkPresenceOnGroupedProductPageActionGroup.xml new file mode 100644 index 0000000000000..bce78f8bf9961 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AssertLinkPresenceOnGroupedProductPageActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!-- Check for the product link. --> + <!-- Assumes web client is on Grouped Product Page --> + <actionGroup name="AssertLinkPresenceOnGroupedProductPage"> + <arguments> + <argument name="productName" type="string"/> + </arguments> + <see selector="{{StorefrontProductInfoMainSection.groupedProductsTable}}" userInput="{{productName}}" stepKey="seeFirstStagedGroupedProduct"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Section/StorefrontProductInfoMainSection.xml new file mode 100644 index 0000000000000..45d8e63343734 --- /dev/null +++ b/app/code/Magento/GroupedProduct/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="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontProductInfoMainSection"> + <element name="groupedProductsTable" type="text" selector="#super-product-table .product-item-name"/> + </section> +</sections> From 1242bb8f905b190c6808e2bc907f8dfd4fd5b7e4 Mon Sep 17 00:00:00 2001 From: "Leandro F. L" <lfluvisotto@gmail.com> Date: Mon, 8 Apr 2019 14:41:02 -0300 Subject: [PATCH 1167/1295] Remove all marketing get params on Varnish to minimize the cache objects (added facebook parameter) --- app/code/Magento/PageCache/etc/varnish4.vcl | 4 ++-- app/code/Magento/PageCache/etc/varnish5.vcl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/PageCache/etc/varnish4.vcl b/app/code/Magento/PageCache/etc/varnish4.vcl index 6b64b5b59f51f..7bda69cd1f8dd 100644 --- a/app/code/Magento/PageCache/etc/varnish4.vcl +++ b/app/code/Magento/PageCache/etc/varnish4.vcl @@ -92,8 +92,8 @@ sub vcl_recv { } # Remove all marketing get parameters to minimize the cache objects - if (req.url ~ "(\?|&)(gclid|cx|ie|cof|siteurl|zanpid|origin|mc_[a-z]+|utm_[a-z]+)=") { - set req.url = regsuball(req.url, "(gclid|cx|ie|cof|siteurl|zanpid|origin|mc_[a-z]+|utm_[a-z]+)=[-_A-z0-9+()%.]+&?", ""); + if (req.url ~ "(\?|&)(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+)=") { + set req.url = regsuball(req.url, "(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+)=[-_A-z0-9+()%.]+&?", ""); set req.url = regsub(req.url, "[?|&]+$", ""); } diff --git a/app/code/Magento/PageCache/etc/varnish5.vcl b/app/code/Magento/PageCache/etc/varnish5.vcl index 2dfa4af25dd52..66832467e96c1 100644 --- a/app/code/Magento/PageCache/etc/varnish5.vcl +++ b/app/code/Magento/PageCache/etc/varnish5.vcl @@ -93,8 +93,8 @@ sub vcl_recv { } # Remove all marketing get parameters to minimize the cache objects - if (req.url ~ "(\?|&)(gclid|cx|ie|cof|siteurl|zanpid|origin|mc_[a-z]+|utm_[a-z]+)=") { - set req.url = regsuball(req.url, "(gclid|cx|ie|cof|siteurl|zanpid|origin|mc_[a-z]+|utm_[a-z]+)=[-_A-z0-9+()%.]+&?", ""); + if (req.url ~ "(\?|&)(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+)=") { + set req.url = regsuball(req.url, "(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+)=[-_A-z0-9+()%.]+&?", ""); set req.url = regsub(req.url, "[?|&]+$", ""); } From 3d4a8db7dfea245d1c615f25d14a8a4ffa900579 Mon Sep 17 00:00:00 2001 From: "Leandro F. L" <lfluvisotto@gmail.com> Date: Tue, 16 Apr 2019 18:53:41 -0300 Subject: [PATCH 1168/1295] Remove all marketing get params on Varnish to minimize the cache objects (added bronto parameter) --- app/code/Magento/PageCache/etc/varnish4.vcl | 4 ++-- app/code/Magento/PageCache/etc/varnish5.vcl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/PageCache/etc/varnish4.vcl b/app/code/Magento/PageCache/etc/varnish4.vcl index 7bda69cd1f8dd..21f48ef76502f 100644 --- a/app/code/Magento/PageCache/etc/varnish4.vcl +++ b/app/code/Magento/PageCache/etc/varnish4.vcl @@ -92,8 +92,8 @@ sub vcl_recv { } # Remove all marketing get parameters to minimize the cache objects - if (req.url ~ "(\?|&)(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+)=") { - set req.url = regsuball(req.url, "(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+)=[-_A-z0-9+()%.]+&?", ""); + if (req.url ~ "(\?|&)(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+|_bta_[a-z]+)=") { + set req.url = regsuball(req.url, "(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+|_bta_[a-z]+)=[-_A-z0-9+()%.]+&?", ""); set req.url = regsub(req.url, "[?|&]+$", ""); } diff --git a/app/code/Magento/PageCache/etc/varnish5.vcl b/app/code/Magento/PageCache/etc/varnish5.vcl index 66832467e96c1..23df172dd3aa4 100644 --- a/app/code/Magento/PageCache/etc/varnish5.vcl +++ b/app/code/Magento/PageCache/etc/varnish5.vcl @@ -93,8 +93,8 @@ sub vcl_recv { } # Remove all marketing get parameters to minimize the cache objects - if (req.url ~ "(\?|&)(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+)=") { - set req.url = regsuball(req.url, "(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+)=[-_A-z0-9+()%.]+&?", ""); + if (req.url ~ "(\?|&)(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+|_bta_[a-z]+)=") { + set req.url = regsuball(req.url, "(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_[a-z]+|utm_[a-z]+|_bta_[a-z]+)=[-_A-z0-9+()%.]+&?", ""); set req.url = regsub(req.url, "[?|&]+$", ""); } From 9d26084636bdd7f0b1ca428300afcf614f94e123 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Tue, 23 Apr 2019 14:08:19 +0300 Subject: [PATCH 1169/1295] Fix static and unit tests. --- .../Unit/Model/ConfigOptionsList/CacheTest.php | 15 +++------------ .../Model/ConfigOptionsList/PageCacheTest.php | 16 +++------------- 2 files changed, 6 insertions(+), 25 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 da3fc662e3adf..f351bca65f89b 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/CacheTest.php @@ -28,6 +28,9 @@ class CacheTest extends \PHPUnit\Framework\TestCase */ private $deploymentConfigMock; + /** + * @inheritdoc + */ protected function setUp() { $this->validatorMock = $this->createMock(RedisConnectionValidator::class); @@ -122,11 +125,6 @@ public function testCreateConfigWithRedisConfig() $this->assertEquals($expectedConfigData, $configData->getData()); } -<<<<<<< HEAD -======= - /** - * testCreateConfigCacheRedis - */ public function testCreateConfigWithFileCache() { $this->deploymentConfigMock->method('get')->willReturn(''); @@ -146,9 +144,6 @@ public function testCreateConfigWithFileCache() $this->assertEquals($expectedConfigData, $configData->getData()); } - /** - * testCreateConfigCacheRedis - */ public function testCreateConfigWithIdPrefix() { $this->deploymentConfigMock->method('get')->willReturn(''); @@ -172,10 +167,6 @@ public function testCreateConfigWithIdPrefix() $this->assertEquals($expectedConfigData, $configData->getData()); } - /** - * testValidateWithValidInput - */ ->>>>>>> 12b7e08c2f26... Set cache id prefix on installation public function testValidateWithValidInput() { $options = [ 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 5bac79e4cde79..0e7c851cb706b 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsList/PageCacheTest.php @@ -28,6 +28,9 @@ class PageCacheTest extends \PHPUnit\Framework\TestCase */ private $deploymentConfigMock; + /** + * @inheritdoc + */ protected function setUp() { $this->validatorMock = $this->createMock(RedisConnectionValidator::class, [], [], '', false); @@ -130,11 +133,6 @@ public function testCreateConfigWithRedisConfiguration() $this->assertEquals($expectedConfigData, $configData->getData()); } -<<<<<<< HEAD -======= - /** - * testCreateConfigWithRedis - */ public function testCreateConfigWithFileCache() { $this->deploymentConfigMock->method('get')->willReturn(''); @@ -154,10 +152,6 @@ public function testCreateConfigWithFileCache() $this->assertEquals($expectedConfigData, $configData->getData()); } - - /** - * testCreateConfigCacheRedis - */ public function testCreateConfigWithIdPrefix() { $this->deploymentConfigMock->method('get')->willReturn(''); @@ -181,10 +175,6 @@ public function testCreateConfigWithIdPrefix() $this->assertEquals($expectedConfigData, $configData->getData()); } - /** - * testValidationWithValidData - */ ->>>>>>> 12b7e08c2f26... Set cache id prefix on installation public function testValidationWithValidData() { $this->validatorMock->expects($this->once()) From 0aa6af2aa3c8a1c4807d33783b19beb7275101c6 Mon Sep 17 00:00:00 2001 From: niravkrish <nirav.patel@krishtechnolabs.com> Date: Tue, 9 Apr 2019 12:10:12 +0530 Subject: [PATCH 1170/1295] Fixed - Admin Order Create Full tax summary calculation missing --- .../adminhtml/templates/order/create/totals/tax.phtml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/create/totals/tax.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/create/totals/tax.phtml index 92139896273da..643146f7bb5cb 100755 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/create/totals/tax.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/create/totals/tax.phtml @@ -33,7 +33,6 @@ $taxAmount = $block->getTotal()->getValue(); <?php $percent = $info['percent']; ?> <?php $amount = $info['amount']; ?> <?php $rates = $info['rates']; ?> - <?php $isFirst = 1; ?> <?php foreach ($rates as $rate): ?> <tr class="summary-details-<?= /* @escapeNotVerified */ $taxIter ?> summary-details<?php if ($isTop): echo ' summary-details-first'; endif; ?>" style="display:none;"> @@ -44,13 +43,10 @@ $taxAmount = $block->getTotal()->getValue(); <?php endif; ?> <br /> </td> - <?php if ($isFirst): ?> - <td style="<?= /* @escapeNotVerified */ $block->getTotal()->getStyle() ?>" class="admin__total-amount" rowspan="<?= count($rates) ?>"> - <?= /* @escapeNotVerified */ $block->formatPrice($amount) ?> - </td> - <?php endif; ?> + <td style="<?= /* @escapeNotVerified */ $block->getTotal()->getStyle() ?>" class="admin__total-amount"> + <?= /* @escapeNotVerified */ $block->formatPrice(($amount*(float)$rate['percent'])/$percent) ?> + </td> </tr> - <?php $isFirst = 0; ?> <?php $isTop = 0; ?> <?php endforeach; ?> <?php endforeach; ?> From 8136b52e41ff5c3a505c3757293bb57da1acad9a Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Tue, 23 Apr 2019 16:09:54 -0500 Subject: [PATCH 1171/1295] MAGETWO-99247: Customizable Option Price input is not saved on Store View level when Catalog Price Scope set to Global --- .../Catalog/Model/ResourceModel/Product/Option/Value.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php index 0362df7a846bb..3927b37016c1e 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php @@ -139,8 +139,7 @@ protected function _saveValuePrices(AbstractModel $object) $optionTypeId = $this->getConnection()->fetchOne($select); if ($optionTypeId) { - if ($object->getStoreId() == '0' //|| $this->dataHelper->isPriceGlobal() - ) { + if ($object->getStoreId() == '0' || $this->dataHelper->isPriceGlobal()) { $bind = ['price' => $price, 'price_type' => $priceType]; $where = [ 'option_type_id = ?' => $optionTypeId, From 9aff2a9aafea39288564699e38e1715029342f28 Mon Sep 17 00:00:00 2001 From: Ravi Chandra <ravi.chandra@krishtechnolabs.com> Date: Wed, 24 Apr 2019 10:30:55 +0530 Subject: [PATCH 1172/1295] Fixed Changing sample for downloadable product failure --- app/code/Magento/Downloadable/Model/SampleRepository.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Downloadable/Model/SampleRepository.php b/app/code/Magento/Downloadable/Model/SampleRepository.php index 5b9e8e784b9f3..00f653e87b5ae 100644 --- a/app/code/Magento/Downloadable/Model/SampleRepository.php +++ b/app/code/Magento/Downloadable/Model/SampleRepository.php @@ -299,8 +299,11 @@ protected function updateSample( $existingSample->setTitle($sample->getTitle()); } - if ($sample->getSampleType() === 'file' && $sample->getSampleFileContent() === null) { - $sample->setSampleFile($existingSample->getSampleFile()); + if ($sample->getSampleType() === 'file' + && $sample->getSampleFileContent() === null + && $sample->getSampleFile() !== null + ) { + $existingSample->setSampleFile($sample->getSampleFile()); } $this->saveSample($product, $sample, $isGlobalScopeContent); return $existingSample->getId(); From 5ad4ab633146d3e4e88223db58a8ff50083c4084 Mon Sep 17 00:00:00 2001 From: niravkrish <nirav.patel@krishtechnolabs.com> Date: Wed, 24 Apr 2019 11:54:38 +0530 Subject: [PATCH 1173/1295] Backport - Qty box visibility issue in wishlist when product is out of stock --- .../Wishlist/view/frontend/templates/item/column/cart.phtml | 2 +- .../Wishlist/view/frontend/templates/item/column/comment.phtml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml index 4787ee04860e3..434bd57a2087f 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/cart.phtml @@ -25,7 +25,7 @@ $allowedQty = $viewModel->setItem($item)->getMinMaxQty(); <label class="label" for="qty[<?= $block->escapeHtmlAttr($item->getId()) ?>]"><span><?= $block->escapeHtml(__('Qty')) ?></span></label> <div class="control"> <input type="number" data-role="qty" id="qty[<?= $block->escapeHtmlAttr($item->getId()) ?>]" class="input-text qty" data-validate="{'required-number':true,'validate-greater-than-zero':true,'validate-item-quantity':{'minAllowed':<?= /* @noEscape */ $allowedQty['minAllowed'] ?>,'maxAllowed':<?= /* @noEscape */ $allowedQty['maxAllowed'] ?>}}" - name="qty[<?= $block->escapeHtmlAttr($item->getId()) ?>]" value="<?= /* @noEscape */ (int)($block->getAddToCartQty($item) * 1) ?>"> + name="qty[<?= $block->escapeHtmlAttr($item->getId()) ?>]" value="<?= /* @noEscape */ (int)($block->getAddToCartQty($item) * 1) ?>" <?= $product->isSaleable() ? '' : 'disabled="disabled"' ?>> </div> </div> <?php endif; ?> diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/column/comment.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/column/comment.phtml index 17e2404ee23cf..5ab5bc5422e7e 100644 --- a/app/code/Magento/Wishlist/view/frontend/templates/item/column/comment.phtml +++ b/app/code/Magento/Wishlist/view/frontend/templates/item/column/comment.phtml @@ -17,6 +17,6 @@ $item = $block->getItem(); <span><?= $block->escapeHtml(__('Comment')) ?></span> </label> <div class="control"> - <textarea id="product-item-comment-<?= $block->escapeHtmlAttr($item->getWishlistItemId()) ?>" placeholder="<?= /* @noEscape */ $this->helper('Magento\Wishlist\Helper\Data')->defaultCommentString() ?>" name="description[<?= $block->escapeHtmlAttr($item->getWishlistItemId()) ?>]" title="<?= $block->escapeHtmlAttr(__('Comment')) ?>" class="product-item-comment"><?= ($block->escapeHtml($item->getDescription())) ?></textarea> + <textarea id="product-item-comment-<?= $block->escapeHtmlAttr($item->getWishlistItemId()) ?>" placeholder="<?= /* @noEscape */ $this->helper('Magento\Wishlist\Helper\Data')->defaultCommentString() ?>" name="description[<?= $block->escapeHtmlAttr($item->getWishlistItemId()) ?>]" title="<?= $block->escapeHtmlAttr(__('Comment')) ?>" class="product-item-comment" <?= $item->getProduct()->isSaleable() ? '' : 'disabled="disabled"' ?>><?= ($block->escapeHtml($item->getDescription())) ?></textarea> </div> </div> From d4b7eda00c2cdf144baf35c42379efdc8d66d0a9 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Wed, 24 Apr 2019 17:16:54 +0300 Subject: [PATCH 1174/1295] MAGETWO-98899: Special Prices cannot save over 4 characters in Japanese Yen - The comma separator seems to be the issue --- .../Backend/TierPrice/UpdateHandler.php | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php index 500e59f26a2c3..bda92fbeab9ed 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php @@ -5,8 +5,9 @@ */ namespace Magento\Catalog\Model\Product\Attribute\Backend\TierPrice; -use Magento\Framework\EntityManager\Operation\ExtensionInterface; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Locale\FormatInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\Catalog\Api\ProductAttributeRepositoryInterface; use Magento\Customer\Api\GroupManagementInterface; @@ -38,19 +39,26 @@ class UpdateHandler extends AbstractHandler */ private $tierPriceResource; + /** + * @var FormatInterface + */ + private $localeFormat; + /** * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository * @param \Magento\Customer\Api\GroupManagementInterface $groupManagement * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool * @param \Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice $tierPriceResource + * @param FormatInterface|null $localeFormat */ public function __construct( StoreManagerInterface $storeManager, ProductAttributeRepositoryInterface $attributeRepository, GroupManagementInterface $groupManagement, MetadataPool $metadataPool, - Tierprice $tierPriceResource + Tierprice $tierPriceResource, + FormatInterface $localeFormat = null ) { parent::__construct($groupManagement); @@ -58,6 +66,7 @@ public function __construct( $this->attributeRepository = $attributeRepository; $this->metadataPoll = $metadataPool; $this->tierPriceResource = $tierPriceResource; + $this->localeFormat = $localeFormat ?: ObjectManager::getInstance()->get(FormatInterface::class); } /** @@ -116,8 +125,9 @@ private function updateValues(array $valuesToUpdate, array $oldValues): bool { $isChanged = false; foreach ($valuesToUpdate as $key => $value) { - if ((!empty($value['value']) && (float)$oldValues[$key]['price'] !== (float)$value['value']) - || $this->getPercentage($oldValues[$key]) !== $this->getPercentage($value) + if ((!empty($value['value']) + && (float)$oldValues[$key]['price'] !== $this->localeFormat->getNumber($value['value']) + ) || $this->getPercentage($oldValues[$key]) !== $this->getPercentage($value) ) { $price = new \Magento\Framework\DataObject( [ From 2587afc129f514721140a6015a105b30f7320bb7 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Wed, 24 Apr 2019 10:44:55 -0500 Subject: [PATCH 1175/1295] MC-15952: [2.2] S2 build failed when upgrading from 2.0 to 2.2 Revert "MC-15097: Incorrect Prolong responses" This reverts commit d5e40099b688e1265a1281df2f0c54d10128fbbf. --- .../src/Magento/Setup/Controller/Session.php | 50 ++++++--------- .../Test/Unit/Controller/SessionTest.php | 62 +++++-------------- 2 files changed, 34 insertions(+), 78 deletions(-) diff --git a/setup/src/Magento/Setup/Controller/Session.php b/setup/src/Magento/Setup/Controller/Session.php index adcbe1f5e0c49..e310dd485ace5 100644 --- a/setup/src/Magento/Setup/Controller/Session.php +++ b/setup/src/Magento/Setup/Controller/Session.php @@ -5,9 +5,6 @@ */ namespace Magento\Setup\Controller; -/** - * Sets up session for setup/index.php/session/prolong or redirects to error page. - */ class Session extends \Zend\Mvc\Controller\AbstractActionController { /** @@ -33,7 +30,7 @@ public function __construct( } /** - * No index action, return 404 error page. + * No index action, return 404 error page * * @return \Zend\View\Model\ViewModel|\Zend\Http\Response */ @@ -42,12 +39,11 @@ public function indexAction() $view = new \Zend\View\Model\ViewModel(); $view->setTemplate('/error/404.phtml'); $this->getResponse()->setStatusCode(\Zend\Http\Response::STATUS_CODE_404); - return $view; } /** - * Prolong session. + * Prolong session * * @return string */ @@ -56,41 +52,32 @@ public function prolongAction() try { if ($this->serviceManager->get(\Magento\Framework\App\DeploymentConfig::class)->isAvailable()) { $objectManager = $this->objectManagerProvider->get(); + /** @var \Magento\Framework\App\State $adminAppState */ + $adminAppState = $objectManager->get(\Magento\Framework\App\State::class); + $adminAppState->setAreaCode(\Magento\Framework\App\Area::AREA_ADMINHTML); + $sessionConfig = $objectManager->get(\Magento\Backend\Model\Session\AdminConfig::class); + /** @var \Magento\Backend\Model\Url $backendUrl */ + $backendUrl = $objectManager->get(\Magento\Backend\Model\Url::class); + $urlPath = parse_url($backendUrl->getBaseUrl(), PHP_URL_PATH); + $cookiePath = $urlPath . 'setup'; + $sessionConfig->setCookiePath($cookiePath); /* @var \Magento\Backend\Model\Auth\Session $session */ - $session = $objectManager->get(\Magento\Backend\Model\Auth\Session::class); - // check if session was already set in \Magento\Setup\Mvc\Bootstrap\InitParamListener::authPreDispatch - if (!$session->isSessionExists()) { - /** @var \Magento\Framework\App\State $adminAppState */ - $adminAppState = $objectManager->get(\Magento\Framework\App\State::class); - $adminAppState->setAreaCode(\Magento\Framework\App\Area::AREA_ADMINHTML); - $sessionConfig = $objectManager->get(\Magento\Backend\Model\Session\AdminConfig::class); - /** @var \Magento\Backend\Model\Url $backendUrl */ - $backendUrl = $objectManager->get(\Magento\Backend\Model\Url::class); - $urlPath = parse_url($backendUrl->getBaseUrl(), PHP_URL_PATH); - $cookiePath = $urlPath . 'setup'; - $sessionConfig->setCookiePath($cookiePath); - /* @var \Magento\Backend\Model\Auth\Session $session */ - $session = $objectManager->create( - \Magento\Backend\Model\Auth\Session::class, - [ - 'sessionConfig' => $sessionConfig, - 'appState' => $adminAppState - ] - ); - } + $session = $objectManager->create( + \Magento\Backend\Model\Auth\Session::class, + [ + 'sessionConfig' => $sessionConfig, + 'appState' => $adminAppState + ] + ); $session->prolong(); - return new \Zend\View\Model\JsonModel(['success' => true]); } } catch (\Exception $e) { } - return new \Zend\View\Model\JsonModel(['success' => false]); } /** - * Unlogin action, return 401 error page. - * * @return \Zend\View\Model\ViewModel|\Zend\Http\Response */ public function unloginAction() @@ -98,7 +85,6 @@ public function unloginAction() $view = new \Zend\View\Model\ViewModel(); $view->setTemplate('/error/401.phtml'); $this->getResponse()->setStatusCode(\Zend\Http\Response::STATUS_CODE_401); - return $view; } } diff --git a/setup/src/Magento/Setup/Test/Unit/Controller/SessionTest.php b/setup/src/Magento/Setup/Test/Unit/Controller/SessionTest.php index 18d299cec37a1..f8e5e7cdc4d70 100644 --- a/setup/src/Magento/Setup/Test/Unit/Controller/SessionTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Controller/SessionTest.php @@ -8,9 +8,6 @@ use \Magento\Setup\Controller\Session; -/** - * Unit test for \Magento\Setup\Controller\Session. - */ class SessionTest extends \PHPUnit\Framework\TestCase { /** @@ -19,18 +16,15 @@ class SessionTest extends \PHPUnit\Framework\TestCase private $objectManager; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Setup\Model\ObjectManagerProvider + * @var \PHPUnit_Framework_MockObject_MockObject| \Magento\Setup\Model\ObjectManagerProvider */ private $objectManagerProvider; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Zend\ServiceManager\ServiceManager + * @var \Zend\ServiceManager\ServiceManager */ private $serviceManager; - /** - * @inheritdoc - */ public function setUp() { $objectManager = @@ -47,40 +41,37 @@ public function setUp() */ public function testUnloginAction() { - $this->objectManagerProvider->expects($this->once())->method('get')->willReturn($this->objectManager); - $deployConfigMock = $this->createPartialMock(\Magento\Framework\App\DeploymentConfig::class, ['isAvailable']); - $deployConfigMock->expects($this->once())->method('isAvailable')->willReturn(true); - - $sessionMock = $this->createPartialMock( - \Magento\Backend\Model\Auth\Session::class, - ['prolong', 'isSessionExists'] + $this->objectManagerProvider->expects($this->once())->method('get')->will( + $this->returnValue($this->objectManager) ); - $sessionMock->expects($this->once())->method('isSessionExists')->willReturn(false); + $deployConfigMock = + $this->createPartialMock(\Magento\Framework\App\DeploymentConfig::class, ['isAvailable']); + $deployConfigMock->expects($this->once())->method('isAvailable')->will($this->returnValue(true)); $stateMock = $this->createPartialMock(\Magento\Framework\App\State::class, ['setAreaCode']); $stateMock->expects($this->once())->method('setAreaCode'); - $sessionConfigMock = $this->createPartialMock( - \Magento\Backend\Model\Session\AdminConfig::class, - ['setCookiePath'] - ); + $sessionConfigMock = + $this->createPartialMock(\Magento\Backend\Model\Session\AdminConfig::class, ['setCookiePath']); $sessionConfigMock->expects($this->once())->method('setCookiePath'); $urlMock = $this->createMock(\Magento\Backend\Model\Url::class); $returnValueMap = [ - [\Magento\Backend\Model\Auth\Session::class, $sessionMock], [\Magento\Framework\App\State::class, $stateMock], [\Magento\Backend\Model\Session\AdminConfig::class, $sessionConfigMock], - [\Magento\Backend\Model\Url::class, $urlMock], + [\Magento\Backend\Model\Url::class, $urlMock] ]; - $this->serviceManager->expects($this->once())->method('get')->willReturn($deployConfigMock); + $this->serviceManager->expects($this->once())->method('get')->will($this->returnValue($deployConfigMock)); $this->objectManager->expects($this->atLeastOnce()) ->method('get') - ->willReturnMap($returnValueMap); + ->will($this->returnValueMap($returnValueMap)); - $this->objectManager->expects($this->once())->method('create')->willReturn($sessionMock); + $sessionMock = $this->createPartialMock(\Magento\Backend\Model\Auth\Session::class, ['prolong']); + $this->objectManager->expects($this->once()) + ->method('create') + ->will($this->returnValue($sessionMock)); $controller = new Session($this->serviceManager, $this->objectManagerProvider); $urlMock->expects($this->once())->method('getBaseUrl'); $controller->prolongAction(); @@ -96,25 +87,4 @@ public function testIndexAction() $viewModel = $controller->unloginAction(); $this->assertInstanceOf(\Zend\View\Model\ViewModel::class, $viewModel); } - - /** - * @covers \Magento\Setup\Controller\SystemConfig::prolongAction - */ - public function testProlongActionWithExistingSession() - { - $this->objectManagerProvider->expects($this->once())->method('get')->willReturn($this->objectManager); - $deployConfigMock = $this->createPartialMock(\Magento\Framework\App\DeploymentConfig::class, ['isAvailable']); - $deployConfigMock->expects($this->once())->method('isAvailable')->willReturn(true); - $sessionMock = $this->createPartialMock( - \Magento\Backend\Model\Auth\Session::class, - ['prolong', 'isSessionExists'] - ); - $sessionMock->expects($this->once())->method('isSessionExists')->willReturn(true); - - $this->serviceManager->expects($this->once())->method('get')->willReturn($deployConfigMock); - $this->objectManager->expects($this->once())->method('get')->willReturn($sessionMock); - - $controller = new Session($this->serviceManager, $this->objectManagerProvider); - $this->assertEquals(new \Zend\View\Model\JsonModel(['success' => true]), $controller->prolongAction()); - } } From 03db5583c67ea4f7fc685dd4dd48672fb326c65c Mon Sep 17 00:00:00 2001 From: Nazarn96 <nazarn96@gmail.com> Date: Thu, 25 Apr 2019 11:02:47 +0300 Subject: [PATCH 1176/1295] api-functional test fix --- .../Magento/Downloadable/Api/ProductRepositoryTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php index 9682c4b2ee603..39a64c94067aa 100644 --- a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php @@ -472,6 +472,10 @@ public function testUpdateDownloadableProductSamplesWithNewFile() 'title' => 'sample2_updated', 'sort_order' => 2, 'sample_type' => 'file', + 'sample_file_content' => [ + 'name' => 'sample2.jpg', + 'file_data' => base64_encode(file_get_contents($this->testImagePath)), + ], ]; $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"] = From 080b08aa5e38d3d11392d3b764d0035a5d70479f Mon Sep 17 00:00:00 2001 From: Roman Lytvynenko <lytvynen@adobe.com> Date: Thu, 25 Apr 2019 09:57:10 -0500 Subject: [PATCH 1177/1295] MAGETWO-98462: Shopping cart does not show all four digits of quantity --- .../luma/Magento_Checkout/web/css/source/module/_cart.less | 2 +- .../luma/Magento_Checkout/web/css/source/module/_minicart.less | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 3fef5b303d718..20d4f4986b611 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 @@ -219,7 +219,7 @@ height: 36px; margin-top: -7px; text-align: center; - width: 45px; + width: 60px; } } diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less index 05ce84995afae..510d5ae507bdd 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less @@ -352,7 +352,7 @@ .item-qty { margin-right: @indent__s; text-align: center; - width: 45px; + width: 60px; } .update-cart-item { From c5dca37b7ec1e8102d2939f5623d413b8b6421bc Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Thu, 25 Apr 2019 13:55:01 -0500 Subject: [PATCH 1178/1295] MAGETWO-99416: Empty email in quote prevent order creation even if payment success - Added order address validation before order placing - Added logging of failed order saving attempts --- .../Magento/Quote/Model/QuoteManagement.php | 17 +++-- .../Quote/Model/SubmitQuoteValidator.php | 71 +++++++++++++++++++ .../Test/Unit/Model/QuoteManagementTest.php | 27 ++++--- .../Sales/Model/Service/OrderService.php | 38 ++++++---- .../Unit/Model/Service/OrderServiceTest.php | 13 +++- .../Fixtures/quote_without_customer_email.php | 28 ++++++++ .../Quote/Model/QuoteManagementTest.php | 29 ++++++++ 7 files changed, 195 insertions(+), 28 deletions(-) create mode 100644 app/code/Magento/Quote/Model/SubmitQuoteValidator.php create mode 100644 dev/tests/integration/testsuite/Magento/Quote/Fixtures/quote_without_customer_email.php diff --git a/app/code/Magento/Quote/Model/QuoteManagement.php b/app/code/Magento/Quote/Model/QuoteManagement.php index b7d994138026a..79170ad90832c 100644 --- a/app/code/Magento/Quote/Model/QuoteManagement.php +++ b/app/code/Magento/Quote/Model/QuoteManagement.php @@ -37,9 +37,9 @@ class QuoteManagement implements \Magento\Quote\Api\CartManagementInterface protected $eventManager; /** - * @var QuoteValidator + * @var SubmitQuoteValidator */ - protected $quoteValidator; + private $submitQuoteValidator; /** * @var OrderFactory @@ -148,7 +148,7 @@ class QuoteManagement implements \Magento\Quote\Api\CartManagementInterface /** * @param EventManager $eventManager - * @param QuoteValidator $quoteValidator + * @param SubmitQuoteValidator $submitQuoteValidator * @param OrderFactory $orderFactory * @param OrderManagement $orderManagement * @param CustomerManagement $customerManagement @@ -173,7 +173,7 @@ class QuoteManagement implements \Magento\Quote\Api\CartManagementInterface */ public function __construct( EventManager $eventManager, - QuoteValidator $quoteValidator, + SubmitQuoteValidator $submitQuoteValidator, OrderFactory $orderFactory, OrderManagement $orderManagement, CustomerManagement $customerManagement, @@ -196,7 +196,7 @@ public function __construct( \Magento\Customer\Api\AddressRepositoryInterface $addressRepository = null ) { $this->eventManager = $eventManager; - $this->quoteValidator = $quoteValidator; + $this->submitQuoteValidator = $submitQuoteValidator; $this->orderFactory = $orderFactory; $this->orderManagement = $orderManagement; $this->customerManagement = $customerManagement; @@ -282,6 +282,7 @@ public function assignCustomer($cartId, $customerId, $storeId) throw new StateException( __('Cannot assign customer to the given cart. Customer already has active cart.') ); + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { } @@ -454,7 +455,7 @@ protected function resolveItems(QuoteEntity $quote) protected function submitQuote(QuoteEntity $quote, $orderData = []) { $order = $this->orderFactory->create(); - $this->quoteValidator->validateBeforeSubmit($quote); + $this->submitQuoteValidator->validateQuote($quote); if (!$quote->getCustomerIsGuest()) { if ($quote->getCustomerId()) { $this->_prepareCustomerQuote($quote); @@ -509,6 +510,7 @@ protected function submitQuote(QuoteEntity $quote, $orderData = []) $order->setCustomerFirstname($quote->getCustomerFirstname()); $order->setCustomerMiddlename($quote->getCustomerMiddlename()); $order->setCustomerLastname($quote->getCustomerLastname()); + $this->submitQuoteValidator->validateOrder($order); $this->eventManager->dispatch( 'sales_model_service_quote_submit_before', @@ -625,12 +627,13 @@ private function rollbackAddresses( 'exception' => $e, ] ); + // phpcs:ignore Magento2.Exceptions.ThrowCatch } catch (\Exception $consecutiveException) { $message = sprintf( "An exception occurred on 'sales_model_service_quote_submit_failure' event: %s", $consecutiveException->getMessage() ); - + // phpcs:ignore Magento2.Exceptions.DirectThrow throw new \Exception($message, 0, $e); } } diff --git a/app/code/Magento/Quote/Model/SubmitQuoteValidator.php b/app/code/Magento/Quote/Model/SubmitQuoteValidator.php new file mode 100644 index 0000000000000..76d31f94d2a62 --- /dev/null +++ b/app/code/Magento/Quote/Model/SubmitQuoteValidator.php @@ -0,0 +1,71 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Quote\Model; + +use Magento\Framework\Exception\LocalizedException; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Address\Validator as OrderAddressValidator; + +/** + * Validates quote and order before quote submit. + */ +class SubmitQuoteValidator +{ + /** + * @var QuoteValidator + */ + private $quoteValidator; + + /** + * @var OrderAddressValidator + */ + private $orderAddressValidator; + + /** + * @param QuoteValidator $quoteValidator + * @param OrderAddressValidator $orderAddressValidator + */ + public function __construct( + QuoteValidator $quoteValidator, + OrderAddressValidator $orderAddressValidator + ) { + $this->quoteValidator = $quoteValidator; + $this->orderAddressValidator = $orderAddressValidator; + } + + /** + * Validates quote. + * + * @param Quote $quote + * @return void + * @throws LocalizedException + */ + public function validateQuote(Quote $quote) + { + $this->quoteValidator->validateBeforeSubmit($quote); + } + + /** + * Validates order. + * + * @param Order $order + * @return void + * @throws LocalizedException + */ + public function validateOrder(Order $order) + { + foreach ($order->getAddresses() as $address) { + $errors = $this->orderAddressValidator->validate($address); + if (!empty($errors)) { + throw new LocalizedException( + __("Failed address validation:\n%1", implode("\n", $errors)) + ); + } + } + } +} diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php index cdfd0df8f9927..aca490ae2b1a1 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php @@ -13,6 +13,8 @@ /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.TooManyFields) + * @SuppressWarnings(PHPMD.TooManyPublicMethods) + * @SuppressWarnings(PHPMD.ExcessiveClassLength) */ class QuoteManagementTest extends \PHPUnit\Framework\TestCase { @@ -22,9 +24,9 @@ class QuoteManagementTest extends \PHPUnit\Framework\TestCase protected $model; /** - * @var \Magento\Quote\Model\QuoteValidator|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Quote\Model\SubmitQuoteValidator|\PHPUnit_Framework_MockObject_MockObject */ - protected $quoteValidator; + protected $submitQuoteValidator; /** * @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject @@ -143,7 +145,7 @@ protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->quoteValidator = $this->createMock(\Magento\Quote\Model\QuoteValidator::class); + $this->submitQuoteValidator = $this->createMock(\Magento\Quote\Model\SubmitQuoteValidator::class); $this->eventManager = $this->getMockForAbstractClass(\Magento\Framework\Event\ManagerInterface::class); $this->orderFactory = $this->createPartialMock( \Magento\Sales\Api\Data\OrderInterfaceFactory::class, @@ -210,7 +212,7 @@ protected function setUp() \Magento\Quote\Model\QuoteManagement::class, [ 'eventManager' => $this->eventManager, - 'quoteValidator' => $this->quoteValidator, + 'submitQuoteValidator' => $this->submitQuoteValidator, 'orderFactory' => $this->orderFactory, 'orderManagement' => $this->orderManagement, 'customerManagement' => $this->customerManagement, @@ -560,7 +562,9 @@ public function testSubmit() $shippingAddress ); - $this->quoteValidator->expects($this->once())->method('validateBeforeSubmit')->with($quote); + $this->submitQuoteValidator->expects($this->once()) + ->method('validateQuote') + ->with($quote); $this->quoteAddressToOrder->expects($this->once()) ->method('convert') ->with($shippingAddress, $orderData) @@ -655,7 +659,7 @@ public function testPlaceOrderIfCustomerIsGuest() ->setConstructorArgs( [ 'eventManager' => $this->eventManager, - 'quoteValidator' => $this->quoteValidator, + 'quoteValidator' => $this->submitQuoteValidator, 'orderFactory' => $this->orderFactory, 'orderManagement' => $this->orderManagement, 'customerManagement' => $this->customerManagement, @@ -712,7 +716,7 @@ public function testPlaceOrder() ->setConstructorArgs( [ 'eventManager' => $this->eventManager, - 'quoteValidator' => $this->quoteValidator, + 'quoteValidator' => $this->submitQuoteValidator, 'orderFactory' => $this->orderFactory, 'orderManagement' => $this->orderManagement, 'customerManagement' => $this->customerManagement, @@ -934,6 +938,9 @@ protected function prepareOrderFactory( return $order; } + /** + * @throws NoSuchEntityException + */ public function testGetCartForCustomer() { $customerId = 100; @@ -978,6 +985,9 @@ protected function setPropertyValue(&$object, $property, $value) return $object; } + /** + * @throws \Magento\Framework\Exception\LocalizedException + */ public function testSubmitForCustomer() { $orderData = []; @@ -1010,7 +1020,8 @@ public function testSubmitForCustomer() $shippingAddress ); - $this->quoteValidator->expects($this->once())->method('validateBeforeSubmit')->with($quote); + $this->submitQuoteValidator->method('validateQuote') + ->with($quote); $this->quoteAddressToOrder->expects($this->once()) ->method('convert') ->with($shippingAddress, $orderData) diff --git a/app/code/Magento/Sales/Model/Service/OrderService.php b/app/code/Magento/Sales/Model/Service/OrderService.php index e4a71f028cc82..2e062caca9a24 100644 --- a/app/code/Magento/Sales/Model/Service/OrderService.php +++ b/app/code/Magento/Sales/Model/Service/OrderService.php @@ -7,6 +7,7 @@ use Magento\Sales\Api\OrderManagementInterface; use Magento\Payment\Gateway\Command\CommandException; +use Psr\Log\LoggerInterface; /** * Class OrderService @@ -55,6 +56,11 @@ class OrderService implements OrderManagementInterface */ private $paymentFailures; + /** + * @var LoggerInterface + */ + private $logger; + /** * Constructor * @@ -65,7 +71,8 @@ class OrderService implements OrderManagementInterface * @param \Magento\Sales\Model\OrderNotifier $notifier * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param \Magento\Sales\Model\Order\Email\Sender\OrderCommentSender $orderCommentSender - * @param \Magento\Sales\Api\PaymentFailuresInterface|null $paymentFailures + * @param \Magento\Sales\Api\PaymentFailuresInterface $paymentFailures + * @param LoggerInterface $logger */ public function __construct( \Magento\Sales\Api\OrderRepositoryInterface $orderRepository, @@ -75,7 +82,8 @@ public function __construct( \Magento\Sales\Model\OrderNotifier $notifier, \Magento\Framework\Event\ManagerInterface $eventManager, \Magento\Sales\Model\Order\Email\Sender\OrderCommentSender $orderCommentSender, - \Magento\Sales\Api\PaymentFailuresInterface $paymentFailures = null + \Magento\Sales\Api\PaymentFailuresInterface $paymentFailures, + LoggerInterface $logger ) { $this->orderRepository = $orderRepository; $this->historyRepository = $historyRepository; @@ -84,8 +92,8 @@ public function __construct( $this->notifier = $notifier; $this->eventManager = $eventManager; $this->orderCommentSender = $orderCommentSender; - $this->paymentFailures = $paymentFailures ? : \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Sales\Api\PaymentFailuresInterface::class); + $this->paymentFailures = $paymentFailures; + $this->logger = $logger; } /** @@ -189,25 +197,31 @@ public function unHold($id) } /** + * Perform place order. + * * @param \Magento\Sales\Api\Data\OrderInterface $order * @return \Magento\Sales\Api\Data\OrderInterface * @throws \Exception */ public function place(\Magento\Sales\Api\Data\OrderInterface $order) { - // transaction will be here - //begin transaction try { $order->place(); - return $this->orderRepository->save($order); - //commit + } catch (CommandException $e) { + $this->paymentFailures->handle((int)$order->getQuoteId(), __($e->getMessage())); + throw $e; + } + + try { + $order = $this->orderRepository->save($order); } catch (\Exception $e) { - if ($e instanceof CommandException) { - $this->paymentFailures->handle((int)$order->getQuoteId(), __($e->getMessage())); - } + $this->logger->critical( + 'Saving order ' . $order->getIncrementId() . ' failed: ' . $e->getMessage() + ); throw $e; - //rollback; } + + return $order; } /** diff --git a/app/code/Magento/Sales/Test/Unit/Model/Service/OrderServiceTest.php b/app/code/Magento/Sales/Test/Unit/Model/Service/OrderServiceTest.php index 067f83d1e5b32..72fe7380ce8e4 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Service/OrderServiceTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Service/OrderServiceTest.php @@ -5,6 +5,9 @@ */ namespace Magento\Sales\Test\Unit\Model\Service; +use Magento\Sales\Api\PaymentFailuresInterface; +use Psr\Log\LoggerInterface; + /** * Class OrderUnHoldTest * @@ -140,6 +143,12 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); + /** @var PaymentFailuresInterface|\PHPUnit_Framework_MockObject_MockObject $paymentFailures */ + $paymentFailures = $this->createMock(PaymentFailuresInterface::class); + + /** @var LoggerInterface|\PHPUnit_Framework_MockObject_MockObject $logger */ + $logger = $this->createMock(LoggerInterface::class); + $this->orderService = new \Magento\Sales\Model\Service\OrderService( $this->orderRepositoryMock, $this->orderStatusHistoryRepositoryMock, @@ -147,7 +156,9 @@ protected function setUp() $this->filterBuilderMock, $this->orderNotifierMock, $this->eventManagerMock, - $this->orderCommentSender + $this->orderCommentSender, + $paymentFailures, + $logger ); } diff --git a/dev/tests/integration/testsuite/Magento/Quote/Fixtures/quote_without_customer_email.php b/dev/tests/integration/testsuite/Magento/Quote/Fixtures/quote_without_customer_email.php new file mode 100644 index 0000000000000..49fd11593c798 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Quote/Fixtures/quote_without_customer_email.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; + +require __DIR__ . '/../../Sales/_files/quote_with_customer.php'; + +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +$quote->getPayment() + ->setMethod('checkmo'); +$quote->getShippingAddress() + ->setShippingMethod('flatrate_flatrate') + ->setCollectShippingRates(true); +$quote->collectTotals(); + +$quote->setCustomerEmail(''); + +/** @var CartRepositoryInterface $repository */ +$repository = $objectManager->get(CartRepositoryInterface::class); +$repository->save($quote); diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php index 356117f2b3dc8..dc784aa55efc4 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php @@ -12,9 +12,11 @@ use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Quote\Api\CartManagementInterface; use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Sales\Api\OrderManagementInterface; use Magento\Sales\Api\OrderRepositoryInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\ExpectationFailedException; /** * Class for testing QuoteManagement model @@ -101,6 +103,33 @@ public function testSubmitWithItemOutOfStock() $this->cartManagement->placeOrder($quote->getId()); } + /** + * Tries to create an order using quote with empty customer email. + * + * Order should not start placing if order validation is failed. + * + * @magentoDataFixture Magento/Quote/Fixtures/quote_without_customer_email.php + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Email has a wrong format + */ + public function testSubmitWithEmptyCustomerEmail() + { + $quote = $this->getQuote('test01'); + $orderManagement = $this->createMock(OrderManagementInterface::class); + $orderManagement->expects($this->never()) + ->method('place'); + $cartManagement = $this->objectManager->create( + CartManagementInterface::class, + ['orderManagement' => $orderManagement] + ); + + try { + $cartManagement->placeOrder($quote->getId()); + } catch (ExpectationFailedException $e) { + $this->fail('Place order method was not expected to be called if order validation is failed'); + } + } + /** * Gets quote by reserved order ID. * From 932506ed491a13ae6f7e3ce0a024f055f4c39438 Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@adobe.com> Date: Wed, 6 Feb 2019 12:30:50 -0600 Subject: [PATCH 1179/1295] MQE-1430: bug fix in dev/tests/functional/utils/command.php etc (cherry picked from commit e66aee3) --- .../lib/Magento/Mtf/Util/Command/Cli.php | 41 ++++++++---- .../Mtf/Util/Command/File/Export/Reader.php | 38 ++++++++--- .../Command/File/Export/ReaderInterface.php | 2 +- .../lib/Magento/Mtf/Util/Command/File/Log.php | 38 +++++++---- .../Mtf/Util/Command/GeneratedCode.php | 39 +++++++++-- .../lib/Magento/Mtf/Util/Command/Locales.php | 42 +++++++++--- .../Magento/Mtf/Util/Command/PathChecker.php | 43 +++++++++--- .../lib/Magento/Mtf/Util/Command/Website.php | 40 ++++++++---- .../CurlTransport/BackendDecorator.php | 65 ++++++++++++++----- .../CurlTransport/WebapiDecorator.php | 32 ++++++++- dev/tests/functional/utils/authenticate.php | 29 +++++++++ dev/tests/functional/utils/command.php | 26 ++++---- .../utils/deleteMagentoGeneratedCode.php | 11 +++- dev/tests/functional/utils/export.php | 39 ++++++----- dev/tests/functional/utils/locales.php | 26 +++++--- dev/tests/functional/utils/log.php | 24 ++++--- dev/tests/functional/utils/pathChecker.php | 17 +++-- dev/tests/functional/utils/website.php | 39 ++++++----- 18 files changed, 435 insertions(+), 156 deletions(-) create mode 100644 dev/tests/functional/utils/authenticate.php diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php index 8fa22122cce89..f0abd280f3ebc 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php @@ -8,6 +8,7 @@ use Magento\Mtf\Util\Protocol\CurlInterface; use Magento\Mtf\Util\Protocol\CurlTransport; +use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * Perform bin/magento commands from command line for functional tests executions. @@ -17,7 +18,7 @@ class Cli /** * Url to command.php. */ - const URL = 'dev/tests/functional/utils/command.php'; + const URL = '/dev/tests/functional/utils/command.php'; /** * Curl transport protocol. @@ -26,12 +27,21 @@ class Cli */ private $transport; + /** + * Webapi handler. + * + * @var WebapiDecorator + */ + private $webapiHandler; + /** * @param CurlTransport $transport + * @param WebapiDecorator $webapiHandler */ - public function __construct(CurlTransport $transport) + public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler) { $this->transport = $transport; + $this->webapiHandler = $webapiHandler; } /** @@ -43,22 +53,31 @@ public function __construct(CurlTransport $transport) */ public function execute($command, $options = []) { - $curl = $this->transport; - $curl->write($this->prepareUrl($command, $options), [], CurlInterface::GET); - $curl->read(); - $curl->close(); + $this->transport->write( + rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, + $this->prepareParamArray($command, $options), + CurlInterface::POST, + [] + ); + $this->transport->read(); + $this->transport->close(); } /** - * Prepare url. + * Prepare parameter array. * * @param string $command * @param array $options [optional] - * @return string + * @return array */ - private function prepareUrl($command, $options = []) + private function prepareParamArray($command, $options = []) { - $command .= ' ' . implode(' ', $options); - return $_ENV['app_frontend_url'] . self::URL . '?command=' . urlencode($command); + if (!empty($options)) { + $command .= ' ' . implode(' ', $options); + } + return [ + 'token' => urlencode($this->webapiHandler->getWebapiToken()), + 'command' => urlencode($command) + ]; } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/Reader.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/Reader.php index 1c05fbaebf625..69df78a5cad64 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/Reader.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/Reader.php @@ -3,12 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Mtf\Util\Command\File\Export; use Magento\Mtf\ObjectManagerInterface; use Magento\Mtf\Util\Protocol\CurlTransport; use Magento\Mtf\Util\Protocol\CurlInterface; +use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * File reader for Magento export files. @@ -36,16 +36,29 @@ class Reader implements ReaderInterface */ private $transport; + /** + * Webapi handler. + * + * @var WebapiDecorator + */ + private $webapiHandler; + /** * @param ObjectManagerInterface $objectManager * @param CurlTransport $transport + * @param WebapiDecorator $webapiHandler * @param string $template */ - public function __construct(ObjectManagerInterface $objectManager, CurlTransport $transport, $template) - { + public function __construct( + ObjectManagerInterface $objectManager, + CurlTransport $transport, + WebapiDecorator $webapiHandler, + $template + ) { $this->objectManager = $objectManager; $this->template = $template; $this->transport = $transport; + $this->webapiHandler = $webapiHandler; } /** @@ -70,20 +83,27 @@ public function getData() */ private function getFiles() { - $this->transport->write($this->prepareUrl(), [], CurlInterface::GET); + $this->transport->write( + rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, + $this->prepareParamArray(), + CurlInterface::POST, + [] + ); $serializedFiles = $this->transport->read(); $this->transport->close(); - return unserialize($serializedFiles); } /** - * Prepare url. + * Prepare parameter array. * - * @return string + * @return array */ - private function prepareUrl() + private function prepareParamArray() { - return $_ENV['app_frontend_url'] . self::URL . '?template=' . urlencode($this->template); + return [ + 'token' => urlencode($this->webapiHandler->getWebapiToken()), + 'template' => urlencode($this->template) + ]; } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/ReaderInterface.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/ReaderInterface.php index 93f7cf1ce9764..3666e8643efa3 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/ReaderInterface.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/ReaderInterface.php @@ -14,7 +14,7 @@ interface ReaderInterface /** * Url to export.php. */ - const URL = 'dev/tests/functional/utils/export.php'; + const URL = '/dev/tests/functional/utils/export.php'; /** * Exporting files as Data object from Magento. diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Log.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Log.php index 8b41924fe0a90..820a5b0a82228 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Log.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Log.php @@ -7,6 +7,7 @@ namespace Magento\Mtf\Util\Command\File; use Magento\Mtf\Util\Protocol\CurlTransport; +use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * Get content of log file in var/log folder. @@ -16,7 +17,7 @@ class Log /** * Url to log.php. */ - const URL = 'dev/tests/functional/utils/log.php'; + const URL = '/dev/tests/functional/utils/log.php'; /** * Curl transport protocol. @@ -25,12 +26,21 @@ class Log */ private $transport; + /** + * Webapi handler. + * + * @var WebapiDecorator + */ + private $webapiHandler; + /** * @param CurlTransport $transport + * @param WebapiDecorator $webapiHandler */ - public function __construct(CurlTransport $transport) + public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler) { $this->transport = $transport; + $this->webapiHandler = $webapiHandler; } /** @@ -41,22 +51,28 @@ public function __construct(CurlTransport $transport) */ public function getFileContent($name) { - $curl = $this->transport; - $curl->write($this->prepareUrl($name), [], CurlTransport::GET); - $data = $curl->read(); - $curl->close(); - + $this->transport->write( + rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, + $this->prepareParamArray($name), + CurlInterface::POST, + [] + ); + $data = $this->transport->read(); + $this->transport->close(); return unserialize($data); } /** - * Prepare url. + * Prepare parameter array. * * @param string $name - * @return string + * @return array */ - private function prepareUrl($name) + private function prepareParamArray($name) { - return $_ENV['app_frontend_url'] . self::URL . '?name=' . urlencode($name); + return [ + 'token' => urlencode($this->webapiHandler->getWebapiToken()), + 'name' => urlencode($name) + ]; } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/GeneratedCode.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/GeneratedCode.php index dde3409ed1562..a9fefa25ffa24 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/GeneratedCode.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/GeneratedCode.php @@ -7,6 +7,7 @@ use Magento\Mtf\Util\Protocol\CurlInterface; use Magento\Mtf\Util\Protocol\CurlTransport; +use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * GeneratedCode removes generated code of Magento (like generated/code and generated/metadata). @@ -16,7 +17,7 @@ class GeneratedCode /** * Url to deleteMagentoGeneratedCode.php. */ - const URL = 'dev/tests/functional/utils/deleteMagentoGeneratedCode.php'; + const URL = '/dev/tests/functional/utils/deleteMagentoGeneratedCode.php'; /** * Curl transport protocol. @@ -25,12 +26,21 @@ class GeneratedCode */ private $transport; + /** + * Webapi handler. + * + * @var WebapiDecorator + */ + private $webapiHandler; + /** * @param CurlTransport $transport + * @param WebapiDecorator $webapiHandler */ - public function __construct(CurlTransport $transport) + public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler) { $this->transport = $transport; + $this->webapiHandler = $webapiHandler; } /** @@ -40,10 +50,25 @@ public function __construct(CurlTransport $transport) */ public function delete() { - $url = $_ENV['app_frontend_url'] . self::URL; - $curl = $this->transport; - $curl->write($url, [], CurlInterface::GET); - $curl->read(); - $curl->close(); + $this->transport->write( + rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, + $this->prepareParamArray(), + CurlInterface::POST, + [] + ); + $this->transport->read(); + $this->transport->close(); + } + + /** + * Prepare parameter array. + * + * @return array + */ + private function prepareParamArray() + { + return [ + 'token' => urlencode($this->webapiHandler->getWebapiToken()) + ]; } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Locales.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Locales.php index f669d91f2f2e5..a55d803f43087 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Locales.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Locales.php @@ -7,6 +7,7 @@ use Magento\Mtf\Util\Protocol\CurlInterface; use Magento\Mtf\Util\Protocol\CurlTransport; +use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * Returns array of locales depends on fetching type. @@ -26,7 +27,7 @@ class Locales /** * Url to locales.php. */ - const URL = 'dev/tests/functional/utils/locales.php'; + const URL = '/dev/tests/functional/utils/locales.php'; /** * Curl transport protocol. @@ -35,12 +36,21 @@ class Locales */ private $transport; + /** + * Webapi handler. + * + * @var WebapiDecorator + */ + private $webapiHandler; + /** * @param CurlTransport $transport Curl transport protocol + * @param WebapiDecorator $webapiHandler */ - public function __construct(CurlTransport $transport) + public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler) { $this->transport = $transport; + $this->webapiHandler = $webapiHandler; } /** @@ -51,12 +61,28 @@ public function __construct(CurlTransport $transport) */ public function getList($type = self::TYPE_ALL) { - $url = $_ENV['app_frontend_url'] . self::URL . '?type=' . $type; - $curl = $this->transport; - $curl->write($url, [], CurlInterface::GET); - $result = $curl->read(); - $curl->close(); - + $this->transport->write( + rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, + $this->prepareParamArray($type), + CurlInterface::POST, + [] + ); + $result = $this->transport->read(); + $this->transport->close(); return explode('|', $result); } + + /** + * Prepare parameter array. + * + * @param string $type + * @return array + */ + private function prepareParamArray($type) + { + return [ + 'token' => urlencode($this->webapiHandler->getWebapiToken()), + 'type' => urlencode($type) + ]; + } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/PathChecker.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/PathChecker.php index fd1f746a6f09c..4b12f6eec87aa 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/PathChecker.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/PathChecker.php @@ -7,6 +7,7 @@ use Magento\Mtf\Util\Protocol\CurlInterface; use Magento\Mtf\Util\Protocol\CurlTransport; +use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * PathChecker checks that path to file or directory exists. @@ -16,7 +17,7 @@ class PathChecker /** * Url to checkPath.php. */ - const URL = 'dev/tests/functional/utils/pathChecker.php'; + const URL = '/dev/tests/functional/utils/pathChecker.php'; /** * Curl transport protocol. @@ -26,11 +27,21 @@ class PathChecker private $transport; /** + * Webapi handler. + * + * @var WebapiDecorator + */ + private $webapiHandler; + + /** + * @constructor * @param CurlTransport $transport + * @param WebapiDecorator $webapiHandler */ - public function __construct(CurlTransport $transport) + public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler) { $this->transport = $transport; + $this->webapiHandler = $webapiHandler; } /** @@ -41,12 +52,28 @@ public function __construct(CurlTransport $transport) */ public function pathExists($path) { - $url = $_ENV['app_frontend_url'] . self::URL . '?path=' . urlencode($path); - $curl = $this->transport; - $curl->write($url, [], CurlInterface::GET); - $result = $curl->read(); - $curl->close(); - + $this->transport->write( + rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, + $this->prepareParamArray($path), + CurlInterface::POST, + [] + ); + $result = $this->transport->read(); + $this->transport->close(); return strpos($result, 'path exists: true') !== false; } + + /** + * Prepare parameter array. + * + * @param string $path + * @return array + */ + private function prepareParamArray($path) + { + return [ + 'token' => urlencode($this->webapiHandler->getWebapiToken()), + 'path' => urlencode($path) + ]; + } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Website.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Website.php index 7d73634c0360d..fec20bb2a8715 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Website.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Website.php @@ -3,11 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Mtf\Util\Command; use Magento\Mtf\Util\Protocol\CurlInterface; use Magento\Mtf\Util\Protocol\CurlTransport; +use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * Perform Website folder creation for functional tests executions. @@ -17,7 +17,7 @@ class Website /** * Url to website.php. */ - const URL = 'dev/tests/functional/utils/website.php'; + const URL = '/dev/tests/functional/utils/website.php'; /** * Curl transport protocol. @@ -26,13 +26,22 @@ class Website */ private $transport; + /** + * Webapi handler. + * + * @var WebapiDecorator + */ + private $webapiHandler; + /** * @constructor * @param CurlTransport $transport + * @param WebapiDecorator $webapiHandler */ - public function __construct(CurlTransport $transport) + public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler) { $this->transport = $transport; + $this->webapiHandler = $webapiHandler; } /** @@ -43,21 +52,28 @@ public function __construct(CurlTransport $transport) */ public function create($websiteCode) { - $curl = $this->transport; - $curl->addOption(CURLOPT_HEADER, 1); - $curl->write($this->prepareUrl($websiteCode), [], CurlInterface::GET); - $curl->read(); - $curl->close(); + $this->transport->addOption(CURLOPT_HEADER, 1); + $this->transport->write( + rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, + $this->prepareParamArray($websiteCode), + CurlInterface::POST, + [] + ); + $this->transport->read(); + $this->transport->close(); } /** - * Prepare url. + * Prepare parameter array. * * @param string $websiteCode - * @return string + * @return array */ - private function prepareUrl($websiteCode) + private function prepareParamArray($websiteCode) { - return $_ENV['app_frontend_url'] . self::URL . '?website_code=' . urlencode($websiteCode); + return [ + 'token' => urlencode($this->webapiHandler->getWebapiToken()), + 'website_code' => urlencode($websiteCode) + ]; } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php index ab333dc7c005a..1126cd29727fc 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php @@ -63,23 +63,54 @@ public function __construct(CurlTransport $transport, DataInterface $configurati */ protected function authorize() { - // Perform GET to backend url so form_key is set - $url = $_ENV['app_backend_url']; - $this->transport->write($url, [], CurlInterface::GET); - $this->read(); - - $url = $_ENV['app_backend_url'] . $this->configuration->get('application/0/backendLoginUrl/0/value'); - $data = [ - 'login[username]' => $this->configuration->get('application/0/backendLogin/0/value'), - 'login[password]' => $this->configuration->get('application/0/backendPassword/0/value'), - 'form_key' => $this->formKey, - ]; - $this->transport->write($url, $data, CurlInterface::POST); - $response = $this->read(); - if (strpos($response, 'login-form')) { - throw new \Exception( - 'Admin user cannot be logged in by curl handler!' - ); + // There are situations where magento application backend url could be slightly different from the environment + // variable we know. It could be intentionally (e.g. InstallTest) or unintentionally. We would still want tests + // to run in this case. + // When the original app_backend_url does not work, we will try 4 variants of the it. i.e. with and without + // url rewrite, http and https. + $urls = []; + $originalUrl = rtrim($_ENV['app_backend_url'], '/') . '/'; + $urls[] = $originalUrl; + if (strpos($originalUrl, '/index.php') !== false) { + $url2 = str_replace('/index.php', '', $originalUrl); + } else { + $url2 = $originalUrl . 'index.php/'; + } + $urls[] = $url2; + if (strpos($originalUrl, 'https') !== false) { + $urls[] = str_replace('https', 'http', $originalUrl); + } else { + $urls[] = str_replace('http', 'https', $url2); + } + + $isAuthorized = false; + foreach ($urls as $url) { + try { + // Perform GET to backend url so form_key is set + $this->transport->write($url, [], CurlInterface::GET); + $this->read(); + + $authUrl = $url . $this->configuration->get('application/0/backendLoginUrl/0/value'); + $data = [ + 'login[username]' => $this->configuration->get('application/0/backendLogin/0/value'), + 'login[password]' => $this->configuration->get('application/0/backendPassword/0/value'), + 'form_key' => $this->formKey, + ]; + + $this->transport->write($authUrl, $data, CurlInterface::POST); + $response = $this->read(); + if (strpos($response, 'login-form')) { + continue; + } + $isAuthorized = true; + $_ENV['app_backend_url'] = $url; + break; + } catch (\Exception $e) { + continue; + } + } + if ($isAuthorized == false) { + throw new \Exception('Admin user cannot be logged in by curl handler!'); } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/WebapiDecorator.php b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/WebapiDecorator.php index 3aa756904ab00..df5ab45a3f96d 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/WebapiDecorator.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/WebapiDecorator.php @@ -70,6 +70,13 @@ class WebapiDecorator implements CurlInterface */ protected $response; + /** + * Webapi token. + * + * @var string + */ + protected $webapiToken; + /** * @construct * @param ObjectManager $objectManager @@ -110,6 +117,9 @@ protected function init() $integration->persist(); $this->setConfiguration($integration); + $this->webapiToken = $integration->getToken(); + } else { + $this->webapiToken = $integrationToken; } } @@ -161,7 +171,13 @@ protected function setConfiguration(Integration $integration) */ protected function isValidIntegration() { - $this->write($_ENV['app_frontend_url'] . 'rest/V1/modules', [], CurlInterface::GET); + $url = rtrim($_ENV['app_frontend_url'], '/'); + if (strpos($url, 'index.php') === false) { + $url .= '/index.php/rest/V1/modules'; + } else { + $url .= '/rest/V1/modules'; + } + $this->write($url, [], CurlInterface::GET); $response = json_decode($this->read(), true); return (null !== $response) && !isset($response['message']); @@ -219,4 +235,18 @@ public function close() { $this->transport->close(); } + + /** + * Return webapiToken. + * + * @return string + */ + public function getWebapiToken() + { + // Request token if integration is no longer valid + if (!$this->isValidIntegration()) { + $this->init(); + } + return $this->webapiToken; + } } diff --git a/dev/tests/functional/utils/authenticate.php b/dev/tests/functional/utils/authenticate.php new file mode 100644 index 0000000000000..15851f6e8000a --- /dev/null +++ b/dev/tests/functional/utils/authenticate.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** + * Check if token passed in is a valid auth token. + * + * @param string $token + * @return bool + */ +function authenticate($token) +{ + require_once __DIR__ . '/../../../../app/bootstrap.php'; + + $magentoObjectManagerFactory = \Magento\Framework\App\Bootstrap::createObjectManagerFactory(BP, $_SERVER); + $magentoObjectManager = $magentoObjectManagerFactory->create($_SERVER); + $tokenModel = $magentoObjectManager->get(\Magento\Integration\Model\Oauth\Token::class); + + $tokenPassedIn = $token; + // Token returned will be null if the token we passed in is invalid + $tokenFromMagento = $tokenModel->loadByToken($tokenPassedIn)->getToken(); + if (!empty($tokenFromMagento) && ($tokenFromMagento == $tokenPassedIn)) { + return true; + } else { + return false; + } +} diff --git a/dev/tests/functional/utils/command.php b/dev/tests/functional/utils/command.php index 8eaf82475a4e4..e7b336464682d 100644 --- a/dev/tests/functional/utils/command.php +++ b/dev/tests/functional/utils/command.php @@ -3,21 +3,25 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - +include __DIR__ . '/authenticate.php'; require_once __DIR__ . '/../../../../app/bootstrap.php'; use Symfony\Component\Console\Input\StringInput; use Symfony\Component\Console\Output\NullOutput; -if (isset($_GET['command'])) { - $command = urldecode($_GET['command']); - $magentoObjectManagerFactory = \Magento\Framework\App\Bootstrap::createObjectManagerFactory(BP, $_SERVER); - $magentoObjectManager = $magentoObjectManagerFactory->create($_SERVER); - $cli = $magentoObjectManager->create(\Magento\Framework\Console\Cli::class); - $input = new StringInput($command); - $input->setInteractive(false); - $output = new NullOutput(); - $cli->doRun($input, $output); +if (!empty($_POST['token']) && !empty($_POST['command'])) { + if (authenticate(urldecode($_POST['token']))) { + $command = urldecode($_POST['command']); + $magentoObjectManagerFactory = \Magento\Framework\App\Bootstrap::createObjectManagerFactory(BP, $_SERVER); + $magentoObjectManager = $magentoObjectManagerFactory->create($_SERVER); + $cli = $magentoObjectManager->create(\Magento\Framework\Console\Cli::class); + $input = new StringInput($command); + $input->setInteractive(false); + $output = new NullOutput(); + $cli->doRun($input, $output); + } else { + echo "Command not unauthorized."; + } } else { - throw new \InvalidArgumentException("Command GET parameter is not set."); + echo "'token' or 'command' parameter is not set."; } diff --git a/dev/tests/functional/utils/deleteMagentoGeneratedCode.php b/dev/tests/functional/utils/deleteMagentoGeneratedCode.php index 99aa9af06e92a..17e3575c87686 100644 --- a/dev/tests/functional/utils/deleteMagentoGeneratedCode.php +++ b/dev/tests/functional/utils/deleteMagentoGeneratedCode.php @@ -3,5 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +include __DIR__ . '/authenticate.php'; -exec('rm -rf ../../../../generated/*'); +if (!empty($_POST['token']) && !empty($_POST['path'])) { + if (authenticate(urldecode($_POST['token']))) { + exec('rm -rf ../../../../generated/*'); + } else { + echo "Command not unauthorized."; + } +} else { + echo "'token' parameter is not set."; +} diff --git a/dev/tests/functional/utils/export.php b/dev/tests/functional/utils/export.php index 343dcc557c832..9357bfa459be0 100644 --- a/dev/tests/functional/utils/export.php +++ b/dev/tests/functional/utils/export.php @@ -3,25 +3,30 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +include __DIR__ . '/authenticate.php'; -if (!isset($_GET['template'])) { - throw new \InvalidArgumentException('Argument "template" must be set.'); -} +if (!empty($_POST['token']) && !empty($_POST['template'])) { + if (authenticate(urldecode($_POST['token']))) { + $varDir = '../../../../var/'; + $template = urldecode($_POST['template']); + $fileList = scandir($varDir, SCANDIR_SORT_NONE); + $files = []; -$varDir = '../../../../var/'; -$template = urldecode($_GET['template']); -$fileList = scandir($varDir, SCANDIR_SORT_NONE); -$files = []; + foreach ($fileList as $fileName) { + if (preg_match("`$template`", $fileName) === 1) { + $filePath = $varDir . $fileName; + $files[] = [ + 'content' => file_get_contents($filePath), + 'name' => $fileName, + 'date' => filectime($filePath), + ]; + } + } -foreach ($fileList as $fileName) { - if (preg_match("`$template`", $fileName) === 1) { - $filePath = $varDir . $fileName; - $files[] = [ - 'content' => file_get_contents($filePath), - 'name' => $fileName, - 'date' => filectime($filePath), - ]; + echo serialize($files); + } else { + echo "Command not unauthorized."; } +} else { + echo "'token' or 'template' parameter is not set."; } - -echo serialize($files); diff --git a/dev/tests/functional/utils/locales.php b/dev/tests/functional/utils/locales.php index 827b8b1b89448..a3b4ec05eed65 100644 --- a/dev/tests/functional/utils/locales.php +++ b/dev/tests/functional/utils/locales.php @@ -3,15 +3,23 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +include __DIR__ . '/authenticate.php'; -if (isset($_GET['type']) && $_GET['type'] == 'deployed') { - $themePath = isset($_GET['theme_path']) ? $_GET['theme_path'] : 'adminhtml/Magento/backend'; - $directory = __DIR__ . '/../../../../pub/static/' . $themePath; - $locales = array_diff(scandir($directory), ['..', '.']); +if (!empty($_POST['token'])) { + if (authenticate(urldecode($_POST['token']))) { + if ($_POST['type'] == 'deployed') { + $themePath = isset($_POST['theme_path']) ? $_POST['theme_path'] : 'adminhtml/Magento/backend'; + $directory = __DIR__ . '/../../../../pub/static/' . $themePath; + $locales = array_diff(scandir($directory), ['..', '.']); + } else { + require_once __DIR__ . DIRECTORY_SEPARATOR . 'bootstrap.php'; + $localeConfig = $magentoObjectManager->create(\Magento\Framework\Locale\Config::class); + $locales = $localeConfig->getAllowedLocales(); + } + echo implode('|', $locales); + } else { + echo "Command not unauthorized."; + } } else { - require_once __DIR__ . DIRECTORY_SEPARATOR . 'bootstrap.php'; - $localeConfig = $magentoObjectManager->create(\Magento\Framework\Locale\Config::class); - $locales = $localeConfig->getAllowedLocales(); + echo "'token' parameter is not set."; } - -echo implode('|', $locales); diff --git a/dev/tests/functional/utils/log.php b/dev/tests/functional/utils/log.php index 8f07d72e2a569..0d6f1fa2ac5cf 100644 --- a/dev/tests/functional/utils/log.php +++ b/dev/tests/functional/utils/log.php @@ -3,15 +3,19 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +include __DIR__ . '/authenticate.php'; -if (!isset($_GET['name'])) { - throw new \InvalidArgumentException( - 'The name of log file is required for getting logs.' - ); -} -$name = urldecode($_GET['name']); -if (preg_match('/\.\.(\\\|\/)/', $name)) { - throw new \InvalidArgumentException('Invalid log file name'); -} +if (!empty($_POST['token']) && !empty($_POST['name'])) { + if (authenticate(urldecode($_POST['token']))) { + $name = urldecode($_POST['name']); + if (preg_match('/\.\.(\\\|\/)/', $name)) { + throw new \InvalidArgumentException('Invalid log file name'); + } -echo serialize(file_get_contents('../../../../var/log' .'/' .$name)); + echo serialize(file_get_contents('../../../../var/log' . '/' . $name)); + } else { + echo "Command not unauthorized."; + } +} else { + echo "'token' or 'name' parameter is not set."; +} diff --git a/dev/tests/functional/utils/pathChecker.php b/dev/tests/functional/utils/pathChecker.php index 11f8229bce56f..b5a2ddb405bde 100644 --- a/dev/tests/functional/utils/pathChecker.php +++ b/dev/tests/functional/utils/pathChecker.php @@ -3,15 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +include __DIR__ . '/authenticate.php'; -if (isset($_GET['path'])) { - $path = urldecode($_GET['path']); +if (!empty($_POST['token']) && !empty($_POST['path'])) { + if (authenticate(urldecode($_POST['token']))) { + $path = urldecode($_POST['path']); - if (file_exists('../../../../' . $path)) { - echo 'path exists: true'; + if (file_exists('../../../../' . $path)) { + echo 'path exists: true'; + } else { + echo 'path exists: false'; + } } else { - echo 'path exists: false'; + echo "Command not unauthorized."; } } else { - throw new \InvalidArgumentException("GET parameter 'path' is not set."); + echo "'token' or 'path' parameter is not set."; } diff --git a/dev/tests/functional/utils/website.php b/dev/tests/functional/utils/website.php index 625f5c6b483f8..ab8e3742f55ae 100644 --- a/dev/tests/functional/utils/website.php +++ b/dev/tests/functional/utils/website.php @@ -3,30 +3,35 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +include __DIR__ . '/authenticate.php'; -if (!isset($_GET['website_code'])) { - throw new \Exception("website_code GET parameter is not set."); -} - -$websiteCode = urldecode($_GET['website_code']); -$rootDir = '../../../../'; -$websiteDir = $rootDir . 'websites/' . $websiteCode . '/'; -$contents = file_get_contents($rootDir . 'index.php'); +if (!empty($_POST['token']) && !empty($_POST['website_code'])) { + if (authenticate(urldecode($_POST['token']))) { + $websiteCode = urldecode($_POST['website_code']); + $rootDir = '../../../../'; + $websiteDir = $rootDir . 'websites/' . $websiteCode . '/'; + $contents = file_get_contents($rootDir . 'index.php'); -$websiteParam = <<<EOD + $websiteParam = <<<EOD \$params = \$_SERVER; \$params[\Magento\Store\Model\StoreManager::PARAM_RUN_CODE] = '$websiteCode'; \$params[\Magento\Store\Model\StoreManager::PARAM_RUN_TYPE] = 'website'; EOD; -$pattern = '`(try {.*?)(\/app\/bootstrap.*?}\n)(.*?)\$_SERVER`mis'; -$replacement = "$1/../..$2\n$websiteParam$3\$params"; + $pattern = '`(try {.*?)(\/app\/bootstrap.*?}\n)(.*?)\$_SERVER`mis'; + $replacement = "$1/../..$2\n$websiteParam$3\$params"; -$contents = preg_replace($pattern, $replacement, $contents); + $contents = preg_replace($pattern, $replacement, $contents); -$old = umask(0); -mkdir($websiteDir, 0760, true); -umask($old); + $old = umask(0); + mkdir($websiteDir, 0760, true); + umask($old); -copy($rootDir . '.htaccess', $websiteDir . '.htaccess'); -file_put_contents($websiteDir . 'index.php', $contents); + copy($rootDir . '.htaccess', $websiteDir . '.htaccess'); + file_put_contents($websiteDir . 'index.php', $contents); + } else { + echo "Command not unauthorized."; + } +} else { + echo "'token' or 'website_code' parameter is not set."; +} From f48e1b0b89c2b510fdeebdaf7321ef6432774612 Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@adobe.com> Date: Fri, 8 Feb 2019 14:08:04 -0600 Subject: [PATCH 1180/1295] MQE-1430: bug fix in dev/tests/functional/utils/command.php etc (cherry picked from commit dd258a5) --- .../Mtf/Util/Protocol/CurlTransport/BackendDecorator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php index 1126cd29727fc..44ddb16a18741 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php @@ -99,7 +99,7 @@ protected function authorize() $this->transport->write($authUrl, $data, CurlInterface::POST); $response = $this->read(); - if (strpos($response, 'login-form')) { + if (strpos($response, 'login-form') !== false) { continue; } $isAuthorized = true; From 5c13d3334a5a36e23ec19ebf560630f921fd7e18 Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@adobe.com> Date: Mon, 11 Feb 2019 10:20:30 -0600 Subject: [PATCH 1181/1295] MQE-1430: bug fix in dev/tests/functional/utils/command.php etc (cherry picked from commit a93901c) --- .../Mtf/Util/Protocol/CurlTransport/BackendDecorator.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php index 44ddb16a18741..2fc26a975e159 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php @@ -71,6 +71,8 @@ protected function authorize() $urls = []; $originalUrl = rtrim($_ENV['app_backend_url'], '/') . '/'; $urls[] = $originalUrl; + // It could be the case that the page needs a refresh, so we will try the original one twice + $urls[] = $originalUrl; if (strpos($originalUrl, '/index.php') !== false) { $url2 = str_replace('/index.php', '', $originalUrl); } else { From bd0613a10f5bdfc554361c39b7b43072f3395868 Mon Sep 17 00:00:00 2001 From: Keyur Kanani <keyurk@emiprotechnologies.com> Date: Wed, 24 Apr 2019 12:21:11 +0530 Subject: [PATCH 1182/1295] Fixed Dependency on Backup Settings Configuration --- app/code/Magento/Backup/etc/adminhtml/system.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/Backup/etc/adminhtml/system.xml b/app/code/Magento/Backup/etc/adminhtml/system.xml index 90f6fa861b40f..aa6635b4dde4a 100644 --- a/app/code/Magento/Backup/etc/adminhtml/system.xml +++ b/app/code/Magento/Backup/etc/adminhtml/system.xml @@ -26,6 +26,7 @@ <label>Scheduled Backup Type</label> <depends> <field id="enabled">1</field> + <field id="functionality_enabled">1</field> </depends> <source_model>Magento\Backup\Model\Config\Source\Type</source_model> </field> @@ -33,12 +34,14 @@ <label>Start Time</label> <depends> <field id="enabled">1</field> + <field id="functionality_enabled">1</field> </depends> </field> <field id="frequency" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Frequency</label> <depends> <field id="enabled">1</field> + <field id="functionality_enabled">1</field> </depends> <source_model>Magento\Cron\Model\Config\Source\Frequency</source_model> <backend_model>Magento\Backup\Model\Config\Backend\Cron</backend_model> @@ -48,6 +51,7 @@ <comment>Please put your store into maintenance mode during backup.</comment> <depends> <field id="enabled">1</field> + <field id="functionality_enabled">1</field> </depends> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> From 7c2ad7b40502c86d173c44a2f4d530e20b0a0614 Mon Sep 17 00:00:00 2001 From: Raoul Rego <rrego@adobe.com> Date: Fri, 26 Apr 2019 12:34:59 -0500 Subject: [PATCH 1183/1295] MAGETWO-99324: Reverting: [Magento Cloud] If a product has a custom attributes which requires a Unique Value, it cannot be saved when there's an active Scheduled Update - reverts MAGETWO-95762 due to invalid fix This reverts commit b456844. --- .../Model/ResourceModel/AbstractResource.php | 12 ++----- .../Catalog/Model/ResourceModel/Product.php | 15 +++----- .../Eav/Model/Entity/AbstractEntity.php | 26 +++++--------- .../Attribute/UniqueValidationInterface.php | 35 ------------------- .../Entity/Attribute/UniqueValidator.php | 34 ------------------ app/code/Magento/Eav/etc/di.xml | 1 - 6 files changed, 16 insertions(+), 107 deletions(-) delete mode 100644 app/code/Magento/Eav/Model/Entity/Attribute/UniqueValidationInterface.php delete mode 100644 app/code/Magento/Eav/Model/Entity/Attribute/UniqueValidator.php diff --git a/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php b/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php index 6614b10ef609e..1e55bd35553ab 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php @@ -9,10 +9,6 @@ namespace Magento\Catalog\Model\ResourceModel; use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; -use Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend; -use Magento\Eav\Model\Entity\Attribute\Frontend\AbstractFrontend; -use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource; -use Magento\Eav\Model\Entity\Attribute\UniqueValidationInterface; /** * Catalog entity abstract model @@ -43,18 +39,16 @@ abstract class AbstractResource extends \Magento\Eav\Model\Entity\AbstractEntity * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\Catalog\Model\Factory $modelFactory * @param array $data - * @param UniqueValidationInterface|null $uniqueValidator */ public function __construct( \Magento\Eav\Model\Entity\Context $context, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Catalog\Model\Factory $modelFactory, - $data = [], - UniqueValidationInterface $uniqueValidator = null + $data = [] ) { $this->_storeManager = $storeManager; $this->_modelFactory = $modelFactory; - parent::__construct($context, $data, $uniqueValidator); + parent::__construct($context, $data); } /** @@ -94,7 +88,7 @@ protected function _isApplicableAttribute($object, $attribute) /** * Check whether attribute instance (attribute, backend, frontend or source) has method and applicable * - * @param AbstractAttribute|AbstractBackend|AbstractFrontend|AbstractSource $instance + * @param AbstractAttribute|\Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend|\Magento\Eav\Model\Entity\Attribute\Frontend\AbstractFrontend|\Magento\Eav\Model\Entity\Attribute\Source\AbstractSource $instance * @param string $method * @param array $args array of arguments * @return boolean diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product.php b/app/code/Magento/Catalog/Model/ResourceModel/Product.php index e438e2f54113d..95f09c7ee80be 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product.php @@ -8,7 +8,6 @@ use Magento\Catalog\Model\ResourceModel\Product\Website\Link as ProductWebsiteLink; use Magento\Framework\App\ObjectManager; use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer; -use Magento\Eav\Model\Entity\Attribute\UniqueValidationInterface; /** * Product entity resource model @@ -102,7 +101,6 @@ class Product extends AbstractResource * @param \Magento\Catalog\Model\Product\Attribute\DefaultAttributes $defaultAttributes * @param array $data * @param TableMaintainer|null $tableMaintainer - * @param UniqueValidationInterface|null $uniqueValidator * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -117,8 +115,7 @@ public function __construct( \Magento\Eav\Model\Entity\TypeFactory $typeFactory, \Magento\Catalog\Model\Product\Attribute\DefaultAttributes $defaultAttributes, $data = [], - TableMaintainer $tableMaintainer = null, - UniqueValidationInterface $uniqueValidator = null + TableMaintainer $tableMaintainer = null ) { $this->_categoryCollectionFactory = $categoryCollectionFactory; $this->_catalogCategory = $catalogCategory; @@ -130,8 +127,7 @@ public function __construct( $context, $storeManager, $modelFactory, - $data, - $uniqueValidator + $data ); $this->connectionName = 'catalog'; $this->tableMaintainer = $tableMaintainer ?: ObjectManager::getInstance()->get(TableMaintainer::class); @@ -661,7 +657,7 @@ public function save(\Magento\Framework\Model\AbstractModel $object) } /** - * Retrieve entity manager object. + * Retrieve entity manager object * * @return \Magento\Framework\EntityManager\EntityManager */ @@ -675,7 +671,7 @@ private function getEntityManager() } /** - * Retrieve ProductWebsiteLink object. + * Retrieve ProductWebsiteLink object * * @deprecated 101.1.0 * @return ProductWebsiteLink @@ -686,7 +682,7 @@ private function getProductWebsiteLink() } /** - * Retrieve CategoryLink object. + * Retrieve CategoryLink object * * @deprecated 101.1.0 * @return \Magento\Catalog\Model\ResourceModel\Product\CategoryLink @@ -702,7 +698,6 @@ private function getProductCategoryLink() /** * Extends parent method to be appropriate for product. - * * Store id is required to correctly identify attribute value we are working with. * * @inheritdoc diff --git a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php index 7f6734b8b6b60..9b7abde37cc19 100644 --- a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php +++ b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php @@ -12,7 +12,6 @@ use Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend; use Magento\Eav\Model\Entity\Attribute\Frontend\AbstractFrontend; use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource; -use Magento\Eav\Model\Entity\Attribute\UniqueValidationInterface; use Magento\Framework\App\Config\Element; use Magento\Framework\DataObject; use Magento\Framework\DB\Adapter\DuplicateException; @@ -218,21 +217,12 @@ abstract class AbstractEntity extends AbstractResource implements EntityInterfac */ protected $objectRelationProcessor; - /** - * @var UniqueValidationInterface - */ - private $uniqueValidator; - /** * @param Context $context * @param array $data - * @param UniqueValidationInterface|null $uniqueValidator */ - public function __construct( - Context $context, - $data = [], - UniqueValidationInterface $uniqueValidator = null - ) { + public function __construct(Context $context, $data = []) + { $this->_eavConfig = $context->getEavConfig(); $this->_resource = $context->getResource(); $this->_attrSetEntity = $context->getAttributeSetEntity(); @@ -241,8 +231,6 @@ public function __construct( $this->_universalFactory = $context->getUniversalFactory(); $this->transactionManager = $context->getTransactionManager(); $this->objectRelationProcessor = $context->getObjectRelationProcessor(); - $this->uniqueValidator = $uniqueValidator ?: - ObjectManager::getInstance()->get(UniqueValidationInterface::class); parent::__construct(); $properties = get_object_vars($this); foreach ($data as $key => $value) { @@ -511,7 +499,6 @@ public function addAttributeByScope(AbstractAttribute $attribute, $entity = null /** * Get attributes by scope * - * @param string $suffix * @return array */ private function getAttributesByScope($suffix) @@ -982,8 +969,12 @@ public function checkAttributeUniqueValue(AbstractAttribute $attribute, $object) $data = $connection->fetchCol($select, $bind); - if ($object->getData($entityIdField)) { - return $this->uniqueValidator->validate($attribute, $object, $this, $entityIdField, $data); + $objectId = $object->getData($entityIdField); + if ($objectId) { + if (isset($data[0])) { + return $data[0] == $objectId; + } + return true; } return !count($data); @@ -1995,7 +1986,6 @@ public function afterDelete(DataObject $object) /** * Load attributes for object - * * If the object will not pass all attributes for this entity type will be loaded * * @param array $attributes diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/UniqueValidationInterface.php b/app/code/Magento/Eav/Model/Entity/Attribute/UniqueValidationInterface.php deleted file mode 100644 index 50a6ff9329fc9..0000000000000 --- a/app/code/Magento/Eav/Model/Entity/Attribute/UniqueValidationInterface.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Eav\Model\Entity\Attribute; - -use Magento\Framework\DataObject; -use Magento\Eav\Model\Entity\AbstractEntity; - -/** - * Interface for unique attribute validator. - */ -interface UniqueValidationInterface -{ - /** - * Validate if attribute value is unique. - * - * @param AbstractAttribute $attribute - * @param DataObject $object - * @param AbstractEntity $entity - * @param string $entityLinkField - * @param array $entityIds - * @return bool - */ - public function validate( - AbstractAttribute $attribute, - DataObject $object, - AbstractEntity $entity, - string $entityLinkField, - array $entityIds - ): bool; -} diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/UniqueValidator.php b/app/code/Magento/Eav/Model/Entity/Attribute/UniqueValidator.php deleted file mode 100644 index 9aa501daf1584..0000000000000 --- a/app/code/Magento/Eav/Model/Entity/Attribute/UniqueValidator.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Eav\Model\Entity\Attribute; - -use Magento\Framework\DataObject; -use Magento\Eav\Model\Entity\AbstractEntity; - -/** - * Class for validate unique attribute value. - */ -class UniqueValidator implements UniqueValidationInterface -{ - /** - * @inheritdoc - */ - public function validate( - AbstractAttribute $attribute, - DataObject $object, - AbstractEntity $entity, - string $entityLinkField, - array $entityIds - ): bool { - if (isset($entityIds[0])) { - return $entityIds[0] == $object->getData($entityLinkField); - } - - return true; - } -} diff --git a/app/code/Magento/Eav/etc/di.xml b/app/code/Magento/Eav/etc/di.xml index 92c1ef11b9c1f..c8afd10aa3eee 100644 --- a/app/code/Magento/Eav/etc/di.xml +++ b/app/code/Magento/Eav/etc/di.xml @@ -8,7 +8,6 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <preference for="Magento\Eav\Model\Entity\Setup\PropertyMapperInterface" type="Magento\Eav\Model\Entity\Setup\PropertyMapper\Composite" /> <preference for="Magento\Eav\Model\Entity\AttributeLoaderInterface" type="Magento\Eav\Model\Entity\AttributeLoader" /> - <preference for="Magento\Eav\Model\Entity\Attribute\UniqueValidationInterface" type="Magento\Eav\Model\Entity\Attribute\UniqueValidator" /> <preference for="Magento\Eav\Api\Data\AttributeInterface" type="Magento\Eav\Model\Entity\Attribute" /> <preference for="Magento\Eav\Api\AttributeRepositoryInterface" type="Magento\Eav\Model\AttributeRepository" /> <preference for="Magento\Eav\Api\Data\AttributeGroupInterface" type="Magento\Eav\Model\Entity\Attribute\Group" /> From 0d936c89a5c14c9e0edf352f7d68988b26dbb9c7 Mon Sep 17 00:00:00 2001 From: Shikha Mishra <shikhamishra@cedcoss.com> Date: Fri, 12 Apr 2019 00:41:22 +0530 Subject: [PATCH 1184/1295] Fixed #22223 Missing/Wrong data display on downloadable report table reports>downloads in BO --- .../ResourceModel/Product/Downloads/Collection.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/code/Magento/Reports/Model/ResourceModel/Product/Downloads/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Product/Downloads/Collection.php index 1985db0b90e2a..a7932e8a11f81 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Product/Downloads/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Product/Downloads/Collection.php @@ -97,4 +97,15 @@ public function addFieldToFilter($field, $condition = null) } return $this; } + + /** + * Get SQL for get record count without left JOINs and group + * + * @return \Magento\Framework\DB\Select + */ + public function getSelectCountSql() { + $countSelect = parent::getSelectCountSql(); + $countSelect->reset(\Zend\Db\Sql\Select::GROUP); + return $countSelect; + } } From 35ca37ea9cb672ffb9c088e1a4de007b5e21ba7b Mon Sep 17 00:00:00 2001 From: Yaroslav Rogoza <enarc@atwix.com> Date: Sat, 13 Apr 2019 09:01:11 +0200 Subject: [PATCH 1185/1295] Code style fix --- .../Model/ResourceModel/Product/Downloads/Collection.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Reports/Model/ResourceModel/Product/Downloads/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Product/Downloads/Collection.php index a7932e8a11f81..aeb0448f8e05a 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Product/Downloads/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Product/Downloads/Collection.php @@ -103,7 +103,8 @@ public function addFieldToFilter($field, $condition = null) * * @return \Magento\Framework\DB\Select */ - public function getSelectCountSql() { + public function getSelectCountSql() + { $countSelect = parent::getSelectCountSql(); $countSelect->reset(\Zend\Db\Sql\Select::GROUP); return $countSelect; From a78bb8e24f7818460e45cbf367b98ded74e05fb0 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Tue, 16 Apr 2019 16:18:59 +0300 Subject: [PATCH 1186/1295] magento/magento2#22291: Static test fix. --- .../Product/Downloads/Collection.php | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Reports/Model/ResourceModel/Product/Downloads/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Product/Downloads/Collection.php index aeb0448f8e05a..2009cd3ff9d92 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Product/Downloads/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Product/Downloads/Collection.php @@ -4,16 +4,16 @@ * See COPYING.txt for license details. */ +namespace Magento\Reports\Model\ResourceModel\Product\Downloads; + /** * Product Downloads Report collection * * @author Magento Core Team <core@magentocommerce.com> - */ -namespace Magento\Reports\Model\ResourceModel\Product\Downloads; - -/** + * * @api * @since 100.0.2 + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection { @@ -97,13 +97,11 @@ public function addFieldToFilter($field, $condition = null) } return $this; } - + /** - * Get SQL for get record count without left JOINs and group - * - * @return \Magento\Framework\DB\Select + * @inheritDoc */ - public function getSelectCountSql() + public function getSelectCountSql() { $countSelect = parent::getSelectCountSql(); $countSelect->reset(\Zend\Db\Sql\Select::GROUP); From 299563686badfd4bb542c9f143c94005e8420dd6 Mon Sep 17 00:00:00 2001 From: niravkrish <nirav.patel@krishtechnolabs.com> Date: Mon, 29 Apr 2019 10:13:43 +0530 Subject: [PATCH 1187/1295] PUT /V1/products/:sku/media/:entryId does not change the file - backport --- .../Catalog/Model/Product/Gallery/GalleryManagement.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php b/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php index 1b5f96baeaf9f..33813d5079215 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php @@ -68,8 +68,10 @@ public function create($sku, ProductAttributeMediaGalleryEntryInterface $entry) $product->setMediaGalleryEntries($existingMediaGalleryEntries); try { $product = $this->productRepository->save($product); + // phpcs:ignore Magento2.Exceptions.ThrowCatch } catch (InputException $inputException) { throw $inputException; + // phpcs:ignore Magento2.Exceptions.ThrowCatch } catch (\Exception $e) { throw new StateException(__('Cannot save product.')); } @@ -100,7 +102,10 @@ public function update($sku, ProductAttributeMediaGalleryEntryInterface $entry) if ($existingEntry->getId() == $entry->getId()) { $found = true; - if ($entry->getFile()) { + + $file = $entry->getContent(); + + if ($file && $file->getBase64EncodedData() || $entry->getFile()) { $entry->setId(null); } $existingMediaGalleryEntries[$key] = $entry; From d707c6b78d508ccf2d5d519fffa2b32a5ee997a3 Mon Sep 17 00:00:00 2001 From: Marius Strajeru <tzyganu@gmail.com> Date: Fri, 12 Apr 2019 14:13:14 +0300 Subject: [PATCH 1188/1295] #22299: Cms block cache key does not contain the store id --- app/code/Magento/Cms/Block/Block.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/code/Magento/Cms/Block/Block.php b/app/code/Magento/Cms/Block/Block.php index d0d75ea691195..4fc37b50dcbc0 100644 --- a/app/code/Magento/Cms/Block/Block.php +++ b/app/code/Magento/Cms/Block/Block.php @@ -84,4 +84,14 @@ public function getIdentities() { return [\Magento\Cms\Model\Block::CACHE_TAG . '_' . $this->getBlockId()]; } + + /** + * {@inheritdoc} + */ + public function getCacheKeyInfo() + { + $cacheKeyInfo = parent::getCacheKeyInfo(); + $cacheKeyInfo[] = $this->_storeManager->getStore()->getId(); + return $cacheKeyInfo; + } } From c17b2328f8d8d881fbebd0a4c40a88b4ae465784 Mon Sep 17 00:00:00 2001 From: Nazarn96 <nazarn96@gmail.com> Date: Fri, 26 Apr 2019 10:48:30 +0300 Subject: [PATCH 1189/1295] magento/magento2#22302 Static test fix --- app/code/Magento/Cms/Block/Block.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Cms/Block/Block.php b/app/code/Magento/Cms/Block/Block.php index 4fc37b50dcbc0..c611f4b1e9f05 100644 --- a/app/code/Magento/Cms/Block/Block.php +++ b/app/code/Magento/Cms/Block/Block.php @@ -86,7 +86,7 @@ public function getIdentities() } /** - * {@inheritdoc} + * @inheritdoc */ public function getCacheKeyInfo() { From f593573d6ea7e24017df911f595824bd812ff166 Mon Sep 17 00:00:00 2001 From: Dan Farmer <dan@danielfarmer.co.uk> Date: Tue, 23 Apr 2019 15:52:57 +0100 Subject: [PATCH 1190/1295] Use final price rather than base price to calculate price increases / decreases on the configurable attribute dropdown. This fixes a bug introduced in PR #17695 as detailed on issue #22270 --- .../ConfigurableProduct/view/frontend/web/js/configurable.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js index e732960421541..ef40dcb9a7323 100644 --- a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js +++ b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js @@ -373,7 +373,7 @@ define([ allowedProducts, i, j, - basePrice = parseFloat(this.options.spConfig.prices.basePrice.amount), + finalPrice = parseFloat(this.options.spConfig.prices.finalPrice.amount), optionFinalPrice, optionPriceDiff, optionPrices = this.options.spConfig.optionPrices, @@ -410,7 +410,7 @@ define([ typeof optionPrices[allowedProducts[0]] !== 'undefined') { allowedProductMinPrice = this._getAllowedProductWithMinPrice(allowedProducts); optionFinalPrice = parseFloat(optionPrices[allowedProductMinPrice].finalPrice.amount); - optionPriceDiff = optionFinalPrice - basePrice; + optionPriceDiff = optionFinalPrice - finalPrice; if (optionPriceDiff !== 0) { options[i].label = options[i].label + ' ' + priceUtils.formatPrice( From 354a7141ce57bcc06d08ee078f33405d03335682 Mon Sep 17 00:00:00 2001 From: Graham Wharton <graham@gwharton.me.uk> Date: Mon, 29 Apr 2019 12:34:49 +0100 Subject: [PATCH 1191/1295] When Saving design config, ensure that temporary file in database is renamed aswell as the local file in pub/media. Initialise Database Helper from Object Manager if not passed by DI. Increases backwards compatability. --- .../Theme/Model/Design/Backend/File.php | 15 ++++++++++- .../Unit/Model/Design/Backend/FileTest.php | 27 ++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Theme/Model/Design/Backend/File.php b/app/code/Magento/Theme/Model/Design/Backend/File.php index b37628e54aa30..54c69cf86682d 100644 --- a/app/code/Magento/Theme/Model/Design/Backend/File.php +++ b/app/code/Magento/Theme/Model/Design/Backend/File.php @@ -20,6 +20,7 @@ use Magento\Framework\UrlInterface; use Magento\MediaStorage\Model\File\UploaderFactory; use Magento\Theme\Model\Design\Config\FileUploader\FileProcessor; +use Magento\MediaStorage\Helper\File\Storage\Database; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -36,6 +37,11 @@ class File extends BackendFile */ private $mime; + /** + * @var Database + */ + private $databaseHelper; + /** * @param Context $context * @param Registry $registry @@ -48,6 +54,7 @@ class File extends BackendFile * @param AbstractResource|null $resource * @param AbstractDb|null $resourceCollection * @param array $data + * @param Database $databaseHelper * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -61,7 +68,8 @@ public function __construct( UrlInterface $urlBuilder, AbstractResource $resource = null, AbstractDb $resourceCollection = null, - array $data = [] + array $data = [], + Database $databaseHelper = null ) { parent::__construct( $context, @@ -76,6 +84,7 @@ public function __construct( $data ); $this->urlBuilder = $urlBuilder; + $this->databaseHelper = $databaseHelper ?: ObjectManager::getInstance()->get(Database::class); } /** @@ -103,6 +112,10 @@ public function beforeSave() $this->getTmpMediaPath($filename), $this->_getUploadDir() . '/' . $filename ); + $this->databaseHelper->renameFile( + $this->getTmpMediaPath($filename), + $this->_getUploadDir() . '/' . $filename + ); if ($result) { $this->_mediaDirectory->delete($this->getTmpMediaPath($filename)); if ($this->_addWhetherScopeInfo()) { diff --git a/app/code/Magento/Theme/Test/Unit/Model/Design/Backend/FileTest.php b/app/code/Magento/Theme/Test/Unit/Model/Design/Backend/FileTest.php index b49b3cb797651..94a6ab0ec565e 100644 --- a/app/code/Magento/Theme/Test/Unit/Model/Design/Backend/FileTest.php +++ b/app/code/Magento/Theme/Test/Unit/Model/Design/Backend/FileTest.php @@ -28,6 +28,11 @@ class FileTest extends \PHPUnit\Framework\TestCase */ private $mime; + /** + * @var \Magento\MediaStorage\Helper\File\Storage\Database|\PHPUnit_Framework_MockObject_MockObject + */ + private $databaseHelper; + public function setUp() { $context = $this->getMockObject(\Magento\Framework\Model\Context::class); @@ -55,6 +60,17 @@ public function setUp() ->disableOriginalConstructor() ->getMock(); + $this->databaseHelper = $this->getMockBuilder(\Magento\MediaStorage\Helper\File\Storage\Database::class) + ->disableOriginalConstructor() + ->getMock(); + + $abstractResource = $this->getMockBuilder(\Magento\Framework\Model\ResourceModel\AbstractResource::class) + ->getMockForAbstractClass(); + + $abstractDb = $this->getMockBuilder(\Magento\Framework\Data\Collection\AbstractDb::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->fileBackend = new File( $context, $registry, @@ -63,7 +79,11 @@ public function setUp() $uploaderFactory, $requestData, $filesystem, - $this->urlBuilder + $this->urlBuilder, + $abstractResource, + $abstractDb, + [], + $this->databaseHelper ); $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -196,6 +216,11 @@ public function testBeforeSave($fileName) ] ); + $this->databaseHelper->expects($this->once()) + ->method('renameFile') + ->with($expectedTmpMediaPath, '/' . $expectedFileName) + ->willReturn(true); + $this->mediaDirectory->expects($this->once()) ->method('copyFile') ->with($expectedTmpMediaPath, '/' . $expectedFileName) From 9d50d634ace7e41b4260d5f4961acc7f9c5cf2f1 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Mon, 29 Apr 2019 09:17:32 -0500 Subject: [PATCH 1192/1295] MAGETWO-99446: Catalog cache gets flushed after Import --- app/code/Magento/Catalog/Block/Product/View.php | 9 +++------ .../Magento/Catalog/Test/Unit/Block/Product/ViewTest.php | 7 +------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Product/View.php b/app/code/Magento/Catalog/Block/Product/View.php index 8055d17a64a84..8b53223de0b9f 100644 --- a/app/code/Magento/Catalog/Block/Product/View.php +++ b/app/code/Magento/Catalog/Block/Product/View.php @@ -169,8 +169,7 @@ public function getAddToCartUrl($product, $additional = []) } /** - * Get JSON encoded configuration array which can be used for JS dynamic - * price calculation depending on product options + * Get JSON encoded configuration which can be used for JS dynamic price calculation depending on product options * * @return string */ @@ -262,6 +261,7 @@ public function isStartCustomization() /** * Get default qty - either as preconfigured, or as 1. + * * Also restricts it by minimal qty. * * @param null|\Magento\Catalog\Model\Product $product @@ -323,10 +323,7 @@ public function getQuantityValidators() public function getIdentities() { $identities = $this->getProduct()->getIdentities(); - $category = $this->_coreRegistry->registry('current_category'); - if ($category) { - $identities[] = Category::CACHE_TAG . '_' . $category->getId(); - } + return $identities; } diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/ViewTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/ViewTest.php index 51bda60b419d0..bb5641fd277ff 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Product/ViewTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/ViewTest.php @@ -67,23 +67,18 @@ public function testGetIdentities() { $productTags = ['cat_p_1']; $product = $this->createMock(\Magento\Catalog\Model\Product::class); - $category = $this->createMock(\Magento\Catalog\Model\Category::class); $product->expects($this->once()) ->method('getIdentities') ->will($this->returnValue($productTags)); - $category->expects($this->once()) - ->method('getId') - ->will($this->returnValue(1)); $this->registryMock->expects($this->any()) ->method('registry') ->will($this->returnValueMap( [ ['product', $product], - ['current_category', $category], ] ) ); - $this->assertEquals(['cat_p_1', 'cat_c_1'], $this->view->getIdentities()); + $this->assertEquals($productTags, $this->view->getIdentities()); } } From 7eb6717f426d811313b916cb4982cfabff7f29fb Mon Sep 17 00:00:00 2001 From: Raoul Rego <rrego@adobe.com> Date: Mon, 29 Apr 2019 09:20:50 -0500 Subject: [PATCH 1193/1295] MAGETWO-99324: Reverting: [Magento Cloud] If a product has a custom attributes which requires a Unique Value, it cannot be saved when there's an active Scheduled Update - Added fix for faulty test behavior --- .../Test/AdminCheckingAttributeValueOnProductEditPageTest.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckingAttributeValueOnProductEditPageTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckingAttributeValueOnProductEditPageTest.xml index 59ac446d7a608..68aac259a22a9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckingAttributeValueOnProductEditPageTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckingAttributeValueOnProductEditPageTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminCheckingAttributeValueOnProductEditPageTest"> + <test name=""> <annotations> <features value="Catalog"/> <stories value="Create/configure Dropdown product attribute"/> @@ -52,6 +52,7 @@ <!--Go to Product edit page--> <amOnPage url="{{AdminProductEditPage.url($$createSimpleProduct.id$$)}}" stepKey="goToProductEditPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> <!--Click on attribute dropdown--> <click selector="{{AdminProductFormSection.customAttributeDropdownField($$createDropdownProductAttribute.attribute[attribute_code]$$)}}" stepKey="clickOnAttributeDropdown"/> <!--Check attribute dropdown options--> From 715eca80dcebd1b84016211109ed7514100dd34a Mon Sep 17 00:00:00 2001 From: Raoul Rego <rrego@adobe.com> Date: Mon, 29 Apr 2019 10:00:26 -0500 Subject: [PATCH 1194/1295] MAGETWO-99324: Reverting: [Magento Cloud] If a product has a custom attributes which requires a Unique Value, it cannot be saved when there's an active Scheduled Update - ReAdded test name --- .../Test/AdminCheckingAttributeValueOnProductEditPageTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckingAttributeValueOnProductEditPageTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckingAttributeValueOnProductEditPageTest.xml index 68aac259a22a9..bd49f643148d9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckingAttributeValueOnProductEditPageTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckingAttributeValueOnProductEditPageTest.xml @@ -7,7 +7,7 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name=""> + <test name="AdminCheckingAttributeValueOnProductEditPageTest"> <annotations> <features value="Catalog"/> <stories value="Create/configure Dropdown product attribute"/> From 4f08d53436eb6f70e71a994ce1f8562f5d10af05 Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@adobe.com> Date: Mon, 29 Apr 2019 10:06:57 -0500 Subject: [PATCH 1195/1295] MQE-1536: Magento\Install\Test\TestCase\InstallTest is failing on php 7.0 --- .../Mtf/Util/Protocol/CurlTransport/BackendDecorator.php | 2 ++ .../tests/app/Magento/Config/Test/Handler/ConfigData/Curl.php | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php index 2fc26a975e159..a5e09232b78a4 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php @@ -81,7 +81,9 @@ protected function authorize() $urls[] = $url2; if (strpos($originalUrl, 'https') !== false) { $urls[] = str_replace('https', 'http', $originalUrl); + $urls[] = str_replace('https', 'http', $url2); } else { + $urls[] = str_replace('http', 'https', $originalUrl); $urls[] = str_replace('http', 'https', $url2); } diff --git a/dev/tests/functional/tests/app/Magento/Config/Test/Handler/ConfigData/Curl.php b/dev/tests/functional/tests/app/Magento/Config/Test/Handler/ConfigData/Curl.php index 66587879848a3..0d89a1d4eba6e 100644 --- a/dev/tests/functional/tests/app/Magento/Config/Test/Handler/ConfigData/Curl.php +++ b/dev/tests/functional/tests/app/Magento/Config/Test/Handler/ConfigData/Curl.php @@ -123,9 +123,9 @@ protected function prepareConfigPath(array $input) */ protected function applyConfigSettings(array $data, $section) { - $url = $this->getUrl($section); $curl = new BackendDecorator(new CurlTransport(), $this->_configuration); $curl->addOption(CURLOPT_HEADER, 1); + $url = $this->getUrl($section); $curl->write($url, $data); $response = $curl->read(); $curl->close(); From 9b9d451a821afad79feeb8def9d0b601041a5664 Mon Sep 17 00:00:00 2001 From: niravkrish <nirav.patel@krishtechnolabs.com> Date: Thu, 18 Apr 2019 15:09:02 +0530 Subject: [PATCH 1196/1295] fixed - issue 21596 --- .../view/frontend/web/js/view/payment.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/payment.js b/app/code/Magento/Checkout/view/frontend/web/js/view/payment.js index c17e5e40d5c98..ad6f39b7d6d5f 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/payment.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/payment.js @@ -66,9 +66,21 @@ define([ navigate: function () { var self = this; - getPaymentInformation().done(function () { - self.isVisible(true); - }); + if(!self.hasShippingMethod()) { + this.isVisible(false); + stepNavigator.setHash('shipping'); + } else { + getPaymentInformation().done(function () { + self.isVisible(true); + }); + } + }, + + /** + * @return {Boolean} + */ + hasShippingMethod: function () { + return window.checkoutConfig.selectedShippingMethod !== null; }, /** From 6645cb21827b2162887b705e63026027343002d5 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Fri, 19 Apr 2019 15:16:10 +0300 Subject: [PATCH 1197/1295] Fix static test. --- app/code/Magento/Checkout/view/frontend/web/js/view/payment.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/payment.js b/app/code/Magento/Checkout/view/frontend/web/js/view/payment.js index ad6f39b7d6d5f..e8994c61b7221 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/payment.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/payment.js @@ -66,7 +66,7 @@ define([ navigate: function () { var self = this; - if(!self.hasShippingMethod()) { + if (!self.hasShippingMethod()) { this.isVisible(false); stepNavigator.setHash('shipping'); } else { From 2a148f665eac3bcebf31110a456e373583946cd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Thu, 4 Apr 2019 14:27:46 +0200 Subject: [PATCH 1198/1295] Fix #12802 - allow to override preference over \Magento\Quote\Api\Data\CartInterface and return correct object from QuoteRepository (+4 squashed commits) Squashed commits: [55b9f3ec52b] Fix #12802 - fix phpmd, mark quoteFactory as deprecated [56ca9a42468] Fix #12802 - change condition in quoteRepository [734212812a4] Fix #12802 - revert change of constructor parameters names [ba8ad543e0f] Fix #12802 - remove instanceof condition --- .../Magento/Quote/Model/QuoteRepository.php | 70 +++++++++------ .../Test/Unit/Model/QuoteRepositoryTest.php | 90 +++++++++++-------- 2 files changed, 93 insertions(+), 67 deletions(-) diff --git a/app/code/Magento/Quote/Model/QuoteRepository.php b/app/code/Magento/Quote/Model/QuoteRepository.php index 01c21bbbe50a7..fa6ab18136688 100644 --- a/app/code/Magento/Quote/Model/QuoteRepository.php +++ b/app/code/Magento/Quote/Model/QuoteRepository.php @@ -5,25 +5,28 @@ */ namespace Magento\Quote\Model; +use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface; +use Magento\Framework\Api\Search\FilterGroup; +use Magento\Framework\Api\SearchCriteria\CollectionProcessor; use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; +use Magento\Framework\Api\SearchCriteriaInterface; use Magento\Framework\App\ObjectManager; -use Magento\Framework\Api\SortOrder; +use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Api\Data\CartInterface; -use Magento\Quote\Model\Quote; -use Magento\Store\Model\StoreManagerInterface; -use Magento\Framework\Api\Search\FilterGroup; -use Magento\Quote\Model\ResourceModel\Quote\Collection as QuoteCollection; -use Magento\Quote\Model\ResourceModel\Quote\CollectionFactory as QuoteCollectionFactory; -use Magento\Framework\Exception\InputException; -use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface; +use Magento\Quote\Api\Data\CartInterfaceFactory; +use Magento\Quote\Api\Data\CartSearchResultsInterfaceFactory; use Magento\Quote\Model\QuoteRepository\SaveHandler; use Magento\Quote\Model\QuoteRepository\LoadHandler; +use Magento\Quote\Model\ResourceModel\Quote\Collection as QuoteCollection; +use Magento\Quote\Model\ResourceModel\Quote\CollectionFactory as QuoteCollectionFactory; +use Magento\Store\Model\StoreManagerInterface; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class QuoteRepository implements \Magento\Quote\Api\CartRepositoryInterface +class QuoteRepository implements CartRepositoryInterface { /** * @var Quote[] @@ -37,6 +40,7 @@ class QuoteRepository implements \Magento\Quote\Api\CartRepositoryInterface /** * @var QuoteFactory + * @deprecated */ protected $quoteFactory; @@ -46,13 +50,13 @@ class QuoteRepository implements \Magento\Quote\Api\CartRepositoryInterface protected $storeManager; /** - * @var \Magento\Quote\Model\ResourceModel\Quote\Collection + * @var QuoteCollection * @deprecated 100.2.0 */ protected $quoteCollection; /** - * @var \Magento\Quote\Api\Data\CartSearchResultsInterfaceFactory + * @var CartSearchResultsInterfaceFactory */ protected $searchResultsDataFactory; @@ -77,39 +81,47 @@ class QuoteRepository implements \Magento\Quote\Api\CartRepositoryInterface private $collectionProcessor; /** - * @var \Magento\Quote\Model\ResourceModel\Quote\CollectionFactory + * @var QuoteCollectionFactory */ private $quoteCollectionFactory; + /** + * @var CartInterfaceFactory + */ + private $cartFactory; + /** * Constructor * * @param QuoteFactory $quoteFactory * @param StoreManagerInterface $storeManager - * @param \Magento\Quote\Model\ResourceModel\Quote\Collection $quoteCollection - * @param \Magento\Quote\Api\Data\CartSearchResultsInterfaceFactory $searchResultsDataFactory + * @param QuoteCollection $quoteCollection + * @param CartSearchResultsInterfaceFactory $searchResultsDataFactory * @param JoinProcessorInterface $extensionAttributesJoinProcessor * @param CollectionProcessorInterface|null $collectionProcessor - * @param \Magento\Quote\Model\ResourceModel\Quote\CollectionFactory|null $quoteCollectionFactory + * @param QuoteCollectionFactory|null $quoteCollectionFactory + * @param CartInterfaceFactory|null $cartFactory * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( QuoteFactory $quoteFactory, StoreManagerInterface $storeManager, - \Magento\Quote\Model\ResourceModel\Quote\Collection $quoteCollection, - \Magento\Quote\Api\Data\CartSearchResultsInterfaceFactory $searchResultsDataFactory, + QuoteCollection $quoteCollection, + CartSearchResultsInterfaceFactory $searchResultsDataFactory, JoinProcessorInterface $extensionAttributesJoinProcessor, CollectionProcessorInterface $collectionProcessor = null, - \Magento\Quote\Model\ResourceModel\Quote\CollectionFactory $quoteCollectionFactory = null + QuoteCollectionFactory $quoteCollectionFactory = null, + CartInterfaceFactory $cartFactory = null ) { $this->quoteFactory = $quoteFactory; $this->storeManager = $storeManager; $this->searchResultsDataFactory = $searchResultsDataFactory; $this->extensionAttributesJoinProcessor = $extensionAttributesJoinProcessor; - $this->collectionProcessor = $collectionProcessor ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\Api\SearchCriteria\CollectionProcessor::class); - $this->quoteCollectionFactory = $quoteCollectionFactory ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Quote\Model\ResourceModel\Quote\CollectionFactory::class); + $this->collectionProcessor = $collectionProcessor ?: ObjectManager::getInstance() + ->get(CollectionProcessor::class); + $this->quoteCollectionFactory = $quoteCollectionFactory ?: ObjectManager::getInstance() + ->get(QuoteCollectionFactory::class); + $this->cartFactory = $cartFactory ?: ObjectManager::getInstance()->get(CartInterfaceFactory::class); } /** @@ -166,7 +178,7 @@ public function getActiveForCustomer($customerId, array $sharedStoreIds = []) /** * {@inheritdoc} */ - public function save(\Magento\Quote\Api\Data\CartInterface $quote) + public function save(CartInterface $quote) { if ($quote->getId()) { $currentQuote = $this->get($quote->getId(), [$quote->getStoreId()]); @@ -186,7 +198,7 @@ public function save(\Magento\Quote\Api\Data\CartInterface $quote) /** * {@inheritdoc} */ - public function delete(\Magento\Quote\Api\Data\CartInterface $quote) + public function delete(CartInterface $quote) { $quoteId = $quote->getId(); $customerId = $quote->getCustomerId(); @@ -203,13 +215,13 @@ public function delete(\Magento\Quote\Api\Data\CartInterface $quote) * @param int $identifier * @param int[] $sharedStoreIds * @throws NoSuchEntityException - * @return Quote + * @return CartInterface */ protected function loadQuote($loadMethod, $loadField, $identifier, array $sharedStoreIds = []) { - /** @var Quote $quote */ - $quote = $this->quoteFactory->create(); - if ($sharedStoreIds) { + /** @var CartInterface $quote */ + $quote = $this->cartFactory->create(); + if ($sharedStoreIds && method_exists($quote, 'setSharedStoreIds')) { $quote->setSharedStoreIds($sharedStoreIds); } $quote->setStoreId($this->storeManager->getStore()->getId())->$loadMethod($identifier); @@ -222,7 +234,7 @@ protected function loadQuote($loadMethod, $loadField, $identifier, array $shared /** * {@inheritdoc} */ - public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria) + public function getList(SearchCriteriaInterface $searchCriteria) { $this->quoteCollection = $this->quoteCollectionFactory->create(); /** @var \Magento\Quote\Api\Data\CartSearchResultsInterface $searchData */ diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteRepositoryTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteRepositoryTest.php index 3101c7d0677a9..095e1760df86f 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/QuoteRepositoryTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteRepositoryTest.php @@ -5,17 +5,31 @@ */ namespace Magento\Quote\Test\Unit\Model; +use Magento\Framework\Api\SearchCriteria; +use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; use Magento\Framework\Api\SortOrder; use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Quote\Api\Data\CartInterface; +use Magento\Quote\Api\Data\CartInterfaceFactory; +use Magento\Quote\Api\Data\CartSearchResultsInterface; +use Magento\Quote\Api\Data\CartSearchResultsInterfaceFactory; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\QuoteRepository; use Magento\Quote\Model\QuoteRepository\LoadHandler; use Magento\Quote\Model\QuoteRepository\SaveHandler; +use Magento\Quote\Model\ResourceModel\Quote\Collection; use Magento\Quote\Model\ResourceModel\Quote\CollectionFactory; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\TestCase; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.TooManyMethods) */ -class QuoteRepositoryTest extends \PHPUnit\Framework\TestCase +class QuoteRepositoryTest extends TestCase { /** * @var \Magento\Quote\Api\CartRepositoryInterface @@ -23,32 +37,32 @@ class QuoteRepositoryTest extends \PHPUnit\Framework\TestCase private $model; /** - * @var \Magento\Quote\Model\QuoteFactory|\PHPUnit_Framework_MockObject_MockObject + * @var CartInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject */ - private $quoteFactoryMock; + private $cartFactoryMock; /** - * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ private $storeManagerMock; /** - * @var \Magento\Store\Model\Store|\PHPUnit_Framework_MockObject_MockObject + * @var Store|\PHPUnit_Framework_MockObject_MockObject */ private $storeMock; /** - * @var \Magento\Quote\Model\Quote|\PHPUnit_Framework_MockObject_MockObject + * @var Quote|\PHPUnit_Framework_MockObject_MockObject */ private $quoteMock; /** - * @var \Magento\Quote\Api\Data\CartSearchResultsInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject + * @var CartSearchResultsInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject */ private $searchResultsDataFactory; /** - * @var \Magento\Quote\Model\ResourceModel\Quote\Collection|\PHPUnit_Framework_MockObject_MockObject + * @var Collection|\PHPUnit_Framework_MockObject_MockObject */ private $quoteCollectionMock; @@ -78,21 +92,21 @@ class QuoteRepositoryTest extends \PHPUnit\Framework\TestCase private $objectManagerMock; /** - * @var \Magento\Quote\Model\ResourceModel\Quote\CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + * @var CollectionFactory|\PHPUnit_Framework_MockObject_MockObject */ private $quoteCollectionFactoryMock; protected function setUp() { - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManager = new ObjectManager($this); - $this->objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); + $this->objectManagerMock = $this->createMock(ObjectManagerInterface::class); \Magento\Framework\App\ObjectManager::setInstance($this->objectManagerMock); - $this->quoteFactoryMock = $this->createPartialMock(\Magento\Quote\Model\QuoteFactory::class, ['create']); - $this->storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); + $this->cartFactoryMock = $this->createPartialMock(CartInterfaceFactory::class, ['create']); + $this->storeManagerMock = $this->createMock(StoreManagerInterface::class); $this->quoteMock = $this->createPartialMock( - \Magento\Quote\Model\Quote::class, + Quote::class, [ 'load', 'loadByIdWithoutStore', @@ -108,35 +122,35 @@ protected function setUp() 'getData' ] ); - $this->storeMock = $this->createMock(\Magento\Store\Model\Store::class); + $this->storeMock = $this->createMock(Store::class); $this->searchResultsDataFactory = $this->createPartialMock( - \Magento\Quote\Api\Data\CartSearchResultsInterfaceFactory::class, + CartSearchResultsInterfaceFactory::class, ['create'] ); $this->quoteCollectionMock = - $this->createMock(\Magento\Quote\Model\ResourceModel\Quote\Collection::class); + $this->createMock(Collection::class); $this->extensionAttributesJoinProcessorMock = $this->createMock( - \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface::class + JoinProcessorInterface::class ); $this->collectionProcessor = $this->createMock( - \Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface::class + CollectionProcessorInterface::class ); $this->quoteCollectionFactoryMock = $this->createPartialMock( - \Magento\Quote\Model\ResourceModel\Quote\CollectionFactory::class, + CollectionFactory::class, ['create'] ); $this->model = $objectManager->getObject( - \Magento\Quote\Model\QuoteRepository::class, + QuoteRepository::class, [ - 'quoteFactory' => $this->quoteFactoryMock, 'storeManager' => $this->storeManagerMock, 'searchResultsDataFactory' => $this->searchResultsDataFactory, 'quoteCollection' => $this->quoteCollectionMock, 'extensionAttributesJoinProcessor' => $this->extensionAttributesJoinProcessorMock, 'collectionProcessor' => $this->collectionProcessor, - 'quoteCollectionFactory' => $this->quoteCollectionFactoryMock + 'quoteCollectionFactory' => $this->quoteCollectionFactoryMock, + 'cartFactory' => $this->cartFactoryMock ] ); @@ -161,7 +175,7 @@ public function testGetWithExceptionById() { $cartId = 14; - $this->quoteFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); + $this->cartFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($this->storeMock); $this->storeMock->expects($this->once())->method('getId')->willReturn(1); $this->quoteMock->expects($this->never())->method('setSharedStoreIds'); @@ -178,7 +192,7 @@ public function testGet() { $cartId = 15; - $this->quoteFactoryMock->expects(static::once()) + $this->cartFactoryMock->expects(static::once()) ->method('create') ->willReturn($this->quoteMock); $this->storeManagerMock->expects(static::once()) @@ -211,7 +225,7 @@ public function testGetForCustomerAfterGet() $cartId = 15; $customerId = 23; - $this->quoteFactoryMock->expects(static::exactly(2)) + $this->cartFactoryMock->expects(static::exactly(2)) ->method('create') ->willReturn($this->quoteMock); $this->storeManagerMock->expects(static::exactly(2)) @@ -249,7 +263,7 @@ public function testGetWithSharedStoreIds() $cartId = 16; $sharedStoreIds = [1, 2]; - $this->quoteFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); + $this->cartFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($this->storeMock); $this->storeMock->expects($this->once())->method('getId')->willReturn(1); $this->quoteMock->expects($this->once()) @@ -275,7 +289,7 @@ public function testGetForCustomer() $cartId = 17; $customerId = 23; - $this->quoteFactoryMock->expects(static::once()) + $this->cartFactoryMock->expects(static::once()) ->method('create') ->willReturn($this->quoteMock); $this->storeManagerMock->expects(static::once()) @@ -310,7 +324,7 @@ public function testGetActiveWithExceptionById() { $cartId = 14; - $this->quoteFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); + $this->cartFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($this->storeMock); $this->storeMock->expects($this->once())->method('getId')->willReturn(1); $this->quoteMock->expects($this->never())->method('setSharedStoreIds'); @@ -332,7 +346,7 @@ public function testGetActiveWithExceptionByIsActive() { $cartId = 15; - $this->quoteFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); + $this->cartFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($this->storeMock); $this->storeMock->expects($this->once())->method('getId')->willReturn(1); $this->quoteMock->expects($this->never())->method('setSharedStoreIds'); @@ -355,7 +369,7 @@ public function testGetActive() { $cartId = 15; - $this->quoteFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); + $this->cartFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($this->storeMock); $this->storeMock->expects($this->once())->method('getId')->willReturn(1); $this->quoteMock->expects($this->never())->method('setSharedStoreIds'); @@ -379,7 +393,7 @@ public function testGetActiveWithSharedStoreIds() $cartId = 16; $sharedStoreIds = [1, 2]; - $this->quoteFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); + $this->cartFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($this->storeMock); $this->storeMock->expects($this->once())->method('getId')->willReturn(1); $this->quoteMock->expects($this->once()) @@ -406,7 +420,7 @@ public function testGetActiveForCustomer() $cartId = 17; $customerId = 23; - $this->quoteFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); + $this->cartFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($this->storeMock); $this->storeMock->expects($this->once())->method('getId')->willReturn(1); $this->quoteMock->expects($this->never())->method('setSharedStoreIds'); @@ -430,14 +444,14 @@ public function testSave() { $cartId = 100; $quoteMock = $this->createPartialMock( - \Magento\Quote\Model\Quote::class, + Quote::class, ['getId', 'getCustomerId', 'getStoreId', 'hasData', 'setData'] ); $quoteMock->expects($this->exactly(3))->method('getId')->willReturn($cartId); $quoteMock->expects($this->once())->method('getCustomerId')->willReturn(2); $quoteMock->expects($this->once())->method('getStoreId')->willReturn(5); - $this->quoteFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); + $this->cartFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($this->storeMock); $this->storeMock->expects($this->once())->method('getId')->willReturn(1); $this->quoteMock->expects($this->once())->method('getId')->willReturn($cartId); @@ -481,8 +495,8 @@ public function testGetList() ->method('load') ->with($cartMock); - $searchResult = $this->createMock(\Magento\Quote\Api\Data\CartSearchResultsInterface::class); - $searchCriteriaMock = $this->createMock(\Magento\Framework\Api\SearchCriteria::class); + $searchResult = $this->createMock(CartSearchResultsInterface::class); + $searchCriteriaMock = $this->createMock(SearchCriteria::class); $this->searchResultsDataFactory ->expects($this->once()) ->method('create') @@ -495,7 +509,7 @@ public function testGetList() $this->extensionAttributesJoinProcessorMock->expects($this->once()) ->method('process') ->with( - $this->isInstanceOf(\Magento\Quote\Model\ResourceModel\Quote\Collection::class) + $this->isInstanceOf(Collection::class) ); $this->quoteCollectionMock->expects($this->atLeastOnce())->method('getItems')->willReturn([$cartMock]); $searchResult->expects($this->once())->method('setTotalCount')->with($pageSize); From ac0e9330ee930073fcc76b3345fc86cf3976193a Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Tue, 16 Apr 2019 17:24:30 +0300 Subject: [PATCH 1199/1295] Fix static tests. --- .../Magento/Quote/Model/QuoteRepository.php | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Quote/Model/QuoteRepository.php b/app/code/Magento/Quote/Model/QuoteRepository.php index fa6ab18136688..30931821ddc7d 100644 --- a/app/code/Magento/Quote/Model/QuoteRepository.php +++ b/app/code/Magento/Quote/Model/QuoteRepository.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Quote\Model; use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface; @@ -24,6 +25,8 @@ use Magento\Store\Model\StoreManagerInterface; /** + * Quote repository. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class QuoteRepository implements CartRepositoryInterface @@ -125,7 +128,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function get($cartId, array $sharedStoreIds = []) { @@ -138,7 +141,7 @@ public function get($cartId, array $sharedStoreIds = []) } /** - * {@inheritdoc} + * @inheritdoc */ public function getForCustomer($customerId, array $sharedStoreIds = []) { @@ -152,7 +155,7 @@ public function getForCustomer($customerId, array $sharedStoreIds = []) } /** - * {@inheritdoc} + * @inheritdoc */ public function getActive($cartId, array $sharedStoreIds = []) { @@ -164,7 +167,7 @@ public function getActive($cartId, array $sharedStoreIds = []) } /** - * {@inheritdoc} + * @inheritdoc */ public function getActiveForCustomer($customerId, array $sharedStoreIds = []) { @@ -176,7 +179,7 @@ public function getActiveForCustomer($customerId, array $sharedStoreIds = []) } /** - * {@inheritdoc} + * @inheritdoc */ public function save(CartInterface $quote) { @@ -196,7 +199,7 @@ public function save(CartInterface $quote) } /** - * {@inheritdoc} + * @inheritdoc */ public function delete(CartInterface $quote) { @@ -232,7 +235,7 @@ protected function loadQuote($loadMethod, $loadField, $identifier, array $shared } /** - * {@inheritdoc} + * @inheritdoc */ public function getList(SearchCriteriaInterface $searchCriteria) { @@ -277,6 +280,7 @@ protected function addFilterGroupToCollection(FilterGroup $filterGroup, QuoteCol /** * Get new SaveHandler dependency for application code. + * * @return SaveHandler * @deprecated 100.1.0 */ @@ -289,6 +293,8 @@ private function getSaveHandler() } /** + * Get load handler instance. + * * @return LoadHandler * @deprecated 100.1.0 */ From f54c202b47c3159f582f8b933358a57f9adb0491 Mon Sep 17 00:00:00 2001 From: Yannis Livasov <ilib@mail.ru> Date: Tue, 9 Apr 2019 02:40:24 +0300 Subject: [PATCH 1200/1295] Shortening currency list in Configuration->General use isset array_flip --- .../Model/Config/Source/Locale/Currency.php | 64 ++++++++++++++++--- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Config/Model/Config/Source/Locale/Currency.php b/app/code/Magento/Config/Model/Config/Source/Locale/Currency.php index b3474674cf76d..5beff0d043ade 100644 --- a/app/code/Magento/Config/Model/Config/Source/Locale/Currency.php +++ b/app/code/Magento/Config/Model/Config/Source/Locale/Currency.php @@ -4,12 +4,15 @@ * See COPYING.txt for license details. */ -/** - * Locale currency source - */ namespace Magento\Config\Model\Config\Source\Locale; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Locale\ListsInterface; + /** + * Locale currency source. + * * @api * @since 100.0.2 */ @@ -21,27 +24,70 @@ class Currency implements \Magento\Framework\Option\ArrayInterface protected $_options; /** - * @var \Magento\Framework\Locale\ListsInterface + * @var ListsInterface */ protected $_localeLists; /** - * @param \Magento\Framework\Locale\ListsInterface $localeLists + * @var ScopeConfigInterface */ - public function __construct(\Magento\Framework\Locale\ListsInterface $localeLists) - { + private $config; + + /** + * @var array + */ + private $installedCurrencies; + + /** + * @param ListsInterface $localeLists + * @param ScopeConfigInterface $config + */ + public function __construct( + ListsInterface $localeLists, + ScopeConfigInterface $config = null + ) { $this->_localeLists = $localeLists; + $this->config = $config ?: ObjectManager::getInstance()->get(ScopeConfigInterface::class); } /** - * @return array + * @inheritdoc */ public function toOptionArray() { if (!$this->_options) { $this->_options = $this->_localeLists->getOptionCurrencies(); } - $options = $this->_options; + + $selected = array_flip($this->getInstalledCurrencies()); + + $options = array_filter( + $this->_options, + function ($option) use ($selected) { + return isset($selected[$option['value']]); + } + ); + return $options; } + + /** + * Retrieve Installed Currencies. + * + * @return array + */ + private function getInstalledCurrencies() + { + if (!$this->installedCurrencies) { + $this->installedCurrencies = explode( + ',', + $this->config->getValue( + 'system/currency/installed', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ) + ); + } + + return $this->installedCurrencies; + } } From f6182eefc4fd413d9e23c70706fbac75402896ab Mon Sep 17 00:00:00 2001 From: niravkrish <nirav.patel@krishtechnolabs.com> Date: Tue, 30 Apr 2019 15:08:21 +0530 Subject: [PATCH 1201/1295] Needs to provide the currency code explicitly in the -bash.00 verification call, otherwise the payment would fail when store's default currency is not USD. --- .../Magento/Paypal/Model/Payflow/Service/Request/SecureToken.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Paypal/Model/Payflow/Service/Request/SecureToken.php b/app/code/Magento/Paypal/Model/Payflow/Service/Request/SecureToken.php index 4afd398da2b54..82ccc59a53c8f 100644 --- a/app/code/Magento/Paypal/Model/Payflow/Service/Request/SecureToken.php +++ b/app/code/Magento/Paypal/Model/Payflow/Service/Request/SecureToken.php @@ -64,6 +64,7 @@ public function requestToken(Quote $quote) $request->setTrxtype(Payflowpro::TRXTYPE_AUTH_ONLY); $request->setVerbosity('HIGH'); $request->setAmt(0); + $request->setCurrency($quote->getBaseCurrencyCode()); $request->setCreatesecuretoken('Y'); $request->setSecuretokenid($this->mathRandom->getUniqueHash()); $request->setReturnurl($this->url->getUrl('paypal/transparent/response')); From c62ab93a45ef9f4dc6fb86d6cd03de127a9665a6 Mon Sep 17 00:00:00 2001 From: niravkrish <nirav.patel@krishtechnolabs.com> Date: Tue, 30 Apr 2019 17:43:26 +0530 Subject: [PATCH 1202/1295] Backport-Adding product from wishlist not adding to cart showing warning message --- .../Customer/Wishlist/Item/Column/Cart.php | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Cart.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Cart.php index b043a8d4b684c..4a6073d6e2e21 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Cart.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Cart.php @@ -14,6 +14,28 @@ */ class Cart extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column { + /** + * @var View + */ + private $productView; + + /** + * @param \Magento\Catalog\Block\Product\Context $context + * @param \Magento\Framework\App\Http\Context $httpContext + * @param \Magento\Catalog\Block\Product\View $productView + * @param array $data + */ + public function __construct( + \Magento\Catalog\Block\Product\Context $context, + \Magento\Framework\App\Http\Context $httpContext, + \Magento\Catalog\Block\Product\View $productView = null, + array $data = [] + ) { + $this->productView = $productView ?: + \Magento\Framework\App\ObjectManager::getInstance()->get(\Magento\Catalog\Block\Product\View::class); + parent::__construct($context, $httpContext, $data); + } + /** * Returns qty to show visually to user * @@ -23,7 +45,9 @@ class Cart extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column public function getAddToCartQty(\Magento\Wishlist\Model\Item $item) { $qty = $item->getQty(); - return $qty ? $qty : 1; + $qty = $qty < $this->productView->getProductDefaultQty($this->getProductItem()) + ? $this->productView->getProductDefaultQty($this->getProductItem()) : $qty; + return $qty ?: 1; } /** From 44027144a61657e0dd6acf41777c17bad6ad4e54 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <d.cheshun@atwix.com> Date: Tue, 30 Apr 2019 15:24:00 +0300 Subject: [PATCH 1203/1295] Fix order of parameters --- .../Wishlist/Block/Customer/Wishlist/Item/Column/Cart.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Cart.php b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Cart.php index 4a6073d6e2e21..97cfd91b32498 100644 --- a/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Cart.php +++ b/app/code/Magento/Wishlist/Block/Customer/Wishlist/Item/Column/Cart.php @@ -28,8 +28,8 @@ class Cart extends \Magento\Wishlist\Block\Customer\Wishlist\Item\Column public function __construct( \Magento\Catalog\Block\Product\Context $context, \Magento\Framework\App\Http\Context $httpContext, - \Magento\Catalog\Block\Product\View $productView = null, - array $data = [] + array $data = [], + \Magento\Catalog\Block\Product\View $productView = null ) { $this->productView = $productView ?: \Magento\Framework\App\ObjectManager::getInstance()->get(\Magento\Catalog\Block\Product\View::class); From 74e912abd53d70b130b437c4f197e09d3bdf0684 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 30 Apr 2019 16:33:54 +0300 Subject: [PATCH 1204/1295] MAGETWO-98948: [2.2.x] UPS CGI url gateway to migrate from http to https --- app/code/Magento/Ups/Model/Carrier.php | 394 ++++++++++-------- app/code/Magento/Ups/etc/config.xml | 2 +- .../Magento/Ups/Model/CarrierTest.php | 26 ++ 3 files changed, 250 insertions(+), 172 deletions(-) diff --git a/app/code/Magento/Ups/Model/Carrier.php b/app/code/Magento/Ups/Model/Carrier.php index 247422e065f75..89ba9721ba1f2 100644 --- a/app/code/Magento/Ups/Model/Carrier.php +++ b/app/code/Magento/Ups/Model/Carrier.php @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - namespace Magento\Ups\Model; use Magento\Framework\HTTP\ClientFactory; @@ -79,7 +76,7 @@ class Carrier extends AbstractCarrierOnline implements CarrierInterface * * @var string */ - protected $_defaultCgiGatewayUrl = 'http://www.ups.com:80/using/services/rave/qcostcgi.cgi'; + protected $_defaultCgiGatewayUrl = 'https://www.ups.com/using/services/rave/qcostcgi.cgi'; /** * Test urls for shipment @@ -323,8 +320,9 @@ public function setRequest(RateRequest $request) } //for UPS, puero rico state for US will assume as puerto rico country - if ($destCountry == self::USA_COUNTRY_ID && ($request->getDestPostcode() == '00912' || - $request->getDestRegionCode() == self::PUERTORICO_COUNTRY_ID) + if ($destCountry == self::USA_COUNTRY_ID + && ($request->getDestPostcode() == '00912' + || $request->getDestRegionCode() == self::PUERTORICO_COUNTRY_ID) ) { $destCountry = self::PUERTORICO_COUNTRY_ID; } @@ -335,9 +333,9 @@ public function setRequest(RateRequest $request) } // For UPS, Las Palmas and Santa Cruz de Tenerife will be represented by Canary Islands country - if ( - $destCountry == self::SPAIN_COUNTRY_ID && - ($request->getDestRegionCode() == self::LAS_PALMAS_REGION_ID || $request->getDestRegionCode() == self::SANTA_CRUZ_DE_TENERIFE_REGION_ID) + if ($destCountry == self::SPAIN_COUNTRY_ID + && ($request->getDestRegionCode() == self::LAS_PALMAS_REGION_ID + || $request->getDestRegionCode() == self::SANTA_CRUZ_DE_TENERIFE_REGION_ID) ) { $destCountry = self::CANARY_ISLANDS_COUNTRY_ID; } @@ -456,7 +454,7 @@ protected function _getCgiQuotes() { $rowRequest = $this->_rawRequest; if (self::USA_COUNTRY_ID == $rowRequest->getDestCountry()) { - $destPostal = substr($rowRequest->getDestPostal(), 0, 5); + $destPostal = substr((string)$rowRequest->getDestPostal(), 0, 5); } else { $destPostal = $rowRequest->getDestPostal(); } @@ -474,7 +472,7 @@ protected function _getCgiQuotes() '47_rate_chart' => $rowRequest->getPickup(), '48_container' => $rowRequest->getContainer(), '49_residential' => $rowRequest->getDestType(), - 'weight_std' => strtolower($rowRequest->getUnitMeasure()), + 'weight_std' => strtolower((string)$rowRequest->getUnitMeasure()), ]; $params['47_rate_chart'] = $params['47_rate_chart']['label']; @@ -495,7 +493,7 @@ protected function _getCgiQuotes() $debugData['result'] = $responseBody; $this->_setCachedQuotes($params, $responseBody); - } catch (\Exception $e) { + } catch (\Throwable $e) { $debugData['result'] = ['error' => $e->getMessage(), 'code' => $e->getCode()]; $responseBody = ''; } @@ -538,7 +536,7 @@ protected function _parseCgiResponse($response) $priceArr = []; if (strlen(trim($response)) > 0) { $rRows = explode("\n", $response); - $allowedMethods = explode(",", $this->getConfigData('allowed_methods')); + $allowedMethods = explode(",", (string)$this->getConfigData('allowed_methods')); foreach ($rRows as $rRow) { $row = explode('%', $rRow); switch (substr($row[0], -1)) { @@ -614,7 +612,7 @@ protected function _getXmlQuotes() $rowRequest = $this->_rawRequest; if (self::USA_COUNTRY_ID == $rowRequest->getDestCountry()) { - $destPostal = substr($rowRequest->getDestPostal(), 0, 5); + $destPostal = substr((string)$rowRequest->getDestPostal(), 0, 5); } else { $destPostal = $rowRequest->getDestPostal(); } @@ -759,7 +757,7 @@ protected function _getXmlQuotes() $xmlResponse = $client->getBody(); $debugData['result'] = $xmlResponse; $this->_setCachedQuotes($xmlRequest, $xmlResponse); - } catch (\Exception $e) { + } catch (\Throwable $e) { $debugData['result'] = ['error' => $e->getMessage(), 'code' => $e->getCode()]; $xmlResponse = ''; } @@ -824,81 +822,30 @@ protected function _parseXmlResponse($xmlResponse) $success = (int)$arr[0]; if ($success === 1) { $arr = $xml->getXpath("//RatingServiceSelectionResponse/RatedShipment"); - $allowedMethods = explode(",", $this->getConfigData('allowed_methods')); + $allowedMethods = explode(",", (string)$this->getConfigData('allowed_methods')); // Negotiated rates $negotiatedArr = $xml->getXpath("//RatingServiceSelectionResponse/RatedShipment/NegotiatedRates"); - $negotiatedActive = $this->getConfigFlag( - 'negotiated_active' - ) && $this->getConfigData( - 'shipper_number' - ) && !empty($negotiatedArr); + $negotiatedActive = $this->getConfigFlag('negotiated_active') + && $this->getConfigData('shipper_number') + && !empty($negotiatedArr); $allowedCurrencies = $this->_currencyFactory->create()->getConfigAllowCurrencies(); + $errorTitle = ''; foreach ($arr as $shipElement) { - $code = (string)$shipElement->Service->Code; - if (in_array($code, $allowedMethods)) { - //The location of tax information is in a different place depending on whether we are using negotiated rates or not - if ($negotiatedActive) { - $includeTaxesArr = $xml->getXpath("//RatingServiceSelectionResponse/RatedShipment/NegotiatedRates/NetSummaryCharges/TotalChargesWithTaxes"); - $includeTaxesActive = $this->getConfigFlag( - 'include_taxes' - ) && !empty($includeTaxesArr); - if ($includeTaxesActive) { - $cost = $shipElement->NegotiatedRates->NetSummaryCharges->TotalChargesWithTaxes->MonetaryValue; - $responseCurrencyCode = $this->mapCurrencyCode( - (string)$shipElement->NegotiatedRates->NetSummaryCharges->TotalChargesWithTaxes->CurrencyCode - ); - } - else { - $cost = $shipElement->NegotiatedRates->NetSummaryCharges->GrandTotal->MonetaryValue; - $responseCurrencyCode = $this->mapCurrencyCode( - (string)$shipElement->NegotiatedRates->NetSummaryCharges->GrandTotal->CurrencyCode - ); - } - } else { - $includeTaxesArr = $xml->getXpath("//RatingServiceSelectionResponse/RatedShipment/TotalChargesWithTaxes"); - $includeTaxesActive = $this->getConfigFlag( - 'include_taxes' - ) && !empty($includeTaxesArr); - if ($includeTaxesActive) { - $cost = $shipElement->TotalChargesWithTaxes->MonetaryValue; - $responseCurrencyCode = $this->mapCurrencyCode( - (string)$shipElement->TotalChargesWithTaxes->CurrencyCode - ); - } - else { - $cost = $shipElement->TotalCharges->MonetaryValue; - $responseCurrencyCode = $this->mapCurrencyCode( - (string)$shipElement->TotalCharges->CurrencyCode - ); - } - } - - //convert price with Origin country currency code to base currency code - $successConversion = true; - if ($responseCurrencyCode) { - if (in_array($responseCurrencyCode, $allowedCurrencies)) { - $cost = (double)$cost * $this->_getBaseCurrencyRate($responseCurrencyCode); - } else { - $errorTitle = __( - 'We can\'t convert a rate from "%1-%2".', - $responseCurrencyCode, - $this->_request->getPackageCurrency()->getCode() - ); - $error = $this->_rateErrorFactory->create(); - $error->setCarrier('ups'); - $error->setCarrierTitle($this->getConfigData('title')); - $error->setErrorMessage($errorTitle); - $successConversion = false; - } - } - - if ($successConversion) { - $costArr[$code] = $cost; - $priceArr[$code] = $this->getMethodPrice((float)$cost, $code); - } - } + $this->processShippingRateForItem( + $shipElement, + $allowedMethods, + $allowedCurrencies, + $costArr, + $priceArr, + $negotiatedActive, + $xml, + $errorTitle + ); + } + if (empty($errorTitle)) { + unset($errorTitle); } } else { $arr = $xml->getXpath("//RatingServiceSelectionResponse/Response/Error/ErrorDescription/text()"); @@ -916,7 +863,7 @@ protected function _parseXmlResponse($xmlResponse) $error = $this->_rateErrorFactory->create(); $error->setCarrier('ups'); $error->setCarrierTitle($this->getConfigData('title')); - if ($this->getConfigData('specificerrmsg') !== '') { + if (!empty($this->getConfigData('specificerrmsg'))) { $errorTitle = $this->getConfigData('specificerrmsg'); } if (!isset($errorTitle)) { @@ -941,6 +888,102 @@ protected function _parseXmlResponse($xmlResponse) return $result; } + /** + * Processing rate for ship element + * + * @param \Magento\Framework\Simplexml\Element $shipElement + * @param array $allowedMethods + * @param array $allowedCurrencies + * @param array $costArr + * @param array $priceArr + * @param bool $negotiatedActive + * @param \Magento\Framework\Simplexml\Config $xml + * @param string $errorTitle + * @return void + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + private function processShippingRateForItem( + \Magento\Framework\Simplexml\Element $shipElement, + array $allowedMethods, + array $allowedCurrencies, + array &$costArr, + array &$priceArr, + bool $negotiatedActive, + \Magento\Framework\Simplexml\Config $xml, + string &$errorTitle + ) { + $code = (string)$shipElement->Service->Code; + if (in_array($code, $allowedMethods)) { + //The location of tax information is in a different place + // depending on whether we are using negotiated rates or not + if ($negotiatedActive) { + $includeTaxesArr = $xml->getXpath( + "//RatingServiceSelectionResponse/RatedShipment/NegotiatedRates" + . "/NetSummaryCharges/TotalChargesWithTaxes" + ); + $includeTaxesActive = $this->getConfigFlag('include_taxes') && !empty($includeTaxesArr); + if ($includeTaxesActive) { + $cost = $shipElement->NegotiatedRates + ->NetSummaryCharges + ->TotalChargesWithTaxes + ->MonetaryValue; + + $responseCurrencyCode = $this->mapCurrencyCode( + (string)$shipElement->NegotiatedRates + ->NetSummaryCharges + ->TotalChargesWithTaxes + ->CurrencyCode + ); + } else { + $cost = $shipElement->NegotiatedRates->NetSummaryCharges->GrandTotal->MonetaryValue; + $responseCurrencyCode = $this->mapCurrencyCode( + (string)$shipElement->NegotiatedRates->NetSummaryCharges->GrandTotal->CurrencyCode + ); + } + } else { + $includeTaxesArr = $xml->getXpath( + "//RatingServiceSelectionResponse/RatedShipment/TotalChargesWithTaxes" + ); + $includeTaxesActive = $this->getConfigFlag('include_taxes') && !empty($includeTaxesArr); + if ($includeTaxesActive) { + $cost = $shipElement->TotalChargesWithTaxes->MonetaryValue; + $responseCurrencyCode = $this->mapCurrencyCode( + (string)$shipElement->TotalChargesWithTaxes->CurrencyCode + ); + } else { + $cost = $shipElement->TotalCharges->MonetaryValue; + $responseCurrencyCode = $this->mapCurrencyCode( + (string)$shipElement->TotalCharges->CurrencyCode + ); + } + } + + //convert price with Origin country currency code to base currency code + $successConversion = true; + if ($responseCurrencyCode) { + if (in_array($responseCurrencyCode, $allowedCurrencies)) { + $cost = (double)$cost * $this->_getBaseCurrencyRate($responseCurrencyCode); + } else { + $errorTitle = __( + 'We can\'t convert a rate from "%1-%2".', + $responseCurrencyCode, + $this->_request->getPackageCurrency()->getCode() + ); + $error = $this->_rateErrorFactory->create(); + $error->setCarrier('ups'); + $error->setCarrierTitle($this->getConfigData('title')); + $error->setErrorMessage($errorTitle); + $successConversion = false; + } + } + + if ($successConversion) { + $costArr[$code] = $cost; + $priceArr[$code] = $this->getMethodPrice((float)$cost, $code); + } + } + } + /** * Get tracking * @@ -1025,7 +1068,6 @@ protected function _getXmlTracking($trackings) $url = $this->getConfigData('tracking_xml_url'); foreach ($trackings as $tracking) { - /** * RequestOption==>'1' to request all activities */ @@ -1040,13 +1082,13 @@ protected function _getXmlTracking($trackings) <IncludeFreight>01</IncludeFreight> </TrackRequest> XMLAuth; - $debugData['request'] = parent::filterDebugData($this->_xmlAccessRequest) . $xmlRequest; + $debugData['request'] = $this->filterDebugData($this->_xmlAccessRequest) . $xmlRequest; try { $client = $this->httpClientFactory->create(); $client->post($url, $this->_xmlAccessRequest . $xmlRequest); $xmlResponse = $client->getBody(); $debugData['result'] = $xmlResponse; - } catch (\Exception $e) { + } catch (\Throwable $e) { $debugData['result'] = ['error' => $e->getMessage(), 'code' => $e->getCode()]; $xmlResponse = ''; } @@ -1071,7 +1113,6 @@ protected function _parseXmlTrackingResponse($trackingValue, $xmlResponse) { $errorTitle = 'For some reason we can\'t retrieve tracking info right now.'; $resultArr = []; - $packageProgress = []; if ($xmlResponse) { $xml = new \Magento\Framework\Simplexml\Config(); @@ -1097,57 +1138,11 @@ protected function _parseXmlTrackingResponse($trackingValue, $xmlResponse) $activityTags = $xml->getXpath("//TrackResponse/Shipment/Package/Activity"); if ($activityTags) { $index = 1; + $resultArr['progressdetail'] = []; foreach ($activityTags as $activityTag) { - $addressArr = []; - if (isset($activityTag->ActivityLocation->Address->City)) { - $addressArr[] = (string)$activityTag->ActivityLocation->Address->City; - } - if (isset($activityTag->ActivityLocation->Address->StateProvinceCode)) { - $addressArr[] = (string)$activityTag->ActivityLocation->Address->StateProvinceCode; - } - if (isset($activityTag->ActivityLocation->Address->CountryCode)) { - $addressArr[] = (string)$activityTag->ActivityLocation->Address->CountryCode; - } - $dateArr = []; - $date = (string)$activityTag->Date; - //YYYYMMDD - $dateArr[] = substr($date, 0, 4); - $dateArr[] = substr($date, 4, 2); - $dateArr[] = substr($date, -2, 2); - - $timeArr = []; - $time = (string)$activityTag->Time; - //HHMMSS - $timeArr[] = substr($time, 0, 2); - $timeArr[] = substr($time, 2, 2); - $timeArr[] = substr($time, -2, 2); - - if ($index === 1) { - $resultArr['status'] = (string)$activityTag->Status->StatusType->Description; - $resultArr['deliverydate'] = implode('-', $dateArr); - //YYYY-MM-DD - $resultArr['deliverytime'] = implode(':', $timeArr); - //HH:MM:SS - $resultArr['deliverylocation'] = (string)$activityTag->ActivityLocation->Description; - $resultArr['signedby'] = (string)$activityTag->ActivityLocation->SignedForByName; - if ($addressArr) { - $resultArr['deliveryto'] = implode(', ', $addressArr); - } - } else { - $tempArr = []; - $tempArr['activity'] = (string)$activityTag->Status->StatusType->Description; - $tempArr['deliverydate'] = implode('-', $dateArr); - //YYYY-MM-DD - $tempArr['deliverytime'] = implode(':', $timeArr); - //HH:MM:SS - if ($addressArr) { - $tempArr['deliverylocation'] = implode(', ', $addressArr); - } - $packageProgress[] = $tempArr; - } + $this->processActivityTagInfo($activityTag, $index, $resultArr); $index++; } - $resultArr['progressdetail'] = $packageProgress; } } else { $arr = $xml->getXpath("//TrackResponse/Response/Error/ErrorDescription/text()"); @@ -1178,6 +1173,68 @@ protected function _parseXmlTrackingResponse($trackingValue, $xmlResponse) return $this->_result; } + /** + * Process tracking info from activity tag + * + * @param \Magento\Framework\Simplexml\Element $activityTag + * @param int $index + * @param array $resultArr + * @return void + */ + private function processActivityTagInfo( + \Magento\Framework\Simplexml\Element $activityTag, + int $index, + array &$resultArr + ) { + $addressArr = []; + if (isset($activityTag->ActivityLocation->Address->City)) { + $addressArr[] = (string)$activityTag->ActivityLocation->Address->City; + } + if (isset($activityTag->ActivityLocation->Address->StateProvinceCode)) { + $addressArr[] = (string)$activityTag->ActivityLocation->Address->StateProvinceCode; + } + if (isset($activityTag->ActivityLocation->Address->CountryCode)) { + $addressArr[] = (string)$activityTag->ActivityLocation->Address->CountryCode; + } + $dateArr = []; + $date = (string)$activityTag->Date; + //YYYYMMDD + $dateArr[] = substr($date, 0, 4); + $dateArr[] = substr($date, 4, 2); + $dateArr[] = substr($date, -2, 2); + + $timeArr = []; + $time = (string)$activityTag->Time; + //HHMMSS + $timeArr[] = substr($time, 0, 2); + $timeArr[] = substr($time, 2, 2); + $timeArr[] = substr($time, -2, 2); + + if ($index === 1) { + $resultArr['status'] = (string)$activityTag->Status->StatusType->Description; + $resultArr['deliverydate'] = implode('-', $dateArr); + //YYYY-MM-DD + $resultArr['deliverytime'] = implode(':', $timeArr); + //HH:MM:SS + $resultArr['deliverylocation'] = (string)$activityTag->ActivityLocation->Description; + $resultArr['signedby'] = (string)$activityTag->ActivityLocation->SignedForByName; + if ($addressArr) { + $resultArr['deliveryto'] = implode(', ', $addressArr); + } + } else { + $tempArr = []; + $tempArr['activity'] = (string)$activityTag->Status->StatusType->Description; + $tempArr['deliverydate'] = implode('-', $dateArr); + //YYYY-MM-DD + $tempArr['deliverytime'] = implode(':', $timeArr); + //HH:MM:SS + if ($addressArr) { + $tempArr['deliverylocation'] = implode(', ', $addressArr); + } + $resultArr['progressdetail'][] = $tempArr; + } + } + /** * Get tracking response * @@ -1215,7 +1272,7 @@ public function getResponse() */ public function getAllowedMethods() { - $allowed = explode(',', $this->getConfigData('allowed_methods')); + $allowed = explode(',', (string)$this->getConfigData('allowed_methods')); $arr = []; $isByCode = $this->getConfigData('type') == 'UPS_XML'; foreach ($allowed as $code) { @@ -1374,11 +1431,8 @@ protected function _formShipmentRequest(\Magento\Framework\DataObject $request) } // ups support reference number only for domestic service - if ($this->_isUSCountry( - $request->getRecipientAddressCountryCode() - ) && $this->_isUSCountry( - $request->getShipperAddressCountryCode() - ) + if ($this->_isUSCountry($request->getRecipientAddressCountryCode()) + && $this->_isUSCountry($request->getShipperAddressCountryCode()) ) { if ($request->getReferenceData()) { $referenceData = $request->getReferenceData() . $request->getPackageId(); @@ -1407,7 +1461,7 @@ protected function _formShipmentRequest(\Magento\Framework\DataObject $request) default: break; } - if (!is_null($serviceOptionsNode)) { + if ($serviceOptionsNode !== null) { $serviceOptionsNode->addChild( 'DeliveryConfirmation' )->addChild( @@ -1422,10 +1476,10 @@ protected function _formShipmentRequest(\Magento\Framework\DataObject $request) ->addChild('BillShipper') ->addChild('AccountNumber', $this->getConfigData('shipper_number')); - if ($request->getPackagingType() != $this->configHelper->getCode('container', 'ULE') && - $request->getShipperAddressCountryCode() == self::USA_COUNTRY_ID && - ($request->getRecipientAddressCountryCode() == 'CA' || - $request->getRecipientAddressCountryCode() == 'PR') + if ($request->getPackagingType() != $this->configHelper->getCode('container', 'ULE') + && $request->getShipperAddressCountryCode() == self::USA_COUNTRY_ID + && ($request->getRecipientAddressCountryCode() == 'CA' + || $request->getRecipientAddressCountryCode() == 'PR') ) { $invoiceLineTotalPart = $shipmentPart->addChild('InvoiceLineTotal'); $invoiceLineTotalPart->addChild('CurrencyCode', $request->getBaseCurrencyCode()); @@ -1453,7 +1507,7 @@ protected function _sendShipmentAcceptRequest(Element $shipmentConfirmResponse) $request = $xmlRequest->addChild('Request'); $request->addChild('RequestAction', 'ShipAccept'); $xmlRequest->addChild('ShipmentDigest', $shipmentConfirmResponse->ShipmentDigest); - $debugData = ['request' => parent::filterDebugData($this->_xmlAccessRequest) . $xmlRequest->asXML()]; + $debugData = ['request' => $this->filterDebugData($this->_xmlAccessRequest) . $xmlRequest->asXML()]; try { $client = $this->httpClientFactory->create(); @@ -1461,14 +1515,14 @@ protected function _sendShipmentAcceptRequest(Element $shipmentConfirmResponse) $xmlResponse = $client->getBody(); $debugData['result'] = $xmlResponse; $this->_setCachedQuotes($xmlRequest, $xmlResponse); - } catch (\Exception $e) { + } catch (\Throwable $e) { $debugData['result'] = ['error' => $e->getMessage(), 'code' => $e->getCode()]; $xmlResponse = ''; } try { $response = $this->_xmlElFactory->create(['data' => $xmlResponse]); - } catch (\Exception $e) { + } catch (\Throwable $e) { $debugData['result'] = ['error' => $e->getMessage(), 'code' => $e->getCode()]; } @@ -1479,6 +1533,7 @@ protected function _sendShipmentAcceptRequest(Element $shipmentConfirmResponse) $shippingLabelContent = (string)$response->ShipmentResults->PackageResults->LabelImage->GraphicImage; $trackingNumber = (string)$response->ShipmentResults->PackageResults->TrackingNumber; + // phpcs:ignore Magento2.Functions.DiscouragedFunction $result->setShippingLabelContent(base64_decode($shippingLabelContent)); $result->setTrackingNumber($trackingNumber); } @@ -1509,7 +1564,6 @@ public function getShipAcceptUrl() * * @param \Magento\Framework\DataObject $request * @return \Magento\Framework\DataObject - * @throws \Exception */ protected function _doShipmentRequest(\Magento\Framework\DataObject $request) { @@ -1521,7 +1575,7 @@ protected function _doShipmentRequest(\Magento\Framework\DataObject $request) $xmlResponse = $this->_getCachedQuotes($xmlRequest); if ($xmlResponse === null) { - $debugData['request'] = parent::filterDebugData($this->_xmlAccessRequest) . $rawXmlRequest; + $debugData['request'] = $this->filterDebugData($this->_xmlAccessRequest) . $rawXmlRequest; $url = $this->getShipConfirmUrl(); $client = $this->httpClientFactory->create(); try { @@ -1529,23 +1583,20 @@ protected function _doShipmentRequest(\Magento\Framework\DataObject $request) $xmlResponse = $client->getBody(); $debugData['result'] = $xmlResponse; $this->_setCachedQuotes($xmlRequest, $xmlResponse); - } catch (\Exception $e) { + } catch (\Throwable $e) { $debugData['result'] = ['code' => $e->getCode(), 'error' => $e->getMessage()]; } } try { $response = $this->_xmlElFactory->create(['data' => $xmlResponse]); - } catch (\Exception $e) { + } catch (\Throwable $e) { $debugData['result'] = ['error' => $e->getMessage(), 'code' => $e->getCode()]; $result->setErrors($e->getMessage()); } if (isset($response->Response->Error) - && in_array( - $response->Response->Error->ErrorSeverity, - ['Hard', 'Transient'] - ) + && in_array($response->Response->Error->ErrorSeverity, ['Hard', 'Transient']) ) { $result->setErrors((string)$response->Response->Error->ErrorDescription); } @@ -1613,20 +1664,20 @@ public function getContainerTypes(\Magento\Framework\DataObject $params = null) ]; } $containerTypes = $containerTypes + [ - '03' => __('UPS Tube'), - '04' => __('PAK'), - '2a' => __('Small Express Box'), - '2b' => __('Medium Express Box'), - '2c' => __('Large Express Box'), - ]; + '03' => __('UPS Tube'), + '04' => __('PAK'), + '2a' => __('Small Express Box'), + '2b' => __('Medium Express Box'), + '2c' => __('Large Express Box'), + ]; } return ['00' => __('Customer Packaging')] + $containerTypes; - } elseif ($countryShipper == self::USA_COUNTRY_ID && - $countryRecipient == self::PUERTORICO_COUNTRY_ID && - ($method == '03' || - $method == '02' || - $method == '01') + } elseif ($countryShipper == self::USA_COUNTRY_ID + && $countryRecipient == self::PUERTORICO_COUNTRY_ID + && ($method == '03' + || $method == '02' + || $method == '01') ) { // Container types should be the same as for domestic $params->setCountryRecipient(self::USA_COUNTRY_ID); @@ -1713,6 +1764,7 @@ public function getCustomizableContainerTypes() /** * Get delivery confirmation level based on origin/destination + * * Return null if delivery confirmation is not acceptable * * @param string|null $countyDestination @@ -1720,7 +1772,7 @@ public function getCustomizableContainerTypes() */ protected function _getDeliveryConfirmationLevel($countyDestination = null) { - if (is_null($countyDestination)) { + if ($countyDestination === null) { return null; } diff --git a/app/code/Magento/Ups/etc/config.xml b/app/code/Magento/Ups/etc/config.xml index 791b325c65e3f..73b10dd5ff41b 100644 --- a/app/code/Magento/Ups/etc/config.xml +++ b/app/code/Magento/Ups/etc/config.xml @@ -37,7 +37,7 @@ <negotiated_active>0</negotiated_active> <include_taxes>0</include_taxes> <mode_xml>1</mode_xml> - <type>UPS</type> + <type>UPS_XML</type> <is_account_live>0</is_account_live> <active_rma>0</active_rma> <is_online>1</is_online> diff --git a/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php b/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php index edea65ee810ba..296eb60b9dac3 100644 --- a/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php +++ b/dev/tests/integration/testsuite/Magento/Ups/Model/CarrierTest.php @@ -57,6 +57,7 @@ public function testGetShipConfirmUrlLive() * @magentoConfigFixture current_store carriers/ups/active 1 * @magentoConfigFixture current_store carriers/ups/allowed_methods 1DA,GND * @magentoConfigFixture current_store carriers/ups/free_method GND + * @magentoConfigFixture current_store carriers/ups/type UPS */ public function testCollectFreeRates() { @@ -76,4 +77,29 @@ public function testCollectFreeRates() $this->assertEquals(0, $methods['GND']['price']); $this->assertNotEquals(0, $methods['1DA']['price']); } + + /** + * Check default UPS carrier parameters. + * + * @return void + */ + public function testValidDefaultParameters() + { + $protocolType = $this->carrier->getConfigData('type'); + $this->assertEquals("UPS_XML", $protocolType, "Default type should be UPS_XML"); + + $gatewayUrl = $this->carrier->getConfigData('gateway_url'); + $this->assertEquals( + "https://www.ups.com/using/services/rave/qcostcgi.cgi", + $gatewayUrl, + "Incorrect gateway url" + ); + + $gatewayXmlUrl = $this->carrier->getConfigData('gateway_xml_url'); + $this->assertEquals( + "https://onlinetools.ups.com/ups.app/xml/Rate", + $gatewayXmlUrl, + "Incorrect gateway XML url" + ); + } } From dd98dde8e8ceaedc6afca34edb6f2a4c5b5fe931 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Wed, 5 Dec 2018 16:52:03 +0200 Subject: [PATCH 1205/1295] MAGETWO-96908: [2.3] Wrong attribute value in flat table --- .../Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php index e8a60d50405a5..5e52101d4bba0 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php @@ -365,7 +365,7 @@ protected function _updateTemporaryTableByStoreValues( [] )->columns( [$columnName => $this->_connection->getIfNullSql('ts.value', 't0.value')] - )->where($attributeCode . ' IS NOT NULL'); + )->where($columnName . ' IS NOT NULL'); if (!empty($changedIds)) { $select->where($this->_connection->quoteInto('et.entity_id IN (?)', $changedIds)); } From 2c5bf7d2f5ec3fd61a525234c1f717e7b32f7083 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Wed, 5 Dec 2018 16:57:44 +0200 Subject: [PATCH 1206/1295] MAGETWO-96908: [2.3] Wrong attribute value in flat table --- .../Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php index 5e52101d4bba0..99ff16a109942 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php @@ -11,6 +11,7 @@ /** * Class FlatTableBuilder + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class FlatTableBuilder @@ -389,7 +390,7 @@ protected function _getTemporaryTableName($tableName) } /** - * Get MetadataPool + * Get metadata pool * * @return \Magento\Framework\EntityManager\MetadataPool */ From 829866193ac93d6404bf48d0322776d311328cfc Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Thu, 27 Dec 2018 15:38:26 +0200 Subject: [PATCH 1207/1295] MAGETWO-96908: [2.3] Wrong attribute value in flat table --- .../Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php index 99ff16a109942..a7273ff37860b 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php @@ -355,6 +355,7 @@ protected function _updateTemporaryTableByStoreValues( //Update not simple attributes (eg. dropdown) $columnName = $attributeCode . $valueFieldSuffix; if (isset($flatColumns[$columnName])) { + $columnValue = $this->_connection->getIfNullSql('ts.value', 't0.value'); $select = $this->_connection->select(); $select->joinLeft( ['t0' => $this->_productIndexerHelper->getTable('eav_attribute_option_value')], @@ -365,8 +366,8 @@ protected function _updateTemporaryTableByStoreValues( 'ts.option_id = et.' . $attributeCode . ' AND ts.store_id = ' . $storeId, [] )->columns( - [$columnName => $this->_connection->getIfNullSql('ts.value', 't0.value')] - )->where($columnName . ' IS NOT NULL'); + [$columnName => $columnValue] + )->where($columnValue . ' IS NOT NULL'); if (!empty($changedIds)) { $select->where($this->_connection->quoteInto('et.entity_id IN (?)', $changedIds)); } From f3595c0debaed5424113f3838f70cf5bd08fc296 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Tue, 30 Apr 2019 16:37:12 -0500 Subject: [PATCH 1208/1295] MAGETWO-99415: Update symfony/http-foundation --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index acdfe237272f1..dbde61451c263 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "442a108524a23e16cbb70c8957cd3097", + "content-hash": "b4aa32fe314ccb486e35a49ecc071a67", "packages": [ { "name": "braintree/braintree_php", @@ -8350,16 +8350,16 @@ }, { "name": "symfony/http-foundation", - "version": "v3.4.23", + "version": "v3.4.26", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "9a96d77ceb1fd913c9d4a89e8a7e1be87604be8a" + "reference": "90454ad44c95d75faf3507d56388056001b74baf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9a96d77ceb1fd913c9d4a89e8a7e1be87604be8a", - "reference": "9a96d77ceb1fd913c9d4a89e8a7e1be87604be8a", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/90454ad44c95d75faf3507d56388056001b74baf", + "reference": "90454ad44c95d75faf3507d56388056001b74baf", "shasum": "" }, "require": { @@ -8400,7 +8400,7 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2019-02-23T15:06:07+00:00" + "time": "2019-04-17T14:51:18+00:00" }, { "name": "symfony/options-resolver", From 1d41abdeeebe08279f69a09fb7a6b5576d9059ab Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Tue, 30 Apr 2019 16:47:49 -0500 Subject: [PATCH 1209/1295] Revert "MAGETWO-99415: Update symfony/http-foundation" This reverts commit f3595c0d --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index dbde61451c263..acdfe237272f1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b4aa32fe314ccb486e35a49ecc071a67", + "content-hash": "442a108524a23e16cbb70c8957cd3097", "packages": [ { "name": "braintree/braintree_php", @@ -8350,16 +8350,16 @@ }, { "name": "symfony/http-foundation", - "version": "v3.4.26", + "version": "v3.4.23", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "90454ad44c95d75faf3507d56388056001b74baf" + "reference": "9a96d77ceb1fd913c9d4a89e8a7e1be87604be8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/90454ad44c95d75faf3507d56388056001b74baf", - "reference": "90454ad44c95d75faf3507d56388056001b74baf", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9a96d77ceb1fd913c9d4a89e8a7e1be87604be8a", + "reference": "9a96d77ceb1fd913c9d4a89e8a7e1be87604be8a", "shasum": "" }, "require": { @@ -8400,7 +8400,7 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2019-04-17T14:51:18+00:00" + "time": "2019-02-23T15:06:07+00:00" }, { "name": "symfony/options-resolver", From 008f8ef03b5eb4b8345bd1657d588bd7cb435e5a Mon Sep 17 00:00:00 2001 From: Szymon Dudzik <szymon.dudzik@ageno.pl> Date: Thu, 28 Mar 2019 22:09:05 +0100 Subject: [PATCH 1210/1295] Fixes issue with non existing file, when adding image to gallery with move option. Fix for #21978 --- app/code/Magento/Catalog/Model/Product/Gallery/Processor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php b/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php index bf7bfbe681929..597b201381228 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php @@ -191,7 +191,7 @@ public function addImage( $mediaGalleryData = $product->getData($attrCode); $position = 0; - $absoluteFilePath = $this->mediaDirectory->getAbsolutePath($file); + $absoluteFilePath = $this->mediaDirectory->getAbsolutePath($destinationFile); $imageMimeType = $this->mime->getMimeType($absoluteFilePath); $imageContent = $this->mediaDirectory->readFile($absoluteFilePath); $imageBase64 = base64_encode($imageContent); From e97f14c8e59f201a6fe8df7ed5326cfb130d0300 Mon Sep 17 00:00:00 2001 From: Nazarn96 <nazarn96@gmail.com> Date: Tue, 30 Apr 2019 10:16:10 +0300 Subject: [PATCH 1211/1295] magento/magento2#22020 static-test-fix --- app/code/Magento/Catalog/Model/Product/Gallery/Processor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php b/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php index 597b201381228..830c1926cc483 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php @@ -489,7 +489,7 @@ protected function getNotDuplicatedFilename($fileName, $dispretionPath) /** * Retrieve data for update attribute * - * @param \Magento\Catalog\Model\Product $object + * @param \Magento\Catalog\Model\Product $object * @return array * @since 101.0.0 */ From 223b71017578ddf9a27989c96f11c60dd74447fb Mon Sep 17 00:00:00 2001 From: Ansari <ziyaurrahman@krishtechnolabs.com> Date: Fri, 12 Apr 2019 12:39:19 +0530 Subject: [PATCH 1212/1295] Resolved Alignment Issue While Editing Order Data containing Downlodable Products #20917 --- .../templates/product/composite/fieldset/downloadable.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Downloadable/view/adminhtml/templates/product/composite/fieldset/downloadable.phtml b/app/code/Magento/Downloadable/view/adminhtml/templates/product/composite/fieldset/downloadable.phtml index c86eb56a39008..79e93abf58b99 100644 --- a/app/code/Magento/Downloadable/view/adminhtml/templates/product/composite/fieldset/downloadable.phtml +++ b/app/code/Magento/Downloadable/view/adminhtml/templates/product/composite/fieldset/downloadable.phtml @@ -29,7 +29,7 @@ value="<?= /* @escapeNotVerified */ $_link->getId() ?>" <?= /* @escapeNotVerified */ $block->getLinkCheckedValue($_link) ?> price="<?= /* @escapeNotVerified */ $block->getCurrencyPrice($_link->getPrice()) ?>"/> <?php endif; ?> - <label for="links_<?= /* @escapeNotVerified */ $_link->getId() ?>" class="label"> + <label for="links_<?= /* @escapeNotVerified */ $_link->getId() ?>" class="label admin__field-label"> <?= $block->escapeHtml($_link->getTitle()) ?> <?php if ($_link->getSampleFile() || $_link->getSampleUrl()): ?>  (<a href="<?= /* @escapeNotVerified */ $block->getLinkSampleUrl($_link) ?>" <?= $block->getIsOpenInNewWindow()?'onclick="this.target=\'_blank\'"':'' ?>><?= /* @escapeNotVerified */ __('sample') ?></a>) From 0abb1374763b8135dc53e8e9abdf24572543c5af Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Thu, 2 May 2019 10:20:16 +0300 Subject: [PATCH 1213/1295] MAGETWO-91328: Checkout doesn't work when AdBlock extension enabled and Google Analytics is enabled --- lib/web/mage/requirejs/resolver.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/mage/requirejs/resolver.js b/lib/web/mage/requirejs/resolver.js index 588a0f8411cff..5ba1f1351bcf6 100644 --- a/lib/web/mage/requirejs/resolver.js +++ b/lib/web/mage/requirejs/resolver.js @@ -34,7 +34,7 @@ define([ * @return {Boolean} */ function isRejected(module) { - return registry[module.id] && registry[module.id].error; + return registry[module.id] && (registry[module.id].inited || registry[module.id].error); } /** From 2211dadcc0602a5bb8e85894e691b6b93713e66b Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Thu, 2 May 2019 15:02:05 +0300 Subject: [PATCH 1214/1295] MAGETWO-98655: Purchasing a downloadable product as guest then creating an account on the onepagesuccess step doesn't link product with account --- ...ustomerFromOrderSuccessPageActionGroup.xml | 21 +++++ ...ontCustomerDownloadableProductsSection.xml | 14 ++++ .../Observer/UpdateLinkPurchasedObserver.php | 80 +++++++++++++++++++ ...tIsPresentInCustomerAccountActionGroup.xml | 20 +++++ .../Test/Mftf/Data/CatalogConfigData.xml | 21 +++++ ...efrontCustomerDownloadableProductsPage.xml | 13 +++ ...ontCustomerDownloadableProductsSection.xml | 14 ++++ ...loadableProductFromGuestToCustomerTest.xml | 64 +++++++++++++++ app/code/Magento/Downloadable/etc/events.xml | 1 + 9 files changed, 248 insertions(+) create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontRegisterCustomerFromOrderSuccessPageActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDownloadableProductsSection.xml create mode 100644 app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php create mode 100644 app/code/Magento/Downloadable/Test/Mftf/ActionGroup/StorefrontAssertDownloadableProductIsPresentInCustomerAccountActionGroup.xml create mode 100644 app/code/Magento/Downloadable/Test/Mftf/Data/CatalogConfigData.xml create mode 100644 app/code/Magento/Downloadable/Test/Mftf/Page/StorefrontCustomerDownloadableProductsPage.xml create mode 100644 app/code/Magento/Downloadable/Test/Mftf/Section/StorefrontCustomerDownloadableProductsSection.xml create mode 100644 app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontRegisterCustomerFromOrderSuccessPageActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontRegisterCustomerFromOrderSuccessPageActionGroup.xml new file mode 100644 index 0000000000000..5e24592bf017f --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontRegisterCustomerFromOrderSuccessPageActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontRegisterCustomerFromOrderSuccessPage"> + <arguments> + <argument name="customer" /> + </arguments> + <click selector="{{CheckoutSuccessRegisterSection.createAccountButton}}" stepKey="clickCreateAccountButton"/> + <fillField selector="{{StorefrontCustomerCreateFormSection.passwordField}}" userInput="{{customer.password}}" stepKey="typePassword"/> + <fillField selector="{{StorefrontCustomerCreateFormSection.confirmPasswordField}}" userInput="{{customer.password}}" stepKey="typeConfirmationPassword"/> + <click selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}" stepKey="clickOnCreateAccount"/> + <see selector="{{StorefrontMessagesSection.success}}" userInput="Thank you for registering" stepKey="verifyAccountCreated"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDownloadableProductsSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDownloadableProductsSection.xml new file mode 100644 index 0000000000000..d45a774077ba0 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDownloadableProductsSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCustomerDownloadableProductsSection"> + <element name="productName" type="text" selector="//table[@id='my-downloadable-products-table']//strong[contains(@class, 'product-name') and normalize-space(.)='{{productName}}']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php b/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php new file mode 100644 index 0000000000000..db391ccda6866 --- /dev/null +++ b/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php @@ -0,0 +1,80 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Downloadable\Observer; + +use Magento\Framework\Event\ObserverInterface; + +/** + * Assign Downloadable links to customer created after issuing guest order. + */ +class UpdateLinkPurchasedObserver implements ObserverInterface +{ + /** + * Core store config + * @var \Magento\Framework\App\Config\ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var \Magento\Downloadable\Model\ResourceModel\Link\Purchased\CollectionFactory + */ + private $purchasedFactory; + + /** + * @var \Magento\Framework\DataObject\Copy + */ + private $objectCopyService; + + /** + * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + * @param \Magento\Downloadable\Model\ResourceModel\Link\Purchased\CollectionFactory $purchasedFactory + * @param \Magento\Framework\DataObject\Copy $objectCopyService + */ + public function __construct( + \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, + \Magento\Downloadable\Model\ResourceModel\Link\Purchased\CollectionFactory $purchasedFactory, + \Magento\Framework\DataObject\Copy $objectCopyService + ) { + $this->scopeConfig = $scopeConfig; + $this->purchasedFactory = $purchasedFactory; + $this->objectCopyService = $objectCopyService; + } + + /** + * Re-save order data after order update. + * + * @param \Magento\Framework\Event\Observer $observer + * @return $this + */ + public function execute(\Magento\Framework\Event\Observer $observer) + { + $order = $observer->getEvent()->getOrder(); + + if (!$order->getId()) { + //order not saved in the database + return $this; + } + + $purchasedLinks = $this->purchasedFactory->create()->addFieldToFilter( + 'order_id', + ['eq' => $order->getId()] + ); + + foreach ($purchasedLinks as $linkPurchased) { + $this->objectCopyService->copyFieldsetToTarget( + \downloadable_sales_copy_order::class, + 'to_downloadable', + $order, + $linkPurchased + ); + $linkPurchased->save(); + } + + return $this; + } +} diff --git a/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/StorefrontAssertDownloadableProductIsPresentInCustomerAccountActionGroup.xml b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/StorefrontAssertDownloadableProductIsPresentInCustomerAccountActionGroup.xml new file mode 100644 index 0000000000000..2e4afbd803205 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/StorefrontAssertDownloadableProductIsPresentInCustomerAccountActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontAssertDownloadableProductIsPresentInCustomerAccount"> + <arguments> + <argument name="product"/> + </arguments> + <amOnPage url="{{StorefrontCustomerDashboardPage.url}}" stepKey="goToMyAccountPage"/> + <click selector="{{StorefrontCustomerSidebarSection.sidebarTab('My Downloadable Products')}}" stepKey="clickDownloadableProducts"/> + <waitForPageLoad stepKey="waitForDownloadableProductsPageLoad" /> + <seeElement selector="{{StorefrontCustomerDownloadableProductsSection.productName(product.name)}}" stepKey="seeStorefontDownloadableProductsProductName" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Data/CatalogConfigData.xml b/app/code/Magento/Downloadable/Test/Mftf/Data/CatalogConfigData.xml new file mode 100644 index 0000000000000..8bb81f9c7579d --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Data/CatalogConfigData.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="EnableGuestCheckoutWithDownloadableItems"> + <data key="path">catalog/downloadable/disable_guest_checkout</data> + <data key="scope_id">0</data> + <data key="value">0</data> + </entity> + <entity name="DisableGuestCheckoutWithDownloadableItems"> + <data key="path">catalog/downloadable/disable_guest_checkout</data> + <data key="scope_id">0</data> + <data key="value">1</data> + </entity> +</entities> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Page/StorefrontCustomerDownloadableProductsPage.xml b/app/code/Magento/Downloadable/Test/Mftf/Page/StorefrontCustomerDownloadableProductsPage.xml new file mode 100644 index 0000000000000..eafb37111fda2 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Page/StorefrontCustomerDownloadableProductsPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="StorefrontCustomerDownloadableProductsPage" url="downloadable/customer/products/" area="storefront" module="Magento_Downloadable"> + <section name="StorefrontCustomerDownloadableProductsSection"/> + </page> +</pages> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Section/StorefrontCustomerDownloadableProductsSection.xml b/app/code/Magento/Downloadable/Test/Mftf/Section/StorefrontCustomerDownloadableProductsSection.xml new file mode 100644 index 0000000000000..d45a774077ba0 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Section/StorefrontCustomerDownloadableProductsSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCustomerDownloadableProductsSection"> + <element name="productName" type="text" selector="//table[@id='my-downloadable-products-table']//strong[contains(@class, 'product-name') and normalize-space(.)='{{productName}}']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml new file mode 100644 index 0000000000000..0a1bf55758687 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="LinkDownloadableProductFromGuestToCustomerTest"> + <annotations> + <stories value="Customer Account"/> + <title value="Customer should see downloadable products after place order as guest and registering after that"/> + <description value="Verify that in 'My Downloadable Products' section in customer account user can see products."/> + <severity value="AVERAGE"/> + <useCaseId value="MAGETWO-98655"/> + <testCaseId value="MC-16148"/> + </annotations> + <before> + <magentoCLI command="config:set {{EnableGuestCheckoutWithDownloadableItems.path}} {{EnableGuestCheckoutWithDownloadableItems.value}}" stepKey="enableGuestCheckoutWithDownloadableItems" /> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="ApiDownloadableProduct" stepKey="createProduct"/> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink"> + <requiredEntity createDataKey="createProduct"/> + </createData> + </before> + <after> + <magentoCLI command="config:set {{DisableGuestCheckoutWithDownloadableItems.path}} {{DisableGuestCheckoutWithDownloadableItems.value}}" stepKey="disableGuestCheckoutWithDownloadableItems" /> + <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> + <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> + </after> + <!--Step 1: Go to Storefront as Guest--> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> + <!--Step 2: Add downloadable product to shopping cart--> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="amOnStorefrontProductPage"/> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="addProductToCart"> + <argument name="product" value="$$createProduct$$"/> + <argument name="productCount" value="1"/> + </actionGroup> + <!--Step 3: Go to checkout--> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart" /> + <!--Step 4: Select Check/Money Order payment, fill required fields and click Update and Place Order--> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyOrderPayment"/> + <actionGroup ref="GuestCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress"> + <argument name="customerVar" value="Simple_US_NY_Customer"/> + <argument name="customerAddressVar" value="US_Address_NY"/> + </actionGroup> + <click selector="{{CheckoutShippingSection.updateAddress}}" stepKey="saveAddress"/> + <waitForPageLoad stepKey="waitUpdateAddress"/> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="placeOrder"> + <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage" /> + </actionGroup> + <!--Step 5: Create customer account after placing order--> + <actionGroup ref="StorefrontRegisterCustomerFromOrderSuccessPage" stepKey="createCustomerAfterPlaceOrder"> + <argument name="customer" value="CustomerEntityOne"/> + </actionGroup> + <!--Step 6: Go To My Account -> My Downloadable Products and check if downloadable product link exist--> + <actionGroup ref="StorefrontAssertDownloadableProductIsPresentInCustomerAccount" stepKey="seeStorefontMyDownloadableProductsProductName"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Downloadable/etc/events.xml b/app/code/Magento/Downloadable/etc/events.xml index 5a985fc33802e..21cc50ddc9669 100644 --- a/app/code/Magento/Downloadable/etc/events.xml +++ b/app/code/Magento/Downloadable/etc/events.xml @@ -11,6 +11,7 @@ </event> <event name="sales_order_save_after"> <observer name="downloadable_observer" instance="Magento\Downloadable\Observer\SetLinkStatusObserver" /> + <observer name="downloadable_observer_assign_customer" instance="Magento\Downloadable\Observer\UpdateLinkPurchasedObserver" /> </event> <event name="sales_model_service_quote_submit_success"> <observer name="checkout_type_onepage_save_order_after" instance="Magento\Downloadable\Observer\SetHasDownloadableProductsObserver" /> From 67e2e85b0a6921cc82d664ebe51e8a0cb2371b8d Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Thu, 2 May 2019 16:18:11 +0300 Subject: [PATCH 1215/1295] MAGETWO-98655: Purchasing a downloadable product as guest then creating an account on the onepagesuccess step doesn't link product with account --- .../Test/LinkDownloadableProductFromGuestToCustomerTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml index 0a1bf55758687..cd73ffa80661c 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml @@ -46,7 +46,7 @@ <argument name="customerVar" value="Simple_US_NY_Customer"/> <argument name="customerAddressVar" value="US_Address_NY"/> </actionGroup> - <click selector="{{CheckoutShippingSection.updateAddress}}" stepKey="saveAddress"/> + <click selector="{{CheckoutPaymentSection.update}}" stepKey="clickUpdateButton" /> <waitForPageLoad stepKey="waitUpdateAddress"/> <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="placeOrder"> <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage"/> From 8db2e21fa7cb70256cf384c3cf3107ade99b8e54 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Fri, 29 Mar 2019 11:23:25 -0500 Subject: [PATCH 1216/1295] MAGETWO-93627: Shopping cart is emptied after reset password procedure --- .../Customer/Model/AccountManagement.php | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Customer/Model/AccountManagement.php b/app/code/Magento/Customer/Model/AccountManagement.php index 379b4979e2070..e90ab18172ecc 100644 --- a/app/code/Magento/Customer/Model/AccountManagement.php +++ b/app/code/Magento/Customer/Model/AccountManagement.php @@ -475,7 +475,7 @@ private function getAuthentication() } /** - * {@inheritdoc} + * @inheritdoc */ public function resendConfirmation($email, $websiteId = null, $redirectUrl = '') { @@ -498,7 +498,7 @@ public function resendConfirmation($email, $websiteId = null, $redirectUrl = '') } /** - * {@inheritdoc} + * @inheritdoc */ public function activate($email, $confirmationKey) { @@ -507,7 +507,7 @@ public function activate($email, $confirmationKey) } /** - * {@inheritdoc} + * @inheritdoc */ public function activateById($customerId, $confirmationKey) { @@ -547,7 +547,7 @@ private function activateCustomer($customer, $confirmationKey) } /** - * {@inheritdoc} + * @inheritdoc */ public function authenticate($username, $password) { @@ -582,7 +582,7 @@ public function authenticate($username, $password) } /** - * {@inheritdoc} + * @inheritdoc */ public function validateResetPasswordLinkToken($customerId, $resetPasswordLinkToken) { @@ -591,7 +591,7 @@ public function validateResetPasswordLinkToken($customerId, $resetPasswordLinkTo } /** - * {@inheritdoc} + * @inheritdoc */ public function initiatePasswordReset($email, $template, $websiteId = null) { @@ -677,7 +677,7 @@ private function matchCustomerByRpToken(string $rpToken): CustomerInterface } /** - * {@inheritdoc} + * @inheritdoc */ public function resetPassword($email, $resetToken, $newPassword) { @@ -706,7 +706,12 @@ public function resetPassword($email, $resetToken, $newPassword) $customerSecure->setPasswordHash($this->createPasswordHash($newPassword)); $this->getAuthentication()->unlock($customer->getId()); $this->destroyCustomerSessions($customer->getId()); - $this->sessionManager->destroy(); + if ($this->sessionManager->isSessionExists() && !headers_sent()) { + //delete old session and move data to the new session + //use this instead of $this->sessionManager->regenerateId because last one doesn't delete old session + // phpcs:ignore Magento2.Functions.DiscouragedFunction + session_regenerate_id(true); + } $this->customerRepository->save($customer); return true; @@ -798,7 +803,7 @@ protected function getMinPasswordLength() } /** - * {@inheritdoc} + * @inheritdoc */ public function getConfirmationStatus($customerId) { @@ -814,7 +819,7 @@ public function getConfirmationStatus($customerId) } /** - * {@inheritdoc} + * @inheritdoc */ public function createAccount(CustomerInterface $customer, $password = null, $redirectUrl = '', $extensions = []) { @@ -835,7 +840,7 @@ public function createAccount(CustomerInterface $customer, $password = null, $re } /** - * {@inheritdoc} + * @inheritdoc * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ @@ -920,7 +925,7 @@ public function createAccountWithPasswordHash( } /** - * {@inheritdoc} + * @inheritdoc */ public function getDefaultBillingAddress($customerId) { @@ -929,7 +934,7 @@ public function getDefaultBillingAddress($customerId) } /** - * {@inheritdoc} + * @inheritdoc */ public function getDefaultShippingAddress($customerId) { @@ -974,7 +979,7 @@ protected function sendEmailConfirmation(CustomerInterface $customer, $redirectU } /** - * {@inheritdoc} + * @inheritdoc */ public function changePassword($email, $currentPassword, $newPassword) { @@ -987,7 +992,7 @@ public function changePassword($email, $currentPassword, $newPassword) } /** - * {@inheritdoc} + * @inheritdoc */ public function changePasswordById($customerId, $currentPassword, $newPassword) { @@ -1046,6 +1051,8 @@ protected function createPasswordHash($password) } /** + * Get EAV validator + * * @return Backend */ private function getEavValidator() @@ -1057,7 +1064,7 @@ private function getEavValidator() } /** - * {@inheritdoc} + * @inheritdoc */ public function validate(CustomerInterface $customer) { @@ -1082,7 +1089,7 @@ public function validate(CustomerInterface $customer) } /** - * {@inheritdoc} + * @inheritdoc */ public function isEmailAvailable($customerEmail, $websiteId = null) { @@ -1098,7 +1105,7 @@ public function isEmailAvailable($customerEmail, $websiteId = null) } /** - * {@inheritDoc} + * @inheritdoc */ public function isCustomerInStore($customerWebsiteId, $storeId) { @@ -1549,6 +1556,7 @@ private function getEmailNotification() /** * Destroy all active customer sessions by customer id (current session will not be destroyed). + * * Customer sessions which should be deleted are collecting from the "customer_visitor" table considering * configured session lifetime. * From 94ea76d52b5dc38a28c7167ba476c67f5881e01f Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Thu, 2 May 2019 11:27:43 -0500 Subject: [PATCH 1217/1295] Revert "PR #4142" --- .../view/payment/method-renderer/cc-form.js | 1 + .../payment/method-renderer/hosted-fields.js | 1 + .../frontend/web/js/view/payment/default.js | 15 ++-- .../frontend/web/js/view/payment/iframe.js | 18 ++--- .../method-renderer/payflowpro-method.js | 5 +- .../Magento/Quote/Model/QuoteManagement.php | 17 ++--- .../Quote/Model/SubmitQuoteValidator.php | 71 ------------------- .../Test/Unit/Model/QuoteManagementTest.php | 27 +++---- .../Sales/Model/Service/OrderService.php | 38 ++++------ .../Unit/Model/Service/OrderServiceTest.php | 13 +--- .../Fixtures/quote_without_customer_email.php | 28 -------- .../Quote/Model/QuoteManagementTest.php | 29 -------- .../payment/method-renderer/cc-form.test.js | 16 +++++ 13 files changed, 57 insertions(+), 222 deletions(-) delete mode 100644 app/code/Magento/Quote/Model/SubmitQuoteValidator.php delete mode 100644 dev/tests/integration/testsuite/Magento/Quote/Fixtures/quote_without_customer_email.php diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js index 73a7b10d5d30f..d078cacb96c2d 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js @@ -79,6 +79,7 @@ define( */ onError: function (response) { braintree.showError($t('Payment ' + this.getTitle() + ' can\'t be initialized')); + this.isPlaceOrderActionAllowed(true); throw response.message; }, diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/hosted-fields.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/hosted-fields.js index cb9110ee8afa1..bfdc2974dd4b0 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/hosted-fields.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/hosted-fields.js @@ -156,6 +156,7 @@ define([ */ placeOrderClick: function () { if (this.validateCardType()) { + this.isPlaceOrderActionAllowed(false); $(this.getSelector('submit')).trigger('click'); } }, diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/payment/default.js b/app/code/Magento/Checkout/view/frontend/web/js/view/payment/default.js index 1b5463c0770a3..7b200860c4d55 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/payment/default.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/payment/default.js @@ -133,14 +133,15 @@ define([ event.preventDefault(); } - if (this.validate() && - additionalValidators.validate() && - this.isPlaceOrderActionAllowed() === true - ) { + if (this.validate() && additionalValidators.validate()) { this.isPlaceOrderActionAllowed(false); this.getPlaceOrderDeferredObject() - .done( + .fail( + function () { + self.isPlaceOrderActionAllowed(true); + } + ).done( function () { self.afterPlaceOrder(); @@ -148,10 +149,6 @@ define([ redirectOnSuccessAction.execute(); } } - ).always( - function () { - self.isPlaceOrderActionAllowed(true); - } ); return true; diff --git a/app/code/Magento/Payment/view/frontend/web/js/view/payment/iframe.js b/app/code/Magento/Payment/view/frontend/web/js/view/payment/iframe.js index 1e352e4297131..5eba4fd89d338 100644 --- a/app/code/Magento/Payment/view/frontend/web/js/view/payment/iframe.js +++ b/app/code/Magento/Payment/view/frontend/web/js/view/payment/iframe.js @@ -114,12 +114,8 @@ define([ * @override */ placeOrder: function () { - var self = this; + if (this.validateHandler() && additionalValidators.validate()) { - if (this.validateHandler() && - additionalValidators.validate() && - this.isPlaceOrderActionAllowed() === true - ) { fullScreenLoader.startLoader(); this.isPlaceOrderActionAllowed(false); @@ -131,15 +127,8 @@ define([ method: this.getCode() } ) - ).done( - this.done.bind(this) - ).fail( - this.fail.bind(this) - ).always( - function () { - self.isPlaceOrderActionAllowed(true); - } - ); + ).done(this.done.bind(this)) + .fail(this.fail.bind(this)); this.initTimeoutHandler(); } @@ -203,6 +192,7 @@ define([ */ fail: function () { fullScreenLoader.stopLoader(); + this.isPlaceOrderActionAllowed(true); return this; }, diff --git a/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/payflowpro-method.js b/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/payflowpro-method.js index 24d06b7d0f8f2..786f1a5aa85fd 100644 --- a/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/payflowpro-method.js +++ b/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/payflowpro-method.js @@ -79,10 +79,7 @@ define([ placeOrder: function () { var self = this; - if (this.validateHandler() && - additionalValidators.validate() && - this.isPlaceOrderActionAllowed() === true - ) { + if (this.validateHandler() && additionalValidators.validate()) { this.isPlaceOrderActionAllowed(false); fullScreenLoader.startLoader(); $.when( diff --git a/app/code/Magento/Quote/Model/QuoteManagement.php b/app/code/Magento/Quote/Model/QuoteManagement.php index 79170ad90832c..b7d994138026a 100644 --- a/app/code/Magento/Quote/Model/QuoteManagement.php +++ b/app/code/Magento/Quote/Model/QuoteManagement.php @@ -37,9 +37,9 @@ class QuoteManagement implements \Magento\Quote\Api\CartManagementInterface protected $eventManager; /** - * @var SubmitQuoteValidator + * @var QuoteValidator */ - private $submitQuoteValidator; + protected $quoteValidator; /** * @var OrderFactory @@ -148,7 +148,7 @@ class QuoteManagement implements \Magento\Quote\Api\CartManagementInterface /** * @param EventManager $eventManager - * @param SubmitQuoteValidator $submitQuoteValidator + * @param QuoteValidator $quoteValidator * @param OrderFactory $orderFactory * @param OrderManagement $orderManagement * @param CustomerManagement $customerManagement @@ -173,7 +173,7 @@ class QuoteManagement implements \Magento\Quote\Api\CartManagementInterface */ public function __construct( EventManager $eventManager, - SubmitQuoteValidator $submitQuoteValidator, + QuoteValidator $quoteValidator, OrderFactory $orderFactory, OrderManagement $orderManagement, CustomerManagement $customerManagement, @@ -196,7 +196,7 @@ public function __construct( \Magento\Customer\Api\AddressRepositoryInterface $addressRepository = null ) { $this->eventManager = $eventManager; - $this->submitQuoteValidator = $submitQuoteValidator; + $this->quoteValidator = $quoteValidator; $this->orderFactory = $orderFactory; $this->orderManagement = $orderManagement; $this->customerManagement = $customerManagement; @@ -282,7 +282,6 @@ public function assignCustomer($cartId, $customerId, $storeId) throw new StateException( __('Cannot assign customer to the given cart. Customer already has active cart.') ); - // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { } @@ -455,7 +454,7 @@ protected function resolveItems(QuoteEntity $quote) protected function submitQuote(QuoteEntity $quote, $orderData = []) { $order = $this->orderFactory->create(); - $this->submitQuoteValidator->validateQuote($quote); + $this->quoteValidator->validateBeforeSubmit($quote); if (!$quote->getCustomerIsGuest()) { if ($quote->getCustomerId()) { $this->_prepareCustomerQuote($quote); @@ -510,7 +509,6 @@ protected function submitQuote(QuoteEntity $quote, $orderData = []) $order->setCustomerFirstname($quote->getCustomerFirstname()); $order->setCustomerMiddlename($quote->getCustomerMiddlename()); $order->setCustomerLastname($quote->getCustomerLastname()); - $this->submitQuoteValidator->validateOrder($order); $this->eventManager->dispatch( 'sales_model_service_quote_submit_before', @@ -627,13 +625,12 @@ private function rollbackAddresses( 'exception' => $e, ] ); - // phpcs:ignore Magento2.Exceptions.ThrowCatch } catch (\Exception $consecutiveException) { $message = sprintf( "An exception occurred on 'sales_model_service_quote_submit_failure' event: %s", $consecutiveException->getMessage() ); - // phpcs:ignore Magento2.Exceptions.DirectThrow + throw new \Exception($message, 0, $e); } } diff --git a/app/code/Magento/Quote/Model/SubmitQuoteValidator.php b/app/code/Magento/Quote/Model/SubmitQuoteValidator.php deleted file mode 100644 index 76d31f94d2a62..0000000000000 --- a/app/code/Magento/Quote/Model/SubmitQuoteValidator.php +++ /dev/null @@ -1,71 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Quote\Model; - -use Magento\Framework\Exception\LocalizedException; -use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Address\Validator as OrderAddressValidator; - -/** - * Validates quote and order before quote submit. - */ -class SubmitQuoteValidator -{ - /** - * @var QuoteValidator - */ - private $quoteValidator; - - /** - * @var OrderAddressValidator - */ - private $orderAddressValidator; - - /** - * @param QuoteValidator $quoteValidator - * @param OrderAddressValidator $orderAddressValidator - */ - public function __construct( - QuoteValidator $quoteValidator, - OrderAddressValidator $orderAddressValidator - ) { - $this->quoteValidator = $quoteValidator; - $this->orderAddressValidator = $orderAddressValidator; - } - - /** - * Validates quote. - * - * @param Quote $quote - * @return void - * @throws LocalizedException - */ - public function validateQuote(Quote $quote) - { - $this->quoteValidator->validateBeforeSubmit($quote); - } - - /** - * Validates order. - * - * @param Order $order - * @return void - * @throws LocalizedException - */ - public function validateOrder(Order $order) - { - foreach ($order->getAddresses() as $address) { - $errors = $this->orderAddressValidator->validate($address); - if (!empty($errors)) { - throw new LocalizedException( - __("Failed address validation:\n%1", implode("\n", $errors)) - ); - } - } - } -} diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php index aca490ae2b1a1..cdfd0df8f9927 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php @@ -13,8 +13,6 @@ /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.TooManyFields) - * @SuppressWarnings(PHPMD.TooManyPublicMethods) - * @SuppressWarnings(PHPMD.ExcessiveClassLength) */ class QuoteManagementTest extends \PHPUnit\Framework\TestCase { @@ -24,9 +22,9 @@ class QuoteManagementTest extends \PHPUnit\Framework\TestCase protected $model; /** - * @var \Magento\Quote\Model\SubmitQuoteValidator|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Quote\Model\QuoteValidator|\PHPUnit_Framework_MockObject_MockObject */ - protected $submitQuoteValidator; + protected $quoteValidator; /** * @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject @@ -145,7 +143,7 @@ protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->submitQuoteValidator = $this->createMock(\Magento\Quote\Model\SubmitQuoteValidator::class); + $this->quoteValidator = $this->createMock(\Magento\Quote\Model\QuoteValidator::class); $this->eventManager = $this->getMockForAbstractClass(\Magento\Framework\Event\ManagerInterface::class); $this->orderFactory = $this->createPartialMock( \Magento\Sales\Api\Data\OrderInterfaceFactory::class, @@ -212,7 +210,7 @@ protected function setUp() \Magento\Quote\Model\QuoteManagement::class, [ 'eventManager' => $this->eventManager, - 'submitQuoteValidator' => $this->submitQuoteValidator, + 'quoteValidator' => $this->quoteValidator, 'orderFactory' => $this->orderFactory, 'orderManagement' => $this->orderManagement, 'customerManagement' => $this->customerManagement, @@ -562,9 +560,7 @@ public function testSubmit() $shippingAddress ); - $this->submitQuoteValidator->expects($this->once()) - ->method('validateQuote') - ->with($quote); + $this->quoteValidator->expects($this->once())->method('validateBeforeSubmit')->with($quote); $this->quoteAddressToOrder->expects($this->once()) ->method('convert') ->with($shippingAddress, $orderData) @@ -659,7 +655,7 @@ public function testPlaceOrderIfCustomerIsGuest() ->setConstructorArgs( [ 'eventManager' => $this->eventManager, - 'quoteValidator' => $this->submitQuoteValidator, + 'quoteValidator' => $this->quoteValidator, 'orderFactory' => $this->orderFactory, 'orderManagement' => $this->orderManagement, 'customerManagement' => $this->customerManagement, @@ -716,7 +712,7 @@ public function testPlaceOrder() ->setConstructorArgs( [ 'eventManager' => $this->eventManager, - 'quoteValidator' => $this->submitQuoteValidator, + 'quoteValidator' => $this->quoteValidator, 'orderFactory' => $this->orderFactory, 'orderManagement' => $this->orderManagement, 'customerManagement' => $this->customerManagement, @@ -938,9 +934,6 @@ protected function prepareOrderFactory( return $order; } - /** - * @throws NoSuchEntityException - */ public function testGetCartForCustomer() { $customerId = 100; @@ -985,9 +978,6 @@ protected function setPropertyValue(&$object, $property, $value) return $object; } - /** - * @throws \Magento\Framework\Exception\LocalizedException - */ public function testSubmitForCustomer() { $orderData = []; @@ -1020,8 +1010,7 @@ public function testSubmitForCustomer() $shippingAddress ); - $this->submitQuoteValidator->method('validateQuote') - ->with($quote); + $this->quoteValidator->expects($this->once())->method('validateBeforeSubmit')->with($quote); $this->quoteAddressToOrder->expects($this->once()) ->method('convert') ->with($shippingAddress, $orderData) diff --git a/app/code/Magento/Sales/Model/Service/OrderService.php b/app/code/Magento/Sales/Model/Service/OrderService.php index 2e062caca9a24..e4a71f028cc82 100644 --- a/app/code/Magento/Sales/Model/Service/OrderService.php +++ b/app/code/Magento/Sales/Model/Service/OrderService.php @@ -7,7 +7,6 @@ use Magento\Sales\Api\OrderManagementInterface; use Magento\Payment\Gateway\Command\CommandException; -use Psr\Log\LoggerInterface; /** * Class OrderService @@ -56,11 +55,6 @@ class OrderService implements OrderManagementInterface */ private $paymentFailures; - /** - * @var LoggerInterface - */ - private $logger; - /** * Constructor * @@ -71,8 +65,7 @@ class OrderService implements OrderManagementInterface * @param \Magento\Sales\Model\OrderNotifier $notifier * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param \Magento\Sales\Model\Order\Email\Sender\OrderCommentSender $orderCommentSender - * @param \Magento\Sales\Api\PaymentFailuresInterface $paymentFailures - * @param LoggerInterface $logger + * @param \Magento\Sales\Api\PaymentFailuresInterface|null $paymentFailures */ public function __construct( \Magento\Sales\Api\OrderRepositoryInterface $orderRepository, @@ -82,8 +75,7 @@ public function __construct( \Magento\Sales\Model\OrderNotifier $notifier, \Magento\Framework\Event\ManagerInterface $eventManager, \Magento\Sales\Model\Order\Email\Sender\OrderCommentSender $orderCommentSender, - \Magento\Sales\Api\PaymentFailuresInterface $paymentFailures, - LoggerInterface $logger + \Magento\Sales\Api\PaymentFailuresInterface $paymentFailures = null ) { $this->orderRepository = $orderRepository; $this->historyRepository = $historyRepository; @@ -92,8 +84,8 @@ public function __construct( $this->notifier = $notifier; $this->eventManager = $eventManager; $this->orderCommentSender = $orderCommentSender; - $this->paymentFailures = $paymentFailures; - $this->logger = $logger; + $this->paymentFailures = $paymentFailures ? : \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Sales\Api\PaymentFailuresInterface::class); } /** @@ -197,31 +189,25 @@ public function unHold($id) } /** - * Perform place order. - * * @param \Magento\Sales\Api\Data\OrderInterface $order * @return \Magento\Sales\Api\Data\OrderInterface * @throws \Exception */ public function place(\Magento\Sales\Api\Data\OrderInterface $order) { + // transaction will be here + //begin transaction try { $order->place(); - } catch (CommandException $e) { - $this->paymentFailures->handle((int)$order->getQuoteId(), __($e->getMessage())); - throw $e; - } - - try { - $order = $this->orderRepository->save($order); + return $this->orderRepository->save($order); + //commit } catch (\Exception $e) { - $this->logger->critical( - 'Saving order ' . $order->getIncrementId() . ' failed: ' . $e->getMessage() - ); + if ($e instanceof CommandException) { + $this->paymentFailures->handle((int)$order->getQuoteId(), __($e->getMessage())); + } throw $e; + //rollback; } - - return $order; } /** diff --git a/app/code/Magento/Sales/Test/Unit/Model/Service/OrderServiceTest.php b/app/code/Magento/Sales/Test/Unit/Model/Service/OrderServiceTest.php index 72fe7380ce8e4..067f83d1e5b32 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Service/OrderServiceTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Service/OrderServiceTest.php @@ -5,9 +5,6 @@ */ namespace Magento\Sales\Test\Unit\Model\Service; -use Magento\Sales\Api\PaymentFailuresInterface; -use Psr\Log\LoggerInterface; - /** * Class OrderUnHoldTest * @@ -143,12 +140,6 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - /** @var PaymentFailuresInterface|\PHPUnit_Framework_MockObject_MockObject $paymentFailures */ - $paymentFailures = $this->createMock(PaymentFailuresInterface::class); - - /** @var LoggerInterface|\PHPUnit_Framework_MockObject_MockObject $logger */ - $logger = $this->createMock(LoggerInterface::class); - $this->orderService = new \Magento\Sales\Model\Service\OrderService( $this->orderRepositoryMock, $this->orderStatusHistoryRepositoryMock, @@ -156,9 +147,7 @@ protected function setUp() $this->filterBuilderMock, $this->orderNotifierMock, $this->eventManagerMock, - $this->orderCommentSender, - $paymentFailures, - $logger + $this->orderCommentSender ); } diff --git a/dev/tests/integration/testsuite/Magento/Quote/Fixtures/quote_without_customer_email.php b/dev/tests/integration/testsuite/Magento/Quote/Fixtures/quote_without_customer_email.php deleted file mode 100644 index 49fd11593c798..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Quote/Fixtures/quote_without_customer_email.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Quote\Api\CartRepositoryInterface; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\ObjectManager; - -require __DIR__ . '/../../Sales/_files/quote_with_customer.php'; - -/** @var ObjectManager $objectManager */ -$objectManager = Bootstrap::getObjectManager(); - -$quote->getPayment() - ->setMethod('checkmo'); -$quote->getShippingAddress() - ->setShippingMethod('flatrate_flatrate') - ->setCollectShippingRates(true); -$quote->collectTotals(); - -$quote->setCustomerEmail(''); - -/** @var CartRepositoryInterface $repository */ -$repository = $objectManager->get(CartRepositoryInterface::class); -$repository->save($quote); diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php index dc784aa55efc4..356117f2b3dc8 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php @@ -12,11 +12,9 @@ use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Quote\Api\CartManagementInterface; use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Sales\Api\OrderManagementInterface; use Magento\Sales\Api\OrderRepositoryInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\ObjectManager; -use PHPUnit\Framework\ExpectationFailedException; /** * Class for testing QuoteManagement model @@ -103,33 +101,6 @@ public function testSubmitWithItemOutOfStock() $this->cartManagement->placeOrder($quote->getId()); } - /** - * Tries to create an order using quote with empty customer email. - * - * Order should not start placing if order validation is failed. - * - * @magentoDataFixture Magento/Quote/Fixtures/quote_without_customer_email.php - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage Email has a wrong format - */ - public function testSubmitWithEmptyCustomerEmail() - { - $quote = $this->getQuote('test01'); - $orderManagement = $this->createMock(OrderManagementInterface::class); - $orderManagement->expects($this->never()) - ->method('place'); - $cartManagement = $this->objectManager->create( - CartManagementInterface::class, - ['orderManagement' => $orderManagement] - ); - - try { - $cartManagement->placeOrder($quote->getId()); - } catch (ExpectationFailedException $e) { - $this->fail('Place order method was not expected to be called if order validation is failed'); - } - } - /** * Gets quote by reserved order ID. * diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js index 817553c30d0f9..6062439db2365 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js @@ -85,5 +85,21 @@ define([ expect(braintreeCcForm.getCode()).toEqual(expectedCode); expect(braintreeCcForm.messageContainer).toEqual(expectedMessageContainer); }); + + it('Check if form validation fails when "Place Order" button should be active.', function () { + var errorMessage = 'Something went wrong.', + + /** + * Anonymous wrapper + */ + func = function () { + braintreeCcForm.clientConfig.onError({ + 'message': errorMessage + }); + }; + + expect(func).toThrow(errorMessage); + expect(braintreeCcForm.isPlaceOrderActionAllowed()).toBeTruthy(); + }); }); }); From 61f3ddc566278873769ea536ff72f6c20696b2ff Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Thu, 2 May 2019 16:26:55 -0500 Subject: [PATCH 1218/1295] MAGETWO-99471: Custom customer address attribute values are empty when address is changed in admin checkout --- .../templates/order/create/form/address.phtml | 2 +- .../Api/ExtensibleDataObjectConverter.php | 37 +++++++++++++++---- .../ExtensibleDataObjectConverterTest.php | 17 +++++++++ 3 files changed, 47 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/create/form/address.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/create/form/address.phtml index b0a88b8fa37dc..c96237149721c 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/create/form/address.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/create/form/address.phtml @@ -32,7 +32,7 @@ if ($block->getIsShipping()): require(["Magento_Sales/order/create/form"], function(){ order.shippingAddressContainer = '<?= /* @escapeNotVerified */ $_fieldsContainerId ?>'; - order.setAddresses(<?= /* @escapeNotVerified */ $customerAddressFormatter->getAddressesJson($addressArray) ?>); + order.setAddresses(<?= /* @escapeVerfied */ $block->getAddressCollectionJson() ?>); }); </script> diff --git a/lib/internal/Magento/Framework/Api/ExtensibleDataObjectConverter.php b/lib/internal/Magento/Framework/Api/ExtensibleDataObjectConverter.php index 538f4e646d060..2a8f0941be05a 100644 --- a/lib/internal/Magento/Framework/Api/ExtensibleDataObjectConverter.php +++ b/lib/internal/Magento/Framework/Api/ExtensibleDataObjectConverter.php @@ -45,6 +45,30 @@ public function toNestedArray( } $dataObjectArray = $this->dataObjectProcessor->buildOutputDataArray($dataObject, $dataObjectType); //process custom attributes if present + $dataObjectArray = $this->processCustomAttributes($dataObjectArray, $skipAttributes); + + if (!empty($dataObjectArray[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY])) { + /** @var array $extensionAttributes */ + $extensionAttributes = $dataObjectArray[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]; + unset($dataObjectArray[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]); + foreach ($extensionAttributes as $attributeKey => $attributeValue) { + if (!in_array($attributeKey, $skipAttributes)) { + $dataObjectArray[$attributeKey] = $attributeValue; + } + } + } + return $dataObjectArray; + } + + /** + * Recursive process array to process customer attributes + * + * @param array $dataObjectArray + * @param array $skipAttributes + * @return array + */ + private function processCustomAttributes(array $dataObjectArray, array $skipAttributes): array + { if (!empty($dataObjectArray[AbstractExtensibleObject::CUSTOM_ATTRIBUTES_KEY])) { /** @var AttributeValue[] $customAttributes */ $customAttributes = $dataObjectArray[AbstractExtensibleObject::CUSTOM_ATTRIBUTES_KEY]; @@ -56,16 +80,13 @@ public function toNestedArray( } } } - if (!empty($dataObjectArray[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY])) { - /** @var array $extensionAttributes */ - $extensionAttributes = $dataObjectArray[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]; - unset($dataObjectArray[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]); - foreach ($extensionAttributes as $attributeKey => $attributeValue) { - if (!in_array($attributeKey, $skipAttributes)) { - $dataObjectArray[$attributeKey] = $attributeValue; - } + + foreach ($dataObjectArray as $key => $value) { + if (is_array($value)) { + $dataObjectArray[$key] = $this->processCustomAttributes($value, $skipAttributes); } } + return $dataObjectArray; } diff --git a/lib/internal/Magento/Framework/Api/Test/Unit/ExtensibleDataObjectConverterTest.php b/lib/internal/Magento/Framework/Api/Test/Unit/ExtensibleDataObjectConverterTest.php index f0e333aa5e1d5..5563460f7ec04 100644 --- a/lib/internal/Magento/Framework/Api/Test/Unit/ExtensibleDataObjectConverterTest.php +++ b/lib/internal/Magento/Framework/Api/Test/Unit/ExtensibleDataObjectConverterTest.php @@ -83,6 +83,17 @@ public function testToNestedArrayCustom() AttributeValue::VALUE => 'custom_attribute_value_skip', ], ], + 'test' => [ + 0 => [ + '3rd_attribute_key' => '3rd_attribute_value', + AbstractExtensibleObject::CUSTOM_ATTRIBUTES_KEY => [ + [ + AttributeValue::ATTRIBUTE_CODE => 'another_custom_attribute_code', + AttributeValue::VALUE => 'another_custom_attribute_value', + ], + ], + ], + ], ]; $resultArray = [ @@ -92,6 +103,12 @@ public function testToNestedArrayCustom() 'custom_attribute_value_multi_1', 'custom_attribute_value_multi_2', ], + 'test' => [ + 0 => [ + '3rd_attribute_key' => '3rd_attribute_value', + 'another_custom_attribute_code' => 'another_custom_attribute_value', + ], + ], ]; $this->processor->expects($this->any()) From 2bb510d8dfe06d85416c4bc600501a995765544d Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Fri, 3 May 2019 11:22:08 +0300 Subject: [PATCH 1219/1295] MAGETWO-98655: Purchasing a downloadable product as guest then creating an account on the onepagesuccess step doesn't link product with account --- ...ustomerFromOrderSuccessPageActionGroup.xml | 1 + ...ontCustomerDownloadableProductsSection.xml | 14 ---- .../Observer/UpdateLinkPurchasedObserver.php | 74 ++++++++++--------- 3 files changed, 40 insertions(+), 49 deletions(-) delete mode 100644 app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDownloadableProductsSection.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontRegisterCustomerFromOrderSuccessPageActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontRegisterCustomerFromOrderSuccessPageActionGroup.xml index 5e24592bf017f..f71a012d9d88d 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontRegisterCustomerFromOrderSuccessPageActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontRegisterCustomerFromOrderSuccessPageActionGroup.xml @@ -16,6 +16,7 @@ <fillField selector="{{StorefrontCustomerCreateFormSection.passwordField}}" userInput="{{customer.password}}" stepKey="typePassword"/> <fillField selector="{{StorefrontCustomerCreateFormSection.confirmPasswordField}}" userInput="{{customer.password}}" stepKey="typeConfirmationPassword"/> <click selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}" stepKey="clickOnCreateAccount"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForConfirmModal"/> <see selector="{{StorefrontMessagesSection.success}}" userInput="Thank you for registering" stepKey="verifyAccountCreated"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDownloadableProductsSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDownloadableProductsSection.xml deleted file mode 100644 index d45a774077ba0..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDownloadableProductsSection.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="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="StorefrontCustomerDownloadableProductsSection"> - <element name="productName" type="text" selector="//table[@id='my-downloadable-products-table']//strong[contains(@class, 'product-name') and normalize-space(.)='{{productName}}']" parameterized="true"/> - </section> -</sections> diff --git a/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php b/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php index db391ccda6866..223a3ad66b3d3 100644 --- a/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php +++ b/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php @@ -7,6 +7,10 @@ namespace Magento\Downloadable\Observer; +use Magento\Downloadable\Model\ResourceModel\Link\Purchased\Collection as PurchasedCollection; +use Magento\Downloadable\Model\ResourceModel\Link\Purchased\CollectionFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; /** @@ -16,65 +20,65 @@ class UpdateLinkPurchasedObserver implements ObserverInterface { /** * Core store config - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * + * @var ScopeConfigInterface */ private $scopeConfig; /** - * @var \Magento\Downloadable\Model\ResourceModel\Link\Purchased\CollectionFactory - */ - private $purchasedFactory; - - /** - * @var \Magento\Framework\DataObject\Copy + * Purchased links collection factory + * + * @var CollectionFactory */ - private $objectCopyService; + private $purchasedCollectionFactory; /** - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig - * @param \Magento\Downloadable\Model\ResourceModel\Link\Purchased\CollectionFactory $purchasedFactory - * @param \Magento\Framework\DataObject\Copy $objectCopyService + * @param ScopeConfigInterface $scopeConfig + * @param CollectionFactory $purchasedCollectionFactory */ public function __construct( - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\Downloadable\Model\ResourceModel\Link\Purchased\CollectionFactory $purchasedFactory, - \Magento\Framework\DataObject\Copy $objectCopyService + ScopeConfigInterface $scopeConfig, + CollectionFactory $purchasedCollectionFactory ) { $this->scopeConfig = $scopeConfig; - $this->purchasedFactory = $purchasedFactory; - $this->objectCopyService = $objectCopyService; + $this->purchasedCollectionFactory = $purchasedCollectionFactory; } /** - * Re-save order data after order update. + * Link customer_id to downloadable link purchased after update order * - * @param \Magento\Framework\Event\Observer $observer + * @param Observer $observer * @return $this */ - public function execute(\Magento\Framework\Event\Observer $observer) + public function execute(Observer $observer) { $order = $observer->getEvent()->getOrder(); - - if (!$order->getId()) { - //order not saved in the database + $orderId = $order->getId(); + $customerId = $order->getCustomerId(); + if (!$orderId || !$customerId) { return $this; } + $purchasedLinksCollection = $this->getPurchasedCollection((int)$orderId); + foreach ($purchasedLinksCollection as $linkPurchased) { + $linkPurchased->setCustomerId($customerId)->save(); + } - $purchasedLinks = $this->purchasedFactory->create()->addFieldToFilter( + return $this; + } + + /** + * Get purchased collection by order id + * + * @param int $orderId + * @return PurchasedCollection + */ + private function getPurchasedCollection(int $orderId): PurchasedCollection + { + $purchasedCollection = $this->purchasedCollectionFactory->create()->addFieldToFilter( 'order_id', - ['eq' => $order->getId()] + ['eq' => $orderId] ); - foreach ($purchasedLinks as $linkPurchased) { - $this->objectCopyService->copyFieldsetToTarget( - \downloadable_sales_copy_order::class, - 'to_downloadable', - $order, - $linkPurchased - ); - $linkPurchased->save(); - } - - return $this; + return $purchasedCollection; } } From 25b18f407d334dc18b259ecff69d98d97a5de53b Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Fri, 3 May 2019 11:23:35 +0300 Subject: [PATCH 1220/1295] MAGETWO-98655: Purchasing a downloadable product as guest then creating an account on the onepagesuccess step doesn't link product with account --- .../Observer/UpdateLinkPurchasedObserver.php | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php b/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php index 223a3ad66b3d3..cadd0f51e8456 100644 --- a/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php +++ b/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php @@ -9,7 +9,6 @@ use Magento\Downloadable\Model\ResourceModel\Link\Purchased\Collection as PurchasedCollection; use Magento\Downloadable\Model\ResourceModel\Link\Purchased\CollectionFactory; -use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; @@ -18,13 +17,6 @@ */ class UpdateLinkPurchasedObserver implements ObserverInterface { - /** - * Core store config - * - * @var ScopeConfigInterface - */ - private $scopeConfig; - /** * Purchased links collection factory * @@ -33,14 +25,11 @@ class UpdateLinkPurchasedObserver implements ObserverInterface private $purchasedCollectionFactory; /** - * @param ScopeConfigInterface $scopeConfig * @param CollectionFactory $purchasedCollectionFactory */ public function __construct( - ScopeConfigInterface $scopeConfig, CollectionFactory $purchasedCollectionFactory ) { - $this->scopeConfig = $scopeConfig; $this->purchasedCollectionFactory = $purchasedCollectionFactory; } From 0bcb750d22188c9f2b6b6bea57a1f047089aaf24 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Fri, 3 May 2019 12:53:28 +0300 Subject: [PATCH 1221/1295] MAGETWO-98655: Purchasing a downloadable product as guest then creating an account on the onepagesuccess step doesn't link product with account --- ...sterCustomerFromOrderSuccessPageActionGroup.xml | 2 +- ...roductIsPresentInCustomerAccountActionGroup.xml | 4 +--- ...kDownloadableProductFromGuestToCustomerTest.xml | 14 ++++++++++---- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontRegisterCustomerFromOrderSuccessPageActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontRegisterCustomerFromOrderSuccessPageActionGroup.xml index f71a012d9d88d..a97ad2a8b1907 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontRegisterCustomerFromOrderSuccessPageActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontRegisterCustomerFromOrderSuccessPageActionGroup.xml @@ -16,7 +16,7 @@ <fillField selector="{{StorefrontCustomerCreateFormSection.passwordField}}" userInput="{{customer.password}}" stepKey="typePassword"/> <fillField selector="{{StorefrontCustomerCreateFormSection.confirmPasswordField}}" userInput="{{customer.password}}" stepKey="typeConfirmationPassword"/> <click selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}" stepKey="clickOnCreateAccount"/> - <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForConfirmModal"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <see selector="{{StorefrontMessagesSection.success}}" userInput="Thank you for registering" stepKey="verifyAccountCreated"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/StorefrontAssertDownloadableProductIsPresentInCustomerAccountActionGroup.xml b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/StorefrontAssertDownloadableProductIsPresentInCustomerAccountActionGroup.xml index 2e4afbd803205..d672e5bd0f95f 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/StorefrontAssertDownloadableProductIsPresentInCustomerAccountActionGroup.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/StorefrontAssertDownloadableProductIsPresentInCustomerAccountActionGroup.xml @@ -12,9 +12,7 @@ <arguments> <argument name="product"/> </arguments> - <amOnPage url="{{StorefrontCustomerDashboardPage.url}}" stepKey="goToMyAccountPage"/> - <click selector="{{StorefrontCustomerSidebarSection.sidebarTab('My Downloadable Products')}}" stepKey="clickDownloadableProducts"/> - <waitForPageLoad stepKey="waitForDownloadableProductsPageLoad" /> + <amOnPage url="{{StorefrontCustomerDownloadableProductsPage.url}}" stepKey="goToCustomerDownloadableProductsPage"/> <seeElement selector="{{StorefrontCustomerDownloadableProductsSection.productName(product.name)}}" stepKey="seeStorefontDownloadableProductsProductName" /> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml index cd73ffa80661c..fd479bda5f9d9 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml @@ -10,28 +10,34 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="LinkDownloadableProductFromGuestToCustomerTest"> <annotations> - <stories value="Customer Account"/> + <features value="Downloadable"/> + <stories value="Downloadable products in customer account"/> <title value="Customer should see downloadable products after place order as guest and registering after that"/> <description value="Verify that in 'My Downloadable Products' section in customer account user can see products."/> <severity value="AVERAGE"/> <useCaseId value="MAGETWO-98655"/> <testCaseId value="MC-16148"/> + <group value="catalog"/> + <group value="downloadable"/> </annotations> <before> <magentoCLI command="config:set {{EnableGuestCheckoutWithDownloadableItems.path}} {{EnableGuestCheckoutWithDownloadableItems.value}}" stepKey="enableGuestCheckoutWithDownloadableItems" /> - <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="ApiDownloadableProduct" stepKey="createProduct"/> <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink"> <requiredEntity createDataKey="createProduct"/> </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> + <actionGroup ref="RemoveCustomerFromAdminActionGroup" stepKey="deleteCustomer"> + <argument name="customer" value="Simple_US_NY_Customer"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> <magentoCLI command="config:set {{DisableGuestCheckoutWithDownloadableItems.path}} {{DisableGuestCheckoutWithDownloadableItems.value}}" stepKey="disableGuestCheckoutWithDownloadableItems" /> <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> - <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> </after> <!--Step 1: Go to Storefront as Guest--> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> + <actionGroup ref="CustomerLogoutStorefrontActionGroup" stepKey="loginAsGuest"/> <!--Step 2: Add downloadable product to shopping cart--> <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="amOnStorefrontProductPage"/> <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="addProductToCart"> From 448b5a6af8b9f3972fb9a78a8418c649216e8127 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Fri, 3 May 2019 13:09:25 +0300 Subject: [PATCH 1222/1295] MAGETWO-98655: Purchasing a downloadable product as guest then creating an account on the onepagesuccess step doesn't link product with account --- .../Downloadable/Observer/UpdateLinkPurchasedObserver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php b/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php index cadd0f51e8456..ea27d0de3be6a 100644 --- a/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php +++ b/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php @@ -52,7 +52,7 @@ public function execute(Observer $observer) $linkPurchased->setCustomerId($customerId)->save(); } - return $this; + return; } /** From 2f41ea91f9ecfa8eea952751fc1c0c34a557cbf9 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Fri, 3 May 2019 13:12:53 +0300 Subject: [PATCH 1223/1295] MAGETWO-98655: Purchasing a downloadable product as guest then creating an account on the onepagesuccess step doesn't link product with account --- .../Downloadable/Observer/UpdateLinkPurchasedObserver.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php b/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php index ea27d0de3be6a..0bae2fd440953 100644 --- a/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php +++ b/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php @@ -52,7 +52,7 @@ public function execute(Observer $observer) $linkPurchased->setCustomerId($customerId)->save(); } - return; + return $this; } /** @@ -63,11 +63,9 @@ public function execute(Observer $observer) */ private function getPurchasedCollection(int $orderId): PurchasedCollection { - $purchasedCollection = $this->purchasedCollectionFactory->create()->addFieldToFilter( + return $purchasedCollection = $this->purchasedCollectionFactory->create()->addFieldToFilter( 'order_id', ['eq' => $orderId] ); - - return $purchasedCollection; } } From 88c2b697854f14f271db7766e9899ca0db745174 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Fri, 3 May 2019 13:19:29 +0300 Subject: [PATCH 1224/1295] MAGETWO-98655: Purchasing a downloadable product as guest then creating an account on the onepagesuccess step doesn't link product with account --- .../Test/LinkDownloadableProductFromGuestToCustomerTest.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml index fd479bda5f9d9..f9046623a8f7c 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml @@ -29,15 +29,15 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> + <magentoCLI command="config:set {{DisableGuestCheckoutWithDownloadableItems.path}} {{DisableGuestCheckoutWithDownloadableItems.value}}" stepKey="disableGuestCheckoutWithDownloadableItems" /> + <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> <actionGroup ref="RemoveCustomerFromAdminActionGroup" stepKey="deleteCustomer"> <argument name="customer" value="Simple_US_NY_Customer"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> - <magentoCLI command="config:set {{DisableGuestCheckoutWithDownloadableItems.path}} {{DisableGuestCheckoutWithDownloadableItems.value}}" stepKey="disableGuestCheckoutWithDownloadableItems" /> - <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> </after> <!--Step 1: Go to Storefront as Guest--> - <actionGroup ref="CustomerLogoutStorefrontActionGroup" stepKey="loginAsGuest"/> + <actionGroup ref="CustomerLogoutStorefrontActionGroup" stepKey="logoutCustomer"/> <!--Step 2: Add downloadable product to shopping cart--> <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="amOnStorefrontProductPage"/> <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="addProductToCart"> From 13f3562331fd35d313f121524f8429fd3729da3c Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Fri, 3 May 2019 15:55:24 +0300 Subject: [PATCH 1225/1295] MAGETWO-98655: Purchasing a downloadable product as guest then creating an account on the onepagesuccess step doesn't link product with account --- .../Downloadable/Observer/UpdateLinkPurchasedObserver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php b/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php index 0bae2fd440953..fc6430710aa59 100644 --- a/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php +++ b/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php @@ -63,7 +63,7 @@ public function execute(Observer $observer) */ private function getPurchasedCollection(int $orderId): PurchasedCollection { - return $purchasedCollection = $this->purchasedCollectionFactory->create()->addFieldToFilter( + return $this->purchasedCollectionFactory->create()->addFieldToFilter( 'order_id', ['eq' => $orderId] ); From 426fa7ea398174c3f6a86b47c373892a6cca90d5 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Mon, 6 May 2019 15:51:22 +0300 Subject: [PATCH 1226/1295] MAGETWO-98949: Product short description changing for each update in multiple schedule update --- .../Section/AdminSlideOutDialogSection.xml | 2 +- .../Cms/Test/Mftf/Data/WysiwygConfigData.xml | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Cms/Test/Mftf/Data/WysiwygConfigData.xml diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminSlideOutDialogSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminSlideOutDialogSection.xml index a2645c9cbf96d..a4270ae9ce6c9 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminSlideOutDialogSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminSlideOutDialogSection.xml @@ -9,7 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminSlideOutDialogSection"> - <element name="closeButton" type="button" selector=".modal-slide._show [data-role='closeBtn']" timeout="30"/> + <element name="closeButton" type="button" selector=".modal-slide._show [data-role="closeBtn"]" timeout="30"/> <element name="cancelButton" type="button" selector="//*[contains(@class, 'modal-slide') and contains(@class, '_show')]//*[contains(@class, 'page-actions')]//button[normalize-space(.)='Cancel']" timeout="30"/> <element name="doneButton" type="button" selector="//*[contains(@class, 'modal-slide') and contains(@class, '_show')]//*[contains(@class, 'page-actions')]//button[normalize-space(.)='Done']" timeout="30"/> <element name="saveButton" type="button" selector="//*[contains(@class, 'modal-slide') and contains(@class, '_show')]//*[contains(@class, 'page-actions')]//button[normalize-space(.)='Save']" timeout="30"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Data/WysiwygConfigData.xml b/app/code/Magento/Cms/Test/Mftf/Data/WysiwygConfigData.xml new file mode 100644 index 0000000000000..bc69c94329ac9 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Data/WysiwygConfigData.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="WysiwygEnabledByDefault"> + <data key="path">cms/wysiwyg/enabled</data> + <data key="scope_id">0</data> + <data key="value">enabled</data> + </entity> + <entity name="WysiwygDisabledByDefault"> + <data key="path">cms/wysiwyg/enabled</data> + <data key="scope_id">0</data> + <data key="value">hidden</data> + </entity> +</entities> From c5d4037ca057b0ebbc41f48018294abf884ccc63 Mon Sep 17 00:00:00 2001 From: Roman Lytvynenko <lytvynen@adobe.com> Date: Mon, 6 May 2019 16:36:26 -0500 Subject: [PATCH 1227/1295] MAGETWO-99562: Frontend email login on Safari cursor issue --- lib/web/mage/trim-input.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/web/mage/trim-input.js b/lib/web/mage/trim-input.js index 678192dcf61ac..3cce49f70f62c 100644 --- a/lib/web/mage/trim-input.js +++ b/lib/web/mage/trim-input.js @@ -41,9 +41,18 @@ define([ * @private */ _trimInput: function () { + // Safari caret position workaround: storing carter position + const caretStart = this.options.cache.input.get(0).selectionStart; + const caretEnd = this.options.cache.input.get(0).selectionEnd; + var input = this._getInputValue().trim(); this.options.cache.input.val(input); + + // Safari caret position workaround: setting caret position to previously stored values + if (caretStart !== null && caretEnd !== null) { + this.options.cache.input.get(0).setSelectionRange(caretStart, caretEnd); + } }, /** From fd579a3e9a2fccf0c0d5b01fca42e7f434f0abc2 Mon Sep 17 00:00:00 2001 From: Roman Lytvynenko <lytvynen@adobe.com> Date: Mon, 6 May 2019 16:46:13 -0500 Subject: [PATCH 1228/1295] MAGETWO-99562: Frontend email login on Safari cursor issue --- lib/web/mage/trim-input.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/web/mage/trim-input.js b/lib/web/mage/trim-input.js index 3cce49f70f62c..69362af490777 100644 --- a/lib/web/mage/trim-input.js +++ b/lib/web/mage/trim-input.js @@ -42,8 +42,8 @@ define([ */ _trimInput: function () { // Safari caret position workaround: storing carter position - const caretStart = this.options.cache.input.get(0).selectionStart; - const caretEnd = this.options.cache.input.get(0).selectionEnd; + var caretStart = this.options.cache.input.get(0).selectionStart; + var caretEnd = this.options.cache.input.get(0).selectionEnd; var input = this._getInputValue().trim(); From b7784bfe48d4843367e8e4e61728397c6134a8b9 Mon Sep 17 00:00:00 2001 From: Roman Lytvynenko <lytvynen@adobe.com> Date: Mon, 6 May 2019 17:37:15 -0500 Subject: [PATCH 1229/1295] MAGETWO-99562: Frontend email login on Safari cursor issue --- lib/web/mage/trim-input.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/web/mage/trim-input.js b/lib/web/mage/trim-input.js index 69362af490777..430db67471991 100644 --- a/lib/web/mage/trim-input.js +++ b/lib/web/mage/trim-input.js @@ -42,8 +42,9 @@ define([ */ _trimInput: function () { // Safari caret position workaround: storing carter position - var caretStart = this.options.cache.input.get(0).selectionStart; - var caretEnd = this.options.cache.input.get(0).selectionEnd; + var caretStart, caretEnd; + caretStart = this.options.cache.input.get(0).selectionStart; + caretEnd = this.options.cache.input.get(0).selectionEnd; var input = this._getInputValue().trim(); From b8b2458fd8e99ff418790b637d5d2495ba981f8b Mon Sep 17 00:00:00 2001 From: Nazarn96 <nazarn96@gmail.com> Date: Thu, 2 May 2019 09:47:53 +0300 Subject: [PATCH 1230/1295] magento/magento2#22447 integration-test-fix --- ...aypal_payflowpro_with_express_checkout.xml | 2 +- .../Config/Structure/Reader/ConverterStub.php | 14 +- .../Config/Structure/Reader}/DataTest.php | 2 +- .../Config/Structure/Reader/ReaderStub.php | 12 +- .../Config/Structure/Reader/ReaderTest.php | 199 ++ .../_files/catalog_new_products_list.xml | 0 .../Structure/Reader/_files}/config.xml | 0 .../Reader}/_files/expectedGlobalArray.php | 0 .../_files/expectedGlobalDesignArray.php | 0 .../Reader}/_files/expectedMergedArray.php | 0 .../Reader}/_files/orders_and_returns.xml | 0 .../_files/orders_and_returns_customized.xml | 0 .../Config/Structure/Reader/ReaderTest.php | 135 - .../Reader/_files/expected/config.xml | 2369 ----------------- .../Widget/Model/Config/ReaderTest.php | 63 - 15 files changed, 220 insertions(+), 2576 deletions(-) rename dev/tests/integration/testsuite/Magento/{Paypal => Config}/Model/Config/Structure/Reader/ConverterStub.php (63%) rename dev/tests/integration/testsuite/Magento/{Widget/Model/Config => Config/Model/Config/Structure/Reader}/DataTest.php (95%) rename dev/tests/integration/testsuite/Magento/{Paypal => Config}/Model/Config/Structure/Reader/ReaderStub.php (53%) create mode 100644 dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/ReaderTest.php rename dev/tests/integration/testsuite/Magento/{Widget/Model/Config => Config/Model/Config/Structure/Reader}/_files/catalog_new_products_list.xml (100%) rename dev/tests/integration/testsuite/Magento/{Paypal/Model/Config/Structure/Reader/_files/actual => Config/Model/Config/Structure/Reader/_files}/config.xml (100%) rename dev/tests/integration/testsuite/Magento/{Widget/Model/Config => Config/Model/Config/Structure/Reader}/_files/expectedGlobalArray.php (100%) rename dev/tests/integration/testsuite/Magento/{Widget/Model/Config => Config/Model/Config/Structure/Reader}/_files/expectedGlobalDesignArray.php (100%) rename dev/tests/integration/testsuite/Magento/{Widget/Model/Config => Config/Model/Config/Structure/Reader}/_files/expectedMergedArray.php (100%) rename dev/tests/integration/testsuite/Magento/{Widget/Model/Config => Config/Model/Config/Structure/Reader}/_files/orders_and_returns.xml (100%) rename dev/tests/integration/testsuite/Magento/{Widget/Model/Config => Config/Model/Config/Structure/Reader}/_files/orders_and_returns_customized.xml (100%) delete mode 100644 dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/ReaderTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/expected/config.xml delete mode 100644 dev/tests/integration/testsuite/Magento/Widget/Model/Config/ReaderTest.php diff --git a/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro_with_express_checkout.xml b/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro_with_express_checkout.xml index ae9ab5c0b407f..82874c3c74ff2 100644 --- a/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro_with_express_checkout.xml +++ b/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro_with_express_checkout.xml @@ -34,7 +34,7 @@ <label>Enable PayPal Credit</label> <comment><![CDATA[PayPal Express Checkout Payflow Edition lets you give customers access to financing through PayPal Credit® - at no additional cost to you. You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. - <a href="https:/www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> + <a href="https://www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> </comment> <config_path>payment/payflow_express_bml/active</config_path> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/ConverterStub.php b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/ConverterStub.php similarity index 63% rename from dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/ConverterStub.php rename to dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/ConverterStub.php index 223ef35c0dcd3..7493d31f02b31 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/ConverterStub.php +++ b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/ConverterStub.php @@ -3,14 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Paypal\Model\Config\Structure\Reader; +declare(strict_types=1); + +namespace Magento\Config\Model\Config\Structure\Reader; + +use Magento\Config\Model\Config\Structure\Converter; /** - * Class ConverterStub + * Class ConverterStub used for ReaderTest. */ -class ConverterStub extends \Magento\Config\Model\Config\Structure\Converter +class ConverterStub extends Converter { /** + * Convert dom document wrapper. + * * @param \DOMDocument $document * @return array|null */ @@ -20,7 +26,7 @@ public function getArrayData(\DOMDocument $document) } /** - * Convert dom document + * Convert dom document. * * @param \DOMNode $source * @return array diff --git a/dev/tests/integration/testsuite/Magento/Widget/Model/Config/DataTest.php b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/DataTest.php similarity index 95% rename from dev/tests/integration/testsuite/Magento/Widget/Model/Config/DataTest.php rename to dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/DataTest.php index d4724072ecdc8..3a97ee25e9aea 100644 --- a/dev/tests/integration/testsuite/Magento/Widget/Model/Config/DataTest.php +++ b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/DataTest.php @@ -4,7 +4,7 @@ * See COPYING.txt for license details. * */ -namespace Magento\Widget\Model\Config; +namespace Magento\Config\Model\Config\Structure\Reader; /** * @magentoAppArea adminhtml diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/ReaderStub.php b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/ReaderStub.php similarity index 53% rename from dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/ReaderStub.php rename to dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/ReaderStub.php index ed1366ad737f9..866ff91678ec4 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/ReaderStub.php +++ b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/ReaderStub.php @@ -3,14 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Paypal\Model\Config\Structure\Reader; +declare(strict_types=1); + +namespace Magento\Config\Model\Config\Structure\Reader; + +use Magento\Config\Model\Config\Structure\Reader; /** - * Class ReaderStub + * Class ReaderStub used for testing protected Reader::_readFiles() method. */ -class ReaderStub extends \Magento\Config\Model\Config\Structure\Reader +class ReaderStub extends Reader { /** + * Wrapper for protected Reader::_readFiles() method. + * * @param array $fileList * @return array * @throws \Magento\Framework\Exception\LocalizedException diff --git a/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/ReaderTest.php b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/ReaderTest.php new file mode 100644 index 0000000000000..53a3d4f643bee --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/ReaderTest.php @@ -0,0 +1,199 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Config\Model\Config\Structure\Reader; + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Config\Model\Config\SchemaLocator; +use Magento\Framework\App\Utility\Files; +use Magento\Framework\Config\Dom; +use Magento\Framework\Config\FileResolverInterface; +use Magento\Framework\Config\ValidationStateInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\TemplateEngine\Xhtml\CompilerInterface; + +/** + * Class ReaderTest check Magento\Config\Model\Config\Structure\Reader::_readFiles() method. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ReaderTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Widget\Model\Config\Reader + */ + private $model; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $fileResolver; + + /** + * Test config location. + * + * @string + */ + const CONFIG = '/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/_files/'; + + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var Files + */ + private $fileUtility; + + /** + * @var ValidationStateInterface + */ + private $validationStateMock; + + /** + * @var \Magento\Framework\Config\SchemaLocatorInterface + */ + private $schemaLocatorMock; + + /** + * @var FileResolverInterface + */ + private $fileResolverMock; + + /** + * @var ReaderStub + */ + private $reader; + + /** + * @var ConverterStub + */ + private $converter; + + /** + * @var CompilerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $compiler; + + /** + * @inheritdoc + */ + public function setUp() + { + $this->fileResolver = $this->getMockForAbstractClass(FileResolverInterface::class); + $objectManager = Bootstrap::getObjectManager(); + $this->model = $objectManager->create( + \Magento\Widget\Model\Config\Reader::class, + ['fileResolver' => $this->fileResolver] + ); + $this->objectManager = Bootstrap::getObjectManager(); + $this->fileUtility = Files::init(); + $this->fileResolverMock = $this->getMockBuilder(FileResolverInterface::class) + ->getMockForAbstractClass(); + $this->converter = $this->objectManager->create(ConverterStub::class); + + //Isolate test from actual configuration, and leave only sample data. + $this->compiler = $this->getMockBuilder(CompilerInterface::class) + ->disableOriginalConstructor() + ->setMethods(['compile']) + ->getMockForAbstractClass(); + } + + /** + * The test checks the file structure after processing the nodes responsible for inserting content. + * + * @return void + */ + public function testXmlConvertedConfigurationAndCompereStructure() + { + $this->validationStateMock = $this->getMockBuilder(ValidationStateInterface::class) + ->setMethods(['isValidationRequired']) + ->getMockForAbstractClass(); + $this->validationStateMock->expects($this->atLeastOnce()) + ->method('isValidationRequired') + ->willReturn(false); + $this->schemaLocatorMock = $this->getMockBuilder(SchemaLocator::class) + ->disableOriginalConstructor() + ->setMethods(['getPerFileSchema']) + ->getMock(); + $this->reader = $this->objectManager->create( + ReaderStub::class, + [ + 'fileResolver' => $this->fileResolverMock, + 'converter' => $this->converter, + 'schemaLocator' => $this->schemaLocatorMock, + 'validationState' => $this->validationStateMock, + 'fileName' => 'no_existing_file.xml', + 'compiler' => $this->compiler, + 'domDocumentClass' => Dom::class + ] + ); + $actual = $this->reader->readFiles(['actual' => $this->getContent()]); + + $document = new \DOMDocument(); + $document->loadXML($this->getContent()); + + $expected = $this->converter->getArrayData($document); + + $this->assertEquals($expected, $actual); + } + + /** + * Get config sample data for test. + * + * @return string + */ + protected function getContent() + { + $files = $this->fileUtility->getFiles([BP . static::CONFIG], 'config.xml'); + + return file_get_contents(reset($files)); + } + + /** + * Checks method read() to get correct config. + * + */ + public function testRead() + { + $this->fileResolver->expects($this->once()) + ->method('get') + ->willReturn([file_get_contents(__DIR__ . '/_files/orders_and_returns.xml')]); + $expected = include __DIR__ . '/_files/expectedGlobalArray.php'; + $this->assertEquals($expected, $this->model->read('global')); + } + + /** + * Checks method _readFiles() to get correct config. + * + */ + public function testReadFile() + { + $file = file_get_contents(__DIR__ . '/_files/orders_and_returns.xml'); + $expected = include __DIR__ . '/_files/expectedGlobalArray.php'; + $this->assertEquals($expected, $this->model->readFile($file)); + } + + /** + * Checks method _readFiles() to get correct config with merged configs. + * + */ + public function testMergeCompleteAndPartial() + { + $fileList = [ + file_get_contents(__DIR__ . '/_files/catalog_new_products_list.xml'), + file_get_contents(__DIR__ . '/_files/orders_and_returns_customized.xml'), + ]; + $this->fileResolver->expects($this->once()) + ->method('get') + ->with('widget.xml', 'global') + ->willReturn($fileList); + $expected = include __DIR__ . '/_files/expectedMergedArray.php'; + $this->assertEquals($expected, $this->model->read('global')); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Widget/Model/Config/_files/catalog_new_products_list.xml b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/_files/catalog_new_products_list.xml similarity index 100% rename from dev/tests/integration/testsuite/Magento/Widget/Model/Config/_files/catalog_new_products_list.xml rename to dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/_files/catalog_new_products_list.xml diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/actual/config.xml b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/_files/config.xml similarity index 100% rename from dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/actual/config.xml rename to dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/_files/config.xml diff --git a/dev/tests/integration/testsuite/Magento/Widget/Model/Config/_files/expectedGlobalArray.php b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/_files/expectedGlobalArray.php similarity index 100% rename from dev/tests/integration/testsuite/Magento/Widget/Model/Config/_files/expectedGlobalArray.php rename to dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/_files/expectedGlobalArray.php diff --git a/dev/tests/integration/testsuite/Magento/Widget/Model/Config/_files/expectedGlobalDesignArray.php b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/_files/expectedGlobalDesignArray.php similarity index 100% rename from dev/tests/integration/testsuite/Magento/Widget/Model/Config/_files/expectedGlobalDesignArray.php rename to dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/_files/expectedGlobalDesignArray.php diff --git a/dev/tests/integration/testsuite/Magento/Widget/Model/Config/_files/expectedMergedArray.php b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/_files/expectedMergedArray.php similarity index 100% rename from dev/tests/integration/testsuite/Magento/Widget/Model/Config/_files/expectedMergedArray.php rename to dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/_files/expectedMergedArray.php diff --git a/dev/tests/integration/testsuite/Magento/Widget/Model/Config/_files/orders_and_returns.xml b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/_files/orders_and_returns.xml similarity index 100% rename from dev/tests/integration/testsuite/Magento/Widget/Model/Config/_files/orders_and_returns.xml rename to dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/_files/orders_and_returns.xml diff --git a/dev/tests/integration/testsuite/Magento/Widget/Model/Config/_files/orders_and_returns_customized.xml b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/_files/orders_and_returns_customized.xml similarity index 100% rename from dev/tests/integration/testsuite/Magento/Widget/Model/Config/_files/orders_and_returns_customized.xml rename to dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/_files/orders_and_returns_customized.xml diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/ReaderTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/ReaderTest.php deleted file mode 100644 index 6b966a045c982..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/ReaderTest.php +++ /dev/null @@ -1,135 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Paypal\Model\Config\Structure\Reader; - -use Magento\Framework\ObjectManagerInterface; - -/** - * Class ReaderTest - */ -class ReaderTest extends \PHPUnit\Framework\TestCase -{ - const EXPECTED = '/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/expected'; - - const ACTUAL = '/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/actual'; - - /** - * @var ObjectManagerInterface - */ - protected $objectManager; - - /** - * @var \Magento\Framework\App\Utility\Files - */ - protected $fileUtility; - - /** - * @var \Magento\Framework\Config\ValidationStateInterface - */ - protected $validationStateMock; - - /** - * @var \Magento\Framework\Config\SchemaLocatorInterface - */ - protected $schemaLocatorMock; - - /** - * @var \Magento\Framework\Config\FileResolverInterface - */ - protected $fileResolverMock; - - /** - * @var \Magento\Paypal\Model\Config\Structure\Reader\ReaderStub - */ - protected $reader; - - /** - * @var \Magento\Paypal\Model\Config\Structure\Reader\ConverterStub - */ - protected $converter; - - /** - * Set up - * - * @return void - */ - protected function setUp() - { - $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->fileUtility = \Magento\Framework\App\Utility\Files::init(); - - $this->validationStateMock = $this->getMockBuilder(\Magento\Framework\Config\ValidationStateInterface::class) - ->setMethods(['isValidationRequired']) - ->getMockForAbstractClass(); - $this->schemaLocatorMock = $this->getMockBuilder(\Magento\Config\Model\Config\SchemaLocator::class) - ->disableOriginalConstructor() - ->setMethods(['getPerFileSchema']) - ->getMock(); - $this->fileResolverMock = $this->getMockBuilder(\Magento\Framework\Config\FileResolverInterface::class) - ->getMockForAbstractClass(); - - $this->validationStateMock->expects($this->atLeastOnce()) - ->method('isValidationRequired') - ->willReturn(false); - $this->schemaLocatorMock->expects($this->atLeastOnce()) - ->method('getPerFileSchema') - ->willReturn(false); - - /** @var \Magento\Paypal\Model\Config\Structure\Reader\ConverterStub $converter */ - $this->converter = $this->objectManager->create( - \Magento\Paypal\Model\Config\Structure\Reader\ConverterStub::class - ); - - $this->reader = $this->objectManager->create( - \Magento\Paypal\Model\Config\Structure\Reader\ReaderStub::class, - [ - 'fileResolver' => $this->fileResolverMock, - 'converter' => $this->converter, - 'schemaLocator' => $this->schemaLocatorMock, - 'validationState' => $this->validationStateMock, - 'fileName' => 'no_existing_file.xml', - 'domDocumentClass' => \Magento\Framework\Config\Dom::class - ] - ); - } - - /** - * The test checks the file structure after processing the nodes responsible for inserting content - * - * @return void - */ - public function testXmlConvertedConfigurationAndCompereStructure() - { - $actual = $this->reader->readFiles(['actual' => $this->getActualContent()]); - - $document = new \DOMDocument(); - $document->loadXML($this->getExpectedContent()); - - $expected = $this->converter->getArrayData($document); - - $this->assertEquals($expected, $actual); - } - - /** - * @return string - */ - protected function getActualContent() - { - $files = $this->fileUtility->getFiles([BP . static::ACTUAL], 'config.xml'); - - return file_get_contents(reset($files)); - } - - /** - * @return string - */ - protected function getExpectedContent() - { - $files = $this->fileUtility->getFiles([BP . static::EXPECTED], 'config.xml'); - - return file_get_contents(reset($files)); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/expected/config.xml b/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/expected/config.xml deleted file mode 100644 index 2552d383bbcc3..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/expected/config.xml +++ /dev/null @@ -1,2369 +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:module:Magento_Config:etc/system_file.xsd"> - <system> - <section id="payment"> - <group id="account" translate="label" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0"> - <label>Merchant Location</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="merchant_country" type="select" translate="label comment" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="0"> - <label>Merchant Country</label> - <comment>If not specified, Default Country from General Config will be used</comment> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Country</frontend_model> - <source_model>Magento\Paypal\Model\System\Config\Source\MerchantCountry</source_model> - <backend_model>Magento\Paypal\Model\System\Config\Backend\MerchantCountry</backend_model> - <config_path>paypal/general/merchant_country</config_path> - </field> - </group> - <group id="recommended_solutions" translate="label" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Recommended Solutions:</label> - <fieldset_css>paypal-top-section paypal-recommended-header</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - </group> - <group id="other_paypal_payment_solutions" translate="label" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Other PayPal Payment Solutions:</label> - <fieldset_css>paypal-top-section paypal-other-header</fieldset_css> - <frontend_model>\Magento\Config\Block\System\Config\Form\Fieldset</frontend_model> - </group> - <group id="other_payment_methods" translate="label" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Other Payment Methods:</label> - <fieldset_css>paypal-top-section payments-other-header</fieldset_css> - <frontend_model>\Magento\Config\Block\System\Config\Form\Fieldset</frontend_model> - </group> - </section> - <section id="payment_all_paypal" showInDefault="0" showInWebsite="0" showInStore="0"> - <group id="paypal_payflowpro" translate="label comment" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="10"> - <label>Payflow Pro</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment</frontend_model> - <fieldset_css>paypal-other-section</fieldset_css> - <comment><![CDATA[Connect your merchant account with a fully customizable gateway that lets customers pay without leaving your site. (<u>Includes Express Checkout</u>)]]></comment> - <attribute type="activity_path">payment/payflowpro/active</attribute> - <attribute type="paypal_ec_separate">1</attribute> - <group id="configuration_details" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="4"> - <comment>http://docs.magento.com/m2/ce/user_guide/payment/paypal-payflow-pro.html</comment> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Hint</frontend_model> - </group> - <group id="paypal_payflow_required" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Required PayPal Settings</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <group id="paypal_payflow_api_settings" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Payflow Pro</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="partner" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Partner</label> - <config_path>payment/payflowpro/partner</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="user" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>User</label> - <config_path>payment/payflowpro/user</config_path> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - <attribute type="shared">1</attribute> - </field> - <field id="vendor" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>Vendor</label> - <config_path>payment/payflowpro/vendor</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="pwd" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1"> - <label>Password</label> - <config_path>payment/payflowpro/pwd</config_path> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - <attribute type="shared">1</attribute> - </field> - <field id="sandbox_flag" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1"> - <label>Test Mode</label> - <config_path>payment/payflowpro/sandbox_flag</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="use_proxy" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1"> - <label>Use Proxy</label> - <config_path>payment/payflowpro/use_proxy</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="proxy_host" translate="label" type="text" sortOrder="80" showInDefault="1" showInWebsite="1"> - <label>Proxy Host</label> - <config_path>payment/payflowpro/proxy_host</config_path> - <depends> - <field id="use_proxy">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="proxy_port" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1"> - <label>Proxy Port</label> - <config_path>payment/payflowpro/proxy_port</config_path> - <depends> - <field id="use_proxy">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - </group> - <field id="enable_paypal_payflow" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Enable this Solution</label> - <config_path>payment/payflowpro/active</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Payment</frontend_model> - <requires> - <group id="paypal_payflow_api_settings"/> - </requires> - </field> - <field id="payflowpro_cc_vault_active" translate="label" type="select" sortOrder="22" showInDefault="1" showInWebsite="1" showInStore="0"> - <label>Vault Enabled</label> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <config_path>payment/payflowpro_cc_vault/active</config_path> - <attribute type="shared">1</attribute> - <requires> - <group id="paypal_payflow_api_settings"/> - </requires> - </field> - </group> - <group id="settings_paypal_payflow" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20"> - <label>Basic Settings - PayPal Payflow Pro</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="title" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Title</label> - <comment>It is recommended to set this value to "Debit or Credit Card" per store views.</comment> - <config_path>payment/payflowpro/title</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="payflowpro_cc_vault_title" translate="label" type="text" sortOrder="15" showInDefault="1" showInWebsite="1" showInStore="0"> - <label>Vault Title</label> - <config_path>payment/payflowpro_cc_vault/title</config_path> - </field> - <field id="sort_order" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Sort Order</label> - <config_path>payment/payflowpro/sort_order</config_path> - <frontend_class>validate-number</frontend_class> - <attribute type="shared">1</attribute> - </field> - <field id="payment_action" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Payment Action</label> - <config_path>payment/payflowpro/payment_action</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\PaymentActions</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="heading_cc" translate="label" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>Credit Card Settings</label> - <frontend_model>Magento\Config\Block\System\Config\Form\Field\Heading</frontend_model> - <attribute type="shared">1</attribute> - </field> - <field id="cctypes" translate="label comment" type="multiselect" sortOrder="50" showInDefault="1" showInWebsite="1"> - <label>Allowed Credit Card Types</label> - <comment> - <![CDATA[Supporting of American Express cards require additional agreement. Learn more at <a href="http://www.paypal.com/amexupdate">http://www.paypal.com/amexupdate</a>.]]> - </comment> - <config_path>payment/payflowpro/cctypes</config_path> - <source_model>Magento\Paypal\Model\Config::getPayflowproCcTypesAsOptionArray</source_model> - <attribute type="shared">1</attribute> - </field> - <group id="settings_paypal_payflow_advanced" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="60"> - <label>Advanced Settings</label> - <fieldset_css>config-advanced</fieldset_css> - <field id="allowspecific" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1"> - <label>Payment Applicable From</label> - <config_path>payment/payflowpro/allowspecific</config_path> - <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Countries Payment Applicable From</label> - <config_path>payment/payflowpro/specificcountry</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BuyerCountry</source_model> - <depends> - <field id="allowspecific">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="debug" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Debug Mode</label> - <config_path>payment/payflowpro/debug</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="verify_peer" translate="label" type="select" sortOrder="35" showInDefault="1" showInWebsite="1"> - <label>Enable SSL verification</label> - <config_path>payment/payflowpro/verify_peer</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="useccv" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>Require CVV Entry</label> - <config_path>payment/payflowpro/useccv</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <group id="paypal_payflow_avs_check" translate="label" showInDefault="1" showInWebsite="1" sortOrder="80"> - <label>CVV and AVS Settings</label> - <field id="heading_avs_settings" translate="label" sortOrder="0" showInDefault="1" showInWebsite="1"> - <label>Reject Transaction if:</label> - <frontend_model>Magento\Config\Block\System\Config\Form\Field\Heading</frontend_model> - <attribute type="shared">1</attribute> - </field> - <field id="avs_street" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1"> - <label>AVS Street Does Not Match</label> - <config_path>payment/payflowpro/avs_street</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="avs_zip" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1"> - <label>AVS Zip Does Not Match</label> - <config_path>payment/payflowpro/avs_zip</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="avs_international" translate="label" type="select" sortOrder="3" showInDefault="1" showInWebsite="1"> - <label>Card Issuer Is Outside The United States</label> - <config_path>payment/payflowpro/avs_international</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="avs_security_code" translate="label" type="select" sortOrder="4" showInDefault="1" showInWebsite="1"> - <label>Card Security Code Does Not Match</label> - <config_path>payment/payflowpro/avs_security_code</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">0</attribute> - </field> - </group> - <group id="paypal_payflow_settlement_report" translate="label" showInDefault="1" showInWebsite="1" sortOrder="90"> - <label>Settlement Report Settings</label> - <field id="heading_sftp" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/heading_sftp"/> - <field id="settlement_reports_ftp_login" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_login"/> - <field id="settlement_reports_ftp_password" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_password"/> - <field id="settlement_reports_ftp_sandbox" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_sandbox"/> - <field id="settlement_reports_ftp_ip" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_ip"/> - <field id="settlement_reports_ftp_path" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_path"/> - <field id="heading_schedule" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/heading_schedule"/> - <field id="settlement_reports_active" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_active"/> - <field id="settlement_reports_schedule" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_schedule"/> - <field id="settlement_reports_time" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_time"/> - </group> - </group> - </group> - </group> - <group id="payflow_link" translate="label comment" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20"> - <label>Payflow Link</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment</frontend_model> - <fieldset_css>paypal-other-section</fieldset_css> - <comment><![CDATA[Connect your merchant account with a PCI-compliant gateway that lets customers pay without leaving your site. (<u>Includes Express Checkout</u>)]]></comment> - <attribute type="activity_path">payment/payflow_link/active</attribute> - <group id="configuration_details" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="4"> - <comment>http://docs.magento.com/m2/ce/user_guide/payment/paypal-payflow-link.html</comment> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Hint</frontend_model> - </group> - <group id="payflow_link_required" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Required PayPal Settings</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <group id="payflow_link_payflow_link" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Payflow Link and Express Checkout</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="business_account" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/business_account" translate="label" sortOrder="5"> - <frontend_class>not-required</frontend_class> - <label>Email Associated with PayPal Merchant Account (Optional)</label> - <attribute type="shared">1</attribute> - </field> - <field id="partner" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1"> - <label>Partner</label> - <config_path>payment/payflow_link/partner</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="vendor" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Vendor</label> - <config_path>payment/payflow_link/vendor</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="user" translate="label comment" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>User</label> - <comment>If you do not have multiple users set up on your account, please re-enter your Vendor/Merchant Login here.</comment> - <config_path>payment/payflow_link/user</config_path> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - <attribute type="shared">1</attribute> - </field> - <field id="pwd" translate="label" type="obscure" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>Password</label> - <config_path>payment/payflow_link/pwd</config_path> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - <attribute type="shared">1</attribute> - </field> - <field id="sandbox_flag" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1"> - <label>Test Mode</label> - <config_path>payment/payflow_link/sandbox_flag</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="use_proxy" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1"> - <label>Use Proxy</label> - <config_path>payment/payflow_link/use_proxy</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="proxy_host" translate="label" type="text" sortOrder="70" showInDefault="1" showInWebsite="1"> - <label>Proxy Host</label> - <config_path>payment/payflow_link/proxy_host</config_path> - <depends> - <field id="use_proxy">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="proxy_port" translate="label" type="text" sortOrder="80" showInDefault="1" showInWebsite="1"> - <label>Proxy Port</label> - <config_path>payment/payflow_link/proxy_port</config_path> - <depends> - <field id="use_proxy">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="payflowlink_info" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="90"> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Payflowlink\Info</frontend_model> - <attribute type="shared">1</attribute> - </field> - </group> - - - - <field id="enable_payflow_link" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Enable Payflow Link</label> - <config_path>payment/payflow_link/active</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Payment</frontend_model> - <requires> - <group id="payflow_link_payflow_link"/> - </requires> - </field> - <field id="enable_express_checkout_basic" translate="label" type="select" sortOrder="40"> - <label>Enable Express Checkout</label> - <config_path>payment/payflow_express/active</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Express</frontend_model> - </field> - <field id="enable_express_checkout" extends="payment_all_paypal/payflow_link/payflow_link_required/enable_express_checkout_basic" showInDefault="1" showInWebsite="1"> - <requires> - <field id="enable_payflow_link"/> - </requires> - </field> - <field id="enable_express_checkout_bml" extends="payment_all_paypal/express_checkout/express_checkout_required/enable_express_checkout_bml" sortOrder="41"> - <comment><![CDATA[Payflow Link lets you give customers access to financing through PayPal Credit® - at no additional cost to you. - You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. - <a href="https:/www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> - </comment> - <config_path>payment/payflow_express_bml/active</config_path> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Bml</frontend_model> - <requires> - <field id="enable_express_checkout"/> - </requires> - </field> - <field id="express_checkout_bml_sort_order" sortOrder="50" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_bml_sort_order"> - <config_path>payment/payflow_express_bml/sort_order</config_path> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Depends\BmlSortOrder</frontend_model> - <depends> - <field id="enable_express_checkout_bml">1</field> - </depends> - </field> - - - - <group id="payflow_link_advertise_bml" translate="label comment" showInDefault="1" showInWebsite="1" sortOrder="60"> - <label>Advertise PayPal Credit</label> - <comment> - <![CDATA[<a href="https:/financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> - <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing - from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. - Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. - The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href="https:/financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> - </comment> - <field id="bml_publisher_id" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_publisher_id" /> - <field id="bml_wizard" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_wizard" /> - <group id="payflow_link_settings_bml_homepage" translate="label" showInWebsite="1" sortOrder="20" showInDefault="1" showInStore="1"> - <label>Home Page</label> - <field id="bml_homepage_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_homepage/bml_homepage_display"/> - <field id="payflow_link_bml_homepage_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_homepage/bml_homepage_position"/> - <field id="payflow_link_bml_homepage_size1" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="30"> - <label>Size</label> - <config_path>payment/paypal_express_bml/homepage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeHPH</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="payflow_link_bml_homepage_position">0</field> - </depends> - </field> - <field id="payflow_link_bml_homepage_size2" extends="payment_all_paypal/payflow_link/payflow_link_required/payflow_link_advertise_bml/payflow_link_settings_bml_homepage/payflow_link_bml_homepage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeHPS</source_model> - <depends> - <field id="payflow_link_bml_homepage_position">1</field> - </depends> - </field> - </group> - <group id="payflow_link_settings_bml_categorypage" translate="label" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Catalog Category Page</label> - <field id="bml_categorypage_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_categorypage/bml_categorypage_display"/> - <field id="payflow_link_bml_categorypage_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_categorypage/bml_categorypage_position" /> - <field id="payflow_link_bml_categorypage_size1" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="40"> - <label>Size</label> - <config_path>payment/paypal_express_bml/categorypage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCCPC</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="payflow_link_bml_categorypage_position">0</field> - </depends> - </field> - <field id="payflow_link_bml_categorypage_size2" extends="payment_all_paypal/payflow_link/payflow_link_required/payflow_link_advertise_bml/payflow_link_settings_bml_categorypage/payflow_link_bml_categorypage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCCPS</source_model> - <depends> - <field id="payflow_link_bml_categorypage_position">1</field> - </depends> - </field> - </group> - <group id="payflow_link_settings_bml_productpage" translate="label" showInDefault="1" showInStore="1" showInWebsite="1" sortOrder="50"> - <label>Catalog Product Page</label> - <field id="bml_productpage_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_productpage/bml_productpage_display" /> - <field id="payflow_link_bml_productpage_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_productpage/bml_productpage_position" /> - <field id="payflow_link_bml_productpage_size1" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="30"> - <label>Size</label> - <config_path>payment/paypal_express_bml/productpage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCPPC</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="payflow_link_bml_productpage_position">0</field> - </depends> - </field> - <field id="payflow_link_bml_productpage_size2" extends="payment_all_paypal/payflow_link/payflow_link_required/payflow_link_advertise_bml/payflow_link_settings_bml_productpage/payflow_link_bml_productpage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCPPN</source_model> - <depends> - <field id="payflow_link_bml_productpage_position">1</field> - </depends> - </field> - </group> - <group id="payflow_link_settings_bml_checkout" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="60"> - <label>Checkout Cart Page</label> - <field id="bml_checkout_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_checkout/bml_checkout_display" /> - <field id="payflow_link_bml_checkout_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_checkout/bml_checkout_position" /> - <field id="payflow_link_bml_checkout_size1" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Size</label> - <config_path>payment/paypal_express_bml/checkout_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCheckoutC</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="payflow_link_bml_checkout_position">0</field> - </depends> - </field> - <field id="payflow_link_bml_checkout_size2" extends="payment_all_paypal/payflow_link/payflow_link_required/payflow_link_advertise_bml/payflow_link_settings_bml_checkout/payflow_link_bml_checkout_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCheckoutN</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="payflow_link_bml_checkout_position">1</field> - </depends> - </field> - </group> - </group> - </group> - <group id="settings_payflow_link" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20"> - <label>Basic Settings - PayPal Payflow Link</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="title" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Title</label> - <comment>It is recommended to set this value to "Debit or Credit Card" per store views.</comment> - <config_path>payment/payflow_link/title</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="sort_order" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Sort Order</label> - <config_path>payment/payflow_link/sort_order</config_path> - <frontend_class>validate-number</frontend_class> - <attribute type="shared">1</attribute> - </field> - <field id="payment_action" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Payment Action</label> - <config_path>payment/payflow_link/payment_action</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\PaymentActions</source_model> - <attribute type="shared">1</attribute> - </field> - <group id="settings_payflow_link_advanced" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="40"> - <label>Advanced Settings</label> - <fieldset_css>config-advanced</fieldset_css> - <field id="allowspecific" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1"> - <label>Payment Applicable From</label> - <config_path>payment/payflow_link/allowspecific</config_path> - <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Countries Payment Applicable From</label> - <config_path>payment/payflow_link/specificcountry</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BuyerCountry</source_model> - <depends> - <field id="allowspecific">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="debug" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Debug Mode</label> - <config_path>payment/payflow_link/debug</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="verify_peer" translate="label" type="select" sortOrder="35" showInDefault="1" showInWebsite="1"> - <label>Enable SSL verification</label> - <config_path>payment/payflow_link/verify_peer</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="csc_editable" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>CVV Entry is Editable</label> - <config_path>payment/payflow_link/csc_editable</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="csc_required" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1"> - <label>Require CVV Entry</label> - <config_path>payment/payflow_link/csc_required</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <depends> - <field id="csc_editable">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="email_confirmation" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1"> - <label>Send Email Confirmation</label> - <config_path>payment/payflow_link/email_confirmation</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="url_method" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1"> - <label>URL method for Cancel URL and Return URL</label> - <config_path>payment/payflow_link/url_method</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\UrlMethod</source_model> - <attribute type="shared">1</attribute> - </field> - <group id="payflow_link_settlement_report" translate="label" showInDefault="1" showInWebsite="1" sortOrder="80"> - <label>Settlement Report Settings</label> - <field id="heading_sftp" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/heading_sftp"/> - <field id="settlement_reports_ftp_login" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_login"/> - <field id="settlement_reports_ftp_password" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_password"/> - <field id="settlement_reports_ftp_sandbox" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_sandbox"/> - <field id="settlement_reports_ftp_ip" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_ip"/> - <field id="settlement_reports_ftp_path" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_path"/> - <field id="heading_schedule" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/heading_schedule"/> - <field id="settlement_reports_active" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_active"/> - <field id="settlement_reports_schedule" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_schedule"/> - <field id="settlement_reports_time" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_time"/> - </group> - <group id="payflow_link_frontend" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="90"> - <label>Frontend Experience Settings</label> - <field id="logo" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/logo"/> - <field id="paypal_pages" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_pages"/> - <field id="page_style" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/page_style"/> - <field id="paypal_hdrimg" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_hdrimg"/> - <field id="paypal_hdrbackcolor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_hdrbackcolor"/> - <field id="paypal_hdrbordercolor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_hdrbordercolor"/> - <field id="paypal_payflowcolor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_payflowcolor"/> - </group> - </group> - </group> - <group id="settings_payflow_link_express_checkout" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="30"> - <label>Basic Settings - PayPal Express Checkout</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="title" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_express_checkout/title" /> - <field id="sort_order" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_express_checkout/sort_order" /> - <field id="payment_action" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_express_checkout/payment_action" /> - <field id="visible_on_product" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_express_checkout/visible_on_product" /> - <group id="settings_payflow_link_express_checkout_advanced" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_express_checkout/paypal_payflow_express_checkout_advanced"/> - </group> - </group> - <group id="express_checkout" translate="label comment" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="60"> - <label>Express Checkout</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment</frontend_model> - <fieldset_css>paypal-other-section</fieldset_css> - <comment>Add PayPal as an additional payment method to your checkout page.</comment> - <attribute type="activity_path">payment/paypal_express/active</attribute> - <group id="configuration_details" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="4"> - <comment>http://docs.magento.com/m2/ce/user_guide/payment/paypal-express-checkout.html</comment> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Hint</frontend_model> - </group> - <group id="express_checkout_required" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Required PayPal Settings</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <group id="express_checkout_required_express_checkout" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Express Checkout</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="business_account" translate="label comment tooltip" showInDefault="1" showInWebsite="1" sortOrder="5"> - <label>Email Associated with PayPal Merchant Account (Optional)</label> - <frontend_class>not-required</frontend_class> - <comment> - <![CDATA[<a href="http://www.magentocommerce.com/paypal">Start accepting payments via PayPal!</a>]]> - </comment> - <tooltip>Don't have a PayPal account? Simply enter your email address.</tooltip> - <config_path>paypal/general/business_account</config_path> - <validate>validate-email</validate> - <attribute type="shared">1</attribute> - </field> - <field id="api_authentication" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>API Authentication Methods</label> - <config_path>paypal/wpp/api_authentication</config_path> - <source_model>Magento\Paypal\Model\Config::getApiAuthenticationMethods</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="api_username" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>API Username</label> - <config_path>paypal/wpp/api_username</config_path> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - <attribute type="shared">1</attribute> - </field> - <field id="api_password" translate="label" type="obscure" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>API Password</label> - <config_path>paypal/wpp/api_password</config_path> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - <attribute type="shared">1</attribute> - </field> - <field id="api_signature" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1"> - <label>API Signature</label> - <config_path>paypal/wpp/api_signature</config_path> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - <attribute type="shared">1</attribute> - <depends> - <field id="api_authentication">0</field> - </depends> - </field> - <field id="api_cert" translate="label" type="file" sortOrder="60" showInDefault="1" showInWebsite="1"> - <label>API Certificate</label> - <config_path>paypal/wpp/api_cert</config_path> - <backend_model>Magento\Paypal\Model\System\Config\Backend\Cert</backend_model> - <attribute type="shared">1</attribute> - <depends> - <field id="api_authentication">1</field> - </depends> - </field> - <field id="api_wizard" translate="button_label attribute sandbox_button_label" sortOrder="70" showInDefault="1" showInWebsite="1"> - <attribute type="button_label">Get Credentials from PayPal</attribute> - <attribute type="button_url"> - <![CDATA[https://www.paypal.com/webapps/merchantboarding/webflow/externalpartnerflow]]> - </attribute> - - <attribute type="sandbox_button_label">Sandbox Credentials</attribute> - <attribute type="sandbox_button_url"> - <![CDATA[https://www.sandbox.paypal.com/webapps/merchantboarding/webflow/externalpartnerflow]]> - </attribute> - - <!-- partnerId --> - <attribute type="partner_id">NB9WWHYEMVUMS</attribute> - <!-- partnerLogoUrl --> - <attribute type="partner_logo_url">Magento_Backend/web/images/logo-magento.png</attribute> - <!-- receiveCredentials --> - <attribute type="receive_credentials">FALSE</attribute> - <!-- showPermissions --> - <attribute type="show_permissions">FALSE</attribute> - <!-- displayMode --> - <attribute type="display_mode">embedded</attribute> - <!-- productIntentID --> - <attribute type="product_intent_id">pp_express</attribute> - - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\ApiWizard</frontend_model> - <attribute type="shared">1</attribute> - </field> - <field id="sandbox_flag" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1"> - <label>Sandbox Mode</label> - <config_path>paypal/wpp/sandbox_flag</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="use_proxy" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1"> - <label>API Uses Proxy</label> - <config_path>paypal/wpp/use_proxy</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="proxy_host" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1"> - <label>Proxy Host</label> - <config_path>paypal/wpp/proxy_host</config_path> - <attribute type="shared">1</attribute> - <depends> - <field id="use_proxy">1</field> - </depends> - </field> - <field id="proxy_port" translate="label" type="text" sortOrder="110" showInDefault="1" showInWebsite="1"> - <label>Proxy Port</label> - <config_path>paypal/wpp/proxy_port</config_path> - <attribute type="shared">1</attribute> - <depends> - <field id="use_proxy">1</field> - </depends> - </field> - </group> - <field id="enable_express_checkout" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Enable this Solution</label> - <config_path>payment/paypal_express/active</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Payment</frontend_model> - <requires> - <group id="express_checkout_required_express_checkout"/> - </requires> - </field> - <field id="enable_in_context_checkout" translate="label comment" type="select" sortOrder="21" showInDefault="1" showInWebsite="1"> - <label>Enable In-Context Checkout Experience</label> - <comment> - <![CDATA[See PayPal Feature Support details and list of supported regions - <a href="https://developer.paypal.com/docs/classic/express-checkout/in-context/" target="_blank">here</a>.]]> - </comment> - <config_path>payment/paypal_express/in_context</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\InContextApi</frontend_model> - <requires> - <field id="enable_express_checkout"/> - </requires> - </field> - <field id="merchant_id" translate="label" type="text" sortOrder="22" showInDefault="1" showInWebsite="1"> - <label>Merchant Account ID</label> - <tooltip>You can look up your merchant ID by logging into https://www.paypal.com/. Click the profile icon on the top right side of the page and then select Profile and settings in the Business Profile menu. (If you do not see the profile icon at the top of the page, click Profile, which appears in the top menu when the My Account tab is selected.) Click My business info on the left, and the Merchant account ID is displayed in the list of profile items on the right.</tooltip> - <config_path>payment/paypal_express/merchant_id</config_path> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Depends\MerchantId</frontend_model> - <depends> - <field id="enable_in_context_checkout">1</field> - </depends> - <validate>required-entry</validate> - </field> - <field id="enable_express_checkout_bml" translate="label comment" type="select" sortOrder="23" showInDefault="1" showInWebsite="1"> - <label>Enable PayPal Credit</label> - <comment><![CDATA[PayPal Express Checkout lets you give customers access to financing through PayPal Credit® - at no additional cost to you. - You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. - <a href="https:/www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> - </comment> - <config_path>payment/paypal_express_bml/active</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\BmlApi</frontend_model> - <requires> - <field id="enable_express_checkout"/> - </requires> - </field> - <field id="express_checkout_bml_sort_order" translate="label" type="text" sortOrder="25" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Sort Order PayPal Credit</label> - <config_path>payment/paypal_express_bml/sort_order</config_path> - <frontend_class>validate-number</frontend_class> - <attribute type="shared">1</attribute> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Depends\BmlApiSortOrder</frontend_model> - <depends> - <field id="enable_express_checkout_bml">1</field> - </depends> - </field> - <group id="advertise_bml" translate="label comment" showInDefault="1" showInWebsite="1" sortOrder="30"> - <label>Advertise PayPal Credit</label> - <comment> - <![CDATA[<a href="https:/financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> - <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing - from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. - Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. - The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href="https:/financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> - </comment> - <field id="bml_publisher_id" translate="label comment tooltip" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Publisher ID</label> - <comment><![CDATA[Required to display a banner]]></comment> - <config_path>payment/paypal_express_bml/publisher_id</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="bml_wizard" translate="button_label" sortOrder="15" showInDefault="1" showInWebsite="1"> - <button_label>Get Publisher ID from PayPal</button_label> - <button_url><![CDATA[https:/financing.paypal.com/ppfinportal/cart/index?dcp=4eff8563b9cc505e0b9afaff3256705081553c79]]></button_url> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\BmlApiWizard</frontend_model> - </field> - <group id="settings_bml_homepage" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20"> - <label>Home Page</label> - <field id="bml_homepage_display" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Display</label> - <config_path>payment/paypal_express_bml/homepage_display</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="bml_homepage_position" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="20"> - <label>Position</label> - <config_path>payment/paypal_express_bml/homepage_position</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlPosition::getBmlPositionsHP</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="bml_homepage_size1" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="30"> - <label>Size</label> - <config_path>payment/paypal_express_bml/homepage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeHPH</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="bml_homepage_position">0</field> - </depends> - </field> - <field id="bml_homepage_size2" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_homepage/bml_homepage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeHPS</source_model> - <depends> - <field id="bml_homepage_position">1</field> - </depends> - </field> - </group> - <group id="settings_bml_categorypage" translate="label" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Catalog Category Page</label> - <field id="bml_categorypage_display" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Display</label> - <config_path>payment/paypal_express_bml/categorypage_display</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="bml_categorypage_position" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="20"> - <label>Position</label> - <config_path>payment/paypal_express_bml/categorypage_position</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlPosition::getBmlPositionsCCP</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="bml_categorypage_size1" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="30"> - <label>Size</label> - <config_path>payment/paypal_express_bml/categorypage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCCPC</source_model> - <attribute type="shared">1</attribute> - <depends><field id="bml_categorypage_position">0</field></depends> - </field> - <field id="bml_categorypage_size2" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_categorypage/bml_categorypage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCCPS</source_model> - <depends><field id="bml_categorypage_position">1</field></depends> - </field> - </group> - <group id="settings_bml_productpage" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="40"> - <label>Catalog Product Page</label> - <field id="bml_productpage_display" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Display</label> - <config_path>payment/paypal_express_bml/productpage_display</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="bml_productpage_position" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="20"> - <label>Position</label> - <config_path>payment/paypal_express_bml/productpage_position</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlPosition::getBmlPositionsCPP</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="bml_productpage_size1" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="30"> - <label>Size</label> - <config_path>payment/paypal_express_bml/productpage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCPPC</source_model> - <attribute type="shared">1</attribute> - <depends><field id="bml_productpage_position">0</field></depends> - </field> - <field id="bml_productpage_size2" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_productpage/bml_productpage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCPPN</source_model> - <depends><field id="bml_productpage_position">1</field></depends> - </field> - </group> - <group id="settings_bml_checkout" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="50"> - <label>Checkout Cart Page</label> - <field id="bml_checkout_display" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Display</label> - <config_path>payment/paypal_express_bml/checkout_display</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="bml_checkout_position" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="20"> - <label>Position</label> - <config_path>payment/paypal_express_bml/checkout_position</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlPosition::getBmlPositionsCheckout</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="bml_checkout_size1" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="30"> - <label>Size</label> - <config_path>payment/paypal_express_bml/checkout_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCheckoutC</source_model> - <attribute type="shared">1</attribute> - <depends><field id="bml_checkout_position">0</field></depends> - </field> - <field id="bml_checkout_size2" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_checkout/bml_checkout_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCheckoutN</source_model> - <attribute type="shared">1</attribute> - <depends><field id="bml_checkout_position">1</field></depends> - </field> - </group> - </group> - </group> - <group id="settings_ec" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20"> - <label>Basic Settings - PayPal Express Checkout</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="title" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Title</label> - <comment>It is recommended to set this value to "PayPal" per store views.</comment> - <config_path>payment/paypal_express/title</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="sort_order" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Sort Order</label> - <config_path>payment/paypal_express/sort_order</config_path> - <frontend_class>validate-number</frontend_class> - <attribute type="shared">1</attribute> - </field> - <field id="payment_action" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Payment Action</label> - <config_path>payment/paypal_express/payment_action</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\PaymentActions\Express</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="visible_on_product" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Display on Product Details Page</label> - <config_path>payment/paypal_express/visible_on_product</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="authorization_honor_period" translate="label comment" type="text" sortOrder="50" showInDefault="1" showInWebsite="1"> - <label>Authorization Honor Period (days)</label> - <comment>Specifies what the Authorization Honor Period is on the merchant’s PayPal account. It must mirror the setting in PayPal.</comment> - <config_path>payment/paypal_express/authorization_honor_period</config_path> - <attribute type="shared">1</attribute> - <depends> - <field id="payment_action">Order</field> - </depends> - </field> - <field id="order_valid_period" translate="label comment" type="text" sortOrder="60" showInDefault="1" showInWebsite="1"> - <label>Order Valid Period (days)</label> - <comment>Specifies what the Order Valid Period is on the merchant’s PayPal account. It must mirror the setting in PayPal.</comment> - <config_path>payment/paypal_express/order_valid_period</config_path> - <attribute type="shared">1</attribute> - <depends> - <field id="payment_action">Order</field> - </depends> - </field> - <field id="child_authorization_number" translate="label comment" type="text" sortOrder="70" showInDefault="1" showInWebsite="1"> - <label>Number of Child Authorizations</label> - <comment>The default number of child authorizations in your PayPal account is 1. To do multiple authorizations please contact PayPal to request an increase.</comment> - <config_path>payment/paypal_express/child_authorization_number</config_path> - <attribute type="shared">1</attribute> - <depends> - <field id="payment_action">Order</field> - </depends> - </field> - <group id="settings_ec_advanced" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="80"> - <label>Advanced Settings</label> - <fieldset_css>config-advanced</fieldset_css> - <field id="visible_on_cart" translate="label comment" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Display on Shopping Cart</label> - <config_path>payment/paypal_express/visible_on_cart</config_path> - <comment>Also affects mini-shopping cart.</comment> - <source_model>Magento\Paypal\Model\System\Config\Source\Yesnoshortcut</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="allowspecific" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1"> - <label>Payment Applicable From</label> - <config_path>payment/paypal_express/allowspecific</config_path> - <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Countries Payment Applicable From</label> - <config_path>payment/paypal_express/specificcountry</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BuyerCountry</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="allowspecific">1</field> - </depends> - </field> - <field id="debug" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Debug Mode</label> - <config_path>payment/paypal_express/debug</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="verify_peer" translate="label" type="select" sortOrder="35" showInDefault="1" showInWebsite="1"> - <label>Enable SSL verification</label> - <config_path>payment/paypal_express/verify_peer</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="line_items_enabled" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>Transfer Cart Line Items</label> - <config_path>payment/paypal_express/line_items_enabled</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="transfer_shipping_options" translate="label tooltip comment" type="select" sortOrder="50" showInDefault="1" showInWebsite="1"> - <label>Transfer Shipping Options</label> - <config_path>payment/paypal_express/transfer_shipping_options</config_path> - <tooltip>If this option is enabled, customer can change shipping address and shipping method on PayPal website. In live mode works via HTTPS protocol only.</tooltip> - <comment>Notice that PayPal can handle up to 10 shipping options. That is why Magento will transfer only first 10 cheapest shipping options if there are more than 10 available.</comment> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="line_items_enabled">1</field> - </depends> - </field> - <field id="button_flavor" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Shortcut Buttons Flavor</label> - <config_path>paypal/wpp/button_flavor</config_path> - <source_model>Magento\Paypal\Model\Config::getExpressCheckoutButtonFlavors</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="solution_type" translate="label comment" type="select" sortOrder="70" showInDefault="1" showInWebsite="1"> - <label>Enable PayPal Guest Checkout</label> - <comment>Ability for buyer to purchase without PayPal account.</comment> - <config_path>payment/paypal_express/solution_type</config_path> - <source_model>Magento\Paypal\Model\Config::getExpressCheckoutSolutionTypes</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="require_billing_address" translate="label comment" type="select" sortOrder="80" showInDefault="1" showInWebsite="1"> - <label>Require Customer's Billing Address</label> - <comment>This feature needs be enabled first for the merchant account through PayPal technical support.</comment> - <config_path>payment/paypal_express/require_billing_address</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\RequireBillingAddress</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="allow_ba_signup" translate="label comment tooltip" type="select" sortOrder="90" showInDefault="1" showInWebsite="1"> - <label>Billing Agreement Signup</label> - <comment>Whether to create a billing agreement, if there are no active billing agreements available.</comment> - <tooltip> - <![CDATA[Merchants need to apply to PayPal for enabling billing agreements feature. Do not enable this option until PayPal confirms that billing agreements are enabled for your merchant account.]]> - </tooltip> - <config_path>payment/paypal_express/allow_ba_signup</config_path> - <source_model>Magento\Paypal\Model\Config::getExpressCheckoutBASignupOptions</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="skip_order_review_step" translate="label" type="select" sortOrder="95" showInDefault="1" showInWebsite="1"> - <label>Skip Order Review Step</label> - <config_path>payment/paypal_express/skip_order_review_step</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <group id="express_checkout_billing_agreement" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="100"> - <label>PayPal Billing Agreement Settings</label> - <field id="active" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="1"> - <label>Enabled</label> - <comment> - <![CDATA[Will appear as a payment option only for customers who have at least one active billing agreement.]]> - </comment> - <config_path>payment/paypal_billing_agreement/active</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="title" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Title</label> - <config_path>payment/paypal_billing_agreement/title</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="sort_order" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Sort Order</label> - <config_path>payment/paypal_billing_agreement/sort_order</config_path> - <frontend_class>validate-number</frontend_class> - <attribute type="shared">1</attribute> - </field> - <field id="payment_action" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>Payment Action</label> - <config_path>payment/paypal_billing_agreement/payment_action</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\PaymentActions</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="allowspecific" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1"> - <label>Payment Applicable From</label> - <config_path>payment/paypal_billing_agreement/allowspecific</config_path> - <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="60" showInDefault="1" showInWebsite="1"> - <label>Countries Payment Applicable From</label> - <config_path>payment/paypal_billing_agreement/specificcountry</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BuyerCountry</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="allowspecific">1</field> - </depends> - </field> - <field id="debug" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1"> - <label>Debug Mode</label> - <config_path>payment/paypal_billing_agreement/debug</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="verify_peer" translate="label" type="select" sortOrder="75" showInDefault="1" showInWebsite="1"> - <label>Enable SSL verification</label> - <config_path>payment/paypal_billing_agreement/verify_peer</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="line_items_enabled" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1"> - <label>Transfer Cart Line Items</label> - <config_path>payment/paypal_billing_agreement/line_items_enabled</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="allow_billing_agreement_wizard" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1"> - <label>Allow in Billing Agreement Wizard</label> - <config_path>payment/paypal_billing_agreement/allow_billing_agreement_wizard</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - </group> - <group id="express_checkout_settlement_report" translate="label" showInDefault="1" showInWebsite="1" sortOrder="110"> - <label>Settlement Report Settings</label> - <field id="heading_sftp" translate="label" sortOrder="10" showInDefault="1" showInWebsite="1"> - <label>SFTP Credentials</label> - <frontend_model>Magento\Config\Block\System\Config\Form\Field\Heading</frontend_model> - <attribute type="shared">1</attribute> - </field> - <field id="settlement_reports_ftp_login" translate="label" type="obscure" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Login</label> - <config_path>paypal/fetch_reports/ftp_login</config_path> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - <attribute type="shared">1</attribute> - </field> - <field id="settlement_reports_ftp_password" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Password</label> - <config_path>paypal/fetch_reports/ftp_password</config_path> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - <attribute type="shared">1</attribute> - </field> - <field id="settlement_reports_ftp_sandbox" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>Sandbox Mode</label> - <config_path>paypal/fetch_reports/ftp_sandbox</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="settlement_reports_ftp_ip" translate="label comment tooltip" type="text" sortOrder="50" showInDefault="1" showInWebsite="1"> - <label>Custom Endpoint Hostname or IP-Address</label> - <comment>By default it is "reports.paypal.com".</comment> - <tooltip>Use colon to specify port. For example: "test.example.com:5224".</tooltip> - <config_path>paypal/fetch_reports/ftp_ip</config_path> - <attribute type="shared">1</attribute> - <depends> - <field id="settlement_reports_ftp_sandbox">0</field> - </depends> - </field> - <field id="settlement_reports_ftp_path" translate="label comeent" type="text" sortOrder="60" showInDefault="1" showInWebsite="1"> - <label>Custom Path</label> - <comment>By default it is "/ppreports/outgoing".</comment> - <config_path>paypal/fetch_reports/ftp_path</config_path> - <attribute type="shared">1</attribute> - <depends> - <field id="settlement_reports_ftp_sandbox">0</field> - </depends> - </field> - <field id="heading_schedule" translate="label" sortOrder="70" showInDefault="1" showInWebsite="1"> - <label>Scheduled Fetching</label> - <frontend_model>Magento\Config\Block\System\Config\Form\Field\Heading</frontend_model> - <attribute type="shared">1</attribute> - </field> - <field id="settlement_reports_active" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1"> - <label>Enable Automatic Fetching</label> - <config_path>paypal/fetch_reports/active</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="settlement_reports_schedule" translate="label comment" type="select" sortOrder="90" showInDefault="1"> - <label>Schedule</label> - <comment>PayPal retains reports for 45 days.</comment> - <config_path>paypal/fetch_reports/schedule</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\FetchingSchedule</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="settlement_reports_time" translate="label" type="time" sortOrder="100" showInDefault="1"> - <label>Time of Day</label> - <config_path>paypal/fetch_reports/time</config_path> - <attribute type="shared">1</attribute> - </field> - </group> - <group id="express_checkout_frontend" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="120"> - <label>Frontend Experience Settings</label> - <field id="logo" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>PayPal Product Logo</label> - <comment>Displays on catalog pages and homepage.</comment> - <config_path>paypal/style/logo</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\Logo</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="paypal_pages" translate="label" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>PayPal Merchant Pages Style</label> - <frontend_model>Magento\Config\Block\System\Config\Form\Field\Heading</frontend_model> - <attribute type="shared">1</attribute> - </field> - <field id="page_style" translate="label tooltip" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Page Style</label> - <config_path>paypal/style/page_style</config_path> - <tooltip> - <![CDATA[Allowable values: "paypal", "primary" (default), your_custom_value (a custom payment page style from your merchant account profile).]]> - </tooltip> - <attribute type="shared">1</attribute> - </field> - <field id="paypal_hdrimg" translate="label tooltip" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Header Image URL</label> - <config_path>paypal/style/paypal_hdrimg</config_path> - <tooltip> - <![CDATA[The image at the top left of the checkout page. Max size is 750x90-pixel. <strong style="color:red">https</strong> is highly encouraged.]]> - </tooltip> - <attribute type="shared">1</attribute> - </field> - <field id="paypal_hdrbackcolor" translate="label tooltip" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Header Background Color</label> - <config_path>paypal/style/paypal_hdrbackcolor</config_path> - <tooltip> - <![CDATA[The background color for the header of the checkout page. Case-insensitive six-character HTML hexadecimal color code in ASCII.]]> - </tooltip> - <attribute type="shared">1</attribute> - </field> - <field id="paypal_hdrbordercolor" translate="label tooltip" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Header Border Color</label> - <config_path>paypal/style/paypal_hdrbordercolor</config_path> - <tooltip>2-pixel perimeter around the header space.</tooltip> - <attribute type="shared">1</attribute> - </field> - <field id="paypal_payflowcolor" translate="label tooltip" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Page Background Color</label> - <config_path>paypal/style/paypal_payflowcolor</config_path> - <tooltip> - <![CDATA[The background color for the checkout page around the header and payment form.]]> - </tooltip> - <attribute type="shared">1</attribute> - </field> - </group> - </group> - </group> - </group> - <group id="payments_pro_hosted_solution" translate="label comment" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20"> - <label>Website Payments Pro Hosted Solution</label> - <fieldset_css>paypal-other-section</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment</frontend_model> - <attribute type="activity_path">payment/hosted_pro/active</attribute> - <comment><![CDATA[Accept payments with a PCI compliant checkout that keeps customers on your site. (<u>Includes Express Checkout</u>)]]></comment> - <attribute type="paypal_ec_separate">1</attribute> - <group id="configuration_details" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="4"> - <comment>http://docs.magento.com/m2/ce/user_guide/payment/paypal-payments-pro.html</comment> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Hint</frontend_model> - </group> - <group id="pphs_required_settings" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Required PayPal Settings</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <group id="pphs_required_settings_pphs" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Payments Pro Hosted Solution</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="business_account" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/business_account"/> - <field id="api_authentication" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/api_authentication"/> - <field id="api_username" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/api_username" /> - <field id="api_password" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/api_password" /> - <field id="api_signature" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/api_signature" /> - <field id="api_cert" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/api_cert" /> - <field id="api_wizard" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/api_wizard" /> - <field id="sandbox_flag" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/sandbox_flag" /> - <field id="use_proxy" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/use_proxy" /> - <field id="proxy_host" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/proxy_host" /> - <field id="proxy_port" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/proxy_port" /> - </group> - <field id="pphs_enable" type="select" translate="label" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Enable this Solution</label> - <config_path>payment/hosted_pro/active</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Payment</frontend_model> - <requires> - <group id="pphs_required_settings_pphs"/> - </requires> - <frontend_class>paypal-enabler paypal-ec-separate</frontend_class> - </field> - - <field id="enable_express_checkout_bml" extends="payment_all_paypal/express_checkout/express_checkout_required/enable_express_checkout_bml" sortOrder="21"> - <comment><![CDATA[Payments Pro Hosted Solution lets you give customers access to financing through PayPal Credit® - at no additional cost to you. - You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. - <a href="https:/www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> - </comment> - <requires> - <field id="pphs_enable"/> - </requires> - </field> - <group id="pphs_advertise_bml" translate="label comment" showInDefault="1" showInWebsite="1" sortOrder="22"> - <label>Advertise PayPal Credit</label> - <comment> - <![CDATA[<a href="https:/financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> - <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing - from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. - Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. - The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href="https:/financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> - </comment> - <field id="bml_publisher_id" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_publisher_id" /> - <field id="bml_wizard" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_wizard" /> - <group id="pphs_settings_bml_homepage" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20"> - <label>Home Page</label> - <field id="bml_homepage_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_homepage/bml_homepage_display"/> - <field id="pphs_bml_homepage_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_homepage/bml_homepage_position"/> - <field id="pphs_bml_homepage_size1" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="30"> - <label>Size</label> - <config_path>payment/paypal_express_bml/homepage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeHPH</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="pphs_bml_homepage_position">0</field> - </depends> - </field> - <field id="pphs_bml_homepage_size2" extends="payment_all_paypal/payments_pro_hosted_solution/pphs_required_settings/pphs_advertise_bml/pphs_settings_bml_homepage/pphs_bml_homepage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeHPS</source_model> - <depends> - <field id="pphs_bml_homepage_position">1</field> - </depends> - </field> - </group> - <group id="pphs_settings_bml_categorypage" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="30"> - <label>Catalog Category Page</label> - <field id="bml_categorypage_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_categorypage/bml_categorypage_display"/> - <field id="pphs_bml_categorypage_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_categorypage/bml_categorypage_position" /> - <field id="pphs_bml_categorypage_size1" translate="label" showInDefault="1" showInWebsite="1" sortOrder="30" type="select"> - <label>Size</label> - <config_path>payment/paypal_express_bml/categorypage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCCPC</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="pphs_bml_categorypage_position">0</field> - </depends> - </field> - <field id="pphs_bml_categorypage_size2" extends="payment_all_paypal/payments_pro_hosted_solution/pphs_required_settings/pphs_advertise_bml/pphs_settings_bml_categorypage/pphs_bml_categorypage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCCPS</source_model> - <depends> - <field id="pphs_bml_categorypage_position">1</field> - </depends> - </field> - </group> - <group id="pphs_settings_bml_productpage" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="40"> - <label>Catalog Product Page</label> - <field id="bml_productpage_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_productpage/bml_productpage_display" /> - <field id="pphs_bml_productpage_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_productpage/bml_productpage_position" /> - <field id="pphs_bml_productpage_size1" translate="label" type="select" showInWebsite="1" showInDefault="1" sortOrder="30"> - <label>Size</label> - <config_path>payment/paypal_express_bml/productpage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCPPC</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="pphs_bml_productpage_position">0</field> - </depends> - </field> - <field id="pphs_bml_productpage_size2" extends="payment_all_paypal/payments_pro_hosted_solution/pphs_required_settings/pphs_advertise_bml/pphs_settings_bml_productpage/pphs_bml_productpage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCPPN</source_model> - <depends> - <field id="pphs_bml_productpage_position">1</field> - </depends> - </field> - </group> - <group id="pphs_settings_bml_checkout" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="50"> - <label>Checkout Cart Page</label> - <field id="bml_checkout_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_checkout/bml_checkout_display" /> - <field id="pphs_bml_checkout_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_checkout/bml_checkout_position" /> - <field id="pphs_bml_checkout_size1" translate="label" type="select" showInWebsite="1" showInDefault="1" sortOrder="30"> - <label>Size</label> - <config_path>payment/paypal_express_bml/checkout_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCheckoutC</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="pphs_bml_checkout_position">0</field> - </depends> - </field> - <field id="pphs_bml_checkout_size2" extends="payment_all_paypal/payments_pro_hosted_solution/pphs_required_settings/pphs_advertise_bml/pphs_settings_bml_checkout/pphs_bml_checkout_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCheckoutN</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="pphs_bml_checkout_position">1</field> - </depends> - </field> - </group> - </group> - </group> - <group id="pphs_settings" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20"> - <label>Basic Settings - PayPal Payments Pro Hosted Solution</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="title" type="text" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="10" translate="label comment"> - <label>Title</label> - <comment>It is recommended to set this value to "PayPal" per store views.</comment> - <config_path>payment/hosted_pro/title</config_path> - </field> - <field id="sort_order" type="text" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20" translate="label"> - <label>Sort Order</label> - <config_path>payment/hosted_pro/sort_order</config_path> - <frontend_class>validate-number</frontend_class> - </field> - <field id="payment_action" type="select" showInDefault="1" showInWebsite="1" sortOrder="30" translate="label"> - <label>Payment Action</label> - <config_path>payment/hosted_pro/payment_action</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\PaymentActions</source_model> - </field> - <field id="display_ec" type="select" showInDefault="1" showInWebsite="1" sortOrder="40" translate="label"> - <label>Display Express Checkout in the Payment Information step</label> - <config_path>payment/hosted_pro/display_ec</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - </field> - <group id="pphs_settings_advanced" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="50"> - <label>Advanced Settings</label> - <fieldset_css>config-advanced</fieldset_css> - <field id="allowspecific" type="select" showInDefault="1" showInWebsite="1" sortOrder="10" translate="label"> - <label>Payment Applicable From</label> - <config_path>payment/hosted_pro/allowspecific</config_path> - <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> - </field> - <field id="specificcountry" type="multiselect" showInDefault="1" showInWebsite="1" sortOrder="20" translate="label"> - <label>Countries Payment Applicable From</label> - <config_path>payment/hosted_pro/specificcountry</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BuyerCountry</source_model> - <depends> - <field id="allowspecific">1</field> - </depends> - </field> - <field id="debug" type="select" showInDefault="1" showInWebsite="1" sortOrder="30" translate="label"> - <label>Debug Mode</label> - <config_path>payment/hosted_pro/debug</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - </field> - <field id="verify_peer" type="select" showInDefault="1" showInWebsite="1" sortOrder="35" translate="label"> - <label>Enable SSL verification</label> - <config_path>payment/hosted_pro/verify_peer</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - </field> - <group id="pphs_settlement_report" showInDefault="1" showInWebsite="1" sortOrder="50" translate="label"> - <label>Settlement Report Settings</label> - <field id="heading_sftp" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/heading_sftp"/> - <field id="settlement_reports_ftp_login" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_login" /> - <field id="settlement_reports_ftp_password" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_password" /> - <field id="settlement_reports_ftp_sandbox" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_sandbox" /> - <field id="settlement_reports_ftp_ip" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_ip" /> - <field id="settlement_reports_ftp_path" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_path" /> - <field id="heading_schedule" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/heading_schedule" /> - <field id="settlement_reports_active" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_active" /> - <field id="settlement_reports_schedule" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_schedule" /> - <field id="settlement_reports_time" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_time" /> - </group> - </group> - </group> - </group> - <group id="payments_pro_hosted_solution_without_bml" extends="payments_pro_hosted_solution"> - <group id="pphs_required_settings"> - <field id="enable_express_checkout_bml" showInDefault="0" showInWebsite="0"/> - <field id="express_checkout_bml_sort_order" showInDefault="0" showInWebsite="0"/> - <group id="pphs_advertise_bml" showInDefault="0" showInWebsite="0"/> - </group> - </group> - </section> - <section id="payment_us" extends="payment" showInDefault="0" showInWebsite="0" showInStore="0"> - <group id="paypal_group_all_in_one" translate="label comment" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="1"> - <label><![CDATA[PayPal All-in-One Payment Solutions <i>Accept and process credit cards and PayPal payments.</i>]]></label> - <fieldset_css>complex paypal-other-section paypal-all-in-one-section</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <comment>Choose a secure bundled payment solution for your business.</comment> - <attribute type="displayIn">other_paypal_payment_solutions</attribute> - <group id="payflow_advanced" translate="label comment" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="30"> - <label>Payments Advanced</label> - <fieldset_css>paypal-other-section</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment</frontend_model> - <comment><![CDATA[Accept payments with a PCI-compliant checkout that keeps customers on your site. (<u>Includes Express Checkout</u>)]]></comment> - <attribute type="activity_path">payment/payflow_advanced/active</attribute> - <group id="configuration_details" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="4"> - <comment>http://docs.magento.com/m2/ce/user_guide/payment/paypal-payments-advanced.html</comment> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Hint</frontend_model> - </group> - <group id="required_settings" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Required PayPal Settings</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <group id="payments_advanced" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Payments Advanced and Express Checkout</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="business_account" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/business_account"> - <label>Email Associated with PayPal Merchant Account (Optional)</label> - </field> - <field id="partner" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Partner</label> - <config_path>payment/payflow_advanced/partner</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="vendor" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Vendor</label> - <config_path>payment/payflow_advanced/vendor</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="user" translate="label comment tooltip" type="obscure" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>User</label> - <comment>PayPal recommends that you set up an additional User on your account at manager.paypal.com</comment> - <tooltip>PayPal recommends you set up an additional User on your account at manager.paypal.com, instead of entering your admin username and password here. This will enhance your security and prevent service interruptions if you later change your password. If you do not want to set up an additional User, you can re-enter your Merchant Login here.</tooltip> - <config_path>payment/payflow_advanced/user</config_path> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - <attribute type="shared">1</attribute> - </field> - <field id="pwd" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1"> - <label>Password</label> - <config_path>payment/payflow_advanced/pwd</config_path> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - <attribute type="shared">1</attribute> - </field> - <field id="sandbox_flag" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1"> - <label>Test Mode</label> - <config_path>payment/payflow_advanced/sandbox_flag</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="use_proxy" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1"> - <label>Use Proxy</label> - <config_path>payment/payflow_advanced/use_proxy</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="proxy_host" translate="label" type="text" sortOrder="80" showInDefault="1" showInWebsite="1"> - <label>Proxy Host</label> - <config_path>payment/payflow_advanced/proxy_host</config_path> - <depends> - <field id="use_proxy">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="proxy_port" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1"> - <label>Proxy Port</label> - <config_path>payment/payflow_advanced/proxy_port</config_path> - <depends> - <field id="use_proxy">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="payflow_advanced_info" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="100"> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Payflowlink\Advanced</frontend_model> - <attribute type="shared">1</attribute> - </field> - </group> - <field id="enable_payflow_advanced" translate="label comment" type="select" sortOrder="41" showInDefault="1" showInWebsite="1"> - <label>Enable this Solution</label> - <config_path>payment/payflow_advanced/active</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Payment</frontend_model> - <requires> - <group id="payments_advanced"/> - </requires> - </field> - <field id="enable_express_checkout" extends="payment_all_paypal/payflow_link/payflow_link_required/enable_express_checkout_basic" showInDefault="1" showInWebsite="1"> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Hidden</frontend_model> - <requires> - <field id="enable_payflow_advanced"/> - </requires> - </field> - <field id="enable_express_checkout_bml" sortOrder="42" extends="payment_all_paypal/express_checkout/express_checkout_required/enable_express_checkout_bml"> - <comment><![CDATA[PayPal Express Checkout Payflow Edition lets you give customers access to financing through PayPal Credit® - at no additional cost to you. - You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. - <a href="https:/www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> - </comment> - <config_path>payment/payflow_express_bml/active</config_path> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Bml</frontend_model> - <requires> - <field id="enable_payflow_advanced"/> - </requires> - </field> - <field id="express_checkout_bml_sort_order" sortOrder="50" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_bml_sort_order"> - <config_path>payment/payflow_express_bml/sort_order</config_path> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Depends\BmlSortOrder</frontend_model> - <depends> - <field id="enable_express_checkout_bml">1</field> - </depends> - </field> - <group id="advanced_advertise_bml" showInDefault="1" showInWebsite="1" sortOrder="60" translate="label comment"> - <label>Advertise PayPal Credit</label> - <comment> - <![CDATA[<a href="https://financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> - <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing - from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. - Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. - The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href="https://financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> - </comment> - <field id="bml_publisher_id" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_publisher_id" /> - <field id="bml_wizard" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_wizard" /> - <group id="advanced_settings_bml_homepage" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20" translate="label"> - <label>Home Page</label> - <field id="bml_homepage_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_homepage/bml_homepage_display"/> - <field id="advanced_bml_homepage_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_homepage/bml_homepage_position"/> - <field id="advanced_bml_homepage_size1" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Size</label> - <config_path>payment/paypal_express_bml/homepage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeHPH</source_model> - <depends> - <field id="advanced_bml_homepage_position">0</field> - </depends> - </field> - <field id="advanced_bml_homepage_size2" extends="payment_us/paypal_group_all_in_one/payflow_advanced/required_settings/advanced_advertise_bml/advanced_settings_bml_homepage/advanced_bml_homepage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeHPS</source_model> - <depends> - <field id="advanced_bml_homepage_position">1</field> - </depends> - </field> - </group> - <group id="advanced_settings_bml_categorypage" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="30" translate="label"> - <label>Catalog Category Page</label> - <field id="bml_categorypage_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_categorypage/bml_categorypage_display"/> - <field id="advanced_bml_categorypage_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_categorypage/bml_categorypage_position"/> - <field id="advanced_bml_categorypage_size1" type="select" showInDefault="1" showInWebsite="1" sortOrder="30" translate="label"> - <label>Size</label> - <config_path>payment/paypal_express_bml/categorypage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCCPC</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="advanced_bml_categorypage_position">0</field> - </depends> - </field> - <field id="advanced_bml_categorypage_size2" extends="payment_us/paypal_group_all_in_one/payflow_advanced/required_settings/advanced_advertise_bml/advanced_settings_bml_categorypage/advanced_bml_categorypage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCCPS</source_model> - <depends> - <field id="advanced_bml_categorypage_position">1</field> - </depends> - </field> - </group> - <group id="advanced_settings_bml_productpage" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="40" translate="label"> - <label>Catalog Product Page</label> - <field id="bml_productpage_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_productpage/bml_productpage_display" /> - <field id="advanced_bml_productpage_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_productpage/bml_productpage_position" /> - <field id="advanced_bml_productpage_size1" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" translate="label"> - <label>Size</label> - <config_path>payment/paypal_express_bml/productpage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCPPC</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="advanced_bml_productpage_position">0</field> - </depends> - </field> - <field id="advanced_bml_productpage_size2" extends="payment_us/paypal_group_all_in_one/payflow_advanced/required_settings/advanced_advertise_bml/advanced_settings_bml_productpage/advanced_bml_productpage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCPPN</source_model> - <depends> - <field id="advanced_bml_productpage_position">1</field> - </depends> - </field> - - </group> - <group id="advanced_settings_bml_checkout" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="50" translate="label"> - <label>Checkout Cart Page</label> - <field id="bml_checkout_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_checkout/bml_checkout_display"/> - <field id="advanced_bml_checkout_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_checkout/bml_checkout_position"/> - <field id="advanced_bml_checkout_size1" type="select" showInDefault="1" showInWebsite="1" sortOrder="30" translate="label"> - <label>Size</label> - <config_path>payment/paypal_express_bml/checkout_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCheckoutC</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="advanced_bml_checkout_position">0</field> - </depends> - </field> - <field id="advanced_bml_checkout_size2" extends="payment_us/paypal_group_all_in_one/payflow_advanced/required_settings/advanced_advertise_bml/advanced_settings_bml_checkout/advanced_bml_checkout_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCheckoutN</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="advanced_bml_checkout_position">1</field> - </depends> - </field> - </group> - </group> - </group> - <group id="settings_payments_advanced" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20"> - <label>Basic Settings - PayPal Payments Advanced</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="title" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Title</label> - <comment>It is recommended to set this value to "Debit or Credit Card" per store views.</comment> - <config_path>payment/payflow_advanced/title</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="sort_order" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Sort Order</label> - <config_path>payment/payflow_advanced/sort_order</config_path> - <frontend_class>validate-number</frontend_class> - <attribute type="shared">1</attribute> - </field> - <field id="payment_action" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Payment Action</label> - <config_path>payment/payflow_advanced/payment_action</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\PaymentActions</source_model> - <attribute type="shared">1</attribute> - </field> - <group id="settings_payments_advanced_advanced" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="40"> - <label>Advanced Settings</label> - <fieldset_css>config-advanced</fieldset_css> - <field id="allowspecific" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Payment Applicable From</label> - <config_path>payment/payflow_advanced/allowspecific</config_path> - <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="25" showInDefault="1" showInWebsite="1"> - <label>Countries Payment Applicable From</label> - <config_path>payment/payflow_advanced/specificcountry</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BuyerCountry</source_model> - <depends> - <field id="allowspecific">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="debug" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Debug Mode</label> - <config_path>payment/payflow_advanced/debug</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="verify_peer" translate="label" type="select" sortOrder="35" showInDefault="1" showInWebsite="1"> - <label>Enable SSL verification</label> - <config_path>payment/payflow_advanced/verify_peer</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="csc_editable" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>CVV Entry is Editable</label> - <config_path>payment/payflow_advanced/csc_editable</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="csc_required" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1"> - <label>Require CVV Entry</label> - <config_path>payment/payflow_advanced/csc_required</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <depends> - <field id="csc_editable">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="email_confirmation" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1"> - <label>Send Email Confirmation</label> - <config_path>payment/payflow_advanced/email_confirmation</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="url_method" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1"> - <label>URL method for Cancel URL and Return URL</label> - <config_path>payment/payflow_advanced/url_method</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\UrlMethod</source_model> - <attribute type="shared">1</attribute> - </field> - <group id="settlement_report" translate="label" showInDefault="1" showInWebsite="1" sortOrder="80"> - <label>Settlement Report Settings</label> - <field id="heading_sftp" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/heading_sftp"/> - <field id="settlement_reports_ftp_login" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_login"/> - <field id="settlement_reports_ftp_password" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_password"/> - <field id="settlement_reports_ftp_sandbox" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_sandbox"/> - <field id="settlement_reports_ftp_ip" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_ip"/> - <field id="settlement_reports_ftp_path" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_path"/> - <field id="heading_schedule" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/heading_schedule"/> - <field id="settlement_reports_active" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_active"/> - <field id="settlement_reports_schedule" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_schedule"/> - <field id="settlement_reports_time" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_time"/> - </group> - <group id="frontend" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="90"> - <label>Frontend Experience Settings</label> - <field id="logo" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/logo"/> - <field id="paypal_pages" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_pages"/> - <field id="page_style" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/page_style"/> - <field id="paypal_hdrimg" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_hdrimg"/> - <field id="paypal_hdrbackcolor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_hdrbackcolor"/> - <field id="paypal_hdrbordercolor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_hdrbordercolor"/> - <field id="paypal_payflowcolor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_payflowcolor"/> - </group> - </group> - </group> - <group id="settings_express_checkout" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="30"> - <label>Basic Settings - PayPal Express Checkout</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="title" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_express_checkout/title" /> - <field id="sort_order" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_express_checkout/sort_order" /> - <field id="payment_action" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_express_checkout/payment_action" /> - <field id="visible_on_product" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_express_checkout/visible_on_product" /> - <group id="settings_express_checkout_advanced" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_express_checkout/paypal_payflow_express_checkout_advanced"/> - </group> - </group> - <group id="wpp_usuk" translate="label" sortOrder="40" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout"> - <label>Payments Pro</label> - <attribute type="activity_path">payment/paypal_payment_pro/active</attribute> - <group id="configuration_details"> - <comment>http://docs.magento.com/m2/ce/user_guide/payment/paypal-payments-pro.html</comment> - </group> - <group id="paypal_payflow_required" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <field id="enable_paypal_payflow"> - <attribute type="shared">0</attribute> - <config_path>payment/paypal_payment_pro/active</config_path> - </field> - <group id="paypal_payflow_api_settings" translate="label"> - <label>Payments Pro and Express Checkout</label> - </group> - </group> - <group id="settings_paypal_payflow" translate="label"> - <label>Basic Settings - PayPal Payments Pro</label> - </group> - </group> - <group id="wps_express" extends="payment_all_paypal/express_checkout"> - <label>Payments Standard</label> - <comment>Accept credit card and PayPal payments securely.</comment> - <attribute type="activity_path">payment/wps_express/active</attribute> - <group id="configuration_details"> - <comment>http://docs.magento.com/m2/ce/user_guide/payment/paypal-payments-standard.html</comment> - </group> - <group id="express_checkout_required"> - <field id="enable_in_context_checkout" showInDefault="0" showInWebsite="0"/> - <field id="merchant_id" showInDefault="0" showInWebsite="0"/> - <group id="express_checkout_required_express_checkout"> - <label>Payments Standard</label> - </group> - <field id="enable_express_checkout"> - <config_path>payment/wps_express/active</config_path> - </field> - <field id="enable_express_checkout_bml"> - <config_path>payment/wps_express_bml/active</config_path> - </field> - </group> - <group id="settings_ec"> - <label>Basic Settings - PayPal Website Payments Standard</label> - </group> - </group> - </group> - <group id="paypal_payment_gateways" translate="label" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="1"> - <label><![CDATA[PayPal Payment Gateways <i>Process payments using your own internet merchant account.</i>]]></label> - <fieldset_css>complex paypal-other-section paypal-gateways-section</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <attribute type="displayIn">other_paypal_payment_solutions</attribute> - <group id="paypal_payflowpro_with_express_checkout" translate="label comment" extends="payment_all_paypal/paypal_payflowpro"> - <label>Payflow Pro</label> - <attribute type="paypal_ec_separate">0</attribute> - <group id="paypal_payflow_required" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Required PayPal Settings</label> - <field id="enable_paypal_payflow"> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Payment</frontend_model> - </field> - <group id="paypal_payflow_api_settings" translate="label"> - <label>Payflow Pro and Express Checkout</label> - <field id="business_account" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/business_account" translate="label" sortOrder="10"> - <frontend_class>not-required</frontend_class> - <label>Email Associated with PayPal Merchant Account (Optional)</label> - <attribute type="shared">1</attribute> - </field> - </group> - <field id="enable_express_checkout" extends="payment_all_paypal/payflow_link/payflow_link_required/enable_express_checkout_basic" showInDefault="1" showInWebsite="1"> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Hidden</frontend_model> - <requires> - <field id="enable_paypal_payflow"/> - </requires> - </field> - <field id="enable_in_context_checkout" showInDefault="0" showInWebsite="0"/> - <field id="merchant_id" showInDefault="0" showInWebsite="0"/> - <field id="enable_express_checkout_bml_payflow" translate="label" type="select" sortOrder="21" showInWebsite="1" showInDefault="1"> - <label>Enable PayPal Credit</label> - <comment><![CDATA[PayPal Express Checkout Payflow Edition lets you give customers access to financing through PayPal Credit® - at no additional cost to you. - You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. - <a href="https:/www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> - </comment> - <config_path>payment/payflow_express_bml/active</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Bml</frontend_model> - <requires> - <field id="enable_paypal_payflow"/> - </requires> - </field> - <field id="express_checkout_bml_sort_order" sortOrder="30" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_bml_sort_order"> - <config_path>payment/payflow_express_bml/sort_order</config_path> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Depends\BmlSortOrder</frontend_model> - <depends> - <field id="enable_express_checkout_bml_payflow">1</field> - </depends> - </field> - <group id="paypal_payflow_advertise_bml" translate="label comment" showInDefault="1" showInWebsite="1" sortOrder="40"> - <label>Advertise PayPal Credit</label> - <comment> - <![CDATA[<a href="https:/financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> - <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing - from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. - Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. - The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href="https:/financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> - </comment> - <field id="bml_publisher_id" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_publisher_id" /> - <field id="bml_wizard" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_wizard" /> - <group id="paypal_payflow_settings_bml_homepage" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20"> - <label>Home Page</label> - <field id="bml_homepage_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_homepage/bml_homepage_display"/> - <field id="paypal_payflow_bml_homepage_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_homepage/bml_homepage_position"/> - <field id="paypal_payflow_bml_homepage_size1" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="30"> - <label>Size</label> - <config_path>payment/paypal_express_bml/homepage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeHPH</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="paypal_payflow_bml_homepage_position">0</field> - </depends> - </field> - <field id="paypal_payflow_bml_homepage_size2" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_required/paypal_payflow_advertise_bml/paypal_payflow_settings_bml_homepage/paypal_payflow_bml_homepage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeHPS</source_model> - <depends> - <field id="paypal_payflow_bml_homepage_position">1</field> - </depends> - </field> - </group> - <group id="paypal_payflow_settings_bml_categorypage" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="30"> - <label>Catalog Category Page</label> - <field id="bml_categorypage_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_categorypage/bml_categorypage_display"/> - <field id="paypal_payflow_bml_categorypage_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_categorypage/bml_categorypage_position" /> - <field id="paypal_payflow_bml_categorypage_size1" translate="label" sortOrder="30" showInWebsite="1" showInDefault="1" type="select"> - <label>Size</label> - <config_path>payment/paypal_express_bml/categorypage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCCPC</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="paypal_payflow_bml_categorypage_position">0</field> - </depends> - </field> - <field id="paypal_payflow_bml_categorypage_size2" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_required/paypal_payflow_advertise_bml/paypal_payflow_settings_bml_categorypage/paypal_payflow_bml_categorypage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCCPS</source_model> - <depends> - <field id="paypal_payflow_bml_categorypage_position">1</field> - </depends> - </field> - </group> - <group id="paypal_payflow_settings_bml_productpage" translate="label" showInDefault="1" showInStore="1" showInWebsite="1" sortOrder="40"> - <label>Catalog Product Page</label> - <field id="bml_productpage_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_productpage/bml_productpage_display" /> - <field id="paypal_payflow_bml_productpage_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_productpage/bml_productpage_position" /> - <field id="paypal_payflow_bml_productpage_size1" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="30"> - <label>Size</label> - <config_path>payment/paypal_express_bml/productpage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCPPC</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="paypal_payflow_bml_productpage_position">0</field> - </depends> - </field> - <field id="paypal_payflow_bml_productpage_size2" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_required/paypal_payflow_advertise_bml/paypal_payflow_settings_bml_productpage/paypal_payflow_bml_productpage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCPPN</source_model> - <depends> - <field id="paypal_payflow_bml_productpage_position">1</field> - </depends> - </field> - </group> - <group id="paypal_payflow_settings_bml_checkout" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="50"> - <label>Checkout Cart Page</label> - <field id="bml_checkout_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_checkout/bml_checkout_display" /> - <field id="paypal_payflow_bml_checkout_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_checkout/bml_checkout_position" /> - <field id="paypal_payflow_bml_checkout_size1" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="30"> - <label>Size</label> - <config_path>payment/paypal_express_bml/checkout_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCheckoutC</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="paypal_payflow_bml_checkout_position">0</field> - </depends> - </field> - <field id="paypal_payflow_bml_checkout_size2" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_required/paypal_payflow_advertise_bml/paypal_payflow_settings_bml_checkout/paypal_payflow_bml_checkout_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCheckoutN</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="paypal_payflow_bml_checkout_position">1</field> - </depends> - </field> - </group> - </group> - </group> - <group id="settings_paypal_payflow" translate="label"> - <group id="settings_paypal_payflow_advanced" translate="label"> - <group id="paypal_payflow_frontend" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="100"> - <label>Frontend Experience Settings</label> - <field id="logo" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/logo"/> - <field id="paypal_pages" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_pages"/> - <field id="page_style" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/page_style"/> - <field id="paypal_hdrimg" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_hdrimg"/> - <field id="paypal_hdrbackcolor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_hdrbackcolor"/> - <field id="paypal_hdrbordercolor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_hdrbordercolor"/> - <field id="paypal_payflowcolor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_payflowcolor"/> - </group> - </group> - </group> - <group id="paypal_payflow_express_checkout" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="30"> - <label>Basic Settings - PayPal Express Checkout</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="title" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Title</label> - <config_path>payment/payflow_express/title</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="sort_order" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Sort Order</label> - <config_path>payment/payflow_express/sort_order</config_path> - <frontend_class>validate-number</frontend_class> - <attribute type="shared">1</attribute> - </field> - <field id="payment_action" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Payment Action</label> - <config_path>payment/payflow_express/payment_action</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\PaymentActions</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="visible_on_product" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Display on Product Details Page</label> - <config_path>payment/payflow_express/visible_on_product</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <group id="paypal_payflow_express_checkout_advanced" translate="label" showInDefault="1" showInWebsite="1" sortOrder="60"> - <label>Advanced Settings</label> - <fieldset_css>config-advanced</fieldset_css> - <field id="visible_on_cart" translate="label comment" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Display on Shopping Cart</label> - <comment>Also affects mini-shopping cart.</comment> - <config_path>payment/payflow_express/visible_on_cart</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\Yesnoshortcut</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="allowspecific" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1"> - <label>Payment Applicable From</label> - <config_path>payment/payflow_express/allowspecific</config_path> - <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Countries Payment Applicable From</label> - <config_path>payment/payflow_express/specificcountry</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BuyerCountry</source_model> - <depends> - <field id="allowspecific">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="debug" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Debug Mode</label> - <config_path>payment/payflow_express/debug</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="verify_peer" translate="label" type="select" sortOrder="35" showInDefault="1" showInWebsite="1"> - <label>Enable SSL verification</label> - <config_path>payment/payflow_express/verify_peer</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="line_items_enabled" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>Transfer Cart Line Items</label> - <config_path>payment/payflow_express/line_items_enabled</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="skip_order_review_step" sortOrder="50" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/skip_order_review_step"/> - </group> - </group> - </group> - <group id="payflow_link_us" extends="payment_all_paypal/payflow_link"/> - </group> - <group id="paypal_alternative_payment_methods" sortOrder="5" showInDefault="0" showInWebsite="0" showInStore="0"> - <group id="express_checkout_us" translate="label comment" extends="payment_all_paypal/express_checkout" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>PayPal Express Checkout</label> - <fieldset_css>complex paypal-express-section</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment</frontend_model> - <comment>Add another payment method to your existing solution or as a stand-alone option.</comment> - <help_url>https://merchant.paypal.com/cgi-bin/marketingweb?cmd=_render-content</help_url> - <attribute type="shared">0</attribute> - <attribute type="activity_path">payment/paypal_express/active</attribute> - <attribute type="activity_path">payment/payflow_express/active</attribute> - <attribute type="displayIn">recommended_solutions</attribute> - </group> - </group> - </section> - <section id="payment_gb" extends="payment" showInDefault="0" showInWebsite="0" showInStore="0"> - <group id="paypal_group_all_in_one" translate="label comment" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="1"> - <label><![CDATA[PayPal All-in-One Payment Solutions  <i>Accept and process credit cards and PayPal payments.</i>]]></label> - <fieldset_css>complex paypal-other-section paypal-all-in-one-section</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <comment>Choose a secure bundled payment solution for your business.</comment> - <help_url>https://www.paypal-marketing.com/emarketing/partner/na/merchantlineup/home.page#mainTab=checkoutlineup&subTab=newlineup</help_url> - <attribute type="displayIn">other_paypal_payment_solutions</attribute> - <group id="wpp_usuk" translate="comment" sortOrder="20"> - <fieldset_css>pp-general-uk</fieldset_css> - <demo_link>http://www.youtube.com/watch?v=LBe-TW87eGI&list=PLF18B1094ABCD7CE8&index=1&feature=plpp_video</demo_link> - <comment>Accept payments with a completely customizable checkout page.</comment> - <group id="wpp_required_settings"> - <field id="enable_express_checkout_bml" showInDefault="0" showInWebsite="0"/> - <field id="express_checkout_bml_sort_order" showInDefault="0" showInWebsite="0"/> - <group id="wpp_advertise_bml" showInDefault="0" showInWebsite="0"/> - </group> - </group> - <group id="payments_pro_hosted_solution_with_express_checkout" translate="label comment" extends="payment_all_paypal/payments_pro_hosted_solution_without_bml" sortOrder="30"> - <label>Website Payments Pro Hosted Solution</label> - <attribute type="paypal_ec_separate">0</attribute> - <group id="pphs_required_settings"> - <group id="pphs_required_settings_pphs" translate="label"> - <label>Website Payments Pro Hosted Solution and Express Checkout</label> - </group> - <field id="pphs_enable"> - <requires> - <group id="pphs_required_settings_pphs"/> - </requires> - <frontend_class>paypal-enabler</frontend_class> - </field> - </group> - <group id="pphs_settings" translate="label"> - <label>Basic Settings - PayPal Website Payments Pro Hosted Solution</label> - <group id="pphs_settings_advanced"> - <group id="pphs_billing_agreement" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="40"> - <label>PayPal Billing Agreement Settings</label> - <field id="active" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_billing_agreement/active" /> - <field id="title" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_billing_agreement/title" /> - <field id="sort_order" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_billing_agreement/sort_order" /> - <field id="payment_action" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_billing_agreement/payment_action" /> - <field id="allowspecific" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_billing_agreement/allowspecific" /> - <field id="specificcountry" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_billing_agreement/specificcountry" /> - <field id="debug" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_billing_agreement/debug" /> - <field id="verify_peer" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_billing_agreement/verify_peer" /> - <field id="line_items_enabled" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_billing_agreement/line_items_enabled" /> - <field id="allow_billing_agreement_wizard" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_billing_agreement/allow_billing_agreement_wizard" /> - </group> - <group id="pphs_frontend" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="60"> - <label>Frontend Experience Settings</label> - <field id="logo" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/logo" /> - <field id="paypal_pages" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_pages" /> - <field id="page_style" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/page_style" /> - <field id="paypal_hdrimg" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_hdrimg" /> - <field id="paypal_hdrbackcolor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_hdrbackcolor" /> - <field id="paypal_hdrbordercolor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_hdrbordercolor" /> - <field id="paypal_payflowcolor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_payflowcolor" /> - </group> - </group> - </group> - <group id="pphs_settings_express_checkout" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="30"> - <label>Basic Settings - PayPal Express Checkout</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="title" extends="payment_all_paypal/express_checkout/settings_ec/title" /> - <field id="sort_order" extends="payment_all_paypal/express_checkout/settings_ec/sort_order" /> - <field id="payment_action" extends="payment_all_paypal/express_checkout/settings_ec/payment_action" /> - <field id="visible_on_product" extends="payment_all_paypal/express_checkout/settings_ec/visible_on_product" /> - <field id="authorization_honor_period" extends="payment_all_paypal/express_checkout/settings_ec/authorization_honor_period" /> - <field id="order_valid_period" extends="payment_all_paypal/express_checkout/settings_ec/order_valid_period" /> - <field id="child_authorization_number" extends="payment_all_paypal/express_checkout/settings_ec/child_authorization_number" /> - <group id="pphs_settings_express_checkout_advanced" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="80"> - <label>Advanced Settings</label> - <fieldset_css>config-advanced</fieldset_css> - <field id="visible_on_cart" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/visible_on_cart" /> - <field id="allowspecific" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/allowspecific" /> - <field id="specificcountry" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/specificcountry" /> - <field id="debug" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/debug" /> - <field id="verify_peer" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/verify_peer" /> - <field id="line_items_enabled" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/line_items_enabled" /> - <field id="transfer_shipping_options" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/transfer_shipping_options" /> - <field id="button_flavor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/button_flavor" /> - <field id="solution_type" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/solution_type" /> - <field id="require_billing_address" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/require_billing_address" /> - <field id="allow_ba_signup" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/allow_ba_signup" /> - <field id="skip_order_review_step" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/skip_order_review_step" /> - </group> - </group> - </group> - <group id="wps_express" extends="payment_all_paypal/express_checkout" sortOrder="50"> - <label>Website Payments Standard</label> - <comment>Accept credit card and PayPal payments securely.</comment> - <attribute type="activity_path">payment/wps_express/active</attribute> - <group id="configuration_details"> - <comment>http://docs.magento.com/m2/ce/user_guide/payment/paypal-payments-standard.html</comment> - </group> - <group id="express_checkout_required"> - <group id="express_checkout_required_express_checkout"> - <label>Website Payments Standard</label> - </group> - <field id="enable_express_checkout"> - <config_path>payment/wps_express/active</config_path> - </field> - <field id="enable_in_context_checkout" showInDefault="0" showInWebsite="0"/> - <field id="merchant_id" showInDefault="0" showInWebsite="0"/> - <field id="express_checkout_bml_sort_order" showInDefault="0" showInWebsite="0"/> - <field id="enable_express_checkout_bml" showInDefault="0" showInWebsite="0"/> - <group id="advertise_bml" showInDefault="0" showInWebsite="0"/> - </group> - <group id="settings_ec"> - <label>Basic Settings - PayPal Website Payments Standard</label> - </group> - </group> - </group> - <group id="paypal_alternative_payment_methods" sortOrder="5" showInDefault="0" showInWebsite="0" showInStore="0"> - <group id="express_checkout_gb" translate="label comment" extends="payment_all_paypal/express_checkout" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>PayPal Express Checkout</label> - <fieldset_css>complex paypal-express-section</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment</frontend_model> - <comment>Add another payment method to your existing solution or as a stand-alone option.</comment> - <help_url>https://merchant.paypal.com/cgi-bin/marketingweb?cmd=_render-content</help_url> - <attribute type="displayIn">recommended_solutions</attribute> - <group id="express_checkout_required"> - <field id="express_checkout_bml_sort_order" showInDefault="0" showInWebsite="0"/> - <field id="enable_express_checkout_bml" showInDefault="0" showInWebsite="0"/> - <group id="advertise_bml" showInDefault="0" showInWebsite="0"/> - </group> - </group> - </group> - </section> - <section id="payment_de" extends="payment" showInDefault="0" showInWebsite="0" showInStore="0"> - <group id="paypal_payment_solutions" showInDefault="0" showInWebsite="0" showInStore="0" sortOrder="5"> - <group id="express_checkout_de" translate="label comment" extends="payment_all_paypal/express_checkout" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>PayPal Express Checkout</label> - <fieldset_css>complex paypal-express-section</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment</frontend_model> - <comment>Add another payment method to your existing solution or as a stand-alone option.</comment> - <help_url>https://www.paypal-marketing.com/emarketing/partner/na/merchantlineup/home.page#mainTab=checkoutlineup&subTab=newlineup</help_url> - <attribute type="displayIn">recommended_solutions</attribute> - <group id="express_checkout_required"> - <field id="enable_express_checkout_bml" showInDefault="0" showInWebsite="0"/> - <field id="express_checkout_bml_sort_order" showInDefault="0" showInWebsite="0"/> - <group id="advertise_bml" showInDefault="0" showInWebsite="0"/> - </group> - <group id="settings_ec"> - <group id="settings_ec_advanced"> - <field id="solution_type" showInDefault="0" showInWebsite="0"/> - </group> - </group> - </group> - </group> - </section> - <section id="payment_other" extends="payment" showInDefault="0" showInWebsite="0" showInStore="0"> - <group id="express_checkout_other" translate="label comment" sortOrder="5" extends="payment_all_paypal/express_checkout" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>PayPal Express Checkout</label> - <fieldset_css>complex paypal-express-section</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment</frontend_model> - <comment>Add another payment method to your existing solution or as a stand-alone option.</comment> - <help_url>https://www.paypal-marketing.com/emarketing/partner/na/merchantlineup/home.page#mainTab=checkoutlineup&subTab=newlineup</help_url> - <attribute type="displayIn">recommended_solutions</attribute> - <group id="express_checkout_required"> - <field id="enable_express_checkout_bml" showInDefault="0" showInWebsite="0"/> - <field id="express_checkout_bml_sort_order" showInDefault="0" showInWebsite="0"/> - <group id="advertise_bml" showInDefault="0" showInWebsite="0"/> - </group> - </group> - <group id="paypal_group_all_in_one" translate="label comment" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="1"> - <label><![CDATA[PayPal All-in-One Payment Solutions  <i>Accept and process credit cards and PayPal payments.</i>]]></label> - <fieldset_css>complex paypal-other-section paypal-all-in-one-section</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <comment>Choose a secure bundled payment solution for your business.</comment> - <help_url>https://www.paypal-marketing.com/emarketing/partner/na/merchantlineup/home.page#mainTab=checkoutlineup&subTab=newlineup</help_url> - <attribute type="displayIn">other_paypal_payment_solutions</attribute> - <group id="wps_other" extends="payment_all_paypal/express_checkout" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Website Payments Standard</label> - <fieldset_css>complex</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment</frontend_model> - <comment>Accept credit card and PayPal payments securely.</comment> - <attribute type="activity_path">payment/wps_express/active</attribute> - <group id="configuration_details"> - <comment>http://docs.magento.com/m2/ce/user_guide/payment/paypal-payments-standard.html</comment> - </group> - <group id="express_checkout_required"> - <group id="express_checkout_required_express_checkout"> - <label>Website Payments Standard</label> - </group> - <field id="enable_in_context_checkout" showInDefault="0" showInWebsite="0"/> - <field id="merchant_id" showInDefault="0" showInWebsite="0"/> - <field id="enable_express_checkout"> - <config_path>payment/wps_express/active</config_path> - </field> - <field id="enable_express_checkout_bml" showInDefault="0" showInWebsite="0"/> - <field id="express_checkout_bml_sort_order" showInDefault="0" showInWebsite="0"/> - <group id="advertise_bml" showInDefault="0" showInWebsite="0"/> - </group> - <group id="settings_ec"> - <label>Basic Settings - PayPal Website Payments Standard</label> - </group> - </group> - </group> - <group id="paypal_payment_gateways" translate="label comment" sortOrder="8" showInDefault="0" showInWebsite="0" showInStore="0"> - <label><![CDATA[PayPal Payment Gateways <i>Process payments using your own internet merchant account.</i>]]></label> - <fieldset_css>complex paypal-other-section paypal-gateways-section</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <comment>Process payments using your own internet merchant account.</comment> - <help_url>https://merchant.paypal.com/cgi-bin/marketingweb?cmd=_render-content</help_url> - <attribute type="displayIn">other_paypal_payment_solutions</attribute> - </group> - </section> - <section id="payment_ca" extends="payment_other"> - <group id="express_checkout_other"> - <attribute type="activity_path">payment/paypal_express/active</attribute> - <attribute type="activity_path">payment/payflow_express/active</attribute> - </group> - <group id="paypal_group_all_in_one"> - <group id="wps_other" sortOrder="20"/> - </group> - <group id="paypal_payment_gateways" showInDefault="1" showInWebsite="1" showInStore="1"> - <fieldset_css>complex paypal-other-section paypal-gateways-section</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <label><![CDATA[PayPal Payment Gateways <i>Process payments using your own internet merchant account.</i>]]></label> - <group id="wpp_ca" translate="label" extends="payment_all_paypal/paypal_payflowpro" sortOrder="30"> - <label>Website Payments Pro</label> - <attribute type="activity_path">payment/paypal_payment_pro/active</attribute> - <group id="configuration_details"> - <comment>http://docs.magento.com/m2/ce/user_guide/payment/paypal-payments-pro.html</comment> - </group> - <group id="paypal_payflow_required" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <group id="paypal_payflow_api_settings"> - <label>Payments Pro</label> - </group> - <field id="enable_in_context_checkout" showInDefault="0" showInWebsite="0"/> - <field id="merchant_id" showInDefault="0" showInWebsite="0"/> - <field id="enable_paypal_payflow"> - <frontend_class>paypal-enabler paypal-ec-pe</frontend_class> - <attribute type="shared">0</attribute> - <config_path>payment/paypal_payment_pro/active</config_path> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Payment</frontend_model> - </field> - </group> - <group id="settings_paypal_payflow"> - <label>Basic Settings - PayPal Payments Pro</label> - </group> - </group> - <group id="paypal_payflowpro_ca" extends="payment_all_paypal/paypal_payflowpro" sortOrder="40"/> - <group id="payflow_link_ca" extends="payment_all_paypal/payflow_link" sortOrder="50"> - <group id="payflow_link_required"> - <field id="enable_express_checkout_bml" showInDefault="0" showInWebsite="0"/> - <field id="express_checkout_bml_sort_order" showInDefault="0" showInWebsite="0"/> - <group id="payflow_link_advertise_bml" showInDefault="0" showInWebsite="0"/> - </group> - </group> - </group> - </section> - <section id="payment_au" extends="payment_other"> - <group id="express_checkout_other"/> - <group id="paypal_group_all_in_one"> - <group id="wps_other" sortOrder="12"/> - <group id="payments_pro_hosted_solution_au" extends="payment_all_paypal/payments_pro_hosted_solution_without_bml" sortOrder="10"/> - </group> - <group id="paypal_payment_gateways" showInDefault="1" showInWebsite="1" showInStore="1"> - <group id="paypal_payflowpro_au" extends="payment_all_paypal/paypal_payflowpro" sortOrder="20"/> - </group> - </section> - <section id="payment_jp" extends="payment_other"> - <group id="express_checkout_other"/> - <group id="paypal_group_all_in_one"> - <group id="wps_other" sortOrder="12"/> - <group id="payments_pro_hosted_solution_jp" extends="payment_all_paypal/payments_pro_hosted_solution_without_bml" sortOrder="10"> - <label>Website Payments Plus</label> - </group> - </group> - </section> - <section id="payment_fr" extends="payment_other"> - <group id="express_checkout_other"/> - <group id="paypal_group_all_in_one"> - <group id="wps_other" sortOrder="12"/> - <group id="payments_pro_hosted_solution_fr" extends="payment_all_paypal/payments_pro_hosted_solution_without_bml" sortOrder="10"> - <label>Integral Evolution</label> - </group> - </group> - </section> - <section id="payment_it" extends="payment_other"> - <group id="express_checkout_other"/> - <group id="paypal_group_all_in_one"> - <group id="wps_other" sortOrder="12"/> - <group id="payments_pro_hosted_solution_it" extends="payment_all_paypal/payments_pro_hosted_solution_without_bml" sortOrder="10"> - <label>Pro</label> - </group> - </group> - </section> - <section id="payment_es" extends="payment_other"> - <group id="express_checkout_other"/> - <group id="paypal_group_all_in_one"> - <group id="wps_other" sortOrder="12"/> - <group id="payments_pro_hosted_solution_es" extends="payment_all_paypal/payments_pro_hosted_solution_without_bml" sortOrder="10"> - <label>Pasarela integral</label> - </group> - </group> - </section> - <section id="payment_hk" extends="payment_other"> - <group id="express_checkout_other"/> - <group id="paypal_group_all_in_one"> - <group id="wps_other" sortOrder="12"/> - <group id="payments_pro_hosted_solution_hk" extends="payment_all_paypal/payments_pro_hosted_solution_without_bml" sortOrder="10"/> - </group> - </section> - <section id="payment_nz" extends="payment_other"> - <group id="express_checkout_other"/> - <group id="paypal_group_all_in_one"> - <group id="wps_other"/> - </group> - <group id="paypal_payment_gateways" showInDefault="1" showInWebsite="1" showInStore="1"> - <group id="paypal_payflowpro_nz" extends="payment_all_paypal/paypal_payflowpro"/> - </group> - </section> - </system> -</config> diff --git a/dev/tests/integration/testsuite/Magento/Widget/Model/Config/ReaderTest.php b/dev/tests/integration/testsuite/Magento/Widget/Model/Config/ReaderTest.php deleted file mode 100644 index b317c9359c4bf..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Widget/Model/Config/ReaderTest.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -// @codingStandardsIgnoreFile - -namespace Magento\Widget\Model\Config; - -use Magento\TestFramework\Helper\Bootstrap; - -class ReaderTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\Widget\Model\Config\Reader - */ - private $model; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $fileResolver; - - public function setUp() - { - $this->fileResolver = $this->getMockForAbstractClass(\Magento\Framework\Config\FileResolverInterface::class); - $objectManager = Bootstrap::getObjectManager(); - $this->model = $objectManager->create( - \Magento\Widget\Model\Config\Reader::class, ['fileResolver' => $this->fileResolver] - ); - } - - public function testRead() - { - $this->fileResolver->expects($this->once()) - ->method('get') - ->willReturn([file_get_contents(__DIR__ . '/_files/orders_and_returns.xml')]); - $expected = include __DIR__ . '/_files/expectedGlobalArray.php'; - $this->assertEquals($expected, $this->model->read('global')); - } - - public function testReadFile() - { - $file = file_get_contents(__DIR__ . '/_files/orders_and_returns.xml'); - $expected = include __DIR__ . '/_files/expectedGlobalArray.php'; - $this->assertEquals($expected, $this->model->readFile($file)); - } - - public function testMergeCompleteAndPartial() - { - $fileList = [ - file_get_contents(__DIR__ . '/_files/catalog_new_products_list.xml'), - file_get_contents(__DIR__ . '/_files/orders_and_returns_customized.xml'), - ]; - $this->fileResolver->expects($this->once()) - ->method('get') - ->with('widget.xml', 'global') - ->willReturn($fileList); - $expected = include __DIR__ . '/_files/expectedMergedArray.php'; - $this->assertEquals($expected, $this->model->read('global')); - } -} From 5539e89909df118f4a948e54d63530de4e37f90f Mon Sep 17 00:00:00 2001 From: Roman Lytvynenko <lytvynen@adobe.com> Date: Tue, 7 May 2019 08:29:07 -0500 Subject: [PATCH 1231/1295] MAGETWO-99562: Frontend email login on Safari cursor issue --- lib/web/mage/trim-input.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/web/mage/trim-input.js b/lib/web/mage/trim-input.js index 430db67471991..d848e7eaac3fc 100644 --- a/lib/web/mage/trim-input.js +++ b/lib/web/mage/trim-input.js @@ -42,11 +42,11 @@ define([ */ _trimInput: function () { // Safari caret position workaround: storing carter position - var caretStart, caretEnd; + var caretStart, caretEnd, input; caretStart = this.options.cache.input.get(0).selectionStart; caretEnd = this.options.cache.input.get(0).selectionEnd; - var input = this._getInputValue().trim(); + this._getInputValue().trim(); this.options.cache.input.val(input); From 11dabd76aad829c631aecc8f627e624f665a24e9 Mon Sep 17 00:00:00 2001 From: Roman Lytvynenko <lytvynen@adobe.com> Date: Tue, 7 May 2019 08:48:12 -0500 Subject: [PATCH 1232/1295] MAGETWO-99562: Frontend email login on Safari cursor issue --- lib/web/mage/trim-input.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/mage/trim-input.js b/lib/web/mage/trim-input.js index d848e7eaac3fc..13bbad45d84d8 100644 --- a/lib/web/mage/trim-input.js +++ b/lib/web/mage/trim-input.js @@ -46,7 +46,7 @@ define([ caretStart = this.options.cache.input.get(0).selectionStart; caretEnd = this.options.cache.input.get(0).selectionEnd; - this._getInputValue().trim(); + input = this._getInputValue().trim(); this.options.cache.input.val(input); From 13590da2665f4c3cd1adac43f59f067b733acdf6 Mon Sep 17 00:00:00 2001 From: Roman Lytvynenko <lytvynen@adobe.com> Date: Tue, 7 May 2019 09:15:59 -0500 Subject: [PATCH 1233/1295] MAGETWO-99562: Frontend email login on Safari cursor issue --- lib/web/mage/trim-input.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/web/mage/trim-input.js b/lib/web/mage/trim-input.js index 13bbad45d84d8..d077dad8dc302 100644 --- a/lib/web/mage/trim-input.js +++ b/lib/web/mage/trim-input.js @@ -43,6 +43,7 @@ define([ _trimInput: function () { // Safari caret position workaround: storing carter position var caretStart, caretEnd, input; + caretStart = this.options.cache.input.get(0).selectionStart; caretEnd = this.options.cache.input.get(0).selectionEnd; From b4bd29ead5d205413111800c268c5ed2d0fcb90d Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Tue, 7 May 2019 11:04:07 -0500 Subject: [PATCH 1234/1295] MQE-1532: Bump MFTF version in Magento - MFTF version bump and lock file update --- composer.json | 2 +- composer.lock | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/composer.json b/composer.json index 8df1d26295124..20a4b449d3ef2 100644 --- a/composer.json +++ b/composer.json @@ -74,7 +74,7 @@ "ramsey/uuid": "~3.7.3" }, "require-dev": { - "magento/magento2-functional-testing-framework": "2.3.13", + "magento/magento2-functional-testing-framework": "2.4.0", "phpunit/phpunit": "~6.2.0", "squizlabs/php_codesniffer": "3.2.2", "phpmd/phpmd": "@stable", diff --git a/composer.lock b/composer.lock index acdfe237272f1..0d33f64678e7f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "442a108524a23e16cbb70c8957cd3097", + "content-hash": "4f20e93e274aba4b8965b3da66b59a1b", "packages": [ { "name": "braintree/braintree_php", @@ -6235,16 +6235,16 @@ }, { "name": "magento/magento2-functional-testing-framework", - "version": "2.3.13", + "version": "2.4.0", "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "2c8a4c3557c9a8412eb2ea50ce3f69abc2f47ba1" + "reference": "ef534dbcb3aeea68f9254dfd018165c546ad2edb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/2c8a4c3557c9a8412eb2ea50ce3f69abc2f47ba1", - "reference": "2c8a4c3557c9a8412eb2ea50ce3f69abc2f47ba1", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/ef534dbcb3aeea68f9254dfd018165c546ad2edb", + "reference": "ef534dbcb3aeea68f9254dfd018165c546ad2edb", "shasum": "" }, "require": { @@ -6257,12 +6257,12 @@ "fzaninotto/faker": "^1.6", "monolog/monolog": "^1.0", "mustache/mustache": "~2.5", - "php": "7.0.2|7.0.4|~7.0.6|~7.1.0|~7.2.0", + "php": "7.0.2||7.0.4||~7.0.6||~7.1.0||~7.2.0||~7.3.0", "symfony/process": "^2.8 || ^3.1 || ^4.0", "vlucas/phpdotenv": "^2.4" }, "require-dev": { - "brainmaestro/composer-git-hooks": "^2.3", + "brainmaestro/composer-git-hooks": "^2.3.1", "codacy/coverage": "^1.4", "codeception/aspect-mock": "^3.0", "doctrine/cache": "<1.7.0", @@ -6304,7 +6304,7 @@ "magento", "testing" ], - "time": "2019-01-29T15:31:14+00:00" + "time": "2019-04-29T20:56:26+00:00" }, { "name": "moontoast/math", From 76ab10ba972fd7a0e641c24caa0bc86f4bbdbca1 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Wed, 1 May 2019 16:17:15 -0500 Subject: [PATCH 1235/1295] MAGETWO-99540: Payment method validatation doesnt use updated billing address country ID for orders placed in admin --- .../view/adminhtml/web/order/create/scripts.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js b/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js index c508a5ecdfa58..a262178a63cb1 100644 --- a/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js +++ b/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js @@ -42,6 +42,7 @@ define([ this.isOnlyVirtualProduct = false; this.excludedPaymentMethods = []; this.summarizePrice = true; + this.selectAddressEvent = false; this.shippingTemplate = template(shippingTemplate, { data: { title: jQuery.mage.__('Shipping Method'), @@ -169,17 +170,19 @@ define([ }, selectAddress : function(el, container){ + id = el.value; if (id.length == 0) { id = '0'; } - if(this.addresses[id]){ - this.fillAddressFields(container, this.addresses[id]); - } - else{ + this.selectAddressEvent = true; + if (this.addresses[id]) { + this.fillAddressFields(container, this.addresses[id]); + } else { this.fillAddressFields(container, {}); } + this.selectAddressEvent = false; var data = this.serializeData(container); data[el.name] = id; @@ -190,6 +193,7 @@ define([ } else{ this.saveData(data); } + }, /** @@ -279,6 +283,10 @@ define([ $('order-' + type + '_address_customer_address_id').value; } + if (name === 'country_id' && this.selectAddressEvent === false) { + $('order-' + type + '_address_customer_address_id').value = ''; + } + this.resetPaymentMethod(); if (data['reset_shipping']) { From 1c80853f5898883f01f17cf9cead5ed70954a7e8 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Tue, 7 May 2019 15:00:47 -0500 Subject: [PATCH 1236/1295] MQE-1532: Bump MFTF version in Magento - Test Stabilization --- .../Test/AdminImportCustomizableOptionToProductWithSKUTest.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml index ce9337dead3f7..bf9535bcfe2df 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml @@ -28,6 +28,8 @@ <requiredEntity createDataKey="createCategory"/> </createData> <updateData createDataKey="createFirstProduct" entity="ProductWithFieldOptions" stepKey="updateProductCustomOptions" /> + <magentoCLI stepKey="reindex" command="indexer:reindex"/> + <magentoCLI stepKey="flushCache" command="cache:flush"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> From b358feed0c93b4a6c7c47aa1f73c9c6b9fabd006 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Wed, 8 May 2019 10:42:48 -0500 Subject: [PATCH 1237/1295] MQE-1532: Bump MFTF version in Magento - Test Stabilization --- .../Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml index e679d59fde791..91ca1f5afe3fc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml @@ -37,6 +37,7 @@ <waitForElementVisible selector="{{AdminProductCustomizableOptionsImportModalSection.nameFilter}}" stepKey="waitForNameField"/> <fillField selector="{{AdminProductCustomizableOptionsImportModalSection.nameFilter}}" userInput="{{productName}}" stepKey="fillProductName"/> <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFilters"/> + <waitForElementVisible selector="{{AdminDataGridTableSection.rowCheckbox('1')}}" stepKey="waitForFilterToApply"/> <checkOption selector="{{AdminDataGridTableSection.rowCheckbox('1')}}" stepKey="checkProductCheckbox"/> <click selector="{{AdminProductCustomizableOptionsImportModalSection.importButton}}" stepKey="clickImport"/> </actionGroup> From dc0394effcde6d40d8fb14cee975109cb2ebcae7 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Wed, 8 May 2019 11:37:29 -0500 Subject: [PATCH 1238/1295] MAGETWO-99446: Catalog cache gets flushed after Import --- app/code/Magento/Catalog/Block/Product/View.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Block/Product/View.php b/app/code/Magento/Catalog/Block/Product/View.php index 8b53223de0b9f..3c12cbbf31450 100644 --- a/app/code/Magento/Catalog/Block/Product/View.php +++ b/app/code/Magento/Catalog/Block/Product/View.php @@ -253,6 +253,7 @@ public function hasRequiredOptions() * instantly. * * @return bool + * @SuppressWarnings(PHPMD.RequestAwareBlockMethod) */ public function isStartCustomization() { From 88d0d48f2a5d1e90aff0f5a98fc5669bd70ef120 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Wed, 8 May 2019 11:43:17 -0500 Subject: [PATCH 1239/1295] MQE-1532: Bump MFTF version in Magento - Skipping MC-15740 --- .../Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml | 1 - .../Test/AdminImportCustomizableOptionToProductWithSKUTest.xml | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml index 91ca1f5afe3fc..e679d59fde791 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml @@ -37,7 +37,6 @@ <waitForElementVisible selector="{{AdminProductCustomizableOptionsImportModalSection.nameFilter}}" stepKey="waitForNameField"/> <fillField selector="{{AdminProductCustomizableOptionsImportModalSection.nameFilter}}" userInput="{{productName}}" stepKey="fillProductName"/> <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFilters"/> - <waitForElementVisible selector="{{AdminDataGridTableSection.rowCheckbox('1')}}" stepKey="waitForFilterToApply"/> <checkOption selector="{{AdminDataGridTableSection.rowCheckbox('1')}}" stepKey="checkProductCheckbox"/> <click selector="{{AdminProductCustomizableOptionsImportModalSection.importButton}}" stepKey="clickImport"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml index bf9535bcfe2df..e03f5f71dbdcc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml @@ -17,6 +17,9 @@ <severity value="MAJOR"/> <testCaseId value="MC-15740"/> <useCaseId value="MAGETWO-73157"/> + <skip> + <issueId value="MC-16313"/> + </skip> <group value="catalog"/> </annotations> <before> From 613ae9ba0eb54d9b4c9b94bd77dfbc2d82ce3d43 Mon Sep 17 00:00:00 2001 From: Oleksandr Dubovyk <odubovyk@magento.com> Date: Wed, 8 May 2019 14:17:39 -0500 Subject: [PATCH 1240/1295] MAGETWO-72788: [Magento Cloud] - Issue with long url - fixed --- lib/web/mage/adminhtml/grid.js | 44 ++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/lib/web/mage/adminhtml/grid.js b/lib/web/mage/adminhtml/grid.js index 5f7a709f04fea..a8b663d72697c 100644 --- a/lib/web/mage/adminhtml/grid.js +++ b/lib/web/mage/adminhtml/grid.js @@ -340,6 +340,45 @@ define([ location.href = url; }, + /** + * Builds the form with fields containing the and submits + * + * @param {String} url + * @param {String} varName + * @param {String} varValue + * @private + */ + _buildFormAndSubmit: function (url, varName, varValue) { + var re = new RegExp('\/(' + varName + '\/.*?\/)'), + parts = url.split(new RegExp('\\?')), + form = jQuery('<form/>'), + inputProps = [ + {name: varName, value: varValue}, + {name: 'form_key', value: window.FORM_KEY} + ], + input; + + url = parts[0].replace(re, '/'); + + if (parts.size() > 1) { + url += '?' + parts[1]; + } + + form.attr('action', url); + form.attr('method', 'POST'); + + inputProps.forEach(function (item) { + input = jQuery('<input/>'); + input.attr('name', item.name); + input.attr('type', 'hidden'); + input.val(item.value); + form.append(input); + }); + jQuery('[data-container="body"]').append(form); + form.submit(); + form.remove(); + }, + /** * @private */ @@ -389,13 +428,14 @@ define([ exportUrl = $(this.containerId + '_export').value; if (this.massaction && this.massaction.checkedString) { - exportUrl = this._addVarToUrl( + this._buildFormAndSubmit( exportUrl, this.massaction.formFieldNameInternal, this.massaction.checkedString ); + } else { + location.href = exportUrl; } - location.href = exportUrl; } }, From c0f0d6261f374d80b4026a4027b176a7d8eb58e9 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 8 May 2019 15:06:50 -0500 Subject: [PATCH 1241/1295] MAGETWO-99471: Custom customer address attribute values are empty --- .../view/adminhtml/templates/order/create/form/address.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/create/form/address.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/create/form/address.phtml index c96237149721c..1fd466c096663 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/create/form/address.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/create/form/address.phtml @@ -32,7 +32,7 @@ if ($block->getIsShipping()): require(["Magento_Sales/order/create/form"], function(){ order.shippingAddressContainer = '<?= /* @escapeNotVerified */ $_fieldsContainerId ?>'; - order.setAddresses(<?= /* @escapeVerfied */ $block->getAddressCollectionJson() ?>); + order.setAddresses(<?= /* @noEscape */ $block->getAddressCollectionJson() ?>); }); </script> From e8c1cdf9448bb383184427174ceef894a45b25e9 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Wed, 8 May 2019 12:15:45 -0500 Subject: [PATCH 1242/1295] MAGETWO-99581: Cannot cancel orders with an expired authorization for PayPal Express --- app/code/Magento/Paypal/Model/Express.php | 22 +++++++- app/code/Magento/Paypal/Model/Pro.php | 22 ++++---- .../Paypal/Test/Unit/Model/ExpressTest.php | 52 +++++++++++++++++-- 3 files changed, 80 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/Paypal/Model/Express.php b/app/code/Magento/Paypal/Model/Express.php index a44fb9e7c15b0..bae391058810c 100644 --- a/app/code/Magento/Paypal/Model/Express.php +++ b/app/code/Magento/Paypal/Model/Express.php @@ -17,10 +17,12 @@ use Magento\Store\Model\ScopeInterface; /** - * PayPal Express Module + * PayPal Express Module. + * * @method \Magento\Quote\Api\Data\PaymentMethodExtensionInterface getExtensionAttributes() * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class Express extends \Magento\Payment\Model\Method\AbstractMethod { @@ -179,6 +181,11 @@ class Express extends \Magento\Payment\Model\Method\AbstractMethod */ protected $transactionBuilder; + /** + * @var string + */ + private static $authorizationExpiredCode = 10601; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -269,6 +276,7 @@ protected function _setApiProcessableErrors() ApiProcessableException::API_MAXIMUM_AMOUNT_FILTER_DECLINE, ApiProcessableException::API_OTHER_FILTER_DECLINE, ApiProcessableException::API_ADDRESS_MATCH_FAIL, + self::$authorizationExpiredCode ] ); } @@ -538,7 +546,17 @@ public function refund(\Magento\Payment\Model\InfoInterface $payment, $amount) */ public function cancel(\Magento\Payment\Model\InfoInterface $payment) { - $this->void($payment); + try { + $this->void($payment); + } catch (ApiProcessableException $e) { + if ((int)$e->getCode() === self::$authorizationExpiredCode) { + $payment->setTransactionId(null); + $payment->setIsTransactionClosed(true); + $payment->setShouldCloseParentTransaction(true); + } else { + throw $e; + } + } return $this; } diff --git a/app/code/Magento/Paypal/Model/Pro.php b/app/code/Magento/Paypal/Model/Pro.php index 698ed87f26c09..c404610590cf8 100644 --- a/app/code/Magento/Paypal/Model/Pro.php +++ b/app/code/Magento/Paypal/Model/Pro.php @@ -10,10 +10,10 @@ use Magento\Paypal\Model\Api\AbstractApi; use Magento\Sales\Api\TransactionRepositoryInterface; -use Magento\Paypal\Model\Info; /** - * PayPal Website Payments Pro implementation for payment method instances + * PayPal Website Payments Pro implementation for payment method instances. + * * This model was created because right now PayPal Direct and PayPal Express payment methods cannot have same abstract */ class Pro @@ -149,7 +149,8 @@ public function getConfig() } /** - * API instance getter + * API instance getter. + * * Sets current store id to current config instance and passes it to API * * @return \Magento\Paypal\Model\Api\Nvp @@ -231,19 +232,22 @@ public function importPaymentInfo(\Magento\Framework\DataObject $from, \Magento\ public function void(\Magento\Framework\DataObject $payment) { $authTransactionId = $this->_getParentTransactionId($payment); - if ($authTransactionId) { - $api = $this->getApi(); - $api->setPayment($payment)->setAuthorizationId($authTransactionId)->callDoVoid(); - $this->importPaymentInfo($api, $payment); - } else { + if (empty($authTransactionId)) { throw new \Magento\Framework\Exception\LocalizedException( __('You need an authorization transaction to void.') ); } + + $api = $this->getApi(); + $api->setPayment($payment); + $api->setAuthorizationId($authTransactionId); + $api->callDoVoid(); + $this->importPaymentInfo($api, $payment); } /** - * Attempt to capture payment + * Attempt to capture payment. + * * Will return false if the payment is not supposed to be captured * * @param \Magento\Framework\DataObject $payment diff --git a/app/code/Magento/Paypal/Test/Unit/Model/ExpressTest.php b/app/code/Magento/Paypal/Test/Unit/Model/ExpressTest.php index 2575408078926..1fd816ecc6799 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/ExpressTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/ExpressTest.php @@ -12,6 +12,7 @@ use Magento\Payment\Model\InfoInterface; use Magento\Payment\Observer\AbstractDataAssignObserver; use Magento\Paypal\Model\Api\Nvp; +use Magento\Paypal\Model\Api\ProcessableException; use Magento\Paypal\Model\Api\ProcessableException as ApiProcessableException; use Magento\Paypal\Model\Express; use Magento\Paypal\Model\Pro; @@ -19,7 +20,7 @@ use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Payment; use Magento\Sales\Model\Order\Payment\Transaction\BuilderInterface; -use \PHPUnit_Framework_MockObject_MockObject as MockObject; +use PHPUnit_Framework_MockObject_MockObject as MockObject; /** * Class ExpressTest @@ -27,10 +28,15 @@ */ class ExpressTest extends \PHPUnit\Framework\TestCase { + /** + * @var string + */ + private static $authorizationExpiredCode = 10601; + /** * @var array */ - protected $errorCodes = [ + private $errorCodes = [ ApiProcessableException::API_INTERNAL_ERROR, ApiProcessableException::API_UNABLE_PROCESS_PAYMENT_ERROR_CODE, ApiProcessableException::API_DO_EXPRESS_CHECKOUT_FAIL, @@ -40,7 +46,7 @@ class ExpressTest extends \PHPUnit\Framework\TestCase ApiProcessableException::API_COUNTRY_FILTER_DECLINE, ApiProcessableException::API_MAXIMUM_AMOUNT_FILTER_DECLINE, ApiProcessableException::API_OTHER_FILTER_DECLINE, - ApiProcessableException::API_ADDRESS_MATCH_FAIL + ApiProcessableException::API_ADDRESS_MATCH_FAIL, ]; /** @@ -80,6 +86,7 @@ class ExpressTest extends \PHPUnit\Framework\TestCase protected function setUp() { + $this->errorCodes[] = self::$authorizationExpiredCode; $this->checkoutSession = $this->createPartialMock( Session::class, ['getPaypalTransactionData', 'setPaypalTransactionData'] @@ -104,16 +111,20 @@ protected function setUp() ); $this->pro = $this->createPartialMock( Pro::class, - ['setMethod', 'getApi', 'importPaymentInfo', 'resetApi'] + ['setMethod', 'getApi', 'importPaymentInfo', 'resetApi', 'void'] ); $this->eventManager = $this->getMockBuilder(ManagerInterface::class) ->setMethods(['dispatch']) ->getMockForAbstractClass(); - $this->pro->expects($this->any())->method('getApi')->will($this->returnValue($this->nvp)); + $this->pro->method('getApi') + ->willReturn($this->nvp); $this->helper = new ObjectManager($this); } + /** + * Tests setting the list of processable errors. + */ public function testSetApiProcessableErrors() { $this->nvp->expects($this->once())->method('setProcessableErrors')->with($this->errorCodes); @@ -128,6 +139,32 @@ public function testSetApiProcessableErrors() ); } + /** + * Tests canceling order payment when expired authorization generates exception on a client. + */ + public function testCancelWithExpiredAuthorizationTransaction() + { + $this->pro->method('void') + ->willThrowException( + new ProcessableException(__('PayPal gateway has rejected request.'), null, 10601) + ); + + $this->model = $this->helper->getObject(Express::class, ['data' => [$this->pro]]); + /** @var Payment|MockObject $paymentModel */ + $paymentModel = $this->createMock(Payment::class); + $paymentModel->expects($this->once()) + ->method('setTransactionId') + ->with(null); + $paymentModel->expects($this->once()) + ->method('setIsTransactionClosed') + ->with(true); + $paymentModel->expects($this->once()) + ->method('setShouldCloseParentTransaction') + ->with(true); + + $this->model->cancel($paymentModel); + } + /** * Tests order payment action. */ @@ -162,6 +199,11 @@ public function testOrder() static::assertEquals($this->model, $this->model->order($paymentModel, 12.3)); } + /** + * Tests data assigning. + * + * @throws \Magento\Framework\Exception\LocalizedException + */ public function testAssignData() { $transportValue = 'something'; From f6bcbfedee9d170282ebef3530119bbe5a974f22 Mon Sep 17 00:00:00 2001 From: Oleksandr Dubovyk <odubovyk@magento.com> Date: Wed, 8 May 2019 15:39:44 -0500 Subject: [PATCH 1243/1295] MAGETWO-72788: [Magento Cloud] - Issue with long url - code style --- lib/web/mage/adminhtml/grid.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/web/mage/adminhtml/grid.js b/lib/web/mage/adminhtml/grid.js index a8b663d72697c..141845f7d3b8a 100644 --- a/lib/web/mage/adminhtml/grid.js +++ b/lib/web/mage/adminhtml/grid.js @@ -353,8 +353,14 @@ define([ parts = url.split(new RegExp('\\?')), form = jQuery('<form/>'), inputProps = [ - {name: varName, value: varValue}, - {name: 'form_key', value: window.FORM_KEY} + { + name: varName, + value: varValue + }, + { + name: 'form_key', + value: window.FORM_KEY + } ], input; From a91de2bed2acc7408d6a2e6cdb35c1282f834aaf Mon Sep 17 00:00:00 2001 From: Oleksii Korshenko <korshenk@adobe.com> Date: Wed, 8 May 2019 16:10:56 -0500 Subject: [PATCH 1244/1295] Renamed travis.yml file to travis.yml.sample - travis build is not required on magento2 repository - all required builds are implemented as GitHub checks --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- .travis.yml => .travis.yml.sample | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename .travis.yml => .travis.yml.sample (100%) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index f191bd9aaba67..9d66ee40d6f59 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -35,4 +35,4 @@ - [ ] Pull request has a meaningful description of its purpose - [ ] All commits are accompanied by meaningful commit messages - [ ] All new or changed code is covered with unit/integration tests (if applicable) - - [ ] All automated tests passed successfully (all builds on Travis CI are green) + - [ ] All automated tests passed successfully (all builds are green) diff --git a/.travis.yml b/.travis.yml.sample similarity index 100% rename from .travis.yml rename to .travis.yml.sample From f9396dbdc9aaac6bc0647c13b0800bcf3f9f8ba4 Mon Sep 17 00:00:00 2001 From: RomanKis <roman.kis.y@gmail.com> Date: Thu, 28 Mar 2019 10:21:37 +0200 Subject: [PATCH 1245/1295] #21473 Form element validation is not triggered when validation rules change --- app/code/Magento/Ui/view/base/web/js/form/element/abstract.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js index 5617110590e50..fdd46a128b64f 100755 --- a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js @@ -296,7 +296,7 @@ define([ this.validation[rule] = options; } - changed = utils.compare(rules, this.validation).equal; + changed = !(utils.compare(rules, this.validation).equal); if (changed) { this.required(!!rules['required-entry']); From 20e38184c350271d4c359e0fc2ad9dd56bab9cc5 Mon Sep 17 00:00:00 2001 From: RomanKis <roman.kis.y@gmail.com> Date: Mon, 1 Apr 2019 12:37:59 +0300 Subject: [PATCH 1246/1295] #21473 Form element validation is not triggered when validation rules change --- app/code/Magento/Ui/view/base/web/js/form/element/abstract.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js index fdd46a128b64f..beb9b339ff55c 100755 --- a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js @@ -296,7 +296,7 @@ define([ this.validation[rule] = options; } - changed = !(utils.compare(rules, this.validation).equal); + changed = !utils.compare(rules, this.validation).equal; if (changed) { this.required(!!rules['required-entry']); From 1db265d90369de1a647c8aead9fc7fc600b7008d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20F=C3=BChr?= <d.fuehr@techdivision.com> Date: Wed, 20 Mar 2019 00:30:02 +0100 Subject: [PATCH 1247/1295] 21842 - do not cache absolute file paths Github issue: https://github.com/magento/magento2/issues/21842 In customer and customer_address validation context the validator factory no longer caches absolute file paths for the validation.xml files (currently two file) as the file content is evaluated later in the filesystem directly and the file paths may not be correct in a multi server setup with shared cache (id_prefix) --- lib/internal/Magento/Framework/Validator/Factory.php | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/lib/internal/Magento/Framework/Validator/Factory.php b/lib/internal/Magento/Framework/Validator/Factory.php index f2089c662e955..4e82c89720462 100644 --- a/lib/internal/Magento/Framework/Validator/Factory.php +++ b/lib/internal/Magento/Framework/Validator/Factory.php @@ -75,17 +75,7 @@ public function __construct( protected function _initializeConfigList() { if (!$this->_configFiles) { - $this->_configFiles = $this->cache->load(self::CACHE_KEY); - if (!$this->_configFiles) { - $this->_configFiles = $this->moduleReader->getConfigurationFiles('validation.xml'); - $this->cache->save( - $this->getSerializer()->serialize($this->_configFiles->toArray()), - self::CACHE_KEY - ); - } else { - $filesArray = $this->getSerializer()->unserialize($this->_configFiles); - $this->_configFiles = $this->getFileIteratorFactory()->create(array_keys($filesArray)); - } + $this->_configFiles = $this->moduleReader->getConfigurationFiles('validation.xml'); } } From 30f3fe1c23492b57266ed65e062d1f9b9f9c6ae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20F=C3=BChr?= <d.fuehr@techdivision.com> Date: Wed, 20 Mar 2019 11:46:41 +0100 Subject: [PATCH 1248/1295] clean up Validator Factory - adapt unit test --- .../Magento/Framework/Validator/Factory.php | 90 +++++-------------- .../Validator/Test/Unit/FactoryTest.php | 76 +--------------- 2 files changed, 24 insertions(+), 142 deletions(-) diff --git a/lib/internal/Magento/Framework/Validator/Factory.php b/lib/internal/Magento/Framework/Validator/Factory.php index 4e82c89720462..7a9eacac6ddbe 100644 --- a/lib/internal/Magento/Framework/Validator/Factory.php +++ b/lib/internal/Magento/Framework/Validator/Factory.php @@ -6,24 +6,26 @@ namespace Magento\Framework\Validator; -use Magento\Framework\Cache\FrontendInterface; +use Magento\Framework\Module\Dir\Reader; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Validator; +/** + * Factory for \Magento\Framework\Validator and \Magento\Framework\Validator\Builder. + */ class Factory { - /** cache key */ - const CACHE_KEY = __CLASS__; - /** - * @var \Magento\Framework\ObjectManagerInterface + * @var ObjectManagerInterface */ protected $_objectManager; /** * Validator config files * - * @var array|null + * @var iterable|null */ - protected $_configFiles = null; + protected $_configFiles; /** * @var bool @@ -31,40 +33,22 @@ class Factory private $isDefaultTranslatorInitialized = false; /** - * @var \Magento\Framework\Module\Dir\Reader + * @var Reader */ private $moduleReader; - /** - * @var FrontendInterface - */ - private $cache; - - /** - * @var \Magento\Framework\Serialize\SerializerInterface - */ - private $serializer; - - /** - * @var \Magento\Framework\Config\FileIteratorFactory - */ - private $fileIteratorFactory; - /** * Initialize dependencies * - * @param \Magento\Framework\ObjectManagerInterface $objectManager - * @param \Magento\Framework\Module\Dir\Reader $moduleReader - * @param FrontendInterface $cache + * @param ObjectManagerInterface $objectManager + * @param Reader $moduleReader */ public function __construct( - \Magento\Framework\ObjectManagerInterface $objectManager, - \Magento\Framework\Module\Dir\Reader $moduleReader, - FrontendInterface $cache + ObjectManagerInterface $objectManager, + Reader $moduleReader ) { $this->_objectManager = $objectManager; $this->moduleReader = $moduleReader; - $this->cache = $cache; } /** @@ -83,6 +67,7 @@ protected function _initializeConfigList() * Create and set default translator to \Magento\Framework\Validator\AbstractValidator. * * @return void + * @throws \Zend_Translate_Exception */ protected function _initializeDefaultTranslator() { @@ -95,7 +80,7 @@ protected function _initializeDefaultTranslator() /** @var \Magento\Framework\Translate\Adapter $translator */ $translator = $this->_objectManager->create(\Magento\Framework\Translate\Adapter::class); $translator->setOptions(['translator' => $translatorCallback]); - \Magento\Framework\Validator\AbstractValidator::setDefaultTranslator($translator); + AbstractValidator::setDefaultTranslator($translator); $this->isDefaultTranslatorInitialized = true; } } @@ -105,14 +90,15 @@ protected function _initializeDefaultTranslator() * * Will instantiate \Magento\Framework\Validator\Config * - * @return \Magento\Framework\Validator\Config + * @return Config + * @throws \Zend_Translate_Exception */ public function getValidatorConfig() { $this->_initializeConfigList(); $this->_initializeDefaultTranslator(); return $this->_objectManager->create( - \Magento\Framework\Validator\Config::class, + Config::class, ['configFiles' => $this->_configFiles] ); } @@ -123,7 +109,8 @@ public function getValidatorConfig() * @param string $entityName * @param string $groupName * @param array|null $builderConfig - * @return \Magento\Framework\Validator\Builder + * @return Builder + * @throws \Zend_Translate_Exception */ public function createValidatorBuilder($entityName, $groupName, array $builderConfig = null) { @@ -137,43 +124,12 @@ public function createValidatorBuilder($entityName, $groupName, array $builderCo * @param string $entityName * @param string $groupName * @param array|null $builderConfig - * @return \Magento\Framework\Validator + * @return Validator + * @throws \Zend_Translate_Exception */ public function createValidator($entityName, $groupName, array $builderConfig = null) { $this->_initializeDefaultTranslator(); return $this->getValidatorConfig()->createValidator($entityName, $groupName, $builderConfig); } - - /** - * Get serializer - * - * @return \Magento\Framework\Serialize\SerializerInterface - * @deprecated 100.2.0 - */ - private function getSerializer() - { - if ($this->serializer === null) { - $this->serializer = $this->_objectManager->get( - \Magento\Framework\Serialize\SerializerInterface::class - ); - } - return $this->serializer; - } - - /** - * Get file iterator factory - * - * @return \Magento\Framework\Config\FileIteratorFactory - * @deprecated 100.2.0 - */ - private function getFileIteratorFactory() - { - if ($this->fileIteratorFactory === null) { - $this->fileIteratorFactory = $this->_objectManager->get( - \Magento\Framework\Config\FileIteratorFactory::class - ); - } - return $this->fileIteratorFactory; - } } diff --git a/lib/internal/Magento/Framework/Validator/Test/Unit/FactoryTest.php b/lib/internal/Magento/Framework/Validator/Test/Unit/FactoryTest.php index 5511627c6dcc3..73a8c95c9a2ff 100644 --- a/lib/internal/Magento/Framework/Validator/Test/Unit/FactoryTest.php +++ b/lib/internal/Magento/Framework/Validator/Test/Unit/FactoryTest.php @@ -25,21 +25,6 @@ class FactoryTest extends \PHPUnit\Framework\TestCase */ private $validatorConfigMock; - /** - * @var \Magento\Framework\Cache\FrontendInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $cacheMock; - - /** - * @var \Magento\Framework\Serialize\SerializerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $serializerMock; - - /** - * @var \Magento\Framework\Config\FileIteratorFactory|\PHPUnit_Framework_MockObject_MockObject - */ - private $fileIteratorFactoryMock; - /** * @var \Magento\Framework\Config\FileIterator|\PHPUnit_Framework_MockObject_MockObject */ @@ -55,11 +40,6 @@ class FactoryTest extends \PHPUnit\Framework\TestCase */ private $factory; - /** - * @var string - */ - private $jsonString = '["\/tmp\/moduleOne\/etc\/validation.xml"]'; - /** * @var array */ @@ -99,23 +79,9 @@ protected function setUp() \Magento\Framework\Validator\Factory::class, [ 'objectManager' => $this->objectManagerMock, - 'moduleReader' => $this->readerMock, - 'cache' => $this->cacheMock + 'moduleReader' => $this->readerMock ] ); - - $this->serializerMock = $this->createMock(\Magento\Framework\Serialize\SerializerInterface::class); - $this->fileIteratorFactoryMock = $this->createMock(\Magento\Framework\Config\FileIteratorFactory::class); - $objectManager->setBackwardCompatibleProperty( - $this->factory, - 'serializer', - $this->serializerMock - ); - $objectManager->setBackwardCompatibleProperty( - $this->factory, - 'fileIteratorFactory', - $this->fileIteratorFactoryMock - ); } /** @@ -147,46 +113,6 @@ public function testGetValidatorConfig() ); } - public function testGetValidatorConfigCacheNotExist() - { - $this->cacheMock->expects($this->once()) - ->method('load') - ->willReturn(false); - $this->readerMock->expects($this->once()) - ->method('getConfigurationFiles') - ->willReturn($this->fileIteratorMock); - $this->fileIteratorMock->method('toArray') - ->willReturn($this->data); - $this->cacheMock->expects($this->once()) - ->method('save') - ->with($this->jsonString); - $this->serializerMock->expects($this->once()) - ->method('serialize') - ->with($this->data) - ->willReturn($this->jsonString); - $this->factory->getValidatorConfig(); - $this->factory->getValidatorConfig(); - } - - public function testGetValidatorConfigCacheExist() - { - $this->cacheMock->expects($this->once()) - ->method('load') - ->willReturn($this->jsonString); - $this->readerMock->expects($this->never()) - ->method('getConfigurationFiles'); - $this->cacheMock->expects($this->never()) - ->method('save'); - $this->serializerMock->expects($this->once()) - ->method('unserialize') - ->with($this->jsonString) - ->willReturn($this->data); - $this->fileIteratorFactoryMock->method('create') - ->willReturn($this->fileIteratorMock); - $this->factory->getValidatorConfig(); - $this->factory->getValidatorConfig(); - } - public function testCreateValidatorBuilder() { $this->readerMock->method('getConfigurationFiles') From 18f3a05cfff05472f0c22bdfe615913773dc7ad6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20F=C3=BChr?= <d.fuehr@techdivision.com> Date: Wed, 20 Mar 2019 14:48:40 +0100 Subject: [PATCH 1249/1295] untouch static method call to prevent codacy fail - refactoring of static method use should be in a separate pull request --- lib/internal/Magento/Framework/Validator/Factory.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Validator/Factory.php b/lib/internal/Magento/Framework/Validator/Factory.php index 7a9eacac6ddbe..198f4fb6730fa 100644 --- a/lib/internal/Magento/Framework/Validator/Factory.php +++ b/lib/internal/Magento/Framework/Validator/Factory.php @@ -8,6 +8,7 @@ use Magento\Framework\Module\Dir\Reader; use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Phrase; use Magento\Framework\Validator; /** @@ -75,12 +76,12 @@ protected function _initializeDefaultTranslator() // Pass translations to \Magento\Framework\TranslateInterface from validators $translatorCallback = function () { $argc = func_get_args(); - return (string)new \Magento\Framework\Phrase(array_shift($argc), $argc); + return (string)new Phrase(array_shift($argc), $argc); }; /** @var \Magento\Framework\Translate\Adapter $translator */ $translator = $this->_objectManager->create(\Magento\Framework\Translate\Adapter::class); $translator->setOptions(['translator' => $translatorCallback]); - AbstractValidator::setDefaultTranslator($translator); + \Magento\Framework\Validator\AbstractValidator::setDefaultTranslator($translator); $this->isDefaultTranslatorInitialized = true; } } From 6c27d26778442a5e36dba8b9bf4a56e9b62bc1aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20F=C3=BChr?= <d.fuehr@techdivision.com> Date: Mon, 1 Apr 2019 10:27:38 +0200 Subject: [PATCH 1250/1295] changes due to backword compatiblity constraints --- .../Magento/Framework/Validator/Factory.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Validator/Factory.php b/lib/internal/Magento/Framework/Validator/Factory.php index 198f4fb6730fa..87c29dd6681c3 100644 --- a/lib/internal/Magento/Framework/Validator/Factory.php +++ b/lib/internal/Magento/Framework/Validator/Factory.php @@ -10,12 +10,20 @@ use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Phrase; use Magento\Framework\Validator; +use Magento\Framework\Cache\FrontendInterface; /** * Factory for \Magento\Framework\Validator and \Magento\Framework\Validator\Builder. */ class Factory { + /** + * cache key + * + * @deprecated + */ + const CACHE_KEY = __CLASS__; + /** * @var ObjectManagerInterface */ @@ -26,7 +34,7 @@ class Factory * * @var iterable|null */ - protected $_configFiles; + protected $_configFiles = null; /** * @var bool @@ -43,10 +51,12 @@ class Factory * * @param ObjectManagerInterface $objectManager * @param Reader $moduleReader + * @param FrontendInterface $cache @deprecated */ public function __construct( ObjectManagerInterface $objectManager, - Reader $moduleReader + Reader $moduleReader, + FrontendInterface $cache ) { $this->_objectManager = $objectManager; $this->moduleReader = $moduleReader; From ac0559e60f0269765eacdd66e4e6586ef36e6f34 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Thu, 18 Apr 2019 15:31:02 +0300 Subject: [PATCH 1251/1295] magento/magento2#21856: Static test fix. --- lib/internal/Magento/Framework/Validator/Factory.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/internal/Magento/Framework/Validator/Factory.php b/lib/internal/Magento/Framework/Validator/Factory.php index 87c29dd6681c3..2a296f7cdcb24 100644 --- a/lib/internal/Magento/Framework/Validator/Factory.php +++ b/lib/internal/Magento/Framework/Validator/Factory.php @@ -52,6 +52,7 @@ class Factory * @param ObjectManagerInterface $objectManager * @param Reader $moduleReader * @param FrontendInterface $cache @deprecated + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( ObjectManagerInterface $objectManager, From 9c8fa5dc40d56ce011db1cc32f85ef999ac2c081 Mon Sep 17 00:00:00 2001 From: Oleksii Korshenko <korshenk@adobe.com> Date: Thu, 9 May 2019 10:20:12 -0500 Subject: [PATCH 1252/1295] Renamed travis.yml file to travis.yml.sample - updated readme file --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index d64ac8061528f..5a200505ec576 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ -[![Build Status](https://travis-ci.org/magento/magento2.svg?branch=2.2-develop)](https://travis-ci.org/magento/magento2) [![Open Source Helpers](https://www.codetriage.com/magento/magento2/badges/users.svg)](https://www.codetriage.com/magento/magento2) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/magento/magento2?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/magento-2/localized.png)](https://crowdin.com/project/magento-2) From 380404c708673818f2da05611420822b47907902 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Fri, 10 May 2019 13:23:17 +0300 Subject: [PATCH 1253/1295] MAGETWO-99358: Custom address attribute multiline error when editing and saving order --- .../Adminhtml/Order/AddressSave.php | 17 +- .../Sales/Model/Order/AddressRepository.php | 77 ++++- .../Model/Order/AddressRepositoryTest.php | 279 ++++++++++++++---- .../Model/Order/AddressRepositoryTest.php | 54 +++- .../order_address_with_multi_attribute.php | 100 +++++++ ..._address_with_multi_attribute_rollback.php | 62 ++++ 6 files changed, 517 insertions(+), 72 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_address_with_multi_attribute.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_address_with_multi_attribute_rollback.php diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/AddressSave.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/AddressSave.php index dc994e554b394..53563ccd70061 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/AddressSave.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/AddressSave.php @@ -9,6 +9,7 @@ use Magento\Backend\App\Action\Context; use Magento\Backend\Model\View\Result\Redirect; use Magento\Directory\Model\RegionFactory; +use Magento\Sales\Api\OrderAddressRepositoryInterface; use Magento\Sales\Api\OrderManagementInterface; use Magento\Sales\Api\OrderRepositoryInterface; use Magento\Sales\Api\Data\OrderAddressInterface; @@ -24,8 +25,11 @@ use Magento\Framework\Controller\Result\RawFactory; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\Action\HttpPostActionInterface; /** + * Sales address save + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class AddressSave extends Order @@ -54,6 +58,7 @@ class AddressSave extends Order * @param OrderRepositoryInterface $orderRepository * @param LoggerInterface $logger * @param RegionFactory|null $regionFactory + * @param OrderAddressRepositoryInterface|null $orderAddressRepository * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -69,9 +74,12 @@ public function __construct( OrderManagementInterface $orderManagement, OrderRepositoryInterface $orderRepository, LoggerInterface $logger, - RegionFactory $regionFactory = null + RegionFactory $regionFactory = null, + OrderAddressRepositoryInterface $orderAddressRepository = null ) { $this->regionFactory = $regionFactory ?: ObjectManager::getInstance()->get(RegionFactory::class); + $this->orderAddressRepository = $orderAddressRepository ?: ObjectManager::getInstance() + ->get(OrderAddressRepositoryInterface::class); parent::__construct( $context, $coreRegistry, @@ -87,6 +95,11 @@ public function __construct( ); } + /** + * @var OrderAddressRepositoryInterface + */ + private $orderAddressRepository; + /** * Save order address * @@ -105,7 +118,7 @@ public function execute() if ($data && $address->getId()) { $address->addData($data); try { - $address->save(); + $this->orderAddressRepository->save($address); $this->_eventManager->dispatch( 'admin_sales_order_address_update', [ diff --git a/app/code/Magento/Sales/Model/Order/AddressRepository.php b/app/code/Magento/Sales/Model/Order/AddressRepository.php index 96dc531a82bf4..7543a298c3a4a 100644 --- a/app/code/Magento/Sales/Model/Order/AddressRepository.php +++ b/app/code/Magento/Sales/Model/Order/AddressRepository.php @@ -5,7 +5,11 @@ */ namespace Magento\Sales\Model\Order; +use Magento\Customer\Model\AttributeMetadataDataProvider; +use Magento\Customer\Model\ResourceModel\Form\Attribute\Collection as AttributeCollection; use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Sales\Api\Data\OrderAddressInterface; use Magento\Sales\Model\ResourceModel\Metadata; use Magento\Sales\Api\Data\OrderAddressSearchResultInterfaceFactory as SearchResultFactory; use Magento\Framework\Exception\CouldNotDeleteException; @@ -40,20 +44,88 @@ class AddressRepository implements \Magento\Sales\Api\OrderAddressRepositoryInte */ private $collectionProcessor; + /** + * @var AttributeMetadataDataProvider + */ + private $attributeMetadataDataProvider; + + /** + * @var AttributeCollection|null + */ + private $attributesList = null; + /** * AddressRepository constructor. * @param Metadata $metadata * @param SearchResultFactory $searchResultFactory * @param CollectionProcessorInterface|null $collectionProcessor + * @param AttributeMetadataDataProvider $attributeMetadataDataProvider */ public function __construct( Metadata $metadata, SearchResultFactory $searchResultFactory, - CollectionProcessorInterface $collectionProcessor = null + CollectionProcessorInterface $collectionProcessor = null, + AttributeMetadataDataProvider $attributeMetadataDataProvider = null ) { $this->metadata = $metadata; $this->searchResultFactory = $searchResultFactory; $this->collectionProcessor = $collectionProcessor ?: $this->getCollectionProcessor(); + $this->attributeMetadataDataProvider = $attributeMetadataDataProvider ?: ObjectManager::getInstance() + ->get(AttributeMetadataDataProvider::class); + } + + /** + * Format multiline and multiselect attributes + * + * @param OrderAddressInterface $orderAddress + * + * @return void + */ + private function formatCustomAddressAttributes(OrderAddressInterface $orderAddress) + { + $attributesList = $this->getAttributesList(); + + foreach ($attributesList as $attribute) { + $attributeCode = $attribute->getAttributeCode(); + if (!$orderAddress->hasData($attributeCode)) { + continue; + } + $attributeValue = $orderAddress->getData($attributeCode); + if (is_array($attributeValue)) { + $glue = $attribute->getFrontendInput() === 'multiline' ? PHP_EOL : ','; + $attributeValue = trim(implode($glue, $attributeValue)); + } + $orderAddress->setData($attributeCode, $attributeValue); + } + } + + /** + * Get list of custom attributes. + * + * @return AttributeCollection|null + */ + private function getAttributesList() + { + if (!$this->attributesList) { + $attributesList = $this->attributeMetadataDataProvider->loadAttributesCollection( + 'customer_address', + 'customer_register_address' + ); + $attributesList->addFieldToFilter('is_user_defined', 1); + $attributesList->addFieldToFilter( + 'frontend_input', + [ + 'in' => [ + 'multiline', + 'multiselect', + ], + ] + ); + + $this->attributesList = $attributesList; + } + + return $this->attributesList; } /** @@ -95,7 +167,7 @@ public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCr $searchResult = $this->searchResultFactory->create(); $this->collectionProcessor->process($searchCriteria, $searchResult); $searchResult->setSearchCriteria($searchCriteria); - + return $searchResult; } @@ -141,6 +213,7 @@ public function deleteById($id) */ public function save(\Magento\Sales\Api\Data\OrderAddressInterface $entity) { + $this->formatCustomAddressAttributes($entity); try { $this->metadata->getMapper()->save($entity); $this->registry[$entity->getEntityId()] = $entity; diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/AddressRepositoryTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/AddressRepositoryTest.php index 87f4a9103be6f..c2be38f2ab096 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/AddressRepositoryTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/AddressRepositoryTest.php @@ -3,127 +3,181 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sales\Test\Unit\Model\Order; +use Magento\Customer\Model\AttributeMetadataDataProvider; +use Magento\Eav\Model\Entity\Attribute; use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Sales\Model\Order\Address as OrderAddress; +use Magento\Framework\Model\ResourceModel\Db\AbstractDb; +use Magento\Sales\Model\Order\AddressRepository; +use Magento\Sales\Model\ResourceModel\Order\Address\Collection as OrderAddressCollection; +use Magento\Customer\Model\ResourceModel\Form\Attribute\Collection as FormAttributeCollection; +use Magento\Framework\Api\SearchCriteria; +use Magento\Sales\Api\Data\OrderAddressSearchResultInterfaceFactory; +use Magento\Sales\Model\ResourceModel\Metadata; +use Magento\Sales\Model\Order\AddressRepository as OrderAddressRepository; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Exception\InputException; /** * Unit test for order address repository class. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class AddressRepositoryTest extends \PHPUnit\Framework\TestCase +class AddressRepositoryTest extends TestCase { /** * Subject of testing. * - * @var \Magento\Sales\Model\Order\AddressRepository + * @var OrderAddressRepository */ protected $subject; /** * Sales resource metadata. * - * @var \Magento\Sales\Model\ResourceModel\Metadata|\PHPUnit_Framework_MockObject_MockObject + * @var Metadata|MockObject */ protected $metadata; /** - * @var \Magento\Sales\Api\Data\OrderAddressSearchResultInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject + * @var OrderAddressSearchResultInterfaceFactory|MockObject */ protected $searchResultFactory; /** - * @var CollectionProcessorInterface |\PHPUnit_Framework_MockObject_MockObject + * @var CollectionProcessorInterface|MockObject */ private $collectionProcessorMock; + /** + * @var Attribute[] + */ + private $attributesList; + + /** + * @var AttributeMetadataDataProvider + */ + private $attributeMetadataDataProvider; + + /** + * @var OrderAddress|MockObject + */ + private $orderAddress; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @inheritdoc + */ protected function setUp() { - $objectManager = new ObjectManager($this); + $this->objectManager = new ObjectManager($this); + $this->orderAddress = $this->createPartialMock(OrderAddress::class, ['getEntityId', 'load']); $this->metadata = $this->createPartialMock( - \Magento\Sales\Model\ResourceModel\Metadata::class, + Metadata::class, ['getNewInstance', 'getMapper'] ); + $this->attributeMetadataDataProvider = $this->getMockBuilder(AttributeMetadataDataProvider::class) + ->disableOriginalConstructor() + ->setMethods(['loadAttributesCollection']) + ->getMock(); + $collectionAttribute = $this->getMockBuilder(FormAttributeCollection::class) + ->setMethods(['addFieldToFilter', 'getIterator']) + ->disableOriginalConstructor() + ->getMock(); + $collectionAttribute->method('getIterator') + ->willReturn(new \ArrayIterator([])); + $this->attributeMetadataDataProvider->method('loadAttributesCollection')->willReturn($collectionAttribute); + $this->searchResultFactory = $this->createPartialMock( - \Magento\Sales\Api\Data\OrderAddressSearchResultInterfaceFactory::class, + OrderAddressSearchResultInterfaceFactory::class, ['create'] ); $this->collectionProcessorMock = $this->getMockBuilder(CollectionProcessorInterface::class) ->getMock(); - $this->subject = $objectManager->getObject( - \Magento\Sales\Model\Order\AddressRepository::class, + $this->subject = $this->objectManager->getObject( + OrderAddressRepository::class, [ 'metadata' => $this->metadata, 'searchResultFactory' => $this->searchResultFactory, 'collectionProcessor' => $this->collectionProcessorMock, + 'attributeMetadataDataProvider' => $this->attributeMetadataDataProvider ] ); } /** + * Test for get order address + * * @param int|null $id * @param int|null $entityId + * + * @return void * @dataProvider getDataProvider */ public function testGet($id, $entityId) { if (!$id) { - $this->expectException( - \Magento\Framework\Exception\InputException::class - ); - + $this->expectException(InputException::class); $this->subject->get($id); } else { - $address = $this->createPartialMock(\Magento\Sales\Model\Order\Address::class, ['load', 'getEntityId']); - $address->expects($this->once()) + + $this->orderAddress->expects($this->once()) ->method('load') ->with($id) - ->willReturn($address); - $address->expects($this->once()) + ->willReturn($this->orderAddress); + $this->orderAddress->expects($this->once()) ->method('getEntityId') ->willReturn($entityId); $this->metadata->expects($this->once()) ->method('getNewInstance') - ->willReturn($address); + ->willReturn($this->orderAddress); if (!$entityId) { - $this->expectException( - \Magento\Framework\Exception\NoSuchEntityException::class - ); - + $this->expectException(NoSuchEntityException::class); $this->subject->get($id); } else { - $this->assertEquals($address, $this->subject->get($id)); + $this->assertEquals($this->orderAddress, $this->subject->get($id)); - $address->expects($this->never()) + $this->orderAddress->expects($this->never()) ->method('load') ->with($id) - ->willReturn($address); - $address->expects($this->never()) + ->willReturn($this->orderAddress); + $this->orderAddress->expects($this->never()) ->method('getEntityId') ->willReturn($entityId); $this->metadata->expects($this->never()) ->method('getNewInstance') - ->willReturn($address); + ->willReturn($this->orderAddress); // Retrieve Address from registry. - $this->assertEquals($address, $this->subject->get($id)); + $this->assertEquals($this->orderAddress, $this->subject->get($id)); } } } /** + * Data for testGet + * * @return array */ - public function getDataProvider() + public function getDataProvider(): array { return [ [null, null], @@ -132,10 +186,15 @@ public function getDataProvider() ]; } + /** + * Test for get list order address + * + * @return void + */ public function testGetList() { - $searchCriteria = $this->createMock(\Magento\Framework\Api\SearchCriteria::class); - $collection = $this->createMock(\Magento\Sales\Model\ResourceModel\Order\Address\Collection::class); + $searchCriteria = $this->createMock(SearchCriteria::class); + $collection = $this->createMock(OrderAddressCollection::class); $this->collectionProcessorMock->expects($this->once()) ->method('process') @@ -147,15 +206,19 @@ public function testGetList() $this->assertEquals($collection, $this->subject->getList($searchCriteria)); } + /** + * Test for delete order address + * + * @return void + */ public function testDelete() { - $address = $this->createPartialMock(\Magento\Sales\Model\Order\Address::class, ['getEntityId']); - $address->expects($this->once()) + $this->orderAddress->expects($this->once()) ->method('getEntityId') ->willReturn(1); $mapper = $this->getMockForAbstractClass( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, + AbstractDb::class, [], '', false, @@ -165,27 +228,29 @@ public function testDelete() ); $mapper->expects($this->once()) ->method('delete') - ->with($address); + ->with($this->orderAddress); $this->metadata->expects($this->any()) ->method('getMapper') ->willReturn($mapper); - $this->assertTrue($this->subject->delete($address)); + $this->assertTrue($this->subject->delete($this->orderAddress)); } /** + * Test for delete order address with exception + * + * @return void * @expectedException \Magento\Framework\Exception\CouldNotDeleteException * @expectedExceptionMessage Could not delete order address */ public function testDeleteWithException() { - $address = $this->createPartialMock(\Magento\Sales\Model\Order\Address::class, ['getEntityId']); - $address->expects($this->never()) + $this->orderAddress->expects($this->never()) ->method('getEntityId'); $mapper = $this->getMockForAbstractClass( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, + AbstractDb::class, [], '', false, @@ -201,18 +266,22 @@ public function testDeleteWithException() ->method('getMapper') ->willReturn($mapper); - $this->subject->delete($address); + $this->subject->delete($this->orderAddress); } + /** + * Test for save order address + * + * @return void + */ public function testSave() { - $address = $this->createPartialMock(\Magento\Sales\Model\Order\Address::class, ['getEntityId']); - $address->expects($this->any()) + $this->orderAddress->expects($this->any()) ->method('getEntityId') ->willReturn(1); $mapper = $this->getMockForAbstractClass( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, + AbstractDb::class, [], '', false, @@ -222,27 +291,29 @@ public function testSave() ); $mapper->expects($this->once()) ->method('save') - ->with($address); + ->with($this->orderAddress); $this->metadata->expects($this->any()) ->method('getMapper') ->willReturn($mapper); - $this->assertEquals($address, $this->subject->save($address)); + $this->assertEquals($this->orderAddress, $this->subject->save($this->orderAddress)); } /** + * Test for save order address with exception + * + * @return void * @expectedException \Magento\Framework\Exception\CouldNotSaveException * @expectedExceptionMessage Could not save order address */ public function testSaveWithException() { - $address = $this->createPartialMock(\Magento\Sales\Model\Order\Address::class, ['getEntityId']); - $address->expects($this->never()) + $this->orderAddress->expects($this->never()) ->method('getEntityId'); $mapper = $this->getMockForAbstractClass( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, + AbstractDb::class, [], '', false, @@ -258,17 +329,117 @@ public function testSaveWithException() ->method('getMapper') ->willReturn($mapper); - $this->assertEquals($address, $this->subject->save($address)); + $this->assertEquals($this->orderAddress, $this->subject->save($this->orderAddress)); } + /** + * Tets for create order address + * + * @return void + */ public function testCreate() { - $address = $this->createPartialMock(\Magento\Sales\Model\Order\Address::class, ['getEntityId']); - $this->metadata->expects($this->once()) ->method('getNewInstance') - ->willReturn($address); + ->willReturn($this->orderAddress); + + $this->assertEquals($this->orderAddress, $this->subject->create()); + } + + /** + * Test for save sales address with multi-attribute. + * + * @param string $attributeType + * @param string $attributeCode + * @param array $attributeValue + * @param string $expected + * + * @return void + * @dataProvider dataMultiAttribute + */ + public function testSaveWithMultiAttribute( + string $attributeType, + string $attributeCode, + array $attributeValue, + string $expected + ) { + $orderAddress = $this->getMockBuilder(OrderAddress::class) + ->disableOriginalConstructor() + ->setMethods(['getEntityId', 'hasData', 'getData', 'setData']) + ->getMock(); + + $orderAddress->expects($this->any()) + ->method('getEntityId') + ->willReturn(1); + + $mapper = $this->getMockForAbstractClass( + AbstractDb::class, + [], + '', + false, + true, + true, + ['save'] + ); + $mapper->method('save') + ->with($orderAddress); + $this->metadata->method('getMapper') + ->willReturn($mapper); + + $attributeModel = $this->getMockBuilder(Attribute::class) + ->setMethods(['getFrontendInput', 'getAttributeCode']) + ->disableOriginalConstructor() + ->getMock(); + $attributeModel->method('getFrontendInput')->willReturn($attributeType); + $attributeModel->method('getAttributeCode')->willReturn($attributeCode); + $this->attributesList = [$attributeModel]; + + $this->subject = $this->objectManager->getObject( + AddressRepository::class, + [ + 'metadata' => $this->metadata, + 'searchResultFactory' => $this->searchResultFactory, + 'collectionProcessor' => $this->collectionProcessorMock, + 'attributeMetadataDataProvider' => $this->attributeMetadataDataProvider, + 'attributesList' => $this->attributesList, + ] + ); + + $orderAddress->method('hasData')->with($attributeCode)->willReturn(true); + $orderAddress->method('getData')->with($attributeCode)->willReturn($attributeValue); + $orderAddress->expects($this->once())->method('setData')->with($attributeCode, $expected); + + $this->assertEquals($orderAddress, $this->subject->save($orderAddress)); + } + + /** + * Data for testSaveWithMultiAttribute + * + * @return array + */ + public function dataMultiAttribute(): array + { + $data = [ + 'multiselect' => [ + 'multiselect', + 'attr_multiselect', + [ + 'opt1', + 'opt2', + ], + 'opt1,opt2', + ], + 'multiline' => [ + 'multiline', + 'attr_multiline', + [ + 'line1', + 'line2', + ], + 'line1'.PHP_EOL.'line2', + ], + ]; - $this->assertEquals($address, $this->subject->create()); + return $data; } } 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 7a38c14685073..775ddd7274a48 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/AddressRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/AddressRepositoryTest.php @@ -3,19 +3,23 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sales\Model\Order; +use Magento\Framework\ObjectManagerInterface; +use Magento\Sales\Api\Data\OrderAddressInterface; +use Magento\Sales\Api\OrderAddressRepositoryInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\Framework\Api\FilterBuilder; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Api\SortOrderBuilder; +use PHPUnit\Framework\TestCase; /** * Class AddressRepositoryTest - * @package Magento\Sales\Model\Order] - * @magentoDbIsolation enabled */ -class AddressRepositoryTest extends \PHPUnit\Framework\TestCase +class AddressRepositoryTest extends TestCase { /** @var AddressRepository */ protected $repository; @@ -29,22 +33,25 @@ class AddressRepositoryTest extends \PHPUnit\Framework\TestCase /** @var SearchCriteriaBuilder */ private $searchCriteriaBuilder; + /** @var ObjectManagerInterface */ + private $objectManager; + + /** + * @inheritdoc + */ protected function setUp() { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->repository = $objectManager->create(AddressRepository::class); - $this->searchCriteriaBuilder = $objectManager->create( - \Magento\Framework\Api\SearchCriteriaBuilder::class - ); - $this->filterBuilder = $objectManager->get( - \Magento\Framework\Api\FilterBuilder::class - ); - $this->sortOrderBuilder = $objectManager->get( - \Magento\Framework\Api\SortOrderBuilder::class - ); + $this->objectManager = Bootstrap::getObjectManager(); + $this->repository = $this->objectManager->get(AddressRepository::class); + $this->searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $this->filterBuilder = $this->objectManager->get(FilterBuilder::class); + $this->sortOrderBuilder = $this->objectManager->get(SortOrderBuilder::class); } /** + * Test for get list with multiple filters and sorting + * + * @return void * @magentoDataFixture Magento/Sales/_files/address_list.php */ public function testGetListWithMultipleFiltersAndSorting() @@ -78,4 +85,23 @@ public function testGetListWithMultipleFiltersAndSorting() $this->assertEquals('ZX0789', array_shift($items)->getPostcode()); $this->assertEquals('47676', array_shift($items)->getPostcode()); } + + /** + * Test for formatting custom sales address multi-attribute + * + * @return void + * @magentoDataFixture Magento/Sales/_files/order_address_with_multi_attribute.php + */ + public function testFormatSalesAddressCustomMultiAttribute() + { + $address = $this->objectManager->get(OrderAddressInterface::class) + ->load('multiattribute@example.com', 'email'); + $address->setData('address_multiselect_attribute', ['dog', 'cat']); + $address->setData('address_multiline_attribute', ['dog', 'cat']); + + $this->objectManager->get(OrderAddressRepositoryInterface::class) + ->save($address); + $this->assertEquals('dog,cat', $address->getData('address_multiselect_attribute')); + $this->assertEquals('dog'.PHP_EOL.'cat', $address->getData('address_multiline_attribute')); + } } diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_address_with_multi_attribute.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_address_with_multi_attribute.php new file mode 100644 index 0000000000000..c78caa66cfe1e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_address_with_multi_attribute.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Eav\Model\Config; +use Magento\Eav\Model\Entity\Attribute\Set; +use Magento\Customer\Model\Attribute; +use Magento\Eav\Model\Entity\Type; +use Magento\Eav\Model\Entity\Attribute\Backend\ArrayBackend; +use Magento\Sales\Model\Order\Address; + +$objectManager = Bootstrap::getObjectManager(); +$addressData = [ + 'region' => 'CA', + 'region_id' => '12', + 'postcode' => '11111', + 'lastname' => 'lastname', + 'firstname' => 'firstname', + 'street' => 'street', + 'city' => 'Los Angeles', + 'email' => 'multiattribute@example.com', + 'telephone' => '2222222', + 'country_id' => 'US' +]; + +/** @var $entityType Type */ +$entityType = $objectManager->get(Config::class) + ->getEntityType('customer_address'); +/** @var $attributeSet Set */ +$attributeSet = $objectManager->get(Set::class); + +$attributeMultiselect = $objectManager->create( + Attribute::class, + [ + 'data' => [ + 'frontend_input' => 'multiselect', + 'frontend_label' => ['Multiselect Attribute'], + 'sort_order' => '0', + 'backend_type' => 'varchar', + 'is_user_defined' => 1, + 'is_system' => 0, + 'is_required' => '0', + 'is_visible' => '0', + 'attribute_set_id' => $entityType->getDefaultAttributeSetId(), + 'attribute_group_id' => $attributeSet->getDefaultGroupId($entityType->getDefaultAttributeSetId()), + 'entity_type_id' => $entityType->getId(), + 'backend_model' => ArrayBackend::class, + 'used_in_forms' => ['customer_register_address'], + 'option' => [ + 'value' => [ + 'dog' => ['Dog'], + 'cat' => ['Cat'], + ], + 'order' => [ + 'dog' => 1, + 'cat' => 2, + ], + ], + ] + ] +); + +$attributeMultiselect->setAttributeCode('address_multiselect_attribute'); +$attributeMultiselect->save(); + +$attributeMultiline = $objectManager->create( + Attribute::class, + [ + 'data' => [ + 'frontend_input' => 'multiline', + 'frontend_label' => ['Multiline Attribute'], + 'multiline_count' => 2, + 'sort_order' => '0', + 'backend_type' => 'varchar', + 'is_user_defined' => 1, + 'is_system' => 0, + 'is_required' => '0', + 'is_visible' => '0', + 'attribute_set_id' => $entityType->getDefaultAttributeSetId(), + 'attribute_group_id' => $attributeSet->getDefaultGroupId($entityType->getDefaultAttributeSetId()), + 'entity_type_id' => $entityType->getId(), + 'backend_model' => ArrayBackend::class, + 'used_in_forms' => ['customer_register_address'], + ] + ] +); + +$attributeMultiline->setAttributeCode('address_multiline_attribute'); +$attributeMultiline->save(); + +$billingAddress = $objectManager->create( + Address::class, + ['data' => $addressData] +); +$billingAddress->setAddressType('billing'); +$billingAddress->save(); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_address_with_multi_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_address_with_multi_attribute_rollback.php new file mode 100644 index 0000000000000..e7014583e872c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_address_with_multi_attribute_rollback.php @@ -0,0 +1,62 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Api\FilterBuilder; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Sales\Api\OrderAddressRepositoryInterface; +use Magento\Sales\Api\Data\OrderAddressInterface; +use Magento\Eav\Api\AttributeRepositoryInterface; + +$attributeCodes = [ + 'fixture_address_multiselect_attribute', + 'fixture_address_multiline_attribute', +]; +$eavConfigType = 'customer_address'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var OrderAddressRepositoryInterface $salesAddressRepository */ +$salesAddressRepository = $objectManager->get(OrderAddressRepositoryInterface::class); +/** @var SearchCriteriaBuilder $searchCriteriaBuilder */ +$searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class); +/** @var FilterBuilder $filterBuilder */ +$filterBuilder = $objectManager->get(FilterBuilder::class); +$filters = [ + $filterBuilder->setField(OrderAddressInterface::EMAIL) + ->setValue('multiattribute@example.com') + ->create(), +]; +$searchCriteria = $searchCriteriaBuilder->addFilters($filters) + ->create(); +$saleAddresses = $salesAddressRepository->getList($searchCriteria) + ->getItems(); +foreach ($saleAddresses as $saleAddress) { + $salesAddressRepository->delete($saleAddress); +} + +/** @var AttributeRepositoryInterface $attributerepository */ +$attributeRepository = $objectManager->get(AttributeRepositoryInterface::class); +/** @var FilterBuilder $filterBuilder */ +$filterBuilder = $objectManager->get(FilterBuilder::class); +$filters = [ + $filterBuilder->setField('attribute_code') + ->setValue( + [ + 'address_multiline_attribute', + 'address_multiselect_attribute', + ] + ) + ->setConditionType('IN') + ->create(), +]; +$searchCriteria = $searchCriteriaBuilder->addFilters($filters) + ->create(); +$attributes = $attributeRepository->getList($eavConfigType, $searchCriteria) + ->getItems(); +foreach ($attributes as $attribute) { + $attributeRepository->delete($attribute); +} From ee436a099c4bea6e978edc7f5f38f4d1272c53b6 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Fri, 10 May 2019 14:40:06 +0300 Subject: [PATCH 1254/1295] MAGETWO-99358: Custom address attribute multiline error when editing and saving order --- .../Sales/Test/Unit/Model/Order/AddressRepositoryTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/AddressRepositoryTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/AddressRepositoryTest.php index c2be38f2ab096..e48fea847a743 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/AddressRepositoryTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/AddressRepositoryTest.php @@ -135,7 +135,6 @@ public function testGet($id, $entityId) $this->expectException(InputException::class); $this->subject->get($id); } else { - $this->orderAddress->expects($this->once()) ->method('load') ->with($id) From d2027b277316c59e2eec4bc6ec571e15e2ed3bf1 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Thu, 18 Apr 2019 09:40:09 -0500 Subject: [PATCH 1255/1295] MAGETWO-99251: Duplicate orders with same quote ID when form is submitted using Enter key - Eliminated sending payment information multiple times --- .../js/view/payment/method-renderer/cc-form.js | 1 - .../payment/method-renderer/hosted-fields.js | 1 - .../frontend/web/js/view/payment/default.js | 15 +++++++++------ .../frontend/web/js/view/payment/iframe.js | 18 ++++++++++++++---- .../method-renderer/payflowpro-method.js | 5 ++++- .../payment/method-renderer/cc-form.test.js | 16 ---------------- 6 files changed, 27 insertions(+), 29 deletions(-) diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js index d078cacb96c2d..73a7b10d5d30f 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js @@ -79,7 +79,6 @@ define( */ onError: function (response) { braintree.showError($t('Payment ' + this.getTitle() + ' can\'t be initialized')); - this.isPlaceOrderActionAllowed(true); throw response.message; }, diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/hosted-fields.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/hosted-fields.js index bfdc2974dd4b0..cb9110ee8afa1 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/hosted-fields.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/hosted-fields.js @@ -156,7 +156,6 @@ define([ */ placeOrderClick: function () { if (this.validateCardType()) { - this.isPlaceOrderActionAllowed(false); $(this.getSelector('submit')).trigger('click'); } }, diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/payment/default.js b/app/code/Magento/Checkout/view/frontend/web/js/view/payment/default.js index 7b200860c4d55..1b5463c0770a3 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/payment/default.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/payment/default.js @@ -133,15 +133,14 @@ define([ event.preventDefault(); } - if (this.validate() && additionalValidators.validate()) { + if (this.validate() && + additionalValidators.validate() && + this.isPlaceOrderActionAllowed() === true + ) { this.isPlaceOrderActionAllowed(false); this.getPlaceOrderDeferredObject() - .fail( - function () { - self.isPlaceOrderActionAllowed(true); - } - ).done( + .done( function () { self.afterPlaceOrder(); @@ -149,6 +148,10 @@ define([ redirectOnSuccessAction.execute(); } } + ).always( + function () { + self.isPlaceOrderActionAllowed(true); + } ); return true; diff --git a/app/code/Magento/Payment/view/frontend/web/js/view/payment/iframe.js b/app/code/Magento/Payment/view/frontend/web/js/view/payment/iframe.js index 5eba4fd89d338..1e352e4297131 100644 --- a/app/code/Magento/Payment/view/frontend/web/js/view/payment/iframe.js +++ b/app/code/Magento/Payment/view/frontend/web/js/view/payment/iframe.js @@ -114,8 +114,12 @@ define([ * @override */ placeOrder: function () { - if (this.validateHandler() && additionalValidators.validate()) { + var self = this; + if (this.validateHandler() && + additionalValidators.validate() && + this.isPlaceOrderActionAllowed() === true + ) { fullScreenLoader.startLoader(); this.isPlaceOrderActionAllowed(false); @@ -127,8 +131,15 @@ define([ method: this.getCode() } ) - ).done(this.done.bind(this)) - .fail(this.fail.bind(this)); + ).done( + this.done.bind(this) + ).fail( + this.fail.bind(this) + ).always( + function () { + self.isPlaceOrderActionAllowed(true); + } + ); this.initTimeoutHandler(); } @@ -192,7 +203,6 @@ define([ */ fail: function () { fullScreenLoader.stopLoader(); - this.isPlaceOrderActionAllowed(true); return this; }, diff --git a/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/payflowpro-method.js b/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/payflowpro-method.js index 786f1a5aa85fd..24d06b7d0f8f2 100644 --- a/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/payflowpro-method.js +++ b/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/payflowpro-method.js @@ -79,7 +79,10 @@ define([ placeOrder: function () { var self = this; - if (this.validateHandler() && additionalValidators.validate()) { + if (this.validateHandler() && + additionalValidators.validate() && + this.isPlaceOrderActionAllowed() === true + ) { this.isPlaceOrderActionAllowed(false); fullScreenLoader.startLoader(); $.when( diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js index 6062439db2365..817553c30d0f9 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js @@ -85,21 +85,5 @@ define([ expect(braintreeCcForm.getCode()).toEqual(expectedCode); expect(braintreeCcForm.messageContainer).toEqual(expectedMessageContainer); }); - - it('Check if form validation fails when "Place Order" button should be active.', function () { - var errorMessage = 'Something went wrong.', - - /** - * Anonymous wrapper - */ - func = function () { - braintreeCcForm.clientConfig.onError({ - 'message': errorMessage - }); - }; - - expect(func).toThrow(errorMessage); - expect(braintreeCcForm.isPlaceOrderActionAllowed()).toBeTruthy(); - }); }); }); From 0e528c0ea2ffe44a153df5b4bffb2b2742e0070a Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Thu, 25 Apr 2019 13:55:01 -0500 Subject: [PATCH 1256/1295] MAGETWO-99416: Empty email in quote prevent order creation even if payment success - Added order address validation before order placing - Added logging of failed order saving attempts --- .../Magento/Quote/Model/QuoteManagement.php | 17 +++-- .../Quote/Model/SubmitQuoteValidator.php | 71 +++++++++++++++++++ .../Test/Unit/Model/QuoteManagementTest.php | 27 ++++--- .../Sales/Model/Service/OrderService.php | 38 ++++++---- .../Unit/Model/Service/OrderServiceTest.php | 13 +++- .../Fixtures/quote_without_customer_email.php | 28 ++++++++ .../Quote/Model/QuoteManagementTest.php | 29 ++++++++ 7 files changed, 195 insertions(+), 28 deletions(-) create mode 100644 app/code/Magento/Quote/Model/SubmitQuoteValidator.php create mode 100644 dev/tests/integration/testsuite/Magento/Quote/Fixtures/quote_without_customer_email.php diff --git a/app/code/Magento/Quote/Model/QuoteManagement.php b/app/code/Magento/Quote/Model/QuoteManagement.php index b7d994138026a..79170ad90832c 100644 --- a/app/code/Magento/Quote/Model/QuoteManagement.php +++ b/app/code/Magento/Quote/Model/QuoteManagement.php @@ -37,9 +37,9 @@ class QuoteManagement implements \Magento\Quote\Api\CartManagementInterface protected $eventManager; /** - * @var QuoteValidator + * @var SubmitQuoteValidator */ - protected $quoteValidator; + private $submitQuoteValidator; /** * @var OrderFactory @@ -148,7 +148,7 @@ class QuoteManagement implements \Magento\Quote\Api\CartManagementInterface /** * @param EventManager $eventManager - * @param QuoteValidator $quoteValidator + * @param SubmitQuoteValidator $submitQuoteValidator * @param OrderFactory $orderFactory * @param OrderManagement $orderManagement * @param CustomerManagement $customerManagement @@ -173,7 +173,7 @@ class QuoteManagement implements \Magento\Quote\Api\CartManagementInterface */ public function __construct( EventManager $eventManager, - QuoteValidator $quoteValidator, + SubmitQuoteValidator $submitQuoteValidator, OrderFactory $orderFactory, OrderManagement $orderManagement, CustomerManagement $customerManagement, @@ -196,7 +196,7 @@ public function __construct( \Magento\Customer\Api\AddressRepositoryInterface $addressRepository = null ) { $this->eventManager = $eventManager; - $this->quoteValidator = $quoteValidator; + $this->submitQuoteValidator = $submitQuoteValidator; $this->orderFactory = $orderFactory; $this->orderManagement = $orderManagement; $this->customerManagement = $customerManagement; @@ -282,6 +282,7 @@ public function assignCustomer($cartId, $customerId, $storeId) throw new StateException( __('Cannot assign customer to the given cart. Customer already has active cart.') ); + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { } @@ -454,7 +455,7 @@ protected function resolveItems(QuoteEntity $quote) protected function submitQuote(QuoteEntity $quote, $orderData = []) { $order = $this->orderFactory->create(); - $this->quoteValidator->validateBeforeSubmit($quote); + $this->submitQuoteValidator->validateQuote($quote); if (!$quote->getCustomerIsGuest()) { if ($quote->getCustomerId()) { $this->_prepareCustomerQuote($quote); @@ -509,6 +510,7 @@ protected function submitQuote(QuoteEntity $quote, $orderData = []) $order->setCustomerFirstname($quote->getCustomerFirstname()); $order->setCustomerMiddlename($quote->getCustomerMiddlename()); $order->setCustomerLastname($quote->getCustomerLastname()); + $this->submitQuoteValidator->validateOrder($order); $this->eventManager->dispatch( 'sales_model_service_quote_submit_before', @@ -625,12 +627,13 @@ private function rollbackAddresses( 'exception' => $e, ] ); + // phpcs:ignore Magento2.Exceptions.ThrowCatch } catch (\Exception $consecutiveException) { $message = sprintf( "An exception occurred on 'sales_model_service_quote_submit_failure' event: %s", $consecutiveException->getMessage() ); - + // phpcs:ignore Magento2.Exceptions.DirectThrow throw new \Exception($message, 0, $e); } } diff --git a/app/code/Magento/Quote/Model/SubmitQuoteValidator.php b/app/code/Magento/Quote/Model/SubmitQuoteValidator.php new file mode 100644 index 0000000000000..76d31f94d2a62 --- /dev/null +++ b/app/code/Magento/Quote/Model/SubmitQuoteValidator.php @@ -0,0 +1,71 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Quote\Model; + +use Magento\Framework\Exception\LocalizedException; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Address\Validator as OrderAddressValidator; + +/** + * Validates quote and order before quote submit. + */ +class SubmitQuoteValidator +{ + /** + * @var QuoteValidator + */ + private $quoteValidator; + + /** + * @var OrderAddressValidator + */ + private $orderAddressValidator; + + /** + * @param QuoteValidator $quoteValidator + * @param OrderAddressValidator $orderAddressValidator + */ + public function __construct( + QuoteValidator $quoteValidator, + OrderAddressValidator $orderAddressValidator + ) { + $this->quoteValidator = $quoteValidator; + $this->orderAddressValidator = $orderAddressValidator; + } + + /** + * Validates quote. + * + * @param Quote $quote + * @return void + * @throws LocalizedException + */ + public function validateQuote(Quote $quote) + { + $this->quoteValidator->validateBeforeSubmit($quote); + } + + /** + * Validates order. + * + * @param Order $order + * @return void + * @throws LocalizedException + */ + public function validateOrder(Order $order) + { + foreach ($order->getAddresses() as $address) { + $errors = $this->orderAddressValidator->validate($address); + if (!empty($errors)) { + throw new LocalizedException( + __("Failed address validation:\n%1", implode("\n", $errors)) + ); + } + } + } +} diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php index cdfd0df8f9927..aca490ae2b1a1 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php @@ -13,6 +13,8 @@ /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.TooManyFields) + * @SuppressWarnings(PHPMD.TooManyPublicMethods) + * @SuppressWarnings(PHPMD.ExcessiveClassLength) */ class QuoteManagementTest extends \PHPUnit\Framework\TestCase { @@ -22,9 +24,9 @@ class QuoteManagementTest extends \PHPUnit\Framework\TestCase protected $model; /** - * @var \Magento\Quote\Model\QuoteValidator|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Quote\Model\SubmitQuoteValidator|\PHPUnit_Framework_MockObject_MockObject */ - protected $quoteValidator; + protected $submitQuoteValidator; /** * @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject @@ -143,7 +145,7 @@ protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->quoteValidator = $this->createMock(\Magento\Quote\Model\QuoteValidator::class); + $this->submitQuoteValidator = $this->createMock(\Magento\Quote\Model\SubmitQuoteValidator::class); $this->eventManager = $this->getMockForAbstractClass(\Magento\Framework\Event\ManagerInterface::class); $this->orderFactory = $this->createPartialMock( \Magento\Sales\Api\Data\OrderInterfaceFactory::class, @@ -210,7 +212,7 @@ protected function setUp() \Magento\Quote\Model\QuoteManagement::class, [ 'eventManager' => $this->eventManager, - 'quoteValidator' => $this->quoteValidator, + 'submitQuoteValidator' => $this->submitQuoteValidator, 'orderFactory' => $this->orderFactory, 'orderManagement' => $this->orderManagement, 'customerManagement' => $this->customerManagement, @@ -560,7 +562,9 @@ public function testSubmit() $shippingAddress ); - $this->quoteValidator->expects($this->once())->method('validateBeforeSubmit')->with($quote); + $this->submitQuoteValidator->expects($this->once()) + ->method('validateQuote') + ->with($quote); $this->quoteAddressToOrder->expects($this->once()) ->method('convert') ->with($shippingAddress, $orderData) @@ -655,7 +659,7 @@ public function testPlaceOrderIfCustomerIsGuest() ->setConstructorArgs( [ 'eventManager' => $this->eventManager, - 'quoteValidator' => $this->quoteValidator, + 'quoteValidator' => $this->submitQuoteValidator, 'orderFactory' => $this->orderFactory, 'orderManagement' => $this->orderManagement, 'customerManagement' => $this->customerManagement, @@ -712,7 +716,7 @@ public function testPlaceOrder() ->setConstructorArgs( [ 'eventManager' => $this->eventManager, - 'quoteValidator' => $this->quoteValidator, + 'quoteValidator' => $this->submitQuoteValidator, 'orderFactory' => $this->orderFactory, 'orderManagement' => $this->orderManagement, 'customerManagement' => $this->customerManagement, @@ -934,6 +938,9 @@ protected function prepareOrderFactory( return $order; } + /** + * @throws NoSuchEntityException + */ public function testGetCartForCustomer() { $customerId = 100; @@ -978,6 +985,9 @@ protected function setPropertyValue(&$object, $property, $value) return $object; } + /** + * @throws \Magento\Framework\Exception\LocalizedException + */ public function testSubmitForCustomer() { $orderData = []; @@ -1010,7 +1020,8 @@ public function testSubmitForCustomer() $shippingAddress ); - $this->quoteValidator->expects($this->once())->method('validateBeforeSubmit')->with($quote); + $this->submitQuoteValidator->method('validateQuote') + ->with($quote); $this->quoteAddressToOrder->expects($this->once()) ->method('convert') ->with($shippingAddress, $orderData) diff --git a/app/code/Magento/Sales/Model/Service/OrderService.php b/app/code/Magento/Sales/Model/Service/OrderService.php index e4a71f028cc82..2e062caca9a24 100644 --- a/app/code/Magento/Sales/Model/Service/OrderService.php +++ b/app/code/Magento/Sales/Model/Service/OrderService.php @@ -7,6 +7,7 @@ use Magento\Sales\Api\OrderManagementInterface; use Magento\Payment\Gateway\Command\CommandException; +use Psr\Log\LoggerInterface; /** * Class OrderService @@ -55,6 +56,11 @@ class OrderService implements OrderManagementInterface */ private $paymentFailures; + /** + * @var LoggerInterface + */ + private $logger; + /** * Constructor * @@ -65,7 +71,8 @@ class OrderService implements OrderManagementInterface * @param \Magento\Sales\Model\OrderNotifier $notifier * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param \Magento\Sales\Model\Order\Email\Sender\OrderCommentSender $orderCommentSender - * @param \Magento\Sales\Api\PaymentFailuresInterface|null $paymentFailures + * @param \Magento\Sales\Api\PaymentFailuresInterface $paymentFailures + * @param LoggerInterface $logger */ public function __construct( \Magento\Sales\Api\OrderRepositoryInterface $orderRepository, @@ -75,7 +82,8 @@ public function __construct( \Magento\Sales\Model\OrderNotifier $notifier, \Magento\Framework\Event\ManagerInterface $eventManager, \Magento\Sales\Model\Order\Email\Sender\OrderCommentSender $orderCommentSender, - \Magento\Sales\Api\PaymentFailuresInterface $paymentFailures = null + \Magento\Sales\Api\PaymentFailuresInterface $paymentFailures, + LoggerInterface $logger ) { $this->orderRepository = $orderRepository; $this->historyRepository = $historyRepository; @@ -84,8 +92,8 @@ public function __construct( $this->notifier = $notifier; $this->eventManager = $eventManager; $this->orderCommentSender = $orderCommentSender; - $this->paymentFailures = $paymentFailures ? : \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Sales\Api\PaymentFailuresInterface::class); + $this->paymentFailures = $paymentFailures; + $this->logger = $logger; } /** @@ -189,25 +197,31 @@ public function unHold($id) } /** + * Perform place order. + * * @param \Magento\Sales\Api\Data\OrderInterface $order * @return \Magento\Sales\Api\Data\OrderInterface * @throws \Exception */ public function place(\Magento\Sales\Api\Data\OrderInterface $order) { - // transaction will be here - //begin transaction try { $order->place(); - return $this->orderRepository->save($order); - //commit + } catch (CommandException $e) { + $this->paymentFailures->handle((int)$order->getQuoteId(), __($e->getMessage())); + throw $e; + } + + try { + $order = $this->orderRepository->save($order); } catch (\Exception $e) { - if ($e instanceof CommandException) { - $this->paymentFailures->handle((int)$order->getQuoteId(), __($e->getMessage())); - } + $this->logger->critical( + 'Saving order ' . $order->getIncrementId() . ' failed: ' . $e->getMessage() + ); throw $e; - //rollback; } + + return $order; } /** diff --git a/app/code/Magento/Sales/Test/Unit/Model/Service/OrderServiceTest.php b/app/code/Magento/Sales/Test/Unit/Model/Service/OrderServiceTest.php index 067f83d1e5b32..72fe7380ce8e4 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Service/OrderServiceTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Service/OrderServiceTest.php @@ -5,6 +5,9 @@ */ namespace Magento\Sales\Test\Unit\Model\Service; +use Magento\Sales\Api\PaymentFailuresInterface; +use Psr\Log\LoggerInterface; + /** * Class OrderUnHoldTest * @@ -140,6 +143,12 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); + /** @var PaymentFailuresInterface|\PHPUnit_Framework_MockObject_MockObject $paymentFailures */ + $paymentFailures = $this->createMock(PaymentFailuresInterface::class); + + /** @var LoggerInterface|\PHPUnit_Framework_MockObject_MockObject $logger */ + $logger = $this->createMock(LoggerInterface::class); + $this->orderService = new \Magento\Sales\Model\Service\OrderService( $this->orderRepositoryMock, $this->orderStatusHistoryRepositoryMock, @@ -147,7 +156,9 @@ protected function setUp() $this->filterBuilderMock, $this->orderNotifierMock, $this->eventManagerMock, - $this->orderCommentSender + $this->orderCommentSender, + $paymentFailures, + $logger ); } diff --git a/dev/tests/integration/testsuite/Magento/Quote/Fixtures/quote_without_customer_email.php b/dev/tests/integration/testsuite/Magento/Quote/Fixtures/quote_without_customer_email.php new file mode 100644 index 0000000000000..49fd11593c798 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Quote/Fixtures/quote_without_customer_email.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; + +require __DIR__ . '/../../Sales/_files/quote_with_customer.php'; + +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +$quote->getPayment() + ->setMethod('checkmo'); +$quote->getShippingAddress() + ->setShippingMethod('flatrate_flatrate') + ->setCollectShippingRates(true); +$quote->collectTotals(); + +$quote->setCustomerEmail(''); + +/** @var CartRepositoryInterface $repository */ +$repository = $objectManager->get(CartRepositoryInterface::class); +$repository->save($quote); diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php index 356117f2b3dc8..dc784aa55efc4 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php @@ -12,9 +12,11 @@ use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Quote\Api\CartManagementInterface; use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Sales\Api\OrderManagementInterface; use Magento\Sales\Api\OrderRepositoryInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\ExpectationFailedException; /** * Class for testing QuoteManagement model @@ -101,6 +103,33 @@ public function testSubmitWithItemOutOfStock() $this->cartManagement->placeOrder($quote->getId()); } + /** + * Tries to create an order using quote with empty customer email. + * + * Order should not start placing if order validation is failed. + * + * @magentoDataFixture Magento/Quote/Fixtures/quote_without_customer_email.php + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Email has a wrong format + */ + public function testSubmitWithEmptyCustomerEmail() + { + $quote = $this->getQuote('test01'); + $orderManagement = $this->createMock(OrderManagementInterface::class); + $orderManagement->expects($this->never()) + ->method('place'); + $cartManagement = $this->objectManager->create( + CartManagementInterface::class, + ['orderManagement' => $orderManagement] + ); + + try { + $cartManagement->placeOrder($quote->getId()); + } catch (ExpectationFailedException $e) { + $this->fail('Place order method was not expected to be called if order validation is failed'); + } + } + /** * Gets quote by reserved order ID. * From df7fad827caa3ec4b5c25f582d6153f3f4016295 Mon Sep 17 00:00:00 2001 From: Satya Prakash <p.satyaprakash.viet.2009@gmail.com> Date: Sat, 4 May 2019 16:41:52 +0530 Subject: [PATCH 1257/1295] Fixed:#22395 Fixed #22395: config:set -le and -lc short form options don't work --- app/code/Magento/Config/Console/Command/ConfigSetCommand.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Config/Console/Command/ConfigSetCommand.php b/app/code/Magento/Config/Console/Command/ConfigSetCommand.php index cb79daddbf5f9..2e76e717ba36b 100644 --- a/app/code/Magento/Config/Console/Command/ConfigSetCommand.php +++ b/app/code/Magento/Config/Console/Command/ConfigSetCommand.php @@ -114,13 +114,13 @@ protected function configure() ), new InputOption( static::OPTION_LOCK_ENV, - 'le', + 'e', InputOption::VALUE_NONE, 'Lock value which prevents modification in the Admin (will be saved in app/etc/env.php)' ), new InputOption( static::OPTION_LOCK_CONFIG, - 'lc', + 'c', InputOption::VALUE_NONE, 'Lock and share value with other installations, prevents modification in the Admin ' . '(will be saved in app/etc/config.php)' From 2e7c766b07b933e5fbcf38efdd6326b922262b16 Mon Sep 17 00:00:00 2001 From: Nazarn96 <nazarn96@gmail.com> Date: Tue, 7 May 2019 10:27:21 +0300 Subject: [PATCH 1258/1295] magento/magento2#22720 static-test-fix --- app/code/Magento/Config/Console/Command/ConfigSetCommand.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Config/Console/Command/ConfigSetCommand.php b/app/code/Magento/Config/Console/Command/ConfigSetCommand.php index 2e76e717ba36b..999d8e41af5bc 100644 --- a/app/code/Magento/Config/Console/Command/ConfigSetCommand.php +++ b/app/code/Magento/Config/Console/Command/ConfigSetCommand.php @@ -139,8 +139,10 @@ protected function configure() /** * Creates and run appropriate processor, depending on input options. * - * {@inheritdoc} + * @param InputInterface $input + * @param OutputInterface $output * @since 100.2.0 + * @return int|null */ protected function execute(InputInterface $input, OutputInterface $output) { From 0c6293df1f88e14e30073b9f8415970267b7cc6a Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Mon, 13 May 2019 13:53:13 +0300 Subject: [PATCH 1259/1295] MAGETWO-93675: Smart Category with tier price conditions --- .../ResourceModel/Product/Collection.php | 90 ++++++++++++++----- .../ResourceModel/Product/CollectionTest.php | 16 ++++ 2 files changed, 86 insertions(+), 20 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index 7db77faaafcaf..a6394ce293a6c 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -1570,26 +1570,9 @@ public function addAttributeToFilter($attribute, $condition = null, $joinType = $this->_allIdsCache = null; if (is_string($attribute) && $attribute == 'is_saleable') { - $columns = $this->getSelect()->getPart(\Magento\Framework\DB\Select::COLUMNS); - foreach ($columns as $columnEntry) { - list($correlationName, $column, $alias) = $columnEntry; - if ($alias == 'is_saleable') { - if ($column instanceof \Zend_Db_Expr) { - $field = $column; - } else { - $connection = $this->getSelect()->getConnection(); - if (empty($correlationName)) { - $field = $connection->quoteColumnAs($column, $alias, true); - } else { - $field = $connection->quoteColumnAs([$correlationName, $column], $alias, true); - } - } - $this->getSelect()->where("{$field} = ?", $condition); - break; - } - } - - return $this; + $this->addIsSaleableAttributeToFilter($condition); + } elseif (is_string($attribute) && $attribute == 'tier_price') { + $this->addTierPriceAttributeToFilter($attribute, $condition); } else { return parent::addAttributeToFilter($attribute, $condition, $joinType); } @@ -2469,4 +2452,71 @@ public function getPricesCount() return $this->_pricesCount; } + + /** + * Add is_saleable attribute to filter + * + * @param array|null $condition + * @return $this + */ + private function addIsSaleableAttributeToFilter($condition) + { + $columns = $this->getSelect()->getPart(Select::COLUMNS); + foreach ($columns as $columnEntry) { + list($correlationName, $column, $alias) = $columnEntry; + if ($alias == 'is_saleable') { + if ($column instanceof \Zend_Db_Expr) { + $field = $column; + } else { + $connection = $this->getSelect()->getConnection(); + if (empty($correlationName)) { + $field = $connection->quoteColumnAs($column, $alias, true); + } else { + $field = $connection->quoteColumnAs([$correlationName, $column], $alias, true); + } + } + $this->getSelect()->where("{$field} = ?", $condition); + break; + } + } + + return $this; + } + + /** + * Add tier price attribute to filter + * + * @param string $attribute + * @param array|null $condition + * @return $this + */ + private function addTierPriceAttributeToFilter($attribute, $condition) + { + $attrCode = $attribute; + $connection = $this->getConnection(); + $attrTable = $this->_getAttributeTableAlias($attrCode); + $entity = $this->getEntity(); + $fKey = 'e.' . $this->getEntityPkName($entity); + $pKey = $attrTable . '.' . $this->getEntityPkName($entity); + $attribute = $entity->getAttribute($attrCode); + $attrFieldName = $attrTable . '.value'; + $fKey = $connection->quoteColumnAs($fKey, null); + $pKey = $connection->quoteColumnAs($pKey, null); + + $condArr = ["{$pKey} = {$fKey}"]; + $this->getSelect()->join( + [$attrTable => $this->getTable('catalog_product_entity_tier_price')], + '(' . implode(') AND (', $condArr) . ')', + [$attrCode => $attrFieldName] + ); + $this->removeAttributeToSelect($attrCode); + $this->_filterAttributes[$attrCode] = $attribute->getId(); + $this->_joinFields[$attrCode] = ['table' => '', 'field' => $attrFieldName]; + $field = $this->_getAttributeTableAlias($attrCode) . '.value'; + $conditionSql = $this->_getConditionSql($field, $condition); + $this->getSelect()->where($conditionSql, null, Select::TYPE_CONDITION); + $this->_totalRecords = null; + + return $this; + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php index 6ac7a8551fd99..19be025b5e40f 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php @@ -5,6 +5,9 @@ */ namespace Magento\Catalog\Model\ResourceModel\Product; +/** + * Collection test + */ class CollectionTest extends \PHPUnit\Framework\TestCase { /** @@ -228,4 +231,17 @@ public function testAddAttributeToFilterAffectsGetSize() $this->collection->addAttributeToFilter('sku', 'Product1'); $this->assertEquals(1, $this->collection->getSize()); } + + /** + * Add tier price attribute filter to collection + * + * @magentoDataFixture Magento/Catalog/Model/ResourceModel/_files/few_simple_products.php + * @magentoDataFixture Magento/Catalog/Model/ResourceModel/_files/product_simple.php + */ + public function testAddAttributeTierPriceToFilter() + { + $this->assertEquals(11, $this->collection->getSize()); + $this->collection->addAttributeToFilter('tier_price', ['gt' => 0]); + $this->assertEquals(1, $this->collection->getSize()); + } } From e3bae73e115bc468407df415644b348f26d3ca1b Mon Sep 17 00:00:00 2001 From: Slava Mankivski <mankivsk@adobe.com> Date: Mon, 13 May 2019 11:24:28 -0500 Subject: [PATCH 1260/1295] MAGETWO-99645: Integration of Allure reports in Magento 2.2 --- .../Model/Product/Type/ConfigurableTest.php | 20 ++++--- .../SalesOrderBeforeSaveObserverTest.php | 2 +- dev/tests/api-functional/phpunit.xml.dist | 53 +++++++++++++++++++ .../framework/tests/unit/phpunit.xml.dist | 28 ++++++++++ dev/tests/integration/phpunit.xml.dist | 47 ++++++++++++++++ ...eWithOptionsTierPriceWithDimensionTest.php | 4 +- .../Model/Export/ProductTest.php | 6 +-- .../Test/Integrity/StaticFilesTest.php | 2 +- .../framework/tests/unit/phpunit.xml.dist | 28 ++++++++++ dev/tests/static/phpunit-all.xml.dist | 8 +++ dev/tests/static/phpunit.xml.dist | 8 +++ .../Magento/Test/Legacy/_files/words_ce.xml | 2 +- dev/tests/unit/phpunit.xml.dist | 20 +++++++ 13 files changed, 209 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php index d1cf77f03a7bd..c351d12fa813d 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php @@ -11,10 +11,13 @@ use Magento\Catalog\Model\Product\Configuration\Item\Option\OptionInterface; use Magento\ConfigurableProduct\Model\Product\Type\Collection\SalableProcessor; use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute; use Magento\ConfigurableProduct\Model\Product\Type\Configurable\AttributeFactory; use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection; use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\CollectionFactory; use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Product\Collection as ProductCollection; +use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Product\CollectionFactory + as ProductCollectionFactory; use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\ConfigurableFactory; use Magento\Customer\Model\Session; use Magento\Eav\Model\Entity\Attribute\Frontend\AbstractFrontend; @@ -27,7 +30,6 @@ * @SuppressWarnings(PHPMD.LongVariable) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.TooManyFields) - * @codingStandardsIgnoreFile */ class ConfigurableTest extends \PHPUnit\Framework\TestCase { @@ -154,8 +156,7 @@ protected function setUp() ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $this->productCollectionFactory = $this->getMockBuilder( - \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Product\CollectionFactory::class) + $this->productCollectionFactory = $this->getMockBuilder(ProductCollectionFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); @@ -281,8 +282,7 @@ public function testSave() $product->expects($this->atLeastOnce()) ->method('getData') ->willReturnMap($dataMap); - $attribute = $this->getMockBuilder( - \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute::class) + $attribute = $this->getMockBuilder(Attribute::class) ->disableOriginalConstructor() ->setMethods(['addData', 'setStoreId', 'setProductId', 'save', '__wakeup', '__sleep']) ->getMock(); @@ -464,8 +464,7 @@ public function testGetConfigurableAttributesAsArray($productStore) $eavAttribute->expects($this->once())->method('getSource')->willReturn($attributeSource); $eavAttribute->expects($this->atLeastOnce())->method('getStoreLabel')->willReturn('Store Label'); - $attribute = $this->getMockBuilder( - \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute::class) + $attribute = $this->getMockBuilder(Attribute::class) ->disableOriginalConstructor() ->setMethods(['getProductAttribute', '__wakeup', '__sleep']) ->getMock(); @@ -524,7 +523,7 @@ public function testGetConfigurableAttributesNewProduct() $this->assertEquals([], $this->model->getConfigurableAttributes($product)); } - public function testGetConfigurableAttributes() + public function testGetConfigurableAttributes() { $configurableAttributes = '_cache_instance_configurable_attributes'; @@ -591,8 +590,7 @@ public function testHasOptionsConfigurableAttribute() ->setMethods(['__wakeup', 'getAttributeCode', 'getOptions', 'hasData', 'getData']) ->disableOriginalConstructor() ->getMock(); - $attributeMock = $this->getMockBuilder( - \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute::class) + $attributeMock = $this->getMockBuilder(Attribute::class) ->disableOriginalConstructor() ->getMock(); @@ -698,7 +696,7 @@ function ($value) { ->disableOriginalConstructor() ->getMock(); $usedAttributeMock = $this->getMockBuilder( - \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute::class + Attribute::class ) ->setMethods(['getProductAttribute']) ->disableOriginalConstructor() diff --git a/app/code/Magento/Payment/Test/Unit/Observer/SalesOrderBeforeSaveObserverTest.php b/app/code/Magento/Payment/Test/Unit/Observer/SalesOrderBeforeSaveObserverTest.php index 63ca1b47dc08c..75916dd2ea99b 100644 --- a/app/code/Magento/Payment/Test/Unit/Observer/SalesOrderBeforeSaveObserverTest.php +++ b/app/code/Magento/Payment/Test/Unit/Observer/SalesOrderBeforeSaveObserverTest.php @@ -160,7 +160,7 @@ public function testSalesOrderBeforeSaveSetForced() * The method should check that the payment is available, as this is not always the case. * * @expectedException \Magento\Framework\Exception\LocalizedException - * @exceptedExceptionMessage Please provide payment for the order. + * @expectedExceptionMessage Please provide payment for the order. */ public function testDoesNothingWhenNoPaymentIsAvailable() { diff --git a/dev/tests/api-functional/phpunit.xml.dist b/dev/tests/api-functional/phpunit.xml.dist index 88919a5596d5d..5d1126c40c0b3 100644 --- a/dev/tests/api-functional/phpunit.xml.dist +++ b/dev/tests/api-functional/phpunit.xml.dist @@ -64,5 +64,58 @@ <!-- Test listeners --> <listeners> <listener class="Magento\TestFramework\Event\PhpUnit"/> + <listener class="Yandex\Allure\Adapter\AllureAdapter" file="../../../vendor/allure-framework/allure-phpunit/src/Yandex/Allure/Adapter/AllureAdapter.php"> + <arguments> + <string>var/allure-results</string> <!-- XML files output folder --> + <boolean>true</boolean> <!-- Whether to delete previous results on rerun --> + <array> <!-- A list of custom annotations to ignore (optional) --> + <element key="codingStandardsIgnoreStart"> + <string>codingStandardsIgnoreStart</string> + </element> + <element key="codingStandardsIgnoreEnd"> + <string>codingStandardsIgnoreEnd</string> + </element> + <element key="expectedExceptionMessageRegExp"> + <string>expectedExceptionMessageRegExp</string> + </element> + <element key="magentoAdminConfigFixture"> + <string>magentoAdminConfigFixture</string> + </element> + <element key="magentoAppArea"> + <string>magentoAppArea</string> + </element> + <element key="magentoAppIsolation"> + <string>magentoAppIsolation</string> + </element> + <element key="magentoCache"> + <string>magentoCache</string> + </element> + <element key="magentoComponentsDir"> + <string>magentoComponentsDir</string> + </element> + <element key="magentoConfigFixture"> + <string>magentoConfigFixture</string> + </element> + <element key="magentoDataFixture"> + <string>magentoDataFixture</string> + </element> + <element key="magentoDataFixtureBeforeTransaction"> + <string>magentoDataFixtureBeforeTransaction</string> + </element> + <element key="magentoDbIsolation"> + <string>magentoDbIsolation</string> + </element> + <element key="magentoIndexerDimensionMode"> + <string>magentoIndexerDimensionMode</string> + </element> + <element key="magentoApiDataFixture"> + <string>magentoApiDataFixture</string> + </element> + <element key="Override"> + <string>Override</string> + </element> + </array> + </arguments> + </listener> </listeners> </phpunit> diff --git a/dev/tests/integration/framework/tests/unit/phpunit.xml.dist b/dev/tests/integration/framework/tests/unit/phpunit.xml.dist index 6152ce4d89495..e4778a63ebc35 100644 --- a/dev/tests/integration/framework/tests/unit/phpunit.xml.dist +++ b/dev/tests/integration/framework/tests/unit/phpunit.xml.dist @@ -20,4 +20,32 @@ <php> <ini name="date.timezone" value="America/Los_Angeles"/> </php> + <listeners> + <listener class="Yandex\Allure\Adapter\AllureAdapter"> + <arguments> + <string>var/allure-results</string> <!-- XML files output directory --> + <boolean>true</boolean> <!-- Whether to delete previous results on rerun --> + <array> <!-- A list of custom annotations to ignore (optional) --> + <element key="magentoAdminConfigFixture"> + <string>magentoAdminConfigFixture</string> + </element> + <element key="magentoAppIsolation"> + <string>magentoAppIsolation</string> + </element> + <element key="magentoComponentsDir"> + <string>magentoComponentsDir</string> + </element> + <element key="magentoConfigFixture"> + <string>magentoConfigFixture</string> + </element> + <element key="@magentoDbIsolation"> + <string>magentoDataFixture</string> + </element> + <element key="magentoDbIsolation"> + <string>magentoDbIsolation</string> + </element> + </array> + </arguments> + </listener> + </listeners> </phpunit> diff --git a/dev/tests/integration/phpunit.xml.dist b/dev/tests/integration/phpunit.xml.dist index 9c4b0bd19d25c..164849c1c35f2 100644 --- a/dev/tests/integration/phpunit.xml.dist +++ b/dev/tests/integration/phpunit.xml.dist @@ -77,5 +77,52 @@ <listeners> <listener class="Magento\TestFramework\Event\PhpUnit"/> <listener class="Magento\TestFramework\ErrorLog\Listener"/> + <listener class="Yandex\Allure\Adapter\AllureAdapter"> + <arguments> + <string>var/allure-results</string> <!-- XML files output directory --> + <boolean>true</boolean> <!-- Whether to delete previous results on rerun --> + <array> <!-- A list of custom annotations to ignore (optional) --> + <element key="codingStandardsIgnoreStart"> + <string>codingStandardsIgnoreStart</string> + </element> + <element key="codingStandardsIgnoreEnd"> + <string>codingStandardsIgnoreEnd</string> + </element> + <element key="expectedExceptionMessageRegExp"> + <string>expectedExceptionMessageRegExp</string> + </element> + <element key="magentoAdminConfigFixture"> + <string>magentoAdminConfigFixture</string> + </element> + <element key="magentoAppArea"> + <string>magentoAppArea</string> + </element> + <element key="magentoAppIsolation"> + <string>magentoAppIsolation</string> + </element> + <element key="magentoCache"> + <string>magentoCache</string> + </element> + <element key="magentoComponentsDir"> + <string>magentoComponentsDir</string> + </element> + <element key="magentoConfigFixture"> + <string>magentoConfigFixture</string> + </element> + <element key="magentoDataFixture"> + <string>magentoDataFixture</string> + </element> + <element key="magentoDataFixtureBeforeTransaction"> + <string>magentoDataFixtureBeforeTransaction</string> + </element> + <element key="magentoDbIsolation"> + <string>magentoDbIsolation</string> + </element> + <element key="magentoIndexerDimensionMode"> + <string>magentoIndexerDimensionMode</string> + </element> + </array> + </arguments> + </listener> </listeners> </phpunit> diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Price/SimpleWithOptionsTierPriceWithDimensionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Price/SimpleWithOptionsTierPriceWithDimensionTest.php index e70fa8f52d269..814f814cdc1fd 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Price/SimpleWithOptionsTierPriceWithDimensionTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Price/SimpleWithOptionsTierPriceWithDimensionTest.php @@ -11,7 +11,7 @@ use Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory; use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; use Magento\Catalog\Pricing\Price\TierPrice; -use Magento\Customer\Model\Group; +use Magento\Customer\Model\Group as CustomerGroup; /** * @group indexer_dimension @@ -54,7 +54,7 @@ public function testTierPrice() $tierPrice = $this->objectManager->create(ProductTierPriceInterfaceFactory::class) ->create(); - $tierPrice->setCustomerGroupId(Group::CUST_GROUP_ALL); + $tierPrice->setCustomerGroupId(CustomerGroup::CUST_GROUP_ALL); $tierPrice->setQty(1.00); $tierPrice->setValue($tierPriceValue); $tierPriceManagement = $this->objectManager->create(ScopedProductTierPriceManagementInterface::class); diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php index 47d74fcfd6719..482cfd13391ca 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php @@ -70,7 +70,7 @@ protected function setUp() /** * @magentoDataFixture Magento/CatalogImportExport/_files/product_export_data.php - * @magentoDbIsolationEnabled + * @magentoDbIsolation enabled */ public function testExport() { @@ -95,7 +95,7 @@ public function testExport() /** * @magentoDataFixture Magento/CatalogImportExport/_files/product_export_data_special_chars.php - * @magentoDbIsolationEnabled + * @magentoDbIsolation enabled */ public function testExportSpecialChars() { @@ -110,7 +110,7 @@ public function testExportSpecialChars() /** * @magentoDataFixture Magento/CatalogImportExport/_files/product_export_with_product_links_data.php - * @magentoDbIsolationEnabled + * @magentoDbIsolation enabled */ public function testExportWithProductLinks() { diff --git a/dev/tests/integration/testsuite/Magento/Test/Integrity/StaticFilesTest.php b/dev/tests/integration/testsuite/Magento/Test/Integrity/StaticFilesTest.php index 46036faa99fca..ce3be1fe99c86 100644 --- a/dev/tests/integration/testsuite/Magento/Test/Integrity/StaticFilesTest.php +++ b/dev/tests/integration/testsuite/Magento/Test/Integrity/StaticFilesTest.php @@ -51,7 +51,7 @@ protected function setUp() /** * Scan references to files from other static files and assert they are correct * - * The CSS or LESS files may refer to other resources using @import or url() notation + * The CSS or LESS files may refer to other resources using `import` or url() notation * We want to check integrity of all these references * Note that the references may have syntax specific to the Magento preprocessing subsystem * diff --git a/dev/tests/static/framework/tests/unit/phpunit.xml.dist b/dev/tests/static/framework/tests/unit/phpunit.xml.dist index 546a437331e0a..918f758d2730e 100644 --- a/dev/tests/static/framework/tests/unit/phpunit.xml.dist +++ b/dev/tests/static/framework/tests/unit/phpunit.xml.dist @@ -19,4 +19,32 @@ <php> <ini name="date.timezone" value="America/Los_Angeles"/> </php> + <listeners> + <listener class="Yandex\Allure\Adapter\AllureAdapter"> + <arguments> + <string>var/allure-results</string> <!-- XML files output directory --> + <boolean>true</boolean> <!-- Whether to delete previous results on rerun --> + <array> <!-- A list of custom annotations to ignore (optional) --> + <element key="magentoAdminConfigFixture"> + <string>magentoAdminConfigFixture</string> + </element> + <element key="magentoAppIsolation"> + <string>magentoAppIsolation</string> + </element> + <element key="magentoComponentsDir"> + <string>magentoComponentsDir</string> + </element> + <element key="magentoConfigFixture"> + <string>magentoConfigFixture</string> + </element> + <element key="@magentoDbIsolation"> + <string>magentoDataFixture</string> + </element> + <element key="magentoDbIsolation"> + <string>magentoDbIsolation</string> + </element> + </array> + </arguments> + </listener> + </listeners> </phpunit> diff --git a/dev/tests/static/phpunit-all.xml.dist b/dev/tests/static/phpunit-all.xml.dist index 131cfa6a5ff60..94c57af1968af 100644 --- a/dev/tests/static/phpunit-all.xml.dist +++ b/dev/tests/static/phpunit-all.xml.dist @@ -22,4 +22,12 @@ <!-- TESTCODESTYLE_IS_FULL_SCAN - specify if full scan should be performed for test code style test --> <const name="TESTCODESTYLE_IS_FULL_SCAN" value="1"/> </php> + <listeners> + <listener class="Yandex\Allure\Adapter\AllureAdapter"> + <arguments> + <string>var/allure-results</string> <!-- XML files output directory --> + <boolean>true</boolean> <!-- Whether to delete previous results on rerun --> + </arguments> + </listener> + </listeners> </phpunit> diff --git a/dev/tests/static/phpunit.xml.dist b/dev/tests/static/phpunit.xml.dist index 1ae9342598e4b..4eec18f7bf8c5 100644 --- a/dev/tests/static/phpunit.xml.dist +++ b/dev/tests/static/phpunit.xml.dist @@ -34,4 +34,12 @@ <!-- TESTS_COMPOSER_PATH - specify the path to composer binary, if a relative reference cannot be resolved --> <!--<const name="TESTS_COMPOSER_PATH" value="/usr/local/bin/composer"/>--> </php> + <listeners> + <listener class="Yandex\Allure\Adapter\AllureAdapter"> + <arguments> + <string>var/allure-results</string> <!-- XML files output directory --> + <boolean>true</boolean> <!-- Whether to delete previous results on rerun --> + </arguments> + </listener> + </listeners> </phpunit> diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/words_ce.xml b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/words_ce.xml index 62406b6cbd30a..520ea38840d89 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/words_ce.xml +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/words_ce.xml @@ -53,7 +53,7 @@ <word>overriden</word> </item> <item> - <path>composer.lock</path> + <path>dev/composer.lock</path> </item> <item> <path>app/code/Magento/Customer/view/frontend/web/js/zxcvbn.js</path> diff --git a/dev/tests/unit/phpunit.xml.dist b/dev/tests/unit/phpunit.xml.dist index b35b53a68b780..1b0b0a52b6f4b 100644 --- a/dev/tests/unit/phpunit.xml.dist +++ b/dev/tests/unit/phpunit.xml.dist @@ -39,6 +39,26 @@ </whitelist> </filter> <listeners> + <listener class="Yandex\Allure\Adapter\AllureAdapter"> + <arguments> + <string>var/allure-results</string> <!-- XML files output directory --> + <boolean>true</boolean> <!-- Whether to delete previous results on rerun --> + <array> <!-- A list of custom annotations to ignore (optional) --> + <element key="codingStandardsIgnoreStart"> + <string>codingStandardsIgnoreStart</string> + </element> + <element key="codingStandardsIgnoreEnd"> + <string>codingStandardsIgnoreEnd</string> + </element> + <element key="cover"> + <string>cover</string> + </element> + <element key="expectedExceptionMessageRegExp"> + <string>expectedExceptionMessageRegExp</string> + </element> + </array> + </arguments> + </listener> <listener class="Magento\Framework\TestFramework\Unit\Listener\ReplaceObjectManager"/> </listeners> <logging> From 0cd58df39f84f48e0dfe3c2f1f585a4e64cf0db3 Mon Sep 17 00:00:00 2001 From: Slava Mankivski <mankivsk@adobe.com> Date: Mon, 13 May 2019 13:28:25 -0500 Subject: [PATCH 1261/1295] MAGETWO-99645: Integration of Allure reports in Magento 2.2 --- composer.json | 1 + composer.lock | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 20a4b449d3ef2..5aee77e9b7f0d 100644 --- a/composer.json +++ b/composer.json @@ -74,6 +74,7 @@ "ramsey/uuid": "~3.7.3" }, "require-dev": { + "allure-framework/allure-phpunit": "~1.2.0", "magento/magento2-functional-testing-framework": "2.4.0", "phpunit/phpunit": "~6.2.0", "squizlabs/php_codesniffer": "3.2.2", diff --git a/composer.lock b/composer.lock index 0d33f64678e7f..cfb522e93182c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4f20e93e274aba4b8965b3da66b59a1b", + "content-hash": "dd391f1f6e3f43c8dfca7382fdff369c", "packages": [ { "name": "braintree/braintree_php", @@ -4224,6 +4224,56 @@ ], "time": "2016-12-07T12:15:46+00:00" }, + { + "name": "allure-framework/allure-phpunit", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/allure-framework/allure-phpunit.git", + "reference": "45504aeba41304cf155a898fa9ac1aae79f4a089" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/allure-framework/allure-phpunit/zipball/45504aeba41304cf155a898fa9ac1aae79f4a089", + "reference": "45504aeba41304cf155a898fa9ac1aae79f4a089", + "shasum": "" + }, + "require": { + "allure-framework/allure-php-api": "~1.1.0", + "mikey179/vfsstream": "1.*", + "php": ">=7.0.0", + "phpunit/phpunit": ">=6.0.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Yandex": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Ivan Krutov", + "email": "vania-pooh@yandex-team.ru", + "role": "Developer" + } + ], + "description": "A PHPUnit adapter for Allure report.", + "homepage": "http://allure.qatools.ru/", + "keywords": [ + "allure", + "attachments", + "cases", + "phpunit", + "report", + "steps", + "testing" + ], + "time": "2017-11-03T13:08:21+00:00" + }, { "name": "behat/gherkin", "version": "v4.6.0", @@ -6306,6 +6356,52 @@ ], "time": "2019-04-29T20:56:26+00:00" }, + { + "name": "mikey179/vfsStream", + "version": "v1.6.6", + "source": { + "type": "git", + "url": "https://github.com/bovigo/vfsStream.git", + "reference": "095238a0711c974ae5b4ebf4c4534a23f3f6c99d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bovigo/vfsStream/zipball/095238a0711c974ae5b4ebf4c4534a23f3f6c99d", + "reference": "095238a0711c974ae5b4ebf4c4534a23f3f6c99d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-0": { + "org\\bovigo\\vfs\\": "src/main/php" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Frank Kleine", + "homepage": "http://frankkleine.de/", + "role": "Developer" + } + ], + "description": "Virtual file system to mock the real file system in unit tests.", + "homepage": "http://vfs.bovigo.org/", + "time": "2019-04-08T13:54:32+00:00" + }, { "name": "moontoast/math", "version": "1.1.2", From 65175ae189be9a22f451e5cf36c768dc0b963570 Mon Sep 17 00:00:00 2001 From: Roman Lytvynenko <lytvynen@adobe.com> Date: Mon, 13 May 2019 13:39:37 -0500 Subject: [PATCH 1262/1295] MAGETWO-99516: Account lock status not showing correctly in Customer Grid --- .../Customer/Model/CustomerAuthUpdate.php | 28 ++++-- .../Unit/Model/CustomerAuthUpdateTest.php | 37 ++++++-- .../CollectionReindexOnAccountLockTest.php | 87 +++++++++++++++++++ 3 files changed, 137 insertions(+), 15 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/Grid/CollectionReindexOnAccountLockTest.php diff --git a/app/code/Magento/Customer/Model/CustomerAuthUpdate.php b/app/code/Magento/Customer/Model/CustomerAuthUpdate.php index 06de649524e71..a805c1957df99 100644 --- a/app/code/Magento/Customer/Model/CustomerAuthUpdate.php +++ b/app/code/Magento/Customer/Model/CustomerAuthUpdate.php @@ -6,31 +6,43 @@ namespace Magento\Customer\Model; +use Magento\Customer\Model\ResourceModel\Customer as CustomerResourceModel; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\NoSuchEntityException; + /** * Customer Authentication update model. */ class CustomerAuthUpdate { /** - * @var \Magento\Customer\Model\CustomerRegistry + * @var CustomerRegistry */ protected $customerRegistry; /** - * @var \Magento\Customer\Model\ResourceModel\Customer + * @var CustomerResourceModel */ protected $customerResourceModel; /** - * @param \Magento\Customer\Model\CustomerRegistry $customerRegistry - * @param \Magento\Customer\Model\ResourceModel\Customer $customerResourceModel + * @var Customer + */ + private $customerModel; + + /** + * @param CustomerRegistry $customerRegistry + * @param CustomerResourceModel $customerResourceModel + * @param Customer|null $customerModel */ public function __construct( - \Magento\Customer\Model\CustomerRegistry $customerRegistry, - \Magento\Customer\Model\ResourceModel\Customer $customerResourceModel + CustomerRegistry $customerRegistry, + CustomerResourceModel $customerResourceModel, + Customer $customerModel = null ) { $this->customerRegistry = $customerRegistry; $this->customerResourceModel = $customerResourceModel; + $this->customerModel = $customerModel ?: ObjectManager::getInstance()->get(Customer::class); } /** @@ -38,6 +50,7 @@ public function __construct( * * @param int $customerId * @return $this + * @throws NoSuchEntityException */ public function saveAuth($customerId) { @@ -53,6 +66,9 @@ public function saveAuth($customerId) $this->customerResourceModel->getConnection()->quoteInto('entity_id = ?', $customerId) ); + $this->customerResourceModel->load($this->customerModel, $customerId); + $this->customerModel->reindex(); + return $this; } } diff --git a/app/code/Magento/Customer/Test/Unit/Model/CustomerAuthUpdateTest.php b/app/code/Magento/Customer/Test/Unit/Model/CustomerAuthUpdateTest.php index a1a243066bb7d..81a612c519f52 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/CustomerAuthUpdateTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/CustomerAuthUpdateTest.php @@ -5,7 +5,14 @@ */ namespace Magento\Customer\Test\Unit\Model; +use Magento\Customer\Model\Customer as CustomerModel; use Magento\Customer\Model\CustomerAuthUpdate; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Customer\Model\Data\CustomerSecure; +use Magento\Customer\Model\ResourceModel\Customer as CustomerResourceModel; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; /** * Class CustomerAuthUpdateTest @@ -18,17 +25,22 @@ class CustomerAuthUpdateTest extends \PHPUnit\Framework\TestCase protected $model; /** - * @var \Magento\Customer\Model\CustomerRegistry|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerRegistry|\PHPUnit_Framework_MockObject_MockObject */ protected $customerRegistry; /** - * @var \Magento\Customer\Model\ResourceModel\Customer|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerResourceModel|\PHPUnit_Framework_MockObject_MockObject */ protected $customerResourceModel; /** - * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + * @var CustomerModel|\PHPUnit_Framework_MockObject_MockObject + */ + protected $customerModel; + + /** + * @var ObjectManager */ protected $objectManager; @@ -37,32 +49,36 @@ class CustomerAuthUpdateTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { - $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->objectManager = new ObjectManager($this); $this->customerRegistry = - $this->createMock(\Magento\Customer\Model\CustomerRegistry::class); + $this->createMock(CustomerRegistry::class); $this->customerResourceModel = - $this->createMock(\Magento\Customer\Model\ResourceModel\Customer::class); + $this->createMock(CustomerResourceModel::class); + $this->customerModel = + $this->createMock(CustomerModel::class); $this->model = $this->objectManager->getObject( - \Magento\Customer\Model\CustomerAuthUpdate::class, + CustomerAuthUpdate::class, [ 'customerRegistry' => $this->customerRegistry, 'customerResourceModel' => $this->customerResourceModel, + 'customerModel' => $this->customerModel ] ); } /** * test SaveAuth + * @throws NoSuchEntityException */ public function testSaveAuth() { $customerId = 1; - $customerSecureMock = $this->createMock(\Magento\Customer\Model\Data\CustomerSecure::class); + $customerSecureMock = $this->createMock(CustomerSecure::class); - $dbAdapter = $this->createMock(\Magento\Framework\DB\Adapter\AdapterInterface::class); + $dbAdapter = $this->createMock(AdapterInterface::class); $this->customerRegistry->expects($this->once()) ->method('retrieveSecureData') @@ -98,6 +114,9 @@ public function testSaveAuth() $customerId ); + $this->customerModel->expects($this->once()) + ->method('reindex'); + $this->model->saveAuth($customerId); } } diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/Grid/CollectionReindexOnAccountLockTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/Grid/CollectionReindexOnAccountLockTest.php new file mode 100644 index 0000000000000..68e61879f9878 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/Grid/CollectionReindexOnAccountLockTest.php @@ -0,0 +1,87 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Customer\Model\ResourceModel\Grid; + +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Framework\Exception\InvalidEmailOrPasswordException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Indexer\TestCase; +use Magento\Tests\NamingConvention\true\mixed; + +/** + * Test if customer account lock on too many failed authentication attempts triggers customer grid reindex + * + * @SuppressWarnings(PHPMD) + */ +class CollectionReindexOnAccountLockTest extends TestCase +{ + /** + * Trigger customer account lock by making 10 failed authentication attempts + */ + private function lockCustomerAccountWithInvalidAuthentications() + { + /** @var AccountManagementInterface */ + $accountManagement = Bootstrap::getObjectManager()->create(AccountManagementInterface::class); + + for ($i = 0; $i < 10; $i++) { + try { + $accountManagement->authenticate('customer@example.com', 'wrongPassword'); + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock + } catch (InvalidEmailOrPasswordException $e) { + } + } + } + + /** + * @return mixed + * @throws NoSuchEntityException + */ + private function getCustomerLockExpire() + { + /** @var CustomerRegistry $customerRegistry */ + $customerRegistry = Bootstrap::getObjectManager()->create(CustomerRegistry::class); + $customerModel = $customerRegistry->retrieve(1); + $this->assertNotEmpty($customerModel); + + return $customerModel->getData('lock_expires'); + } + + /** + * @return mixed + */ + private function getCustomerGridLockExpire() + { + /** @var Collection */ + $gridCustomerCollection = Bootstrap::getObjectManager()->create(Collection::class); + $gridCustomerItem = $gridCustomerCollection->getItemById(1); + $this->assertNotEmpty($gridCustomerItem); + + return $gridCustomerItem->getData('lock_expires'); + } + + /** + * Test if customer account lock on too many failed authentication attempts triggers customer grid reindex + * + * @magentoDataFixture Magento/Customer/_files/customer.php + */ + public function testCustomerAccountReindexOnLock() + { + $this->assertSame( + $this->getCustomerGridLockExpire(), + $this->getCustomerLockExpire() + ); + + $this->lockCustomerAccountWithInvalidAuthentications(); + + $this->assertSame( + $this->getCustomerGridLockExpire(), + $this->getCustomerLockExpire() + ); + } +} From f6d8dddd90feecdfc576a67c7651c671b6a068a1 Mon Sep 17 00:00:00 2001 From: Roman Lytvynenko <lytvynen@adobe.com> Date: Mon, 13 May 2019 14:49:13 -0500 Subject: [PATCH 1263/1295] MAGETWO-99516: Account lock status not showing correctly in Customer Grid --- .../ResourceModel/Grid/CollectionReindexOnAccountLockTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/Grid/CollectionReindexOnAccountLockTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/Grid/CollectionReindexOnAccountLockTest.php index 68e61879f9878..3f3884464763e 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/Grid/CollectionReindexOnAccountLockTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/Grid/CollectionReindexOnAccountLockTest.php @@ -69,6 +69,8 @@ private function getCustomerGridLockExpire() * Test if customer account lock on too many failed authentication attempts triggers customer grid reindex * * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled */ public function testCustomerAccountReindexOnLock() { From d4553a2234bd8c35374f2b474864c7f4f826fb65 Mon Sep 17 00:00:00 2001 From: Serhiy Zhovnir <s.zhovnir@atwix.com> Date: Mon, 13 May 2019 22:57:06 +0300 Subject: [PATCH 1264/1295] #22786 Add dependency for UPS required fields to avoid validation for these fields if UPS is not active --- app/code/Magento/Ups/etc/adminhtml/system.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/code/Magento/Ups/etc/adminhtml/system.xml b/app/code/Magento/Ups/etc/adminhtml/system.xml index f427c5960123f..8b9dc30a0188b 100644 --- a/app/code/Magento/Ups/etc/adminhtml/system.xml +++ b/app/code/Magento/Ups/etc/adminhtml/system.xml @@ -13,6 +13,9 @@ <field id="access_license_number" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Access License Number</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> + <depends> + <field id="carriers/ups/active">1</field> + </depends> </field> <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Enabled for Checkout</label> @@ -84,6 +87,9 @@ <field id="password" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Password</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> + <depends> + <field id="carriers/ups/active">1</field> + </depends> </field> <field id="pickup" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Pickup Method</label> @@ -113,6 +119,9 @@ <field id="username" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> <label>User ID</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> + <depends> + <field id="carriers/ups/active">1</field> + </depends> </field> <field id="negotiated_active" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Enable Negotiated Rates</label> From 80e177e8d45cba5481d5d3b81d36582f213928ce Mon Sep 17 00:00:00 2001 From: Slava Mankivski <mankivsk@adobe.com> Date: Mon, 13 May 2019 16:20:26 -0500 Subject: [PATCH 1265/1295] MAGETWO-99645: Integration of Allure reports in Magento 2.2 --- dev/tests/api-functional/phpunit.xml.dist | 5 ++++- dev/tests/integration/phpunit.xml.dist | 6 ++++++ dev/tests/unit/phpunit.xml.dist | 3 +++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/dev/tests/api-functional/phpunit.xml.dist b/dev/tests/api-functional/phpunit.xml.dist index 5d1126c40c0b3..2e05c1418c960 100644 --- a/dev/tests/api-functional/phpunit.xml.dist +++ b/dev/tests/api-functional/phpunit.xml.dist @@ -64,11 +64,14 @@ <!-- Test listeners --> <listeners> <listener class="Magento\TestFramework\Event\PhpUnit"/> - <listener class="Yandex\Allure\Adapter\AllureAdapter" file="../../../vendor/allure-framework/allure-phpunit/src/Yandex/Allure/Adapter/AllureAdapter.php"> + <listener class="Yandex\Allure\Adapter\AllureAdapter"> <arguments> <string>var/allure-results</string> <!-- XML files output folder --> <boolean>true</boolean> <!-- Whether to delete previous results on rerun --> <array> <!-- A list of custom annotations to ignore (optional) --> + <element key="codingStandardsIgnoreFile"> + <string>codingStandardsIgnoreFile</string> + </element> <element key="codingStandardsIgnoreStart"> <string>codingStandardsIgnoreStart</string> </element> diff --git a/dev/tests/integration/phpunit.xml.dist b/dev/tests/integration/phpunit.xml.dist index 164849c1c35f2..8f54dbfc4109d 100644 --- a/dev/tests/integration/phpunit.xml.dist +++ b/dev/tests/integration/phpunit.xml.dist @@ -82,6 +82,9 @@ <string>var/allure-results</string> <!-- XML files output directory --> <boolean>true</boolean> <!-- Whether to delete previous results on rerun --> <array> <!-- A list of custom annotations to ignore (optional) --> + <element key="codingStandardsIgnoreFile"> + <string>codingStandardsIgnoreFile</string> + </element> <element key="codingStandardsIgnoreStart"> <string>codingStandardsIgnoreStart</string> </element> @@ -121,6 +124,9 @@ <element key="magentoIndexerDimensionMode"> <string>magentoIndexerDimensionMode</string> </element> + <element key="security"> + <string>security</string> + </element> </array> </arguments> </listener> diff --git a/dev/tests/unit/phpunit.xml.dist b/dev/tests/unit/phpunit.xml.dist index 1b0b0a52b6f4b..d64f9f976b78d 100644 --- a/dev/tests/unit/phpunit.xml.dist +++ b/dev/tests/unit/phpunit.xml.dist @@ -44,6 +44,9 @@ <string>var/allure-results</string> <!-- XML files output directory --> <boolean>true</boolean> <!-- Whether to delete previous results on rerun --> <array> <!-- A list of custom annotations to ignore (optional) --> + <element key="codingStandardsIgnoreFile"> + <string>codingStandardsIgnoreFile</string> + </element> <element key="codingStandardsIgnoreStart"> <string>codingStandardsIgnoreStart</string> </element> From b6910d238ab2d83f49bd364677b2fbfacc472c59 Mon Sep 17 00:00:00 2001 From: Slava Mankivski <mankivsk@adobe.com> Date: Mon, 13 May 2019 17:30:09 -0500 Subject: [PATCH 1266/1295] MAGETWO-99645: Integration of Allure reports in Magento 2.2 --- .../Unit/Model/Product/ProductList/ToolbarMemorizerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/ProductList/ToolbarMemorizerTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/ProductList/ToolbarMemorizerTest.php index 5cb341a36b4cc..699931b5fe89d 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/ProductList/ToolbarMemorizerTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/ProductList/ToolbarMemorizerTest.php @@ -197,7 +197,7 @@ public function getMemorizedDataProvider(): array /** * Test method isMemorizingAllowed. * - * @aram bool|null $variableValue + * @param bool|null $variableValue * @param bool $flag * @param bool $expected * @return void From fe67e7b6757dc86b57968d1803059f130a19f367 Mon Sep 17 00:00:00 2001 From: Dmytro Yushkin <dyushkin@adobe.com> Date: Mon, 13 May 2019 13:54:05 -0500 Subject: [PATCH 1267/1295] MAGETWO-99622: Naming product attributes as container_attributename and attributename and placing them one after another causes error --- .../Adminhtml/Product/Attribute/Save.php | 20 ----- .../Magento/Eav/Model/Entity/Attribute.php | 16 ---- .../Eav/Model/Validator/Attribute/Code.php | 82 +++++++++++++++++++ .../Eav/Plugin/Model/Entity/Attribute.php | 43 ++++++++++ app/code/Magento/Eav/Setup/EavSetup.php | 12 ++- .../Model/Validator/Attribute/CodeTest.php | 64 +++++++++++++++ app/code/Magento/Eav/etc/di.xml | 1 + app/code/Magento/Eav/i18n/en_US.csv | 3 + .../Adminhtml/Product/AttributeTest.php | 4 +- 9 files changed, 204 insertions(+), 41 deletions(-) create mode 100644 app/code/Magento/Eav/Model/Validator/Attribute/Code.php create mode 100644 app/code/Magento/Eav/Plugin/Model/Entity/Attribute.php create mode 100644 app/code/Magento/Eav/Test/Unit/Model/Validator/Attribute/CodeTest.php diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php index da17ad3bb80b2..5b15024f21ce8 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php @@ -189,26 +189,6 @@ public function execute() ? $model->getAttributeCode() : $this->getRequest()->getParam('attribute_code'); $attributeCode = $attributeCode ?: $this->generateCode($this->getRequest()->getParam('frontend_label')[0]); - if (strlen($attributeCode) > 0) { - $validatorAttrCode = new \Zend_Validate_Regex( - ['pattern' => '/^[a-z\x{600}-\x{6FF}][a-z\x{600}-\x{6FF}_0-9]{0,30}$/u'] - ); - if (!$validatorAttrCode->isValid($attributeCode)) { - $this->messageManager->addErrorMessage( - __( - 'Attribute code "%1" is invalid. Please use only letters (a-z), ' . - 'numbers (0-9) or underscore(_) in this field, first character should be a letter.', - $attributeCode - ) - ); - - return $this->returnResult( - 'catalog/*/edit', - ['attribute_id' => $attributeId, '_current' => true], - ['error' => true] - ); - } - } $data['attribute_code'] = $attributeCode; //validate frontend_input diff --git a/app/code/Magento/Eav/Model/Entity/Attribute.php b/app/code/Magento/Eav/Model/Entity/Attribute.php index 740b29c9676f3..6c3caef7b3fd6 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute.php @@ -238,22 +238,6 @@ public function beforeSave() ); } - /** - * Check for maximum attribute_code length - */ - if (isset( - $this->_data['attribute_code'] - ) && !\Zend_Validate::is( - $this->_data['attribute_code'], - 'StringLength', - ['max' => self::ATTRIBUTE_CODE_MAX_LENGTH] - ) - ) { - throw new LocalizedException( - __('An attribute code must not be more than %1 characters.', self::ATTRIBUTE_CODE_MAX_LENGTH) - ); - } - $defaultValue = $this->getDefaultValue(); $hasDefaultValue = (string)$defaultValue != ''; diff --git a/app/code/Magento/Eav/Model/Validator/Attribute/Code.php b/app/code/Magento/Eav/Model/Validator/Attribute/Code.php new file mode 100644 index 0000000000000..91443fec97d1e --- /dev/null +++ b/app/code/Magento/Eav/Model/Validator/Attribute/Code.php @@ -0,0 +1,82 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Eav\Model\Validator\Attribute; + +use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier; +use Magento\Eav\Model\Entity\Attribute; +use Magento\Framework\Validator\AbstractValidator; +use Zend_Validate; +use Zend_Validate_Exception; + +class Code extends AbstractValidator +{ + /** + * Validation pattern for attribute code + */ + const VALIDATION_RULE_PATTERN = '/^[a-zA-Z]+[a-zA-Z0-9_]*$/u'; + + /** + * Returns true if and only if $value meets the validation requirements + * + * If $value fails validation, then this method returns false, and + * getMessages() will return an array of messages that explain why the + * validation failed. + * + * @param string $attributeCode + * @return boolean + * @throws Zend_Validate_Exception If validation of $attributeCode is impossible + */ + public function isValid($attributeCode): bool + { + $errorMessages = []; + /** + * Check attribute_code for allowed characters + */ + if (trim($attributeCode) + && !preg_match(self::VALIDATION_RULE_PATTERN, trim($attributeCode)) + ) { + $errorMessages[] = __( + 'Attribute code "%1" is invalid. Please use only letters (a-z or A-Z), ' . + 'numbers (0-9) or underscore (_) in this field, and the first character should be a letter.', + $attributeCode + ); + } + + /** + * Check attribute_code for allowed length + */ + $minLength = Attribute::ATTRIBUTE_CODE_MIN_LENGTH; + $maxLength = Attribute::ATTRIBUTE_CODE_MAX_LENGTH; + $isAllowedLength = Zend_Validate::is( + trim($attributeCode), + 'StringLength', + ['min' => $minLength, 'max' => $maxLength] + ); + if (!$isAllowedLength) { + $errorMessages[] = __( + 'An attribute code must not be less than %1 and more than %2 characters.', + $minLength, + $maxLength + ); + } + + /** + * Check attribute_code for prohibited prefix + */ + if (strpos($attributeCode, AbstractModifier::CONTAINER_PREFIX) === 0) { + $errorMessages[] = __( + '"%1" prefix is reserved by the system and cannot be used in attribute code names.', + AbstractModifier::CONTAINER_PREFIX + ); + } + + $this->_addMessages($errorMessages); + + return !$this->hasMessages(); + } +} diff --git a/app/code/Magento/Eav/Plugin/Model/Entity/Attribute.php b/app/code/Magento/Eav/Plugin/Model/Entity/Attribute.php new file mode 100644 index 0000000000000..ef88ef40e84aa --- /dev/null +++ b/app/code/Magento/Eav/Plugin/Model/Entity/Attribute.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Eav\Plugin\Model\Entity; + +use Magento\Eav\Model\Entity\Attribute as EavEntityAttribute; +use Magento\Eav\Model\Validator\Attribute\Code as AttributeCodeValidator; +use Magento\Framework\Exception\LocalizedException; + +class Attribute +{ + /** + * @var AttributeCodeValidator + */ + private $attributeCodeValidator; + + /** + * @param AttributeCodeValidator $attributeCodeValidator + */ + public function __construct(AttributeCodeValidator $attributeCodeValidator) + { + $this->attributeCodeValidator = $attributeCodeValidator; + } + + /** + * @param EavEntityAttribute $subject + * @throws \Zend_Validate_Exception + * @throws LocalizedException + */ + public function beforeSave(EavEntityAttribute $subject) + { + $attributeCode = $subject->getData('attribute_code') + ?? $subject->getData('frontend_label')[0]; + + if (!$this->attributeCodeValidator->isValid($attributeCode)) { + throw new LocalizedException(current($this->attributeCodeValidator->getMessages())); + } + } +} diff --git a/app/code/Magento/Eav/Setup/EavSetup.php b/app/code/Magento/Eav/Setup/EavSetup.php index 13f55308e6fa6..2f43d6543df3f 100644 --- a/app/code/Magento/Eav/Setup/EavSetup.php +++ b/app/code/Magento/Eav/Setup/EavSetup.php @@ -5,6 +5,7 @@ */ namespace Magento\Eav\Setup; +use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier; use Magento\Eav\Model\Entity\Setup\Context; use Magento\Eav\Model\Entity\Setup\PropertyMapperInterface; use Magento\Eav\Model\ResourceModel\Entity\Attribute\Group\CollectionFactory; @@ -782,13 +783,18 @@ private function _validateAttributeData($data) ); if (!$isAllowedLength) { - $errorMessage = __( + throw new LocalizedException(__( 'An attribute code must not be less than %1 and more than %2 characters.', $minLength, $maxLength - ); + )); + } - throw new LocalizedException($errorMessage); + if (strpos($attributeCode, AbstractModifier::CONTAINER_PREFIX) === 0) { + throw new LocalizedException(__( + '"%1" prefix is reserved by the system and cannot be used in attribute code names.', + AbstractModifier::CONTAINER_PREFIX + )); } return true; diff --git a/app/code/Magento/Eav/Test/Unit/Model/Validator/Attribute/CodeTest.php b/app/code/Magento/Eav/Test/Unit/Model/Validator/Attribute/CodeTest.php new file mode 100644 index 0000000000000..72bd25cf9b665 --- /dev/null +++ b/app/code/Magento/Eav/Test/Unit/Model/Validator/Attribute/CodeTest.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Eav\Test\Unit\Model\Validator\Attribute; + +use Magento\Eav\Model\Validator\Attribute\Code; +use PHPUnit\Framework\TestCase; + +class CodeTest extends TestCase +{ + /** + * Testing \Magento\Eav\Model\Validator\Attribute\Code::isValid + * + * @dataProvider isValidDataProvider + * @param string $attributeCode + * @param bool $expected + * @throws \Zend_Validate_Exception + */ + public function testIsValid(string $attributeCode, bool $expected) + { + $validator = new Code(); + $this->assertEquals($expected, $validator->isValid($attributeCode)); + } + + /** + * Data provider for testIsValid + * + * @return array + */ + public function isValidDataProvider(): array + { + return [ + [ + 'Attribute_code', + true + ], [ + 'attribute_1', + true + ],[ + 'Attribute_1', + true + ], [ + '_attribute_code', + false + ], [ + 'attribute.code', + false + ], [ + '1attribute_code', + false + ], [ + 'more_than_60_chars_more_than_60_chars_more_than_60_chars_more', + false + ], [ + 'container_attribute', + false, + ], + ]; + } +} diff --git a/app/code/Magento/Eav/etc/di.xml b/app/code/Magento/Eav/etc/di.xml index c8afd10aa3eee..9825ed7c177de 100644 --- a/app/code/Magento/Eav/etc/di.xml +++ b/app/code/Magento/Eav/etc/di.xml @@ -53,6 +53,7 @@ </arguments> </type> <type name="Magento\Eav\Model\Entity\Attribute"> + <plugin name="eav_code_validator" type="Magento\Eav\Plugin\Model\Entity\Attribute" /> <arguments> <argument name="reservedAttributeList" xsi:type="object">Magento\Catalog\Model\Product\ReservedAttributeList\Proxy</argument> </arguments> diff --git a/app/code/Magento/Eav/i18n/en_US.csv b/app/code/Magento/Eav/i18n/en_US.csv index 73f8b359d1c1b..c8c376eaa5da8 100644 --- a/app/code/Magento/Eav/i18n/en_US.csv +++ b/app/code/Magento/Eav/i18n/en_US.csv @@ -143,3 +143,6 @@ hello,hello "The value of attribute not valid","The value of attribute not valid" "EAV types and attributes","EAV types and attributes" "Entity types declaration cache","Entity types declaration cache" +"Attribute code ""%1"" is invalid. Please use only letters (a-z), numbers (0-9) or underscore(_) in this field, first character should be a letter.","Attribute code ""%1"" is invalid. Please use only letters (a-z), numbers (0-9) or underscore(_) in this field, first character should be a letter." +"An attribute code must not be less than %1 and more than %2 characters.","An attribute code must not be less than %1 and more than %2 characters." +"""%1"" prefix is reserved by the system and cannot be used in attribute code names.","""%1"" prefix is reserved by the system and cannot be used in attribute code names." diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php index 169b6fa52e307..ee71223a1df16 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php @@ -156,8 +156,8 @@ public function testWrongAttributeCode() /** @var \Magento\Framework\Message\Error $message */ $message = $messages->getItemsByType('error')[0]; $this->assertEquals( - 'Attribute code "_()&&&?" is invalid. Please use only letters (a-z),' - . ' numbers (0-9) or underscore(_) in this field, first character should be a letter.', + 'Attribute code "_()&&&?" is invalid. Please use only letters (a-z or A-Z),' + . ' numbers (0-9) or underscore (_) in this field, and the first character should be a letter.', $message->getText() ); } From 432013470716d82a960c5f45a6697de0293c04af Mon Sep 17 00:00:00 2001 From: Roman Lytvynenko <lytvynen@adobe.com> Date: Tue, 14 May 2019 09:29:59 -0500 Subject: [PATCH 1268/1295] MAGETWO-99516: Account lock status not showing correctly in Customer Grid --- .../Magento/Customer/Model/CustomerAuthUpdate.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Customer/Model/CustomerAuthUpdate.php b/app/code/Magento/Customer/Model/CustomerAuthUpdate.php index a805c1957df99..bc9bffb6ffdf0 100644 --- a/app/code/Magento/Customer/Model/CustomerAuthUpdate.php +++ b/app/code/Magento/Customer/Model/CustomerAuthUpdate.php @@ -56,18 +56,23 @@ public function saveAuth($customerId) { $customerSecure = $this->customerRegistry->retrieveSecureData($customerId); + $this->customerResourceModel->load($this->customerModel, $customerId); + $currentLockExpiresVal = $this->customerModel->getData('lock_expires'); + $newLockExpiresVal = $customerSecure->getData('lock_expires'); + $this->customerResourceModel->getConnection()->update( $this->customerResourceModel->getTable('customer_entity'), [ 'failures_num' => $customerSecure->getData('failures_num'), 'first_failure' => $customerSecure->getData('first_failure'), - 'lock_expires' => $customerSecure->getData('lock_expires'), + 'lock_expires' => $newLockExpiresVal, ], $this->customerResourceModel->getConnection()->quoteInto('entity_id = ?', $customerId) ); - $this->customerResourceModel->load($this->customerModel, $customerId); - $this->customerModel->reindex(); + if ($currentLockExpiresVal !== $newLockExpiresVal) { + $this->customerModel->reindex(); + } return $this; } From 777ff40a754fc2e1fff400197fd5e8720fbcbc00 Mon Sep 17 00:00:00 2001 From: Slava Mankivski <mankivsk@adobe.com> Date: Tue, 14 May 2019 10:29:11 -0500 Subject: [PATCH 1269/1295] MAGETWO-99645: Integration of Allure reports in Magento 2.2 --- dev/tests/unit/phpunit.xml.dist | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dev/tests/unit/phpunit.xml.dist b/dev/tests/unit/phpunit.xml.dist index d64f9f976b78d..e73412eaa8f00 100644 --- a/dev/tests/unit/phpunit.xml.dist +++ b/dev/tests/unit/phpunit.xml.dist @@ -59,6 +59,9 @@ <element key="expectedExceptionMessageRegExp"> <string>expectedExceptionMessageRegExp</string> </element> + <element key="security"> + <string>security</string> + </element> </array> </arguments> </listener> From 7ff9b7d66dfc9116fcb5bcca188f680dfdb5d4dc Mon Sep 17 00:00:00 2001 From: Roman Lytvynenko <lytvynen@adobe.com> Date: Tue, 14 May 2019 16:46:05 -0500 Subject: [PATCH 1270/1295] MAGETWO-97974: Product Content Description on Store view level cannot be updated if WYSIWYG is turned off --- app/code/Magento/Ui/view/base/web/js/form/element/wysiwyg.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/wysiwyg.js b/app/code/Magento/Ui/view/base/web/js/form/element/wysiwyg.js index b25bdc165ccba..c3b9982c4972e 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/wysiwyg.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/wysiwyg.js @@ -112,7 +112,9 @@ define([ * @param {Boolean} status */ setDisabled: function (status) { - this.$wysiwygEditorButton.attr('disabled', status); + if (this.$wysiwygEditorButton) { + this.$wysiwygEditorButton.attr('disabled', status); + } /* eslint-disable no-undef */ if (tinyMCE && tinyMCE.activeEditor) { From 4ced714930232679627a0fdeaad80140917c0fc1 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Wed, 15 May 2019 08:06:27 -0500 Subject: [PATCH 1271/1295] MC-16451: [Backport for 2.2.x] Remove unnecessary sniffs for phtml files --- .../static/framework/Magento/ruleset.xml | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/dev/tests/static/framework/Magento/ruleset.xml b/dev/tests/static/framework/Magento/ruleset.xml index fac0842214d92..161b095d55ade 100644 --- a/dev/tests/static/framework/Magento/ruleset.xml +++ b/dev/tests/static/framework/Magento/ruleset.xml @@ -27,4 +27,26 @@ <rule ref="Squiz.Functions.GlobalFunction"/> <rule ref="Squiz.Operators.ValidLogicalOperators"/> <rule ref="Squiz.WhiteSpace.LogicalOperatorSpacing"/> + + <rule ref="Magento2.Files.LineLength.MaxExceeded"> + <exclude-pattern>*.phtml</exclude-pattern> + </rule> + <rule ref="Squiz.Operators.IncrementDecrementUsage"> + <exclude-pattern>*.phtml</exclude-pattern> + </rule> + <rule ref="PEAR.ControlStructures.ControlSignature"> + <exclude-pattern>*.phtml</exclude-pattern> + </rule> + <rule ref="Squiz.WhiteSpace.ScopeClosingBrace"> + <exclude-pattern>*.phtml</exclude-pattern> + </rule> + <rule ref="PEAR.Functions.FunctionCallSignature"> + <exclude-pattern>*.phtml</exclude-pattern> + </rule> + <rule ref="Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace"> + <exclude-pattern>*.phtml</exclude-pattern> + </rule> + <rule ref="Magento2.Security.LanguageConstruct.DirectOutput"> + <exclude-pattern>*.phtml</exclude-pattern> + </rule> </ruleset> From 0d2ea21aa761e59e089d0d39d5083622dd3b7bd6 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Wed, 15 May 2019 08:24:52 -0500 Subject: [PATCH 1272/1295] MC-16451: [Backport for 2.2.x] Remove unnecessary sniffs for phtml files --- dev/tests/static/framework/Magento/ruleset.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/dev/tests/static/framework/Magento/ruleset.xml b/dev/tests/static/framework/Magento/ruleset.xml index 161b095d55ade..a0b21b1dfd0b1 100644 --- a/dev/tests/static/framework/Magento/ruleset.xml +++ b/dev/tests/static/framework/Magento/ruleset.xml @@ -15,6 +15,7 @@ <property name="lineLimit" value="120"/> <property name="absoluteLineLimit" value="120"/> </properties> + <exclude-pattern>*.phtml</exclude-pattern> </rule> <rule ref="Magento.LiteralNamespaces.LiteralNamespaces"> <exclude-pattern>*/_files/*</exclude-pattern> @@ -28,9 +29,6 @@ <rule ref="Squiz.Operators.ValidLogicalOperators"/> <rule ref="Squiz.WhiteSpace.LogicalOperatorSpacing"/> - <rule ref="Magento2.Files.LineLength.MaxExceeded"> - <exclude-pattern>*.phtml</exclude-pattern> - </rule> <rule ref="Squiz.Operators.IncrementDecrementUsage"> <exclude-pattern>*.phtml</exclude-pattern> </rule> @@ -46,7 +44,4 @@ <rule ref="Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace"> <exclude-pattern>*.phtml</exclude-pattern> </rule> - <rule ref="Magento2.Security.LanguageConstruct.DirectOutput"> - <exclude-pattern>*.phtml</exclude-pattern> - </rule> </ruleset> From 784162c29e729246d0445cdcf79f9cab69bfea9f Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 15 May 2019 10:41:56 -0500 Subject: [PATCH 1273/1295] MAGETWO-99660: Custom customer address attribute (dropdown) not getting populated for addresses for creation of orders in Admin --- .../Adminhtml/Order/Create/Form/Address.php | 12 +- .../Order/Create/Form/AddressTest.php | 260 ++++++++++++++++++ 2 files changed, 268 insertions(+), 4 deletions(-) create mode 100644 app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Create/Form/AddressTest.php diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Address.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Address.php index 6625f438f9515..eb90a67ee9cf2 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Address.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Address.php @@ -9,6 +9,8 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\Data\Form\Element\AbstractElement; use Magento\Framework\Pricing\PriceCurrencyInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Eav\Model\AttributeDataFactory; /** * Order create address form @@ -190,17 +192,19 @@ public function getAddressCollectionJson() $emptyAddressForm = $this->_customerFormFactory->create( 'customer_address', 'adminhtml_customer_address', - [\Magento\Customer\Api\Data\AddressInterface::COUNTRY_ID => $defaultCountryId] + [AddressInterface::COUNTRY_ID => $defaultCountryId] ); - $data = [0 => $emptyAddressForm->outputData(\Magento\Eav\Model\AttributeDataFactory::OUTPUT_FORMAT_JSON)]; + $data = [0 => $emptyAddressForm->outputData(AttributeDataFactory::OUTPUT_FORMAT_JSON)]; foreach ($this->getAddressCollection() as $address) { $addressForm = $this->_customerFormFactory->create( 'customer_address', 'adminhtml_customer_address', - $this->addressMapper->toFlatArray($address) + $this->addressMapper->toFlatArray($address), + false, + false ); $data[$address->getId()] = $addressForm->outputData( - \Magento\Eav\Model\AttributeDataFactory::OUTPUT_FORMAT_JSON + AttributeDataFactory::OUTPUT_FORMAT_JSON ); } diff --git a/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Create/Form/AddressTest.php b/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Create/Form/AddressTest.php new file mode 100644 index 0000000000000..debe103f89fb5 --- /dev/null +++ b/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Create/Form/AddressTest.php @@ -0,0 +1,260 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Test\Unit\Block\Adminhtml\Order\Create\Form; + +use Magento\Backend\Model\Session\Quote as QuoteSession; +use Magento\Store\Model\Store; +use Magento\Directory\Helper\Data as DirectoryHelper; +use Magento\Eav\Model\AttributeDataFactory; +use Magento\Sales\Block\Adminhtml\Order\Create\Form\Address; +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\Data\AddressSearchResultsInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Model\Metadata\Form; +use Magento\Customer\Model\Metadata\FormFactory; +use Magento\Customer\Model\Address\Mapper; +use Magento\Framework\Api\FilterBuilder; +use Magento\Framework\Api\Filter; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Api\SearchCriteria; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +class FormTest extends TestCase +{ + /** + * @var QuoteSession|MockObject + */ + private $quoteSession; + + /** + * @var Store|MockObject + */ + private $store; + + /** + * @var DirectoryHelper|MockObject + */ + private $directoryHelper; + + /** + * @var int + */ + private $defaultCountryId; + + /** + * @var int + */ + private $customerId; + + /** + * @var int + */ + private $addressId; + + /** + * @var FormFactory|MockObject + */ + private $formFactory; + + /** + * @var FilterBuilder|MockObject + */ + private $filterBuilder; + + /** + * @var SearchCriteriaBuilder|MockObject + */ + private $criteriaBuilder; + + /** + * @var AddressInterface|MockObject + */ + private $addressItem; + + /** + * @var AddressRepositoryInterface|MockObject + */ + private $addressService; + + /** + * @var Mapper|MockObject + */ + private $addressMapper; + + /** + * @var Address + */ + private $address; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = new ObjectManager($this); + + $this->defaultCountryId = 1; + $this->customerId = 10; + $this->addressId = 100; + + $this->quoteSession = $this->getMockBuilder(QuoteSession::class) + ->disableOriginalConstructor() + ->setMethods(['getStore', 'getCustomerId']) + ->getMock(); + $this->store = $this->getMockBuilder(Store::class) + ->disableOriginalConstructor() + ->getMock(); + $this->quoteSession->expects($this->any()) + ->method('getStore') + ->willReturn($this->store); + $this->quoteSession->expects($this->any()) + ->method('getCustomerId') + ->willReturn($this->customerId ); + $this->directoryHelper = $this->getMockBuilder(DirectoryHelper::class) + ->disableOriginalConstructor() + ->setMethods(['getDefaultCountry']) + ->getMock(); + $this->directoryHelper->expects($this->any()) + ->method('getDefaultCountry') + ->willReturn($this->defaultCountryId); + $this->formFactory = $this->getMockBuilder(FormFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->filterBuilder = $this->getMockBuilder(FilterBuilder::class) + ->disableOriginalConstructor() + ->setMethods(['setField', 'setValue', 'setConditionType', 'create']) + ->getMock(); + $this->criteriaBuilder = $this->getMockBuilder(SearchCriteriaBuilder::class) + ->disableOriginalConstructor() + ->setMethods(['create', 'addFilters']) + ->getMock(); + $this->addressService = $this->getMockBuilder(AddressRepositoryInterface::class) + ->setMethods(['getList']) + ->getMockForAbstractClass(); + $this->addressItem = $this->getMockBuilder(AddressInterface::class) + ->setMethods(['getId']) + ->getMockForAbstractClass(); + $this->addressItem->expects($this->any()) + ->method('getId') + ->willReturn($this->addressId); + $this->addressMapper = $this->getMockBuilder(Mapper::class) + ->disableOriginalConstructor() + ->setMethods(['toFlatArray']) + ->getMock(); + + $this->address = $this->objectManager->getObject( + Address::class, + [ + 'directoryHelper' => $this->directoryHelper, + 'sessionQuote' => $this->quoteSession, + 'customerFormFactory' => $this->formFactory, + 'filterBuilder' => $this->filterBuilder, + 'criteriaBuilder' => $this->criteriaBuilder, + 'addressService' => $this->addressService, + 'addressMapper' => $this->addressMapper + ] + ); + } + + public function testGetAddressCollectionJson() + { + /** @var Form|MockObject $emptyForm */ + $emptyForm = $this->getMockBuilder(Form::class) + ->disableOriginalConstructor() + ->setMethods(['outputData']) + ->getMock(); + $emptyForm->expects($this->once()) + ->method('outputData') + ->with(AttributeDataFactory::OUTPUT_FORMAT_JSON) + ->willReturn('emptyFormData'); + + /** @var Filter|MockObject $filter */ + $filter = $this->getMockBuilder(Filter::class) + ->disableOriginalConstructor() + ->getMock(); + $this->filterBuilder->expects($this->once()) + ->method('setField') + ->with('parent_id') + ->willReturnSelf(); + $this->filterBuilder->expects($this->once()) + ->method('setValue') + ->with($this->customerId) + ->willReturnSelf(); + $this->filterBuilder->expects($this->once()) + ->method('setConditionType') + ->with('eq') + ->willReturnSelf(); + $this->filterBuilder->expects($this->once()) + ->method('create') + ->willReturn($filter); + + /** @var SearchCriteria|MockObject $searchCriteria */ + $searchCriteria = $this->getMockBuilder(SearchCriteria::class) + ->disableOriginalConstructor() + ->getMock(); + $this->criteriaBuilder->expects($this->once()) + ->method('create') + ->willReturn($searchCriteria); + $this->criteriaBuilder->expects($this->once()) + ->method('addFilters') + ->with([$filter]); + + /** @var AddressSearchResultsInterface|MockObject $result */ + $result = $this->getMockBuilder(AddressSearchResultsInterface::class) + ->setMethods(['getList']) + ->getMockForAbstractClass(); + $result->expects($this->once()) + ->method('getItems') + ->willReturn([$this->addressItem]); + $this->addressService->expects($this->once()) + ->method('getList') + ->with($searchCriteria) + ->willReturn($result); + + /** @var Form|MockObject $emptyForm */ + $addressForm = $this->getMockBuilder(Form::class) + ->disableOriginalConstructor() + ->setMethods(['outputData']) + ->getMock(); + $addressForm->expects($this->once()) + ->method('outputData') + ->with(AttributeDataFactory::OUTPUT_FORMAT_JSON) + ->willReturn('addressFormData'); + $this->addressMapper->expects($this->once()) + ->method('toFlatArray') + ->with($this->addressItem) + ->willReturn([]); + + $this->directoryHelper->expects($this->once()) + ->method('getDefaultCountry') + ->with($this->store) + ->willReturn($this->defaultCountryId); + $this->formFactory->expects($this->at(0)) + ->method('create') + ->with( + 'customer_address', + 'adminhtml_customer_address', + [AddressInterface::COUNTRY_ID => $this->defaultCountryId] + ) + ->willReturn($emptyForm); + $this->formFactory->expects($this->at(1)) + ->method('create') + ->with('customer_address', 'adminhtml_customer_address', [], false, false) + ->willReturn($addressForm); + + $this->address->getAddressCollectionJson(); + } +} From 1a835e8ab0544d574c21a24b504f844369950462 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Wed, 15 May 2019 11:14:23 -0500 Subject: [PATCH 1274/1295] MC-16451: [Backport for 2.2.x] Remove unnecessary sniffs for phtml files --- dev/tests/static/framework/Magento/ruleset.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/dev/tests/static/framework/Magento/ruleset.xml b/dev/tests/static/framework/Magento/ruleset.xml index a0b21b1dfd0b1..8768a8400d1b8 100644 --- a/dev/tests/static/framework/Magento/ruleset.xml +++ b/dev/tests/static/framework/Magento/ruleset.xml @@ -29,18 +29,12 @@ <rule ref="Squiz.Operators.ValidLogicalOperators"/> <rule ref="Squiz.WhiteSpace.LogicalOperatorSpacing"/> - <rule ref="Squiz.Operators.IncrementDecrementUsage"> - <exclude-pattern>*.phtml</exclude-pattern> - </rule> <rule ref="PEAR.ControlStructures.ControlSignature"> <exclude-pattern>*.phtml</exclude-pattern> </rule> <rule ref="Squiz.WhiteSpace.ScopeClosingBrace"> <exclude-pattern>*.phtml</exclude-pattern> </rule> - <rule ref="PEAR.Functions.FunctionCallSignature"> - <exclude-pattern>*.phtml</exclude-pattern> - </rule> <rule ref="Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace"> <exclude-pattern>*.phtml</exclude-pattern> </rule> From db2acb1e6764398a47f6f0ae033b4671a269d0d9 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 15 May 2019 11:15:44 -0500 Subject: [PATCH 1275/1295] MAGETWO-99660: Custom customer address attribute (dropdown) not getting populated for addresses for creation of orders in Admin --- .../Test/Unit/Block/Adminhtml/Order/Create/Form/AddressTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Create/Form/AddressTest.php b/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Create/Form/AddressTest.php index debe103f89fb5..39795a28190cd 100644 --- a/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Create/Form/AddressTest.php +++ b/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Create/Form/AddressTest.php @@ -26,7 +26,7 @@ use PHPUnit\Framework\TestCase; use PHPUnit_Framework_MockObject_MockObject as MockObject; -class FormTest extends TestCase +class AddressTest extends TestCase { /** * @var QuoteSession|MockObject From ee3c85a229547f1ae0118a60f5c65c107156b6a3 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 15 May 2019 11:18:01 -0500 Subject: [PATCH 1276/1295] MAGETWO-99660: Custom customer address attribute (dropdown) not getting populated for addresses for creation of orders in Admin --- .../Unit/Block/Adminhtml/Order/Create/Form/AddressTest.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Create/Form/AddressTest.php b/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Create/Form/AddressTest.php index 39795a28190cd..5b6d6ded1561a 100644 --- a/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Create/Form/AddressTest.php +++ b/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Create/Form/AddressTest.php @@ -26,6 +26,9 @@ use PHPUnit\Framework\TestCase; use PHPUnit_Framework_MockObject_MockObject as MockObject; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class AddressTest extends TestCase { /** @@ -121,7 +124,7 @@ protected function setUp() ->willReturn($this->store); $this->quoteSession->expects($this->any()) ->method('getCustomerId') - ->willReturn($this->customerId ); + ->willReturn($this->customerId); $this->directoryHelper = $this->getMockBuilder(DirectoryHelper::class) ->disableOriginalConstructor() ->setMethods(['getDefaultCountry']) From bcf0b0487969e4eeb97e8b002dee0244bbd6e66e Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Wed, 15 May 2019 12:07:45 -0500 Subject: [PATCH 1277/1295] MC-16451: [Backport for 2.2.x] Remove unnecessary sniffs for phtml files --- dev/tests/static/framework/Magento/ruleset.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/dev/tests/static/framework/Magento/ruleset.xml b/dev/tests/static/framework/Magento/ruleset.xml index 8768a8400d1b8..62068c16fd7c8 100644 --- a/dev/tests/static/framework/Magento/ruleset.xml +++ b/dev/tests/static/framework/Magento/ruleset.xml @@ -32,10 +32,4 @@ <rule ref="PEAR.ControlStructures.ControlSignature"> <exclude-pattern>*.phtml</exclude-pattern> </rule> - <rule ref="Squiz.WhiteSpace.ScopeClosingBrace"> - <exclude-pattern>*.phtml</exclude-pattern> - </rule> - <rule ref="Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace"> - <exclude-pattern>*.phtml</exclude-pattern> - </rule> </ruleset> From b9efb8171f8d547ba706eae159beee5157384731 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 15 May 2019 12:26:59 -0500 Subject: [PATCH 1278/1295] MAGETWO-99660: Custom customer address attribute (dropdown) not getting populated for addresses for creation of orders in Admin --- .../testsuite/Magento/Customer/Fixtures/address_data.php | 8 ++++++++ .../Block/Adminhtml/Order/Create/Form/AddressTest.php | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Customer/Fixtures/address_data.php b/dev/tests/integration/testsuite/Magento/Customer/Fixtures/address_data.php index 62b7fab906c57..f99a8e0a79cca 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Fixtures/address_data.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Fixtures/address_data.php @@ -15,6 +15,10 @@ 'lastname' => 'Smith', 'firstname' => 'John', 'region_id' => 12, + 'prefix' => false, + 'middlename' => false, + 'suffix' => false, + 'fax' => false ], [ 'telephone' => 845454465, @@ -24,5 +28,9 @@ 'street' => ['Tunnel Alexanderpl'], 'lastname' => 'Smith', 'firstname' => 'John', + 'prefix' => false, + 'middlename' => false, + 'suffix' => false, + 'fax' => false ] ]; diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AddressTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AddressTest.php index f5a22ec19ccf3..f3c091e854e82 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AddressTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AddressTest.php @@ -84,6 +84,7 @@ public function testGetAddressCollection() * Checks address collection output encoded to json. * * @magentoDataFixture Magento/Customer/Fixtures/customer_sec_website_2_addresses.php + * @magentoDbIsolation enabled */ public function testGetAddressCollectionJson() { @@ -122,6 +123,10 @@ public function testGetAddressCollectionJson() 'postcode' => '90230', 'telephone' => '3468676', 'vat_id' => false, + 'prefix' => false, + 'middlename' => false, + 'suffix' => false, + 'fax' => false ], $addresses[1]->getId() => [ 'telephone' => '845454465', @@ -135,6 +140,10 @@ public function testGetAddressCollectionJson() 'region' => false, 'region_id' => 0, 'vat_id' => false, + 'prefix' => false, + 'middlename' => false, + 'suffix' => false, + 'fax' => false ] ]; From 4c8d8e66e9d667b468f5d093a45027e329063bd7 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Wed, 15 May 2019 12:40:10 -0500 Subject: [PATCH 1279/1295] MC-16451: [Backport for 2.2.x] Remove unnecessary sniffs for phtml files --- dev/tests/static/framework/Magento/ruleset.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dev/tests/static/framework/Magento/ruleset.xml b/dev/tests/static/framework/Magento/ruleset.xml index 62068c16fd7c8..602207d85829e 100644 --- a/dev/tests/static/framework/Magento/ruleset.xml +++ b/dev/tests/static/framework/Magento/ruleset.xml @@ -32,4 +32,13 @@ <rule ref="PEAR.ControlStructures.ControlSignature"> <exclude-pattern>*.phtml</exclude-pattern> </rule> + <rule ref="Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace"> + <exclude-pattern>*.phtml</exclude-pattern> + </rule> + <rule ref="PEAR.WhiteSpace.ScopeClosingBrace"> + <exclude-pattern>*.phtml</exclude-pattern> + </rule> + <rule ref="Squiz.WhiteSpace.ScopeClosingBrace"> + <exclude-pattern>*.phtml</exclude-pattern> + </rule> </ruleset> From 93680beb2a1d731561778633f824691102334776 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 15 May 2019 12:47:34 -0500 Subject: [PATCH 1280/1295] MAGETWO-99660: Custom customer address attribute (dropdown) not getting populated for addresses for creation of orders in Admin --- .../testsuite/Magento/Customer/Fixtures/address_data.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Customer/Fixtures/address_data.php b/dev/tests/integration/testsuite/Magento/Customer/Fixtures/address_data.php index f99a8e0a79cca..62b7fab906c57 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Fixtures/address_data.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Fixtures/address_data.php @@ -15,10 +15,6 @@ 'lastname' => 'Smith', 'firstname' => 'John', 'region_id' => 12, - 'prefix' => false, - 'middlename' => false, - 'suffix' => false, - 'fax' => false ], [ 'telephone' => 845454465, @@ -28,9 +24,5 @@ 'street' => ['Tunnel Alexanderpl'], 'lastname' => 'Smith', 'firstname' => 'John', - 'prefix' => false, - 'middlename' => false, - 'suffix' => false, - 'fax' => false ] ]; From a387566bfd91d0dca7c9955dc808e16175842c6a Mon Sep 17 00:00:00 2001 From: gauravagarwal1001 <37572719+gauravagarwal1001@users.noreply.github.com> Date: Wed, 8 May 2019 20:03:37 +0530 Subject: [PATCH 1281/1295] Fixed issue #22788 --- .../Sales/view/frontend/templates/email/shipment/track.phtml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/view/frontend/templates/email/shipment/track.phtml b/app/code/Magento/Sales/view/frontend/templates/email/shipment/track.phtml index f1cd5f2b99865..6de8e42dea583 100644 --- a/app/code/Magento/Sales/view/frontend/templates/email/shipment/track.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/email/shipment/track.phtml @@ -9,8 +9,9 @@ ?> <?php $_shipment = $block->getShipment() ?> <?php $_order = $block->getOrder() ?> +<?php if ($_shipment && $_order): ?> <?php $trackCollection = $_order->getTracksCollection($_shipment->getId()) ?> -<?php if ($_shipment && $_order && $trackCollection): ?> +<?php if ($trackCollection): ?> <br /> <table class="shipment-track"> <thead> @@ -29,3 +30,4 @@ </tbody> </table> <?php endif; ?> +<?php endif; ?> From 9277f76c4d884393ebcc5cc167d1462e0fdf4c53 Mon Sep 17 00:00:00 2001 From: Gulshan Chitransh <gulshanchitransh@cedcoss.com> Date: Sat, 4 May 2019 12:39:29 +0530 Subject: [PATCH 1282/1295] Resolved 404 not found form validation url when updating item quantity in cart page Resolved 404 not found form validation url when updating item quantity in cart page --- .../Magento/Checkout/view/frontend/templates/cart/form.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml index 84ab9b13d8f3a..814a569d0946c 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml @@ -14,7 +14,7 @@ method="post" id="form-validate" data-mage-init='{"Magento_Checkout/js/action/update-shopping-cart": - {"validationURL" : "/checkout/cart/updateItemQty", + {"validationURL" : "<?= /* @escapeNotVerified */ $block->getUrl('checkout/cart/updateItemQty') ?>", "updateCartActionContainer": "#update_cart_action_container"} }' class="form form-cart"> From e5b5bc10ba53634a7b5de257ed10e3fa3d472023 Mon Sep 17 00:00:00 2001 From: Surabhi Srivastava <surabhisrivastava@cedcoss.com> Date: Sat, 4 May 2019 06:16:50 +0000 Subject: [PATCH 1283/1295] Fixed issue #22640 Fixed issue #22640 --- .../Magento/Tax/view/adminhtml/templates/rule/rate/form.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Tax/view/adminhtml/templates/rule/rate/form.phtml b/app/code/Magento/Tax/view/adminhtml/templates/rule/rate/form.phtml index c0d185c36b0d6..14abaeba0c71a 100644 --- a/app/code/Magento/Tax/view/adminhtml/templates/rule/rate/form.phtml +++ b/app/code/Magento/Tax/view/adminhtml/templates/rule/rate/form.phtml @@ -10,7 +10,7 @@ <div class="grid-loader"></div> </div> -<div class="form-inline" id="<?= /* @escapeNotVerified */ $block->getNameInLayout() ?>" style="display:none"> +<div class="form-inline admin__scope-old" id="<?= /* @escapeNotVerified */ $block->getNameInLayout() ?>" style="display:none"> <?= $block->getFormHtml() ?> <?= $block->getChildHtml('form_after') ?> </div> From ae9fa7981ef8925baf8ed8f0914440d700cf70aa Mon Sep 17 00:00:00 2001 From: Surabhi Srivastava <surabhisrivastava@cedcoss.com> Date: Sat, 4 May 2019 06:49:06 +0000 Subject: [PATCH 1284/1295] Fixed issue #22640 Fixed issue #22640 --- app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php b/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php index c4c4f28e8a7d1..25e0599a1dcc1 100644 --- a/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php +++ b/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php @@ -176,7 +176,7 @@ protected function _prepareForm() } $legend = $this->getShowLegend() ? __('Tax Rate Information') : ''; - $fieldset = $form->addFieldset('base_fieldset', ['legend' => $legend, 'class' => 'form-inline']); + $fieldset = $form->addFieldset('base_fieldset', ['legend' => $legend, 'class' => 'admin__scope-old form-inline']); if (isset($formData['tax_calculation_rate_id']) && $formData['tax_calculation_rate_id'] > 0) { $fieldset->addField( From a2fc106bf4808cdccf179e288f71ba36ed1e1958 Mon Sep 17 00:00:00 2001 From: Surabhi Srivastava <surabhisrivastava@cedcoss.com> Date: Sat, 4 May 2019 07:00:32 +0000 Subject: [PATCH 1285/1295] Changes done in wrong file so i am reverting my chnages Changes done in wrong file so i am reverting my chnages --- .../Magento/Tax/view/adminhtml/templates/rule/rate/form.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Tax/view/adminhtml/templates/rule/rate/form.phtml b/app/code/Magento/Tax/view/adminhtml/templates/rule/rate/form.phtml index 14abaeba0c71a..c0d185c36b0d6 100644 --- a/app/code/Magento/Tax/view/adminhtml/templates/rule/rate/form.phtml +++ b/app/code/Magento/Tax/view/adminhtml/templates/rule/rate/form.phtml @@ -10,7 +10,7 @@ <div class="grid-loader"></div> </div> -<div class="form-inline admin__scope-old" id="<?= /* @escapeNotVerified */ $block->getNameInLayout() ?>" style="display:none"> +<div class="form-inline" id="<?= /* @escapeNotVerified */ $block->getNameInLayout() ?>" style="display:none"> <?= $block->getFormHtml() ?> <?= $block->getChildHtml('form_after') ?> </div> From 6fc7d7a27a251a19da1afd0afa230bbc6fb8b96d Mon Sep 17 00:00:00 2001 From: Nazarn96 <nazarn96@gmail.com> Date: Tue, 7 May 2019 13:03:42 +0300 Subject: [PATCH 1286/1295] magento/magento2#22655 static-test-fix --- app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php b/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php index 25e0599a1dcc1..790d19ceaeaa7 100644 --- a/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php +++ b/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php @@ -17,6 +17,8 @@ use Magento\Tax\Controller\RegistryConstants; /** + * Tax rate form. + * * @api * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @since 100.0.2 @@ -110,7 +112,7 @@ public function __construct( } /** - * @return void + * @inheritdoc */ protected function _construct() { @@ -119,6 +121,8 @@ protected function _construct() } /** + * Prepare form before rendering HTML. + * * @return $this * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) @@ -132,8 +136,9 @@ protected function _prepareForm() if ($taxRateId) { $taxRateDataObject = $this->_taxRateRepository->get($taxRateId); } + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock } catch (NoSuchEntityException $e) { - /* tax rate not found */ + //tax rate not found// } $sessionFormValues = (array)$this->_coreRegistry->registry(RegistryConstants::CURRENT_TAX_RATE_FORM_DATA); @@ -176,7 +181,10 @@ protected function _prepareForm() } $legend = $this->getShowLegend() ? __('Tax Rate Information') : ''; - $fieldset = $form->addFieldset('base_fieldset', ['legend' => $legend, 'class' => 'admin__scope-old form-inline']); + $fieldset = $form->addFieldset( + 'base_fieldset', + ['legend' => $legend, 'class' => 'admin__scope-old form-inline'] + ); if (isset($formData['tax_calculation_rate_id']) && $formData['tax_calculation_rate_id'] > 0) { $fieldset->addField( From 8d1feb8bc367436a260b6fa437bf8aab025bda6f Mon Sep 17 00:00:00 2001 From: Roman Lytvynenko <lytvynen@adobe.com> Date: Thu, 16 May 2019 16:14:42 -0500 Subject: [PATCH 1287/1295] MAGETWO-94464: Watermark with white or transparent background gets coverted to black when opacity is reduced --- lib/internal/Magento/Framework/Image/Adapter/Gd2.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php index 00194a55dce53..d14a071256cde 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php +++ b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php @@ -415,6 +415,7 @@ public function rotate($angle) * @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @SuppressWarnings(PHPMD.UnusedLocalVariable) * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @throws \Exception */ public function watermark($imagePath, $positionX = 0, $positionY = 0, $opacity = 30, $tile = false) { @@ -433,7 +434,7 @@ public function watermark($imagePath, $positionX = 0, $positionY = 0, $opacity = ) { $newWatermark = imagecreatetruecolor($this->getWatermarkWidth(), $this->getWatermarkHeight()); imagealphablending($newWatermark, false); - $col = imagecolorallocate($newWatermark, 255, 255, 255); + $col = imagecolorallocatealpha($newWatermark, 255, 255, 255, 127); imagecolortransparent($newWatermark, $col); imagefilledrectangle($newWatermark, 0, 0, $this->getWatermarkWidth(), $this->getWatermarkHeight(), $col); imagesavealpha($newWatermark, true); @@ -878,6 +879,8 @@ private function copyImageWithAlphaPercentage( return false; } + imagealphablending($destinationImage, true); + $result = imagecopy( $destinationImage, $tmpImg, From 1d677aa86150b083c6e72e5e6ce3e88f06baccc9 Mon Sep 17 00:00:00 2001 From: Roman Lytvynenko <lytvynen@adobe.com> Date: Thu, 16 May 2019 16:37:34 -0500 Subject: [PATCH 1288/1295] MAGETWO-94464: Watermark with white or transparent background gets coverted to black when opacity is reduced --- lib/internal/Magento/Framework/Image/Adapter/Gd2.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php index d14a071256cde..380f462e276c0 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php +++ b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php @@ -838,6 +838,8 @@ private function copyImageWithAlphaPercentage( ); } + imagealphablending($destinationImage, true); + if ($alphaPercentage >= 100) { return imagecopy( $destinationImage, @@ -879,8 +881,6 @@ private function copyImageWithAlphaPercentage( return false; } - imagealphablending($destinationImage, true); - $result = imagecopy( $destinationImage, $tmpImg, From c07182ea8895d94f6ffc181bc4484b2235eb4abc Mon Sep 17 00:00:00 2001 From: Roman Lytvynenko <lytvynen@adobe.com> Date: Fri, 17 May 2019 09:32:26 -0500 Subject: [PATCH 1289/1295] MAGETWO-94464: Watermark with white or transparent background gets coverted to black when opacity is reduced --- lib/internal/Magento/Framework/Image/Adapter/Gd2.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php index 380f462e276c0..fe0369b331887 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php +++ b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php @@ -426,8 +426,6 @@ public function watermark($imagePath, $positionX = 0, $positionY = 0, $opacity = $imagePath ); - $merged = false; - if ($this->getWatermarkWidth() && $this->getWatermarkHeight() && $this->getWatermarkPosition() != self::POSITION_STRETCH @@ -543,7 +541,7 @@ public function watermark($imagePath, $positionX = 0, $positionY = 0, $opacity = ); } - if ($tile === false && $merged === false) { + if ($tile === false) { $this->copyImageWithAlphaPercentage( $this->_imageHandler, $watermark, From f6b9170df7881a58fb579cf75d3593ccc3d6c67b Mon Sep 17 00:00:00 2001 From: Dmytro Yushkin <dyushkin@adobe.com> Date: Fri, 17 May 2019 13:03:17 -0500 Subject: [PATCH 1290/1295] MAGETWO-98805: Payflow Field format error: 10413-The totals of the cart item amounts do not match order amounts. --- .../Paypal/Model/Payflow/Transparent.php | 35 +++++++++++++++++-- .../Unit/Model/Payflow/TransparentTest.php | 22 ++++++++++-- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Paypal/Model/Payflow/Transparent.php b/app/code/Magento/Paypal/Model/Payflow/Transparent.php index c161580c1b7f1..68b3389ac858e 100644 --- a/app/code/Magento/Paypal/Model/Payflow/Transparent.php +++ b/app/code/Magento/Paypal/Model/Payflow/Transparent.php @@ -3,11 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Paypal\Model\Payflow; use Magento\Payment\Helper\Formatter; use Magento\Payment\Model\InfoInterface; use Magento\Paypal\Model\Payflowpro; +use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\Data\OrderPaymentExtensionInterfaceFactory; use Magento\Sales\Model\Order\Payment; use Magento\Paypal\Model\Payflow\Service\Gateway; @@ -125,6 +128,8 @@ public function __construct( } /** + * Returns Payflow ResponseValidator instance. + * * @return ResponseValidator */ public function getResponceValidator() @@ -166,14 +171,17 @@ public function authorize(InfoInterface $payment, $amount) $request->setData('origid', $token); $request->setData('amt', $this->formatPrice($amount)); $request->setData('currency', $order->getBaseCurrencyCode()); - $request->setData('taxamt', $this->formatPrice($order->getBaseTaxAmount())); + $request->setData('itemamt', $this->formatPrice($order->getBaseSubtotal())); + $request->setData('taxamt', $this->calculateTaxAmount($order)); $request->setData('freightamt', $this->formatPrice($order->getBaseShippingAmount())); + $request->setData('discount', $this->formatPrice(abs($order->getBaseDiscountAmount()))); $response = $this->postRequest($request, $this->getConfig()); $this->processErrors($response); try { $this->responseValidator->validate($response, $this); + // phpcs:ignore Magento2.Exceptions.ThrowCatch } catch (LocalizedException $exception) { $payment->setParentTransactionId($response->getData(self::PNREF)); $this->void($payment); @@ -199,10 +207,12 @@ public function getConfigInterface() } /** + * Creates vault payment token. + * * @param Payment $payment * @param string $token - * @throws LocalizedException * @return void + * @throws \Exception */ protected function createPaymentToken(Payment $payment, $token) { @@ -221,8 +231,11 @@ protected function createPaymentToken(Payment $payment, $token) } /** + * Generates CC expiration date by year and month provided in payment. + * * @param Payment $payment * @return string + * @throws \Exception */ private function getExpirationDate(Payment $payment) { @@ -241,6 +254,8 @@ private function getExpirationDate(Payment $payment) } /** + * Returns payment extension attributes instance. + * * @param Payment $payment * @return \Magento\Sales\Api\Data\OrderPaymentExtensionInterface */ @@ -276,4 +291,20 @@ public function capture(InfoInterface $payment, $amount) return $this; } + + /** + * Calculates tax amount including discount compensation for product/shipping price included tax. + * + * @param OrderInterface $order + * @return string + */ + private function calculateTaxAmount( + OrderInterface $order + ): string { + return $this->formatPrice( + $order->getBaseTaxAmount() + + $order->getBaseDiscountTaxCompensationAmount() + + $order->getBaseShippingDiscountTaxCompensationAmnt() + ); + } } diff --git a/app/code/Magento/Paypal/Test/Unit/Model/Payflow/TransparentTest.php b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/TransparentTest.php index 9b67cedb47885..aa321a27e7617 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/Payflow/TransparentTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/TransparentTest.php @@ -120,8 +120,19 @@ protected function initializationAuthorizeMock() { $this->orderMock = $this->getMockBuilder(\Magento\Sales\Model\Order::class) ->setMethods([ - 'getCustomerId', 'getBillingAddress', 'getShippingAddress', 'getCustomerEmail', - 'getId', 'getIncrementId', 'getBaseCurrencyCode' + 'getCustomerId', + 'getBillingAddress', + 'getShippingAddress', + 'getCustomerEmail', + 'getId', + 'getIncrementId', + 'getBaseCurrencyCode', + 'getBaseSubtotal', + 'getBaseTaxAmount', + 'getBaseDiscountTaxCompensationAmount', + 'getBaseShippingDiscountTaxCompensationAmnt', + 'getBaseShippingAmount', + 'getBaseDiscountAmount', ]) ->disableOriginalConstructor() ->getMock(); @@ -365,6 +376,13 @@ public function testAuthorize() $this->initializationAuthorizeMock(); $this->buildRequestData(); + $this->orderMock->expects($this->once())->method('getBaseSubtotal'); + $this->orderMock->expects($this->once())->method('getBaseTaxAmount'); + $this->orderMock->expects($this->once())->method('getBaseDiscountTaxCompensationAmount'); + $this->orderMock->expects($this->once())->method('getBaseShippingDiscountTaxCompensationAmnt'); + $this->orderMock->expects($this->once())->method('getBaseShippingAmount'); + $this->orderMock->expects($this->once())->method('getBaseDiscountAmount'); + $paymentTokenMock = $this->createMock(PaymentTokenInterface::class); $extensionAttributes = $this->getMockBuilder(\Magento\Sales\Api\Data\OrderPaymentExtensionInterface::class) ->disableOriginalConstructor() From 66663d07321f535f6696abebbf7c48b92c00f692 Mon Sep 17 00:00:00 2001 From: Roman Lytvynenko <lytvynen@adobe.com> Date: Fri, 17 May 2019 16:34:47 -0500 Subject: [PATCH 1291/1295] MAGETWO-94464: Watermark with white or transparent background gets coverted to black when opacity is reduced --- lib/internal/Magento/Framework/Image/Adapter/Gd2.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php index fe0369b331887..8bb05b53ae0e5 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php +++ b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php @@ -456,7 +456,7 @@ public function watermark($imagePath, $positionX = 0, $positionY = 0, $opacity = } elseif ($this->getWatermarkPosition() == self::POSITION_STRETCH) { $newWatermark = imagecreatetruecolor($this->_imageSrcWidth, $this->_imageSrcHeight); imagealphablending($newWatermark, false); - $col = imagecolorallocate($newWatermark, 255, 255, 255); + $col = imagecolorallocatealpha($newWatermark, 255, 255, 255, 127); imagecolortransparent($newWatermark, $col); imagefilledrectangle($newWatermark, 0, 0, $this->_imageSrcWidth, $this->_imageSrcHeight, $col); imagesavealpha($newWatermark, true); From 1cb2f22a7e898b925a736f00794e1b41dbce9026 Mon Sep 17 00:00:00 2001 From: Maksym Novik <m.novik@ism-ukraine.com> Date: Sat, 22 Dec 2018 15:59:26 +0200 Subject: [PATCH 1292/1295] Tierprice can't save float percentage value #18651. Display percentage on PDP in case it is set explicitly. --- .../Magento/Catalog/Pricing/Render/PriceBox.php | 14 +++++++++++++- .../base/templates/product/price/tier_prices.phtml | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Pricing/Render/PriceBox.php b/app/code/Magento/Catalog/Pricing/Render/PriceBox.php index 190168ed583fc..1df0ab2af2956 100644 --- a/app/code/Magento/Catalog/Pricing/Render/PriceBox.php +++ b/app/code/Magento/Catalog/Pricing/Render/PriceBox.php @@ -69,9 +69,11 @@ public function jsonEncode($valueToEncode) /** * Get random string * - * @param int $length + * @param int $length * @param string|null $chars + * * @return string + * @throws \Magento\Framework\Exception\LocalizedException */ public function getRandomString($length, $chars = null) { @@ -93,4 +95,14 @@ public function getCanDisplayQty(Product $product) } return true; } + + /** + * @param float $percent + * + * @return string + */ + public function formatPercent(float $percent):string + { + return rtrim(number_format($percent, 2), '.0'); + } } diff --git a/app/code/Magento/Catalog/view/base/templates/product/price/tier_prices.phtml b/app/code/Magento/Catalog/view/base/templates/product/price/tier_prices.phtml index f5cffb99d75dd..c2b7fb4e60855 100644 --- a/app/code/Magento/Catalog/view/base/templates/product/price/tier_prices.phtml +++ b/app/code/Magento/Catalog/view/base/templates/product/price/tier_prices.phtml @@ -77,7 +77,7 @@ $product = $block->getSaleableItem(); $price['price_qty'], $priceAmountBlock, $index, - $tierPriceModel->getSavePercent($price['price']) + $block->formatPercent($price['percentage_value'] ?? $tierPriceModel->getSavePercent($price['price'])) ) : __('Buy %1 for %2 each', $price['price_qty'], $priceAmountBlock); ?> From f8022f8ccbff824570f857fa25fc09400804084a Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Mon, 18 Feb 2019 15:20:02 +0000 Subject: [PATCH 1293/1295] magento/magento2#19584: Fixed static tests --- app/code/Magento/Catalog/Pricing/Render/PriceBox.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Pricing/Render/PriceBox.php b/app/code/Magento/Catalog/Pricing/Render/PriceBox.php index 1df0ab2af2956..3ec81683329bb 100644 --- a/app/code/Magento/Catalog/Pricing/Render/PriceBox.php +++ b/app/code/Magento/Catalog/Pricing/Render/PriceBox.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Pricing\Render; use Magento\Catalog\Model\Product; @@ -69,7 +71,7 @@ public function jsonEncode($valueToEncode) /** * Get random string * - * @param int $length + * @param int $length * @param string|null $chars * * @return string @@ -97,11 +99,12 @@ public function getCanDisplayQty(Product $product) } /** - * @param float $percent + * Format percent * + * @param float $percent * @return string */ - public function formatPercent(float $percent):string + public function formatPercent(float $percent): string { return rtrim(number_format($percent, 2), '.0'); } From 53aff6039e5caab006050ce38d5c3bcace940240 Mon Sep 17 00:00:00 2001 From: Maksym Novik <novik.kor@gmail.com> Date: Sun, 5 May 2019 17:56:43 +0300 Subject: [PATCH 1294/1295] Tierprice can't save float percentage value #18651. Fixed invalid zeros trimming. --- app/code/Magento/Catalog/Pricing/Render/PriceBox.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Pricing/Render/PriceBox.php b/app/code/Magento/Catalog/Pricing/Render/PriceBox.php index 3ec81683329bb..678b45ce97e7b 100644 --- a/app/code/Magento/Catalog/Pricing/Render/PriceBox.php +++ b/app/code/Magento/Catalog/Pricing/Render/PriceBox.php @@ -102,10 +102,16 @@ public function getCanDisplayQty(Product $product) * Format percent * * @param float $percent + * * @return string */ public function formatPercent(float $percent): string { - return rtrim(number_format($percent, 2), '.0'); + /*First rtrim - trim zeros. So, 10.00 -> 10.*/ + /*Second rtrim - trim dot. So, 10. -> 10*/ + return rtrim( + rtrim(number_format($percent, 2), '0'), + '.' + ); } } From 3143466ea8dc8ae46945d21b2b17c69c6cd45e38 Mon Sep 17 00:00:00 2001 From: Vlad Veselov <vlad.veselov@gmail.com> Date: Mon, 6 May 2019 02:43:36 +0300 Subject: [PATCH 1295/1295] Revert "Magento backend catalog cost without currency symbol" as Cost column must not be added to grid by default This reverts commit 85beeec61b061f9494df14cad448753bd9e75c8b. --- .../view/adminhtml/ui_component/product_listing.xml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml index 578281f44c4cf..65090fa3ac461 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml @@ -190,13 +190,6 @@ <label translate="true">Websites</label> </settings> </column> - <column name="cost" class="Magento\Catalog\Ui\Component\Listing\Columns\Price" sortOrder="120"> - <settings> - <addField>true</addField> - <filter>textRange</filter> - <label translate="true">Cost</label> - </settings> - </column> <actionsColumn name="actions" class="Magento\Catalog\Ui\Component\Listing\Columns\ProductActions" sortOrder="200"> <settings> <indexField>entity_id</indexField>